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 javax.mail.MessagingException;
022import javax.mail.Part;
023import javax.mail.BodyPart;
024import javax.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 javax.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}