KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > SnowMailClient > model > multipart > MimePart


1 package SnowMailClient.model.multipart;
2
3 import javax.swing.tree.DefaultMutableTreeNode JavaDoc;
4 import SnowMailClient.utils.*;
5 import snow.utils.storage.*;
6 import SnowMailClient.crypto.Utilities;
7 import SnowMailClient.utils.MailMessageUtils;
8 import SnowMailClient.model.*;
9
10 import java.util.*;
11 import java.text.*;
12 import java.io.*;
13 import java.nio.charset.*;
14 import java.util.regex.*;
15
16 /** Represent a MimePart
17     (This is a tree node, because it can contain parts itself)
18     nodes content are in leaves child are leafs
19 */

20 public final class MimePart extends DefaultMutableTreeNode JavaDoc
21 {
22   public boolean debug = true;
23
24   // the content (DATA)
25
private final Header header = new Header();
26   private byte[] byteContent = new byte[0];
27
28
29   // discrete types
30
public enum ContentType {
31      NO_TYPE,
32      MULTIPART, TEXT, IMAGE, VIDEO, AUDIO, APPLICATION,
33      MESSAGE // contains a whole message
34
};
35
36   // attachment
37
private boolean isAttachment = false;
38
39   private ContentType contentType = ContentType.TEXT; // default
40
private String JavaDoc contentSubType = ""; // TEXT, HTML, ... (uppercase)
41

42   private String JavaDoc boundary;
43   private String JavaDoc name = "?";
44
45   // when the content is text, this represent the charset used to convert between the byte content and the text.
46
private String JavaDoc charset = "";
47
48
49   private boolean isRoot = false;
50
51   /** the constructor or a mime part.
52       call parseTree to parse the mime tree.
53   */

54   public MimePart()
55   {
56      super("???");
57   }
58
59   public int getPartsCount()
60   {
61      return this.getChildCount();
62   }
63
64   public MimePart getPartAt(int pos)
65   {
66      return (MimePart) this.getChildAt(pos);
67   }
68
69
70
71   /** add a part at the end
72   */

73   public void addPart_if_valid(MimePart part)
74   {
75      if(debug)
76      {
77         System.out.println("===> add part of size "+part.getContentTypeString().length()+"\n");
78      }
79
80
81      if(!part.isMultipart() && (part.byteContent==null || part.byteContent.length==0))
82      {
83        // avoid empty noon multipart mps
84
return;
85      }
86
87
88      this.add(part);
89   }
90
91
92   public byte[] getByteContent()
93   {
94     return byteContent;
95   }
96
97   /** this gives the unicode string corectly decoded, using the charset information
98       found in the header.
99       Should be used to display the message as plain text.
100   */

101   public String JavaDoc getBodyAsText()
102   {
103     String JavaDoc cont = null;
104     try
105     {
106       if(charset.equals(""))
107       {
108         //cont = new String(byteContent, "US-ASCII");
109
cont = new String JavaDoc(byteContent, "iso-8859-1"); // better, also covers 7bits encoding
110
}
111       else
112       {
113         cont =new String JavaDoc(byteContent, charset);
114       }
115     }
116     catch(Exception JavaDoc e)
117     {
118       System.out.println("MimePart: Bad charset decoding, error= "+e.getMessage());
119       System.out.println("charset = "+charset);
120       System.out.println("content length = "+byteContent.length);
121
122       cont = new String JavaDoc(byteContent);
123     }
124
125     /*
126     if(cont.startsWith("aaa"))
127     {
128       System.out.println("\nMimePart.getBodyAsText()=\n<cont>"+cont+"</cont>, charset="+charset);
129       System.out.println("bytes="+StringUtils.toString(byteContent));
130     }*/

131
132     return cont;
133   }
134
135   public boolean containText(String JavaDoc txtUP)
136   {
137     if(this.contentType==ContentType.TEXT)
138     {
139        if(this.getBodyAsText().toUpperCase().indexOf(txtUP)>=0)
140        {
141           return true;
142        }
143     }
144     return false;
145   }
146
147
148   public String JavaDoc getContentTypeString() { return header.getEntryValue("Content-Type", ""); }
149
150
151   /** used to render images embedded in the mime tree
152   */

153   public String JavaDoc getID()
154   {
155     return header.getEntryValue("Content-Id", "?");
156   }
157
158   public boolean isMultipart()
159   {
160     return contentType == ContentType.MULTIPART;
161   }
162
163   public boolean isAttachment()
164   {
165     return isAttachment;
166   }
167
168   /** text / image / audio / video / application / message
169   */

170   public ContentType getContentTYPE()
171   {
172     return contentType;
173   }
174
175   /** for example html in text/html, ...
176   */

177   public String JavaDoc getContentSubType() { return contentSubType; }
178
179
180   /** @return true if the content is pure HTML
181      either if his content type is explicitely set to be text/html
182      or if it starts with <html> or <!
183   */

184   public boolean lookIfContentIsHTML()
185   {
186     if(contentType!=ContentType.TEXT) return false;
187     if(contentSubType.toLowerCase().indexOf("html")>=0) return true;
188     if(this.byteContent == null) return false;
189
190     String JavaDoc cont = this.getBodyAsText();
191     // just a small test... show if the mails starts with <html>
192
if(StringUtils.startsWithIgnoresCaseAndBlanks( cont, "<HTML>")) return true;
193     if(StringUtils.startsWithIgnoresCaseAndBlanks( cont, "<!")) return true;
194
195     // definitely not...
196
return false;
197   }
198
199
200   /** @return true if the content is pure HTML
201      either if his content type is explicitely set to be text/html
202      or if it starts with <html> or <!
203   */

204   public boolean lookIfContentIsAnImage()
205   {
206     if(contentType==ContentType.IMAGE) return true;
207     String JavaDoc name = this.getName().toLowerCase();
208     if( name.endsWith(".bmp") || name.endsWith(".png") || name.endsWith(".jpg")
209       ||name.endsWith("jpeg") || name.endsWith("wbmp") || name.endsWith(".gif")) return true;
210
211     return false;
212   }
213
214   /** @return true if the content is an attachment
215       This is used to know if it will appear in the attachment bar.
216
217   */

218   public boolean lookIfContentIsAnAttachment()
219   {
220     if(isAttachment) return true;
221     if(contentType==ContentType.APPLICATION) return true;
222     if(contentType==ContentType.MESSAGE) return true;
223
224     return false;
225   }
226
227
228
229   /** set the content to be root
230   */

231   public void setContentAsMIME_ROOT(Address from, Vector<Address> tos, String JavaDoc subject)
232   {
233     header.removeAllEntries();
234     isAttachment = false;
235     isRoot = true;
236     setContentType(ContentType.MULTIPART, "Mixed");
237
238     header.setEntryOverwrite("From", from.toString());
239     StringBuffer JavaDoc toBuffer = new StringBuffer JavaDoc();
240     for(int i=0; i<tos.size(); i++)
241     {
242       toBuffer.append(tos.get(i).getMailAddress());
243       if(i<tos.size()-1) toBuffer.append("; ");
244     }
245
246     header.setEntryOverwrite("To", toBuffer.toString());
247     header.setEntryOverwrite("Subject", subject);
248     header.setEntryOverwrite("Date", MailMessageUtils.msgDateFormat(new Date()));
249
250
251     header.setEntryOverwrite("Message-ID", MailMessageUtils.createMessageID(from));
252     header.setEntryOverwrite("MIME-Version", "1.0");
253
254     // FOR SENDING ONLY
255
//header.setEntryOverwrite("Content-Type", "text/plain;\r\n\tcharset=\""+charset+"\"");
256
//if(quotedPrintable)
257
{
258      // header.setEntryOverwrite("Content-Transfer-Encoding", "quoted-printable");
259
}
260 /* else
261     {
262       header.setEntryOverwrite("Content-Transfer-Encoding", "8bit");
263     }*/

264
265
266     header.setEntryOverwrite("X-Priority", "3 (Normal)");
267     header.setEntryOverwrite("X-Mailer", "SnowMail 2.0");
268     header.setEntryOverwrite("Importance", "Normal");
269   }
270
271
272
273   /** this is called to make this part the message plain text part
274       no conversions here.
275   */

276   public void setContentAsPlainTextMailMessage(String JavaDoc text)
277   {
278 /* if(text.startsWith("aaa"))
279     {
280       System.out.println("\nMimePart.setContentAsPlainTextMailMessage()=\n<cont>"+text+"</cont>");
281     }*/

282
283     header.removeAllEntries();
284     isAttachment = false;
285     isRoot = false;
286     setContentType(ContentType.TEXT, "PLAIN");
287
288     isAttachment = false;
289     this.charset = CharsetUtils.getMinimalEncodingCharset(text);;
290
291
292     try
293     {
294       this.byteContent = text.getBytes(charset);
295     }
296     catch(Exception JavaDoc e)
297     {
298       this.byteContent = text.getBytes(); // iso-8859-1
299
e.printStackTrace();
300     }
301
302     /*
303     if(text.startsWith("aaa"))
304     {
305       System.out.println(" charset="+charset);
306       System.out.println(" bytes="+StringUtils.toString(byteContent));
307     }*/

308
309   }
310
311   /** this is called to make this an attachment
312   */

313   public void setContentAsAttachment(ContentType type, String JavaDoc subType,
314       byte[] cont, String JavaDoc name)
315   {
316     setContentType(type, subType);
317     this.isAttachment = true; // ###
318
this.byteContent = cont;
319     this.name = name;
320   }
321
322   public void setContentType(ContentType type, String JavaDoc subType)
323   {
324     contentType = type;
325     this.contentSubType = subType;
326     if(this.isMultipart())
327     {
328       name = "Multipart";
329     }
330   }
331
332   /** write the header (create or overwrite)
333      called before creating string representation to produce a message
334   */

335   private void writeMIMEFlagsInHeader(boolean multipart, boolean needsQuotedPrintableFormat)
336   {
337      if(multipart)
338      {
339        // MULTIPART
340
//
341

342        String JavaDoc mpt = "Multipart";
343        if(!this.contentSubType.equals(""))
344        {
345           mpt += "/"+contentSubType;
346        }
347        header.setEntryOverwrite("Content-Type", ""+mpt+";\n\tboundary=\""+boundary+"\"");
348
349        // no such tag
350
header.remove("Content-Transfer-Encoding");
351      }
352      else
353      {
354        // LEAFS
355
//
356

357        String JavaDoc ctt = "";
358        if( this.contentType==ContentType.TEXT) ctt+="Text";
359        else if(this.contentType==ContentType.VIDEO) ctt+="Video";
360        else if(this.contentType==ContentType.IMAGE) ctt+="Image";
361        else if(this.contentType==ContentType.AUDIO) ctt+="Audio";
362        else if(this.contentType==ContentType.APPLICATION) ctt+="Attachment";
363        else if(this.contentType==ContentType.MESSAGE) ctt+="Message";
364
365
366        if(!this.contentSubType.equals(""))
367        {
368           ctt += "/"+contentSubType;
369        }
370
371        if(this.contentType==ContentType.TEXT)
372        {
373          if(!charset.equals(""))
374          {
375            ctt += ";\n\tcharset="+charset;
376          }
377        }
378
379        if(isAttachment && !name.equals("?"))
380        {
381          ctt += ";\n\tname=\""+name+"\"";
382        }
383
384        header.setEntryOverwrite("Content-Type", ctt);
385        if(this.contentType==ContentType.MULTIPART)
386        {
387          //System.out.println("NO!");
388

389          // the multipart entry has NO Content-Transfer-Encoding
390
header.remove("Content-Transfer-Encoding");
391        }
392        else if(this.contentType==ContentType.TEXT)
393        {
394          if(needsQuotedPrintableFormat)
395          {
396            header.setEntryOverwrite("Content-Transfer-Encoding", "quoted-printable");
397          }
398          else
399          {
400            header.setEntryOverwrite("Content-Transfer-Encoding", "7bit");
401          }
402        }
403        else
404        {
405          header.setEntryOverwrite("Content-Transfer-Encoding", "base64");
406        }
407
408        if(isAttachment)
409        {
410          // ###
411
header.setEntryOverwrite("Content-Disposition", "attachment;\n\tfilename=\""+name+"\"");
412        }
413      }
414
415      if(isRoot)
416      {
417        header.setEntryOverwrite("Mime-Version", "1.0 (SnowMmail admin@www.snowraver.org)");
418      }
419   }
420
421
422   /** used to send the mail.
423   */

424   public byte[] getContent_For_Sending(int level, int rand1, int rand2) throws Exception JavaDoc
425   {
426     ByteArrayOutputStream bos = new ByteArrayOutputStream();
427     if(this.isMultipart())
428     {
429       // use the boundary given as parent
430
this.boundary = MimeUtils.createUniqueBoundary(level,rand1,rand2);
431     }
432
433     boolean needsQuotedPrintableFormat = !MailMessageUtils.is7Bits(this.byteContent);
434     writeMIMEFlagsInHeader(this.isMultipart(), needsQuotedPrintableFormat);
435
436     bos.write((header.to_ASCII_String()+"\r\n").getBytes("us-ascii"));
437
438     if(this.isMultipart())
439     {
440       if(level==1)
441       {
442         bos.write(("\r\nThis is a multi-part message in MIME format.\r\n").getBytes("us-ascii"));
443       }
444
445       for(int i=0; i<this.getChildCount(); i++)
446       {
447         bos.write(("\r\n--"+boundary+"\r\n").getBytes("us-ascii"));
448         MimePart mp = this.getPartAt(i);
449         bos.write(mp.getContent_For_Sending(level+1, rand1, rand2));
450       }
451
452       bos.write(("\r\n--"+boundary+"--\r\n").getBytes("us-ascii"));
453     }
454     else
455     {
456       if(contentType==ContentType.TEXT)
457       {
458          //System.out.println(""+StringUtils.toString(byteContent)+": "+needsQuotedPrintableFormat);
459
if(needsQuotedPrintableFormat)
460          {
461            // encode
462
bos.write( MailMessageUtils.encodeMessageQuotedPrintable(byteContent) );
463          }
464          else
465          {
466            bos.write( byteContent );
467          }
468       }
469       else
470       {
471          bos.write( Utilities.encodeBase64(byteContent).getBytes("us-ascii") );
472          bos.write("\n".getBytes("us-ascii"));
473       }
474     }
475
476
477     return bos.toByteArray();
478   }
479
480
481   /** used to store this mime part in the Message.
482       This is always performed after changes in the mime tree (edit/add/remove).
483   */

484   public String JavaDoc createStringRepresentation(int level, int rand1, int rand2)
485   {
486     if(this.isMultipart())
487     {
488       // use the boundary given as parent
489
this.boundary = MimeUtils.createUniqueBoundary(level,rand1,rand2);
490     }
491
492     writeMIMEFlagsInHeader(this.isMultipart(),
493                            false); // NO quoted printable. Only on send.
494

495     StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
496     sb.append(getHeaderText()+"\n");
497
498     if(this.isMultipart())
499     {
500       if(level==1)
501       {
502         sb.append("\nThis is a multi-part message in MIME format.\n");
503       }
504
505       for(int i=0; i<this.getChildCount(); i++)
506       {
507         sb.append("\n--"+boundary+"\n");
508
509         MimePart mp = this.getPartAt(i);
510         sb.append(mp.createStringRepresentation(level+1, rand1, rand2));
511
512       }
513       sb.append("\n--"+boundary+"--\n");
514     }
515     else
516     {
517       if(contentType==ContentType.TEXT)
518       {
519         if(charset.equals(""))
520         {
521           sb.append(new String JavaDoc(byteContent)+"");
522         }
523         else
524         {
525           try
526           {
527             sb.append(new String JavaDoc(byteContent, charset)+"");
528           }
529           catch(Exception JavaDoc e)
530           {
531             e.printStackTrace();
532             sb.append(new String JavaDoc(byteContent)+"");
533           }
534         }
535       }
536       else
537       {
538         sb.append( Utilities.encodeBase64(byteContent) );
539         sb.append("\n");
540       }
541     }
542
543     return sb.toString();
544   }
545
546   /** used to regenerate content
547   */

548   public String JavaDoc getHeaderText()
549   {
550      return header.toString();
551   }
552
553
554   /** used during the parse operation
555   */

556   boolean parse_boundary_end_reached = false;
557
558   /* PROBLEM: attachment as unique content
559
560 MIME-Version: 1.0
561 Content-Type: application/x-zip-compressed;
562     name="moveproblem.zip"
563 Content-Transfer-Encoding: base64
564 Content-Description: moveproblem.zip
565 Content-Disposition: attachment;
566     filename="moveproblem.zip"
567
568     CCCCCCCCCCContent......
569
570   */

571   /** parse a mime tree (recursively) from a string content.
572
573       @param boundaryToParse should be empty at the beginning and lineReader should read the whole
574         content.
575       @param jumpToNextBoundary cause ignoring lines up to next boundary.
576
577       usage: call
578          parseFrom(wholeContent, "", false, 0);
579
580       the member variable parse_boundary_end_reached indicates if the end of boundaryToParse was reached or not
581   */

582   public void parseMimeTree(NumberedLineReader lineReader,
583                         String JavaDoc boundaryToParse,
584                         boolean jumpToNextBoundary,
585                         int level) throws Exception JavaDoc
586   {
587      if(debug)
588      {
589        System.out.println("\nParsing "+boundaryToParse+", jump="+jumpToNextBoundary+", level="+level+", line="+lineReader.getLineNumber());
590      }
591
592      // we go to the next start
593
if(jumpToNextBoundary)
594      {
595        String JavaDoc deadLine = null;
596        while((deadLine=lineReader.readLine())!=null)
597        {
598          // [Nov2005]: very very strange behaviour by an amazon reply, the boundary was present
599
// somewhere not at beginning:
600
// <ems-render-hash><hash name="EMS.mime-boundary">MuLtIpArT_BoUnDaRy</hash><hash name="EMS.info">log
601
// info </hash><hash name="EMS.status">OK</hash></ems-render-hash>
602
// => only accept if at beginning
603
int pos = deadLine.indexOf(boundaryToParse);
604          if(pos>=0 && pos<10) break; // we found it !
605
}
606        if(deadLine==null)
607        {
608          throw new BoundaryNotFoundException("No start found for boundary "+boundaryToParse);
609        }
610      }
611
612      // read this part header
613
//
614
try
615      {
616        Header.parseHeader(lineReader, header);
617        if(debug)
618        {
619          System.out.println(" Header read "+header.getEntriesCount()+"\n line="+lineReader.getLineNumber());
620        }
621      }
622      catch(Exception JavaDoc e)
623      {
624        // maybe, we have reached the end ???
625
String JavaDoc ll = lineReader.getLastLineCached();
626        contentType = ContentType.NO_TYPE;
627        if(ll==null)
628        {
629          this.parse_boundary_end_reached = true;
630          return ;
631        }
632
633        if(ll.trim().endsWith("--"))
634        {
635          this.parse_boundary_end_reached = true;
636          return ;
637        }
638
639        // error
640
System.out.println("MimePart parse error at level="+level+", Jump "+jumpToNextBoundary+", bound="+boundaryToParse
641          +", line="+lineReader.getLineNumber());
642        throw new Exception JavaDoc("Cannot read mime part header", e);
643      }
644
645      // determine the content type
646
//
647
String JavaDoc contentTypeStringUP = getContentTypeString().trim().toUpperCase();
648
649      if(contentTypeStringUP.equals(""))
650      {
651         this.contentType = ContentType.NO_TYPE;
652         name = "NO_type";
653      }
654      else if(contentTypeStringUP.indexOf("MULTIPART")>=0)
655      {
656         this.contentType = ContentType.MULTIPART;
657         name = "Multipart";
658      }
659      else if(contentTypeStringUP.indexOf("MESSAGE")>=0)
660      {
661         this.contentType =ContentType.MESSAGE;
662         name = "Message";
663      }
664      else if(contentTypeStringUP.indexOf("TEXT")>=0)
665      {
666         this.contentType = ContentType.TEXT;
667         name = "Text";
668      }
669      else if(contentTypeStringUP.indexOf("IMAGE")>=0)
670      {
671         this.contentType = ContentType.IMAGE;
672         name = "Image";
673      }
674      else if(contentTypeStringUP.indexOf("VIDEO")>=0)
675      {
676         this.contentType = ContentType.VIDEO;
677         name = "Video";
678      }
679      else if(contentTypeStringUP.indexOf("AUDIO")>=0)
680      {
681         this.contentType = ContentType.AUDIO;
682         name = "Audio";
683      }
684      else if(contentTypeStringUP.indexOf("APPLICATION")>=0)
685      {
686         this.contentType = ContentType.APPLICATION;
687         name = "Application";
688      }
689      else if(contentTypeStringUP.indexOf("ATTACHMENT")>=0) // ???
690
{
691         this.contentType = ContentType.APPLICATION;
692         name = "Application";
693      }
694      else
695      {
696         throw new RuntimeException JavaDoc("Unknown mime content type: "+contentTypeStringUP);
697      }
698
699      // subtype
700
int posSlash = contentTypeStringUP.indexOf("/");
701      if(posSlash==-1)
702      {
703        this.contentSubType = "";
704      }
705      else
706      {
707        this.contentSubType = contentTypeStringUP.substring(posSlash+1);
708        int posSp = contentSubType.indexOf(";");
709        if(posSp!=-1) contentSubType = contentSubType.substring(0, posSp);
710        //System.out.println("'"+contentSubType+"'");
711
}
712
713
714      // charset
715
charset = HeaderUtils.getHeader_Charset_entry(header);
716
717      // attachment ###
718
String JavaDoc cd = header.getEntryValue("Content-Disposition","").toUpperCase();
719      if(cd.indexOf("ATTACHMENT")!=-1)
720      {
721         this.isAttachment = true;
722      }
723
724      //System.out.println("CT="+contentTypeStringUP+", type="+contentType);
725

726      // Name
727
//
728
int posName = contentTypeStringUP.indexOf("NAME=\"");
729      if(posName!=-1)
730      {
731        int endPos = contentTypeStringUP.indexOf("\"", posName+6);
732        if(endPos>=0)
733        {
734          String JavaDoc ct = getContentTypeString().trim();
735          name = ct.substring(posName+6, endPos);
736          //System.out.println("NAME="+ct.substring(posName+6, endPos));
737
}
738      }
739
740      // [Aug2004]
741
name = MailMessageUtils.decodeCharsetCodedArgumentFully(name);
742
743
744      if(debug)
745      {
746        System.out.println(" MimePart ct="+this.contentType+", "+contentSubType);
747        System.out.println(" cs="+charset+" att="+isAttachment+" name="+name+" multi="+isMultipart());
748      }
749
750      // recursive types (multipart)
751
//
752
if(this.isMultipart())
753      {
754        // find boundary
755
//boolean boundaryWithoutQuotes = false;
756
/*
757        int bd = contentTypeStringUP.indexOf("BOUNDARY=\"");
758        if(bd==-1)
759        {
760          // second chance: some newsletters and ezmlm havn't quotes
761          bd = contentTypeStringUP.indexOf("BOUNDARY=");
762          boundaryWithoutQuotes = true;
763        }
764
765        if(bd==-1)
766        {
767           System.out.println("No boundary found for multipart="+contentTypeStringUP);
768           throw new Exception("No boundary");
769        }
770
771        String ct = getContentTypeString().trim();
772        if(boundaryWithoutQuotes)
773        {
774          boundary = ct.substring(bd+9);
775        }
776        else
777        {
778          boundary = ct.substring(bd+10);
779        }
780
781        int end = boundary.indexOf("\"");
782        if(boundaryWithoutQuotes)
783        {
784          end = boundary.length();
785          //System.out.println("Boundary without quotes found = >"+boundary.substring(0, end)+"<");
786        }
787
788        if(end==-1) throw new Exception("No boundary end");
789        boundary = boundary.substring(0, end).trim();
790
791        if(boundary.length()==0) throw new Exception("Empty boundary !");
792
793        if(debug)
794        {
795          System.out.println(" parse multipart, boundary=" + boundary);
796        }*/

797
798 String JavaDoc boundary = MimePart.extractBoundary(getContentTypeString().trim());
799 if(boundary==null)
800 {
801           System.out.println("No boundary found for multipart="+contentTypeStringUP);
802           throw new Exception JavaDoc("No boundary");
803 }
804
805        // ok, now we can parse the parts, defined with this boundary
806
boolean first = true; // first
807
while(true)
808        {
809           MimePart mp = new MimePart();
810           mp.debug = debug;
811
812           mp.parseMimeTree(lineReader, boundary, first, level+1);
813           if(mp.parse_boundary_end_reached)
814           {
815             if(debug)
816             {
817               System.out.println("end reached !");
818             }
819
820             // last part...
821
if(contentType != ContentType.NO_TYPE)
822             {
823               this.addPart_if_valid(mp);
824             }
825
826             break; // END
827
}
828
829           first = false;
830
831           if(contentType != ContentType.NO_TYPE)
832           {
833             this.addPart_if_valid(mp);
834           }
835        }
836
837        // ###
838
this.parse_boundary_end_reached = false;
839        return;
840
841      }
842      else
843      {
844        // this is not a multipart => read the content
845
//
846
StringBuffer JavaDoc content = new StringBuffer JavaDoc();
847        String JavaDoc line = null;
848        if(debug)
849        {
850          System.out.println("Parsing mimePart content from line "+lineReader.getLineNumber());
851        }
852
853        while((line=lineReader.readLine())!=null)
854        {
855           if(line.indexOf(boundaryToParse)!=-1)
856           {
857              // end boundary found.
858
break;
859           }
860           content.append(line+"\n");
861        }
862
863        if(debug)
864        {
865          System.out.println(" ===== read content size = "+content.length());
866          System.out.println(" ========== START ");
867          System.out.println(content.toString());
868          System.out.println(" *========== END ");
869
870        }
871
872
873        if(line==null)
874        {
875           // no end boundary found !!!
876
//OLD throw new RuntimeException("Boundary end not found.");
877

878           // [June2004] Accept incorectly finished messages...
879
// just do as it would be finished
880
// end
881
this.parse_boundary_end_reached = true;
882
883           if(content.length()>0)
884           {
885             setByteContentFromParsed(content.substring(0,content.length()-1)); // forget the last return
886
}
887           else
888           {
889             setByteContentFromParsed(content.toString());
890           }
891
892           return ;
893        }
894
895        // look how the last line ends
896
this.parse_boundary_end_reached = line.trim().endsWith("--");
897
898        if(content.length()>0)
899        {
900          setByteContentFromParsed(content.substring(0,content.length()-1)); // forget the last return
901
}
902        else
903        {
904          setByteContentFromParsed(content.toString());
905        }
906
907        return ;
908      }
909   }
910
911
912
913   /** this sets the byteContent, extracting the bytes
914   */

915   private void setByteContentFromParsed(String JavaDoc content) throws Exception JavaDoc
916   {
917      //byteContent = content.substring(0,content.length()-1).getBytes("utf-8");
918

919 /* if(content.toString().startsWith("aaa"))
920      {
921         System.out.println("\nMimePart.parseTree()=<cont>"+content.substring(0,content.length()-1)+"</cont>, end reached= "+this.parse_boundary_end_reached);
922         //System.out.println(" chars="+StringUtils.toString(this.byteContent));
923         setByteContentFromParsed(content, true);
924         System.out.println(" chard="+StringUtils.toString(this.byteContent));
925      }
926      else
927      {
928         setByteContentFromParsed(content, false);
929      }*/

930      if(debug)
931      {
932        System.out.println("setByteContentFromParsed false");
933      }
934
935      setByteContentFromParsed(content, debug);
936
937   }
938
939
940   public String JavaDoc getTextForTreeRepresentation()
941   {
942     if(byteContent!=null && byteContent.length>0)
943     {
944       return getName()+" ("+MailMessageUtils.formatSize(byteContent.length)+")";
945     }
946     else
947     {
948       return getName();
949     }
950   }
951
952   public String JavaDoc getName()
953   {
954     // cause html content to be seen as HTML in the tree
955
if(this.lookIfContentIsHTML()) return "HTML";
956     return name;
957   }
958
959   /** Possibilities: 7bits, 8bits, Base64, quoted-printable
960
961   */

962   private void setByteContentFromParsed(String JavaDoc scont, boolean debug) throws Exception JavaDoc
963   {
964     // Encoding...
965
//
966
String JavaDoc contentTransferEncoding = header.getEntryValue("content-transfer-encoding", "").toUpperCase();
967
968     if(debug)
969     {
970       System.out.println("setByteContentFromParsed enc = "+contentTransferEncoding);
971       System.out.println(""+scont+"\n=============");
972     }
973
974     String JavaDoc charsetToDecode = "iso-8859-1";
975     if(charset.length()>0)
976     {
977       charsetToDecode = charset;
978     }
979
980     if(charsetToDecode.equalsIgnoreCase("us-ascii"))
981     {
982       // well: how explain this ?
983
// we enforce here keeping the 8 bits,
984
// iso contains us-ascii but don't cut the 8th bit !
985
charsetToDecode = "iso-8859-1";
986       charset = "iso-8859-1";
987     }
988
989     // look if the charset exists !
990
//
991
try
992     {
993       Charset cs = Charset.forName(charsetToDecode);
994       "hello".getBytes(charsetToDecode);
995     }
996     catch(Exception JavaDoc e)
997     {
998       if(debug)
999       {
1000         System.out.println("MimePart unknown charset "+charsetToDecode+" using iso-8859-1");
1001      }
1002      charsetToDecode = "iso-8859-1";
1003      charset = "iso-8859-1";
1004    }
1005
1006    if(contentTransferEncoding.equals(""))
1007    {
1008      // [June2005] was missing !
1009
byteContent = scont.getBytes(charsetToDecode);
1010    }
1011    else if(contentTransferEncoding.indexOf("8BIT")!=-1)
1012    {
1013      byteContent = scont.getBytes(charsetToDecode);
1014    }
1015    else if(contentTransferEncoding.indexOf("7BIT")!=-1)
1016    {
1017      byteContent = scont.getBytes(charsetToDecode);
1018    }
1019    else if(contentTransferEncoding.indexOf("QUOTED-PRINTABLE")!=-1)
1020    {
1021      try
1022      {
1023        byteContent = MailMessageUtils.decodeQuotedPrintable(scont, charsetToDecode).getBytes(charsetToDecode);
1024      }
1025      catch(Exception JavaDoc e)
1026      {
1027        System.out.println("Cannot decode charset '"+charsetToDecode+"'");
1028        charset = "iso-8859-1";
1029        byteContent = MailMessageUtils.decodeQuotedPrintable(scont, "iso-8859-1").getBytes("iso-8859-1");
1030      }
1031    }
1032    else if(contentTransferEncoding.indexOf("BASE64")!=-1)
1033    {
1034      try
1035      {
1036        byteContent = Utilities.decodeBase64( scont );
1037      }
1038      catch(Exception JavaDoc e)
1039      {
1040        throw new Exception JavaDoc("MimePart: error decoding base 64 content:\n "+e.getMessage());
1041      }
1042    }
1043    else
1044    {
1045       System.out.println("MimePart: unknown content-transfer-encoding: "+contentTransferEncoding);
1046       // [June2005] was missing !
1047
byteContent = scont.getBytes(charsetToDecode);
1048    }
1049
1050  }
1051
1052  public static void main(String JavaDoc[] arguments)
1053  {
1054     System.out.println(extractBoundary("BOUNDARY=\"12a3\""));
1055  }
1056
1057  public static String JavaDoc extractBoundary(String JavaDoc txt)
1058  {
1059      Pattern p = Pattern.compile("boundary\\s*=\\s*(.*)", Pattern.CASE_INSENSITIVE);
1060      Matcher m = p.matcher(txt);
1061      if(m.find())
1062      {
1063         String JavaDoc found = m.group(1).trim();
1064         if(found.startsWith("\"")) found = found.substring(1);
1065         if(found.endsWith(";")) found = found.substring(0,found.length()-1);
1066         if(found.endsWith("\"")) found = found.substring(0,found.length()-1);
1067         return found;
1068      }
1069      else
1070      {
1071         return null;
1072      }
1073
1074  }
1075
1076
1077
1078} // MimePart
Popular Tags