package aipo.webservice.mail;

/*
 * Aipo is a groupware program developed by Aimluck,Inc.
 * Copyright (C) 2004-2011 Aimluck,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/>.
 *
 */

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Properties;

import javax.mail.Folder;
import javax.mail.Message;
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 aipo.webservice.soap.axis2.bean.ReceiveMailViewBean;
import aipo.webservice.util.WsUtils;

import com.aimluck.eip.mail.ALFolder;
import com.aimluck.eip.mail.ALMailReceiverContext;
import com.aimluck.eip.mail.file.ALFilePop3MailReceiver;
import com.sun.mail.pop3.POP3Folder;

/**
 * リマインダー用の、ローカルのファイルシステムを利用したPOP3サーバーのメール受信機能
 * 
 */
public class ReminderFilePop3MailReceiver extends ALFilePop3MailReceiver
    implements ReminderPop3MailReceiver {

  /** ロガー */
  private static final JetspeedLogger logger =
    JetspeedLogFactoryService.getLogger(ReminderFilePop3MailReceiver.class
      .getName());

  /** プロパティファイル */
  private static final String PROPERTY_FILE =
    "WEB-INF/conf/WebService.properties";

  /** POP3接続時のタイムアウトキー */
  private static final String PROPERTY_CONNECTION_TIMEOUT =
    "mail.pop3.connectiontimeout";

  /** 接続時のタイムアウト時間 */
  private static final String CONNECTION_TIMEOUT =
    (String) WsUtils.getProperties(PROPERTY_FILE).get(
      PROPERTY_CONNECTION_TIMEOUT);

  /** POP3接続後のタイムアウトキー */
  private static final String PROPERTY_POP3_TIMEOUT = "mail.pop3.timeout";

  /** 接続後のタイムアウト時間キー */
  private static final String TIMEOUT =
    (String) WsUtils.getProperties(PROPERTY_FILE).get(PROPERTY_POP3_TIMEOUT);

  /** pop3メールストア */
  private static final String POP3_MAIL_STORE = "pop3";

  /** pop3受信メールフォルダ */
  private static final String POP3_MAIL_FOLDER = "INBOX";

  /** メールUIDヘッダ */
  private static final String MAIL_HEADER_UID = "X-UIDL";

  /** ソケット・ファクトリークラスキー */
  private static final String PROPERTY_SOCKET_FACTORY_CLASS =
    "mail.pop3.socketFactory.class";

  /** SSLフォールバックキー */
  private static final String PROPERTY_SOCKET_FACTORY_FALLBACK =
    "mail.pop3.socketFactory.fallback";

  /** SSLフォールバック：false */
  private static final String PROPERTY_SOCKET_FACTORY_FALLBACK_FALSE = "false";

  /** POP3ポートキー */
  private static final String PROPERTY_POP3_PORT = "mail.pop3.port";

  /** ソケット接続用のポートキー */
  private static final String PROPERTY_SOCKET_FACTORY_PORT =
    "mail.pop3.socketFactory.port";

  /** APOP使用設定キー */
  private static final String PROPERTY_POP3_APOP_ENABLE =
    "mail.pop3.apop.enable";

  /** APOP使用可 */
  private static final String PROPERTY_POP3_APOP_ENABLE_TRUE = "true";

  /**
   * コンストラクタ
   * <p>
   * メール受信コンテキストを割り当て、初期化する。<br>
   * 
   * @param rcontext
   *          メール受信コンテキスト
   */
  public ReminderFilePop3MailReceiver(ALMailReceiverContext rcontext) {
    super(rcontext);
  }

  /**
   * 新着メール一覧取得
   * <p>
   * メールサーバーにある新着受信メール情報の一覧を取得する。メールの受信自体は行わない。<br>
   * 
   * @see aipo.webservice.mail.ReminderPop3MailReceiver#
   *      getNewReceiveMailListFromMailServer()
   * @throws Exception
   *           全例外
   */
  public List<ReceiveMailViewBean> getNewReceiveMailListFromMailServer()
      throws Exception {
    logger.debug("getNewReceiveMailListFromMailServer() start.");

    List<ReceiveMailViewBean> result = new ArrayList<ReceiveMailViewBean>(0);

    // POP3 サーバ上のストア
    Store pop3Store = null;
    // POP3 サーバ上のフォルダ
    POP3Folder pop3Folder = null;
    // ローカルストア
    ALFolder receiveFolder = null;

    try {

      // POP3 サーバへの接続
      Session session =
        getSession(rcontext.getAuthReceiveFlag(), rcontext.getEncryptionFlag());
      pop3Store = session.getStore(POP3_MAIL_STORE);
      pop3Store
        .connect(rcontext.getPop3Host(), Integer.parseInt(rcontext
          .getPop3Port()), rcontext.getPop3UserId(), rcontext
          .getPop3UserPasswd());

      logger.debug("connect pop3 server start.");

      if (!pop3Store.isConnected()) {
        throw new Exception("POP3サーバとの接続に失敗しました。");
      }

      logger.debug("connect pop3 server end.");

      // POP3 サーバ上のメールフォルダを開く
      logger.debug("open pop3 folder start.");

      pop3Folder = (POP3Folder) pop3Store.getFolder(POP3_MAIL_FOLDER);
      if (pop3Folder == null) {
        // メールフォルダのオープン失敗
        throw new Exception("POP3サーバの、メールフォルダのオープンに失敗しました。");
      }
      pop3Folder.open(Folder.READ_WRITE);

      logger.debug("open pop3 folder end.");

      // ローカルストアに接続
      logger.debug("connect local store start.");

      receiveFolder = getALFolder();
      if (receiveFolder == null) {
        throw new Exception("ローカルストアとの接続に失敗しました。");
      }

      logger.debug("connect local store end.");

      // POP3サーバに保存されているメッセージ数を取得
      if (pop3Folder.getMessageCount() == 0) {
        // 新着メールなし
        logger.debug("pop3 does not have a message.");
        return result;
      }

      // 保存してある UID の一覧を取得
      List<String> oldUIDL = receiveFolder.loadUID();

      // POP3サーバーのメールで、ローカルに存在しないメールを戻り値に追加する
      logger.debug("get pop3 messages start.");

      Message[] messages = pop3Folder.getMessages();
      for (Message message : messages) {
        String uid = pop3Folder.getUID(message);
        if (uid == null) {
          logger.debug("message uid is null.");
          String[] xuidls = message.getHeader(MAIL_HEADER_UID);
          if (xuidls != null && xuidls.length > 0) {
            logger.debug("uid is mail header uid.");
            uid = xuidls[0];
          } else {
            logger.debug("uid is messageID.");
            uid = ((MimeMessage) message).getMessageID();
          }
        } else {
          logger.debug("message uid is not null.");
        }

        logger.debug("uid:" + uid);

        if (!oldUIDL.contains(uid)) {
          logger.debug("new uid not contains in old uids.");
          ReceiveMailViewBean rm = new ReceiveMailViewBean();
          rm.setuId(uid);
          Date receiveDate = ((MimeMessage) message).getSentDate();
          rm.setReceiveDate(WsUtils.getYMDFromDate(receiveDate));
          rm.setReceiveTime(WsUtils.getHHMIFromDate(receiveDate));
          rm.setSubject(((MimeMessage) message).getSubject());
          rm.setBody("");
          result.add(rm);
        } else {
          logger.debug("new uid contains in old uids.");
        }
      }

      logger.debug("get pop3 messages end.");

    } catch (Exception e) {
      throw new Exception("新着メール一覧取得で予期せぬエラーが発生しました。 " + e.toString());

    } finally {
      // サーバとのコネクションを切断する．
      close(pop3Store, pop3Folder, receiveFolder);
    }

    logger.debug("getNewReceiveMailListFromMailServer() end.");

    return result;
  }

  /**
   * セッション取得
   * <p>
   * セッションからメール設定を取得する。<br>
   * 
   * @param 受信権限フラグ
   *          (True：APOP、False：POP3）
   * @param 接続時暗号化フラグ
   *          (True：SSL、False：通常)
   * @throws Exception
   *           全例外
   */
  private Session getSession(int authReceiveFlag, int encryptFlag) {

    logger.debug("getSession() start.");

    Properties props = new Properties();
    // POP3 サーバへの接続
    if (encryptFlag == ENCRYPTION_SEND_SSL) {
      /** SSL 暗号化 */
      props.setProperty(PROPERTY_SOCKET_FACTORY_CLASS, SSL_FACTORY);
      props.setProperty(
        PROPERTY_SOCKET_FACTORY_FALLBACK,
        PROPERTY_SOCKET_FACTORY_FALLBACK_FALSE);
      props.setProperty(PROPERTY_POP3_PORT, rcontext.getPop3Port());
      props.setProperty(PROPERTY_SOCKET_FACTORY_PORT, rcontext.getPop3Port());
    }
    props.setProperty(PROPERTY_CONNECTION_TIMEOUT, CONNECTION_TIMEOUT);
    props.setProperty(PROPERTY_POP3_TIMEOUT, TIMEOUT);
    if (authReceiveFlag == AUTH_RECEIVE_APOP) {
      props.setProperty(
        PROPERTY_POP3_APOP_ENABLE,
        PROPERTY_POP3_APOP_ENABLE_TRUE);
    }
    Session session = Session.getInstance(props, null);

    logger.debug("getSession() end.");

    return session;
  }

}
