View Javadoc

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 			// ignore
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 			// ignore
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 			// ignore
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 			// ignore
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 			// ignore
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 			// ignore
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 			// ignore
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 			// ignore
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 			// ignore
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 			// ignore
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 		 * TOアドレスのパース
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 			// ignore
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 		 * CCアドレスのパース
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 			// ignore
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 }