    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.system.OgRuntimeException ;         // 6.4.2.0 (2016/01/29)
    019import org.opengion.fukurou.system.LogWriter;
    020import static org.opengion.fukurou.system.HybsConst.CR;                         // 6.1.0.0 (2014/12/26) refactoring
    021
    022import java.io.UnsupportedEncodingException;
    023import java.util.Properties;
    024import java.util.Date;
    025
    026import jakarta.activation.FileDataSource;
    027import jakarta.activation.DataHandler;
    028import jakarta.mail.internet.InternetAddress;
    029import jakarta.mail.internet.AddressException;
    030import jakarta.mail.internet.MimeMessage;
    031import jakarta.mail.internet.MimeMultipart;
    032import jakarta.mail.internet.MimeBodyPart;
    033import jakarta.mail.internet.MimeUtility;
    034import jakarta.mail.Authenticator;                                                                      // 6.2.4.1 (2015/05/22) SMTP_AUTH 対応
    035import jakarta.mail.PasswordAuthentication;                                                     // 6.2.4.1 (2015/05/22) SMTP_AUTH 対応
    036import jakarta.mail.Store;
    037import jakarta.mail.Transport;
    038import jakarta.mail.Session;
    039import jakarta.mail.Message;
    040import jakarta.mail.MessagingException;
    041import jakarta.mail.IllegalWriteException;
    042
    043/**
    044 * MailTX は、SMTPプロトコルによるメール送信プログラムです。
    045 *
    046 * E-Mail で日本語を送信する場合、ISO-2022-JP(JISコード)化して、7bit で
    047 * エンコードして送信する必要がありますが、Windows系の特殊文字や、unicodeと
    048 * 文字のマッピングが異なる文字などが、文字化けします。
    049 * 対応方法としては、
    050 * １．Windows-31J + 8bit 送信
    051 * ２．ISO-2022-JP に独自変換 + 7bit 送信
    052 * の方法があります。
    053 * 今回、この２つの方法について、対応いたしました。
    054 *
    055 * ※ 6.3.8.0 (2015/09/11)
    056 *    useSSL属性=true に設定すると、protocolに、smtps を使用します。
    057 *
    058 * @version  4.0
    059 * @author   Kazuhiko Hasegawa
    060 * @since    JDK5.0,
    061 */
    062public class MailTX {
    063        private static final String AUTH_PBS   = "POP_BEFORE_SMTP";             // 5.4.3.2
    064        private static final String AUTH_SMTPA = "SMTP_AUTH";                   // 6.2.4.1 (2015/05/22) SMTP_AUTH 対応
    065
    066        /** メーラーの名称  {@value} */
    067        public static final String MAILER = "openGion Mail Ver 6.0";
    068
    069        private final String    charset  ;      // Windwos-31J , MS932 , UTF-8 , ISO-2022-JP
    070        private String[]                filename ;
    071        private String                  message  ;
    072        private Session                 session  ;
    073        private MimeMultipart   mmPart   ;
    074        private MimeMessage             mimeMsg  ;
    075        private MailCharset             mcSet    ;
    076
    077        /**
    078         * メールサーバーとデフォルト文字エンコーディングを指定して、オブジェクトを構築します。
    079         *
    080         * デフォルト文字エンコーディングは、ISO-2022-JP です。
    081         *
    082         * @param       host    メールサーバー
    083         * @throws      IllegalArgumentException 引数が null の場合。
    084         */
    085        public MailTX( final String host ) {
    086                this( host,"ISO-2022-JP" );
    087        }
    088
    089        /**
    090         * メールサーバーとデフォルト文字エンコーディングを指定して、オブジェクトを構築します。
    091         *
    092         * 文字エンコーディングには、Windwos-31J , MS932 , UTF-8 , ISO-2022-JP を指定できます。
    093         *
    094         * @og.rev 5.4.3.2 (2012/01/06) 認証対応のため
    095         * @og.rev 5.8.1.1 (2014/11/14) 認証ポート追加
    096         * @og.rev 6.3.8.0 (2015/09/11) SSL接続するかどうかを指定するパラメータを追加します。
    097         *
    098         * @param       host    メールサーバー
    099         * @param       charset 文字エンコーディング
    100         * @throws      IllegalArgumentException 引数が null の場合。
    101         */
    102        public MailTX( final String host , final String charset ) {
    103//              this( host,charset,null,null,null,null,null,false );
    104                this( host,charset,null,null,null,null,null,false,false );              // TSL,SSL
    105        }
    106
    107        /**
    108         * メールサーバーと文字エンコーディングを指定して、オブジェクトを構築します。
    109         * 認証を行う場合は認証方法を指定します。
    110         *
    111         * 文字エンコーディングには、Windwos-31J , MS932 , ISO-2022-JP を指定できます。
    112         *
    113         * @og.rev 5.1.9.0 (2010/08/01) mail.smtp.localhostの設定追加
    114         * @og.rev 5.4.3.2 (2012/01/06) 認証対応(POP Before SMTP)。引数３つ追加(将来的にはAuthentication対応？)
    115         * @og.rev 5.8.1.1 (2014/11/14) 認証ポート追加
    116         * @og.rev 6.2.4.1 (2015/05/22) SMTP_AUTH 対応
    117         * @og.rev 6.3.8.0 (2015/09/11) SSL接続するかどうかを指定するパラメータを追加します。
    118         * @og.rev 5.9.29.2 (2018/02/16) STARTTLS対応(ｷｰﾜｰﾄﾞをVer5 にあわせます)
    119         * @og.rev 5.10.20.1 (2020/03/03) 添付ファイル名文字化け対策
    120         *
    121         * @param       host            メールサーバー
    122         * @param       charset         文字エンコーディング
    123         * @param       smtpPort        SMTPポート
    124         * @param       authType        認証方法(POP_BEFORE_SMTP , SMTP_AUTH)
    125         * @param       authPort        認証ポート
    126         * @param       authUser        認証ユーザ
    127         * @param       authPass        認証パスワード
    128         * @param       useStarttls 暗号化通信設定（STARTTLS) 5.9.29.2
    129         * @param       useSSL          SSL接続するかどうか
    130         * @throws      IllegalArgumentException 引数が null の場合。
    131         */
    132        public MailTX( final String host , final String charset, final String smtpPort
    133//                              ,final String authType, final String authPort, final String authUser, final String authPass, final boolean useSSL) {
    134                                ,final String authType, final String authPort, final String authUser, final String authPass, final boolean useStarttls, final boolean useSSL ) {
    135                if( host == null ) {
    136                        final String errMsg = "host に null はセット出来ません。";
    137                        throw new IllegalArgumentException( errMsg );
    138                }
    139
    140                if( charset == null ) {
    141                        final String errMsg = "charset に null はセット出来ません。";
    142                        throw new IllegalArgumentException( errMsg );
    143                }
    144
    145                this.charset = charset;
    146
    147                mcSet = MailCharsetFactory.newInstance( charset );
    148
    149                // 5.10.20.1 (2020/03/03) 添付ファイル名文字化け対策(暫定)
    150                System.setProperty("mail.mime.splitlongparameters", "false");
    151                System.setProperty("mail.mime.encodeparameters", "false" );
    152
    153                final Properties prop = new Properties();
    154                prop.setProperty("mail.mime.charset"                    , charset );
    155                prop.setProperty("mail.mime.decodetext.strict"  , "false" );
    156                prop.setProperty("mail.mime.address.strict"             , "false" );
    157                prop.setProperty("mail.smtp.host"                               , host );
    158                // 5.1.9.0 (2010/08/01) 設定追加
    159                prop.setProperty("mail.smtp.localhost"                  , host );
    160                prop.setProperty("mail.host"                                    , host );       // MEssage-ID の設定に利用
    161
    162                // 5.4.3.2 ポート追加
    163                if( smtpPort != null && smtpPort.length() > 0 ){
    164                        prop.setProperty("mail.smtp.port"                       , smtpPort);    // MEssage-ID の設定に利用
    165                }
    166
    167                // 6.2.4.1 (2015/05/22) SMTP_AUTH 対応
    168                Authenticator myAuth = null;
    169                if( AUTH_SMTPA.equals( authType ) ) {
    170                        prop.setProperty("mail.smtp.auth"                       , "true" );
    171                        prop.setProperty("mail.transport.protocol"      , "smtps" );    // 6.3.8.0 (2015/09/11)
    172                        // 6.3.9.0 (2015/11/06) 名前付き static 内部クラスにリファクタリング(findbugs)
    173        //              myAuth = new MyAuthenticator( authUser,authPass );
    174                        myAuth = new Authenticator() {                                  // 5.8.7.1 (2015/05/22) SMTP認証用クラス
    175                                /**
    176                                 * パスワード認証が必要な時には呼ばれる。
    177                                 *
    178                                 * @return      PasswordAuthenticationオブジェクト
    179                                 */
    180                                @Override
    181                                protected PasswordAuthentication getPasswordAuthentication() {
    182                                        return new PasswordAuthentication( authUser,authPass );
    183                                }
    184                        };
    185                }
    186
    187                // 6.3.8.0 (2015/09/11) SSL接続するかどうかを指定するパラメータを追加します。
    188                // 5.9.29.2 (2018/02/16) STARTTLS対応  (ｷｰﾜｰﾄﾞをVer5 にあわせます)
    189//              if( useSSL ) {
    190                if ( useStarttls || useSSL ) {
    191                        prop.setProperty("mail.smtp.starttls.enable"            , "true" );                                                     // 6.3.8.0 (2015/09/11)
    192                        prop.setProperty("mail.smtp.starttls.required"          , "true" );                                                     // 6.3.8.0 (2015/09/11)
    193                //      prop.setProperty("mail.smtp.socketFactory.class"        , "javax.net.ssl.SSLSocketFactory" );
    194                //      prop.setProperty("mail.smtp.socketFactory.fallback"     , "false" );
    195                //      prop.setProperty("mail.smtp.socketFactory.port"         , String.valueOf( smtpPort ) );         // 587
    196                //      prop.setProperty("mail.transport.protocol"                      , "smtps" );    // 6.3.8.0 (2015/09/11)
    197                }
    198
    199                // 6.9.1.0 (2018/02/26) STL/SSL 両対応
    200                if ( useSSL ) {
    201                        prop.setProperty("mail.smtp.socketFactory.class"        , "javax.net.ssl.SSLSocketFactory" );
    202                        prop.setProperty("mail.smtp.socketFactory.fallback"     , "false" );
    203                        prop.setProperty("mail.smtp.socketFactory.port"         , String.valueOf( smtpPort ) );         // 465
    204                        prop.setProperty("mail.transport.protocol"                      , "smtps" );    // 6.3.8.0 (2015/09/11)
    205                }
    206
    207                session = Session.getInstance( prop, myAuth );          // 6.2.4.1 (2015/05/22) SMTP_AUTH 対応
    208
    209                // POP before SMTP認証処理 5.4.3.2
    210                if( AUTH_PBS.equals( authType ) ){
    211                        try{
    212                                // 5.8.1.1 (2014/11/14) 認証ポート追加
    213                                final int aPort = authPort == null || authPort.isEmpty() || authPass == null || authPass.isEmpty() ? -1 : Integer.parseInt(authPort) ;
    214                                final Store store = session.getStore("pop3");
    215                                store.connect( host,aPort,authUser,authPass );  // 5.8.1.1 (2014/11/14) 認証ポート追加
    216                                store.close();
    217                        }
    218                        catch( final MessagingException ex ) {
    219                                final String errMsg = "POP3 Auth Exception: "+ host + "/" + authUser;
    220                                throw new OgRuntimeException( errMsg,ex );
    221                        }
    222                }
    223
    224                mimeMsg = new MimeMessage( session );
    225        }
    226
    227        /**
    228         * メールを送信します。
    229         *
    230         */
    231        public void sendmail() {
    232                try {
    233                        mimeMsg.setSentDate( new Date() );
    234
    235                        if( filename == null || filename.length == 0 ) {
    236                                mcSet.setTextContent( mimeMsg,message );
    237                        }
    238                        else {
    239                                mmPart = new MimeMultipart();
    240                                mimeMsg.setContent( mmPart );
    241                                // テキスト本体の登録
    242                                addMmpText( message );
    243
    244                                // 添付ファイルの登録
    245                                for( int i=0; i<filename.length; i++ ) {
    246                                        addMmpFile( filename[i] );
    247                                }
    248                        }
    249
    250                        mimeMsg.setHeader("X-Mailer", MAILER );
    251                        mimeMsg.setHeader("Content-Transfer-Encoding", mcSet.getBit() );
    252                        Transport.send( mimeMsg );
    253                }
    254                catch( final AddressException ex ) {
    255                        final String errMsg = "Address Exception: " + ex.getMessage() ;
    256                        throw new OgRuntimeException( errMsg,ex );
    257                }
    258                catch( final MessagingException mex ) {
    259                        final String errMsg = "MessagingException: " + mex.getMessage() ;
    260                        throw new OgRuntimeException( errMsg,mex );
    261                }
    262        }
    263
    264        /**
    265         * MimeMessageをリセットします。
    266         *
    267         * sendmail() でメールを送信後、セッションを閉じずに別のメールを送信する場合、
    268         * リセットしてから、各種パラメータを再設定してください。
    269         * その場合は、すべてのパラメータが初期化されていますので、もう一度
    270         * 設定しなおす必要があります。
    271         *
    272         */
    273        public void reset() {
    274                mimeMsg = new MimeMessage(session);
    275        }
    276
    277        /**
    278         * 送信元(ＦＲＯＭ)アドレスをセットします。
    279         *
    280         * @param   from 送信元(ＦＲＯＭ)アドレス
    281         */
    282        public void setFrom( final String from ) {
    283                try {
    284                        if( from != null ) {
    285                                mimeMsg.setFrom( getAddress( from ) );
    286                        }
    287                } catch( final AddressException ex ) {
    288                        final String errMsg = "Address Exception: " + ex.getMessage() ;
    289                        throw new OgRuntimeException( errMsg,ex );
    290                } catch( final MessagingException mex ) {
    291                        final String errMsg = "MessagingException: " + mex.getMessage() ;
    292                        throw new OgRuntimeException( errMsg,mex );
    293                }
    294        }
    295
    296        /**
    297         * 送信先(ＴＯ)アドレス配列をセットします。
    298         *
    299         * @param   to 送信先(ＴＯ)アドレス配列(可変長引数)
    300         */
    301        public void setTo( final String... to ) {
    302                try {
    303                        if( to != null && to.length > 0 ) {             // 6.1.1.0 (2015/01/17) 可変長引数でもnullは来る。
    304                                mimeMsg.setRecipients( Message.RecipientType.TO, getAddress( to ) );
    305                        }
    306                } catch( final AddressException ex ) {
    307                        final String errMsg = "Address Exception: " + ex.getMessage() ;
    308                        throw new OgRuntimeException( errMsg,ex );
    309                } catch( final MessagingException mex ) {
    310                        final String errMsg = "MessagingException: " + mex.getMessage() ;
    311                        throw new OgRuntimeException( errMsg,mex );
    312                }
    313        }
    314
    315        /**
    316         * 送信先(ＣＣ)アドレス配列をセットします。
    317         *
    318         * @param   cc 送信先(ＣＣ)アドレス配列(可変長引数)
    319         */
    320        public void setCc( final String... cc ) {
    321                try {
    322                        if( cc != null && cc.length > 0 ) {             // 6.1.1.0 (2015/01/17) 可変長引数でもnullは来る。
    323                                mimeMsg.setRecipients( Message.RecipientType.CC, getAddress( cc ) );
    324                        }
    325                } catch( final AddressException ex ) {
    326                        final String errMsg = "Address Exception: " + ex.getMessage() ;
    327                        throw new OgRuntimeException( errMsg,ex );
    328                } catch( final MessagingException mex ) {
    329                        final String errMsg = "MessagingException: " + mex.getMessage() ;
    330                        throw new OgRuntimeException( errMsg,mex );
    331                }
    332        }
    333
    334        /**
    335         * 送信先(ＢＣＣ)アドレス配列をセットします。
    336         *
    337         * @param   bcc 送信先(ＢＣＣ)アドレス配列(可変長引数)
    338         */
    339        public void setBcc( final String... bcc ) {
    340                try {
    341                        if( bcc != null && bcc.length > 0 ) {           // 6.1.1.0 (2015/01/17) 可変長引数でもnullは来る。
    342                                mimeMsg.setRecipients( Message.RecipientType.BCC, getAddress( bcc ) );
    343                        }
    344                } catch( final AddressException ex ) {
    345                        final String errMsg = "Address Exception: " + ex.getMessage() ;
    346                        throw new OgRuntimeException( errMsg,ex );
    347                } catch( final MessagingException mex ) {
    348                        final String errMsg = "MessagingException: " + mex.getMessage() ;
    349                        throw new OgRuntimeException( errMsg,mex );
    350                }
    351        }
    352
    353        /**
    354         * 送信先(ＴＯ)アドレス配列をクリアします。
    355         * @og.rev 4.3.6.0 (2009/04/01) 新規追加
    356         *
    357         */
    358        public void clearTo() {
    359                try {
    360                        mimeMsg.setRecipients( Message.RecipientType.TO, (InternetAddress[])null );
    361                }
    362                // 7.2.9.5 (2020/11/28) PMD:'catch' branch identical to 'IllegalWriteException' branch
    363                catch( final IllegalWriteException | IllegalStateException ex ) {
    364                        final String errMsg = "Address Exception: " + ex.getMessage() ;
    365                        throw new OgRuntimeException( errMsg,ex );
    366                }
    367//              catch( final IllegalWriteException ex ) {
    368//                      final String errMsg = "Address Exception: " + ex.getMessage() ;
    369//                      throw new OgRuntimeException( errMsg,ex );
    370//              }
    371//              catch( final IllegalStateException ex ) {
    372//                      final String errMsg = "Address Exception: " + ex.getMessage() ;
    373//                      throw new OgRuntimeException( errMsg,ex );
    374//              }
    375                catch( final MessagingException mex ) {
    376                        final String errMsg = "MessagingException: " + mex.getMessage() ;
    377                        throw new OgRuntimeException( errMsg,mex );
    378                }
    379        }
    380
    381        /**
    382         * 送信先(CC)アドレス配列をクリアします。
    383         *
    384         * @og.rev 4.3.6.0 (2009/04/01) 新規追加
    385         */
    386        public void clearCc() {
    387                try {
    388                        mimeMsg.setRecipients( Message.RecipientType.CC, (InternetAddress[])null );
    389                }
    390                // 7.2.9.5 (2020/11/28) PMD:'catch' branch identical to 'IllegalWriteException' branch
    391                catch( final IllegalWriteException | IllegalStateException ex ) {
    392                        final String errMsg = "Address Exception: " + ex.getMessage() ;
    393                        throw new OgRuntimeException( errMsg,ex );
    394                }
    395//              catch( final IllegalWriteException ex ) {
    396//                      final String errMsg = "Address Exception: " + ex.getMessage() ;
    397//                      throw new OgRuntimeException( errMsg,ex );
    398//              }
    399//              catch( final IllegalStateException ex ) {
    400//                      final String errMsg = "Address Exception: " + ex.getMessage() ;
    401//                      throw new OgRuntimeException( errMsg,ex );
    402//              }
    403                catch( final MessagingException mex ) {
    404                        final String errMsg = "MessagingException: " + mex.getMessage() ;
    405                        throw new OgRuntimeException( errMsg,mex );
    406                }
    407        }
    408
    409        /**
    410         * 送信先(BCC)アドレス配列をクリアします。
    411         * @og.rev 4.3.6.0 (2009/04/01) 新規追加
    412         *
    413         */
    414        public void clearBcc() {
    415                try {
    416                        mimeMsg.setRecipients( Message.RecipientType.BCC, (InternetAddress[])null );
    417                }
    418                // 7.2.9.5 (2020/11/28) PMD:'catch' branch identical to 'IllegalWriteException' branch
    419                catch( final IllegalWriteException | IllegalStateException ex ) {
    420                        final String errMsg = "Address Exception: " + ex.getMessage() ;
    421                        throw new OgRuntimeException( errMsg,ex );
    422                }
    423//              catch( final IllegalWriteException ex ) {
    424//                      final String errMsg = "Address Exception: " + ex.getMessage() ;
    425//                      throw new OgRuntimeException( errMsg,ex );
    426//              }
    427//              catch( final IllegalStateException ex ) {
    428//                      final String errMsg = "Address Exception: " + ex.getMessage() ;
    429//                      throw new OgRuntimeException( errMsg,ex );
    430//              }
    431                catch( final MessagingException mex ) {
    432                        final String errMsg = "MessagingException: " + mex.getMessage() ;
    433                        throw new OgRuntimeException( errMsg,mex );
    434                }
    435        }
    436
    437        /**
    438         * 返信元(replyTo)アドレス配列をセットします。
    439         *
    440         * @param   replyTo 返信元(replyTo)アドレス配列(可変長引数)
    441         */
    442        public void setReplyTo( final String... replyTo ) {
    443                try {
    444                        if( replyTo != null && replyTo.length > 0 ) {           // 6.1.1.0 (2015/01/17) 可変長引数でもnullは来る。
    445                                mimeMsg.setReplyTo( getAddress( replyTo ) );
    446                        }
    447                } catch( final AddressException ex ) {
    448                        final String errMsg = "Address Exception: " + ex.getMessage() ;
    449                        throw new OgRuntimeException( errMsg,ex );
    450                } catch( final MessagingException mex ) {
    451                        final String errMsg = "MessagingException: " + mex.getMessage() ;
    452                        throw new OgRuntimeException( errMsg,mex );
    453                }
    454        }
    455
    456        /**
    457         * タイトルをセットします。
    458         *
    459         * @param   subject タイトル
    460         */
    461        public void setSubject( final String subject ) {
    462                // Servlet からの読み込みは、iso8859_1 でエンコードされた文字が
    463                // セットされるので、ユニコードに変更しておかないと文字化けする。
    464                // JRun 3.0 では、問題なかったが、tomcat3.1 では問題がある。
    465                try {
    466                        if( subject != null ) {
    467                                mimeMsg.setSubject( mcSet.encodeWord( subject ) );
    468                        }
    469                } catch( final AddressException ex ) {
    470                        final String errMsg = "Address Exception: " + ex.getMessage() ;
    471                        throw new OgRuntimeException( errMsg,ex );
    472                } catch( final MessagingException mex ) {
    473                        final String errMsg = "MessagingException: " + mex.getMessage() ;
    474                        throw new OgRuntimeException( errMsg,mex );
    475                }
    476        }
    477
    478        /**
    479         * 添付ファイル名配列をセットします。
    480         *
    481         * @param   fname 添付ファイル名配列(可変長引数)
    482         */
    483        public void setFilename( final String... fname ) {
    484                if( fname != null && fname.length > 0 ) {               // 6.1.1.0 (2015/01/17) 可変長引数でもnullは来る。
    485                        final int size = fname.length;
    486                        filename = new String[size];
    487                        System.arraycopy( fname,0,filename,0,size );
    488                }
    489        }
    490
    491        /**
    492         * メッセージ(本文)をセットします。
    493         *
    494         * @param   msg メッセージ(本文)
    495         */
    496        public void setMessage( final String msg ) {
    497                // なぜか、メッセージの最後は、<CR><LF>をセットしておく。
    498
    499                if( msg == null ) { message = CR; }
    500                else {              message = msg + CR; }
    501        }
    502
    503        /**
    504         * デバッグ情報の表示を行うかどうかをセットします。
    505         *
    506         * @param   debug 表示有無[true/false]
    507         */
    508        public void setDebug( final boolean debug ) {
    509            session.setDebug( debug );
    510        }
    511
    512        /**
    513         * 指定されたファイルをマルチパートに追加します。
    514         *
    515         * @param   fileStr マルチパートするファイル名
    516         */
    517        private void addMmpFile( final String fileStr ) {
    518                try {
    519                        final MimeBodyPart mbp = new MimeBodyPart();
    520                        final FileDataSource fds = new FileDataSource(fileStr);
    521                        mbp.setDataHandler(new DataHandler(fds));
    522                        mbp.setFileName(MimeUtility.encodeText(fds.getName(), charset, "B"));
    523                        mbp.setHeader("Content-Transfer-Encoding", "base64");
    524                        mmPart.addBodyPart(mbp);
    525                }
    526                catch( final UnsupportedEncodingException ex ) {
    527                        final String errMsg = "Multipart UnsupportedEncodingException: " + ex.getMessage() ;
    528                        throw new OgRuntimeException( errMsg,ex );
    529                }
    530                catch( final MessagingException mex ) {
    531                        final String errMsg = "MessagingException: " + mex.getMessage() ;
    532                        throw new OgRuntimeException( errMsg,mex );
    533                }
    534        }
    535
    536        /**
    537         * 指定された文字列をマルチパートに追加します。
    538         *
    539         * @param   textStr マルチパートする文字列
    540         */
    541        private void addMmpText( final String textStr ) {
    542                try {
    543                        final MimeBodyPart mbp = new MimeBodyPart();
    544                        mbp.setText(textStr, charset);
    545                        mbp.setHeader("Content-Transfer-Encoding", mcSet.getBit());
    546                        mmPart.addBodyPart(mbp, 0);
    547                }
    548                catch( final MessagingException mex ) {
    549                        final String errMsg = "MessagingException: " + mex.getMessage() ;
    550                        throw new OgRuntimeException( errMsg,mex );
    551                }
    552        }
    553
    554        /**
    555         * 文字エンコードを考慮した InternetAddress を作成します。
    556         *
    557         * @param   adrs オリジナルのアドレス文字列
    558         *
    559         * @return  文字エンコードを考慮した InternetAddress
    560         */
    561        private InternetAddress getAddress( final String adrs ) {
    562                final InternetAddress rtnAdrs ;
    563                final int sep = adrs.indexOf( '<' );
    564                if( sep >= 0 ) {
    565                        final String address  = adrs.substring( sep+1,adrs.indexOf( '>' ) ).trim();
    566                        final String personal = adrs.substring( 0,sep ).trim();
    567
    568                        rtnAdrs = mcSet.getAddress( address,personal );
    569                }
    570                else {
    571                        try {
    572                                rtnAdrs = new InternetAddress( adrs );
    573                        }
    574                        catch( final AddressException ex ) {
    575                                final String errMsg = "指定のアドレスをセットできません。"
    576                                                                        + "adrs=" + adrs + " , msg=" + ex.getMessage() ;
    577                                throw new OgRuntimeException( errMsg,ex );
    578                        }
    579                }
    580
    581                return rtnAdrs ;
    582        }
    583
    584        /**
    585         * 文字エンコードを考慮した InternetAddress を作成します。
    586         * これは、アドレス文字配列から、InternetAddress 配列を作成する、
    587         * コンビニエンスメソッドです。
    588         * 処理そのものは、#getAddress( String ) をループしているだけです。
    589         *
    590         * @param   adrs アドレス文字配列(可変長引数)
    591         *
    592         * @return  文字エンコード後のInternetAddress配列
    593         * @see     #getAddress( String )
    594         */
    595        private InternetAddress[] getAddress( final String... adrs ) {
    596                InternetAddress[] rtnAdrs = new InternetAddress[adrs.length];
    597                for( int i=0; i<adrs.length; i++ ) {
    598                        rtnAdrs[i] = getAddress( adrs[i] );
    599                }
    600
    601                return rtnAdrs ;
    602        }
    603
    604//      /**
    605//       * jakarta.mail.Authenticator クラスの名前付き static 内部クラス
    606//       *
    607//       * SMTP認証用クラスとして使用します。6.2.4.1 (2015/05/22)
    608//       *
    609//       * 名前付き static 内部クラスにリファクタリングします(findbugs)。
    610//       *
    611//       * @og.rev 6.3.9.0 (2015/11/06) 新規追加
    612//       * @og.rev 6.3.9.1 (2015/11/27) 修飾子を、なし → private に変更。
    613//       *
    614//       * @return  Authenticatorオブジェクト
    615//       * @see     jakarta.mail.Authenticator
    616//       */
    617//      private static final class MyAuthenticator extends Authenticator {
    618//              private final String authUser ;                         // 6.3.9.1 (2015/11/27) 修飾子を、なし → private に変更。
    619//              private final String authPass ;                         // 6.3.9.1 (2015/11/27) 修飾子を、なし → private に変更。
    620//
    621//              /**
    622//               * ユーザ,パスワードを指定したコンストラクター。
    623//               *
    624//               * @og.rev 6.3.9.0 (2015/11/06) 新規追加
    625//               * @og.rev 6.4.1.1 (2016/01/16) PMD refactoring. It is a good practice to call super() in a constructor
    626//               *
    627//               * @param       authUser        認証ユーザ
    628//               * @param       authPass        認証パスワード
    629//               */
    630//              public MyAuthenticator( final String authUser, final String authPass ) {
    631//                      super();
    632//                      this.authUser = authUser;
    633//                      this.authPass = authPass;
    634//              }
    635//
    636//              /**
    637//               * パスワード認証が必要な時に呼ばれます。
    638//               *
    639//               * @og.rev 6.3.9.0 (2015/11/06) 新規追加
    640//               *
    641//               * @return      PasswordAuthenticationオブジェクト
    642//               */
    643//              @Override
    644//              protected PasswordAuthentication getPasswordAuthentication() {
    645//                      return new PasswordAuthentication( authUser,authPass );
    646//              }
    647//      }
    648
    649        /**
    650         * コマンドから実行できる、テスト用の main メソッドです。
    651         *
    652         * Usage: java org.opengion.fukurou.mail.MailTX &lt;from&gt; &lt;to&gt; &lt;host&gt; [&lt;file&gt; ....]
    653         * で、複数の添付ファイルを送付することができます。
    654         *
    655         * @og.rev 6.3.6.0 (2015/08/16) System.arraycopy が使える箇所は、置き換えます。
    656         *
    657         * @param       args    コマンド引数配列
    658         */
    659        public static void main( final String[] args ) {
    660                if( args.length < 3 ) {
    661                        LogWriter.log("Usage: java org.opengion.fukurou.mail.MailTX <from> <to> <host> [<file> ....]");
    662                        return ;
    663                }
    664
    665                final String host  = args[2] ;
    666                final String chset = "ISO-2022-JP" ;
    667
    668                final MailTX sender = new MailTX( host,chset );
    669
    670                sender.setFrom( args[0] );
    671                final String[] to = { args[1] };
    672                sender.setTo( to );
    673
    674                if( args.length > 3 ) {
    675                        final String[] filename = new String[args.length-3];
    676                        // 6.3.6.0 (2015/08/16) System.arraycopy が使える箇所は、置き換えます。
    677                        System.arraycopy( args,3,filename,0,filename.length );          // 6.3.6.0 (2015/08/16)
    678                        sender.setFilename( filename );
    679                }
    680
    681                sender.setSubject( "メール送信テスト" );
    682                final String msg = "これはテストメールです。" + CR
    683                                                +       "うまく受信できましたか?" + CR;
    684                sender.setMessage( msg );
    685
    686                sender.sendmail();
    687        }
    688}