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