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     * MailCharsetFactory は、MailCharset インターフェースを実è£?�—たサブクラスã‚?
037     * 作æ?する ファクトリクラスですã?
038     *
039     * 引数のキャラクタセãƒ?ƒˆ名が、Windows-31J 、MS932 の場合ã?ã€?
040     * 『1.Windows-31J + 8bit 送信ã€?の実è£?�§ある、Mail_Windows31J_Charset
041     * サブクラスを返しますã?
042     * それ以外がæŒ?®šされた場合ã?、ISO-2022-JP を使用して、ã??’.ISO-2022-JP に独自変換 + 7bit 送信ã€?
043     * の実è£?�§ある、Mail_ISO2022JP_Charset サブクラスを返しますã?
044     *
045     * @version  4.0
046     * @author   Kazuhiko Hasegawa
047     * @since    JDK5.0,
048     */
049    class 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系はã€?bit / ISO-2022-JP 系はã€?bit になりますã?
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系はã€?bit / ISO-2022-JP 系はã€?bit になりますã?
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    }