001: // Copyright © 2006-2007 ASERT. Released under the Canoo Webtest license.
002: package com.canoo.webtest.plugins.emailtest;
003:
004: import java.io.ByteArrayInputStream;
005: import java.io.IOException;
006: import java.io.InputStream;
007: import java.util.regex.Matcher;
008: import java.util.regex.Pattern;
009:
010: import javax.mail.BodyPart;
011: import javax.mail.Message;
012: import javax.mail.MessagingException;
013: import javax.mail.Multipart;
014: import javax.mail.Part;
015:
016: import org.apache.commons.io.IOUtils;
017: import org.apache.commons.lang.StringUtils;
018: import org.apache.log4j.Logger;
019:
020: import com.canoo.webtest.engine.ContextHelper;
021: import com.canoo.webtest.engine.StepFailedException;
022: import com.canoo.webtest.util.ConversionUtil;
023:
024: import sun.misc.UUDecoder;
025:
026: /**
027: * Returns the content associated with a message (or message Part).
028: *
029: * @author Paul King, ASERT
030: * @webtest.step category="Email"
031: * name="emailMessageContentFilter"
032: * description="Returns the content associated with a message (or message Part) as the current response."
033: */
034: public class EmailMessageContentFilter extends AbstractEmailFilter {
035: private static final Logger LOG = Logger
036: .getLogger(EmailMessageContentFilter.class);
037:
038: private String fContentType;
039: private String fPartIndex;
040: private String fFilename;
041:
042: public String getContentType() {
043: return fContentType;
044: }
045:
046: /**
047: * Sets the contentType.
048: *
049: * @param contentType The contentType of the message/part of interest
050: * @webtest.parameter required="no"
051: * 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."
052: */
053: public void setContentType(final String contentType) {
054: fContentType = contentType;
055: }
056:
057: public String getPartIndex() {
058: return fPartIndex;
059: }
060:
061: /**
062: * Sets the part index.
063: *
064: * @param partIndex The index of the part of interest
065: * @webtest.parameter required="no"
066: * description="The part index."
067: */
068: public void setPartIndex(final String partIndex) {
069: fPartIndex = partIndex;
070: }
071:
072: protected void filterContent(final Message message)
073: throws MessagingException {
074: if (StringUtils.isEmpty(getPartIndex())) {
075: try {
076: defineAsCurrentResponse(IOUtils.toString(message
077: .getInputStream()), message.getContentType());
078: return;
079: } catch (IOException e) {
080: throw new MessagingException(
081: "Error extracting message: " + e.getMessage());
082: }
083: }
084: final int partIndex = ConversionUtil.convertToInt(
085: getPartIndex(), 0);
086: try {
087: final Object content = message.getContent();
088: if (content instanceof Multipart) {
089: extractMultiPartMessage((Multipart) content, partIndex);
090: return;
091: }
092: extractSimpleMessage((String) content, partIndex);
093: } catch (IOException e) {
094: LOG.error("Error processing email message: ", e);
095: throw new MessagingException(
096: "Error processing email message: " + e.getMessage());
097: }
098: }
099:
100: private void extractMultiPartMessage(final Multipart parts,
101: final int partIndex) throws MessagingException {
102: try {
103: if (partIndex >= parts.getCount()) {
104: throw new StepFailedException("PartIndex too large.",
105: this );
106: }
107: final BodyPart part = parts.getBodyPart(partIndex);
108: final String contentType = part.getContentType();
109: if (!StringUtils.isEmpty(getContentType())
110: && !contentType.equals(getContentType())) {
111: throw new MessagingException("Actual contentType of '"
112: + contentType
113: + "' did not match expected contentType of '"
114: + fContentType + "'");
115: }
116: final String disp = part.getDisposition();
117: final String filename = part.getFileName();
118: final InputStream inputStream = part.getInputStream();
119: if (Part.ATTACHMENT.equals(disp)) {
120: fFilename = filename;
121: } else {
122: fFilename = getClass().getName();
123: }
124: ContextHelper.defineAsCurrentResponse(getContext(), IOUtils
125: .toString(inputStream), contentType, "http://"
126: + fFilename);
127: } catch (IOException e) {
128: throw new MessagingException("Error extracting part: "
129: + e.getMessage());
130: }
131: }
132:
133: private void extractSimpleMessage(final String content,
134: final int partIndex) throws MessagingException {
135: final ByteArrayInputStream byteStream = new ByteArrayInputStream(
136: getRawBytes(content, partIndex));
137: final byte[] data;
138: try {
139: final UUDecoder uudc = new UUDecoder();
140: data = uudc.decodeBuffer(byteStream);
141: } catch (IOException e) {
142: throw new MessagingException(
143: "Error Uudecoding attachment: " + e.getMessage());
144: }
145: if (StringUtils.isEmpty(fContentType)) {
146: throw new StepFailedException(
147: "Attribute 'contentType' must be supplied for simple messages.",
148: this );
149: }
150: defineAsCurrentResponse(data, getContentType());
151: }
152:
153: private byte[] getRawBytes(final String content, final int partIndex)
154: throws MessagingException {
155: // iterate over string looking for ^begin ddd$
156: final String lineStr = "(^.*$)";
157: final String startUuencodeStr = "begin \\d\\d\\d .*";
158: final String endUuencodeStr = "^end.*";
159: final Pattern linePattern = Pattern.compile(lineStr,
160: Pattern.MULTILINE);
161: final Matcher matcher = linePattern.matcher(content);
162: boolean extracting = false;
163: int count = 0;
164: final StringBuffer buf = new StringBuffer();
165: while (matcher.find()) {
166: final String line = matcher.group(0);
167: if (extracting) {
168: if (line.matches(endUuencodeStr)) {
169: buf.append(" \n").append(line).append('\n');
170: extracting = false;
171: break;
172: } else {
173: buf.append(line).append('\n');
174: }
175: } else if (line.matches(startUuencodeStr)) {
176: if (count++ == partIndex) {
177: extracting = true;
178: buf.append(line).append('\n');
179: final int lastSpace = line.lastIndexOf(" ");
180: fFilename = line.substring(lastSpace + 1);
181: }
182: }
183: }
184: if (buf.length() == 0) {
185: throw new StepFailedException(
186: "Unable to find part with index " + partIndex + ".");
187: }
188: LOG.debug("buf=" + buf.toString());
189: return buf.toString().getBytes();
190: }
191:
192: protected void verifyParameters() {
193: super .verifyParameters();
194: optionalIntegerParamCheck(getPartIndex(), "partIndex", true);
195: }
196: }
|