/*
* 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.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Date;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.activation.FileTypeMap;
import javax.mail.AuthenticationFailedException;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMultipart;
import org.apache.jetspeed.services.logging.JetspeedLogFactoryService;
import org.apache.jetspeed.services.logging.JetspeedLogger;
import com.aimluck.eip.mail.util.ALMailUtils;
import com.aimluck.eip.services.storage.ALStorageService;
import com.sk_jp.mail.MailUtility;
import com.sun.mail.smtp.SMTPTransport;
/**
* メール送信(SMTP)を操作する抽象クラスです。 <br />
*
*/
public abstract class ALSmtpMailSender implements ALMailSender {
private static final JetspeedLogger logger = JetspeedLogFactoryService
.getLogger(ALSmtpMailSender.class.getName());
/** <code>AUTH_SEND_NONE</code> 送信時の認証方式(認証なし) */
public static final int AUTH_SEND_NONE = 0;
/** <code>AUTH_SEND_POP_BEFORE_SMTP</code> 送信時の認証方式(POP before SMTP) */
public static final int AUTH_SEND_POP_BEFORE_SMTP = 1;
/** <code>AUTH_SEND_SMTP_AUTH</code> 送信時の認証方式(SMTP 認証) */
public static final int AUTH_SEND_SMTP_AUTH = 2;
/** <code>AUTH_SEND_SSL_AUTH</code> 送信時の認証方式(暗号化なし) */
public static final int ENCRYPTION_SEND_NONE = 0;
/** <code>AUTH_SEND_SSL_AUTH</code> 送信時の認証方式(SSL暗号化) */
public static final int ENCRYPTION_SEND_SSL = 1;
/** メール送信時の処理結果(送信に成功した) */
public static final int SEND_MSG_SUCCESS = 0;
/** メール送信時の処理結果(送信に失敗した) */
public static final int SEND_MSG_FAIL = 1;
/** メール送信時の処理結果(メールサイズが送信可能サイズよりも大きいため,送信に失敗した) */
public static final int SEND_MSG_OVER_MAIL_MAX_SIZE = 2;
/** メール送信時の処理結果(ロックがかかっていて,送信に失敗した) */
public static final int SEND_MSG_LOCK = 3;
/** メール送信時の処理結果(Pop before SMTPの認証失敗で送信に失敗した) */
public static final int SEND_MSG_FAIL_POP_BEFORE_SMTP_AUTH = 4;
/** メール送信時の処理結果(SMTP認証の認証失敗で送信に失敗した) */
public static final int SEND_MSG_FAIL_SMTP_AUTH = 5;
/** メール送信時の処理結果(管理者のメールアカウントが設定されていないために送信に失敗した) */
public static final int SEND_MSG_FAIL_NO_ACCOUNT = 6;
/** 接続時のタイムアウト時間 */
private final String CONNECTION_TIMEOUT = "120000";
/** 接続後のタイムアウト時間 */
private final String TIMEOUT = "420000";
/** SMTP サーバ */
public static final String MAIL_SMTP_HOST = "mail.smtp.host";
/** SMTP サーバのポート番号 */
public static final String MAIL_SMTP_PORT = "mail.smtp.port";
/** SMTP サーバとの接続時のタイムアウト */
public static final String MAIL_SMTP_CONNECTION_TIMEOUT =
"mail.stmp.connectiontimeout";
/** SMTP サーバとの接続後のタイムアウト */
public static final String MAIL_SMTP_TIMEOUT = "mail.stmp.timeout";
/** 文字コード(ISO-2022-JP) */
public static final String CHARSET_ISO2022JP = "iso-2022-jp";
/** SMTP サーバへの接続情報 */
protected Properties smtpServerProp = null;
/** POP BEFORE SMTP の WAIT 時間 (ms) */
protected long POP_BEFORE_SMTP_WAIT_TIME = 1000;
/** SSL ファクトリー */
public static final String SSL_FACTORY = "javax.net.ssl.SSLSocketFactory";
/** 送信用セッション */
protected Session session = null;
protected ALSmtpMailSenderContext scontext = null;
public ALSmtpMailSender(ALMailSenderContext scontext) {
this.scontext = (ALSmtpMailSenderContext) scontext;
}
/**
* 新規作成のメールを取得します。
*
* @param mcontext
* @return
*/
private ALLocalMailMessage createMessage(ALSmtpMailContext mcontext) {
System.setProperty("mail.mime.charset", "ISO-2022-JP");
System.setProperty("mail.mime.decodetext.strict", "false");
ALLocalMailMessage msg = null;
smtpServerProp = new Properties();
smtpServerProp.setProperty(MAIL_SMTP_HOST, scontext.getSmtpHost());
smtpServerProp.setProperty(MAIL_SMTP_PORT, scontext.getSmtpPort());
smtpServerProp.put(MAIL_SMTP_CONNECTION_TIMEOUT, CONNECTION_TIMEOUT);
smtpServerProp.put(MAIL_SMTP_TIMEOUT, TIMEOUT);
smtpServerProp.setProperty("mail.mime.address.strict", "false");
smtpServerProp.put("mail.smtp.localhost", "localhost");
if (scontext.getEncryptionFlag() == ENCRYPTION_SEND_SSL) {
/** SSL 暗号化 */
smtpServerProp.setProperty("mail.smtp.socketFactory.class", SSL_FACTORY);
smtpServerProp.setProperty("mail.smtp.socketFactory.fallback", "false");
smtpServerProp.setProperty("mail.smtp.socketFactory.port", scontext
.getSmtpPort());
}
// SMTP サーバのアドレスをセット
if (scontext.getAuthSendFlag() == AUTH_SEND_SMTP_AUTH
&& scontext.getAuthSendUserId() != null
&& !"".equals(scontext.getAuthSendUserId())
&& scontext.getAuthSendUserPassword() != null
&& !"".equals(scontext.getAuthSendUserPassword())) {
/** SMTP AUTH */
smtpServerProp.put("mail.smtp.auth", "true");
session =
Session.getInstance(smtpServerProp, new ALSmtpAuth(scontext
.getAuthSendUserId(), scontext.getAuthSendUserPassword()));
} else {
session = Session.getInstance(smtpServerProp, null);
}
// デバックモード
// session.setDebug(true);
try {
// メッセージを生成
msg = new ALLocalMailMessage(session);
// 送信元メールアドレスと送信者名をセット
msg.setFrom(new InternetAddress(mcontext.getFrom(), ALMailUtils
.encodeWordJIS(mcontext.getName())));
// メールのあて先(to)をセット
if (mcontext.getTo() == null) {
throw new MessagingException();
}
setRecipient(msg, Message.RecipientType.TO, mcontext.getTo());
// メールのあて先(cc)をセット
if (mcontext.getCc() != null) {
setRecipient(msg, Message.RecipientType.CC, mcontext.getCc());
}
// メールのあて先(bcc)をセット
if (mcontext.getBcc() != null) {
setRecipient(msg, Message.RecipientType.BCC, mcontext.getBcc());
}
// メールの件名をセット
msg.setSubject(ALMailUtils.encodeWordJIS(mcontext.getSubject()));
// メールの送信日時をセット
msg.setSentDate(new Date());
if (mcontext.getFilePaths() == null) {
// メールの本文をセット
msg.setText(mcontext.getMsgText() + "\r\n", CHARSET_ISO2022JP);
setHeader(msg, mcontext);
} else {
String[] checkedFilePaths = mcontext.getFilePaths();
int checkedFilePathsLength = checkedFilePaths.length;
if (checkedFilePathsLength <= 0) {
// MultiPart にせず,メールの本文をセット
msg.setText(mcontext.getMsgText() + "\r\n", CHARSET_ISO2022JP);
setHeader(msg, mcontext);
} else {
setHeader(msg, mcontext);
// 複数のボディを格納するマルチパートオブジェクトを生成
Multipart multiPart = new MimeMultipart();
// テキストのボディパートを作成
MimeBodyPart mimeText = new MimeBodyPart();
// メールの内容を指定
mimeText.setText(mcontext.getMsgText(), CHARSET_ISO2022JP);
// 1つ目のボディパートを追加
multiPart.addBodyPart(mimeText);
// 添付ファイルのボディパートを作成
MimeBodyPart mimeFile = null;
for (int i = 0; i < checkedFilePathsLength; i++) {
final String filePath = checkedFilePaths[i];
final String fileName =
ALMailUtils.getFileNameFromText(checkedFilePaths[i]);
mimeFile = new MimeBodyPart();
mimeFile.setDataHandler(new DataHandler(new DataSource() {
@Override
public String getContentType() {
return FileTypeMap.getDefaultFileTypeMap().getContentType(
fileName);
}
@Override
public InputStream getInputStream() throws IOException {
return ALStorageService.getFile(filePath);
}
@Override
public String getName() {
return fileName;
}
@Override
public OutputStream getOutputStream() throws IOException {
throw new UnsupportedOperationException("getOutputStream");
}
}));
MailUtility.setFileName(mimeFile, ALMailUtils
.getFileNameFromText(checkedFilePaths[i]), "ISO-2022-JP", null);
// 添付ファイルをボディパートに追加
multiPart.addBodyPart(mimeFile);
}
// マルチパートオブジェクトをメッセージに設定
msg.setContent(multiPart);
}
}
} catch (Exception e) {
logger.error("ALSmtpMailSender.createMessage", e);
return null;
}
return msg;
}
private void setHeader(ALLocalMailMessage msg, ALSmtpMailContext mcontext)
throws Exception {
// メールの形式をセット
msg.setHeader(
ALLocalMailMessage.CONTENT_TYPE,
"text/plain; charset=ISO-2022-JP");
msg.setHeader(ALLocalMailMessage.CONTENT_TRANSFER_ENCORDING, "7bit");
msg.setHeader(
ALLocalMailMessage.X_Mailer,
ALLocalMailMessage.X_Mailer_Value);
// 追加ヘッダをセットする
Map<String, String> headers = mcontext.getAdditionalHeaders();
if (headers != null && !headers.isEmpty()) {
synchronized (headers) {
String key = null;
String value = null;
Map.Entry<String, String> entry = null;
for (Iterator<Map.Entry<String, String>> i =
headers.entrySet().iterator(); i.hasNext();) {
entry = i.next();
key = entry.getKey();
value = entry.getValue();
msg.setHeader(key, value);
}
}
}
}
/**
* SMTP サーバへメールを送信する.
*
* @param to
* @param cc
* @param bcc
* @param from
* @param name
* @param subject
* @param msgText
* @param filePaths
* @return
*/
@Override
public int send(ALMailContext context) {
try {
ALSmtpMailContext mcontext = (ALSmtpMailContext) context;
ALLocalMailMessage msg = createMessage(mcontext);
if (msg == null) {
return SEND_MSG_FAIL;
}
int mailSize = msg.getSize();
if (mailSize > ALMailUtils.getMaxMailSize()) {
// メール容量のチェック
return SEND_MSG_OVER_MAIL_MAX_SIZE;
}
if (scontext.getAuthSendFlag() == AUTH_SEND_NONE) {
Transport.send(msg);
} else if (scontext.getAuthSendFlag() == AUTH_SEND_POP_BEFORE_SMTP) {
// POP before SMTP を実行する.
// 認証のみ検証する.メールは受信しない.
boolean success =
ALPop3MailReceiver.isAuthenticatedUser(
scontext.getPop3Host(),
scontext.getPop3Port(),
scontext.getPop3UserId(),
scontext.getPop3UserPasswd(),
scontext.getPop3EncryptionFlag());
if (!success) {
return SEND_MSG_FAIL_POP_BEFORE_SMTP_AUTH;
} else {
Thread.sleep(POP_BEFORE_SMTP_WAIT_TIME);
}
Transport.send(msg);
} else if (scontext.getAuthSendFlag() == AUTH_SEND_SMTP_AUTH) {
Transport transport = session.getTransport("smtp");
SMTPTransport smtpt = (SMTPTransport) transport;
smtpt.setSASLRealm("localhost"); // [SASLレルム]
smtpt.connect(
scontext.getSmtpHost(),
scontext.getAuthSendUserId(),
scontext.getAuthSendUserPassword());
smtpt.sendMessage(msg, msg.getAllRecipients());
smtpt.close();
}
// メッセージの保存
ALFolder sendFolder = getALFolder();
sendFolder.saveMail(msg, null);
} catch (AuthenticationFailedException ex) {
logger.error("ALSmtpMailSender.send", ex);
return SEND_MSG_FAIL_SMTP_AUTH;
} catch (Exception ex) {
logger.error("ALSmtpMailSender.send", ex);
return SEND_MSG_FAIL;
} catch (Throwable e) {
logger.error("ALSmtpMailSender.send", e);
return SEND_MSG_FAIL;
}
return SEND_MSG_SUCCESS;
}
abstract protected ALFolder getALFolder();
/**
* メールの宛名をセットする.
*
* @param msg
* @param recipientType
* @param addrString
* @throws AddressException
* @throws MessagingException
*/
private void setRecipient(Message msg, Message.RecipientType recipientType,
String[] addrString) throws AddressException, MessagingException {
if (addrString == null) {
return;
}
int addrStringLength = addrString.length;
InternetAddress[] address = new InternetAddress[addrStringLength];
for (int i = 0; i < addrStringLength; i++) {
address[i] = ALMailUtils.getInternetAddress(addrString[i]);
}
msg.setRecipients(recipientType, address);
}
}