> Alex/Erik,
> I've attached a change to the email processor that allows it to pass
> credentials by adding the following to the config.
>
> <credentials>
> <username>user</username>
> <password>password</password>
> </credentials>
>
> Thanks
>
> Ryan
>
> Ryan Puddephatt
> Software Engineer
>
> Teleflex Group - IT UK
> 1 Michaelson Square
> Livingston
> West Lothian
> Scotland
> EH54 7DP
>
> e>
[hidden email] <mailto:
[hidden email]>
> t> +44(0)1506 407 110
> f> +44(0)1506 407 108
> w> www.teleflex.com <
http://www.teleflex.com>
>
>
> mixed alternative related multipart/mixed multipart/alternative
> multipart/related multipart/.+
> /**
> * Copyright (C) 2004 Orbeon, Inc.
> *
> * 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.1 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.
> *
> * The full text of the license is available at
>
http://www.gnu.org/copyleft/lesser.html> */
> package org.orbeon.oxf.processor;
>
> import
> org.apache.commons.fileupload.DefaultFileItemFactory;
> import org.apache.commons.fileupload.FileItem;
> import org.apache.log4j.Logger;
> import org.dom4j.Document;
> import org.dom4j.Element;
> import org.orbeon.oxf.common.OXFException;
> import org.orbeon.oxf.common.ValidationException;
> import org.orbeon.oxf.pipeline.api.PipelineContext;
> import org.orbeon.oxf.processor.generator.RequestGenerator;
> import org.orbeon.oxf.processor.generator.URLGenerator;
> import org.orbeon.oxf.resources.URLFactory;
> import org.orbeon.oxf.util.Base64;
> import org.orbeon.oxf.util.LoggerFactory;
> import org.orbeon.oxf.util.NetUtils;
> import org.orbeon.oxf.util.SystemUtils;
> import org.orbeon.oxf.xml.ForwardingContentHandler;
> import org.orbeon.oxf.xml.ProcessorOutputXMLReader;
> import org.orbeon.oxf.xml.TransformerUtils;
> import org.orbeon.oxf.xml.XMLUtils;
> import org.orbeon.oxf.xml.dom4j.LocationData;
> import org.orbeon.oxf.xml.dom4j.LocationSAXWriter;
> import org.orbeon.oxf.xml.dom4j.NonLazyUserDataDocument;
> import org.xml.sax.InputSource;
> import org.xml.sax.SAXException;
> import org.xml.sax.XMLReader;
>
> import javax.activation.DataHandler;
> import javax.activation.DataSource;
> import javax.mail.Message;
> import javax.mail.Part;
> import javax.mail.Session;
> import javax.mail.Transport;
> import javax.mail.internet.*;
> import javax.mail.Authenticator;
> import javax.mail.PasswordAuthentication;
> import javax.xml.transform.OutputKeys;
> import javax.xml.transform.Transformer;
> import javax.xml.transform.TransformerException;
> import javax.xml.transform.sax.SAXResult;
> import javax.xml.transform.sax.SAXSource;
> import javax.xml.transform.sax.TransformerHandler;
> import javax.xml.transform.stream.StreamResult;
> import java.io.*;
> import java.util.Iterator;
> import java.util.Properties;
>
> /**
> * This processor allows sending emails. It supports multipart messages and
> inline as well as
> * out-of-line attachments.
> *
> * For some useful JavaMail information:
>
http://java.sun.com/products/javamail/FAQ.html> *
> * TODO:
> * o revise support of text/html
> * o built-in support for HTML could handle src="cid:*" with part/message
> ids
> * o support text/xml? or just XHTML?
> * o build message with SAX, not DOM, so streaming of input is possible [not
> necessarily a big win]
> */
> public class EmailProcessor extends ProcessorImpl {
> static private Logger logger =
> LoggerFactory.createLogger(EmailProcessor.class);
>
> // Properties for this processor
> public static final String EMAIL_SMTP_HOST = "smtp-host";
> public static final String EMAIL_TEST_TO = "test-to";
> public static final String EMAIL_TEST_SMTP_HOST = "test-smtp-host";
>
> public static final String EMAIL_FORCE_TO_DEPRECATED = "forceto"; //
> deprecated
> public static final String EMAIL_HOST_DEPRECATED = "host"; // deprecated
>
> public static final String EMAIL_CONFIG_NAMESPACE_URI =
> "
http://www.orbeon.com/oxf/email";
>
> private static final String DEFAULT_MULTIPART = "mixed";
> private static final String DEFAULT_TEXT_ENCODING = "iso-8859-1";
>
> //private String username;
> //private String password;
>
> public EmailProcessor() {
> addInputInfo(new
> ProcessorInputOutputInfo(INPUT_DATA,
> EMAIL_CONFIG_NAMESPACE_URI));
> }
>
> public void start(PipelineContext pipelineContext) {
> try {
> Document dataDocument =
> readInputAsDOM4J(pipelineContext, INPUT_DATA);
> Element messageElement = dataDocument.getRootElement();
>
> // Get system id (will likely be null if document is generated
> dynamically)
> LocationData locationData = (LocationData)
> messageElement.getData();
> String dataInputSystemId = locationData.getSystemID();
>
> // Set SMTP host
> Properties properties = new Properties();
> String testSmtpHostProperty =
> getPropertySet().getString(EMAIL_TEST_SMTP_HOST);
>
> if (testSmtpHostProperty != null) {
> // Test SMTP Host from properties overrides the local
> configuration
> properties.setProperty("mail.smtp.host",
> testSmtpHostProperty);
> } else {
> // Try regular config parameter and property
> String host =
> messageElement.element("smtp-host").getTextTrim();
> if (host != null && !host.equals("")) {
> // Precedence goes to the local config parameter
> properties.setProperty("mail.smtp.host", host);
> } else {
> // Otherwise try to use a property
> host =
> getPropertySet().getString(EMAIL_SMTP_HOST);
> if (host == null)
> host =
> getPropertySet().getString(EMAIL_HOST_DEPRECATED);
> if (host == null)
> throw new OXFException("Could not find SMTP host in
> configuration or in properties");
> properties.setProperty("mail.smtp.host", host);
>
> }
> }
>
> // Get credentials
> Element credentials = messageElement.element("credentials");
>
> // Create session variable, but don't assign it
> Session session = null;
>
> // Check if credentials are supplied
> if(credentials != null && !credentials.equals("")) {
> if(logger.isInfoEnabled())
> logger.info("Authentication");
> // Set the auth property to true
> properties.setProperty("mail.smtp.auth", "true");
>
> String username =
> credentials.element("username").getStringValue();
> String password =
> credentials.element("password").getStringValue();
>
> if(logger.isInfoEnabled())
> logger.info("Username: "+username+"; Password:
> "+password);
>
> // Create an authenticator
> Authenticator auth = new
> SMTPAuthenticator(username,password);
>
> // Create session with auth
> session = Session.getInstance(properties,auth);
> } else {
> if(logger.isInfoEnabled())
> logger.info("No Authentication");
> session = Session.getInstance(properties);
> }
>
> // Create message
> Message message = new MimeMessage(session);
>
> // Set From
> message.setFrom(createAddress(messageElement.element("from")));
>
> // Set To
> String testToProperty =
> getPropertySet().getString(EMAIL_TEST_TO);
> if (testToProperty == null)
> testToProperty =
> getPropertySet().getString(EMAIL_FORCE_TO_DEPRECATED);
>
> if (testToProperty != null) {
> // Test To from properties overrides local configuration
> message.addRecipient(Message.RecipientType.TO, new
> InternetAddress(testToProperty));
> } else {
> // Regular list of To elements
> for (Iterator i = messageElement.elements("to").iterator();
> i.hasNext();) {
> Element toElement = (Element) i.next();
> InternetAddress address = createAddress(toElement);
> message.addRecipient(Message.RecipientType.TO, address);
> }
> }
>
> // Set Cc
> for (Iterator i = messageElement.elements("cc").iterator();
> i.hasNext();) {
> Element toElement = (Element) i.next();
> InternetAddress address = createAddress(toElement);
> message.addRecipient(Message.RecipientType.CC, address);
> }
>
> // Set Bcc
> for (Iterator i = messageElement.elements("bcc").iterator();
> i.hasNext();) {
> Element toElement = (Element) i.next();
> InternetAddress address = createAddress(toElement);
> message.addRecipient(Message.RecipientType.BCC, address);
> }
>
> // Set headers if any
> for (Iterator i = messageElement.elements("header").iterator();
> i.hasNext();) {
> final Element headerElement = (Element) i.next();
> final String headerName =
> headerElement.element("name").getTextTrim();
> final String headerValue =
> headerElement.element("value").getTextTrim();
>
> message.addHeader(headerName, headerValue);
> }
>
> // Set subject
>
> message.setSubject(messageElement.element("subject").getStringValue());
>
> // Handle body
> Element textElement = messageElement.element("text");
> Element bodyElement = messageElement.element("body");
>
> if (textElement != null) {
> // Old deprecated mechanism (simple text body)
> message.setText(textElement.getStringValue());
> } else if (bodyElement != null) {
> // New mechanism with body and parts
> handleBody(pipelineContext, dataInputSystemId, message,
> bodyElement);
> } else {
> throw new OXFException("Main text or body element not
> found");// TODO: location info
> }
>
> // Send message
> Transport transport = session.getTransport("smtp");
> Transport.send(message);
> transport.close();
> } catch (Exception e) {
> throw new OXFException(e);
> }
> }
>
> private void handleBody(PipelineContext pipelineContext, String
> dataInputSystemId, Part parentPart, Element bodyElement) throws Exception {
>
> // Find out if there are embedded parts
> Iterator parts = bodyElement.elementIterator("part");
> String multipart;
> if (bodyElement.getName().equals("body")) {
> multipart = bodyElement.attributeValue("mime-multipart");
> if (multipart != null && !parts.hasNext())
> throw new OXFException("mime-multipart attribute on body
> element requires part children elements");
> String contentTypeFromAttribute =
> NetUtils.getContentTypeMediaType(bodyElement.attributeValue("content-type"));
> if (contentTypeFromAttribute != null &&
> contentTypeFromAttribute.startsWith("multipart/"))
>
> contentTypeFromAttribute.substring("multipart/".length());
> if (parts.hasNext() && multipart == null)
> multipart = DEFAULT_MULTIPART;
> } else {
> String contentTypeAttribute =
> NetUtils.getContentTypeMediaType(bodyElement.attributeValue("content-type"));
> multipart = (contentTypeAttribute != null &&
> contentTypeAttribute.startsWith("multipart/")) ?
> contentTypeAttribute.substring("multipart/".length()) : null;
> }
>
> if (multipart != null) {
> // Multipart content is requested
> MimeMultipart mimeMultipart = new MimeMultipart(multipart);
>
> // Iterate through parts
> for (Iterator i = parts; i.hasNext();) {
> Element partElement = (Element) i.next();
>
> MimeBodyPart mimeBodyPart = new MimeBodyPart();
> handleBody(pipelineContext, dataInputSystemId, mimeBodyPart,
> partElement);
> mimeMultipart.addBodyPart(mimeBodyPart);
> }
>
> // Set content on parent part
> parentPart.setContent(mimeMultipart);
> } else {
> // No multipart, just use the content of the element and add to
> the current part (which can be the main message)
> handlePart(pipelineContext, dataInputSystemId, parentPart,
> bodyElement);
> }
> }
>
> private void handlePart(PipelineContext pipelineContext, String
> dataInputSystemId, Part parentPart, Element partOrBodyElement) throws
> Exception {
> final String name =
> partOrBodyElement.attributeValue("name");
> String contentTypeAttribute =
> partOrBodyElement.attributeValue("content-type");
> final String contentType =
> NetUtils.getContentTypeMediaType(contentTypeAttribute);
> final String charset;
> {
> String c = NetUtils.getContentTypeCharset(contentTypeAttribute);
> charset = (c != null) ? c : DEFAULT_TEXT_ENCODING;
> }
> final String contentTypeWithCharset = contentType + "; charset=" +
> charset;
> final String src =
> partOrBodyElement.attributeValue("src");
>
> // Either a String or a FileItem
> final Object content;
> if (src != null) {
> // Content of the part is not inline
>
> // Generate a Document from the source
> SAXSource source = getSAXSource(EmailProcessor.this,
> pipelineContext, src, dataInputSystemId, contentType);
> content =
> handleStreamedPartContent(pipelineContext, source,
> contentType, charset);
> } else {
> // Content of the part is inline
>
> // In the cases of text/html and XML, there must be exactly one
> root element
> boolean needsRootElement =
> "text/html".equals(contentType);// ||
> ProcessorUtils.isXMLContentType(contentType);
> if (needsRootElement && partOrBodyElement.elements().size() !=
> 1)
> throw new ValidationException("The <body> or <part> element
> must contain exactly one element for text/html",
> (LocationData) partOrBodyElement.getData());
>
> // Create Document and convert it into a String
> Element rootElement = (Element)(needsRootElement ?
> partOrBodyElement.elements().get(0) : partOrBodyElement);
> Document partDocument = new NonLazyUserDataDocument();
> partDocument.setRootElement((Element) rootElement.clone());
> content = handleInlinePartContent(partDocument,
> contentType);
> }
>
> if (!(ProcessorUtils.isTextContentType(contentType)
> || ProcessorUtils.isXMLContentType(contentType))) {
> // This is binary content
> if (content instanceof FileItem) {
> final FileItem fileItem = (FileItem) content;
> parentPart.setDataHandler(new DataHandler(new DataSource() {
> public String getContentType() {
> return contentType;
> }
>
> public InputStream getInputStream() throws IOException {
> return fileItem.getInputStream();
> }
>
> public String getName() {
> return name;
> }
>
> public OutputStream getOutputStream() throws IOException
> {
> throw new IOException("Write operation not
> supported");
> }
> }));
> } else {
> byte[] data =
> XMLUtils.base64StringToByteArray((String) content);
> parentPart.setDataHandler(new DataHandler(new
> SimpleBinaryDataSource(name, contentType, data)));
> }
> } else {
> // This is text content
> if (content instanceof FileItem) {
> // The text content was encoded when written to the FileItem
> final FileItem fileItem = (FileItem) content;
> parentPart.setDataHandler(new DataHandler(new DataSource() {
> public String getContentType() {
> // This always contains a charset
> return contentTypeWithCharset;
> }
>
> public InputStream getInputStream() throws IOException {
> // This is encoded with the appropriate charset
> (user-defined, or the default)
> return fileItem.getInputStream();
> }
>
> public String getName() {
> return name;
> }
>
> public OutputStream getOutputStream() throws IOException
> {
> throw new IOException("Write operation not
> supported");
> }
> }));
> } else {
> parentPart.setDataHandler(new DataHandler(new
> SimpleTextDataSource(name, contentTypeWithCharset, (String) content)));
> }
> }
>
> // Set content-disposition header
> String contentDisposition =
> partOrBodyElement.attributeValue("content-disposition");
> if (contentDisposition != null)
> parentPart.setDisposition(contentDisposition);
>
> // Set content-id header
> String contentId =
> partOrBodyElement.attributeValue("content-id");
> if (contentId != null)
> parentPart.setHeader("content-id", "<" + contentId + ">");
> //part.setContentID(contentId);
> }
>
> private String handleInlinePartContent(Document
> document, String contentType) throws SAXException {
> if ("text/html".equals(contentType)) {
> // Convert XHTML into an HTML String
> StringWriter writer = new StringWriter();
> TransformerHandler identity =
> TransformerUtils.getIdentityTransformerHandler();
> identity.getTransformer().setOutputProperty(OutputKeys.METHOD,
> "html");
> identity.setResult(new StreamResult(writer));
> LocationSAXWriter saxw = new LocationSAXWriter();
> saxw.setContentHandler(identity);
> saxw.write(document);
>
> return writer.toString();
> } else {
> // For other types, just return the text nodes
> return document.getStringValue();
> }
> }
>
> public static FileItem
> handleStreamedPartContent(PipelineContext pipelineContext,
> SAXSource source, String contentType, String encoding)
> throws IOException, TransformerException {
>
> final FileItem fileItem = new
> DefaultFileItemFactory(RequestGenerator.getMaxMemorySizeProperty(),
> SystemUtils.getTemporaryDirectory())
> .createItem("dummy", "dummy", false, null);
> // Make sure the file is deleted when the context is destroyed
> pipelineContext.addContextListener(new
> PipelineContext.ContextListenerAdapter() {
> public void contextDestroyed(boolean success) {
> fileItem.delete();
> }
> });
> // Write character content to the FileItem instance
> Writer writer = null;
> OutputStream os = null;
>
> final boolean useWriter =
> ProcessorUtils.isTextContentType(contentType) ||
> ProcessorUtils.isXMLContentType(contentType);
>
> try {
> os = fileItem.getOutputStream();
> if (useWriter)
> writer = new BufferedWriter(new OutputStreamWriter(os,
> encoding));
> final OutputStream _os = os;
> final Writer _writer = writer;
> Transformer identity =
> TransformerUtils.getIdentityTransformer();
> identity.transform(source, new SAXResult(new
> ForwardingContentHandler() {
> public void characters(char[] chars, int start, int length)
> {
> try {
> if (useWriter)
> _writer.write(chars, start, length);
> else
> _os.write(Base64.decode(new String(chars, start,
> length)));
>
> } catch (IOException e) {
> throw new OXFException(e);
> }
> }
> }));
> } finally {
> if (writer != null) {
> try {
> writer.close();
> } catch (IOException e) {
> throw new OXFException(e);
> }
> }
> if (os != null) {
> try {
> os.close();
> } catch (IOException e) {
> throw new OXFException(e);
> }
> }
> }
>
> return fileItem;
> }
>
> private InternetAddress createAddress(Element addressElement) throws
> AddressException, UnsupportedEncodingException {
> String email = addressElement.element("email").getStringValue();
> Element nameElement = addressElement.element("name");
> return nameElement == null ? new InternetAddress(email)
> : new InternetAddress(email, nameElement.getStringValue());
> }
>
> private class SimpleTextDataSource implements DataSource {
> String contentType;
> String text;
> String name;
>
> public SimpleTextDataSource(String name, String contentType, String
> text) {
> this.name = name;
> this.contentType = contentType;
> this.text = text;
> }
>
> public String getContentType() {
> return contentType;
> }
>
> public InputStream getInputStream() throws IOException {
> return new ByteArrayInputStream(text.getBytes("utf-8"));
> }
>
> public String getName() {
> return name;
> }
>
> public OutputStream getOutputStream() throws IOException {
> throw new IOException("Write operation not supported");
> }
> }
>
> private class SimpleBinaryDataSource implements DataSource {
> String contentType;
> byte[] data;
> String name;
>
> public SimpleBinaryDataSource(String name, String contentType,
> byte[] data) {
> this.name = name;
> this.contentType = contentType;
> this.data = data;
> }
>
> public String getContentType() {
> return contentType;
> }
>
> public InputStream getInputStream() throws IOException {
> return new ByteArrayInputStream(data);
> }
>
> public String getName() {
> return name;
> }
>
> public OutputStream getOutputStream() throws IOException {
> throw new IOException("Write operation not supported");
> }
> }
>
> public static SAXSource getSAXSource(Processor processor,
> PipelineContext pipelineContext, String href, String base, String
> contentType) {
> try {
> // There are two cases:
> // 1. We read the source as SAX
> // o This is required when reading from a processor input; in
> this case, we behave like
> // the inline case
> // o When reading from another type of URI, the resource
> could be in theory any type
> // of file.
> // 2. We don't read the source as SAX
> // o It is particularly useful to support this when resources
> are to be used as binary
> // attachments such as images.
> // o Here, we consider that the source can be XML, text/html,
> text/*,
> // or binary. We do not handle reading Base64-encoded
> files. We leverage the URL
> // generator to obtain the content in XML format.
> XMLReader xmlReader;
> {
> String inputName =
> ProcessorImpl.getProcessorInputSchemeInputName(href);
> if (inputName != null) {
> // Resolve to input of current processor
> xmlReader = new
> ProcessorOutputXMLReader(pipelineContext,
> processor.getInputByName(inputName).getOutput());
> } else {
> // Resolve to regular URI
> Processor urlGenerator = (contentType == null)
> ? new URLGenerator(URLFactory.createURL(base,
> href))
> : new URLGenerator(URLFactory.createURL(base,
> href), contentType, true);
> xmlReader = new
> ProcessorOutputXMLReader(pipelineContext,
> urlGenerator.createOutput(ProcessorImpl.OUTPUT_DATA));
> }
> }
>
> // Return SAX Source based on XML Reader
> SAXSource saxSource = new SAXSource(xmlReader, new
> InputSource());
> saxSource.setSystemId(href);
> return saxSource;
> } catch (IOException e) {
> throw new OXFException(e);
> }
> }
>
> private class SMTPAuthenticator extends javax.mail.Authenticator {
> private String username;
> private String password;
>
> public SMTPAuthenticator(String user, String pass){
> username = user;
> password = pass;
> }
>
> public PasswordAuthentication getPasswordAuthentication() {
> return new
> PasswordAuthentication(username,password);
> }
> }
> }
>
> // Set content-transfer-encoding header
> // final String contentTransferEncoding =
> partElement.attributeValue("content-transfer-encoding");
> //
> // MimeBodyPart part = new MimeBodyPart() {
> // protected void updateHeaders() throws
> MessagingException {
> // super.updateHeaders();
> // if (contentTransferEncoding != null)
> //
> setHeader("Content-Transfer-Encoding",
> contentTransferEncoding);
> // }
> // };
> // // Set content-disposition header
> // String contentDisposition =
> partElement.attributeValue("content-disposition");
> // if (contentDisposition != null)
> // part.setDisposition(contentDisposition);
> //
> // part.setDataHandler(new DataHandler(new
> SimpleTextDataSource(name, contentType, content)));
> // mimeMultipart.addBodyPart(part);
>
>
> --
> You receive this message as a subscriber of the
[hidden email]
> mailing list.
> To unsubscribe: mailto:
[hidden email]
> For general help: mailto:
[hidden email]?subject=help
> ObjectWeb mailing lists service home page:
http://www.objectweb.org/wws>
>