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.InputStream; 019import java.io.OutputStream; 020import java.io.ByteArrayInputStream; 021import java.io.UnsupportedEncodingException; 022import java.io.IOException; 023 024import jakarta.activation.DataHandler; 025import jakarta.activation.DataSource; 026import jakarta.mail.internet.InternetAddress; 027import jakarta.mail.internet.MimeMessage; 028import jakarta.mail.internet.MimeUtility; 029import jakarta.mail.MessagingException; 030// import com.sun.mail.util.BASE64EncoderStream; // 8.4.0.0 (2022/12/23) java.util.Base64.Encoder に置き換え 031import java.util.Base64; // 8.4.0.0 (2022/12/23) 032import java.nio.charset.Charset; // 5.5.2.6 (2012/05/25) 033 034import org.opengion.fukurou.system.OgRuntimeException ; // 6.4.2.0 (2016/01/29) 035import org.opengion.fukurou.util.UnicodeCorrecter; // 5.9.3.3 (2015/12/26) package を、mail → util に移動のため 036 037/** 038 * MailCharsetFactory は、MailCharset インターフェースを実装したサブクラスを 039 * 作成する ファクトリクラスです。 040 * 041 * 引数のキャラクタセット名が、Windows-31J 、MS932 の場合は、 042 * <del>6.3.8.0 (2015/09/11) 『1.Windows-31J + 8bit 送信』 の実装である、Mail_Windows31J_Charset</del> 043 * 『1.Windows-31J/UTF-8 + 8bit 送信』 の実装である、Mail_8bit_Charset 044 * サブクラスを返します。 045 * それ以外が指定された場合は、ISO-2022-JP を使用して、『2.ISO-2022-JP に独自変換 + 7bit 送信』 046 * の実装である、Mail_ISO2022JP_Charset サブクラスを返します。 047 * 048 * @og.rev 6.3.8.0 (2015/09/11) キャラクタセットに、UTF-8 を追加します。 049 * Mail_Windows31J_Charset のクラス名を変更します。 050 * 051 * @version 4.0 052 * @author Kazuhiko Hasegawa 053 * @since JDK5.0, 054 */ 055class MailCharsetFactory { 056 057 /** 058 * インスタンスの生成を抑止します。 059 */ 060 private MailCharsetFactory() { 061 // 何もありません。(PMD エラー回避) 062 } 063 064 /** 065 * キャラクタセットに応じた、MailCharset オブジェクトを返します。 066 * 067 * Windows-31J 、MS932 、Shift_JIS の場合は、Mail_Windows31J_Charset 068 * その他は、ISO-2022-JP として、Mail_ISO2022JP_Charset を返します。 069 * 070 * 注意:null の場合は、デフォルトではなく、Mail_ISO2022JP_Charset を返します。 071 * 072 * @og.rev 6.3.8.0 (2015/09/11) キャラクタセットに、UTF-8 を追加します。 073 * 074 * @param charset キャラクタセット[Windows-31J/MS932/Shift_JIS/その他] 075 * 076 * @return MailCharsetオブジェクト 077 */ 078 /* default */ static MailCharset newInstance( final String charset ) { 079 final MailCharset mcset; 080 081 if( "MS932".equalsIgnoreCase( charset ) || 082 "Shift_JIS".equalsIgnoreCase( charset ) || 083 "Windows-31J".equalsIgnoreCase( charset ) || 084 "UTF-8".equalsIgnoreCase( charset ) ) { // 6.3.8.0 (2015/09/11) 085 mcset = new Mail_8bit_Charset( charset ); // 6.3.8.0 (2015/09/11) 086 } 087 else { 088 mcset = new Mail_ISO2022JP_Charset(); 089 } 090 return mcset ; 091 } 092 093 /** 094 * MailCharset インターフェースを実装した Windwos-31J/UTF-8 エンコード時のサブクラスです。 095 * 096 * 『1.Windows-31J/UTF-8 + 8bit 送信』 の実装です。 097 * 098 * @og.rev 6.3.8.0 (2015/09/11) キャラクタセットに、UTF-8 を追加します。 099 * @og.rev 6.3.9.1 (2015/11/27) 修飾子を、なし → private に変更。 100 * 101 * @version 4.0 102 * @author Kazuhiko Hasegawa 103 * @since JDK5.0, 104 */ 105 private static final class Mail_8bit_Charset implements MailCharset { 106 private final String charset ; // "Windows-31J" or "MS932" 107 108 /** 109 * 引数に、エンコード方式を指定して、作成するコンストラクタです。 110 * 111 * @og.rev 6.3.8.0 (2015/09/11) キャラクタセットに、UTF-8 を追加します。 112 * 113 * @param charset エンコード 114 */ 115 public Mail_8bit_Charset( final String charset ) { 116 this.charset = charset; 117 } 118 119 /** 120 * テキストをセットします。 121 * Part#setText() の代わりにこちらを使うようにします。 122 * 123 * ※ 内部で、MessagingException が発生した場合は、RuntimeException に変換されて throw されます。 124 * 125 * @param mimeMsg MimeMessageオブジェクト 126 * @param text テキスト 127 */ 128 @Override // MailCharset 129 public void setTextContent( final MimeMessage mimeMsg, final String text ) { 130 try { 131 mimeMsg.setText( text,charset ); // "text/plain" Content 132 } 133 catch( final MessagingException ex ) { 134 final String errMsg = "指定のテキストをセットできません。" 135 + "text=" + text + " , charset=" + charset ; 136 throw new OgRuntimeException( errMsg,ex ); 137 } 138 } 139 140 /** 141 * 日本語を含むヘッダ用テキストを生成します。 142 * 変換結果は ASCII なので、これをそのまま setSubject や InternetAddress 143 * のパラメタとして使用してください。 144 * 145 * ※ 内部で、UnsupportedEncodingException が発生した場合は、RuntimeException に変換されて throw されます。 146 * 147 * @param text テキスト 148 * 149 * @return 日本語を含むヘッダ用テキスト 150 * @og.rtnNotNull 151 */ 152 @Override // MailCharset 153 public String encodeWord( final String text ) { 154 try { 155 return MimeUtility.encodeText( text, charset, "B" ); 156 } 157 catch( final UnsupportedEncodingException ex ) { 158 final String errMsg = "指定のエンコードが出来ません。" 159 + "text=" + text + " , charset=" + charset ; 160 throw new OgRuntimeException( errMsg,ex ); 161 } 162 } 163 164 /** 165 * 日本語を含むアドレスを生成します。 166 * personal に、日本語が含まれると想定しています。 167 * サブクラスで、日本語処理を行う場合の方法は、それぞれ異なります。 168 * 169 * ※ 内部で、UnsupportedEncodingException が発生した場合は、RuntimeException に変換されて throw されます。 170 * 171 * @param address RFC822形式のアドレス 172 * @param personal 個人名 173 * 174 * @return InternetAddressオブジェクト 175 * @og.rtnNotNull 176 */ 177 @Override // MailCharset 178 public InternetAddress getAddress( final String address,final String personal ) { 179 try { 180 return new InternetAddress( address,personal,charset ); 181 } 182 catch( final UnsupportedEncodingException ex ) { 183 final String errMsg = "指定のエンコードが出来ません。" 184 + "address=" + address + " , charset=" + charset ; 185 throw new OgRuntimeException( errMsg,ex ); 186 } 187 } 188 189 /** 190 * Content-Transfer-Encoding を指定する場合の ビット数を返します。 191 * 192 * Windows系は、8bit / ISO-2022-JP 系は、7bit になります。 193 * 194 * @return ビット数("8bit" 固定) 195 * @og.rtnNotNull 196 */ 197 @Override // MailCharset 198 public String getBit() { 199 return "8bit" ; 200 } 201 } 202 203 /** 204 * MailCharset インターフェースを実装した ISO-2022-JP エンコード時のサブクラスです。 205 * 206 * 『2.ISO-2022-JP に独自変換 + 7bit 送信』 の実装です。 207 * 208 * @og.rev 6.3.9.1 (2015/11/27) 修飾子を、なし → private に変更。 209 * @version 4.0 210 * @author Kazuhiko Hasegawa 211 * @since JDK5.0, 212 */ 213 private static final class Mail_ISO2022JP_Charset implements MailCharset { 214 215 /** 216 * プラットフォーム依存のデフォルトの Charset です。 217 * プラットフォーム依存性を考慮する場合、エンコード指定で作成しておく事をお勧めします。 218 * 219 * @og.rev 5.5.2.6 (2012/05/25) findbugs対応 220 */ 221 private static final Charset DEFAULT_CHARSET = Charset.defaultCharset() ; 222 223 /** 224 * テキストをセットします。 225 * Part#setText() の代わりにこちらを使うようにします。 226 * 227 * ※ 内部で、MessagingException が発生した場合は、RuntimeException に変換されて throw されます。 228 * 229 * @param mimeMsg MimeMessageオブジェクト 230 * @param text テキスト 231 */ 232 @Override // MailCharset 233 public void setTextContent( final MimeMessage mimeMsg, final String text ) { 234 try { 235 // mimeMsg.setText(text, "ISO-2022-JP"); 236 mimeMsg.setDataHandler(new DataHandler(new JISDataSource(text))); 237 } 238 catch( final MessagingException ex ) { 239 final String errMsg = "指定のテキストをセットできません。" 240 + "text=" + text ; 241 throw new OgRuntimeException( errMsg,ex ); 242 } 243 } 244 245 /** 246 * 日本語を含むヘッダ用テキストを生成します。 247 * 変換結果は ASCII なので、これをそのまま setSubject や InternetAddress 248 * のパラメタとして使用してください。 249 * 250 * ※ 内部で、UnsupportedEncodingException が発生した場合は、RuntimeException に変換されて throw されます。 251 * 252 * @og.rev 8.4.0.0 (2022/12/23) java.util.Base64.Encoder に置き換え 253 * 254 * @param text テキスト 255 * 256 * @return 日本語を含むヘッダ用テキスト 257 * @og.rtnNotNull 258 */ 259 @Override // MailCharset 260 public String encodeWord( final String text ) { 261 try { 262 return "=?ISO-2022-JP?B?" + 263 new String( 264 // BASE64EncoderStream.encode( 265 Base64.getEncoder().encode( 266 CharCodeConverter.sjisToJis( 267 UnicodeCorrecter.correctToCP932(text).getBytes("Windows-31J") 268 ) 269 ) 270 ,DEFAULT_CHARSET ) + "?="; // 5.5.2.6 (2012/05/25) findbugs対応 271 } 272 catch( final UnsupportedEncodingException ex ) { 273 final String errMsg = "指定のエンコードが出来ません。" 274 + "text=" + text + " , charset=Windows-31J" ; 275 throw new OgRuntimeException( errMsg,ex ); 276 } 277 } 278 279 /** 280 * 日本語を含むアドレスを生成します。 281 * personal に、日本語が含まれると想定しています。 282 * サブクラスで、日本語処理を行う場合の方法は、それぞれ異なります。 283 * 284 * ※ 内部で、UnsupportedEncodingException が発生した場合は、RuntimeException に変換されて throw されます。 285 * 286 * @param address RFC822形式のアドレス 287 * @param personal 個人名 288 * 289 * @return InternetAddressオブジェクト 290 * @og.rtnNotNull 291 */ 292 @Override // MailCharset 293 public InternetAddress getAddress( final String address,final String personal ) { 294 try { 295 return new InternetAddress( address,encodeWord( personal ) ); 296 } 297 catch( final UnsupportedEncodingException ex ) { 298 final String errMsg = "指定のエンコードが出来ません。" 299 + "address=" + address ; 300 throw new OgRuntimeException( errMsg,ex ); 301 } 302 } 303 304 /** 305 * Content-Transfer-Encoding を指定する場合の ビット数を返します。 306 * 307 * Windows系は、8bit / ISO-2022-JP 系は、7bit になります。 308 * 309 * @return ビット数("7bit" 固定) 310 * @og.rtnNotNull 311 */ 312 @Override // MailCharset 313 public String getBit() { 314 return "7bit" ; 315 } 316 } 317 318 /** 319 * テキストの本文を送信するための DataSource です。 320 * 321 * Windows-31J でバイトコードに変換した後、独自エンコードにて、 322 * Shift-JIS ⇒ JIS 変換しています。 323 * 324 * @og.rev 6.3.9.1 (2015/11/27) 修飾子を、なし → private に変更。 325 * 326 * @version 4.0 327 * @author Kazuhiko Hasegawa 328 * @since JDK5.0, 329 */ 330 private static final class JISDataSource implements DataSource { 331 private final byte[] data; 332 333 /** 334 * JIS(Windows-31J) に対応した DataSource オブジェクトのコンストラクタ 335 * 336 * Windows-31J でバイトコードに変換した後、独自エンコードにて、 337 * Shift-JIS ⇒ JIS 変換しています。 338 * 339 * @param str 変換する文字列 340 */ 341 public JISDataSource( final String str ) { 342 try { 343 data = CharCodeConverter.sjisToJis( 344 UnicodeCorrecter.correctToCP932(str).getBytes("Windows-31J")); 345 } catch( final UnsupportedEncodingException ex ) { 346 final String errMsg = "Windows-31J でのエンコーディングが出来ません。" + str; 347 throw new OgRuntimeException( errMsg,ex ); 348 } 349 } 350 351 /** 352 * データの MIME タイプを文字列の形で返します。 353 * かならず有効なタイプを返すべきです。 354 * DataSource の実装がデータタイプを 決定できない場合は、 355 * getContentType は "application/octet-stream" を返すことを 提案します。 356 * 357 * @return MIMEタイプ("text/plain; charset=ISO-2022-JP" 固定) 358 * @og.rtnNotNull 359 */ 360 @Override // DataSource 361 public String getContentType() { 362 return "text/plain; charset=ISO-2022-JP"; 363 } 364 365 /** 366 * データを表す InputStreamオブジェクト を返します。 367 * それができない場合は適切な例外をスローします。 368 * 369 * @return InputStreamオブジェクト 370 * @throws IOException ※ このメソッドからは、IOException は throw されません。 371 * @og.rtnNotNull 372 */ 373 @Override // DataSource 374 public InputStream getInputStream() throws IOException { 375 return new ByteArrayInputStream( data ); 376 } 377 378 /** 379 * データが書込可能なら OutputStreamオブジェクト を返します。 380 * それができない場合は適切な例外をスローします。 381 * 382 * ※ このクラスでは実装されていません。 383 * 384 * @return OutputStreamオブジェクト 385 * @throws IOException ※ このメソッドを実行すると、必ず throw されます。 386 */ 387 @Override // DataSource 388 public OutputStream getOutputStream() throws IOException { 389 final String errMsg = "このクラスでは実装されていません。"; 390 // throw new UnsupportedOperationException( errMsg ); 391 throw new IOException( errMsg ); 392 } 393 394 /** 395 * このオブジェクトの '名前' を返します。 396 * この名前は下層のオブジェクトの性質によります。 397 * ファイルをカプセル化する DataSource なら オブジェクトの 398 * ファイル名を返すようにするかもしれません。 399 * 400 * @return オブジェクトの名前( "JISDataSource" 固定) 401 * @og.rtnNotNull 402 */ 403 @Override // DataSource 404 public String getName() { 405 return "JISDataSource"; 406 } 407 } 408}