001: /**********************************************************************************
002: * $URL: https://source.sakaiproject.org/svn/email/tags/sakai_2-4-1/email-impl/impl/src/java/org/sakaiproject/email/impl/BasicEmailService.java $
003: * $Id: BasicEmailService.java 13897 2006-08-21 19:06:11Z ggolden@umich.edu $
004: ***********************************************************************************
005: *
006: * Copyright (c) 2003, 2004, 2005, 2006 The Sakai Foundation.
007: *
008: * Licensed under the Educational Community License, Version 1.0 (the "License");
009: * you may not use this file except in compliance with the License.
010: * You may obtain a copy of the License at
011: *
012: * http://www.opensource.org/licenses/ecl1.php
013: *
014: * Unless required by applicable law or agreed to in writing, software
015: * distributed under the License is distributed on an "AS IS" BASIS,
016: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: * See the License for the specific language governing permissions and
018: * limitations under the License.
019: *
020: **********************************************************************************/package org.sakaiproject.email.impl;
021:
022: import java.nio.charset.Charset;
023: import java.util.Collection;
024: import java.util.Date;
025: import java.util.Iterator;
026: import java.util.List;
027: import java.util.Properties;
028: import java.util.StringTokenizer;
029: import java.util.Vector;
030:
031: import javax.mail.Address;
032: import javax.mail.Message;
033: import javax.mail.MessagingException;
034: import javax.mail.SendFailedException;
035: import javax.mail.Session;
036: import javax.mail.Transport;
037: import javax.mail.internet.AddressException;
038: import javax.mail.internet.InternetAddress;
039: import javax.mail.internet.MimeMessage;
040:
041: import org.apache.commons.logging.Log;
042: import org.apache.commons.logging.LogFactory;
043: import org.sakaiproject.component.api.ServerConfigurationService;
044: import org.sakaiproject.email.api.EmailService;
045: import org.sakaiproject.user.api.User;
046:
047: /**
048: * <p>
049: * BasicEmailService implements the EmailService.
050: * </p>
051: */
052: public abstract class BasicEmailService implements EmailService {
053: /** Our logger. */
054: private static Log M_log = LogFactory
055: .getLog(BasicEmailService.class);
056:
057: protected static final String POSTMASTER = "postmaster";
058:
059: /** As defined in the com.sun.mail.smtp part of javamail. */
060:
061: /** The SMTP server to connect to. */
062: protected static final String SMTP_HOST = "mail.smtp.host";
063:
064: /** The SMTP server port to connect to, if the connect() method doesn't explicitly specify one. Defaults to 25. */
065: protected static final String SMTP_PORT = "mail.smtp.port";
066:
067: /** Email address to use for SMTP MAIL command. This sets the envelope return address. Defaults to msg.getFrom() or InternetAddress.getLocalAddress(). NOTE: mail.smtp.user was previously used for this. */
068: protected static final String SMTP_FROM = "mail.smtp.from";
069:
070: /**
071: * If set to true, and a message has some valid and some invalid addresses, send the message anyway, reporting the partial failure with a SendFailedException. If set to false (the default), the message is not sent to any of the recipients if there is
072: * an invalid recipient address.
073: */
074: protected static final String SMTP_SENDPARTIAL = "mail.smtp.sendpartial";
075:
076: protected static final String CONTENT_TYPE = "text/plain";
077:
078: /** Protocol name for smtp. */
079: protected static final String SMTP_PROTOCOL = "smtp";
080:
081: /**********************************************************************************************************************************************************************************************************************************************************
082: * Dependencies Note: keep these in sync with the TestEmailService, to make switching between them easier -ggolden
083: *********************************************************************************************************************************************************************************************************************************************************/
084:
085: /**
086: * @return the ServerConfigurationService collaborator.
087: */
088: protected abstract ServerConfigurationService serverConfigurationService();
089:
090: /**********************************************************************************************************************************************************************************************************************************************************
091: * Configuration Note: keep these in sync with the TestEmailService, to make switching between them easier -ggolden
092: *********************************************************************************************************************************************************************************************************************************************************/
093:
094: /** Configuration: smtp server to use. */
095: protected String m_smtp = null;
096:
097: /**
098: * Configuration: smtp server to use.
099: *
100: * @param value
101: * The smtp server string.
102: */
103: public void setSmtp(String value) {
104: m_smtp = value;
105: }
106:
107: /** Configuration: smtp server port to use. */
108: protected String m_smtpPort = null;
109:
110: /**
111: * Configuration: smtp server port to use.
112: *
113: * @param value
114: * The smtp server port string.
115: */
116: public void setSmtpPort(String value) {
117: m_smtpPort = value;
118: }
119:
120: /** Configuration: optional smtp mail envelope return address. */
121: protected String m_smtpFrom = null;
122:
123: /**
124: * Configuration: smtp mail envelope return address.
125: *
126: * @param value
127: * The smtp mail from address string.
128: */
129: public void setSmtpFrom(String value) {
130: m_smtpFrom = value;
131: }
132:
133: /** Configuration: set to go into test mode, where mail is not really sent out. */
134: protected boolean m_testMode = false;
135:
136: /**
137: * Configuration: set test mode.
138: *
139: * @param value
140: * The test mode value
141: */
142: public void setTestMode(boolean value) {
143: m_testMode = value;
144: }
145:
146: /** The max # recipients to include in each message. */
147: protected int m_maxRecipients = 100;
148:
149: /**
150: * Set max # recipients to include in each message.
151: *
152: * @param setting
153: * The max # recipients to include in each message. (as an integer string).
154: */
155: public void setMaxRecipients(String setting) {
156: m_maxRecipients = Integer.parseInt(setting);
157:
158: // validate - if invalid, restore to the default
159: if (m_maxRecipients < 1)
160: m_maxRecipients = 100;
161: }
162:
163: /** Configuration: use a connection to the SMTP for only one message (or not). */
164: protected boolean m_oneMessagePerConnection = false;
165:
166: /**
167: * Configuration: set use a connection to the SMTP for only one message (or not)
168: *
169: * @param value
170: * The setting
171: */
172: public void setOneMessagePerConnection(boolean value) {
173: m_oneMessagePerConnection = value;
174: }
175:
176: /**********************************************************************************************************************************************************************************************************************************************************
177: * Init and Destroy
178: *********************************************************************************************************************************************************************************************************************************************************/
179:
180: /**
181: * Final initialization, once all dependencies are set.
182: */
183: public void init() {
184: // if no m_mailfrom set, set to the postmaster
185: if (m_smtpFrom == null) {
186: m_smtpFrom = POSTMASTER + "@"
187: + serverConfigurationService().getServerName();
188: }
189:
190: // promote these to the system properties, to keep others (James) from messing with them
191: if (m_smtp != null)
192: System.setProperty(SMTP_HOST, m_smtp);
193: if (m_smtpPort != null)
194: System.setProperty(SMTP_PORT, m_smtpPort);
195: System.setProperty(SMTP_FROM, m_smtpFrom);
196:
197: M_log.info("init(): smtp: " + m_smtp
198: + ((m_smtpPort != null) ? (":" + m_smtpPort) : "")
199: + " bounces to: " + m_smtpFrom + " maxRecipients: "
200: + m_maxRecipients + " testMode: " + m_testMode);
201: }
202:
203: /**
204: * Final cleanup.
205: */
206: public void destroy() {
207: M_log.info("destroy()");
208: }
209:
210: /**********************************************************************************************************************************************************************************************************************************************************
211: * Work interface methods: org.sakai.service.email.EmailService
212: *********************************************************************************************************************************************************************************************************************************************************/
213:
214: /**
215: * {@inheritDoc}
216: */
217: public void sendMail(InternetAddress from, InternetAddress[] to,
218: String subject, String content, InternetAddress[] headerTo,
219: InternetAddress[] replyTo, List additionalHeaders) {
220: // some timing for debug
221: long start = 0;
222: if (M_log.isDebugEnabled())
223: start = System.currentTimeMillis();
224:
225: // if in test mode, use the test method
226: if (m_testMode) {
227: testSendMail(from, to, subject, content, headerTo, replyTo,
228: additionalHeaders);
229: return;
230: }
231:
232: if (m_smtp == null) {
233: M_log.warn("sendMail: smtp not set");
234: return;
235: }
236:
237: if (from == null) {
238: M_log.warn("sendMail: null from");
239: return;
240: }
241:
242: if (to == null) {
243: M_log.warn("sendMail: null to");
244: return;
245: }
246:
247: if (content == null) {
248: M_log.warn("sendMail: null content");
249: return;
250: }
251:
252: Properties props = new Properties();
253:
254: // set the server host
255: props.put(SMTP_HOST, m_smtp);
256:
257: // set the port, if specified
258: if (m_smtpPort != null) {
259: props.put(SMTP_PORT, m_smtpPort);
260: }
261:
262: // set the mail envelope return address
263: props.put(SMTP_FROM, m_smtpFrom);
264:
265: Session session = Session.getDefaultInstance(props, null);
266:
267: try {
268: // see if we have a message-id in the additional headers
269: String mid = null;
270: if (additionalHeaders != null) {
271: Iterator i = additionalHeaders.iterator();
272: while (i.hasNext()) {
273: String header = (String) i.next();
274: if (header.toLowerCase().startsWith("message-id: ")) {
275: mid = header.substring(12);
276: }
277: }
278: }
279:
280: // use the special extension that can set the id
281: MimeMessage msg = new MyMessage(session, mid);
282:
283: // the FULL content-type header, for example:
284: // Content-Type: text/plain; charset=windows-1252; format=flowed
285: String contentType = null;
286:
287: // the character set, for example, windows-1252 or UTF-8
288: String charset = null;
289:
290: // set the additional headers on the message
291: // but treat Content-Type specially as we need to check the charset
292: // and we already dealt with the message id
293: if (additionalHeaders != null) {
294: Iterator i = additionalHeaders.iterator();
295: while (i.hasNext()) {
296: String header = (String) i.next();
297: if (header.toLowerCase().startsWith(
298: "content-type: ")) {
299: contentType = header;
300: } else if (!header.toLowerCase().startsWith(
301: "message-id: ")) {
302: msg.addHeaderLine(header);
303: }
304: }
305: }
306:
307: // date
308: if (msg.getHeader("Date") == null) {
309: msg.setSentDate(new Date(System.currentTimeMillis()));
310: }
311:
312: msg.setFrom(from);
313:
314: if (msg.getHeader("To") == null) {
315: if (headerTo != null) {
316: msg.setRecipients(Message.RecipientType.TO,
317: headerTo);
318: }
319: }
320:
321: if ((subject != null) && (msg.getHeader("Subject") == null)) {
322: msg.setSubject(subject);
323: }
324:
325: if ((replyTo != null)
326: && (msg.getHeader("Reply-To") == null)) {
327: msg.setReplyTo(replyTo);
328: }
329:
330: // figure out what charset encoding to use
331: //
332: // first try to use the charset from the forwarded
333: // Content-Type header (if there is one).
334: // if that charset doesn't work, try a couple others.
335: if (contentType != null) {
336: // try and extract the charset from the Content-Type header
337: int charsetStart = contentType.toLowerCase().indexOf(
338: "charset=");
339: if (charsetStart != -1) {
340: int charsetEnd = contentType.indexOf(";",
341: charsetStart);
342: if (charsetEnd == -1)
343: charsetEnd = contentType.length();
344: charset = contentType.substring(
345: charsetStart + "charset=".length(),
346: charsetEnd).trim();
347: }
348: }
349:
350: if (charset != null && canUseCharset(content, charset)) {
351: // use the charset from the Content-Type header
352: } else if (canUseCharset(content, "ISO-8859-1")) {
353: if (contentType != null && charset != null)
354: contentType = contentType.replaceAll(charset,
355: "ISO-8859-1");
356: charset = "ISO-8859-1";
357: } else if (canUseCharset(content, "windows-1252")) {
358: if (contentType != null && charset != null)
359: contentType = contentType.replaceAll(charset,
360: "windows-1252");
361: charset = "windows-1252";
362: } else {
363: // catch-all - UTF-8 should be able to handle anything
364: if (contentType != null && charset != null)
365: contentType = contentType.replaceAll(charset,
366: "UTF-8");
367: charset = "UTF-8";
368: }
369:
370: // fill in the body of the message
371: msg.setText(content, charset);
372:
373: // if we have a full Content-Type header, set it NOW
374: // (after setting the body of the message so that format=flowed is preserved)
375: if (contentType != null) {
376: msg.addHeaderLine(contentType);
377: }
378:
379: long preSend = 0;
380: if (M_log.isDebugEnabled())
381: preSend = System.currentTimeMillis();
382:
383: Transport.send(msg, to);
384:
385: long end = 0;
386: if (M_log.isDebugEnabled())
387: end = System.currentTimeMillis();
388:
389: if (M_log.isInfoEnabled()) {
390: StringBuffer buf = new StringBuffer();
391: buf.append("Email.sendMail: from: ");
392: buf.append(from);
393: buf.append(" subject: ");
394: buf.append(subject);
395: buf.append(" to:");
396: for (int i = 0; i < to.length; i++) {
397: buf.append(" ");
398: buf.append(to[i]);
399: }
400: if (headerTo != null) {
401: buf.append(" headerTo:");
402: for (int i = 0; i < headerTo.length; i++) {
403: buf.append(" ");
404: buf.append(headerTo[i]);
405: }
406: }
407:
408: if (M_log.isDebugEnabled()) {
409: buf.append(" time: ");
410: buf.append("" + (end - start));
411: buf.append(" in send: ");
412: buf.append("" + (end - preSend));
413: }
414:
415: M_log.info(buf.toString());
416: }
417: } catch (MessagingException e) {
418: M_log.warn("Email.sendMail: exception: " + e, e);
419: }
420: }
421:
422: /**
423: * {@inheritDoc}
424: */
425: public void send(String fromStr, String toStr, String subject,
426: String content, String headerToStr, String replyToStr,
427: List additionalHeaders) {
428: // if in test mode, use the test method
429: if (m_testMode) {
430: testSend(fromStr, toStr, subject, content, headerToStr,
431: replyToStr, additionalHeaders);
432: return;
433: }
434:
435: if (fromStr == null) {
436: M_log.warn("send: null fromStr");
437: return;
438: }
439:
440: if (toStr == null) {
441: M_log.warn("send: null toStr");
442: return;
443: }
444:
445: if (content == null) {
446: M_log.warn("send: null content");
447: return;
448: }
449:
450: try {
451: InternetAddress from = new InternetAddress(fromStr);
452:
453: StringTokenizer tokens = new StringTokenizer(toStr, ", ");
454: InternetAddress[] to = new InternetAddress[tokens
455: .countTokens()];
456:
457: int i = 0;
458: while (tokens.hasMoreTokens()) {
459: String next = (String) tokens.nextToken();
460: to[i] = new InternetAddress(next);
461:
462: i++;
463: } // cycle through and collect all of the Internet addresses from the list.
464:
465: InternetAddress[] headerTo = null;
466: if (headerToStr != null) {
467: headerTo = new InternetAddress[1];
468: headerTo[0] = new InternetAddress(headerToStr);
469: }
470:
471: InternetAddress[] replyTo = null;
472: if (replyToStr != null) {
473: replyTo = new InternetAddress[1];
474: replyTo[0] = new InternetAddress(replyToStr);
475: }
476:
477: sendMail(from, to, subject, content, headerTo, replyTo,
478: additionalHeaders);
479:
480: } catch (AddressException e) {
481: M_log.warn("send: " + e);
482: }
483: }
484:
485: /**
486: * {@inheritDoc}
487: */
488: public void sendToUsers(Collection users, Collection headers,
489: String message) {
490: if (m_testMode) {
491: M_log.info("sendToUsers: users: " + usersToStr(users)
492: + " headers: " + listToStr(headers) + " message:\n"
493: + message);
494: return;
495: }
496:
497: // form the list of to: addresses from the users users collection
498: Vector addresses = new Vector();
499: for (Iterator i = users.iterator(); i.hasNext();) {
500: User user = (User) i.next();
501: String email = user.getEmail();
502: if ((email != null) && (email.length() > 0)) {
503: try {
504: addresses.add(new InternetAddress(email));
505: } catch (AddressException e) {
506: if (M_log.isDebugEnabled())
507: M_log.debug("sendToUsers: " + e);
508: }
509: }
510: }
511:
512: // if we have none
513: if (addresses.isEmpty())
514: return;
515:
516: // how many separate messages do we need to send to keep each one at or under m_maxRecipients?
517: int numMessageSets = ((addresses.size() - 1) / m_maxRecipients) + 1;
518:
519: // make an array for each and store them all in the collection
520: Collection messageSets = new Vector();
521: int posInAddresses = 0;
522: for (int i = 0; i < numMessageSets; i++) {
523: // all but the last one are max size
524: int this Size = m_maxRecipients;
525: if (i == numMessageSets - 1) {
526: this Size = addresses.size()
527: - ((numMessageSets - 1) * m_maxRecipients);
528: }
529:
530: // size an array
531: Address[] toAddresses = new Address[this Size];
532: messageSets.add(toAddresses);
533:
534: // fill the array
535: int posInToAddresses = 0;
536: while (posInToAddresses < this Size) {
537: toAddresses[posInToAddresses] = (Address) addresses
538: .elementAt(posInAddresses);
539: posInToAddresses++;
540: posInAddresses++;
541: }
542: }
543:
544: // get a session for our smtp setup, include host, port, reverse-path, and set partial delivery
545: Properties props = new Properties();
546: props.put(SMTP_HOST, m_smtp);
547: if (m_smtpPort != null)
548: props.put(SMTP_PORT, m_smtpPort);
549: props.put(SMTP_FROM, m_smtpFrom);
550: props.put(SMTP_SENDPARTIAL, "true");
551: Session session = Session.getInstance(props);
552:
553: // form our Message
554: MimeMessage msg = new MyMessage(session, headers, message);
555:
556: // transport the message
557: long time1 = 0;
558: long time2 = 0;
559: long time3 = 0;
560: long time4 = 0;
561: long time5 = 0;
562: long time6 = 0;
563: long timeExtraConnect = 0;
564: long timeExtraClose = 0;
565: long timeTmp = 0;
566: int numConnects = 1;
567: try {
568: if (M_log.isDebugEnabled())
569: time1 = System.currentTimeMillis();
570: Transport transport = session.getTransport(SMTP_PROTOCOL);
571:
572: if (M_log.isDebugEnabled())
573: time2 = System.currentTimeMillis();
574: msg.saveChanges();
575:
576: if (M_log.isDebugEnabled())
577: time3 = System.currentTimeMillis();
578: transport.connect();
579:
580: if (M_log.isDebugEnabled())
581: time4 = System.currentTimeMillis();
582:
583: // loop the send for each message set
584: for (Iterator i = messageSets.iterator(); i.hasNext();) {
585: Address[] toAddresses = (Address[]) i.next();
586:
587: try {
588: transport.sendMessage(msg, toAddresses);
589:
590: // if we need to use the connection for just one send, and we have more, close and re-open
591: if ((m_oneMessagePerConnection) && (i.hasNext())) {
592: if (M_log.isDebugEnabled())
593: timeTmp = System.currentTimeMillis();
594: transport.close();
595: if (M_log.isDebugEnabled())
596: timeExtraClose += (System
597: .currentTimeMillis() - timeTmp);
598:
599: if (M_log.isDebugEnabled())
600: timeTmp = System.currentTimeMillis();
601: transport.connect();
602: if (M_log.isDebugEnabled()) {
603: timeExtraConnect += (System
604: .currentTimeMillis() - timeTmp);
605: numConnects++;
606: }
607: }
608: } catch (SendFailedException e) {
609: if (M_log.isDebugEnabled())
610: M_log.debug("sendToUsers: " + e);
611: } catch (MessagingException e) {
612: M_log.warn("sendToUsers: " + e);
613: }
614: }
615:
616: if (M_log.isDebugEnabled())
617: time5 = System.currentTimeMillis();
618: transport.close();
619:
620: if (M_log.isDebugEnabled())
621: time6 = System.currentTimeMillis();
622: } catch (MessagingException e) {
623: M_log.warn("sendToUsers:" + e);
624: }
625:
626: // log
627: if (M_log.isInfoEnabled()) {
628: StringBuffer buf = new StringBuffer();
629: buf.append("sendToUsers: headers[");
630: for (Iterator i = headers.iterator(); i.hasNext();) {
631: String header = (String) i.next();
632: buf.append(" ");
633: buf.append(cleanUp(header));
634: }
635: buf.append("]");
636: for (Iterator i = messageSets.iterator(); i.hasNext();) {
637: Address[] toAddresses = (Address[]) i.next();
638: buf.append(" to[ ");
639: for (int a = 0; a < toAddresses.length; a++) {
640: buf.append(" ");
641: buf.append(toAddresses[a]);
642: }
643: buf.append("]");
644: }
645:
646: if (M_log.isDebugEnabled()) {
647: buf.append(" times[ ");
648: buf
649: .append(" getransport:"
650: + (time2 - time1)
651: + " savechanges:"
652: + (time3 - time2)
653: + " connect(#"
654: + numConnects
655: + "):"
656: + ((time4 - time3) + timeExtraConnect)
657: + " send:"
658: + (((time5 - time4) - timeExtraConnect) - timeExtraClose)
659: + " close:"
660: + ((time6 - time5) + timeExtraClose)
661: + " total: " + (time6 - time1) + " ]");
662: }
663:
664: M_log.info(buf.toString());
665: }
666: }
667:
668: protected String cleanUp(String str) {
669: StringBuffer buf = new StringBuffer(str);
670: for (int i = 0; i < buf.length(); i++) {
671: if (buf.charAt(i) == '\n' || buf.charAt(i) == '\r')
672: buf.replace(i, i + 1, " ");
673: }
674:
675: return buf.toString();
676: }
677:
678: protected String listToStr(Collection list) {
679: if (list == null)
680: return "";
681: return arrayToStr(list.toArray());
682: }
683:
684: protected String arrayToStr(Object[] array) {
685: StringBuffer buf = new StringBuffer();
686: if (array != null) {
687: buf.append("[");
688: for (int i = 0; i < array.length; i++) {
689: if (i != 0)
690: buf.append(", ");
691: buf.append(array[i].toString());
692: }
693: buf.append("]");
694: } else {
695: buf.append("");
696: }
697:
698: return buf.toString();
699: }
700:
701: protected String usersToStr(Collection users) {
702: StringBuffer buf = new StringBuffer();
703: buf.append("[");
704: if (users != null) {
705: for (Iterator i = users.iterator(); i.hasNext();) {
706: User user = (User) i.next();
707: buf.append(user.getDisplayName() + "<"
708: + user.getEmail() + "> ");
709: }
710: }
711:
712: buf.append("]");
713:
714: return buf.toString();
715: }
716:
717: /**
718: * test version of sendMail
719: */
720: protected void testSendMail(InternetAddress from,
721: InternetAddress[] to, String subject, String content,
722: InternetAddress[] headerTo, InternetAddress[] replyTo,
723: List additionalHeaders) {
724: M_log
725: .info("sendMail: from: " + from + " to: "
726: + arrayToStr(to) + " subject: " + subject
727: + " headerTo: " + arrayToStr(headerTo)
728: + " replyTo: " + arrayToStr(replyTo)
729: + " content: " + content
730: + " additionalHeaders: "
731: + listToStr(additionalHeaders));
732: }
733:
734: /**
735: * test version of send
736: */
737: protected void testSend(String fromStr, String toStr,
738: String subject, String content, String headerToStr,
739: String replyToStr, List additionalHeaders) {
740: M_log
741: .info("send: from: " + fromStr + " to: " + toStr
742: + " subject: " + subject + " headerTo: "
743: + headerToStr + " replyTo: " + replyToStr
744: + " content: " + content
745: + " additionalHeaders: "
746: + listToStr(additionalHeaders));
747: }
748:
749: /** Returns true if the given content String can be encoded in the given charset */
750: protected static boolean canUseCharset(String content,
751: String charsetName) {
752: try {
753: return Charset.forName(charsetName).newEncoder().canEncode(
754: content);
755: } catch (Exception e) {
756: return false;
757: }
758: }
759:
760: // inspired by http://java.sun.com/products/javamail/FAQ.html#msgid
761: protected class MyMessage extends MimeMessage {
762: protected String m_id = null;
763:
764: public MyMessage(Session session, String id) {
765: super (session);
766: m_id = id;
767: }
768:
769: public MyMessage(Session session, Collection headers,
770: String message) {
771: super (session);
772:
773: try {
774: // the FULL content-type header, for example: Content-Type: text/plain; charset=windows-1252; format=flowed
775: String contentType = null;
776:
777: // see if we have a message-id: in the headers, or content-type:, otherwise move the headers into the message
778: if (headers != null) {
779: Iterator i = headers.iterator();
780: while (i.hasNext()) {
781: String header = (String) i.next();
782:
783: if (header.toLowerCase().startsWith(
784: "message-id: ")) {
785: m_id = header.substring(12);
786: }
787:
788: else if (header.toLowerCase().startsWith(
789: "content-type: ")) {
790: contentType = header;
791: }
792:
793: else {
794: try {
795: addHeaderLine(header);
796: } catch (MessagingException e) {
797:
798: }
799: }
800: }
801: }
802:
803: // make sure we have a date, use now if needed
804: if (getHeader("Date") == null) {
805: setSentDate(new Date(System.currentTimeMillis()));
806: }
807:
808: // figure out what charset encoding to use
809: // the character set, for example, windows-1252 or UTF-8
810: String charset = null;
811:
812: // first try to use the charset from the forwarded Content-Type header (if there is one).
813: // if that charset doesn't work, try a couple others.
814: if (contentType != null) {
815: // try and extract the charset from the Content-Type header
816: int charsetStart = contentType.toLowerCase()
817: .indexOf("charset=");
818: if (charsetStart != -1) {
819: int charsetEnd = contentType.indexOf(";",
820: charsetStart);
821: if (charsetEnd == -1)
822: charsetEnd = contentType.length();
823: charset = contentType.substring(
824: charsetStart + "charset=".length(),
825: charsetEnd).trim();
826: }
827: }
828:
829: if (charset != null && canUseCharset(message, charset)) {
830: // use the charset from the Content-Type header
831: } else if (canUseCharset(message, "ISO-8859-1")) {
832: if (contentType != null && charset != null)
833: contentType = contentType.replaceAll(charset,
834: "ISO-8859-1");
835: charset = "ISO-8859-1";
836: } else if (canUseCharset(message, "windows-1252")) {
837: if (contentType != null && charset != null)
838: contentType = contentType.replaceAll(charset,
839: "windows-1252");
840: charset = "windows-1252";
841: } else {
842: // catch-all - UTF-8 should be able to handle anything
843: if (contentType != null && charset != null)
844: contentType = contentType.replaceAll(charset,
845: "UTF-8");
846: charset = "UTF-8";
847: }
848:
849: // fill in the body of the message
850: setText(message, charset);
851:
852: // if we have a full Content-Type header, set it NOW (after setting the body of the message so that format=flowed is preserved)
853: if (contentType != null) {
854: addHeaderLine(contentType);
855: }
856: } catch (MessagingException e) {
857:
858: }
859: }
860:
861: protected void updateHeaders() throws MessagingException {
862: super .updateHeaders();
863: if (m_id != null) {
864: setHeader("Message-Id", m_id);
865: }
866: }
867: }
868: }
|