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 を介して、1メールずつ処理します。
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;
333                this.maxRowCount = ( maxRowCount > 0 ) ? maxRowCount : Integer.MAX_VALUE ;
334        }
335
336        /**
337         * メール検索する場合のマッチ条件のキーと値の HybsEntry をセットします。
338         * Subject,From,Body,それ以外は、Header 文字列をキーにします。
339         *
340         * @param       matchTerm HybsEntryオブジェクト
341         */
342        public void addMatchTerm( final HybsEntry matchTerm ) {
343                matchList.add( matchTerm );
344        }
345
346        /**
347         * デバッグ情報の表示を行うかどうかをセットします。
348         *
349         * @param       debug 有無[true/false]
350         */
351        public void setDebug( final boolean debug ) {
352                this.debug = debug;
353        }
354
355        /**
356         * デバッグ情報の表示を行います。
357         * 実際の処理は、debug フラグに設定値によります。
358         *
359         * @param       msgs String... 可変長メッセージ
360         */
361        private void debugMsg( final String... msgs ) {
362                if( debug ) {
363                        for( String msg : msgs ) {
364                                System.out.print( msg );
365                        }
366                        System.out.println();
367                }
368        }
369
370        /**
371         * コマンドから実行できる、テスト用の main メソッドです。
372         *
373         * Usage: java org.opengion.fukurou.mail.MailTX MailRX host user passwd [saveDir]
374         * で、複数の添付ファイルを送付することができます。
375         *
376         * @param   args 引数配列
377         * @throws Exception なんらかのエラーが発生した場合。
378         */
379        public static void main( final String[] args ) throws Exception {
380                if(args.length < 3) {
381                        LogWriter.log("Usage: java org.opengion.fukurou.mail.MailRX host user passwd [saveDir]");
382                        System.exit(1);
383                }
384                final String dir = (args.length == 4) ? args[3] : null;
385
386                MailRX recive = new MailRX();
387
388                recive.setHost( args[0] );
389                recive.setUser( args[1] );
390                recive.setPasswd( args[2] );
391                recive.setCharset( "ISO-2022-JP" );
392
393                MailReceiveListener listener = new MailReceiveListener() {
394                        public boolean receive( final MailMessage message ) {
395                                System.out.println( message.getSimpleMessage() );
396
397                                if( dir != null ) {
398                                        message.saveSimpleMessage( dir );
399                                }
400                                return true ;
401                        }
402                };
403                recive.setMailReceiveListener( listener );
404
405                recive.start();
406        }
407}