1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.james.mailboxmanager.torque;
21
22 import java.io.IOException;
23 import java.io.InputStream;
24 import java.io.Reader;
25 import java.io.StringReader;
26 import java.nio.CharBuffer;
27 import java.nio.charset.IllegalCharsetNameException;
28 import java.nio.charset.UnsupportedCharsetException;
29
30 import org.apache.commons.logging.Log;
31 import org.apache.commons.logging.LogFactory;
32 import org.apache.james.mime4j.MimeException;
33 import org.apache.james.mime4j.parser.MimeTokenStream;
34
35
36
37
38
39 class MessageSearcher {
40
41 private Log logger;
42
43 private CharSequence searchContent = null;
44
45 private boolean isCaseInsensitive = false;
46
47 private boolean includeHeaders = false;
48
49 public MessageSearcher() {
50 }
51
52 public MessageSearcher(CharSequence searchContent,
53 boolean isCaseInsensitive, boolean includeHeaders) {
54 super();
55 this.searchContent = searchContent;
56 this.isCaseInsensitive = isCaseInsensitive;
57 this.includeHeaders = includeHeaders;
58 }
59
60
61
62
63
64
65 public boolean isIncludeHeaders() {
66 return includeHeaders;
67 }
68
69
70
71
72
73
74
75 public synchronized void setIncludeHeaders(boolean includesHeaders) {
76 this.includeHeaders = includesHeaders;
77 }
78
79
80
81
82
83
84 public boolean isCaseInsensitive() {
85 return isCaseInsensitive;
86 }
87
88
89
90
91
92
93
94 public synchronized void setCaseInsensitive(boolean isCaseInsensitive) {
95 this.isCaseInsensitive = isCaseInsensitive;
96 }
97
98
99
100
101
102
103 public CharSequence getSearchContent() {
104 return searchContent;
105 }
106
107
108
109
110
111
112
113 public synchronized void setSearchContent(CharSequence searchContent) {
114 this.searchContent = searchContent;
115 }
116
117
118
119
120
121
122
123
124
125
126
127 public boolean isFoundIn(final InputStream input) throws IOException,
128 MimeException {
129 final boolean includeHeaders;
130 final CharSequence searchContent;
131 final boolean isCaseInsensitive;
132 synchronized (this) {
133 includeHeaders = this.includeHeaders;
134 searchContent = this.searchContent;
135 isCaseInsensitive = this.isCaseInsensitive;
136 }
137 final boolean result;
138 if (searchContent == null || "".equals(searchContent)) {
139 final Log logger = getLogger();
140 logger.debug("Nothing to search for. ");
141 result = false;
142 } else {
143 final CharBuffer buffer = createBuffer(searchContent,
144 isCaseInsensitive);
145 result = parse(input, isCaseInsensitive, includeHeaders, buffer);
146 }
147 return result;
148 }
149
150 private boolean parse(final InputStream input,
151 final boolean isCaseInsensitive, final boolean includeHeaders,
152 final CharBuffer buffer) throws IOException, MimeException {
153 try {
154 boolean result = false;
155 MimeTokenStream parser = new MimeTokenStream();
156 parser.parse(input);
157 while (!result && parser.next() != MimeTokenStream.T_END_OF_STREAM) {
158 final int state = parser.getState();
159 switch (state) {
160 case MimeTokenStream.T_BODY:
161 case MimeTokenStream.T_PREAMBLE:
162 case MimeTokenStream.T_EPILOGUE:
163 result = checkBody(isCaseInsensitive, buffer, result,
164 parser);
165 break;
166 case MimeTokenStream.T_FIELD:
167 if (includeHeaders) {
168 result = checkHeader(isCaseInsensitive, buffer,
169 result, parser);
170 }
171 break;
172 }
173 }
174 return result;
175 } catch (IllegalCharsetNameException e) {
176 handle(e);
177 } catch (UnsupportedCharsetException e) {
178 handle(e);
179 } catch (IllegalStateException e) {
180 handle(e);
181 }
182 return false;
183 }
184
185 private boolean checkHeader(final boolean isCaseInsensitive,
186 final CharBuffer buffer, boolean result, MimeTokenStream parser)
187 throws IOException {
188 final String value = parser.getFieldValue();
189 final StringReader reader = new StringReader(value);
190 if (isFoundIn(reader, buffer, isCaseInsensitive)) {
191 result = true;
192 }
193 return result;
194 }
195
196 private boolean checkBody(final boolean isCaseInsensitive,
197 final CharBuffer buffer, boolean result, MimeTokenStream parser)
198 throws IOException {
199 final Reader reader = parser.getReader();
200 if (isFoundIn(reader, buffer, isCaseInsensitive)) {
201 result = true;
202 }
203 return result;
204 }
205
206 private CharBuffer createBuffer(final CharSequence searchContent,
207 final boolean isCaseInsensitive) {
208 final CharBuffer buffer;
209 if (isCaseInsensitive) {
210 final int length = searchContent.length();
211 buffer = CharBuffer.allocate(length);
212 for (int i = 0; i < length; i++) {
213 final char next = searchContent.charAt(i);
214 final char upperCase = Character.toUpperCase(next);
215 buffer.put(upperCase);
216 }
217 buffer.flip();
218 } else {
219 buffer = CharBuffer.wrap(searchContent);
220 }
221 return buffer;
222 }
223
224 protected void handle(Exception e) throws IOException, MimeException {
225 final Log logger = getLogger();
226 logger.warn("Cannot read MIME body.");
227 logger.debug("Failed to read body.", e);
228 }
229
230 private boolean isFoundIn(final Reader reader, final CharBuffer buffer,
231 final boolean isCaseInsensitive) throws IOException {
232 boolean result = false;
233 int read;
234 while (!result && (read = reader.read()) != -1) {
235 final char next;
236 if (isCaseInsensitive) {
237 next = Character.toUpperCase((char) read);
238 } else {
239 next = (char) read;
240 }
241 result = matches(buffer, next);
242 }
243 return result;
244 }
245
246 private boolean matches(final CharBuffer buffer, final char next) {
247 boolean result = false;
248 if (buffer.hasRemaining()) {
249 final boolean partialMatch = (buffer.position() > 0);
250 final char matching = buffer.get();
251 if (next != matching) {
252 buffer.rewind();
253 if (partialMatch) {
254 result = matches(buffer, next);
255 }
256 }
257 } else {
258 result = true;
259 }
260 return result;
261 }
262
263 public final Log getLogger() {
264 if (logger == null) {
265 logger = LogFactory.getLog(MessageSearcher.class);
266 }
267 return logger;
268 }
269
270 public final void setLogger(Log logger) {
271 this.logger = logger;
272 }
273 }