Package: SearchSequence
SearchSequence
name | instruction | branch | complexity | line | method | ||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
SearchSequence() |
|
|
|
|
|
||||||||||||||||||||
SearchSequence(IMAPProtocol) |
|
|
|
|
|
||||||||||||||||||||
and(AndTerm, String) |
|
|
|
|
|
||||||||||||||||||||
body(BodyTerm, String) |
|
|
|
|
|
||||||||||||||||||||
flag(FlagTerm) |
|
|
|
|
|
||||||||||||||||||||
from(String, String) |
|
|
|
|
|
||||||||||||||||||||
generateSequence(SearchTerm, String) |
|
|
|
|
|
||||||||||||||||||||
header(HeaderTerm, String) |
|
|
|
|
|
||||||||||||||||||||
isAscii(SearchTerm) |
|
|
|
|
|
||||||||||||||||||||
isAscii(SearchTerm[]) |
|
|
|
|
|
||||||||||||||||||||
isAscii(String) |
|
|
|
|
|
||||||||||||||||||||
messageid(MessageIDTerm, String) |
|
|
|
|
|
||||||||||||||||||||
modifiedSince(ModifiedSinceTerm) |
|
|
|
|
|
||||||||||||||||||||
not(NotTerm, String) |
|
|
|
|
|
||||||||||||||||||||
older(OlderTerm) |
|
|
|
|
|
||||||||||||||||||||
or(OrTerm, String) |
|
|
|
|
|
||||||||||||||||||||
receiveddate(DateTerm) |
|
|
|
|
|
||||||||||||||||||||
recipient(Message.RecipientType, String, String) |
|
|
|
|
|
||||||||||||||||||||
sentdate(DateTerm) |
|
|
|
|
|
||||||||||||||||||||
size(SizeTerm) |
|
|
|
|
|
||||||||||||||||||||
static {...} |
|
|
|
|
|
||||||||||||||||||||
subject(SubjectTerm, String) |
|
|
|
|
|
||||||||||||||||||||
toIMAPDate(Date) |
|
|
|
|
|
||||||||||||||||||||
younger(YoungerTerm) |
|
|
|
|
|
Coverage
1: /*
2: * Copyright (c) 1997, 2023 Oracle and/or its affiliates. All rights reserved.
3: *
4: * This program and the accompanying materials are made available under the
5: * terms of the Eclipse Public License v. 2.0, which is available at
6: * http://www.eclipse.org/legal/epl-2.0.
7: *
8: * This Source Code may also be made available under the following Secondary
9: * Licenses when the conditions for such availability set forth in the
10: * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
11: * version 2 with the GNU Classpath Exception, which is available at
12: * https://www.gnu.org/software/classpath/license.html.
13: *
14: * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15: */
16:
17: package org.eclipse.angus.mail.imap.protocol;
18:
19: import java.util.*;
20: import java.io.IOException;
21:
22: import jakarta.mail.*;
23: import jakarta.mail.search.*;
24: import org.eclipse.angus.mail.iap.*;
25: import org.eclipse.angus.mail.imap.OlderTerm;
26: import org.eclipse.angus.mail.imap.YoungerTerm;
27: import org.eclipse.angus.mail.imap.ModifiedSinceTerm;
28: import org.eclipse.angus.mail.iap.Argument;
29:
30: /**
31: * This class traverses a search-tree and generates the
32: * corresponding IMAP search sequence.
33: *
34: * Each IMAPProtocol instance contains an instance of this class,
35: * which might be subclassed by subclasses of IMAPProtocol to add
36: * support for additional product-specific search terms.
37: *
38: * @author        John Mani
39: * @author        Bill Shannon
40: */
41: public class SearchSequence {
42:
43: private IMAPProtocol protocol;        // for hasCapability checks; may be null
44:
45: /**
46: * Create a SearchSequence for this IMAPProtocol.
47: *
48: * @param        p        the IMAPProtocol object for the server
49: * @since        JavaMail 1.6.0
50: */
51: public SearchSequence(IMAPProtocol p) {
52:         protocol = p;
53: }
54:
55: /**
56: * Create a SearchSequence.
57: */
58: @Deprecated
59: public SearchSequence() {
60: }
61:
62: /**
63: * Generate the IMAP search sequence for the given search expression.
64: *
65: * @param        term        the search term
66: * @param        charset        charset for the search
67: * @return                the SEARCH Argument
68: * @exception        SearchException        for failures
69: * @exception        IOException        for I/O errors
70: */
71: public Argument generateSequence(SearchTerm term, String charset)
72:                 throws SearchException, IOException {
73:         /*
74:          * Call the appropriate handler depending on the type of
75:          * the search-term ...
76:          */
77:•        if (term instanceof AndTerm)                 // AND
78:          return and((AndTerm)term, charset);
79:•        else if (term instanceof OrTerm)         // OR
80:          return or((OrTerm)term, charset);
81:•        else if (term instanceof NotTerm)         // NOT
82:          return not((NotTerm)term, charset);
83:•        else if (term instanceof HeaderTerm)         // HEADER
84:          return header((HeaderTerm)term, charset);
85:•        else if (term instanceof FlagTerm)         // FLAG
86:          return flag((FlagTerm)term);
87:•        else if (term instanceof FromTerm) {        // FROM
88:          FromTerm fterm = (FromTerm)term;
89:          return from(fterm.getAddress().toString(), charset);
90:         }
91:•        else if (term instanceof FromStringTerm) { // FROM
92:          FromStringTerm fterm = (FromStringTerm)term;
93:          return from(fterm.getPattern(), charset);
94:         }
95:•        else if (term instanceof RecipientTerm)        { // RECIPIENT
96:          RecipientTerm rterm = (RecipientTerm)term;
97:          return recipient(rterm.getRecipientType(),
98:                          rterm.getAddress().toString(),
99:                          charset);
100:         }
101:•        else if (term instanceof RecipientStringTerm) { // RECIPIENT
102:          RecipientStringTerm rterm = (RecipientStringTerm)term;
103:          return recipient(rterm.getRecipientType(),
104:                          rterm.getPattern(),
105:                          charset);
106:         }
107:•        else if (term instanceof SubjectTerm)        // SUBJECT
108:          return subject((SubjectTerm)term, charset);
109:•        else if (term instanceof BodyTerm)        // BODY
110:          return body((BodyTerm)term, charset);
111:•        else if (term instanceof SizeTerm)        // SIZE
112:          return size((SizeTerm)term);
113:•        else if (term instanceof SentDateTerm)        // SENTDATE
114:          return sentdate((SentDateTerm)term);
115:•        else if (term instanceof ReceivedDateTerm) // INTERNALDATE
116:          return receiveddate((ReceivedDateTerm)term);
117:•        else if (term instanceof OlderTerm)        // RFC 5032 OLDER
118:          return older((OlderTerm)term);
119:•        else if (term instanceof YoungerTerm)        // RFC 5032 YOUNGER
120:          return younger((YoungerTerm)term);
121:•        else if (term instanceof MessageIDTerm) // MessageID
122:          return messageid((MessageIDTerm)term, charset);
123:•        else if (term instanceof ModifiedSinceTerm)        // RFC 4551 MODSEQ
124:          return modifiedSince((ModifiedSinceTerm)term);
125:         else
126:          throw new SearchException("Search too complex");
127: }
128:
129: /**
130: * Check if the "text" terms in the given SearchTerm contain
131: * non US-ASCII characters.
132: *
133: * @param        term        the search term
134: * @return                true if only ASCII
135: */
136: public static boolean isAscii(SearchTerm term) {
137:•        if (term instanceof AndTerm)
138:          return isAscii(((AndTerm)term).getTerms());
139:•        else if (term instanceof OrTerm)
140:          return isAscii(((OrTerm)term).getTerms());
141:•        else if (term instanceof NotTerm)
142:          return isAscii(((NotTerm)term).getTerm());
143:•        else if (term instanceof StringTerm)
144:          return isAscii(((StringTerm)term).getPattern());
145:•        else if (term instanceof AddressTerm)
146:          return isAscii(((AddressTerm)term).getAddress().toString());
147:         
148:         // Any other term returns true.
149:         return true;
150: }
151:
152: /**
153: * Check if any of the "text" terms in the given SearchTerms contain
154: * non US-ASCII characters.
155: *
156: * @param        terms        the search terms
157: * @return                true if only ASCII
158: */
159: public static boolean isAscii(SearchTerm[] terms) {
160:•        for (int i = 0; i < terms.length; i++)
161:•         if (!isAscii(terms[i])) // outta here !
162:                 return false;
163:         return true;
164: }
165:
166: /**
167: * Does this string contain only ASCII characters?
168: *
169: * @param        s        the string
170: * @return                true if only ASCII
171: */
172: public static boolean isAscii(String s) {
173:         int l = s.length();
174:
175:•        for (int i=0; i < l; i++) {
176:•         if ((int)s.charAt(i) > 0177) // non-ascii
177:                 return false;
178:         }
179:         return true;
180: }
181:
182: protected Argument and(AndTerm term, String charset)
183:                         throws SearchException, IOException {
184:         // Combine the sequences for both terms
185:         SearchTerm[] terms = term.getTerms();
186:         // Generate the search sequence for the first term
187:         Argument result = generateSequence(terms[0], charset);
188:         // Append other terms
189:•        for (int i = 1; i < terms.length; i++)
190:          result.append(generateSequence(terms[i], charset));
191:         return result;
192: }
193:
194: protected Argument or(OrTerm term, String charset)
195:                         throws SearchException, IOException {
196:         SearchTerm[] terms = term.getTerms();
197:
198:         /* The IMAP OR operator takes only two operands. So if
199:          * we have more than 2 operands, group them into 2-operand
200:          * OR Terms.
201:          */
202:•        if (terms.length > 2) {
203:          SearchTerm t = terms[0];
204:
205:          // Include rest of the terms
206:•         for (int i = 1; i < terms.length; i++)
207:                 t = new OrTerm(t, terms[i]);
208:
209:          term = (OrTerm)t;         // set 'term' to the new jumbo OrTerm we
210:                                 // just created
211:          terms = term.getTerms();
212:         }
213:
214:         // 'term' now has only two operands
215:         Argument result = new Argument();
216:
217:         // Add the OR search-key, if more than one term
218:•        if (terms.length > 1)
219:          result.writeAtom("OR");
220:
221:         /* If this term is an AND expression, we need to enclose it
222:          * within paranthesis.
223:          *
224:          * AND expressions are either AndTerms or FlagTerms
225:          */
226:•        if (terms[0] instanceof AndTerm || terms[0] instanceof FlagTerm)
227:          result.writeArgument(generateSequence(terms[0], charset));
228:         else
229:          result.append(generateSequence(terms[0], charset));
230:
231:         // Repeat the above for the second term, if there is one
232:•        if (terms.length > 1) {
233:•         if (terms[1] instanceof AndTerm || terms[1] instanceof FlagTerm)
234:                 result.writeArgument(generateSequence(terms[1], charset));
235:          else
236:                 result.append(generateSequence(terms[1], charset));
237:         }
238:
239:         return result;
240: }
241:
242: protected Argument not(NotTerm term, String charset)
243:                         throws SearchException, IOException {
244:         Argument result = new Argument();
245:
246:         // Add the NOT search-key
247:         result.writeAtom("NOT");
248:
249:         /* If this term is an AND expression, we need to enclose it
250:          * within paranthesis.
251:          *
252:          * AND expressions are either AndTerms or FlagTerms
253:          */
254:         SearchTerm nterm = term.getTerm();
255:•        if (nterm instanceof AndTerm || nterm instanceof FlagTerm)
256:          result.writeArgument(generateSequence(nterm, charset));
257:         else
258:          result.append(generateSequence(nterm, charset));
259:
260:         return result;
261: }
262:
263: protected Argument header(HeaderTerm term, String charset)
264:                         throws SearchException, IOException {
265:         Argument result = new Argument();
266:         result.writeAtom("HEADER");
267:         result.writeString(term.getHeaderName());
268:         result.writeString(term.getPattern(), charset);
269:         return result;
270: }
271:
272: protected Argument messageid(MessageIDTerm term, String charset)
273:                         throws SearchException, IOException {
274:         Argument result = new Argument();
275:         result.writeAtom("HEADER");
276:         result.writeString("Message-ID");
277:         // XXX confirm that charset conversion ought to be done
278:         result.writeString(term.getPattern(), charset);
279:         return result;
280: }
281:
282: protected Argument flag(FlagTerm term) throws SearchException {
283:         boolean set = term.getTestSet();
284:
285:         Argument result = new Argument();
286:
287:         Flags flags = term.getFlags();
288:         Flags.Flag[] sf = flags.getSystemFlags();
289:         String[] uf = flags.getUserFlags();
290:•        if (sf.length == 0 && uf.length == 0)
291:          throw new SearchException("Invalid FlagTerm");
292:
293:•        for (int i = 0; i < sf.length; i++) {
294:•         if (sf[i] == Flags.Flag.DELETED)
295:•                result.writeAtom(set ? "DELETED": "UNDELETED");
296:•         else if (sf[i] == Flags.Flag.ANSWERED)
297:•                result.writeAtom(set ? "ANSWERED": "UNANSWERED");
298:•         else if (sf[i] == Flags.Flag.DRAFT)
299:•                result.writeAtom(set ? "DRAFT": "UNDRAFT");
300:•         else if (sf[i] == Flags.Flag.FLAGGED)
301:•                result.writeAtom(set ? "FLAGGED": "UNFLAGGED");
302:•         else if (sf[i] == Flags.Flag.RECENT)
303:•                result.writeAtom(set ? "RECENT": "OLD");
304:•         else if (sf[i] == Flags.Flag.SEEN)
305:•                result.writeAtom(set ? "SEEN": "UNSEEN");
306:         }
307:
308:•        for (int i = 0; i < uf.length; i++) {
309:•         result.writeAtom(set ? "KEYWORD" : "UNKEYWORD");
310:          result.writeAtom(uf[i]);
311:         }
312:         
313:         return result;
314: }
315:
316: protected Argument from(String address, String charset)
317:                         throws SearchException, IOException {
318:         Argument result = new Argument();
319:         result.writeAtom("FROM");
320:         result.writeString(address, charset);
321:         return result;
322: }
323:
324: protected Argument recipient(Message.RecipientType type,
325:                                  String address, String charset)
326:                         throws SearchException, IOException {
327:         Argument result = new Argument();
328:
329:•        if (type == Message.RecipientType.TO)
330:          result.writeAtom("TO");
331:•        else if (type == Message.RecipientType.CC)
332:          result.writeAtom("CC");
333:•        else if (type == Message.RecipientType.BCC)
334:          result.writeAtom("BCC");
335:         else
336:          throw new SearchException("Illegal Recipient type");
337:
338:         result.writeString(address, charset);
339:         return result;
340: }
341:
342: protected Argument subject(SubjectTerm term, String charset)
343:                         throws SearchException, IOException {
344:         Argument result = new Argument();
345:         
346:         result.writeAtom("SUBJECT");
347:         result.writeString(term.getPattern(), charset);
348:         return result;
349: }
350:
351: protected Argument body(BodyTerm term, String charset)
352:                         throws SearchException, IOException {
353:         Argument result = new Argument();
354:
355:         result.writeAtom("BODY");
356:         result.writeString(term.getPattern(), charset);
357:         return result;
358: }
359:
360: protected Argument size(SizeTerm term)
361:                         throws SearchException {
362:         Argument result = new Argument();
363:
364:•        switch (term.getComparison()) {
365:          case ComparisonTerm.GT:
366:                 result.writeAtom("LARGER");
367:                 break;
368:          case ComparisonTerm.LT:
369:                 result.writeAtom("SMALLER");
370:                 break;
371:          default:
372:                 // GT and LT is all we get from IMAP for size
373:                  throw new SearchException("Cannot handle Comparison");
374:         }
375:
376:         result.writeNumber(term.getNumber());
377:         return result;
378: }
379:
380: // Date SEARCH stuff ...
381:
382: // NOTE: The built-in IMAP date comparisons are equivalent to
383: // "<" (BEFORE), "=" (ON), and ">=" (SINCE)!!!
384: // There is no built-in greater-than comparison!
385:
386: /**
387: * Print an IMAP Date string, that is suitable for the Date
388: * SEARCH commands.
389: *
390: * The IMAP Date string is :
391: *        date ::= date_day "-" date_month "-" date_year        
392: *
393: * Note that this format does not contain the TimeZone
394: */
395: private static String monthTable[] = {
396:          "Jan", "Feb", "Mar", "Apr", "May", "Jun",
397:          "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
398: };
399:
400: // A GregorianCalendar object in the current timezone
401: protected Calendar cal = new GregorianCalendar();
402:
403: protected String toIMAPDate(Date date) {
404:         StringBuilder s = new StringBuilder();
405:
406:         cal.setTime(date);
407:
408:         s.append(cal.get(Calendar.DATE)).append("-");
409:         s.append(monthTable[cal.get(Calendar.MONTH)]).append('-');
410:         s.append(cal.get(Calendar.YEAR));
411:
412:         return s.toString();
413: }
414:
415: protected Argument sentdate(DateTerm term)
416:                         throws SearchException {
417:         Argument result = new Argument();
418:         String date = toIMAPDate(term.getDate());
419:
420:•        switch (term.getComparison()) {
421:          case ComparisonTerm.GT:
422:                 result.writeAtom("NOT SENTON " + date + " SENTSINCE " + date);
423:                 break;
424:          case ComparisonTerm.EQ:
425:                 result.writeAtom("SENTON " + date);
426:                 break;
427:          case ComparisonTerm.LT:
428:                 result.writeAtom("SENTBEFORE " + date);
429:                 break;
430:          case ComparisonTerm.GE:
431:                 result.writeAtom("SENTSINCE " + date);
432:                 break;
433:          case ComparisonTerm.LE:
434:                 result.writeAtom("OR SENTBEFORE " + date + " SENTON " + date);
435:                 break;
436:          case ComparisonTerm.NE:
437:                 result.writeAtom("NOT SENTON " + date);
438:                 break;
439:          default:
440:                  throw new SearchException("Cannot handle Date Comparison");
441:         }
442:
443:         return result;
444: }
445:
446: protected Argument receiveddate(DateTerm term)
447:                         throws SearchException {
448:         Argument result = new Argument();
449:         String date = toIMAPDate(term.getDate());
450:
451:•        switch (term.getComparison()) {
452:          case ComparisonTerm.GT:
453:                 result.writeAtom("NOT ON " + date + " SINCE " + date);
454:                 break;
455:          case ComparisonTerm.EQ:
456:                 result.writeAtom("ON " + date);
457:                 break;
458:          case ComparisonTerm.LT:
459:                 result.writeAtom("BEFORE " + date);
460:                 break;
461:          case ComparisonTerm.GE:
462:                 result.writeAtom("SINCE " + date);
463:                 break;
464:          case ComparisonTerm.LE:
465:                 result.writeAtom("OR BEFORE " + date + " ON " + date);
466:                 break;
467:          case ComparisonTerm.NE:
468:                 result.writeAtom("NOT ON " + date);
469:                 break;
470:          default:
471:                  throw new SearchException("Cannot handle Date Comparison");
472:         }
473:
474:         return result;
475: }
476:
477: /**
478: * Generate argument for OlderTerm.
479: *
480: * @param        term        the search term
481: * @return                the SEARCH Argument
482: * @exception        SearchException        for failures
483: * @since        JavaMail 1.5.1
484: */
485: protected Argument older(OlderTerm term) throws SearchException {
486:•        if (protocol != null && !protocol.hasCapability("WITHIN"))
487:          throw new SearchException("Server doesn't support OLDER searches");
488:         Argument result = new Argument();
489:         result.writeAtom("OLDER");
490:         result.writeNumber(term.getInterval());
491:         return result;
492: }
493:
494: /**
495: * Generate argument for YoungerTerm.
496: *
497: * @param        term        the search term
498: * @return                the SEARCH Argument
499: * @exception        SearchException        for failures
500: * @since        JavaMail 1.5.1
501: */
502: protected Argument younger(YoungerTerm term) throws SearchException {
503:•        if (protocol != null && !protocol.hasCapability("WITHIN"))
504:          throw new SearchException("Server doesn't support YOUNGER searches");
505:         Argument result = new Argument();
506:         result.writeAtom("YOUNGER");
507:         result.writeNumber(term.getInterval());
508:         return result;
509: }
510:
511: /**
512: * Generate argument for ModifiedSinceTerm.
513: *
514: * @param        term        the search term
515: * @return                the SEARCH Argument
516: * @exception        SearchException        for failures
517: * @since        JavaMail 1.5.1
518: */
519: protected Argument modifiedSince(ModifiedSinceTerm term)
520:                                 throws SearchException {
521:•        if (protocol != null && !protocol.hasCapability("CONDSTORE"))
522:          throw new SearchException("Server doesn't support MODSEQ searches");
523:         Argument result = new Argument();
524:         result.writeAtom("MODSEQ");
525:         result.writeNumber(term.getModSeq());
526:         return result;
527: }
528: }