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 }