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    }