OSDN Git Service

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