Java tutorial
/* * eXist Mail Module Extension SendEmailFunction * Copyright (C) 2006-09 Adam Retter <adam@exist-db.org> * www.exist-db.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * $Id$ */ package org.exist.xquery.modules.mail; import java.io.*; import java.net.InetAddress; import java.net.Socket; import java.util.*; import javax.activation.DataHandler; import javax.mail.*; import javax.mail.internet.*; import javax.mail.util.ByteArrayDataSource; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.apache.commons.lang3.StringUtils; import org.apache.log4j.Logger; import org.exist.dom.QName; import org.exist.util.Base64Encoder; import org.exist.util.MimeTable; import org.exist.xquery.*; import org.exist.xquery.functions.system.GetVersion; import org.exist.xquery.value.*; import org.w3c.dom.Element; import org.w3c.dom.Node; //send-email specific imports /** * eXist Mail Module Extension SendEmailFunction * * The email sending functionality of the eXist Mail Module Extension that * allows email to be sent from XQuery using either SMTP or Sendmail. * * @author Adam Retter <adam@exist-db.org> * @author Robert Walpole <robert.walpole@devon.gov.uk> * @author Andrzej Taramina <andrzej@chaeron.com> * @author Jos Mara Fernndez <josemariafg@gmail.com> * @serial 2011-08-02 * @version 1.6 * * @see org.exist.xquery.BasicFunction#BasicFunction(org.exist.xquery.XQueryContext, org.exist.xquery.FunctionSignature) */ /* TODO according to RFC 821, SMTP commands must end with <CR><LF>, Java uses platform native end-of-line characters for .println(...) functions and so this function may have issues on non-Windows platforms */ public class SendEmailFunction extends BasicFunction { protected static final Logger logger = Logger.getLogger(SendEmailFunction.class); private final int MIME_BASE64_MAX_LINE_LENGTH = 76; //RFC 2045, page 24 private String charset; public final static FunctionSignature deprecated = new FunctionSignature( new QName("send-email", MailModule.NAMESPACE_URI, MailModule.PREFIX), "Sends an email through the SMTP Server.", new SequenceType[] { new FunctionParameterSequenceType("email", Type.ELEMENT, Cardinality.ONE_OR_MORE, "The email message in the following format: <mail> <from/> <reply-to/> <to/> <cc/> <bcc/> <subject/> <message> <text/> <xhtml/> </message> <attachment filename=\"\" mimetype=\"\">xs:base64Binary</attachment> </mail>."), new FunctionParameterSequenceType("server", Type.STRING, Cardinality.ZERO_OR_ONE, "The SMTP server. If empty, then it tries to use the local sendmail program."), new FunctionParameterSequenceType("charset", Type.STRING, Cardinality.ZERO_OR_ONE, "The charset value used in the \"Content-Type\" message header (Defaults to UTF-8)") }, new FunctionReturnSequenceType(Type.BOOLEAN, Cardinality.ONE_OR_MORE, "true if the email message was successfully sent")); public final static FunctionSignature signatures[] = { new FunctionSignature( new QName("send-email", MailModule.NAMESPACE_URI, MailModule.PREFIX), "Sends an email using javax.mail messaging libraries.", new SequenceType[] { new FunctionParameterSequenceType("mail-handle", Type.LONG, Cardinality.EXACTLY_ONE, "The JavaMail session handle retrieved from mail:get-mail-session()"), new FunctionParameterSequenceType("email", Type.ELEMENT, Cardinality.ONE_OR_MORE, "The email message in the following format: <mail> <from/> <reply-to/> <to/> <cc/> <bcc/> <subject/> <message> <text/> <xhtml/> </message> <attachment filename=\"\" mimetype=\"\">xs:base64Binary</attachment> </mail>.") }, new SequenceType(Type.ITEM, Cardinality.EMPTY)) }; /** * SendEmail Constructor * * @param context The Context of the calling XQuery */ public SendEmailFunction(XQueryContext context, FunctionSignature signature) { super(context, signature); } /** * evaluate the call to the xquery send-email function, * it is really the main entry point of this class * * @param args arguments from the send-email() function call * @param contextSequence the Context Sequence to operate on (not used here internally!) * @return A sequence representing the result of the send-email() function call * * @see org.exist.xquery.BasicFunction#eval(org.exist.xquery.value.Sequence[], org.exist.xquery.value.Sequence) */ public Sequence eval(Sequence[] args, Sequence contextSequence) throws XPathException { if (args.length == 3) { return deprecatedSendEmail(args, contextSequence); } else { return sendEmail(args, contextSequence); } } public Sequence sendEmail(Sequence[] args, Sequence contextSequence) throws XPathException { // was a session handle specified? if (args[0].isEmpty()) { throw (new XPathException(this, "Session handle not specified")); } // get the Session long sessionHandle = ((IntegerValue) args[0].itemAt(0)).getLong(); Session session = MailModule.retrieveSession(context, sessionHandle); if (session == null) { throw (new XPathException(this, "Invalid Session handle specified")); } try { List<Message> messages = parseInputEmails(session, args[1]); String proto = session.getProperty("mail.transport.protocol"); if (proto == null) proto = "smtp"; Transport t = session.getTransport(proto); try { if (session.getProperty("mail." + proto + ".auth") != null) t.connect(session.getProperty("mail." + proto + ".user"), session.getProperty("mail." + proto + ".password")); for (Message msg : messages) { t.sendMessage(msg, msg.getAllRecipients()); } } finally { t.close(); } return (Sequence.EMPTY_SEQUENCE); } catch (TransformerException te) { throw new XPathException(this, "Could not Transform XHTML Message Body: " + te.getMessage(), te); } catch (MessagingException smtpe) { throw new XPathException(this, "Could not send message(s): " + smtpe.getMessage(), smtpe); } catch (IOException ioe) { throw new XPathException(this, "Attachment in some message could not be prepared: " + ioe.getMessage(), ioe); } catch (Throwable t) { throw new XPathException(this, "Unexpected error from JavaMail layer (Is your message well structured?): " + t.getMessage(), t); } } public Sequence deprecatedSendEmail(Sequence[] args, Sequence contextSequence) throws XPathException { try { //get the charset parameter, default to UTF-8 if (!args[2].isEmpty()) { charset = args[2].getStringValue(); } else { charset = "UTF-8"; } //Parse the XML <mail> into a mail Object List<Element> mailElements = new ArrayList<Element>(); if (args[0].getItemCount() > 1 && args[0] instanceof ValueSequence) { for (int i = 0; i < args[0].getItemCount(); i++) { mailElements.add((Element) args[0].itemAt(i)); } } else { mailElements.add((Element) args[0].itemAt(0)); } List<Mail> mails = parseMailElement(mailElements); ValueSequence results = new ValueSequence(); //Send email with Sendmail or SMTP? if (!args[1].isEmpty()) { //SMTP List<Boolean> mailResults = sendBySMTP(mails, args[1].getStringValue()); for (Boolean mailResult : mailResults) { results.add(BooleanValue.valueOf(mailResult)); } } else { for (Mail mail : mails) { boolean result = sendBySendmail(mail); results.add(BooleanValue.valueOf(result)); } } return results; } catch (TransformerException te) { throw new XPathException(this, "Could not Transform XHTML Message Body: " + te.getMessage(), te); } catch (SMTPException smtpe) { throw new XPathException(this, "Could not send message(s)" + smtpe.getMessage(), smtpe); } } private List<Message> parseInputEmails(Session session, Sequence arg) throws IOException, MessagingException, TransformerException { //Parse the XML <mail> into a mail Object List<Element> mailElements = new ArrayList<Element>(); if (arg.getItemCount() > 1 && arg instanceof ValueSequence) { for (int i = 0; i < arg.getItemCount(); i++) { mailElements.add((Element) arg.itemAt(i)); } } else { mailElements.add((Element) arg.itemAt(0)); } return parseMessageElement(session, mailElements); } /** * Sends an email using the Operating Systems sendmail application * * @param mail representation of the email to send * @return boolean value of true of false indicating success or failure to send email */ private boolean sendBySendmail(Mail mail) { PrintWriter out = null; try { //Create a list of all Recipients, should include to, cc and bcc recipient List<String> allrecipients = new ArrayList<String>(); allrecipients.addAll(mail.getTo()); allrecipients.addAll(mail.getCC()); allrecipients.addAll(mail.getBCC()); //Get a string of all recipients email addresses String recipients = ""; for (int x = 0; x < allrecipients.size(); x++) { //Check format of to address does it include a name as well as the email address? if (((String) allrecipients.get(x)).indexOf("<") != -1) { //yes, just add the email address recipients += " " + ((String) allrecipients.get(x)).substring( ((String) allrecipients.get(x)).indexOf("<") + 1, ((String) allrecipients.get(x)).indexOf(">")); } else { //add the email address recipients += " " + ((String) allrecipients.get(x)); } } //Create a sendmail Process Process p = Runtime.getRuntime().exec("/usr/sbin/sendmail" + recipients); //Get a Buffered Print Writer to the Processes stdOut out = new PrintWriter(new OutputStreamWriter(p.getOutputStream(), charset)); //Send the Message writeMessage(out, mail); } catch (IOException e) { LOG.error(e.getMessage(), e); return false; } finally { //Close the stdOut if (out != null) out.close(); } //Message Sent Succesfully LOG.info("send-email() message sent using Sendmail " + new Date()); return true; } private class SMTPException extends Exception { private static final long serialVersionUID = 4859093648476395159L; public SMTPException(String message) { super(message); } public SMTPException(Throwable cause) { super(cause); } } /** * Sends an email using SMTP * * @param mail A mail object representing the email to send * @param SMTPServer The SMTP Server to send the email through * @return boolean value of true of false indicating success or failure to send email */ private List<Boolean> sendBySMTP(List<Mail> mails, String SMTPServer) throws SMTPException { final int TCP_PROTOCOL_SMTP = 25; //SMTP Protocol String smtpResult = ""; //Holds the server Result code when an SMTP Command is executed Socket smtpSock = null; BufferedReader smtpIn = null; PrintWriter smtpOut = null; List<Boolean> sendMailResults = new ArrayList<Boolean>(); try { //Create a Socket and connect to the SMTP Server smtpSock = new Socket(SMTPServer, TCP_PROTOCOL_SMTP); //Create a Buffered Reader for the Socket smtpIn = new BufferedReader(new InputStreamReader(smtpSock.getInputStream())); //Create an Output Writer for the Socket smtpOut = new PrintWriter(new OutputStreamWriter(smtpSock.getOutputStream(), charset)); //First line sent to us from the SMTP server should be "220 blah blah", 220 indicates okay smtpResult = smtpIn.readLine(); if (!smtpResult.substring(0, 3).equals("220")) { String errMsg = "Error - SMTP Server not ready: '" + smtpResult + "'"; LOG.error(errMsg); throw new SMTPException(errMsg); } //Say "HELO" smtpOut.println("HELO " + InetAddress.getLocalHost().getHostName()); smtpOut.flush(); //get "HELLO" response, should be "250 blah blah" smtpResult = smtpIn.readLine(); if (!smtpResult.substring(0, 3).equals("250")) { String errMsg = "Error - SMTP HELO Failed: '" + smtpResult + "'"; LOG.error(errMsg); throw new SMTPException(errMsg); } //write SMTP message(s) for (Mail mail : mails) { boolean mailResult = writeSMTPMessage(mail, smtpOut, smtpIn); sendMailResults.add(mailResult); } } catch (IOException ioe) { LOG.error(ioe.getMessage(), ioe); throw new SMTPException(ioe); } finally { try { if (smtpOut != null) smtpOut.close(); if (smtpIn != null) smtpIn.close(); if (smtpSock != null) smtpSock.close(); } catch (IOException ioe) { LOG.warn(ioe.getMessage(), ioe); } } //Message(s) Sent Succesfully LOG.info("send-email() message(s) sent using SMTP " + new Date()); return sendMailResults; } private boolean writeSMTPMessage(Mail mail, PrintWriter smtpOut, BufferedReader smtpIn) { try { String smtpResult = ""; //Send "MAIL FROM:" //Check format of from address does it include a name as well as the email address? if (mail.getFrom().indexOf("<") != -1) { //yes, just send the email address smtpOut.println("MAIL FROM:<" + mail.getFrom().substring(mail.getFrom().indexOf("<") + 1, mail.getFrom().indexOf(">")) + ">"); } else { //no, doesnt include a name so send the email address smtpOut.println("MAIL FROM:<" + mail.getFrom() + ">"); } smtpOut.flush(); //Get "MAIL FROM:" response smtpResult = smtpIn.readLine(); if (!smtpResult.substring(0, 3).equals("250")) { LOG.error("Error - SMTP MAIL FROM failed: " + smtpResult); return false; } //RCPT TO should be issued for each to, cc and bcc recipient List<String> allrecipients = new ArrayList<>(); allrecipients.addAll(mail.getTo()); allrecipients.addAll(mail.getCC()); allrecipients.addAll(mail.getBCC()); for (String recipient : allrecipients) { //Send "RCPT TO:" //Check format of to address does it include a name as well as the email address? if (((String) recipient).contains("<")) { //yes, just send the email address smtpOut.println("RCPT TO:<" + ((String) recipient).substring( ((String) recipient).indexOf("<") + 1, ((String) recipient).indexOf(">")) + ">"); } else { smtpOut.println("RCPT TO:<" + ((String) recipient) + ">"); } smtpOut.flush(); //Get "RCPT TO:" response smtpResult = smtpIn.readLine(); if (!smtpResult.substring(0, 3).equals("250")) { LOG.error("Error - SMTP RCPT TO failed: " + smtpResult); } } //SEND "DATA" smtpOut.println("DATA"); smtpOut.flush(); //Get "DATA" response, should be "354 blah blah" smtpResult = smtpIn.readLine(); if (!smtpResult.substring(0, 3).equals("354")) { LOG.error("Error - SMTP DATA failed: " + smtpResult); return false; } //Send the Message writeMessage(smtpOut, mail); //Get end message response, should be "250 blah blah" smtpResult = smtpIn.readLine(); if (!smtpResult.substring(0, 3).equals("250")) { LOG.error("Error - Message not accepted: " + smtpResult); return false; } } catch (IOException ioe) { LOG.error(ioe.getMessage(), ioe); return false; } return true; } /** * Writes an email payload (Headers + Body) from a mail object * * @param smtpOut A PrintWriter to receive the email * @param mail A mail object representing the email to write out */ private void writeMessage(PrintWriter out, Mail aMail) throws IOException { String Version = eXistVersion(); //Version of eXist String MultipartBoundary = "eXist.multipart." + Version; //Multipart Boundary //write the message headers out.println("From: " + encode64Address(aMail.getFrom())); if (aMail.getReplyTo() != null) { out.println("Reply-To: " + encode64Address(aMail.getReplyTo())); } for (int x = 0; x < aMail.countTo(); x++) { out.println("To: " + encode64Address(aMail.getTo(x))); } for (int x = 0; x < aMail.countCC(); x++) { out.println("CC: " + encode64Address(aMail.getCC(x))); } for (int x = 0; x < aMail.countBCC(); x++) { out.println("BCC: " + encode64Address(aMail.getBCC(x))); } out.println("Date: " + getDateRFC822()); out.println("Subject: " + encode64(aMail.getSubject())); out.println("X-Mailer: eXist " + Version + " mail:send-email()"); out.println("MIME-Version: 1.0"); boolean multipartAlternative = false; String multipartBoundary = null; if (aMail.attachmentIterator().hasNext()) { // we have an attachment as well as text and/or html so we need a multipart/mixed message multipartBoundary = MultipartBoundary; } else if (!aMail.getText().equals("") && !aMail.getXHTML().equals("")) { // we have text and html so we need a multipart/alternative message and no attachment multipartAlternative = true; multipartBoundary = MultipartBoundary + "_alt"; } else { // we have either text or html and no attachment this message is not multipart } //content type if (multipartBoundary != null) { //multipart message out.println("Content-Type: " + (multipartAlternative ? "multipart/alternative" : "multipart/mixed") + "; boundary=\"" + multipartBoundary + "\";"); //Mime warning out.println(); out.println("Error your mail client is not MIME Compatible"); out.println("--" + multipartBoundary); } // TODO - need to put out a multipart/mixed boundary here when HTML, text and attachment present if (!aMail.getText().equals("") && !aMail.getXHTML().equals("") && aMail.attachmentIterator().hasNext()) { out.println("Content-Type: multipart/alternative; boundary=\"" + MultipartBoundary + "_alt\";"); out.println("--" + MultipartBoundary + "_alt"); } //text email if (!aMail.getText().equals("")) { out.println("Content-Type: text/plain; charset=" + charset); out.println("Content-Transfer-Encoding: 8bit"); //now send the txt message out.println(); out.println(aMail.getText()); if (multipartBoundary != null) { if (!aMail.getXHTML().equals("") || aMail.attachmentIterator().hasNext()) { if (!aMail.getText().equals("") && !aMail.getXHTML().equals("") && aMail.attachmentIterator().hasNext()) { out.println("--" + MultipartBoundary + "_alt"); } else { out.println("--" + multipartBoundary); } } else { if (!aMail.getText().equals("") && !aMail.getXHTML().equals("") && aMail.attachmentIterator().hasNext()) { out.println("--" + MultipartBoundary + "_alt--"); } else { out.println("--" + multipartBoundary + "--"); } } } } //HTML email if (!aMail.getXHTML().equals("")) { out.println("Content-Type: text/html; charset=" + charset); out.println("Content-Transfer-Encoding: 8bit"); //now send the html message out.println(); out.println( "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">"); out.println(aMail.getXHTML()); if (multipartBoundary != null) { if (aMail.attachmentIterator().hasNext()) { if (!aMail.getText().equals("") && !aMail.getXHTML().equals("") && aMail.attachmentIterator().hasNext()) { out.println("--" + MultipartBoundary + "_alt--"); out.println("--" + multipartBoundary); } else { out.println("--" + multipartBoundary); } } else { if (!aMail.getText().equals("") && !aMail.getXHTML().equals("") && aMail.attachmentIterator().hasNext()) { out.println("--" + MultipartBoundary + "_alt--"); } else { out.println("--" + multipartBoundary + "--"); } } } } //attachments if (aMail.attachmentIterator().hasNext()) { for (Iterator<MailAttachment> itAttachment = aMail.attachmentIterator(); itAttachment.hasNext();) { MailAttachment ma = itAttachment.next(); out.println("Content-Type: " + ma.getMimeType() + "; name=\"" + ma.getFilename() + "\""); out.println("Content-Transfer-Encoding: base64"); out.println("Content-Description: " + ma.getFilename()); out.println("Content-Disposition: attachment; filename=\"" + ma.getFilename() + "\""); out.println(); //write out the attachment encoded data in fixed width lines final char buf[] = new char[MIME_BASE64_MAX_LINE_LENGTH]; int read = -1; final Reader attachmentDataReader = new StringReader(ma.getData()); while ((read = attachmentDataReader.read(buf, 0, MIME_BASE64_MAX_LINE_LENGTH)) > -1) { out.println(String.valueOf(buf, 0, read)); } if (itAttachment.hasNext()) { out.println("--" + multipartBoundary); } } //Emd multipart message out.println("--" + multipartBoundary + "--"); } //end the message, <cr><lf>.<cr><lf> out.println(); out.println("."); out.flush(); } /** * Get's the version of eXist we are running * The eXist version is used as part of the multipart separator * * @return The eXist Version */ private String eXistVersion() throws IOException { Properties sysProperties = new Properties(); sysProperties.load(GetVersion.class.getClassLoader().getResourceAsStream("org/exist/system.properties")); return ((String) sysProperties.getProperty("product-version", "unknown version")); } /** * Constructs a mail Object from an XML representation of an email * * The XML email Representation is expected to look something like this * * <mail> * <from></from> * <reply-to></reply-to> * <to></to> * <cc></cc> * <bcc></bcc> * <subject></subject> * <message> * <text></text> * <xhtml></xhtml> * </message> * </mail> * * @param mailElements The XML mail Node * @return A mail Object representing the XML mail Node */ private List<Mail> parseMailElement(List<Element> mailElements) throws TransformerException { List<Mail> mails = new ArrayList<>(); for (Element mailElement : mailElements) { //Make sure that message has a Mail node if (mailElement.getLocalName().equals("mail")) { //New mail Object Mail mail = new Mail(); //Get the First Child Node child = mailElement.getFirstChild(); while (child != null) { //Parse each of the child nodes if (child.getNodeType() == Node.ELEMENT_NODE && child.hasChildNodes()) { switch (child.getLocalName()) { case "from": mail.setFrom(child.getFirstChild().getNodeValue()); break; case "reply-to": mail.setReplyTo(child.getFirstChild().getNodeValue()); break; case "to": mail.addTo(child.getFirstChild().getNodeValue()); break; case "cc": mail.addCC(child.getFirstChild().getNodeValue()); break; case "bcc": mail.addBCC(child.getFirstChild().getNodeValue()); break; case "subject": mail.setSubject(child.getFirstChild().getNodeValue()); break; case "message": //If the message node, then parse the child text and xhtml nodes Node bodyPart = child.getFirstChild(); while (bodyPart != null) { if (bodyPart.getLocalName().equals("text")) { mail.setText(bodyPart.getFirstChild().getNodeValue()); } else if (bodyPart.getLocalName().equals("xhtml")) { //Convert everything inside <xhtml></xhtml> to text TransformerFactory transFactory = TransformerFactory.newInstance(); Transformer transformer = transFactory.newTransformer(); DOMSource source = new DOMSource(bodyPart.getFirstChild()); StringWriter strWriter = new StringWriter(); StreamResult result = new StreamResult(strWriter); transformer.transform(source, result); mail.setXHTML(strWriter.toString()); } //next body part bodyPart = bodyPart.getNextSibling(); } break; case "attachment": Element attachment = (Element) child; MailAttachment ma = new MailAttachment(attachment.getAttribute("filename"), attachment.getAttribute("mimetype"), attachment.getFirstChild().getNodeValue()); mail.addAttachment(ma); break; } } //next node child = child.getNextSibling(); } mails.add(mail); } } return mails; } /** * Constructs a mail Object from an XML representation of an email * * The XML email Representation is expected to look something like this * * <mail> * <from></from> * <reply-to></reply-to> * <to></to> * <cc></cc> * <bcc></bcc> * <subject></subject> * <message> * <text charset="" encoding=""></text> * <xhtml charset="" encoding=""></xhtml> * <generic charset="" type="" encoding=""></generic> * </message> * <attachment mimetype="" filename=""></attachment> * </mail> * * @param mailElements The XML mail Node * @return A mail Object representing the XML mail Node */ private List<Message> parseMessageElement(Session session, List<Element> mailElements) throws IOException, MessagingException, TransformerException { List<Message> mails = new ArrayList<>(); for (Element mailElement : mailElements) { //Make sure that message has a Mail node if (mailElement.getLocalName().equals("mail")) { //New message Object // create a message MimeMessage msg = new MimeMessage(session); ArrayList<InternetAddress> replyTo = new ArrayList<>(); boolean fromWasSet = false; MimeBodyPart body = null; Multipart multibody = null; ArrayList<MimeBodyPart> attachments = new ArrayList<>(); String firstContent = null; String firstContentType = null; String firstCharset = null; String firstEncoding = null; //Get the First Child Node child = mailElement.getFirstChild(); while (child != null) { //Parse each of the child nodes if (child.getNodeType() == Node.ELEMENT_NODE && child.hasChildNodes()) { switch (child.getLocalName()) { case "from": // set the from and to address InternetAddress[] addressFrom = { new InternetAddress(child.getFirstChild().getNodeValue()) }; msg.addFrom(addressFrom); fromWasSet = true; break; case "reply-to": // As we can only set the reply-to, not add them, let's keep // all of them in a list replyTo.add(new InternetAddress(child.getFirstChild().getNodeValue())); msg.setReplyTo(replyTo.toArray(new InternetAddress[0])); break; case "to": msg.addRecipient(Message.RecipientType.TO, new InternetAddress(child.getFirstChild().getNodeValue())); break; case "cc": msg.addRecipient(Message.RecipientType.CC, new InternetAddress(child.getFirstChild().getNodeValue())); break; case "bcc": msg.addRecipient(Message.RecipientType.BCC, new InternetAddress(child.getFirstChild().getNodeValue())); break; case "subject": msg.setSubject(child.getFirstChild().getNodeValue()); break; case "header": // Optional : You can also set your custom headers in the Email if you Want msg.addHeader(((Element) child).getAttribute("name"), child.getFirstChild().getNodeValue()); break; case "message": //If the message node, then parse the child text and xhtml nodes Node bodyPart = child.getFirstChild(); while (bodyPart != null) { if (bodyPart.getNodeType() != Node.ELEMENT_NODE) continue; Element elementBodyPart = (Element) bodyPart; String content = null; String contentType = null; if (bodyPart.getLocalName().equals("text")) { // Setting the Subject and Content Type content = bodyPart.getFirstChild().getNodeValue(); contentType = "plain"; } else if (bodyPart.getLocalName().equals("xhtml")) { //Convert everything inside <xhtml></xhtml> to text TransformerFactory transFactory = TransformerFactory.newInstance(); Transformer transformer = transFactory.newTransformer(); DOMSource source = new DOMSource(bodyPart.getFirstChild()); StringWriter strWriter = new StringWriter(); StreamResult result = new StreamResult(strWriter); transformer.transform(source, result); content = strWriter.toString(); contentType = "html"; } else if (bodyPart.getLocalName().equals("generic")) { // Setting the Subject and Content Type content = elementBodyPart.getFirstChild().getNodeValue(); contentType = elementBodyPart.getAttribute("type"); } // Now, time to store it if (content != null && contentType != null && contentType.length() > 0) { String charset = elementBodyPart.getAttribute("charset"); String encoding = elementBodyPart.getAttribute("encoding"); if (body != null && multibody == null) { multibody = new MimeMultipart("alternative"); multibody.addBodyPart(body); } if (StringUtils.isEmpty(charset)) { charset = "UTF-8"; } if (StringUtils.isEmpty(encoding)) { encoding = "quoted-printable"; } if (body == null) { firstContent = content; firstCharset = charset; firstContentType = contentType; firstEncoding = encoding; } body = new MimeBodyPart(); body.setText(content, charset, contentType); if (encoding != null) { body.setHeader("Content-Transfer-Encoding", encoding); } if (multibody != null) multibody.addBodyPart(body); } //next body part bodyPart = bodyPart.getNextSibling(); } break; case "attachment": Element attachment = (Element) child; MimeBodyPart part; // if mimetype indicates a binary resource, assume the content is base64 encoded if (MimeTable.getInstance().isTextContent(attachment.getAttribute("mimetype"))) { part = new MimeBodyPart(); } else { part = new PreencodedMimeBodyPart("base64"); } StringBuilder content = new StringBuilder(); Node attachChild = attachment.getFirstChild(); while (attachChild != null) { if (attachChild.getNodeType() == Node.ELEMENT_NODE) { TransformerFactory transFactory = TransformerFactory.newInstance(); Transformer transformer = transFactory.newTransformer(); DOMSource source = new DOMSource(attachChild); StringWriter strWriter = new StringWriter(); StreamResult result = new StreamResult(strWriter); transformer.transform(source, result); content.append(strWriter.toString()); } else { content.append(attachChild.getNodeValue()); } attachChild = attachChild.getNextSibling(); } part.setDataHandler(new DataHandler(new ByteArrayDataSource(content.toString(), attachment.getAttribute("mimetype")))); part.setFileName(attachment.getAttribute("filename")); // part.setHeader("Content-Transfer-Encoding", "base64"); attachments.add(part); break; } } //next node child = child.getNextSibling(); } // Lost from if (!fromWasSet) msg.setFrom(); // Preparing content and attachments if (attachments.size() > 0) { if (multibody == null) { multibody = new MimeMultipart("mixed"); if (body != null) { multibody.addBodyPart(body); } } else { MimeMultipart container = new MimeMultipart("mixed"); MimeBodyPart containerBody = new MimeBodyPart(); containerBody.setContent(multibody); container.addBodyPart(containerBody); multibody = container; } for (MimeBodyPart part : attachments) { multibody.addBodyPart(part); } } // And now setting-up content if (multibody != null) { msg.setContent(multibody); } else if (body != null) { msg.setText(firstContent, firstCharset, firstContentType); if (firstEncoding != null) { msg.setHeader("Content-Transfer-Encoding", firstEncoding); } } msg.saveChanges(); mails.add(msg); } } return mails; } /** * Returns the current date and time in an RFC822 format, suitable for an email Date Header * * @return RFC822 formated date and time as a String */ private String getDateRFC822() { String dateString = new String(); final Calendar rightNow = Calendar.getInstance(); //Day of the week switch (rightNow.get(Calendar.DAY_OF_WEEK)) { case Calendar.MONDAY: dateString = "Mon"; break; case Calendar.TUESDAY: dateString = "Tue"; break; case Calendar.WEDNESDAY: dateString = "Wed"; break; case Calendar.THURSDAY: dateString = "Thu"; break; case Calendar.FRIDAY: dateString = "Fri"; break; case Calendar.SATURDAY: dateString = "Sat"; break; case Calendar.SUNDAY: dateString = "Sun"; break; } dateString += ", "; //Date dateString += rightNow.get(Calendar.DAY_OF_MONTH); dateString += " "; //Month switch (rightNow.get(Calendar.MONTH)) { case Calendar.JANUARY: dateString += "Jan"; break; case Calendar.FEBRUARY: dateString += "Feb"; break; case Calendar.MARCH: dateString += "Mar"; break; case Calendar.APRIL: dateString += "Apr"; break; case Calendar.MAY: dateString += "May"; break; case Calendar.JUNE: dateString += "Jun"; break; case Calendar.JULY: dateString += "Jul"; break; case Calendar.AUGUST: dateString += "Aug"; break; case Calendar.SEPTEMBER: dateString += "Sep"; break; case Calendar.OCTOBER: dateString += "Oct"; break; case Calendar.NOVEMBER: dateString += "Nov"; break; case Calendar.DECEMBER: dateString += "Dec"; break; } dateString += " "; //Year dateString += rightNow.get(Calendar.YEAR); dateString += " "; //Time String tHour = Integer.toString(rightNow.get(Calendar.HOUR_OF_DAY)); if (tHour.length() == 1) { tHour = "0" + tHour; } String tMinute = Integer.toString(rightNow.get(Calendar.MINUTE)); if (tMinute.length() == 1) { tMinute = "0" + tMinute; } String tSecond = Integer.toString(rightNow.get(Calendar.SECOND)); if (tSecond.length() == 1) { tSecond = "0" + tSecond; } dateString += tHour + ":" + tMinute + ":" + tSecond + " "; //TimeZone Correction String tzSign = new String(); String tzHours = new String(); String tzMinutes = new String(); final TimeZone thisTZ = rightNow.getTimeZone(); int tzOffset = thisTZ.getOffset(rightNow.getTime().getTime()); //get timezone offset in milliseconds tzOffset = (tzOffset / 1000); //convert to seconds tzOffset = (tzOffset / 60); //convert to minutes //Sign if (tzOffset > 1) { tzSign = "+"; } else { tzSign = "-"; tzOffset *= -1; } //Calc Hours and Minutes? if (tzOffset >= 60) { //Minutes and Hours tzHours += (tzOffset / 60); //hours // do we need to prepend a 0 if (tzHours.length() == 1) { tzHours = "0" + tzHours; } tzMinutes += (tzOffset % 60); //minutes // do we need to prepend a 0 if (tzMinutes.length() == 1) { tzMinutes = "0" + tzMinutes; } } else { //Just Minutes tzHours = "00"; tzMinutes += tzOffset; // do we need to prepend a 0 if (tzMinutes.length() == 1) { tzMinutes = "0" + tzMinutes; } } dateString += tzSign + tzHours + tzMinutes; return dateString; } /** * Base64 Encodes a string (used for message subject) * * @param str The String to encode * @return The encoded String */ private String encode64(String str) throws java.io.UnsupportedEncodingException { Base64Encoder enc = new Base64Encoder(); enc.translate(str.getBytes(charset)); String result = new String(enc.getCharArray()); result = result.replaceAll("\n", "?=\n =?" + charset + "?B?"); result = "=?" + charset + "?B?" + result + "?="; return (result); } /** * Base64 Encodes an email address * * @param str The email address as a String to encode * @return The encoded email address String */ private String encode64Address(String str) throws java.io.UnsupportedEncodingException { String result; int idx = str.indexOf("<"); if (idx != -1) { result = encode64(str.substring(0, idx)) + " " + str.substring(idx); } else { result = str; } return (result); } /** * A simple data class to represent an email * attachment. Just has private * members and some get methods. * * @version 1.2 */ private class MailAttachment { private String filename; private String mimeType; private String data; public MailAttachment(String filename, String mimeType, String data) { this.filename = filename; this.mimeType = mimeType; this.data = data; } public String getData() { return data; } public String getFilename() { return filename; } public String getMimeType() { return mimeType; } } /** * A simple data class to represent an email * doesnt do anything fancy just has private * members and get and set methods * * @version 1.2 */ private class Mail { private String from = ""; //Who is the mail from private String replyTo = null; //Who should you reply to private final List<String> to = new ArrayList<>(); //Who is the mail going to private final List<String> cc = new ArrayList<>(); //Carbon Copy to private final List<String> bcc = new ArrayList<>(); //Blind Carbon Copy to private String subject = ""; //Subject of the mail private String text = ""; //Body text of the mail private String xhtml = ""; //Body XHTML of the mail private final List<MailAttachment> attachment = new ArrayList<>(); //Any attachments //From public void setFrom(String from) { this.from = from; } public String getFrom() { return (this.from); } //reply-to public void setReplyTo(String replyTo) { this.replyTo = replyTo; } public String getReplyTo() { return replyTo; } //To public void addTo(String to) { this.to.add(to); } public int countTo() { return (to.size()); } public String getTo(int index) { return to.get(index); } public List<String> getTo() { return (to); } //CC public void addCC(String cc) { this.cc.add(cc); } public int countCC() { return (cc.size()); } public String getCC(int index) { return cc.get(index); } public List<String> getCC() { return (cc); } //BCC public void addBCC(String bcc) { this.bcc.add(bcc); } public int countBCC() { return bcc.size(); } public String getBCC(int index) { return bcc.get(index); } public List<String> getBCC() { return (bcc); } //Subject public void setSubject(String subject) { this.subject = subject; } public String getSubject() { return subject; } //text public void setText(String text) { this.text = text; } public String getText() { return text; } //xhtml public void setXHTML(String xhtml) { this.xhtml = xhtml; } public String getXHTML() { return xhtml; } public void addAttachment(MailAttachment ma) { attachment.add(ma); } public Iterator<MailAttachment> attachmentIterator() { return attachment.iterator(); } } }