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 */
016 package org.opengion.fukurou.mail;
017
018 // import java.io.InputStream;
019 // import java.io.OutputStream;
020 // import java.io.ByteArrayOutputStream;
021 // import java.io.ByteArrayInputStream;
022 import java.io.UnsupportedEncodingException;
023 // import java.io.IOException;
024
025 // import javax.activation.DataHandler;
026 // import javax.activation.DataSource;
027 import javax.mail.internet.InternetAddress;
028 import javax.mail.internet.MimeMessage;
029 // import javax.mail.internet.MimeUtility;
030 import javax.mail.MessagingException;
031 // import com.sun.mail.util.BASE64EncoderStream;
032
033 // import java.nio.charset.Charset; // 5.5.2.6 (2012/05/25)
034
035 /**
036 * MailCharset は、E-Mail 送信時?エンコードに応じた??行う為の?
037 * インターフェースです?
038 *
039 * E-Mail で日本語を送信する場合?ISO-2022-JP(JISコー?化して?bit で
040 * エンコードして送信する?がありますが、Windows系の特殊文字や、unicodeと
041 * ??マッピングが異なる文字などが??化けします?
042 * 対応方法としては?
043 * 『1.Windows-31J + 8bit 送信?
044 * 『2.ISO-2022-JP に独自変換 + 7bit 送信?
045 * の方法があります?
046 * 今回、この?つの方法につ?、それぞれサブクラス化を行い、??きるように
047 * したのが?こ?インターフェース、およ?、サブクラスです?
048 *
049 * 『1.Windows-31J + 8bit 送信』?方法???常の JavaMail API に準拠して
050 * 処?行う、Mail_Windows31J_Charset サブクラスで実?て?す?
051 * 古?イラーおよび、古?ールサーバ?ではメール転送できな??
052 * こ?方式?、社?使用する場合?みに、利用できますが、主としてWindows系の
053 * 社?ス?においては、こちら?方が?なにかとトラブルは少な?思います?
054 *
055 * 『2.ISO-2022-JP に独自変換 + 7bit 送信』?実???
056 * JAVA PRESS Vol.37 (http://www.gihyo.co.jp/magazines/javapress)の
057 * 【特??決定版??サーバサイドJavaの日本語??
058 * 第3?JavaMailの日本語???ログラミング……木下信
059 *“?ルチ?ラ?フォー??な日本語メール送信?完?解説
060 * でのサンプルアプリケーション
061 * http://www.gihyo.co.jp/book/2004/225371/download/toku1_3.zip
062 * を?使用して、Mail_ISO2022JP_Charset サブクラスで実?て?す?
063 *
064 * これら?サブクラスは、MailCharsetFactory ファクトリクラスより、作?されます?
065 * そ?場合?引数のキャラクタセ?名?、Windows-31J 、MS932 か?それ以外となって?す?
066 * それ以外が?された場合?、ISO-2022-JP を使用します?
067 *
068 * @version 4.0
069 * @author Kazuhiko Hasegawa
070 * @since JDK5.0,
071 */
072 public interface MailCharset {
073
074 /**
075 * ?ストをセ?します?
076 * Part#setText() の代わりにこちらを使??します?
077 *
078 * ※ ?で、MessagingException が発生した?合?、RuntimeException に変換されて throw されます?
079 *
080 * @param mimeMsg MimeMessage?取り込み件数
081 * @param text 設定するテキス?
082 */
083 void setTextContent( MimeMessage mimeMsg, String text ) ;
084
085 /**
086 * 日本語を含???用?ストを生?します?
087 * 変換結果は ASCII なので、これをそ?まま setSubject ?InternetAddress
088 * のパラメタとして使用してください?
089 *
090 * ※ ?で、UnsupportedEncodingException が発生した?合?、RuntimeException に変換されて throw されます?
091 *
092 * @param text 設定するテキス?
093 *
094 * @return 日本語を含???用?ス?
095 */
096 String encodeWord( String text ) ;
097
098 /**
099 * 日本語を含?ドレスを生成します?
100 * personal に、日本語が含まれると想定して?す?
101 * サブクラスで、日本語??行う場合?方法?、それぞれ異なります?
102 *
103 * ※ ?で、UnsupportedEncodingException が発生した?合?、RuntimeException に変換されて throw されます?
104 *
105 * @param address アドレス部?
106 * @param personal 日本語?説明部?
107 *
108 * @return 日本語を含?ドレス
109 */
110 InternetAddress getAddress( String address,String personal ) ;
111
112 /**
113 * Content-Transfer-Encoding を指定する?合? ビット数を返します?
114 *
115 * Windows系は?bit / ISO-2022-JP 系は?bit になります?
116 *
117 * @return ビット数
118 */
119 String getBit() ;
120 }
121
122 /**
123 * MailCharsetFactory は、MailCharset インターフェースを実?たサブクラス?
124 * 作?する ファクトリクラスです?
125 *
126 * 引数のキャラクタセ?名が、Windows-31J 、MS932 の場合??
127 * 『1.Windows-31J + 8bit 送信?の実?ある、Mail_Windows31J_Charset
128 * サブクラスを返します?
129 * それ以外が?された場合?、ISO-2022-JP を使用して、??.ISO-2022-JP に独自変換 + 7bit 送信?
130 * の実?ある、Mail_ISO2022JP_Charset サブクラスを返します?
131 *
132 * @version 4.0
133 * @author Kazuhiko Hasegawa
134 * @since JDK5.0,
135 */
136 //class MailCharsetFactory {
137 //
138 // /**
139 // * インスタンスの生?を抑止します?
140 // */
141 // private MailCharsetFactory() {
142 // // 何もありません?PMD エラー回避)
143 // }
144 //
145 // /**
146 // * キャラクタセ?に応じた?MailCharset オブジェクトを返します?
147 // *
148 // * Windows-31J 、MS932 、Shift_JIS の場合?、Mail_Windows31J_Charset
149 // * そ?他?、ISO-2022-JP として、Mail_ISO2022JP_Charset を返します?
150 // *
151 // * 注意:null の場合?、デフォルトではなく?Mail_ISO2022JP_Charset を返します?
152 // *
153 // * @param charset キャラクタセ?[Windows-31J/MS932/Shift_JIS/そ?他]
154 // *
155 // * @return MailCharset
156 // */
157 // static MailCharset newInstance( final String charset ) {
158 // final MailCharset mcset;
159 //
160 // if( "MS932".equalsIgnoreCase( charset ) ||
161 // "Shift_JIS".equalsIgnoreCase( charset ) ||
162 // "Windows-31J".equalsIgnoreCase( charset ) ) {
163 // mcset = new Mail_Windows31J_Charset( charset );
164 // }
165 // else {
166 // mcset = new Mail_ISO2022JP_Charset();
167 // }
168 // return mcset ;
169 // }
170 //}
171
172 /**
173 * MailCharset インターフェースを実??Windwos-31J エンコード時のサブクラスです?
174 *
175 * 『1.Windows-31J + 8bit 送信?の実?す?
176 *
177 * @version 4.0
178 * @author Kazuhiko Hasegawa
179 * @since JDK5.0,
180 */
181 //class Mail_Windows31J_Charset implements MailCharset {
182 // private final String charset ; // "Windows-31J" or "MS932"
183 //
184 // /**
185 // * 引数に、エンコード方式を?して、作?するコンストラクタです?
186 // *
187 // * @param charset String
188 // */
189 // public Mail_Windows31J_Charset( final String charset ) {
190 // this.charset = charset;
191 // }
192 //
193 // /**
194 // * ?ストをセ?します?
195 // * Part#setText() の代わりにこちらを使??します?
196 // *
197 // * @param mimeMsg MimeMessage
198 // * @param text String
199 // * @throws RuntimeException(MessagingException)
200 // */
201 // public void setTextContent( final MimeMessage mimeMsg, final String text ) {
202 // try {
203 // mimeMsg.setText( text,charset ); // "text/plain" Content
204 // }
205 // catch( MessagingException ex ) {
206 // String errMsg = "???ストをセ?できません?
207 // + "text=" + text + " , charset=" + charset ;
208 // throw new RuntimeException( errMsg,ex );
209 // }
210 // }
211 //
212 // /**
213 // * 日本語を含???用?ストを生?します?
214 // * 変換結果は ASCII なので、これをそ?まま setSubject ?InternetAddress
215 // * のパラメタとして使用してください?
216 // *
217 // * @param text String
218 // *
219 // * @return 日本語を含???用?ス?
220 // * @throws RuntimeException(UnsupportedEncodingException)
221 // */
222 // public String encodeWord( final String text ) {
223 // try {
224 // return MimeUtility.encodeText( text, charset, "B" );
225 // }
226 // catch( UnsupportedEncodingException ex ) {
227 // String errMsg = "??エンコードが出来ません?
228 // + "text=" + text + " , charset=" + charset ;
229 // throw new RuntimeException( errMsg,ex );
230 // }
231 // }
232 //
233 // /**
234 // * 日本語を含?ドレスを生成します?
235 // * personal に、日本語が含まれると想定して?す?
236 // * サブクラスで、日本語??行う場合?方法?、それぞれ異なります?
237 // *
238 // * @param address String
239 // * @param personal String
240 // *
241 // * @return InternetAddress
242 // * @throws RuntimeException(UnsupportedEncodingException)
243 // */
244 // public InternetAddress getAddress( final String address,final String personal ) {
245 // try {
246 // return new InternetAddress( address,personal,charset );
247 // }
248 // catch( UnsupportedEncodingException ex ) {
249 // String errMsg = "??エンコードが出来ません?
250 // + "address=" + address + " , charset=" + charset ;
251 // throw new RuntimeException( errMsg,ex );
252 // }
253 // }
254 //
255 // /**
256 // * Content-Transfer-Encoding を指定する?合? ビット数を返します?
257 // *
258 // * Windows系は?bit / ISO-2022-JP 系は?bit になります?
259 // *
260 // * @return ビット数("8bit" 固?
261 // */
262 // public String getBit() {
263 // return "8bit" ;
264 // }
265 //}
266
267 /**
268 * MailCharset インターフェースを実??ISO-2022-JP エンコード時のサブクラスです?
269 *
270 * 『2.ISO-2022-JP に独自変換 + 7bit 送信?の実?す?
271 *
272 * @version 4.0
273 * @author Kazuhiko Hasegawa
274 * @since JDK5.0,
275 */
276 //class Mail_ISO2022JP_Charset implements MailCharset {
277 //
278 // /**
279 // * プラ?フォー?存??ォルト? Charset です?
280 // * プラ?フォー?存?を?慮する場合?エンコード指定で作?しておく事をお勧めします?
281 // *
282 // * @og.rev 5.5.2.6 (2012/05/25) findbugs対?
283 // */
284 // private static final Charset DEFAULT_CHARSET = Charset.defaultCharset() ;
285 //
286 // /**
287 // * ?ストをセ?します?
288 // * Part#setText() の代わりにこちらを使??します?
289 // *
290 // * @param mimeMsg MimeMessage
291 // * @param text String
292 // * @throws RuntimeException(MessagingException)
293 // */
294 // public void setTextContent( final MimeMessage mimeMsg, final String text ) {
295 // try {
296 // // mimeMsg.setText(text, "ISO-2022-JP");
297 // mimeMsg.setDataHandler(new DataHandler(new JISDataSource(text)));
298 // }
299 // catch( MessagingException ex ) {
300 // String errMsg = "???ストをセ?できません?
301 // + "text=" + text ;
302 // throw new RuntimeException( errMsg,ex );
303 // }
304 // }
305 //
306 // /**
307 // * 日本語を含???用?ストを生?します?
308 // * 変換結果は ASCII なので、これをそ?まま setSubject ?InternetAddress
309 // * のパラメタとして使用してください?
310 // *
311 // * @param text String
312 // *
313 // * @return 日本語を含???用?ス?
314 // * @throws RuntimeException(UnsupportedEncodingException)
315 // */
316 // public String encodeWord( final String text ) {
317 // try {
318 // return "=?ISO-2022-JP?B?" +
319 // new String(
320 // BASE64EncoderStream.encode(
321 // CharCodeConverter.sjisToJis(
322 // UnicodeCorrecter.correctToCP932(text).getBytes("Windows-31J")
323 // )
324 // )
325 // ,DEFAULT_CHARSET ) + "?="; // 5.5.2.6 (2012/05/25) findbugs対?
326 // }
327 // catch( UnsupportedEncodingException ex ) {
328 // String errMsg = "??エンコードが出来ません?
329 // + "text=" + text + " , charset=Windows-31J" ;
330 // throw new RuntimeException( errMsg,ex );
331 // }
332 // }
333 //
334 // /**
335 // * 日本語を含?ドレスを生成します?
336 // * personal に、日本語が含まれると想定して?す?
337 // * サブクラスで、日本語??行う場合?方法?、それぞれ異なります?
338 // *
339 // * @param address String
340 // * @param personal String
341 // *
342 // * @return InternetAddress
343 // * @throws RuntimeException(UnsupportedEncodingException)
344 // */
345 // public InternetAddress getAddress( final String address,final String personal ) {
346 // try {
347 // return new InternetAddress( address,encodeWord( personal ) );
348 // }
349 // catch( UnsupportedEncodingException ex ) {
350 // String errMsg = "??エンコードが出来ません?
351 // + "address=" + address ;
352 // throw new RuntimeException( errMsg,ex );
353 // }
354 // }
355 //
356 // /**
357 // * Content-Transfer-Encoding を指定する?合? ビット数を返します?
358 // *
359 // * Windows系は?bit / ISO-2022-JP 系は?bit になります?
360 // *
361 // * @return ビット数("7bit" 固?
362 // */
363 // public String getBit() {
364 // return "7bit" ;
365 // }
366 //}
367
368 /**
369 * ?スト?本?送信するための DataSource です?
370 *
371 * Windows-31J でバイトコードに変換した後?独自エンコードにて?
372 * Shift-JIS ?JIS 変換して?す?
373 *
374 * @version 4.0
375 * @author Kazuhiko Hasegawa
376 * @since JDK5.0,
377 */
378 //class JISDataSource implements DataSource {
379 // private final byte[] data;
380 //
381 // public JISDataSource( final String str ) {
382 // try {
383 // data = CharCodeConverter.sjisToJis(
384 // UnicodeCorrecter.correctToCP932(str).getBytes("Windows-31J"));
385 //
386 // } catch (UnsupportedEncodingException e) {
387 // String errMsg = "Windows-31J でのエンコー?ングが?来ません? + str;
388 // throw new RuntimeException( errMsg,e );
389 // }
390 // }
391 //
392 // /**
393 // * ??タの MIME タイプを??の形で返します?
394 // * かならず有効なタイプを返すべきです?
395 // * DataSource の実???タタイプを 決定できな??合??
396 // * getContentType は "application/octet-stream" を返すこと?提案します?
397 // *
398 // * @return MIME タイ?
399 // */
400 // public String getContentType() {
401 // return "text/plain; charset=ISO-2022-JP";
402 // }
403 //
404 // /**
405 // * ??タを表?InputStream を返します?
406 // * それができな??合?適?例外をスローします?
407 // *
408 // * @return InputStream
409 // * @throws IOException
410 // */
411 // public InputStream getInputStream() throws IOException {
412 // return new ByteArrayInputStream( data );
413 // }
414 //
415 // /**
416 // * ??タが書込可能な?OutputStream を返します?
417 // * それができな??合?適?例外をスローします?
418 // *
419 // * ※ こ?クラスでは実?れて?せん?
420 // *
421 // * @return OutputStream
422 // * @throws IOException
423 // */
424 // public OutputStream getOutputStream() throws IOException {
425 // String errMsg = "こ?クラスでは実?れて?せん?;
426 // // throw new UnsupportedOperationException( errMsg );
427 // throw new IOException( errMsg );
428 // }
429 //
430 // /**
431 // * こ?オブジェクト? '名前' を返します?
432 // * こ?名前は下層のオブジェクト?性質によります?
433 // * ファイルをカプセル化す?DataSource な?オブジェクト?
434 // * ファイル名を返すようにするかもしれません?
435 // *
436 // * @return オブジェクト?名前
437 // */
438 // public String getName() {
439 // return "JISDataSource";
440 // }
441 //}
442
443 /**
444 * ?関係?コンバ?タです?
445 * ?コード?オリジナルは<a href="http://www-cms.phys.s.u-tokyo.ac.jp/~naoki/CIPINTRO/CCGI/kanjicod.html">Japanese Kanji Code</a>にて公開されて?も?です?
446 * また?http://www.sk-jp.com/cgi-bin/treebbs.cgi?kako=1&all=644&s=681
447 * にて YOSI さんが?開されたコードも参?にして??と?か実質同じで??
448 *
449 * @version 4.0
450 * @author Kazuhiko Hasegawa
451 * @since JDK5.0,
452 */
453 //class CharCodeConverter {
454 // private static final byte[] SJIS_KANA; // 5.1.9.0 (2010/09/01) public ?private へ変更
455 //
456 // /**
457 // * インスタンスの生?を抑止します?
458 // */
459 // private CharCodeConverter() {
460 // // 何もありません?PMD エラー回避)
461 // }
462 //
463 // static {
464 // try {
465 // // 全角への変換??ブル
466 // SJIS_KANA = "。?」?・ヲァィゥェォャュョ??アイウエオカキクケコサシスセソタチツ?ナニヌネノハヒフヘ?マミ?モヤユヨラリルレロワン゛?".getBytes("Shift_JIS");
467 // } catch( UnsupportedEncodingException ex ) {
468 // throw new RuntimeException( "CANT HAPPEN",ex );
469 // }
470 // }
471 //
472 // /**
473 // * Shift_JIS エンコー?ングスキー?基づくバイト??
474 // * ISO-2022-JP エンコー?ングスキー?変換します?
475 // * 「半角カナ?は対応する?角文字に変換します?
476 // *
477 // * @param sjisBytes byte[] エンコードするShift_JISバイト??
478 // *
479 // * @return byte[] 変換後?ISO-2022-JP(JIS)バイト??not null)
480 // */
481 // public static byte[] sjisToJis( final byte[] sjisBytes ) {
482 // ByteArrayOutputStream out = new ByteArrayOutputStream();
483 // boolean nonAscii = false;
484 // int len = sjisBytes.length;
485 // for(int i = 0; i < len; i++ ) {
486 // if(sjisBytes[i] >= 0) {
487 // if(nonAscii) {
488 // nonAscii = false;
489 // out.write(0x1b);
490 // out.write('(');
491 // out.write('B');
492 // }
493 // out.write(sjisBytes[i]);
494 // } else {
495 // if(!nonAscii) {
496 // nonAscii = true;
497 // out.write(0x1b);
498 // out.write('$');
499 // out.write('B');
500 // }
501 // int bt = sjisBytes[i] & 0xff;
502 // if(bt >= 0xa1 && bt <= 0xdf) {
503 // // 半角カナ?全角に変換
504 // int kanaIndex = (bt - 0xA1) * 2;
505 // sjisToJis(out, SJIS_KANA[kanaIndex], SJIS_KANA[kanaIndex + 1]);
506 // } else {
507 // i++;
508 // if(i == len) { break; }
509 // sjisToJis(out, sjisBytes[i - 1], sjisBytes[i]);
510 // }
511 // }
512 // }
513 // if(nonAscii) {
514 // out.write(0x1b);
515 // out.write('(');
516 // out.write('B');
517 // }
518 // return out.toByteArray();
519 // }
520 //
521 // /**
522 // * ?文字??バイ?Shift_JIS コードを JIS コードに変換して書き?します?
523 // */
524 // private static void sjisToJis(
525 // final ByteArrayOutputStream out, final byte bhi, final byte blo) {
526 // int hi = (bhi << 1) & 0xFF;
527 // int lo = blo & 0xFF;
528 // if(lo < 0x9F) {
529 // if(hi < 0x3F) { hi += 0x1F; } else { hi -= 0x61; }
530 // if(lo > 0x7E) { lo -= 0x20; } else { lo -= 0x1F; }
531 // } else {
532 // if(hi < 0x3F) { hi += 0x20; } else { hi -= 0x60; }
533 // lo -= 0x7E;
534 // }
535 // out.write(hi);
536 // out.write(lo);
537 // }
538 //}
539
540 /**
541 * unicode と、JIS との?コード?関係で、変換して?す?
542 *
543 * 0x301c(〜) を?0xff5e(~) へ?
544 * 0x2016(‖) を?0x2225(∥) へ?
545 * 0x2212(−) を?0xff0d(-) へ?
546 * それぞれコード変換します?
547 *
548 * @version 4.0
549 * @author Kazuhiko Hasegawa
550 * @since JDK5.0,
551 */
552 //class UnicodeCorrecter {
553 //
554 // /**
555 // * インスタンスの生?を抑止します?
556 // */
557 // private UnicodeCorrecter() {
558 // // 何もありません?PMD エラー回避)
559 // }
560 //
561 // /**
562 // * Unicode ??の補正を行います?
563 // * "MS932" コンバ?タでエンコードしようとした際に
564 // * 正常に変換できな??補正します?
565 // */
566 // public static String correctToCP932( final String str ) {
567 // String rtn = "";
568 //
569 // if( str != null ) {
570 // int cnt = str.length();
571 // StringBuilder buf = new StringBuilder( cnt );
572 // for(int i=0; i<cnt; i++) {
573 // buf.append(correctToCP932(str.charAt(i)));
574 // }
575 // rtn = buf.toString() ;
576 // }
577 // return rtn ;
578 // }
579 //
580 // /**
581 // * キャラクタ単位に、Unicode ??の補正を行います?
582 // *
583 // * 風間殿のペ?ジを参?して?す?
584 // * @see <a href="http://www.ingrid.org/java/i18n/encoding/ja-conv.html" target="_blank">
585 // * http://www.ingrid.org/java/i18n/encoding/ja-conv.html</a>
586 // */
587 // public static char correctToCP932( final char ch ) {
588 // char rtn = ch;
589 //
590 // switch (ch) {
591 // // case 0x00a2: return 0xffe0; // ≪
592 // // case 0x00a3: return 0xffe1; // ?
593 // // case 0x00ac: return 0xffe2; // μ
594 // // case 0x03bc: return 0x00b5; // ・
595 // // case 0x2014: return 0x2015; // ??
596 // // case 0x2016: return 0x2225; // ≫
597 // // case 0x2212: return 0xff0d; // ?
598 // // case 0x226a: return 0x00ab; // ∥
599 // // case 0x226b: return 0x00bb; // ヴ
600 // // case 0x301c: return 0xff5e; // ??
601 // // case 0x30f4: return 0x3094; // ??
602 // // case 0x30fb: return 0x00b7; // ??
603 // // case 0xff0c: return 0x00b8; // ?
604 // // case 0xffe3: return 0x00af; // ?
605 //
606 // case 0x00a2: rtn = 0xffe0; break; // ??(1-81, CENT SIGN)
607 // case 0x00a3: rtn = 0xffe1; break; // ? (1-82, POUND SIGN)
608 // case 0x00a5: rtn = 0x005c; break; // \ (D/12, YEN SIGN)
609 // case 0x00ac: rtn = 0xffe2; break; // ? (2-44, NOT SIGN)
610 // case 0x2016: rtn = 0x2225; break; // ∥ (1-34, DOUBLE VERTICAL LINE)
611 // case 0x203e: rtn = 0x007e; break; // ~ (F/14, OVERLINE)
612 // case 0x2212: rtn = 0xff0d; break; // ??(1-61, MINUS SIGN)
613 // case 0x301c: rtn = 0xff5e; break; // ??(1-33, WAVE DASH)
614 //
615 // // case 0x301c: return 0xff5e;
616 // // case 0x2016: return 0x2225;
617 // // case 0x2212: return 0xff0d;
618 // default: break; // 4.0.0 (2005/01/31)
619 // }
620 // return rtn;
621 // }
622 //}