OSDN Git Service

CVS最新版の全ファイルを追加
authorIwao AVE! <harawata@gmail.com>
Tue, 15 Nov 2011 04:08:42 +0000 (13:08 +0900)
committerIwao AVE! <harawata@gmail.com>
Tue, 15 Nov 2011 04:08:42 +0000 (13:08 +0900)
118 files changed:
.classpath [new file with mode: 0755]
.cvsignore [new file with mode: 0755]
.project [new file with mode: 0755]
.settings/org.eclipse.jdt.core.prefs [new file with mode: 0755]
pom.xml [new file with mode: 0755]
project.properties [new file with mode: 0755]
project.xml [new file with mode: 0755]
sandbox/src/com/ozacc/mail/util/MXCheck.java [new file with mode: 0755]
src/java/com/ozacc/mail/Mail.java [new file with mode: 0755]
src/java/com/ozacc/mail/MailAuthenticationException.java [new file with mode: 0755]
src/java/com/ozacc/mail/MailBuildException.java [new file with mode: 0755]
src/java/com/ozacc/mail/MailBuilder.java [new file with mode: 0755]
src/java/com/ozacc/mail/MailException.java [new file with mode: 0755]
src/java/com/ozacc/mail/MailSendException.java [new file with mode: 0755]
src/java/com/ozacc/mail/MultipleMailBuilder.java [new file with mode: 0755]
src/java/com/ozacc/mail/NotConnectedException.java [new file with mode: 0755]
src/java/com/ozacc/mail/SendMail.java [new file with mode: 0755]
src/java/com/ozacc/mail/SendMailPro.java [new file with mode: 0755]
src/java/com/ozacc/mail/VelocityMailBuilder.java [new file with mode: 0755]
src/java/com/ozacc/mail/VelocityMultipleMailBuilder.java [new file with mode: 0755]
src/java/com/ozacc/mail/fetch/FetchMail.java [new file with mode: 0755]
src/java/com/ozacc/mail/fetch/FetchMailPro.java [new file with mode: 0755]
src/java/com/ozacc/mail/fetch/MailConverter.java [new file with mode: 0755]
src/java/com/ozacc/mail/fetch/MailFetchException.java [new file with mode: 0755]
src/java/com/ozacc/mail/fetch/ReceivedMail.java [new file with mode: 0755]
src/java/com/ozacc/mail/fetch/impl/FetchMailImpl.java [new file with mode: 0755]
src/java/com/ozacc/mail/fetch/impl/FetchMailProImpl.java [new file with mode: 0755]
src/java/com/ozacc/mail/fetch/impl/MailConverterImpl.java [new file with mode: 0755]
src/java/com/ozacc/mail/fetch/impl/package.html [new file with mode: 0755]
src/java/com/ozacc/mail/fetch/impl/sk_jp/AttachmentsExtractor.java [new file with mode: 0755]
src/java/com/ozacc/mail/fetch/impl/sk_jp/CorrectedContentTypeDataSource.java [new file with mode: 0755]
src/java/com/ozacc/mail/fetch/impl/sk_jp/CorrectedContentTypeDataSourceUTF7Support.java [new file with mode: 0755]
src/java/com/ozacc/mail/fetch/impl/sk_jp/FirstPlainPartExtractor.java [new file with mode: 0755]
src/java/com/ozacc/mail/fetch/impl/sk_jp/HtmlPartExtractor.java [new file with mode: 0755]
src/java/com/ozacc/mail/fetch/impl/sk_jp/JISDataSource.java [new file with mode: 0755]
src/java/com/ozacc/mail/fetch/impl/sk_jp/MailUtility.java [new file with mode: 0755]
src/java/com/ozacc/mail/fetch/impl/sk_jp/MultipartUtility.java [new file with mode: 0755]
src/java/com/ozacc/mail/fetch/impl/sk_jp/PartHandler.java [new file with mode: 0755]
src/java/com/ozacc/mail/fetch/impl/sk_jp/PlainPartExtractor.java [new file with mode: 0755]
src/java/com/ozacc/mail/fetch/impl/sk_jp/io/ByteToCharUTF7.java [new file with mode: 0755]
src/java/com/ozacc/mail/fetch/impl/sk_jp/io/CharCodeConverter.java [new file with mode: 0755]
src/java/com/ozacc/mail/fetch/impl/sk_jp/io/FromCP932Corrector.java [new file with mode: 0755]
src/java/com/ozacc/mail/fetch/impl/sk_jp/io/ToCP932Corrector.java [new file with mode: 0755]
src/java/com/ozacc/mail/fetch/impl/sk_jp/io/UnicodeCorrector.java [new file with mode: 0755]
src/java/com/ozacc/mail/fetch/impl/sk_jp/package.html [new file with mode: 0755]
src/java/com/ozacc/mail/fetch/impl/sk_jp/text/EntityRefEncoder.java [new file with mode: 0755]
src/java/com/ozacc/mail/fetch/impl/sk_jp/text/Translator.java [new file with mode: 0755]
src/java/com/ozacc/mail/fetch/impl/sk_jp/util/StringValues.java [new file with mode: 0755]
src/java/com/ozacc/mail/fetch/package.html [new file with mode: 0755]
src/java/com/ozacc/mail/impl/AbstractXMLMailBuilder.java [new file with mode: 0755]
src/java/com/ozacc/mail/impl/ByteArrayDataSource.java [new file with mode: 0755]
src/java/com/ozacc/mail/impl/Cp932.java [new file with mode: 0755]
src/java/com/ozacc/mail/impl/DTDEntityResolver.java [new file with mode: 0755]
src/java/com/ozacc/mail/impl/JDomXMLMailBuilder.java [new file with mode: 0755]
src/java/com/ozacc/mail/impl/MimeMessageBuilder.java [new file with mode: 0755]
src/java/com/ozacc/mail/impl/OMLMimeMessage.java [new file with mode: 0755]
src/java/com/ozacc/mail/impl/SendMailImpl.java [new file with mode: 0755]
src/java/com/ozacc/mail/impl/SendMailProImpl.java [new file with mode: 0755]
src/java/com/ozacc/mail/impl/VelocityLogSystem.java [new file with mode: 0755]
src/java/com/ozacc/mail/impl/XMLMailBuilderImpl.java [new file with mode: 0755]
src/java/com/ozacc/mail/impl/XMLVelocityMailBuilderImpl.java [new file with mode: 0755]
src/java/com/ozacc/mail/impl/package.html [new file with mode: 0755]
src/java/com/ozacc/mail/mailet/Mailet.java [new file with mode: 0755]
src/java/com/ozacc/mail/mailet/MailetRunner.java [new file with mode: 0755]
src/java/com/ozacc/mail/mailet/MailetWrapper.java [new file with mode: 0755]
src/java/com/ozacc/mail/mailet/Matcher.java [new file with mode: 0755]
src/java/com/ozacc/mail/mailet/package.html [new file with mode: 0755]
src/java/com/ozacc/mail/mock/AssertionFailedException.java [new file with mode: 0755]
src/java/com/ozacc/mail/mock/EqualityCheck.java [new file with mode: 0755]
src/java/com/ozacc/mail/mock/MockFetchMail.java [new file with mode: 0755]
src/java/com/ozacc/mail/mock/MockFetchMailPro.java [new file with mode: 0755]
src/java/com/ozacc/mail/mock/MockMail.java [new file with mode: 0755]
src/java/com/ozacc/mail/mock/MockSendMail.java [new file with mode: 0755]
src/java/com/ozacc/mail/mock/package.html [new file with mode: 0755]
src/java/com/ozacc/mail/ozacc-mail.dtd [new file with mode: 0755]
src/java/com/ozacc/mail/ozacc-multiple-mails.dtd [new file with mode: 0755]
src/java/com/ozacc/mail/package.html [new file with mode: 0755]
src/java/com/ozacc/mail/spring/MailetRunnerJob.java [new file with mode: 0755]
src/java/com/ozacc/mail/spring/XMLMailFactoryBean.java [new file with mode: 0755]
src/java/com/ozacc/mail/spring/package.html [new file with mode: 0755]
src/java/com/ozacc/mail/xml/XMLBuildException.java [new file with mode: 0755]
src/java/com/ozacc/mail/xml/XMLBuilder.java [new file with mode: 0755]
src/java/com/ozacc/mail/xml/impl/JDomXMLBuilder.java [new file with mode: 0755]
src/java/com/ozacc/mail/xml/impl/XMLBuilderImpl.java [new file with mode: 0755]
src/java/com/ozacc/mail/xml/impl/package.html [new file with mode: 0755]
src/java/com/ozacc/mail/xml/package.html [new file with mode: 0755]
src/test/com/dumbster/smtp/SimpleSmtpServer.java [new file with mode: 0755]
src/test/com/dumbster/smtp/SmtpActionType.java [new file with mode: 0755]
src/test/com/dumbster/smtp/SmtpMessage.java [new file with mode: 0755]
src/test/com/dumbster/smtp/SmtpRequest.java [new file with mode: 0755]
src/test/com/dumbster/smtp/SmtpResponse.java [new file with mode: 0755]
src/test/com/dumbster/smtp/SmtpState.java [new file with mode: 0755]
src/test/com/ozacc/mail/fetch/impl/FetchMailImplRealTest.java [new file with mode: 0755]
src/test/com/ozacc/mail/fetch/impl/FetchMailProImplRealTest.java [new file with mode: 0755]
src/test/com/ozacc/mail/image1.jpg [new file with mode: 0755]
src/test/com/ozacc/mail/image2.png [new file with mode: 0755]
src/test/com/ozacc/mail/impl/JDomXMLMailBuilderTest.java [new file with mode: 0755]
src/test/com/ozacc/mail/impl/OMLMimeMessageTest.java [new file with mode: 0755]
src/test/com/ozacc/mail/impl/SendMailImplRealTest.java [new file with mode: 0755]
src/test/com/ozacc/mail/impl/SendMailImplTest.java [new file with mode: 0755]
src/test/com/ozacc/mail/impl/SendMailProImplRealTest.java [new file with mode: 0755]
src/test/com/ozacc/mail/impl/XMLMailBuilderImplTest.java [new file with mode: 0755]
src/test/com/ozacc/mail/impl/XMLVelocityMailBuilderImplTest.java [new file with mode: 0755]
src/test/com/ozacc/mail/mailet/MailetRunnerTest.java [new file with mode: 0755]
src/test/com/ozacc/mail/mock/MockFetchMailTest.java [new file with mode: 0755]
src/test/com/ozacc/mail/mock/MockSendMailTest.java [new file with mode: 0755]
src/test/com/ozacc/mail/spring/XMLMailFactoryBeanTest.java [new file with mode: 0755]
src/test/com/ozacc/mail/spring/testContext.xml [new file with mode: 0755]
src/test/com/ozacc/mail/test-mail1.xml [new file with mode: 0755]
src/test/com/ozacc/mail/test-mail2-invalid.xml [new file with mode: 0755]
src/test/com/ozacc/mail/test-mail3-velocity.xml [new file with mode: 0755]
src/test/com/ozacc/mail/test-mail4.xml [new file with mode: 0755]
src/test/com/ozacc/mail/test-mail5-html.xml [new file with mode: 0755]
src/test/com/ozacc/mail/test-mail6-cdata.xml [new file with mode: 0755]
src/test/com/ozacc/mail/test-mail7-multiple.xml [new file with mode: 0755]
src/test/com/ozacc/mail/xml/impl/JDomXMLBuilderTest.java [new file with mode: 0755]
src/test/com/ozacc/mail/xml/impl/XMLBuilderImplTest.java [new file with mode: 0755]
xdocs/index.xml [new file with mode: 0755]

diff --git a/.classpath b/.classpath
new file mode 100755 (executable)
index 0000000..ad31c20
--- /dev/null
@@ -0,0 +1,17 @@
+<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
diff --git a/.cvsignore b/.cvsignore
new file mode 100755 (executable)
index 0000000..8596007
--- /dev/null
@@ -0,0 +1,7 @@
+target\r
+.classpath\r
+.project\r
+velocity.log\r
+build.properties\r
+build.xml\r
+project.xml.md5\r
diff --git a/.project b/.project
new file mode 100755 (executable)
index 0000000..fab11bf
--- /dev/null
+++ b/.project
@@ -0,0 +1,19 @@
+<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
diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs
new file mode 100755 (executable)
index 0000000..d306986
--- /dev/null
@@ -0,0 +1,5 @@
+#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
diff --git a/pom.xml b/pom.xml
new file mode 100755 (executable)
index 0000000..753f983
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,103 @@
+<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>
diff --git a/project.properties b/project.properties
new file mode 100755 (executable)
index 0000000..777f3a5
--- /dev/null
@@ -0,0 +1,17 @@
+# 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
diff --git a/project.xml b/project.xml
new file mode 100755 (executable)
index 0000000..9bd7ef8
--- /dev/null
@@ -0,0 +1,130 @@
+<?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
diff --git a/sandbox/src/com/ozacc/mail/util/MXCheck.java b/sandbox/src/com/ozacc/mail/util/MXCheck.java
new file mode 100755 (executable)
index 0000000..b0b74bc
--- /dev/null
@@ -0,0 +1,89 @@
+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
diff --git a/src/java/com/ozacc/mail/Mail.java b/src/java/com/ozacc/mail/Mail.java
new file mode 100755 (executable)
index 0000000..f86d3be
--- /dev/null
@@ -0,0 +1,998 @@
+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
diff --git a/src/java/com/ozacc/mail/MailAuthenticationException.java b/src/java/com/ozacc/mail/MailAuthenticationException.java
new file mode 100755 (executable)
index 0000000..27be480
--- /dev/null
@@ -0,0 +1,32 @@
+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
diff --git a/src/java/com/ozacc/mail/MailBuildException.java b/src/java/com/ozacc/mail/MailBuildException.java
new file mode 100755 (executable)
index 0000000..f82e001
--- /dev/null
@@ -0,0 +1,27 @@
+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
diff --git a/src/java/com/ozacc/mail/MailBuilder.java b/src/java/com/ozacc/mail/MailBuilder.java
new file mode 100755 (executable)
index 0000000..b1d27ae
--- /dev/null
@@ -0,0 +1,36 @@
+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
diff --git a/src/java/com/ozacc/mail/MailException.java b/src/java/com/ozacc/mail/MailException.java
new file mode 100755 (executable)
index 0000000..e7e5938
--- /dev/null
@@ -0,0 +1,27 @@
+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
diff --git a/src/java/com/ozacc/mail/MailSendException.java b/src/java/com/ozacc/mail/MailSendException.java
new file mode 100755 (executable)
index 0000000..cec0528
--- /dev/null
@@ -0,0 +1,29 @@
+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
diff --git a/src/java/com/ozacc/mail/MultipleMailBuilder.java b/src/java/com/ozacc/mail/MultipleMailBuilder.java
new file mode 100755 (executable)
index 0000000..c220783
--- /dev/null
@@ -0,0 +1,34 @@
+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
diff --git a/src/java/com/ozacc/mail/NotConnectedException.java b/src/java/com/ozacc/mail/NotConnectedException.java
new file mode 100755 (executable)
index 0000000..63e1af2
--- /dev/null
@@ -0,0 +1,27 @@
+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
diff --git a/src/java/com/ozacc/mail/SendMail.java b/src/java/com/ozacc/mail/SendMail.java
new file mode 100755 (executable)
index 0000000..1db8c45
--- /dev/null
@@ -0,0 +1,46 @@
+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
diff --git a/src/java/com/ozacc/mail/SendMailPro.java b/src/java/com/ozacc/mail/SendMailPro.java
new file mode 100755 (executable)
index 0000000..324a119
--- /dev/null
@@ -0,0 +1,52 @@
+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
diff --git a/src/java/com/ozacc/mail/VelocityMailBuilder.java b/src/java/com/ozacc/mail/VelocityMailBuilder.java
new file mode 100755 (executable)
index 0000000..22de620
--- /dev/null
@@ -0,0 +1,71 @@
+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
diff --git a/src/java/com/ozacc/mail/VelocityMultipleMailBuilder.java b/src/java/com/ozacc/mail/VelocityMultipleMailBuilder.java
new file mode 100755 (executable)
index 0000000..0325bcb
--- /dev/null
@@ -0,0 +1,41 @@
+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
diff --git a/src/java/com/ozacc/mail/fetch/FetchMail.java b/src/java/com/ozacc/mail/fetch/FetchMail.java
new file mode 100755 (executable)
index 0000000..3b955c7
--- /dev/null
@@ -0,0 +1,45 @@
+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
diff --git a/src/java/com/ozacc/mail/fetch/FetchMailPro.java b/src/java/com/ozacc/mail/fetch/FetchMailPro.java
new file mode 100755 (executable)
index 0000000..54b91d0
--- /dev/null
@@ -0,0 +1,118 @@
+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
diff --git a/src/java/com/ozacc/mail/fetch/MailConverter.java b/src/java/com/ozacc/mail/fetch/MailConverter.java
new file mode 100755 (executable)
index 0000000..d43cb22
--- /dev/null
@@ -0,0 +1,30 @@
+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
diff --git a/src/java/com/ozacc/mail/fetch/MailFetchException.java b/src/java/com/ozacc/mail/fetch/MailFetchException.java
new file mode 100755 (executable)
index 0000000..737e3c8
--- /dev/null
@@ -0,0 +1,28 @@
+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
diff --git a/src/java/com/ozacc/mail/fetch/ReceivedMail.java b/src/java/com/ozacc/mail/fetch/ReceivedMail.java
new file mode 100755 (executable)
index 0000000..055744a
--- /dev/null
@@ -0,0 +1,407 @@
+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
+        * 前後に&lt;&gt;が付いたメッセージIDを返します。\r
+        * 受信メールにメッセージIDが存在しない場合はnullを返します。\r
+        * \r
+        * @return 前後に&lt;&gt;が付いたメッセージ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を返します。前後に&lt;&gt;は付きません。\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
diff --git a/src/java/com/ozacc/mail/fetch/impl/FetchMailImpl.java b/src/java/com/ozacc/mail/fetch/impl/FetchMailImpl.java
new file mode 100755 (executable)
index 0000000..a4efe50
--- /dev/null
@@ -0,0 +1,178 @@
+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
diff --git a/src/java/com/ozacc/mail/fetch/impl/FetchMailProImpl.java b/src/java/com/ozacc/mail/fetch/impl/FetchMailProImpl.java
new file mode 100755 (executable)
index 0000000..bb862ec
--- /dev/null
@@ -0,0 +1,415 @@
+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
diff --git a/src/java/com/ozacc/mail/fetch/impl/MailConverterImpl.java b/src/java/com/ozacc/mail/fetch/impl/MailConverterImpl.java
new file mode 100755 (executable)
index 0000000..cb7e571
--- /dev/null
@@ -0,0 +1,500 @@
+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
diff --git a/src/java/com/ozacc/mail/fetch/impl/package.html b/src/java/com/ozacc/mail/fetch/impl/package.html
new file mode 100755 (executable)
index 0000000..bfb996d
--- /dev/null
@@ -0,0 +1,5 @@
+<HTML>\r
+<BODY>\r
+com.ozacc.mail.fetch¥Ñ¥Ã¥±¡¼¥¸¤Î¥¤¥ó¥¿¡¼¥Õ¥§¡¼¥¹¤ò¼ÂÁõ¤·¤¿¥¯¥é¥¹¤òÄ󶡤·¤Þ¤¹¡£\r
+</BODY>\r
+</HTML>
\ No newline at end of file
diff --git a/src/java/com/ozacc/mail/fetch/impl/sk_jp/AttachmentsExtractor.java b/src/java/com/ozacc/mail/fetch/impl/sk_jp/AttachmentsExtractor.java
new file mode 100755 (executable)
index 0000000..dd86ac5
--- /dev/null
@@ -0,0 +1,169 @@
+/*\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
diff --git a/src/java/com/ozacc/mail/fetch/impl/sk_jp/CorrectedContentTypeDataSource.java b/src/java/com/ozacc/mail/fetch/impl/sk_jp/CorrectedContentTypeDataSource.java
new file mode 100755 (executable)
index 0000000..658ba79
--- /dev/null
@@ -0,0 +1,112 @@
+/*\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
diff --git a/src/java/com/ozacc/mail/fetch/impl/sk_jp/CorrectedContentTypeDataSourceUTF7Support.java b/src/java/com/ozacc/mail/fetch/impl/sk_jp/CorrectedContentTypeDataSourceUTF7Support.java
new file mode 100755 (executable)
index 0000000..3e73518
--- /dev/null
@@ -0,0 +1,170 @@
+/*\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
diff --git a/src/java/com/ozacc/mail/fetch/impl/sk_jp/FirstPlainPartExtractor.java b/src/java/com/ozacc/mail/fetch/impl/sk_jp/FirstPlainPartExtractor.java
new file mode 100755 (executable)
index 0000000..82e3285
--- /dev/null
@@ -0,0 +1,49 @@
+/*\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
diff --git a/src/java/com/ozacc/mail/fetch/impl/sk_jp/HtmlPartExtractor.java b/src/java/com/ozacc/mail/fetch/impl/sk_jp/HtmlPartExtractor.java
new file mode 100755 (executable)
index 0000000..5f37793
--- /dev/null
@@ -0,0 +1,45 @@
+/*\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
diff --git a/src/java/com/ozacc/mail/fetch/impl/sk_jp/JISDataSource.java b/src/java/com/ozacc/mail/fetch/impl/sk_jp/JISDataSource.java
new file mode 100755 (executable)
index 0000000..9675cf6
--- /dev/null
@@ -0,0 +1,52 @@
+/*\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
diff --git a/src/java/com/ozacc/mail/fetch/impl/sk_jp/MailUtility.java b/src/java/com/ozacc/mail/fetch/impl/sk_jp/MailUtility.java
new file mode 100755 (executable)
index 0000000..526ad10
--- /dev/null
@@ -0,0 +1,1060 @@
+/*\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
diff --git a/src/java/com/ozacc/mail/fetch/impl/sk_jp/MultipartUtility.java b/src/java/com/ozacc/mail/fetch/impl/sk_jp/MultipartUtility.java
new file mode 100755 (executable)
index 0000000..95ab8cb
--- /dev/null
@@ -0,0 +1,173 @@
+/*\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
diff --git a/src/java/com/ozacc/mail/fetch/impl/sk_jp/PartHandler.java b/src/java/com/ozacc/mail/fetch/impl/sk_jp/PartHandler.java
new file mode 100755 (executable)
index 0000000..410b277
--- /dev/null
@@ -0,0 +1,48 @@
+/*\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
diff --git a/src/java/com/ozacc/mail/fetch/impl/sk_jp/PlainPartExtractor.java b/src/java/com/ozacc/mail/fetch/impl/sk_jp/PlainPartExtractor.java
new file mode 100755 (executable)
index 0000000..ed6a565
--- /dev/null
@@ -0,0 +1,55 @@
+/*\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