001: /*
002: * $Header: /cvsroot/mvnforum/myvietnam/src/net/myvietnam/mvncore/util/MailUtil.java,v 1.59 2008/01/24 08:40:51 tbtrung Exp $
003: * $Author: tbtrung $
004: * $Revision: 1.59 $
005: * $Date: 2008/01/24 08:40:51 $
006: *
007: * ====================================================================
008: *
009: * Copyright (C) 2002-2007 by MyVietnam.net
010: *
011: * All copyright notices regarding MyVietnam and MyVietnam CoreLib
012: * MUST remain intact in the scripts and source code.
013: *
014: * This library is free software; you can redistribute it and/or
015: * modify it under the terms of the GNU Lesser General Public
016: * License as published by the Free Software Foundation; either
017: * version 2.1 of the License, or (at your option) any later version.
018: *
019: * This library is distributed in the hope that it will be useful,
020: * but WITHOUT ANY WARRANTY; without even the implied warranty of
021: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
022: * Lesser General Public License for more details.
023: *
024: * You should have received a copy of the GNU Lesser General Public
025: * License along with this library; if not, write to the Free Software
026: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
027: *
028: * Correspondence and Marketing Questions can be sent to:
029: * info at MyVietnam net
030: *
031: * @author: Minh Nguyen
032: * @author: Mai Nguyen
033: */
034: package net.myvietnam.mvncore.util;
035:
036: import java.io.IOException;
037: import java.io.UnsupportedEncodingException;
038: import java.net.InetAddress;
039: import java.net.UnknownHostException;
040: import java.util.*;
041:
042: import javax.mail.*;
043: import javax.mail.Flags.Flag;
044: import javax.mail.internet.*;
045: import javax.naming.InitialContext;
046: import javax.naming.NamingException;
047:
048: import net.myvietnam.mvncore.MVNCoreConfig;
049: import net.myvietnam.mvncore.MVNCoreConfig.MailConfig;
050: import net.myvietnam.mvncore.exception.BadInputException;
051: import net.myvietnam.mvncore.filter.DisableHtmlTagFilter;
052:
053: import org.apache.commons.logging.Log;
054: import org.apache.commons.logging.LogFactory;
055: import org.masukomi.aspirin.core.MailQue;
056:
057: public final class MailUtil {
058:
059: public static final int MAX_MESSAGES_PER_TRANSPORT = 100;
060:
061: private static Log log = LogFactory.getLog(MailUtil.class);
062:
063: private MailUtil() {// prevent instantiation
064: }
065:
066: //private static MailOptions mailOption = new MailOptions();
067:
068: /**
069: * Get the user name part of an email. Ex: input: test@yahoo.com => output: test
070: * @param email String the email
071: * @return String the user name part of an email
072: */
073: public static String getEmailUsername(String email) {
074: if (email == null)
075: return "";
076: int atIndex = email.indexOf('@');
077: if (atIndex == -1) {
078: return "";
079: }
080: return email.substring(0, atIndex);
081: }
082:
083: /**
084: * Get the domain part of an email. Ex: input: test@yahoo.com => output: yahoo.com
085: * @param email String the email
086: * @return String the user name part of an email
087: */
088: public static String getEmailDomain(String email) {
089: if (email == null)
090: return "";
091: int atIndex = email.indexOf('@');
092: if (atIndex == -1) {
093: return "";
094: }
095: return email.substring(atIndex + 1);
096: }
097:
098: /**
099: * Check if an email is good and safe or not.
100: * This method should be use for all email input from user
101: * @param input String
102: * @throws BadInputException if email is not good
103: */
104: public static void checkGoodEmail(String input)
105: throws BadInputException {
106: if (input == null)
107: throw new BadInputException(
108: "Sorry, null string is not a good email.");//@todo : localize me
109: int atIndex = input.indexOf('@');
110: int dotIndex = input.lastIndexOf('.');
111: if ((atIndex == -1) || (dotIndex == -1)
112: || (atIndex >= dotIndex)) {
113: //@todo : localize me
114: throw new BadInputException("Error: '"
115: + DisableHtmlTagFilter.filter(input)
116: + "' is not a valid email value. Please try again.");
117: }
118:
119: // now check for content of the string
120: int length = input.length();
121: char c = 0;
122:
123: for (int i = 0; i < length; i++) {
124: c = input.charAt(i);
125: if ((c >= 'a') && (c <= 'z')) {
126: // lower char
127: } else if ((c >= 'A') && (c <= 'Z')) {
128: // upper char
129: } else if ((c >= '0') && (c <= '9')/* && (i != 0)*/) {
130: // as of 31 Jan 2004, i relax the email checking
131: // so that the email can start with an numeric char
132: // hopefully it does not introduce a security bug
133: // because this value will be inserted into sql script
134:
135: // numeric char
136: } else if (((c == '_') || (c == '-') || (c == '.') || (c == '@'))
137: && (i != 0)) {
138: // _ char
139: } else {
140: // not good char, throw an BadInputException
141: //@todo : localize me
142: throw new BadInputException(input
143: + " is not a valid email. Reason: character '"
144: + c + "' is not accepted in an email.");
145: }
146: }// for
147:
148: // last check
149: try {
150: new javax.mail.internet.InternetAddress(input);
151: } catch (Exception ex) {
152: log.error("Error when running checkGoodEmail", ex);
153: throw new BadInputException(
154: "Assertion: dont want to occur in Util.checkGoodEmail");
155: }
156: }
157:
158: /**
159: * NOTE: param to, cc, bcc cannot be all empty. At least one must have a valid value
160: * @param from : must be a valid email. However, if this param is null,
161: * then the default mail in config file will be use
162: * @param to : can be null
163: * @param cc : can be null
164: * @param bcc: can be null
165: * @param subject
166: * @param message
167: * @throws MessagingException
168: * @throws BadInputException
169: */
170: public static void sendMail(String from, String to, String cc,
171: String bcc, String subject, String message,
172: boolean sendAsHTML) throws MessagingException,
173: BadInputException, UnsupportedEncodingException {
174:
175: MailMessageStruct mailItem = new MailMessageStruct();
176: mailItem.setFrom(from);
177: mailItem.setTo(to);
178: mailItem.setCc(cc);
179: mailItem.setBcc(bcc);
180: mailItem.setSubject(subject);
181: mailItem.setMessage(message);
182: mailItem.setSendAsHtml(sendAsHTML);
183:
184: sendMail(mailItem);
185: }
186:
187: public static void sendMail(InternetAddress from, String to,
188: String cc, String bcc, String subject, String message,
189: boolean sendAsHTML) throws MessagingException,
190: BadInputException, UnsupportedEncodingException {
191:
192: MailMessageStruct mailItem = new MailMessageStruct();
193: mailItem.setFrom(from.getAddress());
194: mailItem.setFromDisplayName(from.getPersonal());
195: mailItem.setTo(to);
196: mailItem.setCc(cc);
197: mailItem.setBcc(bcc);
198: mailItem.setSubject(subject);
199: mailItem.setMessage(message);
200: mailItem.setSendAsHtml(sendAsHTML);
201:
202: sendMail(mailItem);
203: }
204:
205: public static void sendMail(MailMessageStruct mailItem)
206: throws MessagingException, BadInputException,
207: UnsupportedEncodingException {
208:
209: ArrayList mailList = new ArrayList(1);
210: mailList.add(mailItem);
211: try {
212: sendMail(mailList);
213: } catch (MessagingException mex) {
214: log.error("MessagingException has occured.", mex);
215: log.debug("MessagingException has occured. Detail info:");
216: log.debug("from = " + mailItem.getFrom());
217: log.debug("to = " + mailItem.getTo());
218: log.debug("cc = " + mailItem.getCc());
219: log.debug("bcc = " + mailItem.getBcc());
220: log.debug("subject = " + mailItem.getSubject());
221: log.debug("message = " + mailItem.getMessage());
222: throw mex;// this may look redundant, but it is not :-)
223: }
224: }
225:
226: public static void sendMail(Collection mailStructCollection)
227: throws MessagingException, BadInputException,
228: UnsupportedEncodingException {
229:
230: Session session = null;
231: Transport transport = null;
232: int totalEmails = mailStructCollection.size();
233: int count = 0;
234: int sendFailedExceptionCount = 0;
235:
236: String server = "";
237: String userName = "";
238: String password = "";
239: int port = 25;
240:
241: MailConfig config = MVNCoreConfig.getSendMailConfig();
242: boolean useMailsource = config.useMailSource();
243: boolean useEmbededMailServer = config
244: .useEmbededSMTPMailServer();
245:
246: try {
247: for (Iterator iter = mailStructCollection.iterator(); iter
248: .hasNext();) {
249: if ((transport == null) || (session == null)) {
250: if (useEmbededMailServer) {
251: session = Session
252: .getDefaultInstance(new Properties());
253: } else if (useMailsource) {
254: try {
255: InitialContext ic = new InitialContext();
256: // mailSourceName = "java:comp/env/mail/mailSession";
257: String mailSourceName = config
258: .getSourceName();
259: log.debug("MailUtil : use mailsource = "
260: + mailSourceName);
261: session = (Session) ic
262: .lookup("java:comp/env/"
263: + mailSourceName);
264: transport = session.getTransport("smtp");
265: } catch (NamingException e) {
266: log.error("Cannot get Mail session", e);
267: throw new MessagingException(
268: "Cannot get the mail session from JNDI. Send mail failed.");
269: }
270: } else {// does not use datasourse
271: Properties props = new Properties();
272:
273: server = config.getMailServer();
274: port = config.getPort();
275: userName = config.getUserName();
276: password = config.getPassword();
277:
278: // Local host name used in the SMTP HELO or EHLO command
279: try {
280: if (InetAddress.getLocalHost()
281: .getHostName() == null) {
282: props
283: .put("mail.smtp.localhost",
284: server);
285: }
286: } catch (UnknownHostException e) {
287: props.put("mail.smtp.localhost", server);
288: }
289: props.put("mail.smtp.host", server);
290: props.put("mail.smtp.port", String
291: .valueOf(port));
292: if ((userName != null)
293: && (userName.length() > 0)) {
294: props.put("mail.smtp.auth", "true");
295: }
296: props.put("mail.debug", "true");
297: session = Session.getDefaultInstance(props,
298: null);
299: transport = session.getTransport("smtp");
300: }// end of does not use datasource
301:
302: if (useEmbededMailServer == false) {
303: if ((userName != null)
304: && (userName.length() > 0)) {
305: transport.connect(server, userName,
306: password);
307: } else {
308: transport.connect();
309: }
310: }
311: }
312:
313: MailMessageStruct mailItem = (MailMessageStruct) iter
314: .next();
315:
316: String from = mailItem.getFrom();
317: String fromDisplayName = mailItem.getFromDisplayName();
318: String to = mailItem.getTo();
319: String cc = mailItem.getCc();
320: String bcc = mailItem.getBcc();
321: String subject = mailItem.getSubject();
322: String message = mailItem.getMessage();
323:
324: //if (from == null) from = mailOption.defaultMailFrom;
325: if (from == null)
326: from = MVNCoreConfig.getDefaultMailFrom();
327:
328: try {
329: // this will also check for email error
330: checkGoodEmail(from);
331: InternetAddress fromAddress = null;
332: if (fromDisplayName == null) {
333: fromAddress = new InternetAddress(from);
334: } else {
335: fromAddress = new InternetAddress(from,
336: fromDisplayName);
337: }
338: InternetAddress[] toAddress = getInternetAddressEmails(to);
339: InternetAddress[] ccAddress = getInternetAddressEmails(cc);
340: InternetAddress[] bccAddress = getInternetAddressEmails(bcc);
341: if ((toAddress == null) && (ccAddress == null)
342: && (bccAddress == null)) {
343: //@todo : localize me
344: throw new BadInputException(
345: "Cannot send mail since all To, Cc, Bcc addresses are empty.");
346: }
347:
348: // create a message
349: MimeMessage msg = new MimeMessage(session);
350: msg.setSentDate(new Date());
351: msg.setFrom(fromAddress);
352:
353: if (toAddress != null) {
354: msg.setRecipients(Message.RecipientType.TO,
355: toAddress);
356: }
357: if (ccAddress != null) {
358: msg.setRecipients(Message.RecipientType.CC,
359: ccAddress);
360: }
361: if (bccAddress != null) {
362: msg.setRecipients(Message.RecipientType.BCC,
363: bccAddress);
364: }
365:
366: if (mailItem.isSendAsHtml() == false) {
367: //This code is use to display unicode in Subject
368: //msg.setSubject(MimeUtility.encodeText(subject, "iso-8859-1", "Q"));
369: //msg.setText(message);
370: //String content = new String(message.getBytes(""), "UTF-8");
371: msg.setSubject(subject, "UTF-8");
372: msg.setText(message, "UTF-8");
373: } else {
374: //Below code is use for unicode
375: MimeBodyPart messageBodyPart = new MimeBodyPart();
376: msg.setSubject(subject, "UTF-8");
377: messageBodyPart.setText(message, "UTF-8");
378: messageBodyPart.setHeader("Content-Type",
379: "text/html;charset=UTF-8");
380: messageBodyPart.setHeader(
381: "Content-Transfer-Encoding",
382: "quoted-printable");
383: MimeMultipart multipart = new MimeMultipart(
384: "alternative");
385: multipart.addBodyPart(messageBodyPart);
386: msg.setContent(multipart);
387: }
388: msg.saveChanges();
389:
390: if (useEmbededMailServer) {
391: MailQue.queMail(msg);
392: } else {
393: transport.sendMessage(msg, msg
394: .getAllRecipients());
395: }
396:
397: if (useEmbededMailServer == false) {
398: // now check if sent 100 emails, then close connection (transport)
399: if (((count + 1) % MAX_MESSAGES_PER_TRANSPORT) == 0) {
400: try {
401: if (transport != null)
402: transport.close();
403: } catch (MessagingException ex) {
404: }
405: transport = null;
406: session = null;
407: }
408: }
409:
410: } catch (SendFailedException ex) {
411: sendFailedExceptionCount++;
412: log.error("SendFailedException has occured.", ex);
413: log
414: .warn("SendFailedException has occured. Detail info:");
415: log.warn("from = " + from);
416: log.warn("to = " + to);
417: log.warn("cc = " + cc);
418: log.warn("bcc = " + bcc);
419: log.warn("subject = " + subject);
420: log.info("message = " + message);
421: if ((totalEmails != 1)
422: && (sendFailedExceptionCount > 10)) {
423: throw ex;// this may look redundant, but it is not :-)
424: }
425: } catch (MessagingException mex) {
426: log.error("MessagingException has occured.", mex);
427: log
428: .warn("MessagingException has occured. Detail info:");
429: log.warn("from = " + from);
430: log.warn("to = " + to);
431: log.warn("cc = " + cc);
432: log.warn("bcc = " + bcc);
433: log.warn("subject = " + subject);
434: log.info("message = " + message);
435: throw mex;// this may look redundant, but it is not :-)
436: }
437:
438: // we increase count when send successfully
439: count++;
440: }//for
441: } finally {
442: try {
443: if (transport != null)
444: transport.close();
445: } catch (MessagingException ex) {
446: }
447:
448: if (totalEmails != 1) {
449: String sendMailMethod = "";
450: if (useEmbededMailServer) {
451: sendMailMethod = "Embeded SMTP Server";
452: } else if (useMailsource) {
453: sendMailMethod = "Mail Source";
454: } else {
455: sendMailMethod = "External SMTP Server";
456: }
457: log.info("sendMail: totalEmails = " + totalEmails
458: + " sent count = " + count + " using "
459: + sendMailMethod);
460: }
461: }
462: }
463:
464: /**
465: * This method trim the email variable, so if it contains only spaces,
466: * then it will be empty string, then we have 0 token :-)
467: * The returned value is never null
468: */
469: public static String[] getEmails(String email)
470: throws BadInputException {
471: if (email == null)
472: email = "";
473: email = email.trim();// very important
474: email = email.replace(',', ';');// replace all occurrence of ',' to ';'
475: StringTokenizer t = new StringTokenizer(email, ";");
476: String[] ret = new String[t.countTokens()];
477: int index = 0;
478: while (t.hasMoreTokens()) {
479: String mail = t.nextToken().trim();
480: checkGoodEmail(mail);
481: ret[index] = mail;
482: //log.debug(ret[index]);
483: index++;
484: }
485: return ret;
486: }
487:
488: public static String[] getEmails(String to, String cc, String bcc)
489: throws BadInputException {
490: String[] toMail = getEmails(to);
491: String[] ccMail = getEmails(cc);
492: String[] bccMail = getEmails(bcc);
493: String[] ret = new String[toMail.length + ccMail.length
494: + bccMail.length];
495: int index = 0;
496: for (int i = 0; i < toMail.length; i++) {
497: ret[index] = toMail[i];
498: index++;
499: }
500: for (int i = 0; i < ccMail.length; i++) {
501: ret[index] = ccMail[i];
502: index++;
503: }
504: for (int i = 0; i < bccMail.length; i++) {
505: ret[index] = bccMail[i];
506: index++;
507: }
508: return ret;
509: }
510:
511: /**
512: * This method will return null if there is not any email
513: *
514: * @param email
515: * @return
516: * @throws BadInputException
517: * @throws AddressException
518: */
519: private static InternetAddress[] getInternetAddressEmails(
520: String email) throws BadInputException, AddressException {
521: String[] mails = getEmails(email);
522: if (mails.length == 0)
523: return null;// must return null, not empty array
524:
525: //log.debug("to = " + mails);
526: InternetAddress[] address = new InternetAddress[mails.length];
527: for (int i = 0; i < mails.length; i++) {
528: address[i] = new InternetAddress(mails[i]);
529: //log.debug("to each element = " + mails[i]);
530: }
531: return address;
532: }
533:
534: public static MailMessageStruct[] receiveEmail(String popServer,
535: String popUser, String popPassword) {
536:
537: Store store = null;
538: Folder folder = null;
539: MailMessageStruct[] mailMessageStructsArray = null;
540:
541: try {
542: // -- Get hold of the default session --
543: Properties props = new Properties();
544: props.put("mail.pop3.port", String.valueOf(MVNCoreConfig
545: .getReceiveMailConfig().getPort()));
546: Session session = Session.getDefaultInstance(props, null);
547: session.setDebug(true);
548:
549: // -- Get hold of a POP3 message store, and connect to it --
550: store = session.getStore("pop3");
551: store.connect(popServer, popUser, popPassword);
552:
553: // -- Try to get hold of the default folder --
554: folder = store.getDefaultFolder();
555: if (folder == null) {
556: throw new IOException("No default folder");
557: }
558:
559: // -- ...and its INBOX --
560: folder = folder.getFolder("INBOX");
561: if (folder == null) {
562: throw new IOException("No POP3 INBOX");
563: }
564:
565: // -- Open the folder for read write --
566: folder.open(Folder.READ_WRITE);
567:
568: // -- Get the message wrappers and process them --
569: Message[] msgs = folder.getMessages();
570: mailMessageStructsArray = new MailMessageStruct[msgs.length];
571: for (int msgNum = 0; msgNum < msgs.length; msgNum++) {
572: mailMessageStructsArray[msgNum] = createMessageStructFromMessage(msgs[msgNum]);
573: msgs[msgNum].setFlag(Flag.DELETED, true);
574: }
575:
576: return mailMessageStructsArray;
577: } catch (Exception ex) {
578: log.error("Error when receiving email.", ex);
579: } finally {
580: if (folder != null) {
581: try {
582: folder.close(true);
583: } catch (MessagingException e) {
584: log.error("Cannot close POP Folder.", e);
585: }
586: }
587: if (store != null) {
588: try {
589: store.close();
590: } catch (MessagingException e) {
591: log.error("Cannot close POP Store.", e);
592: }
593: }
594: }
595: return new MailMessageStruct[0];
596: }
597:
598: private static MailMessageStruct createMessageStructFromMessage(
599: Message message) throws MessagingException, IOException {
600:
601: MailMessageStruct mailMessageStruct = new MailMessageStruct();
602: String fromEmailString = null;
603: Address[] from = message.getFrom();
604: if (from != null) {
605: Address fromEmail = from[0];
606: fromEmailString = ((InternetAddress) fromEmail)
607: .getAddress();
608: }
609:
610: String subject = message.getSubject();
611:
612: Part messagePart = message;
613: Object content = messagePart.getContent();
614:
615: // -- or its first body part if it is a multipart message --
616: if (content instanceof Multipart) {
617: messagePart = ((Multipart) content).getBodyPart(0);
618: }
619:
620: // -- Get the content type --
621: String contentType = (String) messagePart.getContent();
622:
623: mailMessageStruct.setFrom(fromEmailString);
624: mailMessageStruct.setSubject(subject);
625: mailMessageStruct.setMessage(contentType);
626:
627: return mailMessageStruct;
628: }
629: }
|