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