/* * Aipo is a groupware program developed by TOWN, Inc. * Copyright (C) 2004-2015 TOWN, Inc. * http://www.aipo.com * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.aimluck.eip.mail; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import java.util.Enumeration; import java.util.GregorianCalendar; import java.util.Properties; import java.util.Random; import java.util.StringTokenizer; import java.util.TimeZone; import javax.mail.Address; import javax.mail.MessagingException; import javax.mail.Session; import javax.mail.internet.AddressException; import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeMessage; import org.apache.jetspeed.services.logging.JetspeedLogFactoryService; import org.apache.jetspeed.services.logging.JetspeedLogger; import com.aimluck.eip.mail.util.ALAttachmentsExtractor; import com.aimluck.eip.mail.util.ALMailUtils; import com.aimluck.eip.mail.util.UnicodeCorrecter; import com.aimluck.eip.services.storage.ALStorageService; import com.sk_jp.mail.MailUtility; import com.sk_jp.mail.MultipartUtility; /** * ローカルに保存するメールを表すクラスです。 <br /> * */ public class ALLocalMailMessage extends MimeMessage implements ALMailMessage { private static final JetspeedLogger logger = JetspeedLogFactoryService .getLogger(ALLocalMailMessage.class.getName()); public static final String MESSAGE_ID = "Message-ID"; public static final String RETURN_PATH = "Return-Path"; public static final String DELIVERED_TO = "Delivered-To"; public static final String RECEIVED = "Received"; public static final String DATE = "Date"; public static final String FROM = "From"; public static final String MIME_VERSION = "MIME-Version"; public static final String TO = "To"; public static final String CC = "Cc"; public static final String BCC = "Bcc"; public static final String SUBJECT = "Subject"; public static final String CONTENT_TYPE = "Content-Type"; public static final String CONTENT_TRANSFER_ENCORDING = "Content-Transfer-Encoding"; public static final String X_Mailer = "X-Mailer"; public static final String X_Mailer_Value = "Groupware Aipo"; public static final String X_AIPO_ATTACHMENT_FILE = "X-AIPO-Attachment-File"; /** 自身をファイルに保存するときのファイル名 */ private String fileName = null; /** HTML メールのファイル名につけるカウンタ */ // private int attachmentHtmlNum = 0; /** * コンストラクタ * * @param source * メール * @param fileName * 自身の保存先のファイル名 * @throws MessagingException */ public ALLocalMailMessage(MimeMessage source, String fileName) throws MessagingException { super(source); this.fileName = fileName; } /** * コンストラクタ * * @param fileName * 自身の保存先のファイル名 * @throws MessagingException */ public ALLocalMailMessage(String fileName) throws MessagingException { super(Session.getDefaultInstance(new Properties())); this.fileName = fileName; } /** * コンストラクタ * * @param session */ public ALLocalMailMessage(Session session) throws MessagingException { super(session); } public ALLocalMailMessage(Session session, java.io.InputStream is) throws MessagingException { super(session, is); } /** * ファイル名を返す. * * @return */ public String getMailMassageFileName() { return fileName; } /** * メールヘッダを追加する. * * @param line * @throws MessagingException */ @Override public void addHeaderLine(String line) throws MessagingException { StringTokenizer st = new StringTokenizer(line, ":"); if (!st.hasMoreTokens()) { return; } String key = st.nextToken(); if (!st.hasMoreTokens()) { return; } String value = st.nextToken().trim(); addHeader(key, value); } /** * 指定されたフォルダからメールを読み込む. * * @param folderPath */ public void readMail(String folderPath) { try { parse(ALStorageService.getFile(folderPath + ALStorageService.separator() + getMailMassageFileName())); } catch (Exception e) { logger.error("ALLocalMailMessage.readMail", e); } } /** * 指定されたフォルダにメールを保存する. * * @param folderPath */ public void saveMail(String folderPath) { try { ALStorageService.createNewFile( getInputStream(), folderPath, getMailMassageFileName()); } catch (Exception e) { logger.error("ALLocalMailMessage.saveMail", e); } } /** * 指定されたフォルダに添付ファイルを保存する. * * @param filePath * @param fileBytes */ public void saveAttachmentFile(String folderPath, String fileName, byte[] fileBytes) { try { ALStorageService.createNewFile( new ByteArrayInputStream(fileBytes), folderPath, fileName); } catch (Exception e) { logger.error("ALLocalMailMessage.saveAttachmentFile", e); } } /** * メールボディ部のテキストを取得する. * * @return */ public String getBodyText() { String text = null; try { String contentType = this.getContentType(); // au iPhone 対策 this.setHeader("Content-Type", contentType .replace("cp932", "Windows-31J")); text = MultipartUtility.getFirstPlainText(this); this.setHeader("Content-Type", contentType); } catch (Exception e) { logger.error("ALLocalMailMessage.getBodyText", e); } return text; } /** * メールの全てのヘッダを取得する. エンコードを変換する. * * @return */ public String getHeader() { StringBuffer sb = new StringBuffer(); try { Enumeration<?> enu = getAllHeaderLines(); while (enu.hasMoreElements()) { String line = (String) enu.nextElement(); sb.append(MailUtility.decodeText(line)).append(ALMailUtils.CR); } } catch (Exception e) { logger.error("ALLocalMailMessage.getHeader", e); return ""; } return UnicodeCorrecter.correctToCP932(sb.toString()); } /** * メールの全てのヘッダ情報を配列として取得する. * * @return */ public String[] getHeaderArray() { return ALMailUtils.getLines(getHeader()); } /** * メールの本文を一行毎に格納した配列を取得する. * * @return */ public String[] getBodyTextArray() { return ALMailUtils.getLines(getBodyText()); } /** * 添付ファイルのファイル名を配列として取得する. * * @return */ public String[] getAttachmentFileNameArray() { String[] filenames = null; ALAttachmentsExtractor h = new ALAttachmentsExtractor(); try { MultipartUtility.process(this, h); filenames = h.getFileNames(); } catch (Exception e) { logger.error("ALLocalMailMessage.getAttachmentFileNameArray", e); return null; } return filenames; } /** * メッセージ ID を独自形式にするためのオーバーライドメソッド. * * @throws MessagingException */ @Override protected void updateHeaders() throws MessagingException { super.updateHeaders(); // メッセージ ID をセット setHeader(MESSAGE_ID, "<" + getMessageId() + ">"); } /** * メッセージ ID を生成する. * * @return */ private String getMessageId() { Calendar cal = new GregorianCalendar(); // 日付を表示形式に変換 SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSS"); sdf.setTimeZone(TimeZone.getDefault()); String time = sdf.format(cal.getTime()); Random random = new Random(cal.getTimeInMillis()); int tmp = random.nextInt(); int randomNumber = (tmp != Integer.MIN_VALUE ? Math.abs(tmp) : Math.abs(tmp + 1)); String smtpHostName = session.getProperty(ALSmtpMailSender.MAIL_SMTP_HOST); StringBuffer messageId = new StringBuffer(); messageId.append(time).append(".").append(randomNumber).append("@").append( smtpHostName); return messageId.toString(); } /** * 件名を取得する. * * @return * @throws MessagingException */ @Override public String getSubject() throws MessagingException { String subject = UnicodeCorrecter.correctToCP932(MailUtility .decodeText(super.getSubject())); if (subject == null || subject.equals("")) { subject = ""; } return subject; } /** * このメールが,HTML メールかを検証する. HTML メールの場合は,true. * * @return */ public boolean isHtmlMail() { try { // 添付ファイルの有無 ALAttachmentsExtractor h = new ALAttachmentsExtractor(); MultipartUtility.process(this, h); boolean hasAttachments = (h.getCount() > 0) ? true : false; return hasAttachments; } catch (Exception e) { logger.error("ALLocalMailMessage.isHtmlMail", e); return false; } } /* * boolean htmlMail = false; try { htmlMail = isHtmlMailSub(this); } catch * (MessagingException me) { htmlMail = false; } catch (IOException e) { * htmlMail = false; } return htmlMail; ? } * * private boolean isHtmlMailSub(Part part) throws IOException, * MessagingException { if (part.isMimeType("text/plain")) { return false; } * else if (part.isMimeType("text/html")) { return true; } else if * (part.isMimeType("multipart/*")) { Multipart mp = (Multipart) * part.getContent(); for (int i = 0; i < mp.getCount(); i++) { if * (isHtmlMailSub(mp.getBodyPart(i))) { return true; } } } * * return false; } * * /** 添付ファイルを含んでいるかを検証する. 含んでいる場合は,true. @return */ public boolean hasAttachments() { ALAttachmentsExtractor h = new ALAttachmentsExtractor(); try { MultipartUtility.process(this, h); if (h.getCount() > 0) { return true; } } catch (Exception e) { logger.error("ALLocalMailMessage.hasAttachments", e); return false; } return false; } /** * 指定したインデックスのコンテンツの InputStream を取得する. * * @param attachmentIndex * @return */ public InputStream getInputStream(int attachmentIndex) { InputStream in = null; ALAttachmentsExtractor h = new ALAttachmentsExtractor(); try { MultipartUtility.process(this, h); in = h.getInputStream(attachmentIndex); } catch (Exception e) { logger.error("ALLocalMailMessage.getInputStream", e); return null; } return in; } /** * 指定したインデックスの添付ファイル名を取得する. * * @param attachmentIndex * @return */ public String getFileName(int attachmentIndex) { String filename = null; ALAttachmentsExtractor h = new ALAttachmentsExtractor(); try { MultipartUtility.process(this, h); filename = h.getFileName(attachmentIndex); } catch (Exception e) { logger.error("ALLocalMailMessage.getFileName", e); return null; } return filename; } @Override public int getSize() { byte[] b = null; ByteArrayOutputStream output = new ByteArrayOutputStream(); try { writeTo(output); b = output.toByteArray(); } catch (Exception e) { logger.error("ALLocalMailMessage.getSize", e); return -1; } finally { try { output.close(); } catch (IOException ioee) { return -1; } } return b.length; } /** * メールの送信日時を取得する. * * @return * @throws MessagingException */ @Override public Date getSentDate() throws MessagingException { return MailUtility.parseDate(getHeader(DATE, null)); } /** * 受信したメールの TO,CC,BCC のフォーマットが RFC に則っていない場合には, 独自処理で対処する: ・「 * <user@domain」や「user@domain>」のように片方のカッコのみ見つかった場合の処理 ・「 < <user@domain>」や「 * <user@domain>>」のようにカッコの対が揃っていない場合の処理 * * @param recipienttype * @return * @throws MessagingException */ @Override public Address[] getRecipients(javax.mail.Message.RecipientType recipienttype) throws MessagingException { // RFC に則っているかを検証する. String recipients = MailUtility.decodeText(this.getHeader(recipienttype.toString(), null)); if (recipients == null) { return super.getRecipients(recipienttype); } StringTokenizer st = new StringTokenizer(recipients, ",;"); String token = null; boolean found = false; while (st.hasMoreTokens()) { token = st.nextToken(); if ((token.indexOf('<') >= 0 && token.indexOf('>') == -1) || (token.indexOf('<') == -1 && token.indexOf('>') >= 0) // 「<user@domain」や「user@domain>」のように // 片方のカッコのみ見つかった場合の処理 || (token.indexOf('(') >= 0 && token.indexOf('>') == -1) || (token.indexOf('(') == -1 && token.indexOf(')') >= 0)) { // 「foo) <user@domain>」や「(foo <user@domain>」のように // 片方のカッコのみ見つかった場合の処理 found = true; } else { if ((token.indexOf('<') >= 0 && token.indexOf('<') != token .lastIndexOf('<')) || (token.indexOf('>') >= 0 && token.indexOf('>') != token .lastIndexOf('>')) // 「<<user@domain>」や「<user@domain>>」のように // カッコの対が揃っていない場合の処理 || (token.indexOf('(') >= 0 && token.indexOf(')') != token .lastIndexOf('(')) || (token.indexOf(')') >= 0 && token.indexOf(')') != token .lastIndexOf(')'))) { // 「((foo) <user@domain>」や「(foo)) <user@domain>」のように // カッコの対が揃っていない場合の処理 found = true; } } } if (found) { int index = 0; st = new StringTokenizer(recipients, ",;"); Address[] addresses = new InternetAddress[st.countTokens()]; while (st.hasMoreTokens()) { token = st.nextToken(); try { addresses[index] = new InternetAddress(token, false); } catch (AddressException ae) { addresses[index] = new InternetAddress(); if (index > 0 && token.contains("\r\n\t")) { token = token.substring(4); } ((InternetAddress) addresses[index]).setAddress(token); } index++; } return addresses; } else { return super.getRecipients(recipienttype); } } /** * TO,CC,BCC のフォーマットをチェックしない場合は strict = false * * @param recipienttype * @param strict * @return * @throws MessagingException */ public Address[] getRecipients( javax.mail.Message.RecipientType recipienttype, boolean strict) throws MessagingException { if (strict) { return getRecipients(recipienttype); } else { Address[] addresses; try { addresses = super.getRecipients(recipienttype); } catch (AddressException e) { String recipients = MailUtility .decodeText(this.getHeader(recipienttype.toString(), null)); if (recipients == null) { return super.getRecipients(recipienttype); } int index = 0; String token = null; StringTokenizer st = new StringTokenizer(recipients, ",;"); addresses = new InternetAddress[st.countTokens()]; while (st.hasMoreTokens()) { token = st.nextToken(); try { addresses[index] = new InternetAddress(token, false); } catch (AddressException ae) { addresses[index] = new InternetAddress(); ((InternetAddress) addresses[index]).setAddress(token); } index++; } } return addresses; } } @Override public void clearContents() { } }