Re: Improvement to the Email Processor
Posted by
Henrik Pettersen on
URL: https://discuss.orbeon.com/Improvement-to-the-Email-Processor-tp32960p32961.html
All,
attempting to use the 'credentials' element, but seem to get stuck at schema validation ('Error tag name "credentials" is not allowed...')
Looking at orbeon\src\java\org\orbeon\oxf\xml\schemas\email.rng, there is no mention of 'credentials'.
I can fix this for me, but you might want to update your email.rng in the repository.
And thank you for this addition, Ryan!
Henrik
On 3/1/07,
Ryan Puddephatt <[hidden email]> wrote:
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> <a onclick="return top.js.OpenExtLink(window,event,this)" href="http://www.teleflex.com" target="_blank">www.teleflex.com <<a onclick="return top.js.OpenExtLink(window,event,this)" href="http://www.teleflex.com" target="_blank">
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 <a onclick="return top.js.OpenExtLink(window,event,this)" href="http://www.gnu.org/copyleft/lesser.html" target="_blank">
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: <a onclick="return top.js.OpenExtLink(window,event,this)" href="http://java.sun.com/products/javamail/FAQ.html" target="_blank">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 = "<a onclick="return top.js.OpenExtLink(window,event,this)" href="http://www.orbeon.com/oxf/email" target="_blank">
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(<a onclick="return top.js.OpenExtLink(window,event,this)" href="http://Message.RecipientType.TO" target="_blank">
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(<a onclick="return top.js.OpenExtLink(window,event,this)" href="http://Message.RecipientType.TO" target="_blank">
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(<a onclick="return top.js.OpenExtLink(window,event,this)" href="http://Message.RecipientType.CC" target="_blank">
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) {
<a onclick="return top.js.OpenExtLink(window,event,this)" href="http://this.name" target="_blank">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) {
<a onclick="return top.js.OpenExtLink(window,event,this)" href="http://this.name" target="_blank">
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: <a onclick="return top.js.OpenExtLink(window,event,this)" href="http://www.objectweb.org/wws" target="_blank">
http://www.objectweb.org/wws
--
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