--- /dev/null
+<classpath>
+ <classpathentry kind="src" path="src/java"/>
+ <classpathentry kind="src" path="src/test" output="target/test-classes"/>
+ <classpathentry kind="output" path="target/classes"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+ <classpathentry kind="var" path="M2_REPO/org/springframework/spring/1.1.1/spring-1.1.1.jar"/>
+ <classpathentry kind="var" path="M2_REPO/log4j/log4j/1.2.8/log4j-1.2.8.jar"/>
+ <classpathentry kind="var" path="M2_REPO/jdom/jdom/1.0/jdom-1.0.jar"/>
+ <classpathentry kind="var" path="M2_REPO/velocity/velocity/1.4/velocity-1.4.jar"/>
+ <classpathentry kind="var" path="M2_REPO/velocity/velocity-dep/1.4/velocity-dep-1.4.jar"/>
+ <classpathentry kind="var" path="M2_REPO/quartz/quartz/1.4.0/quartz-1.4.0.jar"/>
+ <classpathentry kind="var" path="M2_REPO/javax/activation/activation/1.1/activation-1.1.jar"/>
+ <classpathentry kind="var" path="M2_REPO/commons-collections/commons-collections/2.1/commons-collections-2.1.jar"/>
+ <classpathentry kind="var" path="M2_REPO/commons-logging/commons-logging/1.0.4/commons-logging-1.0.4.jar"/>
+ <classpathentry kind="var" path="M2_REPO/junit/junit/3.8.1/junit-3.8.1.jar"/>
+ <classpathentry kind="var" path="M2_REPO/javax/mail/mail/1.4/mail-1.4.jar"/>
+</classpath>
\ No newline at end of file
--- /dev/null
+target\r
+.classpath\r
+.project\r
+velocity.log\r
+build.properties\r
+build.xml\r
+project.xml.md5\r
--- /dev/null
+<projectDescription>
+ <name>ozacc-mail</name>
+ <comment/>
+ <projects/>
+ <buildSpec>
+ <buildCommand>
+ <name>org.maven.ide.eclipse.maven2Builder</name>
+ <arguments/>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments/>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ <nature>org.maven.ide.eclipse.maven2Nature</nature>
+ </natures>
+</projectDescription>
\ No newline at end of file
--- /dev/null
+#Tue Jan 08 16:13:34 JST 2008\r
+eclipse.preferences.version=1\r
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.4\r
+org.eclipse.jdt.core.compiler.compliance=1.4\r
+org.eclipse.jdt.core.compiler.source=1.4\r
--- /dev/null
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>ozacc-mail</groupId>
+ <artifactId>ozacc-mail</artifactId>
+ <packaging>jar</packaging>
+ <version>1.2.2</version>
+ <name>ozacc-mail library</name>
+ <url>http://spring-ext.sourceforge.jp/oml/</url>
+ <dependencies>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>3.8.1</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>javax.mail</groupId>
+ <artifactId>mail</artifactId>
+ <version>1.4</version>
+ </dependency>
+ <dependency>
+ <groupId>javax.activation</groupId>
+ <artifactId>activation</artifactId>
+ <version>1.1</version>
+ </dependency>
+ <dependency>
+ <groupId>commons-logging</groupId>
+ <artifactId>commons-logging</artifactId>
+ <version>1.0.4</version>
+ </dependency>
+ <dependency>
+ <groupId>log4j</groupId>
+ <artifactId>log4j</artifactId>
+ <version>1.2.8</version>
+ </dependency>
+ <dependency>
+ <groupId>jdom</groupId>
+ <artifactId>jdom</artifactId>
+ <version>1.0</version>
+ </dependency>
+ <dependency>
+ <groupId>velocity</groupId>
+ <artifactId>velocity</artifactId>
+ <version>1.4</version>
+ </dependency>
+ <dependency>
+ <groupId>commons-collections</groupId>
+ <artifactId>commons-collections</artifactId>
+ <version>2.1</version>
+ </dependency>
+ <dependency>
+ <groupId>quartz</groupId>
+ <artifactId>quartz</artifactId>
+ <version>1.4.0</version>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring</artifactId>
+ <version>2.5</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <directory>target</directory>
+ <outputDirectory>target/classes</outputDirectory>
+ <finalName>${pom.artifactId}-${pom.version}</finalName>
+ <testOutputDirectory>target/test-classes</testOutputDirectory>
+ <sourceDirectory>src/java</sourceDirectory>
+ <testSourceDirectory>src/test</testSourceDirectory>
+ <resources>
+ <resource>
+ <directory>src/java</directory>
+ <includes>
+ <include>**/*.dtd</include>
+ </includes>
+ <filtering>false</filtering>
+ </resource>
+ </resources>
+ <testResources>
+ <testResource>
+ <directory>src/test</directory>
+ </testResource>
+ </testResources>
+
+ <pluginManagement>
+ <plugins>
+ <plugin>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <source>1.4</source>
+ <target>1.4</target>
+ <encoding>UTF-8</encoding>
+ </configuration>
+ </plugin>
+
+ </plugins>
+ </pluginManagement>
+
+ </build>
+</project>
--- /dev/null
+# Project property file for ozacc-mail\r
+\r
+maven.compile.encoding=EUC-JP\r
+maven.compile.source=1.4\r
+maven.compile.target=1.4\r
+\r
+maven.docs.locale=ja\r
+maven.docs.outputencoding=EUC-JP\r
+\r
+maven.javadoc.links=http://java.sun.com/j2se/1.4/ja/docs/ja/api/, http://jakarta.apache.org/velocity/api/, http://java.sun.com/products/javamail/1.3/docs/javadocs/, http://www.jdom.org/docs/apidocs/, http://www.springframework.org/docs/api/\r
+maven.javadoc.additionalparam=-charset EUC-JP -encoding EUC-JP -docencoding EUC-JP\r
+\r
+maven.xdoc.date=left\r
+maven.xdoc.version=${pom.currentVersion}\r
+\r
+maven.junit.sysproperties = javax.xml.transform.TransformerFactory\r
+javax.xml.transform.TransformerFactory = org.apache.xalan.processor.TransformerFactoryImpl
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>\r
+<project>\r
+ <pomVersion>3</pomVersion>\r
+ <artifactId>ozacc-mail</artifactId>\r
+ <name>ozacc-mail library</name>\r
+ <groupId>ozacc-mail</groupId>\r
+ <currentVersion>1.2.1</currentVersion>\r
+ <organization>\r
+ <name>OZACC</name>\r
+ <url>http://www.ozacc.com/</url>\r
+ </organization>\r
+ <inceptionYear>2004</inceptionYear>\r
+ <package>com.ozacc.mail</package>\r
+ <shortDescription>メール送受信ライブラリ。</shortDescription>\r
+ <url>http://spring-ext.sourceforge.jp/oml/</url>\r
+ <distributionSite>http://sourceforge.jp/projects/spring-ext/</distributionSite>\r
+ <repository />\r
+ <developers>\r
+ <developer>\r
+ <name>Tomohiro Otsuka</name>\r
+ <id>otsuka</id>\r
+ <timezone>+9</timezone>\r
+ </developer>\r
+ </developers>\r
+ <licenses>\r
+ <license>\r
+ <name>GNU Lesser General Public License (LGPL)</name>\r
+ <url>http://www.opensource.org/licenses/lgpl-license.php</url>\r
+ <distribution>repo</distribution>\r
+ </license>\r
+ </licenses>\r
+ <dependencies>\r
+ <dependency>\r
+ <groupId>javax.mail</groupId>\r
+ <artifactId>mail</artifactId>\r
+ <version>1.3.2</version>\r
+ <type>jar</type>\r
+ <url>http://java.sun.com/products/javamail/</url>\r
+ </dependency>\r
+ <dependency>\r
+ <groupId>jaf</groupId>\r
+ <artifactId>jaf</artifactId>\r
+ <version>1.0.2</version>\r
+ <type>jar</type>\r
+ <url>http://java.sun.com/products/javabeans/jaf/</url>\r
+ </dependency>\r
+ <dependency>\r
+ <groupId>commons-logging</groupId>\r
+ <artifactId>commons-logging</artifactId>\r
+ <version>1.0.4</version>\r
+ <type>jar</type>\r
+ <url>http://jakarta.apache.org/commons/logging/</url>\r
+ </dependency>\r
+ <dependency>\r
+ <groupId>log4j</groupId>\r
+ <artifactId>log4j</artifactId>\r
+ <version>1.2.8</version>\r
+ <type>jar</type>\r
+ <url>http://logging.apache.org/log4j/docs/</url>\r
+ </dependency>\r
+ <dependency>\r
+ <groupId>jdom</groupId>\r
+ <artifactId>jdom</artifactId>\r
+ <version>1.0</version>\r
+ <type>jar</type>\r
+ <url>http://www.jdom.org/</url>\r
+ </dependency>\r
+ <dependency>\r
+ <groupId>velocity</groupId>\r
+ <artifactId>velocity</artifactId>\r
+ <version>1.4</version>\r
+ <type>jar</type>\r
+ <url>http://jakarta.apache.org/velocity/</url>\r
+ </dependency>\r
+ <dependency>\r
+ <groupId>commons-collections</groupId>\r
+ <artifactId>commons-collections</artifactId>\r
+ <version>2.1</version>\r
+ <type>jar</type>\r
+ <url>http://jakarta.apache.org/commons/collections/</url>\r
+ </dependency>\r
+ <dependency>\r
+ <groupId>quartz</groupId>\r
+ <artifactId>quartz</artifactId>\r
+ <version>1.4.0</version>\r
+ <type>jar</type>\r
+ </dependency>\r
+ <dependency>\r
+ <groupId>springframework</groupId>\r
+ <artifactId>spring</artifactId>\r
+ <version>1.1.1</version>\r
+ <type>jar</type>\r
+ <url>http://www.springframework.org/</url>\r
+ </dependency>\r
+ </dependencies>\r
+ <build>\r
+ <nagEmailAddress>info@ozacc.com</nagEmailAddress>\r
+ <sourceDirectory>src/java</sourceDirectory>\r
+ <unitTestSourceDirectory>src/test</unitTestSourceDirectory>\r
+ <unitTest>\r
+ <includes>\r
+ <include>**/*Test.java</include>\r
+ </includes>\r
+ <resources>\r
+ <resource>\r
+ <directory>src/test</directory>\r
+ <includes>\r
+ <include>**/*.xml</include>\r
+ </includes>\r
+ <filtering>false</filtering>\r
+ </resource>\r
+ </resources>\r
+ </unitTest>\r
+ <resources>\r
+ <resource>\r
+ <directory>src/java</directory>\r
+ <includes>\r
+ <include>**/*.dtd</include>\r
+ </includes>\r
+ <filtering>false</filtering>\r
+ </resource>\r
+ </resources>\r
+ </build>\r
+ <reports>\r
+ <report>maven-javadoc-plugin</report>\r
+ <report>maven-jxr-plugin</report>\r
+ </reports>\r
+ <properties />\r
+</project>\r
+\r
--- /dev/null
+package com.ozacc.mail.util;\r
+\r
+import javax.mail.internet.AddressException;\r
+import javax.mail.internet.InternetAddress;\r
+import javax.naming.NamingEnumeration;\r
+import javax.naming.NamingException;\r
+import javax.naming.directory.Attributes;\r
+import javax.naming.directory.DirContext;\r
+import javax.naming.directory.InitialDirContext;\r
+\r
+/**\r
+ * DNS ¤Ë MX ¥ì¥³¡¼¥É¤¬ÅÐÏ¿¤µ¤ì¤Æ¤¤¤ë¤«¤ò¥Á¥§¥Ã¥¯¤¹¤ë¥¯¥é¥¹¡£\r
+ * Sun JDK 1.4 °Ê¾å¤Ç»ÈÍѤǤ¤Þ¤¹¡£\r
+ * <p>\r
+ * TODO: ̤´°À®¡£Àµ¤·¤¯Æ°ºî¤·¤Þ¤»¤ó¡£\r
+ * \r
+ * @since 1.1\r
+ * @version $Id: MXCheck.java,v 1.1 2004/09/14 22:27:57 otsuka Exp $\r
+ * @author Tomohiro Otsuka\r
+ */\r
+public class MXCheck {\r
+\r
+ public static boolean isValidEmailAddress(String email, String dns) {\r
+ try {\r
+ new InternetAddress(email, true);\r
+ } catch (AddressException e) {\r
+ return false;\r
+ }\r
+\r
+ int pos = email.lastIndexOf("@");\r
+ String hostName = email.substring(pos + 1);\r
+ return hasMXRecord(hostName, dns);\r
+ }\r
+\r
+ /**\r
+ * »ØÄꤵ¤ì¤¿ host ¤Ë MX ¥ì¥³¡¼¥É¤¬´ØÏ¢ÉÕ¤±¤é¤ì¤Æ¤¤¤ë¤«\r
+ * ¤É¤¦¤«È½Äꤷ¤Þ¤¹¡£\r
+ * \r
+ * @param hostName ¸¡ºº¤¹¤ë¥Û¥¹¥È̾\r
+ * @param dns ¸¡ºº¤Ë»ÈÍѤ¹¤ë DNS ¥µ¡¼¥Ð\r
+ * @return MX ¥ì¥³¡¼¥É¤¬Â¸ºß¤¹¤ì¤Ð true\r
+ */\r
+ public static boolean hasMXRecord(String hostName, String dns) {\r
+ String name = "dns://" + dns + "/" + hostName;\r
+ try {\r
+ DirContext ictx = new InitialDirContext();\r
+ Attributes attrs = ictx.getAttributes(name, new String[] { "MX" });\r
+ NamingEnumeration namingEnumeration = attrs.getAll();\r
+ if (namingEnumeration.hasMore()) {\r
+ return true;\r
+ }\r
+ } catch (NamingException e) {\r
+ return false;\r
+ }\r
+\r
+ return false;\r
+ }\r
+\r
+ /**\r
+ * »ØÄꤵ¤ì¤¿ host ¤Ë MX ¥ì¥³¡¼¥É¤¬´ØÏ¢ÉÕ¤±¤é¤ì¤Æ¤¤¤ë¤«¤É¤¦¤«È½Äꤷ¤Þ¤¹¡£\r
+ * ¸¡ºº¤Ë»ÈÍѤ¹¤ë DNS ¥µ¡¼¥Ð¤Ï localhost ¤Ç¤¹¡£\r
+ * \r
+ * @param hostName ¸¡ºº¤¹¤ë¥Û¥¹¥È̾\r
+ * @return MX ¥ì¥³¡¼¥É¤¬Â¸ºß¤¹¤ì¤Ð true\r
+ */\r
+ public static boolean hasMXRecord(String hostName) {\r
+ return hasMXRecord(hostName, "localhost");\r
+ }\r
+\r
+ /**\r
+ * ¼¡¤Î¥³¥Þ¥ó¥É¤Ç¡¢¥Û¥¹¥È̾¤Ë MX ¥ì¥³¡¼¥É¤¬Â¸ºß¤¹¤ë¤«¤É¤¦¤«Ä´¤Ù¤Þ¤¹¡£\r
+ * <p>\r
+ * <code>java com.ozacc.mail.util.MXChecker [¥Û¥¹¥È̾] [»ÈÍѤ¹¤ëDNS¥µ¡¼¥Ð]</code>\r
+ * \r
+ */\r
+ public static void main(String[] args) {\r
+ boolean result = false;\r
+ if (args.length == 1) {\r
+ result = hasMXRecord(args[0]);\r
+ } else {\r
+ result = hasMXRecord(args[0], args[1]);\r
+ }\r
+\r
+ if (result)\r
+ System.out.println(args[0] + " ¤Î MX ¥ì¥³¡¼¥É¤Ï¸ºß¤·¤Þ¤¹¡£");\r
+ else\r
+ System.out.println(args[0] + " ¤Î MX ¥ì¥³¡¼¥É¤ÏÅÐÏ¿¤µ¤ì¤Æ¤¤¤Þ¤»¤ó¡£");\r
+ }\r
+}
\ No newline at end of file
--- /dev/null
+package com.ozacc.mail;\r
+\r
+import java.io.File;\r
+import java.io.InputStream;\r
+import java.io.UnsupportedEncodingException;\r
+import java.net.URL;\r
+import java.util.ArrayList;\r
+import java.util.Collections;\r
+import java.util.HashMap;\r
+import java.util.Iterator;\r
+import java.util.List;\r
+import java.util.Map;\r
+\r
+import javax.activation.DataSource;\r
+import javax.activation.FileDataSource;\r
+import javax.activation.FileTypeMap;\r
+import javax.activation.URLDataSource;\r
+import javax.mail.internet.AddressException;\r
+import javax.mail.internet.InternetAddress;\r
+\r
+import com.ozacc.mail.impl.ByteArrayDataSource;\r
+\r
+/**\r
+ * メール。\r
+ * \r
+ * @since 1.0\r
+ * @author Tomohiro Otsuka\r
+ * @version $Id: Mail.java,v 1.10.2.9 2007/03/30 13:03:44 otsuka Exp $\r
+ */\r
+public class Mail {\r
+\r
+ /** <code>ISO-2022-JP</code> */\r
+ public static final String JIS_CHARSET = "ISO-2022-JP";\r
+\r
+ public static final String DOCTYPE_PUBLIC = "-//OZACC//DTD MAIL//EN";\r
+\r
+ public static final String DOCTYPE_SYSTEM = "http://www.ozacc.com/library/dtd/ozacc-mail.dtd";\r
+\r
+ public static final String DOCTYPE_PUBLIC_MULTIPLE = "-//OZACC//DTD MULTIPLE MAILS//EN";\r
+\r
+ public static final String DOCTYPE_SYSTEM_MULTIPLE = "http://www.ozacc.com/library/dtd/ozacc-multiple-mails.dtd";\r
+\r
+ private String charset = JIS_CHARSET;\r
+\r
+ protected String text;\r
+\r
+ protected InternetAddress from;\r
+\r
+ protected String subject;\r
+\r
+ protected List to;\r
+\r
+ protected List cc;\r
+\r
+ protected List bcc;\r
+\r
+ protected List envelopeTo;\r
+\r
+ protected InternetAddress returnPath;\r
+\r
+ protected InternetAddress replyTo;\r
+\r
+ protected String importance;\r
+\r
+ protected Map headers = new HashMap();\r
+\r
+ protected String htmlText;\r
+\r
+ protected List attachmentFiles;\r
+\r
+ /**\r
+ * コンストラクタ。\r
+ */\r
+ public Mail() {}\r
+\r
+ /**\r
+ * コンストラクタ。\r
+ * 宛先や差出人の名前をエンコードする時に使用する文字コードを指定します。\r
+ * <p>\r
+ * 日本語環境で利用する場合は通常設定する必要はありません。\r
+ * \r
+ * @param charset エンコードに使用する文字コード\r
+ */\r
+ public Mail(String charset) {\r
+ this();\r
+ this.charset = charset;\r
+ }\r
+\r
+ /**\r
+ * コピーコンストラクタ。\r
+ * シャローコピー(shallow copy)です。\r
+ * \r
+ * @since 1.0.2\r
+ * \r
+ * @param original コピー元のMailインスタンス\r
+ */\r
+ public Mail(Mail original) {\r
+ this.bcc = original.bcc;\r
+ this.cc = original.cc;\r
+ this.charset = original.charset;\r
+ this.from = original.from;\r
+ this.importance = original.importance;\r
+ this.replyTo = original.replyTo;\r
+ this.returnPath = original.returnPath;\r
+ this.subject = original.subject;\r
+ this.text = original.text;\r
+ this.to = original.to;\r
+ this.headers = original.headers;\r
+ this.htmlText = original.htmlText;\r
+ this.attachmentFiles = original.attachmentFiles;\r
+ this.envelopeTo = original.envelopeTo;\r
+ }\r
+\r
+ /**\r
+ * エンコードに使用する文字コードを返します。コンストラクタで設定されなかった場合はnullを返します。\r
+ * \r
+ * @return エンコードに使用する文字コード、またはnull\r
+ */\r
+ public String getCharset() {\r
+ return charset;\r
+ }\r
+\r
+ /**\r
+ * メールの重要度をセットします。\r
+ * 引数で指定可能な値は「high」、「normal」、「low」のいずれかです。\r
+ * \r
+ * @param importance メールの重要度。「high」、「normal」、「low」のいずれか。\r
+ * @throws IllegalArgumentException 指定可能な値以外が指定された場合\r
+ * \r
+ * @see Mail.Importance\r
+ */\r
+ public void setImportance(String importance) throws IllegalArgumentException {\r
+ if ("high".equals(importance) || "normal".equals(importance) || "low".equals(importance)) {\r
+ this.importance = importance;\r
+ } else {\r
+ throw new IllegalArgumentException("'" + importance + "'は、メール重要度には指定できない値です。");\r
+ }\r
+ }\r
+\r
+ /**\r
+ * メールの重要度を返します。\r
+ * 値は「high」、「normal」、「low」のいずれかです。\r
+ * \r
+ * @return メールの重要度。「high」、「normal」、「low」のいずれか。\r
+ */\r
+ public String getImportance() {\r
+ return importance;\r
+ }\r
+\r
+ /**\r
+ * メールの送信先アドレスを追加します。\r
+ * \r
+ * @param address 送信先アドレス\r
+ */\r
+ public void addTo(InternetAddress address) {\r
+ if (to == null) {\r
+ to = new ArrayList();\r
+ }\r
+ to.add(address);\r
+ }\r
+\r
+ /**\r
+ * メールの送信先アドレスを追加します。\r
+ * \r
+ * @param email 送信先アドレス\r
+ * @throws IllegalArgumentException 不正なフォーマットのアドレスが指定された場合\r
+ */\r
+ public void addTo(String email) throws IllegalArgumentException {\r
+ try {\r
+ addTo(new InternetAddress(email));\r
+ } catch (AddressException e) {\r
+ throw new IllegalArgumentException(e.getMessage());\r
+ }\r
+ }\r
+\r
+ /**\r
+ * メールの送信先名とアドレスを追加します。\r
+ * \r
+ * @param email 送信先アドレス\r
+ * @param name 送信先名\r
+ * @throws IllegalArgumentException 不正なフォーマットのアドレスが指定された場合\r
+ */\r
+ public void addTo(String email, String name) throws IllegalArgumentException {\r
+ try {\r
+ addTo(new InternetAddress(email, name, charset));\r
+ } catch (UnsupportedEncodingException e) {\r
+ throw new IllegalArgumentException(e.getMessage());\r
+ }\r
+ }\r
+\r
+ /**\r
+ * メールの送信先アドレスの配列を返します。\r
+ * 送信先アドレスが一件もセットされていないときは空の配列を返します。\r
+ * \r
+ * @return 送信先アドレスの配列\r
+ */\r
+ public InternetAddress[] getTo() {\r
+ if (to == null) {\r
+ return new InternetAddress[0];\r
+ }\r
+ return (InternetAddress[])to.toArray(new InternetAddress[to.size()]);\r
+ }\r
+\r
+ /**\r
+ * CCアドレスを追加します。\r
+ * \r
+ * @param address CCのアドレス\r
+ */\r
+ public void addCc(InternetAddress address) {\r
+ if (cc == null) {\r
+ cc = new ArrayList();\r
+ }\r
+ cc.add(address);\r
+ }\r
+\r
+ /**\r
+ * CCアドレスを追加します。\r
+ * \r
+ * @param email CCのアドレス\r
+ * @throws IllegalArgumentException 不正なフォーマットのアドレスが指定された場合\r
+ */\r
+ public void addCc(String email) throws IllegalArgumentException {\r
+ try {\r
+ addCc(new InternetAddress(email));\r
+ } catch (AddressException e) {\r
+ throw new IllegalArgumentException(e.getMessage());\r
+ }\r
+ }\r
+\r
+ /**\r
+ * CCの宛名とアドレスを追加します。\r
+ * \r
+ * @param email CCのアドレス\r
+ * @param name CCの宛名\r
+ * @throws IllegalArgumentException 不正なフォーマットのアドレスが指定された場合\r
+ */\r
+ public void addCc(String email, String name) throws IllegalArgumentException {\r
+ try {\r
+ addCc(new InternetAddress(email, name, charset));\r
+ } catch (UnsupportedEncodingException e) {\r
+ throw new IllegalArgumentException(e.getMessage());\r
+ }\r
+ }\r
+\r
+ /**\r
+ * メールのCCアドレス配列を返します。\r
+ * CCアドレスが一件もセットされていないときは空の配列を返します。\r
+ * \r
+ * @return CCアドレスの配列\r
+ */\r
+ public InternetAddress[] getCc() {\r
+ if (cc == null) {\r
+ return new InternetAddress[0];\r
+ }\r
+ return (InternetAddress[])cc.toArray(new InternetAddress[cc.size()]);\r
+ }\r
+\r
+ /**\r
+ * BCCアドレスを追加します。\r
+ * \r
+ * @param address BCCのアドレス\r
+ */\r
+ public void addBcc(InternetAddress address) {\r
+ if (bcc == null) {\r
+ bcc = new ArrayList();\r
+ }\r
+ bcc.add(address);\r
+ }\r
+\r
+ /**\r
+ * BCCアドレスを追加します。\r
+ * \r
+ * @param email BCCのアドレス\r
+ * @throws IllegalArgumentException 不正なフォーマットのアドレスが指定された場合\r
+ */\r
+ public void addBcc(String email) throws IllegalArgumentException {\r
+ try {\r
+ addBcc(new InternetAddress(email));\r
+ } catch (AddressException e) {\r
+ throw new IllegalArgumentException(e.getMessage());\r
+ }\r
+ }\r
+\r
+ /**\r
+ * メールのBCCアドレスの配列を返します。\r
+ * BCCアドレスが一件もセットされていないときは空の配列を返します。\r
+ * \r
+ * @return BCCアドレスの配列\r
+ */\r
+ public InternetAddress[] getBcc() {\r
+ if (bcc == null) {\r
+ return new InternetAddress[0];\r
+ }\r
+ return (InternetAddress[])bcc.toArray(new InternetAddress[bcc.size()]);\r
+ }\r
+\r
+ /**\r
+ * メールの差出人アドレスをセットします。\r
+ * \r
+ * @param address 差出人アドレス\r
+ */\r
+ public void setFrom(InternetAddress address) {\r
+ from = address;\r
+ }\r
+\r
+ /**\r
+ * メールの差出人アドレスをセットします。\r
+ * \r
+ * @param email 差出人アドレス\r
+ * @throws IllegalArgumentException 不正なフォーマットのアドレスが指定された場合\r
+ */\r
+ public void setFrom(String email) throws IllegalArgumentException {\r
+ try {\r
+ setFrom(new InternetAddress(email));\r
+ } catch (AddressException e) {\r
+ throw new IllegalArgumentException(e.getMessage());\r
+ }\r
+ }\r
+\r
+ /**\r
+ * メールの差出人名とアドレスをセットします。\r
+ * \r
+ * @param email 差出人アドレス\r
+ * @param name 差出人名\r
+ * @throws IllegalArgumentException 不正なフォーマットのアドレスが指定された場合\r
+ */\r
+ public void setFrom(String email, String name) throws IllegalArgumentException {\r
+ try {\r
+ setFrom(new InternetAddress(email, name, charset));\r
+ } catch (UnsupportedEncodingException e) {\r
+ throw new IllegalArgumentException(e.getMessage());\r
+ }\r
+ }\r
+\r
+ /**\r
+ * メールの差出人アドレスを返します。セットされていない場合はnullを返します。\r
+ * \r
+ * @return メールの差出人アドレス\r
+ */\r
+ public InternetAddress getFrom() {\r
+ return from;\r
+ }\r
+\r
+ /**\r
+ * Return-Pathアドレスをセットします。\r
+ * \r
+ * @param address Return-Pathアドレス\r
+ */\r
+ public void setReturnPath(InternetAddress address) {\r
+ returnPath = address;\r
+ }\r
+\r
+ /**\r
+ * Return-Pathアドレスをセットします。\r
+ * \r
+ * @param email Return-Pathアドレス\r
+ * @throws IllegalArgumentException 不正なフォーマットのアドレスが指定された場合\r
+ */\r
+ public void setReturnPath(String email) throws IllegalArgumentException {\r
+ try {\r
+ setReturnPath(new InternetAddress(email));\r
+ } catch (AddressException e) {\r
+ throw new IllegalArgumentException(e.getMessage());\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Return-Pathアドレスを返します。\r
+ * \r
+ * @return Return-Pathアドレス\r
+ */\r
+ public InternetAddress getReturnPath() {\r
+ return returnPath;\r
+ }\r
+\r
+ /**\r
+ * 返信先アドレスをセットします。\r
+ * \r
+ * @param address 返信先アドレス\r
+ */\r
+ public void setReplyTo(InternetAddress address) {\r
+ replyTo = address;\r
+ }\r
+\r
+ /**\r
+ * 返信先アドレスをセットします。\r
+ * \r
+ * @param email 返信先アドレス\r
+ * @throws IllegalArgumentException 不正なフォーマットのアドレスが指定された場合\r
+ */\r
+ public void setReplyTo(String email) throws IllegalArgumentException {\r
+ try {\r
+ setReplyTo(new InternetAddress(email));\r
+ } catch (AddressException e) {\r
+ throw new IllegalArgumentException(e.getMessage());\r
+ }\r
+ }\r
+\r
+ /**\r
+ * メールの返信先アドレスを返します。セットされていない場合はnullを返します。\r
+ * \r
+ * @return 返信先アドレス\r
+ */\r
+ public InternetAddress getReplyTo() {\r
+ return replyTo;\r
+ }\r
+\r
+ /**\r
+ * メールの件名を返します。セットされていない場合は空文字列を返します。\r
+ * \r
+ * @return メールの件名\r
+ */\r
+ public String getSubject() {\r
+ if (subject == null) {\r
+ return "";\r
+ }\r
+ return subject;\r
+ }\r
+\r
+ /**\r
+ * メールの件名をセットします。\r
+ * \r
+ * @param subject メールの件名\r
+ */\r
+ public void setSubject(String subject) {\r
+ this.subject = subject;\r
+ }\r
+\r
+ /**\r
+ * メール本文を返します。\r
+ * 本文セットされていない場合は空文字列を返します。\r
+ * \r
+ * @return メール本文\r
+ */\r
+ public String getText() {\r
+ if (text == null) {\r
+ return "";\r
+ }\r
+ return text;\r
+ }\r
+\r
+ /**\r
+ * メール本文をセットします。\r
+ * \r
+ * @param text メール本文\r
+ */\r
+ public void setText(String text) {\r
+ this.text = text;\r
+ }\r
+\r
+ /**\r
+ * メールヘッダに任意のヘッダフィールドを追加します。\r
+ * 任意ヘッダは「X-key: value」のフォーマットでメールヘッダに組み込まれます。<br>\r
+ * 同じヘッダ名の値は上書きされます。\r
+ * \r
+ * @param name 任意ヘッダ名。頭が"X-"で始まっていなければ、自動的に付与されます。\r
+ * @param value 任意ヘッダの値\r
+ */\r
+ public void addXHeader(String name, String value) {\r
+ if (headers == null) {\r
+ headers = new HashMap();\r
+ }\r
+ if (name.startsWith("X-")) {\r
+ headers.put(name, value);\r
+ } else {\r
+ headers.put("X-" + name, value);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * メールヘッダに任意のヘッダフィールドを追加します。<br>\r
+ * <b>このメソッドはユーザが使用することを想定していません。</b>\r
+ * 使用する際は、To や From などのフィールドをセットしないように注意してください。\r
+ * <p>\r
+ * このメソッドで設定した同じヘッダ名の値は上書きされます。\r
+ * \r
+ * @since 1.2\r
+ * @param name 任意ヘッダ名\r
+ * @param value 任意ヘッダの値\r
+ */\r
+ public void addHeader(String name, String value) {\r
+ if (headers == null) {\r
+ headers = new HashMap();\r
+ }\r
+ headers.put(name, value);\r
+ }\r
+\r
+ /**\r
+ * メールの任意ヘッダ名と値のMapインスタンスを返します。\r
+ * 任意ヘッダが一件もセットされていないときはnullを返します。\r
+ * <p>\r
+ * このMapインスタンスへの修正はできません。(unmodifiableMapになっています。)\r
+ * \r
+ * @return メールの任意ヘッダ名と値のMapインスタンス。またはnull。\r
+ */\r
+ public Map getHeaders() {\r
+ if (headers == null) {\r
+ return null;\r
+ }\r
+ return Collections.unmodifiableMap(headers);\r
+ }\r
+\r
+ /**\r
+ * メール内容を出力します。<br>\r
+ * メールのソースに似たフォーマットで出力されます。\r
+ * \r
+ * @see java.lang.Object#toString()\r
+ */\r
+ public String toString() {\r
+ StringBuffer buf = new StringBuffer(1000);\r
+ buf.append("Mail\n");\r
+ buf.append("Return-Path: ").append(returnPath).append("\n");\r
+ buf.append("From: ").append(from != null ? from.toUnicodeString() : null).append("\n");\r
+ buf.append("To: ").append(arrayToCommaDelimitedString(to)).append("\n");\r
+ buf.append("Cc: ").append(arrayToCommaDelimitedString(cc)).append("\n");\r
+ buf.append("Bcc: ").append(arrayToCommaDelimitedString(bcc)).append("\n");\r
+ buf.append("Subject: ").append(subject).append("\n");\r
+\r
+ if (headers != null) {\r
+ for (Iterator itr = headers.keySet().iterator(); itr.hasNext();) {\r
+ String header = (String)itr.next();\r
+ String value = (String)headers.get(header);\r
+ buf.append(header).append(": ").append(value).append("\n");\r
+ }\r
+ }\r
+\r
+ buf.append("\n");\r
+ buf.append(text);\r
+\r
+ if (htmlText != null) {\r
+ buf.append("\n\n-----\n\n");\r
+ buf.append(htmlText);\r
+ }\r
+\r
+ return buf.toString();\r
+ }\r
+\r
+ /**\r
+ * 指定されたリストの要素をコンマ区切りの文字列に変換します。\r
+ * nullが指定された場合は「null」文字列を返します。\r
+ * \r
+ * @param list\r
+ * @return リスト要素のコンマ区切り文字列\r
+ */\r
+ protected String arrayToCommaDelimitedString(List list) {\r
+ if (list == null) {\r
+ return "null";\r
+ } else {\r
+ StringBuffer sb = new StringBuffer();\r
+ for (int i = 0, num = list.size(); i < num; i++) {\r
+ if (i > 0) {\r
+ sb.append(", ");\r
+ }\r
+ sb.append(((InternetAddress)list.get(i)).toUnicodeString());\r
+ }\r
+ return sb.toString();\r
+ }\r
+ }\r
+\r
+ /**\r
+ * セットされている送信先アドレス(Toアドレス)を全てクリアします。\r
+ *\r
+ * @since 1.0.2\r
+ */\r
+ public void clearTo() {\r
+ to = null;\r
+ }\r
+\r
+ /**\r
+ * セットされているCCアドレスを全てクリアします。\r
+ *\r
+ * @since 1.0.2\r
+ */\r
+ public void clearCc() {\r
+ cc = null;\r
+ }\r
+\r
+ /**\r
+ * セットされているBCCアドレスを全てクリアします。\r
+ *\r
+ * @since 1.0.2\r
+ */\r
+ public void clearBcc() {\r
+ bcc = null;\r
+ }\r
+\r
+ /**\r
+ * HTMLの本文をセットします。\r
+ * \r
+ * @since 1.1\r
+ * \r
+ * @param htmlText HTMLの本文\r
+ */\r
+ public void setHtmlText(String htmlText) {\r
+ this.htmlText = htmlText;\r
+ }\r
+\r
+ /**\r
+ * HTMLの本文を返します。\r
+ * \r
+ * @since 1.1\r
+ * \r
+ * @return HTMLの本文。またはnull。\r
+ */\r
+ public String getHtmlText() {\r
+ return htmlText;\r
+ }\r
+\r
+ /**\r
+ * 指定されたファイルを添付します。\r
+ * 添付ファイル名には、指定されたファイルの名前が使用されます。\r
+ * このファイルの名前は適切な拡張子が付けられている必要があります。\r
+ * \r
+ * @since 1.1\r
+ * \r
+ * @param file 添付ファイル\r
+ */\r
+ public void addFile(File file) {\r
+ if (attachmentFiles == null) {\r
+ initAttachmentFiles();\r
+ }\r
+ addFile(file, file.getName());\r
+ }\r
+\r
+ /**\r
+ * 指定されたファイルを添付します。\r
+ * 指定するファイル名には適切な拡張子が付けられている必要があります。\r
+ * \r
+ * @since 1.1\r
+ * \r
+ * @param file 添付ファイル\r
+ * @param fileName ファイル名\r
+ */\r
+ public void addFile(File file, String fileName) {\r
+ if (attachmentFiles == null) {\r
+ initAttachmentFiles();\r
+ }\r
+ attachmentFiles.add(new AttachmentFile(fileName, file));\r
+ }\r
+\r
+ /**\r
+ * 指定されたURLのファイルを添付します。\r
+ * 指定するファイル名には適切な拡張子が付けられている必要があります。\r
+ * \r
+ * @since 1.1\r
+ * \r
+ * @param url 添付ファイル\r
+ * @param fileName ファイル名\r
+ */\r
+ public void addFile(URL url, String fileName) {\r
+ if (attachmentFiles == null) {\r
+ initAttachmentFiles();\r
+ }\r
+ attachmentFiles.add(new AttachmentFile(fileName, url));\r
+ }\r
+\r
+ /**\r
+ * 指定されたInputStreamをファイルとして添付します。\r
+ * 指定するファイル名には適切な拡張子が付けられている必要があります。\r
+ * \r
+ * @since 1.1\r
+ * \r
+ * @param is 添付ファイルを生成するInputStream\r
+ * @param fileName ファイル名\r
+ */\r
+ public void addFile(InputStream is, String fileName) {\r
+ if (attachmentFiles == null) {\r
+ initAttachmentFiles();\r
+ }\r
+ attachmentFiles.add(new AttachmentFile(fileName, is));\r
+ }\r
+\r
+ /**\r
+ * 指定されたbyte配列をファイルとして添付します。\r
+ * 指定するファイル名には適切な拡張子が付けられている必要があります。\r
+ * \r
+ * @since 1.2\r
+ * \r
+ * @param bytes 添付ファイルを生成するbyte配列\r
+ * @param fileName ファイル名\r
+ */\r
+ public void addFile(byte[] bytes, String fileName) {\r
+ if (attachmentFiles == null) {\r
+ initAttachmentFiles();\r
+ }\r
+ attachmentFiles.add(new AttachmentFile(fileName, bytes));\r
+ }\r
+\r
+ /**\r
+ * attachmentFilesプロパティを初期化。\r
+ */\r
+ private void initAttachmentFiles() {\r
+ attachmentFiles = new ArrayList();\r
+ }\r
+\r
+ /**\r
+ * 添付ファイルの配列を返します。\r
+ * 添付ファイルがセットされていない場合は、空の配列を返します。\r
+ * \r
+ * @since 1.1\r
+ * \r
+ * @return 添付ファイルの配列。または空の配列。\r
+ */\r
+ public AttachmentFile[] getAttachmentFiles() {\r
+ if (attachmentFiles == null) {\r
+ return new AttachmentFile[0];\r
+ }\r
+ return (AttachmentFile[])attachmentFiles\r
+ .toArray(new AttachmentFile[attachmentFiles.size()]);\r
+ }\r
+\r
+ /**\r
+ * HTMLの本文がセットされているかどうか判定します。\r
+ * \r
+ * @since 1.1\r
+ * \r
+ * @return HTMLの本文がセットされている場合 true\r
+ */\r
+ public boolean isHtmlMail() {\r
+ return (htmlText != null);\r
+ }\r
+\r
+ /**\r
+ * ファイルが添付されているかどうか判定します。\r
+ * \r
+ * @since 1.1\r
+ * \r
+ * @return ファイルが添付されている場合 true\r
+ */\r
+ public boolean isFileAttached() {\r
+ return attachmentFiles != null && attachmentFiles.size() > 0;\r
+ }\r
+\r
+ /**\r
+ * マルチパート・メールかどうか判定します。<br>\r
+ * HTML本文がセットされているか、ファイルが添付されている場合に true が返されます。\r
+ * <p>\r
+ * 注: ここで判定されるマルチパートは、厳密な意味でのマルチパートではありません。\r
+ * \r
+ * @since 1.1\r
+ * \r
+ * @return マルチパート・メールの場合 true\r
+ */\r
+ public boolean isMultipartMail() {\r
+ return isHtmlMail() || isFileAttached();\r
+ }\r
+\r
+ /**\r
+ * セットされている添付ファイルを全てクリアします。\r
+ * \r
+ * @since 1.1\r
+ */\r
+ public void clearFile() {\r
+ initAttachmentFiles();\r
+ }\r
+\r
+ /**\r
+ * envelope-toの宛先アドレスを追加します。\r
+ * <p>\r
+ * envelope-toアドレスがセットされている場合、envelope-toのアドレスにのみメールを送信し、\r
+ * To、Cc、Bccアドレスには実際には送信されません。\r
+ * \r
+ * @since 1.2\r
+ * @param address\r
+ */\r
+ public void addEnvelopeTo(InternetAddress address) {\r
+ if (envelopeTo == null) {\r
+ envelopeTo = new ArrayList();\r
+ }\r
+ envelopeTo.add(address);\r
+ }\r
+\r
+ /**\r
+ * envelope-toの宛先アドレスを追加します。\r
+ * <p>\r
+ * envelope-toアドレスがセットされている場合、envelope-toのアドレスにのみメールを送信し、\r
+ * To、Cc、Bccアドレスには実際には送信されません。\r
+ * \r
+ * @since 1.2\r
+ * @param email\r
+ * @throws IllegalArgumentException 不正なフォーマットのアドレスが指定された場合\r
+ */\r
+ public void addEnvelopeTo(String email) {\r
+ try {\r
+ addEnvelopeTo(new InternetAddress(email));\r
+ } catch (AddressException e) {\r
+ throw new IllegalArgumentException(e.getMessage());\r
+ }\r
+ }\r
+\r
+ /**\r
+ * envelope-toの宛先アドレスを追加します。\r
+ * <p>\r
+ * envelope-toアドレスがセットされている場合、envelope-toのアドレスにのみメールを送信し、\r
+ * To、Cc、Bccアドレスには実際には送信されません。\r
+ * \r
+ * @since 1.2\r
+ * @param addresses\r
+ */\r
+ public void addEnvelopeTo(InternetAddress[] addresses) {\r
+ for (int i = 0; i < addresses.length; i++) {\r
+ addEnvelopeTo(addresses[i]);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * envelope-toの宛先アドレスを追加します。\r
+ * <p>\r
+ * envelope-toアドレスがセットされている場合、envelope-toのアドレスにのみメールを送信し、\r
+ * To、Cc、Bccアドレスには実際には送信されません。\r
+ * \r
+ * @since 1.2\r
+ * @param emails\r
+ * @throws IllegalArgumentException 不正なフォーマットのアドレスが指定された場合\r
+ */\r
+ public void addEnvelopeTo(String[] emails) {\r
+ for (int i = 0; i < emails.length; i++) {\r
+ addEnvelopeTo(emails[i]);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * セットされているenvelope-toアドレスを全てクリアします。\r
+ *\r
+ * @since 1.2\r
+ */\r
+ public void clearEnvelopeTo() {\r
+ envelopeTo = null;\r
+ }\r
+\r
+ /**\r
+ * envelope-toアドレス配列を返します。\r
+ * envelope-toアドレスが一件もセットされていないときは空の配列を返します。\r
+ * \r
+ * @since 1.2\r
+ * @return envelope-toアドレスの配列\r
+ */\r
+ public InternetAddress[] getEnvelopeTo() {\r
+ if (envelopeTo == null) {\r
+ return new InternetAddress[0];\r
+ }\r
+ return (InternetAddress[])envelopeTo.toArray(new InternetAddress[envelopeTo.size()]);\r
+ }\r
+\r
+ /**\r
+ * 添付ファイル。\r
+ * <p>\r
+ * 受信メール(ReceivedMail)の添付ファイルは、常に<code>getFile()</code>メソッドで取得します。\r
+ * <code>getInputStream()</code>、<code>getUrl()</code>メソッドはnullを返します。\r
+ * 受信メールに対しては、<code>ReceivedMail.getFiles()</code>メソッドを使うと添付ファイルの\r
+ * <code>File</code>インスタンス配列を取得することができます。\r
+ * \r
+ * @since 1.1\r
+ * @author Tomohiro Otsuka\r
+ * @version $Id: Mail.java,v 1.10.2.9 2007/03/30 13:03:44 otsuka Exp $\r
+ */\r
+ public class AttachmentFile {\r
+\r
+ private String name;\r
+\r
+ private File file;\r
+\r
+ private InputStream is;\r
+\r
+ private URL url;\r
+\r
+ private byte[] bytes = null;\r
+\r
+ /**\r
+ * ファイル名とファイルを指定して、このクラスのインタンスを生成します。\r
+ * ファイル名には適切な拡張子が付けられている必要があります。\r
+ * \r
+ * @param name メールに表示するファイル名\r
+ * @param file 添付ファイル\r
+ */\r
+ public AttachmentFile(String name, File file) {\r
+ this.name = name;\r
+ this.file = file;\r
+ }\r
+\r
+ /**\r
+ * ファイル名とInputStreamを指定して、このクラスのインタンスを生成します。\r
+ * ファイル名には適切な拡張子が付けられている必要があります。\r
+ * \r
+ * @param name メールに表示するファイル名\r
+ * @param is 添付ファイルを生成するInputStream\r
+ */\r
+ public AttachmentFile(String name, InputStream is) {\r
+ this.name = name;\r
+ this.is = is;\r
+ }\r
+\r
+ /**\r
+ * ファイル名とファイルロケーションのURLを指定して、このクラスのインタンスを生成します。\r
+ * ファイル名には適切な拡張子が付けられている必要があります。\r
+ * \r
+ * @param name メールに表示するファイル名\r
+ * @param url 添付ファイルのロケーションURL\r
+ */\r
+ public AttachmentFile(String name, URL url) {\r
+ this.name = name;\r
+ this.url = url;\r
+ }\r
+\r
+ /**\r
+ * ファイル名とbyte配列を指定して、このクラスのインタンスを生成します。\r
+ * ファイル名には適切な拡張子が付けられている必要があります。\r
+ * \r
+ * @param name メールに表示するファイル名\r
+ * @param bytes 添付ファイルを生成するbyte配列\r
+ */\r
+ public AttachmentFile(String name, byte[] bytes) {\r
+ this.name = name;\r
+ this.bytes = bytes;\r
+ }\r
+\r
+ /**\r
+ * 添付ファイルのDataSourceインスタンスを生成して返します。\r
+ * \r
+ * @return 添付ファイルのDataSourceインスタンス\r
+ */\r
+ public DataSource getDataSource() {\r
+ if (file != null) {\r
+ return new FileDataSource(file);\r
+ }\r
+\r
+ if (url != null) {\r
+ return new URLDataSource(url);\r
+ }\r
+\r
+ // InputStreamからDataSourceを生成\r
+ String contentType = FileTypeMap.getDefaultFileTypeMap().getContentType(name);\r
+ if (is != null) {\r
+ // InputStreamからDataSourceを生成\r
+ return new ByteArrayDataSource(is, contentType);\r
+ } else {\r
+ // byte配列からDataSourceを生成\r
+ return new ByteArrayDataSource(bytes, contentType);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * 添付ファイル名を返します。\r
+ * \r
+ * @return 添付ファイル名\r
+ */\r
+ public String getName() {\r
+ return name;\r
+ }\r
+\r
+ /**\r
+ * @return セットされたファイル。またはnull。\r
+ */\r
+ public File getFile() {\r
+ return file;\r
+ }\r
+\r
+ /**\r
+ * @return セットされたInputStream。またはnull。\r
+ */\r
+ public InputStream getInputStream() {\r
+ return is;\r
+ }\r
+\r
+ /**\r
+ * @return セットされたURL。またはnull。\r
+ */\r
+ public URL getUrl() {\r
+ return url;\r
+ }\r
+\r
+ /**\r
+ * @return セットされたbyte配列。またはnull。\r
+ */\r
+ public byte[] getBytes() {\r
+ return bytes;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * メールの重要度。定数のみを定義。\r
+ * \r
+ * @author Tomohiro Otsuka\r
+ * @version $Id: Mail.java,v 1.10.2.9 2007/03/30 13:03:44 otsuka Exp $\r
+ */\r
+ public static class Importance {\r
+\r
+ /** 重要度「高」 */\r
+ public static final String HIGH = "high";\r
+\r
+ /** 重要度「中」 */\r
+ public static final String NORMAL = "normal";\r
+\r
+ /** 重要度「低」 */\r
+ public static final String LOW = "low";\r
+\r
+ }\r
+}
\ No newline at end of file
--- /dev/null
+package com.ozacc.mail;\r
+\r
+/**\r
+ * SMTPサーバ接続の認証に失敗した際にスローされる例外。\r
+ * \r
+ * @since 1.0\r
+ * @author Tomohiro Otsuka\r
+ * @version $Id: MailAuthenticationException.java,v 1.2 2004/09/13 07:08:22 otsuka Exp $\r
+ */\r
+public class MailAuthenticationException extends MailException {\r
+\r
+ /**\r
+ * @param message \r
+ */\r
+ public MailAuthenticationException(String message) {\r
+ super(message);\r
+ }\r
+\r
+ /**\r
+ * @param message\r
+ * @param cause \r
+ */\r
+ public MailAuthenticationException(String message, Throwable cause) {\r
+ super(message, cause);\r
+ }\r
+\r
+ public MailAuthenticationException(Throwable cause) {\r
+ super("Authentication failed: " + cause.getMessage(), cause);\r
+ }\r
+\r
+}\r
+\r
--- /dev/null
+package com.ozacc.mail;\r
+\r
+/**\r
+ * MimeMessageオブジェクトの生成に失敗した際にスローされる例外。\r
+ * \r
+ * @since 1.0\r
+ * @author Tomohiro Otsuka\r
+ * @version $Id: MailBuildException.java,v 1.2 2004/09/13 07:08:41 otsuka Exp $\r
+ */\r
+public class MailBuildException extends MailException {\r
+\r
+ /**\r
+ * @param message \r
+ */\r
+ public MailBuildException(String message) {\r
+ super(message);\r
+ }\r
+\r
+ /**\r
+ * @param message\r
+ * @param cause \r
+ */\r
+ public MailBuildException(String message, Throwable cause) {\r
+ super(message, cause);\r
+ }\r
+\r
+}
\ No newline at end of file
--- /dev/null
+package com.ozacc.mail;\r
+\r
+import java.io.File;\r
+\r
+/**\r
+ * メールデータが記述されたファイルからMailインスタンスを生成するインスターフェース。\r
+ * サポートするファイルの種類やメールデータ書式は実装クラスに依存します。\r
+ * \r
+ * @see com.ozacc.mail.impl.XMLMailBuilderImpl\r
+ * @see com.ozacc.mail.impl.JDomXMLMailBuilder\r
+ * \r
+ * @since 1.0\r
+ * @author Tomohiro Otsuka\r
+ * @version $Id: MailBuilder.java,v 1.7 2004/09/17 23:07:01 otsuka Exp $\r
+ */\r
+public interface MailBuilder {\r
+\r
+ /**\r
+ * 指定されたクラスパス上のファイルを読み込んでMailインスタンスを生成します。\r
+ * \r
+ * @param classPath メール内容を記述したファイルのパス\r
+ * @return 生成されたMailインスタンス\r
+ * @throws MailBuildException Mailインスタンスの生成に失敗した場合\r
+ */\r
+ Mail buildMail(String classPath) throws MailBuildException;\r
+\r
+ /**\r
+ * 指定されたファイルを読み込んでMailインスタンスを生成します。\r
+ * \r
+ * @param file メール内容を記述したファイル\r
+ * @return 生成されたMailインスタンス\r
+ * @throws MailBuildException Mailインスタンスの生成に失敗した場合\r
+ */\r
+ Mail buildMail(File file) throws MailBuildException;\r
+\r
+}
\ No newline at end of file
--- /dev/null
+package com.ozacc.mail;\r
+\r
+/**\r
+ * メール関連例外の規定クラス。RuntimeExceptionを継承しています。\r
+ * \r
+ * @since 1.0\r
+ * @author Tomohiro Otsuka\r
+ * @version $Id: MailException.java,v 1.2 2004/09/13 07:08:41 otsuka Exp $\r
+ */\r
+public class MailException extends RuntimeException {\r
+\r
+ /**\r
+ * @param message \r
+ */\r
+ public MailException(String message) {\r
+ super(message);\r
+ }\r
+\r
+ /**\r
+ * @param message\r
+ * @param cause \r
+ */\r
+ public MailException(String message, Throwable cause) {\r
+ super(message, cause);\r
+ }\r
+\r
+}
\ No newline at end of file
--- /dev/null
+package com.ozacc.mail;\r
+\r
+/**\r
+ * メール送信に失敗した時にスローされる例外。\r
+ * \r
+ * @since 1.0\r
+ * @author Tomohiro Otsuka\r
+ * @version $Id: MailSendException.java,v 1.2 2004/09/13 07:08:41 otsuka Exp $\r
+ */\r
+public class MailSendException extends MailException {\r
+\r
+ private static final long serialVersionUID = -8590978542027055148L;\r
+\r
+ /**\r
+ * @param message\r
+ */\r
+ public MailSendException(String message) {\r
+ super(message);\r
+ }\r
+\r
+ /**\r
+ * @param message\r
+ * @param cause\r
+ */\r
+ public MailSendException(String message, Throwable cause) {\r
+ super(message, cause);\r
+ }\r
+\r
+}
\ No newline at end of file
--- /dev/null
+package com.ozacc.mail;\r
+\r
+import java.io.File;\r
+\r
+/**\r
+ * 複数のメールデータが記述されたファイルからMailインスタンスを生成するインスターフェース。\r
+ * \r
+ * @since 1.2\r
+ * @author Tomohiro Otsuka\r
+ * @version $Id: MultipleMailBuilder.java,v 1.1.2.2 2005/01/23 06:47:08 otsuka Exp $\r
+ */\r
+public interface MultipleMailBuilder extends MailBuilder {\r
+\r
+ /**\r
+ * 指定されたクラスパス上のファイルを読み込み、mailIdが示すデータからMailインスタンスを生成します。\r
+ * \r
+ * @param classPath メール内容を記述したファイルのパス\r
+ * @param mailId 生成するMailのメールデータを示すID\r
+ * @return 生成されたMailインスタンス\r
+ * @throws MailBuildException Mailインスタンスの生成に失敗した場合\r
+ */\r
+ Mail buildMail(String classPath, String mailId) throws MailBuildException;\r
+\r
+ /**\r
+ * 指定されたファイルを読み込み、mailIdが示すデータからMailインスタンスを生成します。\r
+ * \r
+ * @param file メール内容を記述したファイル\r
+ * @param mailId 生成するMailのメールデータを示すID\r
+ * @return 生成されたMailインスタンス\r
+ * @throws MailBuildException Mailインスタンスの生成に失敗した場合\r
+ */\r
+ Mail buildMail(File file, String mailId) throws MailBuildException;\r
+\r
+}
\ No newline at end of file
--- /dev/null
+package com.ozacc.mail;\r
+\r
+/**\r
+ * SMTPサーバに接続していない時に、接続状態を要する処理を実行してスローされる例外。\r
+ * \r
+ * @since 1.0\r
+ * @author Tomohiro Otsuka\r
+ * @version $Id: NotConnectedException.java,v 1.2 2004/09/13 07:08:41 otsuka Exp $\r
+ */\r
+public class NotConnectedException extends MailException {\r
+\r
+ /**\r
+ * @param message \r
+ */\r
+ public NotConnectedException(String message) {\r
+ super(message);\r
+ }\r
+\r
+ /**\r
+ * @param message\r
+ * @param cause \r
+ */\r
+ public NotConnectedException(String message, Throwable cause) {\r
+ super(message, cause);\r
+ }\r
+\r
+}
\ No newline at end of file
--- /dev/null
+package com.ozacc.mail;\r
+\r
+import javax.mail.internet.MimeMessage;\r
+\r
+/**\r
+ * SendMailインターフェース。\r
+ * \r
+ * @since 1.0\r
+ * @author Tomohiro Otsuka\r
+ * @version $Id: SendMail.java,v 1.3 2004/09/25 02:12:12 otsuka Exp $\r
+ */\r
+public interface SendMail {\r
+\r
+ /**\r
+ * 指定されたメールを送信します。\r
+ * \r
+ * @param mail 送信するメールのMailインスタンス\r
+ * @throws MailException メール送信に失敗した場合\r
+ */\r
+ void send(Mail mail) throws MailException;\r
+\r
+ /**\r
+ * 指定されたメールを送信します。\r
+ * \r
+ * @param mails 送信するメールのMailインスタンス配列\r
+ * @throws MailException メール送信に失敗した場合\r
+ */\r
+ void send(Mail[] mails) throws MailException;\r
+\r
+ /**\r
+ * 指定されたMimeMessageを送信します。\r
+ * \r
+ * @param mimeMessage 送信するメールのMimeMessageインスタンス\r
+ * @throws MailException メール送信に失敗した場合\r
+ */\r
+ void send(MimeMessage mimeMessage) throws MailException;\r
+\r
+ /**\r
+ * 指定されたMimeMessageを送信します。\r
+ * \r
+ * @param mimeMessages 送信するメールのMimeMessageインスタンス配列\r
+ * @throws MailException メール送信に失敗した場合\r
+ */\r
+ void send(MimeMessage[] mimeMessages) throws MailException;\r
+\r
+}
\ No newline at end of file
--- /dev/null
+package com.ozacc.mail;\r
+\r
+import javax.mail.internet.MimeMessage;\r
+\r
+/**\r
+ * SMTPサーバとの接続、切断を任意のタイミングで行いたい場合に使用するSendMailインターフェース。\r
+ * <p>\r
+ * 大量メール配信で、MailやMimeMessageの配列を用意するとメモリを圧迫してしまう場合などに使用します。<br>\r
+ * 接続のクローズを忘れないように注意してください。\r
+ * <p>\r
+ * このインターフェース実装クラスのインスタンスは、メールサーバとの接続を保持するため、\r
+ * スレッドセーフではありません。<br>\r
+ * DIコンテナでの使用の際はシングルトンでインスタンスを取得しないように注意してください。\r
+ * \r
+ * @since 1.0\r
+ * @author Tomohiro Otsuka\r
+ * @version $Id: SendMailPro.java,v 1.2.2.1 2004/11/25 08:02:27 otsuka Exp $\r
+ */\r
+public interface SendMailPro {\r
+\r
+ /**\r
+ * SMTPサーバに接続します。\r
+ * \r
+ * @throws MailException\r
+ */\r
+ void connect() throws MailException;\r
+\r
+ /**\r
+ * SMTPサーバとの接続をクローズします。\r
+ * 接続していない時にこのメソッドを呼んでも何も行いません。\r
+ * \r
+ * @throws MailException\r
+ */\r
+ void disconnect() throws MailException;\r
+\r
+ /**\r
+ * 指定されたMimeMessageを送信します。SMTPサーバに接続していない場合は例外をスローします。\r
+ * \r
+ * @param mimeMessage\r
+ * @throws MailException\r
+ */\r
+ void send(MimeMessage mimeMessage) throws MailException;\r
+\r
+ /**\r
+ * 指定されたMailを送信します。SMTPサーバに接続していない場合は例外をスローします。\r
+ * \r
+ * @param mail\r
+ * @throws MailException\r
+ */\r
+ void send(Mail mail) throws MailException;\r
+\r
+}
\ No newline at end of file
--- /dev/null
+package com.ozacc.mail;\r
+\r
+import java.io.File;\r
+\r
+import org.apache.velocity.VelocityContext;\r
+\r
+/**\r
+ * Velocityと連携して動的にメールデータを生成し、そのデータからMailインスタンスを生成するインターフェース。\r
+ * \r
+ * @see com.ozacc.mail.impl.XMLVelocityMailBuilderImpl\r
+ * @see com.ozacc.mail.impl.JDomXMLMailBuilder\r
+ * \r
+ * @since 1.0\r
+ * @author Tomohiro Otsuka\r
+ * @version $Id: VelocityMailBuilder.java,v 1.4.2.3 2005/01/21 16:51:20 otsuka Exp $\r
+ */\r
+public interface VelocityMailBuilder extends MailBuilder {\r
+\r
+ /**\r
+ * 指定されたクラスパス上のファイルを読み込んでMailインスタンスを生成します。\r
+ * 指定されたVelocityContextを使って、XMLファイルの内容を動的に生成できます。\r
+ * \r
+ * @param classPath メール内容を記述したファイルのパス\r
+ * @param context VelocityContext\r
+ * @return 生成されたMailインスタンス\r
+ * @throws MailBuildException Mailインスタンスの生成に失敗した場合\r
+ */\r
+ Mail buildMail(String classPath, VelocityContext context) throws MailBuildException;\r
+\r
+ /**\r
+ * 指定されたファイルを読み込んでMailインスタンスを生成します。\r
+ * 指定されたVelocityContextを使って、XMLファイルの内容を動的に生成できます。\r
+ * \r
+ * @param file メール内容を記述したファイル\r
+ * @param context VelocityContext\r
+ * @return 生成されたMailインスタンス\r
+ * @throws MailBuildException Mailインスタンスの生成に失敗した場合\r
+ */\r
+ Mail buildMail(File file, VelocityContext context) throws MailBuildException;\r
+\r
+ /**\r
+ * メールデータキャッシュをクリアします。\r
+ * \r
+ * @since 1.1.2\r
+ */\r
+ void clearCache();\r
+\r
+ /**\r
+ * VelocityContextとマージする前のメールデータをキャッシュするかどうかを設定します。\r
+ * デフォルトはキャッシュしない設定です。\r
+ * <p>\r
+ * キャッシュのキーは、<code>buildMail()</code>メソッド引数のメールデータファイルのクラスパス或いはファイルパスです。\r
+ * キャッシュに有効期限はありません。\r
+ * また、メールデータファイルの内容が途中で更新されても、キャッシュされているメールデータは更新されませんので注意してください。\r
+ * <p>\r
+ * <code>false</code>を指定してこのメソッドを呼ぶとメールデータキャッシュはクリアされます。\r
+ * \r
+ * @since 1.1.2\r
+ * @param cacheEnabled メールデータをキャッシュする場合は true\r
+ */\r
+ void setCacheEnabled(boolean cacheEnabled);\r
+\r
+ /**\r
+ * VelocityContextとマージする前のメールデータをキャッシュする設定かどうか判定します。\r
+ * \r
+ * @since 1.1.2\r
+ * @return メールデータをキャッシュする設定の場合は true\r
+ */\r
+ boolean isCacheEnabled();\r
+\r
+}
\ No newline at end of file
--- /dev/null
+package com.ozacc.mail;\r
+\r
+import java.io.File;\r
+\r
+import org.apache.velocity.VelocityContext;\r
+\r
+/**\r
+ * Velocityと連携して動的にメールデータを生成し、そのデータからMailインスタンスを生成するインターフェース。\r
+ * \r
+ * @since 1.2\r
+ * @author Tomohiro Otsuka\r
+ * @version $Id: VelocityMultipleMailBuilder.java,v 1.1.2.2 2005/01/23 06:47:08 otsuka Exp $\r
+ */\r
+public interface VelocityMultipleMailBuilder extends VelocityMailBuilder {\r
+\r
+ /**\r
+ * 指定されたクラスパス上のファイルを読み込み、mailIdが示すデータからMailインスタンスを生成します。\r
+ * 指定されたVelocityContextを使って、XMLファイルの内容を動的に生成できます。\r
+ * \r
+ * @param classPath メール内容を記述したファイルのパス\r
+ * @param context VelocityContext\r
+ * @param mailId 生成するMailのメールデータを示すID\r
+ * @return 生成されたMailインスタンス\r
+ * @throws MailBuildException Mailインスタンスの生成に失敗した場合\r
+ */\r
+ Mail buildMail(String classPath, VelocityContext context, String mailId)\r
+ throws MailBuildException;\r
+\r
+ /**\r
+ * 指定されたファイルを読み込み、mailIdが示すデータからMailインスタンスを生成します。\r
+ * 指定されたVelocityContextを使って、XMLファイルの内容を動的に生成できます。\r
+ * \r
+ * @param file メール内容を記述したファイル\r
+ * @param context VelocityContext\r
+ * @param mailId 生成するMailのメールデータを示すID\r
+ * @return 生成されたMailインスタンス\r
+ * @throws MailBuildException Mailインスタンスの生成に失敗した場合\r
+ */\r
+ Mail buildMail(File file, VelocityContext context, String mailId) throws MailBuildException;\r
+\r
+}
\ No newline at end of file
--- /dev/null
+package com.ozacc.mail.fetch;\r
+\r
+import com.ozacc.mail.MailException;\r
+\r
+/**\r
+ * メールサーバからメールを取得するインターフェース。<br>\r
+ * このインターフェースの実装クラスでメールサーバの情報を設定します。\r
+ * <p>\r
+ * getMails()メソッドはスレッドセーフです。メソッドを呼び出すとメールサーバに接続し、\r
+ * メソッド終了時にサーバとの接続を切断します。\r
+ * \r
+ * @since 1.2\r
+ * @author Tomohiro Otsuka\r
+ * @version $Id: FetchMail.java,v 1.1.2.5 2004/10/27 19:41:35 otsuka Exp $\r
+ */\r
+public interface FetchMail {\r
+\r
+ /**\r
+ * メールサーバからメールを受信し、ReceivedMailインスタンスに変換して返します。<br>\r
+ * 受信したメールは、メールサーバに残されます。\r
+ * <p>\r
+ * このメソッドを呼び出すとメールサーバに接続します。メールを受信した後、メールサーバとの接続を切断します。\r
+ * <p>\r
+ * メールサーバがimapサーバの場合、一度受信したメールには既読フラグ(SEENフラグ)が付けられます。\r
+ * \r
+ * @return 受信したメールのReceivedMailインスタンス配列\r
+ * @throws MailException\r
+ */\r
+ ReceivedMail[] getMails() throws MailException;\r
+\r
+ /**\r
+ * メールサーバからメールを受信し、ReceivedMailインスタンスに変換して返します。<br>\r
+ * deleteパラメータで、受信時にメールサーバからメールを削除するか残すかを指定します。\r
+ * <p>\r
+ * このメソッドを呼び出すとメールサーバに接続します。メールを受信した後、メールサーバとの接続を切断します。\r
+ * <p>\r
+ * メールサーバがimapサーバの場合、一度受信したメールには既読フラグ(SEENフラグ)が付けられます。\r
+ * \r
+ * @param delete 受信時にメールサーバからメールを削除する場合 true\r
+ * @return 受信したメールのReceivedMailインスタンス配列\r
+ * @throws MailException\r
+ */\r
+ ReceivedMail[] getMails(boolean delete) throws MailException;\r
+\r
+}
\ No newline at end of file
--- /dev/null
+package com.ozacc.mail.fetch;\r
+\r
+import javax.mail.internet.MimeMessage;\r
+\r
+import com.ozacc.mail.MailException;\r
+\r
+/**\r
+ * メールサーバからメールを取得する上級インターフェース。<br>\r
+ * このインターフェースの実装クラスでメールサーバの情報を設定します。\r
+ * <p>\r
+ * <code>FetchMail</code>インターフェースと異なり、メール取得時に例外が発生しても、\r
+ * メールサーバとの接続は切断されません。<code>finally</code>ブロックを使用するなりして\r
+ * メールサーバとの接続を確実に切断できるようにすることを推奨します。\r
+ * <p>\r
+ * このインターフェース実装クラスのインスタンスは、メールサーバとの接続を保持するため、\r
+ * スレッドセーフではありません。<br>\r
+ * DIコンテナでの使用の際はシングルトンでインスタンスを取得しないように注意してください。\r
+ * \r
+ * @see FetchMail\r
+ * @since 1.2\r
+ * @author Tomohiro Otsuka\r
+ * @version $Id: FetchMailPro.java,v 1.1.2.7 2005/04/10 05:22:24 otsuka Exp $\r
+ */\r
+public interface FetchMailPro {\r
+\r
+ /**\r
+ * メールサーバに接続し、「INBOX」フォルダをオープンします。\r
+ * \r
+ * @throws MailException メールサーバとの接続に失敗した場合\r
+ */\r
+ void connect() throws MailException;\r
+\r
+ /**\r
+ * メールサーバとの接続を切断します。接続されていなければ何も行いません。\r
+ * \r
+ * @throws MailException メールサーバとの接続切断に失敗した場合\r
+ */\r
+ void disconnect() throws MailException;\r
+\r
+ /**\r
+ * 現在のフォルダに届いているメール数を返します。\r
+ * \r
+ * @return 現在のフォルダにあるメール数\r
+ * @throws MailException\r
+ */\r
+ int getMailCount() throws MailException;\r
+\r
+ /**\r
+ * 現在のフォルダにある指定されたメッセージ番号のメールをReceivedMailに変換して返します。\r
+ * メッセージ番号は1始まりです。\r
+ * <p>\r
+ * メッセージはサーバから削除されません。\r
+ * \r
+ * @param num メッセージ番号。1始まり。\r
+ * @return 指定されたメッセージ番号のReceivedMailインスタンス\r
+ * @throws MailException メール取得に失敗した場合\r
+ */\r
+ ReceivedMail getMail(int num) throws MailException;\r
+\r
+ /**\r
+ * 現在のフォルダにある指定されたメッセージ番号のメールをReceivedMailに変換して返します。\r
+ * メッセージ番号は1始まりです。\r
+ * 指定した番号のメッセージをサーバから削除するかどうかを指定できます。\r
+ * \r
+ * @param num メッセージ番号。1始まり。\r
+ * @param delete 指定された番号のメッセージをサーバから削除する場合 true を指定\r
+ * @return 指定されたメッセージ番号のReceivedMailインスタンス\r
+ * @throws MailException メール取得に失敗した場合\r
+ */\r
+ ReceivedMail getMail(int num, boolean delete) throws MailException;\r
+\r
+ /**\r
+ * 現在のフォルダにある全メールをReceivedMailに変換して返します。\r
+ * \r
+ * @param delete メール取得後にサーバからメールを削除する場合 true\r
+ * @return 現在のフォルダにある全メールのReceivedMailインスタンス\r
+ * @throws MailException メール取得に失敗した場合\r
+ */\r
+ ReceivedMail[] getMails(boolean delete) throws MailException;\r
+\r
+ /**\r
+ * 現在のフォルダにある指定されたメッセージ番号のメールを返します。\r
+ * メッセージ番号は1始まりです。\r
+ * \r
+ * @see javax.mail.Folder#getMessage(int)\r
+ * @param num メッセージ番号。1始まり。\r
+ * @return 指定された番号のMimeMessageインスタンス\r
+ * @throws MailException メール取得に失敗した場合\r
+ */\r
+ MimeMessage getMessage(int num) throws MailException;\r
+\r
+ /**\r
+ * 現在のフォルダにある全メールを返します。\r
+ * \r
+ * @param delete メール取得後にサーバからメールを削除する場合 true\r
+ * @return 現在のフォルダにある全メールのMimeMessageインスタンス\r
+ * @throws MailException メール取得に失敗した場合\r
+ */\r
+ MimeMessage[] getMessages(boolean delete) throws MailException;\r
+\r
+ /**\r
+ * 指定された名前のフォルダに移動します。\r
+ * フォルダ名は"INBOX/XXXX"のように、INBOXからのパス指定します。\r
+ * <p>\r
+ * <strong>注:</strong> このメソッドは、メールサーバがimapサーバの時にのみ使用可能です。\r
+ * \r
+ * @param folderName 移動先のフォルダ名\r
+ * @throws MailException\r
+ */\r
+ void changeFolder(String folderName) throws MailException;\r
+\r
+ /**\r
+ * メールサーバと接続しているかどうか判定します。\r
+ * \r
+ * @return 接続している場合 true\r
+ */\r
+ boolean isConnected();\r
+}
\ No newline at end of file
--- /dev/null
+package com.ozacc.mail.fetch;\r
+\r
+import javax.mail.internet.MimeMessage;\r
+\r
+/**\r
+ * <code>MimeMessage</code>から<code>ReceivedMail</code>を生成するインターフェース。\r
+ * \r
+ * @since 1.2\r
+ * @author gaku\r
+ * @version $Id: MailConverter.java,v 1.1.2.2 2005/04/10 05:25:21 otsuka Exp $\r
+ */\r
+public interface MailConverter {\r
+\r
+ /**\r
+ * 指定された<code>MimeMessage</code>を<code>ReceivedMail</code>に変換して返します。\r
+ * \r
+ * @param message <code>ReceivedMail</code>に変換する<code>MimeMessage</code>\r
+ * @return <code>MimeMessage</code>から生成された<code>ReceivedMail</code>\r
+ */\r
+ ReceivedMail convertIntoMail(MimeMessage message);\r
+\r
+ /**\r
+ * 指定された<code>MimeMessage</code>を<code>ReceivedMail</code>に変換して返します。\r
+ * \r
+ * @param message <code>ReceivedMail</code>に変換する<code>MimeMessage</code>の配列\r
+ * @return <code>MimeMessage</code>から生成された<code>ReceivedMail</code>の配列\r
+ */\r
+ ReceivedMail[] convertIntoMails(MimeMessage[] messages);\r
+\r
+}
\ No newline at end of file
--- /dev/null
+package com.ozacc.mail.fetch;\r
+\r
+import com.ozacc.mail.MailException;\r
+\r
+/**\r
+ * メールの受信に失敗した時にスローされる例外。\r
+ * \r
+ * @author Tomohiro Otsuka\r
+ * @version $Id: MailFetchException.java,v 1.1.2.1 2004/10/24 10:14:31 otsuka Exp $\r
+ */\r
+public class MailFetchException extends MailException {\r
+\r
+ /**\r
+ * @param message \r
+ */\r
+ public MailFetchException(String message) {\r
+ super(message);\r
+ }\r
+\r
+ /**\r
+ * @param message\r
+ * @param cause \r
+ */\r
+ public MailFetchException(String message, Throwable cause) {\r
+ super(message, cause);\r
+ }\r
+\r
+}
\ No newline at end of file
--- /dev/null
+package com.ozacc.mail.fetch;\r
+\r
+import java.io.File;\r
+import java.util.ArrayList;\r
+import java.util.Date;\r
+import java.util.Iterator;\r
+import java.util.List;\r
+\r
+import javax.mail.Flags;\r
+import javax.mail.MessagingException;\r
+import javax.mail.internet.MimeMessage;\r
+\r
+import com.ozacc.mail.Mail;\r
+\r
+/**\r
+ * 受信メール。\r
+ * <p>\r
+ * <code>FetchMail</code>、<code>FetchMailPro</code>の実装クラスで受信したメールが、\r
+ * インターネットメールとしての仕様を満たしていないヘッダ(FromやToなど)の値がセットされていた場合、\r
+ * そのヘッダに該当する<code>ReceivedMail</code>インスタンスのプロパティには何もセットされません。\r
+ * \r
+ * @since 1.2\r
+ * @author Tomohiro Otsuka\r
+ * @version $Id: ReceivedMail.java,v 1.1.2.9 2005/01/23 07:13:13 otsuka Exp $\r
+ */\r
+public class ReceivedMail extends Mail {\r
+\r
+ private String replySubjectPrefix = "Re: ";\r
+\r
+ private Date date;\r
+\r
+ private String messageId;\r
+\r
+ private int size;\r
+\r
+ private List receivedHeaders;\r
+\r
+ private MimeMessage message;\r
+\r
+ /**\r
+ * コンストラクタ。\r
+ */\r
+ public ReceivedMail() {\r
+ super();\r
+ }\r
+\r
+ /**\r
+ * コンストラクタ。\r
+ * \r
+ * @param charset \r
+ */\r
+ public ReceivedMail(String charset) {\r
+ super(charset);\r
+ }\r
+\r
+ /**\r
+ * コピーコンストラクタ。\r
+ * \r
+ * @param original \r
+ */\r
+ public ReceivedMail(Mail original) {\r
+ super(original);\r
+ }\r
+\r
+ /**\r
+ * 送信日時を返します。\r
+ * <p>\r
+ * 注: メールの受信日時ではありません。\r
+ * \r
+ * @return 送信日時\r
+ */\r
+ public Date getDate() {\r
+ return date;\r
+ }\r
+\r
+ /**\r
+ * 送信日時をセットします。\r
+ * \r
+ * @param date 送信日時\r
+ */\r
+ public void setDate(Date date) {\r
+ this.date = date;\r
+ }\r
+\r
+ /**\r
+ * 前後に<>が付いたメッセージIDを返します。\r
+ * 受信メールにメッセージIDが存在しない場合はnullを返します。\r
+ * \r
+ * @return 前後に<>が付いたメッセージID、またはnull\r
+ */\r
+ public String getMessageId() {\r
+ if (messageId == null || messageId.length() == 0) {\r
+ return null;\r
+ }\r
+ if (messageId.startsWith("<") && messageId.endsWith(">")) {\r
+ return messageId;\r
+ }\r
+ return "<" + messageId + ">";\r
+ }\r
+\r
+ /**\r
+ * メッセージIDを返します。前後に<>は付きません。\r
+ * 受信メールにメッセージIDが存在しない場合はnullを返します。\r
+ * \r
+ * @return メッセージID、またはnull\r
+ */\r
+ public String getMessageIdWithoutBracket() {\r
+ if (messageId == null || messageId.length() == 0) {\r
+ return null;\r
+ }\r
+ if (messageId.startsWith("<") && messageId.endsWith(">")) {\r
+ return messageId.substring(1, messageId.length() - 1);\r
+ }\r
+ return messageId;\r
+ }\r
+\r
+ /**\r
+ * メッセージIDをセットします。\r
+ * \r
+ * @param messageId メッセージID\r
+ */\r
+ public void setMessageId(String messageId) {\r
+ this.messageId = messageId;\r
+ }\r
+\r
+ /**\r
+ * In-Reply-Toヘッダの値を返します。\r
+ * In-Reply-Toヘッダがない場合はnullを返します。\r
+ * \r
+ * @return In-Reply-Toヘッダの値\r
+ */\r
+ public String getInReplyTo() {\r
+ return (String)headers.get("In-Reply-To");\r
+ }\r
+\r
+ /**\r
+ * Referencesヘッダの値を返します。\r
+ * Referencesヘッダがない場合はnullを返します。\r
+ * \r
+ * @return Referencesヘッダの値\r
+ */\r
+ public String getRefereces() {\r
+ return (String)headers.get("References");\r
+ }\r
+\r
+ /**\r
+ * @return 返信時の件名に付ける接頭辞\r
+ */\r
+ public String getReplySubjectPrefix() {\r
+ return replySubjectPrefix;\r
+ }\r
+\r
+ /**\r
+ * 返信時の件名に付ける接頭辞をセットします。\r
+ * デフォルトは「Re: 」。\r
+ * \r
+ * @param replySubjectPrefix 返信時の件名に付ける接頭辞\r
+ */\r
+ public void setReplySubjectPrefix(String replySubjectPrefix) {\r
+ this.replySubjectPrefix = replySubjectPrefix;\r
+ }\r
+\r
+ /**\r
+ * メール内容を出力します。<br>\r
+ * メールのソースに似たフォーマットで出力されます。\r
+ * \r
+ * @see java.lang.Object#toString()\r
+ */\r
+ public String toString() {\r
+ StringBuffer buf = new StringBuffer(1000);\r
+ buf.append("Mail\n");\r
+ buf.append("Return-Path: ").append(returnPath).append("\n");\r
+ buf.append("Message-ID: ").append(messageId).append("\n");\r
+ buf.append("Date: ").append(date).append("\n");\r
+ buf.append("From: ").append(from != null ? from.toUnicodeString() : null).append("\n");\r
+ buf.append("To: ").append(arrayToCommaDelimitedString(to)).append("\n");\r
+ buf.append("Cc: ").append(arrayToCommaDelimitedString(cc)).append("\n");\r
+ buf.append("Bcc: ").append(arrayToCommaDelimitedString(bcc)).append("\n");\r
+ buf.append("Reply-To: ").append(replyTo != null ? replyTo.toUnicodeString() : null).append(\r
+ "\n");\r
+ buf.append("Subject: ").append(subject).append("\n");\r
+\r
+ if (headers != null) {\r
+ for (Iterator itr = headers.keySet().iterator(); itr.hasNext();) {\r
+ String header = (String)itr.next();\r
+ String value = (String)headers.get(header);\r
+ buf.append(header).append(": ").append(value).append("\n");\r
+ }\r
+ }\r
+\r
+ buf.append("\n");\r
+ buf.append(text);\r
+\r
+ if (htmlText != null) {\r
+ buf.append("\n\n-----\n\n");\r
+ buf.append(htmlText);\r
+ }\r
+\r
+ if (isFileAttached()) {\r
+ buf.append("\n\nAttachments\n");\r
+ for (int i = 0, num = attachmentFiles.size(); i < num; i++) {\r
+ AttachmentFile f = (AttachmentFile)attachmentFiles.get(i);\r
+ buf.append("[").append(i + 1).append("] ").append(f.getName()).append("\n");\r
+ }\r
+ }\r
+\r
+ return buf.toString();\r
+ }\r
+\r
+ /**\r
+ * @return Returns the message.\r
+ */\r
+ public MimeMessage getMessage() {\r
+ return message;\r
+ }\r
+\r
+ /**\r
+ * @param message The message to set.\r
+ */\r
+ public void setMessage(MimeMessage message) {\r
+ this.message = message;\r
+ }\r
+\r
+ /**\r
+ * メールサーバとの接続切断時に、このメールをメールサーバから削除します。\r
+ * 削除できるように設定ができた場合に true を返します。\r
+ * <p>\r
+ * このメソッドは、<code>FetchMailPro</code>のメソッドによって取得された\r
+ * <code>ReceivedMail</code>インスタンスでのみ有効です。\r
+ * また、<code>FetchMailPro</code>インスタンスがメールサーバに\r
+ * 接続されている状態での呼び出しのみ有効です。<br>\r
+ * これらの条件が満たされない時にこのメソッドが呼び出された場合\r
+ * false を返します。\r
+ * \r
+ * TODO: うまく動いてない。\r
+ * \r
+ * @see FetchMailPro\r
+ * @param delete 削除するように設定する場合 true\r
+ * @return 削除設定が正常に行われた場合 true\r
+ */\r
+ public boolean setDelete(boolean delete) {\r
+ if (message != null) {\r
+ try {\r
+ message.setFlag(Flags.Flag.DELETED, delete);\r
+ } catch (MessagingException e) {\r
+ return false;\r
+ }\r
+ return true;\r
+ }\r
+ return false;\r
+ }\r
+\r
+ /**\r
+ * メールのサイズ(容量)を返します。単位はbyte。\r
+ * この値は厳密なものではないので注意してください。\r
+ * \r
+ * @see MimeMessage#getSize()\r
+ * @return メールのサイズ(単位はbyte)\r
+ */\r
+ public int getSize() {\r
+ return size;\r
+ }\r
+\r
+ /**\r
+ * メールのサイズ(容量)をセットします。単位はbyte。\r
+ * \r
+ * @param size メールのサイズ(単位はbyte)\r
+ */\r
+ public void setSize(int size) {\r
+ this.size = size;\r
+ }\r
+\r
+ /**\r
+ * 添付ファイルのFileインスタンス配列を返します。\r
+ * 添付ファイルがない場合は空の配列を返します。\r
+ * \r
+ * @return 添付ファイルのFileインスタンス配列\r
+ */\r
+ public File[] getFiles() {\r
+ AttachmentFile[] aFiles = getAttachmentFiles();\r
+ File[] files = new File[aFiles.length];\r
+ for (int i = 0; i < aFiles.length; i++) {\r
+ AttachmentFile aFile = aFiles[i];\r
+ files[i] = aFile.getFile();\r
+ }\r
+ return files;\r
+ }\r
+\r
+ /**\r
+ * このメールの返信メール用Mailインスタンスを生成して返します。\r
+ * <ul>\r
+ * <li>宛先(Toアドレス)には、このメールのReply-To、またはFromがセットされます。</li>\r
+ * <li>件名には、このメールの件名が大文字小文字問わず「Re:」で始まっていなければ、「Re: 」を頭に付けた件名がセットされます。「Re:」で始まっている場合には、その件名をそのままセットします。</li>\r
+ * <li>本文には、何もセットされません。</li>\r
+ * <li>このメールにMessage-IDがセットされていれば、In-Reply-Toヘッダにその値がセットされます。</li>\r
+ * <li>このメールにMessage-IDがセットされていれば、Referencesヘッダにその値が加えられます。</li>\r
+ * </ul>\r
+ * \r
+ * @return 返信用のMailインスタンス\r
+ */\r
+ public Mail reply() {\r
+ Mail mail = new Mail();\r
+\r
+ // 宛先\r
+ if (getReplyTo() != null) {\r
+ mail.addTo(getReplyTo());\r
+ } else {\r
+ mail.addTo(getFrom());\r
+ }\r
+\r
+ // 件名\r
+ String subject = getSubject();\r
+ if ((subject.length() >= 3 && !"Re:".equalsIgnoreCase(subject.substring(0, 3)))\r
+ || subject.length() < 3) {\r
+ subject = replySubjectPrefix + subject;\r
+ }\r
+ mail.setSubject(subject);\r
+\r
+ // In-Reply-To, References\r
+ String messageId = getMessageId();\r
+ if (messageId != null && !"<>".equals(messageId)) {\r
+ String references = getRefereces();\r
+ if (references != null) {\r
+ references = messageId + " " + references;\r
+ } else if (getInReplyTo() != null) {\r
+ references = messageId + " " + getInReplyTo();\r
+ } else {\r
+ references = messageId;\r
+ }\r
+ mail.addHeader("References", references);\r
+ mail.addHeader("In-Reply-To", messageId);\r
+ }\r
+\r
+ return mail;\r
+ }\r
+\r
+ /**\r
+ * Receivedヘッダフィールドを追加します。\r
+ * \r
+ * @param rh Receivedヘッダフィールド\r
+ */\r
+ public void addReceviedHeader(ReceivedHeader rh) {\r
+ if (receivedHeaders == null) {\r
+ receivedHeaders = new ArrayList();\r
+ }\r
+ receivedHeaders.add(rh);\r
+ }\r
+\r
+ /**\r
+ * Receivedヘッダフィールドの配列を返します。<br>\r
+ * 自分のサーバ(このメールが届いたサーバ)から送信元のメールサーバを辿る順で並んでいます。<br>\r
+ * 受信メールがReceivedヘッダフィールドを持たない、または解析できなかった場合は空の配列を返します。\r
+ * \r
+ * @return Receivedヘッダフィールドの配列\r
+ */\r
+ public ReceivedHeader[] getReceivedHeaders() {\r
+ if (receivedHeaders == null) {\r
+ return new ReceivedHeader[0];\r
+ }\r
+ return (ReceivedHeader[])receivedHeaders\r
+ .toArray(new ReceivedHeader[receivedHeaders.size()]);\r
+ }\r
+\r
+ /**\r
+ * Receviedヘッダフィールドを表すクラス。\r
+ */\r
+ public static class ReceivedHeader {\r
+\r
+ private String from;\r
+\r
+ private String by;\r
+\r
+ /**\r
+ * @param from メールを送信したサーバのホスト名\r
+ * @param by メールを受信したサーバのホスト名\r
+ */\r
+ public ReceivedHeader(String from, String by) {\r
+ this.from = from;\r
+ this.by = by;\r
+ }\r
+\r
+ /**\r
+ * @see java.lang.Object#toString()\r
+ */\r
+ public String toString() {\r
+ return "Sent from " + from + " and received by " + by;\r
+ }\r
+\r
+ /**\r
+ * メールを受信したサーバのホスト名を返します。\r
+ * \r
+ * @return メールを受信したサーバのホスト名\r
+ */\r
+ public String getBy() {\r
+ return by;\r
+ }\r
+\r
+ /**\r
+ * メールを送信したサーバのホスト名を返します。\r
+ * \r
+ * @return メールを送信したサーバのホスト名\r
+ */\r
+ public String getFrom() {\r
+ return from;\r
+ }\r
+ }\r
+}
\ No newline at end of file
--- /dev/null
+package com.ozacc.mail.fetch.impl;\r
+\r
+import com.ozacc.mail.MailException;\r
+import com.ozacc.mail.fetch.FetchMail;\r
+import com.ozacc.mail.fetch.ReceivedMail;\r
+\r
+/**\r
+ * <code>FetchMail</code>インターフェースの実装クラス。\r
+ * <p>\r
+ * <code>FetchMailProImpl</code>クラスに処理を委譲しています。\r
+ * \r
+ * @since 1.2\r
+ * @see FetchMailProImpl\r
+ * \r
+ * @author Tomohiro Otsuka\r
+ * @version $Id: FetchMailImpl.java,v 1.1.2.6 2005/01/29 22:33:40 otsuka Exp $\r
+ */\r
+public class FetchMailImpl implements FetchMail {\r
+\r
+ /** デフォルトのSMTPサーバ。「localhost」 */\r
+ public static final String DEFAULT_HOST = "localhost";\r
+\r
+ /** デフォルトのプロトコル。「pop3」 */\r
+ public static final String DEFAULT_PROTOCOL = "pop3";\r
+\r
+ /**\r
+ * デフォルトのポート。「-1」<br>\r
+ * -1はプロトコルに応じた適切なポートを設定する特別な値。\r
+ */\r
+ public static final int DEFAULT_PORT = -1;\r
+\r
+ private static final String INBOX_NAME = "INBOX";\r
+\r
+ private String host = DEFAULT_HOST;\r
+\r
+ private String protocol = DEFAULT_PROTOCOL;\r
+\r
+ private int port = DEFAULT_PORT;\r
+\r
+ private String username;\r
+\r
+ private String password;\r
+\r
+ /**\r
+ * コンストラクタ。\r
+ */\r
+ public FetchMailImpl() {}\r
+\r
+ /**\r
+ * @see com.ozacc.mail.fetch.FetchMail#getMails()\r
+ */\r
+ public ReceivedMail[] getMails() throws MailException {\r
+ return getMails(false);\r
+ }\r
+\r
+ /**\r
+ * @see com.ozacc.mail.fetch.FetchMail#getMails(boolean)\r
+ */\r
+ public ReceivedMail[] getMails(boolean delete) throws MailException {\r
+ FetchMailProImpl fetchMailProImpl = createFetchMailProImpl();\r
+ fetchMailProImpl.connect();\r
+ try {\r
+ return fetchMailProImpl.getMails(delete);\r
+ } finally {\r
+ fetchMailProImpl.disconnect();\r
+ }\r
+ }\r
+\r
+ /**\r
+ * サーバ情報をセットしたFetchMailProImplインスタンスを生成します。\r
+ * \r
+ * @return サーバ情報をセットしたFetchMailProImplインスタンス\r
+ */\r
+ private FetchMailProImpl createFetchMailProImpl() {\r
+ FetchMailProImpl fmp = new FetchMailProImpl();\r
+ fmp.setHost(host);\r
+ fmp.setPort(port);\r
+ fmp.setProtocol(protocol);\r
+ fmp.setUsername(username);\r
+ fmp.setPassword(password);\r
+ return fmp;\r
+ }\r
+\r
+ /**\r
+ * メールサーバのホスト名、またはIPアドレスをセットします。\r
+ * デフォルトは localhost です。\r
+ * \r
+ * @param host メールサーバのホスト名、またはIPアドレス\r
+ */\r
+ public void setHost(String host) {\r
+ this.host = host;\r
+ }\r
+\r
+ /**\r
+ * メールサーバの認証パスワード名をセットします。\r
+ * \r
+ * @param password メールサーバの認証パスワード\r
+ */\r
+ public void setPassword(String password) {\r
+ this.password = password;\r
+ }\r
+\r
+ /**\r
+ * メール受信に使用するポート番号をセットします。\r
+ * プロトコルに応じたポート番号が自動的に使用されますので、通常ここでポート番号をセットする必要はありません。\r
+ * \r
+ * @param port ポート番号\r
+ */\r
+ public void setPort(int port) {\r
+ this.port = port;\r
+ }\r
+\r
+ /**\r
+ * メール受信に使用するプロトコロルをセットします。\r
+ * 現在サポートされているプロトコルは、「pop3」と「imap」の二つです。\r
+ * デフォルトは「pop3」です。\r
+ * <p>\r
+ * POP3サーバへの認証をAPOPで行いたい場合は、プロトコル名ではありませんが、\r
+ * 「apop」を指定してください。APOP認証を使用するには、JavaMail 1.3.2以降が必要です。\r
+ * \r
+ * @param protocol プロトコル\r
+ */\r
+ public void setProtocol(String protocol) {\r
+ this.protocol = protocol;\r
+ }\r
+\r
+ /**\r
+ * メールサーバの認証ユーザ名をセットします。\r
+ * \r
+ * @param username メールサーバの認証ユーザ名\r
+ */\r
+ public void setUsername(String username) {\r
+ this.username = username;\r
+ }\r
+\r
+ /**\r
+ * メールサーバのホスト名、またはIPアドレスを返します。\r
+ * \r
+ * @return メールサーバのホスト名、またはIPアドレス\r
+ */\r
+ public String getHost() {\r
+ return host;\r
+ }\r
+\r
+ /**\r
+ * メールサーバの認証パスワードを返します。\r
+ * \r
+ * @return メールサーバの認証パスワード\r
+ */\r
+ public String getPassword() {\r
+ return password;\r
+ }\r
+\r
+ /**\r
+ * @return ポート番号\r
+ */\r
+ public int getPort() {\r
+ return port;\r
+ }\r
+\r
+ /**\r
+ * メール受信に使用するプロトコロルをセットします。\r
+ * \r
+ * @return プロトコル\r
+ */\r
+ public String getProtocol() {\r
+ return protocol;\r
+ }\r
+\r
+ /**\r
+ * メールサーバの認証ユーザ名を返します。\r
+ * \r
+ * @return メールサーバの認証ユーザ名\r
+ */\r
+ public String getUsername() {\r
+ return username;\r
+ }\r
+}
\ No newline at end of file
--- /dev/null
+package com.ozacc.mail.fetch.impl;\r
+\r
+import java.util.Properties;\r
+\r
+import javax.mail.AuthenticationFailedException;\r
+import javax.mail.Flags;\r
+import javax.mail.Folder;\r
+import javax.mail.Message;\r
+import javax.mail.MessagingException;\r
+import javax.mail.NoSuchProviderException;\r
+import javax.mail.Session;\r
+import javax.mail.Store;\r
+import javax.mail.internet.MimeMessage;\r
+\r
+import org.apache.commons.logging.Log;\r
+import org.apache.commons.logging.LogFactory;\r
+\r
+import com.ozacc.mail.MailAuthenticationException;\r
+import com.ozacc.mail.MailException;\r
+import com.ozacc.mail.NotConnectedException;\r
+import com.ozacc.mail.fetch.FetchMailPro;\r
+import com.ozacc.mail.fetch.MailConverter;\r
+import com.ozacc.mail.fetch.MailFetchException;\r
+import com.ozacc.mail.fetch.ReceivedMail;\r
+\r
+/**\r
+ * <code>FetchMail</code>インターフェースの実装クラス。\r
+ * <p>\r
+ * このクラスのインスタンスは、インスタンス変数を用いて状態を保持するため、\r
+ * ステートレスではありません。ステートフルです。\r
+ * \r
+ * @since 1.2\r
+ * @author Tomohiro Otsuka\r
+ * @author gaku\r
+ * @version $Id: FetchMailProImpl.java,v 1.1.2.13 2005/04/10 05:22:24 otsuka Exp $\r
+ */\r
+public class FetchMailProImpl implements FetchMailPro {\r
+\r
+ private static Log log = LogFactory.getLog(FetchMailProImpl.class);\r
+\r
+ /** デフォルトのSMTPサーバ。「localhost」 */\r
+ public static final String DEFAULT_HOST = "localhost";\r
+\r
+ /** デフォルトのプロトコル。「pop3」 */\r
+ public static final String DEFAULT_PROTOCOL = "pop3";\r
+\r
+ /**\r
+ * デフォルトのポート。「-1」<br>\r
+ * -1はプロトコルに応じた適切なポートを設定する特別な値。\r
+ */\r
+ public static final int DEFAULT_PORT = -1;\r
+\r
+ private static final String INBOX_NAME = "INBOX";\r
+\r
+ private String host = DEFAULT_HOST;\r
+\r
+ private String protocol = DEFAULT_PROTOCOL;\r
+\r
+ private int port = DEFAULT_PORT;\r
+\r
+ private String username;\r
+\r
+ private String password;\r
+\r
+ private boolean javaMailLogEnabled;\r
+\r
+ private Store store;\r
+\r
+ private Folder currentFolder;\r
+\r
+ /** MailConver の実装インスタンス。 */\r
+ private MailConverter mailConverter = new MailConverterImpl();\r
+\r
+ /**\r
+ * コンストラクタ。\r
+ */\r
+ public FetchMailProImpl() {\r
+ System.setProperty("mail.mime.multipart.ignoremissingendboundary", "true");\r
+ }\r
+\r
+ /**\r
+ * @see com.ozacc.mail.fetch.FetchMailPro#connect()\r
+ */\r
+ public synchronized void connect() throws MailException {\r
+ if (isConnected()) {\r
+ log.warn("既にサーバ[" + host + "]に接続されています。再接続するには先に接続を切断する必要があります。");\r
+ return;\r
+ }\r
+\r
+ log.debug(protocol.toUpperCase() + "サーバ[" + host + "]に接続します。");\r
+ Session session = Session.getInstance(createProperties(), null);\r
+ if (javaMailLogEnabled) {\r
+ session.setDebug(true);\r
+ }\r
+ try {\r
+ store = session.getStore(protocol);\r
+ store.connect(host, port, username, password);\r
+ } catch (NoSuchProviderException e) {\r
+ log.error("指定されたプロトコル[" + protocol + "]はサポートされていません。", e);\r
+ throw new MailException("指定されたプロトコル[" + protocol + "]はサポートされていません。", e);\r
+ } catch (AuthenticationFailedException e) {\r
+ log.error(protocol.toUpperCase() + "サーバ[" + host + "]への接続認証に失敗しました。", e);\r
+ throw new MailAuthenticationException(protocol.toUpperCase() + "サーバ[" + host\r
+ + "]への接続認証に失敗しました。", e);\r
+ } catch (MessagingException e) {\r
+ log.error(protocol.toUpperCase() + "サーバ[" + host + "]への接続に失敗しました。", e);\r
+ throw new MailException(protocol.toUpperCase() + "サーバ[" + host + "]への接続に失敗しました。", e);\r
+ }\r
+ log.info(protocol.toUpperCase() + "サーバ[" + host + "]に接続しました。");\r
+\r
+ changeFolder(INBOX_NAME);\r
+ }\r
+\r
+ /**\r
+ * Sessionに渡すPropertiesインスタンスを返します。\r
+ * APOP認証を行う場合に、"mail.pop3.apop.enable"をセットします。\r
+ * \r
+ * @return Sessionに渡すPropertiesインスタンス\r
+ */\r
+ private Properties createProperties() {\r
+ Properties prop = new Properties();\r
+ if ("apop".equalsIgnoreCase(protocol)) {\r
+ prop.put("mail.pop3.apop.enable", "true");\r
+ }\r
+ return prop;\r
+ }\r
+\r
+ /**\r
+ * @see com.ozacc.mail.fetch.FetchMailPro#disconnect()\r
+ */\r
+ public synchronized void disconnect() throws MailException {\r
+ try {\r
+ closeCurrentFolderIfOpen();\r
+ } finally {\r
+ if (isConnected()) {\r
+ log.debug(protocol.toUpperCase() + "サーバ[" + host + "]との接続を切断します。");\r
+ try {\r
+ store.close();\r
+ store = null;\r
+ } catch (MessagingException e) {\r
+ throw new MailException("サーバ[" + host + "]との接続切断に失敗しました。", e);\r
+ }\r
+ }\r
+ }\r
+ log.info(protocol.toUpperCase() + "サーバ[" + host + "]との接続を切断しました。");\r
+ }\r
+\r
+ /**\r
+ * 現在のメッセージフォルダをクローズします。\r
+ * \r
+ * @throws MailException メッセージフォルダのクローズに失敗した場合\r
+ */\r
+ private void closeCurrentFolderIfOpen() throws MailException {\r
+ if (currentFolder != null && currentFolder.isOpen()) {\r
+ log.debug("メッセージフォルダ[" + currentFolder.getName() + "]をクローズします。");\r
+ try {\r
+ currentFolder.close(true);\r
+ } catch (MessagingException e) {\r
+ log.error("メッセージフォルダ[" + currentFolder.getName() + "]のクローズに失敗しました。", e);\r
+ throw new MailException("メッセージフォルダ[" + currentFolder.getName() + "]のクローズに失敗しました。",\r
+ e);\r
+ }\r
+ log.debug("メッセージフォルダ[" + currentFolder.getName() + "]をクローズしました。");\r
+ currentFolder = null;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * @see com.ozacc.mail.fetch.FetchMailPro#changeFolder(java.lang.String)\r
+ */\r
+ public synchronized void changeFolder(String folderName) throws MailException {\r
+ if (!isConnected()) {\r
+ log.warn("メールサーバに接続されていません。");\r
+ return;\r
+ }\r
+\r
+ closeCurrentFolderIfOpen();\r
+ log.debug("メッセージフォルダ[" + folderName + "]をオープンします。");\r
+ try {\r
+ currentFolder = store.getFolder(folderName);\r
+ currentFolder.open(Folder.READ_WRITE);\r
+ } catch (MessagingException e) {\r
+ log.error("メッセージフォルダ[" + folderName + "]のオープンに失敗しました。", e);\r
+ throw new MailException("メッセージフォルダ[" + folderName + "]のオープンに失敗しました。", e);\r
+ }\r
+ log.debug("メッセージフォルダ[" + folderName + "]をオープンしました。");\r
+ }\r
+\r
+ /**\r
+ * @see com.ozacc.mail.fetch.FetchMailPro#getMailCount()\r
+ */\r
+ public int getMailCount() throws MailException {\r
+ checkIfCurrentFolderIsOpen();\r
+ try {\r
+ return currentFolder.getMessageCount();\r
+ } catch (MessagingException e) {\r
+ throw new MailFetchException("メール数の取得に失敗しました。", e);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * メールサーバに接続されていて、フォルダが操作できる状態かどうか調べます。\r
+ * フォルダが操作できる状態にない場合、NotConnectedExceptionをスローします。\r
+ * \r
+ * @throws NotConnectedException\r
+ */\r
+ private void checkIfCurrentFolderIsOpen() throws NotConnectedException {\r
+ if (currentFolder == null || !currentFolder.isOpen()) {\r
+ throw new NotConnectedException(protocol.toUpperCase() + "サーバ[" + host + "]に接続されていません。");\r
+ }\r
+ }\r
+\r
+ /**\r
+ * @see com.ozacc.mail.fetch.FetchMailPro#getMail(int)\r
+ */\r
+ public ReceivedMail getMail(int num) throws MailException {\r
+ return getMail(num, false);\r
+ }\r
+\r
+ /**\r
+ * @see com.ozacc.mail.fetch.FetchMailPro#getMail(int, boolean)\r
+ */\r
+ public ReceivedMail getMail(int num, boolean delete) throws MailException {\r
+ MimeMessage mimeMessage = getMessage(num);\r
+ try {\r
+ mimeMessage.setFlag(Flags.Flag.DELETED, delete);\r
+ log.debug(num + "番目のメッセージにDELETEDフラグをセットしました。");\r
+ } catch (MessagingException e) {\r
+ throw new MailException("DELETEDフラグのセットに失敗しました。", e);\r
+ }\r
+ return mailConverter.convertIntoMail(mimeMessage);\r
+ }\r
+\r
+ /**\r
+ * @see com.ozacc.mail.fetch.FetchMailPro#getMails(boolean)\r
+ */\r
+ public ReceivedMail[] getMails(boolean delete) throws MailException {\r
+ MimeMessage[] mimeMessages = getMessages(delete);\r
+ return mailConverter.convertIntoMails(mimeMessages);\r
+ }\r
+\r
+ /**\r
+ * @see com.ozacc.mail.fetch.FetchMailPro#getMessage(int)\r
+ */\r
+ public synchronized MimeMessage getMessage(int num) throws MailException {\r
+ checkIfCurrentFolderIsOpen();\r
+ try {\r
+ return (MimeMessage)currentFolder.getMessage(num);\r
+ } catch (MessagingException e) {\r
+ log.error("メッセージの取得に失敗しました。", e);\r
+ throw new MailFetchException("メッセージの取得に失敗しました。", e);\r
+ }\r
+ }\r
+\r
+ public synchronized MimeMessage[] getMessages(boolean delete) throws MailException {\r
+ checkIfCurrentFolderIsOpen();\r
+ try {\r
+ Message[] messages = currentFolder.getMessages();\r
+ if (log.isInfoEnabled()) {\r
+ if (messages.length > 0) {\r
+ log.info(messages.length + "通のメールを受信します。");\r
+ } else {\r
+ log.info("受信するメールはありません。");\r
+ }\r
+ }\r
+ // SEENフラグを立てる\r
+ currentFolder.setFlags(messages, new Flags(Flags.Flag.SEEN), true);\r
+ // DELETEDフラグを立てる\r
+ if (delete) {\r
+ currentFolder.setFlags(messages, new Flags(Flags.Flag.DELETED), true);\r
+ }\r
+ MimeMessage[] mimeMessages = new MimeMessage[messages.length];\r
+ for (int i = 0; i < messages.length; i++) {\r
+ mimeMessages[i] = (MimeMessage)messages[i];\r
+ }\r
+ return mimeMessages;\r
+ } catch (MessagingException e) {\r
+ log.error("メッセージの取得に失敗しました。", e);\r
+ throw new MailFetchException("メッセージの取得に失敗しました。", e);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * @see com.ozacc.mail.fetch.FetchMailPro#isConnected()\r
+ */\r
+ public boolean isConnected() {\r
+ return store != null && store.isConnected();\r
+ }\r
+\r
+ /**\r
+ * メールサーバのホスト名、またはIPアドレスを返します。\r
+ * \r
+ * @return メールサーバのホスト名、またはIPアドレス\r
+ */\r
+ public String getHost() {\r
+ return host;\r
+ }\r
+\r
+ /**\r
+ * メールサーバのホスト名、またはIPアドレスをセットします。\r
+ * デフォルトは localhost です。\r
+ * \r
+ * @param host メールサーバのホスト名、またはIPアドレス\r
+ */\r
+ public void setHost(String host) {\r
+ this.host = host;\r
+ }\r
+\r
+ /**\r
+ * メールサーバの認証パスワードを返します。\r
+ * \r
+ * @return メールサーバの認証パスワード\r
+ */\r
+ public String getPassword() {\r
+ return password;\r
+ }\r
+\r
+ /**\r
+ * メールサーバの認証パスワード名をセットします。\r
+ * \r
+ * @param password メールサーバの認証パスワード\r
+ */\r
+ public void setPassword(String password) {\r
+ this.password = password;\r
+ }\r
+\r
+ /**\r
+ * メール受信に使用するプロトコロルをセットします。\r
+ * \r
+ * @return プロトコル\r
+ */\r
+ public String getProtocol() {\r
+ return protocol;\r
+ }\r
+\r
+ /**\r
+ * メール受信に使用するプロトコロルをセットします。\r
+ * 現在サポートされているプロトコルは、「pop3」と「imap」の二つです。\r
+ * デフォルトは「pop3」です。\r
+ * <p>\r
+ * POP3サーバへの認証をAPOPで行いたい場合は、プロトコル名ではありませんが、\r
+ * 「apop」を指定してください。APOP認証を使用するには、JavaMail 1.3.2以降が必要です。\r
+ * \r
+ * @param protocol プロトコル\r
+ */\r
+ public void setProtocol(String protocol) {\r
+ this.protocol = protocol;\r
+ }\r
+\r
+ /**\r
+ * @return 認証ユーザ名\r
+ */\r
+ public String getUsername() {\r
+ return username;\r
+ }\r
+\r
+ /**\r
+ * メールサーバの認証ユーザ名をセットします。\r
+ * \r
+ * @param username 認証ユーザ名\r
+ */\r
+ public void setUsername(String username) {\r
+ this.username = username;\r
+ }\r
+\r
+ /**\r
+ * @return ポート番号\r
+ */\r
+ public int getPort() {\r
+ return port;\r
+ }\r
+\r
+ /**\r
+ * メール受信に使用するポート番号をセットします。\r
+ * プロトコルに応じたポート番号が自動的に使用されますので、通常ここでポート番号をセットする必要はありません。\r
+ * \r
+ * @param port ポート番号\r
+ */\r
+ public void setPort(int port) {\r
+ this.port = port;\r
+ }\r
+\r
+ /**\r
+ * JavaMailのデバッグが有効かどうか判定します。\r
+ * \r
+ * @return JavaMailのデバッグが有効な場合 ture\r
+ */\r
+ public boolean isJavaMailLogEnabled() {\r
+ return javaMailLogEnabled;\r
+ }\r
+\r
+ /**\r
+ * JavaMailのデバッグを有効にするかどうか指定します。\r
+ * 有効にすると、<code>System.out</code>のデバッグメッセージが出力されます。<br>\r
+ * デフォルトは無効になっています。\r
+ * \r
+ * @see javax.mail.session#setDebug(boolean)\r
+ * @param javaMailLogEnabled The javaMailLogEnabled to set.\r
+ */\r
+ public void setJavaMailLogEnabled(boolean javaMailLogEnabled) {\r
+ this.javaMailLogEnabled = javaMailLogEnabled;\r
+ }\r
+\r
+ /**\r
+ * MailConveterインターフェースの実装インスタンスをセットします。\r
+ * デフォルトでは、MailConverterImplが使用されます。\r
+ * \r
+ * @see com.ozacc.mail.fetch.MailConveter\r
+ * @see com.ozacc.mail.fetch.impl.MailConveterImpl\r
+ * @param mailConverter MailConveterインターフェースの実装インスタンス\r
+ */\r
+ public void setMailConverter(MailConverter mailConverter) {\r
+ this.mailConverter = mailConverter;\r
+ }\r
+}
\ No newline at end of file
--- /dev/null
+package com.ozacc.mail.fetch.impl;\r
+\r
+import java.io.BufferedOutputStream;\r
+import java.io.File;\r
+import java.io.FileNotFoundException;\r
+import java.io.FileOutputStream;\r
+import java.io.FilenameFilter;\r
+import java.io.IOException;\r
+import java.io.InputStream;\r
+import java.io.OutputStream;\r
+import java.util.Date;\r
+import java.util.Enumeration;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+import javax.mail.Address;\r
+import javax.mail.Header;\r
+import javax.mail.Message;\r
+import javax.mail.MessagingException;\r
+import javax.mail.internet.AddressException;\r
+import javax.mail.internet.InternetAddress;\r
+import javax.mail.internet.MimeMessage;\r
+\r
+import org.apache.commons.logging.Log;\r
+import org.apache.commons.logging.LogFactory;\r
+\r
+import com.ozacc.mail.fetch.MailConverter;\r
+import com.ozacc.mail.fetch.ReceivedMail;\r
+import com.ozacc.mail.fetch.ReceivedMail.ReceivedHeader;\r
+import com.ozacc.mail.fetch.impl.sk_jp.AttachmentsExtractor;\r
+import com.ozacc.mail.fetch.impl.sk_jp.HtmlPartExtractor;\r
+import com.ozacc.mail.fetch.impl.sk_jp.MailUtility;\r
+import com.ozacc.mail.fetch.impl.sk_jp.MultipartUtility;\r
+\r
+/**\r
+ * MimeMessageからMailを生成するクラス。\r
+ * <p>\r
+ * 変換時に生じたチェック例外は、このクラス内でキャッチされ無視されます。\r
+ * 例外が生じた項目(差出人や宛先など)に該当するMailインスタンスのプロパティには何もセットされません。\r
+ * \r
+ * @since 1.2\r
+ * @author Tomohiro Otsuka\r
+ * @author gaku\r
+ * @version $Id: MailConverterImpl.java,v 1.1.2.3 2006/03/03 06:01:18 otsuka Exp $\r
+ */\r
+public class MailConverterImpl implements MailConverter {\r
+\r
+ private static final String ATTACHMENT_DIR_PREFIX = "OML_";\r
+\r
+ private static final String JAVA_IO_TMPDIR = "java.io.tmpdir";\r
+\r
+ private static Log log = LogFactory.getLog(MailConverterImpl.class);\r
+\r
+ private static Pattern receivedHeaderPattern = Pattern.compile("^from (.+?) .*by (.+?) .*$");\r
+\r
+ /**\r
+ * 保存された添付ファイルの生存時間。デフォルトは12時間。\r
+ */\r
+ private long attachmentLifetime = 3600 * 1000 * 12;\r
+\r
+ /**\r
+ * コンストラクタ。\r
+ */\r
+ public MailConverterImpl() {}\r
+\r
+ /**\r
+ * @see com.ozacc.mail.fetch.MailConverter#convertIntoMails(javax.mail.internet.MimeMessage[])\r
+ */\r
+ public ReceivedMail[] convertIntoMails(MimeMessage[] messages) {\r
+ log.debug("計" + messages.length + "通のMimeMessageをMailに変換します。");\r
+ ReceivedMail[] results = new ReceivedMail[messages.length];\r
+ for (int i = 0; i < messages.length; i++) {\r
+ log.debug((i + 1) + "通目のMimeMessageをMailに変換します。");\r
+ results[i] = convertIntoMail(messages[i]);\r
+ log.debug((i + 1) + "通目のMimeMessageをMailに変換しました。");\r
+ log.debug(results[i].toString());\r
+ }\r
+ log.debug("計" + messages.length + "通のMimeMessageをMailに変換しました。");\r
+ return results;\r
+ }\r
+\r
+ /**\r
+ * @param mm\r
+ * @param mail \r
+ */\r
+ private void setReceivedHeaders(MimeMessage mm, ReceivedMail mail) {\r
+ String[] headerValues = null;\r
+ try {\r
+ headerValues = mm.getHeader("Received");\r
+ } catch (MessagingException e) {\r
+ // ignore\r
+ log.warn(e.getMessage());\r
+ }\r
+ if (headerValues != null) {\r
+ for (int i = 0; i < headerValues.length; i++) {\r
+ String received = headerValues[i];\r
+ // from で始まるものだけを抽出し、改行を削除\r
+ if (received.startsWith("from")) {\r
+ received = received.replaceAll("\n", "").replaceAll("\\s+", " ");\r
+ log.debug("Received='" + received + "'");\r
+\r
+ Matcher m = receivedHeaderPattern.matcher(received);\r
+ if (m.matches()) {\r
+ String from = m.group(1);\r
+ String by = m.group(2);\r
+ log.debug("Sent from '" + from + "', Received by '" + by + "'");\r
+ ReceivedHeader rh = new ReceivedHeader(from, by);\r
+ mail.addReceviedHeader(rh);\r
+ }\r
+ }\r
+ }\r
+\r
+ }\r
+ }\r
+\r
+ /**\r
+ * 指定されたMimeMessageに添付されているファイルを全て抽出し、\r
+ * システムプロパティにセットされた一時ファイルディレクトリ内に保存した後、\r
+ * そのファイルを指定されたReceivedMailにセットします。\r
+ * <p>\r
+ * 保存された添付ファイルはJVM終了時に削除されます。\r
+ * \r
+ * @param mm\r
+ * @param mail \r
+ */\r
+ private void setAttachmentFiles(MimeMessage mm, ReceivedMail mail) {\r
+ try {\r
+ cleanTempDir();\r
+\r
+ AttachmentsExtractor ae = new AttachmentsExtractor(\r
+ AttachmentsExtractor.MODE_IGNORE_MESSAGE);\r
+ MultipartUtility.process(mm, ae);\r
+ for (int i = 0, num = ae.getCount(); i < num; i++) {\r
+ String fileName = ae.getFileName(i);\r
+ if (fileName == null || "".equals(fileName)) {\r
+ fileName = "attachment" + (i + 1) + ".tmp";\r
+ }\r
+ String path = getTempDirPath() + File.separator + ATTACHMENT_DIR_PREFIX\r
+ + System.currentTimeMillis() + File.separator + fileName;\r
+ log.debug((i + 1) + "個目の添付ファイルを保存します。[" + path + "]");\r
+ File f = new File(path);\r
+ f.getParentFile().mkdirs();\r
+ InputStream is = ae.getInputStream(i);\r
+ try {\r
+ writeTo(f, is);\r
+ } finally {\r
+ if (is != null) {\r
+ is.close();\r
+ }\r
+ }\r
+\r
+ f.getParentFile().deleteOnExit();\r
+ f.deleteOnExit();\r
+\r
+ mail.addFile(f, fileName);\r
+ log.debug((i + 1) + "個目の添付ファイルを保存しました。[" + path + "]");\r
+ }\r
+ } catch (IOException e) {\r
+ log.error("添付ファイルの取得に失敗しました。", e);\r
+ } catch (MessagingException e) {\r
+ // ignore\r
+ log.warn(e.getMessage());\r
+ }\r
+ }\r
+\r
+ /**\r
+ * 一時ディレクトリ内に保存された添付ファイルの内、生存時間を越えているものを削除します。\r
+ */\r
+ private void cleanTempDir() {\r
+ File tempDir = new File(getTempDirPath());\r
+ File[] omlDirs = tempDir.listFiles(new FilenameFilter() {\r
+\r
+ public boolean accept(File dir, String name) {\r
+ return name.startsWith(ATTACHMENT_DIR_PREFIX);\r
+ }\r
+ });\r
+ log.debug("現在" + omlDirs.length + "個の添付ファイル用ディレクトリ[" + tempDir.getAbsolutePath()\r
+ + "]が一時ディレクトリに存在します。");\r
+ long now = System.currentTimeMillis();\r
+ for (int i = 0; i < omlDirs.length; i++) {\r
+ File dir = omlDirs[i];\r
+ log.debug(dir.lastModified() + "");\r
+ if (now - dir.lastModified() >= attachmentLifetime) {\r
+ deleteDir(dir);\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * 一時ディレクトリのパスを返します。\r
+ * \r
+ * @return 一時ディレクトリのパス\r
+ */\r
+ private String getTempDirPath() {\r
+ return System.getProperty(JAVA_IO_TMPDIR);\r
+ }\r
+\r
+ /**\r
+ * 指定されたディレクトリを中身のファイルを含めて削除します。\r
+ * \r
+ * @param dir 削除するディレクトリ\r
+ */\r
+ private void deleteDir(File dir) {\r
+ File[] files = dir.listFiles();\r
+ for (int i = 0; i < files.length; i++) {\r
+ File f = files[i];\r
+ f.delete();\r
+ }\r
+ dir.delete();\r
+ }\r
+\r
+ /**\r
+ * 指定されたInputStreamデータを指定されたファイルに保存します。\r
+ * \r
+ * @param destFile 保存するファイル\r
+ * @param is ソースとなるInputStream\r
+ * @throws FileNotFoundException\r
+ * @throws IOException \r
+ */\r
+ private void writeTo(File destFile, InputStream is) throws FileNotFoundException, IOException {\r
+ BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destFile),\r
+ 1024 * 50);\r
+ try {\r
+ copy(is, bos);\r
+ } finally {\r
+ if (bos != null) {\r
+ bos.close();\r
+ }\r
+ }\r
+ }\r
+\r
+ private static void copy(InputStream in, OutputStream out) throws IOException {\r
+ byte[] buffer = new byte[1024 * 4];\r
+ int n = 0;\r
+ while (-1 != (n = in.read(buffer))) {\r
+ out.write(buffer, 0, n);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * 指定されたMimeMessageからX-Header、References、In-Reply-Toヘッダを解析し、\r
+ * 指定されたReceivedMailにセットします。\r
+ * \r
+ * @param mm\r
+ * @param mail\r
+ */\r
+ private void setXHeaders(MimeMessage mm, ReceivedMail mail) {\r
+ log.debug("X-HeaderをMailにセットします。");\r
+ Enumeration headerEnum = null;\r
+ try {\r
+ headerEnum = mm.getAllHeaders();\r
+ } catch (MessagingException e) {\r
+ // ignore\r
+ log.warn(e.getMessage());\r
+ }\r
+ while (headerEnum != null && headerEnum.hasMoreElements()) {\r
+ Header header = (Header)headerEnum.nextElement();\r
+ if (header.getName().startsWith("X-")\r
+ || "References".equalsIgnoreCase(header.getName())\r
+ || "In-Reply-To".equalsIgnoreCase(header.getName())) {\r
+ mail.addHeader(header.getName(), header.getValue());\r
+ log.debug(header.getName() + "をMailにセットしました。[" + header.getName() + "='"\r
+ + header.getValue() + "']");\r
+ }\r
+ }\r
+ }\r
+\r
+ private void setReplyToAddress(MimeMessage mm, ReceivedMail mail) {\r
+ log.debug("Reply-ToアドレスをMailにセットします。");\r
+ Address[] addresses = null;\r
+ try {\r
+ addresses = mm.getReplyTo();\r
+ } catch (MessagingException e) {\r
+ // ignore\r
+ log.warn(e.getMessage());\r
+ }\r
+ if (addresses != null) {\r
+ log.debug(addresses.length + "つのReply-Toアドレスが見つかりました。最初のアドレスのみ取得されます。");\r
+ for (int j = 0; j < addresses.length; j++) {\r
+ Address address = addresses[j];\r
+ mail.setReplyTo((InternetAddress)address);\r
+ break;\r
+ }\r
+ } else {\r
+ log.debug("Reply-Toアドレスは見つかりませんでした。");\r
+ }\r
+ }\r
+\r
+ /**\r
+ * メールの容量(byte)をMimeMessageから取得してReceivedMailにセットします。\r
+ * 取得に失敗した場合は -1 をセットします。\r
+ * \r
+ * @param mm\r
+ * @param mail \r
+ */\r
+ private void setSize(MimeMessage mm, ReceivedMail mail) {\r
+ try {\r
+ mail.setSize(mm.getSize());\r
+ } catch (MessagingException e) {\r
+ mail.setSize(-1);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * @param mm\r
+ * @param mail\r
+ * @throws MessagingException \r
+ */\r
+ private void setHtmlText(MimeMessage mm, ReceivedMail mail) {\r
+ try {\r
+ HtmlPartExtractor hpe = new HtmlPartExtractor();\r
+ MultipartUtility.process(mm, hpe);\r
+ String htmlText = hpe.getHtml();\r
+ mail.setHtmlText(htmlText);\r
+ } catch (MessagingException e) {\r
+ // ignore\r
+ log.warn(e.getMessage());\r
+ }\r
+ }\r
+\r
+ private void setText(MimeMessage mm, ReceivedMail mail) {\r
+ try {\r
+ String text = MultipartUtility.getPlainText(mm);\r
+ mail.setText(text);\r
+ } catch (MessagingException e) {\r
+ // ignore\r
+ log.warn(e.getMessage());\r
+ }\r
+ }\r
+\r
+ private void setMessageId(MimeMessage mm, ReceivedMail mail) {\r
+ try {\r
+ String messageId = mm.getMessageID();\r
+ mail.setMessageId(messageId);\r
+ log.debug("Message-IDをMailにセットしました。[Message-ID='" + messageId + "']");\r
+ } catch (MessagingException e) {\r
+ // ignore\r
+ log.warn(e.getMessage());\r
+ }\r
+ }\r
+\r
+ /**\r
+ * 指定されたMimeMessageから件名を取得し、ReceivedMailにセットします。\r
+ * sk_jpのMailUtility.decodeText()メソッドを用いて、件名の文字化けを回避します。\r
+ * \r
+ * @param mm\r
+ * @param mail\r
+ */\r
+ private void setSubject(MimeMessage mm, ReceivedMail mail) {\r
+ try {\r
+ String subject = MailUtility.decodeText(mm.getSubject());\r
+ mail.setSubject(subject);\r
+ } catch (MessagingException e) {\r
+ // ignore\r
+ log.warn(e.getMessage());\r
+ }\r
+ }\r
+\r
+ private void setDate(MimeMessage mm, ReceivedMail mail) {\r
+ try {\r
+ Date d = mm.getSentDate();\r
+ mail.setDate(d);\r
+ } catch (MessagingException e) {\r
+ // ignore\r
+ log.warn(e.getMessage());\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Return-Pathアドレスは必ずしもセットされてはいません。\r
+ * 特にspam系のメールでは不正なフォーマットのメールアドレスが\r
+ * セットされている場合もあるので要注意。\r
+ * \r
+ * @param mm\r
+ * @param mail\r
+ */\r
+ private void setReturnPath(MimeMessage mm, ReceivedMail mail) {\r
+ log.debug("Return-Pathアドレスを検出します。");\r
+ String[] returnPath = null;\r
+ try {\r
+ returnPath = mm.getHeader("Return-Path");\r
+ } catch (MessagingException e) {\r
+ // ignore\r
+ log.warn(e.getMessage());\r
+ }\r
+ if (returnPath != null && returnPath.length > 0) {\r
+ String email = returnPath[0].substring(1, returnPath[0].length() - 1);\r
+ if (email.length() > 0) {\r
+ try {\r
+ mail.setReturnPath(email);\r
+ log.debug("Return-PathアドレスをMailにセットしました。[Return-Path='" + email + "']");\r
+ } catch (IllegalArgumentException e) {\r
+ log.warn("Return-Pathアドレスが不正なメールアドレスフォーマットです。[Return-Path='" + email + "']");\r
+ }\r
+ } else {\r
+ log.debug("Return-Pathアドレスは見つかりませんでした。");\r
+ }\r
+ } else {\r
+ log.debug("Return-Pathアドレスは見つかりませんでした。");\r
+ }\r
+ }\r
+\r
+ private void setFromAddress(MimeMessage mm, ReceivedMail mail) {\r
+ log.debug("Fromアドレスを検出します。");\r
+ Address[] addresses = null;\r
+ try {\r
+ addresses = mm.getFrom();\r
+ } catch (MessagingException e) {\r
+ // ignore\r
+ log.warn(e.getMessage());\r
+ }\r
+ if (addresses != null) {\r
+ log.debug(addresses.length + "つのFromアドレスが見つかりました。");\r
+ for (int j = 0; j < addresses.length; j++) {\r
+ InternetAddress address = (InternetAddress)addresses[j];\r
+ mail.setFrom(address);\r
+ log.debug("FromアドレスをMailにセットしました。[From='" + address.toUnicodeString() + "']");\r
+ }\r
+ } else {\r
+ log.debug("Fromアドレスは見つかりませんでした。");\r
+ }\r
+ }\r
+\r
+ private void setRecipientAddresses(MimeMessage mm, ReceivedMail mail) {\r
+ /*\r
+ * TOアドレスのパース\r
+ */\r
+ log.debug("Toアドレスを検出します。");\r
+ Address[] toAddresses = null;\r
+ try {\r
+ toAddresses = mm.getRecipients(Message.RecipientType.TO);\r
+ } catch (AddressException e) {\r
+ log.warn("不正なメールアドレスが検出されました。[" + e.getRef() + "]");\r
+ } catch (MessagingException e) {\r
+ // ignore\r
+ log.warn(e.getMessage());\r
+ }\r
+ if (toAddresses != null) {\r
+ log.debug(toAddresses.length + "つのToアドレスが見つかりました。");\r
+ for (int j = 0; j < toAddresses.length; j++) {\r
+ InternetAddress address = (InternetAddress)toAddresses[j];\r
+ mail.addTo(address);\r
+ log.debug("ToアドレスをMailにセットしました。[To='" + address.toUnicodeString() + "']");\r
+ }\r
+ } else {\r
+ log.debug("Toアドレスは見つかりませんでした。");\r
+ }\r
+\r
+ /*\r
+ * CCアドレスのパース\r
+ */\r
+ log.debug("Ccアドレスを検出します。");\r
+ Address[] ccAddresses = null;\r
+ try {\r
+ ccAddresses = mm.getRecipients(Message.RecipientType.CC);\r
+ } catch (AddressException e) {\r
+ log.warn("不正なメールアドレスが検出されました。[" + e.getRef() + "]");\r
+ } catch (MessagingException e) {\r
+ // ignore\r
+ log.warn(e.getMessage());\r
+ }\r
+ if (ccAddresses != null) {\r
+ log.debug(ccAddresses.length + "つのCcアドレスが見つかりました。");\r
+ for (int j = 0; j < ccAddresses.length; j++) {\r
+ InternetAddress address = (InternetAddress)ccAddresses[j];\r
+ mail.addCc(address);\r
+ log.debug("CcアドレスをMailにセットしました。[Cc='" + address.toUnicodeString() + "']");\r
+ }\r
+ } else {\r
+ log.debug("Ccアドレスは見つかりませんでした。");\r
+ }\r
+ }\r
+\r
+ /**\r
+ * @see com.ozacc.mail.fetch.MailConverter#convertIntoMail(javax.mail.internet.MimeMessage)\r
+ */\r
+ public ReceivedMail convertIntoMail(MimeMessage mm) {\r
+ ReceivedMail mail = createReceivedMail();\r
+ setReturnPath(mm, mail);\r
+ setReceivedHeaders(mm, mail);\r
+ setDate(mm, mail);\r
+ setFromAddress(mm, mail);\r
+ setRecipientAddresses(mm, mail);\r
+ setMessageId(mm, mail);\r
+ setReplyToAddress(mm, mail);\r
+ setSubject(mm, mail);\r
+ setXHeaders(mm, mail);\r
+ setText(mm, mail);\r
+ setHtmlText(mm, mail);\r
+ setAttachmentFiles(mm, mail);\r
+ setSize(mm, mail);\r
+ mail.setMessage(mm);\r
+ return mail;\r
+ }\r
+\r
+ protected ReceivedMail createReceivedMail() {\r
+ return new ReceivedMail();\r
+ }\r
+\r
+}
\ No newline at end of file
--- /dev/null
+<HTML>\r
+<BODY>\r
+com.ozacc.mail.fetch¥Ñ¥Ã¥±¡¼¥¸¤Î¥¤¥ó¥¿¡¼¥Õ¥§¡¼¥¹¤ò¼ÂÁõ¤·¤¿¥¯¥é¥¹¤òÄ󶡤·¤Þ¤¹¡£\r
+</BODY>\r
+</HTML>
\ No newline at end of file
--- /dev/null
+/*\r
+ * @(#) $Id: AttachmentsExtractor.java,v 1.1.2.2 2005/09/25 12:51:38 otsuka Exp $\r
+ * Copyright (c) 2000 Shin Kinoshita All Rights Reserved.\r
+ */\r
+package com.ozacc.mail.fetch.impl.sk_jp;\r
+\r
+import java.io.IOException;\r
+import java.io.InputStream;\r
+import java.io.OutputStream;\r
+import java.util.ArrayList;\r
+import java.util.List;\r
+import javax.mail.MessagingException;\r
+import javax.mail.Part;\r
+import javax.mail.internet.ContentType;\r
+\r
+/**\r
+ * 添付ファイルを抽出するPartHandlerです。\r
+ * <p>\r
+ * MultipartUtility#process()呼び出し後にgetFileNames()によって、 添付ファイル名の配列を得ることができます。\r
+ * </p>\r
+ * <p>\r
+ * ファイル名配列のindexを指定してその添付ファイルに対する\r
+ * InputStreamを得たり、渡されたOutputStreamに対して書き出すことができます。\r
+ * </p>\r
+ * @version $Revision: 1.1.2.2 $ $Date: 2005/09/25 12:51:38 $\r
+ * @author Shin\r
+ */\r
+public class AttachmentsExtractor implements PartHandler {\r
+\r
+ /** message/*のパートを無視します。 */\r
+ public static final int MODE_IGNORE_MESSAGE = 1;\r
+\r
+ /** Content-Disposition: inline; パートはfilenameがあっても無視します。 */\r
+ public static final int MODE_IGNORE_INLINE = 2;\r
+\r
+ private final int mode;\r
+\r
+ private final List attachmentParts = new ArrayList();\r
+\r
+ /**\r
+ * 添付ファイル一覧を得るためのPartHandlerを作成します。 message/*のパートやinline且つファイル名指定ありのパートも\r
+ * 添付ファイルとして扱います。\r
+ */\r
+ public AttachmentsExtractor() {\r
+ this(0);\r
+ }\r
+\r
+ /**\r
+ * 添付ファイル一覧を得るためのPartHandlerを作成します。\r
+ * @param mode 動作モード。MODE_で始まる識別子をor指定します。\r
+ */\r
+ public AttachmentsExtractor(int mode) {\r
+ this.mode = mode;\r
+ }\r
+\r
+ /** MultipartUtility#process()から呼びだされるメソッドです。 */\r
+ public boolean processPart(Part part, ContentType context) throws MessagingException,\r
+ IOException {\r
+ // Apple Mail対策\r
+ if (part.getContentType().indexOf("application/applefile") != -1) {\r
+ return true;\r
+ }\r
+\r
+ if (part.isMimeType("message/*")) {\r
+ if ((mode & MODE_IGNORE_MESSAGE) != 0) {\r
+ return true;\r
+ }\r
+ attachmentParts.add(part);\r
+ return true;\r
+ }\r
+ if (MailUtility.getFileName(part) == null) {\r
+ return true;\r
+ }\r
+ if ((mode & MODE_IGNORE_INLINE) != 0 && Part.INLINE.equalsIgnoreCase(part.getDisposition())) {\r
+ return true;\r
+ }\r
+\r
+ attachmentParts.add(part);\r
+ return true;\r
+ }\r
+\r
+ /**\r
+ * 添付ファイル個数を返します。\r
+ */\r
+ public int getCount() {\r
+ return attachmentParts.size();\r
+ }\r
+\r
+ /**\r
+ * 添付ファイル名の配列を返します。\r
+ * <P>\r
+ * 添付ファイルが存在しない場合は空の配列を返します。 <BR>\r
+ * ファイル名は同一のものが複数存在する事もありえます。\r
+ * </P>\r
+ */\r
+ public String[] getFileNames() throws MessagingException {\r
+ String[] names = new String[getCount()];\r
+ for (int i = 0; i < names.length; i++) {\r
+ names[i] = getFileName(i);\r
+ }\r
+ return names;\r
+ }\r
+\r
+ /**\r
+ * 指定添付ファイルのファイル名を返します。\r
+ */\r
+ public String getFileName(int index) throws MessagingException {\r
+ Part part = (Part)attachmentParts.get(index);\r
+ String name = MailUtility.getFileName(part);\r
+ if (name == null) {\r
+ // 添付ファイル名が取得できない場合は、指定されていなかった場合か、\r
+ // あるいはmessage/*のパートの場合です。\r
+ // この場合は仮のファイル名を付けることとします。\r
+ if (part.isMimeType("message/*")) {\r
+ // If part is Message, create temporary filename.\r
+ name = "message" + index + ".eml";\r
+ } else {\r
+ name = "file" + index + ".tmp";\r
+ }\r
+ }\r
+ return name;\r
+ }\r
+\r
+ /**\r
+ * 指定添付ファイルのContent-Typeを返します。\r
+ */\r
+ public String getContentType(int index) throws MessagingException {\r
+ return MailUtility.unfold(((Part)attachmentParts.get(index)).getContentType());\r
+ }\r
+\r
+ /**\r
+ * 指定添付ファイルのサイズを返します。\r
+ */\r
+ public int getSize(int index) throws MessagingException {\r
+ return ((Part)attachmentParts.get(index)).getSize();\r
+ }\r
+\r
+ /**\r
+ * 指定添付ファイルを読み込むストリームを返します。\r
+ */\r
+ public InputStream getInputStream(int index) throws MessagingException, IOException {\r
+ return ((Part)attachmentParts.get(index)).getInputStream();\r
+ }\r
+\r
+ /**\r
+ * 指定添付ファイルを指定ストリームに書き出します。\r
+ */\r
+ public void writeTo(int index, OutputStream out) throws MessagingException, IOException {\r
+ InputStream in = getInputStream(index);\r
+ byte[] buf = new byte[1024];\r
+ int len;\r
+ while ((len = in.read(buf)) != -1) {\r
+ out.write(buf, 0, len);\r
+ }\r
+ }\r
+\r
+ public static void main(String[] args) throws Exception {\r
+ javax.mail.internet.MimeMessage msg = new javax.mail.internet.MimeMessage(\r
+ javax.mail.Session.getDefaultInstance(System.getProperties(), null), System.in);\r
+ AttachmentsExtractor h = new AttachmentsExtractor();\r
+ MultipartUtility.process(msg, h);\r
+ for (int i = 0; i < h.getCount(); i++) {\r
+ System.out.println("Attachment no : " + i);\r
+ System.out.println("Filename = " + h.getFileName(i));\r
+ System.out.println("******************");\r
+ h.writeTo(i, System.out);\r
+ }\r
+ }\r
+}
\ No newline at end of file
--- /dev/null
+/*\r
+ * @(#) $Id: CorrectedContentTypeDataSource.java,v 1.1.2.2 2004/10/24 10:27:40 otsuka Exp $\r
+ * $Revision: 1.1.2.2 $\r
+ * Copyright (c) 2000 Shin Kinoshita All Rights Reserved.\r
+ */\r
+package com.ozacc.mail.fetch.impl.sk_jp;\r
+\r
+import java.io.IOException;\r
+import java.io.InputStream;\r
+import java.io.OutputStream;\r
+import javax.activation.DataSource;\r
+import javax.mail.MessageAware;\r
+import javax.mail.MessageContext;\r
+import javax.mail.MessagingException;\r
+import javax.mail.Part;\r
+import javax.mail.internet.ContentType;\r
+import javax.mail.internet.ParseException;\r
+\r
+/**\r
+ * Content-Type:の不適合をISO-2022-JPに補正します。\r
+ * 使用方法は<PRE>\r
+ * Object o = new DataHandler(\r
+ * new CorrectedContentTypeDataSource(part, charset)\r
+ * ).getContent();\r
+ * </PRE><P>のようになります。</P><P>\r
+ * スレッドセーフではありませんので利用者側で排他制御を行ってください。\r
+ * </P>\r
+ * @author Shin\r
+ * @version $Revision: 1.1.2.2 $ $Date: 2004/10/24 10:27:40 $\r
+ */\r
+class CorrectedContentTypeDataSource implements DataSource, MessageAware {\r
+\r
+ protected DataSource source;\r
+\r
+ protected String defaultCharset;\r
+\r
+ protected String forceCharset;\r
+\r
+ public CorrectedContentTypeDataSource() {}\r
+\r
+ public CorrectedContentTypeDataSource(DataSource dataSource, String defaultCharset) {\r
+ setDataSource(dataSource);\r
+ setDefaultCharset(defaultCharset);\r
+ }\r
+\r
+ public CorrectedContentTypeDataSource(Part part, String defaultCharset)\r
+ throws MessagingException {\r
+ setPart(part);\r
+ setDefaultCharset(defaultCharset);\r
+ }\r
+\r
+ public void setPart(Part part) throws MessagingException {\r
+ // getDataHandler() method creates a implicit DataSource.\r
+ setDataSource(part.getDataHandler().getDataSource());\r
+ }\r
+\r
+ public void setDataSource(DataSource newSource) {\r
+ source = newSource;\r
+ }\r
+\r
+ public void setDefaultCharset(String defaultCharset) {\r
+ this.defaultCharset = defaultCharset;\r
+ }\r
+\r
+ /**\r
+ * 指定された文字コードで既存の文字コードを上書きします。\r
+ * \r
+ * @param forceCharset 強制的に適用する文字コード\r
+ * @author Tomohiro Otsuka\r
+ */\r
+ public void setForceCharset(String forceCharset) {\r
+ this.forceCharset = forceCharset;\r
+ }\r
+\r
+ public String getContentType() {\r
+ ContentType contentType = null;\r
+ try {\r
+ contentType = new ContentType(source.getContentType());\r
+ } catch (ParseException e) {\r
+ return "text/plain; charset=" + defaultCharset;\r
+ }\r
+ String specifiedCharset = contentType.getParameter("charset");\r
+ if (specifiedCharset == null) {\r
+ // Content-Type:が存在しない場合は"text/plain"になってしまう。\r
+ // 本当にtext/plainだった場合は正しくない事になるが、\r
+ // charset=ISO-2022-JPにする場合は一応表示上は問題ない。\r
+ contentType.setParameter("charset", defaultCharset);\r
+ } else if (forceCharset != null) {\r
+ contentType.setParameter("charset", forceCharset);\r
+ }\r
+ return contentType.toString();\r
+ }\r
+\r
+ public String getName() {\r
+ return source.getName();\r
+ }\r
+\r
+ public InputStream getInputStream() throws IOException {\r
+ return source.getInputStream();\r
+ }\r
+\r
+ public OutputStream getOutputStream() throws IOException {\r
+ return source.getOutputStream();\r
+ }\r
+\r
+ public synchronized MessageContext getMessageContext() {\r
+ if (source instanceof MessageAware) {\r
+ return ((MessageAware)source).getMessageContext();\r
+ }\r
+ throw new RuntimeException(source + " isn't MessageAware.");\r
+ }\r
+}
\ No newline at end of file
--- /dev/null
+/*\r
+ * @(#) $Id: CorrectedContentTypeDataSourceUTF7Support.java,v 1.1.2.1 2004/09/29 00:57:59 otsuka Exp $\r
+ * $Revision: 1.1.2.1 $\r
+ * Copyright (c) 2000 Shin Kinoshita All Rights Reserved.\r
+ */\r
+package com.ozacc.mail.fetch.impl.sk_jp;\r
+\r
+import java.io.ByteArrayInputStream;\r
+import java.io.ByteArrayOutputStream;\r
+import java.io.IOException;\r
+import java.io.InputStream;\r
+\r
+import javax.activation.DataSource;\r
+import javax.mail.MessageAware;\r
+import javax.mail.MessagingException;\r
+import javax.mail.Part;\r
+import javax.mail.internet.ContentType;\r
+import javax.mail.internet.MimeBodyPart;\r
+import javax.mail.internet.MimeMessage;\r
+import javax.mail.internet.ParseException;\r
+\r
+import com.ozacc.mail.fetch.impl.sk_jp.io.ByteToCharUTF7;\r
+\r
+/**\r
+ * Content-Type:の不適合をISO-2022-JPに補正します。\r
+ * さらにcharset=UTF-7の場合にUTF-16のストリームに変換してgetContent()を\r
+ * 無理やり成功させます。<BR>\r
+ * また、未知のTES(Content-Transfer-Encoding:)だった場合に、"7bit"\r
+ * と見なしてボディを取得します。\r
+ * 使用方法は<PRE>\r
+ * Object o = new DataHandler(\r
+ * new CorrectedContentTypeDataSourceUTF7Support(part, charset)\r
+ * ).getContent();\r
+ * </PRE><P>のようになります。</P><P>\r
+ * スレッドセーフではありませんので利用者側で排他制御を行ってください。\r
+ * </P>\r
+ * @author Shin\r
+ * @version $Revision: 1.1.2.1 $ $Date: 2004/09/29 00:57:59 $\r
+ */\r
+class CorrectedContentTypeDataSourceUTF7Support extends CorrectedContentTypeDataSource {\r
+\r
+ private boolean utf7 = false;\r
+\r
+ public CorrectedContentTypeDataSourceUTF7Support() {}\r
+\r
+ public CorrectedContentTypeDataSourceUTF7Support(DataSource dataSource, String defaultCharset) {\r
+ super(dataSource, defaultCharset);\r
+ }\r
+\r
+ public CorrectedContentTypeDataSourceUTF7Support(Part part, String defaultCharset)\r
+ throws MessagingException {\r
+ super(part, defaultCharset);\r
+ }\r
+\r
+ public void setDataSource(DataSource newSource) {\r
+ super.setDataSource(newSource);\r
+ utf7 = false;\r
+ }\r
+\r
+ public void setDefaultCharset(String defaultCharset) {\r
+ super.setDefaultCharset(defaultCharset);\r
+ utf7 = false;\r
+ }\r
+\r
+ public String getContentType() {\r
+ try {\r
+ ContentType contentType = new ContentType(super.getContentType());\r
+ String specifiedCharset = contentType.getParameter("charset");\r
+ if ("UTF-7".equalsIgnoreCase(specifiedCharset)) {\r
+ // UTF-7コンバータが存在しない為、\r
+ // 独自フィルタストリームを用いる。\r
+ contentType.setParameter("charset", "UTF-16");\r
+ utf7 = true;\r
+ }\r
+ return contentType.toString();\r
+ } catch (ParseException e) {\r
+ throw new InternalError();\r
+ }\r
+ }\r
+\r
+ public InputStream getInputStream() throws IOException {\r
+ InputStream in = null;\r
+ if (isInvalidEncodingAsMultipart()) {\r
+ // multipart/*でありながら、不正なTransfer-Encodingだった場合\r
+ // 2001/09/01 JPhone(SH07)の送信する画像付きメイルが、\r
+ // Content-Type: multipart/mixed\r
+ // Content-Transfer-Encoding: base64\r
+ // 等というメッセージを送る場合があり、JavaMailが\r
+ // これをデコードできない問題を回避。\r
+ // multipart/*の場合のContent-Transfer-Encodingは、\r
+ // "7bit""8bit""binary"に限られる。\r
+ // それ以外の場合は生ストリームを返すようにしておく。\r
+ in = getRawInputStream();\r
+ }\r
+ if (in == null) {\r
+ try {\r
+ in = super.getInputStream();\r
+ } catch (IOException e) {\r
+ // ここでのIOExceptionはエンコーディング不良の可能性が高い。\r
+ // 生InputStreamを得てリトライ\r
+ in = getRawInputStream();\r
+ if (in == null)\r
+ throw e;\r
+ }\r
+ }\r
+ if (!utf7) {\r
+ return in;\r
+ }\r
+ ByteArrayOutputStream out = new ByteArrayOutputStream();\r
+ int c;\r
+\r
+ while ((c = in.read()) != -1) {\r
+ out.write(c);\r
+ }\r
+\r
+ ByteToCharUTF7 btc = new ByteToCharUTF7();\r
+ byte[] bytes = out.toByteArray();\r
+ char[] chars = new char[bytes.length];\r
+\r
+ // Bug fixed. Thanx to MOHI.\r
+ // http://www.sk-jp.com/cgi-bin/treebbs.cgi?all=1220&s=1220\r
+ int len = btc.convert(bytes, 0, bytes.length, chars, 0, chars.length);\r
+ char[] w = new char[len];\r
+ System.arraycopy(chars, 0, w, 0, len);\r
+ String string = new String(w);\r
+ return new ByteArrayInputStream(string.getBytes("UTF-16"));\r
+ }\r
+\r
+ // Transfer-Encodingにしたがったデコードを行う前のストリームを得ます。\r
+ // sourceがMessageAwareでない場合はnullが返されます。\r
+ private InputStream getRawInputStream() throws IOException {\r
+ if (!(source instanceof MessageAware)) {\r
+ return null;\r
+ }\r
+ Part part = ((MessageAware)source).getMessageContext().getPart();\r
+ try {\r
+ if (part instanceof MimeMessage) {\r
+ return ((MimeMessage)part).getRawInputStream();\r
+ } else if (part instanceof MimeBodyPart) {\r
+ return ((MimeBodyPart)part).getRawInputStream();\r
+ } else {\r
+ return null;\r
+ }\r
+ } catch (MessagingException mex) {\r
+ throw new IOException(mex.toString());\r
+ }\r
+ }\r
+\r
+ // 不正なContent-Transfer-Encodingの場合にtrueを返します。\r
+ private boolean isInvalidEncodingAsMultipart() {\r
+ try {\r
+ if (!new ContentType(getContentType()).match("multipart/*")) {\r
+ return false;\r
+ }\r
+ if (!(source instanceof MessageAware)) {\r
+ return false;\r
+ }\r
+ Part part = ((MessageAware)source).getMessageContext().getPart();\r
+ String encoding = ((javax.mail.internet.MimePart)part).getEncoding();\r
+ if ("7bit".equalsIgnoreCase(encoding) || "8bit".equalsIgnoreCase(encoding)\r
+ || "binary".equalsIgnoreCase(encoding)) {\r
+ return false;\r
+ }\r
+ } catch (Exception e) {\r
+ // この場合も不正だ、と。\r
+ }\r
+ return true;\r
+ }\r
+\r
+}
\ No newline at end of file
--- /dev/null
+/*\r
+ * @(#) $Id: FirstPlainPartExtractor.java,v 1.1.2.1 2004/09/29 00:57:59 otsuka Exp $\r
+ * $Revision: 1.1.2.1 $\r
+ * Copyright (c) 2000 Shin Kinoshita All Rights Reserved.\r
+ */\r
+package com.ozacc.mail.fetch.impl.sk_jp;\r
+\r
+import java.io.IOException;\r
+import javax.mail.Part;\r
+import javax.mail.MessagingException;\r
+import javax.mail.internet.ContentType;\r
+\r
+/**\r
+ * 最初に見つけたtext/plainパートの本文を得るPartHandlerです。\r
+ * <P>\r
+ * </P>\r
+ * @version $Revision: 1.1.2.1 $ $Date: 2004/09/29 00:57:59 $\r
+ * @author Shin\r
+ */\r
+public class FirstPlainPartExtractor implements PartHandler {\r
+\r
+ private String text = null;\r
+\r
+ public boolean processPart(Part part, ContentType context) throws MessagingException,\r
+ IOException {\r
+ String type = part.getContentType();\r
+ // Bug fixed. Thx > ei\r
+ // http://www.sk-jp.com/cgi-bin/treebbs.cgi?kako=1&all=1292&s=1292\r
+ if (!part.isMimeType("text/plain") && type != null && !type.trim().equalsIgnoreCase("text")) {\r
+ return true;\r
+ }\r
+ text = (String)MultipartUtility.getContent(part);\r
+ return false;\r
+ }\r
+\r
+ public String getText() {\r
+ return text;\r
+ }\r
+\r
+ public static void main(String[] args) throws Exception {\r
+ javax.mail.internet.MimeMessage msg = new javax.mail.internet.MimeMessage(\r
+ javax.mail.Session.getDefaultInstance(System.getProperties(), null), System.in);\r
+ FirstPlainPartExtractor h = new FirstPlainPartExtractor();\r
+ MultipartUtility.process(msg, h);\r
+\r
+ System.out.println("This is the first detected text/plain part.");\r
+ System.out.println(h.getText());\r
+ }\r
+}
\ No newline at end of file
--- /dev/null
+/*\r
+ * @(#) $Id: HtmlPartExtractor.java,v 1.1.2.1 2004/09/29 00:57:59 otsuka Exp $\r
+ * $Revision: 1.1.2.1 $\r
+ * Copyright (c) 2000 Shin Kinoshita All Rights Reserved.\r
+ */\r
+package com.ozacc.mail.fetch.impl.sk_jp;\r
+\r
+import java.io.IOException;\r
+\r
+import javax.mail.MessagingException;\r
+import javax.mail.Part;\r
+import javax.mail.internet.ContentType;\r
+\r
+/**\r
+ * text/htmlを結合した文字列を得るPartHandlerです。\r
+ * \r
+ * @version $Revision: 1.1.2.1 $ $Date: 2004/09/29 00:57:59 $\r
+ * @author Shin\r
+ */\r
+public class HtmlPartExtractor implements PartHandler {\r
+\r
+ private String html = null;\r
+\r
+ public boolean processPart(Part part, ContentType context) throws MessagingException,\r
+ IOException {\r
+ if (!part.isMimeType("text/html")) {\r
+ return true;\r
+ }\r
+ if (html == null) {\r
+ // 最初のテキストパートを無条件に抽出\r
+ html = (String)MultipartUtility.getContent(part);\r
+ } else {\r
+ String disposition = part.getDisposition();\r
+ if (disposition == null || disposition.equalsIgnoreCase(Part.INLINE)) {\r
+ html += "\r\n\r\n-- inline --\r\n\r\n" + (String)MultipartUtility.getContent(part);\r
+ }\r
+ }\r
+ return true;\r
+ }\r
+\r
+ public String getHtml() {\r
+ return html;\r
+ }\r
+\r
+}
\ No newline at end of file
--- /dev/null
+/*\r
+ * @(#) $Id: JISDataSource.java,v 1.1.2.1 2005/01/18 07:20:59 otsuka Exp $\r
+ * $Revision: 1.1.2.1 $\r
+ * Copyright (c) 2000 Shin Kinoshita All Rights Reserved.\r
+ */\r
+package com.ozacc.mail.fetch.impl.sk_jp;\r
+\r
+import java.io.ByteArrayInputStream;\r
+import java.io.IOException;\r
+import java.io.InputStream;\r
+import java.io.OutputStream;\r
+import java.io.UnsupportedEncodingException;\r
+\r
+import javax.activation.DataSource;\r
+\r
+import com.ozacc.mail.fetch.impl.sk_jp.io.CharCodeConverter;\r
+import com.ozacc.mail.fetch.impl.sk_jp.io.UnicodeCorrector;\r
+\r
+/**\r
+ * テキストの本文を送信するための DataSource です。\r
+ */\r
+public class JISDataSource implements DataSource {\r
+\r
+ private byte[] data;\r
+\r
+ public JISDataSource(String s) {\r
+ try {\r
+ data = CharCodeConverter.sjisToJis(UnicodeCorrector.getInstance("Windows-31J").correct(\r
+ s).getBytes("Windows-31J"));\r
+ } catch (UnsupportedEncodingException e) {\r
+ throw new RuntimeException("CANT HAPPEN");\r
+ }\r
+ }\r
+\r
+ public String getContentType() {\r
+ return "text/plain; charset=ISO-2022-JP";\r
+ }\r
+\r
+ public InputStream getInputStream() throws IOException {\r
+ if (data == null)\r
+ throw new IOException("no data");\r
+ return new ByteArrayInputStream(data);\r
+ }\r
+\r
+ public OutputStream getOutputStream() throws IOException {\r
+ throw new IOException("cannot do this");\r
+ }\r
+\r
+ public String getName() {\r
+ return "dummy";\r
+ }\r
+}
\ No newline at end of file
--- /dev/null
+/*\r
+ * @(#) $Id: MailUtility.java,v 1.1.2.1 2005/01/18 07:20:59 otsuka Exp $\r
+ * Copyright (c) 2000-2004 Shin Kinoshita All Rights Reserved.\r
+ */\r
+package com.ozacc.mail.fetch.impl.sk_jp;\r
+\r
+import java.io.ByteArrayInputStream;\r
+import java.io.IOException;\r
+import java.io.InputStream;\r
+import java.io.UnsupportedEncodingException;\r
+import java.util.Date;\r
+\r
+import javax.activation.DataHandler;\r
+import javax.mail.BodyPart;\r
+import javax.mail.Message;\r
+import javax.mail.MessagingException;\r
+import javax.mail.Multipart;\r
+import javax.mail.Part;\r
+import javax.mail.internet.AddressException;\r
+import javax.mail.internet.ContentDisposition;\r
+import javax.mail.internet.ContentType;\r
+import javax.mail.internet.HeaderTokenizer;\r
+import javax.mail.internet.InternetAddress;\r
+import javax.mail.internet.MailDateFormat;\r
+import javax.mail.internet.MimeUtility;\r
+import javax.mail.internet.ParseException;\r
+\r
+import com.ozacc.mail.fetch.impl.sk_jp.io.CharCodeConverter;\r
+import com.ozacc.mail.fetch.impl.sk_jp.io.UnicodeCorrector;\r
+import com.ozacc.mail.fetch.impl.sk_jp.text.EntityRefEncoder;\r
+import com.ozacc.mail.fetch.impl.sk_jp.util.StringValues;\r
+import com.sun.mail.util.BASE64EncoderStream;\r
+\r
+/**\r
+ * JavaMailのサポートクラスです。\r
+ * <P>\r
+ * 主にヘッダに対するさまざまな加工機能を提供します。\r
+ * </P>\r
+ * @author Shin\r
+ * @version $Revision: 1.1.2.1 $ $Date: 2005/01/18 07:20:59 $\r
+ */\r
+public class MailUtility {\r
+\r
+ public static String getPersonal(InternetAddress a) {\r
+ if (a.getPersonal() != null)\r
+ return a.getPersonal();\r
+ return a.toString();\r
+ }\r
+\r
+ /** get comma separated E-Mail addresses. */\r
+ public static String getMailAddresses(InternetAddress[] addresses) {\r
+ if (addresses == null)\r
+ return null;\r
+ StringValues buf = new StringValues();\r
+ for (int i = 0; i < addresses.length; i++) {\r
+ buf.add(addresses[i].getAddress());\r
+ }\r
+ return buf.getString();\r
+ }\r
+\r
+ /** get comma separated personal names. */\r
+ public static String getPersonalNames(InternetAddress[] addresses) {\r
+ if (addresses == null)\r
+ return null;\r
+ StringValues buf = new StringValues();\r
+ String name;\r
+ for (int i = 0; i < addresses.length; i++) {\r
+ name = decodeText(unfold(addresses[i].getPersonal()));\r
+ if (name == null) {\r
+ name = addresses[i].toString();\r
+ }\r
+ buf.add(name);\r
+ }\r
+ return buf.getString();\r
+ }\r
+\r
+ public static String getAddressesHTML(InternetAddress[] addresses) {\r
+ if (addresses == null)\r
+ return null;\r
+ StringValues buf = new StringValues();\r
+ StringBuffer href = new StringBuffer();\r
+ String name;\r
+ for (int i = 0; i < addresses.length; i++) {\r
+ href.append("<a href=\"mailto:");\r
+ href.append(addresses[i].getAddress());\r
+ href.append("\">");\r
+ name = addresses[i].getPersonal();\r
+ if (name != null) {\r
+ name = decodeText(name);\r
+ }\r
+ if (name == null) {\r
+ name = addresses[i].toString();\r
+ }\r
+ href.append(EntityRefEncoder.encode(name));\r
+ href.append("</a>");\r
+ buf.add(new String(href));\r
+ href.setLength(0);\r
+ }\r
+ return buf.getString();\r
+ }\r
+\r
+ /** get the Content-Transfer-Encoding: header value. */\r
+ public static String getTransferEncoding(byte[] b) {\r
+ int nonAscii = 0;\r
+ for (int i = 0; i < b.length; i++) {\r
+ if (b[i] < 0) {\r
+ nonAscii++;\r
+ }\r
+ }\r
+ if (nonAscii == 0)\r
+ return "7bit";\r
+ if (nonAscii < b.length - nonAscii)\r
+ return "quoted-printable";\r
+ return "base64";\r
+ }\r
+\r
+ /**\r
+ * パートを保有する親Messageオブジェクトを返します。\r
+ * @param part パート\r
+ * @return ツリー構造の最上位にあたるメッセージオブジェクト\r
+ */\r
+ public static Message getParentMessage(Part part) {\r
+ Part current = part;\r
+ Multipart mp;\r
+ while (!(current instanceof Message)) {\r
+ mp = ((BodyPart)current).getParent();\r
+ if (mp == null)\r
+ return null; // Should it throw exception?\r
+ current = mp.getParent();\r
+ if (current == null)\r
+ return null; // Should it throw exception?\r
+ }\r
+ return (Message)current;\r
+ }\r
+\r
+ //////////////////////////////////////////////////////////////////////////\r
+ // note: JavaMail1.2 later\r
+ private static MailDateFormat mailDateFormat = new MailDateFormat();\r
+\r
+ /**\r
+ * Date構文の誤った"JST"タイムゾーンの補正を行います。\r
+ * <P>\r
+ * JavaMailは"JST"と記述されるタイムゾーンを解釈しません。 ここは本来"+0900"でなければならないところです。 <BR>\r
+ * 仕方がないので" JST"が含まれる文字列の場合は"+0900"を補完して\r
+ * MailDateFormat#parse()を通すようなparse()のラッパを用意します。\r
+ * </P>\r
+ * <P>\r
+ * この実装は一時回避的なものであり、完全なものではありません。\r
+ * </P>\r
+ */\r
+ public static Date parseDate(String rfc822DateString) {\r
+ if (rfc822DateString == null) {\r
+ return null;\r
+ }\r
+ try {\r
+ if (rfc822DateString.indexOf(" JST") == -1 || rfc822DateString.indexOf('+') >= 0) {\r
+ synchronized (mailDateFormat) {\r
+ return mailDateFormat.parse(rfc822DateString);\r
+ }\r
+ }\r
+ // correct the pseudo header\r
+ StringBuffer buf = new StringBuffer(rfc822DateString.substring(0, rfc822DateString\r
+ .indexOf("JST")));\r
+ buf.append("+0900");\r
+ synchronized (mailDateFormat) {\r
+ return mailDateFormat.parse(new String(buf));\r
+ }\r
+ } catch (java.text.ParseException e) {\r
+ return null;\r
+ }\r
+ }\r
+\r
+ //////////////////////////////////////////////////////////////////////////\r
+ /**\r
+ * Subject:に"Re: "を付加します。\r
+ * <P>\r
+ * ある程度寛容に"Re: "に近い文字列と"[hoge]"を取り除きます。 <BR>\r
+ * ただし、意図しない部分が消されてしまう事もあり得ます。 <BR>\r
+ * JavaMailのreply()では"Re: "がエンコードされていた場合に 正しく"Re: "を取り除いてくれません。\r
+ * </P>\r
+ */\r
+ public static String createReplySubject(String src) {\r
+ if (src == null || src.length() == 0) {\r
+ return "Re: (no subject)";\r
+ }\r
+ String work = src;\r
+ if (work.charAt(0) == '[' && work.indexOf(']') > 0) {\r
+ int afterBracket = indexOfNonLWSP(work, work.indexOf(']') + 1, false);\r
+ if (afterBracket < 0) {\r
+ work = "";\r
+ } else {\r
+ work = work.substring(afterBracket);\r
+ }\r
+ }\r
+ if (work.length() > 3 && "Re:".equalsIgnoreCase(work.substring(0, 3))) {\r
+ int afterRe = indexOfNonLWSP(work, 3, false);\r
+ if (afterRe < 0) {\r
+ work = "";\r
+ } else {\r
+ work = work.substring(afterRe);\r
+ }\r
+ }\r
+ return "Re: " + work;\r
+ }\r
+\r
+ //////////////////////////////////////////////////////////////////////////\r
+ /**\r
+ * 入力されたアドレスをInternetAddress形式に変換します。\r
+ * <p>\r
+ * "名無し君 <abc@example.com>(コメント)"等の文字列(エンコード無し)を\r
+ * 渡されても、正しくpersonal文字列が設定されるようにします。 <br>\r
+ * InternetAddress#parse()はエンコード済みの文字列を前提にしているため、 このメソッドの目的には沿いません。\r
+ * </p>\r
+ * @param addresses メイルアドレス文字列(カンマ区切り)\r
+ */\r
+ public static InternetAddress[] parseAddresses(String addressesString) throws AddressException {\r
+ return parseAddresses(addressesString, true);\r
+ }\r
+\r
+ public static InternetAddress[] parseAddresses(String addressesString, boolean strict)\r
+ throws AddressException {\r
+ if (addressesString == null)\r
+ return null;\r
+ try {\r
+ InternetAddress[] addresses = InternetAddress.parse(addressesString, strict);\r
+ // correct personals\r
+ for (int i = 0; i < addresses.length; i++) {\r
+ addresses[i].setPersonal(addresses[i].getPersonal(), "ISO-2022-JP");\r
+ }\r
+ return addresses;\r
+ } catch (UnsupportedEncodingException e) {\r
+ throw new InternalError(e.toString());\r
+ }\r
+ }\r
+\r
+ // InternetAddress.parse(\r
+ // encodeText(addressesString, "ISO-2022-JP", "B"), strict);\r
+ // で良さそうなものだが、これでは・・たしかなんか問題があったはず。\r
+ //////////////////////////////////////////////////////////////////////////\r
+ /**\r
+ * header valueの unfolding を行います。 空白を厳密に扱うためには decodeText より先に呼び出す必要があります。\r
+ */\r
+ public static String unfold(String source) {\r
+ if (source == null)\r
+ return null;\r
+ StringBuffer buf = new StringBuffer();\r
+ boolean skip = false;\r
+ char c;\r
+ // <CRLF>シーケンスを前提とするならindexOf()で十分ですが、\r
+ // 念のためCR、LFいずれも許容します。\r
+ for (int i = 0; i < source.length(); i++) {\r
+ c = source.charAt(i);\r
+ if (skip) {\r
+ if (isLWSP(c)) {\r
+ continue;\r
+ }\r
+ skip = false;\r
+ }\r
+ if (c != '\r' && c != '\n') {\r
+ buf.append(c);\r
+ } else {\r
+ buf.append(' ');\r
+ skip = true;\r
+ }\r
+ }\r
+ return new String(buf);\r
+ }\r
+\r
+ /**\r
+ * header valueの folding を行います。\r
+ * <P>\r
+ * white spaceをfolding対象にします。 <BR>\r
+ * 76bytesを超えないwhite space位置に <CRLF>を挿入します。\r
+ * </P>\r
+ * <P>\r
+ * 注:quoteを無視しますので、structured fieldでは不都合が 発生する可能性があります。\r
+ * </P>\r
+ * @param used ヘッダの':'までの文字数。76 - usedが最初のfolding候補桁\r
+ * @return foldingされた( <CRLF>SPACEが挿入された)文字列\r
+ */\r
+ public static String fold(String source, int used) {\r
+ if (source == null)\r
+ return null;\r
+ StringBuffer buf = new StringBuffer();\r
+ String work = source;\r
+ int lineBreakIndex;\r
+ while (work.length() > 76) {\r
+ lineBreakIndex = work.lastIndexOf(' ', 76);\r
+ if (lineBreakIndex == -1)\r
+ break;\r
+ buf.append(work.substring(0, lineBreakIndex));\r
+ buf.append("\r\n");\r
+ work = work.substring(lineBreakIndex);\r
+ }\r
+ buf.append(work);\r
+ return new String(buf);\r
+ }\r
+\r
+ //////////////////////////////////////////////////////////////////////////\r
+ /**\r
+ * パートにテキストをセットします。\r
+ * Part#setText() の代わりにこちらを使うことで、\r
+ * "ISO-2022-JP" コンバータではエンコードできない CP932 の\r
+ * 文字をエンコードできます。\r
+ */\r
+ public static void setTextContent(Part p, String s) throws MessagingException {\r
+ //p.setText(content, "ISO-2022-JP");\r
+ p.setDataHandler(new DataHandler(new JISDataSource(s)));\r
+ p.setHeader("Content-Transfer-Encoding", "7bit");\r
+ }\r
+\r
+ /**\r
+ * 日本語を含むヘッダ用テキストを生成します。\r
+ * 変換結果は ASCII なので、これをそのまま setSubject や InternetAddress\r
+ * のパラメタとして使用してください。\r
+ * "ISO-2022-JP" コンバータではエンコードできない CP932 の\r
+ * 文字をエンコードできます。ただし、encodeText() と異なり、\r
+ * folding の意識をしておらず、また ASCII 部分を除いて分割\r
+ * エンコードを行うこともできません。\r
+ */\r
+ public static String encodeWordJIS(String s) {\r
+ try {\r
+ return "=?ISO-2022-JP?B?"\r
+ + new String(BASE64EncoderStream.encode(CharCodeConverter\r
+ .sjisToJis(UnicodeCorrector.getInstance("Windows-31J").correct(s)\r
+ .getBytes("Windows-31J")))) + "?=";\r
+ } catch (UnsupportedEncodingException e) {\r
+ throw new RuntimeException("CANT HAPPEN");\r
+ }\r
+ }\r
+\r
+ //////////////////////////////////////////////////////////////////////////\r
+ /**\r
+ * ヘッダ内の文字列をデコードします。\r
+ * <p>\r
+ * MimeUtilityの制約を緩めて日本で流通するエンコード形式に対応。\r
+ * 本来は、encoded-wordとnon-encoded-wordの間にはlinear-white-spaceが必要\r
+ * なのですが、空白が無い場所でエンコードするタコメイラが多いので。\r
+ * </p>\r
+ * <p>\r
+ * JISコードをエンコード無しで記述するタコメイラもあります。 <br>\r
+ * ソースにESCが含まれていたら生JISと見なします。\r
+ * </p>\r
+ * <p>\r
+ * =?utf-8?Q?・・・JISコード・・?=なんてさらにタコなメイラも。 <br>\r
+ * 試しにデコード後にまだESCが残ってたらISO-2022-JPと見なすことにします。\r
+ * </p>\r
+ * <p>\r
+ * さらに、multibyte character の前後で別の encoded-word に切ってしまう メイラも…。隣接する\r
+ * encoded-word の CES が同じ場合はバイト列の 結合を行ってから CES デコードを行うようにした…。\r
+ * </p>\r
+ * <p>\r
+ * 日本語に特化してますねえ・・・。\r
+ * </p>\r
+ * @param source encoded text\r
+ * @return decoded text\r
+ */\r
+ public static String decodeText(String source) {\r
+ if (source == null)\r
+ return null;\r
+ // specially for Japanese\r
+ if (source.indexOf('\u001b') >= 0) {\r
+ // ISO-2022-JP\r
+ try {\r
+ return new String(source.getBytes("ISO-8859-1"), "ISO-2022-JP");\r
+ } catch (UnsupportedEncodingException e) {\r
+ throw new InternalError();\r
+ }\r
+ }\r
+ String decodedText = new RFC2047Decoder(source).get();\r
+ if (decodedText.indexOf('\u001b') >= 0) {\r
+ try {\r
+ return new String(decodedText.getBytes("ISO-8859-1"), "ISO-2022-JP");\r
+ } catch (UnsupportedEncodingException e) {\r
+ throw new InternalError();\r
+ }\r
+ }\r
+ return decodedText;\r
+ }\r
+\r
+ // 日本語をデコードする上で問題があるので、encoded-wordの切り出しはすべて独自に\r
+ // Netscapeなどは"()."等の文字でencoded-wordを切ってしまうが、JavaMailは\r
+ // このときencoded-wordの終わりを判定できず、一部の文字を欠落させてしまう。\r
+ // また、encoded-word を文字デコードするのを遅延させ、隣接する encoded-word\r
+ // の CES が同じ場合は、先に TES デコードを行ったバイト列を結合してから\r
+ // CES に従ったデコードを行う。マルチバイト文字を分断する sender がいるから。\r
+ static class RFC2047Decoder {\r
+\r
+ private String source;\r
+\r
+ private String pooledCES;\r
+\r
+ private byte[] pooledBytes;\r
+\r
+ private StringBuffer buf;\r
+\r
+ private int pos = 0;\r
+\r
+ private int startIndex;\r
+\r
+ private int endIndex;\r
+\r
+ public RFC2047Decoder(String source) {\r
+ this.source = source;\r
+ buf = new StringBuffer(source.length());\r
+ parse();\r
+ }\r
+\r
+ private void parse() {\r
+ while (hasEncodedWord()) {\r
+ String work = source.substring(pos, startIndex);\r
+ if (indexOfNonLWSP(work, 0, false) > -1) {\r
+ sweepPooledBytes();\r
+ buf.append(work);\r
+ } // encoded-word同士の間のLWSPは削除\r
+ parseWord();\r
+ }\r
+ sweepPooledBytes();\r
+ buf.append(source.substring(pos));\r
+ }\r
+\r
+ // encoded-word があった場合、startIndex/endIndex をセットする\r
+ private boolean hasEncodedWord() {\r
+ startIndex = source.indexOf("=?", pos);\r
+ if (startIndex == -1)\r
+ return false;\r
+ endIndex = source.indexOf("?=", startIndex + 2);\r
+ if (endIndex == -1)\r
+ return false;\r
+ // 本来は encoded-word 中に LWSP があってはいけないが\r
+ // encoded-word の途中で folding してしまう sender がいるらしい\r
+ // 以下をコメントにすることで encoded-word の誤認識の可能性も\r
+ // 出てくるが、誤認識になる確率以上に前記のような illegal な\r
+ // メッセージの方が多いのが実情のようだ。\r
+ // thx > YOSI\r
+ //int i = indexOfLWSP(source, startIndex + 2, false, (char)0);\r
+ //if (i >= 0 && i < endIndex)\r
+ // return false;\r
+ endIndex += 2;\r
+ return true;\r
+ }\r
+\r
+ private void parseWord() {\r
+ try {\r
+ int s = startIndex + 2;\r
+ int e = source.indexOf('?', s);\r
+ if (e == endIndex - 2)\r
+ throw new RuntimeException();\r
+ String ces = source.substring(s, e);\r
+ try {\r
+ "".getBytes(ces); // FIXME: check whether supported or not\r
+ } catch (UnsupportedEncodingException ex) {\r
+ ces = "JISAutoDetect";\r
+ }\r
+ s = e + 1;\r
+ e = source.indexOf('?', s);\r
+ if (e == endIndex - 2)\r
+ throw new RuntimeException();\r
+ String tes = source.substring(s, e);\r
+ byte[] bytes = decodeByTES(source.substring(e + 1, endIndex - 2), tes);\r
+ if (ces.equals(pooledCES)) {\r
+ // append bytes\r
+ byte[] w = new byte[pooledBytes.length + bytes.length];\r
+ System.arraycopy(pooledBytes, 0, w, 0, pooledBytes.length);\r
+ System.arraycopy(bytes, 0, w, pooledBytes.length, bytes.length);\r
+ pooledBytes = w;\r
+ } else {\r
+ sweepPooledBytes();\r
+ pooledCES = ces;\r
+ pooledBytes = bytes;\r
+ }\r
+ } catch (Exception ex) {\r
+ ex.printStackTrace();\r
+ // contains RuntimeException\r
+ buf.append(source.substring(startIndex, endIndex));\r
+ }\r
+ pos = endIndex;\r
+ }\r
+\r
+ private void sweepPooledBytes() {\r
+ if (pooledBytes == null)\r
+ return;\r
+ try {\r
+ buf.append(new String(pooledBytes, pooledCES));\r
+ } catch (UnsupportedEncodingException e) {\r
+ throw new InternalError("CANT HAPPEN: Illegal encoding = " + pooledCES);\r
+ }\r
+ pooledCES = null;\r
+ pooledBytes = null;\r
+ }\r
+\r
+ public String get() {\r
+ return new String(buf);\r
+ }\r
+ }\r
+\r
+ private static byte[] decodeByTES(String s, String tes) {\r
+ // 通常あり得ないが、LWSP を詰める\r
+ int i;\r
+ while ((i = indexOfLWSP(s, 0, false, (char)0)) >= 0)\r
+ s = s.substring(0, i) + s.substring(i + 1);\r
+ if (tes.equalsIgnoreCase("B") && s.length() % 4 != 0) {\r
+ // BASE64DecoderStream は正確にパディングされていないと\r
+ // IOException になるので、無理やり矯正。\r
+ switch (4 - s.length() % 4) {\r
+ case 1:\r
+ s += '=';\r
+ break;\r
+ case 2:\r
+ s += "==";\r
+ break;\r
+ case 3:\r
+ if (s.charAt(s.length() - 1) != '=')\r
+ s += "===";\r
+ else\r
+ s = s.substring(0, s.length() - 1);\r
+ break;\r
+ }\r
+ }\r
+ try {\r
+ ByteArrayInputStream bis = new ByteArrayInputStream(com.sun.mail.util.ASCIIUtility\r
+ .getBytes(s));\r
+ InputStream is;\r
+ if (tes.equalsIgnoreCase("B"))\r
+ is = new com.sun.mail.util.BASE64DecoderStream(bis);\r
+ else if (tes.equalsIgnoreCase("Q"))\r
+ is = new com.sun.mail.util.QDecoderStream(bis);\r
+ else\r
+ throw new UnsupportedEncodingException(tes);\r
+ int count = bis.available();\r
+ byte[] bytes = new byte[count];\r
+ count = is.read(bytes, 0, count);\r
+ if (count != bytes.length) {\r
+ byte[] w = new byte[count];\r
+ System.arraycopy(bytes, 0, w, 0, count);\r
+ bytes = w;\r
+ }\r
+ return bytes;\r
+ } catch (IOException e) {\r
+ e.printStackTrace();\r
+ throw new RuntimeException("CANT HAPPEN");\r
+ }\r
+ }\r
+\r
+ /**\r
+ * 文字列をエンコードします。\r
+ * <p>\r
+ * MimeUtility(強いてはMimeMessage等も)では、1字でも非ASCII文字が含まれる\r
+ * と文字列全体をエンコードしてしまいます。\r
+ * <br>\r
+ * このメソッドでは空白で区切られた範囲だけをエンコードします。 <br>\r
+ * Subjectの"Re: "等がエンコードされていると、この文字列でIn-Reply-To:\r
+ * References:の代わりにスレッドを形成しようとしても失敗することになる\r
+ * ため、こちらのエンコード方式を用いたがる人もいるかもしれません・・。\r
+ * </p>\r
+ * <p>\r
+ * 方針は、ASCII部に前後の空白一つを含ませ、それ以外は空白も含めて全て\r
+ * encoded-wordとします。()の内側は空白無しでもエンコード対象です。\r
+ * </p>\r
+ * @param source text\r
+ * @return encoded text\r
+ */\r
+ // "()" の扱いにこだわりすぎて異常に汚い-_-。\r
+ // "()"なんか無視してまとめて encode するようにすればすっきるするけど…。\r
+ public static String encodeText(String source, String charset, String encoding)\r
+ throws UnsupportedEncodingException {\r
+ if (source == null)\r
+ return null;\r
+ int boundaryIndex;\r
+ int startIndex;\r
+ int endIndex = 0;\r
+ int lastLWSPIndex;\r
+ StringBuffer buf = new StringBuffer();\r
+ while (true) {\r
+ // check the end of ASCII part\r
+ boundaryIndex = indexOfNonAscii(source, endIndex);\r
+ if (boundaryIndex == -1) {\r
+ buf.append(source.substring(endIndex));\r
+ return new String(buf);\r
+ }\r
+ // any LWSP has taken (back track).\r
+ lastLWSPIndex = indexOfLWSP(source, boundaryIndex, true, '(');\r
+ startIndex = indexOfNonLWSP(source, lastLWSPIndex, true) + 1;\r
+ // ASCII part の終了位置は、次の non ASCII と比べて\r
+ // 最も ASCII 文字よりの空白文字位置または'('の次位置\r
+ startIndex = (endIndex > startIndex) ? endIndex : startIndex;\r
+ if (startIndex > endIndex) {\r
+ // ASCII part\r
+ buf.append(source.substring(endIndex, startIndex));\r
+ // JavaMailはencodeWord内でfoldingするけどそれはencodedWord\r
+ // に対してのみ。ヘッダそのものに対するfoldingはしてくれない。\r
+ if (isLWSP(source.charAt(startIndex))) {\r
+ // folding により 空白一つが確保されるのでスキップ\r
+ buf.append("\r\n ");\r
+ startIndex++;\r
+ // なお、'('の場合は空白を入れないので folding しない\r
+ }\r
+ }\r
+ // any LWSP has taken.\r
+ endIndex = indexOfNonLWSP(source, boundaryIndex, false);\r
+ while ((endIndex = indexOfLWSP(source, endIndex, false, ')')) != -1) {\r
+ endIndex = indexOfNonLWSP(source, endIndex, false);\r
+ int nextBoundary = indexOfLWSP(source, endIndex, false, (char)0);\r
+ if (nextBoundary == -1) {\r
+ if (indexOfNonAscii(source, endIndex) != -1) {\r
+ endIndex = -1;\r
+ break;\r
+ }\r
+ } else {\r
+ int nonAscii = indexOfNonAscii(source, endIndex);\r
+ if (nonAscii != -1 && nonAscii < nextBoundary) {\r
+ endIndex = nextBoundary;\r
+ continue;\r
+ }\r
+ }\r
+ break;\r
+ }\r
+ boolean needFolding = false;\r
+ if (endIndex < 0) {\r
+ endIndex = source.length();\r
+ } else if (isLWSP(source.charAt(endIndex - 1))) {\r
+ // folding により 空白一つが確保される(予定)なので減らす\r
+ endIndex--;\r
+ needFolding = true;\r
+ }\r
+ String encodeTargetText = source.substring(startIndex, endIndex);\r
+ buf.append(MimeUtility.encodeWord(encodeTargetText, charset, encoding));\r
+ if (needFolding) {\r
+ // folding により 空白一つが確保されるのでスキップ\r
+ endIndex++;\r
+ buf.append("\r\n ");\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * 指定位置から最初に見つかった非ASCII文字のIndexを返します。 startIndex が範囲外の場合は -1 を返します。\r
+ * (IndexOutOfBoundsException ではない)\r
+ * @param source 検索する文字列\r
+ * @param startIndex 検索開始位置\r
+ * @return 検出した非ASCII文字Index。見つからなければ-1。\r
+ */\r
+ public static int indexOfNonAscii(String source, int startIndex) {\r
+ for (int i = startIndex; i < source.length(); i++) {\r
+ if (source.charAt(i) > 0x7f) {\r
+ return i;\r
+ }\r
+ }\r
+ return -1;\r
+ }\r
+\r
+ /**\r
+ * 指定位置から最初に見つかったLWSP以外の文字のIndexを返します。 startIndex が範囲外の場合は -1 を返します。\r
+ * (IndexOutOfBoundsException ではない)\r
+ * @param source 検索する文字列\r
+ * @param startIndex 検索開始位置\r
+ * @param decrease trueで後方検索\r
+ * @return 検出した非ASCII文字Index。見つからなければ-1。\r
+ */\r
+ public static int indexOfNonLWSP(String source, int startIndex, boolean decrease) {\r
+ char c;\r
+ int inc = 1;\r
+ if (decrease)\r
+ inc = -1;\r
+ for (int i = startIndex; i >= 0 && i < source.length(); i += inc) {\r
+ c = source.charAt(i);\r
+ if (!isLWSP(c)) {\r
+ return i;\r
+ }\r
+ }\r
+ return -1;\r
+ }\r
+\r
+ /**\r
+ * 指定位置から最初に見つかったLWSPのIndexを返します。 startIndex が範囲外の場合は -1 を返します。\r
+ * (IndexOutOfBoundsException ではない)\r
+ * @param source 検索する文字列\r
+ * @param startIndex 検索開始位置\r
+ * @param decrease trueで後方検索\r
+ * @param additionalDelimiter LWSP以外に区切りとみなす文字(1字のみ)\r
+ * @return 検出した非ASCII文字Index。見つからなければ-1。\r
+ */\r
+ public static int indexOfLWSP(String source, int startIndex, boolean decrease,\r
+ char additionalDelimiter) {\r
+ char c;\r
+ int inc = 1;\r
+ if (decrease)\r
+ inc = -1;\r
+ for (int i = startIndex; i >= 0 && i < source.length(); i += inc) {\r
+ c = source.charAt(i);\r
+ if (isLWSP(c) || c == additionalDelimiter) {\r
+ return i;\r
+ }\r
+ }\r
+ return -1;\r
+ }\r
+\r
+ public static boolean isLWSP(char c) {\r
+ return c == '\r' || c == '\n' || c == ' ' || c == '\t';\r
+ }\r
+\r
+ //////////////////////////////////////////////////////////////////////////\r
+ /**\r
+ * This method set Content-Disposition: with RFC2231 encoding. It is\r
+ * required JavaMail1.2.\r
+ */\r
+ /**\r
+ * Part#setFileName()のマルチバイト対応版です。 JavaMail1.2でなければコンパイルできません\r
+ */\r
+ public static void setFileName(Part part, String filename, String charset, String lang)\r
+ throws MessagingException {\r
+ // Set the Content-Disposition "filename" parameter\r
+ ContentDisposition disposition;\r
+ String[] strings = part.getHeader("Content-Disposition");\r
+ if (strings == null || strings.length < 1) {\r
+ disposition = new ContentDisposition(Part.ATTACHMENT);\r
+ } else {\r
+ disposition = new ContentDisposition(strings[0]);\r
+ disposition.getParameterList().remove("filename");\r
+ }\r
+ part.setHeader("Content-Disposition", disposition.toString()\r
+ + encodeParameter("filename", filename, charset, lang));\r
+ ContentType cType;\r
+ strings = part.getHeader("Content-Type");\r
+ if (strings == null || strings.length < 1) {\r
+ cType = new ContentType(part.getDataHandler().getContentType());\r
+ } else {\r
+ cType = new ContentType(strings[0]);\r
+ }\r
+ try {\r
+ // I want to public the MimeUtility#doEncode()!!!\r
+ String mimeString = MimeUtility.encodeWord(filename, charset, "B");\r
+ // cut <CRLF>...\r
+ StringBuffer sb = new StringBuffer();\r
+ int i;\r
+ while ((i = mimeString.indexOf('\r')) != -1) {\r
+ sb.append(mimeString.substring(0, i));\r
+ mimeString = mimeString.substring(i + 2);\r
+ }\r
+ sb.append(mimeString);\r
+ cType.setParameter("name", new String(sb));\r
+ } catch (UnsupportedEncodingException e) {\r
+ throw new MessagingException("Encoding error", e);\r
+ }\r
+ part.setHeader("Content-Type", cType.toString());\r
+ }\r
+\r
+ /**\r
+ * This method encodes the parameter.\r
+ * <P>\r
+ * But most MUA cannot decode the encoded parameters by this method. <BR>\r
+ * I recommend using the "Content-Type:"'s name parameter both.\r
+ * </P>\r
+ */\r
+ /**\r
+ * ヘッダのパラメタ部のエンコードを行います。\r
+ * <P>\r
+ * 現状は受信できないものが多いのでこのメソッドだけでは使えません。 <BR>\r
+ * Content-Disposition:のfilenameのみに使用し、さらに Content-Type:のnameにMIME\r
+ * encodingでの記述も行うのが妥当でしょう。 <BR>\r
+ * パラメタは必ず行頭から始まるものとします。 (ヘッダの開始行から折り返された位置を開始位置とします)\r
+ * </P>\r
+ * <P>\r
+ * foldingの方針はascii/non ascii境界のみをチェックします。 現状は連続するascii/non\r
+ * asciiの長さのチェックは現状行っていません。 (エンコード後のバイト数でチェックしなければならないのでかなり面倒)\r
+ * </P>\r
+ * @param name パラメタ名\r
+ * @param value エンコード対象のパラメタ値\r
+ * @param encoding 文字エンコーディング\r
+ * @param lang 言語指定子\r
+ * @return エンコード済み文字列 ";\r\n name*0*=ISO-8859-2''・・・;\r\n name*1*=・・"\r
+ */\r
+ // 1.全体をエンコードして長かったら半分に切ってエンコードを繰り返す\r
+ public static String encodeParameter(String name, String value, String encoding, String lang) {\r
+ StringBuffer result = new StringBuffer();\r
+ StringBuffer encodedPart = new StringBuffer();\r
+ boolean needWriteCES = !isAllAscii(value);\r
+ boolean CESWasWritten = false;\r
+ boolean encoded;\r
+ boolean needFolding = false;\r
+ int sequenceNo = 0;\r
+ int column;\r
+ while (value.length() > 0) {\r
+ // index of boundary of ascii/non ascii\r
+ int lastIndex;\r
+ boolean isAscii = value.charAt(0) < 0x80;\r
+ for (lastIndex = 1; lastIndex < value.length(); lastIndex++) {\r
+ if (value.charAt(lastIndex) < 0x80) {\r
+ if (!isAscii)\r
+ break;\r
+ } else {\r
+ if (isAscii)\r
+ break;\r
+ }\r
+ }\r
+ if (lastIndex != value.length())\r
+ needFolding = true;\r
+ RETRY: while (true) {\r
+ encodedPart.setLength(0);\r
+ String target = value.substring(0, lastIndex);\r
+ byte[] bytes;\r
+ try {\r
+ if (isAscii) {\r
+ bytes = target.getBytes("us-ascii");\r
+ } else {\r
+ bytes = target.getBytes(encoding);\r
+ }\r
+ } catch (UnsupportedEncodingException e) {\r
+ bytes = target.getBytes(); // use default encoding\r
+ encoding = MimeUtility.mimeCharset(MimeUtility.getDefaultJavaCharset());\r
+ }\r
+ encoded = false;\r
+ // It is not strict.\r
+ column = name.length() + 7; // size of " " and "*nn*=" and ";"\r
+ for (int i = 0; i < bytes.length; i++) {\r
+ if ((bytes[i] >= '0' && bytes[i] <= '9')\r
+ || (bytes[i] >= 'A' && bytes[i] <= 'Z')\r
+ || (bytes[i] >= 'a' && bytes[i] <= 'z') || bytes[i] == '$'\r
+ || bytes[i] == '.' || bytes[i] == '!') {\r
+ // 2001/09/01 しかるべき文字が符号化されない問題修正\r
+ // attribute-char(符号化しなくてもよい文字)の定義は\r
+ // <any (US-ASCII) CHAR except SPACE, CTLs,\r
+ // "*", "'", "%", or tspecials>\r
+ // だが、ややこしいので英数字のみとしておく\r
+ // "$.!"はおまけ^^。エンコード時は大して意識はいらない\r
+ encodedPart.append((char)bytes[i]);\r
+ column++;\r
+ } else {\r
+ encoded = true;\r
+ encodedPart.append('%');\r
+ String hex = Integer.toString(bytes[i] & 0xff, 16);\r
+ if (hex.length() == 1) {\r
+ encodedPart.append('0');\r
+ }\r
+ encodedPart.append(hex);\r
+ column += 3;\r
+ }\r
+ if (column > 76) {\r
+ needFolding = true;\r
+ lastIndex /= 2;\r
+ continue RETRY;\r
+ }\r
+ }\r
+ result.append(";\r\n ").append(name);\r
+ if (needFolding) {\r
+ result.append('*').append(sequenceNo);\r
+ sequenceNo++;\r
+ }\r
+ if (!CESWasWritten && needWriteCES) {\r
+ result.append("*=");\r
+ CESWasWritten = true;\r
+ result.append(encoding).append('\'');\r
+ if (lang != null)\r
+ result.append(lang);\r
+ result.append('\'');\r
+ } else if (encoded) {\r
+ result.append("*=");\r
+ /*\r
+ * 本当にcharacter encodingは先頭パートに書かないとだめなのか? if (encoded) {\r
+ * result.append("*="); if (!CESWasWritten && needWriteCES) {\r
+ * CESWasWritten = true;\r
+ * result.append(encoding).append('\''); if (lang != null)\r
+ * result.append(lang); result.append('\''); }\r
+ */\r
+ } else {\r
+ result.append('=');\r
+ }\r
+ result.append(new String(encodedPart));\r
+ value = value.substring(lastIndex);\r
+ break;\r
+ }\r
+ }\r
+ return new String(result);\r
+ }\r
+\r
+ /** check if contains only ascii characters in text. */\r
+ public static boolean isAllAscii(String text) {\r
+ for (int i = 0; i < text.length(); i++) {\r
+ if (text.charAt(i) > 0x7f) { // non-ascii\r
+ return false;\r
+ }\r
+ }\r
+ return true;\r
+ }\r
+\r
+ //////////////////////////////////////////////////////////////////////////\r
+ /**\r
+ * This method decode the RFC2231 encoded filename parameter instead of\r
+ * Part#getFileName().\r
+ */\r
+ /**\r
+ * Part#getFileName()のマルチバイト対応版です。\r
+ */\r
+ public static String getFileName(Part part) throws MessagingException {\r
+ String[] disposition = part.getHeader("Content-Disposition");\r
+ // A patch by YOSI (Thanx)\r
+ // http://www.sk-jp.com/cgibin/treebbs.cgi?kako=1&all=227&s=227\r
+ String filename;\r
+ if (disposition == null || disposition.length < 1\r
+ || (filename = getParameter(disposition[0], "filename")) == null) {\r
+ filename = part.getFileName();\r
+ if (filename != null) {\r
+ return decodeParameterSpciallyJapanese(filename);\r
+ }\r
+ return null;\r
+ }\r
+ return filename;\r
+ }\r
+\r
+ static class Encoding {\r
+\r
+ String encoding = "us-ascii";\r
+\r
+ String lang = "";\r
+ }\r
+\r
+ /**\r
+ * This method decodes the parameter which be encoded (folded) by RFC2231\r
+ * method.\r
+ * <P>\r
+ * The parameter's order should be considered.\r
+ * </P>\r
+ */\r
+ /**\r
+ * ヘッダのパラメタ部のデコードを行います。\r
+ * <P>\r
+ * RFC2231形式でfolding(分割)されたパラメタを結合し、デコードします。\r
+ * 尚、RFC2231にはパラメタの順番に依存するなと書かれていますが、 それを実装すると大変面倒(一度分割された全てのパートを\r
+ * 保持してソートしなければならない)なので、 シーケンス番号に関係なく(0から)順番に 並んでいるものとみなして処理することにします。\r
+ * </P>\r
+ * @param header ヘッダの値全体\r
+ * @param name 取得したいパラメタ名\r
+ * @return デコード済み文字列 (パラメタが存在しない場合は null)\r
+ */\r
+ public static String getParameter(String header, String name) throws ParseException {\r
+ if (header == null)\r
+ return null;\r
+ // 本来これは不要。日本固有のデコード処理です。\r
+ // 2001/07/22 書籍版では"あ.txt"の生JISパラメタ値がデコードできない\r
+ // これは、ISO-2022-JPバイト列のままHeaderTokenizerにかけると、\r
+ // "あ"のバイトシーケンスに含まれる0x22がダブルクォートと\r
+ // 解釈されるため。\r
+ // JIS/Shift_JISの生バイトと思われるもののデコードを先に行う事で回避\r
+ header = decodeParameterSpciallyJapanese(header);\r
+ HeaderTokenizer tokenizer = new HeaderTokenizer(header, ";=\t ", true);\r
+ HeaderTokenizer.Token token;\r
+ StringBuffer sb = new StringBuffer();\r
+ // It is specified in first encoded-part.\r
+ Encoding encoding = new Encoding();\r
+ String n;\r
+ String v;\r
+ try {\r
+ while (true) {\r
+ token = tokenizer.next();\r
+ if (token.getType() == HeaderTokenizer.Token.EOF)\r
+ break;\r
+ if (token.getType() != ';')\r
+ continue;\r
+ token = tokenizer.next();\r
+ checkType(token);\r
+ n = token.getValue();\r
+ token = tokenizer.next();\r
+ if (token.getType() != '=') {\r
+ throw new ParseException("Illegal token : " + token.getValue());\r
+ }\r
+ token = tokenizer.next();\r
+ checkType(token);\r
+ v = token.getValue();\r
+ if (n.equalsIgnoreCase(name)) {\r
+ // It is not divided and is not encoded.\r
+ return v;\r
+ }\r
+ int index = name.length();\r
+ if (!n.startsWith(name) || n.charAt(index) != '*') {\r
+ // another parameter\r
+ continue;\r
+ }\r
+ // be folded, or be encoded\r
+ int lastIndex = n.length() - 1;\r
+ if (n.charAt(lastIndex) == '*') {\r
+ // http://www.sk-jp.com/cgibin/treebbs.cgi?all=399&s=399\r
+ if (index == lastIndex || n.charAt(index + 1) == '0') {\r
+ // decode as initial-section\r
+ sb.append(decodeRFC2231(v, encoding, true));\r
+ } else {\r
+ // decode as other-sections\r
+ sb.append(decodeRFC2231(v, encoding, false));\r
+ }\r
+ } else {\r
+ sb.append(v);\r
+ }\r
+ if (index == lastIndex) {\r
+ // not folding\r
+ break;\r
+ }\r
+ }\r
+ if (sb.length() == 0)\r
+ return null;\r
+ return new String(sb);\r
+ } catch (UnsupportedEncodingException e) {\r
+ throw new ParseException(e.toString());\r
+ }\r
+ }\r
+\r
+ private static void checkType(HeaderTokenizer.Token token) throws ParseException {\r
+ int t = token.getType();\r
+ if (t != HeaderTokenizer.Token.ATOM && t != HeaderTokenizer.Token.QUOTEDSTRING) {\r
+ throw new ParseException("Illegal token : " + token.getValue());\r
+ }\r
+ }\r
+\r
+ // "lang" tag is ignored...\r
+ private static String decodeRFC2231(String s, Encoding encoding, boolean isInitialSection)\r
+ throws ParseException,\r
+ UnsupportedEncodingException {\r
+ StringBuffer sb = new StringBuffer();\r
+ int i = 0;\r
+ if (isInitialSection) {\r
+ int work = s.indexOf('\'');\r
+ if (work > 0) {\r
+ encoding.encoding = s.substring(0, work);\r
+ work++;\r
+ i = s.indexOf('\'', work);\r
+ if (i < 0) {\r
+ throw new ParseException("lang tag area was missing.");\r
+ }\r
+ encoding.lang = s.substring(work, i);\r
+ i++;\r
+ }\r
+ }\r
+ try {\r
+ for (; i < s.length(); i++) {\r
+ if (s.charAt(i) == '%') {\r
+ sb.append((char)Integer.parseInt(s.substring(i + 1, i + 3), 16));\r
+ i += 2;\r
+ continue;\r
+ }\r
+ sb.append(s.charAt(i));\r
+ }\r
+ return new String(new String(sb).getBytes("ISO-8859-1"), encoding.encoding);\r
+ } catch (IndexOutOfBoundsException e) {\r
+ throw new ParseException(s + " :: this string were not decoded.");\r
+ }\r
+ }\r
+\r
+ // 日本語向けデコード\r
+ private static String decodeParameterSpciallyJapanese(String s) throws ParseException {\r
+ try {\r
+ // decode by character encoding.\r
+ // if string are all ASCII, it is not translated.\r
+ s = new String(s.getBytes("ISO-8859-1"), "JISAutoDetect");\r
+ // decode by RFC2047.\r
+ // if string doesn't contain encoded-word, it is not translated.\r
+ return decodeText(s);\r
+ } catch (UnsupportedEncodingException e) {}\r
+ throw new ParseException("Unsupported Encoding");\r
+ }\r
+\r
+ private MailUtility() {}\r
+}
\ No newline at end of file
--- /dev/null
+/*\r
+ * @(#) $Id: MultipartUtility.java,v 1.1.2.2 2004/10/24 10:26:50 otsuka Exp $\r
+ * $Revision: 1.1.2.2 $\r
+ * Copyright (c) 2000 Shin Kinoshita All Rights Reserved.\r
+ */\r
+package com.ozacc.mail.fetch.impl.sk_jp;\r
+\r
+import java.io.IOException;\r
+import java.io.UnsupportedEncodingException;\r
+\r
+import javax.activation.DataHandler;\r
+import javax.mail.MessagingException;\r
+import javax.mail.Multipart;\r
+import javax.mail.Part;\r
+import javax.mail.internet.ContentType;\r
+import javax.mail.internet.MimeBodyPart;\r
+import javax.mail.internet.MimeMultipart;\r
+\r
+/**\r
+ * メッセージボディを取り出す手段を提供するstaticメソッドのセットです。\r
+ * <p>\r
+ * </p>\r
+ * @version $Revision: 1.1.2.2 $ $Date: 2004/10/24 10:26:50 $\r
+ * @author Shin\r
+ * @author Tomohiro Otsuka\r
+ */\r
+public class MultipartUtility {\r
+\r
+ private static final String JIS_CHARSET = "ISO-2022-JP";\r
+\r
+ /**\r
+ * 指定パートのボディを返します。\r
+ * <P>\r
+ * Part#getContent()の代わりです。\r
+ * MIMEに準拠しないContent-Type:の補正を行います。\r
+ * charset指定がない場合は"ISO-2022-JP"を補います。\r
+ * </P><P>\r
+ * パートがUTF-7の場合も正常に内容を取得出来ます。\r
+ * </P>\r
+ */\r
+ public static Object getContent(Part part) throws MessagingException, IOException {\r
+ return getContent(part, JIS_CHARSET);\r
+ }\r
+\r
+ private static CorrectedContentTypeDataSource correctedDataSource = new CorrectedContentTypeDataSourceUTF7Support();\r
+\r
+ private static DataHandler correctedDataHandler = new DataHandler(correctedDataSource);\r
+\r
+ /**\r
+ * 指定パートのボディを返します。\r
+ * <P>\r
+ * MIMEに準拠しないContent-Type:の補正を行います。\r
+ * charset指定がない場合はcharsetで指定されたもので補います。\r
+ * </P><P>\r
+ * パートがUTF-7の場合も正常に内容を取得出来ます。\r
+ * </P>\r
+ */\r
+ public static Object getContent(Part part, String charset) throws MessagingException,\r
+ IOException {\r
+ synchronized (correctedDataSource) {\r
+\r
+ correctedDataSource.setPart(part);\r
+ try {\r
+ correctedDataSource.setDefaultCharset(charset);\r
+ return correctedDataHandler.getContent();\r
+ } catch (UnsupportedEncodingException e) {\r
+ /*\r
+ * 不正な文字コードがcharsetにセットされ例外がスローされた場合に\r
+ * JIS_CHARSETに文字コードを置き換え、再度ホディの取得を試みます。\r
+ * \r
+ * by otsuka\r
+ */\r
+ correctedDataSource.setForceCharset(JIS_CHARSET);\r
+ return correctedDataHandler.getContent();\r
+ }\r
+\r
+ }\r
+ }\r
+\r
+ /**\r
+ * 指定パート配下で最初に見つけたテキストパートのボディを返します。\r
+ * process()を呼び出して結果を返すだけのconvenience methodです。\r
+ */\r
+ public static String getFirstPlainText(Part part) throws MessagingException {\r
+ FirstPlainPartExtractor h = new FirstPlainPartExtractor();\r
+ process(part, h);\r
+ return h.getText();\r
+ }\r
+\r
+ /**\r
+ * 指定パート配下のinlineなテキストパートを集めて表示用のボディを返します。\r
+ * process()を呼び出して結果を返すだけのconvenience methodです。\r
+ */\r
+ public static String getPlainText(Part part) throws MessagingException {\r
+ PlainPartExtractor h = new PlainPartExtractor();\r
+ process(part, h);\r
+ return h.getText();\r
+ }\r
+\r
+ /**\r
+ * 指定パート配下の各パートを処理します。\r
+ * <P>\r
+ * すべてのPartに対してPartHandlerが呼び出されます。<BR>\r
+ * </P>\r
+ */\r
+ public static void process(Part part, PartHandler handler) throws MessagingException {\r
+ process(part, handler, null);\r
+ }\r
+\r
+ private static boolean process(Part part, PartHandler handler, ContentType context)\r
+ throws MessagingException {\r
+ try {\r
+ if (part.isMimeType("multipart/*")) {\r
+ Multipart mp = (Multipart)part.getContent();\r
+ ContentType cType = new ContentType(part.getContentType());\r
+ for (int i = 0; i < mp.getCount(); i++) {\r
+ if (!process(mp.getBodyPart(i), handler, cType)) {\r
+ return false;\r
+ }\r
+ }\r
+ return true;\r
+ }\r
+ return handler.processPart(part, context);\r
+ } catch (IOException e) {\r
+ throw new MessagingException("Got exception \nin " + part + "\n", e);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * 指定partにbodyPartを追加します。\r
+ * partがマルチパーとコンテナの場合はそのコンテナにbodyPartを追加します。\r
+ * そうでない場合はpartのボディとしてmultipart/mixedのコンテナを設定し、\r
+ * 元のpartのボディとbodyPartのボディをそのコンテナに追加します。\r
+ */\r
+ public static void addBodyPart(Part part, MimeBodyPart bodyPart) throws MessagingException,\r
+ IOException {\r
+ if (part.isMimeType("multipart/*")) {\r
+ ((MimeMultipart)part.getContent()).addBodyPart(bodyPart);\r
+ return;\r
+ }\r
+ // 仮\r
+ MimeMultipart mp = new MimeMultipart("mixed");\r
+ MimeBodyPart original = new MimeBodyPart();\r
+ original.setContent(part.getContent(), part.getContentType());\r
+ mp.addBodyPart(original);\r
+ mp.addBodyPart(bodyPart);\r
+ part.setContent(mp);\r
+ }\r
+\r
+ /**\r
+ * partのツリー構造をダンプするデバッグ用メソッドです。\r
+ */\r
+ public static void dump(Part part) {\r
+ dump(part, 0);\r
+ }\r
+\r
+ private static void dump(Part part, int layer) {\r
+ for (int i = 0; i < layer; i++) {\r
+ System.out.print(" ");\r
+ }\r
+ try {\r
+ System.out.println(part.getClass() + ":" + part.getContentType());\r
+ if (part.isMimeType("multipart/*")) {\r
+ MimeMultipart mp = (MimeMultipart)part.getContent();\r
+ for (int i = 0; i < mp.getCount(); i++) {\r
+ dump(mp.getBodyPart(i), layer + 1);\r
+ }\r
+ }\r
+ } catch (Exception e) {\r
+ e.printStackTrace();\r
+ }\r
+ }\r
+}
\ No newline at end of file
--- /dev/null
+/*\r
+ * @(#) $Id: PartHandler.java,v 1.1.2.1 2004/09/29 00:57:59 otsuka Exp $\r
+ * $Revision: 1.1.2.1 $\r
+ * Copyright (c) 2000 Shin Kinoshita All Rights Reserved.\r
+ */\r
+package com.ozacc.mail.fetch.impl.sk_jp;\r
+\r
+import java.io.IOException;\r
+import javax.mail.Part;\r
+import javax.mail.MessagingException;\r
+import javax.mail.internet.ContentType;\r
+\r
+/**\r
+ * PartHandlerです。\r
+ * <p>\r
+ * MultipartUtility#process()に渡すことで、Message内の各Partオブジェクト\r
+ * に対してprocessPartが呼び出されます。<BR>\r
+ * 特定のMIMEタイプに対してのみ処理を行う場合などに有効です。\r
+ * </p><p>\r
+ * 使用方法としては、実装クラス上に各パートの処理結果を蓄積していき、\r
+ * MultipartUtility#process()メソッド復帰後にそのオブジェクトから最終結果を\r
+ * 取り出すような形式が考えられます。\r
+ * </p>\r
+ * @version $Revision: 1.1.2.1 $ $Date: 2004/09/29 00:57:59 $\r
+ * @author Shin\r
+ */\r
+public interface PartHandler {\r
+\r
+ /**\r
+ * パートに対して処理を行います。\r
+ * <P>\r
+ * contextにはそのパートがmultipart/*の子パートである場合に、\r
+ * そのmultipart/*のMIMEタイプが渡されます。<BR>\r
+ * 続けて次のパートを処理するか否かを復帰値で返してください。\r
+ * </P><P>\r
+ * message/rfc822パートの内部も走査したい場合は、実装クラス内で\r
+ * 以下のようにコーディングしてください。\r
+ * </P>\r
+ * <PRE>if (part.isMimeType("message/rfc822")) {\r
+ * // message/rfc822パートの処理オブジェクト\r
+ * AnyHandler h = new AnyHandler();\r
+ * MultipartUtility.process(part, h);\r
+ * }\r
+ * </PRE>\r
+ * @return true:次のパート、或いは内包メッセージ内部も処理する\r
+ */\r
+ boolean processPart(Part part, ContentType context) throws MessagingException, IOException;\r
+}
\ No newline at end of file
--- /dev/null
+/*\r
+ * @(#) $Id: PlainPartExtractor.java,v 1.1.2.1 2004/09/29 00:57:59 otsuka Exp $\r
+ * $Revision: 1.1.2.1 $\r
+ * Copyright (c) 2000 Shin Kinoshita All Rights Reserved.\r
+ */\r
+package com.ozacc.mail.fetch.impl.sk_jp;\r
+\r
+import java.io.IOException;\r
+\r
+import javax.mail.MessagingException;\r
+import javax.mail.Part;\r
+import javax.mail.internet.ContentType;\r
+import javax.mail.internet.MimeMessage;\r
+\r
+/**\r
+ * text/plainを結合した文字列を得るPartHandlerです。\r
+ * \r
+ * @version $Revision: 1.1.2.1 $ $Date: 2004/09/29 00:57:59 $\r
+ * @author Shin\r
+ */\r
+public class PlainPartExtractor implements PartHandler {\r
+\r
+ private String text = null;\r
+\r
+ public boolean processPart(Part part, ContentType context) throws MessagingException,\r
+ IOException {\r
+ if (!part.isMimeType("text/plain")) {\r
+ return true;\r
+ }\r
+ if (text == null) {\r
+ // 最初のテキストパートを無条件に抽出\r
+ text = (String)MultipartUtility.getContent(part);\r
+ } else {\r
+ String disposition = part.getDisposition();\r
+ if (disposition == null || disposition.equalsIgnoreCase(Part.INLINE)) {\r
+ text += "\r\n\r\n-- inline --\r\n\r\n" + (String)MultipartUtility.getContent(part);\r
+ }\r
+ }\r
+ return true;\r
+ }\r
+\r
+ public String getText() {\r
+ return text;\r
+ }\r
+\r
+ public static void main(String[] args) throws Exception {\r
+ MimeMessage msg = new MimeMessage(javax.mail.Session.getDefaultInstance(System\r
+ .getProperties(), null), System.in);\r
+ PlainPartExtractor h = new PlainPartExtractor();\r
+ MultipartUtility.process(msg, h);\r
+\r
+ System.out.println("This is the detected text/plain parts.");\r
+ System.out.println(h.getText());\r
+ }\r
+}
\ No newline at end of file
--- /dev/null
+/*\r
+ * @(#) $Id: ByteToCharUTF7.java,v 1.1.2.1 2004/09/29 00:57:59 otsuka Exp $\r
+ * $Revision: 1.1.2.1 $\r
+ * Copyright (c) 2000 Shin Kinoshita All Rights Reserved.\r
+ */\r
+package com.ozacc.mail.fetch.impl.sk_jp.io;\r
+\r
+/**\r
+ * UTF-7のデコーダです。\r
+ * <p>\r
+ * </p>\r
+ * @author Shin\r
+ */\r
+public class ByteToCharUTF7 extends sun.io.ByteToCharConverter {\r
+ public String getCharacterEncoding() {\r
+ return "UTF7";\r
+ }\r
+\r
+ public int flush(char[] chars, int off, int len) {\r
+ byteOff = 0;\r
+ charOff = 0;\r
+ b64Context = false;\r
+ currentB64Off = 0;\r
+ currentChar = 0;\r
+ return 0;\r
+ }\r
+ public void reset() {\r
+ byteOff = 0;\r
+ charOff = 0;\r
+ b64Context = false;\r
+ currentB64Off = 0;\r
+ currentChar = 0;\r
+ }\r
+\r
+ private boolean b64Context = false;\r
+ private int currentB64Off = 0;\r
+ private char currentChar = 0;\r
+\r
+ public int convert(\r
+ byte[] bytes,\r
+ int byteStart,\r
+ int byteEnd,\r
+ char[] chars,\r
+ int charStart,\r
+ int charEnd)\r
+ throws\r
+ sun.io.ConversionBufferFullException,\r
+ sun.io.UnknownCharacterException {\r
+ charOff = charStart;\r
+\r
+ for (byteOff = byteStart; byteOff < byteEnd; byteOff++) {\r
+ if (charOff >= charEnd) {\r
+ throw new sun.io.ConversionBufferFullException();\r
+ }\r
+ if (b64Context) {\r
+ if (bytes[byteOff] == '-') {\r
+ if (currentB64Off != 0 && currentChar > 0) {\r
+ chars[charOff] = currentChar;\r
+ charOff++;\r
+ }\r
+ b64Context = false;\r
+ continue;\r
+ }\r
+ int part =\r
+ (\r
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"\r
+ + "abcdefghijklmnopqrstuvwxyz0123456789+/").indexOf(\r
+ bytes[byteOff]);\r
+ if (part == -1) {\r
+ throw new sun.io.UnknownCharacterException(\r
+ "Invalid UTF-7 code: " + (char)bytes[byteOff]);\r
+ }\r
+\r
+ switch (currentB64Off) {\r
+ case 0 :\r
+ currentChar = (char) (part << 10);\r
+ break;\r
+ case 1 :\r
+ currentChar |= (char) (part << 4);\r
+ break;\r
+ case 2 :\r
+ currentChar |= (char) (part >> 2);\r
+ chars[charOff] = currentChar;\r
+ charOff++;\r
+ currentChar = (char) ((part & 0x03) << 14);\r
+ break;\r
+ case 3 :\r
+ currentChar |= (char) (part << 8);\r
+ break;\r
+ case 4 :\r
+ currentChar |= (char) (part << 2);\r
+ break;\r
+ case 5 :\r
+ currentChar |= (char) (part >> 4);\r
+ chars[charOff] = currentChar;\r
+ charOff++;\r
+ currentChar = (char) ((part & 0x0f) << 12);\r
+ break;\r
+ case 6 :\r
+ currentChar |= (char) (part << 6);\r
+ break;\r
+ case 7 :\r
+ currentChar |= (char)part;\r
+ chars[charOff] = currentChar;\r
+ charOff++;\r
+ break;\r
+ }\r
+ currentB64Off = (currentB64Off + 1) % 8;\r
+ continue;\r
+ }\r
+\r
+ if (bytes[byteOff] == '+') {\r
+ // shift character\r
+ // This is start of the Base64 sequence.\r
+ b64Context = true;\r
+ currentB64Off = 0;\r
+ continue;\r
+ }\r
+ chars[charOff] = (char)bytes[byteOff];\r
+ charOff++;\r
+ }\r
+ return charOff - charStart;\r
+ }\r
+\r
+ /*\r
+ public static void main(String[] args) throws Exception {\r
+ System.setProperty("file.encoding.pkg", "com.sk_jp.io");\r
+ ByteArrayOutputStream o = new ByteArrayOutputStream();\r
+ byte[] b = new byte[2048];\r
+ int len;\r
+ while ((len = System.in.read(b)) != -1) {\r
+ o.write(b, 0, len);\r
+ }\r
+ byte[] bytes = o.toByteArray();\r
+ \r
+ System.out.println(new String(bytes, "UTF7"));\r
+ }\r
+ */\r
+}\r
--- /dev/null
+/*\r
+ * Copyright (c) 2003 Shin Kinoshita All Rights Reserved.\r
+ */\r
+package com.ozacc.mail.fetch.impl.sk_jp.io;\r
+\r
+import java.io.ByteArrayOutputStream;\r
+import java.io.UnsupportedEncodingException;\r
+\r
+/**\r
+ * 文字関係のコンバータです。\r
+ * 一部コードのオリジナルは<a href="http://www-cms.phys.s.u-tokyo.ac.jp/~naoki/CIPINTRO/CCGI/kanjicod.html">Japanese Kanji Code</a>にて公開されているものです。\r
+ * また、http://www.sk-jp.com/cgi-bin/treebbs.cgi?kako=1&all=644&s=681\r
+ * にて YOSI さんが公開されたコードも参考にしています(というか実質同じです)。\r
+ *\r
+ * @author Shin\r
+ * @version $Revision: 1.1.2.1 $ $Date: 2005/01/18 07:20:36 $\r
+ */\r
+public class CharCodeConverter {\r
+ public static final byte[] SJIS_KANA;\r
+ static {\r
+ try {\r
+ // 全角への変換テーブル\r
+ SJIS_KANA = "。「」、・ヲァィゥェォャュョッーアイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン゛゜".getBytes("Shift_JIS");\r
+ } catch (UnsupportedEncodingException e) {\r
+ throw new RuntimeException("CANT HAPPEN");\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Shift_JIS エンコーディングスキームに基づくバイト列を\r
+ * ISO-2022-JP エンコーディングスキームに変換します。\r
+ * 「半角カナ」は対応する全角文字に変換します。\r
+ */\r
+ public static byte[] sjisToJis(byte[] sjisBytes) {\r
+ ByteArrayOutputStream out = new ByteArrayOutputStream();\r
+ boolean nonAscii = false;\r
+ int len = sjisBytes.length;\r
+ for (int i = 0; i < len; i++ ) {\r
+ if (sjisBytes[i] >= 0) {\r
+ if (nonAscii) {\r
+ nonAscii = false;\r
+ out.write(0x1b);\r
+ out.write('(');\r
+ out.write('B');\r
+ }\r
+ out.write(sjisBytes[i]);\r
+ } else {\r
+ if (!nonAscii) {\r
+ nonAscii = true;\r
+ out.write(0x1b);\r
+ out.write('$');\r
+ out.write('B');\r
+ }\r
+ int b = sjisBytes[i] & 0xff;\r
+ if (b >= 0xa1 && b <= 0xdf) {\r
+ // 半角カナは全角に変換\r
+ int kanaIndex = (b - 0xA1) * 2;\r
+ sjisToJis(out, SJIS_KANA[kanaIndex], SJIS_KANA[kanaIndex + 1]);\r
+ } else {\r
+ i++;\r
+ if (i == len) break;\r
+ sjisToJis(out, sjisBytes[i - 1], sjisBytes[i]);\r
+ }\r
+ }\r
+ }\r
+ if (nonAscii) {\r
+ out.write(0x1b);\r
+ out.write('(');\r
+ out.write('B');\r
+ }\r
+ return out.toByteArray();\r
+ }\r
+ /**\r
+ * 1文字の2バイト Shift_JIS コードを JIS コードに変換して書き出します。\r
+ */\r
+ private static void sjisToJis(\r
+ ByteArrayOutputStream out, byte bh, byte bl) {\r
+ int h = (bh << 1) & 0xFF;\r
+ int l = bl & 0xFF;\r
+ if (l < 0x9F) {\r
+ if (h < 0x3F) h += 0x1F; else h -= 0x61;\r
+ if (l > 0x7E) l -= 0x20; else l -= 0x1F;\r
+ } else {\r
+ if (h < 0x3F) h += 0x20; else h -= 0x60;\r
+ l -= 0x7E;\r
+ }\r
+ out.write(h);\r
+ out.write(l);\r
+ }\r
+\r
+ /**\r
+ * 配列はワイドキャラクタの境界にないことを前提としています。\r
+ * また、エスケープシーケンスが適切に含まれることも前提です。\r
+ * エスケープシーケンスが"(B"/"$B"以外の場合は無視します。\r
+ */\r
+ public byte[] jisTosjis(byte[] jisBytes) {\r
+ ByteArrayOutputStream out = new ByteArrayOutputStream();\r
+ boolean nonAscii = false;\r
+ boolean kana = false;\r
+ int len = jisBytes.length;\r
+ for (int i = 0; i < len; i++) {\r
+ if (jisBytes[i] == 0x1b) {\r
+ if (i + 2 >= len) break;\r
+ if (jisBytes[i + 1] == '$' && jisBytes[i + 2] == 'B') {\r
+ nonAscii = true;\r
+ i += 2;\r
+ } else if (jisBytes[i + 1] == '(' && jisBytes[i + 2] == 'I') {\r
+ kana = true;\r
+ i += 2;\r
+ } else if (jisBytes[i + 1] == '(' && jisBytes[i + 2] == 'B') {\r
+ nonAscii = false;\r
+ kana = false;\r
+ i += 2;\r
+ } else {\r
+ // illegal sequence は当面無視\r
+ nonAscii = false;\r
+ kana = false;\r
+ }\r
+ continue;\r
+ }\r
+ if (jisBytes[i] == 0x0e) { // SO\r
+ kana = true;\r
+ continue;\r
+ }\r
+ if (jisBytes[i] == 0x0f) { // SI\r
+ kana = false;\r
+ continue;\r
+ }\r
+ if (kana) {\r
+ out.write(jisBytes[i] | 0x80);\r
+ } else if (nonAscii) {\r
+ i++;\r
+ if (i == jisBytes.length) break;\r
+ jisToSjis(out, jisBytes[i - 1], jisBytes[i]);\r
+ } else {\r
+ out.write(jisBytes[i]);\r
+ }\r
+ }\r
+ return out.toByteArray();\r
+ }\r
+ /**\r
+ * 1文字の2バイト JIS コードを Shift_JIS に変換して書き出します。\r
+ */\r
+ private static void jisToSjis(\r
+ ByteArrayOutputStream out, byte bh, byte bl) {\r
+ int h = bh & 0xFF;\r
+ int l = bl & 0xFF;\r
+ if ((h & 0x01) > 0) {\r
+ h >>= 1;\r
+ if (h < 0x2F) h += 0x71; else h -= 0x4F;\r
+ if (l > 0x5F) l += 0x20; else l += 0x1F;\r
+ } else {\r
+ h >>= 1;\r
+ if (h < 0x2F) h += 0x70; else h -= 0x50;\r
+ l += 0x7E;\r
+ }\r
+ out.write(h);\r
+ out.write(l);\r
+ }\r
+}\r
--- /dev/null
+/*\r
+ * @(#) $Id: FromCP932Corrector.java,v 1.1.2.1 2005/01/18 07:20:36 otsuka Exp $\r
+ * $Revision: 1.1.2.1 $\r
+ * Copyright (c) 2000 Shin Kinoshita All Rights Reserved.\r
+ */\r
+package com.ozacc.mail.fetch.impl.sk_jp.io;\r
+\r
+/**\r
+ * FromCP932Corrector.\r
+ * <p>\r
+ * CorrectOutputStreamWriterで用いられる文字列バッファ補正クラスです。<br>\r
+ * 風間一洋さんのJavaHouse-Brewers投稿記事[14452]のCp932クラスから\r
+ * 変換表を参考にさせていただいています。<br>\r
+ * </p>\r
+ * @version $Revision: 1.1.2.1 $ $Date: 2005/01/18 07:20:36 $\r
+ * @author Shin\r
+ */\r
+public class FromCP932Corrector extends UnicodeCorrector {\r
+ /**\r
+ * Unicode文字配列の補正を行います。\r
+ * <p>\r
+ * "MS932""Shift_JIS"エンコーディング以外で出力しようとした際の\r
+ * sun.ioやcom.msコンバータでは正常に変換できない部分を補正します。\r
+ * </p>\r
+ * @param c source character\r
+ * @return Result character that corrected.\r
+ */\r
+ public char correct(char c) {\r
+ switch (c) {\r
+ // ISO-2022-JPコンバータが正しく解釈しているようだ\r
+// case 0xff3c: // FULLWIDTH REVERSE SOLIDUS ->\r
+// return 0x005c; // REVERSE SOLIDUS\r
+ case 0xff5e: // FULLWIDTH TILDE ->\r
+ return 0x301c; // WAVE DASH\r
+ case 0x2225: // PARALLEL TO ->\r
+ return 0x2016; // DOUBLE VERTICAL LINE\r
+ case 0xff0d: // FULLWIDTH HYPHEN-MINUS ->\r
+ return 0x2212; // MINUS SIGN\r
+ case 0xffe0: // FULLWIDTH CENT SIGN ->\r
+ return 0x00a2; // CENT SIGN\r
+ case 0xffe1: // FULLWIDTH POUND SIGN ->\r
+ return 0x00a3; // POUND SIGN\r
+ case 0xffe2: // FULLWIDTH NOT SIGN ->\r
+ return 0x00ac; // NOT SIGN\r
+ }\r
+ return c;\r
+ }\r
+}\r
--- /dev/null
+/*\r
+ * @(#) $Id: ToCP932Corrector.java,v 1.1.2.1 2005/01/18 07:20:36 otsuka Exp $\r
+ * $Revision: 1.1.2.1 $\r
+ * Copyright (c) 2000 Shin Kinoshita All Rights Reserved.\r
+ */\r
+package com.ozacc.mail.fetch.impl.sk_jp.io;\r
+\r
+/**\r
+ * ToCP932Corrector.\r
+ * <p>\r
+ * CorrectOutputStreamWriterで用いられる文字列バッファ補正クラスです。<br>\r
+ * 風間一洋さんのJavaHouse-Brewers投稿記事[14452]のCp932クラス\r
+ * (XML日本語プロファイルも同じです)から\r
+ * 変換表を参考にさせていただいています。<br>\r
+ * </p>\r
+ * @version $Revision: 1.1.2.1 $ $Date: 2005/01/18 07:20:36 $\r
+ * @author Shin\r
+ */\r
+public class ToCP932Corrector extends UnicodeCorrector {\r
+ /**\r
+ * Unicode文字の補正を行います。\r
+ * <p>\r
+ * 特定の文字を"MS932""Shift_JIS"エンコーディングで出力しようとした際の\r
+ * sun.ioコンバータでは正常に変換できない部分を補正します。\r
+ * </p>\r
+ * @param c source character\r
+ * @return Result character that corrected.\r
+ */\r
+ public char correct(char c) {\r
+ switch (c) {\r
+// case 0x005c: // REVERSE SOLIDUS ->\r
+// return 0xff3c; // FULLWIDTH REVERSE SOLIDUS\r
+ case 0x301c: // WAVE DASH ->\r
+ return 0xff5e; // FULLWIDTH TILDE\r
+ case 0x2016: // DOUBLE VERTICAL LINE ->\r
+ return 0x2225; // PARALLEL TO\r
+ case 0x2212: // MINUS SIGN ->\r
+ return 0xff0d; // FULLWIDTH HYPHEN-MINUS\r
+ // MS932コンバータが正しく解釈しているようだ\r
+/*\r
+ case 0x00a2: // CENT SIGN ->\r
+ return 0xffe0; // FULLWIDTH CENT SIGN\r
+ case 0x00a3: // POUND SIGN ->\r
+ return 0xffe1; // FULLWIDTH POUND SIGN\r
+ case 0x00ac: // NOT SIGN ->\r
+ return 0xffe2; // FULLWIDTH NOT SIGN\r
+*/\r
+ }\r
+ return c;\r
+ }\r
+}\r
--- /dev/null
+/*\r
+ * @(#) $Id: UnicodeCorrector.java,v 1.1.2.1 2005/01/18 07:20:36 otsuka Exp $\r
+ * $Revision: 1.1.2.1 $\r
+ * Copyright (c) 2000 Shin Kinoshita All Rights Reserved.\r
+ */\r
+package com.ozacc.mail.fetch.impl.sk_jp.io;\r
+\r
+import java.io.UnsupportedEncodingException;\r
+import java.util.HashMap;\r
+import java.util.Map;\r
+\r
+/**\r
+ * UnicodeCorrector.\r
+ * <p>\r
+ * CorrectOutputStreamWriterで用いられる文字列バッファ補正クラスです。<br>\r
+ * 出力エンコーディングによって補正すべき文字コードが異なるので、\r
+ * 実際の補正処理はサブクラスで行います。<br>\r
+ *\r
+ * 対応しているのは以下のコンバータ名です。\r
+ * </p>\r
+ * <UL>\r
+ * <LI>ISO2022JP\r
+ * <LI>ISO-2022-JP\r
+ * <LI>EUC-JP\r
+ * <LI>EUCJIS\r
+ * <LI>SJIS\r
+ * <LI>Shift_JIS\r
+ * <LI>MS932\r
+ * <LI>Windows-31J\r
+ * </UL>\r
+ * @author Shin\r
+ * @version $Revision: 1.1.2.1 $ $Date: 2005/01/18 07:20:36 $\r
+ */\r
+public abstract class UnicodeCorrector {\r
+ private static final Map correctorMap = new HashMap();\r
+ static {\r
+ // x-sjis-cp932等でおかしく変換されたUnicodeを他のエンコーディングで\r
+ // 出力する場合です\r
+ correctorMap.put("iso2022jp", FromCP932Corrector.class);\r
+ correctorMap.put("iso-2022-jp", FromCP932Corrector.class);\r
+ correctorMap.put("euc-jp", FromCP932Corrector.class);\r
+ correctorMap.put("eucjis", FromCP932Corrector.class);\r
+ // 同じ"SJIS"でもSunとMS-VMでは異なります。\r
+ // Sun-JDK1.1はSJISはx-sjis-jdk-1.1.7で、MS-VMはx-sjis-cp932です。\r
+ // しかし、MS-JDK中のクラスライブラリだけはx-sjis-jdk-1.1.7相当になるので\r
+ // 困ったものです。(ファイル書き出し等のときのみおかしくなる)\r
+ // これはWindowsネイティブな変換表で生成されたUnicodeを\r
+ // x-sjis-jdk-1.1.7で出力するためのものです。\r
+ correctorMap.put("sjis", FromCP932Corrector.class);\r
+ correctorMap.put("shift_jis", FromCP932Corrector.class);\r
+ // MS-VMでファイル出力を行う場合に限り、こちらでなければなりません。\r
+ // correctorMap.put("sjis", ToCP932Corrector.class);\r
+ // JDK1.2からはWindowsのデフォルトが"MS932"(x-sjis-cp932相当)となりました。\r
+ // これで、他のエンコーディングで変換された(正しい)Unicodeが"ms932"\r
+ // での出力時におかしくなってしまいます。\r
+ // これを補正します。\r
+ correctorMap.put("ms932", ToCP932Corrector.class);\r
+ correctorMap.put("windows-31j", ToCP932Corrector.class);\r
+ }\r
+\r
+ /**\r
+ * Create an UnicodeCorrector that uses\r
+ * the named character encoding.\r
+ * @param enc Name of the encoding to be used\r
+ * @exception UnsupportedEncodingException\r
+ * If the named encoding is not supported\r
+ */\r
+ public static UnicodeCorrector getInstance(String enc)\r
+ throws UnsupportedEncodingException {\r
+ Class correctorClass = (Class)correctorMap.get(enc.toLowerCase());\r
+ if (correctorClass == null) {\r
+ throw new UnsupportedEncodingException(enc);\r
+ }\r
+ try {\r
+ return (UnicodeCorrector)correctorClass.newInstance();\r
+ } catch (Exception e) {\r
+ throw new UnsupportedEncodingException(\r
+ correctorClass + " cannot get newInstance.\n" + e);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Unicode文字配列の補正を行います。\r
+ * <p>\r
+ * 特定の文字を特定エンコーディングで出力しようとした際の\r
+ * sun.ioコンバータでは正常に変換できない部分を補正します。\r
+ * </p>\r
+ * @param cbuf Buffer of characters\r
+ * @param off Offset from which to start writing characters\r
+ * @param len Number of characters to write\r
+ * @return Result that corrected.\r
+ * Note:Return array is different from <code>cbuf</code>\r
+ * in case of different result size.\r
+ */\r
+ public char[] correct(char cbuf[], int off, int len) {\r
+ StringBuffer buf = new StringBuffer();\r
+ for (int i = off; i < len; i++) {\r
+ buf.append(correct(cbuf[i]));\r
+ }\r
+ return new String(buf).toCharArray();\r
+ }\r
+\r
+ public String correct(String s) {\r
+ StringBuffer buf = new StringBuffer();\r
+ for (int i = 0; i < s.length(); i++) {\r
+ buf.append(correct(s.charAt(i)));\r
+ }\r
+ return new String(buf);\r
+ }\r
+\r
+ // 性能的にこれをabstractにするのはためらったが・・・\r
+ public abstract char correct(char c);\r
+\r
+ /**\r
+ * 新しいUnicodeCorrectorを追加します。\r
+ * <p>\r
+ * このソースコードを変えずに、動的に新たな出力エンコーディングに\r
+ * 対応したUnicodeCorrectorを登録したい場合に用います。\r
+ * </p>\r
+ * @param enc 対応するエンコーディング名\r
+ * @param correctorClass UnicodeCorrectorサブクラスのClassオブジェクト\r
+ */\r
+ public static void addCorrector(String enc, Class correctorClass) {\r
+ if (!correctorClass.isInstance(UnicodeCorrector.class)) {\r
+ throw new IllegalArgumentException(\r
+ "Corrector is not UnicodeCorrector type.");\r
+ }\r
+ correctorMap.put(enc, correctorClass);\r
+ }\r
+}
\ No newline at end of file
--- /dev/null
+<HTML>\r
+<BODY>\r
+¼õ¿®¥á¡¼¥ë¤Î²òÀϤ˻ÈÍѤ¹¤ë¥¯¥é¥¹¤òÄ󶡤·¤Þ¤¹¡£\r
+¤³¤Î¥Ñ¥Ã¥±¡¼¥¸ÇÛ²¼¤Ë¤¢¤ë¥¯¥é¥¹¤Ï¡¢<a href="http://www.sk-jp.com/java/library/">http://www.sk-jp.com/java/library/</a>¤ÇÇÛÉÛ¤µ¤ì¤Æ¤¤¤ë¥é¥¤¥Ö¥é¥ê¤ò°ìÉô²þÊѤ·¤Æ¤¤¤Þ¤¹¡£\r
+</BODY>\r
+</HTML>
\ No newline at end of file
--- /dev/null
+/*\r
+ * @(#) $Id: EntityRefEncoder.java,v 1.1.2.1 2005/01/18 07:20:43 otsuka Exp $\r
+ * $Revision: 1.1.2.1 $\r
+ * Copyright (c) 2000 Shin Kinoshita All Rights Reserved.\r
+ */\r
+\r
+package com.ozacc.mail.fetch.impl.sk_jp.text;\r
+\r
+import java.io.IOException;\r
+import java.io.Reader;\r
+import java.io.Writer;\r
+\r
+/**\r
+ * <>&"を&lt;&gt;&amp;&quot;に\r
+ * 変換するTranslatorです。\r
+ * リエントラントなので、通常はINSTANCE/CANONICAL_INSTANCEを用いればよいです。\r
+ * またStringオブジェクトに対してはencode()メソッドが使用できます。\r
+ * @version $Revision: 1.1.2.1 $ $Date: 2005/01/18 07:20:43 $\r
+ * @author Shin\r
+ */\r
+public class EntityRefEncoder implements Translator {\r
+ public static final EntityRefEncoder CANONICAL_INSTANCE =\r
+ new EntityRefEncoder(true);\r
+ public static final EntityRefEncoder INSTANCE =\r
+ new EntityRefEncoder(false);\r
+\r
+ public EntityRefEncoder() {\r
+ this(false);\r
+ }\r
+ public EntityRefEncoder(boolean canonical) {\r
+ setCanonical(canonical);\r
+ }\r
+\r
+ private boolean canonicalStatus;\r
+ private void setCanonical(boolean canonical) {\r
+// public void setCanonical(boolean canonical) {\r
+ this.canonicalStatus = canonical;\r
+ }\r
+\r
+ /**\r
+ * 文字ストリームから入力した文字列を任意の変換を\r
+ * 行いながら出力ストリームに書き出します。\r
+ * <p>\r
+ * </p>\r
+ */\r
+ public void translate(Reader r, Writer w) throws IOException {\r
+ int c;\r
+ while ((c = r.read()) != -1) {\r
+ translate((char)c, w, canonicalStatus);\r
+ }\r
+ w.flush();\r
+ }\r
+\r
+ public String translate(String source) {\r
+ return encode(source);\r
+ }\r
+/*\r
+ public static String encode(String s) {\r
+ if (s == null) return "";\r
+\r
+ StringWriter w = new StringWriter();\r
+ try {\r
+ EntityRefEncoder.INSTANCE.translate(new StringReader(s), w);\r
+ } catch (IOException e) {\r
+ throw new RuntimeException();\r
+ }\r
+ return w.toString();\r
+ }\r
+*/\r
+\r
+ /**\r
+ * 文字列の実体参照化を行います。\r
+ * @param s 対象文字列\r
+ * @return 変換後文字列\r
+ */\r
+ // 似たようなコードを書きたくは無いが高速化の為…\r
+ public static String encode(String s) {\r
+ if (s == null) return "";\r
+\r
+ int len = s.length();\r
+ StringBuffer buf = new StringBuffer(len + 128);\r
+ char c;\r
+\r
+ for (int i = 0; i < len; i++) {\r
+ c = s.charAt(i);\r
+ switch (c) {\r
+ case '<': buf.append("<"); break;\r
+ case '>': buf.append(">"); break;\r
+ case '&': buf.append("&"); break;\r
+ case '"': buf.append("""); break;\r
+ default:\r
+ buf.append(c);\r
+ }\r
+ }\r
+ return new String(buf);\r
+ }\r
+\r
+ /**\r
+ * 特定の文字を実体参照に変換して書き出します。\r
+ */\r
+ public static void translate(char c, Writer w, boolean canonical)\r
+ throws IOException {\r
+ switch (c) {\r
+ case '<': w.write("<"); break;\r
+ case '>': w.write(">"); break;\r
+ case '&': w.write("&"); break;\r
+ case '"': w.write("""); break;\r
+ case '\r':\r
+ case '\n':\r
+ if (canonical) {\r
+ w.write("&#");\r
+ w.write(Integer.toString(c));\r
+ w.write(';');\r
+ } else {\r
+ w.write(c);\r
+ }\r
+ break;\r
+ default:\r
+ w.write(c);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * 実体参照変換されている文字列をもとに戻します。\r
+ * @param s 対象文字列\r
+ * @return 変換後文字列\r
+ */\r
+ public static String decode(String s) {\r
+ if (s == null) return "";\r
+\r
+ int len = s.length();\r
+ StringBuffer buf = new StringBuffer(len);\r
+ char c;\r
+\r
+ for (int i = 0; i < len; i++) {\r
+ c = s.charAt(i);\r
+ if (c != '&' || i > len - 4) {\r
+ buf.append(c);\r
+ continue;\r
+ }\r
+ if ((s.charAt(i + 2) == 't' || s.charAt(i + 2) == 'T') &&\r
+ s.charAt(i + 3) == ';') {\r
+ switch (s.charAt(i + 1)) {\r
+ case 'l':\r
+ case 'L':\r
+ buf.append('<');\r
+ i += 3;\r
+ continue;\r
+ case 'g':\r
+ case 'G':\r
+ buf.append('>');\r
+ i += 3;\r
+ continue;\r
+ }\r
+ } else if (i < len - 4 &&\r
+ (s.charAt(i + 1) == 'a' || s.charAt(i + 1) == 'A') &&\r
+ (s.charAt(i + 2) == 'm' || s.charAt(i + 2) == 'M') &&\r
+ (s.charAt(i + 3) == 'p' || s.charAt(i + 3) == 'P') &&\r
+ s.charAt(i + 4) == ';') {\r
+ buf.append('&');\r
+ i += 4;\r
+ continue;\r
+ } else if (i < len - 5 &&\r
+ (s.charAt(i + 1) == 'q' || s.charAt(i + 1) == 'Q') &&\r
+ (s.charAt(i + 2) == 'u' || s.charAt(i + 2) == 'U') &&\r
+ (s.charAt(i + 3) == 'o' || s.charAt(i + 3) == 'O') &&\r
+ (s.charAt(i + 4) == 't' || s.charAt(i + 4) == 'T') &&\r
+ s.charAt(i + 5) == ';') {\r
+ buf.append('"');\r
+ i += 5;\r
+ continue;\r
+ }\r
+ buf.append(c);\r
+ }\r
+ return new String(buf);\r
+ }\r
+}\r
--- /dev/null
+/*\r
+ * @(#) $Id: Translator.java,v 1.1.2.1 2005/01/18 07:20:43 otsuka Exp $\r
+ * $Revision: 1.1.2.1 $\r
+ * Copyright (c) 2000 Shin Kinoshita All Rights Reserved.\r
+ */\r
+\r
+package com.ozacc.mail.fetch.impl.sk_jp.text;\r
+\r
+import java.io.IOException;\r
+import java.io.Reader;\r
+import java.io.Writer;\r
+\r
+/**\r
+ * Character Translatorのインターフェイスです。\r
+ * @version $Revision: 1.1.2.1 $ $Date: 2005/01/18 07:20:43 $\r
+ * @author Shin\r
+ */\r
+public interface Translator {\r
+ /**\r
+ * 文字ストリームから入力した文字列を任意の変換を\r
+ * 行いながら出力ストリームに書き出します。\r
+ * <p>\r
+ * フィルタとして用いる場合はPipedInput/OutputStreamと併用しましょう。<BR>\r
+ * このメソッドの実装者はリエントラントに設計すべきでしょう。\r
+ * </p>\r
+ */\r
+ void translate(Reader r, Writer w) throws IOException;\r
+ /**\r
+ * 入力文字列に任意の変換を施した文字列を返します。\r
+ */\r
+ String translate(String src);\r
+}\r
--- /dev/null
+/*\r
+ * @(#) $Id: StringValues.java,v 1.1.2.1 2005/01/18 07:20:49 otsuka Exp $\r
+ * $Revision: 1.1.2.1 $\r
+ */\r
+package com.ozacc.mail.fetch.impl.sk_jp.util;\r
+\r
+import java.util.*;\r
+\r
+/**\r
+ * StringValues.\r
+ * <p>\r
+ * 複数の文字列をカンマやタブ区切りなどの一つの文字列として管理可能とします。\r
+ * <br>内部的にはListに入れますのでindexによるアクセスも可能ですが、\r
+ * 構築時、及びgetString()時に区切り文字で一本にされた文字列として\r
+ * やりとりすることもできます。\r
+ * エスケープ処理がされないので、デリミタを含むトークンを認識できません。\r
+ * </p>\r
+ * @version $Revision: 1.1.2.1 $ $Date: 2005/01/18 07:20:49 $\r
+ * @author Shin\r
+ */\r
+public class StringValues {\r
+ private List src_ = new ArrayList();\r
+ /**\r
+ * 複数文字列管理オブジェクトを生成します。\r
+ */\r
+ public StringValues() {}\r
+ /**\r
+ * 複数文字列管理オブジェクトを生成します。\r
+ * @param str 区切り文字で連結された文字列\r
+ */\r
+ public StringValues(String str) {\r
+ parse(str);\r
+ }\r
+ /**\r
+ * 複数文字列管理オブジェクトを生成します。\r
+ * @param str 区切り文字で連結された文字列\r
+ * @param delim デリミタ\r
+ */\r
+ public StringValues(String str, String delim) {\r
+ parse(str, delim);\r
+ }\r
+ /**\r
+ * 複数文字列管理オブジェクトを生成します。\r
+ * @param strings 文字列の配列\r
+ */\r
+ public StringValues(String[] strings) {\r
+ add(strings);\r
+ }\r
+ /**\r
+ * 複数文字列管理オブジェクトを生成します。\r
+ * @param strings 文字列の配列\r
+ */\r
+ public StringValues(Object[] o) {\r
+ for (int i = 0; i < o.length; i++) {\r
+ add(o[i].toString());\r
+ }\r
+ }\r
+//////////////////////////////////////////////////////////////////////////////\r
+ /**\r
+ * デリミタで区切られた文字列を分割して追加します。\r
+ * <p>\r
+ * デリミタはStringTokenizerの標準のデリミタ\r
+ * <code>"\t\n\r\f"</code>が使われます。\r
+ * </p>\r
+ * @param str 区切り文字で連結された文字列\r
+ * @param delim デリミタ\r
+ */\r
+ public void parse(String str) {\r
+ StringTokenizer st = new StringTokenizer(str);\r
+ parse(st);\r
+ }\r
+ /**\r
+ * デリミタで区切られた文字列を分割して追加します。\r
+ * <p>\r
+ * </p>\r
+ * @param str 区切り文字で連結された文字列\r
+ * @param delim デリミタ\r
+ */\r
+ public void parse(String str, String delim) {\r
+ StringTokenizer st = new StringTokenizer(str, delim);\r
+ parse(st);\r
+ }\r
+ private void parse(StringTokenizer st) {\r
+ while (st.hasMoreTokens()) {\r
+ add(st.nextToken());\r
+ }\r
+ }\r
+ /**\r
+ * 文字列群へのイテレータを返します。\r
+ * @return Iteratorオブジェクト\r
+ */\r
+ public Iterator iterator() {\r
+ return src_.iterator();\r
+ }\r
+\r
+ /**\r
+ * カンマ区切り文字列を得ます。\r
+ * <p>\r
+ * 区切り文字列は文字列同士の間に単純に挿入されます。\r
+ * </p>\r
+ * @return 文字列化したオブジェクト\r
+ */\r
+ public String getString() {\r
+ return getString(", ");\r
+ }\r
+ /**\r
+ * 指定した区切り文字で連結された文字列を得ます。\r
+ * <p>\r
+ * 区切り文字列は文字列同士の間に単純に挿入されます。\r
+ * </p>\r
+ * @param delim 区切り文字列\r
+ * @return 文字列化したオブジェクト\r
+ */\r
+ public String getString(String delim) {\r
+ StringBuffer buf = new StringBuffer();\r
+ Iterator iterator = iterator();\r
+ if (iterator.hasNext()) {\r
+ buf.append(iterator.next());\r
+ }\r
+ while (iterator.hasNext()) {\r
+ buf.append(delim).append(iterator.next());\r
+ }\r
+ return new String(buf);\r
+ }\r
+ public String toString() {\r
+ return getString();\r
+ }\r
+ /**\r
+ * 指定indexの文字列を取得します。\r
+ * @param index 文字列群中の位置\r
+ */\r
+ public String get(int index) {\r
+ return (String)src_.get(index);\r
+ }\r
+ /**\r
+ * 文字列を追加します。\r
+ * @param str 追加する文字列\r
+ */\r
+ public void add(String str) {\r
+ src_.add(str);\r
+ }\r
+ /**\r
+ * 文字列群を追加します。\r
+ * @param str 追加する文字列\r
+ */\r
+ public void add(String[] str) {\r
+ for (int i = 0; i < str.length; i++) {\r
+ src_.add(str[i]);\r
+ }\r
+ }\r
+ /**\r
+ * 管理している文字列を削除します。\r
+ */\r
+ public void clear() {\r
+ src_.clear();\r
+ }\r
+}\r
--- /dev/null
+<HTML>\r
+<BODY>\r
+¥á¡¼¥ë¤Î¼õ¿®¥¤¥ó¥¿¡¼¥Õ¥§¡¼¥¹¤òÄ󶡤·¤Þ¤¹¡£\r
+</BODY>\r
+</HTML>
\ No newline at end of file
--- /dev/null
+package com.ozacc.mail.impl;\r
+\r
+import java.io.File;\r
+import java.io.IOException;\r
+import java.io.InputStream;\r
+import java.util.HashMap;\r
+import java.util.Map;\r
+\r
+import javax.xml.parsers.DocumentBuilder;\r
+import javax.xml.parsers.DocumentBuilderFactory;\r
+import javax.xml.parsers.FactoryConfigurationError;\r
+import javax.xml.parsers.ParserConfigurationException;\r
+\r
+import org.w3c.dom.Document;\r
+import org.w3c.dom.Element;\r
+import org.w3c.dom.Node;\r
+import org.w3c.dom.NodeList;\r
+import org.xml.sax.SAXException;\r
+\r
+import com.ozacc.mail.Mail;\r
+\r
+/**\r
+ * XMLMailBuilder実装が継承する基底クラス。\r
+ * \r
+ * @since 1.1\r
+ * \r
+ * @author Tomohiro Otsuka\r
+ * @version $Id: AbstractXMLMailBuilder.java,v 1.4.2.2 2004/11/25 08:00:49 otsuka Exp $\r
+ */\r
+public abstract class AbstractXMLMailBuilder {\r
+\r
+ protected Map documentBuilderCache;\r
+\r
+ /**\r
+ * コンストラクタ。\r
+ */\r
+ public AbstractXMLMailBuilder() {\r
+ documentBuilderCache = new HashMap();\r
+ }\r
+\r
+ /**\r
+ * 指定されたXMLファイルを読み込み、DOM Documentを生成します。\r
+ * ignoreCommentが指定されている場合は、XMLのコメントを削除しません。\r
+ * \r
+ * @param file XMLファイル\r
+ * @return DOM Document\r
+ * @throws IOException\r
+ * @throws SAXException\r
+ */\r
+ protected synchronized Document getDocumentFromFile(File file, boolean ignoreComment)\r
+ throws SAXException,\r
+ IOException {\r
+ DocumentBuilder db = createDocumentBuilder(ignoreComment);\r
+ return db.parse(file);\r
+ }\r
+\r
+ /**\r
+ * 指定されたXMLファイルを読み込み、DOM Documentを生成します。\r
+ * XMLのコメントや改行は削除されます。\r
+ * \r
+ * @param file XMLファイル\r
+ * @return DOM Document\r
+ * @throws IOException\r
+ * @throws SAXException\r
+ */\r
+ protected Document getDocumentFromFile(File file) throws SAXException, IOException {\r
+ return getDocumentFromFile(file, true);\r
+ }\r
+\r
+ /**\r
+ * DocumentBuilderインスタンスを生成します。\r
+ * ignoreCommentが指定されている場合は、コメントを削除しないように設定されたDocumentBuilderを生成します。\r
+ * \r
+ * @param ignoreComment\r
+ * @return DocumentBuilder\r
+ * @throws FactoryConfigurationError \r
+ */\r
+ protected DocumentBuilder createDocumentBuilder(boolean ignoreComment)\r
+ throws FactoryConfigurationError {\r
+ Boolean dbKey = Boolean.valueOf(ignoreComment);\r
+ DocumentBuilder db = (DocumentBuilder)documentBuilderCache.get(dbKey);\r
+ if (db == null) {\r
+ DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();\r
+ dbf.setIgnoringComments(ignoreComment);\r
+ dbf.setCoalescing(ignoreComment);\r
+ dbf.setIgnoringElementContentWhitespace(true);\r
+ dbf.setValidating(true);\r
+ try {\r
+ db = dbf.newDocumentBuilder();\r
+ db.setEntityResolver(new DTDEntityResolver());\r
+ documentBuilderCache.put(dbKey, db);\r
+ } catch (ParserConfigurationException e) {\r
+ // never be thrown\r
+ throw new RuntimeException(e);\r
+ }\r
+ }\r
+ return db;\r
+ }\r
+\r
+ /**\r
+ * DocumentBuilderインスタンスを生成します。\r
+ * このDocumentBuilderを使用して生成されるDOM Documentでは、元のXMLデータにあるコメントは削除されます。\r
+ * \r
+ * @return DocumentBuilder\r
+ * @throws FactoryConfigurationError \r
+ */\r
+ protected DocumentBuilder createDocumentBuilder() throws FactoryConfigurationError {\r
+ return createDocumentBuilder(true);\r
+ }\r
+\r
+ /**\r
+ * 指定されたクラスパスのXMLファイルを読み込み、DOM Documentを生成します。\r
+ * ignoreCommentが指定されている場合は、XMLのコメントを削除しません。\r
+ * \r
+ * @param ignoreComment\r
+ * @param classPath\r
+ * @return DOM Document\r
+ * @throws IOException\r
+ * @throws SAXException\r
+ */\r
+ protected synchronized Document getDocumentFromClassPath(String classPath, boolean ignoreComment)\r
+ throws SAXException,\r
+ IOException {\r
+ InputStream is = getClass().getResourceAsStream(classPath);\r
+ DocumentBuilder db = createDocumentBuilder(ignoreComment);\r
+ try {\r
+ return db.parse(is);\r
+ } finally {\r
+ if (is != null) {\r
+ is.close();\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * 指定されたクラスパスのXMLファイルを読み込み、DOM Documentを生成します。\r
+ * XMLのコメントや改行は削除されます。\r
+ * \r
+ * @param classPath\r
+ * @return DOM Document\r
+ * @throws IOException\r
+ * @throws SAXException\r
+ */\r
+ protected Document getDocumentFromClassPath(String classPath) throws SAXException, IOException {\r
+ return getDocumentFromClassPath(classPath, true);\r
+ }\r
+\r
+ /**\r
+ * @param root\r
+ * @param mail \r
+ */\r
+ protected void setReplyTo(Element root, Mail mail) {\r
+ NodeList nodes = root.getElementsByTagName("replyTo");\r
+ Element replyTo = (Element)nodes.item(0);\r
+ if (replyTo != null && replyTo.getAttribute("email").length() > 0) {\r
+ mail.setReplyTo(replyTo.getAttribute("email"));\r
+ }\r
+ }\r
+\r
+ /**\r
+ * @param root\r
+ * @param mail \r
+ */\r
+ protected void setText(Element root, Mail mail) {\r
+ NodeList nodes = root.getElementsByTagName("body");\r
+ Element bodyElem = (Element)nodes.item(0);\r
+ if (bodyElem == null) {\r
+ return;\r
+ }\r
+ String body = bodyElem.getFirstChild().getNodeValue();\r
+ mail.setText(body.trim());\r
+ }\r
+\r
+ /**\r
+ * HTML本文をセット。\r
+ * \r
+ * @param root\r
+ * @param mail\r
+ */\r
+ protected void setHtml(Element root, Mail mail) {\r
+ NodeList nodes = root.getElementsByTagName("html");\r
+ Element htmlElem = (Element)nodes.item(0);\r
+ if (htmlElem == null) {\r
+ return;\r
+ }\r
+ String html = htmlElem.getFirstChild().getNodeValue();\r
+ mail.setHtmlText(html.trim());\r
+ }\r
+\r
+ /**\r
+ * @param root\r
+ * @param mail \r
+ */\r
+ protected void setSubject(Element root, Mail mail) {\r
+ NodeList nodes = root.getElementsByTagName("subject");\r
+ Element subjectElem = (Element)nodes.item(0);\r
+ if (subjectElem == null) {\r
+ return;\r
+ }\r
+ String subject = subjectElem.getFirstChild().getNodeValue();\r
+ mail.setSubject(subject.trim());\r
+ }\r
+\r
+ /**\r
+ * @param root\r
+ * @param mail \r
+ */\r
+ protected void setRecipients(Element root, Mail mail) {\r
+ NodeList nodes = root.getElementsByTagName("recipients");\r
+ Element recipientsElem = (Element)nodes.item(0);\r
+ if (recipientsElem == null) {\r
+ return;\r
+ }\r
+\r
+ NodeList recipientElemList = recipientsElem.getChildNodes();\r
+ for (int i = 0, max = recipientElemList.getLength(); i < max; i++) {\r
+ Node node = recipientElemList.item(i);\r
+ if (node.getNodeType() != Node.ELEMENT_NODE) {\r
+ continue;\r
+ }\r
+ Element e = (Element)node;\r
+ if ("to".equals(e.getNodeName())) { // to\r
+ if (e.getAttribute("email").length() > 0) {\r
+ if (e.getAttribute("name").length() > 0) {\r
+ mail.addTo(e.getAttribute("email"), e.getAttribute("name"));\r
+ } else {\r
+ mail.addTo(e.getAttribute("email"));\r
+ }\r
+ }\r
+ } else if ("cc".equals(e.getNodeName())) { // cc\r
+ if (e.getAttribute("email").length() > 0) {\r
+ if (e.getAttribute("name").length() > 0) {\r
+ mail.addCc(e.getAttribute("email"), e.getAttribute("name"));\r
+ } else {\r
+ mail.addCc(e.getAttribute("email"));\r
+ }\r
+ }\r
+ } else {\r
+ if (e.getAttribute("email").length() > 0) { // bcc\r
+ mail.addBcc(e.getAttribute("email"));\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * @param root\r
+ * @param mail \r
+ */\r
+ protected void setReturnPath(Element root, Mail mail) {\r
+ NodeList nodes = root.getElementsByTagName("returnPath");\r
+ Element returnPath = (Element)nodes.item(0);\r
+ if (returnPath != null && returnPath.getAttribute("email").length() > 0) {\r
+ mail.setReturnPath(returnPath.getAttribute("email"));\r
+ }\r
+ }\r
+\r
+ /**\r
+ * @param root\r
+ * @param mail \r
+ */\r
+ protected void setFrom(Element root, Mail mail) {\r
+ NodeList nodes = root.getElementsByTagName("from");\r
+ Element from = (Element)nodes.item(0);\r
+ if (from != null && from.getAttribute("email").length() > 0) {\r
+ if (from.getAttribute("name").length() > 0) {\r
+ mail.setFrom(from.getAttribute("email"), from.getAttribute("name"));\r
+ } else {\r
+ mail.setFrom(from.getAttribute("email"));\r
+ }\r
+ }\r
+ }\r
+\r
+}
\ No newline at end of file
--- /dev/null
+package com.ozacc.mail.impl;\r
+\r
+/*\r
+ * @(#)ByteArrayDataSource.java 1.4 01/05/23\r
+ *\r
+ * Copyright 1998-2000 Sun Microsystems, Inc. All Rights Reserved.\r
+ *\r
+ * Redistribution and use in source and binary forms, with or without\r
+ * modification, are permitted provided that the following conditions\r
+ * are met:\r
+ * \r
+ * - Redistributions of source code must retain the above copyright\r
+ * notice, this list of conditions and the following disclaimer.\r
+ * \r
+ * - Redistribution in binary form must reproduce the above copyright\r
+ * notice, this list of conditions and the following disclaimer in the\r
+ * documentation and/or other materials provided with the distribution.\r
+ * \r
+ * Neither the name of Sun Microsystems, Inc. or the names of contributors\r
+ * may be used to endorse or promote products derived from this software\r
+ * without specific prior written permission.\r
+ * \r
+ * This software is provided "AS IS," without a warranty of any kind. ALL\r
+ * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,\r
+ * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A\r
+ * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND\r
+ * ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES OR LIABILITIES\r
+ * SUFFERED BY LICENSEE AS A RESULT OF OR RELATING TO USE, MODIFICATION\r
+ * OR DISTRIBUTION OF THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL\r
+ * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR\r
+ * FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE\r
+ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,\r
+ * ARISING OUT OF THE USE OF OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS\r
+ * BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.\r
+ * \r
+ * You acknowledge that Software is not designed, licensed or intended\r
+ * for use in the design, construction, operation or maintenance of any\r
+ * nuclear facility.\r
+ */\r
+\r
+import java.io.ByteArrayInputStream;\r
+import java.io.ByteArrayOutputStream;\r
+import java.io.IOException;\r
+import java.io.InputStream;\r
+import java.io.OutputStream;\r
+import java.io.UnsupportedEncodingException;\r
+\r
+import javax.activation.DataSource;\r
+\r
+/**\r
+ * InputStream、byte array、StringからDataSourceインスタンスを生成するクラス。<br>\r
+ * JavaMailパッケージ付属品。\r
+ * <p>\r
+ * <strong>注:</strong> このクラスはpublicですが、ozacc-mail library外からは使用しないでください。\r
+ * \r
+ * @since 1.1\r
+ * \r
+ * @author John Mani\r
+ * @author Bill Shannon\r
+ * @author Max Spivak\r
+ */\r
+public class ByteArrayDataSource implements DataSource {\r
+\r
+ private byte[] data; // data\r
+\r
+ private String type; // content-type\r
+\r
+ /* Create a DataSource from an input stream */\r
+ public ByteArrayDataSource(InputStream is, String type) {\r
+ this.type = type;\r
+ try {\r
+ ByteArrayOutputStream os = new ByteArrayOutputStream();\r
+ int ch;\r
+\r
+ while ((ch = is.read()) != -1)\r
+ // XXX - must be made more efficient by\r
+ // doing buffered reads, rather than one byte reads\r
+ os.write(ch);\r
+ data = os.toByteArray();\r
+\r
+ } catch (IOException ioex) {}\r
+ }\r
+\r
+ /* Create a DataSource from a byte array */\r
+ public ByteArrayDataSource(byte[] data, String type) {\r
+ this.data = data;\r
+ this.type = type;\r
+ }\r
+\r
+ /* Create a DataSource from a String */\r
+ public ByteArrayDataSource(String data, String type) {\r
+ try {\r
+ // Assumption that the string contains only ASCII\r
+ // characters! Otherwise just pass a charset into this\r
+ // constructor and use it in getBytes()\r
+ this.data = data.getBytes("iso-8859-1");\r
+ } catch (UnsupportedEncodingException uex) {}\r
+ this.type = type;\r
+ }\r
+\r
+ /**\r
+ * Return an InputStream for the data.\r
+ * Note - a new stream must be returned each time.\r
+ */\r
+ public InputStream getInputStream() throws IOException {\r
+ if (data == null)\r
+ throw new IOException("no data");\r
+ return new ByteArrayInputStream(data);\r
+ }\r
+\r
+ public OutputStream getOutputStream() throws IOException {\r
+ throw new IOException("cannot do this");\r
+ }\r
+\r
+ public String getContentType() {\r
+ return type;\r
+ }\r
+\r
+ public String getName() {\r
+ return "dummy";\r
+ }\r
+}
\ No newline at end of file
--- /dev/null
+package com.ozacc.mail.impl;\r
+\r
+import java.util.Locale;\r
+\r
+/**\r
+ * The Cp932 class contains a utility method for converting Microsoft's\r
+ * Cp 932 into JIS.\r
+ * <p>\r
+ * <strong>注:</strong> このクラスはpublicですが、ozacc-mail library外からは使用しないでください。\r
+ *\r
+ * @author Kazuhiro Kazama\r
+ * @version 1.0 01/06/97\r
+ * \r
+ * @since 1.0.2\r
+ */\r
+public class Cp932 {\r
+\r
+ private static boolean isCp932 = false;\r
+\r
+ static {\r
+ String p = System.getProperty("iscp932");\r
+ String os = System.getProperty("os.name");\r
+ if (Locale.getDefault().getLanguage().equals("ja")) {\r
+ if (p != null && Boolean.getBoolean(p))\r
+ isCp932 = true;\r
+ else if (os != null && (os.startsWith("Win")))\r
+ isCp932 = true;\r
+ }\r
+ }\r
+\r
+ /*\r
+ * You can't use this constructor.\r
+ */\r
+ private Cp932() {}\r
+\r
+ /*\r
+ * This method converts Cp932 to JIS.\r
+ */\r
+ public static String toJIS(String s) {\r
+\r
+ /* commented by otsuka\r
+ * \r
+ * if (!isCp932)\r
+ * return s;\r
+ */\r
+\r
+ StringBuffer sb = new StringBuffer();\r
+ char c;\r
+ for (int i = 0; i < s.length(); i++) {\r
+ c = s.charAt(i);\r
+\r
+ switch (c) {\r
+ case 0xff3c: // FULLWIDTH REVERSE SOLIDUS ->\r
+ c = 0x005c; // REVERSE SOLIDUS\r
+ break;\r
+ case 0xff5e: // FULLWIDTH TILDE ->\r
+ c = 0x301c; // WAVE DASH\r
+ break;\r
+ case 0x2225: // PARALLEL TO ->\r
+ c = 0x2016; // DOUBLE VERTICAL LINE\r
+ break;\r
+ case 0xff0d: // FULLWIDTH HYPHEN-MINUS ->\r
+ c = 0x2212; // MINUS SIGN\r
+ break;\r
+ case 0xffe0: // FULLWIDTH CENT SIGN ->\r
+ c = 0x00a2; // CENT SIGN\r
+ break;\r
+ case 0xffe1: // FULLWIDTH POUND SIGN ->\r
+ c = 0x00a3; // POUND SIGN\r
+ break;\r
+ case 0xffe2: // FULLWIDTH NOT SIGN ->\r
+ c = 0x00ac; // NOT SIGN\r
+ break;\r
+ }\r
+ sb.append(c);\r
+ }\r
+ return new String(sb);\r
+ }\r
+\r
+ /*\r
+ * This method convert JIS to Cp932.\r
+ */\r
+ public static String toCp932(String s) {\r
+ if (!isCp932)\r
+ return s;\r
+ StringBuffer sb = new StringBuffer();\r
+ char c;\r
+ for (int i = 0; i < s.length(); i++) {\r
+ c = s.charAt(i);\r
+\r
+ switch (c) {\r
+ case 0x005c: // REVERSE SOLIDUS ->\r
+ c = 0xff3c; // FULLWIDTH REVERSE SOLIDUS\r
+ break;\r
+ case 0x301c: // WAVE DASH ->\r
+ c = 0xff5e; // FULLWIDTH TILDE\r
+ break;\r
+ case 0x2016: // DOUBLE VERTICAL LINE ->\r
+ c = 0x2225; // PARALLEL TO\r
+ break;\r
+ case 0x2212: // MINUS SIGN ->\r
+ c = 0xff0d; // FULLWIDTH HYPHEN-MINUS\r
+ break;\r
+ case 0x00a2: // CENT SIGN ->\r
+ c = 0xffe0; // FULLWIDTH CENT SIGN\r
+ break;\r
+ case 0x00a3: // POUND SIGN ->\r
+ c = 0xffe1; // FULLWIDTH POUND SIGN\r
+ break;\r
+ case 0x00ac: // NOT SIGN ->\r
+ c = 0xffe2; // FULLWIDTH NOT SIGN\r
+ break;\r
+ }\r
+ sb.append(c);\r
+ }\r
+ return new String(sb);\r
+ }\r
+}
\ No newline at end of file
--- /dev/null
+package com.ozacc.mail.impl;\r
+\r
+import java.io.IOException;\r
+import java.io.InputStream;\r
+\r
+import org.apache.commons.logging.Log;\r
+import org.apache.commons.logging.LogFactory;\r
+import org.xml.sax.EntityResolver;\r
+import org.xml.sax.InputSource;\r
+import org.xml.sax.SAXException;\r
+\r
+/**\r
+ * ozacc-mail libraryのDTDファイルをクラスパス上から検出するEntityResolver実装。\r
+ * \r
+ * @since 1.1\r
+ * \r
+ * @author Tomohiro Otsuka\r
+ * @version $Id: DTDEntityResolver.java,v 1.1.2.2 2004/11/25 08:01:07 otsuka Exp $\r
+ */\r
+public class DTDEntityResolver implements EntityResolver {\r
+\r
+ private static Log log = LogFactory.getLog(DTDEntityResolver.class);\r
+\r
+ private static final String URL = "http://www.ozacc.com/library/dtd/";\r
+\r
+ /**\r
+ * クラスパス「com/ozacc/mail」上で、指定されたsystemIdのファイル名と同じファイルを検出します。\r
+ * もしも検出できなければnullを返します。(必ず検出できるはずです。)\r
+ * \r
+ * @see org.xml.sax.EntityResolver#resolveEntity(java.lang.String, java.lang.String)\r
+ */\r
+ public InputSource resolveEntity(String publicId, String systemId) throws SAXException,\r
+ IOException {\r
+ if (systemId != null && systemId.startsWith(URL)) {\r
+ log.debug("クラスパス[com/ozacc/mail/]上で'" + systemId + "'の取得を試みます。");\r
+\r
+ // Search for DTD\r
+ ClassLoader classLoader = this.getClass().getClassLoader();\r
+ InputStream dtdStream = classLoader.getResourceAsStream("com/ozacc/mail/"\r
+ + systemId.substring(URL.length()));\r
+\r
+ if (dtdStream == null) {\r
+ log.debug("'" + systemId + "'はクラスパス上に見つかりませんでした。");\r
+ return null;\r
+ } else {\r
+ log.debug("'" + systemId + "'をクラスパス上で取得しました。");\r
+ InputSource source = new InputSource(dtdStream);\r
+ source.setPublicId(publicId);\r
+ source.setSystemId(systemId);\r
+ return source;\r
+ }\r
+ } else {\r
+ // use the default behaviour\r
+ return null;\r
+ }\r
+ }\r
+\r
+}
\ No newline at end of file
--- /dev/null
+package com.ozacc.mail.impl;\r
+\r
+import java.io.File;\r
+import java.io.IOException;\r
+import java.io.InputStream;\r
+import java.io.StringReader;\r
+import java.io.StringWriter;\r
+import java.util.HashMap;\r
+import java.util.Iterator;\r
+import java.util.List;\r
+import java.util.Map;\r
+\r
+import org.apache.commons.logging.Log;\r
+import org.apache.commons.logging.LogFactory;\r
+import org.apache.velocity.VelocityContext;\r
+import org.apache.velocity.app.Velocity;\r
+import org.apache.velocity.exception.MethodInvocationException;\r
+import org.apache.velocity.exception.ParseErrorException;\r
+import org.apache.velocity.exception.ResourceNotFoundException;\r
+import org.apache.velocity.runtime.log.LogSystem;\r
+import org.jdom.Document;\r
+import org.jdom.Element;\r
+import org.jdom.JDOMException;\r
+import org.jdom.input.SAXBuilder;\r
+import org.jdom.output.XMLOutputter;\r
+\r
+import com.ozacc.mail.Mail;\r
+import com.ozacc.mail.MailBuildException;\r
+import com.ozacc.mail.MultipleMailBuilder;\r
+import com.ozacc.mail.VelocityMultipleMailBuilder;\r
+\r
+/**\r
+ * <a href="http://www.jdom.org/">JDOM</a>を利用してXMLファイルからMailインスタンスを生成するクラス。\r
+ * <p>\r
+ * ソースXMLを読み込む際に、DTDバリデーションが実行されますので妥当なXMLデータ(Valid XML Document)でなければいけません。\r
+ * \r
+ * @since 1.0\r
+ * \r
+ * @author Tomohiro Otsuka\r
+ * @version $Id: JDomXMLMailBuilder.java,v 1.10.2.5 2005/02/01 20:37:49 otsuka Exp $\r
+ */\r
+public class JDomXMLMailBuilder implements MultipleMailBuilder, VelocityMultipleMailBuilder {\r
+\r
+ private static Log log = LogFactory.getLog(JDomXMLMailBuilder.class);\r
+\r
+ private static String CACHE_KEY_SEPARATOR = "#";\r
+\r
+ private static String DEFAULT_MAIL_ID = "DEFAULT";\r
+\r
+ protected LogSystem velocityLogSystem = new VelocityLogSystem();\r
+\r
+ private boolean cacheEnabled = false;\r
+\r
+ protected Map templateCache = new HashMap();\r
+\r
+ /**\r
+ * コンストラクタ。\r
+ */\r
+ public JDomXMLMailBuilder() {}\r
+\r
+ /**\r
+ * 指定されたクラスパス上のXMLファイルからMailインスタンスを生成します。\r
+ * \r
+ * @param classPath メール内容を記述したXMLファイルのパス\r
+ * @return 生成されたMailインスタンス\r
+ * @throws MailBuildException Mailインスタンスの生成に失敗した場合\r
+ */\r
+ public Mail buildMail(String classPath) throws MailBuildException {\r
+ Document doc = getDocumentFromClassPath(classPath);\r
+ return build(doc.getRootElement());\r
+ }\r
+\r
+ /**\r
+ * 指定されたクラスパス上のXMLファイルからMailインスタンスを生成します。\r
+ * 指定されたVelocityContextを使って、XMLファイルの内容を動的に生成できます。\r
+ * \r
+ * @param classPath メール内容を記述したXMLファイルのパス\r
+ * @param context VelocityContext\r
+ * @return 生成されたMailインスタンス\r
+ * @throws MailBuildException Mailインスタンスの生成に失敗した場合\r
+ */\r
+ public Mail buildMail(String classPath, VelocityContext context) throws MailBuildException {\r
+ String cacheKey = classPath + CACHE_KEY_SEPARATOR + DEFAULT_MAIL_ID;\r
+ String templateXmlText;\r
+ if (!hasTemplateCache(cacheKey)) {\r
+ Document doc = getDocumentFromClassPath(classPath);\r
+ templateXmlText = cacheTemplateText(doc, cacheKey);\r
+ } else {\r
+ templateXmlText = getTemplateCache(cacheKey);\r
+ }\r
+ try {\r
+ return build(templateXmlText, context);\r
+ } catch (Exception e) {\r
+ throw new MailBuildException("メールの生成に失敗しました。", e);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * 指定されたXMLファイルからMailインスタンスを生成します。\r
+ * \r
+ * @param file メール内容を記述したXMLファイル\r
+ * @return 生成されたMailインスタンス\r
+ * @throws MailBuildException Mailインスタンスの生成に失敗した場合\r
+ */\r
+ public Mail buildMail(File file) throws MailBuildException {\r
+ Document doc = getDocumentFromFile(file);\r
+ return build(doc.getRootElement());\r
+ }\r
+\r
+ /**\r
+ * 指定されたXMLファイルからMailインスタンスを生成します。\r
+ * 指定されたVelocityContextを使って、XMLファイルの内容を動的に生成できます。\r
+ * \r
+ * @param file メール内容を記述したXMLファイル\r
+ * @param context VelocityContext\r
+ * @return 生成されたMailインスタンス\r
+ * @throws MailBuildException Mailインスタンスの生成に失敗した場合\r
+ */\r
+ public Mail buildMail(File file, VelocityContext context) throws MailBuildException {\r
+ String cacheKey = file.getAbsolutePath() + CACHE_KEY_SEPARATOR + DEFAULT_MAIL_ID;\r
+ String templateXmlText;\r
+ if (!hasTemplateCache(cacheKey)) {\r
+ Document doc = getDocumentFromFile(file);\r
+ templateXmlText = cacheTemplateText(doc, cacheKey);\r
+ } else {\r
+ templateXmlText = getTemplateCache(cacheKey);\r
+ }\r
+ try {\r
+ return build(templateXmlText, context);\r
+ } catch (Exception e) {\r
+ throw new MailBuildException("メールの生成に失敗しました。", e);\r
+ }\r
+ }\r
+\r
+ private String cacheTemplateText(Document doc, String cacheKey) {\r
+ XMLOutputter output = new XMLOutputter();\r
+ String templateXmlText = "<!DOCTYPE mail PUBLIC \"" + Mail.DOCTYPE_PUBLIC + "\" \""\r
+ + Mail.DOCTYPE_SYSTEM + "\">\n" + output.outputString(doc.getRootElement());\r
+ log.debug("以下のXMLデータをキャッシュします。\n" + templateXmlText);\r
+ putTemplateCache(cacheKey, templateXmlText);\r
+ return templateXmlText;\r
+ }\r
+\r
+ /**\r
+ * 指定されたクラスパス上のファイルを読み込んで、XMLドキュメントを生成します。\r
+ * \r
+ * @param classPath\r
+ * @return JDOM Document\r
+ */\r
+ protected Document getDocumentFromClassPath(String classPath) throws MailBuildException {\r
+ InputStream is = getClass().getResourceAsStream(classPath);\r
+ SAXBuilder builder = new SAXBuilder(true);\r
+ builder.setEntityResolver(new DTDEntityResolver());\r
+ Document doc;\r
+ try {\r
+ doc = builder.build(is);\r
+ } catch (JDOMException e) {\r
+ throw new MailBuildException("XMLのパースに失敗しました。" + e.getMessage(), e);\r
+ } catch (IOException e) {\r
+ throw new MailBuildException("XMLファイルの読み込みに失敗しました。", e);\r
+ } finally {\r
+ if (is != null) {\r
+ try {\r
+ is.close();\r
+ } catch (IOException e) {\r
+ // ignore\r
+ }\r
+ }\r
+ }\r
+ return doc;\r
+ }\r
+\r
+ /**\r
+ * 指定されたファイルを読み込んで、XMLドキュメントを生成します。\r
+ * \r
+ * @param file\r
+ * @return JDOM Document\r
+ */\r
+ protected Document getDocumentFromFile(File file) {\r
+ SAXBuilder builder = new SAXBuilder(true);\r
+ builder.setEntityResolver(new DTDEntityResolver());\r
+ Document doc;\r
+ try {\r
+ doc = builder.build(file);\r
+ } catch (JDOMException e) {\r
+ throw new MailBuildException("XMLのパースに失敗しました。" + e.getMessage(), e);\r
+ } catch (IOException e) {\r
+ throw new MailBuildException("XMLファイルの読み込みに失敗しました。", e);\r
+ }\r
+ return doc;\r
+ }\r
+\r
+ /**\r
+ * XMLのmailルートエレメントからMailインスタンスを生成します。\r
+ * \r
+ * @param mailElement mail要素を示すElementインスタンス\r
+ * @return Mail 生成されたMail\r
+ */\r
+ protected Mail build(Element mailElement) {\r
+ Mail mail = new Mail();\r
+ setFrom(mailElement, mail);\r
+ setRecipients(mailElement, mail);\r
+ setSubject(mailElement, mail);\r
+ setBody(mailElement, mail);\r
+ setReplyTo(mailElement, mail);\r
+ setReturnPath(mailElement, mail);\r
+\r
+ setHtml(mailElement, mail);\r
+\r
+ return mail;\r
+ }\r
+\r
+ /**\r
+ * VelocityContextとXMLテンプレートをマージさせ、Mailインスタンスを生成します。\r
+ * \r
+ * @param templateText マージするXMLテンプレートの文字列\r
+ * @param context マージするVelocityContext\r
+ * @return Mail\r
+ * \r
+ * @throws Exception\r
+ * @throws ParseErrorException\r
+ * @throws MethodInvocationException\r
+ * @throws ResourceNotFoundException\r
+ * @throws IOException\r
+ * @throws JDOMException \r
+ */\r
+ protected Mail build(String templateText, VelocityContext context) throws Exception,\r
+ ParseErrorException,\r
+ MethodInvocationException,\r
+ ResourceNotFoundException,\r
+ IOException, JDOMException {\r
+ if (log.isDebugEnabled()) {\r
+ log.debug("ソースXMLデータ\n" + templateText);\r
+ }\r
+\r
+ Velocity.setProperty(Velocity.RUNTIME_LOG_LOGSYSTEM, velocityLogSystem);\r
+ Velocity.init();\r
+ StringWriter w = new StringWriter();\r
+ Velocity.evaluate(context, w, "XML Mail Data", templateText);\r
+\r
+ if (log.isDebugEnabled()) {\r
+ log.debug("VelocityContextとマージ後のXMLデータ\n" + w.toString());\r
+ }\r
+\r
+ StringReader reader = new StringReader(w.toString());\r
+ SAXBuilder builder = new SAXBuilder(true);\r
+ builder.setEntityResolver(new DTDEntityResolver());\r
+ Document mergedDoc = builder.build(reader);\r
+\r
+ return build(mergedDoc.getRootElement());\r
+ }\r
+\r
+ /**\r
+ * @param root\r
+ * @param mail \r
+ */\r
+ protected void setReturnPath(Element root, Mail mail) {\r
+ Element returnPathElem = root.getChild("returnPath");\r
+ if (returnPathElem != null && returnPathElem.getAttributeValue("email") != null) {\r
+ mail.setReturnPath(returnPathElem.getAttributeValue("email"));\r
+ }\r
+ }\r
+\r
+ /**\r
+ * @param root\r
+ * @param mail \r
+ */\r
+ protected void setReplyTo(Element root, Mail mail) {\r
+ Element replyToElem = root.getChild("replyTo");\r
+ if (replyToElem != null && replyToElem.getAttributeValue("email") != null) {\r
+ mail.setReplyTo(replyToElem.getAttributeValue("email"));\r
+ }\r
+ }\r
+\r
+ /**\r
+ * @param root\r
+ * @param mail \r
+ */\r
+ protected void setBody(Element root, Mail mail) {\r
+ Element bodyElem = root.getChild("body");\r
+ if (bodyElem != null) {\r
+ mail.setText(bodyElem.getTextTrim());\r
+ }\r
+ }\r
+\r
+ /**\r
+ * @param root\r
+ * @param mail\r
+ */\r
+ protected void setHtml(Element root, Mail mail) {\r
+ Element htmlElem = root.getChild("html");\r
+ if (htmlElem != null) {\r
+ mail.setHtmlText(htmlElem.getTextTrim());\r
+ }\r
+ }\r
+\r
+ /**\r
+ * @param root\r
+ * @param mail \r
+ */\r
+ protected void setSubject(Element root, Mail mail) {\r
+ Element subjectElem = root.getChild("subject");\r
+ if (subjectElem != null) {\r
+ mail.setSubject(subjectElem.getTextTrim());\r
+ }\r
+ }\r
+\r
+ /**\r
+ * @param root\r
+ * @param mail \r
+ */\r
+ protected void setRecipients(Element root, Mail mail) {\r
+ Element recipientsElem = root.getChild("recipients");\r
+ if (recipientsElem == null) {\r
+ return;\r
+ }\r
+\r
+ List recipientElemList = recipientsElem.getChildren();\r
+ for (int i = 0, max = recipientElemList.size(); i < max; i++) {\r
+ Element e = (Element)recipientElemList.get(i);\r
+ if ("to".equals(e.getName())) { // to\r
+ if (e.getAttributeValue("email") != null) {\r
+ if (e.getAttributeValue("name") != null) {\r
+ mail.addTo(e.getAttributeValue("email"), e.getAttributeValue("name"));\r
+ } else {\r
+ mail.addTo(e.getAttributeValue("email"));\r
+ }\r
+ }\r
+ } else if ("cc".equals(e.getName())) { // cc\r
+ if (e.getAttributeValue("email") != null) {\r
+ if (e.getAttributeValue("name") != null) {\r
+ mail.addCc(e.getAttributeValue("email"), e.getAttributeValue("name"));\r
+ } else {\r
+ mail.addCc(e.getAttributeValue("email"));\r
+ }\r
+ }\r
+ } else {\r
+ if (e.getAttributeValue("email") != null) { // bcc\r
+ mail.addBcc(e.getAttributeValue("email"));\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * @param root\r
+ * @param mail \r
+ */\r
+ protected void setFrom(Element root, Mail mail) {\r
+ Element fromElem = root.getChild("from");\r
+ if (fromElem != null && fromElem.getAttributeValue("email") != null) {\r
+ if (fromElem.getAttributeValue("name") != null) {\r
+ mail.setFrom(fromElem.getAttributeValue("email"), fromElem\r
+ .getAttributeValue("name"));\r
+ } else {\r
+ mail.setFrom(fromElem.getAttributeValue("email"));\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * @see com.ozacc.mail.VelocityMailBuilder#clearCache()\r
+ */\r
+ public synchronized void clearCache() {\r
+ log.debug("テンプレートキャッシュをクリアします。");\r
+ templateCache.clear();\r
+ }\r
+\r
+ /**\r
+ * @see com.ozacc.mail.VelocityMailBuilder#isCacheEnabled()\r
+ */\r
+ public boolean isCacheEnabled() {\r
+ return cacheEnabled;\r
+ }\r
+\r
+ /**\r
+ * @see com.ozacc.mail.VelocityMailBuilder#setCacheEnabled(boolean)\r
+ */\r
+ public void setCacheEnabled(boolean cacheEnabled) {\r
+ if (!cacheEnabled) {\r
+ clearCache();\r
+ }\r
+ this.cacheEnabled = cacheEnabled;\r
+ }\r
+\r
+ protected boolean hasTemplateCache(String key) {\r
+ if (cacheEnabled) {\r
+ return templateCache.containsKey(key);\r
+ }\r
+ return false;\r
+ }\r
+\r
+ protected void putTemplateCache(String key, String templateXmlText) {\r
+ if (cacheEnabled) {\r
+ log.debug("テンプレートをキャッシュします。[key='" + key + "']");\r
+ templateCache.put(key, templateXmlText);\r
+ }\r
+ }\r
+\r
+ protected String getTemplateCache(String key) {\r
+ if (hasTemplateCache(key)) {\r
+ log.debug("テンプレートキャッシュを返します。[key='" + key + "']");\r
+ return (String)templateCache.get(key);\r
+ }\r
+ return null;\r
+ }\r
+\r
+ /**\r
+ * @see com.ozacc.mail.VelocityMultipleMailBuilder#buildMail(java.lang.String, org.apache.velocity.VelocityContext, java.lang.String)\r
+ */\r
+ public Mail buildMail(String classPath, VelocityContext context, String mailId)\r
+ throws MailBuildException {\r
+ if (mailId == null || "".equals(mailId)) {\r
+ throw new IllegalArgumentException("メールIDが指定されていません。");\r
+ }\r
+\r
+ String cacheKey = classPath + CACHE_KEY_SEPARATOR + mailId;\r
+ String templateXmlText;\r
+ if (!hasTemplateCache(cacheKey)) {\r
+ Document doc = getDocumentFromClassPath(classPath);\r
+ templateXmlText = getAndCacheTemplateText(doc, mailId, cacheKey);\r
+ } else {\r
+ templateXmlText = getTemplateCache(cacheKey);\r
+ }\r
+ try {\r
+ return build(templateXmlText, context);\r
+ } catch (Exception e) {\r
+ throw new MailBuildException("メールの生成に失敗しました。", e);\r
+ }\r
+ }\r
+\r
+ private String getAndCacheTemplateText(Document doc, String mailId, String cacheKey)\r
+ throws MailBuildException {\r
+ Element mailElem = getElementById(doc, mailId);\r
+ XMLOutputter output = new XMLOutputter();\r
+ String templateXmlText = output.outputString(mailElem);\r
+\r
+ putTemplateCache(cacheKey, templateXmlText);\r
+ return templateXmlText;\r
+ }\r
+\r
+ /**\r
+ * @see com.ozacc.mail.VelocityMultipleMailBuilder#buildMail(java.io.File, org.apache.velocity.VelocityContext, java.lang.String)\r
+ */\r
+ public Mail buildMail(File file, VelocityContext context, String mailId)\r
+ throws MailBuildException {\r
+ if (mailId == null || "".equals(mailId)) {\r
+ throw new IllegalArgumentException("メールIDが指定されていません。");\r
+ }\r
+\r
+ String cacheKey = file.getAbsolutePath() + CACHE_KEY_SEPARATOR + mailId;\r
+ String templateXmlText;\r
+ if (!hasTemplateCache(cacheKey)) {\r
+ Document doc = getDocumentFromFile(file);\r
+ templateXmlText = getAndCacheTemplateText(doc, mailId, cacheKey);\r
+ } else {\r
+ templateXmlText = getTemplateCache(cacheKey);\r
+ }\r
+ try {\r
+ return build(templateXmlText, context);\r
+ } catch (Exception e) {\r
+ throw new MailBuildException("メールの生成に失敗しました。", e);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * @see com.ozacc.mail.MultipleMailBuilder#buildMail(java.lang.String, java.lang.String)\r
+ */\r
+ public Mail buildMail(String classPath, String mailId) throws MailBuildException {\r
+ Document doc = getDocumentFromClassPath(classPath);\r
+ Element mailElem = getElementById(doc, mailId);\r
+ return build(mailElem);\r
+ }\r
+\r
+ /**\r
+ * @see com.ozacc.mail.MultipleMailBuilder#buildMail(java.io.File, java.lang.String)\r
+ */\r
+ public Mail buildMail(File file, String mailId) throws MailBuildException {\r
+ Document doc = getDocumentFromFile(file);\r
+ Element mailElem = getElementById(doc, mailId);\r
+ return build(mailElem);\r
+ }\r
+\r
+ /**\r
+ * 指定されたXMLドキュメントの中から、指定されたid属性がセットされている要素を取得します。\r
+ * \r
+ * @param doc XMLドキュメント\r
+ * @param id 抽出する要素のid属性値\r
+ * @return XMLドキュメントで見つかったid属性を持つ要素\r
+ */\r
+ private Element getElementById(Document doc, String id) {\r
+ Element mailsElem = doc.getRootElement(); // <mails>\r
+ List mailElemList = mailsElem.getChildren("mail");\r
+ for (Iterator itr = mailElemList.iterator(); itr.hasNext();) {\r
+ Element mailElem = (Element)itr.next();\r
+ String mailId = mailElem.getAttributeValue("id");\r
+ if (mailId.equals(id)) {\r
+ return mailElem;\r
+ }\r
+ }\r
+ throw new MailBuildException("指定されたID[" + id + "]のメールデータは見つかりませんでした。");\r
+ }\r
+\r
+}
\ No newline at end of file
--- /dev/null
+package com.ozacc.mail.impl;\r
+\r
+import java.io.UnsupportedEncodingException;\r
+import java.util.Iterator;\r
+import java.util.Map;\r
+\r
+import javax.activation.DataHandler;\r
+import javax.activation.DataSource;\r
+import javax.mail.MessagingException;\r
+import javax.mail.internet.InternetAddress;\r
+import javax.mail.internet.MimeBodyPart;\r
+import javax.mail.internet.MimeMessage;\r
+import javax.mail.internet.MimeMultipart;\r
+import javax.mail.internet.MimePart;\r
+import javax.mail.internet.MimeUtility;\r
+\r
+import com.ozacc.mail.Mail;\r
+\r
+/**\r
+ * MimeMessageインスタンスを生成するクラス。Mail一通毎に生成されます。\r
+ * \r
+ * @since 1.0\r
+ * @author Tomohiro Otsuka\r
+ * @version $Id: MimeMessageBuilder.java,v 1.11.2.2 2007/03/30 13:03:44 otsuka Exp $\r
+ */\r
+public class MimeMessageBuilder {\r
+\r
+ private MimeMessage mimeMessage;\r
+\r
+ private String charset = Mail.JIS_CHARSET;\r
+\r
+ private boolean hasRecipient = false;\r
+\r
+ /**\r
+ * コンストラクタ。\r
+ * デフォルトの文字コード ISO-2022-JP がエンコーディングに使用されます。\r
+ * \r
+ * @param mimeMessage\r
+ */\r
+ public MimeMessageBuilder(MimeMessage mimeMessage) {\r
+ this.mimeMessage = mimeMessage;\r
+ }\r
+\r
+ /**\r
+ * コンストラクタ。\r
+ * 本文や件名のエンコーディングに使用する文字コードを指定します。\r
+ * \r
+ * @param mimeMessage\r
+ * @param charset エンコーディングに使用する文字コード\r
+ */\r
+ public MimeMessageBuilder(MimeMessage mimeMessage, String charset) {\r
+ this.mimeMessage = mimeMessage;\r
+ this.charset = charset;\r
+ }\r
+\r
+ /**\r
+ * コンストラクタの引数で渡されたMimeMessageをそのまま返します。\r
+ * \r
+ * @return MimeMessage\r
+ */\r
+ public MimeMessage getMimeMessage() {\r
+ return this.mimeMessage;\r
+ }\r
+\r
+ /**\r
+ * 指定されたメールからMimeMessageを生成します。\r
+ * \r
+ * @param mail MimeMessageのソースとなるMail\r
+ * @throws MessagingException\r
+ * @throws UnsupportedEncodingException\r
+ */\r
+ public void buildMimeMessage(Mail mail) throws UnsupportedEncodingException, MessagingException {\r
+ setCharset(mail);\r
+\r
+ setTo(mail);\r
+ setCc(mail);\r
+ setBcc(mail);\r
+ // 宛先の指定がない場合エラー\r
+ if (!hasRecipient) {\r
+ throw new MessagingException("宛先の指定がありません。To、Cc、Bccのいずれか一つは指定する必要があります。");\r
+ }\r
+ setFrom(mail);\r
+ setSubject(mail);\r
+ setReplyTo(mail);\r
+ setXHeaders(mail);\r
+ setImportance(mail);\r
+\r
+ if (mail.isMultipartMail()) {\r
+\r
+ if (!mail.isFileAttached() && mail.isHtmlMail()) { // Plain text, HTML\r
+\r
+ if (mail.getText() != null && mail.getText().length() > 0) { // Plain text, HTML\r
+\r
+ MimeMultipart textAndHtmlMultipart = new MimeMultipart("alternative");\r
+ setPlainText(mail, textAndHtmlMultipart);\r
+ setHtmlText(mail, textAndHtmlMultipart);\r
+ this.mimeMessage.setContent(textAndHtmlMultipart);\r
+\r
+ } else { // HTML Only マルチパートは使用しない\r
+\r
+ setHtmlText(mail.getHtmlText(), this.mimeMessage);\r
+\r
+ }\r
+\r
+ } else if (mail.isFileAttached() && mail.isHtmlMail()) { // Plain text, HMTL, File\r
+\r
+ MimeMultipart textAndHtmlMultipart = new MimeMultipart("alternative");\r
+ setPlainText(mail, textAndHtmlMultipart);\r
+ setHtmlText(mail, textAndHtmlMultipart);\r
+\r
+ MimeMultipart containingMultipart = new MimeMultipart();\r
+ MimeBodyPart textBodyPart = createMimeBodyPart(containingMultipart);\r
+ textBodyPart.setContent(textAndHtmlMultipart);\r
+ setAttachmentFiles(mail, containingMultipart);\r
+\r
+ this.mimeMessage.setContent(containingMultipart);\r
+\r
+ } else if (mail.isFileAttached() && !mail.isHtmlMail()) { // Plain text, File\r
+\r
+ MimeMultipart textAndFileMultipart = new MimeMultipart();\r
+ setPlainText(mail, textAndFileMultipart);\r
+ setAttachmentFiles(mail, textAndFileMultipart);\r
+ this.mimeMessage.setContent(textAndFileMultipart);\r
+\r
+ } else { // Plain text only マルチパートは使用しない\r
+\r
+ setText(mail.getText(), this.mimeMessage);\r
+\r
+ }\r
+\r
+ } else {\r
+\r
+ setText(mail.getText(), this.mimeMessage);\r
+\r
+ }\r
+\r
+ }\r
+\r
+ /**\r
+ * メールに使用するcharsetを決定します。\r
+ * Mail#charsetが設定されている場合、その値が優先されます。\r
+ * \r
+ * @since 1.2.1\r
+ * @param mail\r
+ */\r
+ private void setCharset(Mail mail) {\r
+ if (mail.getCharset() != null && !"".equals(mail.getCharset())) {\r
+ charset = mail.getCharset();\r
+ }\r
+ }\r
+\r
+ /**\r
+ * \r
+ * @since 1.1\r
+ * \r
+ * @param mail\r
+ * @param mimeMultipart\r
+ * @throws MessagingException\r
+ * @throws UnsupportedEncodingException \r
+ */\r
+ private void setAttachmentFiles(Mail mail, MimeMultipart mimeMultipart)\r
+ throws MessagingException,\r
+ UnsupportedEncodingException {\r
+ Mail.AttachmentFile[] files = mail.getAttachmentFiles();\r
+ for (int i = 0; i < files.length; i++) {\r
+ MimeBodyPart bodyPart = createMimeBodyPart(mimeMultipart);\r
+ Mail.AttachmentFile attachmentFile = files[i];\r
+ addAttachment(attachmentFile.getName(), attachmentFile.getDataSource(), bodyPart);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * \r
+ * @since 1.1\r
+ * \r
+ * @param mail\r
+ * @param mimeMultipart\r
+ * @throws MessagingException \r
+ */\r
+ private void setHtmlText(Mail mail, MimeMultipart mimeMultipart) throws MessagingException {\r
+ if (mail.isHtmlMail()) {\r
+ MimeBodyPart bodyPart = createMimeBodyPart(mimeMultipart);\r
+ setHtmlText(mail.getHtmlText(), bodyPart);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * \r
+ * @since 1.1\r
+ * \r
+ * @param mail\r
+ * @param mimeMultipart\r
+ * @throws MessagingException \r
+ */\r
+ private void setPlainText(Mail mail, MimeMultipart mimeMultipart) throws MessagingException {\r
+ if (mail.getText() != null && mail.getText().length() > 0) {\r
+ MimeBodyPart bodyPart = createMimeBodyPart(mimeMultipart);\r
+ setText(mail.getText(), bodyPart);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * 新しいMimeBodyPartインスタンスを生成し、指定されたMimeMultipartに登録します。\r
+ * \r
+ * このメソッドはマルチパートメール生成時にのみ呼び出すことができます。\r
+ * プレーンテキストメール生成時には、mimeMulipartがnullなので、\r
+ * NullPointerExceptionがスローされます。\r
+ * \r
+ * @since 1.1\r
+ * \r
+ * @param mm\r
+ * @return 生成されたMimeBodyPart\r
+ * @throws MessagingException \r
+ */\r
+ private MimeBodyPart createMimeBodyPart(MimeMultipart mm) throws MessagingException {\r
+ MimeBodyPart bodyPart = new MimeBodyPart();\r
+ mm.addBodyPart(bodyPart);\r
+ return bodyPart;\r
+ }\r
+\r
+ /**\r
+ * @since 1.1\r
+ * \r
+ * @param htmlText\r
+ * @param mimePart \r
+ * @throws MessagingException\r
+ */\r
+ private void setHtmlText(final String htmlText, MimePart mimePart) throws MessagingException {\r
+ if (charset != null) {\r
+ mimePart.setContent(htmlText, "text/html; charset=" + charset);\r
+ } else {\r
+ mimePart.setContent(htmlText, "text/html");\r
+ }\r
+ setContentTransferEncoding(mimePart);\r
+ }\r
+\r
+ /**\r
+ * @param mail \r
+ * @throws MessagingException\r
+ */\r
+ private void setXHeaders(Mail mail) throws MessagingException {\r
+ Map headers = mail.getHeaders();\r
+ if (headers == null) {\r
+ return;\r
+ }\r
+\r
+ Iterator itr = headers.keySet().iterator();\r
+ while (itr.hasNext()) {\r
+ String key = (String)itr.next();\r
+ String value = (String)headers.get(key);\r
+ mimeMessage.setHeader(key, value);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * @param mail \r
+ * @throws MessagingException\r
+ */\r
+ private void setImportance(Mail mail) throws MessagingException {\r
+ if (mail.getImportance() != null) {\r
+ mimeMessage.setHeader("Importance", mail.getImportance());\r
+\r
+ int level = 3;\r
+ if (Mail.Importance.HIGH.equals(mail.getImportance())) {\r
+ level = 1;\r
+ } else if (Mail.Importance.LOW.equals(mail.getImportance())) {\r
+ level = 5;\r
+ }\r
+ mimeMessage.setHeader("X-Priority", String.valueOf(level));\r
+ }\r
+ }\r
+\r
+ /**\r
+ * @param mail \r
+ * @throws MessagingException\r
+ * @throws UnsupportedEncodingException \r
+ */\r
+ private void setReplyTo(Mail mail) throws MessagingException, UnsupportedEncodingException {\r
+ if (mail.getReplyTo() != null) {\r
+ mimeMessage.setReplyTo(new InternetAddress[] { convertCharset(mail.getReplyTo()) });\r
+ }\r
+ }\r
+\r
+ /**\r
+ * @param mail \r
+ * @throws MessagingException\r
+ * @throws UnsupportedEncodingException \r
+ */\r
+ private void setBcc(Mail mail) throws MessagingException, UnsupportedEncodingException {\r
+ if (mail.getBcc().length > 0) {\r
+ mimeMessage.setRecipients(MimeMessage.RecipientType.BCC, convertCharset(mail.getBcc()));\r
+ hasRecipient = true;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * @param mail \r
+ * @throws MessagingException\r
+ * @throws UnsupportedEncodingException \r
+ */\r
+ private void setCc(Mail mail) throws MessagingException, UnsupportedEncodingException {\r
+ if (mail.getCc().length > 0) {\r
+ mimeMessage.setRecipients(MimeMessage.RecipientType.CC, convertCharset(mail.getCc()));\r
+ hasRecipient = true;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * @param mail \r
+ * @throws MessagingException\r
+ * @throws UnsupportedEncodingException \r
+ */\r
+ private void setTo(Mail mail) throws MessagingException, UnsupportedEncodingException {\r
+ if (mail.getTo().length > 0) {\r
+ mimeMessage.setRecipients(MimeMessage.RecipientType.TO, convertCharset(mail.getTo()));\r
+ hasRecipient = true;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * 本文をセット。\r
+ * <p>\r
+ * NOTE: 本文の最後に改行がないとMozilla系のメーラーで最終行の日本語が文字化けしてしまう為、\r
+ * message.setTextの引数で最後に\nを追加している。\r
+ * \r
+ * @since 1.1\r
+ * \r
+ * @param text 本文\r
+ * @param mimePart 本文をセットするMimePart \r
+ * @throws MessagingException\r
+ */\r
+ private void setText(String text, MimePart mimePart) throws MessagingException {\r
+ if (charset != null) {\r
+ if (charset.equalsIgnoreCase(Mail.JIS_CHARSET)) {\r
+ // Cp932クラスを使用して、怪しい記号を強制的にJIS変換\r
+ mimePart.setText(Cp932.toJIS(text) + "\n", charset);\r
+ } else {\r
+ mimePart.setText(text + "\n", charset);\r
+ }\r
+ } else {\r
+ mimePart.setText(text + "\n");\r
+ }\r
+ setContentTransferEncoding(mimePart);\r
+ }\r
+\r
+ /**\r
+ * charsetに応じてContent-Trnasfer-Encodingを設定します。\r
+ * が、UTF-8の時に8bitになること以外分かりません。。。\r
+ * \r
+ * @since 1.2.1\r
+ * @param mimePart\r
+ * @throws MessagingException \r
+ */\r
+ private void setContentTransferEncoding(MimePart mimePart) throws MessagingException {\r
+ String contentTransferEncoding = "7bit";\r
+ if ("UTF-8".equalsIgnoreCase(charset)) {\r
+ contentTransferEncoding = "8bit";\r
+ }\r
+ mimePart.setHeader("Content-Transfer-Encoding", contentTransferEncoding);\r
+ }\r
+\r
+ /**\r
+ * @param mail\r
+ * @throws MessagingException\r
+ * @throws UnsupportedEncodingException\r
+ */\r
+ private void setSubject(Mail mail) throws UnsupportedEncodingException, MessagingException {\r
+ if (charset != null) {\r
+ if (Mail.JIS_CHARSET.equalsIgnoreCase(charset)) {\r
+ String subject = Cp932.toJIS(mail.getSubject());\r
+ mimeMessage.setSubject(MimeUtility.encodeText(subject, charset, "B"));\r
+ } else {\r
+ mimeMessage.setSubject(mail.getSubject(), charset);\r
+ }\r
+ } else {\r
+ mimeMessage.setSubject(mail.getSubject());\r
+ }\r
+ }\r
+\r
+ /**\r
+ * @param mail\r
+ * @throws MessagingException \r
+ * @throws UnsupportedEncodingException \r
+ */\r
+ private void setFrom(Mail mail) throws MessagingException, UnsupportedEncodingException {\r
+ InternetAddress address = convertCharset(mail.getFrom());\r
+ mimeMessage.setFrom(address);\r
+ }\r
+\r
+ private InternetAddress convertCharset(InternetAddress address)\r
+ throws UnsupportedEncodingException {\r
+ String name = address.getPersonal();\r
+ if (name != null && !"".equals(name) && Mail.JIS_CHARSET.equalsIgnoreCase(charset)) {\r
+ name = Cp932.toJIS(name);\r
+ }\r
+ return new InternetAddress(address.getAddress(), name, charset);\r
+ }\r
+\r
+ private InternetAddress[] convertCharset(InternetAddress[] addresses)\r
+ throws UnsupportedEncodingException {\r
+ for (int i = 0; i < addresses.length; i++) {\r
+ addresses[i] = convertCharset(addresses[i]);\r
+ }\r
+ return addresses;\r
+ }\r
+\r
+ /**\r
+ * 添付ファイルデータを指定されたMimeBodyPartにセットします。\r
+ * \r
+ * @since 1.1\r
+ * \r
+ * @param fileName\r
+ * @param dataSource\r
+ * @param mimeBodyPart ファイルデータをセットするMimeBodyPart\r
+ * @throws UnsupportedEncodingException\r
+ * @throws MessagingException\r
+ */\r
+ private void addAttachment(String fileName, DataSource dataSource, MimeBodyPart mimeBodyPart)\r
+ throws UnsupportedEncodingException,\r
+ MessagingException {\r
+ if (charset != null) {\r
+ // ファイル名のエンコード\r
+ mimeBodyPart.setFileName(MimeUtility.encodeText(fileName, charset, "B"));\r
+ } else {\r
+ mimeBodyPart.setFileName(fileName);\r
+ }\r
+\r
+ mimeBodyPart.setDataHandler(new DataHandler(dataSource));\r
+ }\r
+\r
+}
\ No newline at end of file
--- /dev/null
+package com.ozacc.mail.impl;\r
+\r
+import java.util.Random;\r
+\r
+import javax.mail.MessagingException;\r
+import javax.mail.Session;\r
+import javax.mail.internet.MimeMessage;\r
+\r
+/**\r
+ * Message-Idヘッダがカスタマイズ可能なMimeMessageのサブクラス。\r
+ * \r
+ * @since 1.1\r
+ * \r
+ * @author Tomohiro Otsuka\r
+ * @version $Id: OMLMimeMessage.java,v 1.3.2.3 2006/01/20 10:57:50 otsuka Exp $\r
+ */\r
+public class OMLMimeMessage extends MimeMessage {\r
+\r
+ private static Random random = new Random();\r
+\r
+ private String domainPartOfMessageId;\r
+\r
+ private String messageId;\r
+\r
+ /**\r
+ * コンストラクタ。\r
+ * \r
+ * @param session \r
+ * @param domainPartOfMessageId Message-Idヘッダのドメイン部分に使用する文字列\r
+ */\r
+ public OMLMimeMessage(Session session, String domainPartOfMessageId) {\r
+ super(session);\r
+\r
+ String[] parts = domainPartOfMessageId.split("@");\r
+ if (parts.length == 1) {\r
+ this.domainPartOfMessageId = "@" + domainPartOfMessageId;\r
+ } else if (parts.length == 2) {\r
+ if (parts[0].length() > 0 && !parts[0].startsWith(".")) {\r
+ this.domainPartOfMessageId = "." + domainPartOfMessageId;\r
+ } else {\r
+ this.domainPartOfMessageId = domainPartOfMessageId;\r
+ }\r
+ }\r
+ messageId = generateRandomMessageId();\r
+ }\r
+\r
+ /**\r
+ * Message-Idヘッダをここでセットします。\r
+ * <p>\r
+ * 参考ページ<br>\r
+ * <a href="http://java.sun.com/products/javamail/FAQ.html#msgid">http://java.sun.com/products/javamail/FAQ.html#msgid</a>\r
+ * \r
+ * @see javax.mail.internet.MimeMessage#updateHeaders()\r
+ */\r
+ protected void updateHeaders() throws MessagingException {\r
+ super.updateHeaders();\r
+ setHeader("Message-ID", messageId);\r
+ }\r
+\r
+ /**\r
+ * タイムスタンプ + 16桁の乱数 + messageIdプロパティを連結した文字列を返します。\r
+ * \r
+ * @return タイムスタンプ + 16桁の乱数 + messageIdプロパティを連結した文字列\r
+ */\r
+ protected String generateRandomMessageId() {\r
+ StringBuffer buf = new StringBuffer();\r
+ buf.append("<");\r
+ buf.append(System.currentTimeMillis()).append(".");\r
+ for (int i = 0; i < 16; i++) {\r
+ long num = Math.abs(random.nextInt(10));\r
+ buf.append(num);\r
+ }\r
+ buf.append(domainPartOfMessageId);\r
+ buf.append(">");\r
+ return buf.toString();\r
+ }\r
+\r
+ /**\r
+ * 生成されたMessage-Idを返します。\r
+ * \r
+ * @return 生成されたMessage-Id\r
+ */\r
+ public String getMessageId() {\r
+ return messageId;\r
+ }\r
+}
\ No newline at end of file
--- /dev/null
+package com.ozacc.mail.impl;\r
+\r
+import java.io.UnsupportedEncodingException;\r
+import java.util.Date;\r
+import java.util.Properties;\r
+\r
+import javax.mail.AuthenticationFailedException;\r
+import javax.mail.MessagingException;\r
+import javax.mail.Session;\r
+import javax.mail.Transport;\r
+import javax.mail.internet.InternetAddress;\r
+import javax.mail.internet.MimeMessage;\r
+\r
+import org.apache.commons.logging.Log;\r
+import org.apache.commons.logging.LogFactory;\r
+\r
+import com.ozacc.mail.Mail;\r
+import com.ozacc.mail.MailAuthenticationException;\r
+import com.ozacc.mail.MailBuildException;\r
+import com.ozacc.mail.MailException;\r
+import com.ozacc.mail.MailSendException;\r
+import com.ozacc.mail.SendMail;\r
+\r
+/**\r
+ * SendMailインターフェースの実装クラス。\r
+ * \r
+ * @since 1.0\r
+ * @author Tomohiro Otsuka\r
+ * @version $Id: SendMailImpl.java,v 1.7.2.6 2007/03/30 13:03:44 otsuka Exp $\r
+ */\r
+public class SendMailImpl implements SendMail {\r
+\r
+ private static Log log = LogFactory.getLog(SendMailImpl.class);\r
+\r
+ /** デフォルトのプロトコル。「smtp」 */\r
+ public static final String DEFAULT_PROTOCOL = "smtp";\r
+\r
+ /**\r
+ * デフォルトのポート。「-1」<br>\r
+ * -1はプロトコルに応じた適切なポートを設定する特別な値。\r
+ * */\r
+ public static final int DEFAULT_PORT = -1;\r
+\r
+ /** デフォルトのSMTPサーバ。「localhost」 */\r
+ public static final String DEFAULT_HOST = "localhost";\r
+\r
+ /** ISO-2022-JP */\r
+ public static final String JIS_CHARSET = "ISO-2022-JP";\r
+\r
+ private static final String RETURN_PATH_KEY = "mail.smtp.from";\r
+\r
+ /** 接続タイムアウト */\r
+ private static final int DEFAULT_CONNECTION_TIMEOUT = 5000;\r
+\r
+ /** 読込タイムアウト */\r
+ private static final int DEFAULT_READ_TIMEOUT = 5000;\r
+\r
+ private String protocol = DEFAULT_PROTOCOL;\r
+\r
+ private String host = DEFAULT_HOST;\r
+\r
+ private int port = DEFAULT_PORT;\r
+\r
+ private String username;\r
+\r
+ private String password;\r
+\r
+ private String charset = JIS_CHARSET;\r
+\r
+ private String returnPath;\r
+\r
+ private String messageId;\r
+\r
+ private int connectionTimeout = DEFAULT_CONNECTION_TIMEOUT;\r
+\r
+ private int readTimeout = DEFAULT_READ_TIMEOUT;\r
+\r
+ /**\r
+ * コンストラクタ。\r
+ */\r
+ public SendMailImpl() {}\r
+\r
+ /**\r
+ * コンストラクタ。使用するSMTPサーバを指定します。\r
+ * \r
+ * @param host SMTPサーバのホスト名、またはIPアドレス\r
+ */\r
+ public SendMailImpl(String host) {\r
+ this();\r
+ setHost(host);\r
+ }\r
+\r
+ /**\r
+ * @see com.ozacc.mail.SendMail#send(com.ozacc.mail.Mail)\r
+ */\r
+ public void send(Mail mail) throws MailException {\r
+ send(new Mail[] { mail });\r
+ }\r
+\r
+ /**\r
+ * @see com.ozacc.mail.SendMail#send(com.ozacc.mail.Mail[])\r
+ */\r
+ public void send(Mail[] mails) throws MailException {\r
+ MimeMessageWrapper[] mmws = new MimeMessageWrapper[mails.length];\r
+ Session session = Session.getInstance(new Properties());\r
+ for (int i = 0; i < mails.length; i++) {\r
+ Mail mail = mails[i];\r
+\r
+ // MimeMessageを生成\r
+ MimeMessage message = createMimeMessage(session);\r
+ if (isMessageIdCustomized()) {\r
+ mail.addHeader("Message-ID", ((OMLMimeMessage)message).getMessageId());\r
+ }\r
+ MimeMessageBuilder builder = new MimeMessageBuilder(message, charset);\r
+ try {\r
+ builder.buildMimeMessage(mail);\r
+ } catch (UnsupportedEncodingException e) {\r
+ throw new MailBuildException("サポートされていない文字コードが指定されました。", e);\r
+ } catch (MessagingException e) {\r
+ throw new MailBuildException("MimeMessageの生成に失敗しました。", e);\r
+ }\r
+\r
+ // Return-Pathを取得\r
+ String returnPath;\r
+ if (mail.getReturnPath() != null) {\r
+ returnPath = mail.getReturnPath().getAddress();\r
+ } else {\r
+ returnPath = this.returnPath;\r
+ }\r
+\r
+ mmws[i] = new MimeMessageWrapper(message, returnPath, mail.getEnvelopeTo());\r
+ }\r
+ processSend(mmws);\r
+ }\r
+\r
+ /**\r
+ * @see com.ozacc.mail.SendMail#send(javax.mail.internet.MimeMessage)\r
+ */\r
+ public void send(MimeMessage message) throws MailException {\r
+ send(new MimeMessage[] { message });\r
+ }\r
+\r
+ /**\r
+ * @see com.ozacc.mail.SendMail#send(javax.mail.internet.MimeMessage[])\r
+ */\r
+ public void send(MimeMessage[] messages) throws MailException {\r
+ MimeMessageWrapper[] mmws = new MimeMessageWrapper[messages.length];\r
+ for (int i = 0; i < messages.length; i++) {\r
+ mmws[i] = new MimeMessageWrapper(messages[i], returnPath);\r
+ }\r
+ processSend(mmws);\r
+ }\r
+\r
+ private void processSend(MimeMessageWrapper[] mmws) throws MailException {\r
+\r
+ Properties prop = new Properties();\r
+ // タイムアウトの設定\r
+ prop.put("mail.smtp.connectiontimeout", String.valueOf(connectionTimeout));\r
+ prop.put("mail.smtp.timeout", String.valueOf(readTimeout));\r
+ // mail.smtp.authプロパティの設定\r
+ if (username != null && !"".equals(username) && password != null && !"".equals(password)) {\r
+ prop.put("mail.smtp.auth", "true");\r
+ }\r
+ Session session = Session.getInstance(prop);\r
+\r
+ Transport transport = null;\r
+ try {\r
+ // SMTPサーバに接続\r
+ log.debug("SMTPサーバ[" + host + "]に接続します。");\r
+ transport = session.getTransport(protocol);\r
+ transport.connect(host, port, username, password);\r
+ log.debug("SMTPサーバ[" + host + "]に接続しました。");\r
+\r
+ for (int i = 0; i < mmws.length; i++) {\r
+ MimeMessage mimeMessage = mmws[i].getMimeMessage();\r
+ // Return-Pathをセット\r
+ String returnPath = mmws[i].getReturnPath();\r
+ if (returnPath != null) {\r
+ session.getProperties().put(RETURN_PATH_KEY, returnPath);\r
+ log.debug("Return-Path[" + returnPath + "]を設定しました。");\r
+ }\r
+ // 送信日時をセット\r
+ mimeMessage.setSentDate(new Date());\r
+ mimeMessage.saveChanges();\r
+\r
+ // 送信\r
+ log.debug("メールを送信します。");\r
+ if (mmws[i].hasEnvelopeTo()) {\r
+ log.debug("メールはenvelope-toアドレスに送信されます。");\r
+ transport.sendMessage(mimeMessage, mmws[i].getEnvelopeTo());\r
+ } else {\r
+ transport.sendMessage(mimeMessage, mimeMessage.getAllRecipients());\r
+ }\r
+ log.debug("メールを送信しました。");\r
+\r
+ // Return-Pathを解除\r
+ if (returnPath != null) {\r
+ session.getProperties().remove(RETURN_PATH_KEY);\r
+ log.debug("Return-Path設定をクリアしました。");\r
+ }\r
+ }\r
+ } catch (AuthenticationFailedException ex) {\r
+ log.error("SMTPサーバ[" + host + "]への接続認証に失敗しました。", ex);\r
+ throw new MailAuthenticationException(ex);\r
+ } catch (MessagingException ex) {\r
+ log.error("メールの送信に失敗しました。", ex);\r
+ throw new MailSendException("メールの送信に失敗しました。", ex);\r
+ } finally {\r
+ if (transport != null && transport.isConnected()) {\r
+ log.debug("SMTPサーバ[" + host + "]との接続を切断します。");\r
+ try {\r
+ // SMTPサーバとの接続を切断\r
+ transport.close();\r
+ } catch (MessagingException e) {\r
+ log.error("SMTPサーバ[" + host + "]との接続切断に失敗しました。", e);\r
+ throw new MailException("SMTPサーバ[" + host + "]との接続切断に失敗しました。");\r
+ }\r
+ log.debug("SMTPサーバ[" + host + "]との接続を切断しました。");\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * 新しいMimeMessageオブジェクトを生成します。<br>\r
+ * messageIdプロパティがセットされている場合、OMLMimeMessageのインスタンスを生成します。\r
+ * \r
+ * @return 新しいMimeMessageオブジェクト\r
+ */\r
+ private MimeMessage createMimeMessage(Session session) {\r
+ if (isMessageIdCustomized()) {\r
+ return new OMLMimeMessage(session, messageId);\r
+ }\r
+ return new MimeMessage(session);\r
+ }\r
+\r
+ /**\r
+ * Message-Idヘッダのドメイン部分を独自にセットしているかどうか判定します。\r
+ * \r
+ * @return Message-Idヘッダのドメイン部分を独自にセットしている場合 true\r
+ */\r
+ private boolean isMessageIdCustomized() {\r
+ return messageId != null;\r
+ }\r
+\r
+ /**\r
+ * エンコーディングに使用する文字コードを返します。\r
+ * \r
+ * @return エンコーディングに使用する文字コード\r
+ */\r
+ public String getCharset() {\r
+ return charset;\r
+ }\r
+\r
+ /**\r
+ * メールの件名や本文のエンコーディングに使用する文字コードを指定します。\r
+ * デフォルトは<code>ISO-2022-JP</code>です。\r
+ * <p>\r
+ * 日本語環境で利用する場合は通常変更する必要はありません。\r
+ * \r
+ * @param charset エンコーディングに使用する文字コード\r
+ */\r
+ public void setCharset(String charset) {\r
+ this.charset = charset;\r
+ }\r
+\r
+ /**\r
+ * セットされたSMTPサーバのホスト名、またはIPアドレスを返します。\r
+ * \r
+ * @return SMTPサーバのホスト名、またはIPアドレス\r
+ */\r
+ public String getHost() {\r
+ return host;\r
+ }\r
+\r
+ /**\r
+ * SMTPサーバのホスト名、またはIPアドレスをセットします。\r
+ * デフォルトは localhost です。\r
+ * \r
+ * @param host SMTPサーバのホスト名、またはIPアドレス\r
+ */\r
+ public void setHost(String host) {\r
+ this.host = host;\r
+ }\r
+\r
+ /**\r
+ * @return SMTPサーバ認証パスワード\r
+ */\r
+ public String getPassword() {\r
+ return password;\r
+ }\r
+\r
+ /**\r
+ * SMTPサーバの接続認証が必要な場合にパスワードをセットします。\r
+ * \r
+ * @param password SMTPサーバ認証パスワード\r
+ */\r
+ public void setPassword(String password) {\r
+ this.password = password;\r
+ }\r
+\r
+ /**\r
+ * @return SMTPサーバのポート番号\r
+ */\r
+ public int getPort() {\r
+ return port;\r
+ }\r
+\r
+ /**\r
+ * SMTPサーバのポート番号をセットします。\r
+ * \r
+ * @param port SMTPサーバのポート番号\r
+ */\r
+ public void setPort(int port) {\r
+ this.port = port;\r
+ }\r
+\r
+ /**\r
+ * プロトコルを返します。\r
+ * \r
+ * @return プロトコル\r
+ */\r
+ public String getProtocol() {\r
+ return protocol;\r
+ }\r
+\r
+ /**\r
+ * プロトコルをセットします。デフォルトは「smtp」。\r
+ * \r
+ * @param protocol プロトコル\r
+ */\r
+ public void setProtocol(String protocol) {\r
+ this.protocol = protocol;\r
+ }\r
+\r
+ /**\r
+ * @return Return-Pathアドレス\r
+ */\r
+ public String getReturnPath() {\r
+ return returnPath;\r
+ }\r
+\r
+ /**\r
+ * Return-Pathアドレスをセットします。\r
+ * <p>\r
+ * 送信するMailインスタンスに指定されたFromアドレス以外のアドレスをReturn-Pathとしたい場合に使用します。\r
+ * ここでセットされたReturn-Pathより、MailインスタンスにセットされたReturn-Pathが優先されます。\r
+ * \r
+ * @param returnPath Return-Pathアドレス\r
+ */\r
+ public void setReturnPath(String returnPath) {\r
+ this.returnPath = returnPath;\r
+ }\r
+\r
+ /**\r
+ * @return SMTPサーバ認証ユーザ名\r
+ */\r
+ public String getUsername() {\r
+ return username;\r
+ }\r
+\r
+ /**\r
+ * SMTPサーバの接続認証が必要な場合にユーザ名をセットします。\r
+ * \r
+ * @param username SMTPサーバ認証ユーザ名\r
+ */\r
+ public void setUsername(String username) {\r
+ this.username = username;\r
+ }\r
+\r
+ /**\r
+ * SMTPサーバとの接続タイムアウトをセットします。\r
+ * 単位はミリ秒。デフォルトは5,000ミリ秒(5秒)です。\r
+ * <p>\r
+ * -1を指定すると無限大になりますが、お薦めしません。\r
+ * \r
+ * @since 1.1.4\r
+ * @param connectionTimeout SMTPサーバとの接続タイムアウト\r
+ */\r
+ public void setConnectionTimeout(int connectionTimeout) {\r
+ this.connectionTimeout = connectionTimeout;\r
+ }\r
+\r
+ /**\r
+ * SMTPサーバへの送信時のタイムアウトをセットします。\r
+ * 単位はミリ秒。デフォルトは5,000ミリ秒(5秒)です。<br>\r
+ * 送信時にタイムアウトすると、<code>com.ozacc.mail.MailSendException</code>がスローされます。\r
+ * <p>\r
+ * -1を指定すると無限大になりますが、お薦めしません。\r
+ * \r
+ * @since 1.1.4\r
+ * @param readTimeout SMTPサーバへの送受信時のタイムアウト\r
+ */\r
+ public void setReadTimeout(int readTimeout) {\r
+ this.readTimeout = readTimeout;\r
+ }\r
+\r
+ /**\r
+ * 生成されるMimeMessageに付けられるMessage-Idヘッダのドメイン部分を指定します。<br>\r
+ * 指定されない場合(nullや空文字列の場合)は、JavaMailがMessage-Idヘッダを生成します。\r
+ * JavaMailが生成する「JavaMail.実行ユーザ名@ホスト名」のMessage-Idを避けたい場合に、このメソッドを使用します。\r
+ * <p>\r
+ * messageIdプロパティがセットされている場合、Mailから生成されるMimeMessageのMessage-Idには\r
+ * <code>タイムスタンプ + ランダムに生成される16桁の数値 + ここでセットされた値</code>\r
+ * が使用されます。\r
+ * <p>\r
+ * 生成されるMessage-Idの例。 (実際の数値部分は送信メール毎に変わります)<ul>\r
+ * <li>messageIdに'example.com'を指定した場合・・・1095714924963.5619528074501343@example.com</li>\r
+ * <li>messageIdに'@example.com'を指定した場合・・・1095714924963.5619528074501343@example.com (上と同じ)</li>\r
+ * <li>messageIdに'OML@example.com'を指定した場合・・・1095714924963.5619528074501343.OML@example.com</li>\r
+ * <li>messageIdに'.OML@example.com'を指定した場合・・・1095714924963.5619528074501343.OML@example.com (上と同じ)</li>\r
+ * </ul>\r
+ * <p>\r
+ * <strong>注:</strong> このMessage-Idは<code>send(Mail)</code>か<code>send(Mail[])</code>メソッドが呼びだれた時にのみ有効です。MimeMessageを直接送信する場合には適用されません。\r
+ * \r
+ * @param messageId メールに付けられるMessage-Idヘッダのドメイン部分\r
+ * @throws IllegalArgumentException @を複数含んだ文字列を指定した場合\r
+ */\r
+ public void setMessageId(String messageId) {\r
+ if (messageId == null || messageId.length() < 1) {\r
+ return;\r
+ }\r
+\r
+ String[] parts = messageId.split("@");\r
+ if (parts.length > 2) {\r
+ throw new IllegalArgumentException("messageIdプロパティに'@'を複数含むことはできません。[" + messageId\r
+ + "]");\r
+ }\r
+\r
+ this.messageId = messageId;\r
+ }\r
+\r
+ /**\r
+ * MimeMessageインスタンスと、そのメールに対応するReturn-Path、envelope-toアドレスをラップするクラス。\r
+ * \r
+ * @author Tomohiro Otsuka\r
+ * @version $Id: SendMailImpl.java,v 1.7.2.6 2007/03/30 13:03:44 otsuka Exp $\r
+ */\r
+ private static class MimeMessageWrapper {\r
+\r
+ private MimeMessage mimeMessage;\r
+\r
+ private String returnPath;\r
+\r
+ private InternetAddress[] envelopeTo;\r
+\r
+ public MimeMessageWrapper(MimeMessage mimeMessage, String returnPath) {\r
+ this.mimeMessage = mimeMessage;\r
+ this.returnPath = returnPath;\r
+ }\r
+\r
+ public MimeMessageWrapper(MimeMessage mimeMessage, String returnPath,\r
+ InternetAddress[] envelopeTo) {\r
+ this.mimeMessage = mimeMessage;\r
+ this.returnPath = returnPath;\r
+ this.envelopeTo = envelopeTo;\r
+ }\r
+\r
+ public MimeMessage getMimeMessage() {\r
+ return mimeMessage;\r
+ }\r
+\r
+ public String getReturnPath() {\r
+ return returnPath;\r
+ }\r
+\r
+ public boolean hasEnvelopeTo() {\r
+ return envelopeTo.length > 0;\r
+ }\r
+\r
+ public InternetAddress[] getEnvelopeTo() {\r
+ return envelopeTo;\r
+ }\r
+ }\r
+\r
+}
\ No newline at end of file
--- /dev/null
+package com.ozacc.mail.impl;\r
+\r
+import java.io.UnsupportedEncodingException;\r
+import java.util.Date;\r
+import java.util.Properties;\r
+\r
+import javax.mail.Address;\r
+import javax.mail.AuthenticationFailedException;\r
+import javax.mail.MessagingException;\r
+import javax.mail.Session;\r
+import javax.mail.Transport;\r
+import javax.mail.internet.MimeMessage;\r
+\r
+import org.apache.commons.logging.Log;\r
+import org.apache.commons.logging.LogFactory;\r
+\r
+import com.ozacc.mail.Mail;\r
+import com.ozacc.mail.MailAuthenticationException;\r
+import com.ozacc.mail.MailBuildException;\r
+import com.ozacc.mail.MailException;\r
+import com.ozacc.mail.MailSendException;\r
+import com.ozacc.mail.NotConnectedException;\r
+import com.ozacc.mail.SendMailPro;\r
+\r
+/**\r
+ * SendMailProインターフェースの実装クラス。\r
+ * \r
+ * @since 1.0\r
+ * @author Tomohiro Otsuka\r
+ * @version $Id: SendMailProImpl.java,v 1.4.2.5 2006/08/07 13:45:22 otsuka Exp $\r
+ */\r
+public class SendMailProImpl implements SendMailPro {\r
+\r
+ /** smtp */\r
+ public static final String DEFAULT_PROTOCOL = "smtp";\r
+\r
+ /** -1 */\r
+ public static final int DEFAULT_PORT = -1;\r
+\r
+ /** localhost */\r
+ public static final String DEFAULT_HOST = "localhost";\r
+\r
+ /** ISO-2022-JP */\r
+ public static final String JIS_CHARSET = "ISO-2022-JP";\r
+\r
+ private static final String RETURN_PATH_KEY = "mail.smtp.from";\r
+\r
+ private static Log log = LogFactory.getLog(SendMailProImpl.class);\r
+\r
+ /** 接続タイムアウト */\r
+ private static final int DEFAULT_CONNECTION_TIMEOUT = 5000;\r
+\r
+ /** 読込タイムアウト */\r
+ private static final int DEFAULT_READ_TIMEOUT = 5000;\r
+\r
+ private String protocol = DEFAULT_PROTOCOL;\r
+\r
+ private String host = DEFAULT_HOST;\r
+\r
+ private int port = DEFAULT_PORT;\r
+\r
+ private String username;\r
+\r
+ private String password;\r
+\r
+ private String charset = JIS_CHARSET;\r
+\r
+ private String returnPath;\r
+\r
+ private Session session;\r
+\r
+ private Transport transport;\r
+\r
+ private boolean connected;\r
+\r
+ private String messageId;\r
+\r
+ private int connectionTimeout = DEFAULT_CONNECTION_TIMEOUT;\r
+\r
+ private int readTimeout = DEFAULT_READ_TIMEOUT;\r
+\r
+ /**\r
+ * コンストラクタ。\r
+ */\r
+ public SendMailProImpl() {}\r
+\r
+ /**\r
+ * コンストラクタ。使用するSMTPサーバを指定します。\r
+ * \r
+ * @param host SMTPサーバのホスト名、またはIPアドレス\r
+ */\r
+ public SendMailProImpl(String host) {\r
+ this();\r
+ setHost(host);\r
+ }\r
+\r
+ /**\r
+ * @see com.ozacc.mail.SendMailPro#connect()\r
+ */\r
+ public synchronized void connect() throws MailException {\r
+ if (session == null) {\r
+ initSession();\r
+ }\r
+\r
+ // グローバルReturn-Pathの設定\r
+ putOnReturnPath(this.returnPath);\r
+\r
+ try {\r
+ // SMTPサーバに接続\r
+ log.debug("SMTPサーバ[" + host + "]に接続します。");\r
+\r
+ transport = session.getTransport(protocol);\r
+ transport.connect(host, port, username, password);\r
+ } catch (AuthenticationFailedException ex) {\r
+ log.error("SMTPサーバ[" + host + "]への接続認証に失敗しました。", ex);\r
+ throw new MailAuthenticationException(ex);\r
+ } catch (MessagingException ex) {\r
+ log.error("SMTPサーバ[" + host + "]への接続に失敗しました。", ex);\r
+ throw new MailSendException("SMTPサーバ[" + host + "]への接続に失敗しました。", ex);\r
+ }\r
+\r
+ log.debug("SMTPサーバ[" + host + "]に接続しました。");\r
+\r
+ connected = true;\r
+ }\r
+\r
+ /**\r
+ * Sessionの初期化を行います。\r
+ * タイムアウト値を設定したPropertiesをセットします。\r
+ */\r
+ private void initSession() {\r
+ Properties prop = new Properties();\r
+ // タイムアウトの設定\r
+ prop.put("mail.smtp.connectiontimeout", String.valueOf(connectionTimeout));\r
+ prop.put("mail.smtp.timeout", String.valueOf(readTimeout));\r
+ // mail.smtp.authプロパティの設定\r
+ if (username != null && !"".equals(username) && password != null && !"".equals(password)) {\r
+ prop.put("mail.smtp.auth", "true");\r
+ }\r
+ session = Session.getInstance(prop);\r
+ }\r
+\r
+ /**\r
+ * @see com.ozacc.mail.SendMailPro#disconnect()\r
+ */\r
+ public synchronized void disconnect() throws MailException {\r
+ if (connected) {\r
+ try {\r
+ log.debug("SMTPサーバ[" + host + "]との接続を切断します。");\r
+\r
+ // SMTPサーバとの接続を切断\r
+ transport.close();\r
+ connected = false;\r
+\r
+ log.debug("SMTPサーバ[" + host + "]との接続を切断しました。");\r
+ } catch (MessagingException ex) {\r
+ log.error("SMTPサーバ[" + host + "]との接続切断に失敗しました。", ex);\r
+ throw new MailException("SMTPサーバ[" + host + "]との接続切断に失敗しました。");\r
+ } finally {\r
+ // グローバルReturn-Pathの解除\r
+ releaseReturnPath(false);\r
+ }\r
+ } else {\r
+ log.warn("SMTPサーバ[" + host + "]との接続が確立されていない状態で、接続の切断がリクエストされました。");\r
+ }\r
+ }\r
+\r
+ /**\r
+ * ReturnPathをセットします。\r
+ * \r
+ * @param returnPath\r
+ */\r
+ private void putOnReturnPath(String returnPath) {\r
+ if (returnPath != null) {\r
+ session.getProperties().put(RETURN_PATH_KEY, returnPath);\r
+ log.debug("Return-Path[" + returnPath + "]を設定しました。");\r
+ }\r
+ }\r
+\r
+ /**\r
+ * ReturnPathの設定をクリアします。\r
+ * <p>\r
+ * setGlobalReturnPathAgainがtrueに指定されている場合、一旦Return-Path設定をクリアした後に、\r
+ * グローバルなReturn-Path(setReturnPath()メソッドで、このインスタンスにセットされたReturn-Pathアドレス)を設定します。\r
+ * グローバルなReturn-PathがセットされていなければReturn-Pathはクリアされたままになります。\r
+ * <p>\r
+ * クリアされた状態でsend()メソッドが実行されると、Fromの値がReturn-Pathに使用されます。\r
+ * \r
+ * @param setGlobalReturnPathAgain Return-Path設定をクリアした後、再度グローバルなReturn-Pathをセットする場合 true\r
+ */\r
+ private void releaseReturnPath(boolean setGlobalReturnPathAgain) {\r
+ session.getProperties().remove(RETURN_PATH_KEY);\r
+ log.debug("Return-Path設定をクリアしました。");\r
+\r
+ if (setGlobalReturnPathAgain && this.returnPath != null) {\r
+ putOnReturnPath(this.returnPath);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * @see com.ozacc.mail.SendMailPro#send(javax.mail.internet.MimeMessage)\r
+ */\r
+ public void send(MimeMessage mimeMessage) throws MailException {\r
+ Address[] addresses;\r
+ try {\r
+ addresses = mimeMessage.getAllRecipients();\r
+ } catch (MessagingException ex) {\r
+ log.error("メールの送信に失敗しました。", ex);\r
+ throw new MailSendException("メールの送信に失敗しました。", ex);\r
+ }\r
+ processSend(mimeMessage, addresses);\r
+ }\r
+\r
+ /**\r
+ * @param mimeMessage \r
+ */\r
+ private void processSend(MimeMessage mimeMessage, Address[] addresses) {\r
+ if (!connected) {\r
+ log.error("SMTPサーバへの接続が確立されていません。");\r
+ throw new NotConnectedException("SMTPサーバへの接続が確立されていません。");\r
+ }\r
+\r
+ try {\r
+ // 送信日時をセット\r
+ mimeMessage.setSentDate(new Date());\r
+ mimeMessage.saveChanges();\r
+ // 送信\r
+ log.debug("メールを送信します。");\r
+ transport.sendMessage(mimeMessage, addresses);\r
+ log.debug("メールを送信しました。");\r
+ } catch (MessagingException ex) {\r
+ log.error("メールの送信に失敗しました。", ex);\r
+ throw new MailSendException("メールの送信に失敗しました。", ex);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * @see com.ozacc.mail.SendMailPro#send(com.ozacc.mail.Mail)\r
+ */\r
+ public void send(Mail mail) throws MailException {\r
+ if (mail.getReturnPath() != null) {\r
+ sendMailWithReturnPath(mail);\r
+ } else {\r
+ sendMail(mail);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * 指定されたMailからMimeMessageを生成し、send(MimeMessage)メソッドに渡します。\r
+ * \r
+ * @param mail\r
+ * @throws MailException\r
+ */\r
+ private void sendMail(Mail mail) throws MailException {\r
+ // MimeMessageの生成\r
+ MimeMessage message = createMimeMessage();\r
+ if (isMessageIdCustomized()) {\r
+ mail.addHeader("Message-ID", ((OMLMimeMessage)message).getMessageId());\r
+ }\r
+ MimeMessageBuilder builder = new MimeMessageBuilder(message, charset);\r
+ try {\r
+ builder.buildMimeMessage(mail);\r
+ } catch (UnsupportedEncodingException e) {\r
+ throw new MailBuildException("サポートされていない文字コードが指定されました。", e);\r
+ } catch (MessagingException e) {\r
+ throw new MailBuildException("MimeMessageの生成に失敗しました。", e);\r
+ }\r
+ // 送信\r
+ if (mail.getEnvelopeTo().length > 0) {\r
+ log.debug("メールはenvelope-toアドレスに送信されます。");\r
+ processSend(message, mail.getEnvelopeTo());\r
+ } else {\r
+ send(message);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * 指定されたMailにセットされたReturn-Pathを設定して、メールを送信します。\r
+ * 同期メソッドです。\r
+ * \r
+ * @param mail\r
+ * @throws MailException\r
+ */\r
+ private synchronized void sendMailWithReturnPath(Mail mail) throws MailException {\r
+ putOnReturnPath(mail.getReturnPath().getAddress());\r
+\r
+ sendMail(mail);\r
+\r
+ releaseReturnPath(true);\r
+ }\r
+\r
+ /**\r
+ * 新しいMimeMessageオブジェクトを生成します。\r
+ * \r
+ * @return 新しいMimeMessageオブジェクト\r
+ */\r
+ public MimeMessage createMimeMessage() {\r
+ if (isMessageIdCustomized()) {\r
+ return new OMLMimeMessage(session, messageId);\r
+ }\r
+ return new MimeMessage(session);\r
+ }\r
+\r
+ /**\r
+ * Message-Idヘッダのドメイン部分を独自にセットしているかどうか判定します。\r
+ * \r
+ * @return Message-Idヘッダのドメイン部分を独自にセットしている場合 true\r
+ */\r
+ private boolean isMessageIdCustomized() {\r
+ return messageId != null;\r
+ }\r
+\r
+ /**\r
+ * @return Sessionインスタンス\r
+ */\r
+ protected Session getSession() {\r
+ return session;\r
+ }\r
+\r
+ /**\r
+ * エンコーディングに使用する文字コードを返します。\r
+ * \r
+ * @return エンコーディングに使用する文字コード\r
+ */\r
+ public String getCharset() {\r
+ return charset;\r
+ }\r
+\r
+ /**\r
+ * メールの件名や本文のエンコーディングに使用する文字コードを指定します。\r
+ * デフォルトは ISO-2022-JP です。\r
+ * <p>\r
+ * 日本語環境で利用する場合は通常変更する必要はありません。\r
+ * \r
+ * @param charset エンコーディングに使用する文字コード\r
+ */\r
+ public void setCharset(String charset) {\r
+ this.charset = charset;\r
+ }\r
+\r
+ /**\r
+ * @return Returns the host.\r
+ */\r
+ public String getHost() {\r
+ return host;\r
+ }\r
+\r
+ /**\r
+ * SMTPサーバのホスト名、またはIPアドレスをセットします。\r
+ * デフォルトは localhost です。\r
+ * \r
+ * @param host SMTPサーバのホスト名、またはIPアドレス\r
+ */\r
+ public void setHost(String host) {\r
+ this.host = host;\r
+ }\r
+\r
+ /**\r
+ * @return SMTPサーバ認証パスワード\r
+ */\r
+ public String getPassword() {\r
+ return password;\r
+ }\r
+\r
+ /**\r
+ * SMTPサーバの接続認証が必要な場合にパスワードをセットします。\r
+ * \r
+ * @param password SMTPサーバ認証パスワード\r
+ */\r
+ public void setPassword(String password) {\r
+ this.password = password;\r
+ }\r
+\r
+ /**\r
+ * @return SMTPサーバのポート番号\r
+ */\r
+ public int getPort() {\r
+ return port;\r
+ }\r
+\r
+ /**\r
+ * SMTPサーバのポート番号をセットします。\r
+ * \r
+ * @param port SMTPサーバのポート番号\r
+ */\r
+ public void setPort(int port) {\r
+ this.port = port;\r
+ }\r
+\r
+ /**\r
+ * プロトコルを返します。\r
+ * \r
+ * @return プロトコル\r
+ */\r
+ public String getProtocol() {\r
+ return protocol;\r
+ }\r
+\r
+ /**\r
+ * プロトコルをセットします。デフォルトは「smtp」。\r
+ * \r
+ * @param protocol プロトコル\r
+ */\r
+ public void setProtocol(String protocol) {\r
+ this.protocol = protocol;\r
+ }\r
+\r
+ /**\r
+ * @return Return-Pathアドレス\r
+ */\r
+ public String getReturnPath() {\r
+ return returnPath;\r
+ }\r
+\r
+ /**\r
+ * Return-Pathアドレスをセットします。\r
+ * <p>\r
+ * 送信するMailインスタンスに指定されたFromアドレス以外のアドレスをReturn-Pathとしたい場合に使用します。\r
+ * ここでセットされたReturn-Pathより、MailインスタンスにセットされたReturn-Pathが優先されます。\r
+ * \r
+ * @param returnPath Return-Pathアドレス\r
+ */\r
+ public void setReturnPath(String returnPath) {\r
+ this.returnPath = returnPath;\r
+ }\r
+\r
+ /**\r
+ * @return SMTPサーバ認証ユーザ名\r
+ */\r
+ public String getUsername() {\r
+ return username;\r
+ }\r
+\r
+ /**\r
+ * SMTPサーバの接続認証が必要な場合にユーザ名をセットします。\r
+ * \r
+ * @param username SMTPサーバ認証ユーザ名\r
+ */\r
+ public void setUsername(String username) {\r
+ this.username = username;\r
+ }\r
+\r
+ /**\r
+ * 生成されるMimeMessageに付けられるMessage-Idヘッダのドメイン部分を指定します。<br>\r
+ * 指定されない場合(nullや空文字列の場合)は、JavaMailがMessage-Idヘッダを生成します。\r
+ * JavaMailが生成する「JavaMail.実行ユーザ名@ホスト名」のMessage-Idを避けたい場合に、このメソッドを使用します。\r
+ * <p>\r
+ * messageIdプロパティがセットされている場合、Mailから生成されるMimeMessageのMessage-Idには\r
+ * <code>タイムスタンプ + ランダムに生成される16桁の数値 + ここでセットされた値</code>\r
+ * が使用されます。\r
+ * <p>\r
+ * 生成されるMessage-Idの例。 (実際の数値部分は送信メール毎に変わります)<ul>\r
+ * <li>messageIdに'example.com'を指定した場合・・・1095714924963.5619528074501343@example.com</li>\r
+ * <li>messageIdに'@example.com'を指定した場合・・・1095714924963.5619528074501343@example.com (上と同じ)</li>\r
+ * <li>messageIdに'OML@example.com'を指定した場合・・・1095714924963.5619528074501343.OML@example.com</li>\r
+ * <li>messageIdに'.OML@example.com'を指定した場合・・・1095714924963.5619528074501343.OML@example.com (上と同じ)</li>\r
+ * </ul>\r
+ * <p>\r
+ * <strong>注:</strong> このMessage-Idは<code>send(Mail)</code>か<code>send(Mail[])</code>メソッドが呼びだれた時にのみ有効です。MimeMessageを直接送信する場合には適用されません。\r
+ * \r
+ * @param messageId メールに付けられるMessage-Idヘッダのドメイン部分\r
+ * @throws IllegalArgumentException @を複数含んだ文字列を指定した場合\r
+ */\r
+ public void setMessageId(String messageId) {\r
+ if (messageId == null || messageId.length() < 1) {\r
+ return;\r
+ }\r
+ String[] parts = messageId.split("@");\r
+ if (parts.length > 2) {\r
+ throw new IllegalArgumentException("messageIdプロパティに'@'を複数含むことはできません。[" + messageId\r
+ + "]");\r
+ }\r
+ this.messageId = messageId;\r
+ }\r
+\r
+ /**\r
+ * SMTPサーバとの接続タイムアウトをセットします。\r
+ * 単位はミリ秒。デフォルトは5,000ミリ秒(5秒)です。\r
+ * <p>\r
+ * -1を指定すると無限大になりますが、お薦めしません。\r
+ * \r
+ * @since 1.1.4\r
+ * @param connectionTimeout SMTPサーバとの接続タイムアウト\r
+ */\r
+ public void setConnectionTimeout(int connectionTimeout) {\r
+ this.connectionTimeout = connectionTimeout;\r
+ }\r
+\r
+ /**\r
+ * SMTPサーバへの送受信時のタイムアウトをセットします。\r
+ * 単位はミリ秒。デフォルトは5,000ミリ秒(5秒)です。\r
+ * <p>\r
+ * -1を指定すると無限大になりますが、お薦めしません。\r
+ * \r
+ * @since 1.1.4\r
+ * @param readTimeout SMTPサーバへの送受信時のタイムアウト\r
+ */\r
+ public void setReadTimeout(int readTimeout) {\r
+ this.readTimeout = readTimeout;\r
+ }\r
+}
\ No newline at end of file
--- /dev/null
+package com.ozacc.mail.impl;\r
+\r
+import org.apache.commons.logging.Log;\r
+import org.apache.commons.logging.LogFactory;\r
+import org.apache.velocity.app.Velocity;\r
+import org.apache.velocity.runtime.RuntimeServices;\r
+import org.apache.velocity.runtime.log.LogSystem;\r
+\r
+/**\r
+ * VelocityのログメッセージをCommonsLoggingを通して出力させるクラス。\r
+ * \r
+ * \r
+ * @see XMLVelocityMailBuilderImpl\r
+ * @see JDomXMLMailBuilder\r
+ * \r
+ * @since 1.0.3\r
+ * \r
+ * @author Tomohiro Otsuka\r
+ * @version $Id: VelocityLogSystem.java,v 1.2.2.1 2004/11/25 08:01:07 otsuka Exp $\r
+ */\r
+public class VelocityLogSystem implements LogSystem {\r
+\r
+ private static Log log = LogFactory.getLog(Velocity.class);\r
+\r
+ /**\r
+ * @see org.apache.velocity.runtime.log.LogSystem#init(org.apache.velocity.runtime.RuntimeServices)\r
+ */\r
+ public void init(RuntimeServices rsvc) throws Exception {\r
+ // do nothing\r
+ }\r
+\r
+ /**\r
+ * @see org.apache.velocity.runtime.log.LogSystem#logVelocityMessage(int, java.lang.String)\r
+ */\r
+ public void logVelocityMessage(int level, String message) {\r
+ switch (level) {\r
+ case DEBUG_ID:\r
+ log.debug(message);\r
+ break;\r
+ case INFO_ID:\r
+ log.info(message);\r
+ break;\r
+ case WARN_ID:\r
+ log.warn(message);\r
+ break;\r
+ case ERROR_ID:\r
+ log.error(message);\r
+ break;\r
+ }\r
+ }\r
+\r
+}
\ No newline at end of file
--- /dev/null
+package com.ozacc.mail.impl;\r
+\r
+import java.io.File;\r
+import java.io.IOException;\r
+\r
+import javax.xml.parsers.FactoryConfigurationError;\r
+\r
+import org.apache.commons.logging.Log;\r
+import org.apache.commons.logging.LogFactory;\r
+import org.w3c.dom.Document;\r
+import org.w3c.dom.Element;\r
+import org.xml.sax.SAXException;\r
+\r
+import com.ozacc.mail.Mail;\r
+import com.ozacc.mail.MailBuildException;\r
+import com.ozacc.mail.MultipleMailBuilder;\r
+\r
+/**\r
+ * メールデータのXMLファイルからMailインスタンスを生成するクラス。\r
+ * <p>\r
+ * ソースXMLを読み込む際に、DTDバリデーションが実行されますので妥当なXMLデータ(Valid XML Document)でなければいけません。\r
+ * メールデータXMLのDTDは、<a href="http://www.ozacc.com/library/dtd/ozacc-mail.dtd">http://www.ozacc.com/library/dtd/ozacc-mail.dtd</a>を参照。\r
+ * \r
+ * @since 1.0.1\r
+ * @author Tomohiro Otsuka\r
+ * @version $Id: XMLMailBuilderImpl.java,v 1.5.2.1 2005/01/21 22:16:31 otsuka Exp $\r
+ */\r
+public class XMLMailBuilderImpl extends AbstractXMLMailBuilder implements MultipleMailBuilder {\r
+\r
+ private static Log log = LogFactory.getLog(XMLMailBuilderImpl.class);\r
+\r
+ /**\r
+ * コンストラクタ。\r
+ */\r
+ public XMLMailBuilderImpl() {\r
+ super();\r
+ }\r
+\r
+ /**\r
+ * @see com.ozacc.mail.MailBuilder#buildMail(java.lang.String)\r
+ */\r
+ public Mail buildMail(String classPath) throws MailBuildException {\r
+ Document doc = retrieveDocument(classPath);\r
+ return buildMail(doc.getDocumentElement());\r
+ }\r
+\r
+ /**\r
+ * @see com.ozacc.mail.MailBuilder#buildMail(java.io.File)\r
+ */\r
+ public Mail buildMail(File file) throws MailBuildException {\r
+ Document doc = retrieveDocument(file);\r
+ return buildMail(doc.getDocumentElement());\r
+ }\r
+\r
+ /**\r
+ * @param classPath\r
+ * @return \r
+ * @throws MailBuildException\r
+ */\r
+ private Document retrieveDocument(String classPath) throws MailBuildException {\r
+ try {\r
+ return getDocumentFromClassPath(classPath);\r
+ } catch (SAXException e) {\r
+ throw new MailBuildException("XMLのパースに失敗しました。" + e.getMessage(), e);\r
+ } catch (IOException e) {\r
+ throw new MailBuildException("XMLファイルの読み込みに失敗しました。", e);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * @param file\r
+ * @return \r
+ * @throws MailBuildException\r
+ */\r
+ private Document retrieveDocument(File file) throws MailBuildException {\r
+ try {\r
+ return getDocumentFromFile(file);\r
+ } catch (SAXException e) {\r
+ throw new MailBuildException("XMLのパースに失敗しました。" + e.getMessage(), e);\r
+ } catch (IOException e) {\r
+ throw new MailBuildException("XMLファイルの読み込みに失敗しました。", e);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * 指定されたXMLのmail要素からMailインスタンスを生成します。\r
+ * \r
+ * @param root メールデータのmail要素\r
+ * @return 生成されたMailインスタンス\r
+ */\r
+ protected Mail buildMail(Element root) {\r
+ Mail mail = new Mail();\r
+ setReturnPath(root, mail);\r
+ setFrom(root, mail);\r
+ setRecipients(root, mail);\r
+ setReplyTo(root, mail);\r
+ setSubject(root, mail);\r
+ setText(root, mail);\r
+ setHtml(root, mail);\r
+ return mail;\r
+ }\r
+\r
+ /**\r
+ * @see com.ozacc.mail.MultipleMailBuilder#buildMail(java.lang.String, java.lang.String)\r
+ */\r
+ public Mail buildMail(String classPath, String mailId) throws MailBuildException {\r
+ if (mailId == null || "".equals(mailId)) {\r
+ throw new IllegalArgumentException("メールIDが指定されていません。");\r
+ }\r
+ Document doc = retrieveDocument(classPath);\r
+ if (Mail.DOCTYPE_PUBLIC.equals(doc.getDoctype().getPublicId())) {\r
+ throw new MailBuildException("指定されたクラスパスのXMLはシングルメールテンプレートです。[classPath='" + classPath\r
+ + "']");\r
+ }\r
+ Element mailElem = doc.getElementById(mailId);\r
+ return buildMail(mailElem);\r
+ }\r
+\r
+ /**\r
+ * @see com.ozacc.mail.MultipleMailBuilder#buildMail(java.io.File, java.lang.String)\r
+ */\r
+ public Mail buildMail(File file, String mailId) throws MailBuildException {\r
+ if (mailId == null || "".equals(mailId)) {\r
+ throw new IllegalArgumentException("メールIDが指定されていません。");\r
+ }\r
+ Document doc = retrieveDocument(file);\r
+ if (Mail.DOCTYPE_PUBLIC.equals(doc.getDoctype().getPublicId())) {\r
+ throw new MailBuildException("指定されたファイルのXMLはシングルメールテンプレートです。[filePath='"\r
+ + file.getAbsolutePath() + "']");\r
+ }\r
+ return buildMail(doc, mailId);\r
+ }\r
+\r
+ /**\r
+ * マルチプルメールテンプレートのXMLドキュメント上の指定されたIDが示すメールテンプレートからMailインスタンスを生成して返します。\r
+ * \r
+ * @param doc\r
+ * @param mailId\r
+ * @return 生成されたMailインスタンス\r
+ * @throws FactoryConfigurationError \r
+ */\r
+ protected Mail buildMail(Document doc, String mailId) throws FactoryConfigurationError {\r
+ Element mailElem = doc.getElementById(mailId);\r
+ if (mailElem == null) {\r
+ throw new MailBuildException("指定されたID[" + mailId + "]のメールデータは見つかりませんでした。");\r
+ }\r
+ log.debug(mailElem);\r
+ return buildMail(mailElem);\r
+ }\r
+\r
+}
\ No newline at end of file
--- /dev/null
+package com.ozacc.mail.impl;\r
+\r
+import java.io.File;\r
+import java.io.IOException;\r
+import java.io.StringReader;\r
+import java.io.StringWriter;\r
+import java.util.HashMap;\r
+import java.util.Map;\r
+import java.util.Properties;\r
+\r
+import javax.xml.parsers.DocumentBuilder;\r
+import javax.xml.transform.OutputKeys;\r
+import javax.xml.transform.Transformer;\r
+import javax.xml.transform.TransformerConfigurationException;\r
+import javax.xml.transform.TransformerException;\r
+import javax.xml.transform.TransformerFactory;\r
+import javax.xml.transform.TransformerFactoryConfigurationError;\r
+import javax.xml.transform.dom.DOMSource;\r
+import javax.xml.transform.stream.StreamResult;\r
+\r
+import org.apache.commons.logging.Log;\r
+import org.apache.commons.logging.LogFactory;\r
+import org.apache.velocity.VelocityContext;\r
+import org.apache.velocity.app.Velocity;\r
+import org.apache.velocity.exception.MethodInvocationException;\r
+import org.apache.velocity.exception.ParseErrorException;\r
+import org.apache.velocity.exception.ResourceNotFoundException;\r
+import org.apache.velocity.runtime.log.LogSystem;\r
+import org.w3c.dom.Document;\r
+import org.w3c.dom.Element;\r
+import org.xml.sax.InputSource;\r
+import org.xml.sax.SAXException;\r
+\r
+import com.ozacc.mail.Mail;\r
+import com.ozacc.mail.MailBuildException;\r
+import com.ozacc.mail.VelocityMultipleMailBuilder;\r
+\r
+/**\r
+ * XMLファイルを読み込み、Velocityと連携して動的にメールデータを生成し、そのデータからMailインスタンスを生成するクラス。\r
+ * \r
+ * @since 1.0.1\r
+ * @author Tomohiro Otsuka\r
+ * @version $Id: XMLVelocityMailBuilderImpl.java,v 1.4.2.4 2005/01/23 06:13:10 otsuka Exp $\r
+ */\r
+public class XMLVelocityMailBuilderImpl extends XMLMailBuilderImpl implements\r
+ VelocityMultipleMailBuilder {\r
+\r
+ private static Log log = LogFactory.getLog(XMLVelocityMailBuilderImpl.class);\r
+\r
+ private static String CACHE_KEY_SEPARATOR = "#";\r
+\r
+ private static String DEFAULT_MAIL_ID = "DEFAULT";\r
+\r
+ protected String charset = "UTF-8";\r
+\r
+ protected LogSystem velocityLogSystem = new VelocityLogSystem();\r
+\r
+ protected Map templateCache = new HashMap();\r
+\r
+ private boolean cacheEnabled = false;\r
+\r
+ protected boolean hasTemplateCache(String key) {\r
+ if (cacheEnabled) {\r
+ return templateCache.containsKey(key);\r
+ }\r
+ return false;\r
+ }\r
+\r
+ protected void putTemplateCache(String key, String templateXmlText) {\r
+ if (cacheEnabled) {\r
+ log.debug("テンプレートをキャッシュします。[key='" + key + "']");\r
+ templateCache.put(key, templateXmlText);\r
+ }\r
+ }\r
+\r
+ protected String getTemplateCache(String key) {\r
+ if (hasTemplateCache(key)) {\r
+ log.debug("テンプレートキャッシュを返します。[key='" + key + "']");\r
+ return (String)templateCache.get(key);\r
+ }\r
+ return null;\r
+ }\r
+\r
+ /**\r
+ * @see com.ozacc.mail.VelocityMailBuilder#clearCache()\r
+ */\r
+ public synchronized void clearCache() {\r
+ log.debug("テンプレートキャッシュをクリアします。");\r
+ templateCache.clear();\r
+ }\r
+\r
+ /**\r
+ * @see com.ozacc.mail.VelocityMailBuilder#isCacheEnabled()\r
+ */\r
+ public boolean isCacheEnabled() {\r
+ return cacheEnabled;\r
+ }\r
+\r
+ /**\r
+ * @see com.ozacc.mail.VelocityMailBuilder#setCacheEnabled(boolean)\r
+ */\r
+ public void setCacheEnabled(boolean cacheEnabled) {\r
+ if (!cacheEnabled) {\r
+ clearCache();\r
+ }\r
+ this.cacheEnabled = cacheEnabled;\r
+ }\r
+\r
+ /**\r
+ * @see com.ozacc.mail.VelocityMailBuilder#buildMail(java.lang.String, org.apache.velocity.VelocityContext)\r
+ */\r
+ public Mail buildMail(String classPath, VelocityContext context) throws MailBuildException {\r
+ String cacheKey = classPath + CACHE_KEY_SEPARATOR + DEFAULT_MAIL_ID;\r
+\r
+ String templateXmlText;\r
+ if (!hasTemplateCache(cacheKey)) {\r
+ Document doc;\r
+ try {\r
+ // Velocityマージ前のXMLではコメントを許可する\r
+ doc = getDocumentFromClassPath(classPath, false);\r
+ } catch (SAXException e) {\r
+ throw new MailBuildException("XMLのパースに失敗しました。" + e.getMessage(), e);\r
+ } catch (IOException e) {\r
+ throw new MailBuildException("XMLファイルの読み込みに失敗しました。", e);\r
+ }\r
+ templateXmlText = convertDocumentIntoString(doc.getDocumentElement());\r
+ putTemplateCache(cacheKey, templateXmlText);\r
+ } else {\r
+ templateXmlText = getTemplateCache(cacheKey);\r
+ }\r
+\r
+ try {\r
+ return build(templateXmlText, context);\r
+ } catch (Exception e) {\r
+ throw new MailBuildException("メールの生成に失敗しました。", e);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * @see com.ozacc.mail.VelocityMailBuilder#buildMail(java.io.File, org.apache.velocity.VelocityContext)\r
+ */\r
+ public Mail buildMail(File file, VelocityContext context) throws MailBuildException {\r
+ String cacheKey = file.getAbsolutePath() + CACHE_KEY_SEPARATOR + DEFAULT_MAIL_ID;\r
+\r
+ String templateXmlText;\r
+ if (!hasTemplateCache(cacheKey)) {\r
+ Document doc;\r
+ try {\r
+ // Velocityマージ前のXMLではコメントを許可する\r
+ doc = getDocumentFromFile(file, false);\r
+ } catch (SAXException e) {\r
+ throw new MailBuildException("XMLのパースに失敗しました。" + e.getMessage(), e);\r
+ } catch (IOException e) {\r
+ throw new MailBuildException("XMLファイルの読み込みに失敗しました。", e);\r
+ }\r
+ templateXmlText = convertDocumentIntoString(doc.getDocumentElement());\r
+ putTemplateCache(cacheKey, templateXmlText);\r
+ } else {\r
+ templateXmlText = getTemplateCache(cacheKey);\r
+ }\r
+\r
+ try {\r
+ return build(templateXmlText, context);\r
+ } catch (Exception e) {\r
+ throw new MailBuildException("メールの生成に失敗しました。", e);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * メールデータをVelocityContextとマージして生成されたXMLからMailインスタンスを生成します。\r
+ * \r
+ * @param templateXmlText メールデータのテンプレート\r
+ * @param context テンプレートにマージする内容を格納したVelocityContext\r
+ * @return VelocityContextをテンプレートにマージして生成されたXMLから生成されたMailインスタンス\r
+ * @throws TransformerFactoryConfigurationError\r
+ * @throws Exception\r
+ * @throws ParseErrorException\r
+ * @throws MethodInvocationException\r
+ * @throws ResourceNotFoundException\r
+ * @throws IOException\r
+ */\r
+ protected synchronized Mail build(String templateXmlText, VelocityContext context)\r
+ throws TransformerFactoryConfigurationError,\r
+ Exception,\r
+ ParseErrorException,\r
+ MethodInvocationException,\r
+ ResourceNotFoundException,\r
+ IOException {\r
+ if (log.isDebugEnabled()) {\r
+ log.debug("Source XML Mail Data\n" + templateXmlText);\r
+ }\r
+\r
+ Velocity.setProperty(Velocity.RUNTIME_LOG_LOGSYSTEM, velocityLogSystem);\r
+ Velocity.init();\r
+ StringWriter w = new StringWriter();\r
+ Velocity.evaluate(context, w, "XML Mail Data", templateXmlText);\r
+ StringReader reader = new StringReader(w.toString());\r
+\r
+ DocumentBuilder db = createDocumentBuilder();\r
+ InputSource source = new InputSource(reader);\r
+ Document newDoc = db.parse(source);\r
+\r
+ if (log.isDebugEnabled()) {\r
+ String newXmlContent = convertDocumentIntoString(newDoc.getDocumentElement());\r
+ log.debug("VelocityContext-merged XML Mail Data\n" + newXmlContent);\r
+ }\r
+\r
+ return buildMail(newDoc.getDocumentElement());\r
+ }\r
+\r
+ /**\r
+ * 指定されたDOM Documentを文字列に変換します。\r
+ * \r
+ * @param mailElement\r
+ * @return XMLドキュメントの文字列\r
+ * @throws TransformerFactoryConfigurationError \r
+ */\r
+ protected String convertDocumentIntoString(Element mailElement)\r
+ throws TransformerFactoryConfigurationError {\r
+ TransformerFactory tf = TransformerFactory.newInstance();\r
+ Transformer t;\r
+ try {\r
+ t = tf.newTransformer();\r
+ } catch (TransformerConfigurationException e) {\r
+ throw new MailBuildException(e.getMessage(), e);\r
+ }\r
+ t.setOutputProperties(getOutputProperties());\r
+\r
+ DOMSource source = new DOMSource(mailElement);\r
+ StringWriter w = new StringWriter();\r
+ StreamResult result = new StreamResult(w);\r
+ try {\r
+ t.transform(source, result);\r
+ } catch (TransformerException e) {\r
+ throw new MailBuildException(e.getMessage(), e);\r
+ }\r
+\r
+ return w.toString();\r
+ }\r
+\r
+ /**\r
+ * 出力プロパティを生成。\r
+ * @return 出力プロパティを設定したPropertiesインスタンス\r
+ */\r
+ protected Properties getOutputProperties() {\r
+ Properties p = new Properties();\r
+ p.put(OutputKeys.ENCODING, charset);\r
+ p.put(OutputKeys.DOCTYPE_PUBLIC, Mail.DOCTYPE_PUBLIC);\r
+ p.put(OutputKeys.DOCTYPE_SYSTEM, Mail.DOCTYPE_SYSTEM);\r
+ return p;\r
+ }\r
+\r
+ /**\r
+ * @see com.ozacc.mail.VelocityMultipleMailBuilder#buildMail(java.lang.String, org.apache.velocity.VelocityContext, java.lang.String)\r
+ */\r
+ public Mail buildMail(String classPath, VelocityContext context, String mailId)\r
+ throws MailBuildException {\r
+ if (mailId == null || "".equals(mailId)) {\r
+ throw new IllegalArgumentException("メールIDが指定されていません。");\r
+ }\r
+\r
+ String cacheKey = classPath + CACHE_KEY_SEPARATOR + mailId;\r
+\r
+ String templateXmlText;\r
+ if (!hasTemplateCache(cacheKey)) {\r
+ Document doc;\r
+ try {\r
+ // Velocityマージ前のXMLではコメントを許可する\r
+ doc = getDocumentFromClassPath(classPath, false);\r
+ } catch (SAXException e) {\r
+ throw new MailBuildException("XMLのパースに失敗しました。" + e.getMessage(), e);\r
+ } catch (IOException e) {\r
+ throw new MailBuildException("XMLファイルの読み込みに失敗しました。", e);\r
+ }\r
+ if (Mail.DOCTYPE_PUBLIC.equals(doc.getDoctype().getPublicId())) {\r
+ throw new MailBuildException("指定されたクラスパスのXMLはシングルメールテンプレートです。[classPath='"\r
+ + classPath + "']");\r
+ }\r
+ templateXmlText = getAndCacheTemplateText(doc, mailId, cacheKey);\r
+ } else {\r
+ templateXmlText = getTemplateCache(cacheKey);\r
+ }\r
+\r
+ try {\r
+ return build(templateXmlText, context);\r
+ } catch (Exception e) {\r
+ throw new MailBuildException("メールの生成に失敗しました。", e);\r
+ }\r
+ }\r
+\r
+ private String getAndCacheTemplateText(Document doc, String mailId, String cacheKey)\r
+ throws TransformerFactoryConfigurationError {\r
+ Element mailElem = doc.getElementById(mailId);\r
+ if (mailElem == null) {\r
+ throw new MailBuildException("指定されたID[" + mailId + "]のメールデータは見つかりませんでした。");\r
+ }\r
+ String templateXmlText = convertDocumentIntoString(mailElem);\r
+ putTemplateCache(cacheKey, templateXmlText);\r
+ return templateXmlText;\r
+ }\r
+\r
+ /**\r
+ * @see com.ozacc.mail.VelocityMultipleMailBuilder#buildMail(java.io.File, org.apache.velocity.VelocityContext, java.lang.String)\r
+ */\r
+ public Mail buildMail(File file, VelocityContext context, String mailId)\r
+ throws MailBuildException {\r
+ if (mailId == null || "".equals(mailId)) {\r
+ throw new IllegalArgumentException("メールIDが指定されていません。");\r
+ }\r
+\r
+ String cacheKey = file.getAbsolutePath() + CACHE_KEY_SEPARATOR + mailId;\r
+\r
+ String templateXmlText;\r
+ if (!hasTemplateCache(cacheKey)) {\r
+ Document doc;\r
+ try {\r
+ // Velocityマージ前のXMLではコメントを許可する\r
+ doc = getDocumentFromFile(file, false);\r
+ } catch (SAXException e) {\r
+ throw new MailBuildException("XMLのパースに失敗しました。" + e.getMessage(), e);\r
+ } catch (IOException e) {\r
+ throw new MailBuildException("XMLファイルの読み込みに失敗しました。", e);\r
+ }\r
+ if (Mail.DOCTYPE_PUBLIC.equals(doc.getDoctype().getPublicId())) {\r
+ throw new MailBuildException("指定されたファイルのXMLはシングルメールテンプレートです。[filePath='"\r
+ + file.getAbsolutePath() + "']");\r
+ }\r
+ templateXmlText = getAndCacheTemplateText(doc, mailId, cacheKey);\r
+ } else {\r
+ templateXmlText = getTemplateCache(cacheKey);\r
+ }\r
+\r
+ try {\r
+ return build(templateXmlText, context);\r
+ } catch (Exception e) {\r
+ throw new MailBuildException("メールの生成に失敗しました。", e);\r
+ }\r
+ }\r
+\r
+}
\ No newline at end of file
--- /dev/null
+<HTML>\r
+<BODY>\r
+<code>com.ozacc.mail</code>¥Ñ¥Ã¥±¡¼¥¸¤Î¥¤¥ó¥¿¡¼¥Õ¥§¡¼¥¹¤ò¼ÂÁõ¤·¤¿¥¯¥é¥¹¤òÄ󶡤·¤Þ¤¹¡£\r
+<p>\r
+¤³¤Î¥Ñ¥Ã¥±¡¼¥¸¤Ë¤Ï¡¢¼ÂÁõ¥¯¥é¥¹¤¬ÍøÍѤ¹¤ëÊä½õŪÌò³ä¤Î¥¯¥é¥¹¤¬´Þ¤Þ¤ì¤Æ¤¤¤Þ¤¹¡£Ä̾¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¤¬ÍøÍѤ¹¤ë¥¯¥é¥¹¤Ï¡¢²¼É½¤Ç¼¨¤¹¥¤¥ó¥¿¡¼¥Õ¥§¡¼¥¹¤Î¼ÂÁõ¥¯¥é¥¹¤À¤±¤Ç¤¹¡£\r
+\r
+<table cellpadding="5" cellspacing="1" border="1">\r
+ <thead>\r
+ <tr>\r
+ <th>¥¤¥ó¥¿¡¼¥Õ¥§¡¼¥¹</th><th>¼ÂÁõ¥¯¥é¥¹</th>\r
+ </tr>\r
+ </thead>\r
+ <tbody>\r
+ <tr>\r
+ <td>SendMail</td><td>SendMailImpl</td>\r
+ </tr>\r
+ <tr>\r
+ <td>SendMailPro</td><td>SendMailProImpl</td>\r
+ </tr>\r
+ <tr>\r
+ <td rowspan="2">MailBuilder</td><td>XMLMailBuilderImpl</td>\r
+ </tr>\r
+ <tr>\r
+ <td>JDomXMLMailBuilder</td>\r
+ </tr>\r
+ <tr>\r
+ <td rowspan="2">VelocityMailBuilder</td><td>XMLVelocityMailBuilderImpl</td>\r
+ </tr>\r
+ <tr>\r
+ <td>JDomXMLMailBuilder</td>\r
+ </tr>\r
+ </tbody>\r
+</table>\r
+ \r
+\r
+\r
+</BODY>\r
+</HTML>
\ No newline at end of file
--- /dev/null
+package com.ozacc.mail.mailet;\r
+\r
+import com.ozacc.mail.fetch.ReceivedMail;\r
+\r
+/**\r
+ * Mailetインターフェース。\r
+ * \r
+ * @since 1.2\r
+ * @author Tomohiro Otsuka\r
+ * @version $Id: Mailet.java,v 1.1.2.2 2005/01/23 06:46:54 otsuka Exp $\r
+ */\r
+public interface Mailet {\r
+\r
+ void service(ReceivedMail mail);\r
+\r
+}
\ No newline at end of file
--- /dev/null
+package com.ozacc.mail.mailet;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Iterator;\r
+import java.util.List;\r
+\r
+import com.ozacc.mail.fetch.FetchMailPro;\r
+import com.ozacc.mail.fetch.ReceivedMail;\r
+\r
+/**\r
+ * メールの受信とMailetの起動を行うクラス。\r
+ * \r
+ * @since 1.2\r
+ * @author Tomohiro Otsuka\r
+ * @version $Id: MailetRunner.java,v 1.1.2.5 2005/04/19 14:51:55 otsuka Exp $\r
+ */\r
+public class MailetRunner {\r
+\r
+ private List mailetWrapperList;\r
+\r
+ private FetchMailPro fetchMailPro;\r
+\r
+ /**\r
+ * コンストラクタ。\r
+ */\r
+ public MailetRunner() {\r
+ mailetWrapperList = new ArrayList();\r
+ }\r
+\r
+ /**\r
+ * メール受信とMailetの起動を行います。\r
+ */\r
+ public void run() {\r
+ fetchMailPro.connect();\r
+ try {\r
+ int count = fetchMailPro.getMailCount();\r
+ for (int i = 1; i <= count; i++) {\r
+ ReceivedMail mail = fetchMailPro.getMail(i);\r
+ processMail(mail);\r
+ }\r
+ } finally {\r
+ fetchMailPro.disconnect();\r
+ }\r
+ }\r
+\r
+ /**\r
+ * 指定された受信メールに対してMailetを適用します。\r
+ * \r
+ * @param mail MailetWrapperに渡す受信メール\r
+ */\r
+ private void processMail(ReceivedMail mail) {\r
+ for (Iterator itr = mailetWrapperList.iterator(); itr.hasNext();) {\r
+ MailetWrapper mailetWrapper = (MailetWrapper)itr.next();\r
+ mailetWrapper.execute(mail);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * メールの受信に使用するFetchMailProインターフェースの実装インスタンスをセットします。\r
+ * \r
+ * @param fetchMailPro FetchMailProインターフェースの実装インスタンス\r
+ */\r
+ public void setFetchMailPro(FetchMailPro fetchMailPro) {\r
+ this.fetchMailPro = fetchMailPro;\r
+ }\r
+\r
+ /**\r
+ * 実行するMailetのMailetWrapperリストをセットします。\r
+ * \r
+ * @param mailetWrapperList 実行するMailetのMailetWrapperリスト\r
+ */\r
+ public void setMailetWrapperList(List mailetWrapperList) {\r
+ this.mailetWrapperList = mailetWrapperList;\r
+ }\r
+}
\ No newline at end of file
--- /dev/null
+package com.ozacc.mail.mailet;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Iterator;\r
+import java.util.List;\r
+\r
+import com.ozacc.mail.fetch.ReceivedMail;\r
+\r
+/**\r
+ * MailetインスタンスとMatcherインスタンスのリストを持つMailetの実行単位となるクラス。\r
+ * \r
+ * @since 1.2\r
+ * @author Tomohiro Otsuka\r
+ * @version $Id: MailetWrapper.java,v 1.1.2.2 2005/01/23 06:47:01 otsuka Exp $\r
+ */\r
+public class MailetWrapper {\r
+\r
+ private Mailet mailet;\r
+\r
+ private List matcherList;\r
+\r
+ /**\r
+ * コンストラクタ。\r
+ */\r
+ public MailetWrapper() {\r
+ matcherList = new ArrayList();\r
+ }\r
+\r
+ /**\r
+ * コンストラクタ。\r
+ * \r
+ * @param mailet Mailetインスタンス\r
+ * @param matcherList Matcherインスタンスのリスト\r
+ */\r
+ public MailetWrapper(Mailet mailet, List matcherList) {\r
+ this();\r
+ this.mailet = mailet;\r
+ this.matcherList = matcherList;\r
+ }\r
+\r
+ /**\r
+ * リストされているMatcherの条件をクリアしたMailetを実行します。\r
+ * \r
+ * @param mail 受信メール\r
+ */\r
+ public void execute(ReceivedMail mail) {\r
+ for (Iterator itr = matcherList.iterator(); itr.hasNext();) {\r
+ Matcher m = (Matcher)itr.next();\r
+ if (!m.match(mail)) {\r
+ return;\r
+ }\r
+ }\r
+ mailet.service(mail);\r
+ }\r
+\r
+ /**\r
+ * Mailetインスタンスを返します。\r
+ * \r
+ * @return Mailetインスタンス\r
+ */\r
+ public Mailet getMailet() {\r
+ return mailet;\r
+ }\r
+\r
+ /**\r
+ * Mailetインスタンスをセットします。\r
+ * \r
+ * @param mailet Mailetインスタンス\r
+ */\r
+ public void setMailet(Mailet mailet) {\r
+ this.mailet = mailet;\r
+ }\r
+\r
+ /**\r
+ * Matcherインスタンスのリストを返します。\r
+ * \r
+ * @return Matcherインスタンスのリスト\r
+ */\r
+ public List getMatcherList() {\r
+ return matcherList;\r
+ }\r
+\r
+ /**\r
+ * Matcherインスタンスのリストをセットします。\r
+ * \r
+ * @param matcherList Matcherインスタンスのリスト\r
+ */\r
+ public void setMatcherList(List matcherList) {\r
+ this.matcherList = matcherList;\r
+ }\r
+}
\ No newline at end of file
--- /dev/null
+package com.ozacc.mail.mailet;\r
+\r
+import com.ozacc.mail.fetch.ReceivedMail;\r
+\r
+/**\r
+ * Mailetの実行条件を満たすかどうか判定するインターフェース。\r
+ * \r
+ * @since 1.2\r
+ * @author Tomohiro Otsuka\r
+ * @version $Id: Matcher.java,v 1.1.2.2 2005/01/23 06:47:01 otsuka Exp $\r
+ */\r
+public interface Matcher {\r
+\r
+ /**\r
+ * 指定された受信メールがMailet実行条件を満たすかどうか判定します。\r
+ * \r
+ * @param mail 受信メール\r
+ * @return 受信メールMailet実行条件を満たす場合 true\r
+ */\r
+ boolean match(ReceivedMail mail);\r
+\r
+}
\ No newline at end of file
--- /dev/null
+<HTML>\r
+<BODY>\r
+Mailet¥¤¥ó¥¿¡¼¥Õ¥§¡¼¥¹¤È¼Â¹Ô´Ä¶¤òÄ󶡤·¤Þ¤¹¡£\r
+</BODY>\r
+</HTML>
\ No newline at end of file
--- /dev/null
+package com.ozacc.mail.mock;\r
+\r
+/**\r
+ * MockSendMailで期待メールと送信メールが一致しなかった時にスローされる非チェック例外。\r
+ * \r
+ * @since 1.0\r
+ * @author Tomohiro Otsuka\r
+ * @version $Id: AssertionFailedException.java,v 1.2 2004/09/13 07:07:42 otsuka Exp $\r
+ */\r
+public class AssertionFailedException extends RuntimeException {\r
+\r
+ public AssertionFailedException() {\r
+ super();\r
+ }\r
+\r
+ public AssertionFailedException(String message) {\r
+ super(message);\r
+ }\r
+\r
+ public AssertionFailedException(Throwable cause) {\r
+ super(cause);\r
+ }\r
+\r
+ public AssertionFailedException(String message, Throwable cause) {\r
+ super(message, cause);\r
+ }\r
+\r
+}
\ No newline at end of file
--- /dev/null
+package com.ozacc.mail.mock;\r
+\r
+import javax.mail.internet.InternetAddress;\r
+\r
+import com.ozacc.mail.Mail;\r
+\r
+/**\r
+ * メールが同値であることを調べるメソッドを提供。\r
+ * <p>\r
+ * <strong>注:</strong> 添付ファイルは比較対象になりません。\r
+ * \r
+ * @since 1.1\r
+ * \r
+ * @author Tomohiro Otsuka\r
+ * @version $Id: EqualityCheck.java,v 1.3.2.1 2004/11/25 08:01:18 otsuka Exp $\r
+ */\r
+public class EqualityCheck {\r
+\r
+ private EqualityCheck() {}\r
+\r
+ /**\r
+ * expectedとsentのメール内容が同一であるかどうかを判定します。<br>\r
+ * MultipartMailのインスタンスも指定できます。但し、添付ファイルはチェックされません。\r
+ * \r
+ * @param expected\r
+ * @param sent\r
+ * @return expectedとsentのメール内容が同一である場合 true\r
+ */\r
+ public static boolean equals(Mail expected, Mail sent) {\r
+ boolean mockMode = (expected instanceof MockMail);\r
+\r
+ // マルチパートメールの場合\r
+ if (expected.isMultipartMail()) {\r
+\r
+ // HTML\r
+ if (!mockMode) {\r
+ if ((expected.getHtmlText() == null && sent.getHtmlText() != null)\r
+ || (expected.getHtmlText() != null && sent.getHtmlText() == null)\r
+ || (!expected.getHtmlText().equals(sent.getHtmlText()))) {\r
+ return false;\r
+ }\r
+ } else if (mockMode && expected.getHtmlText() != null) {\r
+ if (!expected.getHtmlText().equals(sent.getHtmlText())) {\r
+ return false;\r
+ }\r
+ }\r
+ }\r
+\r
+ // Return-Path\r
+ if (!mockMode || (mockMode && expected.getReturnPath() != null)) {\r
+ if (expected.getReturnPath() != null && sent.getReturnPath() != null) {\r
+ if (!expected.getReturnPath().equals(sent.getReturnPath())) {\r
+ return false;\r
+ }\r
+ } else if ((expected.getReturnPath() != null && sent.getReturnPath() == null)\r
+ || (expected.getReturnPath() == null && sent.getReturnPath() != null)) {\r
+ return false;\r
+ }\r
+ }\r
+\r
+ // From\r
+ if (!mockMode || (mockMode && expected.getFrom() != null)) {\r
+ if (expected.getFrom() != null && sent.getFrom() != null) {\r
+ if (!equals(expected.getFrom(), sent.getFrom())) {\r
+ return false;\r
+ }\r
+ } else if ((expected.getFrom() != null && sent.getFrom() == null)\r
+ || (expected.getFrom() == null && sent.getFrom() != null)) {\r
+ return false;\r
+ }\r
+ }\r
+\r
+ // to\r
+ InternetAddress[] expectedAddresses = expected.getTo();\r
+ InternetAddress[] sentAddresses = sent.getTo();\r
+ if (!mockMode || (mockMode && expectedAddresses.length > 0)) {\r
+ if (expectedAddresses.length != sentAddresses.length) {\r
+ return false;\r
+ }\r
+ for (int i = 0; i < expectedAddresses.length; i++) {\r
+ if (!equals(expectedAddresses[i], sentAddresses[i])) {\r
+ return false;\r
+ }\r
+ }\r
+ }\r
+\r
+ // cc\r
+ expectedAddresses = expected.getCc();\r
+ sentAddresses = sent.getCc();\r
+ if (!mockMode || (mockMode && expectedAddresses.length > 0)) {\r
+ if (expectedAddresses.length != sentAddresses.length) {\r
+ return false;\r
+ }\r
+ for (int i = 0; i < expectedAddresses.length; i++) {\r
+ if (!equals(expectedAddresses[i], sentAddresses[i])) {\r
+ return false;\r
+ }\r
+ }\r
+ }\r
+\r
+ // bcc\r
+ expectedAddresses = expected.getBcc();\r
+ sentAddresses = sent.getBcc();\r
+ if (!mockMode || (mockMode && expectedAddresses.length > 0)) {\r
+ if (expectedAddresses.length != sentAddresses.length) {\r
+ return false;\r
+ }\r
+ for (int i = 0; i < expectedAddresses.length; i++) {\r
+ if (!equals(expectedAddresses[i], sentAddresses[i])) {\r
+ return false;\r
+ }\r
+ }\r
+ }\r
+\r
+ // Reply-To\r
+ if (!mockMode || (mockMode && expected.getReplyTo() != null)) {\r
+ if (expected.getReplyTo() != null && sent.getReplyTo() != null) {\r
+ if (!equals(expected.getReplyTo(), sent.getReplyTo())) {\r
+ return false;\r
+ }\r
+ } else if ((expected.getReplyTo() != null && sent.getReplyTo() == null)\r
+ || (expected.getReplyTo() == null && sent.getReplyTo() != null)) {\r
+ return false;\r
+ }\r
+ }\r
+\r
+ // 件名\r
+ if (!mockMode || (mockMode && expected.getSubject().length() > 0)) {\r
+ if (!expected.getSubject().equals(sent.getSubject())) {\r
+ return false;\r
+ }\r
+ }\r
+\r
+ // 本文\r
+ if (!mockMode || (mockMode && expected.getText().length() > 0)) {\r
+ if (!expected.getText().equals(sent.getText())) {\r
+ return false;\r
+ }\r
+ }\r
+\r
+ return true;\r
+ }\r
+\r
+ /**\r
+ * 指定された二つのInternetAddressインスタンスが等しいかどうか判定します。\r
+ * <p>\r
+ * InternetAddress#equals()メソッドでは、メールアドレスしか検査しないため、\r
+ * このメソッドではInternetAddressに名前が含まれている場合、その名前も\r
+ * 等しいかどうか検査します。\r
+ * \r
+ * @since 1.1.3\r
+ * @param a 比較するInternetAddressインスタンス\r
+ * @param b 比較するInternetAddressインスタンス\r
+ * @return 二つのInternetAddressインスタンスが等しい場合 true\r
+ */\r
+ public static boolean equals(InternetAddress a, InternetAddress b) {\r
+ if (a.equals(b)) {\r
+ if (a.getPersonal() != null || b.getPersonal() != null) {\r
+ return a.getPersonal().equals(b.getPersonal());\r
+ } else {\r
+ return true;\r
+ }\r
+ }\r
+ return false;\r
+ }\r
+}
\ No newline at end of file
--- /dev/null
+package com.ozacc.mail.mock;\r
+\r
+import java.util.ArrayList;\r
+import java.util.List;\r
+\r
+import org.apache.commons.logging.Log;\r
+import org.apache.commons.logging.LogFactory;\r
+\r
+import com.ozacc.mail.MailException;\r
+import com.ozacc.mail.fetch.FetchMail;\r
+import com.ozacc.mail.fetch.ReceivedMail;\r
+\r
+/**\r
+ * FetchMailImplクラスのMock。<br>\r
+ * <code>setupGetMails()</code>メソッドで<code>ReceivedMail</code>インスタンスをセットすると、<code>getMails()</code>メソッドがそのインスタンスを返します。\r
+ * \r
+ * @since 1.2\r
+ * @author Tomohiro Otsuka\r
+ * @version $Id: MockFetchMail.java,v 1.1.2.3 2005/02/05 09:25:25 otsuka Exp $\r
+ */\r
+public class MockFetchMail implements FetchMail {\r
+\r
+ private static Log log = LogFactory.getLog(MockFetchMail.class);\r
+\r
+ /** デフォルトのSMTPサーバ。「localhost」 */\r
+ public static final String DEFAULT_HOST = "localhost";\r
+\r
+ /** デフォルトのプロトコル。「pop3」 */\r
+ public static final String DEFAULT_PROTOCOL = "pop3";\r
+\r
+ /**\r
+ * デフォルトのポート。「-1」<br>\r
+ * -1はプロトコルに応じた適切なポートを設定する特別な値。\r
+ */\r
+ public static final int DEFAULT_PORT = -1;\r
+\r
+ private static final String INBOX_NAME = "INBOX";\r
+\r
+ private String host = DEFAULT_HOST;\r
+\r
+ private String protocol = DEFAULT_PROTOCOL;\r
+\r
+ private int port = DEFAULT_PORT;\r
+\r
+ private String username;\r
+\r
+ private String password;\r
+\r
+ private List receivedMails;\r
+\r
+ /**\r
+ * コンストラクタ。\r
+ */\r
+ public MockFetchMail() {\r
+ super();\r
+ receivedMails = new ArrayList();\r
+ }\r
+\r
+ /**\r
+ * <code>MockFetchMail</code>の<code>getMails()</code>メソッドが返す\r
+ * <code>ReceivedMail</code>インスタンスをセットします。\r
+ * \r
+ * @param mail <code>getMails()</code>メソッドが返す<code>ReceivedMail</code>インスタンス\r
+ */\r
+ public void setupGetMails(ReceivedMail mail) {\r
+ receivedMails.add(mail);\r
+ }\r
+\r
+ /**\r
+ * <code>MockFetchMail</code>の<code>getMails()</code>メソッドが返す\r
+ * <code>ReceivedMail</code>インスタンスをセットします。\r
+ * \r
+ * @param mails <code>getMails()</code>メソッドが返す<code>ReceivedMail</code>インスタンス配列\r
+ */\r
+ public void setupGetMails(ReceivedMail[] mails) {\r
+ for (int i = 0; i < mails.length; i++) {\r
+ ReceivedMail mail = mails[i];\r
+ setupGetMails(mail);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * @see com.ozacc.mail.fetch.FetchMail#getMails()\r
+ */\r
+ public ReceivedMail[] getMails() throws MailException {\r
+ log.debug(protocol.toUpperCase() + "サーバ[" + host + "]に接続しるフリ。");\r
+ log.debug(protocol.toUpperCase() + "サーバ[" + host + "]に接続したフリ。");\r
+\r
+ if (receivedMails.size() > 0) {\r
+ log.debug(receivedMails.size() + "通のメールを受信するフリ。");\r
+ } else {\r
+ log.debug("受信するフリをするメールはありません。");\r
+ }\r
+ try {\r
+ return (ReceivedMail[])receivedMails.toArray(new ReceivedMail[receivedMails.size()]);\r
+ } finally {\r
+ log.debug(protocol.toUpperCase() + "サーバ[" + host + "]との接続を切断するフリ。");\r
+ log.debug(protocol.toUpperCase() + "サーバ[" + host + "]との接続を切断したフリ。");\r
+ }\r
+ }\r
+\r
+ /**\r
+ * @see com.ozacc.mail.fetch.FetchMail#getMails(boolean)\r
+ */\r
+ public ReceivedMail[] getMails(boolean delete) throws MailException {\r
+ ReceivedMail[] result = getMails();\r
+ if (delete) {\r
+ receivedMails.clear();\r
+ }\r
+ return result;\r
+ }\r
+\r
+ /**\r
+ * @return Returns the host.\r
+ */\r
+ public String getHost() {\r
+ return host;\r
+ }\r
+\r
+ /**\r
+ * @param host The host to set.\r
+ */\r
+ public void setHost(String host) {\r
+ this.host = host;\r
+ }\r
+\r
+ /**\r
+ * @return Returns the password.\r
+ */\r
+ public String getPassword() {\r
+ return password;\r
+ }\r
+\r
+ /**\r
+ * @param password The password to set.\r
+ */\r
+ public void setPassword(String password) {\r
+ this.password = password;\r
+ }\r
+\r
+ /**\r
+ * @return Returns the port.\r
+ */\r
+ public int getPort() {\r
+ return port;\r
+ }\r
+\r
+ /**\r
+ * @param port The port to set.\r
+ */\r
+ public void setPort(int port) {\r
+ this.port = port;\r
+ }\r
+\r
+ /**\r
+ * @return Returns the protocol.\r
+ */\r
+ public String getProtocol() {\r
+ return protocol;\r
+ }\r
+\r
+ /**\r
+ * @param protocol The protocol to set.\r
+ */\r
+ public void setProtocol(String protocol) {\r
+ this.protocol = protocol;\r
+ }\r
+\r
+ /**\r
+ * @return Returns the username.\r
+ */\r
+ public String getUsername() {\r
+ return username;\r
+ }\r
+\r
+ /**\r
+ * @param username The username to set.\r
+ */\r
+ public void setUsername(String username) {\r
+ this.username = username;\r
+ }\r
+}
\ No newline at end of file
--- /dev/null
+package com.ozacc.mail.mock;\r
+\r
+import java.util.ArrayList;\r
+import java.util.List;\r
+\r
+import javax.mail.internet.MimeMessage;\r
+\r
+import org.apache.commons.logging.Log;\r
+import org.apache.commons.logging.LogFactory;\r
+\r
+import com.ozacc.mail.MailException;\r
+import com.ozacc.mail.NotConnectedException;\r
+import com.ozacc.mail.fetch.FetchMailPro;\r
+import com.ozacc.mail.fetch.ReceivedMail;\r
+\r
+/**\r
+ * FetchMailProImplクラスのMock。\r
+ * \r
+ * @since 1.2\r
+ * @author Tomohiro Otsuka\r
+ * @version $Id: MockFetchMailPro.java,v 1.1.2.2 2005/04/10 05:22:34 otsuka Exp $\r
+ */\r
+public class MockFetchMailPro implements FetchMailPro {\r
+\r
+ private static Log log = LogFactory.getLog(MockFetchMailPro.class);\r
+\r
+ /** デフォルトのSMTPサーバ。「localhost」 */\r
+ public static final String DEFAULT_HOST = "localhost";\r
+\r
+ /** デフォルトのプロトコル。「pop3」 */\r
+ public static final String DEFAULT_PROTOCOL = "pop3";\r
+\r
+ /**\r
+ * デフォルトのポート。「-1」<br>\r
+ * -1はプロトコルに応じた適切なポートを設定する特別な値。\r
+ */\r
+ public static final int DEFAULT_PORT = -1;\r
+\r
+ private static final String INBOX_NAME = "INBOX";\r
+\r
+ private String host = DEFAULT_HOST;\r
+\r
+ private String protocol = DEFAULT_PROTOCOL;\r
+\r
+ private int port = DEFAULT_PORT;\r
+\r
+ private String username;\r
+\r
+ private String password;\r
+\r
+ private boolean javaMailLogEnabled;\r
+\r
+ private boolean connected = false;\r
+\r
+ private List receivedMails;\r
+\r
+ /**\r
+ * コンストラクタ。\r
+ */\r
+ public MockFetchMailPro() {\r
+ super();\r
+ receivedMails = new ArrayList();\r
+ }\r
+\r
+ /**\r
+ * @see com.ozacc.mail.fetch.FetchMailPro#connect()\r
+ */\r
+ public synchronized void connect() throws MailException {\r
+ if (isConnected()) {\r
+ log.warn("既にサーバ[" + host + "]に接続されています。再接続するには先に接続を切断する必要があります。");\r
+ return;\r
+ }\r
+\r
+ log.debug(protocol.toUpperCase() + "サーバ[" + host + "]に接続するフリ。");\r
+ connected = true;\r
+ log.info(protocol.toUpperCase() + "サーバ[" + host + "]に接続したフリ。");\r
+ }\r
+\r
+ /**\r
+ * @see com.ozacc.mail.fetch.FetchMailPro#disconnect()\r
+ */\r
+ public synchronized void disconnect() throws MailException {\r
+ if (isConnected()) {\r
+ log.debug(protocol.toUpperCase() + "サーバ[" + host + "]との接続を切断するフリ。");\r
+ connected = false;\r
+ log.debug(protocol.toUpperCase() + "サーバ[" + host + "]との接続を切断したフリ。");\r
+ }\r
+ }\r
+\r
+ /**\r
+ * <code>MockFetchMailPro</code>の<code>getMails()</code>メソッドが返す\r
+ * <code>ReceivedMail</code>インスタンスをセットします。\r
+ * \r
+ * @param mail <code>getMails()</code>メソッドが返す<code>ReceivedMail</code>インスタンス\r
+ */\r
+ public void setupGetMails(ReceivedMail mail) {\r
+ receivedMails.add(mail);\r
+ }\r
+\r
+ /**\r
+ * <code>MockFetchMailPro</code>の<code>getMails()</code>メソッドが返す\r
+ * <code>ReceivedMail</code>インスタンスをセットします。\r
+ * \r
+ * @param mails <code>getMails()</code>メソッドが返す<code>ReceivedMail</code>インスタンス配列\r
+ */\r
+ public void setupGetMails(ReceivedMail[] mails) {\r
+ for (int i = 0; i < mails.length; i++) {\r
+ ReceivedMail mail = mails[i];\r
+ setupGetMails(mail);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * @see com.ozacc.mail.fetch.FetchMailPro#getMailCount()\r
+ */\r
+ public int getMailCount() throws MailException {\r
+ return receivedMails.size();\r
+ }\r
+\r
+ /**\r
+ * @see com.ozacc.mail.fetch.FetchMailPro#getMail(int)\r
+ */\r
+ public synchronized ReceivedMail getMail(int num) throws MailException {\r
+ return getMail(num, false);\r
+ }\r
+\r
+ /**\r
+ * @see com.ozacc.mail.fetch.FetchMailPro#getMail(int, boolean)\r
+ */\r
+ public synchronized ReceivedMail getMail(int num, boolean delete) throws MailException {\r
+ if (isConnected()) {\r
+ if (delete) {\r
+ return (ReceivedMail)receivedMails.remove(num - 1);\r
+ } else {\r
+ return (ReceivedMail)receivedMails.get(num - 1);\r
+ }\r
+ } else {\r
+ throw new NotConnectedException(protocol.toUpperCase() + "サーバ[" + host + "]に接続されていません。");\r
+ }\r
+ }\r
+\r
+ /**\r
+ * @see com.ozacc.mail.fetch.FetchMailPro#getMails(boolean)\r
+ */\r
+ public synchronized ReceivedMail[] getMails(boolean delete) throws MailException {\r
+ if (isConnected()) {\r
+ ReceivedMail[] results = (ReceivedMail[])receivedMails\r
+ .toArray(new ReceivedMail[receivedMails.size()]);\r
+ if (delete) {\r
+ receivedMails.clear();\r
+ }\r
+ return results;\r
+ } else {\r
+ throw new NotConnectedException(protocol.toUpperCase() + "サーバ[" + host + "]に接続されていません。");\r
+ }\r
+ }\r
+\r
+ /**\r
+ * @see com.ozacc.mail.fetch.FetchMailPro#getMessage(int)\r
+ */\r
+ public MimeMessage getMessage(int num) throws MailException {\r
+ throw new UnsupportedOperationException("申し訳ございません。MockFetchMailProでは、このメソッドをサポートしていません。");\r
+ }\r
+\r
+ /**\r
+ * @see com.ozacc.mail.fetch.FetchMailPro#getMessages(boolean)\r
+ */\r
+ public MimeMessage[] getMessages(boolean delete) throws MailException {\r
+ throw new UnsupportedOperationException("申し訳ございません。MockFetchMailProでは、このメソッドをサポートしていません。");\r
+ }\r
+\r
+ /**\r
+ * @see com.ozacc.mail.fetch.FetchMailPro#changeFolder(java.lang.String)\r
+ */\r
+ public synchronized void changeFolder(String folderName) throws MailException {\r
+ if (!isConnected()) {\r
+ log.warn("メールサーバに接続されていません。");\r
+ return;\r
+ }\r
+\r
+ log.debug("メッセージフォルダ[" + folderName + "]をオープンするフリ。");\r
+ log.debug("メッセージフォルダ[" + folderName + "]をオープンしたフリ。");\r
+ }\r
+\r
+ /**\r
+ * @see com.ozacc.mail.fetch.FetchMailPro#isConnected()\r
+ */\r
+ public boolean isConnected() {\r
+ return connected;\r
+ }\r
+\r
+ /**\r
+ * @return Returns the host.\r
+ */\r
+ public String getHost() {\r
+ return host;\r
+ }\r
+\r
+ /**\r
+ * @param host The host to set.\r
+ */\r
+ public void setHost(String host) {\r
+ this.host = host;\r
+ }\r
+\r
+ /**\r
+ * @return Returns the javaMailLogEnabled.\r
+ */\r
+ public boolean isJavaMailLogEnabled() {\r
+ return javaMailLogEnabled;\r
+ }\r
+\r
+ /**\r
+ * @param javaMailLogEnabled The javaMailLogEnabled to set.\r
+ */\r
+ public void setJavaMailLogEnabled(boolean javaMailLogEnabled) {\r
+ this.javaMailLogEnabled = javaMailLogEnabled;\r
+ }\r
+\r
+ /**\r
+ * @return Returns the password.\r
+ */\r
+ public String getPassword() {\r
+ return password;\r
+ }\r
+\r
+ /**\r
+ * @param password The password to set.\r
+ */\r
+ public void setPassword(String password) {\r
+ this.password = password;\r
+ }\r
+\r
+ /**\r
+ * @return Returns the port.\r
+ */\r
+ public int getPort() {\r
+ return port;\r
+ }\r
+\r
+ /**\r
+ * @param port The port to set.\r
+ */\r
+ public void setPort(int port) {\r
+ this.port = port;\r
+ }\r
+\r
+ /**\r
+ * @return Returns the protocol.\r
+ */\r
+ public String getProtocol() {\r
+ return protocol;\r
+ }\r
+\r
+ /**\r
+ * @param protocol The protocol to set.\r
+ */\r
+ public void setProtocol(String protocol) {\r
+ this.protocol = protocol;\r
+ }\r
+\r
+ /**\r
+ * @return Returns the username.\r
+ */\r
+ public String getUsername() {\r
+ return username;\r
+ }\r
+\r
+ /**\r
+ * @param username The username to set.\r
+ */\r
+ public void setUsername(String username) {\r
+ this.username = username;\r
+ }\r
+\r
+}
\ No newline at end of file
--- /dev/null
+package com.ozacc.mail.mock;\r
+\r
+import com.ozacc.mail.Mail;\r
+\r
+/**\r
+ * 値がセットされた項目だけ送信メールと比較する、MockSendMailでのテスト用Mailクラス。\r
+ * \r
+ * @since 1.0\r
+ * @author Tomohiro Otsuka\r
+ * @version $Id: MockMail.java,v 1.5 2004/09/16 04:52:45 otsuka Exp $\r
+ */\r
+public class MockMail extends Mail {\r
+\r
+ /**\r
+ * コンストラクタ。\r
+ */\r
+ public MockMail() {}\r
+\r
+ /**\r
+ * コピーコンストラクタ。\r
+ * シャローコピー(shallow copy)です。\r
+ * \r
+ * @since 1.0.2\r
+ * \r
+ * @param original コピー元のMailインスタンス\r
+ */\r
+ public MockMail(Mail original) {\r
+ super(original);\r
+ }\r
+\r
+}
\ No newline at end of file
--- /dev/null
+package com.ozacc.mail.mock;\r
+\r
+import java.io.UnsupportedEncodingException;\r
+import java.util.ArrayList;\r
+import java.util.List;\r
+import java.util.Properties;\r
+\r
+import javax.mail.MessagingException;\r
+import javax.mail.Session;\r
+import javax.mail.internet.InternetAddress;\r
+import javax.mail.internet.MimeMessage;\r
+\r
+import com.ozacc.mail.Mail;\r
+import com.ozacc.mail.MailBuildException;\r
+import com.ozacc.mail.MailException;\r
+import com.ozacc.mail.SendMail;\r
+import com.ozacc.mail.impl.MimeMessageBuilder;\r
+\r
+/**\r
+ * SendMailImplクラスのMock。<br>\r
+ * 実存するSMTPサーバを設定しても、実際には送信されません。\r
+ * デバッグモードを有効にすると、メールを送信するタイミングでコンソールに送信メール内容が出力されます。\r
+ * <p>\r
+ * Mailインスタンスを addExpectedMail() にセットし verify() メソッドを実行すると、send() されたMailインスタンスと全てのプロパティ(XHeader、添付ファイルを除く)が一致しなければAssertionFailedExceptionがスローされます。\r
+ * <p>\r
+ * 例えば、send() されたMailインスタンスのFromアドレスと件名だけチェックし、その他のプロパティはチェックしたくない場合は、MockMailインスタンスを使用します。\r
+ * <pre>Mail sentMail = new Mail();\r
+ *sentMail.setFrom("from@example.com");\r
+ *sentMail.setSubject("件名");\r
+ *sentMail.addTo("to@example.com");\r
+ *sentMail.setText("動的生成される本文");\r
+ *\r
+ *Mail expectedMail = new Mail();\r
+ *expectedMail.setFrom("from@example.com");\r
+ *expectedMail.setSubject("件名");\r
+ *\r
+ *MockMail mockMail = new MockMail();\r
+ *mockMail.setFrom("from@example.com");\r
+ *mockMail.setSubject("件名");\r
+ *\r
+ *MockSendMail sendMail = new MockSendMail();\r
+ *sendMail.addExpectedMail(expectedMail);\r
+ *sendMail.send(sentMail);\r
+ *sendMail.verify(); // 失敗 AssertionFailedException\r
+ *\r
+ *sendMail = new MockSendMail();\r
+ *sendMail.addExpectedMail(mockMail);\r
+ *sendMail.send(sentMail);\r
+ *sendMail.verify(); // 成功</pre>\r
+ * <p>\r
+ * <strong>注:</strong> 添付ファイルは比較対象になりません。\r
+ * \r
+ * @since 1.0\r
+ * @author Tomohiro Otsuka\r
+ * @version $Id: MockSendMail.java,v 1.10.2.1 2004/11/25 08:01:47 otsuka Exp $\r
+ */\r
+public class MockSendMail implements SendMail {\r
+\r
+ /** デフォルトのプロトコル。「smtp」 */\r
+ public static final String DEFAULT_PROTOCOL = "smtp";\r
+\r
+ /** デフォルトのポート。「-1」 */\r
+ public static final int DEFAULT_PORT = -1;\r
+\r
+ /** デフォルトのSMTPサーバ。「localhost」 */\r
+ public static final String DEFAULT_HOST = "localhost";\r
+\r
+ /** ISO-2022-JP */\r
+ public static final String JIS_CHARSET = "ISO-2022-JP";\r
+\r
+ private static final String RETURN_PATH_KEY = "mail.smtp.from";\r
+\r
+ private String protocol = DEFAULT_PROTOCOL;\r
+\r
+ private String host = DEFAULT_HOST;\r
+\r
+ private int port = DEFAULT_PORT;\r
+\r
+ private String username;\r
+\r
+ private String password;\r
+\r
+ private String charset = JIS_CHARSET;\r
+\r
+ private String returnPath;\r
+\r
+ private List sentMails;\r
+\r
+ private List mimeMessages;\r
+\r
+ private List expectedMails;\r
+\r
+ private boolean debug;\r
+\r
+ /**\r
+ * コンストラクタ。\r
+ */\r
+ public MockSendMail() {\r
+ super();\r
+ initialize();\r
+ }\r
+\r
+ /**\r
+ * MockSendMailインスタンスを初期化します。\r
+ */\r
+ public void initialize() {\r
+ sentMails = new ArrayList();\r
+ expectedMails = new ArrayList();\r
+ mimeMessages = new ArrayList();\r
+ }\r
+\r
+ /**\r
+ * 送信されたメールのMimeMessageインスタンスを返します。\r
+ * 送信順の配列です。\r
+ * \r
+ * @return 送信メールのMimeMessageインスタンス配列\r
+ */\r
+ public MimeMessage[] getMimeMessages() {\r
+ return (MimeMessage[])mimeMessages.toArray(new MimeMessage[mimeMessages.size()]);\r
+ }\r
+\r
+ /**\r
+ * 送信されたMailインスタンスを返します。送信順の配列です。\r
+ * \r
+ * @return 送信メールのMailインスタンス配列\r
+ */\r
+ public Mail[] getSentMails() {\r
+ return (Mail[])sentMails.toArray(new Mail[sentMails.size()]);\r
+ }\r
+\r
+ /**\r
+ * デバッグモードが有効になっているか判定します。\r
+ * \r
+ * @return Returns 有効になっている場合 true\r
+ */\r
+ public boolean isDebug() {\r
+ return debug;\r
+ }\r
+\r
+ /**\r
+ * デバッグモード(コンソールにログを出力)を有効にします。\r
+ * デフォルトは無効です。\r
+ * \r
+ * @param debug デバッグモードを有効にする場合 true\r
+ */\r
+ public void setDebug(boolean debug) {\r
+ this.debug = debug;\r
+ }\r
+\r
+ /**\r
+ * デバッグモードが有効のとき、指定されたメッセージをコンソールに出力します。\r
+ * \r
+ * @param message コンソール出力するメッセージ\r
+ */\r
+ private void debug(String message) {\r
+ if (debug) {\r
+ System.out.println("[" + Thread.currentThread().getName() + "] DEBUG "\r
+ + getClass().getName() + " - " + message);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * \r
+ * @param expectedMail\r
+ */\r
+ public void addExpectedMail(Mail expectedMail) {\r
+ expectedMails.add(expectedMail);\r
+ }\r
+\r
+ /**\r
+ * \r
+ * @param expectedMails\r
+ */\r
+ public void addExpectedMail(Mail[] expectedMails) {\r
+ for (int i = 0; i < expectedMails.length; i++) {\r
+ addExpectedMail(expectedMails[i]);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * \r
+ * @throws AssertionFailedException\r
+ */\r
+ public void verify() throws AssertionFailedException {\r
+ debug("======================================================");\r
+ debug(" verify() ");\r
+ debug("======================================================");\r
+\r
+ // メールの数を比較\r
+ int numOfExpectedMails = expectedMails.size();\r
+ int numOfSentMails = sentMails.size();\r
+ if (numOfExpectedMails != numOfSentMails) {\r
+ throw new AssertionFailedException("期待メール数<" + numOfExpectedMails + ">と送信メール数<"\r
+ + numOfSentMails + ">が一致しませんでした。");\r
+ }\r
+\r
+ debug("期待メール数と送信メール数は一致しました。[" + numOfExpectedMails + "通]");\r
+\r
+ // メール内容を比較\r
+ for (int i = 0; i < numOfExpectedMails; i++) {\r
+ Mail expected = (Mail)expectedMails.get(i);\r
+ Mail sent = (Mail)sentMails.get(i);\r
+ debug((i + 1) + "通目のチェックを開始します。("\r
+ + ((expected instanceof MockMail) ? "MockMail" : "Mail") + " - Mail)");\r
+ checkEquality(expected, sent, i + 1);\r
+ debug((i + 1) + "通目の期待メールと送信メール内容は一致しました。");\r
+ }\r
+\r
+ debug("verifyプロセスは全て成功しました。");\r
+ debug("======================================================");\r
+ }\r
+\r
+ /**\r
+ * @param expected\r
+ * @param sent \r
+ * @throws AssertionFailedException\r
+ */\r
+ public void checkEquality(Mail expected, Mail sent, int num) throws AssertionFailedException {\r
+ boolean mockMode = (expected instanceof MockMail);\r
+\r
+ // マルチパートメールの場合\r
+ if (expected.isMultipartMail()) {\r
+\r
+ // HTML\r
+ if (!mockMode) {\r
+ if ((expected.getHtmlText() == null && sent.getHtmlText() != null)\r
+ || (expected.getHtmlText() != null && sent.getHtmlText() == null)\r
+ || (!expected.getHtmlText().equals(sent.getHtmlText()))) {\r
+ throwExceptioWithMessage("HTML本文", expected.getHtmlText(), sent.getHtmlText(),\r
+ num);\r
+ }\r
+ } else if (mockMode && expected.getHtmlText() != null) {\r
+ if (!expected.getHtmlText().equals(sent.getHtmlText())) {\r
+ throwExceptioWithMessage("HTML本文", expected.getHtmlText(), sent.getHtmlText(),\r
+ num);\r
+ }\r
+ }\r
+ }\r
+\r
+ // Return-Path\r
+ if (!mockMode || (mockMode && expected.getReturnPath() != null)) {\r
+ if (expected.getReturnPath() != null && sent.getReturnPath() != null) {\r
+ if (!expected.getReturnPath().equals(sent.getReturnPath())) {\r
+ throwExceptioWithMessage("Return-Pathアドレス", expected.getReturnPath()\r
+ .toUnicodeString(), sent.getReturnPath().toUnicodeString(), num);\r
+ }\r
+ } else if ((expected.getReturnPath() != null && sent.getReturnPath() == null)\r
+ || (expected.getReturnPath() == null && sent.getReturnPath() != null)) {\r
+ throw new AssertionFailedException();\r
+ }\r
+ }\r
+\r
+ // From\r
+ if (!mockMode || (mockMode && expected.getFrom() != null)) {\r
+ if (expected.getFrom() != null && sent.getFrom() != null) {\r
+ if (!EqualityCheck.equals(expected.getFrom(), sent.getFrom())) {\r
+ throwExceptioWithMessage("Fromアドレス", expected.getFrom().toUnicodeString(), sent\r
+ .getFrom().toUnicodeString(), num);\r
+ }\r
+ } else if ((expected.getFrom() != null && sent.getFrom() == null)\r
+ || (expected.getFrom() == null && sent.getFrom() != null)) {\r
+ throw new AssertionFailedException();\r
+ }\r
+ }\r
+\r
+ // to\r
+ InternetAddress[] expectedAddresses = expected.getTo();\r
+ InternetAddress[] sentAddresses = sent.getTo();\r
+ if (!mockMode || (mockMode && expectedAddresses.length > 0)) {\r
+ if (expectedAddresses.length != sentAddresses.length) {\r
+ throwExceptioWithMessage("Toアドレス数", Integer.toString(expectedAddresses.length),\r
+ Integer.toString(sentAddresses.length), num);\r
+ }\r
+ for (int i = 0; i < expectedAddresses.length; i++) {\r
+ if (!EqualityCheck.equals(expectedAddresses[i], sentAddresses[i])) {\r
+ throwExceptioWithMessage("Toアドレス", expectedAddresses[i].toUnicodeString(),\r
+ sentAddresses[i].toUnicodeString(), num);\r
+ }\r
+ }\r
+ }\r
+\r
+ // cc\r
+ expectedAddresses = expected.getCc();\r
+ sentAddresses = sent.getCc();\r
+ if (!mockMode || (mockMode && expectedAddresses.length > 0)) {\r
+ if (expectedAddresses.length != sentAddresses.length) {\r
+ throwExceptioWithMessage("Ccアドレス数", Integer.toString(expectedAddresses.length),\r
+ Integer.toString(sentAddresses.length), num);\r
+ }\r
+ for (int i = 0; i < expectedAddresses.length; i++) {\r
+ if (!EqualityCheck.equals(expectedAddresses[i], sentAddresses[i])) {\r
+ throwExceptioWithMessage("Ccアドレス", expectedAddresses[i].toUnicodeString(),\r
+ sentAddresses[i].toUnicodeString(), num);\r
+ }\r
+ }\r
+ }\r
+\r
+ // bcc\r
+ expectedAddresses = expected.getBcc();\r
+ sentAddresses = sent.getBcc();\r
+ if (!mockMode || (mockMode && expectedAddresses.length > 0)) {\r
+ if (expectedAddresses.length != sentAddresses.length) {\r
+ throwExceptioWithMessage("Bccアドレス数", Integer.toString(expectedAddresses.length),\r
+ Integer.toString(sentAddresses.length), num);\r
+ }\r
+ for (int i = 0; i < expectedAddresses.length; i++) {\r
+ if (!EqualityCheck.equals(expectedAddresses[i], sentAddresses[i])) {\r
+ throwExceptioWithMessage("Bccアドレス", expectedAddresses[i].toUnicodeString(),\r
+ sentAddresses[i].toUnicodeString(), num);\r
+ }\r
+ }\r
+ }\r
+\r
+ // Reply-To\r
+ if (!mockMode || (mockMode && expected.getReplyTo() != null)) {\r
+ if (expected.getReplyTo() != null && sent.getReplyTo() != null) {\r
+ if (!EqualityCheck.equals(expected.getReplyTo(), sent.getReplyTo())) {\r
+ throwExceptioWithMessage("ReplyToアドレス",\r
+ expected.getReplyTo().toUnicodeString(), sent.getReplyTo()\r
+ .toUnicodeString(), num);\r
+ }\r
+ } else if ((expected.getReplyTo() != null && sent.getReplyTo() == null)\r
+ || (expected.getReplyTo() == null && sent.getReplyTo() != null)) {\r
+ throw new AssertionFailedException();\r
+ }\r
+ }\r
+\r
+ // 件名\r
+ if (!mockMode || (mockMode && expected.getSubject().length() > 0)) {\r
+ if (!expected.getSubject().equals(sent.getSubject())) {\r
+ throwExceptioWithMessage("件名", expected.getSubject(), sent.getSubject(), num);\r
+ }\r
+ }\r
+\r
+ // 本文\r
+ if (!mockMode || (mockMode && expected.getText().length() > 0)) {\r
+ if (!expected.getText().equals(sent.getText())) {\r
+ throwExceptioWithMessage("本文", expected.getText(), sent.getText(), num);\r
+ }\r
+ }\r
+\r
+ }\r
+\r
+ /**\r
+ * 引数の値を受けてエラーメッセージを生成し、AssertionFailedErrorをスローします。\r
+ * \r
+ * @param name 一致しなかった項目名\r
+ * @param expectedValue 期待値\r
+ * @param sentValue 実際値\r
+ * @param num N番目のメール\r
+ * @throws AssertionFailedException 生成された例外\r
+ */\r
+ protected void throwExceptioWithMessage(String name, String expectedValue, String sentValue,\r
+ int num) throws AssertionFailedException {\r
+ String message = num + "番目のメッセージで、「" + name + "」が一致しませんでした。期待値='" + expectedValue\r
+ + "', 送信値='" + sentValue + "'";\r
+\r
+ debug(message);\r
+ debug("verifyプロセスは失敗しました。");\r
+ debug("******************************************************");\r
+\r
+ throw new AssertionFailedException(message);\r
+ }\r
+\r
+ /**\r
+ * @see com.ozacc.mail.SendMail#send(com.ozacc.mail.Mail)\r
+ */\r
+ public void send(Mail mail) throws MailException {\r
+ send(new Mail[] { mail });\r
+ }\r
+\r
+ /**\r
+ * @see com.ozacc.mail.SendMail#send(com.ozacc.mail.Mail[])\r
+ */\r
+ public void send(Mail[] mails) throws MailException {\r
+ debug("SMTPサーバ[" + host + "]に接続するフリ。");\r
+ debug("SMTPサーバ[" + host + "]に接続したフリ。");\r
+\r
+ Session session = Session.getInstance(new Properties());\r
+ for (int i = 0; i < mails.length; i++) {\r
+\r
+ Mail mail = mails[i];\r
+\r
+ // MimeMessageを生成\r
+ MimeMessage message = new MimeMessage(session);\r
+ MimeMessageBuilder builder = new MimeMessageBuilder(message);\r
+ try {\r
+ builder.buildMimeMessage(mail);\r
+ } catch (UnsupportedEncodingException e) {\r
+ throw new MailBuildException("サポートされていない文字コードが指定されました。", e);\r
+ } catch (MessagingException e) {\r
+ throw new MailBuildException("MimeMessageの生成に失敗しました。", e);\r
+ }\r
+ mimeMessages.add(message);\r
+\r
+ debug("メールを送信するフリ。");\r
+ sentMails.add(mail);\r
+ debug(mail.toString());\r
+ debug("メールを送信したフリ。");\r
+ }\r
+\r
+ debug("SMTPサーバ[" + host + "]との接続を切断するフリ。");\r
+ debug("SMTPサーバ[" + host + "]との接続を切断したフリ。");\r
+ }\r
+\r
+ /**\r
+ * @see com.ozacc.mail.SendMail#send(javax.mail.internet.MimeMessage)\r
+ */\r
+ public void send(MimeMessage mimeMessage) throws MailException {\r
+ throw new UnsupportedOperationException("申し訳ございません。MockSendMailでは、このメソッドをサポートしていません。");\r
+ }\r
+\r
+ /**\r
+ * @see com.ozacc.mail.SendMail#send(javax.mail.internet.MimeMessage[])\r
+ */\r
+ public void send(MimeMessage[] mimeMessages) throws MailException {\r
+ throw new UnsupportedOperationException("申し訳ございません。MockSendMailでは、このメソッドをサポートしていません。");\r
+ }\r
+\r
+ /**\r
+ * エンコーディングに使用する文字コードを返します。\r
+ * \r
+ * @return エンコーディングに使用する文字コード\r
+ */\r
+ public String getCharset() {\r
+ return charset;\r
+ }\r
+\r
+ /**\r
+ * メールの件名や本文のエンコーディングに使用する文字コードを指定します。\r
+ * デフォルトは ISO-2022-JP です。\r
+ * \r
+ * @param charset エンコーディングに使用する文字コード\r
+ */\r
+ public void setCharset(String charset) {\r
+ this.charset = charset;\r
+ }\r
+\r
+ /**\r
+ * セットされたSMTPサーバのホスト名、またはIPアドレスを返します。\r
+ * \r
+ * @return SMTPサーバのホスト名、またはIPアドレス\r
+ */\r
+ public String getHost() {\r
+ return host;\r
+ }\r
+\r
+ /**\r
+ * SMTPサーバのホスト名、またはIPアドレスをセットします。\r
+ * デフォルトは localhost です。\r
+ * \r
+ * @param host SMTPサーバのホスト名、またはIPアドレス\r
+ */\r
+ public void setHost(String host) {\r
+ this.host = host;\r
+ }\r
+\r
+ /**\r
+ * @return SMTPサーバ認証パスワード\r
+ */\r
+ public String getPassword() {\r
+ return password;\r
+ }\r
+\r
+ /**\r
+ * SMTPサーバの接続認証が必要な場合にパスワードをセットします。\r
+ * \r
+ * @param password SMTPサーバ認証パスワード\r
+ */\r
+ public void setPassword(String password) {\r
+ this.password = password;\r
+ }\r
+\r
+ /**\r
+ * @return SMTPサーバのポート番号\r
+ */\r
+ public int getPort() {\r
+ return port;\r
+ }\r
+\r
+ /**\r
+ * SMTPサーバのポート番号をセットします。\r
+ * \r
+ * @param port SMTPサーバのポート番号\r
+ */\r
+ public void setPort(int port) {\r
+ this.port = port;\r
+ }\r
+\r
+ /**\r
+ * @return Returns the protocol.\r
+ */\r
+ public String getProtocol() {\r
+ return protocol;\r
+ }\r
+\r
+ /**\r
+ * @param protocol The protocol to set.\r
+ */\r
+ public void setProtocol(String protocol) {\r
+ this.protocol = protocol;\r
+ }\r
+\r
+ /**\r
+ * @return Return-Pathアドレス\r
+ */\r
+ public String getReturnPath() {\r
+ return returnPath;\r
+ }\r
+\r
+ /**\r
+ * Return-Pathアドレスをセットします。\r
+ * \r
+ * @param returnPath Return-Pathアドレス\r
+ */\r
+ public void setReturnPath(String returnPath) {\r
+ this.returnPath = returnPath;\r
+ }\r
+\r
+ /**\r
+ * @return SMTPサーバ認証ユーザ名\r
+ */\r
+ public String getUsername() {\r
+ return username;\r
+ }\r
+\r
+ /**\r
+ * SMTPサーバの接続認証が必要な場合にユーザ名をセットします。\r
+ * \r
+ * @param username SMTPサーバ認証ユーザ名\r
+ */\r
+ public void setUsername(String username) {\r
+ this.username = username;\r
+ }\r
+\r
+}
\ No newline at end of file
--- /dev/null
+<HTML>\r
+<BODY>\r
+SendMail¤Î¥â¥Ã¥¯¼ÂÁõ¥¯¥é¥¹¤Ç¤¹¡£\r
+</BODY>\r
+</HTML>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>\r
+\r
+<!--\r
+ XML documents that conform to this DTD should declare the following doctype:\r
+ <!DOCTYPE mail PUBLIC "-//OZACC//DTD MAIL//EN"\r
+ "http://www.ozacc.com/library/dtd/ozacc-mail.dtd">\r
+ \r
+ $Id: ozacc-mail.dtd,v 1.3 2004/09/15 07:48:44 otsuka Exp $\r
+-->\r
+\r
+<!ELEMENT mail (returnPath?,from?,recipients?,replyTo?,subject?,body?,html?)>\r
+\r
+<!-- メールの差出人 -->\r
+<!ELEMENT from EMPTY>\r
+<!ATTLIST from\r
+ email CDATA #REQUIRED\r
+ name CDATA #IMPLIED\r
+>\r
+\r
+<!-- メールの返信先 -->\r
+<!ELEMENT replyTo EMPTY>\r
+<!ATTLIST replyTo\r
+ email CDATA #REQUIRED\r
+>\r
+\r
+<!-- Return-Path -->\r
+<!ELEMENT returnPath EMPTY>\r
+<!ATTLIST returnPath\r
+ email CDATA #REQUIRED\r
+>\r
+\r
+<!-- メールの宛先 To、Cc、Bccの内、ひとつ以上を指定する -->\r
+<!ELEMENT recipients (to|cc|bcc)+>\r
+\r
+<!ELEMENT to EMPTY>\r
+<!ATTLIST to\r
+ email CDATA #REQUIRED\r
+ name CDATA #IMPLIED\r
+>\r
+\r
+<!ELEMENT cc EMPTY>\r
+<!ATTLIST cc\r
+ email CDATA #REQUIRED\r
+ name CDATA #IMPLIED\r
+>\r
+\r
+<!ELEMENT bcc EMPTY>\r
+<!ATTLIST bcc\r
+ email CDATA #REQUIRED\r
+>\r
+\r
+<!-- メールの件名 -->\r
+<!ELEMENT subject (#PCDATA)>\r
+\r
+<!-- メールの本文 -->\r
+<!ELEMENT body (#PCDATA)>\r
+\r
+<!-- HTML -->\r
+<!ELEMENT html (#PCDATA)>\r
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>\r
+\r
+<!--\r
+ XML documents that conform to this DTD should declare the following doctype:\r
+ <!DOCTYPE mails PUBLIC "-//OZACC//DTD MULTIPLE MAILS//EN"\r
+ "http://www.ozacc.com/library/dtd/ozacc-multiple-mails.dtd">\r
+ \r
+ $Id: ozacc-multiple-mails.dtd,v 1.1.2.1 2005/01/21 22:16:56 otsuka Exp $\r
+-->\r
+\r
+<!ELEMENT mails (mail+)>\r
+\r
+<!ELEMENT mail (returnPath?,from?,recipients?,replyTo?,subject?,body?,html?)>\r
+<!ATTLIST mail\r
+ id ID #REQUIRED\r
+>\r
+\r
+<!-- メールの差出人 -->\r
+<!ELEMENT from EMPTY>\r
+<!ATTLIST from\r
+ email CDATA #REQUIRED\r
+ name CDATA #IMPLIED\r
+>\r
+\r
+<!-- メールの返信先 -->\r
+<!ELEMENT replyTo EMPTY>\r
+<!ATTLIST replyTo\r
+ email CDATA #REQUIRED\r
+>\r
+\r
+<!-- Return-Path -->\r
+<!ELEMENT returnPath EMPTY>\r
+<!ATTLIST returnPath\r
+ email CDATA #REQUIRED\r
+>\r
+\r
+<!-- メールの宛先 To、Cc、Bccの内、ひとつ以上を指定する -->\r
+<!ELEMENT recipients (to|cc|bcc)+>\r
+\r
+<!ELEMENT to EMPTY>\r
+<!ATTLIST to\r
+ email CDATA #REQUIRED\r
+ name CDATA #IMPLIED\r
+>\r
+\r
+<!ELEMENT cc EMPTY>\r
+<!ATTLIST cc\r
+ email CDATA #REQUIRED\r
+ name CDATA #IMPLIED\r
+>\r
+\r
+<!ELEMENT bcc EMPTY>\r
+<!ATTLIST bcc\r
+ email CDATA #REQUIRED\r
+>\r
+\r
+<!-- メールの件名 -->\r
+<!ELEMENT subject (#PCDATA)>\r
+\r
+<!-- メールの本文 -->\r
+<!ELEMENT body (#PCDATA)>\r
+\r
+<!-- HTML -->\r
+<!ELEMENT html (#PCDATA)>\r
--- /dev/null
+<HTML>\r
+<BODY>\r
+¥á¡¼¥ë¤ÎÁ÷¿®¥¤¥ó¥¿¡¼¥Õ¥§¡¼¥¹¤È¡¢XML¥É¥¥å¥á¥ó¥È¤«¤é¥á¡¼¥ë¤òÀ¸À®¤¹¤ë¥¤¥ó¥¿¡¼¥Õ¥§¡¼¥¹¤òÄ󶡤·¤Þ¤¹¡£\r
+</BODY>\r
+</HTML>
\ No newline at end of file
--- /dev/null
+package com.ozacc.mail.spring;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Iterator;\r
+import java.util.List;\r
+\r
+import org.quartz.JobExecutionContext;\r
+import org.quartz.JobExecutionException;\r
+import org.springframework.scheduling.quartz.QuartzJobBean;\r
+\r
+import com.ozacc.mail.fetch.FetchMailPro;\r
+import com.ozacc.mail.fetch.ReceivedMail;\r
+import com.ozacc.mail.mailet.MailetWrapper;\r
+\r
+/**\r
+ * Spring FrameworkのQuartz実行用MailetRunner。\r
+ * \r
+ * @since 1.2\r
+ * @author Tomohiro Otsuka\r
+ * @version $Id: MailetRunnerJob.java,v 1.1.2.3 2005/01/23 06:47:16 otsuka Exp $\r
+ */\r
+public class MailetRunnerJob extends QuartzJobBean {\r
+\r
+ private List mailetWrapperList;\r
+\r
+ private FetchMailPro fetchMailPro;\r
+\r
+ /**\r
+ * コンストラクタ。\r
+ */\r
+ public MailetRunnerJob() {\r
+ mailetWrapperList = new ArrayList();\r
+ }\r
+\r
+ /**\r
+ * メール受信とMailetの起動を行います。\r
+ * \r
+ * @see org.springframework.scheduling.quartz.QuartzJobBean#executeInternal(org.quartz.JobExecutionContext)\r
+ */\r
+ protected void executeInternal(JobExecutionContext context) throws JobExecutionException {\r
+ fetchMailPro.connect();\r
+ try {\r
+ int count = fetchMailPro.getMailCount();\r
+ for (int i = 1; i <= count; i++) {\r
+ ReceivedMail mail = fetchMailPro.getMail(i);\r
+ processMail(mail);\r
+ }\r
+ } finally {\r
+ if (fetchMailPro.isConnected()) {\r
+ fetchMailPro.disconnect();\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * 指定された受信メールに対してMailetを適用します。\r
+ * \r
+ * @param mail MailetUnitに渡す受信メール\r
+ */\r
+ private void processMail(ReceivedMail mail) {\r
+ for (Iterator itr = mailetWrapperList.iterator(); itr.hasNext();) {\r
+ MailetWrapper mailetWrapper = (MailetWrapper)itr.next();\r
+ mailetWrapper.execute(mail);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * メールの受信に使用するFetchMailProインターフェースの実装インスタンスをセットします。\r
+ * \r
+ * @param fetchMailPro FetchMailProインターフェースの実装インスタンス\r
+ */\r
+ public void setFetchMailPro(FetchMailPro fetchMailPro) {\r
+ this.fetchMailPro = fetchMailPro;\r
+ }\r
+\r
+ /**\r
+ * 実行するMailetのMailetWrapperリストをセットします。\r
+ * \r
+ * @param mailetWrapperList 実行するMailetのMailetWrapperリスト\r
+ */\r
+ public void setMailetWrapperList(List mailetWrapperList) {\r
+ this.mailetWrapperList = mailetWrapperList;\r
+ }\r
+\r
+}
\ No newline at end of file
--- /dev/null
+package com.ozacc.mail.spring;\r
+\r
+import java.io.File;\r
+\r
+import org.springframework.beans.factory.config.AbstractFactoryBean;\r
+import org.springframework.core.io.Resource;\r
+\r
+import com.ozacc.mail.Mail;\r
+import com.ozacc.mail.MailBuildException;\r
+import com.ozacc.mail.MailBuilder;\r
+import com.ozacc.mail.impl.XMLMailBuilderImpl;\r
+\r
+/**\r
+ * Springの設定ファイルで指定されたロケーションのXMLファイルからMailインスタンスを生成するFactoryBean。\r
+ * デフォルトでは、singletonプロパティはfalseに設定されます。\r
+ * <p>\r
+ * location、classPath、filePathの順で、一番先にセットされているプロパティ値がXMLファイルのパスとして使われます。\r
+ * \r
+ * @see com.ozacc.mail.impl.XMLMailBuilderImpl\r
+ * \r
+ * @since 1.0\r
+ * @author Tomohiro Otsuka\r
+ * @version $Id: XMLMailFactoryBean.java,v 1.4 2004/09/13 19:48:16 otsuka Exp $\r
+ */\r
+public class XMLMailFactoryBean extends AbstractFactoryBean {\r
+\r
+ private String classPath;\r
+\r
+ private String filePath;\r
+\r
+ private Resource location;\r
+\r
+ private MailBuilder mailBuilder;\r
+\r
+ /**\r
+ * コンストラクタ。\r
+ */\r
+ public XMLMailFactoryBean() {\r
+ setSingleton(false);\r
+ }\r
+\r
+ /**\r
+ * @see org.springframework.beans.factory.config.AbstractFactoryBean#createInstance()\r
+ */\r
+ protected Object createInstance() throws Exception {\r
+ if (mailBuilder == null) {\r
+ init();\r
+ }\r
+\r
+ if (getLocation() != null && getLocation().getFile() != null) {\r
+ return mailBuilder.buildMail(getLocation().getFile());\r
+ }\r
+ if (getClassPath() != null) {\r
+ return mailBuilder.buildMail(getClassPath());\r
+ }\r
+ if (getFilePath() != null) {\r
+ return mailBuilder.buildMail(new File(getFilePath()));\r
+ }\r
+ throw new MailBuildException("Mailインスタンスの生成に失敗しました。XMLデータのロケーションが指定されていません。");\r
+ }\r
+\r
+ /**\r
+ * mailBuilderインスタンスを生成します。\r
+ */\r
+ private void init() {\r
+ mailBuilder = new XMLMailBuilderImpl();\r
+ }\r
+\r
+ /**\r
+ * @see org.springframework.beans.factory.FactoryBean#getObjectType()\r
+ */\r
+ public Class getObjectType() {\r
+ return Mail.class;\r
+ }\r
+\r
+ /**\r
+ * <code>MailBuilder</code>インターフェースの実装クラスのインスタンスをセットします。\r
+ * デフォルトでは、<code>XMLMailBuilderImpl</code>が使用されます。\r
+ * <p>\r
+ * ただし、ここでセットしない場合は、<code>XMLMailFactoryBean</code>ひとつに付き、\r
+ * <code>XMLMailBuilderImpl</code>インスタンス一つが保持されます。\r
+ * シングルトンの<code>MailBuilder</code>インスタンスをセットすることを推奨します。\r
+ * \r
+ * @param mailBuilder MailBuilderインスタンス\r
+ */\r
+ public void setMailBuilder(MailBuilder mailBuilder) {\r
+ this.mailBuilder = mailBuilder;\r
+ }\r
+\r
+ /**\r
+ * @return Returns the classPath.\r
+ */\r
+ public String getClassPath() {\r
+ return classPath;\r
+ }\r
+\r
+ /**\r
+ * @param classPath The classPath to set.\r
+ */\r
+ public void setClassPath(String classPath) {\r
+ this.classPath = classPath;\r
+ }\r
+\r
+ /**\r
+ * @return Returns the filePath.\r
+ */\r
+ public String getFilePath() {\r
+ return filePath;\r
+ }\r
+\r
+ /**\r
+ * @param filePath The filePath to set.\r
+ */\r
+ public void setFilePath(String filePath) {\r
+ this.filePath = filePath;\r
+ }\r
+\r
+ /**\r
+ * @return Returns the location.\r
+ */\r
+ public Resource getLocation() {\r
+ return location;\r
+ }\r
+\r
+ /**\r
+ * @param location The location to set.\r
+ */\r
+ public void setLocation(Resource location) {\r
+ this.location = location;\r
+ }\r
+}
\ No newline at end of file
--- /dev/null
+<HTML>\r
+<BODY>\r
+Spring Framework¤ÇÍøÍѤǤ¤ë¥¯¥é¥¹¤òÄ󶡤·¤Þ¤¹¡£\r
+</BODY>\r
+</HTML>
\ No newline at end of file
--- /dev/null
+package com.ozacc.mail.xml;\r
+\r
+/**\r
+ * XMLドキュメントの生成に失敗した時にスローされる例外。\r
+ * \r
+ * @since 1.0\r
+ * @author Tomohiro Otsuka\r
+ * @version $Id: XMLBuildException.java,v 1.2 2004/09/13 07:08:11 otsuka Exp $\r
+ */\r
+public class XMLBuildException extends RuntimeException {\r
+\r
+ /**\r
+ * @param message \r
+ */\r
+ public XMLBuildException(String message) {\r
+ super(message);\r
+ }\r
+\r
+ /**\r
+ * @param message\r
+ * @param cause \r
+ */\r
+ public XMLBuildException(String message, Throwable cause) {\r
+ super(message, cause);\r
+ }\r
+}
\ No newline at end of file
--- /dev/null
+package com.ozacc.mail.xml;\r
+\r
+import java.io.File;\r
+\r
+import org.w3c.dom.Document;\r
+\r
+import com.ozacc.mail.Mail;\r
+\r
+/**\r
+ * MailインスタンスからXMLドキュメントを生成します。\r
+ * \r
+ * @see com.ozacc.mail.xml.impl.XMLBuilderImpl\r
+ * @see com.ozacc.mail.xml.impl.JDomXMLBuilder\r
+ * \r
+ * @since 1.0\r
+ * @author Tomohiro Otsuka\r
+ * @version $Id: XMLBuilder.java,v 1.2 2004/09/13 07:13:31 otsuka Exp $\r
+ */\r
+public interface XMLBuilder {\r
+\r
+ /**\r
+ * 指定されたMailインスタンスからXMLドキュメントを生成します。\r
+ * \r
+ * @param mail\r
+ * @return DOM Document\r
+ * @throws XMLBuildException\r
+ */\r
+ Document buildDocument(Mail mail) throws XMLBuildException;\r
+\r
+ /**\r
+ * 指定されたMailインスタンスからXMLドキュメントを生成し、\r
+ * 指定されたファイルに保存します。\r
+ * \r
+ * @param mail\r
+ * @param destFile MailデータXMLの保存先ファイル\r
+ * @throws XMLBuildException\r
+ */\r
+ void saveDocument(Mail mail, File destFile) throws XMLBuildException;\r
+\r
+}
\ No newline at end of file
--- /dev/null
+package com.ozacc.mail.xml.impl;\r
+\r
+import java.io.File;\r
+import java.io.FileOutputStream;\r
+import java.io.IOException;\r
+\r
+import javax.mail.internet.InternetAddress;\r
+\r
+import org.jdom.CDATA;\r
+import org.jdom.DocType;\r
+import org.jdom.Document;\r
+import org.jdom.Element;\r
+import org.jdom.JDOMException;\r
+import org.jdom.output.DOMOutputter;\r
+import org.jdom.output.Format;\r
+import org.jdom.output.XMLOutputter;\r
+\r
+import com.ozacc.mail.Mail;\r
+import com.ozacc.mail.xml.XMLBuildException;\r
+import com.ozacc.mail.xml.XMLBuilder;\r
+\r
+/**\r
+ * XMLBuilderの実装クラス。\r
+ * \r
+ * @since 1.0\r
+ * @author Tomohiro Otsuka\r
+ * @version $Id: JDomXMLBuilder.java,v 1.6 2004/09/18 00:39:17 otsuka Exp $\r
+ */\r
+public class JDomXMLBuilder implements XMLBuilder {\r
+\r
+ public static final String DOCTYPE_PUBLIC = "-//OZACC//DTD MAIL//EN";\r
+\r
+ public static final String DOCTYPE_SYSTEM = "http://www.ozacc.com/library/dtd/ozacc-mail.dtd";\r
+\r
+ private String charset = "UTF-8";\r
+\r
+ /**\r
+ * コンストラクタ。\r
+ */\r
+ public JDomXMLBuilder() {}\r
+\r
+ /**\r
+ * コンストラクタ。\r
+ * 出力XMLファイルの文字コードを指定します。デフォルトはUTF-8。\r
+ * \r
+ * @param charset 出力XMLファイルの文字コード\r
+ */\r
+ public JDomXMLBuilder(String charset) {\r
+ this();\r
+ setCharset(charset);\r
+ }\r
+\r
+ /**\r
+ * 出力XMLファイルの文字コードを指定します。デフォルトはUTF-8。\r
+ * \r
+ * @param charset 出力XMLファイルの文字コード\r
+ */\r
+ public void setCharset(String charset) {\r
+ this.charset = charset;\r
+ }\r
+\r
+ /**\r
+ * 出力XMLファイルの文字コードを返します。\r
+ * \r
+ * @return 出力XMLファイルの文字コード\r
+ */\r
+ public String getCharset() {\r
+ return charset;\r
+ }\r
+\r
+ /**\r
+ * @see com.ozacc.mail.xml.XMLBuilder#buildDocument(com.ozacc.mail.Mail)\r
+ */\r
+ public org.w3c.dom.Document buildDocument(Mail mail) throws XMLBuildException {\r
+ Document doc = buildJDomDocument(mail);\r
+ DOMOutputter outputter = new DOMOutputter();\r
+ try {\r
+ return outputter.output(doc);\r
+ } catch (JDOMException e) {\r
+ throw new XMLBuildException("DOM Documentの生成に失敗しました。", e);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * 指定されたMailインスタンスからJDOMドキュメントを生成します。\r
+ * \r
+ * @return 生成されたJDOMドキュメント\r
+ */\r
+ public Document buildJDomDocument(Mail mail) {\r
+\r
+ Element mailElem = new Element("mail");\r
+\r
+ // Return-Path\r
+ if (mail.getReturnPath() != null) {\r
+ InternetAddress returnPath = mail.getReturnPath();\r
+ Element returnPathElem = convertInternetAddressIntoElement(returnPath, "returnPath");\r
+ mailElem.addContent(returnPathElem);\r
+ }\r
+\r
+ // From\r
+ if (mail.getFrom() != null) {\r
+ InternetAddress from = mail.getFrom();\r
+ Element fromElem = convertInternetAddressIntoElement(from, "from");\r
+ mailElem.addContent(fromElem);\r
+ }\r
+\r
+ if (mail.getTo().length > 0 || mail.getCc().length > 0 || mail.getBcc().length > 0) {\r
+ Element recipientsElem = new Element("recipients");\r
+\r
+ // To\r
+ if (mail.getTo().length > 0) {\r
+ for (int i = 0; i < mail.getTo().length; i++) {\r
+ InternetAddress to = mail.getTo()[i];\r
+ Element toElem = convertInternetAddressIntoElement(to, "to");\r
+ recipientsElem.addContent(toElem);\r
+ }\r
+ }\r
+ // Cc\r
+ if (mail.getCc().length > 0) {\r
+ for (int i = 0; i < mail.getCc().length; i++) {\r
+ InternetAddress cc = mail.getCc()[i];\r
+ Element ccElem = convertInternetAddressIntoElement(cc, "cc");\r
+ recipientsElem.addContent(ccElem);\r
+ }\r
+ }\r
+ // Bcc\r
+ if (mail.getBcc().length > 0) {\r
+ for (int i = 0; i < mail.getBcc().length; i++) {\r
+ InternetAddress bcc = mail.getBcc()[i];\r
+ Element bccElem = convertInternetAddressIntoElement(bcc, "bcc");\r
+ recipientsElem.addContent(bccElem);\r
+ }\r
+ }\r
+ mailElem.addContent(recipientsElem);\r
+ }\r
+\r
+ // Reply-To\r
+ if (mail.getReplyTo() != null) {\r
+ InternetAddress replyTo = mail.getReplyTo();\r
+ Element replyToElem = convertInternetAddressIntoElement(replyTo, "replyTo");\r
+ mailElem.addContent(replyToElem);\r
+ }\r
+\r
+ // Subject\r
+ if (mail.getSubject() != null) {\r
+ Element subjectElem = new Element("subject");\r
+ subjectElem.setText(mail.getSubject());\r
+ mailElem.addContent(subjectElem);\r
+ }\r
+\r
+ // Body\r
+ if (mail.getText() != null) {\r
+ Element textElem = new Element("body");\r
+ textElem.setText(mail.getText());\r
+ mailElem.addContent(textElem);\r
+ }\r
+\r
+ // Html\r
+ if (mail.isHtmlMail()) {\r
+ Element htmlElem = new Element("html");\r
+ htmlElem.setContent(new CDATA(mail.getHtmlText()));\r
+ mailElem.addContent(htmlElem);\r
+ }\r
+\r
+ Document doc = new Document(mailElem);\r
+ DocType docType = new DocType("mail", DOCTYPE_PUBLIC, DOCTYPE_SYSTEM);\r
+ doc.setDocType(docType);\r
+ return doc;\r
+ }\r
+\r
+ /**\r
+ * \r
+ * @param address\r
+ * @param elemName\r
+ * @return\r
+ */\r
+ private Element convertInternetAddressIntoElement(InternetAddress address, String elemName) {\r
+ Element element = new Element(elemName);\r
+ element.setAttribute("email", address.getAddress());\r
+ if (address.getPersonal() != null) {\r
+ element.setAttribute("name", address.getPersonal());\r
+ }\r
+ return element;\r
+ }\r
+\r
+ /**\r
+ * @see com.ozacc.mail.xml.XMLBuilder#saveDocument(com.ozacc.mail.Mail, java.io.File)\r
+ */\r
+ public void saveDocument(Mail mail, File destFile) throws XMLBuildException {\r
+ // JDOM Documentを生成\r
+ Document doc = buildJDomDocument(mail);\r
+\r
+ // ファイル出力\r
+ try {\r
+ FileOutputStream fos = new FileOutputStream(destFile);\r
+ XMLOutputter outputter = getXMLOutputter();\r
+ outputter.output(doc, fos);\r
+ fos.close();\r
+ } catch (IOException e) {\r
+ throw new XMLBuildException("DOM Documentのファイル出力に失敗しました。", e);\r
+ }\r
+ }\r
+\r
+ public XMLOutputter getXMLOutputter() {\r
+ Format format = Format.getPrettyFormat();\r
+ format.setEncoding(charset);\r
+ XMLOutputter outputter = new XMLOutputter(format);\r
+ return outputter;\r
+ }\r
+}
\ No newline at end of file
--- /dev/null
+package com.ozacc.mail.xml.impl;\r
+\r
+import java.io.File;\r
+import java.util.Properties;\r
+\r
+import javax.mail.internet.InternetAddress;\r
+import javax.xml.parsers.DocumentBuilder;\r
+import javax.xml.parsers.DocumentBuilderFactory;\r
+import javax.xml.parsers.FactoryConfigurationError;\r
+import javax.xml.parsers.ParserConfigurationException;\r
+import javax.xml.transform.OutputKeys;\r
+import javax.xml.transform.Transformer;\r
+import javax.xml.transform.TransformerException;\r
+import javax.xml.transform.TransformerFactory;\r
+import javax.xml.transform.dom.DOMSource;\r
+import javax.xml.transform.stream.StreamResult;\r
+\r
+import org.w3c.dom.Document;\r
+import org.w3c.dom.Element;\r
+\r
+import com.ozacc.mail.Mail;\r
+import com.ozacc.mail.xml.XMLBuildException;\r
+import com.ozacc.mail.xml.XMLBuilder;\r
+\r
+/**\r
+ * JDK 1.4以降の標準XMLライブラリを使用して実装されたXMLBuilder。\r
+ * \r
+ * @since 1.0\r
+ * @author Tomohiro Otsuka\r
+ * @version $Id: XMLBuilderImpl.java,v 1.4.2.1 2005/01/21 22:15:07 otsuka Exp $\r
+ */\r
+public class XMLBuilderImpl implements XMLBuilder {\r
+\r
+ private String charset = "UTF-8";\r
+\r
+ /**\r
+ * コンストラクタ。\r
+ */\r
+ public XMLBuilderImpl() {}\r
+\r
+ /**\r
+ * コンストラクタ。\r
+ * 出力XMLファイルの文字コードを指定します。デフォルトはUTF-8。\r
+ * \r
+ * @param charset 出力XMLファイルの文字コード\r
+ */\r
+ public XMLBuilderImpl(String charset) {\r
+ super();\r
+ this.charset = charset;\r
+ }\r
+\r
+ /**\r
+ * 出力XMLファイルの文字コードを返します。\r
+ * \r
+ * @return 出力XMLファイルの文字コード\r
+ */\r
+ public String getCharset() {\r
+ return charset;\r
+ }\r
+\r
+ /**\r
+ * 出力XMLファイルの文字コードを指定します。デフォルトはUTF-8。\r
+ * \r
+ * @param charset 出力XMLファイルの文字コード\r
+ */\r
+ public void setCharset(String charset) {\r
+ this.charset = charset;\r
+ }\r
+\r
+ /**\r
+ * @see com.ozacc.mail.xml.XMLBuilder#buildDocument(com.ozacc.mail.Mail)\r
+ */\r
+ public Document buildDocument(Mail mail) throws XMLBuildException {\r
+ Document doc = createNewDocument();\r
+\r
+ /*DOMImplementation domImpl = doc.getImplementation();\r
+ DocumentType docType = domImpl.createDocumentType("mail", Mail.DOCTYPE_PUBLIC, Mail.DOCTYPE_SYSTEM);\r
+ doc.appendChild(docType);*/\r
+\r
+ Element mailElem = doc.createElement("mail");\r
+\r
+ // Return-Path\r
+ if (mail.getReturnPath() != null) {\r
+ InternetAddress returnPath = mail.getReturnPath();\r
+ Element returnPathElem = convertInternetAddressIntoElement(returnPath, "returnPath",\r
+ doc);\r
+ mailElem.appendChild(returnPathElem);\r
+ }\r
+\r
+ // From\r
+ if (mail.getFrom() != null) {\r
+ InternetAddress from = mail.getFrom();\r
+ Element fromElem = convertInternetAddressIntoElement(from, "from", doc);\r
+ mailElem.appendChild(fromElem);\r
+ }\r
+\r
+ if (mail.getTo().length > 0 || mail.getCc().length > 0 || mail.getBcc().length > 0) {\r
+ Element recipientsElem = doc.createElement("recipients");\r
+\r
+ // To\r
+ if (mail.getTo().length > 0) {\r
+ for (int i = 0; i < mail.getTo().length; i++) {\r
+ InternetAddress to = mail.getTo()[i];\r
+ Element toElem = convertInternetAddressIntoElement(to, "to", doc);\r
+ recipientsElem.appendChild(toElem);\r
+ }\r
+ }\r
+ // Cc\r
+ if (mail.getCc().length > 0) {\r
+ for (int i = 0; i < mail.getCc().length; i++) {\r
+ InternetAddress cc = mail.getCc()[i];\r
+ Element ccElem = convertInternetAddressIntoElement(cc, "cc", doc);\r
+ recipientsElem.appendChild(ccElem);\r
+ }\r
+ }\r
+ // Bcc\r
+ if (mail.getBcc().length > 0) {\r
+ for (int i = 0; i < mail.getBcc().length; i++) {\r
+ InternetAddress bcc = mail.getBcc()[i];\r
+ Element bccElem = convertInternetAddressIntoElement(bcc, "bcc", doc);\r
+ recipientsElem.appendChild(bccElem);\r
+ }\r
+ }\r
+ mailElem.appendChild(recipientsElem);\r
+ }\r
+\r
+ // Reply-To\r
+ if (mail.getReplyTo() != null) {\r
+ InternetAddress replyTo = mail.getReplyTo();\r
+ Element replyToElem = convertInternetAddressIntoElement(replyTo, "replyTo", doc);\r
+ mailElem.appendChild(replyToElem);\r
+ }\r
+\r
+ // Subject\r
+ if (mail.getSubject() != null) {\r
+ Element subjectElem = doc.createElement("subject");\r
+ subjectElem.appendChild(doc.createTextNode(mail.getSubject()));\r
+ mailElem.appendChild(subjectElem);\r
+ }\r
+\r
+ // Body\r
+ if (mail.getText() != null) {\r
+ Element bodyElem = doc.createElement("body");\r
+ bodyElem.appendChild(doc.createTextNode(mail.getText()));\r
+ mailElem.appendChild(bodyElem);\r
+ }\r
+\r
+ // Html\r
+ if (mail.isHtmlMail()) {\r
+ Element htmlElem = doc.createElement("html");\r
+ htmlElem.appendChild(doc.createCDATASection(mail.getHtmlText()));\r
+ mailElem.appendChild(htmlElem);\r
+ }\r
+\r
+ doc.appendChild(mailElem);\r
+\r
+ return doc;\r
+ }\r
+\r
+ public static Document createNewDocument() throws FactoryConfigurationError {\r
+ DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();\r
+ try {\r
+ DocumentBuilder db = dbf.newDocumentBuilder();\r
+ Document doc = db.newDocument();\r
+ return doc;\r
+ } catch (ParserConfigurationException e) {\r
+ // never be thrown\r
+ throw new XMLBuildException("", e);\r
+ }\r
+ }\r
+\r
+ private Element convertInternetAddressIntoElement(InternetAddress address, String elemName,\r
+ Document doc) {\r
+ Element element = doc.createElement(elemName);\r
+ element.setAttribute("email", address.getAddress());\r
+ if (address.getPersonal() != null) {\r
+ element.setAttribute("name", address.getPersonal());\r
+ }\r
+ return element;\r
+ }\r
+\r
+ /**\r
+ * 指定されたMailインスタンスからXMLドキュメントを生成し、\r
+ * 指定されたファイルに保存します。\r
+ * \r
+ * このメソッド内部で使用されるTransformerFactoryがスレッドセーフではないため、synchronzedメソッドになっています。\r
+ * \r
+ * @see com.ozacc.mail.xml.XMLBuilder#saveDocument(com.ozacc.mail.Mail, java.io.File)\r
+ * @see TransformerFactory\r
+ */\r
+ public synchronized void saveDocument(Mail mail, File destFile) throws XMLBuildException {\r
+ Document doc = buildDocument(mail);\r
+\r
+ Transformer t;\r
+ try {\r
+ t = TransformerFactory.newInstance().newTransformer();\r
+ } catch (Exception e) {\r
+ // never be thrown\r
+ throw new XMLBuildException(e.getMessage());\r
+ }\r
+ t.setOutputProperties(getOutputProperties());\r
+\r
+ DOMSource source = new DOMSource(doc);\r
+ StreamResult result = new StreamResult(destFile);\r
+ try {\r
+ t.transform(source, result);\r
+ } catch (TransformerException e) {\r
+ throw new XMLBuildException("XMLファイルの保存に失敗しました。", e);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * 出力プロパティを生成。\r
+ * @return 出力プロパティを設定したPropertiesインスタンス\r
+ */\r
+ private Properties getOutputProperties() {\r
+ Properties p = new Properties();\r
+ p.put(OutputKeys.ENCODING, charset);\r
+ p.put(OutputKeys.INDENT, "yes");\r
+ p.put(OutputKeys.DOCTYPE_PUBLIC, Mail.DOCTYPE_PUBLIC);\r
+ p.put(OutputKeys.DOCTYPE_SYSTEM, Mail.DOCTYPE_SYSTEM);\r
+ return p;\r
+ }\r
+\r
+}
\ No newline at end of file
--- /dev/null
+<HTML>\r
+<BODY>\r
+<code>com.ozacc.mail.xml</code>¥Ñ¥Ã¥±¡¼¥¸¤Î¥¤¥ó¥¿¡¼¥Õ¥§¡¼¥¹¤ò¼ÂÁõ¤·¤¿¥¯¥é¥¹¤òÄ󶡤·¤Þ¤¹¡£\r
+</BODY>\r
+</HTML>
\ No newline at end of file
--- /dev/null
+<HTML>\r
+<BODY>\r
+Mail¥¤¥ó¥¹¥¿¥ó¥¹¤«¤éXML¥É¥¥å¥á¥ó¥È¤òÀ¸À®¤¹¤ë¥¤¥ó¥¿¡¼¥Õ¥§¡¼¥¹¤òÄ󶡤·¤Þ¤¹¡£\r
+</BODY>\r
+</HTML>
\ No newline at end of file
--- /dev/null
+/*\r
+ * Dumbster: a dummy SMTP server.\r
+ * Copyright (C) 2003, Jason Paul Kitchen\r
+ * lilnottsman@yahoo.com\r
+ *\r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU Lesser General Public\r
+ * License as published by the Free Software Foundation; either\r
+ * version 2.1 of the License, or (at your option) any later version.\r
+ *\r
+ * This library is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
+ * Lesser General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this library; if not, write to the Free Software\r
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
+ */\r
+package com.dumbster.smtp;\r
+\r
+import java.io.BufferedReader;\r
+import java.io.IOException;\r
+import java.io.InputStreamReader;\r
+import java.io.InterruptedIOException;\r
+import java.io.PrintWriter;\r
+import java.net.ServerSocket;\r
+import java.net.Socket;\r
+import java.util.ArrayList;\r
+import java.util.Iterator;\r
+import java.util.List;\r
+\r
+/**\r
+ * Dummy SMTP server for testing purposes.\r
+ */\r
+public class SimpleSmtpServer implements Runnable {\r
+\r
+ /** Stores all of the email received since this instance started up. */\r
+ private List receivedMail;\r
+\r
+ /** Default SMTP port is 25. */\r
+ public static final int DEFAULT_SMTP_PORT = 25;\r
+\r
+ /** Indicates whether this server is stopped or not. */\r
+ private volatile boolean stopped = true;\r
+\r
+ /** Indicates if a stop request has been sent to this server. */\r
+ private volatile boolean doStop = false;\r
+\r
+ /** Port the server listens on - set to the default SMTP port initially. */\r
+ private int port = DEFAULT_SMTP_PORT;\r
+\r
+ /**\r
+ * Constructor.\r
+ */\r
+ public SimpleSmtpServer(int port) {\r
+ receivedMail = new ArrayList();\r
+ this.port = port;\r
+ }\r
+\r
+ /**\r
+ * Main loop of the SMTP server.\r
+ */\r
+ public void run() {\r
+ stopped = false;\r
+ ServerSocket serverSocket = null;\r
+ try {\r
+ serverSocket = new ServerSocket(port);\r
+ serverSocket.setSoTimeout(500); // Block for maximum of 1.5 seconds\r
+\r
+ // Server: loop until stopped\r
+ while (!doStop) {\r
+ Socket socket = null;\r
+ try {\r
+ socket = serverSocket.accept();\r
+ } catch (InterruptedIOException iioe) {\r
+ if (socket != null) {\r
+ socket.close();\r
+ }\r
+ continue; // Non-blocking socket timeout occurred: try accept() again\r
+ }\r
+\r
+ // Get the input and output streams\r
+ BufferedReader input = new BufferedReader(new InputStreamReader(socket\r
+ .getInputStream()));\r
+ PrintWriter out = new PrintWriter(socket.getOutputStream());\r
+\r
+ // Initialize the state machine\r
+ SmtpState smtpState = SmtpState.CONNECT;\r
+ SmtpRequest smtpRequest = new SmtpRequest(SmtpActionType.CONNECT, "", smtpState);\r
+\r
+ // Execute the connection request\r
+ SmtpResponse smtpResponse = smtpRequest.execute();\r
+\r
+ // Send initial response\r
+ sendResponse(out, smtpResponse);\r
+ smtpState = smtpResponse.getNextState();\r
+\r
+ SmtpMessage msg = new SmtpMessage();\r
+\r
+ while (smtpState != SmtpState.CONNECT) {\r
+ String line = input.readLine();\r
+\r
+ if (line == null) {\r
+ break;\r
+ }\r
+ // Create request from client input and current state\r
+ SmtpRequest request = SmtpRequest.createRequest(line, smtpState);\r
+ // Execute request and create response object\r
+ SmtpResponse response = request.execute();\r
+ // Move to next internal state\r
+ smtpState = response.getNextState();\r
+ // Send reponse to client\r
+ sendResponse(out, response);\r
+\r
+ // Store input in message\r
+ msg.store(response, request.getParams());\r
+ }\r
+\r
+ receivedMail.add(msg);\r
+ socket.close();\r
+ }\r
+ } catch (Exception e) {\r
+ // e.printStackTrace();\r
+ } finally {\r
+ if (serverSocket != null) {\r
+ try {\r
+ serverSocket.close();\r
+ } catch (IOException e) {\r
+ e.printStackTrace();\r
+ }\r
+ }\r
+ }\r
+ stopped = true;\r
+ }\r
+\r
+ /**\r
+ * Send response to client.\r
+ * @param out socket output stream\r
+ * @param smtpResponse response object\r
+ */\r
+ private void sendResponse(PrintWriter out, SmtpResponse smtpResponse) {\r
+ if (smtpResponse.getCode() > 0) {\r
+ out.print(smtpResponse.getCode() + " " + smtpResponse.getMessage() + "\r\n");\r
+ out.flush();\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Get email received by this instance since start up.\r
+ * @return List of String\r
+ */\r
+ public Iterator getReceivedEmail() {\r
+ return receivedMail.iterator();\r
+ }\r
+\r
+ /**\r
+ * Get the number of messages received.\r
+ * @return size of received email list\r
+ */\r
+ public int getReceievedEmailSize() {\r
+ return receivedMail.size();\r
+ }\r
+\r
+ /**\r
+ * Forces the server to stop after processing the current request.\r
+ */\r
+ public void stop() {\r
+ doStop = true;\r
+ while (!isStopped()) {} // Wait for email server to stop\r
+ }\r
+\r
+ /**\r
+ * Indicates whether this server is stopped or not.\r
+ * @return true iff this server is stopped\r
+ */\r
+ public boolean isStopped() {\r
+ return stopped;\r
+ }\r
+\r
+ /**\r
+ * Creates an instance of SimpleSmtpServer and starts it. Will listen on the default port.\r
+ * @return a reference to the SMTP server\r
+ */\r
+ public static SimpleSmtpServer start() {\r
+ return start(DEFAULT_SMTP_PORT);\r
+ }\r
+\r
+ /**\r
+ * Creates an instance of SimpleSmtpServer and starts it.\r
+ * @param port port number the server should listen to\r
+ * @return a reference to the SMTP server\r
+ */\r
+ public static SimpleSmtpServer start(int port) {\r
+ SimpleSmtpServer server = new SimpleSmtpServer(port);\r
+ Thread t = new Thread(server);\r
+ t.start();\r
+ return server;\r
+ }\r
+\r
+}
\ No newline at end of file
--- /dev/null
+/*\r
+ * Dumbster: a dummy SMTP server.\r
+ * Copyright (C) 2003, Jason Paul Kitchen\r
+ * lilnottsman@yahoo.com\r
+ *\r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU Lesser General Public\r
+ * License as published by the Free Software Foundation; either\r
+ * version 2.1 of the License, or (at your option) any later version.\r
+ *\r
+ * This library is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
+ * Lesser General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this library; if not, write to the Free Software\r
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
+ */\r
+package com.dumbster.smtp;\r
+\r
+/**\r
+ * Represents an SMTP action or command.\r
+ */\r
+public class SmtpActionType {\r
+\r
+ /** Internal value for the action type. */\r
+ private byte value;\r
+\r
+ /** Internal representation of the CONNECT action. */\r
+ private static final byte CONNECT_BYTE = 1;\r
+\r
+ /** Internal representation of the EHLO action. */\r
+ private static final byte EHLO_BYTE = 2;\r
+\r
+ /** Internal representation of the MAIL FROM action. */\r
+ private static final byte MAIL_BYTE = 3;\r
+\r
+ /** Internal representation of the RCPT action. */\r
+ private static final byte RCPT_BYTE = 4;\r
+\r
+ /** Internal representation of the DATA action. */\r
+ private static final byte DATA_BYTE = 5;\r
+\r
+ /** Internal representation of the DATA END (.) action. */\r
+ private static final byte DATA_END_BYTE = 6;\r
+\r
+ /** Internal representation of the QUIT action. */\r
+ private static final byte QUIT_BYTE = 7;\r
+\r
+ /** Internal representation of an unrecognized action: body text gets this action type. */\r
+ private static final byte UNREC_BYTE = 8;\r
+\r
+ /** Internal representation of the blank line action: separates headers and body text. */\r
+ private static final byte BLANK_LINE_BYTE = 9;\r
+\r
+ /** Internal representation of the stateless RSET action. */\r
+ private static final byte RSET_BYTE = -1;\r
+\r
+ /** Internal representation of the stateless VRFY action. */\r
+ private static final byte VRFY_BYTE = -2;\r
+\r
+ /** Internal representation of the stateless EXPN action. */\r
+ private static final byte EXPN_BYTE = -3;\r
+\r
+ /** Internal representation of the stateless HELP action. */\r
+ private static final byte HELP_BYTE = -4;\r
+\r
+ /** Internal representation of the stateless NOOP action. */\r
+ private static final byte NOOP_BYTE = -5;\r
+\r
+ /** CONNECT action. */\r
+ public static final SmtpActionType CONNECT = new SmtpActionType(CONNECT_BYTE);\r
+\r
+ /** EHLO action. */\r
+ public static final SmtpActionType EHLO = new SmtpActionType(EHLO_BYTE);\r
+\r
+ /** MAIL action. */\r
+ public static final SmtpActionType MAIL = new SmtpActionType(MAIL_BYTE);\r
+\r
+ /** RCPT action. */\r
+ public static final SmtpActionType RCPT = new SmtpActionType(RCPT_BYTE);\r
+\r
+ /** DATA action. */\r
+ public static final SmtpActionType DATA = new SmtpActionType(DATA_BYTE);\r
+\r
+ /** "." action. */\r
+ public static final SmtpActionType DATA_END = new SmtpActionType(DATA_END_BYTE);\r
+\r
+ /** Body text action. */\r
+ public static final SmtpActionType UNRECOG = new SmtpActionType(UNREC_BYTE);\r
+\r
+ /** QUIT action. */\r
+ public static final SmtpActionType QUIT = new SmtpActionType(QUIT_BYTE);\r
+\r
+ /** Header/body separator action. */\r
+ public static final SmtpActionType BLANK_LINE = new SmtpActionType(BLANK_LINE_BYTE);\r
+\r
+ /** Stateless RSET action. */\r
+ public static final SmtpActionType RSET = new SmtpActionType(RSET_BYTE);\r
+\r
+ /** Stateless VRFY action. */\r
+ public static final SmtpActionType VRFY = new SmtpActionType(VRFY_BYTE);\r
+\r
+ /** Stateless EXPN action. */\r
+ public static final SmtpActionType EXPN = new SmtpActionType(EXPN_BYTE);\r
+\r
+ /** Stateless HELP action. */\r
+ public static final SmtpActionType HELP = new SmtpActionType(HELP_BYTE);\r
+\r
+ /** Stateless NOOP action. */\r
+ public static final SmtpActionType NOOP = new SmtpActionType(NOOP_BYTE);\r
+\r
+ /**\r
+ * Create a new SMTP action type. Private to ensure no invalid values.\r
+ * @param value one of the _BYTE values\r
+ */\r
+ private SmtpActionType(byte value) {\r
+ this.value = value;\r
+ }\r
+\r
+ /**\r
+ * Indicates whether the action is stateless or not.\r
+ * @return true iff the action is stateless\r
+ */\r
+ public boolean isStateless() {\r
+ return value < 0;\r
+ }\r
+\r
+ /**\r
+ * String representation of this SMTP action type.\r
+ * @return a String\r
+ */\r
+ public String toString() {\r
+ switch (value) {\r
+ case CONNECT_BYTE:\r
+ return "Connect";\r
+ case EHLO_BYTE:\r
+ return "EHLO";\r
+ case MAIL_BYTE:\r
+ return "MAIL";\r
+ case RCPT_BYTE:\r
+ return "RCPT";\r
+ case DATA_BYTE:\r
+ return "DATA";\r
+ case DATA_END_BYTE:\r
+ return ".";\r
+ case QUIT_BYTE:\r
+ return "QUIT";\r
+ case RSET_BYTE:\r
+ return "RSET";\r
+ case VRFY_BYTE:\r
+ return "VRFY";\r
+ case EXPN_BYTE:\r
+ return "EXPN";\r
+ case HELP_BYTE:\r
+ return "HELP";\r
+ case NOOP_BYTE:\r
+ return "NOOP";\r
+ case UNREC_BYTE:\r
+ return "Unrecognized command / data";\r
+ case BLANK_LINE_BYTE:\r
+ return "Blank line";\r
+ default:\r
+ return "Unknown";\r
+ }\r
+ }\r
+}
\ No newline at end of file
--- /dev/null
+/*\r
+ * Dumbster: a dummy SMTP server.\r
+ * Copyright (C) 2003, Jason Paul Kitchen\r
+ * lilnottsman@yahoo.com\r
+ *\r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU Lesser General Public\r
+ * License as published by the Free Software Foundation; either\r
+ * version 2.1 of the License, or (at your option) any later version.\r
+ *\r
+ * This library is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
+ * Lesser General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this library; if not, write to the Free Software\r
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
+ */\r
+package com.dumbster.smtp;\r
+\r
+import java.util.ArrayList;\r
+import java.util.HashMap;\r
+import java.util.Iterator;\r
+import java.util.List;\r
+import java.util.Map;\r
+\r
+/**\r
+ * Container for a complete SMTP message - headers and message body.\r
+ */\r
+public class SmtpMessage {\r
+\r
+ /** Headers: Map of List of String hashed on header name. */\r
+ private Map headers;\r
+\r
+ /** Message body. */\r
+ private StringBuffer body;\r
+\r
+ /**\r
+ * Constructor. Initializes headers Map and body buffer.\r
+ */\r
+ public SmtpMessage() {\r
+ headers = new HashMap();\r
+ body = new StringBuffer();\r
+ }\r
+\r
+ /**\r
+ * Update the headers or body depending on the SmtpResponse object and line of input.\r
+ * @param response SmtpResponse object\r
+ * @param params remainder of input line after SMTP command has been removed\r
+ */\r
+ public void store(SmtpResponse response, String params) {\r
+ if (params != null) {\r
+ if (SmtpState.DATA_HDR == response.getNextState()) {\r
+ int headerNameEnd = params.indexOf(':');\r
+ if (headerNameEnd >= 0) {\r
+ String name = params.substring(0, headerNameEnd).trim();\r
+ String value = params.substring(headerNameEnd + 1).trim();\r
+ addHeader(name, value);\r
+ }\r
+ } else if (SmtpState.DATA_BODY == response.getNextState()) {\r
+ body.append(params);\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Get an Iterator over the header names.\r
+ * @return an Iterator over the set of header names (String)\r
+ */\r
+ public Iterator getHeaderNames() {\r
+ return headers.keySet().iterator();\r
+ }\r
+\r
+ /**\r
+ * Get the value(s) associated with the given header name.\r
+ * @param name header name\r
+ * @return value(s) associated with the header name\r
+ */\r
+ public String[] getHeaderValues(String name) {\r
+ List values = (List)headers.get(name);\r
+ if (values == null) {\r
+ return new String[0];\r
+ } else {\r
+ return (String[])values.toArray(new String[0]);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Get the first values associated with a given header name.\r
+ * @param name header name\r
+ * @return first value associated with the header name\r
+ */\r
+ public String getHeaderValue(String name) {\r
+ List values = (List)headers.get(name);\r
+ if (values == null) {\r
+ return null;\r
+ } else {\r
+ return (String)values.iterator().next();\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Get the message body.\r
+ * @return message body\r
+ */\r
+ public String getBody() {\r
+ return body.toString();\r
+ }\r
+\r
+ /**\r
+ * Adds a header to the Map.\r
+ * @param name header name\r
+ * @param value header value\r
+ */\r
+ private void addHeader(String name, String value) {\r
+ List valueList = (List)headers.get(name);\r
+ if (valueList == null) {\r
+ valueList = new ArrayList();\r
+ headers.put(name, valueList);\r
+ }\r
+ valueList.add(value);\r
+ }\r
+\r
+ /**\r
+ * String representation of the SmtpMessage.\r
+ * @return a String\r
+ */\r
+ public String toString() {\r
+ StringBuffer msg = new StringBuffer();\r
+ for (Iterator i = headers.keySet().iterator(); i.hasNext();) {\r
+ String name = (String)i.next();\r
+ List values = (List)headers.get(name);\r
+ for (Iterator j = values.iterator(); j.hasNext();) {\r
+ String value = (String)j.next();\r
+ msg.append(name);\r
+ msg.append(": ");\r
+ msg.append(value);\r
+ msg.append('\n');\r
+ }\r
+ }\r
+ msg.append('\n');\r
+ msg.append(body);\r
+ msg.append('\n');\r
+ return msg.toString();\r
+ }\r
+}
\ No newline at end of file
--- /dev/null
+/*\r
+ * Dumbster: a dummy SMTP server.\r
+ * Copyright (C) 2003, Jason Paul Kitchen\r
+ * lilnottsman@yahoo.com\r
+ *\r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU Lesser General Public\r
+ * License as published by the Free Software Foundation; either\r
+ * version 2.1 of the License, or (at your option) any later version.\r
+ *\r
+ * This library is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
+ * Lesser General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this library; if not, write to the Free Software\r
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
+ */\r
+package com.dumbster.smtp;\r
+\r
+/**\r
+ * Contains an SMTP client request.\r
+ */\r
+public class SmtpRequest {\r
+\r
+ /** SMTP action received from client. */\r
+ private SmtpActionType action;\r
+\r
+ /** Current state of the SMTP state table. */\r
+ private SmtpState state;\r
+\r
+ /** Additional information passed from the client with the SMTP action. */\r
+ private String params;\r
+\r
+ /**\r
+ * Create a new SMTP client request.\r
+ * @param actionType type of action/command\r
+ * @param params remainder of command line once command is removed\r
+ * @param state current SMTP server state\r
+ */\r
+ public SmtpRequest(SmtpActionType actionType, String params, SmtpState state) {\r
+ this.action = actionType;\r
+ this.state = state;\r
+ this.params = params;\r
+ }\r
+\r
+ /**\r
+ * Execute the SMTP request returning a response. This method models the state transition table for the SMTP server.\r
+ * @return reponse to the request\r
+ */\r
+ public SmtpResponse execute() {\r
+ SmtpResponse response = null;\r
+ if (action.isStateless()) {\r
+ if (SmtpActionType.EXPN == action || SmtpActionType.VRFY == action) {\r
+ response = new SmtpResponse(252, "Not supported", this.state);\r
+ } else if (SmtpActionType.HELP == action) {\r
+ response = new SmtpResponse(211, "No help available", this.state);\r
+ } else if (SmtpActionType.NOOP == action) {\r
+ response = new SmtpResponse(250, "OK", this.state);\r
+ } else if (SmtpActionType.VRFY == action) {\r
+ response = new SmtpResponse(252, "Not supported", this.state);\r
+ } else if (SmtpActionType.RSET == action) {\r
+ response = new SmtpResponse(250, "OK", SmtpState.GREET);\r
+ } else {\r
+ response = new SmtpResponse(500, "Command not recognized", this.state);\r
+ }\r
+ } else { // Stateful commands\r
+ if (SmtpActionType.CONNECT == action) {\r
+ if (SmtpState.CONNECT == state) {\r
+ response = new SmtpResponse(220, "localhost Dumbster SMTP service ready",\r
+ SmtpState.GREET);\r
+ } else {\r
+ response = new SmtpResponse(503, "Bad sequence of commands: " + action,\r
+ this.state);\r
+ }\r
+ } else if (SmtpActionType.EHLO == action) {\r
+ if (SmtpState.GREET == state) {\r
+ response = new SmtpResponse(250, "OK", SmtpState.MAIL);\r
+ } else {\r
+ response = new SmtpResponse(503, "Bad sequence of commands: " + action,\r
+ this.state);\r
+ }\r
+ } else if (SmtpActionType.MAIL == action) {\r
+ if (SmtpState.MAIL == state) {\r
+ response = new SmtpResponse(250, "OK", SmtpState.RCPT);\r
+ } else {\r
+ response = new SmtpResponse(503, "Bad sequence of commands: " + action,\r
+ this.state);\r
+ }\r
+ } else if (SmtpActionType.RCPT == action) {\r
+ if (SmtpState.RCPT == state) {\r
+ response = new SmtpResponse(250, "OK", this.state);\r
+ } else {\r
+ response = new SmtpResponse(503, "Bad sequence of commands: " + action,\r
+ this.state);\r
+ }\r
+ } else if (SmtpActionType.DATA == action) {\r
+ if (SmtpState.RCPT == state) {\r
+ response = new SmtpResponse(354, "Start mail input; end with <CRLF>.<CRLF>",\r
+ SmtpState.DATA_HDR);\r
+ } else {\r
+ response = new SmtpResponse(503, "Bad sequence of commands: " + action,\r
+ this.state);\r
+ }\r
+ } else if (SmtpActionType.UNRECOG == action) {\r
+ if (SmtpState.DATA_HDR == state || SmtpState.DATA_BODY == state) {\r
+ response = new SmtpResponse(-1, "", this.state);\r
+ } else {\r
+ response = new SmtpResponse(503, "Bad sequence of commands: " + action,\r
+ this.state);\r
+ }\r
+ } else if (SmtpActionType.DATA_END == action) {\r
+ if (SmtpState.DATA_HDR == state || SmtpState.DATA_BODY == state) {\r
+ response = new SmtpResponse(250, "OK", SmtpState.QUIT);\r
+ } else {\r
+ response = new SmtpResponse(503, "Bad sequence of commands: " + action,\r
+ this.state);\r
+ }\r
+ } else if (SmtpActionType.BLANK_LINE == action) {\r
+ if (SmtpState.DATA_HDR == state) {\r
+ response = new SmtpResponse(-1, "", SmtpState.DATA_BODY);\r
+ } else if (SmtpState.DATA_BODY == state) {\r
+ response = new SmtpResponse(-1, "", this.state);\r
+ } else {\r
+ response = new SmtpResponse(503, "Bad sequence of commands: " + action,\r
+ this.state);\r
+ }\r
+ } else if (SmtpActionType.QUIT == action) {\r
+ if (SmtpState.QUIT == state) {\r
+ response = new SmtpResponse(250, "OK", SmtpState.CONNECT);\r
+ } else {\r
+ response = new SmtpResponse(503, "Bad sequence of commands: " + action,\r
+ this.state);\r
+ }\r
+ } else {\r
+ response = new SmtpResponse(500, "Command not recognized", this.state);\r
+ }\r
+ }\r
+ return response;\r
+ }\r
+\r
+ /**\r
+ * Create an SMTP request object given a line of the input stream from the client and the current internal state.\r
+ * @param s line of input\r
+ * @param state current state\r
+ * @return a populated SmtpRequest object\r
+ */\r
+ public static SmtpRequest createRequest(String s, SmtpState state) {\r
+ SmtpActionType action = null;\r
+ String params = null;\r
+\r
+ if (state == SmtpState.DATA_HDR) {\r
+ if (s.equals(".")) {\r
+ action = SmtpActionType.DATA_END;\r
+ } else if (s.length() < 1) {\r
+ action = SmtpActionType.BLANK_LINE;\r
+ } else {\r
+ action = SmtpActionType.UNRECOG;\r
+ params = s;\r
+ }\r
+ } else if (state == SmtpState.DATA_BODY) {\r
+ if (s.equals(".")) {\r
+ action = SmtpActionType.DATA_END;\r
+ } else {\r
+ action = SmtpActionType.UNRECOG;\r
+ if (s.length() < 1) {\r
+ params = "\n";\r
+ } else {\r
+ params = s;\r
+ }\r
+ }\r
+ } else {\r
+ String su = s.toUpperCase();\r
+ if (su.startsWith("EHLO ") || su.startsWith("HELO")) {\r
+ action = SmtpActionType.EHLO;\r
+ params = s.substring(5);\r
+ } else if (su.startsWith("MAIL FROM:")) {\r
+ action = SmtpActionType.MAIL;\r
+ params = s.substring(10);\r
+ } else if (su.startsWith("RCPT TO:")) {\r
+ action = SmtpActionType.RCPT;\r
+ params = s.substring(8);\r
+ } else if (su.startsWith("DATA")) {\r
+ action = SmtpActionType.DATA;\r
+ } else if (su.startsWith("QUIT")) {\r
+ action = SmtpActionType.QUIT;\r
+ } else if (su.startsWith("RSET")) {\r
+ action = SmtpActionType.RSET;\r
+ } else if (su.startsWith("NOOP")) {\r
+ action = SmtpActionType.NOOP;\r
+ } else if (su.startsWith("EXPN")) {\r
+ action = SmtpActionType.EXPN;\r
+ } else if (su.startsWith("VRFY")) {\r
+ action = SmtpActionType.VRFY;\r
+ } else if (su.startsWith("HELP")) {\r
+ action = SmtpActionType.HELP;\r
+ }\r
+ }\r
+\r
+ SmtpRequest req = new SmtpRequest(action, params, state);\r
+ return req;\r
+ }\r
+\r
+ /**\r
+ * Get the parameters of this request (remainder of command line once the command is removed.\r
+ * @return parameters\r
+ */\r
+ public String getParams() {\r
+ return params;\r
+ }\r
+}
\ No newline at end of file
--- /dev/null
+/*\r
+ * Dumbster: a dummy SMTP server.\r
+ * Copyright (C) 2003, Jason Paul Kitchen\r
+ * lilnottsman@yahoo.com\r
+ *\r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU Lesser General Public\r
+ * License as published by the Free Software Foundation; either\r
+ * version 2.1 of the License, or (at your option) any later version.\r
+ *\r
+ * This library is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
+ * Lesser General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this library; if not, write to the Free Software\r
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
+ */\r
+package com.dumbster.smtp;\r
+\r
+/**\r
+ * SMTP response container.\r
+ */\r
+public class SmtpResponse {\r
+\r
+ /** Response code - see RFC-2821. */\r
+ private int code;\r
+\r
+ /** Response message. */\r
+ private String message;\r
+\r
+ /** New state of the SMTP server once the request has been executed. */\r
+ private SmtpState nextState;\r
+\r
+ /**\r
+ * Constructor.\r
+ * @param code response code\r
+ * @param message response message\r
+ * @param next next state of the SMTP server\r
+ */\r
+ public SmtpResponse(int code, String message, SmtpState next) {\r
+ this.code = code;\r
+ this.message = message;\r
+ this.nextState = next;\r
+ }\r
+\r
+ /**\r
+ * Get the response code.\r
+ * @return response code\r
+ */\r
+ public int getCode() {\r
+ return code;\r
+ }\r
+\r
+ /**\r
+ * Get the response message.\r
+ * @return response message\r
+ */\r
+ public String getMessage() {\r
+ return message;\r
+ }\r
+\r
+ /**\r
+ * Get the next SMTP server state.\r
+ * @return state\r
+ */\r
+ public SmtpState getNextState() {\r
+ return nextState;\r
+ }\r
+}
\ No newline at end of file
--- /dev/null
+/*\r
+ * Dumbster: a dummy SMTP server.\r
+ * Copyright (C) 2003, Jason Paul Kitchen\r
+ * lilnottsman@yahoo.com\r
+ *\r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU Lesser General Public\r
+ * License as published by the Free Software Foundation; either\r
+ * version 2.1 of the License, or (at your option) any later version.\r
+ *\r
+ * This library is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
+ * Lesser General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this library; if not, write to the Free Software\r
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
+ */\r
+package com.dumbster.smtp;\r
+\r
+/**\r
+ * SMTP server state.\r
+ */\r
+public class SmtpState {\r
+\r
+ /** Internal representation of the state. */\r
+ private byte value;\r
+\r
+ /** Internal representation of the CONNECT state. */\r
+ private static final byte CONNECT_BYTE = 1;\r
+\r
+ /** Internal representation of the GREET state. */\r
+ private static final byte GREET_BYTE = 2;\r
+\r
+ /** Internal representation of the MAIL state. */\r
+ private static final byte MAIL_BYTE = 3;\r
+\r
+ /** Internal representation of the RCPT state. */\r
+ private static final byte RCPT_BYTE = 4;\r
+\r
+ /** Internal representation of the DATA_HEADER state. */\r
+ private static final byte DATA_HEADER_BYTE = 5;\r
+\r
+ /** Internal representation of the DATA_BODY state. */\r
+ private static final byte DATA_BODY_BYTE = 6;\r
+\r
+ /** Internal representation of the QUIT state. */\r
+ private static final byte QUIT_BYTE = 7;\r
+\r
+ /** CONNECT state: waiting for a client connection. */\r
+ public static final SmtpState CONNECT = new SmtpState(CONNECT_BYTE);\r
+\r
+ /** GREET state: wating for a ELHO message. */\r
+ public static final SmtpState GREET = new SmtpState(GREET_BYTE);\r
+\r
+ /** MAIL state: waiting for the MAIL FROM: command. */\r
+ public static final SmtpState MAIL = new SmtpState(MAIL_BYTE);\r
+\r
+ /** RCPT state: waiting for a RCPT <email address> command. */\r
+ public static final SmtpState RCPT = new SmtpState(RCPT_BYTE);\r
+\r
+ /** Waiting for headers. */\r
+ public static final SmtpState DATA_HDR = new SmtpState(DATA_HEADER_BYTE);\r
+\r
+ /** Processing body text. */\r
+ public static final SmtpState DATA_BODY = new SmtpState(DATA_BODY_BYTE);\r
+\r
+ /** End of client transmission. */\r
+ public static final SmtpState QUIT = new SmtpState(QUIT_BYTE);\r
+\r
+ /**\r
+ * Create a new SmtpState object. Private to ensure that only valid states can be created.\r
+ * @param value one of the _BYTE values.\r
+ */\r
+ private SmtpState(byte value) {\r
+ this.value = value;\r
+ }\r
+\r
+ /**\r
+ * String representation of this SmtpState.\r
+ * @return a String\r
+ */\r
+ public String toString() {\r
+ switch (value) {\r
+ case CONNECT_BYTE:\r
+ return "CONNECT";\r
+ case GREET_BYTE:\r
+ return "GREET";\r
+ case MAIL_BYTE:\r
+ return "MAIL";\r
+ case RCPT_BYTE:\r
+ return "RCPT";\r
+ case DATA_HEADER_BYTE:\r
+ return "DATA_HDR";\r
+ case DATA_BODY_BYTE:\r
+ return "DATA_BODY";\r
+ case QUIT_BYTE:\r
+ return "QUIT";\r
+ default:\r
+ return "Unknown";\r
+ }\r
+ }\r
+}
\ No newline at end of file
--- /dev/null
+package com.ozacc.mail.fetch.impl;\r
+\r
+import junit.framework.TestCase;\r
+\r
+import org.apache.log4j.BasicConfigurator;\r
+\r
+/**\r
+ * FetchMailImplクラスのテストケース。実際にメールサーバから受信して確認するテストです。\r
+ * \r
+ * @since 1.2\r
+ * @author Tomohiro Otsuka\r
+ * @version $Id: FetchMailImplRealTest.java,v 1.1.2.5 2005/01/23 06:52:04 otsuka Exp $\r
+ */\r
+public class FetchMailImplRealTest extends TestCase {\r
+\r
+ private String username = "username";\r
+\r
+ private String password = "password";\r
+\r
+ private String protocol = "pop3";\r
+\r
+ private String host = "localhost";\r
+\r
+ private FetchMailImpl fetchMail;\r
+\r
+ /*\r
+ * @see TestCase#setUp()\r
+ */\r
+ protected void setUp() throws Exception {\r
+ super.setUp();\r
+ BasicConfigurator.configure();\r
+\r
+ fetchMail = new FetchMailImpl();\r
+ fetchMail.setHost(host);\r
+ fetchMail.setProtocol(protocol);\r
+ fetchMail.setUsername(username);\r
+ fetchMail.setPassword(password);\r
+ }\r
+\r
+ /*\r
+ * @see TestCase#tearDown()\r
+ */\r
+ protected void tearDown() throws Exception {\r
+ super.tearDown();\r
+ BasicConfigurator.resetConfiguration();\r
+ }\r
+\r
+ /*public void testGetMails() throws Exception {\r
+ ReceivedMail[] mails = fetchMail.getMails(true);\r
+ for (int i = 0; i < mails.length; i++) {\r
+ ReceivedMail mail = mails[i];\r
+ }\r
+ }*/\r
+\r
+ /**\r
+ * テストケースがひとつもないとビルド時にエラーになるので、ダミー。\r
+ */\r
+ public void testDummy() {\r
+ assertTrue(true);\r
+ }\r
+}
\ No newline at end of file
--- /dev/null
+package com.ozacc.mail.fetch.impl;\r
+\r
+import junit.framework.TestCase;\r
+\r
+import org.apache.log4j.BasicConfigurator;\r
+\r
+/**\r
+ * \r
+ * @since 1.2\r
+ * @author Tomohiro Otsuka\r
+ * @version $Id: FetchMailProImplRealTest.java,v 1.1.2.3 2005/01/18 07:29:31 otsuka Exp $\r
+ */\r
+public class FetchMailProImplRealTest extends TestCase {\r
+\r
+ private String username = "username";\r
+\r
+ private String password = "password";\r
+\r
+ private String protocol = "pop3";\r
+\r
+ private String host = "localhost";\r
+\r
+ private FetchMailProImpl fetchMail;\r
+\r
+ /*\r
+ * @see TestCase#setUp()\r
+ */\r
+ protected void setUp() throws Exception {\r
+ super.setUp();\r
+ BasicConfigurator.configure();\r
+\r
+ fetchMail = new FetchMailProImpl();\r
+ fetchMail.setHost(host);\r
+ fetchMail.setProtocol(protocol);\r
+ fetchMail.setUsername(username);\r
+ fetchMail.setPassword(password);\r
+ }\r
+\r
+ /*\r
+ * @see TestCase#tearDown()\r
+ */\r
+ protected void tearDown() throws Exception {\r
+ super.tearDown();\r
+ BasicConfigurator.resetConfiguration();\r
+ }\r
+\r
+ /*public void testGetMails() throws Exception {\r
+ fetchMail.connect();\r
+ ReceivedMail[] mails = fetchMail.getMails(false);\r
+ fetchMail.disconnect();\r
+ }*/\r
+\r
+ /**\r
+ * テストケースがひとつもないとビルド時にエラーになるので、ダミー。\r
+ */\r
+ public void testDummy() {\r
+ assertTrue(true);\r
+ }\r
+}
\ No newline at end of file
--- /dev/null
+package com.ozacc.mail.impl;\r
+\r
+import java.io.File;\r
+\r
+import javax.mail.internet.InternetAddress;\r
+\r
+import junit.framework.TestCase;\r
+\r
+import org.apache.log4j.BasicConfigurator;\r
+import org.apache.velocity.VelocityContext;\r
+\r
+import com.ozacc.mail.Mail;\r
+import com.ozacc.mail.MailBuildException;\r
+\r
+/**\r
+ * XMLMailBuilderのテストケース。\r
+ * \r
+ * @author Tomohiro Otsuka\r
+ * @version $Id: JDomXMLMailBuilderTest.java,v 1.3.2.2 2005/02/01 20:38:14 otsuka Exp $\r
+ */\r
+public class JDomXMLMailBuilderTest extends TestCase {\r
+\r
+ private JDomXMLMailBuilder builder;\r
+\r
+ protected void setUp() throws Exception {\r
+ super.setUp();\r
+ BasicConfigurator.configure();\r
+\r
+ builder = new JDomXMLMailBuilder();\r
+ }\r
+\r
+ protected void tearDown() throws Exception {\r
+ super.tearDown();\r
+ BasicConfigurator.resetConfiguration();\r
+ }\r
+\r
+ public final void testBuildMailCDATA() throws Exception {\r
+ String classPath = "/com/ozacc/mail/test-mail6-cdata.xml";\r
+\r
+ String expectedBody = "これはCDATAのテキストです。";\r
+\r
+ Mail result = builder.buildMail(classPath);\r
+\r
+ assertEquals(expectedBody, result.getText());\r
+ }\r
+\r
+ /*\r
+ * Class under test for Mail buildMail(String)\r
+ * 存在しないファイルのパスを指定して失敗。\r
+ */\r
+ public final void testBuildMailFromClassPathNotExist() throws Exception {\r
+ String classPath = "/com/ozacc/mail/testtest-mail1.xml";\r
+ try {\r
+ Mail result = builder.buildMail(classPath);\r
+ fail("This should never be called.");\r
+ } catch (MailBuildException expected) {\r
+ // success\r
+ }\r
+ }\r
+\r
+ /*\r
+ * Class under test for Mail buildMail(File)\r
+ * 存在しないファイルを指定して失敗\r
+ */\r
+ public final void testBuildMailFromFileNotExist() throws Exception {\r
+ String path = "src/test/com/ozacc/mail/testtest-mail1.xml";\r
+ File file = new File(path);\r
+ try {\r
+ Mail result = builder.buildMail(file);\r
+ fail("This should never be called.");\r
+ } catch (MailBuildException expected) {\r
+ // success\r
+ }\r
+ }\r
+\r
+ /*\r
+ * Class under test for Mail buildMail(String)\r
+ * DTD違反のXMLのため失敗。\r
+ */\r
+ public final void testBuildMailFromClassPathInvalidXML() throws Exception {\r
+ String classPath = "/com/ozacc/mail/test-mail2-invalid.xml";\r
+ try {\r
+ Mail result = builder.buildMail(classPath);\r
+ fail("This should never be called.");\r
+ } catch (MailBuildException expected) {\r
+ // success\r
+ }\r
+ }\r
+\r
+ /*\r
+ * Class under test for Mail buildMail(String)\r
+ * XMLファイルのクラスパスからMailインスタンスを生成。\r
+ */\r
+ public final void testBuildMailFromClassPath() throws Exception {\r
+ String classPath = "/com/ozacc/mail/test-mail1.xml";\r
+\r
+ String subject = "XMLMailBuilderのテストケース";\r
+ String text = "改行します。\n改行しました。\nテストは成功。";\r
+\r
+ InternetAddress from = new InternetAddress("from@example.com", "差出人");\r
+ InternetAddress returnPath = new InternetAddress("return@example.com");\r
+ InternetAddress replyTo = new InternetAddress("reply@example.com");\r
+\r
+ InternetAddress to1 = new InternetAddress("to1@example.com", "宛先1");\r
+ InternetAddress to2 = new InternetAddress("to2@example.com");\r
+\r
+ InternetAddress cc1 = new InternetAddress("cc1@example.com", "CC1");\r
+ InternetAddress cc2 = new InternetAddress("cc2@example.com");\r
+\r
+ InternetAddress bcc = new InternetAddress("bcc@example.com");\r
+\r
+ Mail result = builder.buildMail(classPath);\r
+\r
+ assertEquals(subject, result.getSubject());\r
+ assertEquals(text, result.getText());\r
+\r
+ assertEquals(from, result.getFrom());\r
+ assertEquals(returnPath, result.getReturnPath());\r
+ assertEquals(replyTo, result.getReplyTo());\r
+\r
+ InternetAddress[] tos = result.getTo();\r
+ assertEquals(2, tos.length);\r
+ assertEquals(to1, tos[0]);\r
+ assertEquals(to2, tos[1]);\r
+\r
+ InternetAddress[] ccs = result.getCc();\r
+ assertEquals(2, ccs.length);\r
+ assertEquals(cc1, ccs[0]);\r
+ assertEquals(cc2, ccs[1]);\r
+\r
+ InternetAddress[] bccs = result.getBcc();\r
+ assertEquals(1, bccs.length);\r
+ assertEquals(bcc, bccs[0]);\r
+ }\r
+\r
+ /*\r
+ * Class under test for Mail buildMail(File)\r
+ * XMLファイルのFileインスタンスからMailインスタンスを生成。\r
+ */\r
+ public final void testBuildMailFromFile() throws Exception {\r
+ String path = "src/test/com/ozacc/mail/test-mail1.xml";\r
+ File file = new File(path);\r
+\r
+ String subject = "XMLMailBuilderのテストケース";\r
+ String text = "改行します。\n改行しました。\nテストは成功。";\r
+\r
+ InternetAddress from = new InternetAddress("from@example.com", "差出人");\r
+ InternetAddress returnPath = new InternetAddress("return@example.com");\r
+ InternetAddress replyTo = new InternetAddress("reply@example.com");\r
+\r
+ InternetAddress to1 = new InternetAddress("to1@example.com", "宛先1");\r
+ InternetAddress to2 = new InternetAddress("to2@example.com");\r
+\r
+ InternetAddress cc1 = new InternetAddress("cc1@example.com", "CC1");\r
+ InternetAddress cc2 = new InternetAddress("cc2@example.com");\r
+\r
+ InternetAddress bcc = new InternetAddress("bcc@example.com");\r
+\r
+ Mail result = builder.buildMail(file);\r
+\r
+ assertEquals(subject, result.getSubject());\r
+ assertEquals(text, result.getText());\r
+\r
+ assertEquals(from, result.getFrom());\r
+ assertEquals(returnPath, result.getReturnPath());\r
+ assertEquals(replyTo, result.getReplyTo());\r
+\r
+ InternetAddress[] tos = result.getTo();\r
+ assertEquals(2, tos.length);\r
+ assertEquals(to1, tos[0]);\r
+ assertEquals(to2, tos[1]);\r
+\r
+ InternetAddress[] ccs = result.getCc();\r
+ assertEquals(2, ccs.length);\r
+ assertEquals(cc1, ccs[0]);\r
+ assertEquals(cc2, ccs[1]);\r
+\r
+ InternetAddress[] bccs = result.getBcc();\r
+ assertEquals(1, bccs.length);\r
+ assertEquals(bcc, bccs[0]);\r
+ }\r
+\r
+ /*\r
+ * Class under test for Mail buildMail(String, VelocityContext)\r
+ */\r
+ public final void testBuildMailStringVelocityContext() throws Exception {\r
+ String classPath = "/com/ozacc/mail/test-mail3-velocity.xml";\r
+\r
+ String name = "伊東美咲";\r
+ String email = "misaki@example.com";\r
+ Customer customer = new Customer(name, email);\r
+ String item = "GIVE&TAKE (Beige)\n\nDesigned by:Ronan & Erwan Boroullec, Size:W313*D84*H370, Color:グリーン, 本体:ABS樹脂";\r
+\r
+ InternetAddress from = new InternetAddress("shop@example.com", "XMLMailBuilderオンラインショップ");\r
+ InternetAddress to = new InternetAddress(email, name);\r
+\r
+ String subject = "XMLMailBuilderオンラインショップ - ご注文の確認";\r
+ String text = name\r
+ + "様\n\nお買い上げありがとうございました。\n\nGIVE&TAKE (Beige)\n\nDesigned by:Ronan & Erwan Boroullec, Size:W313*D84*H370, Color:グリーン, 本体:ABS樹脂";\r
+\r
+ VelocityContext context = new VelocityContext();\r
+ context.put("customer", customer);\r
+ context.put("item", item);\r
+\r
+ // メール生成実行\r
+ Mail result = builder.buildMail(classPath, context);\r
+\r
+ assertEquals(from, result.getFrom());\r
+ assertEquals(to, result.getTo()[0]);\r
+ assertEquals(subject, result.getSubject());\r
+ assertEquals(text, result.getText());\r
+ }\r
+\r
+ /*\r
+ * Class under test for Mail buildMail(File, VelocityContext)\r
+ */\r
+ public final void testBuildMailFileVelocityContext() throws Exception {\r
+ String path = "src/test/com/ozacc/mail/test-mail3-velocity.xml";\r
+ File file = new File(path);\r
+\r
+ String name = "伊東美咲";\r
+ String email = "misaki@example.com";\r
+ Customer customer = new Customer(name, email);\r
+ String item = "GIVE&TAKE (Beige)\n\nDesigned by:Ronan & Erwan Boroullec, Size:W313*D84*H370, Color:グリーン, 本体:ABS樹脂";\r
+\r
+ InternetAddress from = new InternetAddress("shop@example.com", "XMLMailBuilderオンラインショップ");\r
+ InternetAddress to = new InternetAddress(email, name);\r
+\r
+ String subject = "XMLMailBuilderオンラインショップ - ご注文の確認";\r
+ String text = name\r
+ + "様\n\nお買い上げありがとうございました。\n\nGIVE&TAKE (Beige)\n\nDesigned by:Ronan & Erwan Boroullec, Size:W313*D84*H370, Color:グリーン, 本体:ABS樹脂";\r
+\r
+ VelocityContext context = new VelocityContext();\r
+ context.put("customer", customer);\r
+ context.put("item", item);\r
+\r
+ // メール生成実行\r
+ Mail result = builder.buildMail(file, context);\r
+\r
+ assertEquals(from, result.getFrom());\r
+ assertEquals(to, result.getTo()[0]);\r
+ assertEquals(subject, result.getSubject());\r
+ assertEquals(text, result.getText());\r
+ }\r
+\r
+ public void testBuildMailFromMultipleMailsTemplate() throws Exception {\r
+ String path = "src/test/com/ozacc/mail/test-mail7-multiple.xml";\r
+ File file = new File(path);\r
+\r
+ Mail result1 = builder.buildMail(file, "first");\r
+ assertEquals("1通目", result1.getText());\r
+\r
+ Mail result2 = builder.buildMail(file, "second");\r
+ assertEquals("2通目", result2.getText());\r
+\r
+ try {\r
+ Mail result3 = builder.buildMail(file, "サード");\r
+ } catch (MailBuildException expected) {\r
+\r
+ }\r
+ }\r
+\r
+ public static class Customer {\r
+\r
+ private String name;\r
+\r
+ private String email;\r
+\r
+ public Customer(String name, String email) {\r
+ this.name = name;\r
+ this.email = email;\r
+ }\r
+\r
+ /**\r
+ * @return Returns the email.\r
+ */\r
+ public String getEmail() {\r
+ return email;\r
+ }\r
+\r
+ /**\r
+ * @param email The email to set.\r
+ */\r
+ public void setEmail(String email) {\r
+ this.email = email;\r
+ }\r
+\r
+ /**\r
+ * @return Returns the name.\r
+ */\r
+ public String getName() {\r
+ return name;\r
+ }\r
+\r
+ /**\r
+ * @param name The name to set.\r
+ */\r
+ public void setName(String name) {\r
+ this.name = name;\r
+ }\r
+ }\r
+\r
+}
\ No newline at end of file
--- /dev/null
+package com.ozacc.mail.impl;\r
+\r
+import java.util.Properties;\r
+\r
+import javax.mail.Session;\r
+\r
+import junit.framework.TestCase;\r
+\r
+/**\r
+ * \r
+ * @author Tomohiro Otsuka\r
+ * @version $Id: OMLMimeMessageTest.java,v 1.1 2004/09/20 21:43:33 otsuka Exp $\r
+ */\r
+public class OMLMimeMessageTest extends TestCase {\r
+\r
+ public void testRandom() throws Exception {\r
+ Properties p = new Properties();\r
+ OMLMimeMessage msg1 = new OMLMimeMessage(Session.getInstance(p), "JavaMail@ozacc.com");\r
+ OMLMimeMessage msg2 = new OMLMimeMessage(Session.getInstance(p), ".JavaMail@ozacc.com");\r
+ \r
+ String id1 = msg1.generateRandomMessageId();\r
+ String id2 = msg2.generateRandomMessageId();\r
+\r
+ assertNotSame(id1, id2);\r
+\r
+ System.out.println(id1);\r
+ System.out.println(id2);\r
+ }\r
+}
\ No newline at end of file
--- /dev/null
+package com.ozacc.mail.impl;\r
+\r
+import junit.framework.TestCase;\r
+\r
+import org.apache.log4j.BasicConfigurator;\r
+\r
+import com.ozacc.mail.Mail;\r
+import com.ozacc.mail.MailBuilder;\r
+\r
+/**\r
+ * SendMailImplクラスのテストケース。実際に送信し、メーラーで受信して確認するテストです。\r
+ * \r
+ * @author Tomohiro Otsuka\r
+ * @version $Id: SendMailImplRealTest.java,v 1.6.2.1 2005/01/23 06:51:56 otsuka Exp $\r
+ */\r
+public class SendMailImplRealTest extends TestCase {\r
+\r
+ private MailBuilder builder;\r
+\r
+ private String email;\r
+\r
+ private String envelopeTo;\r
+\r
+ private SendMailImpl sendMail;\r
+\r
+ /*\r
+ * @see TestCase#setUp()\r
+ */\r
+ protected void setUp() throws Exception {\r
+ super.setUp();\r
+\r
+ BasicConfigurator.configure();\r
+\r
+ email = "to@example.com";\r
+ envelopeTo = "to@example.com";\r
+\r
+ String host = "localhost";\r
+ sendMail = new SendMailImpl(host);\r
+ sendMail.setMessageId("example.com");\r
+\r
+ builder = new XMLMailBuilderImpl();\r
+ }\r
+\r
+ /**\r
+ * @see junit.framework.TestCase#tearDown()\r
+ */\r
+ protected void tearDown() throws Exception {\r
+ BasicConfigurator.resetConfiguration();\r
+ }\r
+\r
+ /*\r
+ public void testSendMailWithAttachmentInputStream() throws Exception {\r
+ String classPath = "/com/ozacc/mail/test-mail4.xml";\r
+ Mail mail = builder.buildMail(classPath);\r
+ mail.addTo(email);\r
+\r
+ File image1 = new File("src/test/com/ozacc/mail/image1.jpg");\r
+ FileInputStream fis = new FileInputStream(image1);\r
+\r
+ mail.addFile(fis, "野菜画像.jpg");\r
+ mail.setSubject("添付ファイルのInputStream送信テスト");\r
+\r
+ sendMail.send(mail);\r
+ }\r
+\r
+ \r
+ public void testSendMailEnvelopeTo() throws Exception {\r
+ String classPath = "/com/ozacc/mail/test-mail4.xml";\r
+ Mail mail = builder.buildMail(classPath);\r
+ mail.addTo(email);\r
+ mail.addEnvelopeTo(envelopeTo);\r
+ sendMail.send(mail);\r
+ }\r
+\r
+ public void testSendMailSimpl() throws Exception {\r
+ String classPath = "/com/ozacc/mail/test-mail4.xml";\r
+ Mail mail = builder.buildMail(classPath);\r
+ mail.addTo(email);\r
+\r
+ sendMail.send(mail);\r
+ }\r
+\r
+ public void testSendMailWithAttachmentFile() throws Exception {\r
+ String classPath = "/com/ozacc/mail/test-mail4.xml";\r
+ Mail mail = builder.buildMail(classPath);\r
+ mail.addTo(email);\r
+\r
+ File image1 = new File("src/test/com/ozacc/mail/image1.jpg");\r
+ File image2 = new File("src/test/com/ozacc/mail/image2.png");\r
+\r
+ mail.addFile(image1);\r
+ mail.addFile(image2, "野菜画像.png");\r
+ mail.setSubject("添付ファイル送信テスト");\r
+\r
+ sendMail.send(mail);\r
+ }\r
+\r
+ public void testSendMailHTML() throws Exception {\r
+ String classPath = "/com/ozacc/mail/test-mail5-html.xml";\r
+ Mail mail = builder.buildMail(classPath);\r
+ mail.addTo(email);\r
+ mail.setHtmlText(mail.getText());\r
+ mail.setText("プレーンテキスト");\r
+ sendMail.send(mail);\r
+ }\r
+ \r
+ public void testSendMailHTMLOnly() throws Exception {\r
+ String classPath = "/com/ozacc/mail/test-mail5-html.xml";\r
+ Mail mail = builder.buildMail(classPath);\r
+ mail.addTo(email);\r
+ mail.setHtmlText(mail.getText());\r
+ mail.setText(null);\r
+ mail.setSubject("HTMLオンリー");\r
+ sendMail.send(mail);\r
+ }\r
+\r
+ public void testSendMailHTMLWithAttachmentFile() throws Exception {\r
+ String classPath = "/com/ozacc/mail/test-mail5-html.xml";\r
+ Mail mail = builder.buildMail(classPath);\r
+ mail.addTo(email);\r
+ mail.setHtmlText(mail.getText());\r
+ mail.setText("プレーンテキスト");\r
+\r
+ File image1 = new File("src/test/com/ozacc/mail/image1.jpg");\r
+ mail.addFile(image1);\r
+\r
+ sendMail.send(mail);\r
+ }\r
+ */\r
+ /**\r
+ * テストケースがひとつもないとエラーになるので、ダミー。\r
+ */\r
+ public void testSendMailSuccess() {\r
+ Mail mail;\r
+ assertTrue(true);\r
+ }\r
+\r
+}
\ No newline at end of file
--- /dev/null
+package com.ozacc.mail.impl;\r
+\r
+import java.io.UnsupportedEncodingException;\r
+import java.util.Iterator;\r
+\r
+import javax.mail.internet.MimeUtility;\r
+\r
+import junit.framework.TestCase;\r
+\r
+import org.apache.log4j.BasicConfigurator;\r
+\r
+import com.dumbster.smtp.SimpleSmtpServer;\r
+import com.dumbster.smtp.SmtpMessage;\r
+import com.ozacc.mail.Mail;\r
+import com.ozacc.mail.MailException;\r
+\r
+/**\r
+ * SendMailImplクラスのテストケース。\r
+ * <p>\r
+ * Dumbsterを使用してテストしているが、サポートされていない機能が多い。\r
+ * \r
+ * @author Tomohiro Otsuka\r
+ * @version $Id: SendMailImplTest.java,v 1.3 2004/09/14 00:06:13 otsuka Exp $\r
+ */\r
+public class SendMailImplTest extends TestCase {\r
+\r
+ private SendMailImpl sendMail;\r
+\r
+ private SimpleSmtpServer server;\r
+\r
+ /*\r
+ * @see TestCase#setUp()\r
+ */\r
+ protected void setUp() throws Exception {\r
+ super.setUp();\r
+\r
+ BasicConfigurator.configure();\r
+\r
+ int port = 2525;\r
+ server = SimpleSmtpServer.start(port);\r
+ sendMail = new SendMailImpl();\r
+ sendMail.setPort(port);\r
+ }\r
+\r
+ /**\r
+ * @see junit.framework.TestCase#tearDown()\r
+ */\r
+ protected void tearDown() throws Exception {\r
+ BasicConfigurator.resetConfiguration();\r
+ }\r
+\r
+ private String convertJisValue(String str) throws UnsupportedEncodingException {\r
+ return new String(str.getBytes(), "JIS");\r
+ }\r
+\r
+ /**\r
+ * 単発メールのテスト。\r
+ * \r
+ * @throws Exception\r
+ */\r
+ public void testSendMail() throws Exception {\r
+ String from = "from@example.com";\r
+ String fromName = "差出人";\r
+ String to = "info@example.com";\r
+ String subject = "件名";\r
+ String text = "テスト成功";\r
+\r
+ Mail mail = new Mail();\r
+ mail.setFrom(from, fromName);\r
+ mail.addTo(to);\r
+ mail.setSubject(subject);\r
+ mail.setText(text);\r
+\r
+ sendMail.send(mail);\r
+\r
+ server.stop();\r
+\r
+ assertEquals("1", 1, server.getReceievedEmailSize());\r
+ Iterator inbox = server.getReceivedEmail();\r
+ SmtpMessage email = (SmtpMessage)inbox.next();\r
+\r
+ assertEquals("2", mail.getTo()[0].toString(), email.getHeaderValue("To"));\r
+ assertEquals("3", mail.getFrom().toString(), email.getHeaderValue("From"));\r
+\r
+ assertEquals("4", mail.getSubject(), MimeUtility\r
+ .decodeText(email.getHeaderValue("Subject")));\r
+ assertEquals("5", mail.getText() + "\n", convertJisValue(email.getBody()));\r
+ }\r
+\r
+ /**\r
+ * 複数メールの一括送信テスト。\r
+ * 同一接続内の複数メッセージを送信するとDumbsterがエラーを吐くので、\r
+ * とりあえず1つのMailインスタンスの配列でテスト。\r
+ * 実際のSMTPサーバ(qmail)で正常に送信できることは確認済み。\r
+ * \r
+ * @throws Exception\r
+ */\r
+ public void testSendMailMultiple() throws Exception {\r
+ String from = "from@example.com";\r
+ String fromName = "差出人";\r
+ String to = "info@example.com";\r
+ String subject = "件名";\r
+ String text = "テスト成功";\r
+\r
+ Mail mail1 = new Mail();\r
+ mail1.setFrom(from, fromName);\r
+ mail1.addTo(to);\r
+ mail1.setSubject(subject);\r
+ mail1.setText(text);\r
+\r
+ Mail mail2 = new Mail();\r
+ mail2.setFrom(from, fromName);\r
+ mail2.addTo(to);\r
+ mail2.setSubject(subject);\r
+ mail2.setText(text);\r
+\r
+ Mail mail3 = new Mail();\r
+ mail3.setFrom(from, fromName);\r
+ mail3.addTo(to);\r
+ mail3.setSubject(subject);\r
+ mail3.setText(text);\r
+\r
+ // Dumbsterのバグが直ったら、mail1, mail2, mail3 を含めてテスト\r
+ sendMail.send(new Mail[] { mail1 });\r
+\r
+ server.stop();\r
+\r
+ // Dumbsterのバグが直ったら、3 に。\r
+ assertEquals("1", 1, server.getReceievedEmailSize());\r
+\r
+ Iterator inbox = server.getReceivedEmail();\r
+ SmtpMessage email = (SmtpMessage)inbox.next();\r
+\r
+ assertEquals("2", mail1.getTo()[0].toString(), email.getHeaderValue("To"));\r
+ assertEquals("3", mail1.getFrom().toString(), email.getHeaderValue("From"));\r
+\r
+ assertEquals("4", mail1.getSubject(), MimeUtility.decodeText(email\r
+ .getHeaderValue("Subject")));\r
+ assertEquals("5", mail1.getText() + "\n", convertJisValue(email.getBody()));\r
+ }\r
+\r
+ public void testSendMailWithReturnPath() throws Exception {\r
+ String from = "from@example.com";\r
+ String fromName = "差出人";\r
+ String to = "info@example.com";\r
+ String subject = "件名";\r
+ String text = "テスト成功";\r
+ String returnPath = "return-path@example.com";\r
+\r
+ Mail mail = new Mail();\r
+ mail.setFrom(from, fromName);\r
+ mail.addTo(to);\r
+ mail.setSubject(subject);\r
+ mail.setText(text);\r
+ mail.setReturnPath(returnPath);\r
+ mail.setImportance(Mail.Importance.HIGH);\r
+\r
+ sendMail.send(mail);\r
+\r
+ server.stop();\r
+\r
+ assertEquals(1, server.getReceievedEmailSize());\r
+ Iterator inbox = server.getReceivedEmail();\r
+ SmtpMessage email = (SmtpMessage)inbox.next();\r
+\r
+ // ヘッダー出力\r
+ /*\r
+ Iterator itr = email.getHeaderNames();\r
+ while (itr.hasNext()) {\r
+ String name = (String)itr.next();\r
+ System.out.println(name + "='" + email.getHeaderValue(name) + "'");\r
+ }\r
+ */\r
+\r
+ // Dumbsterでは、Return-Pathヘッダを保持していない\r
+ //assertEquals(mail.getReturnPath().toString(), email.getHeaderValue("Return-Path"));\r
+ // 重要度を確認\r
+ assertEquals(mail.getImportance(), email.getHeaderValue("Importance"));\r
+ assertEquals("1", email.getHeaderValue("X-Priority"));\r
+ }\r
+\r
+ /**\r
+ * 宛先を一件も指定していないためsend()時に例外をスロー。\r
+ * To、Cc、Bccを一件でも指定すれば、この例外は起こらない。\r
+ * \r
+ * @throws Exception\r
+ */\r
+ public void testSendMailNoRecpient() throws Exception {\r
+ String from = "from@example.com";\r
+ String fromName = "差出人";\r
+ String subject = "件名";\r
+ String text = "テスト成功";\r
+\r
+ Mail mail = new Mail();\r
+ mail.setFrom(from, fromName);\r
+ mail.setSubject(subject);\r
+ mail.setText(text);\r
+\r
+ try {\r
+ sendMail.send(mail);\r
+ fail("This should never be called.");\r
+ } catch (MailException expected) {\r
+ assertEquals("MimeMessageの生成に失敗しました。", expected.getMessage());\r
+ }\r
+ }\r
+\r
+}
\ No newline at end of file
--- /dev/null
+package com.ozacc.mail.impl;\r
+\r
+import junit.framework.TestCase;\r
+\r
+import org.apache.log4j.BasicConfigurator;\r
+\r
+import com.ozacc.mail.Mail;\r
+import com.ozacc.mail.MailBuilder;\r
+\r
+/**\r
+ * SendMailProImplクラスのテストケース。実際に送信し、メーラーで受信して確認するテストです。\r
+ * \r
+ * @author Tomohiro Otsuka\r
+ * @version $Id: SendMailProImplRealTest.java,v 1.1.2.1 2005/01/23 06:51:27 otsuka Exp $\r
+ */\r
+public class SendMailProImplRealTest extends TestCase {\r
+\r
+ private MailBuilder builder;\r
+\r
+ private String email;\r
+\r
+ private String envelopeTo;\r
+\r
+ private SendMailProImpl sendMail;\r
+\r
+ /*\r
+ * @see TestCase#setUp()\r
+ */\r
+ protected void setUp() throws Exception {\r
+ super.setUp();\r
+\r
+ BasicConfigurator.configure();\r
+\r
+ email = "to@example.com";\r
+ envelopeTo = "to@example.com";\r
+\r
+ String host = "localhost";\r
+ sendMail = new SendMailProImpl(host);\r
+ sendMail.setMessageId("example.com");\r
+\r
+ builder = new XMLMailBuilderImpl();\r
+ }\r
+\r
+ /**\r
+ * @see junit.framework.TestCase#tearDown()\r
+ */\r
+ protected void tearDown() throws Exception {\r
+ BasicConfigurator.resetConfiguration();\r
+ }\r
+\r
+ /* public void testSendMailWithAttachmentInputStream() throws Exception {\r
+ String classPath = "/com/ozacc/mail/test-mail4.xml";\r
+ Mail mail = builder.buildMail(classPath);\r
+ mail.addTo(email);\r
+\r
+ File image1 = new File("src/test/com/ozacc/mail/image1.jpg");\r
+ FileInputStream fis = new FileInputStream(image1);\r
+\r
+ mail.addFile(fis, "野菜画像.jpg");\r
+ mail.setSubject("添付ファイルのInputStream送信テスト");\r
+\r
+ sendMail.connect();\r
+ sendMail.send(mail);\r
+ sendMail.disconnect();\r
+ }\r
+\r
+ public void testSendMailEnvelopeTo() throws Exception {\r
+ String classPath = "/com/ozacc/mail/test-mail4.xml";\r
+ Mail mail = builder.buildMail(classPath);\r
+ mail.addTo(email);\r
+ mail.addEnvelopeTo(envelopeTo);\r
+\r
+ sendMail.connect();\r
+ sendMail.send(mail);\r
+ sendMail.disconnect();\r
+ }*/\r
+\r
+ /**\r
+ * テストケースがひとつもないとエラーになるので、ダミー。\r
+ */\r
+ public void testSendMailSuccess() {\r
+ Mail mail;\r
+ assertTrue(true);\r
+ }\r
+\r
+}
\ No newline at end of file
--- /dev/null
+package com.ozacc.mail.impl;\r
+\r
+import java.io.File;\r
+\r
+import javax.mail.internet.InternetAddress;\r
+\r
+import junit.framework.TestCase;\r
+\r
+import org.apache.log4j.BasicConfigurator;\r
+\r
+import com.ozacc.mail.Mail;\r
+import com.ozacc.mail.MailBuildException;\r
+import com.ozacc.mail.MultipleMailBuilder;\r
+\r
+/**\r
+ * \r
+ * @since 1.0.1\r
+ * @author Tomohiro Otsuka\r
+ * @version $Id: XMLMailBuilderImplTest.java,v 1.6.2.1 2005/01/21 22:17:27 otsuka Exp $\r
+ */\r
+public class XMLMailBuilderImplTest extends TestCase {\r
+\r
+ private MultipleMailBuilder builder;\r
+\r
+ /*\r
+ * @see TestCase#setUp()\r
+ */\r
+ protected void setUp() throws Exception {\r
+ super.setUp();\r
+ BasicConfigurator.configure();\r
+\r
+ builder = new XMLMailBuilderImpl();\r
+ }\r
+\r
+ /**\r
+ * @see junit.framework.TestCase#tearDown()\r
+ */\r
+ protected void tearDown() throws Exception {\r
+ super.tearDown();\r
+ BasicConfigurator.resetConfiguration();\r
+ }\r
+\r
+ public final void testBuildMailCDATA() throws Exception {\r
+ String classPath = "/com/ozacc/mail/test-mail6-cdata.xml";\r
+\r
+ String expectedBody = "これはCDATAのテキストです。";\r
+\r
+ Mail result = builder.buildMail(classPath);\r
+\r
+ assertEquals(expectedBody, result.getText());\r
+ }\r
+\r
+ public final void testBuildMultipartMailFromFile() throws Exception {\r
+ String path = "src/test/com/ozacc/mail/test-mail1.xml";\r
+ File file = new File(path);\r
+\r
+ String subject = "XMLMailBuilderのテストケース";\r
+ String text = "改行します。\n改行しました。\nテストは成功。";\r
+ String htmlText = "<html><body>これはHTMLテキスト</body></html>";\r
+\r
+ InternetAddress from = new InternetAddress("from@example.com", "差出人");\r
+ InternetAddress returnPath = new InternetAddress("return@example.com");\r
+ InternetAddress replyTo = new InternetAddress("reply@example.com");\r
+\r
+ InternetAddress to1 = new InternetAddress("to1@example.com", "宛先1");\r
+ InternetAddress to2 = new InternetAddress("to2@example.com");\r
+\r
+ InternetAddress cc1 = new InternetAddress("cc1@example.com", "CC1");\r
+ InternetAddress cc2 = new InternetAddress("cc2@example.com");\r
+\r
+ InternetAddress bcc = new InternetAddress("bcc@example.com");\r
+\r
+ Mail result = builder.buildMail(file);\r
+\r
+ System.out.println(result);\r
+\r
+ assertEquals(subject, result.getSubject());\r
+ assertEquals(text, result.getText());\r
+\r
+ assertTrue(result.isMultipartMail());\r
+ assertTrue(result.isHtmlMail());\r
+ assertEquals(htmlText, result.getHtmlText());\r
+\r
+ assertEquals(from, result.getFrom());\r
+ assertEquals(returnPath, result.getReturnPath());\r
+ assertEquals(replyTo, result.getReplyTo());\r
+\r
+ InternetAddress[] tos = result.getTo();\r
+ assertEquals(2, tos.length);\r
+ assertEquals(to1, tos[0]);\r
+ assertEquals(to2, tos[1]);\r
+\r
+ InternetAddress[] ccs = result.getCc();\r
+ assertEquals(2, ccs.length);\r
+ assertEquals(cc1, ccs[0]);\r
+ assertEquals(cc2, ccs[1]);\r
+\r
+ InternetAddress[] bccs = result.getBcc();\r
+ assertEquals(1, bccs.length);\r
+ assertEquals(bcc, bccs[0]);\r
+ }\r
+\r
+ /*\r
+ * Class under test for Mail buildMail(String)\r
+ */\r
+ public final void testBuildMailFromClassPath() throws Exception {\r
+ String classPath = "/com/ozacc/mail/test-mail1.xml";\r
+\r
+ String subject = "XMLMailBuilderのテストケース";\r
+ String text = "改行します。\n改行しました。\nテストは成功。";\r
+\r
+ InternetAddress from = new InternetAddress("from@example.com", "差出人");\r
+ InternetAddress returnPath = new InternetAddress("return@example.com");\r
+ InternetAddress replyTo = new InternetAddress("reply@example.com");\r
+\r
+ InternetAddress to1 = new InternetAddress("to1@example.com", "宛先1");\r
+ InternetAddress to2 = new InternetAddress("to2@example.com");\r
+\r
+ InternetAddress cc1 = new InternetAddress("cc1@example.com", "CC1");\r
+ InternetAddress cc2 = new InternetAddress("cc2@example.com");\r
+\r
+ InternetAddress bcc = new InternetAddress("bcc@example.com");\r
+\r
+ Mail result = builder.buildMail(classPath);\r
+\r
+ assertEquals(subject, result.getSubject());\r
+ assertEquals(text, result.getText());\r
+\r
+ assertEquals(from, result.getFrom());\r
+ assertEquals(returnPath, result.getReturnPath());\r
+ assertEquals(replyTo, result.getReplyTo());\r
+\r
+ InternetAddress[] tos = result.getTo();\r
+ assertEquals(2, tos.length);\r
+ assertEquals(to1, tos[0]);\r
+ assertEquals(to2, tos[1]);\r
+\r
+ InternetAddress[] ccs = result.getCc();\r
+ assertEquals(2, ccs.length);\r
+ assertEquals(cc1, ccs[0]);\r
+ assertEquals(cc2, ccs[1]);\r
+\r
+ InternetAddress[] bccs = result.getBcc();\r
+ assertEquals(1, bccs.length);\r
+ assertEquals(bcc, bccs[0]);\r
+ }\r
+\r
+ /*\r
+ * Class under test for Mail buildMail(File)\r
+ */\r
+ public final void testBuildMailFile() throws Exception {\r
+ String path = "src/test/com/ozacc/mail/test-mail1.xml";\r
+ File file = new File(path);\r
+\r
+ String subject = "XMLMailBuilderのテストケース";\r
+ String text = "改行します。\n改行しました。\nテストは成功。";\r
+\r
+ InternetAddress from = new InternetAddress("from@example.com", "差出人");\r
+ InternetAddress returnPath = new InternetAddress("return@example.com");\r
+ InternetAddress replyTo = new InternetAddress("reply@example.com");\r
+\r
+ InternetAddress to1 = new InternetAddress("to1@example.com", "宛先1");\r
+ InternetAddress to2 = new InternetAddress("to2@example.com");\r
+\r
+ InternetAddress cc1 = new InternetAddress("cc1@example.com", "CC1");\r
+ InternetAddress cc2 = new InternetAddress("cc2@example.com");\r
+\r
+ InternetAddress bcc = new InternetAddress("bcc@example.com");\r
+\r
+ Mail result = builder.buildMail(file);\r
+\r
+ assertEquals(subject, result.getSubject());\r
+ assertEquals(text, result.getText());\r
+\r
+ assertEquals(from, result.getFrom());\r
+ assertEquals(returnPath, result.getReturnPath());\r
+ assertEquals(replyTo, result.getReplyTo());\r
+\r
+ InternetAddress[] tos = result.getTo();\r
+ assertEquals(2, tos.length);\r
+ assertEquals(to1, tos[0]);\r
+ assertEquals(to2, tos[1]);\r
+\r
+ InternetAddress[] ccs = result.getCc();\r
+ assertEquals(2, ccs.length);\r
+ assertEquals(cc1, ccs[0]);\r
+ assertEquals(cc2, ccs[1]);\r
+\r
+ InternetAddress[] bccs = result.getBcc();\r
+ assertEquals(1, bccs.length);\r
+ assertEquals(bcc, bccs[0]);\r
+ }\r
+\r
+ public void testBuildMailFromMultipleMailsTemplate() throws Exception {\r
+ String path = "src/test/com/ozacc/mail/test-mail7-multiple.xml";\r
+ File file = new File(path);\r
+\r
+ Mail result1 = builder.buildMail(file, "first");\r
+ assertEquals("1通目", result1.getText());\r
+\r
+ Mail result2 = builder.buildMail(file, "second");\r
+ assertEquals("2通目", result2.getText());\r
+\r
+ try {\r
+ Mail result3 = builder.buildMail(file, "サード");\r
+ } catch (MailBuildException expected) {\r
+ \r
+ }\r
+ }\r
+}
\ No newline at end of file
--- /dev/null
+package com.ozacc.mail.impl;\r
+\r
+import java.io.File;\r
+\r
+import javax.mail.internet.InternetAddress;\r
+\r
+import junit.framework.TestCase;\r
+\r
+import org.apache.log4j.BasicConfigurator;\r
+import org.apache.velocity.VelocityContext;\r
+\r
+import com.ozacc.mail.Mail;\r
+import com.ozacc.mail.VelocityMailBuilder;\r
+import com.ozacc.mail.impl.JDomXMLMailBuilderTest.Customer;\r
+\r
+/**\r
+ * \r
+ * @since 1.0.1\r
+ * @author Tomohiro Otsuka\r
+ * @version $Id: XMLVelocityMailBuilderImplTest.java,v 1.1.2.1 2004/10/24 10:28:09 otsuka Exp $\r
+ */\r
+public class XMLVelocityMailBuilderImplTest extends TestCase {\r
+\r
+ private VelocityMailBuilder builder;\r
+\r
+ /*\r
+ * @see TestCase#setUp()\r
+ */\r
+ protected void setUp() throws Exception {\r
+ super.setUp();\r
+ BasicConfigurator.configure();\r
+ builder = new XMLVelocityMailBuilderImpl();\r
+ }\r
+\r
+ /**\r
+ * @see junit.framework.TestCase#tearDown()\r
+ */\r
+ protected void tearDown() throws Exception {\r
+ BasicConfigurator.resetConfiguration();\r
+ }\r
+\r
+ public void testBuildMailFromFile() throws Exception {\r
+ String path = "src/test/com/ozacc/mail/test-mail3-velocity.xml";\r
+ File file = new File(path);\r
+\r
+ String name = "伊東美咲";\r
+ String email = "misaki@example.com";\r
+ Customer customer = new Customer(name, email);\r
+ String item = "GIVE&TAKE (Beige)";\r
+\r
+ InternetAddress from = new InternetAddress("shop@example.com", "XMLMailBuilderオンラインショップ");\r
+ InternetAddress to = new InternetAddress(email, name);\r
+\r
+ String subject = "XMLMailBuilderオンラインショップ - ご注文の確認";\r
+ String text = name + "様\n\nお買い上げありがとうございました。\n\nGIVE&TAKE (Beige)";\r
+\r
+ VelocityContext context = new VelocityContext();\r
+ context.put("customer", customer);\r
+ context.put("item", item);\r
+\r
+ // メール生成実行\r
+ Mail result = builder.buildMail(file, context);\r
+\r
+ assertEquals(from, result.getFrom());\r
+ assertEquals(to, result.getTo()[0]);\r
+ assertEquals(subject, result.getSubject());\r
+ assertEquals(text, result.getText());\r
+ }\r
+\r
+ public final void testBuildMailStringVelocityContext() throws Exception {\r
+ String classPath = "/com/ozacc/mail/test-mail3-velocity.xml";\r
+\r
+ String name = "伊東美咲";\r
+ String email = "misaki@example.com";\r
+ Customer customer = new Customer(name, email);\r
+ String item = "GIVE&TAKE (Beige)";\r
+\r
+ InternetAddress from = new InternetAddress("shop@example.com", "XMLMailBuilderオンラインショップ");\r
+ InternetAddress to = new InternetAddress(email, name);\r
+\r
+ String subject = "XMLMailBuilderオンラインショップ - ご注文の確認";\r
+ String text = name + "様\n\nお買い上げありがとうございました。\n\nGIVE&TAKE (Beige)";\r
+\r
+ VelocityContext context = new VelocityContext();\r
+ context.put("customer", customer);\r
+ context.put("item", item);\r
+\r
+ // メール生成実行\r
+ Mail result = builder.buildMail(classPath, context);\r
+\r
+ assertEquals(from, result.getFrom());\r
+ assertEquals(to, result.getTo()[0]);\r
+ assertEquals(subject, result.getSubject());\r
+ assertEquals(text, result.getText());\r
+ }\r
+\r
+ public final void testBuildMailStringVelocityContextWithCache() throws Exception {\r
+ builder.setCacheEnabled(true);\r
+\r
+ String classPath = "/com/ozacc/mail/test-mail3-velocity.xml";\r
+\r
+ String name = "伊東美咲";\r
+ String email = "misaki@example.com";\r
+ Customer customer = new Customer(name, email);\r
+ String item = "GIVE&TAKE (Beige)";\r
+\r
+ InternetAddress from = new InternetAddress("shop@example.com", "XMLMailBuilderオンラインショップ");\r
+ InternetAddress to = new InternetAddress(email, name);\r
+ String subject = "XMLMailBuilderオンラインショップ - ご注文の確認";\r
+ String text = name + "様\n\nお買い上げありがとうございました。\n\nGIVE&TAKE (Beige)";\r
+\r
+ VelocityContext context = new VelocityContext();\r
+ context.put("customer", customer);\r
+ context.put("item", item);\r
+\r
+ // メール生成実行\r
+ Mail result = builder.buildMail(classPath, context);\r
+ Mail result2 = builder.buildMail(classPath, context);\r
+ builder.clearCache();\r
+ Mail result3 = builder.buildMail(classPath, context);\r
+\r
+ assertEquals(from, result.getFrom());\r
+ assertEquals(to, result.getTo()[0]);\r
+ assertEquals(subject, result.getSubject());\r
+ assertEquals(text, result.getText());\r
+ }\r
+}
\ No newline at end of file
--- /dev/null
+package com.ozacc.mail.mailet;\r
+\r
+import java.util.ArrayList;\r
+import java.util.List;\r
+\r
+import javax.mail.internet.InternetAddress;\r
+\r
+import org.apache.log4j.BasicConfigurator;\r
+\r
+import junit.framework.TestCase;\r
+\r
+import com.ozacc.mail.fetch.ReceivedMail;\r
+import com.ozacc.mail.mock.MockFetchMailPro;\r
+\r
+/**\r
+ * MailetRunnerのテストケース。\r
+ * \r
+ * @author Tomohiro Otsuka\r
+ * @version $Id: MailetRunnerTest.java,v 1.1.2.1 2005/02/06 13:53:54 otsuka Exp $\r
+ */\r
+public class MailetRunnerTest extends TestCase {\r
+\r
+ private MailetRunner mailetRunner;\r
+\r
+ /*\r
+ * @see TestCase#setUp()\r
+ */\r
+ protected void setUp() throws Exception {\r
+ super.setUp();\r
+ BasicConfigurator.configure();\r
+\r
+ MockFetchMailPro fetchMailPro = new MockFetchMailPro();\r
+ fetchMailPro.setupGetMails(getSampleMails());\r
+\r
+ Mailet mailet = getSubjectOutputMailet();\r
+ Matcher matcher = getExampleComAddressMatcher();\r
+ List matcherList = new ArrayList();\r
+ matcherList.add(matcher);\r
+ MailetWrapper mw = new MailetWrapper(mailet, matcherList);\r
+ List mwList = new ArrayList();\r
+ mwList.add(mw);\r
+ mwList.add(mw);\r
+\r
+ mailetRunner = new MailetRunner();\r
+ mailetRunner.setFetchMailPro(fetchMailPro);\r
+ mailetRunner.setMailetWrapperList(mwList);\r
+ }\r
+\r
+ /**\r
+ * @return \r
+ */\r
+ private ReceivedMail[] getSampleMails() {\r
+ List list = new ArrayList();\r
+ ReceivedMail m1 = new ReceivedMail();\r
+ m1.setSubject("m1 - 出力されます。");\r
+ m1.addTo("to@example.com");\r
+ list.add(m1);\r
+ ReceivedMail m2 = new ReceivedMail();\r
+ m2.setSubject("m2 - 出力されません。");\r
+ m2.addTo("to@example.net");\r
+ list.add(m2);\r
+ ReceivedMail m3 = new ReceivedMail();\r
+ m3.setSubject("m3 - 出力されます。");\r
+ m3.addTo("to@example.net");\r
+ m3.addTo("to@example.com");\r
+ list.add(m3);\r
+ return (ReceivedMail[])list.toArray(new ReceivedMail[list.size()]);\r
+ }\r
+\r
+ /**\r
+ * @return \r
+ */\r
+ private Matcher getExampleComAddressMatcher() {\r
+ Matcher m = new Matcher() {\r
+\r
+ public boolean match(ReceivedMail mail) {\r
+ InternetAddress[] toAddresses = mail.getTo();\r
+ for (int i = 0; i < toAddresses.length; i++) {\r
+ InternetAddress address = toAddresses[i];\r
+ if (address.getAddress().equalsIgnoreCase("to@example.com")) {\r
+ return true;\r
+ }\r
+ }\r
+ return false;\r
+ }\r
+ };\r
+ return m;\r
+ }\r
+\r
+ /**\r
+ * @return \r
+ */\r
+ private Mailet getSubjectOutputMailet() {\r
+ return new Mailet() {\r
+\r
+ public void service(ReceivedMail mail) {\r
+ System.out.println(mail.getSubject());\r
+ }\r
+ };\r
+ }\r
+\r
+ /*\r
+ * @see TestCase#tearDown()\r
+ */\r
+ protected void tearDown() throws Exception {\r
+ super.tearDown();\r
+ BasicConfigurator.resetConfiguration();\r
+ }\r
+\r
+ public void testRun() {\r
+ mailetRunner.run();\r
+ }\r
+\r
+}
\ No newline at end of file
--- /dev/null
+package com.ozacc.mail.mock;\r
+\r
+import junit.framework.TestCase;\r
+\r
+import org.apache.log4j.BasicConfigurator;\r
+\r
+import com.ozacc.mail.fetch.ReceivedMail;\r
+\r
+/**\r
+ * MockFetchMailのテストケース。\r
+ * \r
+ * @author Tomohiro Otsuka\r
+ * @version $Id: MockFetchMailTest.java,v 1.1.2.2 2005/02/05 09:28:58 otsuka Exp $\r
+ */\r
+public class MockFetchMailTest extends TestCase {\r
+\r
+ MockFetchMail mockFetchMail;\r
+\r
+ /*\r
+ * @see TestCase#setUp()\r
+ */\r
+ protected void setUp() throws Exception {\r
+ super.setUp();\r
+ BasicConfigurator.configure();\r
+ mockFetchMail = new MockFetchMail();\r
+ }\r
+\r
+ /*\r
+ * @see junit.framework.TestCase#tearDown()\r
+ */\r
+ protected void tearDown() throws Exception {\r
+ BasicConfigurator.resetConfiguration();\r
+ }\r
+\r
+ public final void testGetMailsReturnZero() {\r
+ ReceivedMail[] mails = mockFetchMail.getMails();\r
+ assertEquals(0, mails.length);\r
+ }\r
+\r
+ public final void testGetMails() {\r
+ ReceivedMail expectedMail = new ReceivedMail();\r
+ expectedMail.setFrom("from@example.net", "差出人");\r
+ expectedMail.addTo("to@example.com", "宛名");\r
+ expectedMail.setSubject("MockFetchMailTest");\r
+ expectedMail.setText("本文");\r
+\r
+ mockFetchMail.setupGetMails(expectedMail);\r
+\r
+ ReceivedMail[] mails = mockFetchMail.getMails();\r
+ assertEquals("1通受信", 1, mails.length);\r
+\r
+ ReceivedMail mail = mails[0];\r
+ assertEquals("差出人", "差出人", mail.getFrom().getPersonal());\r
+ assertEquals("差出人アドレス", "from@example.net", mail.getFrom().getAddress());\r
+ assertEquals("宛先は1つ", 1, mail.getTo().length);\r
+ assertEquals("宛名", "to@example.com", (mail.getTo()[0]).getAddress());\r
+ assertEquals("件名", "MockFetchMailTest", mail.getSubject());\r
+ assertEquals("本文", "本文", mail.getText());\r
+ }\r
+\r
+}
\ No newline at end of file
--- /dev/null
+package com.ozacc.mail.mock;\r
+\r
+import junit.framework.TestCase;\r
+\r
+import org.apache.log4j.BasicConfigurator;\r
+\r
+import com.dumbster.smtp.SimpleSmtpServer;\r
+import com.ozacc.mail.Mail;\r
+\r
+/**\r
+ * SendMailImplクラスのテストケース。\r
+ * <p>\r
+ * Dumbsterを使用してテストしているが、サポートされていない機能が多い。\r
+ * \r
+ * @author Tomohiro Otsuka\r
+ * @version $Id: MockSendMailTest.java,v 1.6.2.1 2005/01/18 07:24:47 otsuka Exp $\r
+ */\r
+public class MockSendMailTest extends TestCase {\r
+\r
+ private MockSendMail mockSendMail;\r
+\r
+ private SimpleSmtpServer server;\r
+\r
+ /*\r
+ * @see TestCase#setUp()\r
+ */\r
+ protected void setUp() throws Exception {\r
+ super.setUp();\r
+\r
+ BasicConfigurator.configure();\r
+\r
+ mockSendMail = new MockSendMail();\r
+ mockSendMail.setDebug(true);\r
+ }\r
+\r
+ /**\r
+ * @see junit.framework.TestCase#tearDown()\r
+ */\r
+ protected void tearDown() throws Exception {\r
+ BasicConfigurator.resetConfiguration();\r
+ }\r
+\r
+ public void testSendMailNotMatchMailNum() throws Exception {\r
+ Mail mail = new Mail();\r
+ mail.addTo("to@example.com");\r
+ mail.setFrom("from@example.com");\r
+\r
+ mockSendMail.addExpectedMail(mail);\r
+ mockSendMail.addExpectedMail(mail);\r
+\r
+ mockSendMail.send(mail);\r
+\r
+ try {\r
+ mockSendMail.verify();\r
+ fail("This should never be called.");\r
+ } catch (AssertionFailedException expected) {\r
+ // success\r
+ }\r
+ }\r
+\r
+ public void testSendMailSuccess() throws Exception {\r
+ String from = "from@example.com";\r
+ String fromName = "差出人";\r
+ String to = "info@example.com";\r
+ String subject = "件名";\r
+ String text = "テスト成功";\r
+\r
+ Mail mail = new Mail();\r
+ mail.setFrom(from, fromName);\r
+ mail.addTo(to);\r
+ mail.setSubject(subject);\r
+ mail.setText(text);\r
+\r
+ mockSendMail.addExpectedMail(mail);\r
+\r
+ mockSendMail.send(mail);\r
+\r
+ mockSendMail.verify();\r
+ }\r
+\r
+ public void testSendMailToAddressNotMatch() throws Exception {\r
+ String from = "from@example.com";\r
+ String fromName = "差出人";\r
+ String to = "info@example.com";\r
+ String subject = "件名";\r
+ String text = "テスト成功";\r
+\r
+ Mail mail1 = new Mail();\r
+ mail1.setFrom(from, fromName);\r
+ mail1.addTo(to);\r
+ mail1.setSubject(subject);\r
+ mail1.setText(text);\r
+\r
+ Mail mail2 = new Mail();\r
+ mail2.setFrom(from, fromName);\r
+ mail2.addTo("contact@example.com");\r
+ mail2.setSubject(subject);\r
+ mail2.setText(text);\r
+\r
+ mockSendMail.addExpectedMail(mail1);\r
+\r
+ mockSendMail.send(mail2);\r
+\r
+ try {\r
+ mockSendMail.verify();\r
+ fail("This should never be called.");\r
+ } catch (AssertionFailedException expected) {\r
+ // success\r
+ }\r
+ }\r
+\r
+ public void testSendMailWithMockMail() throws Exception {\r
+ Mail sentMail = new Mail();\r
+ sentMail.setFrom("from@example.com");\r
+ sentMail.setSubject("件名");\r
+ sentMail.addTo("to@example.com");\r
+ sentMail.setText("動的生成される本文");\r
+\r
+ Mail expectedMail = new Mail();\r
+ expectedMail.setFrom("from@example.com");\r
+ expectedMail.setSubject("件名");\r
+\r
+ MockMail mockMail = new MockMail();\r
+ mockMail.setFrom("from@example.com");\r
+ mockMail.setSubject("件名");\r
+\r
+ MockSendMail sendMail = new MockSendMail();\r
+ sendMail.setDebug(true);\r
+\r
+ sendMail.addExpectedMail(mockMail);\r
+ sendMail.send(sentMail);\r
+ sendMail.verify(); // 成功\r
+\r
+ sendMail = new MockSendMail();\r
+ sendMail.addExpectedMail(expectedMail);\r
+ sendMail.send(sentMail);\r
+ try {\r
+ sendMail.verify(); // エラー\r
+ fail("This should never be called.");\r
+ } catch (AssertionFailedException expected) {\r
+ // success\r
+ }\r
+ }\r
+\r
+ public void testSendMailWithMockMulitpartMail() throws Exception {\r
+ Mail sentMail = new Mail();\r
+ sentMail.setFrom("from@example.com");\r
+ sentMail.setSubject("件名");\r
+ sentMail.addTo("to@example.com");\r
+ sentMail.setText("動的生成される本文");\r
+\r
+ Mail expectedMail = new Mail();\r
+ expectedMail.setFrom("from@example.com");\r
+ expectedMail.setSubject("件名");\r
+ expectedMail.addTo("to@example.com");\r
+ expectedMail.setText("動的生成される本文");\r
+ expectedMail.setHtmlText("<html><body>HTMLの本文</body></html>");\r
+\r
+ MockSendMail sendMail = new MockSendMail();\r
+ sendMail.setDebug(true);\r
+\r
+ sendMail.addExpectedMail(expectedMail);\r
+ sendMail.send(sentMail);\r
+\r
+ try {\r
+ sendMail.verify();\r
+ fail("This should never be called.");\r
+ } catch (AssertionFailedException expected) {\r
+ // success\r
+ }\r
+\r
+ sendMail.initialize();\r
+\r
+ sendMail.addExpectedMail(expectedMail);\r
+ sendMail.send(sentMail);\r
+ try {\r
+ sendMail.verify();\r
+ fail("This should never be called.");\r
+ } catch (AssertionFailedException expected) {\r
+ // HTML本文が一致しない\r
+ }\r
+\r
+ sendMail.initialize();\r
+\r
+ sentMail.setHtmlText("<html><body>HTMLの本文</body></html>");\r
+ sendMail.addExpectedMail(expectedMail);\r
+ sendMail.send(sentMail);\r
+ sendMail.verify();\r
+\r
+ sendMail.initialize();\r
+\r
+ expectedMail.setHtmlText(null);\r
+ sendMail.addExpectedMail(expectedMail);\r
+ sendMail.send(sentMail);\r
+ // expectedはマルチパートではないからsentMailのHTMLとは比較されない\r
+ sendMail.verify();\r
+\r
+ sendMail.initialize();\r
+\r
+ MockMail expectedMockMail = new MockMail(expectedMail);\r
+ sendMail.addExpectedMail(expectedMockMail);\r
+ sendMail.send(sentMail);\r
+ try {\r
+ sendMail.verify();\r
+ } catch (AssertionFailedException expected) {\r
+ fail("This should never be called.");\r
+ }\r
+ }\r
+\r
+ public void testSendMailToNameNotMatch() throws Exception {\r
+ String from = "from@example.com";\r
+ String fromName = "差出人";\r
+ String to = "info@example.com";\r
+ String subject = "件名";\r
+ String text = "テスト成功";\r
+\r
+ Mail mail1 = new Mail();\r
+ mail1.setFrom(from, fromName);\r
+ mail1.addTo(to, "宛先A");\r
+ mail1.setSubject(subject);\r
+ mail1.setText(text);\r
+\r
+ Mail mail2 = new Mail();\r
+ mail2.setFrom(from, fromName);\r
+ mail2.addTo(to, "宛先B");\r
+ mail2.setSubject(subject);\r
+ mail2.setText(text);\r
+\r
+ mockSendMail.addExpectedMail(mail1);\r
+\r
+ mockSendMail.send(mail2);\r
+\r
+ try {\r
+ mockSendMail.verify();\r
+ fail("This should never be called.");\r
+ } catch (AssertionFailedException expected) {\r
+ // success\r
+ }\r
+ }\r
+ \r
+ public void testSendMailFromNotMatch() throws Exception {\r
+ String from = "from@example.com";\r
+ String fromName = "差出人";\r
+ String to = "info@example.com";\r
+ String subject = "件名";\r
+ String text = "テスト成功";\r
+\r
+ Mail mail1 = new Mail();\r
+ mail1.setFrom(from, fromName);\r
+ mail1.addTo(to);\r
+ mail1.setSubject(subject);\r
+ mail1.setText(text);\r
+\r
+ Mail mail2 = new Mail();\r
+ mail2.setFrom("from@foo.com", fromName);\r
+ mail2.addTo(to);\r
+ mail2.setSubject(subject);\r
+ mail2.setText(text);\r
+\r
+ mockSendMail.addExpectedMail(mail1);\r
+\r
+ mockSendMail.send(mail2);\r
+\r
+ try {\r
+ mockSendMail.verify();\r
+ fail("This should never be called.");\r
+ } catch (AssertionFailedException expected) {\r
+ // success\r
+ }\r
+ }\r
+ \r
+}
\ No newline at end of file
--- /dev/null
+package com.ozacc.mail.spring;\r
+\r
+import junit.framework.TestCase;\r
+\r
+import org.apache.log4j.BasicConfigurator;\r
+import org.springframework.context.ApplicationContext;\r
+import org.springframework.context.support.FileSystemXmlApplicationContext;\r
+\r
+import com.ozacc.mail.Mail;\r
+import com.ozacc.mail.mock.EqualityCheck;\r
+\r
+/**\r
+ * XMLMailFactoryBeanのTestCase。\r
+ * \r
+ * @author Tomohiro Otsuka\r
+ * @version $Id: XMLMailFactoryBeanTest.java,v 1.5 2004/09/17 20:34:31 otsuka Exp $\r
+ */\r
+public class XMLMailFactoryBeanTest extends TestCase {\r
+\r
+ Mail mail;\r
+\r
+ /**\r
+ * @see junit.framework.TestCase#setUp()\r
+ */\r
+ public void setUp() throws Exception {\r
+ super.setUp();\r
+\r
+ BasicConfigurator.configure();\r
+\r
+ mail = new Mail();\r
+ mail.setSubject("XMLMailBuilderのテストケース");\r
+ mail.setText("改行します。\n改行しました。\nテストは成功。");\r
+ mail.setFrom("from@example.com", "差出人");\r
+ mail.setReturnPath("return@example.com");\r
+ mail.setReplyTo("reply@example.com");\r
+ mail.addTo("to1@example.com", "宛先1");\r
+ mail.addTo("to2@example.com");\r
+ mail.addCc("cc1@example.com", "CC1");\r
+ mail.addCc("cc2@example.com");\r
+ mail.addBcc("bcc@example.com");\r
+ }\r
+\r
+ /**\r
+ * @see junit.framework.TestCase#tearDown()\r
+ */\r
+ protected void tearDown() throws Exception {\r
+ BasicConfigurator.resetConfiguration();\r
+ }\r
+\r
+ public void testMailFactoryBeanClassPathFail() throws Exception {\r
+ mail.addBcc("ccb@example.com");\r
+\r
+ ApplicationContext context = new FileSystemXmlApplicationContext(\r
+ "src/test/com/ozacc/mail/spring/testContext.xml");\r
+ Mail result = (Mail)context.getBean("mail");\r
+\r
+ assertFalse(EqualityCheck.equals(mail, result));\r
+\r
+ }\r
+\r
+ public void testMailFactoryBeanClassPath() throws Exception {\r
+ ApplicationContext context = new FileSystemXmlApplicationContext(\r
+ "src/test/com/ozacc/mail/spring/testContext.xml");\r
+ Mail result = (Mail)context.getBean("mail");\r
+\r
+ assertTrue(EqualityCheck.equals(mail, result));\r
+ }\r
+\r
+ public void testMailFactoryBeanFilePath() throws Exception {\r
+ ApplicationContext context = new FileSystemXmlApplicationContext(\r
+ "src/test/com/ozacc/mail/spring/testContext.xml");\r
+ Mail result = (Mail)context.getBean("mail2");\r
+\r
+ assertTrue(EqualityCheck.equals(mail, result));\r
+ }\r
+\r
+ public void testMailFactoryBeanLocation() throws Exception {\r
+ ApplicationContext context = new FileSystemXmlApplicationContext(\r
+ "src/test/com/ozacc/mail/spring/testContext.xml");\r
+ Mail result = (Mail)context.getBean("mail3");\r
+\r
+ assertTrue(EqualityCheck.equals(mail, result));\r
+ }\r
+\r
+ public void testMailFactoryBeanLocationClasspath() throws Exception {\r
+ ApplicationContext context = new FileSystemXmlApplicationContext(\r
+ "src/test/com/ozacc/mail/spring/testContext.xml");\r
+ Mail result = (Mail)context.getBean("mail4");\r
+\r
+ assertTrue(EqualityCheck.equals(mail, result));\r
+ }\r
+\r
+}
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>\r
+<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">\r
+\r
+<beans>\r
+ <bean id="mail" class="com.ozacc.mail.spring.XMLMailFactoryBean">\r
+ <property name="classPath">\r
+ <value>/com/ozacc/mail/test-mail1.xml</value>\r
+ </property>\r
+ </bean>\r
+ <bean id="mail2" class="com.ozacc.mail.spring.XMLMailFactoryBean">\r
+ <property name="filePath">\r
+ <value>src/test/com/ozacc/mail/test-mail1.xml</value>\r
+ </property>\r
+ </bean>\r
+ <bean id="mail3" class="com.ozacc.mail.spring.XMLMailFactoryBean">\r
+ <property name="location">\r
+ <value>file:src/test/com/ozacc/mail/test-mail1.xml</value>\r
+ </property>\r
+ </bean>\r
+ <bean id="mail4" class="com.ozacc.mail.spring.XMLMailFactoryBean">\r
+ <property name="location">\r
+ <value>classpath:/com/ozacc/mail/test-mail1.xml</value>\r
+ </property>\r
+ </bean>\r
+</beans>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8" ?>\r
+<!DOCTYPE mail PUBLIC "-//OZACC//DTD MAIL//EN" "http://www.ozacc.com/library/dtd/ozacc-mail.dtd">\r
+<mail>\r
+\r
+ <returnPath email="return@example.com" />\r
+ \r
+ <from email="from@example.com" name="差出人" />\r
+ \r
+ <recipients>\r
+ <to email="to1@example.com" name="宛先1" />\r
+ <cc email="cc1@example.com" name="CC1" />\r
+ <bcc email="bcc@example.com" />\r
+ <cc email="cc2@example.com" />\r
+ <to email="to2@example.com" />\r
+ </recipients>\r
+ \r
+ <replyTo email="reply@example.com" />\r
+ \r
+ <!-- subject(件名)の前後のホワイトスペース(半角スペース、タブ、改行)は削除されます。 -->\r
+ <subject>\r
+ XMLMailBuilderのテストケース\r
+ </subject>\r
+ \r
+ <!-- body(本文)の前後のホワイトスペース(半角スペース、タブ、改行)は削除されます。 -->\r
+ <body>\r
+改行します。\r
+改行しました。\r
+テストは成功。\r
+ </body>\r
+ \r
+ <html>\r
+<![CDATA[\r
+<html><body>これはHTMLテキスト</body></html>\r
+]]>\r
+ </html>\r
+ \r
+</mail>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8" ?>\r
+<!DOCTYPE mail PUBLIC "-//OZACC//DTD MAIL//EN" "http://www.ozacc.com/library/dtd/ozacc-mail.dtd">\r
+<mail>\r
+\r
+ <returnPath email="return@example.com" />\r
+ \r
+ <from email="from@example.com" name="差出人" />\r
+ \r
+ <recipients>\r
+ <!-- to、cc、bccのひとつも指定されていないのでDTD違反 -->\r
+ </recipients>\r
+ \r
+ <replyTo email="reply@example.com" />\r
+ \r
+ <!-- subject(件名)の前後のホワイトスペース(半角スペース、タブ、改行)は削除されます。 -->\r
+ <subject>\r
+ XMLMailBuilderのテストケース\r
+ </subject>\r
+ \r
+ <!-- body(本文)の前後のホワイトスペース(半角スペース、タブ、改行)は削除されます。 -->\r
+ <body>\r
+改行します。\r
+改行しました。\r
+テストは成功。\r
+ </body>\r
+ \r
+</mail>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8" ?>\r
+<!DOCTYPE mail PUBLIC "-//OZACC//DTD MAIL//EN" "http://www.ozacc.com/library/dtd/ozacc-mail.dtd">\r
+<mail>\r
+ \r
+ <from email="shop@example.com" name="XMLMailBuilderオンラインショップ" />\r
+ \r
+ <recipients>\r
+ <to email="${customer.email}" name="${customer.name}" />\r
+ </recipients>\r
+ \r
+ <!-- subject(件名)の前後のホワイトスペース(半角スペース、タブ、改行)は削除されます。 -->\r
+ <subject>\r
+ XMLMailBuilderオンラインショップ - ご注文の確認\r
+ </subject>\r
+ \r
+ <!-- body(本文)の前後のホワイトスペース(半角スペース、タブ、改行)は削除されます。 -->\r
+ <body><![CDATA[\r
+${customer.name}様\r
+\r
+お買い上げありがとうございました。\r
+\r
+#if (${customer.name} == "伊東美咲" && ${customer.email} == "misaki@example.com")\r
+${item}\r
+#end\r
+ ]]></body>\r
+ \r
+</mail>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" ?>\r
+<!DOCTYPE mail PUBLIC "-//OZACC//DTD MAIL//EN" "http://www.ozacc.com/library/dtd/ozacc-mail.dtd">\r
+<mail>\r
+ \r
+ <from email="from@example.com" name="-~¢£∥¬" />\r
+ \r
+ <!-- subject(件名)の前後のホワイトスペース(半角スペース、タブ、改行)は削除されます。 -->\r
+ <subject>\r
+ 文字化けテスト -~¢£∥¬\r
+ </subject>\r
+ \r
+ <!-- body(本文)の前後のホワイトスペース(半角スペース、タブ、改行)は削除されます。 -->\r
+ <body>\r
+文字化けテスト -~¢£∥¬\r
+\r
+ソニーは、ソニースタイル専用モデルとして「VAIO type B」を10月2日より発売する。BTOに対応し、直販価格は149,800円~。OSはWindows XP Professional。\r
+\r
+14.1型液晶を搭載する2スピンドルノート「バイオノートZ」の後継モデル。夏のVAIOシリーズモデルチェンジ時にはラインナップされておらず、今回新たに直販専用モデルとしてリニューアルされた。\r
+\r
+デザインはほぼ従来モデルを踏襲しながら各所を強化。従来モデルと比較して、マグネシウム合金部分の肉厚が0.3mm増え、強度が上昇したほか、パワーオンパスワード、ハードディスクパスワードなどデータ漏洩防止機能が追加され、ビジネス用途も意識した仕様となった。\r
+\r
+ボディカラーは若干変更され、従来モデルではシルバーだったパームレスト部分がブラックに変更されるなど、落ち着いた印象となった。\r
+\r
+液晶パネルは、従来モデルでは1,400×1,050ドット(SXGA+)液晶を搭載していたが、今回のモデルでは1,024×768ドット(XGA)の液晶パネルを搭載したモデルも用意される。\r
+\r
+また、CPUとしてPentium M 755(2GHz)/745(1.8GHz)/725(1.6GHz)などのほか、Celeron-M 350(1.3GHz)も選択できるなど、ハイエンドからエントリーまでがカバーされる。\r
+\r
+そのほかの主な仕様は、メモリ256MB~1.5GB、HDD 40GB~80GB、Intel 855GME(ビデオ機能内蔵)チップセットなどを搭載。光学ドライブとしてDVD±RWドライブまたはDVD/CD-RWドライブを選択可能。\r
+\r
+インターフェイスはEthernet、IEEE 802.11b/g無線LAN、Type2 PCカードスロット×1、USB 2.0×2、IEEE 1394(4ピン)×1、D-Sub15ピンなどを搭載する。なお、各種インターフェイスを備えるポートリプリケータが付属するが、従来モデルに搭載されていたDVI出力端子が省略されている。\r
+\r
+バッテリはリチウムイオンバッテリで、駆動時間は約4時間(Sバッテリ)。本体サイズは321×255.3×29~43.2mm(幅×奥行き×高さ)、重量は約2.3kg。\r
+\r
+なお、企業向けのビジネスパーソナル向けとして液晶パネルやCPUなどが異なる8モデルも用意される。付属のアプリケーションなどが最小限に抑えられるなど、より企業ユーザー向けのモデルとなる。\r
+ </body>\r
+ \r
+</mail>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" ?>\r
+<!DOCTYPE mail PUBLIC "-//OZACC//DTD MAIL//EN" "http://www.ozacc.com/library/dtd/ozacc-mail.dtd">\r
+<mail>\r
+ \r
+ <from email="from@example.com" name="HTMLメール送信者" />\r
+ \r
+ <!-- subject(件名)の前後のホワイトスペース(半角スペース、タブ、改行)は削除されます。 -->\r
+ <subject>\r
+ HTML送信テスト\r
+ </subject>\r
+ \r
+ <!-- body(本文)の前後のホワイトスペース(半角スペース、タブ、改行)は削除されます。 -->\r
+ <body>\r
+<![CDATA[\r
+<html>\r
+<head>\r
+<meta http-equiv=Content-Typecontent="text/html;charset=ISO-2022-JP">\r
+<title>Power Mac G5、登場。</title>\r
+<style type="text/css">\r
+<!--\r
+.p10 { font-size: 10px}\r
+.p12 { font-size: 12px; line-height: 14px}\r
+.p14 { font-size: 14px}\r
+.p16 { font-size: 16px}\r
+.p14b { font-size: 14px; font-weight: bold}\r
+.p12b { font-size: 12px; font-weight: bold}\r
+.p16b { font-size: 16px; font-weight: bold}\r
+.p11 { font-size: 11px}\r
+-->\r
+</style>\r
+</head>\r
+\r
+<body bgcolor="#FFFFFF">\r
+\r
+<table width="560" border="0" cellspacing="0" cellpadding="0" align="center" bgcolor="#FFFFFF">\r
+\r
+<tr valign="top"><td width="560"><img src="http://www.apple.co.jp/articles/backnumber/html/amd/20030624_g5/spacer.gif" width="560" height="10" alt="" border="0"><a href="http://news.apple.co.jp/"><img src="http://www.apple.co.jp/articles/backnumber/html/amd/20030624_g5/030624am_p01.jpg" width="560" height="189" alt="Power Mac G5、登場。" border="0"></a></td></tr>\r
+<tr valign="top"><td width="560"><img src="http://www.apple.co.jp/articles/backnumber/html/amd/20030624_g5/spacer.gif" width="560" height="10" alt="" border="0"><img src="http://www.apple.co.jp/articles/backnumber/html/amd/20030624_g5/spacer.gif" width="560" height="12" alt="" border="0"></td></tr>\r
+<tr valign="top">\r
+<td>\r
+<table width="560" border="0" cellspacing="0" cellpadding="0">\r
+\r
+<tr valign="top">\r
+<td width="348" valign="top">\r
+<table width="348" border="0" cellspacing="0" cellpadding="0">\r
+<tr valign="top"><td width="348"><img src="http://www.apple.co.jp/articles/backnumber/html/amd/20030624_g5/spacer.gif" width="348" height="3" alt="" border="0"><img src="http://www.apple.co.jp/articles/backnumber/html/amd/20030624_g5/spacer.gif" width="348" height="4" alt="" border="0"></td></tr>\r
+<tr valign="top"><td width="348">\r
+\r
+<font size="3" color="#000000"><span class="p12"><img src="http://www.apple.co.jp/articles/backnumber/html/amd/20030624_g5/spacer.gif" width="1" height="1" alt="" border="0">アップルが再び、コンピュータの世界を変えます。<br>\r
+<img src="http://www.apple.co.jp/articles/backnumber/html/amd/20030624_g5/spacer.gif" width="1" height="1" alt="" border="0">世界初の64ビットプロセッサを搭載したパーソナルコンピュータ、<a href="http://news.apple.co.jp/">Power Mac G5</a>の登場です。未来はもう、あなたの手の届くところにあります。<br><br></span></font>\r
+\r
+\r
+<table border="0" cellspacing="0" cellpadding="0"><tr valign="top"><td>\r
+<font size="3" color="#000000"><b><span class="p12b">\r
+<img src="http://www.apple.co.jp/articles/backnumber/html/amd/20030205/space.gif" width="1" height="1" alt="" border="0">すべてが新しいPower Mac G5。</span></b></font></td></tr></table>\r
+\r
+<font size="3" color="#000000"><span class="p12"><img src="http://www.apple.co.jp/articles/backnumber/html/amd/20030624_g5/spacer.gif" width="1" height="1" alt="" border="0">Power Mac G5には、最新技術が詰まっています。まずはアップルとIBMの共同開発による非常にパワフルな64ビット<br>\r
+\r
+<img src="http://www.apple.co.jp/articles/backnumber/html/amd/20030624_g5/spacer.gif" width="1" height="1" alt="" border="0"><a href="http://news.apple.co.jp/cgi-bin16/DM/y/hQHF0C7VkY0G7y0lKB0AA">PowerPC G5プロセッサ</a>。\r
+これに、業界最速のフロントエンド・バス、高バンド幅のシステムアーキテクチャ、最大8GBまで増設可能な高速メモリが加わり、デスクトップコンピュータの処理能力を飛躍的に進化させました。実際に、Power Mac G5のパフォーマンスは、最速のPentium 4を搭載したデスクトップコンピュータをも凌ぐのです*。<br><br></span></font>\r
+\r
+<table border="0" cellspacing="0" cellpadding="0"><tr valign="top"><td>\r
+<font size="3" color="#000000"><b><span class="p12b">\r
+<img src="http://www.apple.co.jp/articles/backnumber/html/amd/20030205/space.gif" width="1" height="1" alt="" border="0">このマシンをまだ「パーソナルコンピュータ」と呼んでいいのだろうか?</span></b></font></td></tr></table>\r
+\r
+<font size="3" color="#000000"><span class="p12"><img src="http://www.apple.co.jp/articles/backnumber/html/amd/20030624_g5/spacer.gif" width="1" height="1" alt="" border="0">Power Mac G5には、高速PCI-Xスロット、最新鋭のストレージ、AGP 8X Proグラフィック、驚くほど静かなエンクロージャなど、いままでのPCでは実現不可能だった革新的な機能が満載で、高度なマシンスペックが要求されるグラフィック制作や映像編集でさえも楽々とこなすことができます。<br>\r
+<img src="http://www.apple.co.jp/articles/backnumber/html/amd/20030624_g5/spacer.gif" width="1" height="1" alt="" border="0">さらに<a href="http://news.apple.co.jp/cgi-bin16/DM/y/hQHF0C7VkY0G7y0lKH0AG">Mac OS X v10.2</a>がPowerPC G5プロセッサに合わせて特別にチューンされているため、どんなアプリケーションを使っても、すぐに64ビットのパワーを実感できます。</span></font></td></tr>\r
+\r
+<tr valign="top"><td width="348"><img src="http://www.apple.co.jp/articles/backnumber/html/amd/20030624_g5/spacer.gif" width="348" height="5" alt="" border="0"><img src="http://www.apple.co.jp/articles/backnumber/html/amd/20030624_g5/spacer.gif" width="348" height="5" alt="" border="0"></td></tr>\r
+\r
+</table>\r
+</td>\r
+<td width="6"><img src="http://www.apple.co.jp/articles/backnumber/html/amd/20030624_g5/spacer.gif" width="6" height="1" alt="" border="0"></td>\r
+<td width="1" bgcolor="#CCCCCC"><img src="http://www.apple.co.jp/articles/backnumber/html/amd/20030624_g5/spacer.gif" width="1" height="1" alt="" border="0"></td>\r
+<td width="9"><img src="http://www.apple.co.jp/articles/backnumber/html/amd/20030624_g5/spacer.gif" width="9" height="1" alt="" border="0"></td>\r
+<td width="196" valign="top">\r
+<img src="http://www.apple.co.jp/articles/backnumber/html/amd/20030624_g5/030624am_i01.gif" width="196" height="336" alt="" border="0" usemap="#RightNavi"></td>\r
+</tr>\r
+</table>\r
+\r
+<map name="RightNavi">\r
+\r
+<area shape="rect" coords="0,8,47,56" href="http://news.apple.co.jp/" alt="Order Now" title="Order Now">\r
+<area shape="rect" coords="57,30,97,43" href="http://news.apple.co.jp/" alt="先行予約" title="先行予約">\r
+<area shape="rect" coords="0,80,173,162" href="http://news.apple.co.jp/" alt="Power Mac G5" title="Power Mac G5">\r
+<area shape="rect" coords="114,175,176,187" href="http://news.apple.co.jp/" alt="もっと詳しく" title="もっと詳しく">\r
+<area shape="rect" coords="48,249,180,263" href="http://news.apple.co.jp/" alt="SPEC CPU2000ベンチマーク" title="SPEC CPU2000ベンチマーク">\r
+</map>\r
+\r
+\r
+</td>\r
+</tr>\r
+<tr valign="top"><td width="560">\r
+\r
+<table border="0" cellspacing="0" cellpadding="0"><tr valign="top"><td>\r
+<font size="3" color="#000000"><b><span class="p12b">\r
+<img src="http://www.apple.co.jp/articles/backnumber/html/amd/20030205/space.gif" width="1" height="1" alt="" border="0">コンピュータは永遠に進化する。</span></b></font></td></tr></table>\r
+\r
+<font size="3" color="#000000"><span class="p12"><img src="http://www.apple.co.jp/articles/backnumber/html/amd/20030624_g5/spacer.gif" width="1" height="1" alt="" border="0">いったん64ビットを体験したら、もう後戻りはできません。あなたが前へ進むための、エキサイティングな3つの選択肢を用意しています。<a href="http://news.apple.co.jp/">Power Mac G5</a>には、1.6GHz、1.8GHz、そしてデュアル2GHzの3つのモデルがあります。\r
+価格は驚きの244,800円(Apple Storeプライス、税別)から。Power Mac G5のお見積、ご購入に関するご相談は<a href="http://news.apple.co.jp/">こちら</a>からどうぞ。どのモデルにもSuperDriveが搭載され、自分自身でCDやDVDを作成することができるなど、Power Mac G5の魅力はまだまだたくさんあります。<br><br>\r
+\r
+<img src="http://www.apple.co.jp/articles/backnumber/html/amd/20030624_g5/spacer.gif" width="1" height="1" alt="" border="0">これは革命にほかなりません。未来は64ビットPower Mac G5の中に。</span></font></td></tr>\r
+<tr valign="top"><td width="560"><img src="http://www.apple.co.jp/articles/backnumber/html/amd/20030624_g5/spacer.gif" width="560" height="20" alt="" border="0"><img src="http://www.apple.co.jp/articles/backnumber/html/amd/20030624_g5/560_line.gif" width="560" height="1" alt="" border="0"><img src="http://www.apple.co.jp/articles/backnumber/html/amd/20030624_g5/spacer.gif" width="560" height="20" alt="" border="0"></td></tr>\r
+\r
+<tr valign="top"><td>\r
+\r
+<font size="1" color="#999999"><span class="p11"><img src="http://www.apple.co.jp/articles/backnumber/html/amd/20030205/space.gif" width="1" height="1" alt="" border="0">「世界最速」は、SPEC CPU2000ベンチマークと、プロ向けの最新アプリケーションでのパフォーマンステストの結果に基づきます。このテストの比較には、3GHz Pentium 4搭載のDell Dimension 8300と、デュアル3.06GHz Xeon搭載のDell Precision 650を使用しています。SPEC CPU 2000ベンチマークはGCC 3.3で実行し、個別にテストしています。\r
+詳細レポートはVeritest社より入手できます。プロ向けアプリケーションを使ったテストは、2003年6月にアップル社内で実施されました。<br><br>\r
+\r
+<img src="http://www.apple.co.jp/articles/backnumber/html/amd/20030205/space.gif" width="1" height="1" alt="" border="0">* SPEC CPU2000ベンチマークと、プロ向けの最新アプリケーションでのパフォーマンステストの結果に基づきます。このテストの比較には、3GHz Pentium 4搭載のDell Dimension 8300と、デュアル3.06GHz Xeon搭載のDell Precision 650を使用しています。SPEC CPU2000ベンチマークはGCC 3.3で実行し、個別にテストしています。プロ向けアプリケーションを使ったテストは、2003年6月にApple社内で実施されました。<br><br>\r
+\r
+<img src="http://www.apple.co.jp/articles/backnumber/html/amd/20030205/space.gif" width="1" height="1" alt="" border="0">Copyright (C) 2003 Apple Computer, Inc. <a href="http://news.apple.co.jp/">All rights reserved</a>. | <a href="http://news.apple.co.jp/cgi-bin16/DM/y/hQHF0C7VkY0G7y0S6Z0Am">登録内容の変更</a> | <a href="http://news.apple.co.jp/">プライバシーについて</a><br>\r
+\r
+<img src="http://www.apple.co.jp/articles/backnumber/html/amd/20030205/space.gif" width="1" height="1" alt="" border="0"><a href="http://news.apple.co.jp/">メールフォーマットの変更</a></span></font></td></tr>\r
+</table>\r
+</body>\r
+\r
+<IMG SRC="http://news.apple.co.jp/cgi-bin16/flosensing"></html>\r
+]]>\r
+\r
+ </body>\r
+ \r
+</mail>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8" ?>\r
+<!DOCTYPE mail PUBLIC "-//OZACC//DTD MAIL//EN" "http://www.ozacc.com/library/dtd/ozacc-mail.dtd">\r
+<mail>\r
+\r
+ \r
+ <!-- subject(件名)の前後のホワイトスペース(半角スペース、タブ、改行)は削除されます。 -->\r
+ <subject>\r
+ CDATA取り扱いのテストケース\r
+ </subject>\r
+ \r
+ <!-- body(本文)の前後のホワイトスペース(半角スペース、タブ、改行)は削除されます。 -->\r
+ <body>\r
+<![CDATA[これはCDATAのテキストです。]]>\r
+ </body>\r
+ \r
+</mail>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8" ?>\r
+<!DOCTYPE mails PUBLIC "-//OZACC//DTD MUTIPLE MAILS//EN" "http://www.ozacc.com/library/dtd/ozacc-multiple-mails.dtd">\r
+<mails>\r
+ <mail id="first">\r
+ <from email="from@example.com" name="差出人" /> \r
+ <recipients>\r
+ <to email="to1@example.com" name="宛先1" />\r
+ </recipients>\r
+ \r
+ <subject>\r
+ MultipleMailBuilder 1通目\r
+ </subject>\r
+ \r
+ <body>1通目</body>\r
+ </mail>\r
+ \r
+ <mail id="second">\r
+ <from email="from@example.com" name="差出人" /> \r
+ <recipients>\r
+ <to email="to2@example.com" name="宛先2" />\r
+ </recipients>\r
+ \r
+ <subject>\r
+ MultipleMailBuilder 2通目\r
+ </subject>\r
+ \r
+ <body>2通目</body>\r
+ </mail>\r
+ \r
+ <mail id="third">\r
+ <from email="from@example.com" name="差出人" /> \r
+ <recipients>\r
+ <to email="to3@example.com" name="宛先3" />\r
+ </recipients>\r
+ \r
+ <subject>\r
+ MultipleMailBuilder 3通目\r
+ </subject>\r
+ \r
+ <body>3通目</body>\r
+ </mail>\r
+</mails>
\ No newline at end of file
--- /dev/null
+package com.ozacc.mail.xml.impl;\r
+\r
+import java.io.File;\r
+\r
+import junit.framework.TestCase;\r
+\r
+import org.apache.log4j.BasicConfigurator;\r
+import org.jdom.Document;\r
+import org.jdom.input.DOMBuilder;\r
+import org.jdom.output.XMLOutputter;\r
+\r
+import com.ozacc.mail.Mail;\r
+\r
+/**\r
+ * \r
+ * @author Tomohiro Otsuka\r
+ * @version $Id: JDomXMLBuilderTest.java,v 1.2.2.1 2004/10/24 10:28:09 otsuka Exp $\r
+ */\r
+public class JDomXMLBuilderTest extends TestCase {\r
+\r
+ private JDomXMLBuilder builder;\r
+\r
+ /**\r
+ * @see TestCase#setUp()\r
+ */\r
+ protected void setUp() throws Exception {\r
+ super.setUp();\r
+\r
+ BasicConfigurator.configure();\r
+\r
+ builder = new JDomXMLBuilder();\r
+ }\r
+\r
+ /**\r
+ * @see junit.framework.TestCase#tearDown()\r
+ */\r
+ protected void tearDown() throws Exception {\r
+ BasicConfigurator.resetConfiguration();\r
+ }\r
+\r
+ public final void testCreateDocument() throws Exception {\r
+ Mail mail = getMailForTest();\r
+\r
+ org.w3c.dom.Document doc = builder.buildDocument(mail);\r
+\r
+ DOMBuilder builder = new DOMBuilder();\r
+ Document jdomDoc = builder.build(doc);\r
+\r
+ System.out.println(jdomDoc);\r
+\r
+ XMLOutputter outputter = new XMLOutputter();\r
+ String document = outputter.outputString(jdomDoc);\r
+ System.out.println(document);\r
+\r
+ }\r
+\r
+ /*\r
+ * Class under test for void saveDocument(Mail, File)\r
+ */\r
+ public final void testSaveDocumentMailFile() throws Exception {\r
+ Mail mail = getMailForTest();\r
+\r
+ String filePath = "target/test/data/mail.xml";\r
+ File file = new File(filePath);\r
+ file.getParentFile().mkdirs();\r
+\r
+ builder.saveDocument(mail, file);\r
+ }\r
+\r
+ public final void testSaveDocumentHtml() throws Exception {\r
+ Mail mail = getMailForTest();\r
+ mail.setHtmlText("<html><body>テスト成功</body></html>");\r
+\r
+ String filePath = "target/test/data/mail-jdk-html.xml";\r
+ File file = new File(filePath);\r
+ file.getParentFile().mkdirs();\r
+\r
+ builder.saveDocument(mail, file);\r
+ }\r
+\r
+ /**\r
+ * @return \r
+ */\r
+ private Mail getMailForTest() {\r
+ String from = "from@example.com";\r
+ String fromName = "差出人";\r
+ String to = "info@example.com";\r
+ String subject = "件名";\r
+ String text = "テスト成功\n&<>";\r
+\r
+ Mail mail = new Mail();\r
+ mail.setFrom(from, fromName);\r
+ mail.addTo(to);\r
+ mail.setSubject(subject);\r
+ mail.setText(text);\r
+ return mail;\r
+ }\r
+\r
+}
\ No newline at end of file
--- /dev/null
+package com.ozacc.mail.xml.impl;\r
+\r
+import java.io.File;\r
+\r
+import junit.framework.TestCase;\r
+\r
+import org.apache.log4j.BasicConfigurator;\r
+import org.jdom.Document;\r
+import org.jdom.input.DOMBuilder;\r
+import org.jdom.output.XMLOutputter;\r
+\r
+import com.ozacc.mail.Mail;\r
+import com.ozacc.mail.xml.XMLBuilder;\r
+\r
+/**\r
+ * \r
+ * @author Tomohiro Otsuka\r
+ * @version $Id: XMLBuilderImplTest.java,v 1.4.2.1 2004/10/24 10:28:09 otsuka Exp $\r
+ */\r
+public class XMLBuilderImplTest extends TestCase {\r
+\r
+ private XMLBuilder builder;\r
+\r
+ /**\r
+ * @see junit.framework.TestCase#setUp()\r
+ */\r
+ protected void setUp() throws Exception {\r
+ super.setUp();\r
+\r
+ BasicConfigurator.configure();\r
+\r
+ builder = new XMLBuilderImpl();\r
+ }\r
+\r
+ /**\r
+ * @see junit.framework.TestCase#tearDown()\r
+ */\r
+ protected void tearDown() throws Exception {\r
+ BasicConfigurator.resetConfiguration();\r
+ }\r
+\r
+ public final void testCreateDocument() throws Exception {\r
+ Mail mail = getMailForTest();\r
+\r
+ org.w3c.dom.Document doc = builder.buildDocument(mail);\r
+\r
+ DOMBuilder builder = new DOMBuilder();\r
+ Document jdomDoc = builder.build(doc);\r
+\r
+ System.out.println(jdomDoc);\r
+\r
+ XMLOutputter outputter = new XMLOutputter();\r
+ String document = outputter.outputString(jdomDoc);\r
+ System.out.println(document);\r
+ }\r
+\r
+ /*\r
+ * Class under test for void saveDocument(Mail, File)\r
+ */\r
+ public final void testSaveDocumentMailFile() throws Exception {\r
+ Mail mail = getMailForTest();\r
+\r
+ String filePath = "target/test/data/mail-jdk.xml";\r
+ File file = new File(filePath);\r
+ file.getParentFile().mkdirs();\r
+\r
+ builder.saveDocument(mail, file);\r
+ }\r
+\r
+ public final void testSaveDocumentHtml() throws Exception {\r
+ Mail mail = getMailForTest();\r
+ mail.setHtmlText("<html><body>テスト成功</body></html>");\r
+\r
+ String filePath = "target/test/data/mail-jdk-html.xml";\r
+ File file = new File(filePath);\r
+ file.getParentFile().mkdirs();\r
+\r
+ builder.saveDocument(mail, file);\r
+ }\r
+\r
+ /**\r
+ * @return \r
+ */\r
+ private Mail getMailForTest() {\r
+ String from = "from@example.com";\r
+ String fromName = "差出人";\r
+ String to = "info@example.com";\r
+ String subject = "件名";\r
+ String text = "テスト成功\n&<>";\r
+\r
+ Mail mail = new Mail();\r
+ mail.setFrom(from, fromName);\r
+ mail.addTo(to);\r
+ mail.setSubject(subject);\r
+ mail.setText(text);\r
+ return mail;\r
+ }\r
+\r
+}
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" ?>\r
+<document>\r
+\r
+ <properties>\r
+ <title>ozacc-mail library</title>\r
+ <author email="info@ozacc.com">Tomohiro Otsuka</author>\r
+ </properties>\r
+\r
+ <body>\r
+\r
+ <section name="ozacc-mail library">\r
+ <p>ozacc-mail libraryは、Spring FrameworkやSeasar2といったDIコンテナに組み込んで使用できるメール送信ライブラリです。DIコンテナ上での使用を想定していますが、DIコンテナ環境外での使用も可能です。</p>\r
+ </section>\r
+\r
+ <section name="システム条件">\r
+ <p>JDK 1.4以上の環境でご使用ください。<br />また、次のJARファイルをクラスパスに含めてください。<br />\r
+ 各ライブラリは、ozacc-mail libraryの配布ファイルには含まれていませんので、<a href="dependencies.html">Dependenciesページ</a>を参考に各自で取得してください。</p>\r
+ \r
+ <ul>\r
+ <li>JavaMail 1.3.1 (mail.jar) [必須]</li>\r
+ <li>JavaBeans Activation Framework(JAF) 1.0.2 (activation.jar) [必須]</li>\r
+ <li>Jakarta Commons Logging 1.0.4 [必須]<p /></li>\r
+ \r
+ <li>JDOM 1.0 [<code>com.ozacc.mail.impl.JDomXMLMailBuilder</code>を使用する場合]</li>\r
+ <li>Jakarta Velocity 1.4 [<code>com.ozacc.mail.impl.XMLVelocityMailBuilder</code>、<code>com.ozacc.mail.impl.JDomXMLMailBuilder</code>を使用する場合]</li>\r
+ <li>Jakarta Commons Collection 1.4 [<code>com.ozacc.mail.impl.XMLVelocityMailBuilder</code>、<code>com.ozacc.mail.impl.JDomXMLMailBuilder</code>を使用する場合]</li>\r
+ </ul>\r
+ </section>\r
+ \r
+ <section name="ダウンロード">\r
+ <p>次のページからozacc-mail libraryの最新リリースをダウンロードできます。<br /><a href="http://sourceforge.jp/projects/spring-ext/">http://sourceforge.jp/projects/spring-ext/</a></p>\r
+ <p>またMaven用のリモートレポジトリも用意しています。URLは「<code>http://spring-ext.sourceforge.jp/maven/</code>」です。このURLを<code>maven.repo.remote</code>プロパティに設定してください。<code>groupId</code>、<code>artifactId</code>は共に「<code>ozacc-mail</code>」です。</p>\r
+ </section>\r
+ \r
+ <section name="ライセンス">\r
+ <p><a href="http://www.opensource.org/licenses/lgpl-license.html">GNU Library or "Lesser" Public License</a> (LGPL).</p>\r
+ </section>\r
+ \r
+ <section name="APIドキュメント">\r
+ <p><a href="apidocs/index.html">JavaDoc API</a></p>\r
+ </section>\r
+ \r
+ <section name="SendMail 使用方法 with Spring">\r
+ <p><code>SendMail</code>は、JavaMail APIをラップし、メール送信のための至極シンプルなインターフェースを提供しています。提供しているメソッド名はたった一つ、<code>send()</code>です。メールデータを表す<code>com.ozacc.mail.Mail</code>インスタンスか、JavaMailの<code>MimeMessage</code>インスタンスを引数に指定すると、それを送信します。(これらの配列も指定可能です。)</p>\r
+ \r
+ <p><code>SendMail(SendMailImpl).send()</code>メソッドは、スレッドセーフな設計になっていますが、呼び出すスレッドの数だけSMTPサーバに接続します。通常の使用では問題にならないはずですが、メールサーバやその設定によっては注意が必要です。</p>\r
+ \r
+ <p><code>SendMail</code>の使用方法を、Springと連携させる場合を例にとって説明します。</p>\r
+ \r
+ <p>▼applicationContext.xmlでのBean定義</p>\r
+ <source><beans>\r
+ <bean id="sendMail" class="com.ozacc.mail.impl.SendMailImpl">\r
+ <!-- SMTPサーバ -->\r
+ <property name="host"><value>smtp.example.com</value></property>\r
+ </bean>\r
+</beans></source>\r
+ \r
+ <p>▼Javaソース</p>\r
+ <source>// Mailインスタンスの生成\r
+Mail mail = new Mail();\r
+mail.setFrom("shop@example.com", "XXXオンラインショップ");\r
+mail.addTo("misaki@foo.com", "伊東美咲さま");\r
+mail.addBcc("order@example.com");\r
+mail.setSubject("ご注文の確認");\r
+mail.setText("お買い上げありがとうございました。\n\nご注文明細・・・");\r
+\r
+// SendMailインスタンスの取得\r
+SendMail sendMail = (SendMail)applicationContext.getBean("sendMail");\r
+\r
+// メールの送信\r
+sendMail.send(mail);</source>\r
+ \r
+ <p><code>sendMail.send(mail)</code>で、何らかの原因で送信に失敗すると、<code>com.ozacc.mail.MailException</code>がスローされます。<code>MailException</code>は非チェック例外なので、プログラムの要求に応じてキャッチしてください。</p>\r
+ \r
+ </section>\r
+\r
+ <section name="MailBuilder 使用方法 with Spring">\r
+ \r
+ <p><code>MailBuilder</code>を使用すると、ファイルに記述されたメールデータから<code>Mail</code>インスタンスを生成できます。<br />さらに、<code>MailBuilder</code>インターフェースを継承した<code>VelocityMailBuilder</code>を使用すると、<code>Velocity</code>と連携して動的にメールデータを生成し、そのデータからMailインスタンスを生成できます。</p>\r
+ \r
+ <p>現バージョンでは、XML形式のメールデータを扱う<code>XMLMailBuilderImpl</code>クラスが提供されています。ここではその使用方法を、Springと連携させる場合を例にとって説明します。</p>\r
+ \r
+ <p>▼applicationContext.xmlでのBean定義</p>\r
+ <source><beans>\r
+ <bean id="sendMail" class="com.ozacc.mail.impl.SendMailImpl">\r
+ <property name="host"><value>smtp.example.com</value></property>\r
+ </bean>\r
+\r
+ <bean id="mailBuilder" class="com.ozacc.mail.impl.XMLMailBuilderImpl" />\r
+</beans></source>\r
+\r
+ <p>▼Javaソース</p>\r
+ <source>// MailBuilderインスタンスの取得\r
+MailBuilder mailBuilder = (MailBuilder)applicationContext.getBean("mailBuilder");\r
+\r
+// メールデータXMLファイルのパス (クラスパス上)\r
+String path = "/com/example/mail/mail-template.xml";\r
+\r
+// Mailインスタンスを生成\r
+Mail mail = mailBuilder.buildMail(path);\r
+\r
+// SendMailインスタンスの取得\r
+SendMail sendMail = (SendMail)applicationContext.getBean("sendMail");\r
+\r
+// メールの送信\r
+sendMail.send(mail);</source>\r
+\r
+ <p>▼mail-template.xml</p>\r
+ <source><?xml version="1.0" encoding="utf-8" ?>\r
+<!DOCTYPE mail PUBLIC "-//OZACC//DTD MAIL//EN" "http://www.ozacc.com/library/dtd/ozacc-mail.dtd">\r
+\r
+<mail>\r
+\r
+ <returnPath email="return@example.com" />\r
+\r
+ <from email="from@example.com" name="差出人" />\r
+\r
+ <recipients>\r
+ <to email="to1@example.com" name="宛先1" />\r
+ <cc email="cc1@example.com" name="CC1" />\r
+ <bcc email="bcc@example.com" />\r
+ <cc email="cc2@example.com" />\r
+ <to email="to2@example.com" />\r
+ </recipients>\r
+\r
+ <replyTo email="reply@example.com" />\r
+\r
+ <!-- subject(件名)の前後のホワイトスペース(半角スペース、タブ、改行)は削除されます。 -->\r
+ <subject>\r
+ 件名\r
+ </subject>\r
+\r
+ <!-- body(本文)の前後のホワイトスペース(半角スペース、タブ、改行)は削除されます。 -->\r
+ <body>\r
+ 本文\r
+ </body>\r
+\r
+</mail></source>\r
+\r
+ <p>DTDで定義されているように、ルート要素<code><mail></code>以外の全要素はオプションです。<br />\r
+ 例えば<code><from></code>要素だけ含んだXMLからは、<code>from</code>プロパティだけセットされた<ocde>Mail</ocde>インスタンスが生成されます。</p>\r
+\r
+ <p>Velocityとの連携については、テストケース(<code>src/test/com/ozacc/mail/impl/XMLVelocityMailBuilderImplTest</code>)を参照してください。</p>\r
+\r
+ </section>\r
+ \r
+ <section name="XMLMailFactoryBean 使用方法">\r
+ \r
+ <p><code>XMLMailFactoryBean</code>はSpringと連携している場合にのみ使用可能です。</p>\r
+\r
+ <p>上述した<code>MailBuilder</code>を使ってXMLメールデータから<code>Mail</code>インスタンスを生成する場合、XMLファイルのロケーションを示すパスがソース内にハードコーディングされています。一般的にはアプリケーション側で、<code>MailBuilder</code>のインスタンスとXMLファイルのロケーションパスをプロパティとして保持するクラスを作り、DIコンテナでプロパティを設定するようにします。<br />\r
+ Springを使用している場合、このようなクラスを作らずともXMLファイルのロケーションパスをソースから追い出し、コンテナ上でMailインスタンスを生成させることができます。</p>\r
+ \r
+ <p>▼applicationContext.xmlでのBean定義</p>\r
+ <source><beans>\r
+ <bean id="sendMail" class="com.ozacc.mail.impl.SendMailImpl">\r
+ <property name="host"><value>smtp.example.com</value></property>\r
+ </bean>\r
+\r
+ <bean id="mail" class="com.ozacc.mail.spring.XMLMailFactoryBean">\r
+ <!-- メールデータXMLファイルのパス (クラスパス上) -->\r
+ <property name="classPath"><value>/com/example/mail/mail-template.xml</value></property>\r
+ </bean>\r
+</beans></source>\r
+\r
+ <p>▼Javaソース</p>\r
+ <source>// Mailインスタンスの生成、取得\r
+Mail mail = (Mail)applicationContext.getBean("mail");\r
+\r
+// SendMailインスタンスの取得\r
+SendMail sendMail = (SendMail)applicationContext.getBean("sendMail");\r
+\r
+// メールの送信\r
+sendMail.send(mail);</source>\r
+\r
+ <p><code>XMLMailFactoryBean</code>から生成される<code>Mail</code>インスタンスは、<code>prototype</code>です。つまりシングルトンではなく、呼び出すたびに新しい<code>Mail</code>インスタンスが生成されます。何かの理由で<code>Mail</code>インスタンスをシングルトンにしたい場合は、<code>XMLMailFactoryBean</code>の<code>singlton</code>プロパティに<code>true</code>をセットするだけです。</p>\r
+ \r
+ <p><code>XMLMailFactoryBean</code>では、Velocityと連携させて<code>Mail</code>インスタンスを生成することはできません。</p>\r
+ </section>\r
+ \r
+ <section name="変更履歴">\r
+ <p>1.1.2\r
+ <ul>\r
+ <li><code>VelocityMailBuilder</code>がテンプレートメールデータのキャッシュをサポートしました。</li>\r
+ </ul>\r
+ </p>\r
+ <p>1.1.1\r
+ <ul>\r
+ <li><code>XMLVelocityMailBuilderImpl</code>がXMLメールデータを読み込む際に、<code><![CDATA[]]></code>タグを削除しないように修正。</li>\r
+ </ul>\r
+ </p>\r
+ <p>1.1 rc2\r
+ <ul>\r
+ <li><code>SendMailImpl</code>と<code>SendMailProImpl</code>クラスに<code>setMessageId(String)</code>メソッドを追加。<code>com.ozacc.mail.impl.OMLMimeMessage</code>クラスを追加。Message-IDヘッダのドメイン部分がカスタマイズ可能に。</li>\r
+ <li><code>com.ozacc.mail.impl.DTDEntityResolver</code>クラスを追加。ネットワークアクセスなしでDTDを参照できるようになった。</li>\r
+ <li>本文がHTMLテキストだけの場合に生成される<code>MimeMessage</code>をマルチパートからシングルパートに修正。</li>\r
+ </ul>\r
+ </p>\r
+ <p>1.1 rc1\r
+ <ul>\r
+ <li><code>MultipartMail</code>クラスを<code>Mail</code>クラスに統合。</li>\r
+ <li>添付ファイルとして<code>InputStream</code>と<code>URL</code>インスタンスを指定できるメソッドを追加。</li>\r
+ <li><code>com.ozacc.mail.mock.EqualityCheck</code>クラスを追加。</li>\r
+ </ul>\r
+ </p>\r
+ <p>1.1 beta1\r
+ <ul>\r
+ <li><code>com.ozacc.mail.MultipartMail</code>クラスを追加。</li>\r
+ <li><code>XMLMailBuilderImpl</code>、<code>XMLVelocityMailBuilderImpl</code>が読み込むXMLで<![CDATA[]]>が利用できるように修正。</li>\r
+ <li><code>MimeMessageBuilder</code>が<code>MultipartMail</code>インスタンスを判別して、マルチパート対応の<code>MimeMessage</code>を生成できるように修正。</li>\r
+ <li><code>ozacc-mail.dtd</code>に<html>要素定義を追加。</li>\r
+ </ul>\r
+ </p>\r
+ <p>1.0.3\r
+ <ul>\r
+ <li><code>com.ozacc.mail.impl.VelocityLogSystem</code>クラスを追加。<code>XMLVelocityMailBuilderImpl</code>と<code>JDomXMLMailBuilder</code>で使用されいてるVelocityのログメッセージをcommons-logging経由で出力します。</li>\r
+ <li><del><code>XMLVelocityMailBuilderImpl</code>クラスを修正。<code>buildMail(File, VelocityContext)</code>、<code>buildMail(String, VelocityContext)</code>メソッドで読み込まれるXMLファイルのコメントに、VTL(Velocity Template Language)を記述できるようになった。</del></li>\r
+ </ul>\r
+ </p>\r
+ <p>1.0.2\r
+ <ul>\r
+ <li><code>Cp932</code>クラスを用いて、文字化け懸念のある記号(全角のハイフンやチルダ等)を予めJISエンコードするように修正。</li>\r
+ <li><code>Mail</code>クラスに<code>clearTo(), clearCc(), clearBcc()</code>メソッドを追加。</li>\r
+ <li><code>Mail</code>クラスと<code>MockMail</code>クラスにコピーコンストラクタを追加。</li>\r
+ </ul>\r
+ </p>\r
+ <p>1.0.1\r
+ <ul>\r
+ <li><code>com.ozacc.mail.impl.XMLMailBuilderImpl</code>クラスを追加。</li>\r
+ <li><code>com.ozacc.mail.impl.XMLVelocityMailBuilderImpl</code>クラスを追加。</li>\r
+ </ul>\r
+ </p>\r
+ </section>\r
+ \r
+ <section name="メールデータXMLの全要素">\r
+ <source><?xml version="1.0" encoding="utf-8" ?>\r
+<!DOCTYPE mail PUBLIC "-//OZACC//DTD MAIL//EN" "http://www.ozacc.com/library/dtd/ozacc-mail.dtd">\r
+\r
+<mail>\r
+ \r
+ <!-- Return-Path (?) -->\r
+ <!-- [attribute] email メールアドレス (必須) -->\r
+ <returnPath email="return@example.com" />\r
+\r
+ <!-- 差出人 (?) -->\r
+ <!-- [attribute] email メールアドレス (必須) -->\r
+ <!-- [attribute] name 差出人名 (オプション) -->\r
+ <from email="from@example.com" name="差出人名" />\r
+\r
+ <!-- 送信先 (?) -->\r
+ <recipients>\r
+ <!-- Toアドレス (*) -->\r
+ <!-- [attribute] email メールアドレス (必須) -->\r
+ <!-- [attribute] name 宛名 (オプション) -->\r
+ <to email="to@example.com" name="宛名" />\r
+ \r
+ <!-- Ccアドレス (*) -->\r
+ <!-- [attribute] email メールアドレス (必須) -->\r
+ <!-- [attribute] name 宛名 (オプション) -->\r
+ <cc email="cc@example.com" name="宛名" />\r
+ \r
+ <!-- Bccアドレス (*) -->\r
+ <!-- [attribute] email メールアドレス (必須) -->\r
+ <bcc email="bcc@example.com" />\r
+ </recipients>\r
+\r
+ <!-- 返信先 (?) -->\r
+ <!-- [attribute] email メールアドレス (必須) -->\r
+ <replyTo email="reply@example.com" />\r
+\r
+ <!-- 件名 (?) -->\r
+ <subject><![CDATA[\r
+ 件名\r
+ ]]></subject>\r
+\r
+ <!-- 本文 (?) -->\r
+ <body><![CDATA[\r
+ 本文\r
+ ]]></body>\r
+ \r
+ <!-- HTMLメールの本文 (?) -->\r
+ <html><![CDATA[\r
+ HTML\r
+ ]]></html>\r
+\r
+</mail></source>\r
+ </section>\r
+ \r
+ </body>\r
+</document>
\ No newline at end of file