    001/*
    002 * Copyright (c) 2009 The openGion Project.
    003 *
    004 * Licensed under the Apache License, Version 2.0 (the "License");
    005 * you may not use this file except in compliance with the License.
    006 * You may obtain a copy of the License at
    007 *
    008 *     http://www.apache.org/licenses/LICENSE-2.0
    009 *
    010 * Unless required by applicable law or agreed to in writing, software
    011 * distributed under the License is distributed on an "AS IS" BASIS,
    012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
    013 * either express or implied. See the License for the specific language
    014 * governing permissions and limitations under the License.
    015 */
    016package org.opengion.fukurou.mail;
    017
    018import org.opengion.fukurou.util.HybsEntry ;
    019import org.opengion.fukurou.util.Argument ;
    020import static org.opengion.fukurou.system.HybsConst.CR;                         // 6.1.0.0 (2014/12/26) refactoring
    021
    022import java.util.Map;
    023import java.util.LinkedHashMap;
    024
    025import java.util.logging.Logger;
    026import java.util.logging.Level;
    027import jakarta.mail.MessagingException ;
    028
    029/**
    030 * MailReceiver は、POP3/IMAPプロトコルによるメール受信プログラムです。
    031 *
    032 * 引数には、host,user,passwd などが指定できます。
    033 * メールは、メッセージID名をファイル名として、セーブします。
    034 * 添付ファイルは、元のファイル名で、指定のディレクトリに出力される為、複数のメールを
    035 * 同時に抜く場合は、uniq 属性を付与してください。その場合は、メッセージID＋連番＋元のファイル名
    036 * で、ファイルをセーブします。添付ファイルには、同名のファイルを複数添付することが
    037 * できるため、保存時には、添付ファイルの番号を連番としてファイル名に、付与します。
    038 *
    039 * 引数のプロパテイのキー部は、大文字・小文字が厳格に適用されますので、正確に記述願います。
    040 *
    041 * Usage: java org.opengion.fukurou.fukurou.mail.MailReceiver
    042 *        -host=メールサーバー(必須)
    043 *        -user=メールを取得するログインユーザー(必須)
    044 *        -passwd=メールを取得するログインパスワード(必須)
    045 *        -protocol=受信サーバーのプロトコル[imap/pop3]を指定(初期値:{@og.value #PROTOCOL})
    046 *        -port=受信サーバーのポートを指定(初期値:{@og.value #PORT})
    047 *        -useSSL=SSL接続するかどうか[true:する/false:しない]を指定(初期値:false:しない)
    048 *        -mailSaveDir=受信メールをセーブするディレクトリ。指定がない場合は、標準出力へ出力する。
    049 *        -fileSaveDir=添付ファイルをセーブするディレクトリ。指定がない場合は抜き出さない。
    050 *        -useMsgId=添付ファイルをセーブするディレクトリに、MesssageIdフォルダを個別に割り当てるかどうか(初期値:false)
    051 *        -maxRowCount=受信メールの最大取り込み件数(初期値:{@og.value #MAX_ROW_COUNT})(0:[無制限])
    052 *        -match_Subject=受信メールのSubjectを選択する条件
    053 *        -match_Body=受信メールのBodyを選択する条件
    054 *        -match_From=受信メールのFromを選択する条件
    055 *        -match_XXXX=受信メールのヘッダー部のキーXXXXを選択する条件
    056 *        -delete=検索後、メールをサーバーから削除するかどうかを、true/falseで指定(初期値:false)
    057 *        -help=使用方法を出力して、終了します。
    058 *
    059 * ※ 6.3.8.0 (2015/09/11)
    060 *    useSSL属性は、protocolに、pop3s/imaps を指定した場合、
    061 *    自動的に、ture に設定するようにしています。
    062 *
    063 * @version  0.9.0  2000/11/13
    064 * @author   Kazuhiko Hasegawa
    065 * @since    JDK5.0,
    066 */
    067public class MailReceiver {
    068        // 6.3.9.1 (2015/11/27) The Logger variable declaration does not contain the static and final modifiers(PMD)
    069        private static final Logger LOGGER = Logger.getLogger( MailReceiver.class.getName() );                                                  // 6.8.1.0 (2017/07/14) ｸﾗｽ名を動的作成に変更
    070
    071        /** 受信メールの最大取り込み件数を指定します 「={@value}」  */
    072        public static final int MAX_ROW_COUNT = 100 ;
    073
    074        /** 検索後、メールをサーバーから削除するかどうかを、true/falseで指定します 「={@value}」  */
    075        public static final boolean DELETE_MESSAGE = false ;
    076
    077        /** メールサーバーのデフォルトプロトコル 「={@value}」  */
    078        public static final String PROTOCOL = "pop3" ;
    079
    080        /** メールサーバーのデフォルトポート番号 「={@value}」  */
    081        public static final int PORT = -1 ;
    082
    083        private Argument argment ;
    084
    085        /** staticイニシャライザ後、読み取り専用にするので、ConcurrentHashMap を使用しません。 */
    086        private static final Map<String,String> MUST_PROPARTY   ;               // 6.4.1.1 (2016/01/16) mustProparty   → MUST_PROPARTY   refactoring
    087        /** staticイニシャライザ後、読み取り専用にするので、ConcurrentHashMap を使用しません。 */
    088        private static final Map<String,String> USABLE_PROPARTY ;               // 6.4.1.1 (2016/01/16) usableProparty → USABLE_PROPARTY refactoring
    089
    090        static {
    091                MUST_PROPARTY = new LinkedHashMap<>();
    092                MUST_PROPARTY.put( "host"       , "メールサーバー(必須)" );
    093                MUST_PROPARTY.put( "user"       , "メールを取得するログインユーザー(必須)" );
    094                MUST_PROPARTY.put( "passwd"     , "メールを取得するログインパスワード(必須)" );
    095
    096                USABLE_PROPARTY = new LinkedHashMap<>();
    097                USABLE_PROPARTY.put( "protocol"         , "受信サーバーのプロトコル(imap,pop3)を指定(初期値:pop3)" );
    098                USABLE_PROPARTY.put( "port"                     , "受信サーバーのポートを指定(初期値:-1)" );
    099                USABLE_PROPARTY.put( "useSSL"           , "SSL接続するかどうかを指定(初期値:false:しない)" );            // 6.3.8.0 (2015/09/11)
    100                USABLE_PROPARTY.put( "mailSaveDir"      , "受信メールをセーブするディレクトリ。" +
    101                                                                                        CR + "指定がない場合は、標準出力へ出力する。" );
    102                USABLE_PROPARTY.put( "fileSaveDir"      , "添付ファイルをセーブするディレクトリ。" +
    103                                                                                        CR + "指定がない場合は抜き出さない。" );
    104                USABLE_PROPARTY.put( "useMsgId"         , "添付ファイルをセーブするディレクトリに、" +
    105                                                                                        CR + "MesssageIdフォルダを個別に割り当てるかどうか。" );
    106                USABLE_PROPARTY.put( "maxRowCount"      , "受信メールの最大取り込み件数(初期値:100)(0:[無制限])" );
    107                USABLE_PROPARTY.put( "match_Subject"    , "受信メールのSubjectを選択する条件" );
    108                USABLE_PROPARTY.put( "match_Body"       , "受信メールのBodyを選択する条件" );
    109                USABLE_PROPARTY.put( "match_From"       , "受信メールのFromを選択する条件" );
    110                USABLE_PROPARTY.put( "match_"           , "受信メールのヘッダー部のキーXXXXを選択する条件" );
    111                USABLE_PROPARTY.put( "delete"           , "検索後、メールをサーバーから削除するかどうかを、" +
    112                                                                                        CR + "true/falseで指定(初期値:false)" );
    113                USABLE_PROPARTY.put( "help"                     , "使用方法を出力して、終了します。" );
    114        }
    115
    116        /**
    117         * レシーバーを開始します。
    118         *
    119         * @og.rev 4.3.3.5 (2008/11/08) Argument オブジェクトへの引数を util → mail に訂正します。
    120         * @og.rev 6.3.8.0 (2015/09/11) SSL接続するかどうかを指定するパラメータを追加します。
    121         *
    122         * @param   args 引数配列(可変長引数)
    123         * @throws MessagingException なんらかのエラーが発生した場合。
    124         */
    125        public void start( final String... args ) throws MessagingException {
    126
    127                // パラメータの解析、取得
    128                LOGGER.fine( "パラメータの解析、取得" );
    129                argment = new Argument( "org.opengion.fukurou.fukurou.mail.MailReceiver" );             // 4.3.3.5 (2008/11/08)
    130                argment.setMustProparty( MUST_PROPARTY );
    131                argment.setUsableProparty( USABLE_PROPARTY );
    132
    133                argment.setArgument( args );
    134
    135                // help パラメータが true に指定された場合の処理。
    136                if( argment.getProparty( "help",false ) ) {
    137                        System.out.println( argment.toString() );
    138                        return;
    139                }
    140
    141                // 処理に必要な各種パラメータを取得しておきます。
    142                LOGGER.fine( "処理に必要な各種パラメータを取得します。" );
    143                final MailRX recive = new MailRX();
    144
    145                recive.setHost(                 argment.getProparty( "host" ) ) ;
    146                recive.setUser(                 argment.getProparty( "user" ) ) ;
    147                recive.setPasswd(               argment.getProparty( "passwd" ) ) ;
    148                recive.setProtocol(             argment.getProparty( "protocol" ,PROTOCOL ) ) ;
    149                recive.setPort(                 argment.getProparty( "port"             ,PORT ) ) ;
    150                recive.useSSL(                  argment.getProparty( "useSSL"   ,false ) ) ;                    // 6.3.8.0 (2015/09/11)
    151                recive.setDelete(               argment.getProparty( "delete"   ,DELETE_MESSAGE ) ) ;
    152                recive.setMaxRowCount(  argment.getProparty( "maxRowCount",MAX_ROW_COUNT ) ) ;
    153
    154                // 指定の条件にマッチしたメッセージのみ抜き出す為の、SearchTerm オブジェクトの作成
    155                LOGGER.fine( "指定の条件にマッチしたメッセージのみ抜き出す条件を設定します。" );
    156                final HybsEntry[] matchs = argment.getEntrys( "match_" );
    157                for( int i=0; i<matchs.length; i++ ) {
    158                        recive.addMatchTerm( matchs[i] ) ;
    159                }
    160
    161                // リスナーを設定して、受信メールを一件ずつ処理します。
    162                LOGGER.fine( "リスナーを設定して、受信メールを一件ずつ処理します。" );
    163                final String mailSaveDir = argment.getProparty( "mailSaveDir" );
    164                final String fileSaveDir = argment.getProparty( "fileSaveDir" );
    165                final boolean useMsgId   = argment.getProparty( "useMsgId",false );
    166
    167                final MailReceiveListener listener = new ReceiveListener( mailSaveDir,fileSaveDir,useMsgId ) ;
    168                recive.setMailReceiveListener( listener );
    169
    170                recive.start();
    171        }
    172
    173        /**
    174         * メール受信プログラムで使用する MailReceiveListener の実装内部クラスです。
    175         *
    176         * @version  0.9.0  2000/11/13
    177         * @author   Kazuhiko Hasegawa
    178         * @since    JDK5.0,
    179         */
    180        private static final class ReceiveListener implements MailReceiveListener {
    181                private final String  mailSaveDir ;
    182                private final String  fileSaveDir ;
    183                private final boolean useMsgId ;
    184                private int   counter ;
    185
    186                /**
    187                 * コンストラクター
    188                 *
    189                 * @param       mailSaveDir     メールをセーブする場合の保存フォルダ名
    190                 * @param       fileSaveDir     メールの添付ファイルをセーブする場合の保存フォルダ名
    191                 * @param       useMsgId        添付ファイルをセーブする場合に、メッセージIDを使用するかどうか
    192                 */
    193                public ReceiveListener( final String mailSaveDir,final String fileSaveDir,final boolean useMsgId ) {
    194                        this.mailSaveDir = mailSaveDir;
    195                        this.fileSaveDir = fileSaveDir;
    196                        this.useMsgId    = useMsgId;
    197                }
    198
    199                /**
    200                 * 受信処理を行います。
    201                 *
    202                 * @param   message MailMessageオブジェクト
    203                 * @return      結果(true:正常/false:異常)
    204                 */
    205                @Override       // MailReceiveListener
    206                public boolean receive( final MailMessage message ) {
    207                        final String msg = "[" + counter++ + "]" + message.getMessageID() + " 受信中" ;
    208                        System.out.println( msg );
    209
    210                        // 6.4.1.1 (2016/01/16) PMD refactoring. Avoid if (x != y) ..; else ..;
    211                        if( mailSaveDir == null ) {
    212                                System.out.println( message.getSubject() );
    213                                System.out.println( message.getContent() );
    214                        }
    215                        else {
    216                                message.saveMessage( mailSaveDir );
    217                        }
    218
    219                        if( fileSaveDir != null ) {
    220                                message.saveAttachFiles( fileSaveDir,useMsgId );
    221                        }
    222                        return true ;
    223                }
    224        }
    225
    226        /**
    227         * main メソッドです。
    228         *
    229         * @param       args    コマンド引数配列
    230         */
    231        public static void main ( final String[] args ) {
    232                final MailReceiver receiver = new MailReceiver();
    233                try {
    234                        LOGGER.info( "メール受信処理を開始します  ---------------------------------------------" );
    235                        receiver.start( args );
    236                        LOGGER.info( "正常に終了しました。" );
    237                }
    238                catch( final Throwable th ) {
    239                        final String errMsg = "メール受信中に例外が発生しました。 "
    240                                                        + CR + receiver.argment
    241                                                        + CR + th.getMessage() ;
    242                        LOGGER.log( Level.SEVERE,errMsg, th );
    243                }
    244        }
    245}