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 javax.activation.DataHandler; 025import javax.activation.DataSource; 026import javax.mail.internet.InternetAddress; 027import javax.mail.internet.MimeMessage; 028import javax.mail.internet.MimeUtility; 029import javax.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 public void setTextContent( final MimeMessage mimeMsg, final String text ) { 128 try { 129 mimeMsg.setText( text,charset ); // "text/plain" Content 130 } 131 catch( final MessagingException ex ) { 132 final String errMsg = "指定のテキストをセットできません。" 133 + "text=" + text + " , charset=" + charset ; 134 throw new OgRuntimeException( errMsg,ex ); 135 } 136 } 137 138 /** 139 * 日本語を含むヘッダ用テキストを生成します。 140 * 変換結果は ASCII なので、これをそのまま setSubject や InternetAddress 141 * のパラメタとして使用してください。 142 * 143 * ※ 内部で、UnsupportedEncodingException が発生した場合は、RuntimeException に変換されて throw されます。 144 * 145 * @param text テキスト 146 * 147 * @return 日本語を含むヘッダ用テキスト 148 * @og.rtnNotNull 149 */ 150 public String encodeWord( final String text ) { 151 try { 152 return MimeUtility.encodeText( text, charset, "B" ); 153 } 154 catch( final UnsupportedEncodingException ex ) { 155 final String errMsg = "指定のエンコードが出来ません。" 156 + "text=" + text + " , charset=" + charset ; 157 throw new OgRuntimeException( errMsg,ex ); 158 } 159 } 160 161 /** 162 * 日本語を含むアドレスを生成します。 163 * personal に、日本語が含まれると想定しています。 164 * サブクラスで、日本語処理を行う場合の方法は、それぞれ異なります。 165 * 166 * ※ 内部で、UnsupportedEncodingException が発生した場合は、RuntimeException に変換されて throw されます。 167 * 168 * @param address RFC822形式のアドレス 169 * @param personal 個人名 170 * 171 * @return InternetAddressオブジェクト 172 * @og.rtnNotNull 173 */ 174 public InternetAddress getAddress( final String address,final String personal ) { 175 try { 176 return new InternetAddress( address,personal,charset ); 177 } 178 catch( final UnsupportedEncodingException ex ) { 179 final String errMsg = "指定のエンコードが出来ません。" 180 + "address=" + address + " , charset=" + charset ; 181 throw new OgRuntimeException( errMsg,ex ); 182 } 183 } 184 185 /** 186 * Content-Transfer-Encoding を指定する場合の ビット数を返します。 187 * 188 * Windows系は、8bit / ISO-2022-JP 系は、7bit になります。 189 * 190 * @return ビット数("8bit" 固定) 191 * @og.rtnNotNull 192 */ 193 public String getBit() { 194 return "8bit" ; 195 } 196 } 197 198 /** 199 * MailCharset インターフェースを実装した ISO-2022-JP エンコード時のサブクラスです。 200 * 201 * 『2.ISO-2022-JP に独自変換 + 7bit 送信』 の実装です。 202 * 203 * 204 * @og.rev 6.3.9.1 (2015/11/27) 修飾子を、なし → private に変更。 205 * @version 4.0 206 * @author Kazuhiko Hasegawa 207 * @since JDK5.0, 208 */ 209 private static final class Mail_ISO2022JP_Charset implements MailCharset { 210 211 /** 212 * プラットフォーム依存のデフォルトの Charset です。 213 * プラットフォーム依存性を考慮する場合、エンコード指定で作成しておく事をお勧めします。 214 * 215 * @og.rev 5.5.2.6 (2012/05/25) findbugs対応 216 */ 217 private static final Charset DEFAULT_CHARSET = Charset.defaultCharset() ; 218 219 /** 220 * テキストをセットします。 221 * Part#setText() の代わりにこちらを使うようにします。 222 * 223 * ※ 内部で、MessagingException が発生した場合は、RuntimeException に変換されて throw されます。 224 * 225 * @param mimeMsg MimeMessageオブジェクト 226 * @param text テキスト 227 */ 228 public void setTextContent( final MimeMessage mimeMsg, final String text ) { 229 try { 230 // mimeMsg.setText(text, "ISO-2022-JP"); 231 mimeMsg.setDataHandler(new DataHandler(new JISDataSource(text))); 232 } 233 catch( final MessagingException ex ) { 234 final String errMsg = "指定のテキストをセットできません。" 235 + "text=" + text ; 236 throw new OgRuntimeException( errMsg,ex ); 237 } 238 } 239 240 /** 241 * 日本語を含むヘッダ用テキストを生成します。 242 * 変換結果は ASCII なので、これをそのまま setSubject や InternetAddress 243 * のパラメタとして使用してください。 244 * 245 * ※ 内部で、UnsupportedEncodingException が発生した場合は、RuntimeException に変換されて throw されます。 246 * 247 * @param text テキスト 248 * 249 * @return 日本語を含むヘッダ用テキスト 250 * @og.rtnNotNull 251 */ 252 public String encodeWord( final String text ) { 253 try { 254 return "=?ISO-2022-JP?B?" + 255 new String( 256 BASE64EncoderStream.encode( 257 CharCodeConverter.sjisToJis( 258 UnicodeCorrecter.correctToCP932(text).getBytes("Windows-31J") 259 ) 260 ) 261 ,DEFAULT_CHARSET ) + "?="; // 5.5.2.6 (2012/05/25) findbugs対応 262 } 263 catch( final UnsupportedEncodingException ex ) { 264 final String errMsg = "指定のエンコードが出来ません。" 265 + "text=" + text + " , charset=Windows-31J" ; 266 throw new OgRuntimeException( errMsg,ex ); 267 } 268 } 269 270 /** 271 * 日本語を含むアドレスを生成します。 272 * personal に、日本語が含まれると想定しています。 273 * サブクラスで、日本語処理を行う場合の方法は、それぞれ異なります。 274 * 275 * ※ 内部で、UnsupportedEncodingException が発生した場合は、RuntimeException に変換されて throw されます。 276 * 277 * @param address RFC822形式のアドレス 278 * @param personal 個人名 279 * 280 * @return InternetAddressオブジェクト 281 * @og.rtnNotNull 282 */ 283 public InternetAddress getAddress( final String address,final String personal ) { 284 try { 285 return new InternetAddress( address,encodeWord( personal ) ); 286 } 287 catch( final UnsupportedEncodingException ex ) { 288 final String errMsg = "指定のエンコードが出来ません。" 289 + "address=" + address ; 290 throw new OgRuntimeException( errMsg,ex ); 291 } 292 } 293 294 /** 295 * Content-Transfer-Encoding を指定する場合の ビット数を返します。 296 * 297 * Windows系は、8bit / ISO-2022-JP 系は、7bit になります。 298 * 299 * @return ビット数("7bit" 固定) 300 * @og.rtnNotNull 301 */ 302 public String getBit() { 303 return "7bit" ; 304 } 305 } 306 307 /** 308 * テキストの本文を送信するための DataSource です。 309 * 310 * Windows-31J でバイトコードに変換した後、独自エンコードにて、 311 * Shift-JIS ⇒ JIS 変換しています。 312 * 313 * @og.rev 6.3.9.1 (2015/11/27) 修飾子を、なし → private に変更。 314 * 315 * @version 4.0 316 * @author Kazuhiko Hasegawa 317 * @since JDK5.0, 318 */ 319 private static final class JISDataSource implements DataSource { 320 private final byte[] data; 321 322 /** 323 * JIS(Windows-31J) に対応した DataSource オブジェクトのコンストラクタ 324 * 325 * Windows-31J でバイトコードに変換した後、独自エンコードにて、 326 * Shift-JIS ⇒ JIS 変換しています。 327 * 328 * @param str 変換する文字列 329 */ 330 public JISDataSource( final String str ) { 331 try { 332 data = CharCodeConverter.sjisToJis( 333 UnicodeCorrecter.correctToCP932(str).getBytes("Windows-31J")); 334 335 } catch( final UnsupportedEncodingException ex ) { 336 final String errMsg = "Windows-31J でのエンコーディングが出来ません。" + str; 337 throw new OgRuntimeException( errMsg,ex ); 338 } 339 } 340 341 /** 342 * データの MIME タイプを文字列の形で返します。 343 * かならず有効なタイプを返すべきです。 344 * DataSource の実装がデータタイプを 決定できない場合は、 345 * getContentType は "application/octet-stream" を返すことを 提案します。 346 * 347 * @return MIMEタイプ("text/plain; charset=ISO-2022-JP" 固定) 348 * @og.rtnNotNull 349 */ 350 public String getContentType() { 351 return "text/plain; charset=ISO-2022-JP"; 352 } 353 354 /** 355 * データを表す InputStreamオブジェクト を返します。 356 * それができない場合は適切な例外をスローします。 357 * 358 * @return InputStreamオブジェクト 359 * @throws IOException ※ このメソッドからは、IOException は throw されません。 360 * @og.rtnNotNull 361 */ 362 public InputStream getInputStream() throws IOException { 363 return new ByteArrayInputStream( data ); 364 } 365 366 /** 367 * データが書込可能なら OutputStreamオブジェクト を返します。 368 * それができない場合は適切な例外をスローします。 369 * 370 * ※ このクラスでは実装されていません。 371 * 372 * @return OutputStreamオブジェクト 373 * @throws IOException ※ このメソッドを実行すると、必ず throw されます。 374 */ 375 public OutputStream getOutputStream() throws IOException { 376 final String errMsg = "このクラスでは実装されていません。"; 377 // throw new UnsupportedOperationException( errMsg ); 378 throw new IOException( errMsg ); 379 } 380 381 /** 382 * このオブジェクトの '名前' を返します。 383 * この名前は下層のオブジェクトの性質によります。 384 * ファイルをカプセル化する DataSource なら オブジェクトの 385 * ファイル名を返すようにするかもしれません。 386 * 387 * @return オブジェクトの名前( "JISDataSource" 固定) 388 * @og.rtnNotNull 389 */ 390 public String getName() { 391 return "JISDataSource"; 392 } 393 } 394}