/*
* 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.util.ArrayList;
import java.util.BitSet;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Properties;
import javax.mail.AuthenticationFailedException;
import javax.mail.Flags;
import javax.mail.Folder;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Store;
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.ALMailUtils;
import com.aimluck.eip.mail.util.ALStaticObject;
import com.sun.mail.pop3.POP3Folder;
import com.sun.mail.pop3.POP3Message;
/**
* メール受信(POP3)を操作する抽象クラスです。 <br />
*
*/
public abstract class ALPop3MailReceiver implements ALMailReceiver {
private static final JetspeedLogger logger = JetspeedLogFactoryService
.getLogger(ALPop3MailReceiver.class.getName());
/** <code>AUTH_RECEIVE_NORMAL</code> 受信時の認証方式(標準) */
public static final int AUTH_RECEIVE_NORMAL = 0;
/** <code>AUTH_RECEIVE_APOP</code> 受信時の認証方式(APOP) */
public static final int AUTH_RECEIVE_APOP = 1;
/** <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 RECEIVE_MSG_FAIL_LOCKED = -2;
/** メール受信時の処理結果(メールサイズが受信可能サイズよりも大きいため,受信に失敗した) */
public static final int RECEIVE_MSG_FAIL_OVER_MAIL_MAX_SIZE = -3;
/** メール受信時の処理結果(POP3 サーバと接続できず,受信に失敗した) */
public static final int RECEIVE_MSG_FAIL_CONNECT = -4;
/** メール受信時の処理結果(POP3 サーバとの認証に失敗し,受信に失敗した) */
public static final int RECEIVE_MSG_FAIL_AUTH = -5;
/** メール受信時の処理結果(Exception のエラーが発生し,受信に失敗した) */
public static final int RECEIVE_MSG_FAIL_EXCEPTION = -6;
/** メール受信時の処理結果(OutOfMemory のエラーが発生し,受信に失敗した) */
public static final int RECEIVE_MSG_FAIL_OUTOFMEMORY = -7;
/** 接続時のタイムアウト時間 */
private static final String CONNECTION_TIMEOUT = "60000";
/** 接続後のタイムアウト時間 */
private static final String TIMEOUT = "300000";
/** UIDに追記する件数 */
private static final int UID_ADD_NUM = 100;
protected ALPop3MailReceiverContext rcontext;
/** SSL ファクトリー */
public static final String SSL_FACTORY = "javax.net.ssl.SSLSocketFactory";
/** 現在の受信メール数 */
private int receivedMailNum = 0;
/** 現在の受信可能メール総数 */
private int mailNumOnServer = 0;
/**
* コンストラクタ
*
* @param rcontext
*/
public ALPop3MailReceiver(ALMailReceiverContext rcontext) {
this.rcontext = (ALPop3MailReceiverContext) rcontext;
}
protected abstract ALFolder getALFolder();
/**
* POP3 サーバからメールを受信する.
*
* @param delete
* 受信したメールを削除するかどうか.削除する場合は,true.
* @param enableSavingDays
* 指定日数を超えたメールを削除するかどうか.削除する場合は,true.
* @param savingDays
* メール削除の指定日数
* @param denyReceivedMail
* 受信済みメッセージを取り込まない場合は,true.
* @param authReceiveFlag
* 受信時の認証方式
* @throws Exception
*/
@Override
public int receive(String orgId) throws Exception {
// POP3 サーバ上のストア
Store pop3Store = null;
// POP3 サーバ上のフォルダ
POP3Folder pop3Folder = null;
// ローカルストア
ALFolder receiveFolder = null;
// このセッション中のメールの UID の一覧
List<String> newUIDL = null;
// このセッションで保存したメールの UID の一覧
List<String> receivedUIDL = new ArrayList<String>();
// 今,受信しているメールの UID
String nowReceivedUID = null;
boolean overMailMaxSize = false;
// 一通のメールの受信を完了したかのフラグ
boolean finishedReceiving = false;
try {
// POP3 サーバへの接続
Session session =
getSession(rcontext.getAuthReceiveFlag(), rcontext.getEncryptionFlag());
pop3Store = session.getStore("pop3");
pop3Store
.connect(rcontext.getPop3Host(), Integer.parseInt(rcontext
.getPop3Port()), rcontext.getPop3UserId(), rcontext
.getPop3UserPasswd());
if (!pop3Store.isConnected()) {
// POP3 サーバへの接続失敗時の処理
close(pop3Store, pop3Folder, receiveFolder);
return RECEIVE_MSG_FAIL_CONNECT;
}
// POP3 サーバ上のメールフォルダを開く
pop3Folder = (POP3Folder) pop3Store.getFolder("INBOX");
if (pop3Folder == null) {
close(pop3Store, null, receiveFolder);
return RECEIVE_MSG_FAIL;
}
pop3Folder.open(Folder.READ_WRITE);
// ローカルストアに接続
receiveFolder = getALFolder();
if (receiveFolder == null) {
close(pop3Store, pop3Folder, null);
return RECEIVE_MSG_FAIL;
}
// POP3 サーバに保存されているメッセージ数を取得
int totalMessages = pop3Folder.getMessageCount();
if (totalMessages == 0) {
// 新着メール数を保存する.
receiveFolder.setNewMailNum(0);
close(pop3Store, pop3Folder, receiveFolder);
receivedMailNum = 0;
return receivedMailNum;
}
// 保存してある UID の一覧を取得
List<String> oldUIDL = receiveFolder.loadUID();
// 受信するメールの UID の一覧を取得
newUIDL = new ArrayList<String>();
Message[] messages = pop3Folder.getMessages();
String uid = null;
totalMessages = messages.length;
for (int i = 0; i < totalMessages; i++) {
uid = pop3Folder.getUID(messages[i]);
if (uid == null) {
String[] xuidls = messages[i].getHeader("X-UIDL");
if (xuidls != null && xuidls.length > 0) {
uid = xuidls[0];
} else {
uid = ((MimeMessage) messages[i]).getMessageID();
}
}
newUIDL.add(uid);
}
// UID の差分を取得
BitSet retrieveFlags = new BitSet();
if (rcontext.getDenyReceivedMail()) {
for (int i = 0; i < totalMessages; i++) {
if (!oldUIDL.contains(newUIDL.get(i))) {
retrieveFlags.set(i);
mailNumOnServer++;
} else {
receivedUIDL.add(newUIDL.get(i));
}
}
} else {
for (int i = 0; i < totalMessages; i++) {
retrieveFlags.set(i);
}
mailNumOnServer = totalMessages;
}
oldUIDL.clear();
newUIDL.clear();
ALStaticObject.getInstance().updateAccountStat(
rcontext.getAccountId(),
ALPop3MailReceiveThread.KEY_RECEIVE_MAIL_ALL_NUM,
Integer.valueOf(mailNumOnServer));
// 現時点から指定日数を引いた日時を取得.
Calendar cal = Calendar.getInstance();
cal.setTime(new Date());
cal.set(Calendar.DATE, cal.get(Calendar.DATE) - rcontext.getSavingDays());
Date limitDate = cal.getTime();
MimeMessage tmpMessage = null;
ALMailMessage alPop3MailMessage = null;
for (int i = 0; i < totalMessages; i++) {
finishedReceiving = false;
tmpMessage = (MimeMessage) pop3Folder.getMessage(i + 1);
// 新着メールであるかを確認
if (retrieveFlags.get(i)) {
nowReceivedUID = pop3Folder.getUID(tmpMessage);
if (nowReceivedUID == null) {
String[] xuidls = tmpMessage.getHeader("X-UIDL");
if (xuidls != null && xuidls.length > 0) {
nowReceivedUID = xuidls[0];
} else {
nowReceivedUID = tmpMessage.getMessageID();
}
}
// (新着メール)メールを受信し保存する.
alPop3MailMessage =
new ALPop3Message((POP3Message) tmpMessage, i + 1);
if (tmpMessage.getSize() <= ALMailUtils.getMaxMailSize()) {
// 受信可能なメール容量以下であれば,登録する.
if (receiveFolder.saveMail(alPop3MailMessage, orgId)) {
if (rcontext.getDelete()) {
// 受信したメールを POP3 サーバ上から削除する
tmpMessage.setFlag(Flags.Flag.DELETED, rcontext.getDelete());
} else {
if (rcontext.getEnableSavingDays()) {
// 指定日数を過ぎたメールを削除する(ヘッダ Received の日付で判断する).
Date receivedDate = ALMailUtils.getReceivedDate(tmpMessage);
if (receivedDate != null && receivedDate.before(limitDate)) {
// 受信したメールを POP3 サーバ上から削除する
tmpMessage.setFlag(Flags.Flag.DELETED, true);
}
}
}
receivedUIDL.add(nowReceivedUID);
receivedMailNum++;
}
} else {
if (receiveFolder.saveDefectiveMail(alPop3MailMessage, orgId)) {
receivedUIDL.add(nowReceivedUID);
receivedMailNum++;
}
overMailMaxSize = true;
}
ALStaticObject.getInstance().updateAccountStat(
rcontext.getAccountId(),
ALPop3MailReceiveThread.KEY_RECEIVE_MAIL_NUM,
Integer.valueOf(receivedMailNum));
if (receivedMailNum % UID_ADD_NUM == 0 && receivedMailNum != 0) {
receiveFolder.saveUID(receivedUIDL);
}
}
finishedReceiving = true;
}
// POP3 サーバとの接続を閉じる
close(pop3Store, pop3Folder, null);
} catch (AuthenticationFailedException ae) {
logger.error("ALPop3MailReceiver.receive", ae);
// 受信済みの最新の UID の一覧を保存する.
if (!finishedReceiving) {
receivedUIDL.remove(nowReceivedUID);
}
receiveFolder.saveUID(receivedUIDL);
if (receivedUIDL != null) {
receivedUIDL.clear();
}
// 新着メール数を保存する.
receiveFolder.setNewMailNum(receivedMailNum);
// サーバとのコネクションを切断する.
close(pop3Store, pop3Folder, receiveFolder);
return RECEIVE_MSG_FAIL_AUTH;
} catch (MessagingException me) {
logger.error("ALPop3MailReceiver.receive", me);
// 受信済みの最新の UID の一覧を保存する.
if (!finishedReceiving) {
receivedUIDL.remove(nowReceivedUID);
}
receiveFolder.saveUID(receivedUIDL);
if (receivedUIDL != null) {
receivedUIDL.clear();
}
// 新着メール数を保存する.
receiveFolder.setNewMailNum(receivedMailNum);
// サーバとのコネクションを切断する.
close(pop3Store, pop3Folder, receiveFolder);
return RECEIVE_MSG_FAIL_CONNECT;
} catch (Exception e) {
logger.error("ALPop3MailReceiver.receive", e);
// 受信済みの最新の UID の一覧を保存する.
if (!finishedReceiving) {
receivedUIDL.remove(nowReceivedUID);
}
receiveFolder.saveUID(receivedUIDL);
if (receivedUIDL != null) {
receivedUIDL.clear();
}
// 新着メール数を保存する.
receiveFolder.setNewMailNum(receivedMailNum);
// サーバとのコネクションを切断する.
close(pop3Store, pop3Folder, receiveFolder);
return RECEIVE_MSG_FAIL_EXCEPTION;
} catch (Throwable t) {
logger.error("ALPop3MailReceiver.receive", t);
// 受信済みの最新の UID の一覧を保存する.
if (!finishedReceiving) {
receivedUIDL.remove(nowReceivedUID);
}
receiveFolder.saveUID(receivedUIDL);
if (receivedUIDL != null) {
receivedUIDL.clear();
}
// 新着メール数を保存する.
receiveFolder.setNewMailNum(receivedMailNum);
// サーバとのコネクションを切断する.
close(pop3Store, pop3Folder, receiveFolder);
return RECEIVE_MSG_FAIL_OUTOFMEMORY;
}
try {
// 受信済みの最新の UID の一覧を保存する.
receiveFolder.saveUID(receivedUIDL);
if (receivedUIDL != null) {
receivedUIDL.clear();
}
// 新着メール数を保存する.
receiveFolder.setNewMailNum(receivedMailNum);
// ローカルフォルダを閉じる.
receiveFolder.close();
} catch (Exception e) {
logger.error("ALPop3MailReceiver.receive", e);
return RECEIVE_MSG_FAIL_EXCEPTION;
} catch (Throwable t) {
logger.error("ALPop3MailReceiver.receive", t);
return RECEIVE_MSG_FAIL_OUTOFMEMORY;
}
if (overMailMaxSize) {
return RECEIVE_MSG_FAIL_OVER_MAIL_MAX_SIZE;
}
return RECEIVE_MSG_SUCCESS;
}
/**
* POP3 サーバとの接続を終了する.
*/
public void close(Store pop3Store, Folder pop3Folder, ALFolder receiveFolder) {
try {
if (pop3Folder != null && pop3Folder.isOpen()) {
pop3Folder.close(true);
}
if (pop3Store != null) {
pop3Store.close();
}
if (receiveFolder != null) {
receiveFolder.close();
}
} catch (Exception e) {
logger.error("ALPop3MailReceiver.close", e);
}
}
/**
* Pop3 before SMTP 用のユーザ認証.
*
* @param pop3Host
* @param pop3Port
* @param pop3UserId
* @param pop3UserPasswd
* @param pop3EncryptionFlag
* @return
*/
public static boolean isAuthenticatedUser(String pop3Host, String pop3Port,
String pop3UserId, String pop3UserPasswd, int pop3EncryptionFlag) {
boolean res = false;
Store pop3Store = null;
try {
Properties props = new Properties();
// POP3 サーバへの接続
if (pop3EncryptionFlag == ENCRYPTION_SEND_SSL) {
/** SSL 暗号化 */
props.setProperty("mail.pop3.socketFactory.class", SSL_FACTORY);
props.setProperty("mail.pop3.socketFactory.fallback", "false");
props.setProperty("mail.pop3.port", pop3Port);
props.setProperty("mail.pop3.socketFactory.port", pop3Port);
}
props.setProperty("mail.pop3.connectiontimeout", CONNECTION_TIMEOUT);
props.setProperty("mail.pop3.timeout", TIMEOUT);
if (pop3EncryptionFlag == AUTH_RECEIVE_APOP) {
props.setProperty("mail.pop3.apop.enable", "true");
}
Session session = Session.getInstance(props, null);
pop3Store = session.getStore("pop3");
pop3Store.connect(
pop3Host,
Integer.parseInt(pop3Port),
pop3UserId,
pop3UserPasswd);
res = pop3Store.isConnected();
} catch (Exception ex) {
logger.error("ALPop3MailReceiver.isAuthenticatedUser", ex);
res = false;
} finally {
if (pop3Store != null && pop3Store.isConnected()) {
try {
pop3Store.close();
} catch (MessagingException ex) {
logger.error("ALPop3MailReceiver.isAuthenticatedUser", ex);
res = false;
}
}
}
return res;
}
/**
* 新着メール数を取得する.
*
* @param denyReceivedMail
* 受信済みメッセージを取り込まない場合は,true.
* @param authReceiveFlag
* 受信時の認証方式
* @return
*/
@Override
public int getNewMailSum() {
// POP3 サーバ上のストア
Store pop3Store = null;
// POP3 サーバ上のフォルダ
POP3Folder pop3Folder = null;
// ローカルストア
ALFolder receiveFolder = null;
int newMailSum = -1;
try {
// POP3 サーバへの接続
Session session =
getSession(rcontext.getAuthReceiveFlag(), rcontext.getEncryptionFlag());
pop3Store = session.getStore("pop3");
pop3Store
.connect(rcontext.getPop3Host(), Integer.parseInt(rcontext
.getPop3Port()), rcontext.getPop3UserId(), rcontext
.getPop3UserPasswd());
// POP3 サーバ上のメールフォルダを開く
pop3Folder = (POP3Folder) pop3Store.getFolder("INBOX");
if (pop3Folder == null) {
// POP3 サーバとの接続を閉じる
pop3Store.close();
return -1;
}
pop3Folder.open(Folder.READ_WRITE);
// ローカルストアに接続
receiveFolder = getALFolder();
if (receiveFolder == null) {
// POP3 サーバとの接続を閉じる
pop3Folder.close(true);
pop3Store.close();
return -1;
}
// POP3 サーバに保存されているメッセージ数を取得
int totalMessages = pop3Folder.getMessageCount();
if (totalMessages == 0) {
// POP3 サーバとの接続を閉じる
close(pop3Store, pop3Folder, receiveFolder);
return 0;
}
if (!rcontext.getDenyReceivedMail()) {
// POP3 サーバとの接続を閉じる
close(pop3Store, pop3Folder, receiveFolder);
// 受信済みメッセージを取り込む場合,
// POP3 サーバ上に存在するメール数を新着メール数として返す.
return totalMessages;
}
// ローカルに保存してある UID の一覧を取得
List<String> oldUIDL = receiveFolder.loadUID();
// POP3 サーバから UID を取得
// 受信するメールの UID の一覧を取得
List<String> newUIDL = new ArrayList<String>();
Message[] messages = pop3Folder.getMessages();
String uid = null;
totalMessages = messages.length;
for (int i = 0; i < totalMessages; i++) {
uid = pop3Folder.getUID(messages[i]);
if (uid == null) {
String[] xuidls = messages[i].getHeader("X-UIDL");
if (xuidls != null && xuidls.length > 0) {
uid = xuidls[0];
} else {
uid = ((MimeMessage) messages[i]).getMessageID();
}
}
newUIDL.add(uid);
}
// UID の差分から新着数を取得
newMailSum = 0;
for (int i = 0; i < totalMessages; i++) {
if (!oldUIDL.contains(newUIDL.get(i))) {
newMailSum++;
}
}
// POP3 サーバとの接続を閉じる
close(pop3Store, pop3Folder, receiveFolder);
} catch (MessagingException e) {
logger.error("ALPop3MailReceiver.getNewMailSum", e);
// POP3 サーバとの接続を閉じる
close(pop3Store, pop3Folder, null);
return -1;
}
return newMailSum;
}
/**
*
* @param authReceiveFlag
* 受信時の認証方式
* @param encryptFlag
* 暗号化方式
* @return
*/
private Session getSession(int authReceiveFlag, int encryptFlag) {
Properties props = new Properties();
// POP3 サーバへの接続
if (encryptFlag == ENCRYPTION_SEND_SSL) {
/** SSL 暗号化 */
props.setProperty("mail.pop3.socketFactory.class", SSL_FACTORY);
props.setProperty("mail.pop3.socketFactory.fallback", "false");
props.setProperty("mail.pop3.port", rcontext.getPop3Port());
props.setProperty("mail.pop3.socketFactory.port", rcontext.getPop3Port());
}
props.setProperty("mail.pop3.connectiontimeout", CONNECTION_TIMEOUT);
props.setProperty("mail.pop3.timeout", TIMEOUT);
if (authReceiveFlag == AUTH_RECEIVE_APOP) {
props.setProperty("mail.pop3.apop.enable", "true");
}
Session session = Session.getInstance(props, null);
return session;
}
}