    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.hayabusa.mail;
    017
    018import org.opengion.fukurou.system.OgRuntimeException ;                         // 6.4.2.0 (2016/01/29)
    019import static org.opengion.fukurou.system.HybsConst.BUFFER_MIDDLE;      // 6.1.0.0 (2014/12/26) refactoring
    020import static org.opengion.fukurou.system.HybsConst.CR ;                        // 6.1.0.0 (2014/12/26)
    021import static org.opengion.fukurou.util.StringUtil.csv2ArrayOnly;
    022import org.opengion.fukurou.db.DBUtil;
    023import org.opengion.hayabusa.common.HybsSystem;
    024import org.opengion.fukurou.security.HybsCryptography;                          // 5.10.11.1 (2019/05/10)
    025import org.opengion.fukurou.util.StringUtil;                                            // 5.10.11.1 (2019/05/10)
    026import org.opengion.fukurou.util.XHTMLTag;                                                      // 5.10.11.1 (2019/05/10)
    027
    028import java.util.ArrayList;
    029import java.util.List;
    030import java.util.Map;
    031import java.util.concurrent.ConcurrentMap;                                                      // 6.4.3.3 (2016/03/04)
    032import java.util.concurrent.ConcurrentSkipListMap;                                      // 6.4.3.1 (2016/02/12) refactoring
    033
    034import jakarta.mail.internet.AddressException;
    035import jakarta.mail.internet.InternetAddress;
    036
    037/**
    038 * メール定型文及びそれも基づいて各項目の合成を行うクラスです。
    039 * コンストラクタには定型文ID及びシステムIDで定型文マスタよりメールの定型文を取得します。
    040 * メール各項目のゲッターでは、定型文の内容を元にパラメータ値とマージして各項目を合成します。
    041 * 宛先について、セットした社員ID、グループIDと定型文の宛先設定に基づき、社員マスタと
    042 * グループマスタよりメールアドレス情報を取得して宛先マップを作成します。
    043 *
    044 *
    045 * @og.rev 5.6.6.0 (2013/07/05) host指定対応。GE37必須です。
    046 *
    047 * @og.group メールモジュール
    048 *
    049 * @version  4.0
    050 * @author   Sen.Li
    051 * @since    JDK1.6
    052 */
    053public class MailPattern {
    054
    055//      // 5.2.0.0 (2010/09/01) Ver4互換モード対応
    056//      // 6.9.5.0 (2018/04/23) VER4_COMPATIBLE_MODE 廃止
    057//      private static final String CONTENTS = HybsSystem.sysBool( "VER4_COMPATIBLE_MODE" ) ? "CONTENT" : "CONTENTS";
    058//      private static final String ADDRESS  = HybsSystem.sysBool( "VER4_COMPATIBLE_MODE" ) ? "MEMBER"  : "ADDRESS";
    059//      private static final String NAME_JA  = HybsSystem.sysBool( "VER4_COMPATIBLE_MODE" ) ? "NAME"    : "NAME_JA";
    060//      private static final String KBNAME   = HybsSystem.sysBool( "VER4_COMPATIBLE_MODE" ) ? "NM_KBN"  : "KBNAME";
    061
    062        // 5.1.0.0 (2009/11/04) CONTENT ⇒ CONTENTS
    063        // 5.2.0.0 (2010/09/01) Ver4互換モード対応
    064        // 6.4.1.1 (2016/01/16) selGE31 → SEL_GE31 , selGE33 → SEL_GE33 , selGE37 → SEL_GE37 refactoring
    065        // 6.9.5.0 (2018/04/23) VER4_COMPATIBLE_MODE 廃止
    066//      private static final String SEL_GE31    = "SELECT PTN_ID,FROM_ID,TO_ID,CC_ID,BCC_ID,TITLE,"+CONTENTS
    067        private static final String SEL_GE31    = "SELECT PTN_ID,FROM_ID,TO_ID,CC_ID,BCC_ID,TITLE,CONTENTS"
    068                                                                                        + " ,JOKEN" // 5.6.6.0 (2013/07/05)
    069                                                                                        + " FROM GE31"
    070                                                                                        + " WHERE SYSTEM_ID =? AND PTN_ID=? AND FGJ='1'";
    071        // 5.1.0.0 (2009/11/04) MEMBER ⇒ ADDRESS , NM_KBN ⇒ KBNAME , NAME ⇒ NAME_JA
    072        // 5.2.0.0 (2010/09/01) Ver4互換モード対応
    073        // 6.9.5.0 (2018/04/23) VER4_COMPATIBLE_MODE 廃止
    074//      private static final String     SEL_GE33        = "SELECT A."+ADDRESS+",A."+NAME_JA+",B."+NAME_JA
    075        private static final String     SEL_GE33        = "SELECT A.ADDRESS,A.NAME_JA,B.NAME_JA"
    076                                                                                        + " FROM GE33 A,GE33 B"
    077                                                                                        + " WHERE A.FGJ='1' AND B.FGJ(+)='1' AND A.GROUP_ID=B.GROUP_ID(+)"
    078//                                                                                      + " AND A."+KBNAME+"='1' AND B."+KBNAME+"(+)='0' AND A.SYSTEM_ID=B.SYSTEM_ID(+)"
    079                                                                                        + " AND A.KBNAME='1' AND B.KBNAME(+)='0' AND A.SYSTEM_ID=B.SYSTEM_ID(+)"
    080                                                                                        + " AND A.SYSTEM_ID =? "
    081                                                                                        + " AND A.GROUP_ID =?";
    082
    083        /**
    084         * GE35のメールアドレス検索文
    085         *
    086         * @og.rev 6.4.1.1 (2016/01/16) selGE35 → SEL_GE35 refactoring
    087         */
    088        // 6.9.5.0 (2018/04/23) VER4_COMPATIBLE_MODE 廃止
    089//      public static final String      SEL_GE35        = "SELECT  "+NAME_JA+",MAIL"
    090        public static final String      SEL_GE35        = "SELECT NAME_JA,MAIL"
    091                                                                                        + " FROM GE35"
    092                                                                                        + " WHERE USERID=?";
    093
    094        // 5.6.6.0 (2013/07/05) 振り分け条件
    095        private static final String     SEL_GE37        = "SELECT  HOST,PORT,AUTH,AUTHUSER,AUTHPASS"
    096                                                                                        + " FROM GE37"
    097                                                                                        + " WHERE SYSTEM_ID = ?"
    098                                                                                        + " AND JOKEN = ?"
    099                                                                                        + " AND FGJ ='1'";
    100
    101        // 内部データのカラム番号(定型文マスタテーブル)
    102        // 5.1.9.0 (2010/09/01) public ⇒ private へ変更
    103 //     private static final int GE31_PTN_ID    = 0 ;           // 未使用
    104        private static final int GE31_FROM_ID   = 1 ;
    105        private static final int GE31_TO_ID             = 2 ;
    106        private static final int GE31_CC_ID             = 3 ;
    107        private static final int GE31_BCC_ID    = 4 ;
    108        private static final int GE31_TITLE             = 5 ;
    109        private static final int GE31_CONTENTS  = 6 ;           // 5.1.0.0 (2009/11/04) CONTENT ⇒ CONTENTS
    110        private static final int GE31_JOKEN             = 7 ;           // 5.6.6.0 (2013/07/05)
    111
    112        // 内部データのカラム番号(グループマスタ)
    113        private static final int GE33_ADDRESS   = 0 ;           // 5.1.0.0 (2009/11/04) MEMBER ⇒ ADDRESS
    114        private static final int GE33_MNAME             = 1 ;
    115        private static final int GE33_GNAME             = 2 ;
    116
    117        /**
    118         * GE35のユーザ名称
    119         */
    120        public static final int GE35_NAME               = 0 ;
    121        /**
    122         * GE35のメールアドレス
    123         */
    124        public static final int GE35_MAIL               = 1 ;
    125
    126        // 内部データのカラム番号(送信ホストマスタ)
    127        private static final int GE37_HOST              = 0 ;
    128        private static final int GE37_PORT              = 1 ;
    129        private static final int GE37_AUTH              = 2 ;
    130        private static final int GE37_AUTHUSER  = 3 ;
    131        private static final int GE37_AUTHPASS  = 4 ;
    132
    133        // 内部データのカラム番号(宛先テーブル)
    134        /** カラム番号(宛先テーブル) {@value} */
    135        public static final int IDX_DST_ID      = 0 ;
    136        /** カラム番号(宛先テーブル) {@value} */
    137        public static final int IDX_GROUP_ID    = 1 ;
    138        /** カラム番号(宛先テーブル) {@value} */
    139        public static final int IDX_GROUP_NAME  = 2 ;
    140        /** カラム番号(宛先テーブル) {@value} */
    141        public static final int IDX_DST_NAME    = 3 ;
    142        /** カラム番号(宛先テーブル) {@value} */
    143        public static final int IDX_DST_ADDR    = 4 ;
    144        /** カラム番号(宛先テーブル) {@value} */
    145        public static final int IDX_DST_KBN     = 5 ;
    146        /** カラム番号(宛先テーブル) {@value} */
    147        public static final int IDX_FGJ                 = 6 ;
    148
    149        /** メール送信区分 {@value} */
    150        public static final int KBN_TO                  = 0 ;   // メール送信区分(TO)
    151        /** メール送信区分 {@value} */
    152        public static final int KBN_CC                  = 1 ;   // メール送信区分(CC)
    153        /** メール送信区分 {@value} */
    154        public static final int KBN_BCC                 = 2 ;   // メール送信区分(BCC)
    155
    156        private static final String PREFIX_GRP = "GP.";                                                 // 6.4.1.1 (2016/01/16) PreFixGroup → PREFIX_GRP refactoring
    157
    158        private final List<String> errAddrList  = new ArrayList<>();
    159        /** 6.4.3.1 (2016/02/12) 作成元のMapを、HashMap から ConcurrentHashMap に置き換え。  */
    160        private final ConcurrentMap<String, String> paramMap ;                                  // 6.4.3.3 (2016/03/04)
    161        private String  fromId                  ;
    162        private final String    toId    ;
    163        private final String    ccId    ;
    164        private final String    bccId   ;
    165        private String[][]      title           ;
    166        private String[][]      content         ;
    167
    168        // 5.6.6.0 (2013/07/05)
    169        private String host     = HybsSystem.sys( "COMMON_MAIL_SERVER" );
    170        private String smtpPort = HybsSystem.sys( "SMTP_PORT" );
    171        private String authType = HybsSystem.sys( "MAIL_SEND_AUTH" );                   // 6.0.3.0 (2014/11/13) Ver6用キーワード変更
    172        private String authPort = HybsSystem.sys( "MAIL_SEND_AUTH_PORT" );              // 5.8.1.1 (2014/11/14)
    173        private String authUser = HybsSystem.sys( "MAIL_SEND_AUTH_USER" );
    174        private String authPass = HybsSystem.sys( "MAIL_SEND_AUTH_PASSWORD" );
    175//      // 5.9.29.2 (2018/02/16) STARTTLS対応(ｷｰﾜｰﾄﾞをVer5 にあわせます)
    176//      private boolean useTLS  = HybsSystem.sysBool( "MAIL_SEND_USE_STARTTLS" ); // 5.9.29.2 (2018/02/16)
    177
    178        private final String DBID = HybsSystem.sys( "RESOURCE_DBID" );                  // 5.5.5.1 (2012/08/07) リソース系DBID 付け忘れ対応
    179
    180        private static final String urlCheckCrypt = HybsSystem.sys( "URL_CHECK_CRYPT" ); // 5.10.11.1 (2019/05/10)
    181
    182        /**
    183         * メール定型文オブジェクトを作成するコンストラクタです。
    184         * 定型文マスタより取得したデータを各フィルードにセットします。
    185         *
    186         * @og.rev 5.5.5.1 (2012/08/07) リソース系DBID 付け忘れ対策
    187         * @og.rev 5.6.6.0 (2013/07/05) 振り分け対応
    188         * @og.rev 6.0.3.0 (2014/11/13) Ver6用キーワード変更
    189         * @og.rev 6.4.1.1 (2016/01/16) DefaultMailManager.appInfo → DefaultMailManager.APP_INFO refactoring
    190         * @og.rev 6.4.3.3 (2016/03/04) 戻すMapが、not null制限つきであることを示すため、ConcurrentMap に置き換えます。
    191         *
    192         * @param       params  パラメータのマップ
    193         */
    194        public MailPattern( final ConcurrentMap<String, String> params ){
    195                paramMap = params;
    196                final String sysId = params.get( "SYSTEM_ID" );
    197                final String pid   = params.get( "PTN_ID" );
    198                final String[] selGE30Args = { sysId,pid };
    199                final String[][] ptn = DBUtil.dbExecute( SEL_GE31, selGE30Args, DefaultMailManager.APP_INFO, DBID );    // 6.4.1.1 (2016/01/16)
    200                if( ptn == null || ptn.length <=0 ) {
    201                        final String errMsg = "定型文取得できません。システムID：" + sysId + "定型文ID：" + pid + CR
    202                                                                + " SQL=" + SEL_GE31 + CR ;
    203                        throw new OgRuntimeException( errMsg );
    204                }
    205                fromId  = ptn[0][GE31_FROM_ID];
    206                toId    = ptn[0][GE31_TO_ID];
    207                ccId    = ptn[0][GE31_CC_ID];
    208                bccId   = ptn[0][GE31_BCC_ID];
    209                final String tit = ptn[0][GE31_TITLE];
    210                if( tit != null && tit.length() > 0 ) {
    211                        title = splitParam( tit );
    212                }
    213                final String con = ptn[0][GE31_CONTENTS];                       // 5.1.0.0 (2009/11/04) CONTENT ⇒ CONTENTS
    214                if( con != null && con.length() > 0 ) {
    215                        content = splitParam( con );
    216                }
    217                // 5.6.6.0 (2013/07/05) 振り分け対応
    218                final String joken = ptn[0][GE31_JOKEN];
    219                if( joken != null && joken.length() > 0 ) {
    220                        final String[] selGE37Args = { sysId,joken };
    221                        final String[][] jkn = DBUtil.dbExecute( SEL_GE37, selGE37Args, DefaultMailManager.APP_INFO, DBID );    // 6.4.1.1 (2016/01/16)
    222                        // 6.0.2.5 (2014/10/31) ptn → jkn の間違い訂正
    223                        if( jkn == null || jkn.length <=0 ) {
    224                                final String errMsg = "メールホストの振分先が取得できません。システムID：" + sysId + "振分条件：" + joken + CR
    225                                                                        + " SQL=" + SEL_GE37 + CR ;
    226                                throw new OgRuntimeException( errMsg );
    227                        }
    228                        host            = jkn[0][GE37_HOST];
    229                        smtpPort        = jkn[0][GE37_PORT];
    230                        authType        = jkn[0][GE37_AUTH];                    // 6.0.3.0 (2014/11/13) Ver6用キーワード変更
    231                        authUser        = jkn[0][GE37_AUTHUSER];
    232                        authPass        = jkn[0][GE37_AUTHPASS];
    233                }
    234        }
    235
    236        /**
    237         * 定型文の送信者ID欄の設定値とパラメータ{&#064;FROM}を元に送信者アドレスを取得します。
    238         * 定型文には{&#064;FROM}とセットされている場合は、パラメータ｛&#064;FROM｝の値を元に、社員IDをセット
    239         * されている場合、直接そのIDを元に社員マスタから送信者アドレスを取得します。
    240         * 送信者アドレス取得できなければ例外を投げます。
    241         *
    242         * @og.rev 4.3.7.5 (2009/07/08) 送信元名称が設定されていない場合は、アドレスを&lt;&gt;で囲わない
    243         *
    244         * @return      送信者アドレス
    245         */
    246        public String getFromAddr(){
    247                // 定型文に{&#064;FROM}⇒ユーザーよりセットしたデータで置換える、ユーザーIDの場合はそのまま
    248                if( "{@FROM}".equals( fromId ) ) {
    249                        fromId = paramMap.get( "FROM" );
    250                }
    251                paramMap.put( "FROM_ID" , fromId );     // 送信者ID
    252
    253                final String[] userInf = getUserInfo( fromId );
    254                String fromAddr = null;
    255                if( userInf != null && checkAddr ( fromId, userInf[1] ) ){      // 送信者メールアドレスチェック
    256                        paramMap.put( "FROM_NAME", userInf[0] );                                // 送信者名前
    257                        // 4.3.7.5 (2009/07/08)
    258                        if( userInf[0] != null && userInf[0].length() > 0 ) {
    259                                fromAddr = userInf[0] + "<" + userInf[1] + ">" ;
    260                        }
    261                        else {
    262                                fromAddr = userInf[1];
    263                        }
    264                        paramMap.put( "FROM_ADDR", fromAddr );                                  // 送信者メールアドレス
    265                }
    266                else {
    267                        final String errMsg = "送信者ユーザー情報エラー。ユーザーID：" + fromId;
    268                        throw new OgRuntimeException( errMsg );
    269                }
    270
    271                return fromAddr;
    272        }
    273
    274        /**
    275         * マージ済のメールタイトルを返します。
    276         *
    277         * @return      メールタイトル
    278         *
    279         */
    280        public String getTitle(){
    281                return marge( title );
    282        }
    283
    284        /**
    285         * マージ済のメール本文を返します。
    286         *
    287         * @og.rev 5.1.0.0 (2009/11/04) HEADER ⇒ H_TXT , FOOTER ⇒ F_TXT カラム名変更
    288         *
    289         * @return      メール本文
    290         * @og.rtnNotNull
    291         */
    292        public String getContent(){
    293                final StringBuilder contentBuf = new StringBuilder( BUFFER_MIDDLE );
    294                final String header = paramMap.get( "HEADER" );         // 5.1.0.0 (2009/11/04) HEADER ⇒ H_TXT
    295                if( header != null ) {
    296                        contentBuf.append( header ).append( '\n' );
    297                }
    298                contentBuf.append( marge( content ) ).append( '\n' );
    299                final String fooder = paramMap.get( "FOOTER" );         // 5.1.0.0 (2009/11/04) FOOTER ⇒ F_TXT
    300                if( fooder != null ) {
    301                        contentBuf.append( fooder );
    302                }
    303
    304                return contentBuf.toString();
    305        }
    306
    307        /**
    308         * 引数の文字列により、定数文字列の部分とパラメータの部分を分解します。
    309         * 例："A{&#064;PARAM1}B"⇒rtn[0][0]="A",rtn[0][1]="B",rtn[1][0]="PARAM1"
    310         *
    311         * @param       src             分解対象の文字列
    312         *
    313         * @return      定数文字列の部分とパラメータの部分を分解した配列
    314         * @og.rtnNotNull
    315         */
    316        private String[][] splitParam( final String src ) {
    317                if( src == null ) { return new String[2][0]; }
    318
    319                final ArrayList<String> listCons = new ArrayList<>() ;
    320                final ArrayList<String> listPara = new ArrayList<>() ;
    321
    322                int start = 0;
    323                int index = src.indexOf( "{@" );
    324                while( index >= 0 ) {
    325                        listCons.add( src.substring( start, index ) );
    326                        final int end = src.indexOf( '}',index );
    327                        if( end < 0 ) {
    328                                final String errMsg = "{@ と } との対応関係がずれています。"
    329                                                        + "src=[" + src + "] : index=" + index ;
    330                                throw new OgRuntimeException( errMsg );
    331                        }
    332                        listPara.add( src.substring( index + 2, end ));
    333
    334                        start = end+1 ;
    335                        index = src.indexOf( "{@",start );
    336                }
    337                listCons.add ( src.substring( start ) );
    338
    339                String[][] rtn = new String[2][];
    340                rtn[0] = listCons.toArray( new String[listCons.size()] );
    341                rtn[1] = listPara.toArray( new String[listPara.size()] );
    342
    343                return rtn;
    344        }
    345
    346        /**
    347         * 送信先のアドレスをセットします。
    348         * 定型文に定義されている宛先(TO、CC、BCC)を引数として渡します。引数には{&#064;TO}、{&#064;CC}、{&#064;BCC}が含まれています。
    349         * {&#064;TO}、{&#064;CC}、{&#064;BCC}を初期設定の値で置換えて、実のメールアドレスマップを作成します。
    350         *
    351         * @og.rev 6.4.3.1 (2016/02/12) 毎回作成しているので、ローカルに移動する。
    352         * @og.rev 6.4.3.3 (2016/03/04) 戻すMapが、not null制限つきであることを示すため、ConcurrentMap に置き換えます。
    353         *
    354         * @return 宛先のマップ
    355         */
    356        public ConcurrentMap<String, String[]> getDstMap(){
    357                final String[] toBuf  = getDstArray( toId );
    358                final String[] ccBuf  = getDstArray( ccId );
    359                final String[] bccBuf = getDstArray( bccId );
    360
    361                final ConcurrentMap<String, String[]> mailDstMap = new ConcurrentSkipListMap<>() ;
    362
    363                // 送信先(TO、CC、BCC)のマップを作成します。
    364                setDstAddrMap( mailDstMap , bccBuf, KBN_BCC );
    365                setDstAddrMap( mailDstMap , ccBuf , KBN_CC  );
    366                setDstAddrMap( mailDstMap , toBuf , KBN_TO  );
    367
    368                setDstWord( mailDstMap );
    369
    370                return mailDstMap;
    371        }
    372
    373        /**
    374         * アドレスエラーのメッセージリストを返します。
    375         *
    376         * @return      メッセージリスト
    377         */
    378        public List<String> getErrList(){
    379                return errAddrList;
    380        }
    381
    382        /**
    383         * 送信先のアドレスをcsv形式から配列にセットします。
    384         * 引数は定型文マスタにセットしたものです。例：{&#064;TO},C12345,GP.IT
    385         * ｛&#064;TO｝、｛&#064;CC｝、｛&#064;BCC｝はパラメータテーブルにセットしたもので置き換えます。
    386         * 分解後の配列には、ユーザーID及びグループIDが混在します。
    387         *
    388         * @param       csvId   csv形式のアドレス
    389         *
    390         * @return      送信先のアドレスの配列
    391         * @og.rtnNotNull
    392         */
    393        private String[] getDstArray( final String csvId ){
    394                final String[] csvArr = csv2ArrayOnly( csvId );
    395                final List<String> list = new ArrayList<>();
    396
    397                final int size = csvArr.length;
    398                for( int i=0; i<size; i++ ){
    399                        if( csvArr[i].startsWith( "{@" )){
    400                                final String[] tmp = csv2ArrayOnly( paramMap.get( csvArr[i].substring( 2, csvArr[i].length() - 1 ) ) );
    401                                // 6.1.0.0 (2014/12/26) refactoring : Use asList instead of tight loops
    402                                for( final String dst : tmp ) { list.add( dst ); }
    403                        }
    404                        else{
    405                                list.add( csvArr[i] );
    406                        }
    407                }
    408                return list.toArray( new String[list.size()] );
    409        }
    410
    411        /**
    412         * 送信先のアドレスマップを作成します。
    413         *
    414         *  ・引数 dstBuf にはユーザーIDとグループID混在する配列です。
    415         *  ・ユーザーIDの場合、社員マスタのビューから名前及びメールアドレスを取得してマップにセットします。
    416         *  ・グループIDの場合、グループマスタより、名前及びメールアドレスを取得してマップにセットします。
    417         *  ・重複のユーザーが存在する場合、最後にセットした方が採用されます。
    418         *
    419         * @og.rev 5.1.0.0 (2009/11/04) MEMBER ⇒ ADDRESS カラム名変更
    420         * @og.rev 6.4.3.1 (2016/02/12) dstMap をローカル変数にして、引数で渡すように変更。
    421         * @og.rev 6.4.3.3 (2016/03/04) ConcurrentHashMap を受け取ることを明確にするため、I/FをConcurrentMapに変更します。
    422         *
    423         * @param       dstMap  設定するMapオブジェクト
    424         * @param       dstBuf  ユーザーIDとグループID混在する配列
    425         * @param       kbn             送信区分[0:TO/1:CC/2:BCC]
    426         */
    427        private void setDstAddrMap( final ConcurrentMap<String, String[]> dstMap, final String[] dstBuf, final int kbn ){
    428                 // IDX_DST_ID ,IDX_GROUP_ID, IDX_GROUP_NAME ,IDX_DST_NAME ,IDX_DST_ADDR ,IDX_DST_KBN ,IDX_FGJ
    429                 final String[] dstInit = { "", "", "", "", "", Integer.toString( kbn ), "7" };
    430
    431                 final int len = dstBuf.length;
    432                 for( int i=0; i<len; i++ ){
    433                         if( dstBuf[i].startsWith( PREFIX_GRP ) ) { // グループIDの場合、グループマスタより、メンバーを取得します。
    434                                final String[][] groupUsers = getGroupUsers( dstBuf[i].substring( PREFIX_GRP.length() ) );
    435                                if( groupUsers.length > 0 ) {
    436                                        final int memberCnt = groupUsers.length;
    437                                        for( int j=0; j<memberCnt; j++ ) { // グループメンバーの処理
    438                                                String[] grpMember = dstInit.clone();
    439                                                grpMember[IDX_GROUP_ID]   = dstBuf[i].substring( PREFIX_GRP.length() ); // グループID
    440                                                grpMember[IDX_GROUP_NAME] = groupUsers[j][GE33_GNAME];          // グループ名
    441                                                grpMember[IDX_DST_ID]     = groupUsers[j][GE33_ADDRESS];        // 宛先ID
    442                                                grpMember[IDX_DST_NAME]   = groupUsers[j][GE33_MNAME];          // 宛先名
    443                                                if( groupUsers[j][GE33_ADDRESS].contains( "@" ) ) {             // メールアドレスがセットされる場合
    444                                                        grpMember[IDX_DST_ADDR] = groupUsers[j][GE33_ADDRESS];  //　メールアドレス
    445                                                }
    446                                                else { // 社員IDがセットされた場合
    447                                                        final String[] userAddr = getUserInfo( groupUsers[j][GE33_ADDRESS] );
    448                                                        if( userAddr != null && userAddr.length > 0 ){
    449                                                                grpMember[IDX_DST_ADDR] = userAddr[GE35_MAIL];          //　メールアドレス
    450                                                        }
    451                                                }
    452                                                if( checkAddr( grpMember[IDX_DST_ID], grpMember[IDX_DST_ADDR] ) ){ // アドレス構文チェック
    453                                                        grpMember[IDX_FGJ] = DefaultMailManager.FGJ_SEND_WAIT;      // 送信待
    454                                                }
    455                                                else {
    456                                                        grpMember[IDX_FGJ] = DefaultMailManager.FGJ_ADDR_ERR;       // アドレス取得エラー
    457                                                        errAddrList.add( "アドレス取得エラー。ユーザーID：" + grpMember[IDX_DST_ID] + " アドレス：" + grpMember[IDX_DST_ADDR] );
    458                                                }
    459                                                dstMap.put( groupUsers[j][GE33_ADDRESS], grpMember );
    460                                        }
    461                                }
    462                                else { // グループマスタよりメンバー取得できない場合
    463                                        String[] emptyGp = dstInit.clone();
    464                                        emptyGp[IDX_GROUP_ID] = dstBuf[i]; // グループID
    465                                        emptyGp[IDX_GROUP_NAME] = "*"; // グループID
    466                                        emptyGp[IDX_DST_ID] = "NO-MEMBER"; // 宛先ID
    467                                        dstMap.put( dstBuf[i], emptyGp );
    468                                }
    469                        }
    470                        else { // ユーザーIDの場合
    471                                String[] indMember = dstInit.clone();
    472                                indMember[IDX_DST_ID] = dstBuf[i];                                                      // 宛先ID
    473                                indMember[IDX_GROUP_ID] = "*";                                                          // グループID
    474                                indMember[IDX_GROUP_NAME] = "*";                                                        // グループID
    475                                final String[] userAddr = getUserInfo( dstBuf[i] );
    476                                if( userAddr != null && userAddr.length > 0 ){
    477                                        indMember[IDX_DST_NAME] = userAddr[GE35_NAME];                  // 宛先名
    478                                        indMember[IDX_DST_ADDR] = userAddr[GE35_MAIL];                  //　メールアドレス
    479                                        if( checkAddr( indMember[IDX_DST_ID], userAddr[1] ) ) {
    480                                                indMember[IDX_FGJ] = DefaultMailManager.FGJ_SEND_WAIT;
    481                                        }
    482                                        else {
    483                                                indMember[IDX_FGJ] = DefaultMailManager.FGJ_ADDR_ERR;
    484                                                errAddrList.add( "アドレス取得エラー。ユーザーID：" + indMember[IDX_DST_ID] + " アドレス：" + indMember[IDX_DST_ADDR] );
    485                                        }
    486                                }
    487                                else {
    488                                        indMember[IDX_FGJ] = DefaultMailManager.FGJ_ADDR_ERR; // 状況コード
    489                                }
    490                                dstMap.put( dstBuf[i], indMember );
    491                        }
    492                }
    493        }
    494
    495        /**
    496         * グループマスタより、ユーザー情報を取得します。
    497         * 戻り値の配列には、ユーザーIDまたはメールアドレス、ユーザー名、グループ名が格納されています。
    498         *
    499         * @og.rev 5.5.5.1 (2012/08/07) リソース系DBID 付け忘れ対策
    500         * @og.rev 6.4.1.1 (2016/01/16) DefaultMailManager.appInfo → DefaultMailManager.APP_INFO refactoring
    501         *
    502         * @param       groupId グループID
    503         *
    504         * @return      ユーザー情報
    505         */
    506        private String[][] getGroupUsers( final String groupId ){
    507                final String sysId = paramMap.get( "SYSTEM_ID" );
    508                final String[] ge33SelArgs = { sysId,groupId };
    509                final String[][] ge33Datas = DBUtil.dbExecute( SEL_GE33,ge33SelArgs,DefaultMailManager.APP_INFO, DBID );        // 6.4.1.1 (2016/01/16)
    510
    511                if( ge33Datas.length == 0 ) {
    512                        final String errMsg = "グループ情報取得できません。グループID：" + groupId ;
    513                        if( "true".equals( paramMap.get( "ADDR_CHECK" ) ) ){
    514                                throw new OgRuntimeException( errMsg );
    515                        }
    516                        else {
    517                                errAddrList.add( errMsg );
    518                        }
    519                }
    520                return ge33Datas;
    521        }
    522
    523        /**
    524         * メール送信ホストを返します。
    525         * GE31のJOKEN(振り分け条件)を元に、GE37テーブルの HOST を取得します。
    526         * 振り分け条件が未設定の場合は、システム定数のCOMMON_MAIL_SERVER を使用します。
    527         *
    528         * (初期値:システム定数のCOMMON_MAIL_SERVER[={@og.value SystemData#COMMON_MAIL_SERVER}])。
    529         *
    530         * @og.rev 5.6.6.0 (2013/07/05)
    531         *
    532         * @return      メール送信ホスト
    533         */
    534        public String getHost(){
    535                return host;
    536        }
    537
    538        /**
    539         * メール送信ポート番号を返します
    540         * GE31のJOKEN(振り分け条件)を元に、GE37テーブルの PORT を取得します。
    541         * 振り分け条件が未設定の場合は、システム定数のSMTP_PORT を使用します。
    542         *
    543         * (初期値:システム定数のSMTP_PORT[={@og.value SystemData#SMTP_PORT}])。
    544         *
    545         * @og.rev 5.6.6.0 (2013/07/05)
    546         *
    547         * @return      メール送信ポート番号
    548         */
    549        public String getSmtpPort(){
    550                return smtpPort;
    551        }
    552
    553        /**
    554         * メール送信時認証有無を返します
    555         * GE31のJOKEN(振り分け条件)を元に、GE37テーブルの AUTH を取得します。
    556         * 振り分け条件が未設定の場合は、システム定数のMAIL_SEND_AUTH を使用します。
    557         *
    558         * (初期値:システム定数のMAIL_SEND_AUTH[={@og.value SystemData#MAIL_SEND_AUTH}])。
    559         *
    560         * @og.rev 6.0.3.0 (2014/11/13) Ver6用キーワード変更
    561         *
    562         * @return      メール送信時認証有無
    563         */
    564        public String getAuthType(){
    565                return authType;
    566        }
    567
    568        /**
    569         * メール送信認証ポートを返します。
    570         * 「POP_BEFORE_SMTP」認証を行う場合に、POPサーバーに接続するポート番号を返します。
    571         * GE37テーブルに指定するカラムはありません。
    572         * 初期値は、システム定数のMAIL_SEND_AUTH_PORT を使用します。
    573         *
    574         * (初期値:システム定数のMAIL_SEND_AUTH_PORT[={@og.value SystemData#MAIL_SEND_AUTH_PORT}])。
    575         *
    576         * @og.rev 5.8.1.1 (2014/11/14) メール送信時認証「POP_BEFORE_SMTP」追加
    577         *
    578         * @return      メール送信認証ポート
    579         */
    580        public String getAuthPort(){
    581                return authPort;
    582        }
    583
    584        /**
    585         * メール送信認証ユーザを返します
    586         * GE31のJOKEN(振り分け条件)を元に、GE37テーブルの AUTHUSER を取得します。
    587         * 振り分け条件が未設定の場合は、システム定数のMAIL_SEND_AUTH_USER を使用します。
    588         *
    589         * (初期値:システム定数のMAIL_SEND_AUTH_USER[={@og.value SystemData#MAIL_SEND_AUTH_USER}])。
    590         *
    591         * @og.rev 5.6.6.0 (2013/07/05)
    592         *
    593         * @return      メール送信認証ユーザ
    594         */
    595        public String getAuthUser(){
    596                return authUser;
    597        }
    598
    599        /**
    600         * メール送信認証パスワードを返します
    601         * GE31のJOKEN(振り分け条件)を元に、GE37テーブルの AUTHPASS を取得します。
    602         * 振り分け条件が未設定の場合は、システム定数のMAIL_SEND_AUTH_PASSWORD を使用します。
    603         *
    604         * (初期値:システム定数のMAIL_SEND_AUTH_PASSWORD[={@og.value SystemData#MAIL_SEND_AUTH_PASSWORD}])。
    605         *
    606         * @og.rev 5.6.6.0 (2013/07/05)
    607         *
    608         * @return      メール送信認証パスワード
    609         */
    610        public String getAuthPass(){
    611                return authPass;
    612        }
    613
    614//      /**
    615//       * メール送信時にSTARTTLSの暗号化を行うかを指定します。
    616//       * 現在はシステム定数のMAIL_SEND_USE_STARTTLSをそのまま使用します。
    617//       *
    618//       * (初期値:システム定数のMAIL_SEND_AUTH_PASSWORD[={@og.value org.opengion.hayabusa.common.SystemData#MAIL_SEND_USE_STARTTLS}])。
    619//       *
    620//       * @og.rev 5.9.29.2 (2018/02/16) STARTTLS対応(ｷｰﾜｰﾄﾞをVer5 にあわせます)
    621//       *
    622//       * @return      メール送信時TLS利用有無
    623//       */
    624//      public boolean getUseTLS(){
    625//              return useTLS;
    626//      }
    627
    628        /**
    629         * アドレスの構文チェックを行います、合法の場合は'true'(取得できた)、違法の場合は'false'(有効アドレス取得できず)を返します。
    630         *
    631         * @param   userId  ユーザーID
    632         * @param   address アドレス
    633         *
    634         * @return  アドレスの構文チェック結果(true:取得/false:取得できず)
    635         */
    636        private boolean checkAddr( final String userId, final String address ) {
    637                boolean rtn = true;
    638                try {
    639                        new InternetAddress( address );
    640                }
    641                catch( final AddressException aep ) {
    642                        if( "true".equals( paramMap.get( "ADDR_CHECK" ) ) ) {
    643                                final String errMsg = "ユーザーメールアドレスエラー。ユーザーID：" + userId + " アドレス：" + address;
    644                                throw new OgRuntimeException( errMsg,aep );
    645                        }
    646                        rtn = false;
    647                }
    648                return rtn;
    649        }
    650
    651        /**
    652         * 引数の配列から文書を合成します。
    653         * src[0]に配列には定数文字列、src[1]にはパラメータ
    654         *
    655         * @og.rev 5.10.11.1 (2019/05/10) 予約語対応
    656         *
    657         * @param       src     引数の配列
    658         *
    659         * @return      合成された文章
    660         * @og.rtnNotNull
    661         */
    662        private String marge( final String[][] src ){
    663                final StringBuilder rtnBuf = new StringBuilder( BUFFER_MIDDLE );
    664                if( src != null ) {
    665                        final int len = src[1].length;
    666                        for( int i=0; i< len; i++ ) {
    667                                rtnBuf.append( src[0][i] );
    668//                              rtnBuf.append( paramMap.get( src[1][i] ) );
    669                                rtnBuf.append( getReservedValue( src[1][i] ) ); // 5.10.11.1 (2019/05/10)
    670                        }
    671                        rtnBuf.append( src[0][len] );
    672                }
    673                return rtnBuf.toString();                       // 6.1.0.0 (2014/12/26) refactoring
    674        }
    675        /**
    676         * メールでのみ有効な予約語関係の処理を行います。
    677         *
    678         * LINK.URLCはURLチェックを付加したURLを作成するための予約語です。
    679         * {&#064;LINK.URLC url?key= &#064;PARAMx [CryptkeyStr]} ではをURL?key=ParamXにURLチェックを付加したものを返します。
    680         * 第一パラメータはURLで、第二パラメータを付加します。複数のパラメータの付加は出来ません。
    681         * ユーザ制限無し、期間は固定です。
    682         *
    683         * @og.rev 5.10.11.1 (2019/05/10) 新規作成
    684         *
    685         * @param key 予約語
    686         * @return 変換後文字列
    687         */
    688        private String getReservedValue( final String key ) {
    689                if( key == null ) { return ""; }
    690
    691                String rtn="" ;
    692                final int adrs = key.indexOf( '.' );
    693                if( adrs > 0 ) {
    694                        final String subKey = key.substring( adrs+1 );
    695
    696                        // 利用頻度が低いと思われるため、この中で処理してしまう
    697                        if( key.startsWith( "LINK.URLC" ) ) {
    698                                final String[] vals = StringUtil.csv2Array( subKey,' ' );
    699        //                      final String prm0 = vals[0] ; //URLCなので利用しない
    700                                String prm1 = vals.length >= 2 ? vals[1] : "" ; //URL
    701                                String prm2 = vals.length >= 3 ? vals[2] : "" ; //リクエスト変数
    702                                String prm3 = vals.length >= 4 ? vals[3] : urlCheckCrypt ;
    703
    704//                              if( prm1 != null && prm1.startsWith( "@" ) ) {                  // 7.2.9.4 (2020/11/20) PMD:This call to String.startsWith can be rewritten using String.charAt(0)
    705                                if( StringUtil.startsChar( prm1,'@' ) ) {
    706                                        prm1 = paramMap.get( prm1.substring(1) );
    707                                }
    708//                              if( prm2 != null && prm2.startsWith( "@" ) ) {                  // 7.2.9.4 (2020/11/20) PMD:This call to String.startsWith can be rewritten using String.charAt(0)
    709                                if( StringUtil.startsChar( prm2,'@' ) ) {
    710                                        prm2 = paramMap.get( prm2.substring(1) );
    711                                }
    712//                              if( prm3 != null && prm3.startsWith( "@" ) ) {                  // 7.2.9.4 (2020/11/20) PMD:This call to String.startsWith can be rewritten using String.charAt(0)
    713                                if( StringUtil.startsChar( prm3,'@' ) ) {
    714                                        prm3 = paramMap.get( prm3.substring(1) );
    715                                }
    716
    717                                rtn = prm1 + prm2; // URLとパラメータを合成
    718
    719                                if( rtn != null && rtn.length() > 0) {
    720                                        HybsCryptography criptCls = null;
    721                                        if( prm3 != null && prm3.length() > 0 ){
    722                                                criptCls = new HybsCryptography( prm3 );
    723                                        }
    724                                        //ユーザ指定なし、30日間有効のURLを作成
    725                                        rtn = XHTMLTag.addURLCheckKey( rtn, HybsSystem.URL_CHECK_KEY, "*", System.currentTimeMillis() + 1000L*60*60*24*30, criptCls );
    726                                }
    727                        }
    728                }
    729                else {
    730                        rtn = paramMap.get(key);
    731                }
    732                return rtn;
    733        }
    734
    735        /**
    736         * 宛先(TO、CC、BCC)のID、名前、メールアドレスをパラメータマップにセットします。
    737         *
    738         * @param       mailDstMap      メールあて先マップ
    739         *
    740         * @og.rev 6.3.9.0 (2015/11/06) Map.keySet() ではなく、Map.entrySet() を使う様に変更。
    741         * @og.rev 6.4.3.1 (2016/02/12) dstMap をローカル変数にして、引数で渡すように変更。
    742         * @og.rev 6.4.3.3 (2016/03/04) ConcurrentHashMap を受け取ることを明確にするため、I/FをConcurrentMapに変更します。
    743         */
    744        private void setDstWord( final ConcurrentMap<String, String[]> mailDstMap ) {
    745                final StringBuilder to_id    = new StringBuilder( BUFFER_MIDDLE );
    746                final StringBuilder to_name  = new StringBuilder( BUFFER_MIDDLE );
    747                final StringBuilder to_addr  = new StringBuilder( BUFFER_MIDDLE );
    748                final StringBuilder cc_id    = new StringBuilder( BUFFER_MIDDLE );
    749                final StringBuilder cc_name  = new StringBuilder( BUFFER_MIDDLE );
    750                final StringBuilder cc_addr  = new StringBuilder( BUFFER_MIDDLE );
    751                final StringBuilder bcc_id   = new StringBuilder( BUFFER_MIDDLE );
    752                final StringBuilder bcc_name = new StringBuilder( BUFFER_MIDDLE );
    753                final StringBuilder bcc_addr = new StringBuilder( BUFFER_MIDDLE );
    754                int kbn;
    755                // 6.3.9.0 (2015/11/06) Map.keySet() ではなく、Map.entrySet() を使う様に変更。
    756                for( final Map.Entry<String,String[]> entry : mailDstMap.entrySet() ) {
    757                        final String   dstId   = entry.getKey();
    758                        final String[] dstInfo = entry.getValue();
    759
    760                        kbn = Integer.parseInt( dstInfo[IDX_DST_KBN]);
    761                        switch( kbn ) {
    762                                case KBN_TO:
    763                                        to_id.append(   ',' ).append( dstId );
    764                                        to_name.append( ',' ).append( dstInfo[IDX_DST_NAME] );
    765                                        to_addr.append( ',' ).append( dstInfo[IDX_DST_NAME] )
    766                                                .append( '<' ).append( dstInfo[IDX_DST_ADDR] ).append( '>' );           // 6.0.2.5 (2014/10/31) char を append する。
    767                                        break;
    768                                case KBN_CC:
    769                                        cc_id.append(   ',' ).append( dstId );
    770                                        cc_name.append( ',' ).append( dstInfo[IDX_DST_NAME] );
    771                                        cc_addr.append( ',' ).append( dstInfo[IDX_DST_NAME] )
    772                                                .append( '<' ).append( dstInfo[IDX_DST_ADDR] ).append( '>' );           // 6.0.2.5 (2014/10/31) char を append する。
    773                                        break;
    774                                case KBN_BCC:
    775                                        bcc_id.append(   ',' ).append( dstId );
    776                                        bcc_name.append( ',' ).append( dstInfo[IDX_DST_NAME] );
    777                                        bcc_addr.append( ',' ).append( dstInfo[IDX_DST_NAME] )
    778                                                .append( '<' ).append( dstInfo[IDX_DST_ADDR] ).append( '>' );           // 6.0.2.5 (2014/10/31) char を append する。
    779                                        break;
    780                                default:
    781                                        final String errMsg = "このアドレス区分がサポートされません。区分：" + kbn;
    782                                        throw new OgRuntimeException( errMsg );
    783                        }
    784                }
    785
    786                // 6.4.3.1 (2016/02/12) String#toString().substring( 1 ) でなく、StringBuilder#substring( 1 ) で、String が返されます。
    787                // 予約語マップに追加します。
    788                paramMap.put( "TO_ID"   , to_id         .length() > 0 ? to_id   .substring( 1 ) : "" );
    789                paramMap.put( "TO_NAME" , to_name       .length() > 0 ? to_name .substring( 1 ) : "" );
    790                paramMap.put( "TO_ADDR" , to_addr       .length() > 0 ? to_addr .substring( 1 ) : "" );
    791                paramMap.put( "CC_ID"   , cc_id         .length() > 0 ? cc_id   .substring( 1 ) : "" );
    792                paramMap.put( "CC_NAME" , cc_name       .length() > 0 ? cc_name .substring( 1 ) : "" );
    793                paramMap.put( "CC_ADDR" , cc_addr       .length() > 0 ? cc_addr .substring( 1 ) : "" );
    794                paramMap.put( "BCC_ID"  , bcc_id        .length() > 0 ? bcc_id  .substring( 1 ) : "" );
    795                paramMap.put( "BCC_NAME", bcc_name      .length() > 0 ? bcc_name.substring( 1 ) : "" );
    796                paramMap.put( "BCC_ADDR", bcc_addr      .length() > 0 ? bcc_addr.substring( 1 ) : "" );
    797        }
    798
    799        /**
    800         * 社員マスタより名前、メールアドレスを取得します。
    801         * 戻り値 rtn[0]:ユーザー名、 rtn[1]:ユーザーメールアドレス
    802         *
    803         * @og.rev 4.3.6.6 (2009/05/15) メールアドレスが直接指定された場合に対応
    804         * @og.rev 5.5.5.1 (2012/08/07) リソース系DBID 付け忘れ対策
    805         * @og.rev 6.4.1.1 (2016/01/16) DefaultMailManager.appInfo → DefaultMailManager.APP_INFO refactoring
    806         *
    807         * @param   userId  ユーザーID
    808         *
    809         * @return      配列文字列(rtn[0]:ユーザー名、 rtn[1]:ユーザーメールアドレス)
    810         */
    811        private String[] getUserInfo( final String userId ){
    812                String[] rtn = null;
    813
    814                if( userId.contains( "@" ) ) {
    815                        rtn = new String[2];
    816                        rtn[0] = "";
    817                        rtn[1] = userId;
    818                }
    819                else {
    820                        final String[] ge35SelArgs = { userId };
    821                        final String[][] ge35Datas = DBUtil.dbExecute( SEL_GE35,ge35SelArgs,DefaultMailManager.APP_INFO, DBID );        // 6.4.1.1 (2016/01/16)
    822                        if( ge35Datas.length > 0 ) {
    823                                rtn = ge35Datas[0];
    824                        }
    825                        else {
    826                                final String errMsg = "ユーザー情報取得できません。ユーザーID：" + userId ;
    827                                if( "true".equals( paramMap.get( "ADDR_CHECK" ) ) ){
    828                                        throw new OgRuntimeException( errMsg );
    829                                }
    830                                else {
    831                                        errAddrList.add( errMsg );
    832                                }
    833                        }
    834                }
    835                return rtn;
    836        }
    837}