1
2
3
4
5
6
7
8
9
10
11
12
13 package org.abstracthorizon.mercury.imap.cmd;
14
15 import java.io.IOException;
16 import java.util.ArrayList;
17 import java.util.GregorianCalendar;
18 import javax.mail.Flags;
19 import javax.mail.Folder;
20 import javax.mail.Message;
21 import javax.mail.MessagingException;
22 import javax.mail.UIDFolder;
23 import javax.mail.internet.MimeMessage;
24 import javax.mail.internet.MimePart;
25 import javax.mail.search.AndTerm;
26 import javax.mail.search.BodyTerm;
27 import javax.mail.search.ComparisonTerm;
28 import javax.mail.search.FlagTerm;
29 import javax.mail.search.FromStringTerm;
30 import javax.mail.search.HeaderTerm;
31 import javax.mail.search.NotTerm;
32 import javax.mail.search.OrTerm;
33 import javax.mail.search.ReceivedDateTerm;
34 import javax.mail.search.RecipientStringTerm;
35 import javax.mail.search.SearchTerm;
36 import javax.mail.search.SentDateTerm;
37 import javax.mail.search.SizeTerm;
38 import javax.mail.search.StringTerm;
39 import javax.mail.search.SubjectTerm;
40
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
43
44 import org.abstracthorizon.mercury.common.command.CommandException;
45 import org.abstracthorizon.mercury.imap.IMAPSession;
46 import org.abstracthorizon.mercury.imap.response.SearchResponse;
47 import org.abstracthorizon.mercury.imap.util.ComposedSequence;
48 import org.abstracthorizon.mercury.imap.util.MessageUtilities;
49 import org.abstracthorizon.mercury.imap.util.ParserException;
50 import org.abstracthorizon.mercury.imap.util.IMAPScanner;
51
52
53
54
55
56
57 public class Search extends UIDCommand {
58
59 public static final Flags ANSWERED = new Flags(Flags.Flag.ANSWERED);
60 public static final Flags DELETED = new Flags(Flags.Flag.DELETED);
61 public static final Flags DRAFT = new Flags(Flags.Flag.DRAFT);
62 public static final Flags SEEN = new Flags(Flags.Flag.SEEN);
63 public static final Flags RECENT = new Flags(Flags.Flag.RECENT);
64 public static final Flags FLAGGED = new Flags(Flags.Flag.FLAGGED);
65
66
67 protected boolean asuid = false;
68
69
70 protected StringBuffer charset = new StringBuffer();
71
72
73 protected static final Logger logger = LoggerFactory.getLogger(Search.class);
74
75
76
77
78
79 public Search(String mnemonic) {
80 super(mnemonic);
81 }
82
83
84
85
86 public void setAsUID() {
87 asuid = true;
88 }
89
90
91
92
93
94
95
96
97
98 protected void execute(IMAPSession session) throws ParserException, MessagingException, CommandException, IOException {
99 IMAPScanner scanner = session.getScanner();
100 SearchTerm term = search(scanner);
101
102 Folder f = session.getSelectedFolder();
103 Message[] ms = f.search(term);
104
105 checkEOL(session);
106
107 SearchResponse response = new SearchResponse(session);
108 if (ms.length > 0) {
109 for (int i=0; i<ms.length; i++) {
110 if (asuid) {
111 response.addMessageNumber(MessageUtilities.findUID(ms[i]));
112 } else {
113 response.addMessageNumber(ms[i].getMessageNumber());
114 }
115 }
116 }
117 response.submit();
118 sendOK(session);
119 }
120
121 public void process(IMAPSession session, MimeMessage m) throws MessagingException {
122 }
123
124
125 public SearchTerm search(IMAPScanner scanner) throws IOException, ParserException, MessagingException {
126
127
128
129 charset.delete(0, charset.length());
130 if (scanner.keyword("CHARSET")) {
131 if (!scanner.is_char(' ')) {
132 throw new ParserException("<SP>");
133 }
134 if (!scanner.astring(charset)) {
135 throw new ParserException("<charset>");
136 }
137 }
138
139 ArrayList<SearchTerm> list = new ArrayList<SearchTerm>();
140 list.add(searchKey(scanner));
141 while (scanner.is_char(' ')) {
142 list.add(searchKey(scanner));
143 }
144 if (list.size() == 1) {
145 return (SearchTerm)list.get(0);
146 } else {
147 SearchTerm[] sts = new SearchTerm[list.size()];
148 sts = list.toArray(sts);
149 return new AndTerm(sts);
150 }
151 }
152
153 protected SearchTerm searchKey(IMAPScanner scanner) throws IOException, ParserException, MessagingException {
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172 if (scanner.is_char('(')) {
173 ArrayList<SearchTerm> list = new ArrayList<SearchTerm>();
174 list.add(search(scanner));
175 while (scanner.is_char(' ')) {
176 list.add(search(scanner));
177 }
178 if (!scanner.is_char(')')) {
179 throw new ParserException("')'");
180 }
181 if (list.size() == 1) {
182 return (SearchTerm)list.get(0);
183 } else {
184 SearchTerm[] sts = new SearchTerm[list.size()];
185 sts = (SearchTerm[])list.toArray(sts);
186 return new AndTerm(sts);
187 }
188 } else if (scanner.keyword("OR")) {
189 if (!scanner.is_char(' ')) {
190 throw new ParserException("<SP>");
191 }
192 SearchTerm t1 = searchKey(scanner);
193 if (!scanner.is_char(' ')) {
194 throw new ParserException("<SP>");
195 }
196 SearchTerm t2 = searchKey(scanner);
197 return new OrTerm(t1, t2);
198 } else if (scanner.keyword("OR")) {
199 if (!scanner.is_char(' ')) {
200 throw new ParserException("<SP>");
201 }
202 SearchTerm t1 = searchKey(scanner);
203 return new NotTerm(t1);
204 } else if (scanner.keyword("ANSWERED")) {
205 return new FlagTerm(ANSWERED, true);
206 } else if (scanner.keyword("UNANSWERED")) {
207 return new FlagTerm(ANSWERED, false);
208 } else if (scanner.keyword("DRAFT")) {
209 return new FlagTerm(DRAFT, true);
210 } else if (scanner.keyword("UNDRAFT")) {
211 return new FlagTerm(DRAFT, false);
212 } else if (scanner.keyword("DELETED")) {
213 return new FlagTerm(DELETED, true);
214 } else if (scanner.keyword("UNDELETED")) {
215 return new FlagTerm(DELETED, false);
216 } else if (scanner.keyword("FLAGGED")) {
217 return new FlagTerm(FLAGGED, true);
218 } else if (scanner.keyword("UNFLAGGED")) {
219 return new FlagTerm(FLAGGED, false);
220 } else if (scanner.keyword("SEEN")) {
221 return new FlagTerm(SEEN, true);
222 } else if (scanner.keyword("UNSEEN")) {
223 return new FlagTerm(SEEN, false);
224 } else if (scanner.keyword("NEW")) {
225 return new AndTerm(new FlagTerm(RECENT, true), new FlagTerm(SEEN, false));
226 } else if (scanner.keyword("RECENT")) {
227 return new FlagTerm(RECENT, true);
228 } else if (scanner.keyword("OLD")) {
229 return new FlagTerm(RECENT, false);
230 } else if (scanner.keyword("BODY")) {
231 if (!scanner.is_char(' ')) {
232 throw new ParserException("<SP>");
233 }
234 StringBuffer body = new StringBuffer();
235 if (!scanner.astring(body)) {
236 throw new ParserException("<string>");
237 }
238 return new BodyTerm(body.toString());
239 } else if (scanner.keyword("TEXT")) {
240 if (!scanner.is_char(' ')) {
241 throw new ParserException("<SP>");
242 }
243 StringBuffer body = new StringBuffer();
244 if (!scanner.astring(body)) {
245 throw new ParserException("<string>");
246 }
247 return new TextTerm(body.toString());
248 } else if (scanner.keyword("SUBJECT")) {
249 if (!scanner.is_char(' ')) {
250 throw new ParserException("<SP>");
251 }
252 StringBuffer subject = new StringBuffer();
253 if (!scanner.astring(subject)) {
254 throw new ParserException("<string>");
255 }
256 return new SubjectTerm(subject.toString());
257 } else if (scanner.keyword("FROM")) {
258 if (!scanner.is_char(' ')) {
259 throw new ParserException("<SP>");
260 }
261 StringBuffer from = new StringBuffer();
262 if (!scanner.astring(from)) {
263 throw new ParserException("<string>");
264 }
265 return new FromStringTerm(from.toString());
266 } else if (scanner.keyword("TO")) {
267 if (!scanner.is_char(' ')) {
268 throw new ParserException("<SP>");
269 }
270 StringBuffer to = new StringBuffer();
271 if (!scanner.astring(to)) {
272 throw new ParserException("<string>");
273 }
274 return new RecipientStringTerm(Message.RecipientType.TO, to.toString());
275 } else if (scanner.keyword("CC")) {
276 if (!scanner.is_char(' ')) {
277 throw new ParserException("<SP>");
278 }
279 StringBuffer cc = new StringBuffer();
280 if (!scanner.astring(cc)) {
281 throw new ParserException("<string>");
282 }
283 return new RecipientStringTerm(Message.RecipientType.CC, cc.toString());
284 } else if (scanner.keyword("BCC")) {
285 if (!scanner.is_char(' ')) {
286 throw new ParserException("<SP>");
287 }
288 StringBuffer bcc = new StringBuffer();
289 if (!scanner.astring(bcc)) {
290 throw new ParserException("<string>");
291 }
292 return new RecipientStringTerm(Message.RecipientType.BCC, bcc.toString());
293 } else if (scanner.keyword("HEADER")) {
294 if (!scanner.is_char(' ')) {
295 throw new ParserException("<SP>");
296 }
297 StringBuffer name = new StringBuffer();
298 if (!scanner.astring(name)) {
299 throw new ParserException("<string>");
300 }
301 if (!scanner.is_char(' ')) {
302 throw new ParserException("<SP>");
303 }
304 StringBuffer value = new StringBuffer();
305 if (!scanner.astring(value)) {
306 throw new ParserException("<string>");
307 }
308 return new HeaderTerm(name.toString(), value.toString());
309 } else if (scanner.keyword("LARGER")) {
310 if (!scanner.is_char(' ')) {
311 throw new ParserException("<SP>");
312 }
313 IMAPScanner.Number number = new IMAPScanner.Number();
314 if (!scanner.number(number)) {
315 throw new ParserException("<number>");
316 }
317 return new SizeTerm(ComparisonTerm.GT, number.number);
318 } else if (scanner.keyword("SMALLER")) {
319 if (!scanner.is_char(' ')) {
320 throw new ParserException("<SP>");
321 }
322 IMAPScanner.Number number = new IMAPScanner.Number();
323 if (!scanner.number(number)) {
324 throw new ParserException("<number>");
325 }
326 return new SizeTerm(ComparisonTerm.LT, number.number);
327 } else if (scanner.keyword("BEFORE")) {
328 if (!scanner.is_char(' ')) {
329 throw new ParserException("<SP>");
330 }
331 GregorianCalendar date = new GregorianCalendar();
332 if (!scanner.date(date)) {
333 throw new ParserException("<date>");
334 }
335 return new ReceivedDateTerm(ComparisonTerm.LT, date.getTime());
336 } else if (scanner.keyword("ON")) {
337 if (!scanner.is_char(' ')) {
338 throw new ParserException("<SP>");
339 }
340 GregorianCalendar date = new GregorianCalendar();
341 if (!scanner.date(date)) {
342 throw new ParserException("<date>");
343 }
344 return new ReceivedDateTerm(ComparisonTerm.EQ, date.getTime());
345 } else if (scanner.keyword("SINCE")) {
346 if (!scanner.is_char(' ')) {
347 throw new ParserException("<SP>");
348 }
349 GregorianCalendar date = new GregorianCalendar();
350 if (!scanner.date(date)) {
351 throw new ParserException("<date>");
352 }
353 return new ReceivedDateTerm(ComparisonTerm.GT, date.getTime());
354 } else if (scanner.keyword("SENTBEFORE")) {
355 if (!scanner.is_char(' ')) {
356 throw new ParserException("<SP>");
357 }
358 GregorianCalendar date = new GregorianCalendar();
359 if (!scanner.date(date)) {
360 throw new ParserException("<date>");
361 }
362 return new SentDateTerm(ComparisonTerm.LT, date.getTime());
363 } else if (scanner.keyword("SENTON")) {
364 if (!scanner.is_char(' ')) {
365 throw new ParserException("<SP>");
366 }
367 GregorianCalendar date = new GregorianCalendar();
368 if (!scanner.date(date)) {
369 throw new ParserException("<date>");
370 }
371 return new SentDateTerm(ComparisonTerm.EQ, date.getTime());
372 } else if (scanner.keyword("SENTSINCE")) {
373 if (!scanner.is_char(' ')) {
374 throw new ParserException("<SP>");
375 }
376 GregorianCalendar date = new GregorianCalendar();
377 if (!scanner.date(date)) {
378 throw new ParserException("<date>");
379 }
380 return new SentDateTerm(ComparisonTerm.GT, date.getTime());
381 } else if (scanner.keyword("KEYWORD")) {
382 if (!scanner.is_char(' ')) {
383 throw new ParserException("<SP>");
384 }
385 StringBuffer name = new StringBuffer();
386 if (!scanner.astring(name)) {
387 throw new ParserException("<string>");
388 }
389 return new NoneTerm();
390 } else if (scanner.keyword("UNKEYWORD")) {
391 if (!scanner.is_char(' ')) {
392 throw new ParserException("<SP>");
393 }
394 StringBuffer name = new StringBuffer();
395 if (!scanner.astring(name)) {
396 throw new ParserException("<string>");
397 }
398 return new AllTerm();
399 } else if (scanner.keyword("ALL")) {
400 return new AllTerm();
401 } else if (scanner.keyword("UID")) {
402 if (!scanner.is_char(' ')) {
403 throw new ParserException("<SP>");
404 }
405 ComposedSequence sequenceSet = new ComposedSequence();
406 if (!scanner.sequence_set(sequenceSet)) {
407 throw new ParserException("<sequence_set>");
408 }
409 return new UIDTerm(ComparisonTerm.GT, sequenceSet.getMin());
410 } else {
411 throw new ParserException(
412 "'ALL', 'ANSWERED', 'BCC', " +
413 "'BEFORE', 'BODY', 'CC', 'DELETED', 'FLAGGED', " +
414 "'FROM', 'KEYWORD', 'NEW', 'OLD', 'ON', 'RECENT', 'SEEN', " +
415 "'SINCE', 'SUBJECT', 'TEXT', 'TO', 'UNANSWERED', 'UNDELETED', 'UNFLAGGED', " +
416 "'UNKEYWORD', 'UNSEEN', " +
417
418 "'DRAFT', 'HEADER', 'LARGER', 'NOT', 'OR', 'SENTBEFORE', "+
419 "'SENTON', +'SENTSINCE', 'SMALLER', 'UNDRAFT', '('");
420 }
421
422 }
423
424 public class AllTerm extends SearchTerm {
425 public AllTerm() {
426 }
427
428 public boolean match(Message m) {
429 return true;
430 }
431 }
432
433 public class NoneTerm extends SearchTerm {
434 public NoneTerm() {
435 }
436
437 public boolean match(Message m) {
438 return false;
439 }
440 }
441
442 public class TextTerm extends StringTerm {
443
444
445 public TextTerm(String searchText) {
446 super(searchText);
447 }
448
449 public boolean match(Message m) {
450 if (m instanceof MimePart) {
451 try {
452 if (MessageUtilities.createHeaders((MimePart)m).indexOf(pattern) > 0) {
453 return true;
454 }
455 } catch (MessagingException e) {
456 logger.error("Search message error", e);
457
458 }
459 }
460 BodyTerm bt = new BodyTerm(pattern);
461 return bt.match(m);
462 }
463
464 }
465
466 public class UIDTerm extends ComparisonTerm {
467 long uid;
468
469 public UIDTerm(int comparison, long uid) {
470 this.comparison = comparison;
471 this.uid = uid;
472 }
473
474 public boolean match(Message m) {
475 Folder f = m.getFolder();
476 if (f instanceof UIDFolder) {
477 UIDFolder uf = (UIDFolder)f;
478 long u;
479 try {
480 u = uf.getUID(m);
481 if (comparison == ComparisonTerm.EQ) {
482 return uid == u;
483 } else if (comparison == ComparisonTerm.NE) {
484 return uid != u;
485 } else if (comparison == ComparisonTerm.LT) {
486 return uid < u;
487 } else if (comparison == ComparisonTerm.LE) {
488 return uid <= u;
489 } else if (comparison == ComparisonTerm.GT) {
490 return uid > u;
491 } else if (comparison == ComparisonTerm.GE) {
492 return uid >= u;
493 }
494 } catch (MessagingException e) {
495 logger.error("Search message error", e);
496
497 }
498 }
499 return false;
500 }
501 }
502
503 }