1 package com.ozacc.mail.fetch.impl;
2
3 import java.io.BufferedOutputStream;
4 import java.io.File;
5 import java.io.FileNotFoundException;
6 import java.io.FileOutputStream;
7 import java.io.FilenameFilter;
8 import java.io.IOException;
9 import java.io.InputStream;
10 import java.util.ArrayList;
11 import java.util.Date;
12 import java.util.Enumeration;
13 import java.util.List;
14
15 import javax.mail.Address;
16 import javax.mail.Header;
17 import javax.mail.Message;
18 import javax.mail.MessagingException;
19 import javax.mail.internet.AddressException;
20 import javax.mail.internet.InternetAddress;
21 import javax.mail.internet.MimeMessage;
22
23 import org.apache.commons.logging.Log;
24 import org.apache.commons.logging.LogFactory;
25
26 import com.ozacc.mail.fetch.ReceivedMail;
27 import com.ozacc.mail.fetch.impl.sk_jp.AttachmentsExtractor;
28 import com.ozacc.mail.fetch.impl.sk_jp.HtmlPartExtractor;
29 import com.ozacc.mail.fetch.impl.sk_jp.MultipartUtility;
30
31 /***
32 * MimeMessageからMailを生成するクラス。
33 * <p>
34 * 変換時に生じたチェック例外は、このクラス内でキャッチされ無視されます。
35 * 例外が生じた項目(差出人や宛先など)に該当するMailインスタンスのプロパティには何もセットされません。
36 *
37 * @since 1.2
38 * @author Tomohiro Otsuka
39 * @version $Id: MailConverter.java,v 1.1.2.9 2005/01/19 14:23:55 otsuka Exp $
40 */
41 public class MailConverter {
42
43 private static final String ATTACHMENT_DIR_PREFIX = "OML_";
44
45 private static final String JAVA_IO_TMPDIR = "java.io.tmpdir";
46
47 private static Log log = LogFactory.getLog(MailConverter.class);
48
49 /***
50 * 保存された添付ファイルの生存時間。デフォルトは12時間。
51 */
52 private long attachmentLifetime = 3600 * 1000 * 12;
53
54 private MimeMessage[] messages;
55
56 /***
57 * @param mimeMessage
58 */
59 public MailConverter(MimeMessage mimeMessage) {
60 this(new MimeMessage[] { mimeMessage });
61 }
62
63 /***
64 * @param mimeMessages
65 */
66 public MailConverter(MimeMessage[] mimeMessages) {
67 this.messages = mimeMessages;
68 }
69
70 /***
71 * @return
72 */
73 public ReceivedMail[] convertIntoMails() {
74 log.debug("計" + messages.length + "通のMimeMessageをMailに変換します。");
75 List list = new ArrayList();
76 for (int i = 0; i < messages.length; i++) {
77 log.debug((i + 1) + "通目のMimeMessageをMailに変換します。");
78
79 MimeMessage mm = messages[i];
80 ReceivedMail mail = new ReceivedMail();
81
82 setReturnPath(mm, mail);
83 setDate(mm, mail);
84 setFromAddress(mm, mail);
85 setRecipientAddresses(mm, mail);
86 setMessageId(mm, mail);
87 setReplyToAddress(mm, mail);
88 setSubject(mm, mail);
89 setXHeaders(mm, mail);
90 setText(mm, mail);
91 setHtmlText(mm, mail);
92 setAttachmentFiles(mm, mail);
93
94 setSize(mm, mail);
95
96 mail.setMessage(mm);
97
98 log.debug((i + 1) + "通目のMimeMessageをMailに変換しました。");
99 log.debug(mail.toString());
100
101 list.add(mail);
102 }
103 log.debug("計" + messages.length + "通のMimeMessageをMailに変換しました。");
104 return (ReceivedMail[])list.toArray(new ReceivedMail[list.size()]);
105 }
106
107 /***
108 * 指定されたMimeMessageに添付されているファイルを全て抽出し、
109 * システムプロパティにセットされた一時ファイルディレクトリ内に保存した後、
110 * そのファイルを指定されたReceivedMailにセットします。
111 * <p>
112 * 保存された添付ファイルはJVM終了時に削除されます。
113 *
114 * @param mm
115 * @param mail
116 */
117 private void setAttachmentFiles(MimeMessage mm, ReceivedMail mail) {
118 try {
119 cleanTempDir();
120
121 AttachmentsExtractor ae = new AttachmentsExtractor(
122 AttachmentsExtractor.MODE_IGNORE_MESSAGE
123 | AttachmentsExtractor.MODE_IGNORE_INLINE);
124 MultipartUtility.process(mm, ae);
125 for (int i = 0, num = ae.getCount(); i < num; i++) {
126 String fileName = ae.getFileName(i);
127 if (fileName == null || "".equals(fileName)) {
128 fileName = "attachment" + (i + 1) + ".tmp";
129 }
130 String path = getTempDirPath() + File.separator + ATTACHMENT_DIR_PREFIX
131 + System.currentTimeMillis() + File.separator + fileName;
132 log.debug((i + 1) + "個目の添付ファイルを保存します。[" + path + "]");
133 File f = new File(path);
134 f.getParentFile().mkdirs();
135 InputStream is = ae.getInputStream(i);
136 writeTo(f, is);
137
138 f.getParentFile().deleteOnExit();
139 f.deleteOnExit();
140
141 mail.addFile(f, fileName);
142 log.debug((i + 1) + "個目の添付ファイルを保存しました。[" + path + "]");
143 }
144 } catch (IOException e) {
145 log.error("添付ファイルの取得に失敗しました。", e);
146 } catch (MessagingException e) {
147
148 log.warn(e.getMessage());
149 }
150 }
151
152 /***
153 * 一時ディレクトリ内に保存された添付ファイルの内、生存時間を越えているものを削除します。
154 */
155 private void cleanTempDir() {
156 File tempDir = new File(getTempDirPath());
157 File[] omlDirs = tempDir.listFiles(new FilenameFilter() {
158 public boolean accept(File dir, String name) {
159 return name.startsWith(ATTACHMENT_DIR_PREFIX);
160 }
161 });
162 log.debug("現在" + omlDirs.length + "個の添付ファイル用ディレクトリが一時ディレクトリに存在します。");
163 long now = System.currentTimeMillis();
164 for (int i = 0; i < omlDirs.length; i++) {
165 File dir = omlDirs[i];
166 log.debug(dir.lastModified() + "");
167 if (now - dir.lastModified() >= attachmentLifetime) {
168 deleteDir(dir);
169 }
170 }
171 }
172
173 /***
174 * 一時ディレクトリのパスを返します。
175 *
176 * @return 一時ディレクトリのパス
177 */
178 private String getTempDirPath() {
179 return System.getProperty(JAVA_IO_TMPDIR);
180 }
181
182 /***
183 * 指定されたディレクトリを中身のファイルを含めて削除します。
184 *
185 * @param dir 削除するディレクトリ
186 */
187 private void deleteDir(File dir) {
188 File[] files = dir.listFiles();
189 for (int i = 0; i < files.length; i++) {
190 File f = files[i];
191 f.delete();
192 }
193 dir.delete();
194 }
195
196 /***
197 * 指定されたInputStreamデータを指定されたファイルに保存します。
198 *
199 * @param destFile 保存するファイル
200 * @param is ソースとなるInputStream
201 * @throws FileNotFoundException
202 * @throws IOException
203 */
204 private void writeTo(File destFile, InputStream is) throws FileNotFoundException, IOException {
205 BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destFile));
206 byte[] b = new byte[8192];
207 while (is.read(b) != -1) {
208 bos.write(b);
209 }
210 bos.flush();
211 bos.close();
212 is.close();
213 }
214
215 private void setXHeaders(MimeMessage mm, ReceivedMail mail) {
216 log.debug("X-HeaderをMailにセットします。");
217 String[] xHeaders = null;
218 Enumeration headerEnum = null;
219 try {
220 headerEnum = mm.getAllHeaders();
221 } catch (MessagingException e) {
222
223 log.warn(e.getMessage());
224 }
225 while (headerEnum != null && headerEnum.hasMoreElements()) {
226 Header header = (Header)headerEnum.nextElement();
227 if (header.getName().startsWith("X-")) {
228 mail.addXHeader(header.getName(), header.getValue());
229 }
230 }
231 }
232
233 private void setReplyToAddress(MimeMessage mm, ReceivedMail mail) {
234 log.debug("Reply-ToアドレスをMailにセットします。");
235 Address[] addresses = null;
236 try {
237 addresses = mm.getReplyTo();
238 } catch (MessagingException e) {
239
240 log.warn(e.getMessage());
241 }
242 if (addresses != null) {
243 log.debug(addresses.length + "つのReply-Toアドレスが見つかりました。最初のアドレスのみ取得されます。");
244 for (int j = 0; j < addresses.length; j++) {
245 Address address = addresses[j];
246 mail.setReplyTo((InternetAddress)address);
247 break;
248 }
249 } else {
250 log.debug("Reply-Toアドレスは見つかりませんでした。");
251 }
252 }
253
254 /***
255 * メールの容量(byte)をMimeMessageから取得してReceivedMailにセットします。
256 * 取得に失敗した場合は -1 をセットします。
257 *
258 * @param mm
259 * @param mail
260 */
261 private void setSize(MimeMessage mm, ReceivedMail mail) {
262 try {
263 mail.setSize(mm.getSize());
264 } catch (MessagingException e) {
265 mail.setSize(-1);
266 }
267 }
268
269 /***
270 * @param mm
271 * @param mail
272 * @throws MessagingException
273 */
274 private void setHtmlText(MimeMessage mm, ReceivedMail mail) {
275 try {
276 HtmlPartExtractor hpe = new HtmlPartExtractor();
277 MultipartUtility.process(mm, hpe);
278 String htmlText = hpe.getHtml();
279 mail.setHtmlText(htmlText);
280 } catch (MessagingException e) {
281
282 log.warn(e.getMessage());
283 }
284 }
285
286 private void setText(MimeMessage mm, ReceivedMail mail) {
287 try {
288 String text = MultipartUtility.getPlainText(mm);
289 mail.setText(text);
290 } catch (MessagingException e) {
291
292 log.warn(e.getMessage());
293 }
294 }
295
296 private void setMessageId(MimeMessage mm, ReceivedMail mail) {
297 try {
298 String messageId = mm.getMessageID();
299 mail.setMessageId(messageId);
300 } catch (MessagingException e) {
301
302 log.warn(e.getMessage());
303 }
304 }
305
306 private void setSubject(MimeMessage mm, ReceivedMail mail) {
307 try {
308 String subject = mm.getSubject();
309 mail.setSubject(subject);
310 } catch (MessagingException e) {
311
312 log.warn(e.getMessage());
313 }
314 }
315
316 private void setDate(MimeMessage mm, ReceivedMail mail) {
317 try {
318 Date d = mm.getSentDate();
319 mail.setDate(d);
320 } catch (MessagingException e) {
321
322 log.warn(e.getMessage());
323 }
324 }
325
326 /***
327 * Return-Pathアドレスは必ずしもセットされてはいません。
328 * 特にspam系のメールでは不正なフォーマットのメールアドレスが
329 * セットされている場合もあるので要注意。
330 *
331 * @param mm
332 * @param mail
333 */
334 private void setReturnPath(MimeMessage mm, ReceivedMail mail) {
335 log.debug("Return-Pathアドレスを検出します。");
336 String[] returnPath = null;
337 try {
338 returnPath = mm.getHeader("Return-Path");
339 } catch (MessagingException e) {
340
341 log.warn(e.getMessage());
342 }
343 if (returnPath != null && returnPath.length > 0) {
344 String email = returnPath[0].substring(1, returnPath[0].length() - 1);
345 if (email.length() > 0) {
346 try {
347 mail.setReturnPath(email);
348 log.debug("Return-PathアドレスをMailにセットしました。[Return-Path='" + email + "']");
349 } catch (IllegalArgumentException e) {
350 log.warn("Return-Pathアドレスが不正なメールアドレスフォーマットです。[Return-Path='" + email + "']");
351 }
352 } else {
353 log.debug("Return-Pathアドレスは見つかりませんでした。");
354 }
355 } else {
356 log.debug("Return-Pathアドレスは見つかりませんでした。");
357 }
358 }
359
360 private void setFromAddress(MimeMessage mm, ReceivedMail mail) {
361 log.debug("Fromアドレスを検出します。");
362 Address[] addresses = null;
363 try {
364 addresses = mm.getFrom();
365 } catch (MessagingException e) {
366
367 log.warn(e.getMessage());
368 }
369 if (addresses != null) {
370 log.debug(addresses.length + "つのFromアドレスが見つかりました。");
371 for (int j = 0; j < addresses.length; j++) {
372 InternetAddress address = (InternetAddress)addresses[j];
373 mail.setFrom(address);
374 log.debug("FromアドレスをMailにセットしました。[From='" + address.toUnicodeString() + "']");
375 }
376 } else {
377 log.debug("Fromアドレスは見つかりませんでした。");
378 }
379 }
380
381 private void setRecipientAddresses(MimeMessage mm, ReceivedMail mail) {
382
383
384
385 log.debug("Toアドレスを検出します。");
386 Address[] toAddresses = null;
387 try {
388 toAddresses = mm.getRecipients(Message.RecipientType.TO);
389 } catch (AddressException e) {
390 log.warn("不正なメールアドレスが検出されました。[" + e.getRef() + "]");
391 } catch (MessagingException e) {
392
393 log.warn(e.getMessage());
394 }
395 if (toAddresses != null) {
396 log.debug(toAddresses.length + "つのToアドレスが見つかりました。");
397 for (int j = 0; j < toAddresses.length; j++) {
398 InternetAddress address = (InternetAddress)toAddresses[j];
399 mail.addTo(address);
400 log.debug("ToアドレスをMailにセットしました。[To='" + address.toUnicodeString() + "']");
401 }
402 } else {
403 log.debug("Toアドレスは見つかりませんでした。");
404 }
405
406
407
408
409 log.debug("Ccアドレスを検出します。");
410 Address[] ccAddresses = null;
411 try {
412 ccAddresses = mm.getRecipients(Message.RecipientType.CC);
413 } catch (AddressException e) {
414 log.warn("不正なメールアドレスが検出されました。[" + e.getRef() + "]");
415 } catch (MessagingException e) {
416
417 log.warn(e.getMessage());
418 }
419 if (ccAddresses != null) {
420 log.debug(ccAddresses.length + "つのCcアドレスが見つかりました。");
421 for (int j = 0; j < ccAddresses.length; j++) {
422 InternetAddress address = (InternetAddress)ccAddresses[j];
423 mail.addCc(address);
424 log.debug("CcアドレスをMailにセットしました。[Cc='" + address.toUnicodeString() + "']");
425 }
426 } else {
427 log.debug("Ccアドレスは見つかりませんでした。");
428 }
429 }
430
431 }