KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > SnowMailClient > model > MailMessage


1 package SnowMailClient.model;
2
3 import snow.utils.storage.*;
4 import SnowMailClient.utils.*;
5 import snow.utils.gui.*;
6 import snow.concurrent.*;
7 import SnowMailClient.*;
8 import SnowMailClient.crypto.*;
9 import SnowMailClient.model.multipart.*;
10 import SnowMailClient.Language.Language;
11 import SnowMailClient.SpamFilter.WordStatistic;
12 import SnowMailClient.model.events.MailMessageChangeListener;
13 import SnowMailClient.gnupg.GnuPGLink;
14 import SnowMailClient.gnupg.model.*;
15
16 import java.util.*;
17 import java.text.*;
18 import java.io.*;
19 import javax.swing.event.ChangeListener JavaDoc;
20 import javax.swing.event.ChangeEvent JavaDoc;
21
22 /** In the model, the message is as it was received: A STRING and nothing else
23     this string contains the header, body and attachments, in mime format or whatever.
24     At each recreation from vec rep, it is reparsed and restored.
25
26       message = header + body
27
28     When edited, the body changes,
29     after edition, one must call recomposeMessageFromParts()
30 */

31 public final class MailMessage implements Vectorizable
32 {
33   public boolean debug = false;
34
35   // Only this part of the mail is stored, the rest is recomposed at each recreation
36
//
37

38   private String JavaDoc completeContent;
39
40   // general props, snowmail specific, NOT in the header
41
private AppProperties properties = new AppProperties();
42
43   //
44
// parsed from complete content
45
//
46

47   private String JavaDoc messageBody = "";
48
49   // from, to, subject, ... are in the header of received and sended mails.
50
private Header header = new Header();
51
52   // attachements, ...
53
private MimeTreeModel mimeTreeModel = new MimeTreeModel(new MimePart());
54
55
56   // Other non persistent data
57
//
58

59   private Vector<MailMessageChangeListener> changeListeners = new Vector<MailMessageChangeListener>();
60
61   private final SimpleDateFormat dateFormat = new SimpleDateFormat("dd MMM yyyy HH':'mm", Language.getInstance().getLocale());
62
63   // PUBLIC: used in the view to keep selection
64
public boolean selectedInView = false;
65
66   /** create this mail as the parsed passed content
67   */

68   public void parse(String JavaDoc completeContent)
69   {
70     if(debug) System.out.println("\nMailMessage Parse debug:");
71     header.removeAllEntries();
72
73     this.completeContent = completeContent;
74     this.parse(completeContent, true);
75   }
76
77   // Header
78
//
79

80   public Header getHeader() { return header; }
81
82   public Address getFromAddress() { return new Address(header.getEntryValue("from","?")); }
83   public Vector<Address> getToAddresses() { return Address.parseAddressList(header.getEntryValue("to","?")); }
84   public String JavaDoc getToAddressesText() { return header.getEntryValue("to","?"); }
85
86   public String JavaDoc getSubject() { return header.getEntryValue("subject","?"); }
87   public String JavaDoc getMessageID() { return header.getEntryValue("message-id","?"); }
88   public String JavaDoc getDateFieldFormHeader() { return header.getEntryValue("date","?"); }
89   public String JavaDoc getContentType() { return header.getEntryValue("content-type", ""); }
90   public int getSize() { return completeContent.length(); }
91
92   /** @return true if the content is pure HTML
93      either if his content type is explicitely set to be text/html
94      or if it starts with <html> or <!
95   */

96   public boolean lookIfContentIsHTML()
97   {
98     String JavaDoc ct = header.getEntryValue("content-type", "").toUpperCase();
99     if(ct.indexOf("TEXT/HTML")!=-1) return true;
100
101     // just a small test... show if the mails starts with <html>
102
if(StringUtils.startsWithIgnoresCaseAndBlanks( this.getMessageBody(), "<HTML")) return true;
103     if(StringUtils.startsWithIgnoresCaseAndBlanks( this.getMessageBody(), "<x-HTML")) return true;
104     if(StringUtils.startsWithIgnoresCaseAndBlanks( this.getMessageBody(), "<!")) return true;
105
106     // definitely not...
107
return false;
108   }
109
110   /** only if the WHOLE content is encrypted !
111
112   */

113   public boolean lookIfContentIsPGPEncrypted()
114   {
115     if( messageBody.length()<100) return false;
116
117     if( this.messageBody.substring(0,80).toUpperCase().indexOf("-----BEGIN PGP MESSAGE-----")==-1) return false;
118     if( this.messageBody.substring(messageBody.length()-80).toUpperCase().indexOf("-----END PGP MESSAGE-----")==-1) return false;
119
120     return true;
121   }
122
123   public boolean lookIfContentIsPGPSigned()
124   {
125     if( messageBody.length()<100) return false;
126
127     if( this.messageBody.substring(0,80).toUpperCase().indexOf("-----BEGIN PGP SIGNED MESSAGE-----")==-1) return false;
128     if( this.messageBody.substring(messageBody.length()-80).toUpperCase().indexOf("-----END PGP SIGNATURE-----")==-1) return false;
129
130     return true;
131   }
132
133   // Properties
134
//
135

136   public boolean getIsNew() { return properties.getBoolean("isNew", true); }
137
138   public long getParsedMessageTime() { return properties.getLong( "parsedMessageTime", 0L); }
139
140   public boolean getHasBeenSent() { return properties.getBoolean( "hasBeenSent", false); }
141   public void setHasBeenSent(boolean b)
142   {
143     properties.setBoolean( "hasBeenSent", b);
144     this.notifyChangeListeners(MailMessageChangeListener.MailMessageChangeType.PROPERTY, "has been sent="+b);
145   }
146   public boolean getHasBeenReceived() { return properties.getBoolean( "HasBeenReceived", false); }
147   public void setHasBeenReceived(boolean b)
148   {
149     properties.setBoolean( "HasBeenReceived", b);
150     this.notifyChangeListeners(MailMessageChangeListener.MailMessageChangeType.PROPERTY, "has been received="+b);
151   }
152
153   public void setSPAM(boolean is)
154   {
155     properties.setBoolean( "isSpam", is);
156     if(is) properties.setBoolean( "isHam", false);
157     this.notifyChangeListeners(MailMessageChangeListener.MailMessageChangeType.PROPERTY, "is spam="+is);
158   }
159   public boolean getIsSPAM() { return properties.getBoolean("isSpam", false); }
160
161   public void setHAM(boolean is)
162   {
163     properties.setBoolean( "isHam", is);
164     if(is) properties.setBoolean( "isSpam", false);
165     this.notifyChangeListeners(MailMessageChangeListener.MailMessageChangeType.PROPERTY, "is ham="+is);
166   }
167   public boolean getIsHAM()
168   {
169     if( properties.getBoolean("isHam", false) ) return true;
170     if(getIsSPAM()) return false;
171     if( SnowMailClientApp.getInstance().getAddressBook().hasAddress(getFromAddress().getMailAddress()) )
172     {
173       return true;
174     }
175     return false;
176   }
177
178   /** if the spam probability is great but the message is HAM, we have a false positive !!
179       this must be displayed with a BIG warning !
180   */

181   public boolean getIsFalsePositive()
182   {
183      double prob = this.getSPAMProbability();
184      return WordStatistic.isSpam(prob) && getIsHAM();
185   }
186
187   public void setSPAMProbability(double p)
188   {
189     properties.setDouble( "SPAMProbability", p);
190     this.notifyChangeListeners(MailMessageChangeListener.MailMessageChangeType.PROPERTY, "spam prob="+p);
191   }
192
193   public double getSPAMProbability()
194   {
195     return properties.getDouble("SPAMProbability", -1);
196   }
197
198
199
200   /** Reset the new state.
201       This means either that the mail was send or read
202   */

203   public void setIsNoMoreNew()
204   {
205     properties.setBoolean("isNew", false);
206     this.notifyChangeListeners(MailMessageChangeListener.MailMessageChangeType.PROPERTY, "is no more new");
207   }
208
209   public void setMustBeSigned(boolean must)
210   {
211     System.out.println("Message must be signed");
212     properties.setBoolean("mustBeSigned", must);
213     this.notifyChangeListeners(MailMessageChangeListener.MailMessageChangeType.PROPERTY, "must be signed="+must);
214   }
215
216   public boolean getMustBeSigned()
217   {
218     return properties.getBoolean("mustBeSigned", false);
219   }
220
221   public void setMustBeEncrypted(boolean must)
222   {
223     properties.setBoolean("mustBeEncrypted", must);
224     this.notifyChangeListeners(MailMessageChangeListener.MailMessageChangeType.PROPERTY, "must be enctypted="+must);
225   }
226
227   public boolean getMustBeEncrypted() { return properties.getBoolean("mustBeEncrypted", false); }
228
229   public void setHasBeenEncrypted(boolean was)
230   {
231     properties.setBoolean("hasBeenEncrypted", was);
232     this.notifyChangeListeners(MailMessageChangeListener.MailMessageChangeType.PROPERTY, "was enctypted="+was);
233   }
234
235   private void setHasBeenSigned(boolean was)
236   {
237     properties.setBoolean("hasBeenSigned", was);
238     this.notifyChangeListeners(MailMessageChangeListener.MailMessageChangeType.PROPERTY, "was signed="+was);
239   }
240
241
242   public boolean getHasBeenEncrypted() { return properties.getBoolean("hasBeenEncrypted", false); }
243   public boolean getHasBeenSigned( ) { return properties.getBoolean("hasBeenSigned", false); }
244
245   /** + has been sent and signed/encrypted
246       + has been decrypted
247   */

248   public String JavaDoc getStatusRemark()
249   {
250       StringBuffer JavaDoc rem = new StringBuffer JavaDoc();
251
252       // Show a message if the message has been sent
253
if(getHasBeenSent())
254       {
255
256         if(this.getHasBeenEncrypted() && this.getHasBeenSigned())
257         {
258            rem.append( Language.translate("Message has been sent signed and encrypted"));
259         }
260         else if(this.getHasBeenEncrypted())
261         {
262            rem.append( Language.translate("Message has been sent encrypted"));
263         }
264         else if(this.getHasBeenSigned())
265         {
266            rem.append( Language.translate("Message has been sent signed"));
267         }
268         else
269         {
270            rem.append("\n"+ Language.translate("Message has been sent"));
271 // " on %", dateFormat.format(new Date(getParsedMessageTime()))));
272
}
273       }
274       else if(getHasBeenReceived())
275       {
276         if( this.getHasBeenDecrypted() )
277         {
278            rem.append( Language.translate("Message has been decrypted") );
279         }
280         //return "Message has been received";
281
//return "";
282
}
283       else
284       {
285         rem.append( Language.translate("Message has not been sent yet") );
286       }
287
288       String JavaDoc signatureVerifications = properties.getStringLCK("signatureVerifications","");
289       if(signatureVerifications.length()>0)
290       {
291          rem.append("\n"+signatureVerifications);
292       }
293
294       return rem.toString().trim();
295   }
296
297   /** set the actual date as the message date.
298       should be reset each time when the message is edited
299       and when it is send.
300   */

301   public void setActualDate()
302   {
303     header.setEntryOverwrite("date", MailMessageUtils.msgDateFormat(new Date()));
304     recomposeMessageFromParts();
305     this.notifyChangeListeners(MailMessageChangeListener.MailMessageChangeType.HEADER, "Date changed");
306   }
307
308   /** add a read and a delivery receipt request
309   */

310   public void setRequestNotification_(Address address)
311   {
312     // Read receipt
313
header.setEntryOverwrite("Disposition-Notification-To", address.toString());
314
315     // Delivery receipt
316
header.setEntryOverwrite("Return-Receipt-To", address.toString());
317     recomposeMessageFromParts();
318     this.notifyChangeListeners(MailMessageChangeListener.MailMessageChangeType.HEADER, "return address set to "+address);
319   }
320
321
322   public void setIsReplyMessage(String JavaDoc id_original)
323   {
324      header.setEntryOverwrite("In-Reply-To", id_original);
325      header.setEntryOverwrite("References", id_original);
326      this.notifyChangeListeners(MailMessageChangeListener.MailMessageChangeType.HEADER, "Reply to message id set = "+id_original);
327   }
328
329
330   /** this only sets the header.
331       called from the view when only the header is edited, to reflect the changes in the
332       table view without to have to recompose the message !
333   */

334   public void setMessageHeader(Address from, Vector<Address> tos, String JavaDoc subject)
335   {
336       String JavaDoc fromString = "";
337       if(from!=null)
338       {
339         fromString = from.toString();
340       }
341
342       header.setEntryOverwrite("From", fromString);
343       StringBuffer JavaDoc toBuffer = new StringBuffer JavaDoc();
344       if(tos!=null)
345       {
346         for(int i=0; i<tos.size(); i++)
347         {
348           toBuffer.append(tos.elementAt(i).getMailAddress());
349           if(i<tos.size()-1) toBuffer.append("; ");
350         }
351       }
352       header.setEntryOverwrite("To", toBuffer.toString());
353       header.setEntryOverwrite("Subject", subject);
354       header.setEntryOverwrite("Date", MailMessageUtils.msgDateFormat(new Date()));
355       properties.setLong("parsedMessageTime", new Date().getTime());
356
357       if(from!=null)
358       {
359         header.setEntryOverwrite("Message-ID", MailMessageUtils.createMessageID(from));
360       }
361       header.setEntryOverwrite("Reply-To", fromString);
362       header.setEntryOverwrite("Return-Path", fromString);
363
364       this.notifyChangeListeners(MailMessageChangeListener.MailMessageChangeType.HEADER, "header set");
365   }
366
367   /** Called from the editor.
368       Cause a recomposition of the body or a reparsing in the mime case.
369   */

370   public void setMessage(Address from, Vector<Address> tos, String JavaDoc subject, String JavaDoc body)
371   {
372     if(this.mimeTreeModel.isMimeMessageFormat())
373     {
374       mimeTreeModel.setMessagePlainText(from, tos, subject, body);
375       // ### this object header is just ignored !!!
376
// ### => getDate, from, to are to be reparsed...
377
completeContent = mimeTreeModel.createMessageStringContent();
378       this.parse(completeContent);
379     }
380     else
381     {
382       header.setEntryOverwrite("From", from.toString());
383       StringBuffer JavaDoc toBuffer = new StringBuffer JavaDoc();
384       for(int i=0; i<tos.size(); i++)
385       {
386         toBuffer.append(tos.elementAt(i).getMailAddress());
387         if(i<tos.size()-1) toBuffer.append("; ");
388       }
389       header.setEntryOverwrite("To", toBuffer.toString());
390       header.setEntryOverwrite("Subject", subject);
391       header.setEntryOverwrite("Date", MailMessageUtils.msgDateFormat(new Date()));
392
393       properties.setLong("parsedMessageTime", new Date().getTime());
394
395       header.setEntryOverwrite("Message-ID", MailMessageUtils.createMessageID(from));
396       header.setEntryOverwrite("MIME-Version", "1.0");
397
398
399
400       header.setEntryOverwrite("X-Mailer", "SnowMail 1.0");
401       header.setEntryOverwrite("Reply-To", from.toString());
402       header.setEntryOverwrite("Return-Path", from.toString());
403
404 /*
405       // not necessary
406       header.setEntryOverwrite("X-Priority", "3 (Normal)");
407       header.setEntryOverwrite("X-MSMail-Priority", "Normal");
408       header.setEntryOverwrite("Importance", "Normal");
409  */

410
411
412       // ### put in the UI
413
// this CAUSE A NOTIFICATION SENT for each received mail (if destinee client support&activated them)
414
setRequestNotification_(from);
415
416       // THE cencoding is only made at the transport level !!!
417

418       //this.messageBody = MailMessageUtils.encodeMessageQuotedPrintable(body, charset);
419
//header.setEntryOverwrite("Content-Type", "text/plain;\r\n\tcharset=\""+charset+"\"");
420
//header.setEntryOverwrite("Content-Transfer-Encoding", "8bit"); // 8bit
421

422       this.messageBody = body;
423
424       recomposeMessageFromParts();
425     }
426
427     this.notifyChangeListeners(MailMessageChangeListener.MailMessageChangeType.MESSAGE, "message set");
428   }
429
430
431   /** Call this when something changed, to save changes in the model.
432   */

433   private void recomposeMessageFromParts()
434   {
435       this.completeContent =
436            header.toString()
437          + "\r\n"
438          + messageBody;
439   }
440
441   public boolean isEditable(){ return properties.getBoolean("isEditable", false); }
442   public void setEditable(boolean is)
443   {
444     properties.setBoolean("isEditable", is);
445     this.notifyChangeListeners(MailMessageChangeListener.MailMessageChangeType.PROPERTY, "is editable="+is);
446   }
447
448
449   /** @return true if the text was found
450   */

451   public boolean searchText(String JavaDoc textUP)
452   {
453      MimePart[] partsFound = this.mimeTreeModel.searchInTextParts(textUP);
454      if(partsFound.length>0)
455      {
456        return true;
457      }
458
459      if(this.messageBody.toUpperCase().indexOf(textUP)>=0)
460      {
461        return true;
462      }
463
464      // not found
465
return false;
466   }
467
468   /** used to regenerate content
469   *
470   public String getHeaderText()
471   {
472      return header.toString();
473   }*/

474
475
476   /** @return the message body
477   */

478   public String JavaDoc getMessageBody()
479   {
480     return messageBody;
481   }
482
483   /** this is the complete content of the mail message (header + body)
484       WITHOUT charset conversions... as it was received !
485   */

486   public String JavaDoc getCompleteContentAsString()
487   {
488     return completeContent;
489   }
490
491
492
493   /** this is the complete content to send
494       it is encoded for sending
495       these bytes can be safely recomposed in string and decoded using us-ascii (7bits).
496       they can be sent as is !
497   */

498   public byte[] getCompleteContentToSend(GnuPGLink gpg, GnuPGKeyID signingKid, byte[] signingPass, Interrupter interrupter) throws Exception JavaDoc
499   {
500     ByteArrayOutputStream buffer = new ByteArrayOutputStream(1024);
501     if(this.getMimeTree().isMimeMessageFormat())
502     {
503       if(signingKid!=null)
504       {
505         throw new Exception JavaDoc("Signing is currently not supported for MIME messages");
506       }
507
508       buffer.write( mimeTreeModel.getContent_For_Sending() );
509
510       /*
511       buffer.write(this.header.to_ASCII_String().getBytes("us-ascii"));
512       buffer.write("\r\n".getBytes("us-ascii"));
513       // no charset, each part has already do the job
514       buffer.write(this.messageBody.getBytes("us-ascii"));
515       */

516
517     }
518     else
519     {
520
521       String JavaDoc encodingCharset = CharsetUtils.getMinimalEncodingCharset(messageBody);
522       byte[] messageBodyBytes = messageBody.getBytes(encodingCharset);
523       header.setEntryOverwrite("Content-Type", "text/plain;\r\n\tcharset=\""+encodingCharset+"\"");
524
525       if(signingKid!=null)
526       {
527         //
528
ByteArrayInputStream bin = new ByteArrayInputStream( messageBodyBytes.clone());
529         messageBodyBytes = gpg.sign(bin, signingKid, signingPass, interrupter);
530         setHasBeenSigned(true);
531       }
532
533       boolean useQuotedPrintable = !MailMessageUtils.is7Bits(messageBodyBytes);
534
535       if(useQuotedPrintable)
536       {
537         header.setEntryOverwrite("Content-Transfer-Encoding", "quoted-printable"); // 8bit
538
messageBodyBytes = MailMessageUtils.encodeMessageQuotedPrintable(messageBodyBytes);
539       }
540       else
541       {
542         header.setEntryOverwrite("Content-Transfer-Encoding", "8bit"); // 8bit
543
}
544
545       buffer.write(this.header.to_ASCII_String().getBytes("us-ascii"));
546       buffer.write("\r\n".getBytes("us-ascii"));
547       buffer.write(messageBodyBytes);
548     }
549     return buffer.toByteArray();
550   }
551
552   /** non empty if this is a mime tree
553   */

554   public MimeTreeModel getMimeTree()
555   {
556     return mimeTreeModel;
557   }
558
559   /** parse the header and (optional) the body and mime multiparts
560       called at the creating of the message from its vector representation and
561       when created the first time from the received content
562   */

563   private void parse(String JavaDoc mess, boolean parseBody)
564   {
565      NumberedLineReader reader = new NumberedLineReader( mess );
566
567      try
568      {
569         Header.parseHeader(reader, header);
570
571         if(parseBody)
572         {
573            // message content
574
StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
575            String JavaDoc fromLine = reader.readLine();
576            while(fromLine!=null)
577            {
578               sb.append(fromLine);
579               sb.append("\n");
580               fromLine = reader.readLine();
581            }
582
583            messageBody = sb.toString();
584
585            // decode the content (byte => string)
586
// ( charset, quoted-printable, base64, 8bit, ...)
587
messageBody = decodeContent(messageBody);
588
589            if(debug)
590            {
591              System.out.println("================ Body part ("+messageBody.length()+") chars");
592            }
593
594            if(MimeUtils.isMultipart(this))
595            {
596               mimeTreeModel.clearMimeTree(); // delete the whole tree...
597

598               MimePart rootPart = mimeTreeModel.getRootPart();
599               rootPart.debug = debug;
600               rootPart.parseMimeTree(
601                 new NumberedLineReader(mess),
602                 "#no#boundary%&/?", // [Jan2006]: not empty string ! because it will block when parsing
603
//mails whose full content is an attachment
604
false, 0);
605            }
606
607         }
608         reader.close();
609      }
610      catch(Exception JavaDoc e)
611      {
612
613         messageBody = mess;
614         error = e.getMessage();
615         System.out.println("Mail Parse Error: "+e.getMessage());
616         System.out.println(" from = "+this.getFromAddress());
617         System.out.println(" to = "+this.getToAddresses());
618         System.out.println(" subject = "+this.getSubject());
619
620         if(e.getMessage().length()==0)
621         {
622           // try to know a little bit more about the error
623
e.printStackTrace();
624           error = "Mail parse error";
625           //new Throwable("Mail parse error trace").printStackTrace(); // see the caller...
626
}
627         else
628         {
629           // DEBUG !
630
e.printStackTrace();
631         }
632      }
633
634      // post analysis
635
String JavaDoc dateString = this.getDateFieldFormHeader();
636      if(dateString.equals("?"))
637      {
638         // set the actual date
639
header.setEntryOverwrite("date", MailMessageUtils.msgDateFormat(new Date()));
640         recomposeMessageFromParts();
641      }
642
643      try
644      {
645        properties.setLong("parsedMessageTime",
646            MailMessageUtils.parseDateFromString(dateString).getTime());
647      }
648      catch(Exception JavaDoc e)
649      {
650        properties.setLong("parsedMessageTime", 0L);
651        if(!dateString.equals("?"))
652        {
653          System.out.println("Cannot parse date from '"+dateString+"'");
654        }
655      }
656   }
657
658   String JavaDoc error = null;
659   public String JavaDoc getErrorMessage()
660   {
661     return error;
662   }
663
664   public boolean hasParseError() { return error!=null; }
665
666
667
668
669   /** decodes the base64 and quoted printable formats
670   */

671   private String JavaDoc decodeContent(String JavaDoc text) throws Exception JavaDoc
672   {
673     String JavaDoc contentTransferEncoding = header.getEntryValue("content-transfer-encoding", "").toUpperCase();
674     String JavaDoc cs = HeaderUtils.getHeader_Charset_entry(header);
675     if(debug)
676     {
677       System.out.println("MailMessage decode content ct="+contentTransferEncoding+", cs="+cs);
678     }
679
680     if(contentTransferEncoding.indexOf("QUOTED-PRINTABLE")>=0)
681     {
682       return MailMessageUtils.decodeQuotedPrintable(text, cs);
683     }
684     else if(contentTransferEncoding.indexOf("8BIT")>=0)
685     {
686       try
687       {
688         // the format of the default encoded bytes at reception from server "utf-8"
689
//byte[] bytes = text.getBytes(); // use the same encoding ?
690

691         if(!CharsetUtils.isEncodingSupported(text,"iso-8859-1"))
692         {
693
694           if(debug)
695           {
696             System.out.print("===== Bad format iso-8859-1 for text =====");
697             int pos = CharsetUtils.getFirstEncodingError(text, "iso-8859-1");
698             if(pos>=0)
699             {
700               char errorChar = text.charAt(pos);
701               System.out.print(" char= ' "+errorChar+" ', code="+((int) errorChar));
702             }
703             System.out.println(", from= " + this.getFromAddress()+", subj= "+this.getSubject());
704
705             System.out.println(""+ text + "===== end =====");
706           }
707
708           //just return the text. It may already have been decoded !
709
return text;
710         }
711
712         byte[] bytes = text.getBytes("iso-8859-1"); // Since [June2005]
713
String JavaDoc dec = new String JavaDoc(bytes, cs);
714
715         return dec;
716
717       }
718       catch(Exception JavaDoc e)
719       {
720         return text;
721       }
722     }
723     else if(contentTransferEncoding.indexOf("BASE64")>=0)
724     {
725       try
726       {
727         return new String JavaDoc(Utilities.decodeBase64(text), cs);
728       }
729       catch(Exception JavaDoc e)
730       {
731         throw new Exception JavaDoc("Error decoding part base 64 content:\n "+e.getMessage());
732       }
733     }
734     else
735     {
736       return text;
737     }
738   }
739
740
741
742   /** this is used in CTRL+C or printing
743   */

744   public String JavaDoc getTextRepresentationForPrinting()
745   {
746     StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
747     sb.append("From: \t"+this.getFromAddress().toString());
748     sb.append("\nTo: \t"+this.getToAddressesText());
749     sb.append("\nSubject:\t"+this.getSubject());
750     sb.append("\nDate: \t"+this.getDateFieldFormHeader());
751     sb.append("\n\n");
752
753     if(this.getMimeTree().isMimeMessageFormat())
754     {
755       MimePart mp = getMimeTree().getFirstPlainTextPart();
756       if(mp!=null)
757       {
758         sb.append(""+mp.getBodyAsText());
759       }
760       else
761       {
762         sb.append(this.getMessageBody());
763       }
764     }
765     else
766     {
767       sb.append(this.getMessageBody());
768     }
769
770     return sb.toString();
771   }
772
773   //
774
// Listeners (normally only the parent folder is interrested in changes...)
775
//
776

777   public void addChangeListener(MailMessageChangeListener mcl)
778   {
779     this.changeListeners.add(mcl);
780   }
781
782   public void removeChangeListener(MailMessageChangeListener mcl)
783   {
784     this.changeListeners.remove(mcl);
785   }
786
787   private void notifyChangeListeners(MailMessageChangeListener.MailMessageChangeType type, String JavaDoc detail)
788   {
789      // do a copy of the listeners to decouple them from the vector
790
// because they may perform operation on the vector in the notify loop (remove listener)
791
// and caus concurrent modification exceptions
792
MailMessageChangeListener[] mcls = null;
793      synchronized(changeListeners)
794      {
795        mcls = changeListeners.toArray(new MailMessageChangeListener[changeListeners.size()]);
796      }
797      for(MailMessageChangeListener mcl : mcls)
798      {
799         mcl.mailMessageChanged(this, type, detail);
800      }
801   }
802
803   /** THIS IS CALLED FROM OUTSIDE to notify that this message is currently being edited.
804      For performance reason, the message is NOT actual, its new conetnt is only set later,
805      when the editor has another message or when save is called.
806
807      This is called at each key entered in MailView...
808   */

809   public void notifyThatThisMessageIsBeingEditedNow(String JavaDoc detail)
810   {
811      // do a copy of the listeners to decouple them from the vector
812
// because they may perform operation on the vector in the notify loop (remove listener)
813
// and caus concurrent modification exceptions
814
MailMessageChangeListener[] mcls = null;
815      synchronized(changeListeners)
816      {
817        mcls = changeListeners.toArray(new MailMessageChangeListener[changeListeners.size()]);
818      }
819      for(MailMessageChangeListener mcl : mcls)
820      {
821        mcl.mailMessageChanged(this, MailMessageChangeListener.MailMessageChangeType.IN_EDITION, detail);
822      }
823   }
824
825   // GPG operations
826
//
827

828
829   /** this reparse the decrypted message and set mark it as decrypted
830   */

831   public void setDecryptedMessage(String JavaDoc decryptedMessage)
832   {
833      this.parse(decryptedMessage);
834      properties.setBoolean( "HasBeenDecrypted", true);
835      this.notifyChangeListeners(MailMessageChangeListener.MailMessageChangeType.DECRYPTED, "decrypted message"); // for the mail view
836
this.notifyChangeListeners(MailMessageChangeListener.MailMessageChangeType.HEADER, "decrypted message"); // for the folder view
837
}
838
839   /** @return true if this message has been received encrypted and has been decrypted
840   */

841   public boolean getHasBeenDecrypted()
842   {
843      return properties.getBoolean( "HasBeenDecrypted", false);
844   }
845
846
847   public void setSignatureVerification(Vector<SignatureVerificationResult> res)
848   {
849      StringBuffer JavaDoc signaturesText = new StringBuffer JavaDoc();
850      if(res.size()==0)
851      {
852        signaturesText.append(Language.translate("NO SIGNATURES FOUND"));
853      }
854
855      for(SignatureVerificationResult sign: res)
856      {
857        signaturesText.append("\n"+sign.toString());
858      }
859      properties.setStringLCK("signatureVerifications", signaturesText.toString().trim());
860      this.notifyChangeListeners(MailMessageChangeListener.MailMessageChangeType.DECRYPTED, "signature verification"); // for the mail view
861
}
862
863
864   //
865
// Vectorizable
866
//
867

868   public MailMessage()
869   {
870   }
871
872   public Vector<Object JavaDoc> getVectorRepresentation() throws VectorizeException
873   {
874
875     Vector<Object JavaDoc> rep = new Vector<Object JavaDoc>();
876     rep.addElement(2);
877     rep.addElement(completeContent);
878     rep.addElement(properties.getVectorRepresentation());
879
880     return rep;
881   }
882
883   /** don't notify change listeners...
884   */

885 @SuppressWarnings JavaDoc("unchecked")
886   public void createFromVectorRepresentation(Vector<Object JavaDoc> v) throws VectorizeException
887   {
888     int version = (Integer JavaDoc) v.get(0);
889     if(version>=1)
890     {
891       completeContent = (String JavaDoc) v.get(1);
892       properties.createFromVectorRepresentation((Vector) v.get(2));
893       this.parse(completeContent, true);
894
895       if(version>=2)
896       {
897        // isNew = (Boolean) v.get(3);
898
}
899     }
900     else
901     {
902       throw new VectorizeException(Language.translate("bad version %",""+version));
903     }
904   }
905
906
907
908 } // MailMessage
Popular Tags