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