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                public boolean receive( final MailMessage message ) {
206                        final String msg = "[" + counter++ + "]" + message.getMessageID() + " 受信中" ;
207                        System.out.println( msg );
208
209                        // 6.4.1.1 (2016/01/16) PMD refactoring. Avoid if (x != y) ..; else ..;
210                        if( mailSaveDir == null ) {
211                                System.out.println( message.getSubject() );
212                                System.out.println( message.getContent() );
213                        }
214                        else {
215                                message.saveMessage( mailSaveDir );
216                        }
217
218                        if( fileSaveDir != null ) {
219                                message.saveAttachFiles( fileSaveDir,useMsgId );
220                        }
221                        return true ;
222                }
223        }
224
225        /**
226         * main メソッドです。
227         *
228         * @param       args    コマンド引数配列
229         */
230        public static void main ( final String[] args ) {
231                final MailReceiver receiver = new MailReceiver();
232                try {
233                        LOGGER.info( "メール受信処理を開始します  ---------------------------------------------" );
234                        receiver.start( args );
235                        LOGGER.info( "正常に終了しました。" );
236                }
237                catch( final Throwable th ) {
238                        final String errMsg = "メール受信中に例外が発生しました。 "
239                                                        + CR + receiver.argment
240                                                        + CR + th.getMessage() ;
241                        LOGGER.log( Level.SEVERE,errMsg, th );
242                }
243        }
244}