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