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