    001/*
    002 * Copyright (c) 2009 The openGion Project.
    003 *
    004 * Licensed under the Apache License, Version 2.0 (the "License");
    005 * you may not use this file except in compliance with the License.
    006 * You may obtain a copy of the License at
    007 *
    008 *     http://www.apache.org/licenses/LICENSE-2.0
    009 *
    010 * Unless required by applicable law or agreed to in writing, software
    011 * distributed under the License is distributed on an "AS IS" BASIS,
    012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
    013 * either express or implied. See the License for the specific language
    014 * governing permissions and limitations under the License.
    015 */
    016package org.opengion.fukurou.mail;
    017
    018import org.opengion.fukurou.util.Closer ;
    019
    020import javax.mail.MessagingException;
    021import javax.mail.Part;
    022import javax.mail.BodyPart;
    023import javax.mail.Multipart;
    024import java.io.File;
    025import java.io.InputStream;
    026import java.io.FileOutputStream;
    027import java.io.IOException;
    028import java.util.List;
    029import java.util.ArrayList;
    030import java.util.Set;
    031import java.util.HashSet;
    032
    033/**
    034 * メール添付ファイル処理クラス
    035 *
    036 * このクラスは、添付ファイルを処理するためのクラスです。
    037 * 添付ファイルは、マルチパートに含まれている為、再帰的に探す必要があります。
    038 *
    039 * @version  4.0
    040 * @author   Kazuhiko Hasegawa
    041 * @since    JDK5.0,
    042 */
    043public class MailAttachFiles {
    044        private final List<Part> files ;
    045        private final String[] names ;
    046
    047        /**
    048         * デフォルトコンストラクター
    049         *
    050         * 内部変数の初期化を行います。
    051         *
    052         * @param       part    Partオブジェクト
    053         */
    054        public MailAttachFiles( final Part part ) {
    055                files = new ArrayList<Part>();
    056                names = makeNames( part );
    057        }
    058
    059        /**
    060         * 添付ファイルの名称を文字列配列として求めます。
    061         *
    062         * @return 添付ファイルの名称を文字列配列
    063         */
    064        public String[] getNames() {
    065                String[] rtn = null ;
    066
    067                if( names != null ) { rtn = names.clone(); }
    068
    069                return rtn ;
    070        }
    071
    072        /**
    073         * 添付ファイルの名称を文字列配列として求めます。
    074         *
    075         * この処理の中で、添付ファイルを持つ Part を見つけて内部配列(List)に登録します。
    076         * ファイル名が未指定の場合は、"noNameFile" + i + ".tmp" というファイル名をつけます。
    077         * i は、添付ファイルの連番です。
    078         * また、同一添付ファイル名が存在する場合は、頭に添付ファイルの連番を付加して、
    079         * ファイル名としてユニーク化します。
    080         *
    081         * @og.rev 4.3.3.5 (2008/11/08) 日本語添付ファイルが処理できるように修正
    082         *
    083         * @param part Partオブジェクト
    084         *
    085         * @return 添付ファイルの名称を文字列配列
    086         */
    087        private String[] makeNames( final Part part ) {
    088                final String[] nms;
    089                try {
    090                        Set<String> set = new HashSet<String>();
    091
    092                        fileSearch( part );
    093                        nms = new String[files.size()];
    094                        for( int i=0; i<nms.length; i++ ) {
    095        //                      String name = ((Part)files.get(i)).getFileName();
    096                                String name = (files.get(i)).getFileName();
    097                                if( name == null ) {    // message か、ファイル名未指定のケース
    098                                        nms[i] = "noNameFile" + i + ".tmp" ;
    099                                }
    100//                              else {
    101//                                      // encode-word の =? の前にはスペースが必要。
    102//                                      StringBuilder buf = new StringBuilder( name );
    103//                                      int pos = buf.indexOf( "?==?" );                // デコードの終了と開始が連結している箇所
    104//                                      // 先頭でなく、かつ開始記号が含まれている。
    105//                                      while( pos > 0 ) {
    106//                                              buf.insert( pos+2," " );
    107//                                              pos = buf.indexOf( "?==?",pos+4 );
    108//                                      }
    109//                              }
    110                                // 4.3.3.5 (2008/11/08) 日本語添付ファイルが処理できるように修正
    111                                else {
    112                                        nms[i] = MailMessage.mimeDecode( name );
    113                                }
    114
    115                                // 重複チェック
    116                                if( !set.add( nms[i] ) ) {
    117                                        nms[i] = i + "_" + nms[i] ;             // 重複時に名称変更します。
    118                                }
    119                        }
    120                }
    121                catch( MessagingException ex ) {
    122                        String errMsg = "メッセージ情報のハンドリングに失敗しました。"
    123                                                + ex.getMessage();                              // 5.1.8.0 (2010/07/01) errMsg 修正
    124                        throw new RuntimeException( errMsg,ex );
    125                }
    126//              catch( UnsupportedEncodingException ex ) {
    127//                      String errMsg = "テキスト情報のデコードに失敗しました。" ;
    128//                      throw new RuntimeException( errMsg,ex );
    129//              }
    130                catch( IOException ex ) {
    131                        String errMsg = "テキスト情報の取り出しに失敗しました。"
    132                                                + ex.getMessage();                              // 5.1.8.0 (2010/07/01) errMsg 修正
    133                        throw new RuntimeException( errMsg,ex );
    134                }
    135                return nms;
    136        }
    137
    138        /**
    139         * 添付ファイルが存在するかどうかをサーチします。
    140         *
    141         * 添付ファイルは、マルチパートで指定されると、再帰的に検索する必要が
    142         * 出てきます。このメソッドでは、再帰的にファイルかどうかを検索し、
    143         * ファイルであれば、内部変数(List)に追加(add)していきます。
    144         *
    145         * @param part Partオブジェクト
    146         *
    147         * @return 再帰検索終了 true
    148         * @throws MessagingException javax.mail 関連のエラーが発生したとき
    149         * @throws IOException 入出力エラーが発生したとき
    150         *
    151         */
    152        private boolean fileSearch( final Part part ) throws MessagingException ,IOException {
    153                if( part.isMimeType( "multipart/*" ) ) {
    154                        Multipart mpt = (Multipart)part.getContent();
    155
    156                        int count = mpt.getCount();
    157                        for(int i = 0; i < count; i++) {
    158                                BodyPart bpt = mpt.getBodyPart(i);
    159                                fileSearch( bpt );
    160                        }
    161                }
    162                else {
    163                        if( part.isMimeType( "message/*" )      ||
    164                                part.getFileName() != null              ||
    165                                Part.INLINE.equalsIgnoreCase( part.getDisposition() ) ) {
    166                                        files.add( part );
    167                        }
    168                }
    169                return true;
    170        }
    171
    172        /**
    173         * 添付ファイルを指定のフォルダにセーブします。
    174         *
    175         * 内部変数List の 添付ファイルを持つ Part について、ファイルを抜出し、
    176         * 指定のディレクトリに保存していきます。
    177         * ファイル名は、基本的に添付ファイル名そのものですが、
    178         * 同一名称の添付ファイルが複数登録されている場合は、その重複ファイルの番号を
    179         * 頭につけ、番号 + "_" + 添付ファイル名 として、ユニーク化します。
    180         *
    181         * ※ ディレクトリの作成に失敗した場合、RuntimeException が throw されます。
    182         *
    183         * @param       dir     セーブするディレクトリ。null の場合は、セーブしない。
    184         * @param       newNm   セーブするファイル名 null の場合は、非重複化された添付ファイル名
    185         * @param       fno     添付ファイルの番号
    186         */
    187        public void saveFileName( final String dir, final String newNm, final int fno ) {
    188                if( dir == null ) { return ; }          // ファイルをセーブしない。
    189
    190                File fileDir = new File( dir );
    191                if( !fileDir.exists() ) {
    192                        boolean isOk = fileDir.mkdirs();
    193                        if( ! isOk ) {
    194                                String errMsg = "ディレクトリの作成に失敗しました。[" + dir + "]";
    195                                throw new RuntimeException( errMsg );
    196                        }
    197                }
    198
    199                String newName = ( newNm != null ) ? newNm : names[fno] ;
    200
    201                InputStream      input  = null;
    202                FileOutputStream output = null;
    203
    204                try {
    205                        Part prt = files.get( fno );
    206                        input = prt.getInputStream();
    207                        output = new FileOutputStream( new File( fileDir,newName ) );
    208                        byte[] buf = new byte[1024];
    209                        int len;
    210                        while( (len = input.read(buf)) != -1 ) {
    211                                output.write( buf,0,len );
    212                        }
    213                }
    214                catch( MessagingException ex ) {
    215                        String errMsg = "メッセージオブジェクトの操作中にエラーが発生しました。"
    216                                                + "dir=[" + dir + "], file=[" + newName + "], No=[" + fno + "]"
    217                                                + ex.getMessage();                      // 5.1.8.0 (2010/07/01) errMsg 修正
    218                        throw new RuntimeException( errMsg,ex );
    219                }
    220                catch( IOException ex ) {
    221                        String errMsg = "添付ファイルの取り扱い中にエラーが発生しました。"
    222                                                + "dir=[" + dir + "], file=[" + newName + "], No=[" + fno + "]"
    223                                                + ex.getMessage();                      // 5.1.8.0 (2010/07/01) errMsg 修正
    224                        throw new RuntimeException( errMsg,ex );
    225                }
    226                finally {
    227                        Closer.ioClose( output );
    228                        Closer.ioClose( input  );
    229                }
    230        }
    231}