/*
 * blanco Framework
 * Copyright (C) 2004-2007 IGA Tosiki
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 */
package blanco.mail.core;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.ConnectException;
import java.util.Enumeration;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

import javax.mail.Address;
import javax.mail.AuthenticationFailedException;
import javax.mail.Flags;
import javax.mail.Folder;
import javax.mail.Header;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.NoSuchProviderException;
import javax.mail.Part;
import javax.mail.Session;
import javax.mail.Store;
import javax.mail.Message.RecipientType;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeUtility;

import blanco.commons.util.BlancoStreamUtil;
import blanco.commons.util.BlancoStringUtil;
import blanco.log.BlancoLogBinaryLogger;
import blanco.mail.core.resourcebundle.BlancoMailCoreMessageResourceBundle;
import blanco.mail.core.valueobject.BlancoMailMessage;
import blanco.mail.core.valueobject.BlancoMailMessageAttachment;
import blanco.mail.core.valueobject.BlancoMailMessageHeader;
import blanco.mail.core.valueobject.BlancoMailSetting;
import blanco.mail.core.valueobject.BlancoMailStatistics;

/**
 * メール受信クラス実装。
 * 
 * 簡単で安全なメール受信を実現するための参照実装です。 blancoMail コアライブラリのひとつ。
 * クラス実装を取得するためには、BlancoMailReceiverFactory#getInstance を利用します。
 * 
 * このクラスを拡張して利用する場面を想定し、クラスアクセスは public クラスと設定しています。
 * 
 * @author IGA Tosiki
 */
public class BlancoMailReceiverImpl implements BlancoMailReceiver {
    /**
     * blancoMailCoreメッセージクラス。
     */
    protected final BlancoMailCoreMessageResourceBundle fMsg = new BlancoMailCoreMessageResourceBundle();

    /**
     * 統計情報オブジェクト。デフォルトではnullがセットされている点に注意してください。
     */
    protected BlancoMailStatistics fStatistics = null;

    /**
     * コンストラクタは隠します。
     * 
     * Factoryからアクセス可能になるようにパッケージアクセスを許容しています。
     */
    BlancoMailReceiverImpl() {
    }

    /**
     * メールサーバから、最初のメールメッセージを受信します。
     * 
     * 受信したメールは メールサーバから削除されます。
     * 
     * @param argSetting
     *            blancoMailの各種設定情報をあらわすバリューオブジェクト。サーバ接続設定や送受信時のデフォルト値などが格納されます。(注意)このクラスのインスタンスは
     *            BlancoMailSettingFactory クラスから取得してください。
     * @return 受信したメール本体をあらわすバリューオブジェクト。このオブジェクトにメールの本文や宛先などの情報が入ります。１件もメールを受信しなかった場合には
     *         null が戻ります。
     * @throws ConnectException
     *             メールサーバへの接続失敗に関する例外。
     * @throws IOException
     *             メールデータの受信失敗に関する例外。
     */
    public BlancoMailMessage receive(final BlancoMailSetting argSetting)
            throws ConnectException, IOException {
        // パラメータチェック。
        if (argSetting == null) {
            throw new IllegalArgumentException(fMsg.getMbmcr001());
        }

        if (fStatistics == null) {
            // 統計情報オブジェクトが設定されていない場合には これを設定します。
            // これにより、メソッド内では fStatisticsは常に非nullであることを保障します。
            fStatistics = new BlancoMailStatistics();
        }

        // BlancoMailSetting内容の妥当性の検証。
        checkSetting(argSetting);

        final Properties props = new Properties();

        // 接続情報に関するプロパティをセットします。
        prepareSessionProperties(argSetting, props);

        if (argSetting.getTestMode()) {
            // 実際のメール受信処理は実施しません。そして returnします。
            return new BlancoMailTestStub().receive(argSetting);
        }

        // 通信開始
        final long startTimeMillis = System.currentTimeMillis();

        final Session session = BlancoMailCoreUtil.getReceiveSession(
                argSetting, props);
        final ByteArrayOutputStream outWorkByteArrayOutputStream = BlancoMailCoreUtil
                .beginSessionLogOutputStream(argSetting, session);

        // メールサーバのストアオブジェクト。
        final Store store = connectStore(argSetting, session);

        // 正常終了したかどうかをあらわすフラグ。
        boolean isSuccessEnd = false;
        // メールを受信したかどうかを表すフラグ。
        // このフラグにより、正常終了時のセッションログを出力するかどうかの判断をおこないます。
        boolean isMailReceived = false;

        try {
            // メールサーバのフォルダオブジェクト。
            Folder folder = null;
            try {
                try {
                    // INBOX フォルダを取得します。
                    // ※INBOXは予約語です。
                    folder = store.getFolder("INBOX");
                } catch (MessagingException e) {
                    fStatistics.setReceiveErrorCount(fStatistics
                            .getReceiveErrorCount() + 1);
                    throw new ConnectException(fMsg.getMbmcr302(e.toString()));
                }
                try {
                    // 読み書きモードでフォルダをオープンします。
                    folder.open(Folder.READ_WRITE);
                } catch (MessagingException e) {
                    fStatistics.setReceiveErrorCount(fStatistics
                            .getReceiveErrorCount() + 1);
                    throw new ConnectException(fMsg.getMbmcr303(e.toString()));
                }

                // メッセージ一覧を取得
                Message messages[] = null;
                try {
                    messages = folder.getMessages();
                } catch (MessagingException e) {
                    fStatistics.setReceiveErrorCount(fStatistics
                            .getReceiveErrorCount() + 1);
                    throw new IOException(fMsg.getMbmcr305(e.toString()));
                }

                for (int index = 0; index < messages.length; index++) {
                    final Message message = messages[index];

                    final BlancoMailMessage blancoMailMessage = parseMessage(
                            argSetting, message);
                    if (blancoMailMessage == null) {
                        // このメールの受信はスキップされました。
                        // 次のメールの受信へと処理を進めます。
                        continue;
                    }

                    try {
                        // メールを受信したことを示すフラグを立てつつフォルダをクローズします。
                        // メールを受信したので 更新モードでフォルダをクローズします。
                        // この処理が成功して、はじめて正常終了扱いとなります。
                        folder.close(true);
                        // フォルダオブジェクトにnullを設定します。
                        // ここでnullを設定することにより finally節でのクローズが動かないようにします。
                        folder = null;
                    } catch (MessagingException e) {
                        fStatistics.setReceiveErrorCount(fStatistics
                                .getReceiveErrorCount() + 1);
                        throw new IOException(fMsg.getMbmcr304(e.toString()));
                    }

                    // メールを受信したことを示すフラグを立てます。
                    isMailReceived = true;

                    // 正常終了したことを示すフラグを立てます。
                    isSuccessEnd = true;

                    // 統計情報の更新。
                    fStatistics
                            .setReceiveCount(fStatistics.getReceiveCount() + 1);
                    // サイズ情報を更新。
                    fStatistics.setReceiveSize(fStatistics.getReceiveSize()
                            + blancoMailMessage.getSize());

                    // このメールメッセージを 取得結果として戻します。
                    return blancoMailMessage;
                }

                // 一件もメールが無かった場合。

                // 正常終了したことを示すフラグを立てます。
                isSuccessEnd = true;

                fStatistics.setReceiveWithoutProcessCount(fStatistics
                        .getReceiveWithoutProcessCount() + 1);

                // 該当するメールは一件もありませんでした。
                return null;

            } finally {
                if (folder != null) {
                    try {
                        // フォルダをクローズします。
                        // ここに入ってきた際には、メールの受信処理は実施していません。
                        // 受信なしのフォルダクローズ処理については、これは例外扱いとはしません。
                        // TODO ロギングシステム導入時には、これをログ出力してください。
                        folder.close(false);
                    } catch (MessagingException e) {
                        System.out.println(fMsg.getMbmcr306(e.toString()));
                    }
                }
            }
        } finally {
            try {
                // ストアをクローズします。
                store.close();
            } catch (MessagingException e) {
                // 接続クローズ例外については、これは例外扱いとはしません。
                // TODO ロギングシステム導入時には、これをログ出力してください。
                System.out.println(fMsg.getMbmcr307(e.toString()));
            }

            // 通信終了
            final long endTimeMillis = System.currentTimeMillis();

            // 受信時間の統計情報を更新。
            fStatistics.setReceiveTime(fStatistics.getReceiveTime()
                    + (endTimeMillis - startTimeMillis));

            // メールサーバとの一連のやりとりをセッションログファイルに出力します。

            String sessionLogDirectory = null;
            if (isSuccessEnd) {
                if (isMailReceived) {
                    // 実際にメールを受信した時のみセッションログを出力します。
                    sessionLogDirectory = argSetting
                            .getSessionLogDirectorySuccess();
                } else if (argSetting.getSessionLogOutputWithNoReceiveProcess()) {
                    // 1件もメール受信処理を伴わなかった場合のログを出力するかどうかのフラグがtrueの場合。
                    // (デフォルト)通常はfalse(残さない)で運用することを想定しています。
                    sessionLogDirectory = argSetting
                            .getSessionLogDirectorySuccess();
                }
            } else {
                sessionLogDirectory = argSetting.getSessionLogDirectoryError();
            }

            if (BlancoStringUtil.null2Blank(sessionLogDirectory).length() > 0) {
                final BlancoLogBinaryLogger logger = new BlancoLogBinaryLogger(
                        sessionLogDirectory, "mail", "r", "log");
                try {
                    logger.log(outWorkByteArrayOutputStream.toByteArray());
                } catch (IOException e) {
                    throw new IOException(fMsg.getMbmcr309(e.toString()));
                }
            }
        }
    }

    /**
     * 統計情報取得のためのバリューオブジェクトをセットします。
     * 
     * @param argStatistics
     *            統計情報。
     */
    public void setStatistics(final BlancoMailStatistics argStatistics) {
        fStatistics = argStatistics;
    }

    /**
     * 統計情報取得のためのバリューオブジェクトをゲットします。
     * 
     * @return 統計情報。
     */
    public BlancoMailStatistics getStatistics() {
        return fStatistics;
    }

    /**
     * JavaMailのオブジェクトをパースして blancoMailのオブジェクトを取得します。
     * 
     * このメソッドのレベルでは MessagingException は送出しないようにします。
     * 
     * @param argSetting
     *            blancoMailの各種設定情報をあらわすバリューオブジェクト。サーバ接続設定や送受信時のデフォルト値などが格納されます。(注意)このクラスのインスタンスは
     *            BlancoMailSettingFactory クラスから取得してください。
     * @param argJavaMailMessage
     *            JavaMail APIのメッセージオブジェクト。
     * @return メール本体をあらわすバリューオブジェクト。このオブジェクトにメールの本文や宛先などの情報が入ります。
     * @throws IOException
     *             メールデータの受信失敗に関する例外。
     */
    private BlancoMailMessage parseMessage(final BlancoMailSetting argSetting,
            final Message argJavaMailMessage) throws IOException {
        final BlancoMailMessage blancoMailMessage = new BlancoMailMessage();

        try {
            // RETRコマンド送出前までの処理をおこない、受信すべきメールかどうか判断します。
            final boolean isReceived = parseMessageWithoutRetr(argSetting,
                    argJavaMailMessage, blancoMailMessage);
            if (isReceived == false) {
                // 受信すべきメールは無かったものと判断します。
                return null;
            }
        } catch (MessagingException e) {
            fStatistics
                    .setReceiveErrorCount(fStatistics.getReceiveErrorCount() + 1);
            throw new IOException(fMsg.getMbmcr301(e.toString()));
        }

        // System.out.println("TRACE: ここまではRETRコマンドは送出されない。");

        try {
            // 受信すべきメールと判断し、RETRコマンド送出を伴ったメール受信を行います。
            parseMessageWithRetr(argSetting, argJavaMailMessage,
                    blancoMailMessage);

            // 処理の結果得られたメール本体をあらわすバリューオブジェクトを戻します。
            return blancoMailMessage;

        } catch (MessagingException e) {
            fStatistics
                    .setReceiveErrorCount(fStatistics.getReceiveErrorCount() + 1);
            throw new IOException(fMsg.getMbmcr308(e.toString()));
        }
    }

    /**
     * JavaMailのオブジェクトをパースして
     * blancoMailのオブジェクトを取得する処理の内部処理。RETRコマンド送出前までの処理を担います。
     * 
     * Subjectフィルタ設定の結果処理対象ではないものやサイズオーバーのものは処理しないメールと判断します。
     * 
     * @param argSetting
     *            blancoMailの各種設定情報をあらわすバリューオブジェクト。サーバ接続設定や送受信時のデフォルト値などが格納されます。(注意)このクラスのインスタンスは
     *            BlancoMailSettingFactory クラスから取得してください。
     * @param argJavaMailMessage
     *            JavaMail APIのメッセージオブジェクト。
     * @param blancoMailMessage
     *            受信したメール本体をあらわすバリューオブジェクト。このオブジェクトにメールの本文や宛先などの情報が入ります。
     * @return true:処理すべきメールが見つかった場合。 false:処理すべきメールが一件もなかった場合。
     * @throws MessagingException
     *             JavaMail APIに由来する例外。
     */
    private boolean parseMessageWithoutRetr(final BlancoMailSetting argSetting,
            final Message argJavaMailMessage,
            final BlancoMailMessage blancoMailMessage)
            throws MessagingException {
        // message.getContentを呼び出す前までは、TOPコマンド止まりで、RETRコマンドは送出されない。

        // SubjectとSizeのみ最初に取得。
        blancoMailMessage.setSubject(argJavaMailMessage.getSubject());
        blancoMailMessage.setSize(argJavaMailMessage.getSize());

        // Subjectフィルタ処理。
        if (BlancoStringUtil.null2Blank(argSetting.getReceiveFilterBySubject())
                .length() > 0) {
            // 正規表現については checkSettingメソッドで既に妥当性チェック済みです。
            // そのため、この場所で正規表現のコンパイル失敗に関する例外は発生しません。
            final Pattern pattern = Pattern.compile(argSetting
                    .getReceiveFilterBySubject());
            final Matcher matcher = pattern.matcher(blancoMailMessage
                    .getSubject());
            if (matcher.matches() == false) {
                // Subjectフィルタ設定の結果処理対象ではないメールと判断します。
                // このメールは受信しません。
                return false;
            }
        }

        if (blancoMailMessage.getSize() > argSetting.getReceiveMaxMessageSize()) {
            // メッセージオブジェクトのサイズが、最大許容サイズを超えています。
            // このメールは受信しません。
            return false;
        }

        // SubjectとSize以外の属性について取得。
        blancoMailMessage.setSentDate(argJavaMailMessage.getSentDate());
        blancoMailMessage.setContentType(argJavaMailMessage.getContentType());
        blancoMailMessage.setDescription(argJavaMailMessage.getDescription());
        blancoMailMessage.setDisposition(argJavaMailMessage.getDisposition());
        blancoMailMessage.setLineCount(argJavaMailMessage.getLineCount());

        if (argJavaMailMessage.getFrom() != null
                && argJavaMailMessage.getFrom().length > 0) {
            // Fromが設定されている場合にのみ処理します。
            // なお、Fromが2つ以上設定されていても最初の Fromアドレスのみを取得します。
            final InternetAddress addrFrom = (InternetAddress) argJavaMailMessage
                    .getFrom()[0];
            blancoMailMessage.setFrom(BlancoMailCoreUtil
                    .toBlancoMailAddress(addrFrom));
        }

        // To:を処理。
        final Address[] addrsTo = argJavaMailMessage
                .getRecipients(RecipientType.TO);
        if (addrsTo != null) {
            for (int loop = 0; loop < addrsTo.length; loop++) {
                final InternetAddress addrTo = (InternetAddress) addrsTo[loop];
                blancoMailMessage.getTo().add(
                        BlancoMailCoreUtil.toBlancoMailAddress(addrTo));
            }
        }

        // Cc:を処理。
        final Address[] addrsCc = argJavaMailMessage
                .getRecipients(RecipientType.CC);
        if (addrsCc != null) {
            for (int loop = 0; loop < addrsCc.length; loop++) {
                final InternetAddress addrCc = (InternetAddress) addrsCc[loop];
                blancoMailMessage.getCc().add(
                        BlancoMailCoreUtil.toBlancoMailAddress(addrCc));
            }
        }

        // Bcc:を念のため処理。
        final Address[] addrsBcc = argJavaMailMessage
                .getRecipients(RecipientType.BCC);
        if (addrsBcc != null) {
            for (int loop = 0; loop < addrsBcc.length; loop++) {
                final InternetAddress addrBcc = (InternetAddress) addrsBcc[loop];
                blancoMailMessage.getBcc().add(
                        BlancoMailCoreUtil.toBlancoMailAddress(addrBcc));
            }
        }

        if (argJavaMailMessage.getReplyTo() != null
                && argJavaMailMessage.getReplyTo().length > 0) {
            // ReplyToの指定がある場合にのみ設定します。
            // なお、ReplyToが2つ以上設定されていても最初の ReplyToアドレスのみを取得します。
            final InternetAddress addrReplyTo = (InternetAddress) argJavaMailMessage
                    .getReplyTo()[0];
            blancoMailMessage.setReplyTo(BlancoMailCoreUtil
                    .toBlancoMailAddress(addrReplyTo));
        }

        // メールのヘッダー(Header)を全て取り出す。
        final Enumeration headers = argJavaMailMessage.getAllHeaders();
        for (; headers.hasMoreElements();) {
            final Object objHeader = headers.nextElement();
            if (objHeader instanceof Header == false) {
                throw new IllegalArgumentException(
                        "ありえない例外: getAllHeadersがHeaderオブジェクト以外をEnumerationに含めて返却しました。");
            }
            final Header header = (Header) objHeader;

            final BlancoMailMessageHeader blancoHeader = new BlancoMailMessageHeader();
            blancoHeader.setName(header.getName());
            blancoHeader.setValue(header.getValue());
            blancoMailMessage.getHeaderList().add(blancoHeader);
        }

        // メール受信を行うべきものと判断しました。
        return true;
    }

    /**
     * JavaMailのオブジェクトをパースして blancoMailのオブジェクトを取得する処理の内部処理。RETRコマンド送出を伴う処理を担います。
     * 
     * @param argSetting
     *            blancoMailの各種設定情報をあらわすバリューオブジェクト。サーバ接続設定や送受信時のデフォルト値などが格納されます。(注意)このクラスのインスタンスは
     *            BlancoMailSettingFactory クラスから取得してください。
     * @param argJavaMailMessage
     *            JavaMail APIのメッセージオブジェクト。
     * @param blancoMailMessage
     *            受信したメール本体をあらわすバリューオブジェクト。このオブジェクトにメールの本文や宛先などの情報が入ります。
     * @throws MessagingException
     *             JavaMail APIに由来する例外。
     * @throws IOException
     *             メールデータの受信失敗に関する例外。
     */
    private void parseMessageWithRetr(final BlancoMailSetting argSetting,
            final Message argJavaMailMessage,
            final BlancoMailMessage blancoMailMessage)
            throws MessagingException, IOException {
        // message.getContentを呼び出したメールに対しては、RETRコマンドが送出されます。
        try {
            final Object objContent = argJavaMailMessage.getContent();
            if (objContent instanceof Multipart) {
                // マルチパートの場合。

                final Multipart multiPart = (Multipart) objContent;
                for (int indexPart = 0; indexPart < multiPart.getCount(); indexPart++) {
                    final BlancoMailMessageAttachment attachment = new BlancoMailMessageAttachment();

                    final Part part = multiPart.getBodyPart(indexPart);
                    attachment.setContentType(part.getContentType());
                    attachment.setDescription(part.getDescription());
                    attachment.setDisposition(part.getDisposition());
                    attachment.setLineCount(part.getLineCount());
                    attachment.setSize(part.getSize());
                    if (Part.ATTACHMENT.equals(attachment.getDisposition())
                            || Part.INLINE.equals(attachment.getDisposition())) {
                        attachment.setFileName(MimeUtility
                                .decodeText(BlancoStringUtil.null2Blank(part
                                        .getFileName())));

                        // ストリーム読み込み処理を行う。
                        // ※ポイント：一定時間、添付ファイル分のデータがメモリを占有する点に注意。
                        final ByteArrayOutputStream outStream = new ByteArrayOutputStream();
                        BlancoStreamUtil.copy(part.getInputStream(), outStream);
                        outStream.flush();
                        outStream.close();
                        attachment.setData(outStream.toByteArray());

                        blancoMailMessage.getAttachmentList().add(attachment);
                    } else {
                        // マルチパートでテキスト本文が複数回あることは まずありえないと考えています。
                        // しかし、仕様上存在しうるため、下記のように既存Textフィールドに対して追加する形を取っています。
                        blancoMailMessage.setText(BlancoStringUtil
                                .null2Blank(blancoMailMessage.getText())
                                + part.getContent().toString());
                    }
                }
            } else {
                // マルチパートではない場合。

                // テキスト本文が複数回あることは まずありえないと考えています。
                // 念のため、下記のように既存Textフィールドに対して追加する形を取っています。
                blancoMailMessage.setText(BlancoStringUtil
                        .null2Blank(blancoMailMessage.getText())
                        + objContent.toString());
            }
        } finally {
            // 注意：メールのパースに失敗しようが、削除マークをつけています！
            // JavaMail API
            // によるメールパースに失敗したとしても、エラーフォルダにログが格納されるから良いだろう、という意味です。

            // ここで読み込みをした (getContentをした)
            // メールについてメールサーバから削除するフラグをセットします。
            argJavaMailMessage.setFlag(Flags.Flag.DELETED, true);
        }
    }

    /**
     * BlancoMailSettingの内容の妥当性を検証します。
     * 
     * @param argSetting
     *            blancoMailの各種設定情報をあらわすバリューオブジェクト。サーバ接続設定や送受信時のデフォルト値などが格納されます。(注意)このクラスのインスタンスは
     *            BlancoMailSettingFactory クラスから取得してください。
     */
    private void checkSetting(final BlancoMailSetting argSetting) {
        if (argSetting == null) {
            throw new IllegalArgumentException(
                    "BlancoMailReceiver#checkSetting: メソッドのパラメータ(argSetting)に nullが渡されました。このオブジェクトは nullであってはなりません。");
        }

        // BlancoMailSettingの内容をチェック。
        if (argSetting.getReceiveHost() == null) {
            throw new IllegalArgumentException(fMsg.getMbmcr101());
        }
        if (argSetting.getReceiveUserName() == null) {
            throw new IllegalArgumentException(fMsg.getMbmcr102());
        }
        if (argSetting.getReceivePassword() == null) {
            throw new IllegalArgumentException(fMsg.getMbmcr103());
        }
        if (argSetting.getReceiveProtocol() == null) {
            throw new IllegalArgumentException(fMsg.getMbmcr104());
        }

        // Subjectマッチャーの内容妥当性のチェック。
        if (BlancoStringUtil.null2Blank(argSetting.getReceiveFilterBySubject())
                .length() > 0) {
            try {
                Pattern.compile(argSetting.getReceiveFilterBySubject());
            } catch (PatternSyntaxException e) {
                throw new IllegalArgumentException(fMsg.getMbmcr105(argSetting
                        .getReceiveFilterBySubject(), e.toString()));
            }
        }

        // メール送受信で共通の BlancoMailSettingの設定内容チェック。
        BlancoMailCoreUtil.checkSetting(argSetting);
    }

    /**
     * JavaMail API セッションオブジェクト生成時に必要となるプロパティオブジェクトを準備します。
     * 
     * @param argSetting
     *            blancoMailの各種設定情報をあらわすバリューオブジェクト。サーバ接続設定や送受信時のデフォルト値などが格納されます。(注意)このクラスのインスタンスは
     *            BlancoMailSettingFactory クラスから取得してください。
     * @param argProps
     *            接続情報に関するプロパティオブジェクト。このオブジェクトにJavaMail API呼び出し時の接続情報がセットされます。
     */
    private void prepareSessionProperties(final BlancoMailSetting argSetting,
            final Properties argProps) {
        if ("pop3".equals(argSetting.getReceiveProtocol()) == false
                && "imap".equals(argSetting.getReceiveProtocol()) == false) {
            throw new IllegalArgumentException(fMsg.getMbmcr106(argSetting
                    .getReceiveProtocol()));
        }

        // 基本情報。
        argProps.setProperty("mail." + argSetting.getReceiveProtocol()
                + ".host", argSetting.getReceiveHost());

        if (argSetting.getReceivePort() >= 0) {
            // ポート番号を変更。
            argProps.setProperty("mail." + argSetting.getReceiveProtocol()
                    + ".port", String.valueOf(argSetting.getReceivePort()));
        }

        // タイムアウト設定
        if (argSetting.getMailConnectionTimeout() > 0) {
            argProps.setProperty("mail." + argSetting.getReceiveProtocol()
                    + ".connectiontimeout", String.valueOf(argSetting
                    .getMailConnectionTimeout()));
        }
        if (argSetting.getMailTimeout() > 0) {
            argProps.setProperty("mail." + argSetting.getReceiveProtocol()
                    + ".timeout", String.valueOf(argSetting.getMailTimeout()));
        }

        // POP3プロトコルの場合、APOPを利用するかどうか。
        if (argSetting.getReceivePop3Apop()) {
            argProps.setProperty("mail.pop3.apop.enable", "true");
        }

        if (argSetting.getReceiveUseSsl()) {
            // SSL関連設定
            argProps.setProperty("mail." + argSetting.getReceiveProtocol()
                    + ".socketFactory.class", argSetting
                    .getSocketfactoryClass());
            argProps.setProperty("mail." + argSetting.getReceiveProtocol()
                    + ".socketFactory.fallback", String.valueOf(argSetting
                    .getSocketfactoryFallback()));
            if (argSetting.getReceivePort() >= 0) {
                // ポート番号を変更。
                argProps.setProperty("mail." + argSetting.getReceiveProtocol()
                        + ".socketFactory.port", String.valueOf(argSetting
                        .getReceivePort()));
            }
        }
    }

    /**
     * JavaMail API を経由してストア(メールサーバ)に接続します。
     * 
     * @param argSetting
     *            blancoMailの各種設定情報をあらわすバリューオブジェクト。サーバ接続設定や送受信時のデフォルト値などが格納されます。(注意)このクラスのインスタンスは
     *            BlancoMailSettingFactory クラスから取得してください。
     * @param argSession
     *            JavaMailのセッションオブジェクト。
     * @return JavaMailのストアオブジェクト。
     * @throws ConnectException
     *             メールサーバへの接続失敗に関する例外。
     */
    private Store connectStore(final BlancoMailSetting argSetting,
            final Session argSession) throws ConnectException {
        Store store = null;
        try {
            store = argSession.getStore(argSetting.getReceiveProtocol());
        } catch (NoSuchProviderException e) {
            fStatistics
                    .setReceiveErrorCount(fStatistics.getReceiveErrorCount() + 1);
            throw new ConnectException(fMsg.getMbmcr201(argSetting
                    .getReceiveProtocol(), e.toString()));
        }

        try {
            store.connect();
        } catch (AuthenticationFailedException e) {
            fStatistics
                    .setReceiveErrorCount(fStatistics.getReceiveErrorCount() + 1);
            throw new ConnectException(fMsg.getMbmcr202(argSetting
                    .getReceiveUserName(), e.toString()));
        } catch (MessagingException e) {
            fStatistics
                    .setReceiveErrorCount(fStatistics.getReceiveErrorCount() + 1);
            throw new ConnectException(fMsg.getMbmcr203(argSetting
                    .getReceiveHost(), e.toString()));
        }

        return store;
    }
}