1 package com.ozacc.mail;
2
3 import java.io.File;
4 import java.io.InputStream;
5 import java.io.UnsupportedEncodingException;
6 import java.net.URL;
7 import java.util.ArrayList;
8 import java.util.Collections;
9 import java.util.HashMap;
10 import java.util.Iterator;
11 import java.util.List;
12 import java.util.Map;
13
14 import javax.activation.DataSource;
15 import javax.activation.FileDataSource;
16 import javax.activation.FileTypeMap;
17 import javax.activation.URLDataSource;
18 import javax.mail.internet.AddressException;
19 import javax.mail.internet.InternetAddress;
20
21 import com.ozacc.mail.impl.ByteArrayDataSource;
22 import com.ozacc.mail.impl.Cp932;
23
24 /***
25 * メール。
26 *
27 * @since 1.0
28 * @author Tomohiro Otsuka
29 * @version $Id: Mail.java,v 1.11 2005/01/17 09:08:16 otsuka Exp $
30 */
31 public class Mail {
32
33 /*** <code>ISO-2022-JP</code> */
34 public static final String JIS_CHARSET = "ISO-2022-JP";
35
36 public static final String DOCTYPE_PUBLIC = "-//OZACC//DTD MAIL//EN";
37
38 public static final String DOCTYPE_SYSTEM = "http://www.ozacc.com/library/dtd/ozacc-mail.dtd";
39
40 private String charset = JIS_CHARSET;
41
42 private String text;
43
44 private InternetAddress from;
45
46 private String subject;
47
48 private List to;
49
50 private List cc;
51
52 private List bcc;
53
54 private InternetAddress returnPath;
55
56 private InternetAddress replyTo;
57
58 private String importance;
59
60 private Map xHeaders;
61
62 private String htmlText;
63
64 private List attachmentFiles;
65
66 /***
67 * コンストラクタ。
68 */
69 public Mail() {}
70
71 /***
72 * コンストラクタ。
73 * 宛先や差出人の名前をエンコードする時に使用する文字コードを指定します。
74 * デフォルトは<code>ISO-2022-JP</code>です。
75 * <p>
76 * 日本語環境で利用する場合は通常変更する必要はありません。
77 *
78 * @param charset エンコードに使用する文字コード
79 */
80 public Mail(String charset) {
81 this();
82 this.charset = charset;
83 }
84
85 /***
86 * コピーコンストラクタ。
87 * シャローコピー(shallow copy)です。
88 *
89 * @since 1.0.2
90 *
91 * @param original コピー元のMailインスタンス
92 */
93 public Mail(Mail original) {
94 this.bcc = original.bcc;
95 this.cc = original.cc;
96 this.charset = original.charset;
97 this.from = original.from;
98 this.importance = original.importance;
99 this.replyTo = original.replyTo;
100 this.returnPath = original.returnPath;
101 this.subject = original.subject;
102 this.text = original.text;
103 this.to = original.to;
104 this.xHeaders = original.xHeaders;
105 this.htmlText = original.htmlText;
106 this.attachmentFiles = original.attachmentFiles;
107 }
108
109 /***
110 * エンコードに使用する文字コードを返します。
111 *
112 * @return エンコードに使用する文字コード
113 */
114 public String getCharset() {
115 return charset;
116 }
117
118 /***
119 * メールの重要度をセットします。
120 * 引数で指定可能な値は「high」、「normal」、「low」のいずれかです。
121 *
122 * @param importance メールの重要度。「high」、「normal」、「low」のいずれか。
123 * @throws IllegalArgumentException 指定可能な値以外が指定された場合
124 *
125 * @see Mail.Importance
126 */
127 public void setImportance(String importance) throws IllegalArgumentException {
128 if ("high".equals(importance) || "normal".equals(importance) || "low".equals(importance)) {
129 this.importance = importance;
130 } else {
131 throw new IllegalArgumentException("'" + importance + "'は、メール重要度には指定できない値です。");
132 }
133 }
134
135 /***
136 * メールの重要度を返します。
137 * 値は「high」、「normal」、「low」のいずれかです。
138 *
139 * @return メールの重要度。「high」、「normal」、「low」のいずれか。
140 */
141 public String getImportance() {
142 return importance;
143 }
144
145 /***
146 * メールの送信先アドレスを追加します。
147 *
148 * @param address 送信先アドレス
149 */
150 public void addTo(InternetAddress address) {
151 if (to == null) {
152 to = new ArrayList();
153 }
154 to.add(address);
155 }
156
157 /***
158 * メールの送信先アドレスを追加します。
159 *
160 * @param email 送信先アドレス
161 * @throws IllegalArgumentException 不正なフォーマットのアドレスが指定された場合
162 */
163 public void addTo(String email) throws IllegalArgumentException {
164 try {
165 addTo(new InternetAddress(email));
166 } catch (AddressException e) {
167 throw new IllegalArgumentException(e.getMessage());
168 }
169 }
170
171 /***
172 * メールの送信先名とアドレスを追加します。
173 * 名前はJIS_CHARSETでエンコードされます。
174 *
175 * @param email 送信先アドレス
176 * @param name 送信先名
177 * @throws IllegalArgumentException 不正なフォーマットのアドレスが指定された場合
178 */
179 public void addTo(String email, String name) throws IllegalArgumentException {
180 if (charset.equals(JIS_CHARSET)) {
181 name = Cp932.toJIS(name);
182 }
183 try {
184 addTo(new InternetAddress(email, name, charset));
185 } catch (UnsupportedEncodingException e) {
186 throw new IllegalArgumentException(e.getMessage());
187 }
188 }
189
190 /***
191 * メールの送信先アドレスの配列を返します。
192 * 送信先アドレスが一件もセットされていないときは空の配列を返します。
193 *
194 * @return 送信先アドレスの配列
195 */
196 public InternetAddress[] getTo() {
197 if (to == null) {
198 return new InternetAddress[0];
199 }
200 return (InternetAddress[])to.toArray(new InternetAddress[to.size()]);
201 }
202
203 /***
204 * CCアドレスを追加します。
205 *
206 * @param address CCのアドレス
207 */
208 public void addCc(InternetAddress address) {
209 if (cc == null) {
210 cc = new ArrayList();
211 }
212 cc.add(address);
213 }
214
215 /***
216 * CCアドレスを追加します。
217 *
218 * @param email CCのアドレス
219 * @throws IllegalArgumentException 不正なフォーマットのアドレスが指定された場合
220 */
221 public void addCc(String email) throws IllegalArgumentException {
222 try {
223 addCc(new InternetAddress(email));
224 } catch (AddressException e) {
225 throw new IllegalArgumentException(e.getMessage());
226 }
227 }
228
229 /***
230 * CCの宛名とアドレスを追加します。
231 * 名前はJIS_CHARSETでエンコードされます。
232 *
233 * @param email CCのアドレス
234 * @param name CCの宛名
235 * @throws IllegalArgumentException 不正なフォーマットのアドレスが指定された場合
236 */
237 public void addCc(String email, String name) throws IllegalArgumentException {
238 if (charset.equals(JIS_CHARSET)) {
239 name = Cp932.toJIS(name);
240 }
241 try {
242 addCc(new InternetAddress(email, name, charset));
243 } catch (UnsupportedEncodingException e) {
244 throw new IllegalArgumentException(e.getMessage());
245 }
246 }
247
248 /***
249 * メールのCCアドレス配列を返します。
250 * CCアドレスが一件もセットされていないときは空の配列を返します。
251 *
252 * @return CCアドレスの配列
253 */
254 public InternetAddress[] getCc() {
255 if (cc == null) {
256 return new InternetAddress[0];
257 }
258 return (InternetAddress[])cc.toArray(new InternetAddress[cc.size()]);
259 }
260
261 /***
262 * BCCアドレスを追加します。
263 *
264 * @param address BCCのアドレス
265 */
266 public void addBcc(InternetAddress address) {
267 if (bcc == null) {
268 bcc = new ArrayList();
269 }
270 bcc.add(address);
271 }
272
273 /***
274 * BCCアドレスを追加します。
275 *
276 * @param email BCCのアドレス
277 * @throws IllegalArgumentException 不正なフォーマットのアドレスが指定された場合
278 */
279 public void addBcc(String email) throws IllegalArgumentException {
280 try {
281 addBcc(new InternetAddress(email));
282 } catch (AddressException e) {
283 throw new IllegalArgumentException(e.getMessage());
284 }
285 }
286
287 /***
288 * メールのBCCアドレスの配列を返します。
289 * BCCアドレスが一件もセットされていないときは空の配列を返します。
290 *
291 * @return BCCアドレスの配列
292 */
293 public InternetAddress[] getBcc() {
294 if (bcc == null) {
295 return new InternetAddress[0];
296 }
297 return (InternetAddress[])bcc.toArray(new InternetAddress[bcc.size()]);
298 }
299
300 /***
301 * メールの差出人アドレスをセットします。
302 *
303 * @param address 差出人アドレス
304 */
305 public void setFrom(InternetAddress address) {
306 from = address;
307 }
308
309 /***
310 * メールの差出人アドレスをセットします。
311 *
312 * @param email 差出人アドレス
313 * @throws IllegalArgumentException 不正なフォーマットのアドレスが指定された場合
314 */
315 public void setFrom(String email) throws IllegalArgumentException {
316 try {
317 setFrom(new InternetAddress(email));
318 } catch (AddressException e) {
319 throw new IllegalArgumentException(e.getMessage());
320 }
321 }
322
323 /***
324 * メールの差出人名とアドレスをセットします。
325 * 名前はJIS_CHARSETでエンコードされます。
326 *
327 * @param email 差出人アドレス
328 * @param name 差出人名
329 * @throws IllegalArgumentException 不正なフォーマットのアドレスが指定された場合
330 */
331 public void setFrom(String email, String name) throws IllegalArgumentException {
332 if (charset.equals(JIS_CHARSET)) {
333 name = Cp932.toJIS(name);
334 }
335 try {
336 setFrom(new InternetAddress(email, name, charset));
337 } catch (UnsupportedEncodingException e) {
338 throw new IllegalArgumentException(e.getMessage());
339 }
340 }
341
342 /***
343 * メールの差出人アドレスを返します。セットされていない場合はnullを返します。
344 *
345 * @return メールの差出人アドレス
346 */
347 public InternetAddress getFrom() {
348 return from;
349 }
350
351 /***
352 * Return-Pathアドレスをセットします。
353 *
354 * @param address Return-Pathアドレス
355 */
356 public void setReturnPath(InternetAddress address) {
357 returnPath = address;
358 }
359
360 /***
361 * Return-Pathアドレスをセットします。
362 *
363 * @param email Return-Pathアドレス
364 * @throws IllegalArgumentException 不正なフォーマットのアドレスが指定された場合
365 */
366 public void setReturnPath(String email) throws IllegalArgumentException {
367 try {
368 setReturnPath(new InternetAddress(email));
369 } catch (AddressException e) {
370 throw new IllegalArgumentException(e.getMessage());
371 }
372 }
373
374 /***
375 * Return-Pathアドレスを返します。
376 *
377 * @return Return-Pathアドレス
378 */
379 public InternetAddress getReturnPath() {
380 return returnPath;
381 }
382
383 /***
384 * 返信先アドレスをセットします。
385 *
386 * @param address 返信先アドレス
387 */
388 public void setReplyTo(InternetAddress address) {
389 replyTo = address;
390 }
391
392 /***
393 * 返信先アドレスをセットします。
394 *
395 * @param email 返信先アドレス
396 * @throws IllegalArgumentException 不正なフォーマットのアドレスが指定された場合
397 */
398 public void setReplyTo(String email) throws IllegalArgumentException {
399 try {
400 setReplyTo(new InternetAddress(email));
401 } catch (AddressException e) {
402 throw new IllegalArgumentException(e.getMessage());
403 }
404 }
405
406 /***
407 * メールの返信先アドレスを返します。セットされていない場合はnullを返します。
408 *
409 * @return 返信先アドレス
410 */
411 public InternetAddress getReplyTo() {
412 return replyTo;
413 }
414
415 /***
416 * メールの件名を返します。セットされていない場合は空文字列を返します。
417 *
418 * @return メールの件名
419 */
420 public String getSubject() {
421 if (subject == null) {
422 return "";
423 }
424 return subject;
425 }
426
427 /***
428 * メールの件名をセットします。
429 *
430 * @param subject メールの件名
431 */
432 public void setSubject(String subject) {
433 this.subject = subject;
434 }
435
436 /***
437 * メール本文を返します。
438 * 本文セットされていない場合は空文字列を返します。
439 *
440 * @return メール本文
441 */
442 public String getText() {
443 if (text == null) {
444 return "";
445 }
446 return text;
447 }
448
449 /***
450 * メール本文をセットします。
451 *
452 * @param text メール本文
453 */
454 public void setText(String text) {
455 this.text = text;
456 }
457
458 /***
459 * メールヘッダに任意のヘッダを追加します。
460 * 任意ヘッダは「X-key: value」のフォーマットでメールヘッダに組み込まれます。
461 *
462 * @param key 任意ヘッダ名。頭が"X-"で始まっていなければ、自動的に付与されます。
463 * @param value 任意ヘッダの値
464 */
465 public void addXHeader(String key, String value) {
466 if (xHeaders == null) {
467 xHeaders = new HashMap();
468 }
469 if (key.startsWith("X-")) {
470 xHeaders.put(key, value);
471 } else {
472 xHeaders.put("X-" + key, value);
473 }
474 }
475
476 /***
477 * メールの任意ヘッダ名と値のMapインスタンスを返します。
478 * 任意ヘッダが一件もセットされていないときはnullを返します。
479 * <p>
480 * このMapインスタンスへの修正はできません。(unmodifiableMapになっています。)
481 *
482 * @return メールの任意ヘッダ名と値のMapインスタンス。またはnull。
483 */
484 public Map getXHeaders() {
485 if (xHeaders == null) {
486 return null;
487 }
488 return Collections.unmodifiableMap(xHeaders);
489 }
490
491 /***
492 * メール内容を出力します。<br>
493 * メールのソースに似たフォーマットで出力されます。
494 *
495 * @see java.lang.Object#toString()
496 */
497 public String toString() {
498 StringBuffer buf = new StringBuffer(1000);
499 buf.append("Mail\n");
500 buf.append("Return-Path: ").append(returnPath).append("\n");
501 buf.append("From: ").append(from != null ? from.toUnicodeString() : null).append("\n");
502 buf.append("To: ").append(arrayToCommaDelimitedString(to)).append("\n");
503 buf.append("Cc: ").append(arrayToCommaDelimitedString(cc)).append("\n");
504 buf.append("Bcc: ").append(arrayToCommaDelimitedString(bcc)).append("\n");
505 buf.append("Reply-To: ").append(replyTo != null ? replyTo.toUnicodeString() : null).append(
506 "\n");
507 buf.append("Subject: ").append(subject).append("\n");
508
509 if (xHeaders != null) {
510 for (Iterator itr = xHeaders.keySet().iterator(); itr.hasNext();) {
511 String header = (String)itr.next();
512 String value = (String)xHeaders.get(header);
513 buf.append(header).append(": ").append(value).append("\n");
514 }
515 }
516
517 buf.append("\n");
518 buf.append(text);
519
520 if (htmlText != null) {
521 buf.append("\n\n-----\n\n");
522 buf.append(htmlText);
523 }
524
525 return buf.toString();
526 }
527
528 /***
529 * @param list
530 * @return
531 */
532 private String arrayToCommaDelimitedString(List list) {
533 if (list == null) {
534 return "null";
535 } else {
536 StringBuffer sb = new StringBuffer();
537 for (int i = 0, num = list.size(); i < num; i++) {
538 if (i > 0) {
539 sb.append(", ");
540 }
541 sb.append(((InternetAddress)list.get(i)).toUnicodeString());
542 }
543 return sb.toString();
544 }
545 }
546
547 /***
548 * セットされている送信先アドレス(Toアドレス)を全てクリアします。
549 *
550 * @since 1.0.2
551 */
552 public void clearTo() {
553 to = null;
554 }
555
556 /***
557 * セットされているCCアドレスを全てクリアします。
558 *
559 * @since 1.0.2
560 */
561 public void clearCc() {
562 cc = null;
563 }
564
565 /***
566 * セットされているBCCアドレスを全てクリアします。
567 *
568 * @since 1.0.2
569 */
570 public void clearBcc() {
571 bcc = null;
572 }
573
574 /***
575 * HTMLの本文をセットします。
576 *
577 * @since 1.1
578 *
579 * @param htmlText HTMLの本文
580 */
581 public void setHtmlText(String htmlText) {
582 this.htmlText = htmlText;
583 }
584
585 /***
586 * HTMLの本文を返します。
587 *
588 * @since 1.1
589 *
590 * @return HTMLの本文。またはnull。
591 */
592 public String getHtmlText() {
593 return htmlText;
594 }
595
596 /***
597 * 指定されたファイルを添付します。
598 * 添付ファイル名には、指定されたファイルの名前が使用されます。
599 * このファイルの名前は適切な拡張子が付けられている必要があります。
600 *
601 * @since 1.1
602 *
603 * @param file 添付ファイル
604 */
605 public void addFile(File file) {
606 if (attachmentFiles == null) {
607 initAttachmentFiles();
608 }
609 addFile(file, file.getName());
610 }
611
612 /***
613 * 指定されたファイルを添付します。
614 * 指定するファイル名には適切な拡張子が付けられている必要があります。
615 *
616 * @since 1.1
617 *
618 * @param file 添付ファイル
619 * @param fileName ファイル名
620 */
621 public void addFile(File file, String fileName) {
622 if (attachmentFiles == null) {
623 initAttachmentFiles();
624 }
625 attachmentFiles.add(new AttachmentFile(fileName, file));
626 }
627
628 /***
629 * 指定されたURLのファイルを添付します。
630 * 指定するファイル名には適切な拡張子が付けられている必要があります。
631 *
632 * @since 1.1
633 *
634 * @param url 添付ファイル
635 * @param fileName ファイル名
636 */
637 public void addFile(URL url, String fileName) {
638 if (attachmentFiles == null) {
639 initAttachmentFiles();
640 }
641 attachmentFiles.add(new AttachmentFile(fileName, url));
642 }
643
644 /***
645 * 指定されたInputStreamをファイルとして添付します。
646 * 指定するファイル名には適切な拡張子が付けられている必要があります。
647 *
648 * @since 1.1
649 *
650 * @param is 添付ファイルを生成するInputStream
651 * @param fileName ファイル名
652 */
653 public void addFile(InputStream is, String fileName) {
654 if (attachmentFiles == null) {
655 initAttachmentFiles();
656 }
657 attachmentFiles.add(new AttachmentFile(fileName, is));
658 }
659
660 /***
661 * 指定されたbyte配列をファイルとして添付します。
662 * 指定するファイル名には適切な拡張子が付けられている必要があります。
663 *
664 * @since 1.1.7
665 *
666 * @param bytes 添付ファイルを生成するbyte配列
667 * @param fileName ファイル名
668 */
669 public void addFile(byte[] bytes, String fileName) {
670 if (attachmentFiles == null) {
671 initAttachmentFiles();
672 }
673 attachmentFiles.add(new AttachmentFile(fileName, bytes));
674 }
675
676 /***
677 * attachmentFilesプロパティを初期化。
678 */
679 private void initAttachmentFiles() {
680 attachmentFiles = new ArrayList();
681 }
682
683 /***
684 * 添付ファイルの配列を返します。
685 * 添付ファイルがセットされていない場合は、空の配列を返します。
686 *
687 * @since 1.1
688 *
689 * @return 添付ファイルの配列。または空の配列。
690 */
691 public AttachmentFile[] getAttachmentFiles() {
692 if (attachmentFiles == null) {
693 return new AttachmentFile[0];
694 }
695 return (AttachmentFile[])attachmentFiles
696 .toArray(new AttachmentFile[attachmentFiles.size()]);
697 }
698
699 /***
700 * HTMLの本文がセットされているかどうか判定します。
701 *
702 * @since 1.1
703 *
704 * @return HTMLの本文がセットされている場合 true
705 */
706 public boolean isHtmlMail() {
707 return (htmlText != null);
708 }
709
710 /***
711 * ファイルが添付されているかどうか判定します。
712 *
713 * @since 1.1
714 *
715 * @return ファイルが添付されている場合 true
716 */
717 public boolean isFileAttached() {
718 return attachmentFiles != null && attachmentFiles.size() > 0;
719 }
720
721 /***
722 * マルチパート・メールかどうか判定します。<br>
723 * HTML本文がセットされているか、ファイルが添付されている場合に true が返されます。
724 * <p>
725 * 注: ここで判定されるマルチパートは、厳密な意味でのマルチパートではありません。
726 *
727 * @since 1.1
728 *
729 * @return マルチパート・メールの場合 true
730 */
731 public boolean isMultipartMail() {
732 return isHtmlMail() || isFileAttached();
733 }
734
735 /***
736 * セットされている添付ファイルを全てクリアします。
737 *
738 * @since 1.1
739 */
740 public void clearFile() {
741 initAttachmentFiles();
742 }
743
744 /***
745 * 添付ファイル。
746 *
747 * @since 1.1
748 * @author Tomohiro Otsuka
749 * @version $Id: Mail.java,v 1.11 2005/01/17 09:08:16 otsuka Exp $
750 */
751 public class AttachmentFile {
752
753 private String name;
754
755 private File file;
756
757 private InputStream is;
758
759 private URL url;
760
761 private byte[] bytes = null;
762
763 /***
764 * ファイル名とファイルを指定して、このクラスのインタンスを生成します。
765 * ファイル名には適切な拡張子が付けられている必要があります。
766 *
767 * @param name メールに表示するファイル名
768 * @param file 添付ファイル
769 */
770 public AttachmentFile(String name, File file) {
771 this.name = name;
772 this.file = file;
773 }
774
775 /***
776 * ファイル名とInputStreamを指定して、このクラスのインタンスを生成します。
777 * ファイル名には適切な拡張子が付けられている必要があります。
778 *
779 * @param name メールに表示するファイル名
780 * @param is 添付ファイルを生成するInputStream
781 */
782 public AttachmentFile(String name, InputStream is) {
783 this.name = name;
784 this.is = is;
785 }
786
787 /***
788 * ファイル名とファイルロケーションのURLを指定して、このクラスのインタンスを生成します。
789 * ファイル名には適切な拡張子が付けられている必要があります。
790 *
791 * @param name メールに表示するファイル名
792 * @param url 添付ファイルのロケーションURL
793 */
794 public AttachmentFile(String name, URL url) {
795 this.name = name;
796 this.url = url;
797 }
798
799 /***
800 * ファイル名とbyte配列を指定して、このクラスのインタンスを生成します。
801 * ファイル名には適切な拡張子が付けられている必要があります。
802 *
803 * @param name メールに表示するファイル名
804 * @param bytes 添付ファイルを生成するbyte配列
805 */
806 public AttachmentFile(String name, byte[] bytes) {
807 this.name = name;
808 this.bytes = bytes;
809 }
810
811 /***
812 * 添付ファイルのDataSourceインスタンスを生成して返します。
813 *
814 * @return 添付ファイルのDataSourceインスタンス
815 */
816 public DataSource getDataSource() {
817 if (file != null) {
818 return new FileDataSource(file);
819 }
820
821 if (url != null) {
822 return new URLDataSource(url);
823 }
824
825
826 String contentType = FileTypeMap.getDefaultFileTypeMap().getContentType(name);
827 if (is != null) {
828
829 return new ByteArrayDataSource(is, contentType);
830 } else {
831
832 return new ByteArrayDataSource(bytes, contentType);
833 }
834 }
835
836 /***
837 * 添付ファイル名を返します。
838 *
839 * @return 添付ファイル名
840 */
841 public String getName() {
842 return name;
843 }
844
845 /***
846 * @return セットされたファイル。またはnull。
847 */
848 public File getFile() {
849 return file;
850 }
851
852 /***
853 * @return セットされたInputStream。またはnull。
854 */
855 public InputStream getInputStream() {
856 return is;
857 }
858
859 /***
860 * @return セットされたURL。またはnull。
861 */
862 public URL getUrl() {
863 return url;
864 }
865
866 /***
867 * @return セットされたbyte配列。またはnull。
868 */
869 public byte[] getBytes() {
870 return bytes;
871 }
872 }
873
874 /***
875 * メールの重要度。定数のみを定義。
876 *
877 * @author Tomohiro Otsuka
878 * @version $Id: Mail.java,v 1.11 2005/01/17 09:08:16 otsuka Exp $
879 */
880 public static class Importance {
881
882 /*** 重要度「高」 */
883 public static final String HIGH = "high";
884
885 /*** 重要度「中」 */
886 public static final String NORMAL = "normal";
887
888 /*** 重要度「低」 */
889 public static final String LOW = "low";
890
891 }
892 }