1 /*****************************************************************
2 * Licensed to the Apache Software Foundation (ASF) under one *
3 * or more contributor license agreements. See the NOTICE file *
4 * distributed with this work for additional information *
5 * regarding copyright ownership. The ASF licenses this file *
6 * to you under the Apache License, Version 2.0 (the *
7 * "License"); you may not use this file except in compliance *
8 * with the License. You may obtain a copy of the License at *
9 * *
10 * http://www.apache.org/licenses/LICENSE-2.0 *
11 * *
12 * Unless required by applicable law or agreed to in writing, *
13 * software distributed under the License is distributed on an *
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
15 * KIND, either express or implied. See the License for the *
16 * specific language governing permissions and limitations *
17 * under the License. *
18 ****************************************************************/
19 package org.apache.james.postage.mail;
20
21 import org.apache.james.postage.PostageRunner;
22 import org.apache.james.postage.PostageRuntimeException;
23 import org.apache.james.postage.classloading.CachedInstanceFactory;
24 import org.apache.james.postage.result.MailProcessingRecord;
25 import org.apache.james.util.io.IOUtil;
26 import org.apache.commons.logging.Log;
27 import org.apache.commons.logging.LogFactory;
28
29 import javax.mail.BodyPart;
30 import javax.mail.MessagingException;
31 import javax.mail.internet.MimeMessage;
32 import javax.mail.internet.MimeMultipart;
33
34 import java.io.ByteArrayOutputStream;
35 import java.io.IOException;
36 import java.io.InputStream;
37 import java.util.regex.Pattern;
38
39 /***
40 * helps matching, analysing and validating result mails and sent test mails
41 */
42 public class MailMatchingUtils {
43
44 private static Log log = LogFactory.getLog(MailMatchingUtils.class);
45
46 /***
47 * if this mail was created by postage, whatever run - but by startup check
48 */
49 public static boolean isPostageStartupCheckMail(MimeMessage message) {
50 String headerValue = getMailIdHeader(message);
51 return HeaderConstants.JAMES_POSTAGE_STARTUPCHECK_HEADER_ID.equals(headerValue);
52 }
53
54 /***
55 * if this mail was created by postage, whatever run - but not by startup check
56 */
57 public static boolean isPostageTestMail(MimeMessage message) {
58 return isPostageMail(message) && !isPostageStartupCheckMail(message);
59 }
60
61 /***
62 * if this mail was created by postage, whatever run - if startup check or live test
63 */
64 public static boolean isPostageMail(MimeMessage message) {
65 return null != getUniqueHeader(message, HeaderConstants.JAMES_POSTAGE_HEADER);
66 }
67
68 public static boolean isPostageIdHeaderPresent(MimeMessage message) {
69 return null != getMailIdHeader(message);
70 }
71
72 public static String getMailIdHeader(MimeMessage message) {
73 return getUniqueHeader(message, HeaderConstants.MAIL_ID_HEADER);
74 }
75
76 /***
77 * if this mail was created by the currently running postage scenario - not by
78 * any of those before.
79 */
80 public static boolean isCurrentRunnerMail(MimeMessage message) {
81 String headerValue = getMailIdHeader(message);
82 return headerValue != null && headerValue.startsWith(PostageRunner.getMessageIdPrefix());
83 }
84
85 public static boolean matchHeader(MimeMessage message, String header, String valueRegex) {
86 return Pattern.matches(valueRegex, getUniqueHeader(message, header));
87 }
88
89 public static String getUniqueHeader(MimeMessage message, String header) {
90 String[] idHeaders;
91 try {
92 idHeaders = message.getHeader(header);
93 } catch (MessagingException e) {
94 throw new PostageRuntimeException(e);
95 }
96 if (idHeaders != null && idHeaders.length > 0) {
97 return idHeaders[0];
98 }
99 return null;
100 }
101
102 public static boolean isMatchCandidate(MimeMessage message) {
103 try {
104 if (!isPostageIdHeaderPresent(message)) {
105 if (isPostageMail(message)) {
106 log.warn(HeaderConstants.MAIL_ID_HEADER + " header is missing from James test mail");
107 }
108 else log.info("skipping non-postage mail. remains on server. subject was: " + message.getSubject());
109 return false;
110 }
111 } catch (MessagingException e) {
112 log.info("failed to get mail subject for logging. remains on server. mails might be corrupt.");
113 return false;
114 }
115 if (MailMatchingUtils.isPostageStartupCheckMail(message)) return false;
116 return true;
117 }
118
119 public static boolean validateMail(MimeMessage message, MailProcessingRecord mailProcessingRecord) {
120 String classname = getUniqueHeader(message, HeaderConstants.JAMES_POSTAGE_VALIDATORCLASSNAME_HEADER);
121 MailValidator validator = (MailValidator)CachedInstanceFactory.createInstance(classname);
122 if (validator == null) return false;
123
124 boolean isValid = validator.validate(message, mailProcessingRecord);
125 if (isValid) mailProcessingRecord.setValid();
126 else log.warn("failed to validate mail");
127
128 return isValid;
129 }
130
131 public static MimeMultipart convertToMimeMultipart(MimeMessage message) {
132 try {
133 return new MimeMultipart(message.getDataHandler().getDataSource());
134 } catch (MessagingException e) {
135 throw new RuntimeException("could not convert MimeMessage to MimeMultipart", e);
136 }
137 }
138
139 public static int getMimePartSize(MimeMultipart parts, String mimeType) {
140 if (parts != null) {
141 try {
142 for (int i = 0; i < parts.getCount(); i++) {
143 BodyPart bodyPart = parts.getBodyPart(i);
144 if (bodyPart.getContentType().startsWith(mimeType)) {
145 try {
146 Object content = bodyPart.getContent();
147 if (content instanceof InputStream) {
148 ByteArrayOutputStream os = new ByteArrayOutputStream();
149 IOUtil.copy(((InputStream) content), os);
150 return os.size();
151 } else if (content instanceof String) {
152 return ((String) content).length();
153 } else {
154 throw new IllegalStateException("Unsupported content: "+content.getClass().toString());
155 }
156 } catch (IOException e) {
157 throw new IllegalStateException("Unexpected IOException in getContent()");
158 }
159 }
160 }
161 } catch (MessagingException e) {
162 log.info("failed to process body parts.", e);
163 }
164 }
165 return 0;
166 }
167 }