    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.Closer ;
    019import org.opengion.fukurou.system.OgRuntimeException ;         // 6.4.2.0 (2016/01/29)
    020
    021import jakarta.mail.MessagingException;
    022import jakarta.mail.Part;
    023import jakarta.mail.BodyPart;
    024import jakarta.mail.Multipart;
    025import java.io.File;
    026import java.io.InputStream;
    027import java.io.FileOutputStream;
    028import java.io.IOException;
    029import java.util.List;
    030import java.util.ArrayList;
    031import java.util.Set;
    032import java.util.HashSet;
    033
    034/**
    035 * メール添付ファイル処理クラス
    036 *
    037 * このクラスは、添付ファイルを処理するためのクラスです。
    038 * 添付ファイルは、マルチパートに含まれている為、再帰的に探す必要があります。
    039 *
    040 * @version  4.0
    041 * @author   Kazuhiko Hasegawa
    042 * @since    JDK5.0,
    043 */
    044public class MailAttachFiles {
    045        private final List<Part> files ;
    046        private final String[] names ;
    047
    048        /**
    049         * Partオブジェクトを受け取るコンストラクター
    050         *
    051         * 内部変数の初期化を行います。
    052         *
    053         * @param       part    Partオブジェクト
    054         */
    055        public MailAttachFiles( final Part part ) {
    056                files = new ArrayList<>();
    057                names = makeNames( part );
    058        }
    059
    060        /**
    061         * 添付ファイルの名称を文字列配列として求めます。
    062         *
    063         * @return 添付ファイルの名称を文字列配列
    064         */
    065        public String[] getNames() {
    066                String[] rtn = null ;
    067
    068                if( names != null ) { rtn = names.clone(); }
    069
    070                return rtn ;
    071        }
    072
    073        /**
    074         * 添付ファイルの名称を文字列配列として求めます。
    075         *
    076         * この処理の中で、添付ファイルを持つ Part を見つけて内部配列(List)に登録します。
    077         * ファイル名が未指定の場合は、"noNameFile" + i + ".tmp" というファイル名をつけます。
    078         * i は、添付ファイルの連番です。
    079         * また、同一添付ファイル名が存在する場合は、頭に添付ファイルの連番を付加して、
    080         * ファイル名としてユニーク化します。
    081         *
    082         * @og.rev 4.3.3.5 (2008/11/08) 日本語添付ファイルが処理できるように修正
    083         *
    084         * @param part Partオブジェクト
    085         *
    086         * @return 添付ファイルの名称を文字列配列
    087         */
    088        private String[] makeNames( final Part part ) {
    089                final String[] nms;
    090                try {
    091                        final Set<String> set = new HashSet<>();
    092
    093                        fileSearch( part );
    094                        nms = new String[files.size()];
    095                        for( int i=0; i<nms.length; i++ ) {
    096                                final String name = files.get(i).getFileName();
    097                                if( name == null ) {    // message か、ファイル名未指定のケース
    098                                        nms[i] = "noNameFile" + i + ".tmp" ;
    099                                }
    100                                // 4.3.3.5 (2008/11/08) 日本語添付ファイルが処理できるように修正
    101                                else {
    102                                        nms[i] = MailMessage.mimeDecode( name );
    103                                }
    104
    105                                // 重複チェック
    106                                if( !set.add( nms[i] ) ) {
    107                                        nms[i] = i + "_" + nms[i] ;             // 重複時に名称変更します。
    108                                }
    109                        }
    110                }
    111                catch( final MessagingException ex ) {
    112                        final String errMsg = "メッセージ情報のハンドリングに失敗しました。"
    113                                                        + ex.getMessage();                              // 5.1.8.0 (2010/07/01) errMsg 修正
    114                        throw new OgRuntimeException( errMsg,ex );
    115                }
    116                catch( final IOException ex ) {
    117                        final String errMsg = "テキスト情報の取り出しに失敗しました。"
    118                                                        + ex.getMessage();                              // 5.1.8.0 (2010/07/01) errMsg 修正
    119                        throw new OgRuntimeException( errMsg,ex );
    120                }
    121                return nms;
    122        }
    123
    124        /**
    125         * 添付ファイルが存在するかどうかをサーチします。
    126         *
    127         * 添付ファイルは、マルチパートで指定されると、再帰的に検索する必要が
    128         * 出てきます。このメソッドでは、再帰的にファイルかどうかを検索し、
    129         * ファイルであれば、内部変数(List)に追加(add)していきます。
    130         *
    131         * @param part Partオブジェクト
    132         *
    133         * @return 再帰検索終了 true
    134         * @throws MessagingException jakarta.mail 関連のエラーが発生したとき
    135         * @throws IOException 入出力エラーが発生したとき
    136         *
    137         */
    138        private boolean fileSearch( final Part part ) throws MessagingException ,IOException {
    139                if( part.isMimeType( "multipart/*" ) ) {
    140                        final Multipart mpt = (Multipart)part.getContent();
    141
    142                        final int count = mpt.getCount();
    143                        for( int i=0; i<count; i++ ) {
    144                                final BodyPart bpt = mpt.getBodyPart(i);
    145                                fileSearch( bpt );
    146                        }
    147                }
    148                else {
    149                        if( part.isMimeType( "message/*" )      ||
    150                                part.getFileName() != null              ||
    151                                Part.INLINE.equalsIgnoreCase( part.getDisposition() ) ) {
    152                                        files.add( part );
    153                        }
    154                }
    155                return true;
    156        }
    157
    158        /**
    159         * 添付ファイルを指定のフォルダにセーブします。
    160         *
    161         * 内部変数List の 添付ファイルを持つ Part について、ファイルを抜出し、
    162         * 指定のディレクトリに保存していきます。
    163         * ファイル名は、基本的に添付ファイル名そのものですが、
    164         * 同一名称の添付ファイルが複数登録されている場合は、その重複ファイルの番号を
    165         * 頭につけ、番号 + "_" + 添付ファイル名 として、ユニーク化します。
    166         *
    167         * ※ ディレクトリの作成に失敗した場合、RuntimeException が throw されます。
    168         *
    169         * @param       dir     セーブするディレクトリ (nullの場合は、セーブしない)
    170         * @param       newNm   セーブするファイル名 (nullの場合は、非重複化された添付ファイル名)
    171         * @param       fno     添付ファイルの番号
    172         */
    173        public void saveFileName( final String dir, final String newNm, final int fno ) {
    174                if( dir == null ) { return ; }          // ファイルをセーブしない。
    175
    176                final File fileDir = new File( dir );
    177                if( !fileDir.exists() ) {
    178                        final boolean isOk = fileDir.mkdirs();
    179                        if( ! isOk ) {
    180                                final String errMsg = "ディレクトリの作成に失敗しました。[" + dir + "]";
    181                                throw new OgRuntimeException( errMsg );
    182                        }
    183                }
    184
    185                // 6.4.1.1 (2016/01/16) PMD refactoring. Avoid if (x != y) ..; else ..;
    186                final String newName = newNm == null ? names[fno] : newNm ;
    187
    188                InputStream      input  = null;
    189                FileOutputStream output = null;
    190
    191                try {
    192                        final Part prt = files.get( fno );
    193                        input = prt.getInputStream();
    194                        output = new FileOutputStream( new File( fileDir,newName ) );
    195                        final byte[] buf = new byte[1024];
    196                        int len;
    197                        while( (len = input.read(buf)) != -1 ) {
    198                                output.write( buf,0,len );
    199                        }
    200                }
    201                catch( final MessagingException ex ) {
    202                        final String errMsg = "メッセージオブジェクトの操作中にエラーが発生しました。"
    203                                                        + "dir=[" + dir + "], file=[" + newName + "], No=[" + fno + "]"
    204                                                        + ex.getMessage();                      // 5.1.8.0 (2010/07/01) errMsg 修正
    205                        throw new OgRuntimeException( errMsg,ex );
    206                }
    207                catch( final IOException ex ) {
    208                        final String errMsg = "添付ファイルの取り扱い中にエラーが発生しました。"
    209                                                        + "dir=[" + dir + "], file=[" + newName + "], No=[" + fno + "]"
    210                                                        + ex.getMessage();                      // 5.1.8.0 (2010/07/01) errMsg 修正
    211                        throw new OgRuntimeException( errMsg,ex );
    212                }
    213                finally {
    214                        Closer.ioClose( output );
    215                        Closer.ioClose( input  );
    216                }
    217        }
    218}