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