View Javadoc

1   /*
2    * Copyright (c) 2004-2007 Creative Sphere Limited.
3    * All rights reserved. This program and the accompanying materials
4    * are made available under the terms of the Eclipse Public License v1.0
5    * which accompanies this distribution, and is available at
6    * http://www.eclipse.org/legal/epl-v10.html
7    *
8    * Contributors:
9    *
10   *   Creative Sphere - initial API and implementation
11   *
12   */
13  package org.abstracthorizon.mercury.imap.cmd;
14  
15  import java.io.IOException;
16  import java.text.SimpleDateFormat;
17  import java.util.ArrayList;
18  import java.util.List;
19  import javax.mail.Folder;
20  import javax.mail.MessagingException;
21  import javax.mail.internet.MimeMessage;
22  
23  import org.slf4j.Logger;
24  import org.slf4j.LoggerFactory;
25  
26  import org.abstracthorizon.mercury.common.command.CommandException;
27  import org.abstracthorizon.mercury.imap.IMAPSession;
28  import org.abstracthorizon.mercury.imap.response.FetchResponse;
29  import org.abstracthorizon.mercury.imap.util.ComposedSequence;
30  import org.abstracthorizon.mercury.imap.util.FlagUtilities;
31  import org.abstracthorizon.mercury.imap.util.MessageUtilities;
32  import org.abstracthorizon.mercury.imap.util.ParserException;
33  import org.abstracthorizon.mercury.imap.util.IMAPScanner;
34  import org.abstracthorizon.mercury.imap.util.section.Body;
35  import org.abstracthorizon.mercury.imap.util.section.BodyStructure;
36  import org.abstracthorizon.mercury.imap.util.section.Envelope;
37  import org.abstracthorizon.mercury.imap.util.section.Flags;
38  import org.abstracthorizon.mercury.imap.util.section.HeaderSection;
39  import org.abstracthorizon.mercury.imap.util.section.Internaldate;
40  import org.abstracthorizon.mercury.imap.util.section.RFC822Size;
41  import org.abstracthorizon.mercury.imap.util.section.TextSection;
42  import org.abstracthorizon.mercury.imap.util.section.UID;
43  
44  
45  /**
46   * Fetch IMAP command
47   *
48   * @author Daniel Sendula
49   */
50  public class Fetch extends UIDCommand {
51  
52      /** Logger */
53      protected static final Logger logger = LoggerFactory.getLogger(Fetch.class);
54  
55      /** Internal date */
56      protected static SimpleDateFormat internalDate = new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss Z");
57  
58      /** Tag */
59      protected boolean tagSent = false;
60  
61      /** Attributes */
62      protected List<Object> attrs;
63  
64      /**
65       * Constructor
66       * @param mnemonic mnemonic
67       */
68      public Fetch(String mnemonic) {
69          super(mnemonic);
70          unilateral = IMAPCommand.ALWAYS_SUPRESS_UNILATERAL_DATA;
71      }
72  
73      /**
74       * Defines as UID function
75       */
76      public void setAsUID() {
77          asuid = true;
78      }
79  
80      /**
81       * Executes the command
82       * @param session
83       * @throws ParserException
84       * @throws MessagingException
85       * @throws CommandException
86       * @throws IOException
87       */
88      protected void execute(IMAPSession session) throws ParserException, MessagingException, CommandException, IOException {
89          fetch(session);
90      }
91  
92      /**
93       * Executes the command
94       * @param session
95       * @throws ParserException
96       * @throws MessagingException
97       * @throws CommandException
98       * @throws IOException
99       */
100     public void fetch(IMAPSession session) throws IOException, ParserException, MessagingException {
101         IMAPScanner scanner = session.getScanner();
102         attrs = new ArrayList<Object>();
103 
104         ComposedSequence sequenceSet = new ComposedSequence();
105         if (!scanner.sequence_set(sequenceSet)) {
106             throw new ParserException("<sequence_set>");
107         }
108         //logger.debug("0) min="+sequenceSet.getMin()+" max="+sequenceSet.getMax());
109         if (!scanner.is_char(' ')) {
110             throw new ParserException("<SP>");
111         }
112 
113         if (scanner.keyword("ALL")) {
114             attrs.add(new Flags());
115             attrs.add(new Internaldate());
116             RFC822Size rfc822 = new RFC822Size();
117             attrs.add(rfc822);
118             attrs.add(new Envelope());
119         } else if (scanner.keyword("FULL")) {
120             attrs.add(new Flags());
121             attrs.add(new Internaldate());
122             RFC822Size rfc822 = new RFC822Size();
123             attrs.add(rfc822);
124             attrs.add(new Envelope());
125             attrs.add(new Body());
126         } else if (scanner.keyword("FAST")) {
127             attrs.add(new Flags());
128             attrs.add(new Internaldate());
129             RFC822Size rfc822 = new RFC822Size();
130             attrs.add(rfc822);
131         } else if (scanner.is_char('(')) {
132             if (fetch_att(scanner, attrs)) {
133                 boolean ok = true;
134                 while (ok && scanner.is_char(' ')) {
135                     ok = fetch_att(scanner, attrs);
136                 } // while
137                 if (ok && !scanner.is_char(')')) {
138                     throw new ParserException("')'");
139                 }
140             }
141         } else {
142             if (!fetch_att(scanner, attrs)) {
143                 throw new ParserException("'ALL','FULL', 'FAST', '('");
144             }
145         }
146 
147         if (asuid) { // Implicit UID in UID FETCH
148             if (!attrs.contains(UID.instance)) {
149                 attrs.add(0, UID.instance);
150             }
151         }
152         checkEOL(session);
153 
154         Folder f = session.getSelectedFolder();
155 
156         MessageUtilities.sequenceIterator(session, this, f, sequenceSet, asuid);
157         sendOK(session);
158     }
159 
160     /**
161      * Parses fetch attributes
162      * @param scanner scanner
163      * @param atts attributes
164      * @return <code>true</code> if parsing was correct
165      * @throws IOException
166      * @throws ParserException
167      */
168     public static boolean fetch_att(IMAPScanner scanner, List<Object> atts) throws IOException, ParserException {
169         if (scanner.keyword("ENVELOPE")) {
170             atts.add(new Envelope());
171         } else if (scanner.keyword("FLAGS")) {
172             atts.add(new Flags());
173         } else if (scanner.keyword("INTERNALDATE")) {
174             atts.add(new Internaldate());
175         } else if (scanner.keyword("RFC822")) {
176             if (scanner.is_char('.')) {
177                 if (scanner.keyword("HEADER")) {
178                     Body body = new Body();
179                     body.peek = true;
180                     body.rfc822 = true;
181                     HeaderSection hs = new HeaderSection();
182                     body.child = hs;
183                     if (scanner.is_char('.')) { // from obsolete rfc-1730
184                         if (scanner.keyword("LINES")) {
185                             hs.fields = new ArrayList<String>();
186                             if (scanner.is_char(' ')) {
187                                 if (scanner.header_list(hs.fields)) {
188                                 }
189                             }
190                         }
191                     }
192                     atts.add(body);
193                 } else if (scanner.keyword("SIZE")) {
194                     RFC822Size rfc822 = new RFC822Size();
195                     atts.add(rfc822);
196                 } else if (scanner.keyword("TEXT")) {
197                     Body body = new Body();
198                     body.rfc822 = true;
199                     body.child = new TextSection();
200                     atts.add(body);
201                 } else if (scanner.keyword("PEEK")) {
202                     // Non standard Outlook express dialect
203                     Body body = new Body();
204                     body.rfc822 = true;
205                     body.peek = true;
206                     atts.add(body);
207                 } else {
208                     throw new ParserException("'HEADER', 'SIZE', 'TEXT'");
209                 }
210             } else {
211                 Body body = new Body();
212                 body.rfc822 = true;
213                 atts.add(body);
214             }
215         } else if (scanner.keyword("BODY")) {
216             //Body body = new Body();
217             //atts.add(body);
218             if (scanner.keyword("STRUCTURE")) {
219                 BodyStructure b = new BodyStructure();
220                 b.structure = true;
221                 atts.add(b);
222             } else {
223                 boolean peek = false;
224                 if (scanner.keyword(".PEEK")) {
225                     peek = true;
226                 }
227                 Body body = new Body();
228                 if (scanner.section(body)) {
229                     body.peek = peek;
230                     atts.add(body);
231                     //throw new ParserException("Syntax error - expecting BODY section");
232                     if (scanner.is_char('<')) {
233                         IMAPScanner.Number num1 = new IMAPScanner.Number();
234                         if (!scanner.number(num1)) {
235                             throw new ParserException("<number>");
236                         }
237                         body.from = num1.number;
238                         if (!scanner.is_char('.')) {
239                             throw new ParserException("'.'");
240                         }
241                         IMAPScanner.Number num2 = new IMAPScanner.Number();
242                         if (!scanner.nz_number(num2)) {
243                             throw new ParserException("<number>");
244                         }
245                         if (!scanner.is_char('>')) {
246                             throw new ParserException("'>'");
247                         }
248                         body.from = num2.number;
249                     }
250                 } else {
251                     BodyStructure b = new BodyStructure();
252                     b.structure = false;
253                     atts.add(b);
254                 }
255             }
256         } else if (scanner.keyword("UID")) {
257             atts.add(UID.instance);
258         } else {
259             throw new ParserException("'ENVELOPE', 'FLAGS', 'INTERNALDATE', 'RFC822', 'BODY', 'UID'");
260         }
261         return true;
262     }
263 
264     /**
265      * Processes message
266      * @param session session
267      * @param m mime message
268      * @throws MessagingException
269      */
270     public void process(IMAPSession session, MimeMessage m) throws IOException, MessagingException {
271         try {
272             //StringBuffer response = new StringBuffer(256);
273             boolean first = true;
274             // Not sure is this really needed and what to do with messages that are
275             // expunged in the middle of Fetch.
276             boolean expunged = m.isExpunged();
277             int msgNo = m.getMessageNumber();
278             FetchResponse response = new FetchResponse(session, msgNo);
279             response.append('(');
280             for (int j = 0; j < attrs.size(); j++) {
281                 Object o = attrs.get(j);
282                 if (o instanceof UID) {
283                     first = spaceSeparator(first, response);
284                     response.append("UID ");
285                     response.append(MessageUtilities.findUID(m));
286                 } else if (o instanceof Flags) {
287                     first = spaceSeparator(first, response);
288                     response.append("FLAGS (").append(FlagUtilities.toString(m.getFlags())).append(')');
289                 } else if (o instanceof Envelope) {
290                     if (!expunged) {
291                         first = spaceSeparator(first, response);
292                         response.append("ENVELOPE ");
293                         response.createEnvelope(m);
294                     }
295                 } else if (o instanceof Internaldate) {
296                     if (!expunged) {
297                         first = spaceSeparator(first, response);
298                         response.append("INTERNALDATE ");
299                         response.append('"');
300                         response.append(internalDate.format(m.getReceivedDate()));
301                         response.append('"');
302                     }
303                 } else if (o instanceof RFC822Size) {
304                     if (!expunged) {
305                         first = spaceSeparator(first, response);
306                         int size = m.getSize();
307                         if (size < 0) {
308                             // Nothing we can do in order to find out size of the message
309                             // so we will just assume that it is zero!
310                             size = 0;
311                         }
312                         String headers = MessageUtilities.createHeaders(m);
313                         int headersSize = headers.getBytes().length;
314                         size = size + headersSize;
315 
316                         //RFC822Size rfc = (RFC822Size) o;
317                         response.append("RFC822.SIZE ");
318                         response.append(size);
319                     }
320                 } else if (o instanceof Body) {
321                     if (!expunged) {
322                         first = spaceSeparator(first, response);
323                         Body b = (Body) o;
324                         response.append(b, m);
325                         if (!b.peek) {
326                             if (!m.getFlags().contains(javax.mail.Flags.Flag.RECENT)) {
327                                 m.setFlag(javax.mail.Flags.Flag.RECENT, false);
328                             }
329                             if (!m.getFlags().contains(javax.mail.Flags.Flag.SEEN)) {
330                                 m.setFlag(javax.mail.Flags.Flag.SEEN, true);
331                             }
332                         }
333                     }
334                 } else if (o instanceof BodyStructure) {
335                     if (!expunged) {
336                         first = spaceSeparator(first, response);
337                         response.append("BODYSTRUCTURE ");
338                         //response.append("BODY ");
339                         response.createBodyStructure(m, true);
340                     }
341                 }
342 
343             } // for
344 
345             response.append(')');
346             response.submit();
347             tagSent = false;
348         } catch (MessagingException e) {
349             session.setKeepLog(true);
350             logger.error("Fetch processing message error", e);
351             // we don't want to stop processing other messages only because of this one.
352         }
353     }
354 
355     /**
356      * Adds space if not first
357      * @param flag flag
358      * @param response fetch response
359      * @return negated flag
360      */
361     protected boolean spaceSeparator(boolean flag, FetchResponse response) {
362         if (flag) {
363             flag = false;
364         } else {
365             response.append(' ');
366         }
367         return flag;
368     }
369 }