KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > james > imapserver > store > SimpleMessageAttributes


1 /***********************************************************************
2  * Copyright (c) 2000-2004 The Apache Software Foundation. *
3  * All rights reserved. *
4  * ------------------------------------------------------------------- *
5  * Licensed under the Apache License, Version 2.0 (the "License"); you *
6  * may not use this file except in compliance with the License. You *
7  * may obtain a copy of the License at: *
8  * *
9  * http://www.apache.org/licenses/LICENSE-2.0 *
10  * *
11  * Unless required by applicable law or agreed to in writing, software *
12  * distributed under the License is distributed on an "AS IS" BASIS, *
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or *
14  * implied. See the License for the specific language governing *
15  * permissions and limitations under the License. *
16  ***********************************************************************/

17
18 package org.apache.james.imapserver.store;
19
20 import org.apache.avalon.framework.logger.AbstractLogEnabled;
21 import org.apache.james.util.RFC822DateFormat;
22 import org.apache.mailet.MailAddress;
23
24 import javax.mail.BodyPart JavaDoc;
25 import javax.mail.MessagingException JavaDoc;
26 import javax.mail.internet.AddressException JavaDoc;
27 import javax.mail.internet.InternetAddress JavaDoc;
28 import javax.mail.internet.MimeMessage JavaDoc;
29 import javax.mail.internet.MimeMultipart JavaDoc;
30 import javax.mail.internet.MimePart JavaDoc;
31 import javax.mail.internet.ParseException JavaDoc;
32 import java.util.ArrayList JavaDoc;
33 import java.util.Date JavaDoc;
34 import java.util.HashSet JavaDoc;
35 import java.util.Iterator JavaDoc;
36 import java.util.List JavaDoc;
37 import java.util.Set JavaDoc;
38
39 /**
40  * Attributes of a Message in IMAP4rev1 style. Message
41  * Attributes should be set when a message enters a mailbox.
42  * <p> Note that the message in a mailbox have the same order using either
43  * Message Sequence Numbers or UIDs.
44  * <p> reinitialize() must be called on deserialization to reset Logger
45  *
46  * Reference: RFC 2060 - para 2.3
47  * @version 0.2 on 04 Aug 2002
48  */

49 public class SimpleMessageAttributes
50     extends AbstractLogEnabled
51     implements ImapMessageAttributes
52 {
53
54     private final static String JavaDoc SP = " ";
55     private final static String JavaDoc NIL = "NIL";
56     private final static String JavaDoc Q = "\"";
57     private final static String JavaDoc LB = "(";
58     private final static String JavaDoc RB = ")";
59     private final static boolean DEBUG = false;
60     private final static String JavaDoc MULTIPART = "MULTIPART";
61     private final static String JavaDoc MESSAGE = "MESSAGE";
62
63     private int uid;
64     private int messageSequenceNumber;
65     private Date JavaDoc internalDate;
66     private String JavaDoc internalDateString;
67     private String JavaDoc bodyStructure;
68     private String JavaDoc envelope;
69     private int size;
70     private int lineCount;
71     public ImapMessageAttributes[] parts;
72     private List JavaDoc headers;
73
74     //rfc822 or MIME header fields
75
//arrays only if multiple values allowed under rfc822
76
private String JavaDoc subject;
77     private String JavaDoc[] from;
78     private String JavaDoc[] sender;
79     private String JavaDoc[] replyTo;
80     private String JavaDoc[] to;
81     private String JavaDoc[] cc;
82     private String JavaDoc[] bcc;
83     private String JavaDoc[] inReplyTo;
84     private String JavaDoc[] date;
85     private String JavaDoc[] messageID;
86     private String JavaDoc contentType;
87     private String JavaDoc primaryType; // parsed from contentType
88
private String JavaDoc secondaryType; // parsed from contentType
89
private Set JavaDoc parameters; // parsed from contentType
90
private String JavaDoc contentID;
91     private String JavaDoc contentDesc;
92     private String JavaDoc contentEncoding;
93
94     SimpleMessageAttributes() {
95     }
96
97     void setAttributesFor(MimeMessage JavaDoc msg) throws MessagingException JavaDoc {
98         size = msg.getSize();
99         try {
100             internalDate = msg.getSentDate();
101         } catch (MessagingException JavaDoc me) {
102             internalDate = new Date JavaDoc();
103         }
104
105         internalDateString = RFC822DateFormat.toString(internalDate); // not right format
106
parseMimePart(msg);
107         envelope = null;
108         bodyStructure = null;
109     }
110
111     void setUID(int thisUID) {
112         uid = thisUID;
113     }
114
115     /**
116      * Parses key data items from a MimeMessage for seperate storage.
117      * TODO this is a mess, and should be completely revamped.
118      */

119     void parseMimePart(MimePart JavaDoc part) {
120         // Section 1 - Message Headers
121
if (part instanceof MimeMessage JavaDoc) {
122             try {
123                 subject = ((MimeMessage JavaDoc)part).getSubject();
124             } catch (MessagingException JavaDoc me) {
125                 if (DEBUG) getLogger().debug("Messaging Exception for getSubject: " + me);
126             }
127         }
128         try {
129             from = part.getHeader("From");
130         } catch (MessagingException JavaDoc me) {
131             if (DEBUG) getLogger().debug("Messaging Exception for getHeader(From): " + me);
132         }
133         try {
134             sender = part.getHeader("Sender");
135         } catch (MessagingException JavaDoc me) {
136             if (DEBUG) getLogger().debug("Messaging Exception for getHeader(Sender): " + me);
137         }
138         try {
139             replyTo = part.getHeader("Reply To");
140         } catch (MessagingException JavaDoc me) {
141             if (DEBUG) getLogger().debug("Messaging Exception for getHeader(Reply To): " + me);
142         }
143         try {
144             to = part.getHeader("To");
145         } catch (MessagingException JavaDoc me) {
146             if (DEBUG) getLogger().debug("Messaging Exception for getHeader(To): " + me);
147         }
148         try {
149             cc = part.getHeader("Cc");
150         } catch (MessagingException JavaDoc me) {
151             if (DEBUG) getLogger().debug("Messaging Exception for getHeader(To): " + me);
152         }
153         try {
154             bcc = part.getHeader("Bcc");
155         } catch (MessagingException JavaDoc me) {
156             if (DEBUG) getLogger().debug("Messaging Exception for getHeader(To): " + me);
157         }
158         try {
159             inReplyTo = part.getHeader("In Reply To");
160         } catch (MessagingException JavaDoc me) {
161             if (DEBUG) getLogger().debug("Messaging Exception for getHeader(In Reply To): " + me);
162         }
163         try {
164             date = part.getHeader("Date");
165         } catch (MessagingException JavaDoc me) {
166             if (DEBUG) getLogger().debug("Messaging Exception for getHeader(Date): " + me);
167         }
168         try {
169             messageID = part.getHeader("Message-ID");
170         } catch (MessagingException JavaDoc me) {
171             if (DEBUG) getLogger().debug("Messaging Exception for getHeader(messageID): " + me);
172         }
173         String JavaDoc contentTypeLine = null;
174         try {
175             contentTypeLine = part.getContentType();
176         } catch (MessagingException JavaDoc me) {
177             if (DEBUG) getLogger().debug("Messaging Exception for getContentType(): " + me);
178         }
179         if (contentTypeLine !=null ) {
180             decodeContentType(contentTypeLine);
181         }
182         try {
183             contentID = part.getContentID();
184         } catch (MessagingException JavaDoc me) {
185             if (DEBUG) getLogger().debug("Messaging Exception for getContentUD(): " + me);
186         }
187         try {
188             contentDesc = part.getDescription();
189         } catch (MessagingException JavaDoc me) {
190             if (DEBUG) getLogger().debug("Messaging Exception for getDescription(): " + me);
191         }
192         try {
193             contentEncoding = part.getEncoding();
194             // default value.
195
if ( contentEncoding == null ) {
196                 contentEncoding = "7BIT";
197             }
198         } catch (MessagingException JavaDoc me) {
199             if (DEBUG) getLogger().debug("Messaging Exception for getEncoding(): " + me);
200         }
201         if (DEBUG) {
202             try {
203                 String JavaDoc contentDisposition = part.getDisposition();
204             } catch (MessagingException JavaDoc me) {
205                 getLogger().debug("Messaging Exception for getEncoding(): " + me);
206             }
207         }
208
209         try {
210             // TODO this doesn't work
211
lineCount = part.getLineCount();
212         } catch (MessagingException JavaDoc me) {
213             if (DEBUG) getLogger().debug("Messaging Exception for getLineCount(): " + me);
214         } catch (Exception JavaDoc e) {
215             if (DEBUG) getLogger().debug("Exception for getLineCount(): " + e);
216         }
217
218         // Recurse through any embedded parts
219
if (primaryType.equalsIgnoreCase(MULTIPART)) {
220             MimeMultipart JavaDoc container;
221             try {
222                 container =(MimeMultipart JavaDoc) part.getContent();
223                 int count = container.getCount();
224                 parts = new SimpleMessageAttributes[count];
225                 for (int i = 0; i < count ; i ++) {
226                     BodyPart JavaDoc nextPart = container.getBodyPart(i);
227
228                     if (nextPart instanceof MimePart JavaDoc) {
229                         SimpleMessageAttributes partAttrs = new SimpleMessageAttributes();
230                         setupLogger(partAttrs); // reset transient logger
231
partAttrs.parseMimePart((MimePart JavaDoc)nextPart);
232                         parts[i] = partAttrs;
233
234                     } else {
235                         getLogger().info("Found a non-Mime bodyPart");
236                     }
237                 }
238             } catch (Exception JavaDoc e) {
239                 getLogger().debug("Messaging Exception for getContent(): " + e);
240                 e.printStackTrace();
241             }
242         } else if (primaryType.equalsIgnoreCase("message")) {
243             getLogger().info("This part contains an embedded message of subtype: " + secondaryType);
244             getLogger().info("Uses java class: " + part.getClass().getName());
245             if (secondaryType.equalsIgnoreCase("RFC822")) {
246                 //try {
247

248                     /*
249                     MimeMessageWrapper message = new MimeMessageWrapper(part.getInputStream());
250                     SimpleMessageAttributes msgAttrs = new SimpleMessageAttributes();
251                     msgAttrs.setAttributesFor(message);
252
253                     if (part instanceof MimeMessage) {
254                         Comments out because I don't know what it should do here
255                         MimeMessage msg1 = (MimeMessage) part;
256                         MimeMessageWrapper message2 = new MimeMessageWrapper(msg1);
257                         SimpleMessageAttributes msgAttrs2 = new SimpleMessageAttributes();
258                         msgAttrs.setAttributesFor(message2);
259                     }
260
261                     parts = new SimpleMessageAttributes[1];
262                     parts[0] = msgAttrs;
263                     */

264                 //} catch (Exception e) {
265
//getLogger().error("Error interpreting a message/rfc822: " + e);
266
//e.printStackTrace();
267
//}
268
} else {
269                 getLogger().info("Unknown subtype of message encountered.");
270                 System.out.println("Unknown subtype of message encountered.");
271             }
272         }
273         else {
274             System.out.println("parseMimePart: its just a plain message");
275         }
276     }
277
278     /**
279      * Builds IMAP envelope String from pre-parsed data.
280      */

281     String JavaDoc parseEnvelope() {
282         List JavaDoc response = new ArrayList JavaDoc();
283         response.add( LB + Q + internalDateString + Q + SP);
284         if (subject != null && (!subject.equals(""))) {
285             response.add( Q + subject + Q + SP );
286         } else {
287             response.add( NIL + SP );
288         }
289         if (from != null && from.length > 0) {
290             response.add(LB);
291             for (int i=0; i<from.length; i++) {
292                 response.add(parseAddress( from[i]) );
293             }
294             response.add(RB);
295         } else {
296             response.add( NIL);
297         }
298         response.add(SP);
299         if (sender != null && sender.length >0) {
300             if (DEBUG) getLogger().debug("parsingEnvelope - sender[0] is: " + sender[0]);
301             //Check for Netscape feature - sender is local part only
302
if (sender[0].indexOf("@") == -1) {
303                 response.add(LB + (String JavaDoc)response.get(3) + RB); //first From address
304
} else {
305                 response.add(LB);
306                 for (int i=0; i<sender.length; i++) {
307                     response.add( parseAddress(sender[i]));
308                 }
309                 response.add(RB);
310             }
311         } else {
312             if (from != null && from.length > 0) {
313                 response.add(LB + (String JavaDoc)response.get(3) + RB); //first From address
314
} else {
315                 response.add( NIL);
316             }
317         }
318         response.add(SP);
319         if (replyTo != null && replyTo.length >0) {
320             if (replyTo[0].indexOf("@") == -1) {
321                 response.add(LB + (String JavaDoc)response.get(3) + RB); //first From address
322
} else {
323                 response.add(LB);
324                 for (int i=0; i<replyTo.length; i++) {
325                     response.add( parseAddress(replyTo[i]));
326                 }
327                 response.add(RB);
328             }
329         } else {
330             if (from != null && from.length > 0) {
331                 response.add(LB + (String JavaDoc)response.get(3) + RB); //first From address
332
} else {
333                 response.add( NIL);
334             }
335         }
336         response.add(SP);
337         if (to != null && to.length >0) {
338             response.add(LB);
339             for (int i=0; i<to.length; i++) {
340                 response.add( parseAddress(to[i]));
341             }
342             response.add(RB);
343         } else {
344             response.add( NIL);
345         }
346         response.add(SP);
347         if (cc != null && cc.length >0) {
348             response.add(LB);
349             for (int i=0; i<cc.length; i++) {
350                 response.add( parseAddress(cc[i]));
351             }
352             response.add(RB);
353         } else {
354             response.add( NIL);
355         }
356         response.add(SP);
357         if (bcc != null && bcc.length >0) {
358             response.add(LB);
359             for (int i=0; i<bcc.length; i++) {
360                 response.add( parseAddress(bcc[i]));
361             }
362             response.add(RB);
363         } else {
364             response.add( NIL);
365         }
366         response.add(SP);
367         if (inReplyTo != null && inReplyTo.length>0) {
368             response.add( inReplyTo[0]);
369         } else {
370             response.add( NIL);
371         }
372         response.add(SP);
373         if (messageID != null && messageID.length>0) {
374             response.add(Q + messageID[0] + Q);
375         } else {
376             response.add( NIL);
377         }
378         response.add(RB);
379
380         StringBuffer JavaDoc buf = new StringBuffer JavaDoc(16 * response.size());
381         for (int j=0; j<response.size(); j++) {
382             buf.append((String JavaDoc)response.get(j));
383         }
384
385         return buf.toString();
386     }
387
388     /**
389      * Parses a String email address to an IMAP address string.
390      */

391     String JavaDoc parseAddress(String JavaDoc address) {
392         int comma = address.indexOf(",");
393         StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
394         if (comma == -1) { //single address
395
buf.append(LB);
396             InternetAddress JavaDoc netAddr = null;
397             try {
398                 netAddr = new InternetAddress JavaDoc(address);
399             } catch (AddressException JavaDoc ae) {
400                 return null;
401             }
402             String JavaDoc personal = netAddr.getPersonal();
403             if (personal != null && (!personal.equals(""))) {
404                 buf.append(Q + personal + Q);
405             } else {
406                 buf.append( NIL);
407             }
408             buf.append( SP);
409             buf.append( NIL) ; // should add route-addr
410
buf.append( SP);
411             try {
412                 MailAddress mailAddr = new MailAddress(netAddr);
413                 buf.append(Q + mailAddr.getUser() + Q);
414                 buf.append(SP);
415                 buf.append(Q + mailAddr.getHost() + Q);
416             } catch (ParseException JavaDoc pe) {
417                 buf.append( NIL + SP + NIL);
418             }
419             buf.append(RB);
420         } else {
421             buf.append(parseAddress(address.substring(0, comma)));
422             buf.append(SP);
423             buf.append(parseAddress(address.substring(comma + 1)));
424         }
425         return buf.toString();
426     }
427
428     /**
429      * Decode a content Type header line into types and parameters pairs
430      */

431     void decodeContentType(String JavaDoc rawLine) {
432         int slash = rawLine.indexOf("/");
433         if( slash == -1){
434             if (DEBUG) getLogger().debug("decoding ... no slash found");
435             return;
436         } else {
437             primaryType = rawLine.substring(0, slash).trim();
438         }
439         int semicolon = rawLine.indexOf(";");
440         if (semicolon == -1) {
441             if (DEBUG) getLogger().debug("decoding ... no semicolon found");
442             secondaryType = rawLine.substring(slash + 1).trim();
443             return;
444         }
445         // have parameters
446
parameters = new HashSet JavaDoc();
447         secondaryType = rawLine.substring(slash + 1, semicolon).trim();
448         int pos = semicolon;
449         int nextsemi = rawLine.indexOf(";", pos+1);
450         while (nextsemi != -1) {
451             if (DEBUG) getLogger().debug("decoding ... found another semicolon");
452             String JavaDoc param = rawLine.substring(pos + 1, nextsemi);
453             int esign = param.indexOf("=") ;
454             if (esign == -1) {
455                 if (DEBUG) getLogger().debug("Whacky parameter found: " + param);
456             } else {
457                 String JavaDoc name = param.substring(0, esign).trim();
458                 String JavaDoc value = param.substring(esign + 1).trim();
459                 parameters.add(name + SP + value);
460                 if (DEBUG) getLogger().debug("Found parameter: " + name + SP + value);
461             }
462             pos = nextsemi;
463             nextsemi = rawLine.indexOf(";", pos +1);
464         }
465         String JavaDoc lastParam = rawLine.substring(pos + 1);
466         int esign = lastParam.indexOf("=") ;
467         if (esign == -1) {
468             if (DEBUG) getLogger().debug("Whacky parameter found: " + lastParam);
469         } else {
470             String JavaDoc name = lastParam.substring(0, esign).trim();
471             String JavaDoc value = lastParam.substring(esign + 1).trim();
472             parameters.add(Q + name + Q + SP + Q + value + Q);
473             if (DEBUG) getLogger().debug("Found parameter: " + name + SP + value);
474         }
475     }
476
477     String JavaDoc parseBodyFields() {
478         StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
479         if (parameters == null || parameters.isEmpty()) {
480             buf.append(NIL);
481         } else {
482             buf.append(LB);
483             Iterator JavaDoc it = parameters.iterator();
484             while(it.hasNext()) {
485                 buf.append((String JavaDoc)it.next());
486             }
487             buf.append(RB);
488         }
489         buf.append(SP);
490         if(contentID == null) {
491             buf.append(NIL);
492         } else {
493             buf.append(Q + contentID + Q);
494         }
495         buf.append(SP);
496         if(contentDesc == null) {
497             buf.append(NIL);
498         } else {
499             buf.append(Q + contentDesc + Q);
500         }
501         buf.append(SP);
502         if(contentEncoding == null) {
503             buf.append( NIL );
504         } else {
505             buf.append(Q + contentEncoding + Q);
506         }
507         buf.append(SP);
508         buf.append(size);
509         return buf.toString();
510     }
511
512     /**
513      * Produce the IMAP formatted String for the BodyStructure of a pre-parsed MimeMessage
514      * TODO handle extension elements - Content-disposition, Content-Language and other parameters.
515      */

516     String JavaDoc parseBodyStructure() {
517         try {
518             String JavaDoc fields = parseBodyFields();
519             StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
520             buf.append(LB);
521             if (primaryType.equalsIgnoreCase("Text")) {
522                 buf.append("\"TEXT\" \"" );
523                 buf.append( secondaryType.toUpperCase() );
524                 buf.append( "\" ");
525                 buf.append( fields );
526                 buf.append( " " );
527                 buf.append( lineCount );
528
529                 // is: * 1 FETCH (BODYSTRUCTURE ("Text" "plain" NIL NIL NIL NIL 4 -1))
530
// wants: * 1 FETCH (BODYSTRUCTURE ("text" "plain" NIL NIL NIL "8bit" 6 1 NIL NIL NIL))
531
// or: * 1 FETCH (BODYSTRUCTURE ("text" "plain" NIL NIL NIL "7bit" 28 1 NIL NIL NIL))
532

533             } else if (primaryType.equalsIgnoreCase(MESSAGE) && secondaryType.equalsIgnoreCase("rfc822")) {
534                 buf.append("\"MESSAGE\" \"RFC822\" ");
535                 buf.append(fields + SP);
536                 setupLogger(parts[0]); // reset transient logger
537
buf.append(parts[0].getEnvelope() + SP);
538                 buf.append(parts[0].getBodyStructure( false ) + SP);
539                 buf.append(lineCount);
540             } else if (primaryType.equalsIgnoreCase(MULTIPART)) {
541                 for (int i=0; i<parts.length; i++) {
542                     setupLogger(parts[i]); // reset transient getLogger()
543
buf.append(parts[i].getBodyStructure( false ));
544                 }
545                 buf.append(SP + secondaryType);
546             }
547             buf.append(RB);
548             return buf.toString();
549         } catch (Exception JavaDoc e) {
550             getLogger().error("Exception while parsing BodyStrucuture: " + e);
551             e.printStackTrace();
552             throw new RuntimeException JavaDoc("Exception in parseBodyStructure");
553         }
554     }
555
556     /**
557      * Provides the current Message Sequence Number for this message. MSNs
558      * change when messages are expunged from the mailbox.
559      *
560      * @return int a positive non-zero integer
561      */

562     public int getMessageSequenceNumber() {
563         return messageSequenceNumber;
564     }
565
566     void setMessageSequenceNumber(int newMsn) {
567         messageSequenceNumber = newMsn;
568     }
569
570
571     /**
572      * Provides the unique identity value for this message. UIDs combined with
573      * a UIDValidity value form a unique reference for a message in a given
574      * mailbox. UIDs persist across sessions unless the UIDValidity value is
575      * incremented. UIDs are not copied if a message is copied to another
576      * mailbox.
577      *
578      * @return int a 32-bit value
579      */

580     public int getUID() {
581         return uid;
582     }
583
584     /**
585      * Provides the date and time at which the message was received. In the
586      * case of delivery by SMTP, this SHOULD be the date and time of final
587      * delivery as defined for SMTP. In the case of messages copied from
588      * another mailbox, it shuld be the internalDate of the source message. In
589      * the case of messages Appended to the mailbox, example drafts, the
590      * internalDate is either specified in the Append command or is the
591      * current dat and time at the time of the Append.
592      *
593      * @return Date imap internal date
594      */

595     public Date JavaDoc getInternalDate() {
596         return internalDate;
597     }
598
599     public String JavaDoc getInternalDateAsString() {
600         return internalDateString;
601     }
602
603     /**
604      * Provides the sizeof the message in octets.
605      *
606      * @return int number of octets in message.
607      */

608     public int getSize() {
609         return size;
610     }
611
612     /**
613      * Provides the Envelope structure information for this message. This is a parsed representation of the rfc-822 envelope information. This is not to be confused with the SMTP envelope!
614      *
615      * @return String satisfying envelope syntax in rfc 2060.
616      */

617     public String JavaDoc getEnvelope() {
618         return parseEnvelope();
619     }
620
621     /**
622      * Provides the Body Structure information for this message. This is a parsed representtion of the MIME structure of the message.
623      *
624      * @return String satisfying body syntax in rfc 2060.
625      */

626     public String JavaDoc getBodyStructure( boolean includeExtensions ) {
627         return parseBodyStructure();
628     }
629 }
630
Popular Tags