    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.StringUtil ;
    019import org.opengion.fukurou.util.HybsEntry ;
    020import org.opengion.fukurou.util.LogWriter;
    021
    022import java.util.Properties;
    023import java.util.List;
    024import java.util.ArrayList;
    025
    026import javax.mail.Session;
    027import javax.mail.Store;
    028import javax.mail.Folder;
    029import javax.mail.Message;
    030import javax.mail.Flags;
    031import javax.mail.MessagingException;
    032import javax.mail.NoSuchProviderException;
    033import javax.mail.search.SearchTerm;
    034import javax.mail.search.SubjectTerm;
    035import javax.mail.search.FromStringTerm;
    036import javax.mail.search.BodyTerm;
    037import javax.mail.search.HeaderTerm;
    038import javax.mail.search.AndTerm;
    039
    040/**
    041 * MailRX は、POP3プロトコルによるメール受信プログラムです。
    042 *
    043 * メールへの接続条件(host,user,passwd など)と、選択条件(matchTermなど)を指定し、
    044 * MailReceiveListener をセットして、start() メソッドを呼びます。
    045 * 実際のメール処理は、MailReceiveListener を介して、１メールずつ処理します。
    046 * 添付ファイルを処理する場合は、MailAttachFiles クラスを使用します。
    047 *
    048 *        host          メールサーバー(必須)
    049 *        user          メールを取得するログインユーザー(必須)
    050 *        passwd        メールを取得するログインパスワード(必須)
    051 *        protocol      受信サーバーのプロトコル[imap/pop3]を指定(初期値:pop3)
    052 *        port          受信サーバーのポートを指定(初期値:-1)
    053 *        mbox          受信サーバーのメールボックスを指定(初期値:INBOX)
    054 *        maxRowCount   受信メールの最大取り込み件数(初期値:100)(0:[無制限])
    055 *        charset       メールのデフォルトエンコード(初期値:ISO-2022-JP)
    056 *        matchTerm     受信メールを選択する条件のMINEntryオブジェクト
    057 *        delete        検索後、メールをサーバーから削除するかどうかを、true/falseで指定(初期値:false)。
    058 *
    059 * @version  4.0
    060 * @author   Kazuhiko Hasegawa
    061 * @since    JDK5.0,
    062 */
    063public class MailRX {
    064
    065        /** 受信メールの最大取り込み件数を指定します。 {@value}  */
    066        public static final int MAX_ROW_COUNT = 100 ;
    067
    068        /** 検索後、メールをサーバーから削除するかどうかを、true/falseで指定します。 {@value}  */
    069        public static final boolean DELETE_MESSAGE = false ;
    070
    071        /** メールサーバーのデフォルトプロトコル {@value}  */
    072        public static final String PROTOCOL = "pop3" ;
    073
    074        /** メールサーバーのデフォルトポート番号 {@value}  */
    075        public static final int PORT = -1 ;
    076
    077        /** メールサーバーのデフォルトメールボックス {@value}  */
    078        public static final String MBOX = "INBOX" ;
    079
    080        /** メールのデフォルトエンコード {@value}
    081         * Windwos-31J , MS932 , ISO-2022-JP を指定します。
    082         */
    083        public static final String CHARSET = "ISO-2022-JP" ;
    084
    085        // メール受信毎に発生するイベントを伝えるリスナーを登録します。
    086        private MailReceiveListener listener = null;
    087
    088        private String  host            = null;
    089        private String  user            = null;
    090        private String  passwd          = null;
    091        private String  protocol        = PROTOCOL;
    092        private int             port            = PORT;
    093        private String  mbox            = MBOX;
    094        private boolean deleteFlag      = DELETE_MESSAGE;
    095
    096        private String  charset         = CHARSET;
    097
    098        private int             maxRowCount     = MAX_ROW_COUNT;
    099        private final List<HybsEntry>   matchList       = new ArrayList<HybsEntry>();
    100        private boolean debug           = false;
    101
    102        /**
    103         * レシーバーを開始します。
    104         *
    105         * @throws MessagingException レシーバー処理中に、なんらかのエラーが発生した場合。
    106         * @throws NoSuchProviderException なんらかのエラーが発生した場合。
    107         */
    108        public void start() throws MessagingException,NoSuchProviderException {
    109
    110                // パラメータの解析、取得
    111                debugMsg( "パラメータの解析、取得" );
    112
    113                // 指定の条件にマッチしたメッセージのみ抜き出す為の、SearchTerm オブジェクトの作成
    114                debugMsg( "指定の条件にマッチしたメッセージのみ抜き出す条件を設定します。"  );
    115                HybsEntry[] matchs = matchList.toArray( new HybsEntry[matchList.size()] );
    116                SearchTerm[] term = new SearchTerm[matchs.length];
    117                for( int i=0; i<matchs.length; i++ ) {
    118                        String key = matchs[i].getKey();
    119                        if( "Subject".equalsIgnoreCase( key ) ) {
    120                                term[i] = new SubjectTerm( matchs[i].getValue() );
    121                        }
    122                        else if( "From".equalsIgnoreCase( key ) ) {
    123                                term[i] = new FromStringTerm( matchs[i].getValue() );
    124                        }
    125                        else if( "Body".equalsIgnoreCase( key ) ) {
    126                                term[i] = new BodyTerm( matchs[i].getValue() );
    127                        }
    128                        else {
    129                                term[i] = new HeaderTerm( key,matchs[i].getValue() );
    130                        }
    131                }
    132                SearchTerm srchTerm = new AndTerm( term );
    133
    134                // 空の properties を設定。気休め程度に、初期値を設定しておきます。
    135                debugMsg( "空の properties を設定"  );
    136                Properties prop = new Properties();
    137                prop.setProperty("mail.mime.charset", charset);
    138                prop.setProperty("mail.mime.decodetext.strict", "false");
    139                prop.setProperty("mail.mime.address.strict", "false");
    140
    141                // session を取得
    142                debugMsg( "session を取得" );
    143                Session session = Session.getInstance(prop, null);
    144
    145                Store store = null;
    146                Folder folder = null;
    147                try {
    148                        // store の取得
    149                        debugMsg( "store の取得 protocol=",protocol );
    150                        store = session.getStore( protocol );
    151
    152                        // サーバーと connect します。
    153                        debugMsg( "サーバーと connect します。" );
    154                        store.connect(host, port, user, passwd);
    155
    156                        // folder の取得
    157                        debugMsg( "folder の取得" );
    158                        folder = store.getFolder( mbox );
    159                        if( deleteFlag ) {
    160                                folder.open(Folder.READ_WRITE);
    161                        }
    162                        else {
    163                                folder.open(Folder.READ_ONLY);
    164                        }
    165
    166                        // メッセージ情報の取得
    167                        debugMsg( "メッセージ情報の取得" );
    168        //              Message[] message = folder.getMessages();
    169                        Message[] message = folder.search( srchTerm );
    170
    171                        for(int i=0, n=message.length; i<n && i<maxRowCount; i++) {
    172                                MailMessage mailMessage = new MailMessage( message[i],host,user );
    173                                debugMsg( "[" , String.valueOf(i) , "]" , mailMessage.getMessageID() , " 受信中" );
    174
    175                                // メールの削除[true/false]:先にフラグを立てているので、エラーでも削除されます。
    176                                message[i].setFlag(Flags.Flag.DELETED, deleteFlag);
    177
    178                                boolean okFlag = true;
    179                                if( listener != null ) {
    180                                        // メール本体の処理
    181                                        okFlag = listener.receive( mailMessage );
    182                                }
    183
    184                                // 受領確認の返信メール
    185                                String notifyTo = mailMessage.getNotificationTo() ;
    186                                if( okFlag && notifyTo != null ) {
    187                                        MailTX tx = new MailTX( host );
    188                                        tx.setFrom( user );
    189                                        tx.setTo( StringUtil.csv2Array( notifyTo ) );
    190                                        tx.setSubject( "受領:" + mailMessage.getSubject() );
    191                                        tx.setMessage( mailMessage.getContent() );
    192                                        tx.sendmail();
    193                                }
    194                        }
    195                }
    196                finally {
    197                        // セッション終了
    198                        debugMsg( "セッション終了処理" );
    199                        if( folder != null ) {
    200                                folder.close(deleteFlag);               // true の場合は、終了時に実際に削除します。
    201                        }
    202                        if( store != null ) {
    203                                store.close();
    204                        }
    205                }
    206        }
    207
    208        /**
    209         * メールサーバーをセットします。
    210         *
    211         * @param       host メールサーバー
    212         * @throws      IllegalArgumentException 引数が null の場合。
    213         */
    214        public void setHost( final String host ) {
    215                if( host == null ) {
    216                        String errMsg = "host に null はセット出来ません。";
    217                        throw new IllegalArgumentException( errMsg );
    218                }
    219
    220                this.host = host;
    221        }
    222
    223        /**
    224         * 受信ユーザーをセットします。
    225         *
    226         * @param       user 受信ユーザー
    227         * @throws      IllegalArgumentException 引数が null の場合。
    228         */
    229        public void setUser( final String user ) {
    230                if( user == null ) {
    231                        String errMsg = "user に null はセット出来ません。";
    232                        throw new IllegalArgumentException( errMsg );
    233                }
    234                this.user = user;
    235        }
    236
    237        /**
    238         * パスワードをセットします。
    239         *
    240         * @param       passwd パスワード
    241         * @throws      IllegalArgumentException 引数が null の場合。
    242         */
    243        public void setPasswd( final String passwd ) {
    244                if( passwd == null ) {
    245                        String errMsg = "passwd に null はセット出来ません。";
    246                        throw new IllegalArgumentException( errMsg );
    247                }
    248                this.passwd = passwd;
    249        }
    250
    251        /**
    252         * 受信プロトコルをセットします。
    253         *
    254         * @param       protocol 受信プロトコル名
    255         * @throws      IllegalArgumentException 引数が null の場合。
    256         */
    257        public void setProtocol( final String protocol ) {
    258                if( protocol == null ) {
    259                        String errMsg = "protocol に null はセット出来ません。";
    260                        throw new IllegalArgumentException( errMsg );
    261                }
    262                this.protocol = protocol;
    263        }
    264
    265        /**
    266         * ポート番号をセットします。
    267         *
    268         * @param       port ポート番号
    269         */
    270        public void setPort( final int port ) {
    271                this.port = port;
    272        }
    273
    274        /**
    275         * 受信メイルボックスをセットします。
    276         *
    277         * @param       mbox 受信メイルボックス名
    278         * @throws      IllegalArgumentException 引数が null の場合。
    279         */
    280        public void setMbox( final String mbox ) {
    281                if( mbox == null ) {
    282                        String errMsg = "mbox に null はセット出来ません。";
    283                        throw new IllegalArgumentException( errMsg );
    284                }
    285                this.mbox = mbox;
    286        }
    287
    288        /**
    289         * メール受信毎に発生するイベントを伝えるリスナーをセットします。
    290         *
    291         * @param       listener MailReceiveリスナー
    292         */
    293        public void setMailReceiveListener( final MailReceiveListener listener ) {
    294                this.listener = listener;
    295        }
    296
    297        /**
    298         * メッセージをメールサーバーから削除するかどうかをセットします。
    299         *
    300         * @param       deleteFlag 削除するかどうか[true:行う/false:行わない]
    301         */
    302        public void setDelete( final boolean deleteFlag ) {
    303                this.deleteFlag = deleteFlag;
    304        }
    305
    306        /**
    307         * 文字エンコーディングをセットします。
    308         *
    309         * 文字エンコーディングには、Windwos-31J , MS932 , ISO-2022-JP を指定できます。
    310         * 初期値は、SystemResource.properties ファイルの MAIL_DEFAULT_CHARSET 属性で
    311         * 設定できます。
    312         *
    313         * @param   charset 文字エンコーディング
    314         * @throws      IllegalArgumentException 引数が null の場合。
    315         */
    316        public void setCharset( final String charset ) {
    317                if( charset == null ) {
    318                        String errMsg = "charset に null はセット出来ません。";
    319                        throw new IllegalArgumentException( errMsg );
    320                }
    321                this.charset = charset;
    322        }
    323
    324        /**
    325         * 最大取り込み件数をセットします(初期値:100)(0:[無制限])。
    326         *
    327         * @og.rev 5.5.8.5 (2012/11/27) 0を無制限として処理します。
    328         *
    329         * @param       maxRowCount 最大取り込み件数
    330         */
    331        public void setMaxRowCount( final int maxRowCount ) {
    332                this.maxRowCount = ( maxRowCount > 0 ) ? maxRowCount : Integer.MAX_VALUE ;
    333        }
    334
    335        /**
    336         * メール検索する場合のマッチ条件のキーと値の HybsEntry をセットします。
    337         * Subject,From,Body,それ以外は、Header 文字列をキーにします。
    338         *
    339         * @param       matchTerm HybsEntryオブジェクト
    340         */
    341        public void addMatchTerm( final HybsEntry matchTerm ) {
    342                matchList.add( matchTerm );
    343        }
    344
    345        /**
    346         * デバッグ情報の表示を行うかどうかをセットします。
    347         *
    348         * @param       debug 有無[true/false]
    349         */
    350        public void setDebug( final boolean debug ) {
    351                this.debug = debug;
    352        }
    353
    354        /**
    355         * デバッグ情報の表示を行います。
    356         * 実際の処理は、debug フラグに設定値によります。
    357         *
    358         * @param       msgs String... 可変長メッセージ
    359         */
    360        private void debugMsg( final String... msgs ) {
    361                if( debug ) {
    362                        for( String msg : msgs ) {
    363                                System.out.print( msg );
    364                        }
    365                        System.out.println();
    366                }
    367        }
    368
    369        /**
    370         * コマンドから実行できる、テスト用の main メソッドです。
    371         *
    372         * Usage: java org.opengion.fukurou.mail.MailTX MailRX host user passwd [saveDir]
    373         * で、複数の添付ファイルを送付することができます。
    374         *
    375         * @param   args 引数配列
    376         * @throws Exception なんらかのエラーが発生した場合。
    377         */
    378        public static void main( final String[] args ) throws Exception {
    379                if(args.length < 3) {
    380                        LogWriter.log("Usage: java org.opengion.fukurou.mail.MailRX host user passwd [saveDir]");
    381                        System.exit(1);
    382                }
    383                final String dir = (args.length == 4) ? args[3] : null;
    384
    385                MailRX recive = new MailRX();
    386
    387                recive.setHost( args[0] );
    388                recive.setUser( args[1] );
    389                recive.setPasswd( args[2] );
    390                recive.setCharset( "ISO-2022-JP" );
    391
    392                MailReceiveListener listener = new MailReceiveListener() {
    393                        public boolean receive( final MailMessage message ) {
    394                                System.out.println( message.getSimpleMessage() );
    395
    396                                if( dir != null ) {
    397                                        message.saveSimpleMessage( dir );
    398                                }
    399                                return true ;
    400                        }
    401                };
    402                recive.setMailReceiveListener( listener );
    403
    404                recive.start();
    405        }
    406}