    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 = files.get(i).getFileName();
    096                                if( name == null ) {    // message か、ファイル名未指定のケース
    097                                        nms[i] = "noNameFile" + i + ".tmp" ;
    098                                }
    099                                // 4.3.3.5 (2008/11/08) 日本語添付ファイルが処理できるように修正
    100                                else {
    101                                        nms[i] = MailMessage.mimeDecode( name );
    102                                }
    103
    104                                // 重複チェック
    105                                if( !set.add( nms[i] ) ) {
    106                                        nms[i] = i + "_" + nms[i] ;             // 重複時に名称変更します。
    107                                }
    108                        }
    109                }
    110                catch( MessagingException ex ) {
    111                        String errMsg = "メッセージ情報のハンドリングに失敗しました。"
    112                                                + ex.getMessage();                              // 5.1.8.0 (2010/07/01) errMsg 修正
    113                        throw new RuntimeException( errMsg,ex );
    114                }
    115                catch( IOException ex ) {
    116                        String errMsg = "テキスト情報の取り出しに失敗しました。"
    117                                                + ex.getMessage();                              // 5.1.8.0 (2010/07/01) errMsg 修正
    118                        throw new RuntimeException( errMsg,ex );
    119                }
    120                return nms;
    121        }
    122
    123        /**
    124         * 添付ファイルが存在するかどうかをサーチします。
    125         *
    126         * 添付ファイルは、マルチパートで指定されると、再帰的に検索する必要が
    127         * 出てきます。このメソッドでは、再帰的にファイルかどうかを検索し、
    128         * ファイルであれば、内部変数(List)に追加(add)していきます。
    129         *
    130         * @param part Partオブジェクト
    131         *
    132         * @return 再帰検索終了 true
    133         * @throws MessagingException javax.mail 関連のエラーが発生したとき
    134         * @throws IOException 入出力エラーが発生したとき
    135         *
    136         */
    137        private boolean fileSearch( final Part part ) throws MessagingException ,IOException {
    138                if( part.isMimeType( "multipart/*" ) ) {
    139                        Multipart mpt = (Multipart)part.getContent();
    140
    141                        int count = mpt.getCount();
    142                        for(int i = 0; i < count; i++) {
    143                                BodyPart bpt = mpt.getBodyPart(i);
    144                                fileSearch( bpt );
    145                        }
    146                }
    147                else {
    148                        if( part.isMimeType( "message/*" )      ||
    149                                part.getFileName() != null              ||
    150                                Part.INLINE.equalsIgnoreCase( part.getDisposition() ) ) {
    151                                        files.add( part );
    152                        }
    153                }
    154                return true;
    155        }
    156
    157        /**
    158         * 添付ファイルを指定のフォルダにセーブします。
    159         *
    160         * 内部変数List の 添付ファイルを持つ Part について、ファイルを抜出し、
    161         * 指定のディレクトリに保存していきます。
    162         * ファイル名は、基本的に添付ファイル名そのものですが、
    163         * 同一名称の添付ファイルが複数登録されている場合は、その重複ファイルの番号を
    164         * 頭につけ、番号 + "_" + 添付ファイル名 として、ユニーク化します。
    165         *
    166         * ※ ディレクトリの作成に失敗した場合、RuntimeException が throw されます。
    167         *
    168         * @param       dir     セーブするディレクトリ。null の場合は、セーブしない。
    169         * @param       newNm   セーブするファイル名 null の場合は、非重複化された添付ファイル名
    170         * @param       fno     添付ファイルの番号
    171         */
    172        public void saveFileName( final String dir, final String newNm, final int fno ) {
    173                if( dir == null ) { return ; }          // ファイルをセーブしない。
    174
    175                File fileDir = new File( dir );
    176                if( !fileDir.exists() ) {
    177                        boolean isOk = fileDir.mkdirs();
    178                        if( ! isOk ) {
    179                                String errMsg = "ディレクトリの作成に失敗しました。[" + dir + "]";
    180                                throw new RuntimeException( errMsg );
    181                        }
    182                }
    183
    184                String newName = ( newNm != null ) ? newNm : names[fno] ;
    185
    186                InputStream      input  = null;
    187                FileOutputStream output = null;
    188
    189                try {
    190                        Part prt = files.get( fno );
    191                        input = prt.getInputStream();
    192                        output = new FileOutputStream( new File( fileDir,newName ) );
    193                        byte[] buf = new byte[1024];
    194                        int len;
    195                        while( (len = input.read(buf)) != -1 ) {
    196                                output.write( buf,0,len );
    197                        }
    198                }
    199                catch( MessagingException ex ) {
    200                        String errMsg = "メッセージオブジェクトの操作中にエラーが発生しました。"
    201                                                + "dir=[" + dir + "], file=[" + newName + "], No=[" + fno + "]"
    202                                                + ex.getMessage();                      // 5.1.8.0 (2010/07/01) errMsg 修正
    203                        throw new RuntimeException( errMsg,ex );
    204                }
    205                catch( IOException ex ) {
    206                        String errMsg = "添付ファイルの取り扱い中にエラーが発生しました。"
    207                                                + "dir=[" + dir + "], file=[" + newName + "], No=[" + fno + "]"
    208                                                + ex.getMessage();                      // 5.1.8.0 (2010/07/01) errMsg 修正
    209                        throw new RuntimeException( errMsg,ex );
    210                }
    211                finally {
    212                        Closer.ioClose( output );
    213                        Closer.ioClose( input  );
    214                }
    215        }
    216}