// Copyright � 2006-2007 ASERT. Released under the Canoo Webtest license.
package com.canoo.webtest.plugins.emailtest;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.mail.BodyPart;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.Part;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import com.canoo.webtest.engine.ContextHelper;
import com.canoo.webtest.engine.StepFailedException;
import com.canoo.webtest.util.ConversionUtil;
import sun.misc.UUDecoder;
/**
* Returns the content associated with a message (or message Part).
*
* @author Paul King, ASERT
* @webtest.step category="Email"
* name="emailMessageContentFilter"
* description="Returns the content associated with a message (or message Part) as the current response."
*/
public class EmailMessageContentFilter extends AbstractEmailFilter
{
private static final Logger LOG = Logger.getLogger(EmailMessageContentFilter.class);
private String fContentType;
private String fPartIndex;
private String fFilename;
public String getContentType() {
return fContentType;
}
/**
* Sets the contentType.
*
* @param contentType The contentType of the message/part of interest
* @webtest.parameter required="no"
* description="The contentType. The contentType to use for a <em>Simple</em> message with uuencoded attachments. For MIME MultiPart messages, the contentType if supplied is checked against the contentType found."
*/
public void setContentType(final String contentType) {
fContentType = contentType;
}
public String getPartIndex() {
return fPartIndex;
}
/**
* Sets the part index.
*
* @param partIndex The index of the part of interest
* @webtest.parameter required="no"
* description="The part index."
*/
public void setPartIndex(final String partIndex) {
fPartIndex = partIndex;
}
protected void filterContent(final Message message) throws MessagingException {
if (StringUtils.isEmpty(getPartIndex())) {
try {
defineAsCurrentResponse(IOUtils.toString(message.getInputStream()), message.getContentType());
return;
} catch (IOException e) {
throw new MessagingException("Error extracting message: " + e.getMessage());
}
}
final int partIndex = ConversionUtil.convertToInt(getPartIndex(), 0);
try {
final Object content = message.getContent();
if (content instanceof Multipart) {
extractMultiPartMessage((Multipart) content, partIndex);
return;
}
extractSimpleMessage((String) content, partIndex);
} catch (IOException e) {
LOG.error("Error processing email message: ", e);
throw new MessagingException("Error processing email message: " + e.getMessage());
}
}
private void extractMultiPartMessage(final Multipart parts, final int partIndex) throws MessagingException {
try {
if (partIndex >= parts.getCount()) {
throw new StepFailedException("PartIndex too large.", this);
}
final BodyPart part = parts.getBodyPart(partIndex);
final String contentType = part.getContentType();
if (!StringUtils.isEmpty(getContentType()) && !contentType.equals(getContentType())) {
throw new MessagingException("Actual contentType of '" + contentType +
"' did not match expected contentType of '" + fContentType + "'");
}
final String disp = part.getDisposition();
final String filename = part.getFileName();
final InputStream inputStream = part.getInputStream();
if (Part.ATTACHMENT.equals(disp)) {
fFilename = filename;
} else {
fFilename = getClass().getName();
}
ContextHelper.defineAsCurrentResponse(getContext(), IOUtils.toString(inputStream),
contentType, "http://" + fFilename);
} catch (IOException e) {
throw new MessagingException("Error extracting part: " + e.getMessage());
}
}
private void extractSimpleMessage(final String content, final int partIndex) throws MessagingException {
final ByteArrayInputStream byteStream = new ByteArrayInputStream(getRawBytes(content, partIndex));
final byte[] data;
try {
final UUDecoder uudc = new UUDecoder();
data = uudc.decodeBuffer(byteStream);
} catch (IOException e) {
throw new MessagingException("Error Uudecoding attachment: " + e.getMessage());
}
if (StringUtils.isEmpty(fContentType)) {
throw new StepFailedException("Attribute 'contentType' must be supplied for simple messages.", this);
}
defineAsCurrentResponse(data, getContentType());
}
private byte[] getRawBytes(final String content, final int partIndex) throws MessagingException {
// iterate over string looking for ^begin ddd$
final String lineStr = "(^.*$)";
final String startUuencodeStr = "begin \\d\\d\\d .*";
final String endUuencodeStr = "^end.*";
final Pattern linePattern = Pattern.compile(lineStr, Pattern.MULTILINE);
final Matcher matcher = linePattern.matcher(content);
boolean extracting = false;
int count = 0;
final StringBuffer buf = new StringBuffer();
while (matcher.find()) {
final String line = matcher.group(0);
if (extracting) {
if (line.matches(endUuencodeStr)) {
buf.append(" \n").append(line).append('\n');
extracting = false;
break;
} else {
buf.append(line).append('\n');
}
} else if (line.matches(startUuencodeStr)) {
if (count++ == partIndex) {
extracting = true;
buf.append(line).append('\n');
final int lastSpace = line.lastIndexOf(" ");
fFilename = line.substring(lastSpace + 1);
}
}
}
if (buf.length() == 0) {
throw new StepFailedException("Unable to find part with index " + partIndex + ".");
}
LOG.debug("buf=" + buf.toString());
return buf.toString().getBytes();
}
protected void verifyParameters() {
super.verifyParameters();
optionalIntegerParamCheck(getPartIndex(), "partIndex", true);
}
}