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 */
016 package org.opengion.fukurou.mail;
017
018 import org.opengion.fukurou.util.LogWriter;
019
020 import java.io.UnsupportedEncodingException;
021 import java.util.Properties;
022 import java.util.Date;
023
024 import javax.activation.FileDataSource;
025 import javax.activation.DataHandler;
026 import javax.mail.internet.InternetAddress;
027 import javax.mail.internet.AddressException;
028 import javax.mail.internet.MimeMessage;
029 import javax.mail.internet.MimeMultipart;
030 import javax.mail.internet.MimeBodyPart;
031 import javax.mail.internet.MimeUtility;
032 import javax.mail.Store;
033 import javax.mail.Transport;
034 import javax.mail.Session;
035 import javax.mail.Message;
036 import javax.mail.MessagingException;
037 import javax.mail.IllegalWriteException;
038
039 /**
040 * MailTX はã€?¼³??´?°プロトコルによるメール送信プログラãƒ?�§すã?
041 *
042 * E-Mail で日本語を送信する場合ã?ISO-2022-JP(JISコーãƒ?化してã€?bit で
043 * エンコードして送信するå¿?¦�がありますが、Windows系の特殊文字や、unicodeと
044 * æ–?—ã?マッピングが異なる文字などがã?æ–?—化けしますã?
045 * 対応方法としてはã€?
046 * ?‘.Windows-31J + 8bit 送信
047 * ?’.ISO-2022-JP に独自変換 + 7bit 送信
048 * の方法がありますã?
049 * 今回、この?’つの方法につã�?�¦、対応いたしましたã€?
050 *
051 * @version 4.0
052 * @author Kazuhiko Hasegawa
053 * @since JDK5.0,
054 */
055 public class MailTX {
056 private static final String CR = System.getProperty("line.separator");
057 private static final String AUTH_PBS = "POP_BEFORE_SMTP"; // 5.4.3.2
058 // private static final String AUTH_SMTPA = "SMTP_AUTH"; // 5.4.3.2
059
060 /** メーラーの名称 {@value} */
061 public static final String MAILER = "Hayabusa Mail Ver 4.0";
062
063 private final String charset ; // Windwos-31J , MS932 , ISO-2022-JP
064 private String[] filename = null;
065 private String message = null;
066 private Session session = null;
067 private MimeMultipart mmPart = null;
068 private MimeMessage mimeMsg = null;
069 private MailCharset mcSet = null;
070
071 /**
072 * メールサーバã?とãƒ?ƒ•ォルト文字エンコーãƒ?‚£ングを指定して、オブジェクトを構築しますã?
073 *
074 * ãƒ?ƒ•ォルト文字エンコーãƒ?‚£ングは、ISO-2022-JP ですã?
075 *
076 * @param host メールサーバã?
077 * @throws IllegalArgumentException 引数ã�?null の場合ã?
078 */
079 public MailTX( final String host ) {
080 this( host,"ISO-2022-JP" );
081 }
082
083 /**
084 * メールサーバã?とãƒ?ƒ•ォルト文字エンコーãƒ?‚£ングを指定して、オブジェクトを構築しますã?
085 *
086 * æ–?—エンコーãƒ?‚£ングには、Windwos-31J , MS932 , ISO-2022-JP を指定できますã?
087 *
088 * @og.rev 5.4.3.2 (2012/01/06) 認証対応ã?ため
089 * @og.rev 5.8.1.1 (2014/11/14) 認証ポã?ト追åŠ?
090 *
091 * @param host メールサーバã?
092 * @param charset æ–?—エンコーãƒ?‚£ング
093 * @throws IllegalArgumentException 引数ã�?null の場合ã?
094 */
095 public MailTX( final String host , final String charset ) {
096 // this( host,charset,null,null,null,null );
097 this( host,charset,null,null,null,null,null );
098 }
099
100 /**
101 * メールサーバã?とæ–?—エンコーãƒ?‚£ングを指定して、オブジェクトを構築しますã?
102 * 認証を行う場合ã?認証方法をæŒ?®šしますã?
103 *
104 * æ–?—エンコーãƒ?‚£ングには、Windwos-31J , MS932 , ISO-2022-JP を指定できますã?
105 *
106 * @og.rev 5.1.9.0 (2010/08/01) mail.smtp.localhostの設定追åŠ?
107 * @og.rev 5.4.3.2 (2012/01/06) 認証対å¿?POP Before SMTP)。引数?“つ追åŠ?å°?�¥çš?�«はAuthentication対応ï¼?
108 * @og.rev 5.8.1.1 (2014/11/14) 認証ポã?ト追åŠ?
109 *
110 * @param host メールサーバã?
111 * @param charset æ–?—エンコーãƒ?‚£ング
112 * @param smtpPort SMTPポã?ãƒ?
113 * @param authType 認証方æ³?5.4.3.2
114 * @param authPort 認証ポã?ãƒ?5.4.3.2
115 * @param authUser 認証ユーザ 5.4.3.2
116 * @param authPass 認証パスワーãƒ?5.4.3.2
117 * @throws IllegalArgumentException 引数ã�?null の場合ã?
118 */
119 // public MailTX( final String host , final String charset, final String port
120 // ,final String auth, final String user, final String pass) {
121 public MailTX( final String host , final String charset, final String smtpPort
122 ,final String authType, final String authPort, final String authUser, final String authPass) {
123 if( host == null ) {
124 String errMsg = "host に null はセãƒ?ƒˆ出来ませんã€?;
125 throw new IllegalArgumentException( errMsg );
126 }
127
128 if( charset == null ) {
129 String errMsg = "charset に null はセãƒ?ƒˆ出来ませんã€?;
130 throw new IllegalArgumentException( errMsg );
131 }
132
133 this.charset = charset;
134
135 mcSet = MailCharsetFactory.newInstance( charset );
136
137 Properties prop = new Properties();
138 prop.setProperty("mail.mime.charset", charset);
139 prop.setProperty("mail.mime.decodetext.strict", "false");
140 prop.setProperty("mail.mime.address.strict", "false");
141 prop.setProperty("mail.smtp.host", host);
142 // 5.1.9.0 (2010/08/01) 設定追åŠ?
143 prop.setProperty("mail.smtp.localhost", host);
144 prop.setProperty("mail.host", host); // MEssage-ID の設定に利用
145 // 5.4.3.2 ポã?ト追åŠ?
146 // if( port != null && port.length() > 0 ){
147 // prop.setProperty("mail.smtp.port", port); // MEssage-ID の設定に利用
148 // }
149 if( smtpPort != null && smtpPort.length() > 0 ){
150 prop.setProperty("mail.smtp.port", smtpPort); // MEssage-ID の設定に利用
151 }
152
153 session = Session.getInstance(prop, null);
154
155 // POP before SMTP認証処ç�?5.4.3.2
156 // if(AUTH_PBS.equals( auth )){
157 if(AUTH_PBS.equals( authType )){
158 try{
159 // 5.8.1.1 (2014/11/14) 認証ポã?ト追åŠ?
160 int aPort = (authPass == null || authPass.isEmpty()) ? -1 : Integer.parseInt(authPort) ;
161 Store store = session.getStore("pop3");
162 // store.connect(host,-1,user,pass); // 同ä¸??ストとする
163 store.connect(host,aPort,authUser,authPass); // 5.8.1.1 (2014/11/14) 認証ポã?ト追åŠ?
164 store.close();
165 }
166 catch(MessagingException ex){
167 // String errMsg = "POP3 Auth Exception: "+ host + "/" + user;
168 String errMsg = "POP3 Auth Exception: "+ host + "/" + authUser;
169 throw new RuntimeException( errMsg,ex );
170 }
171 }
172
173 mimeMsg = new MimeMessage(session);
174 }
175
176 /**
177 * メールをé?信しますã?
178 *
179 */
180 public void sendmail() {
181 try {
182 mimeMsg.setSentDate( new Date() );
183
184 if( filename == null || filename.length == 0 ) {
185 mcSet.setTextContent( mimeMsg,message );
186 }
187 else {
188 mmPart = new MimeMultipart();
189 mimeMsg.setContent( mmPart );
190 // ãƒ?‚スト本体ã?登録
191 addMmpText( message );
192
193 // 添付ファイルの登録
194 for( int i=0; i<filename.length; i++ ) {
195 addMmpFile( filename[i] );
196 }
197 }
198
199 mimeMsg.setHeader("X-Mailer", MAILER );
200 mimeMsg.setHeader("Content-Transfer-Encoding", mcSet.getBit() );
201 Transport.send( mimeMsg );
202
203 }
204 catch( AddressException ex ) {
205 String errMsg = "Address Exception: ";
206 throw new RuntimeException( errMsg,ex );
207 }
208 catch ( MessagingException mex ) {
209 String errMsg = "MessagingException: ";
210 throw new RuntimeException( errMsg,mex );
211 }
212 }
213
214 /**
215 * MimeMessageをリセãƒ?ƒˆしますã?
216 *
217 * sendmail() でメールをé?信後ã?セãƒ?‚·ョンを閉じずに別のメールをé?信する場合ã?
218 * リセãƒ?ƒˆしてから、各種パラメータをå?設定してくださいã€?
219 * そã?場合ã?、すべてのパラメータがå?期化されてã�?�¾すã?で、もã�?¸?º¦
220 * 設定しなおすå¿?¦�がありますã?
221 *
222 */
223 public void reset() {
224 mimeMsg = new MimeMessage(session);
225 }
226
227 /**
228 * 送信å…??¦?²?¯?)アドレスをセãƒ?ƒˆしますã?
229 *
230 * @param from 送信å…??¦?²?¯?)アドレス
231 */
232 public void setFrom( final String from ) {
233 try {
234 if( from != null ) {
235 mimeMsg.setFrom( getAddress( from ) );
236 }
237 } catch( AddressException ex ) {
238 String errMsg = "Address Exception: ";
239 throw new RuntimeException( errMsg,ex );
240 } catch ( MessagingException mex ) {
241 String errMsg = "MessagingException: ";
242 throw new RuntimeException( errMsg,mex );
243 }
244 }
245
246 /**
247 * 送信å…??´?¯)アドレス配å?をセãƒ?ƒˆしますã?
248 *
249 * @param to 送信å…??´?¯)アドレス配å?
250 */
251 public void setTo( final String[] to ) {
252 try {
253 if( to != null ) {
254 mimeMsg.setRecipients( Message.RecipientType.TO, getAddress( to ) );
255 }
256 } catch( AddressException ex ) {
257 String errMsg = "Address Exception: ";
258 throw new RuntimeException( errMsg,ex );
259 } catch ( MessagingException mex ) {
260 String errMsg = "MessagingException: ";
261 throw new RuntimeException( errMsg,mex );
262 }
263 }
264
265 /**
266 * 送信å…??£?£)アドレス配å?をセãƒ?ƒˆしますã?
267 *
268 * @param cc 送信å…??£?£)アドレス配å?
269 */
270 public void setCc( final String[] cc ) {
271 try {
272 if( cc != null ) {
273 mimeMsg.setRecipients( Message.RecipientType.CC, getAddress( cc ) );
274 }
275 } catch( AddressException ex ) {
276 String errMsg = "Address Exception: ";
277 throw new RuntimeException( errMsg,ex );
278 } catch ( MessagingException mex ) {
279 String errMsg = "MessagingException: ";
280 throw new RuntimeException( errMsg,mex );
281 }
282 }
283
284 /**
285 * 送信å…??¢?£?£)アドレス配å?をセãƒ?ƒˆしますã?
286 *
287 * @param bcc 送信å…??¢?£?£)アドレス配å?
288 */
289 public void setBcc( final String[] bcc ) {
290 try {
291 if( bcc != null ) {
292 mimeMsg.setRecipients( Message.RecipientType.BCC, getAddress( bcc ) );
293 }
294 } catch( AddressException ex ) {
295 String errMsg = "Address Exception: ";
296 throw new RuntimeException( errMsg,ex );
297 } catch ( MessagingException mex ) {
298 String errMsg = "MessagingException: ";
299 throw new RuntimeException( errMsg,mex );
300 }
301 }
302
303 /**
304 * 送信å…??´?¯)アドレス配å?をクリアしますã?
305 * @og.rev 4.3.6.0 (2009/04/01) 新規追åŠ?
306 *
307 */
308 public void clearTo() {
309 try {
310 mimeMsg.setRecipients( Message.RecipientType.TO, (InternetAddress[])null );
311 } catch( IllegalWriteException ex ) {
312 String errMsg = "Address Exception: ";
313 throw new RuntimeException( errMsg,ex );
314 } catch( IllegalStateException ex ) {
315 String errMsg = "Address Exception: ";
316 throw new RuntimeException( errMsg,ex );
317 } catch ( MessagingException mex ) {
318 String errMsg = "MessagingException: ";
319 throw new RuntimeException( errMsg,mex );
320 }
321 }
322
323 /**
324 * 送信å…?CC)アドレス配å?をクリアしますã?
325 * @og.rev 4.3.6.0 (2009/04/01) 新規追åŠ?
326 *
327 */
328 public void clearCc() {
329 try {
330 mimeMsg.setRecipients( Message.RecipientType.CC, (InternetAddress[])null );
331 } catch( IllegalWriteException ex ) {
332 String errMsg = "Address Exception: ";
333 throw new RuntimeException( errMsg,ex );
334 } catch( IllegalStateException ex ) {
335 String errMsg = "Address Exception: ";
336 throw new RuntimeException( errMsg,ex );
337 } catch ( MessagingException mex ) {
338 String errMsg = "MessagingException: ";
339 throw new RuntimeException( errMsg,mex );
340 }
341 }
342
343 /**
344 * 送信å…?BCC)アドレス配å?をクリアしますã?
345 * @og.rev 4.3.6.0 (2009/04/01) 新規追åŠ?
346 *
347 */
348 public void clearBcc() {
349 try {
350 mimeMsg.setRecipients( Message.RecipientType.BCC, (InternetAddress[])null );
351 } catch( IllegalWriteException ex ) {
352 String errMsg = "Address Exception: ";
353 throw new RuntimeException( errMsg,ex );
354 } catch( IllegalStateException ex ) {
355 String errMsg = "Address Exception: ";
356 throw new RuntimeException( errMsg,ex );
357 } catch ( MessagingException mex ) {
358 String errMsg = "MessagingException: ";
359 throw new RuntimeException( errMsg,mex );
360 }
361 }
362
363 /**
364 * 返信å…?replyTo)アドレス配å?をセãƒ?ƒˆしますã?
365 *
366 * @param replyTo 返信å…?replyTo)アドレス配å?
367 */
368 public void setReplyTo( final String[] replyTo ) {
369 try {
370 if( replyTo != null ) {
371 mimeMsg.setReplyTo( getAddress( replyTo ) );
372 }
373 } catch( AddressException ex ) {
374 String errMsg = "Address Exception: ";
375 throw new RuntimeException( errMsg,ex );
376 } catch ( MessagingException mex ) {
377 String errMsg = "MessagingException: ";
378 throw new RuntimeException( errMsg,mex );
379 }
380 }
381
382 /**
383 * タイトルをセãƒ?ƒˆしますã?
384 *
385 * @param subject タイトル
386 */
387 public void setSubject( final String subject ) {
388 // Servlet からの読み込みは、iso8859_1 でエンコードされたæ–?—が
389 // セãƒ?ƒˆされるã?で、ユニコードに変更しておかなã�?�¨æ–?—化けするã?
390 // JRun 3.0 では、問題なかったが、tomcat3.1 では問題があるã€?
391 try {
392 if( subject != null ) {
393 mimeMsg.setSubject( mcSet.encodeWord( subject ) );
394 }
395 } catch( AddressException ex ) {
396 String errMsg = "Address Exception: ";
397 throw new RuntimeException( errMsg,ex );
398 } catch ( MessagingException mex ) {
399 String errMsg = "MessagingException: ";
400 throw new RuntimeException( errMsg,mex );
401 }
402 }
403
404 /**
405 * 添付ファイル名é?列をセãƒ?ƒˆしますã?
406 *
407 * @param fname 添付ファイル名é?åˆ?
408 */
409 public void setFilename( final String[] fname ) {
410 if( fname != null && fname.length > 0 ) {
411 int size = fname.length;
412 filename = new String[size];
413 System.arraycopy( fname,0,filename,0,size );
414 }
415 }
416
417 /**
418 * メãƒ?‚»ージ(本æ–?をセãƒ?ƒˆしますã?
419 *
420 * @param msg メãƒ?‚»ージ(本æ–?
421 */
422 public void setMessage( final String msg ) {
423 // なぜか、メãƒ?‚»ージのæœ?¾Œã?ã€?CR><LF>をセãƒ?ƒˆしておくã€?
424
425 if( msg == null ) { message = CR; }
426 else { message = msg + CR; }
427 }
428
429 /**
430 * ãƒ?ƒ�ãƒ?‚°æƒ??の表示を行うかどã�?�‹をセãƒ?ƒˆしますã?
431 *
432 * @param debug 表示有無[true/false]
433 */
434 public void setDebug( final boolean debug ) {
435 session.setDebug( debug );
436 }
437
438 /**
439 * æŒ?®šされたファイルをã?ルチパートに追åŠ?�—ますã?
440 *
441 * @param fileStr マルチパートするファイルå�?
442 */
443 private void addMmpFile( final String fileStr ) {
444 try {
445 MimeBodyPart mbp = new MimeBodyPart();
446 FileDataSource fds = new FileDataSource(fileStr);
447 mbp.setDataHandler(new DataHandler(fds));
448 mbp.setFileName(MimeUtility.encodeText(fds.getName(), charset, "B"));
449 mbp.setHeader("Content-Transfer-Encoding", "base64");
450 mmPart.addBodyPart(mbp);
451 }
452 catch( UnsupportedEncodingException ex ) {
453 String errMsg = "Multipart UnsupportedEncodingException: ";
454 throw new RuntimeException( errMsg,ex );
455 }
456 catch ( MessagingException mex ) {
457 String errMsg = "MessagingException: ";
458 throw new RuntimeException( errMsg,mex );
459 }
460 }
461
462 /**
463 * æŒ?®šされたæ–?—å?をã?ルチパートに追åŠ?�—ますã?
464 *
465 * @param textStr マルチパートする文字å?
466 */
467 private void addMmpText( final String textStr ) {
468 try {
469 MimeBodyPart mbp = new MimeBodyPart();
470 mbp.setText(textStr, charset);
471 mbp.setHeader("Content-Transfer-Encoding", mcSet.getBit());
472 mmPart.addBodyPart(mbp, 0);
473 }
474 catch ( MessagingException mex ) {
475 String errMsg = "MessagingException: ";
476 throw new RuntimeException( errMsg,mex );
477 }
478 }
479
480 /**
481 * æ–?—エンコードをè€??した InternetAddress を作æ?しますã?
482 *
483 * @param adrs オリジナルのアドレスæ–?—å?
484 *
485 * @return æ–?—エンコードをè€??した InternetAddress
486 */
487 private InternetAddress getAddress( final String adrs ) {
488 final InternetAddress rtnAdrs ;
489 int sep = adrs.indexOf( '<' );
490 if( sep >= 0 ) {
491 String address = adrs.substring( sep+1,adrs.indexOf( '>' ) ).trim();
492 String personal = adrs.substring( 0,sep ).trim();
493
494 rtnAdrs = mcSet.getAddress( address,personal );
495 }
496 else {
497 try {
498 rtnAdrs = new InternetAddress( adrs );
499 }
500 catch( AddressException ex ) {
501 String errMsg = "æŒ?®šã?アドレスをセãƒ?ƒˆできませんã€?
502 + "adrs=" + adrs ;
503 throw new RuntimeException( errMsg,ex );
504 }
505 }
506
507 return rtnAdrs ;
508 }
509
510 /**
511 * æ–?—エンコードをè€??した InternetAddress を作æ?しますã?
512 * これは、アドレスæ–?—é?列からã?InternetAddress 配å?を作æ?するã€?
513 * コンビニエンスメソãƒ?ƒ‰ですã?
514 * 処ç�?��のもã?はã€?getAddress( String ) をループしてã�?‚‹ã�?�‘ですã?
515 *
516 * @param adrs アドレスæ–?—é?åˆ?
517 *
518 * @return æ–?—エンコード後ã?InternetAddress配å?
519 * @see #getAddress( String )
520 */
521 private InternetAddress[] getAddress( final String[] adrs ) {
522 InternetAddress[] rtnAdrs = new InternetAddress[adrs.length];
523 for( int i=0; i<adrs.length; i++ ) {
524 rtnAdrs[i] = getAddress( adrs[i] );
525 }
526
527 return rtnAdrs ;
528 }
529
530 /**
531 * コマンドから実行できる、テスト用の main メソãƒ?ƒ‰ですã?
532 *
533 * Usage: java org.opengion.fukurou.mail.MailTX <from> <to> <host> [<file> ....]
534 * で、è¤?•°の添付ファイルをé?付することができますã?
535 *
536 * @param args コマンド引数配å?
537 * @throws Exception なんらかã?エラーが発生したå?合ã?
538 */
539 public static void main( final String[] args ) throws Exception {
540 if(args.length < 3) {
541 LogWriter.log("Usage: java org.opengion.fukurou.mail.MailTX <from> <to> <host> [<file> ....]");
542 return ;
543 }
544
545 String host = args[2] ;
546 String chset = "ISO-2022-JP" ;
547
548 MailTX sender = new MailTX( host,chset );
549
550 sender.setFrom( args[0] );
551 String[] to = { args[1] };
552 sender.setTo( to );
553
554 if( args.length > 3 ) {
555 String[] filename = new String[ args.length-3 ];
556 for( int i=0; i<args.length-3; i++ ) {
557 filename[i] = args[i+3];
558 }
559 sender.setFilename( filename );
560 }
561
562 sender.setSubject( "メール送信ãƒ?‚¹ãƒ? );
563 String msg = "これはãƒ?‚¹トメールですã?" + CR +
564 "ã�?�¾く受信できましたã�?" + CR;
565 sender.setMessage( msg );
566
567 sender.sendmail();
568 }
569 }