Create new trunk for updates. 



git-svn-id: https://svn.apache.org/repos/asf/geronimo/javamail/trunk@568081 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/geronimo-javamail_1.4/LICENSE.txt b/geronimo-javamail_1.4/LICENSE.txt
new file mode 100644
index 0000000..6b0b127
--- /dev/null
+++ b/geronimo-javamail_1.4/LICENSE.txt
@@ -0,0 +1,203 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
diff --git a/geronimo-javamail_1.4/NOTICE.txt b/geronimo-javamail_1.4/NOTICE.txt
new file mode 100644
index 0000000..9d2d74b
--- /dev/null
+++ b/geronimo-javamail_1.4/NOTICE.txt
@@ -0,0 +1,5 @@
+Apache Geronimo
+Copyright 2006 The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
diff --git a/geronimo-javamail_1.4/README.txt b/geronimo-javamail_1.4/README.txt
new file mode 100644
index 0000000..c39bd24
--- /dev/null
+++ b/geronimo-javamail_1.4/README.txt
@@ -0,0 +1,20 @@
+
+Building
+========
+
+To build you will need:
+
+ * J2SE SDK 1.4.2+ (http://java.sun.com/j2se/1.4.2)
+ * Maven 2.0.4+ (http://maven.apache.org)
+
+NOTE: If you use JDK 1.5 you may run into unexpected errors, so stick to 1.4.
+
+To build all changes incrementally:
+
+    mvn install
+
+To perform clean builds, which are sometimes needed after some changes to the 
+source tree:
+
+    mvn clean install
+
diff --git a/geronimo-javamail_1.4/geronimo-javamail_1.4_mail/LICENSE.txt b/geronimo-javamail_1.4/geronimo-javamail_1.4_mail/LICENSE.txt
new file mode 100644
index 0000000..6b0b127
--- /dev/null
+++ b/geronimo-javamail_1.4/geronimo-javamail_1.4_mail/LICENSE.txt
@@ -0,0 +1,203 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
diff --git a/geronimo-javamail_1.4/geronimo-javamail_1.4_mail/NOTICE.txt b/geronimo-javamail_1.4/geronimo-javamail_1.4_mail/NOTICE.txt
new file mode 100644
index 0000000..439eb83
--- /dev/null
+++ b/geronimo-javamail_1.4/geronimo-javamail_1.4_mail/NOTICE.txt
@@ -0,0 +1,3 @@
+This product includes software developed by
+The Apache Software Foundation (http://www.apache.org/).
+
diff --git a/geronimo-javamail_1.4/geronimo-javamail_1.4_mail/pom.xml b/geronimo-javamail_1.4/geronimo-javamail_1.4_mail/pom.xml
new file mode 100644
index 0000000..e6fd2b3
--- /dev/null
+++ b/geronimo-javamail_1.4/geronimo-javamail_1.4_mail/pom.xml
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+-->
+
+<!-- $Rev$ $Date$ -->
+
+<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>
+
+    <parent>
+        <groupId>org.apache.geronimo.javamail</groupId>
+        <artifactId>geronimo-javamail-1.4</artifactId>
+        <version>1.2</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>geronimo-javamail_1.4_mail</artifactId>
+    <name>Geronimo JavaMail :: 1.4 Mail</name>
+    <packaging>pom</packaging>
+
+    <dependencies>
+
+        <dependency>
+            <groupId>org.apache.geronimo.javamail</groupId>
+            <artifactId>geronimo-javamail_1.4_provider</artifactId>
+            <version>${pom.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.geronimo.specs</groupId>
+            <artifactId>geronimo-javamail_1.4_spec</artifactId>
+        </dependency>
+
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-assembly-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>attached</goal>
+                        </goals>
+                        <configuration>
+                            <descriptors>
+                                <descriptor>${pom.basedir}/src/main/assembly/javamail.xml</descriptor>
+                            </descriptors>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/geronimo-javamail_1.4/geronimo-javamail_1.4_mail/src/main/assembly/javamail.xml b/geronimo-javamail_1.4/geronimo-javamail_1.4_mail/src/main/assembly/javamail.xml
new file mode 100644
index 0000000..023e8ca
--- /dev/null
+++ b/geronimo-javamail_1.4/geronimo-javamail_1.4_mail/src/main/assembly/javamail.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+    
+     http://www.apache.org/licenses/LICENSE-2.0
+    
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+-->
+
+<!-- $Rev$ $Date$ -->
+
+<assembly>
+    
+    <formats>
+        <format>jar</format>
+    </formats>
+    
+    <includeBaseDirectory>false</includeBaseDirectory>
+    
+    <dependencySets>
+        <dependencySet>
+            <outputDirectory>/</outputDirectory>
+            <unpack>true</unpack>
+            <scope>runtime</scope>
+            <excludes>
+                <exclude>org.apache.geronimo.specs:geronimo-activation_1.1_spec</exclude>
+            </excludes>
+        </dependencySet>
+    </dependencySets>
+    
+</assembly>
diff --git a/geronimo-javamail_1.4/geronimo-javamail_1.4_mail/src/site/site.xml b/geronimo-javamail_1.4/geronimo-javamail_1.4_mail/src/site/site.xml
new file mode 100644
index 0000000..80f99dd
--- /dev/null
+++ b/geronimo-javamail_1.4/geronimo-javamail_1.4_mail/src/site/site.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+    
+     http://www.apache.org/licenses/LICENSE-2.0
+    
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+-->
+
+<!-- $Rev$ $Date$ -->
+
+<project name="${project.name}">
+    
+    <body>
+        
+        ${parentProject}
+        
+        ${modules}
+        
+        ${reports}
+        
+    </body>
+
+</project>
+
+
diff --git a/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/LICENSE.txt b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/LICENSE.txt
new file mode 100644
index 0000000..6b0b127
--- /dev/null
+++ b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/LICENSE.txt
@@ -0,0 +1,203 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
diff --git a/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/NOTICE.txt b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/NOTICE.txt
new file mode 100644
index 0000000..439eb83
--- /dev/null
+++ b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/NOTICE.txt
@@ -0,0 +1,3 @@
+This product includes software developed by
+The Apache Software Foundation (http://www.apache.org/).
+
diff --git a/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/pom.xml b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/pom.xml
new file mode 100644
index 0000000..7dcef0e
--- /dev/null
+++ b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/pom.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+-->
+
+<!-- $Rev$ $Date$ -->
+
+<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>
+
+    <parent>
+        <groupId>org.apache.geronimo.javamail</groupId>
+        <artifactId>geronimo-javamail-1.4</artifactId>
+        <version>1.2</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>geronimo-javamail_1.4_provider</artifactId>
+    <name>Geronimo JavaMail :: 1.4 Provider</name>
+
+    <dependencies>
+
+        <dependency>
+            <groupId>org.apache.geronimo.specs</groupId>
+            <artifactId>geronimo-activation_1.1_spec</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.geronimo.specs</groupId>
+            <artifactId>geronimo-javamail_1.4_spec</artifactId>
+        </dependency>
+
+    </dependencies>
+
+</project>
diff --git a/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/authentication/ClientAuthenticator.java b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/authentication/ClientAuthenticator.java
new file mode 100644
index 0000000..0404d36
--- /dev/null
+++ b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/authentication/ClientAuthenticator.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.geronimo.javamail.authentication;
+
+import javax.mail.MessagingException;
+
+/**
+ * Simplified version of the Java 5 SaslClient interface. This is used to
+ * implement a javamail authentication framework that mimics the Sasl framework
+ * on a 1.4.2 JVM. Only the methods required by the Javamail code are
+ * implemented here, but it should be a simple migration to the fuller SASL
+ * interface.
+ */
+public interface ClientAuthenticator {
+    /**
+     * Evaluate a challenge and return a response that can be sent back to the
+     * server. Bot the challenge information and the response information are
+     * "raw data", minus any special encodings used by the transport. For
+     * example, SMTP DIGEST-MD5 authentication protocol passes information as
+     * Base64 encoded strings. That encoding must be removed before calling
+     * evaluateChallenge() and the resulting respose must be Base64 encoced
+     * before transmission to the server.
+     * 
+     * It is the authenticator's responsibility to keep track of the state of
+     * the evaluations. That is, if the authentication process requires multiple
+     * challenge/response cycles, then the authenticator needs to keep track of
+     * context of the challenges.
+     * 
+     * @param challenge
+     *            The challenge data.
+     * 
+     * @return An appropriate response for the challenge data.
+     */
+
+    public byte[] evaluateChallenge(byte[] challenge) throws MessagingException;
+
+    /**
+     * Indicates that the authenticator has data that should be sent when the
+     * authentication process is initiated. For example, the SMTP PLAIN
+     * authentication sends userid/password without waiting for a challenge
+     * response.
+     * 
+     * If this method returns true, then the initial response is retrieved using
+     * evaluateChallenge() passing null for the challenge information.
+     * 
+     * @return True if the challenge/response process starts with an initial
+     *         response on the client side.
+     */
+    public boolean hasInitialResponse();
+
+    /**
+     * Indicates whether the client believes the challenge/response sequence is
+     * now complete.
+     * 
+     * @return true if the client has evaluated what it believes to be the last
+     *         challenge, false if there are additional stages to evaluate.
+     */
+
+    public boolean isComplete();
+
+    /**
+     * Return the mechanism name implemented by this authenticator.
+     * 
+     * @return The string name of the authentication mechanism. This name should
+     *         match the names commonly used by the mail servers (e.g., "PLAIN",
+     *         "LOGIN", "DIGEST-MD5", etc.).
+     */
+    public String getMechanismName();
+}
diff --git a/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/authentication/CramMD5Authenticator.java b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/authentication/CramMD5Authenticator.java
new file mode 100644
index 0000000..17da4db
--- /dev/null
+++ b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/authentication/CramMD5Authenticator.java
@@ -0,0 +1,175 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.geronimo.javamail.authentication;
+
+import java.io.UnsupportedEncodingException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+import javax.mail.MessagingException;
+
+import org.apache.geronimo.mail.util.Hex;
+
+public class CramMD5Authenticator implements ClientAuthenticator {
+
+    // the user we're authenticating
+    protected String username;
+
+    // the user's password (the "shared secret")
+    protected String password;
+
+    // indicates whether we've gone through the entire challenge process.
+    protected boolean complete = false;
+
+    /**
+     * Main constructor.
+     * 
+     * @param username
+     *            The login user name.
+     * @param password
+     *            The login password.
+     */
+    public CramMD5Authenticator(String username, String password) {
+        this.username = username;
+        this.password = password;
+    }
+
+    /**
+     * Respond to the hasInitialResponse query. This mechanism does not have an
+     * initial response.
+     * 
+     * @return Always returns false.
+     */
+    public boolean hasInitialResponse() {
+        return false;
+    }
+
+    /**
+     * Indicate whether the challenge/response process is complete.
+     * 
+     * @return True if the last challenge has been processed, false otherwise.
+     */
+    public boolean isComplete() {
+        return complete;
+    }
+
+    /**
+     * Retrieve the authenticator mechanism name.
+     * 
+     * @return Always returns the string "CRAM-MD5"
+     */
+    public String getMechanismName() {
+        return "CRAM-MD5";
+    }
+
+    /**
+     * Evaluate a CRAM-MD5 login challenge, returning the a result string that
+     * should satisfy the clallenge.
+     * 
+     * @param challenge
+     *            The decoded challenge data, as a byte array.
+     * 
+     * @return A formatted challege response, as an array of bytes.
+     * @exception MessagingException
+     */
+    public byte[] evaluateChallenge(byte[] challenge) throws MessagingException {
+        // we create the challenge from the userid and password information (the
+        // "shared secret").
+        byte[] passBytes;
+
+        try {
+            // get the password in an UTF-8 encoding to create the token
+            passBytes = password.getBytes("UTF-8");
+            // compute the password digest using the key
+            byte[] digest = computeCramDigest(passBytes, challenge);
+
+            // create a unified string using the user name and the hex encoded
+            // digest
+            String responseString = username + " " + new String(Hex.encode(digest));
+            complete = true;
+            return responseString.getBytes();
+        } catch (UnsupportedEncodingException e) {
+            // got an error, fail this
+            throw new MessagingException("Invalid character encodings");
+        }
+
+    }
+
+    /**
+     * Compute a CRAM digest using the hmac_md5 algorithm. See the description
+     * of RFC 2104 for algorithm details.
+     * 
+     * @param key
+     *            The key (K) for the calculation.
+     * @param input
+     *            The encrypted text value.
+     * 
+     * @return The computed digest, as a byte array value.
+     * @exception NoSuchAlgorithmException
+     */
+    protected byte[] computeCramDigest(byte[] key, byte[] input) throws MessagingException {
+        // CRAM digests are computed using the MD5 algorithm.
+        MessageDigest digest;
+        try {
+            digest = MessageDigest.getInstance("MD5");
+        } catch (NoSuchAlgorithmException e) {
+            throw new MessagingException("Unable to access MD5 message digest", e);
+        }
+
+        // if the key is longer than 64 bytes, then we get a digest of the key
+        // and use that instead.
+        // this is required by RFC 2104.
+        if (key.length > 64) {
+            digest.update(key);
+            key = digest.digest();
+        }
+
+        // now we create two 64 bit padding keys, initialized with the key
+        // information.
+        byte[] ipad = new byte[64];
+        byte[] opad = new byte[64];
+
+        System.arraycopy(key, 0, ipad, 0, key.length);
+        System.arraycopy(key, 0, opad, 0, key.length);
+
+        // and these versions are munged by XORing with "magic" values.
+
+        for (int i = 0; i < 64; i++) {
+            ipad[i] ^= 0x36;
+            opad[i] ^= 0x5c;
+        }
+
+        // now there are a pair of MD5 operations performed, and inner and an
+        // outer. The spec defines this as
+        // H(K XOR opad, H(K XOR ipad, text)), where H is the MD5 operation.
+
+        // inner operation
+        digest.reset();
+        digest.update(ipad);
+        digest.update(input); // this appends the text to the pad
+        byte[] md5digest = digest.digest();
+
+        // outer operation
+        digest.reset();
+        digest.update(opad);
+        digest.update(md5digest);
+        return digest.digest(); // final result
+    }
+}
diff --git a/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/authentication/DigestMD5Authenticator.java b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/authentication/DigestMD5Authenticator.java
new file mode 100644
index 0000000..c762f70
--- /dev/null
+++ b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/authentication/DigestMD5Authenticator.java
@@ -0,0 +1,624 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.geronimo.javamail.authentication;
+
+import java.io.UnsupportedEncodingException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.util.ArrayList;
+
+import javax.mail.AuthenticationFailedException;
+import javax.mail.MessagingException;
+
+import org.apache.geronimo.mail.util.Base64;
+import org.apache.geronimo.mail.util.Hex;
+
+/**
+ * Process a DIGEST-MD5 authentication, using the challenge/response mechanisms.
+ */
+public class DigestMD5Authenticator implements ClientAuthenticator {
+
+    protected static final int AUTHENTICATE_CLIENT = 0;
+
+    protected static final int AUTHENTICATE_SERVER = 1;
+
+    protected static final int AUTHENTICATION_COMPLETE = 2;
+
+    // the host server name
+    protected String host;
+
+    // the user we're authenticating
+    protected String username;
+
+    // the user's password (the "shared secret")
+    protected String password;
+
+    // the target login realm
+    protected String realm;
+
+    // our message digest for processing the challenges.
+    MessageDigest digest;
+
+    // the string we send to the server on the first challenge.
+    protected String clientResponse;
+
+    // the response back from an authentication challenge.
+    protected String authenticationResponse = null;
+
+    // our list of realms received from the server (normally just one).
+    protected ArrayList realms;
+
+    // the nonce value sent from the server
+    protected String nonce;
+
+    // indicates whether we've gone through the entire challenge process.
+    protected int stage = AUTHENTICATE_CLIENT;
+
+    /**
+     * Main constructor.
+     * 
+     * @param host
+     *            The server host name.
+     * @param username
+     *            The login user name.
+     * @param password
+     *            The login password.
+     * @param realm
+     *            The target login realm (can be null).
+     */
+    public DigestMD5Authenticator(String host, String username, String password, String realm) {
+        this.host = host;
+        this.username = username;
+        this.password = password;
+        this.realm = realm;
+    }
+
+    /**
+     * Respond to the hasInitialResponse query. This mechanism does not have an
+     * initial response.
+     * 
+     * @return Always returns false.
+     */
+    public boolean hasInitialResponse() {
+        return false;
+    }
+
+    /**
+     * Indicate whether the challenge/response process is complete.
+     * 
+     * @return True if the last challenge has been processed, false otherwise.
+     */
+    public boolean isComplete() {
+        return stage == AUTHENTICATION_COMPLETE;
+    }
+
+    /**
+     * Retrieve the authenticator mechanism name.
+     * 
+     * @return Always returns the string "DIGEST-MD5"
+     */
+    public String getMechanismName() {
+        return "DIGEST-MD5";
+    }
+
+    /**
+     * Evaluate a DIGEST-MD5 login challenge, returning the a result string that
+     * should satisfy the clallenge.
+     * 
+     * @param challenge
+     *            The decoded challenge data, as a string.
+     * 
+     * @return A formatted challege response, as an array of bytes.
+     * @exception MessagingException
+     */
+    public byte[] evaluateChallenge(byte[] challenge) throws MessagingException {
+
+        // DIGEST-MD5 authentication goes in two stages. First state involves us
+        // validating with the
+        // server, the second stage is the server validating with us, using the
+        // shared secret.
+        switch (stage) {
+        // stage one of the process.
+        case AUTHENTICATE_CLIENT: {
+            // get the response and advance the processing stage.
+            byte[] response = authenticateClient(challenge);
+            stage = AUTHENTICATE_SERVER;
+            return response;
+        }
+
+        // stage two of the process.
+        case AUTHENTICATE_SERVER: {
+            // get the response and advance the processing stage to completed.
+            byte[] response = authenticateServer(challenge);
+            stage = AUTHENTICATION_COMPLETE;
+            return response;
+        }
+
+        // should never happen.
+        default:
+            throw new MessagingException("Invalid LOGIN challenge");
+        }
+    }
+
+    /**
+     * Evaluate a DIGEST-MD5 login server authentication challenge, returning
+     * the a result string that should satisfy the clallenge.
+     * 
+     * @param challenge
+     *            The decoded challenge data, as a string.
+     * 
+     * @return A formatted challege response, as an array of bytes.
+     * @exception MessagingException
+     */
+    public byte[] authenticateServer(byte[] challenge) throws MessagingException {
+        // parse the challenge string and validate.
+        if (!parseChallenge(challenge)) {
+            return null;
+        }
+
+        try {
+            // like all of the client validation steps, the following is order
+            // critical.
+            // first add in the URI information.
+            digest.update((":smtp/" + host).getBytes("US-ASCII"));
+            // now mix in the response we sent originally
+            String responseString = clientResponse + new String(Hex.encode(digest.digest()));
+            digest.update(responseString.getBytes("US-ASCII"));
+
+            // now convert that into a hex encoded string.
+            String validationText = new String(Hex.encode(digest.digest()));
+
+            // if everything went well, this calculated value should match what
+            // we got back from the server.
+            // our response back is just a null string....
+            if (validationText.equals(authenticationResponse)) {
+                return new byte[0];
+            }
+            throw new AuthenticationFailedException("Invalid DIGEST-MD5 response from server");
+        } catch (UnsupportedEncodingException e) {
+            throw new MessagingException("Invalid character encodings");
+        }
+
+    }
+
+    /**
+     * Evaluate a DIGEST-MD5 login client authentication challenge, returning
+     * the a result string that should satisfy the clallenge.
+     * 
+     * @param challenge
+     *            The decoded challenge data, as a string.
+     * 
+     * @return A formatted challege response, as an array of bytes.
+     * @exception MessagingException
+     */
+    public byte[] authenticateClient(byte[] challenge) throws MessagingException {
+        // parse the challenge string and validate.
+        if (!parseChallenge(challenge)) {
+            return null;
+        }
+
+        SecureRandom randomGenerator;
+        // before doing anything, make sure we can get the required crypto
+        // support.
+        try {
+            randomGenerator = new SecureRandom();
+            digest = MessageDigest.getInstance("MD5");
+        } catch (NoSuchAlgorithmException e) {
+            throw new MessagingException("Unable to access cryptography libraries");
+        }
+
+        // if not configured for a realm, take the first realm from the list, if
+        // any
+        if (realm == null) {
+            // if not handed any realms, just use the host name.
+            if (realms.isEmpty()) {
+                realm = host;
+            } else {
+                // pretty arbitrary at this point, so just use the first one.
+                realm = (String) realms.get(0);
+            }
+        }
+
+        // use secure random to generate a collection of bytes. that is our
+        // cnonce value.
+        byte[] cnonceBytes = new byte[32];
+
+        randomGenerator.nextBytes(cnonceBytes);
+        // and get this as a base64 encoded string.
+        String cnonce = new String(Base64.encode(cnonceBytes));
+
+        // Now the digest computation part. This gets a bit tricky, and must be
+        // done in strict order.
+
+        try {
+            // this identifies where we're logging into.
+            String idString = username + ":" + realm + ":" + password;
+            // we get a digest for this string, then use the digest for the
+            // first stage
+            // of the next digest operation.
+            digest.update(digest.digest(idString.getBytes("US-ASCII")));
+
+            // now we add the nonce strings to the digest.
+            String nonceString = ":" + nonce + ":" + cnonce;
+            digest.update(nonceString.getBytes("US-ASCII"));
+
+            // hex encode this digest, and add on the string values
+            // NB, we only support "auth" for the quality of protection value
+            // (qop). We save this in an
+            // instance variable because we'll need this to validate the
+            // response back from the server.
+            clientResponse = new String(Hex.encode(digest.digest())) + ":" + nonce + ":00000001:" + cnonce + ":auth:";
+
+            // now we add in identification values to the hash.
+            String authString = "AUTHENTICATE:smtp/" + host;
+            digest.update(authString.getBytes("US-ASCII"));
+
+            // this gets added on to the client response
+            String responseString = clientResponse + new String(Hex.encode(digest.digest()));
+            // and this gets fed back into the digest
+            digest.update(responseString.getBytes("US-ASCII"));
+
+            // and FINALLY, the challege digest is hex encoded for sending back
+            // to the server (whew).
+            String challengeResponse = new String(Hex.encode(digest.digest()));
+
+            // now finally build the keyword/value part of the challenge
+            // response. These can be
+            // in any order.
+            StringBuffer response = new StringBuffer();
+
+            response.append("username=\"");
+            response.append(username);
+            response.append("\"");
+
+            response.append(",realm=\"");
+            response.append(realm);
+            response.append("\"");
+
+            // we only support auth qop values, and the nonce-count (nc) is
+            // always 1.
+            response.append(",qop=auth");
+            response.append(",nc=00000001");
+
+            response.append(",nonce=\"");
+            response.append(nonce);
+            response.append("\"");
+
+            response.append(",cnonce=\"");
+            response.append(cnonce);
+            response.append("\"");
+
+            response.append(",digest-uri=\"smtp/");
+            response.append(host);
+            response.append("\"");
+
+            response.append(",response=");
+            response.append(challengeResponse);
+
+            return response.toString().getBytes("US-ASCII");
+
+        } catch (UnsupportedEncodingException e) {
+            throw new MessagingException("Invalid character encodings");
+        }
+    }
+
+    /**
+     * Parse the challege string, pulling out information required for our
+     * challenge response.
+     * 
+     * @param challenge
+     *            The challenge data.
+     * 
+     * @return true if there were no errors parsing the string, false otherwise.
+     * @exception MessagingException
+     */
+    protected boolean parseChallenge(byte[] challenge) throws MessagingException {
+        realms = new ArrayList();
+
+        DigestParser parser = new DigestParser(new String(challenge));
+
+        // parse the entire string...but we ignore everything but the options we
+        // support.
+        while (parser.hasMore()) {
+            NameValuePair pair = parser.parseNameValuePair();
+
+            String name = pair.name;
+
+            // realm to add to our list?
+            if (name.equalsIgnoreCase("realm")) {
+                realms.add(pair.value);
+            }
+            // we need the nonce to evaluate the client challenge.
+            else if (name.equalsIgnoreCase("nonce")) {
+                nonce = pair.value;
+            }
+            // rspauth is the challenge replay back, which allows us to validate
+            // that server is also legit.
+            else if (name.equalsIgnoreCase("rspauth")) {
+                authenticationResponse = pair.value;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Inner class for parsing a DIGEST-MD5 challenge string, which is composed
+     * of "name=value" pairs, separated by "," characters.
+     */
+    class DigestParser {
+        // the challenge we're parsing
+        String challenge;
+
+        // length of the challenge
+        int length;
+
+        // current parsing position
+        int position;
+
+        /**
+         * Normal constructor.
+         * 
+         * @param challenge
+         *            The challenge string to be parsed.
+         */
+        public DigestParser(String challenge) {
+            this.challenge = challenge;
+            this.length = challenge.length();
+            position = 0;
+        }
+
+        /**
+         * Test if there are more values to parse.
+         * 
+         * @return true if we've not reached the end of the challenge string,
+         *         false if the challenge has been completely consumed.
+         */
+        private boolean hasMore() {
+            return position < length;
+        }
+
+        /**
+         * Return the character at the current parsing position.
+         * 
+         * @return The string character for the current parse position.
+         */
+        private char currentChar() {
+            return challenge.charAt(position);
+        }
+
+        /**
+         * step forward to the next character position.
+         */
+        private void nextChar() {
+            position++;
+        }
+
+        /**
+         * Skip over any white space characters in the challenge string.
+         */
+        private void skipSpaces() {
+            while (position < length && Character.isWhitespace(currentChar())) {
+                position++;
+            }
+        }
+
+        /**
+         * Parse a quoted string used with a name/value pair, accounting for
+         * escape characters embedded within the string.
+         * 
+         * @return The string value of the character string.
+         */
+        private String parseQuotedValue() {
+            // we're here because we found the starting double quote. Step over
+            // it and parse to the closing
+            // one.
+            nextChar();
+
+            StringBuffer value = new StringBuffer();
+
+            while (hasMore()) {
+                char ch = currentChar();
+
+                // is this an escape char?
+                if (ch == '\\') {
+                    // step past this, and grab the following character
+                    nextChar();
+                    // we have an invalid quoted string....
+                    if (!hasMore()) {
+                        return null;
+                    }
+                    value.append(currentChar());
+                }
+                // end of the string?
+                else if (ch == '"') {
+                    // step over this so the caller doesn't process it.
+                    nextChar();
+                    // return the constructed string.
+                    return value.toString();
+                } else {
+                    // step over the character and contine with the next
+                    // characteer1
+                    value.append(ch);
+                }
+                nextChar();
+            }
+            /* fell off the end without finding a closing quote! */
+            return null;
+        }
+
+        /**
+         * Parse a token value used with a name/value pair.
+         * 
+         * @return The string value of the token. Returns null if nothing is
+         *         found up to the separater.
+         */
+        private String parseTokenValue() {
+
+            StringBuffer value = new StringBuffer();
+
+            while (hasMore()) {
+                char ch = currentChar();
+                switch (ch) {
+                // process the token separators.
+                case ' ':
+                case '\t':
+                case '(':
+                case ')':
+                case '<':
+                case '>':
+                case '@':
+                case ',':
+                case ';':
+                case ':':
+                case '\\':
+                case '"':
+                case '/':
+                case '[':
+                case ']':
+                case '?':
+                case '=':
+                case '{':
+                case '}':
+                    // no token characters found? this is bad.
+                    if (value.length() == 0) {
+                        return null;
+                    }
+                    // return the accumulated characters.
+                    return value.toString();
+
+                default:
+                    // is this a control character? That's a delimiter (likely
+                    // invalid for the next step,
+                    // but it is a token terminator.
+                    if (ch < 32 || ch > 127) {
+                        // no token characters found? this is bad.
+                        if (value.length() == 0) {
+                            return null;
+                        }
+                        // return the accumulated characters.
+                        return value.toString();
+                    }
+                    value.append(ch);
+                    break;
+                }
+                // step to the next character.
+                nextChar();
+            }
+            // no token characters found? this is bad.
+            if (value.length() == 0) {
+                return null;
+            }
+            // return the accumulated characters.
+            return value.toString();
+        }
+
+        /**
+         * Parse out a name token of a name/value pair.
+         * 
+         * @return The string value of the name.
+         */
+        private String parseName() {
+            // skip to the value start
+            skipSpaces();
+
+            // the name is a token.
+            return parseTokenValue();
+        }
+
+        /**
+         * Parse out a a value of a name/value pair.
+         * 
+         * @return The string value associated with the name.
+         */
+        private String parseValue() {
+            // skip to the value start
+            skipSpaces();
+
+            // start of a quoted string?
+            if (currentChar() == '"') {
+                // parse it out as a string.
+                return parseQuotedValue();
+            }
+            // the value must be a token.
+            return parseTokenValue();
+        }
+
+        /**
+         * Parse a name/value pair in an DIGEST-MD5 string.
+         * 
+         * @return A NameValuePair object containing the two parts of the value.
+         * @exception MessagingException
+         */
+        public NameValuePair parseNameValuePair() throws MessagingException {
+            // get the name token
+            String name = parseName();
+            if (name == null) {
+                throw new MessagingException("Name syntax error");
+            }
+
+            // the name should be followed by an "=" sign
+            if (!hasMore() || currentChar() != '=') {
+                throw new MessagingException("Name/value pair syntax error");
+            }
+
+            // step over the equals
+            nextChar();
+
+            // now get the value part
+            String value = parseValue();
+            if (value == null) {
+                throw new MessagingException("Name/value pair syntax error");
+            }
+
+            // skip forward to the terminator, which should either be the end of
+            // the line or a ","
+            skipSpaces();
+            // all that work, only to have a syntax error at the end (sigh)
+            if (hasMore()) {
+                if (currentChar() != ',') {
+                    throw new MessagingException("Name/value pair syntax error");
+                }
+                // step over, and make sure we position ourselves at either the
+                // end or the first
+                // real character for parsing the next name/value pair.
+                nextChar();
+                skipSpaces();
+            }
+            return new NameValuePair(name, value);
+        }
+    }
+
+    /**
+     * Simple inner class to represent a name/value pair.
+     */
+    public class NameValuePair {
+        public String name;
+
+        public String value;
+
+        NameValuePair(String name, String value) {
+            this.name = name;
+            this.value = value;
+        }
+
+    }
+}
diff --git a/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/authentication/LoginAuthenticator.java b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/authentication/LoginAuthenticator.java
new file mode 100644
index 0000000..9c52f62
--- /dev/null
+++ b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/authentication/LoginAuthenticator.java
@@ -0,0 +1,140 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.geronimo.javamail.authentication;
+
+import java.io.UnsupportedEncodingException;
+
+import javax.mail.MessagingException;
+
+public class LoginAuthenticator implements ClientAuthenticator {
+
+    // constants for the authentication stages
+    protected static final int USERNAME = 0;
+
+    protected static final int PASSWORD = 1;
+
+    protected static final int COMPLETE = 2;
+
+    // the user we're authenticating
+    protected String username;
+
+    // the user's password (the "shared secret")
+    protected String password;
+
+    // indicates whether we've gone through the entire challenge process.
+    protected int stage = USERNAME;
+
+    /**
+     * Main constructor.
+     * 
+     * @param username
+     *            The login user name.
+     * @param password
+     *            The login password.
+     */
+    public LoginAuthenticator(String username, String password) {
+        this.username = username;
+        this.password = password;
+    }
+
+    /**
+     * Respond to the hasInitialResponse query. This mechanism does not have an
+     * initial response.
+     * 
+     * @return Always returns false;
+     */
+    public boolean hasInitialResponse() {
+        return false;
+    }
+
+    /**
+     * Indicate whether the challenge/response process is complete.
+     * 
+     * @return True if the last challenge has been processed, false otherwise.
+     */
+    public boolean isComplete() {
+        return stage == COMPLETE;
+    }
+
+    /**
+     * Retrieve the authenticator mechanism name.
+     * 
+     * @return Always returns the string "LOGIN"
+     */
+    public String getMechanismName() {
+        return "LOGIN";
+    }
+
+    /**
+     * Evaluate a PLAIN login challenge, returning the a result string that
+     * should satisfy the clallenge.
+     * 
+     * @param challenge
+     *            The decoded challenge data, as a byte array
+     * 
+     * @return A formatted challege response, as an array of bytes.
+     * @exception MessagingException
+     */
+    public byte[] evaluateChallenge(byte[] challenge) throws MessagingException {
+
+        // process the correct stage for the challenge
+        switch (stage) {
+        // should never happen
+        case COMPLETE:
+            throw new MessagingException("Invalid LOGIN challenge");
+
+        case USERNAME: {
+            byte[] userBytes;
+
+            try {
+                // get the username and password in an UTF-8 encoding to create
+                // the token
+                userBytes = username.getBytes("UTF-8");
+            } catch (UnsupportedEncodingException e) {
+                // got an error, fail this (this should never happen).
+                throw new MessagingException("Invalid encoding");
+            }
+
+            // next time through we're looking for a password.
+            stage = PASSWORD;
+            // the user bytes are the entire challenge respose.
+            return userBytes;
+        }
+
+        case PASSWORD: {
+            byte[] passBytes;
+
+            try {
+                // get the username and password in an UTF-8 encoding to create
+                // the token
+                passBytes = password.getBytes("UTF-8");
+            } catch (UnsupportedEncodingException e) {
+                // got an error, fail this (this should never happen).
+                throw new MessagingException("Invalid encoding");
+            }
+            // we're finished
+            stage = COMPLETE;
+            return passBytes;
+        }
+        }
+        // should never get here.
+        throw new MessagingException("Invalid LOGIN challenge");
+    }
+}
diff --git a/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/authentication/PlainAuthenticator.java b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/authentication/PlainAuthenticator.java
new file mode 100644
index 0000000..95711ed
--- /dev/null
+++ b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/authentication/PlainAuthenticator.java
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.geronimo.javamail.authentication;
+
+import java.io.UnsupportedEncodingException;
+
+import javax.mail.MessagingException;
+
+public class PlainAuthenticator implements ClientAuthenticator {
+
+    // the user we're authenticating
+    protected String username;
+
+    // the user's password (the "shared secret")
+    protected String password;
+
+    // indicates whether we've gone through the entire challenge process.
+    protected boolean complete = false;
+
+    /**
+     * Main constructor.
+     * 
+     * @param username
+     *            The login user name.
+     * @param password
+     *            The login password.
+     */
+    public PlainAuthenticator(String username, String password) {
+        this.username = username;
+        this.password = password;
+    }
+
+    /**
+     * Respond to the hasInitialResponse query. This mechanism does have an
+     * initial response, which is the entire challenge sequence.
+     * 
+     * @return Always returns true.
+     */
+    public boolean hasInitialResponse() {
+        return true;
+    }
+
+    /**
+     * Indicate whether the challenge/response process is complete.
+     * 
+     * @return True if the last challenge has been processed, false otherwise.
+     */
+    public boolean isComplete() {
+        return complete;
+    }
+
+    /**
+     * Retrieve the authenticator mechanism name.
+     * 
+     * @return Always returns the string "PLAIN"
+     */
+    public String getMechanismName() {
+        return "PLAIN";
+    }
+
+    /**
+     * Evaluate a PLAIN login challenge, returning the a result string that
+     * should satisfy the clallenge.
+     * 
+     * @param challenge
+     *            The decoded challenge data, as byte array.
+     * 
+     * @return A formatted challege response, as an array of bytes.
+     * @exception MessagingException
+     */
+    public byte[] evaluateChallenge(byte[] challenge) throws MessagingException {
+        try {
+            // get the username and password in an UTF-8 encoding to create the
+            // token
+            byte[] userBytes = username.getBytes("UTF-8");
+            byte[] passBytes = password.getBytes("UTF-8");
+
+            // our token has two copies of the username, one copy of the
+            // password, and nulls
+            // between
+            byte[] tokenBytes = new byte[(userBytes.length * 2) + passBytes.length + 2];
+
+            System.arraycopy(userBytes, 0, tokenBytes, 0, userBytes.length);
+            System.arraycopy(userBytes, 0, tokenBytes, userBytes.length + 1, userBytes.length);
+            System.arraycopy(passBytes, 0, tokenBytes, (userBytes.length * 2) + 2, passBytes.length);
+
+            complete = true;
+            return tokenBytes;
+
+        } catch (UnsupportedEncodingException e) {
+            // got an error, fail this
+            throw new MessagingException("Invalid encoding");
+        }
+    }
+}
diff --git a/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/nntp/NNTPFolder.java b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/nntp/NNTPFolder.java
new file mode 100644
index 0000000..ba330e2
--- /dev/null
+++ b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/nntp/NNTPFolder.java
@@ -0,0 +1,449 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.geronimo.javamail.store.nntp;
+
+import javax.mail.Flags;
+import javax.mail.Folder;
+import javax.mail.IllegalWriteException;
+import javax.mail.Message;
+import javax.mail.MessagingException;
+import javax.mail.MethodNotSupportedException;
+import javax.mail.Session;
+import javax.mail.event.ConnectionEvent;
+
+import org.apache.geronimo.javamail.transport.nntp.NNTPConnection;
+
+/**
+ * The base NNTP implementation of the javax.mail.Folder This is a base class
+ * for both the Root NNTP server and each NNTP group folder.
+ * 
+ * @see javax.mail.Folder
+ * 
+ * @version $Rev$
+ */
+public class NNTPFolder extends Folder {
+
+    // our active connection.
+    protected NNTPConnection connection;
+
+    // our attached session
+    protected Session session;
+
+    // the name of this folder (either the name of the server for the root or
+    // the news group name).
+    protected String name;
+
+    // the "full" name of the folder. For the root folder, this is the name
+    // returned by the connection
+    // welcome string. Otherwise, this is the same as the name.
+    protected String fullName;
+
+    // the parent folder. For the root folder, this is null. For a group folder,
+    // this is the root.
+    protected Folder parent;
+
+    // the folder open state
+    protected boolean folderOpen = false;
+
+    // the folder message count. For the root folder, this is always 0.
+    protected int messageCount = 0;
+
+    // the persistent flags we save in the store (basically just the SEEN flag).
+    protected Flags permanentFlags;
+
+    /**
+     * Super class constructor the base NNTPFolder class.
+     * 
+     * @param store
+     *            The javamail store this folder is attached to.
+     */
+    protected NNTPFolder(NNTPStore store) {
+        super(store);
+        // get the active connection from the store...all commands are sent
+        // there
+        this.connection = store.getConnection();
+        this.session = store.getSession();
+
+        // set up our permanent flags bit.
+        permanentFlags = new Flags();
+        permanentFlags.add(Flags.Flag.SEEN);
+    }
+
+    /**
+     * Retrieve the folder name.
+     * 
+     * @return The folder's name.
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * Retrieve the folder's full name (including hierarchy information). NNTP
+     * folders are flat, so the full name is generally the same as the name.
+     * 
+     * @return The full name value.
+     */
+    public String getFullName() {
+        return fullName;
+    }
+
+    /**
+     * Returns the parent folder for this folder. Returns null if this is the
+     * root folder.
+     */
+    public Folder getParent() throws MessagingException {
+        return parent;
+    }
+
+    /**
+     * Indicated whether the folder "exists" or not. Existance in this context
+     * indicates that the group still exists on the server.
+     * 
+     * @return
+     * @exception MessagingException
+     */
+    public boolean exists() throws MessagingException {
+        // by default, return true. This is really only the case for the root.
+        // The group folder will
+        // need to override this.
+        return true;
+    }
+
+    /**
+     * List the subfolders. For group folders, this is a meaningless so we throw
+     * a MethodNotSupportedException.
+     * 
+     * @param pattern
+     *            The folder pattern string.
+     * 
+     * @return Never returns.
+     * @exception MessagingException
+     */
+    public Folder[] list(String pattern) throws MessagingException {
+        throw new MethodNotSupportedException("NNTP group folders cannot contain sub folders");
+    }
+
+    /**
+     * Retrieve the list of subscribed folders that match the given pattern
+     * string.
+     * 
+     * @param pattern
+     *            The pattern string used for the matching
+     * 
+     * @return An array of matching folders from the subscribed list.
+     */
+    public Folder[] listSubscribed(String pattern) throws MessagingException {
+        throw new MethodNotSupportedException("NNTP group folders cannot contain sub folders");
+    }
+
+    /**
+     * No sub folders, hence there is no notion of a seperator. We return a null
+     * character (consistent with what Sun returns for POP3 folders).
+     */
+    public char getSeparator() throws MessagingException {
+        return '\0';
+    }
+
+    /**
+     * Return whether this folder can hold just messages or also subfolders.
+     * Only the root folder can hold other folders, so it will need to override.
+     * 
+     * @return Either Folder.HOLDS_MESSAGES or Folder.HOLDS_FOLDERS.
+     * @exception MessagingException
+     */
+    public int getType() throws MessagingException {
+        return HOLDS_MESSAGES;
+    }
+
+    /**
+     * Create a new folder. NNTP folders are read only, so this is a nop.
+     * 
+     * @param type
+     *            The type of folder.
+     * 
+     * @return Not support, throws an exception.
+     * @exception MessagingException
+     */
+    public boolean create(int type) throws MessagingException {
+        throw new MethodNotSupportedException("Sub folders cannot be created in NNTP");
+    }
+
+    /**
+     * Check for new messages. We always return false for the root folder. The
+     * group folders will need to override.
+     * 
+     * @return Always returns false.
+     * @exception MessagingException
+     */
+    public boolean hasNewMessages() throws MessagingException {
+        return false;
+    }
+
+    /**
+     * Get a named subfolder from this folder. This only has meaning from the
+     * root NNTP folder.
+     * 
+     * @param name
+     *            The requested name.
+     * 
+     * @return If the folder exists, returns a Folder object representing the
+     *         named folder.
+     * @exception MessagingException
+     */
+    public Folder getFolder(String name) throws MessagingException {
+        throw new MethodNotSupportedException("NNTP Group folders do not support sub folders");
+    }
+
+    /**
+     * Delete a folder. This is not supported for NNTP.
+     * 
+     * @param recurse
+     *            The recusion flag.
+     * 
+     * @return Never returns.
+     * @exception MessagingException
+     */
+    public boolean delete(boolean recurse) throws MessagingException {
+        throw new MethodNotSupportedException("Deleting of NNTP folders is not supported");
+    }
+
+    /**
+     * Rename a folder. Not supported for NNTP folders.
+     * 
+     * @param f
+     *            The new folder specifying the rename location.
+     * 
+     * @return
+     * @exception MessagingException
+     */
+    public boolean renameTo(Folder f) throws MessagingException {
+        throw new MethodNotSupportedException("Renaming of NNTP folders is not supported.");
+    }
+
+    /**
+     * @see javax.mail.Folder#open(int)
+     */
+    public void open(int mode) throws MessagingException {
+
+        // we don't support READ_WRITE mode, so don't allow opening in that
+        // mode.
+        if (mode == READ_WRITE) {
+            throw new IllegalWriteException("Newsgroup folders cannot be opened read/write");
+        }
+
+        // an only be performed on a closed folder
+        checkClosed();
+
+        this.mode = mode;
+
+        // perform folder type-specific open actions.
+        openFolder();
+
+        folderOpen = true;
+
+        notifyConnectionListeners(ConnectionEvent.OPENED);
+    }
+
+    /**
+     * Perform folder type-specific open actions. The default action is to do
+     * nothing.
+     * 
+     * @exception MessagingException
+     */
+    protected void openFolder() throws MessagingException {
+    }
+
+    /**
+     * Peform folder type-specific close actions. The default action is to do
+     * nothing.
+     * 
+     * @exception MessagingException
+     */
+    protected void closeFolder() throws MessagingException {
+    }
+
+    /**
+     * Close the folder. Cleans up resources, potentially expunges messages
+     * marked for deletion, and sends an event notification.
+     * 
+     * @param expunge
+     *            The expunge flag, which is ignored for NNTP folders.
+     * 
+     * @exception MessagingException
+     */
+    public void close(boolean expunge) throws MessagingException {
+        // Can only be performed on an open folder
+        checkOpen();
+
+        // give the subclasses an opportunity to do some cleanup
+        closeFolder();
+
+        folderOpen = false;
+        notifyConnectionListeners(ConnectionEvent.CLOSED);
+    }
+
+    /**
+     * Tests the open status of the folder.
+     * 
+     * @return true if the folder is open, false otherwise.
+     */
+    public boolean isOpen() {
+        return folderOpen;
+    }
+
+    /**
+     * Get the permanentFlags
+     * 
+     * @return The set of permanent flags we support (only SEEN).
+     */
+    public Flags getPermanentFlags() {
+        // we need a copy of our master set.
+        return new Flags(permanentFlags);
+    }
+
+    /**
+     * Get the count of messages in this folder.
+     * 
+     * @return The message count.
+     * @exception MessagingException
+     */
+    public int getMessageCount() throws MessagingException {
+        return messageCount;
+    }
+
+    /**
+     * Checks wether the message is in cache, if not will create a new message
+     * object and return it.
+     * 
+     * @see javax.mail.Folder#getMessage(int)
+     */
+    public Message getMessage(int msgNum) throws MessagingException {
+        // for the base, we just throw an exception.
+        throw new MethodNotSupportedException("Root NNTP folder does not contain messages");
+    }
+
+    /**
+     * Append messages to a folder. NNTP folders are read only, so this is not
+     * supported.
+     * 
+     * @param msgs
+     *            The list of messages to append.
+     * 
+     * @exception MessagingException
+     */
+    public void appendMessages(Message[] msgs) throws MessagingException {
+        throw new MethodNotSupportedException("Root NNTP folder does not contain messages");
+
+    }
+
+    /**
+     * Expunge messages marked for deletion and return a list of the Messages.
+     * Not supported for NNTP.
+     * 
+     * @return Never returns.
+     * @exception MessagingException
+     */
+    public Message[] expunge() throws MessagingException {
+        throw new MethodNotSupportedException("Root NNTP folder does not contain messages");
+    }
+
+    /**
+     * Below is a list of convenience methods that avoid repeated checking for a
+     * value and throwing an exception
+     */
+
+    /** Ensure the folder is open */
+    protected void checkOpen() throws IllegalStateException {
+        if (!folderOpen) {
+            throw new IllegalStateException("Folder is not Open");
+        }
+    }
+
+    /** Ensure the folder is not open */
+    protected void checkClosed() throws IllegalStateException {
+        if (folderOpen) {
+            throw new IllegalStateException("Folder is Open");
+        }
+    }
+
+    /**
+     * @see javax.mail.Folder#notifyMessageChangedListeners(int,
+     *      javax.mail.Message)
+     * 
+     * this method is protected and cannot be used outside of Folder, therefore
+     * had to explicitly expose it via a method in NNTPFolder, so that
+     * NNTPMessage has access to it
+     * 
+     * Bad design on the part of the Java Mail API.
+     */
+    public void notifyMessageChangedListeners(int type, Message m) {
+        super.notifyMessageChangedListeners(type, m);
+    }
+
+    /**
+     * Retrieve the subscribed status for a folder. This default implementation
+     * just returns false (which is true for the root folder).
+     * 
+     * @return Always returns true.
+     */
+    public boolean isSubscribed() {
+        return false;
+    }
+
+    /**
+     * Set the subscribed status for a folder.
+     * 
+     * @param flag
+     *            The new subscribed status.
+     * 
+     * @exception MessagingException
+     */
+    public void setSubscribed(boolean flag) throws MessagingException {
+        throw new MessagingException("Root NNTP folder cannot be subscribed to");
+    }
+
+    /**
+     * Test if a given article number is marked as SEEN.
+     * 
+     * @param article
+     *            The target article number.
+     * 
+     * @return The articles current seen status.
+     */
+    public boolean isSeen(int article) {
+        return false;
+    }
+
+    /**
+     * Set the SEEN status for an article.
+     * 
+     * @param article
+     *            The target article.
+     * @param flag
+     *            The new seen setting.
+     * 
+     * @exception MessagingException
+     */
+    public void setSeen(int article, boolean flag) throws MessagingException {
+        throw new MessagingException("Root NNTP folder does not contain articles");
+    }
+
+}
diff --git a/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/nntp/NNTPGroupFolder.java b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/nntp/NNTPGroupFolder.java
new file mode 100644
index 0000000..19e51fd
--- /dev/null
+++ b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/nntp/NNTPGroupFolder.java
@@ -0,0 +1,386 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.geronimo.javamail.store.nntp;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.StringTokenizer;
+
+import javax.mail.FetchProfile;
+import javax.mail.FolderNotFoundException;
+import javax.mail.Message;
+import javax.mail.MessagingException;
+
+import org.apache.geronimo.javamail.store.nntp.newsrc.NNTPNewsrcGroup;
+import org.apache.geronimo.javamail.transport.nntp.NNTPReply;
+
+/**
+ * The NNTP implementation of the javax.mail.Folder Note that only INBOX is
+ * supported in NNTP
+ * <p>
+ * <url>http://www.faqs.org/rfcs/rfc1939.html</url>
+ * </p>
+ * 
+ * @see javax.mail.Folder
+ * 
+ * @version $Rev$ $Date$
+ */
+public class NNTPGroupFolder extends NNTPFolder {
+
+    // holders for status information returned by the GROUP command.
+    protected int firstArticle = -1;
+
+    protected int lastArticle = -1;
+
+    // retrieved articles, mapped by article number.
+    Map articles;
+
+    // information stored in the newsrc group.
+    NNTPNewsrcGroup groupInfo;
+
+    /**
+     * Construct a "real" folder representing an NNTP news group.
+     * 
+     * @param parent
+     *            The parent root folder.
+     * @param store
+     *            The Store this folder is attached to.
+     * @param name
+     *            The folder name.
+     * @param groupInfo
+     *            The newsrc group information attached to the newsrc database.
+     *            This contains subscription and article "SEEN" information.
+     */
+    protected NNTPGroupFolder(NNTPRootFolder parent, NNTPStore store, String name, NNTPNewsrcGroup groupInfo) {
+        super(store);
+        // the name and the full name are the same.
+        this.name = name;
+        this.fullName = name;
+        // set the parent appropriately.
+        this.parent = parent = parent;
+        this.groupInfo = groupInfo;
+    }
+
+    /**
+     * Ping the server and update the group count, first, and last information.
+     * 
+     * @exception MessagingException
+     */
+    private void updateGroupStats() throws MessagingException {
+        // ask the server for information about the group. This is a one-line
+        // reponse with status on
+        // the group, if it exists.
+        NNTPReply reply = connection.sendCommand("GROUP " + name);
+
+        // explicitly not there?
+        if (reply.getCode() == NNTPReply.NO_SUCH_NEWSGROUP) {
+            throw new FolderNotFoundException(this, "Folder does not exist on server: " + reply);
+        } else if (reply.getCode() != NNTPReply.GROUP_SELECTED) {
+            throw new MessagingException("Error requesting group information: " + reply);
+        }
+
+        // we've gotten back a good response, now parse out the group specifics
+        // from the
+        // status response.
+
+        StringTokenizer tokenizer = new StringTokenizer(reply.getMessage());
+
+        // we should have a least 3 tokens here, in the order "count first
+        // last".
+
+        // article count
+        if (tokenizer.hasMoreTokens()) {
+            String count = tokenizer.nextToken();
+            try {
+                messageCount = Integer.parseInt(count);
+            } catch (NumberFormatException e) {
+                // ignore
+            }
+        }
+
+        // first article number
+        if (tokenizer.hasMoreTokens()) {
+            String first = tokenizer.nextToken();
+            try {
+                firstArticle = Integer.parseInt(first);
+            } catch (NumberFormatException e) {
+                // ignore
+            }
+        }
+
+        // last article number.
+        if (tokenizer.hasMoreTokens()) {
+            String last = tokenizer.nextToken();
+            try {
+                lastArticle = Integer.parseInt(last);
+            } catch (NumberFormatException e) {
+                // ignore
+            }
+        }
+    }
+
+    /**
+     * Test to see if this folder actually exists. This pings the server for
+     * information about the GROUP and updates the article count and index
+     * information.
+     * 
+     * @return true if the newsgroup exists on the server, false otherwise.
+     * @exception MessagingException
+     */
+    public boolean exists() throws MessagingException {
+
+        try {
+            // update the group statistics. If the folder doesn't exist, we'll
+            // get an exception that we
+            // can turn into a false reply.
+            updateGroupStats();
+            // updated ok, so it must be there.
+            return true;
+        } catch (FolderNotFoundException e) {
+            return false;
+        }
+    }
+
+    /**
+     * Ping the NNTP server to check if a newsgroup has any new messages.
+     * 
+     * @return True if the server has new articles from the last time we
+     *         checked. Also returns true if this is the first time we've
+     *         checked.
+     * @exception MessagingException
+     */
+    public boolean hasNewMessages() throws MessagingException {
+        int oldLast = lastArticle;
+        updateGroupStats();
+
+        return lastArticle > oldLast;
+    }
+
+    /**
+     * Open the folder for use. This retrieves article count information from
+     * the server.
+     * 
+     * @exception MessagingException
+     */
+    public void openFolder() throws MessagingException {
+        // update the group specifics, especially the message count.
+        updateGroupStats();
+
+        // get a cache for retrieve articles
+        articles = new HashMap();
+    }
+
+    /**
+     * Close the folder, which also clears out the article caches.
+     * 
+     * @exception MessagingException
+     */
+    public void closeFolder() throws MessagingException {
+        // get ride of any retrieve articles, and flip over the open for
+        // business sign.
+        articles = null;
+    }
+
+    /**
+     * Checks wether the message is in cache, if not will create a new message
+     * object and return it.
+     * 
+     * @see javax.mail.Folder#getMessage(int)
+     */
+    public Message getMessage(int msgNum) throws MessagingException {
+        // Can only be performed on an Open folder
+        checkOpen();
+
+        // get an object form to look up in the retrieve messages list (oh how I
+        // wish there was
+        // something like Map that could use integer keys directly!).
+        Integer key = new Integer(msgNum);
+        NNTPMessage message = (NNTPMessage) articles.get(key);
+        if (message != null) {
+            // piece of cake!
+            return message;
+        }
+
+        // we need to suck a message down from the server.
+        // but first, make sure the group is still valid.
+        updateGroupStats();
+
+        // just send a STAT command to this message. Right now, all we want is
+        // existance proof. We'll
+        // retrieve the other bits when requested.
+        NNTPReply reply = connection.sendCommand("STAT " + Integer.toString(msgNum));
+        if (reply.getCode() != NNTPReply.REQUEST_TEXT_SEPARATELY) {
+            throw new MessagingException("Error retrieving article from NNTP server: " + reply);
+        }
+
+        // we need to parse out the message id.
+        String response = reply.getMessage();
+
+        int idStart = response.indexOf('<');
+        int idEnd = response.indexOf('>');
+
+        message = new NNTPMessage(this, (NNTPStore) store, msgNum, response.substring(idStart + 1, idEnd));
+
+        // add this to the article cache.
+        articles.put(key, message);
+
+        return message;
+    }
+
+    /**
+     * Retrieve all articles in the group.
+     * 
+     * @return An array of all messages in the group.
+     */
+    public Message[] getMessages() throws MessagingException {
+        // we're going to try first with XHDR, which will allow us to retrieve
+        // everything in one shot. If that
+        // fails, we'll fall back on issing STAT commands for the entire article
+        // range.
+        NNTPReply reply = connection.sendCommand("XHDR Message-ID " + Integer.toString(firstArticle) + "-"
+                + Integer.toString(lastArticle), NNTPReply.HEAD_FOLLOWS);
+
+        List messages = new ArrayList();
+
+        if (reply.getCode() == NNTPReply.HEAD_FOLLOWS) {
+            List lines = reply.getData();
+
+            for (int i = 0; i < lines.size(); i++) {
+                String line = (String) lines.get(i);
+
+                try {
+                    int pos = line.indexOf(' ');
+                    int articleID = Integer.parseInt(line.substring(0, pos));
+                    String messageID = line.substring(pos + 1);
+                    Integer key = new Integer(articleID);
+                    // see if we have this message cached, If not, create it.
+                    Message message = (Message) articles.get(key);
+                    if (message == null) {
+                        message = new NNTPMessage(this, (NNTPStore) store, key.intValue(), messageID);
+                        articles.put(key, message);
+                    }
+
+                    messages.add(message);
+
+                } catch (NumberFormatException e) {
+                    // should never happen, but just skip this entry if it does.
+                }
+            }
+        } else {
+            // grumble, we need to stat each article id to see if it
+            // exists....lots of round trips.
+            for (int i = firstArticle; i <= lastArticle; i++) {
+                try {
+                    messages.add(getMessage(i));
+                } catch (MessagingException e) {
+                    // just assume if there is an error, it's because the
+                    // message id doesn't exist.
+                }
+            }
+        }
+
+        return (Message[]) messages.toArray(new Message[0]);
+    }
+
+    /**
+     * @see javax.mail.Folder#fetch(javax.mail.Message[],
+     *      javax.mail.FetchProfile)
+     * 
+     * The JavaMail API recommends that this method be overrident to provide a
+     * meaningfull implementation.
+     */
+    public void fetch(Message[] msgs, FetchProfile fp) throws MessagingException {
+        // Can only be performed on an Open folder
+        checkOpen();
+
+        for (int i = 0; i < msgs.length; i++) {
+            Message msg = msgs[i];
+            // we can only perform this operation for NNTPMessages.
+            if (msg == null || !(msg instanceof NNTPMessage)) {
+                // we can't fetch if it's the wrong message type
+                continue;
+            }
+
+            // fetching both the headers and body?
+            if (fp.contains(FetchProfile.Item.ENVELOPE) && fp.contains(FetchProfile.Item.CONTENT_INFO)) {
+
+                // retrive everything
+                ((NNTPMessage) msg).loadArticle();
+            }
+            // headers only?
+            else if (fp.contains(FetchProfile.Item.ENVELOPE)) {
+                ((NNTPMessage) msg).loadHeaders();
+            } else if (fp.contains(FetchProfile.Item.CONTENT_INFO)) {
+                ((NNTPMessage) msg).loadContent();
+            }
+        }
+    }
+
+    /**
+     * Return the subscription status of this folder.
+     * 
+     * @return true if the folder is marked as subscribed, false for
+     *         unsubscribed.
+     */
+    public boolean isSubscribed() {
+        return groupInfo.isSubscribed();
+    }
+
+    /**
+     * Set or clear the subscription status of a file.
+     * 
+     * @param flag
+     *            The new subscription state.
+     */
+    public void setSubscribed(boolean flag) {
+        groupInfo.setSubscribed(flag);
+    }
+
+    /**
+     * Return the "seen" state for an article in a folder.
+     * 
+     * @param article
+     *            The article number.
+     * 
+     * @return true if the article is marked as seen in the newsrc file, false
+     *         for unseen files.
+     */
+    public boolean isSeen(int article) {
+        return groupInfo.isArticleSeen(article);
+    }
+
+    /**
+     * Set the seen state for an article in a folder.
+     * 
+     * @param article
+     *            The article number.
+     * @param flag
+     *            The new seen state.
+     */
+    public void setSeen(int article, boolean flag) {
+        if (flag) {
+            groupInfo.markArticleSeen(article);
+        } else {
+            groupInfo.markArticleUnseen(article);
+        }
+    }
+}
diff --git a/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/nntp/NNTPMessage.java b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/nntp/NNTPMessage.java
new file mode 100644
index 0000000..e57d146
--- /dev/null
+++ b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/nntp/NNTPMessage.java
@@ -0,0 +1,377 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.geronimo.javamail.store.nntp;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Enumeration;
+
+import javax.mail.Flags;
+import javax.mail.IllegalWriteException;
+import javax.mail.MessagingException;
+import javax.mail.Session;
+import javax.mail.internet.InternetHeaders;
+import javax.mail.internet.MimeMessage;
+
+import org.apache.geronimo.javamail.transport.nntp.NNTPConnection;
+import org.apache.geronimo.javamail.transport.nntp.NNTPReply;
+import org.apache.geronimo.javamail.transport.nntp.StringListInputStream;
+
+/**
+ * NNTP implementation of javax.mail.internet.MimeMessage
+ * 
+ * Only the most basic information is given and Message objects created here is
+ * a light-weight reference to the actual Message As per the JavaMail spec items
+ * from the actual message will get filled up on demand
+ * 
+ * If some other items are obtained from the server as a result of one call,
+ * then the other details are also processed and filled in. For ex if RETR is
+ * called then header information will also be processed in addition to the
+ * content
+ * 
+ * @version $Rev$ $Date$
+ */
+public class NNTPMessage extends MimeMessage {
+    // the server message identifer
+    String messageID = null;
+
+    // our attached session
+    protected Session session;
+
+    // the Store we're stored in (which manages the connection and other stuff).
+    protected NNTPStore store;
+
+    // our active connection.
+    protected NNTPConnection connection;
+
+    // used to force loading of headers
+    protected boolean headersLoaded = false;
+
+    // use to force content loading
+    protected boolean contentLoaded = false;
+
+    /**
+     * Contruct an NNTPMessage instance.
+     * 
+     * @param folder
+     *            The hosting folder for the message.
+     * @param store
+     *            The Store owning the article (and folder).
+     * @param msgnum
+     *            The article message number.
+     * @param messageID
+     *            The article messageID (as assigned by the server).
+     * 
+     * @exception MessagingException
+     */
+    NNTPMessage(NNTPFolder folder, NNTPStore store, int msgnum, String messageID) throws MessagingException {
+        super(folder, msgnum);
+        this.messageID = messageID;
+        this.store = store;
+        this.session = ((NNTPStore) store).getSession();
+        // get the active connection from the store...all commands are sent
+        // there
+        this.connection = ((NNTPStore) store).getConnection();
+
+        // get our flag set from the folder.
+        flags = folder.getPermanentFlags();
+        // now check our initial SEEN state and set the flags appropriately
+        if (folder.isSeen(msgnum)) {
+            flags.add(Flags.Flag.SEEN);
+        } else {
+            flags.remove(Flags.Flag.SEEN);
+        }
+    }
+
+    /**
+     * Retrieve the size of the message content. The content will be retrieved
+     * from the server, if necessary.
+     * 
+     * @return The size of the content.
+     * @exception MessagingException
+     */
+    public int getSize() throws MessagingException {
+        // make sure we've retrieved the message content and continue with the
+        // superclass version.
+        loadContent();
+        return super.getSize();
+    }
+
+    /**
+     * Get a line count for the NNTP message. This is potentially stored in the
+     * Lines article header. If not there, we return a default of -1.
+     * 
+     * @return The header line count estimate, or -1 if not retrieveable.
+     * @exception MessagingException
+     */
+    public int getLineCount() throws MessagingException {
+        String[] headers = getHeader("Lines");
+
+        // hopefully, there's only a single one of these. No sensible way of
+        // interpreting
+        // multiples.
+        if (headers.length == 1) {
+            try {
+                return Integer.parseInt(headers[0].trim());
+
+            } catch (NumberFormatException e) {
+                // ignore
+            }
+        }
+        // dunno...and let them know I don't know.
+        return -1;
+    }
+
+    /**
+     * @see javax.mail.internet.MimeMessage#getContentStream()
+     */
+    protected InputStream getContentStream() throws MessagingException {
+        // get the article information.
+        loadArticle();
+        return super.getContentStream();
+    }
+
+    /***************************************************************************
+     * Following is a set of methods that deal with headers These methods are
+     * just overrides on the superclass methods to allow lazy loading of the
+     * header information.
+     **************************************************************************/
+
+    public String[] getHeader(String name) throws MessagingException {
+        loadHeaders();
+        return headers.getHeader(name);
+    }
+
+    public String getHeader(String name, String delimiter) throws MessagingException {
+        loadHeaders();
+        return headers.getHeader(name, delimiter);
+    }
+
+    public Enumeration getAllHeaders() throws MessagingException {
+        loadHeaders();
+        return headers.getAllHeaders();
+    }
+
+    public Enumeration getMatchingHeaders(String[] names) throws MessagingException {
+        loadHeaders();
+        return headers.getMatchingHeaders(names);
+    }
+
+    public Enumeration getNonMatchingHeaders(String[] names) throws MessagingException {
+        loadHeaders();
+        return headers.getNonMatchingHeaders(names);
+    }
+
+    public Enumeration getAllHeaderLines() throws MessagingException {
+        loadHeaders();
+        return headers.getAllHeaderLines();
+    }
+
+    public Enumeration getMatchingHeaderLines(String[] names) throws MessagingException {
+        loadHeaders();
+        return headers.getMatchingHeaderLines(names);
+    }
+
+    public Enumeration getNonMatchingHeaderLines(String[] names) throws MessagingException {
+        loadHeaders();
+        return headers.getNonMatchingHeaderLines(names);
+    }
+
+    // the following are overrides for header modification methods. These
+    // messages are read only,
+    // so the headers cannot be modified.
+    public void addHeader(String name, String value) throws MessagingException {
+        throw new IllegalWriteException("NNTP messages are read-only");
+    }
+
+    public void setHeader(String name, String value) throws MessagingException {
+        throw new IllegalWriteException("NNTP messages are read-only");
+    }
+
+    public void removeHeader(String name) throws MessagingException {
+        throw new IllegalWriteException("NNTP messages are read-only");
+    }
+
+    public void addHeaderLine(String line) throws MessagingException {
+        throw new IllegalWriteException("IMAP messages are read-only");
+    }
+
+    /**
+     * We cannot modify these messages
+     */
+    public void saveChanges() throws MessagingException {
+        throw new IllegalWriteException("NNTP messages are read-only");
+    }
+
+    /**
+     * Retrieve the message headers from the NNTP server.
+     * 
+     * @exception MessagingException
+     */
+    public void loadHeaders() throws MessagingException {
+        // don't retrieve if already loaded.
+        if (headersLoaded) {
+            return;
+        }
+
+        NNTPReply reply = connection.sendCommand("HEAD " + messageID, NNTPReply.HEAD_FOLLOWS);
+
+        if (reply.getCode() == NNTPReply.HEAD_FOLLOWS) {
+            try {
+                // wrap a stream around the reply data and read as headers.
+                updateHeaders(new StringListInputStream(reply.getData()));
+            } catch (IOException e) {
+                throw new MessagingException("Error retrieving article headers from server", e);
+            }
+        } else {
+            throw new MessagingException("Error retrieving article headers from server: " + reply);
+        }
+    }
+
+    /**
+     * Update the message headers from an input stream.
+     * 
+     * @param in
+     *            The InputStream source for the header information.
+     * 
+     * @exception MessagingException
+     */
+    public void updateHeaders(InputStream in) throws MessagingException {
+        // wrap a stream around the reply data and read as headers.
+        headers = new InternetHeaders(in);
+        headersLoaded = true;
+    }
+
+    /**
+     * Load just the message content from the NNTP server.
+     * 
+     * @exception MessagingException
+     */
+    public void loadContent() throws MessagingException {
+        if (contentLoaded) {
+            return;
+        }
+
+        NNTPReply reply = connection.sendCommand("BODY " + messageID, NNTPReply.BODY_FOLLOWS);
+
+        if (reply.getCode() == NNTPReply.BODY_FOLLOWS) {
+            try {
+                InputStream in = new StringListInputStream(reply.getData());
+                updateContent(in);
+            } catch (IOException e) {
+                throw new MessagingException("Error retrieving article body from server", e);
+            }
+        } else {
+            throw new MessagingException("Error retrieving article body from server: " + reply);
+        }
+    }
+
+    /**
+     * Load the entire article from the NNTP server. This updates both the
+     * headers and the content.
+     * 
+     * @exception MessagingException
+     */
+    public void loadArticle() throws MessagingException {
+        // if the headers are already loaded, retrieve the content portion.
+        if (headersLoaded) {
+            loadContent();
+            return;
+        }
+
+        // we need to retrieve everything.
+        NNTPReply reply = connection.sendCommand("ARTICLE " + messageID, NNTPReply.ARTICLE_FOLLOWS);
+
+        if (reply.getCode() == NNTPReply.ARTICLE_FOLLOWS) {
+            try {
+                InputStream in = new StringListInputStream(reply.getData());
+                // update both the headers and the content.
+                updateHeaders(in);
+                updateContent(in);
+            } catch (IOException e) {
+                throw new MessagingException("Error retrieving article from server", e);
+            }
+        } else {
+            throw new MessagingException("Error retrieving article from server: " + reply);
+        }
+    }
+
+    /**
+     * Update the article content from an input stream.
+     * 
+     * @param in
+     *            The content data source.
+     * 
+     * @exception MessagingException
+     */
+    public void updateContent(InputStream in) throws MessagingException {
+        try {
+            ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+            byte[] buffer = new byte[4096];
+
+            // copy the content data from the stream into a byte buffer for the
+            // content.
+            while (true) {
+                int read = in.read(buffer);
+                if (read == -1) {
+                    break;
+                }
+                out.write(buffer, 0, read);
+            }
+
+            content = out.toByteArray();
+            contentLoaded = true;
+        } catch (IOException e) {
+            throw new MessagingException("Error retrieving message body from server", e);
+        }
+    }
+
+    /**
+     * Get the server assigned messageid for the article.
+     * 
+     * @return The server assigned message id.
+     */
+    public String getMessageId() {
+        return messageID;
+    }
+
+    /**
+     * Override of setFlags(). We need to ensure that if the SEEN flag is set or
+     * cleared, that the newsrc file correctly reflects the current state.
+     * 
+     * @param flag
+     *            The flag being set.
+     * @param newvalue
+     *            The new flag value.
+     * 
+     * @exception MessagingException
+     */
+    public void setFlags(Flags flag, boolean newvalue) throws MessagingException {
+        // if this is the SEEN flag, make sure we shadow this in the newsrc
+        // file.
+        if (flag.contains(Flags.Flag.SEEN)) {
+            ((NNTPFolder) folder).setSeen(msgnum, newvalue);
+        }
+        // have the superclass do the real flag setting.
+        super.setFlags(flag, newvalue);
+    }
+}
diff --git a/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/nntp/NNTPRootFolder.java b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/nntp/NNTPRootFolder.java
new file mode 100644
index 0000000..605c59e
--- /dev/null
+++ b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/nntp/NNTPRootFolder.java
@@ -0,0 +1,399 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.geronimo.javamail.store.nntp;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.mail.Folder;
+import javax.mail.MessagingException;
+
+import org.apache.geronimo.javamail.store.nntp.newsrc.NNTPNewsrcGroup;
+import org.apache.geronimo.javamail.transport.nntp.NNTPReply;
+import org.apache.geronimo.mail.util.SessionUtil;
+
+/**
+ * The base NNTP implementation of the javax.mail.Folder This is a base class
+ * for both the Root NNTP server and each NNTP group folder.
+ * 
+ * @see javax.mail.Folder
+ * 
+ * @version $Rev$
+ */
+public class NNTPRootFolder extends NNTPFolder {
+    protected static final String NNTP_LISTALL = "mail.nntp.listall";
+
+    /**
+     * Construct the NNTPRootFolder.
+     * 
+     * @param store
+     *            The owning Store.
+     * @param name
+     *            The folder name (by default, this is the server host name).
+     * @param fullName
+     *            The fullName to use for this server (derived from welcome
+     *            string).
+     */
+    protected NNTPRootFolder(NNTPStore store, String name, String fullName) {
+        super(store);
+
+        this.name = name;
+        this.fullName = fullName;
+    }
+
+    /**
+     * List the subfolders. For group folders, this is a meaningless so we throw
+     * a MethodNotSupportedException.
+     * 
+     * @param pattern
+     *            The folder pattern string.
+     * 
+     * @return Never returns.
+     * @exception MessagingException
+     */
+    public synchronized Folder[] list(String pattern) throws MessagingException {
+        // the pattern specfied for javamail uses two wild card characters, "%"
+        // and "*". The "%" matches
+        // and character except hierarchy separators. Since we have a flag
+        // hierarchy, "%" and "*" are
+        // essentially the same. If we convert the "%" into "*", we can just
+        // treat this as a wildmat
+        // formatted pattern and pass this on to the server rather than having
+        // to read everything and
+        // process the strings on the client side.
+
+        pattern = pattern.replace('%', '*');
+
+        // if we're not supposed to list everything, then just filter the list
+        // of subscribed groups.
+        if (SessionUtil.getBooleanProperty(NNTP_LISTALL, false)) {
+            return filterActiveGroups(pattern);
+        } else {
+            return filterSubscribedGroups(pattern);
+        }
+    }
+
+    /**
+     * Retrieve the list of subscribed folders that match the given pattern
+     * string.
+     * 
+     * @param pattern
+     *            The pattern string used for the matching
+     * 
+     * @return An array of matching folders from the subscribed list.
+     */
+    public Folder[] listSubscribed(String pattern) throws MessagingException {
+        // the pattern specfied for javamail uses two wild card characters, "%"
+        // and "*". The "%" matches
+        // and character except hierarchy separators. Since we have a flag
+        // hierarchy, "%" and "*" are
+        // essentially the same. If we convert the "%" into "*", we can just
+        // treat this as a wildmat
+        // formatted pattern and pass this on to the server rather than having
+        // to read everything and
+        // process the strings on the client side.
+
+        pattern = pattern.replace('%', '*');
+
+        return filterSubscribedGroups(pattern);
+    }
+
+    /**
+     * Retrieve the list of matching groups from the NNTP server using the LIST
+     * ACTIVE command. The server does the wildcard matching for us.
+     * 
+     * @param pattern
+     *            The pattern string (in wildmat format) used to match.
+     * 
+     * @return An array of folders for the matching groups.
+     */
+    protected Folder[] filterActiveGroups(String pattern) throws MessagingException {
+        NNTPReply reply = connection.sendCommand("LIST ACTIVE " + pattern, NNTPReply.LIST_FOLLOWS);
+
+        // if the LIST ACTIVE command isn't supported,
+        if (reply.getCode() == NNTPReply.COMMAND_NOT_RECOGNIZED) {
+            // only way to list all is to retrieve all and filter.
+            return filterAllGroups(pattern);
+        } else if (reply.getCode() != NNTPReply.LIST_FOLLOWS) {
+            throw new MessagingException("Error retrieving group list from NNTP server: " + reply);
+        }
+
+        // get the response back from the server and process each returned group
+        // name.
+        List groups = reply.getData();
+
+        Folder[] folders = new Folder[groups.size()];
+        for (int i = 0; i < groups.size(); i++) {
+            folders[i] = getFolder(getGroupName((String) groups.get(i)));
+        }
+        return folders;
+    }
+
+    /**
+     * Retrieve a list of all groups from the server and filter on the names.
+     * Not recommended for the usenet servers, as there are over 30000 groups to
+     * process.
+     * 
+     * @param pattern
+     *            The pattern string used for the selection.
+     * 
+     * @return The Folders for the matching groups.
+     */
+    protected Folder[] filterAllGroups(String pattern) throws MessagingException {
+        NNTPReply reply = connection.sendCommand("LIST", NNTPReply.LIST_FOLLOWS);
+
+        if (reply.getCode() != NNTPReply.LIST_FOLLOWS) {
+            throw new MessagingException("Error retrieving group list from NNTP server: " + reply);
+        }
+
+        // get the response back from the server and process each returned group
+        // name.
+        List groups = reply.getData();
+
+        WildmatMatcher matcher = new WildmatMatcher(pattern);
+
+        List folders = new ArrayList();
+        for (int i = 0; i < groups.size(); i++) {
+            String name = getGroupName((String) groups.get(i));
+            // does this match our pattern? Add to the list
+            if (matcher.matches(name)) {
+                folders.add(getFolder(name));
+            }
+        }
+        return (Folder[]) folders.toArray(new Folder[0]);
+    }
+
+    /**
+     * Return the set of groups from the newsrc subscribed groups list that
+     * match a given filter.
+     * 
+     * @param pattern
+     *            The selection pattern.
+     * 
+     * @return The Folders for the matching groups.
+     */
+    protected Folder[] filterSubscribedGroups(String pattern) throws MessagingException {
+        Iterator groups = ((NNTPStore) store).getNewsrcGroups();
+
+        WildmatMatcher matcher = new WildmatMatcher(pattern);
+
+        List folders = new ArrayList();
+        while (groups.hasNext()) {
+            NNTPNewsrcGroup group = (NNTPNewsrcGroup) groups.next();
+            if (group.isSubscribed()) {
+                // does this match our pattern? Add to the list
+                if (matcher.matches(group.getName())) {
+                    folders.add(getFolder(group.getName()));
+                }
+            }
+        }
+        return (Folder[]) folders.toArray(new Folder[0]);
+    }
+
+    /**
+     * Utility method for extracting a name from a group list response.
+     * 
+     * @param response
+     *            The response string.
+     * 
+     * @return The group name.
+     */
+    protected String getGroupName(String response) {
+        int blank = response.indexOf(' ');
+        return response.substring(0, blank).trim();
+    }
+
+    /**
+     * Return whether this folder can hold just messages or also subfolders.
+     * Only the root folder can hold other folders, so it will need to override.
+     * 
+     * @return Always returns Folder.HOLDS_FOLDERS.
+     * @exception MessagingException
+     */
+    public int getType() throws MessagingException {
+        return HOLDS_FOLDERS;
+    }
+
+    /**
+     * Get a new folder from the root folder. This creates a new folder, which
+     * might not actually exist on the server. If the folder doesn't exist, an
+     * error will occur on folder open.
+     * 
+     * @param name
+     *            The name of the requested folder.
+     * 
+     * @return A new folder object for this folder.
+     * @exception MessagingException
+     */
+    public Folder getFolder(String name) throws MessagingException {
+        // create a new group folder and return
+        return new NNTPGroupFolder(this, (NNTPStore) store, name, ((NNTPStore) store).getNewsrcGroup(name));
+    }
+
+    /**
+     * Utility class to do Wildmat pattern matching on folder names.
+     */
+    class WildmatMatcher {
+        // middle match sections...because these are separated by wildcards, if
+        // they appear in
+        // sequence in the string, it is a match.
+        List matchSections = new ArrayList();
+
+        // just a "*" match, so everything is true
+        boolean matchAny = false;
+
+        // no wildcards, so this must be an exact match.
+        String exactMatch = null;
+
+        // a leading section which must be at the beginning
+        String firstSection = null;
+
+        // a trailing section which must be at the end of the string.
+        String lastSection = null;
+
+        /**
+         * Create a wildmat pattern matcher.
+         * 
+         * @param pattern
+         *            The wildmat pattern to apply to string matches.
+         */
+        public WildmatMatcher(String pattern) {
+            int section = 0;
+
+            // handle the easy cases first
+
+            // single wild card?
+            if (pattern.equals("*")) {
+                matchAny = true;
+                return;
+            }
+
+            // find the first wild card
+            int wildcard = pattern.indexOf('*');
+
+            // no wild card at all?
+            if (wildcard == -1) {
+                exactMatch = pattern;
+                return;
+            }
+
+            // pattern not begin with a wildcard? We need to pull off the
+            // leading section
+            if (!pattern.startsWith("*")) {
+                firstSection = pattern.substring(0, wildcard);
+                section = wildcard + 1;
+                // this could be "yada*", so we could be done.
+                if (section >= pattern.length()) {
+                    return;
+                }
+            }
+
+            // now parse off the middle sections, making sure to handle the end
+            // condition correctly.
+            while (section < pattern.length()) {
+                // find the next wildcard position
+                wildcard = pattern.indexOf('*', section);
+                if (wildcard == -1) {
+                    // not found, we're at the end of the pattern. We need to
+                    // match on the end.
+                    lastSection = pattern.substring(section);
+                    return;
+                }
+                // we could have a null section, which we'll just ignore.
+                else if (wildcard == section) {
+                    // step over the wild card
+                    section++;
+                } else {
+                    // pluck off the next section
+                    matchSections.add(pattern.substring(section, wildcard));
+                    // step over the wild card character and check if we've
+                    // reached the end.
+                    section = wildcard + 1;
+                }
+            }
+        }
+
+        /**
+         * Test if a name string matches to parsed wildmat pattern.
+         * 
+         * @param name
+         *            The name to test.
+         * 
+         * @return true if the string matches the pattern, false otherwise.
+         */
+        public boolean matches(String name) {
+
+            // handle the easy cases first
+
+            // full wildcard? Always matches
+            if (matchAny) {
+                return true;
+            }
+
+            // required exact matches are easy.
+            if (exactMatch != null) {
+                return exactMatch.equals(name);
+            }
+
+            int span = 0;
+
+            // must match the beginning?
+            if (firstSection != null) {
+                // if it doesn't start with that, it can't be true.
+                if (!name.startsWith(firstSection)) {
+                    return false;
+                }
+
+                // we do all additional matching activity from here.
+                span = firstSection.length();
+            }
+
+            // scan for each of the sections along the string
+            for (int i = 1; i < matchSections.size(); i++) {
+                // if a section is not found, this is false
+
+                String nextMatch = (String) matchSections.get(i);
+                int nextLocation = name.indexOf(nextMatch, span);
+                if (nextLocation == -1) {
+                    return false;
+                }
+                // step over that one
+                span = nextMatch.length() + nextLocation;
+            }
+
+            // we've matched everything up to this point, now check to see if
+            // need an end match
+            if (lastSection != null) {
+                // we need to have at least the number of characters of the end
+                // string left, else this fails.
+                if (name.length() - span < lastSection.length()) {
+                    return false;
+                }
+
+                // ok, make sure we end with this string
+                return name.endsWith(lastSection);
+            }
+
+            // no falsies, this must be the truth.
+            return true;
+        }
+    }
+}
diff --git a/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/nntp/NNTPStore.java b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/nntp/NNTPStore.java
new file mode 100644
index 0000000..b69bea4
--- /dev/null
+++ b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/nntp/NNTPStore.java
@@ -0,0 +1,345 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.geronimo.javamail.store.nntp;
+
+import java.io.File;
+import java.io.PrintStream;
+import java.util.Iterator;
+
+import javax.mail.Folder;
+import javax.mail.MessagingException;
+import javax.mail.Session;
+import javax.mail.Store;
+import javax.mail.URLName;
+
+import org.apache.geronimo.javamail.store.nntp.newsrc.NNTPNewsrc;
+import org.apache.geronimo.javamail.store.nntp.newsrc.NNTPNewsrcFile;
+import org.apache.geronimo.javamail.store.nntp.newsrc.NNTPNewsrcGroup;
+import org.apache.geronimo.javamail.transport.nntp.NNTPConnection;
+import org.apache.geronimo.mail.util.SessionUtil;
+
+/**
+ * NNTP implementation of javax.mail.Store POP protocol spec is implemented in
+ * org.apache.geronimo.javamail.store.pop3.NNTPConnection
+ * 
+ * @version $Rev$ $Date$
+ */
+public class NNTPStore extends Store {
+
+    protected static final String NNTP_AUTH = "auth";
+
+    protected static final String NNTP_PORT = "port";
+
+    protected static final String NNTP_NEWSRC = "newsrc";
+
+    protected static final String protocol = "nntp";
+
+    protected static final int DEFAULT_NNTP_PORT = 119;
+
+    // the active connection object.
+    protected NNTPConnection connection;
+
+    // the newsrc file where we store subscriptions and seen message markers.
+    protected NNTPNewsrc newsrc;
+
+    // the root folder
+    protected NNTPRootFolder root;
+
+    // our session provided debug output stream.
+    protected PrintStream debugStream;
+
+    /**
+     * Construct an NNTPStore item. This will load the .newsrc file associated
+     * with the server.
+     * 
+     * @param session
+     *            The owning javamail Session.
+     * @param urlName
+     *            The Store urlName, which can contain server target
+     *            information.
+     */
+    public NNTPStore(Session session, URLName urlName) {
+        super(session, urlName);
+
+        // get our debug output.
+        debugStream = session.getDebugOut();
+
+    }
+
+    /**
+     * @see javax.mail.Store#getDefaultFolder()
+     * 
+     * This returns a root folder object for all of the news groups.
+     */
+    public Folder getDefaultFolder() throws MessagingException {
+        checkConnectionStatus();
+        if (root == null) {
+            return new NNTPRootFolder(this, connection.getHost(), connection.getWelcomeString());
+        }
+        return root;
+    }
+
+    /**
+     * @see javax.mail.Store#getFolder(java.lang.String)
+     */
+    public Folder getFolder(String name) throws MessagingException {
+        return getDefaultFolder().getFolder(name);
+    }
+
+    /**
+     * 
+     * @see javax.mail.Store#getFolder(javax.mail.URLName)
+     */
+    public Folder getFolder(URLName url) throws MessagingException {
+        return getDefaultFolder().getFolder(url.getFile());
+    }
+
+    /**
+     * @see javax.mail.Service#protocolConnect(java.lang.String, int,
+     *      java.lang.String, java.lang.String)
+     */
+    protected synchronized boolean protocolConnect(String host, int port, String username, String password)
+            throws MessagingException {
+        if (debug) {
+            debugOut("Connecting to server " + host + ":" + port + " for user " + username);
+        }
+
+        // first check to see if we need to authenticate. If we need this, then
+        // we must have a username and
+        // password specified. Failing this may result in a user prompt to
+        // collect the information.
+        boolean mustAuthenticate = getBooleanProperty(NNTP_AUTH, false);
+
+        // if we need to authenticate, and we don't have both a userid and
+        // password, then we fail this
+        // immediately. The Service.connect() method will try to obtain the user
+        // information and retry the
+        // connection one time.
+        if (mustAuthenticate && (username == null || password == null)) {
+            return false;
+        }
+
+        // if the port is defaulted, then see if we have something configured in
+        // the session.
+        // if not configured, we just use the default default.
+        if (port == -1) {
+            // check for a property and fall back on the default if it's not
+            // set.
+            port = getIntProperty(NNTP_PORT, DEFAULT_NNTP_PORT);
+        }
+
+        // create socket and connect to server.
+        connection = new NNTPConnection(protocol, session, host, port, username, password, debug);
+        connection.connect();
+
+        // see if we have a newsrc file location specified
+        String newsrcFile = getProperty(NNTP_NEWSRC);
+
+        File source = null;
+
+        // not given as a property? Then look for a file in user.home
+        if (newsrcFile != null) {
+            source = new File(newsrcFile);
+        } else {
+            // ok, look for a file in the user.home directory. If possible,
+            // we'll try for a file
+            // with the hostname appended.
+            String home = SessionUtil.getProperty("user.home");
+
+            // try for a host-specific file first. If not found, use (and
+            // potentially create) a generic
+            // .newsrc file.
+            newsrcFile = ".newsrc-" + host;
+            source = new File(home, newsrcFile);
+            if (!source.exists()) {
+                source = new File(home, ".newsrc");
+            }
+        }
+
+        // now create a newsrc read and load the file.
+        newsrc = new NNTPNewsrcFile(source);
+        newsrc.load();
+
+        // we're going to return success here, but in truth, the server may end
+        // up asking for our
+        // bonafides at any time, and we'll be expected to authenticate then.
+        return true;
+    }
+
+    /**
+     * @see javax.mail.Service#close()
+     */
+    public void close() throws MessagingException {
+        // This is done to ensure proper event notification.
+        super.close();
+        // persist the newsrc file, if possible
+        newsrc.close();
+        connection.close();
+        connection = null;
+    }
+
+    private void checkConnectionStatus() throws MessagingException {
+        if (!this.isConnected()) {
+            throw new MessagingException("Not connected ");
+        }
+    }
+
+    /**
+     * Internal debug output routine.
+     * 
+     * @param value
+     *            The string value to output.
+     */
+    void debugOut(String message) {
+        debugStream.println("NNTPTransport DEBUG: " + message);
+    }
+
+    /**
+     * Internal debugging routine for reporting exceptions.
+     * 
+     * @param message
+     *            A message associated with the exception context.
+     * @param e
+     *            The received exception.
+     */
+    void debugOut(String message, Throwable e) {
+        debugOut("Received exception -> " + message);
+        debugOut("Exception message -> " + e.getMessage());
+        e.printStackTrace(debugStream);
+    }
+
+    /**
+     * Retrieve the server connection created by this store.
+     * 
+     * @return The active connection object.
+     */
+    NNTPConnection getConnection() {
+        return connection;
+    }
+
+    /**
+     * Retrieve the Session object this Store is operating under.
+     * 
+     * @return The attached Session instance.
+     */
+    Session getSession() {
+        return session;
+    }
+
+    /**
+     * Retrieve all of the groups we nave persistent store information about.
+     * 
+     * @return The set of groups contained in the newsrc file.
+     */
+    Iterator getNewsrcGroups() {
+        return newsrc.getGroups();
+    }
+
+    /**
+     * Retrieve the newsrc group information for a named group. If the file does
+     * not currently include this group, an unsubscribed group will be added to
+     * the file.
+     * 
+     * @param name
+     *            The name of the target group.
+     * 
+     * @return The NNTPNewsrcGroup item corresponding to this name.
+     */
+    NNTPNewsrcGroup getNewsrcGroup(String name) {
+        return newsrc.getGroup(name);
+    }
+
+    /**
+     * Get a property associated with this mail protocol.
+     * 
+     * @param name
+     *            The name of the property.
+     * 
+     * @return The property value (returns null if the property has not been
+     *         set).
+     */
+    String getProperty(String name) {
+        // the name we're given is the least qualified part of the name. We
+        // construct the full property name
+        // using the protocol (either "nntp" or "nntp-post").
+        String fullName = "mail." + protocol + "." + name;
+        return session.getProperty(fullName);
+    }
+
+    /**
+     * Get a property associated with this mail session. Returns the provided
+     * default if it doesn't exist.
+     * 
+     * @param name
+     *            The name of the property.
+     * @param defaultValue
+     *            The default value to return if the property doesn't exist.
+     * 
+     * @return The property value (returns defaultValue if the property has not
+     *         been set).
+     */
+    String getProperty(String name, String defaultValue) {
+        // the name we're given is the least qualified part of the name. We
+        // construct the full property name
+        // using the protocol (either "nntp" or "nntp-post").
+        String fullName = "mail." + protocol + "." + name;
+        return SessionUtil.getProperty(session, fullName, defaultValue);
+    }
+
+    /**
+     * Get a property associated with this mail session as an integer value.
+     * Returns the default value if the property doesn't exist or it doesn't
+     * have a valid int value.
+     * 
+     * @param name
+     *            The name of the property.
+     * @param defaultValue
+     *            The default value to return if the property doesn't exist.
+     * 
+     * @return The property value converted to an int.
+     */
+    int getIntProperty(String name, int defaultValue) {
+        // the name we're given is the least qualified part of the name. We
+        // construct the full property name
+        // using the protocol (either "nntp" or "nntp-post").
+        String fullName = "mail." + protocol + "." + name;
+        return SessionUtil.getIntProperty(session, fullName, defaultValue);
+    }
+
+    /**
+     * Get a property associated with this mail session as an boolean value.
+     * Returns the default value if the property doesn't exist or it doesn't
+     * have a valid int value.
+     * 
+     * @param name
+     *            The name of the property.
+     * @param defaultValue
+     *            The default value to return if the property doesn't exist.
+     * 
+     * @return The property value converted to a boolean
+     */
+    boolean getBooleanProperty(String name, boolean defaultValue) {
+        // the name we're given is the least qualified part of the name. We
+        // construct the full property name
+        // using the protocol (either "nntp" or "nntp-post").
+        String fullName = "mail." + protocol + "." + name;
+        return SessionUtil.getBooleanProperty(session, fullName, defaultValue);
+    }
+}
diff --git a/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/nntp/newsrc/NNTPNewsrc.java b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/nntp/newsrc/NNTPNewsrc.java
new file mode 100644
index 0000000..def485c
--- /dev/null
+++ b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/nntp/newsrc/NNTPNewsrc.java
@@ -0,0 +1,182 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.geronimo.javamail.store.nntp.newsrc;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.Writer;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * Base class implementation of a standard news reader news rc file. This is
+ * used to track newsgroup subscriptions and SEEN flags for articles. This is an
+ * abstract class designed for subclasses to bridge to the physical store type
+ * used for the newsgroup information.
+ */
+public abstract class NNTPNewsrc {
+
+    // the group information we've read from the news rc file.
+    Map groups = new HashMap();
+
+    // flag to let us know of we need to persist the newsrc file on close.
+    boolean dirty = false;
+
+    /**
+     * Base class constructor for NNTPNewsrc items. Subclasses provide their own
+     * domain-specific intialization.
+     */
+    protected NNTPNewsrc() {
+    }
+
+    /**
+     * Load the data from the newsrc file and parse into an instore group
+     * database.
+     */
+    public void load() {
+        BufferedReader in = null;
+
+        try {
+            in = getInputReader();
+
+            String line = in.readLine();
+
+            while (line != null) {
+                // parse the line...this returns null if it's something
+                // unrecognized.
+                NNTPNewsrcGroup group = NNTPNewsrcGroup.parse(this, line);
+                // if it parsed ok, add it to the group list, and potentially to
+                // the subscribed list.
+                if (group != null) {
+                    groups.put(group.getName(), group);
+                }
+
+                line = in.readLine();
+            }
+
+            in.close();
+        } catch (IOException e) {
+            // an IOException may mean that the file just doesn't exist, which
+            // is fine. We'll ignore and
+            // proceed with the information we have.
+        } finally {
+            if (in != null) {
+                try {
+                    in.close();
+                } catch (IOException e) {
+                    // ignore
+                }
+            }
+        }
+    }
+
+    /**
+     * Save the newsrc file data back to the original source file.
+     * 
+     * @exception IOException
+     */
+    public void save() throws IOException {
+        Writer out = getOutputWriter();
+
+        Iterator i = groups.values().iterator();
+
+        while (i.hasNext()) {
+            NNTPNewsrcGroup group = (NNTPNewsrcGroup) i.next();
+            group.save(out);
+        }
+
+        out.close();
+    }
+
+    /**
+     * Abstract open method intended for sub class initialization. The subclass
+     * is responsible for creating the BufferedReaded used to read the .newsrc
+     * file.
+     * 
+     * @return A BufferedReader for reading the .newsrc file.
+     * @exception IOException
+     */
+    abstract public BufferedReader getInputReader() throws IOException;
+
+    /**
+     * Abstract open for output method intended for subclass implementation. The
+     * subclasses are reponsible for opening the output stream and creating an
+     * appropriate Writer for saving the .newsrc file.
+     * 
+     * @return A Writer target at the .newsrc file save location.
+     * @exception IOException
+     */
+    abstract public Writer getOutputWriter() throws IOException;
+
+    /**
+     * Retrieve the newsrc group information for a named group. If the file does
+     * not currently include this group, an unsubscribed group will be added to
+     * the file.
+     * 
+     * @param name
+     *            The name of the target group.
+     * 
+     * @return The NNTPNewsrcGroup item corresponding to this name.
+     */
+    public NNTPNewsrcGroup getGroup(String name) {
+        NNTPNewsrcGroup group = (NNTPNewsrcGroup) groups.get(name);
+        // if we don't know about this, create a new one and add to the list.
+        // This
+        // will be an unsubscribed one.
+        if (group == null) {
+            group = new NNTPNewsrcGroup(this, name, null, false);
+            groups.put(name, group);
+            // we've added a group, so we need to resave
+            dirty = true;
+        }
+        return group;
+    }
+
+    /**
+     * Mark this newsrc database as dirty.
+     */
+    public void setDirty() {
+        dirty = true;
+    }
+
+    /**
+     * Close the newsrc file, persisting it back to disk if the file has
+     * changed.
+     */
+    public void close() {
+        if (dirty) {
+            try {
+                save();
+            } catch (IOException e) {
+                // ignore
+            }
+        }
+    }
+
+    /**
+     * Retrieve the current set of loaded groups.
+     * 
+     * @return An iterator for traversing the group set.
+     */
+    public Iterator getGroups() {
+        return groups.values().iterator();
+    }
+}
diff --git a/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/nntp/newsrc/NNTPNewsrcFile.java b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/nntp/newsrc/NNTPNewsrcFile.java
new file mode 100644
index 0000000..9211aca
--- /dev/null
+++ b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/nntp/newsrc/NNTPNewsrcFile.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.geronimo.javamail.store.nntp.newsrc;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+
+public class NNTPNewsrcFile extends NNTPNewsrc {
+    // source for the file data
+    File source;
+
+    /**
+     * Construct a NNTPNewsrc object that is targetted at a file-based backing
+     * store.
+     * 
+     * @param source
+     *            The source File for the .newsrc data.
+     */
+    public NNTPNewsrcFile(File source) {
+        this.source = source;
+    }
+
+    /**
+     * Retrieve an input reader for loading the newsrc file.
+     * 
+     * @return A BufferedReader object for reading from the newsrc file.
+     * @exception IOException
+     */
+    public BufferedReader getInputReader() throws IOException {
+        return new BufferedReader(new InputStreamReader(new FileInputStream(source)));
+    }
+
+    /**
+     * Obtain a writer for saving a newsrc file.
+     * 
+     * @return The output writer targetted to the newsrc file.
+     * @exception IOException
+     */
+    public Writer getOutputWriter() throws IOException {
+        // open this for overwriting
+        return new OutputStreamWriter(new FileOutputStream(source, false));
+    }
+}
diff --git a/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/nntp/newsrc/NNTPNewsrcGroup.java b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/nntp/newsrc/NNTPNewsrcGroup.java
new file mode 100644
index 0000000..b628361
--- /dev/null
+++ b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/nntp/newsrc/NNTPNewsrcGroup.java
@@ -0,0 +1,185 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.geronimo.javamail.store.nntp.newsrc;
+
+import java.io.IOException;
+import java.io.Writer;
+
+public class NNTPNewsrcGroup {
+    // the newsrc database we're part of
+    NNTPNewsrc newsrc;
+
+    // the name of the group
+    protected String name;
+
+    // the subscription flage
+    protected boolean subscribed;
+
+    // the range of already seen articles.
+    protected RangeList ranges;
+
+    /**
+     * Construct a NNTPNewsrcGroup item associated with a given .newsrc
+     * database.
+     * 
+     * @param newsrc
+     *            The owning .newsrc database.
+     * @param line
+     *            The .newsrc range entries in .newsrc format. These ranges are
+     *            parsed to create a set of seen flags.
+     * 
+     * @return A created NNTPNewsrcGroup item.
+     */
+    public static NNTPNewsrcGroup parse(NNTPNewsrc newsrc, String line) {
+        String groupName = null;
+        String ranges = null;
+
+        // subscribed lines have a ':' marker acting as a delimiter
+        int marker = line.indexOf(':');
+
+        if (marker != -1) {
+            groupName = line.substring(0, marker);
+            ranges = line.substring(marker + 1);
+            return new NNTPNewsrcGroup(newsrc, groupName, ranges, true);
+        }
+
+        // now check for an unsubscribed group
+        marker = line.indexOf('!');
+
+        if (marker != -1) {
+            groupName = line.substring(0, marker);
+            ranges = line.substring(marker + 1);
+            return new NNTPNewsrcGroup(newsrc, groupName, ranges, false);
+        }
+
+        // must be a comment line
+        return null;
+    }
+
+    /**
+     * Construct a .newsrc group item.
+     * 
+     * @param newsrc
+     *            The owning newsrc database.
+     * @param name
+     *            The group name.
+     * @param newsrcRanges
+     *            The initial set of seen ranges for the group (may be null).
+     * @param subscribed
+     *            The initial group subscription state.
+     */
+    public NNTPNewsrcGroup(NNTPNewsrc newsrc, String name, String newsrcRanges, boolean subscribed) {
+        this.newsrc = newsrc;
+        this.name = name;
+        this.subscribed = subscribed;
+        this.ranges = new RangeList(newsrcRanges);
+    }
+
+    /**
+     * Get the group name.
+     * 
+     * @return The String name of the group.
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * Get the newsrc subscribed status for an article.
+     * 
+     * @return The current subscription flag.
+     */
+    public boolean isSubscribed() {
+        return subscribed;
+    }
+
+    /**
+     * Set the subscription status for an article.
+     * 
+     * @param flag
+     *            The new subscription value.
+     */
+    public void setSubscribed(boolean flag) {
+        // we don't blindly set this to the new value since we only want to
+        // resave the newsrc file if
+        // something changes.
+        if (flag && !subscribed) {
+            subscribed = true;
+            newsrc.setDirty();
+        } else if (!flag && subscribed) {
+            subscribed = false;
+            newsrc.setDirty();
+        }
+    }
+
+    /**
+     * Test if an article has been seen yet.
+     * 
+     * @param article
+     *            The target article.
+     * 
+     * @return The seen mark for the article.
+     */
+    public boolean isArticleSeen(int article) {
+        return ranges.isMarked(article);
+    }
+
+    /**
+     * Mark an article as seen.
+     * 
+     * @param article
+     *            The target article number.
+     */
+    public void markArticleSeen(int article) {
+        ranges.setMarked(article);
+        if (ranges.isDirty()) {
+            newsrc.setDirty();
+        }
+    }
+
+    /**
+     * Mark an article as unseen.
+     * 
+     * @param article
+     *            The target article number.
+     */
+    public void markArticleUnseen(int article) {
+        ranges.setUnmarked(article);
+        if (ranges.isDirty()) {
+            newsrc.setDirty();
+        }
+    }
+
+    /**
+     * Save this group definition to a .newsrc file.
+     * 
+     * @param out
+     *            The output writer to send the information to.
+     * 
+     * @exception IOException
+     */
+    public void save(Writer out) throws IOException {
+        out.write(name);
+        out.write(subscribed ? ": " : "! ");
+        ranges.save(out);
+        // put a terminating line end
+        out.write("\r\n");
+    }
+}
diff --git a/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/nntp/newsrc/Range.java b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/nntp/newsrc/Range.java
new file mode 100644
index 0000000..90e4707
--- /dev/null
+++ b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/nntp/newsrc/Range.java
@@ -0,0 +1,318 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.geronimo.javamail.store.nntp.newsrc;
+
+import java.io.IOException;
+import java.io.Writer;
+
+/**
+ * Represent a single Range in a newsrc file. A Range can be either a single
+ * number (start == end) or a span of article numbers.
+ */
+public class Range {
+    // the low end of the range
+    int start;
+
+    // the high end of the range (start and end are inclusive);
+    int end;
+
+    /**
+     * Construct a Range item for a single digit range.
+     * 
+     * @param spot
+     *            The location of the singleton.
+     */
+    public Range(int spot) {
+        this(spot, spot);
+    }
+
+    /**
+     * Construct a Range item.
+     * 
+     * @param start
+     *            The starting point of the Range.
+     * @param end
+     *            The Range end point (which may be equal to the starting
+     *            point).
+     */
+    public Range(int start, int end) {
+        this.start = start;
+        this.end = end;
+    }
+
+    /**
+     * Parse a section of a .newsrc range string into a single Range item. The
+     * range is either a single number, or a pair of numbers separated by a
+     * hyphen.
+     * 
+     * @param range
+     *            The range string.
+     * 
+     * @return A constructed Range item, or null if there is a parsing error.
+     */
+    static public Range parse(String range) {
+        // a range from a newsrc file is either a single number or in the format
+        // 'nnnn-mmmm'. We need
+        // to figure out which type this is.
+        int marker = range.indexOf('-');
+
+        try {
+            if (marker != -1) {
+                String rangeStart = range.substring(0, marker).trim();
+                String rangeEnd = range.substring(marker + 1).trim();
+
+                int start = Integer.parseInt(rangeStart);
+                int end = Integer.parseInt(rangeEnd);
+
+                if (start >= 0 && end >= 0) {
+                    return new Range(start, end);
+                }
+            } else {
+                // use the entire token
+                int start = Integer.parseInt(range);
+                // and start and the end are the same
+                return new Range(start, start);
+
+            }
+        } catch (NumberFormatException e) {
+        }
+        // return null for any bad values
+        return null;
+    }
+
+    /**
+     * Get the starting point for the Range.
+     * 
+     * @return The beginning of the mark range.
+     */
+    public int getStart() {
+        return start;
+    }
+
+    /**
+     * Set the starting point for a Range.
+     * 
+     * @param start
+     *            The new start value.
+     */
+    public void setStart(int start) {
+        this.start = start;
+    }
+
+    /**
+     * Get the ending point for the Range.
+     * 
+     * @return The end of the mark range.
+     */
+    public int getEnd() {
+        return end;
+    }
+
+    /**
+     * Set the ending point for a Range.
+     * 
+     * @param end
+     *            The new end value.
+     */
+    public void setEnd(int end) {
+        this.end = end;
+    }
+
+    /**
+     * Test if a range contains a point value.
+     * 
+     * @param target
+     *            The article location to test.
+     * 
+     * @return True if the target is between the start and end values,
+     *         inclusive.
+     */
+    public boolean contains(int target) {
+        return target >= start && target <= end;
+    }
+
+    /**
+     * Test if one range is completely contained within another Range.
+     * 
+     * @param other
+     *            The other test range.
+     * 
+     * @return true if the other start and end points are contained within this
+     *         range.
+     */
+    public boolean contains(Range other) {
+        return contains(other.getStart()) && contains(other.getEnd());
+    }
+
+    /**
+     * Tests if two ranges overlap
+     * 
+     * @param other
+     *            The other test range.
+     * 
+     * @return true if the start or end points of either range are contained
+     *         within the range of the other.
+     */
+    public boolean overlaps(Range other) {
+        return other.contains(start) || other.contains(end) || contains(other.getStart()) || contains(other.getEnd());
+    }
+
+    /**
+     * Test if two ranges exactly abutt each other.
+     * 
+     * @param other
+     *            The other Range to test.
+     * 
+     * @return true if the end of one range abutts the start of the other range.
+     */
+    public boolean abutts(Range other) {
+        return other.getStart() == end + 1 || other.getEnd() == start - 1;
+    }
+
+    /**
+     * Tests if a single point abutts either the start or end of this Range.
+     * 
+     * @param article
+     *            The point to test.
+     * 
+     * @return true if test point is equal to start - 1 or end + 1.
+     */
+    public boolean abutts(int article) {
+        return article == start - 1 || article == end + 1;
+    }
+
+    /**
+     * Test if a point is below the test Range.
+     * 
+     * @param article
+     *            The point to test.
+     * 
+     * @return true if the entire range is less than the test point.
+     */
+    public boolean lessThan(int article) {
+        return end < article;
+    }
+
+    /**
+     * Test if another Range is less than this Range.
+     * 
+     * @param other
+     *            The other Range to test.
+     * 
+     * @return true if the other Range lies completely below this Range.
+     */
+    public boolean lessThan(Range other) {
+        return end < other.start;
+    }
+
+    /**
+     * Test if a point is above the test Range.
+     * 
+     * @param article
+     *            The point to test.
+     * 
+     * @return true if the entire range is greater than the test point.
+     */
+    public boolean greaterThan(int article) {
+        return start > article;
+    }
+
+    /**
+     * Test if another Range is greater than this Range.
+     * 
+     * @param other
+     *            The other Range to test.
+     * 
+     * @return true if the other Range lies completely below this Range.
+     */
+    public boolean greaterThan(Range other) {
+        return start > other.end;
+    }
+
+    /**
+     * Merge another Range into this one. Merging will increase the bounds of
+     * this Range to encompass the entire span of the two. If the Ranges do not
+     * overlap, the newly created range will include the gap between the two.
+     * 
+     * @param other
+     *            The Range to merge.
+     */
+    public void merge(Range other) {
+        if (other.start < start) {
+            start = other.start;
+        }
+
+        if (other.end > end) {
+            end = other.end;
+        }
+    }
+
+    /**
+     * Split a range at a given split point. Splitting will truncate at the
+     * split location - 1 and return a new range beginning at location + 1; This
+     * code assumes that the split location is at neither end poing.
+     * 
+     * @param location
+     *            The split location. Location must be in the range start <
+     *            location < end.
+     * 
+     * @return A new Range object for the split portion of the range.
+     */
+    public Range split(int location) {
+        int newEnd = end;
+
+        end = location - 1;
+
+        return new Range(location + 1, newEnd);
+    }
+
+    /**
+     * Save an individual range element to a newsrc file. The range is expressed
+     * either as a single number, or a hypenated pair of numbers.
+     * 
+     * @param out
+     *            The output writer used to save the data.
+     * 
+     * @exception IOException
+     */
+    public void save(Writer out) throws IOException {
+        // do we have a single data point range?
+        if (start == end) {
+            out.write(Integer.toString(start));
+        } else {
+            out.write(Integer.toString(start));
+            out.write("-");
+            out.write(Integer.toString(end));
+        }
+    }
+
+    /**
+     * Convert a Range into String form. Used mostly for debugging.
+     * 
+     * @return The String representation of the Range.
+     */
+    public String toString() {
+        if (start == end) {
+            return Integer.toString(start);
+        } else {
+            return Integer.toString(start) + "-" + Integer.toString(end);
+        }
+    }
+}
diff --git a/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/nntp/newsrc/RangeList.java b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/nntp/newsrc/RangeList.java
new file mode 100644
index 0000000..845f54a
--- /dev/null
+++ b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/nntp/newsrc/RangeList.java
@@ -0,0 +1,227 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.geronimo.javamail.store.nntp.newsrc;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.StringTokenizer;
+
+/**
+ * Manage a list of ranges values from a newsrc file.
+ */
+public class RangeList {
+    boolean dirty = false;
+
+    ArrayList ranges = new ArrayList();
+
+    /**
+     * Create a RangeList instance from a newsrc range line. Values are saved as
+     * a comma separated set of range values. A range value is either a single
+     * number or a hypenated pair of numbers.
+     * 
+     * @param line
+     *            The newsrc range line.
+     */
+    public RangeList(String line) {
+
+        // we might be creating an first time list, so nothing to parse.
+        if (line != null) {
+            // ranges are comma delimited tokens
+            StringTokenizer tokenizer = new StringTokenizer(line, ",");
+
+            while (tokenizer.hasMoreTokens()) {
+                String rangeString = (String) tokenizer.nextToken();
+                rangeString = rangeString.trim();
+                if (rangeString.length() != 0) {
+                    Range range = Range.parse(rangeString);
+                    if (range != null) {
+                        insert(range);
+                    }
+                }
+            }
+        }
+        // make sure we start out in a clean state. Any changes from this point
+        // will flip on the dirty flat.
+        dirty = false;
+    }
+
+    /**
+     * Insert a range item into our list. If possible, the inserted range will
+     * be merged with existing ranges.
+     * 
+     * @param newRange
+     *            The new range item.
+     */
+    public void insert(Range newRange) {
+        // first find the insertion point
+        for (int i = 0; i < ranges.size(); i++) {
+            Range range = (Range) ranges.get(i);
+            // does an existing range fully contain the new range, we don't need
+            // to insert anything.
+            if (range.contains(newRange)) {
+                return;
+            }
+
+            // does the current one abutt or overlap with the inserted range?
+            if (range.abutts(newRange) || range.overlaps(newRange)) {
+                // rats, we have an overlap...and it is possible that we could
+                // overlap with
+                // the next range after this one too. Therefore, we merge these
+                // two ranges together,
+                // remove the place where we're at, and then recursively insert
+                // the larger range into
+                // the list.
+                dirty = true;
+                newRange.merge(range);
+                ranges.remove(i);
+                insert(newRange);
+                return;
+            }
+
+            // ok, we don't touch the current one at all. If it is completely
+            // above
+            // range we're adding, we can just poke this into the list here.
+            if (newRange.lessThan(range)) {
+                dirty = true;
+                ranges.add(i, newRange);
+                return;
+            }
+        }
+        dirty = true;
+        // this is easy (and fairly common)...we just tack this on to the end.
+        ranges.add(newRange);
+    }
+
+    /**
+     * Test if a given article point falls within one of the contained Ranges.
+     * 
+     * @param article
+     *            The test point.
+     * 
+     * @return true if this falls within one of our current mark Ranges, false
+     *         otherwise.
+     */
+    public boolean isMarked(int article) {
+        for (int i = 0; i < ranges.size(); i++) {
+            Range range = (Range) ranges.get(i);
+            if (range.contains(article)) {
+                return true;
+            }
+            // we've passed the point where a match is possible.
+            if (range.greaterThan(article)) {
+                return false;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Mark a target article as having been seen.
+     * 
+     * @param article
+     *            The target article number.
+     */
+    public void setMarked(int article) {
+        // go through the insertion logic.
+        insert(new Range(article, article));
+    }
+
+    /**
+     * Clear the seen mark for a target article.
+     * 
+     * @param article
+     *            The target article number.
+     */
+    public void setUnmarked(int article) {
+        for (int i = 0; i < ranges.size(); i++) {
+            Range range = (Range) ranges.get(i);
+            // does this fall within an existing range? We don't need to do
+            // anything here.
+            if (range.contains(article)) {
+                // ok, we've found where to insert, now to figure out how to
+                // insert
+                // article is at the beginning of the range. We can just
+                // increment the lower
+                // bound, or if this is a single element range, we can remove it
+                // entirely.
+                if (range.getStart() == article) {
+                    if (range.getEnd() == article) {
+                        // piece of cake!
+                        ranges.remove(i);
+                    } else {
+                        // still pretty easy.
+                        range.setStart(article + 1);
+                    }
+                } else if (range.getEnd() == article) {
+                    // pretty easy also
+                    range.setEnd(article - 1);
+                } else {
+                    // split this into two ranges and insert the trailing piece
+                    // after this.
+                    Range section = range.split(article);
+                    ranges.add(i + 1, section);
+                }
+                dirty = true;
+                return;
+            }
+            // did we find a point where any articles are greater?
+            if (range.greaterThan(article)) {
+                // nothing to do
+                return;
+            }
+        }
+        // didn't find it at all. That was easy!
+    }
+
+    /**
+     * Save this List of Ranges out to a .newsrc file. This creates a
+     * comma-separated list of range values from each of the Ranges.
+     * 
+     * @param out
+     *            The target output stream.
+     * 
+     * @exception IOException
+     */
+    public void save(Writer out) throws IOException {
+        // we have an empty list
+        if (ranges.size() == 0) {
+            return;
+        }
+
+        Range range = (Range) ranges.get(0);
+        range.save(out);
+
+        for (int i = 1; i < ranges.size(); i++) {
+            out.write(",");
+            range = (Range) ranges.get(i);
+            range.save(out);
+        }
+    }
+
+    /**
+     * Return the state of the dirty flag.
+     * 
+     * @return True if the range list information has changed, false otherwise.
+     */
+    public boolean isDirty() {
+        return dirty;
+    }
+}
diff --git a/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/POP3Command.java b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/POP3Command.java
new file mode 100644
index 0000000..b161994
--- /dev/null
+++ b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/POP3Command.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.geronimo.javamail.store.pop3;
+
+/**
+ * An abstraction for POP3Commands
+ * 
+ * @see org.apache.geronimo.javamail.store.pop3.POP3CommandFactory
+ * 
+ * @version $Rev$ $Date$
+ */
+public interface POP3Command {
+
+    /**
+     * This method will get the POP3 command in string format according o
+     * rfc1939
+     */
+    public String getCommand();
+
+    /**
+     * Indicates wether this command expects a multiline response or not
+     * 
+     */
+    public boolean isMultiLineResponse();
+}
diff --git a/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/POP3CommandFactory.java b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/POP3CommandFactory.java
new file mode 100644
index 0000000..a66f97f
--- /dev/null
+++ b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/POP3CommandFactory.java
@@ -0,0 +1,235 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.geronimo.javamail.store.pop3;
+
+/**
+ * Provides concrete implementations of
+ * org.apache.geronimo.javamail.store.pop3.POP3Command objects representing the
+ * POP3 commands defined in rfc 1939
+ * 
+ * @link http://www.faqs.org/rfcs/rfc1939.html
+ * @version $Rev$ $Date$
+ */
+public final class POP3CommandFactory implements POP3Constants {
+
+    public static POP3Command getCOMMAND_USER(final String user) {
+        return new POP3Command() {
+            public String getCommand() {
+                return "USER" + SPACE + user + CRLF;
+            }
+
+            public boolean isMultiLineResponse() {
+                return false;
+            }
+        };
+    }
+
+    public static POP3Command getCOMMAND_PASS(final String passwd) {
+        return new POP3Command() {
+            public String getCommand() {
+                return "PASS" + SPACE + passwd + CRLF;
+            }
+
+            public boolean isMultiLineResponse() {
+                return false;
+            }
+        };
+    }
+
+    public static POP3Command getCOMMAND_QUIT() {
+        return new POP3Command() {
+            public String getCommand() {
+                return "QUIT" + CRLF;
+            }
+
+            public boolean isMultiLineResponse() {
+                return false;
+            }
+        };
+    }
+
+    public static POP3Command getCOMMAND_NOOP() {
+        return new POP3Command() {
+            public String getCommand() {
+                return "NOOP" + CRLF;
+            }
+
+            public boolean isMultiLineResponse() {
+                return false;
+            }
+        };
+    }
+
+    public static POP3Command getCOMMAND_STAT() {
+        return new POP3Command() {
+            public String getCommand() {
+                return "STAT" + CRLF;
+            }
+
+            public boolean isMultiLineResponse() {
+                return false;
+            }
+        };
+    }
+
+    public static POP3Command getCOMMAND_LIST() {
+        return getCOMMAND_LIST(-1);
+    }
+
+    public static POP3Command getCOMMAND_LIST(final int msgNo) {
+        return new POP3Command() {
+            public String getCommand() {
+                if (msgNo > 0) {
+                    return "LIST" + SPACE + msgNo + CRLF;
+                } else {
+                    return "LIST" + CRLF;
+                }
+            }
+
+            /**
+             * If a msg num is specified then the the message details will be on
+             * the first line for ex. +OK 3 4520
+             * 
+             * if no msgnum is specified then all the msg details are return in
+             * a multiline format for ex. +OK 2 messages 1 456 2 46456 ..... n
+             * 366
+             */
+            public boolean isMultiLineResponse() {
+                return (msgNo < 0);
+            }
+        };
+    }
+
+    public static POP3Command getCOMMAND_RETR(final int msgNo) {
+        return new POP3Command() {
+            public String getCommand() {
+                return "RETR" + SPACE + msgNo + CRLF;
+            }
+
+            public boolean isMultiLineResponse() {
+                return true;
+            }
+        };
+    }
+
+    public static POP3Command getCOMMAND_DELE(final int msgNo) {
+        return new POP3Command() {
+            public String getCommand() {
+                return "DELE" + SPACE + msgNo + CRLF;
+            }
+
+            public boolean isMultiLineResponse() {
+                return false;
+            }
+        };
+    }
+
+    public static POP3Command getCOMMAND_REST(final int msgNo) {
+        return new POP3Command() {
+            public String getCommand() {
+                return "REST" + SPACE + msgNo + CRLF;
+            }
+
+            public boolean isMultiLineResponse() {
+                return false;
+            }
+        };
+    }
+
+    public static POP3Command getCOMMAND_TOP(final int msgNo, final int numLines) {
+        return new POP3Command() {
+            public String getCommand() {
+                return "TOP" + SPACE + msgNo + SPACE + numLines + CRLF;
+            }
+
+            public boolean isMultiLineResponse() {
+                return true;
+            }
+        };
+    }
+
+    public static POP3Command getCOMMAND_UIDL() {
+        return getCOMMAND_UIDL(-1);
+    }
+
+    public static POP3Command getCOMMAND_UIDL(final int msgNo) {
+        return new POP3Command() {
+            public String getCommand() {
+                if (msgNo > 0) {
+                    return "UIDL" + SPACE + msgNo + CRLF;
+                } else {
+                    return "UIDL" + CRLF;
+                }
+            }
+
+            public boolean isMultiLineResponse() {
+                return true;
+            }
+        };
+    }
+    
+    public static POP3Command getCOMMAND_CAPA() {
+    	return new POP3Command() {
+    		public String getCommand() {
+                return "CAPA" + CRLF;
+    		}
+    		
+    		public boolean isMultiLineResponse() {
+    			return true;
+    		}
+    	};
+    }
+
+    public static POP3Command getCOMMAND_AUTH(final String protocol) {
+    	return new POP3Command() {
+    		public String getCommand() {
+    			return "AUTH " + protocol + CRLF;
+    		}
+    		
+    		public boolean isMultiLineResponse() {
+    			return false;
+    		}
+    	};
+    }
+    
+    public static POP3Command getCOMMAND_AUTH(final String protocol, final String initialResponse) {
+    	return new POP3Command() {
+    		public String getCommand() {
+    			return "AUTH " + protocol + " " + initialResponse + CRLF;
+    		}
+    		
+    		public boolean isMultiLineResponse() {
+    			return false;
+    		}
+    	};
+    }
+    
+    public static POP3Command getCOMMAND_ChallengeReply(final String command) {
+    	return new POP3Command() {
+    			public String getCommand() {
+   						return command + CRLF;
+   				}
+    			
+    			public boolean isMultiLineResponse() {
+    				return false;
+    			}	
+    		};
+    }
+}
diff --git a/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/POP3Connection.java b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/POP3Connection.java
new file mode 100644
index 0000000..e5ccd4f
--- /dev/null
+++ b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/POP3Connection.java
@@ -0,0 +1,564 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.geronimo.javamail.store.pop3;
+
+import java.io.BufferedOutputStream;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.InetAddress;
+import java.net.Socket;
+
+import javax.mail.MessagingException;
+import javax.mail.Session;
+
+/**
+ * Represents a connection with the POP3 mail server. The connection is owned by
+ * a pop3 store and is only associated with one user who owns the respective
+ * POP3Store instance
+ * 
+ * @version $Rev$ $Date$
+ */
+
+
+public class POP3Connection {
+
+	protected static final String MAIL_SSLFACTORY_CLASS = "mail.SSLSocketFactory.class";
+	
+    protected static final String MAIL_POP3_FACTORY_CLASS = "socketFactory.class";
+
+    protected static final String MAIL_POP3_FACTORY_FALLBACK = "socketFactory.fallback";
+
+    protected static final String MAIL_POP3_FACTORY_PORT = "socketFactory.port";
+    
+    protected static final String MAIL_POP3_LOCALADDRESS = "localAddress";
+    
+    protected static final String MAIL_POP3_LOCALPORT = "localPort";
+    
+    protected static final String MAIL_POP3_TIMEOUT = "timeout";
+	
+    private Socket socket;
+
+    private Session session;
+
+    private String host;
+
+    private int port;
+
+    private PrintWriter writer;
+
+    private BufferedReader reader;
+    
+    private String protocol;
+    
+    private boolean sslConnection;
+
+    POP3Connection(Session session, String host, int port, boolean sslConnection, String protocol) {
+
+        this.session = session;
+        this.host = host;
+        this.port = port;
+        this.sslConnection = sslConnection;
+        this.protocol = protocol;
+    }
+
+    public void open() throws Exception {
+        try {
+
+        	if (!sslConnection) {
+        		getConnectedSocket();
+        	} else {
+        		getConnectedSSLSocket();
+        	}
+
+            if (session.getDebug()) {
+                session.getDebugOut().println("Connection successful " + this.toString());
+            }
+
+            buildInputReader();
+            buildOutputWriter();
+
+            // consume the greeting
+            if (session.getDebug()) {
+                session.getDebugOut().println("Greeting from server " + reader.readLine());
+            } else {
+                reader.readLine();
+            }
+
+        } catch (IOException e) {
+            Exception ex = new Exception("Error opening connection " + this.toString(), e);
+            throw ex;
+        }
+    }
+
+    void close() throws Exception {
+        try {
+            socket.close();
+            if (session.getDebug()) {
+                session.getDebugOut().println("Connection successfuly closed " + this.toString());
+            }
+
+        } catch (IOException e) {
+            Exception ex = new Exception("Error closing connection " + this.toString(), e);
+            throw ex;
+        }
+
+    }
+
+    public synchronized POP3Response sendCommand(POP3Command cmd) throws MessagingException {
+        if (socket.isConnected()) {
+
+            // if the underlying output stream is down
+            // attempt to rebuild the writer
+            if (socket.isOutputShutdown()) {
+                buildOutputWriter();
+            }
+
+            // if the underlying inout stream is down
+            // attempt to rebuild the reader
+            if (socket.isInputShutdown()) {
+                buildInputReader();
+            }
+
+            if (session.getDebug()) {
+                session.getDebugOut().println("\nCommand sent " + cmd.getCommand());
+            }
+
+            POP3Response res = null;
+
+            // this method supresses IOException
+            // but choose bcos of ease of use
+            {
+                writer.write(cmd.getCommand());
+                writer.flush();
+                res = POP3ResponseBuilder.buildResponse(session, reader, cmd.isMultiLineResponse());
+            }
+
+            return res;
+        }
+
+        throw new MessagingException("Connection to Mail Server is lost, connection " + this.toString());
+    }
+
+    private void buildInputReader() throws MessagingException {
+        try {
+            reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
+        } catch (IOException e) {
+            throw new MessagingException("Error obtaining input stream " + this.toString(), e);
+        }
+    }
+
+    private void buildOutputWriter() throws MessagingException {
+        try {
+            writer = new PrintWriter(new BufferedOutputStream(socket.getOutputStream()));
+        } catch (IOException e) {
+            throw new MessagingException("Error obtaining output stream " + this.toString(), e);
+        }
+    }
+
+    public String toString() {
+        return "POP3Connection host: " + host + " port: " + port;
+    }
+
+
+	/**
+	 * Creates a connected socket
+	 *
+	 * @exception MessagingException
+	 */
+	protected void getConnectedSocket() throws IOException {
+	
+	    // the socket factory can be specified via a session property. By
+	    // default, we just directly
+	    // instantiate a socket without using a factor.
+	    String socketFactory = getProtocolProperty(MAIL_POP3_FACTORY_CLASS);
+	
+	    // there are several protocol properties that can be set to tune the
+	    // created socket. We need to
+	    // retrieve those bits before creating the socket.
+	    int timeout = getIntProtocolProperty(MAIL_POP3_TIMEOUT, -1);
+	    InetAddress localAddress = null;
+	    // see if we have a local address override.
+	    String localAddrProp = getProtocolProperty(MAIL_POP3_LOCALADDRESS);
+	    if (localAddrProp != null) {
+	        localAddress = InetAddress.getByName(localAddrProp);
+	    }
+	
+	    // check for a local port...default is to allow socket to choose.
+	    int localPort = getIntProtocolProperty(MAIL_POP3_LOCALPORT, 0);
+	
+	    socket = null;
+	
+	    // if there is no socket factory defined (normal), we just create a
+	    // socket directly.
+	    if (socketFactory == null) {
+	        socket = new Socket(host, port, localAddress, localPort);
+	    }
+	
+	    else {
+	        try {
+	            int socketFactoryPort = getIntProtocolProperty(MAIL_POP3_FACTORY_PORT, -1);
+	
+	            // we choose the port used by the socket based on overrides.
+	            Integer portArg = new Integer(socketFactoryPort == -1 ? port : socketFactoryPort);
+	
+	            // use the current context loader to resolve this.
+	            ClassLoader loader = Thread.currentThread().getContextClassLoader();
+	            Class factoryClass = loader.loadClass(socketFactory);
+	
+	            // done indirectly, we need to invoke the method using
+	            // reflection.
+	            // This retrieves a factory instance.
+	            Method getDefault = factoryClass.getMethod("getDefault", new Class[0]);
+	            Object defFactory = getDefault.invoke(new Object(), new Object[0]);
+	
+	            // now that we have the factory, there are two different
+	            // createSocket() calls we use,
+	            // depending on whether we have a localAddress override.
+	
+	            if (localAddress != null) {
+	                // retrieve the createSocket(String, int, InetAddress, int)
+	                // method.
+	                Class[] createSocketSig = new Class[] { String.class, Integer.TYPE, InetAddress.class, Integer.TYPE };
+	                Method createSocket = factoryClass.getMethod("createSocket", createSocketSig);
+	
+	                Object[] createSocketArgs = new Object[] { host, portArg, localAddress, new Integer(localPort) };
+	                socket = (Socket) createSocket.invoke(defFactory, createSocketArgs);
+	            } else {
+	                // retrieve the createSocket(String, int) method.
+	                Class[] createSocketSig = new Class[] { String.class, Integer.TYPE };
+	                Method createSocket = factoryClass.getMethod("createSocket", createSocketSig);
+	
+	                Object[] createSocketArgs = new Object[] { host, portArg };
+	                socket = (Socket) createSocket.invoke(defFactory, createSocketArgs);
+	            }
+	        } catch (Throwable e) {
+	            // if a socket factory is specified, then we may need to fall
+	            // back to a default. This behavior
+	            // is controlled by (surprise) more session properties.
+	            if (isProtocolPropertyTrue(MAIL_POP3_FACTORY_FALLBACK)) {
+	                socket = new Socket(host, port, localAddress, localPort);
+	            }
+	            // we have an exception. We're going to throw an IOException,
+	            // which may require unwrapping
+	            // or rewrapping the exception.
+	            else {
+	                // we have an exception from the reflection, so unwrap the
+	                // base exception
+	                if (e instanceof InvocationTargetException) {
+	                    e = ((InvocationTargetException) e).getTargetException();
+	                }
+
+	
+	                // throw this as an IOException, with the original exception
+	                // attached.
+	                IOException ioe = new IOException("Error connecting to " + host + ", " + port);
+	                ioe.initCause(e);
+	                throw ioe;
+	            }
+	        }
+	    }
+	
+	    if (timeout >= 0) {
+	        socket.setSoTimeout(timeout);
+	    }
+	}
+
+	/**
+	 * Creates a connected SSL socket for an initial SSL connection.
+	 *
+	 * @exception MessagingException
+	 */
+	protected void getConnectedSSLSocket() throws IOException {
+
+	    if (session.getDebug()) {
+	        session.getDebugOut().println("Attempting SSL socket connection to server " + host + ":" + port);
+	    }
+	    // the socket factory can be specified via a protocol property, a
+	    // session property, and if all else
+	    // fails (which it usually does), we fall back to the standard factory
+	    // class.
+	    String socketFactory = getProtocolProperty(MAIL_POP3_FACTORY_CLASS, getSessionProperty(MAIL_SSLFACTORY_CLASS,
+	            "javax.net.ssl.SSLSocketFactory"));
+	
+	    // there are several protocol properties that can be set to tune the
+	    // created socket. We need to
+	    // retrieve those bits before creating the socket.
+	    int timeout = getIntProtocolProperty(MAIL_POP3_TIMEOUT, -1);
+	    InetAddress localAddress = null;
+	    // see if we have a local address override.
+	    String localAddrProp = getProtocolProperty(MAIL_POP3_LOCALADDRESS);
+	    if (localAddrProp != null) {
+	        localAddress = InetAddress.getByName(localAddrProp);
+	    }
+	
+	    // check for a local port...default is to allow socket to choose.
+	    int localPort = getIntProtocolProperty(MAIL_POP3_LOCALPORT, 0);
+	
+	    socket = null;
+	
+	    // if there is no socket factory defined (normal), we just create a
+	    // socket directly.
+	    if (socketFactory == null) {
+	    	System.out.println("SocketFactory was null so creating the connection using a default");
+	        socket = new Socket(host, port, localAddress, localPort);
+	    }
+	
+	    else {
+	        // we'll try this with potentially two different factories if we're
+	        // allowed to fall back.
+	        boolean fallback = isProtocolPropertyTrue(MAIL_POP3_FACTORY_FALLBACK);
+	        while(true) {
+	            try {
+	            	
+	            	
+	            	if (socket != null) {
+	            		if (socket.isConnected())
+	            		break;
+	            	}
+	            	
+	                if (session.getDebug()) {
+	                    session.getDebugOut().println("Creating SSL socket using factory " + socketFactory);
+	                }
+	                
+	                int socketFactoryPort = getIntProtocolProperty(MAIL_POP3_FACTORY_PORT, -1);
+	
+	                // we choose the port used by the socket based on overrides.
+	                Integer portArg = new Integer(socketFactoryPort == -1 ? port : socketFactoryPort);
+	
+	                // use the current context loader to resolve this.
+	                ClassLoader loader = Thread.currentThread().getContextClassLoader();
+	                Class factoryClass = loader.loadClass(socketFactory);
+	
+	                // done indirectly, we need to invoke the method using
+	                // reflection.
+	                // This retrieves a factory instance.
+	                Method getDefault = factoryClass.getMethod("getDefault", new Class[0]);
+	                Object defFactory = getDefault.invoke(new Object(), new Object[0]);
+	
+	                // now that we have the factory, there are two different
+	                // createSocket() calls we use,
+	                // depending on whether we have a localAddress override.
+	
+	                if (localAddress != null) {
+	                    // retrieve the createSocket(String, int, InetAddress,
+	                    // int) method.
+	                    Class[] createSocketSig = new Class[] { String.class, Integer.TYPE, InetAddress.class,
+	                            Integer.TYPE };
+	                    Method createSocket = factoryClass.getMethod("createSocket", createSocketSig);
+	
+	                    Object[] createSocketArgs = new Object[] { host, portArg, localAddress, new Integer(localPort) };
+	                    socket = (Socket) createSocket.invoke(defFactory, createSocketArgs);
+	                } else {
+	                    // retrieve the createSocket(String, int) method.
+	                    Class[] createSocketSig = new Class[] { String.class, Integer.TYPE };
+	                    Method createSocket = factoryClass.getMethod("createSocket", createSocketSig);
+	
+	                    Object[] createSocketArgs = new Object[] { host, portArg };
+	                    socket = (Socket) createSocket.invoke(defFactory, createSocketArgs);
+	                }
+	            } catch (Throwable e) {
+	                // if we're allowed to fallback, then use the default
+	                // factory and try this again. We only
+	                // allow this to happen once.
+                    if (session.getDebug()) {
+                        session.getDebugOut().println("First attempt at creating SSL socket failed, falling back to default factory");
+                    }
+	                if (fallback) {
+	                    socketFactory = "javax.net.ssl.SSLSocketFactory";
+	                    fallback = false;
+	                    continue;
+	                }
+	                // we have an exception. We're going to throw an
+	                // IOException, which may require unwrapping
+	                // or rewrapping the exception.
+	                else {
+	                    // we have an exception from the reflection, so unwrap
+	                    // the base exception
+	                    if (e instanceof InvocationTargetException) {
+	                        e = ((InvocationTargetException) e).getTargetException();
+	                    }
+	
+	                    if (session.getDebug()) {
+	                        session.getDebugOut().println("Failure creating SSL socket: " + e);
+	                    }
+	                    // throw this as an IOException, with the original
+	                    // exception attached.
+	                    IOException ioe = new IOException("Error connecting to " + host + ", " + port);
+	                    ioe.initCause(e);
+	                    throw ioe;
+	                }
+	            }
+	        }
+	    }
+	
+	    if (timeout >= 0) {
+	        socket.setSoTimeout(timeout);
+	    }
+	}
+	
+    /**
+     * Process a session property as a boolean value, returning either true or
+     * false.
+     *
+     * @return True if the property value is "true". Returns false for any other
+     *         value (including null).
+     */
+    protected boolean isProtocolPropertyTrue(String name) {
+        // the name we're given is the least qualified part of the name. We
+        // construct the full property name
+        // using the protocol ("pop3").
+        String fullName = "mail." + protocol + "." + name;
+        return isSessionPropertyTrue(fullName);
+    }
+
+    /**
+     * Process a session property as a boolean value, returning either true or
+     * false.
+     *
+     * @return True if the property value is "true". Returns false for any other
+     *         value (including null).
+     */
+    protected boolean isSessionPropertyTrue(String name) {
+        String property = session.getProperty(name);
+        if (property != null) {
+            return property.equals("true");
+        }
+        return false;
+    }
+    
+    /**
+     * Get a property associated with this mail session as an integer value.
+     * Returns the default value if the property doesn't exist or it doesn't
+     * have a valid int value.
+     *
+     * @param name
+     *            The name of the property.
+     * @param defaultValue
+     *            The default value to return if the property doesn't exist.
+     *
+     * @return The property value converted to an int.
+     */
+    protected int getIntProtocolProperty(String name, int defaultValue) {
+        // the name we're given is the least qualified part of the name. We
+        // construct the full property name
+        // using the protocol (pop3).
+        String fullName = "mail." + protocol + "." + name;
+        return getIntSessionProperty(fullName, defaultValue);
+    }
+    
+    /**
+     * Get a property associated with this mail session as an integer value.
+     * Returns the default value if the property doesn't exist or it doesn't
+     * have a valid int value.
+     *
+     * @param name
+     *            The name of the property.
+     * @param defaultValue
+     *            The default value to return if the property doesn't exist.
+     *
+     * @return The property value converted to an int.
+     */
+    protected int getIntSessionProperty(String name, int defaultValue) {
+        String result = getSessionProperty(name);
+        if (result != null) {
+            try {
+                // convert into an int value.
+                return Integer.parseInt(result);
+            } catch (NumberFormatException e) {
+            }
+        }
+        // return default value if it doesn't exist is isn't convertable.
+        return defaultValue;
+    }
+
+    /**
+     * Get a property associated with this mail session. Returns the provided
+     * default if it doesn't exist.
+     *
+     * @param name
+     *            The name of the property.
+     * @param defaultValue
+     *            The default value to return if the property doesn't exist.
+     *
+     * @return The property value (returns defaultValue if the property has not
+     *         been set).
+     */
+    protected String getSessionProperty(String name, String defaultValue) {
+        String result = session.getProperty(name);
+        if (result == null) {
+            return defaultValue;
+        }
+        return result;
+    }
+
+    /**
+     * Get a property associated with this mail session. Returns the provided
+     * default if it doesn't exist.
+     *
+     * @param name
+     *            The name of the property.
+     * @param defaultValue
+     *            The default value to return if the property doesn't exist.
+     *
+     * @return The property value (returns defaultValue if the property has not
+     *         been set).
+     */
+    protected String getProtocolProperty(String name, String defaultValue) {
+        // the name we're given is the least qualified part of the name. We
+        // construct the full property name
+        // using the protocol ("pop3").
+        String fullName = "mail." + protocol + "." + name;
+        return getSessionProperty(fullName, defaultValue);
+    }
+    
+    /**
+     * Get a property associated with this mail protocol.
+     *
+     * @param name
+     *            The name of the property.
+     *
+     * @return The property value (returns null if the property has not been
+     *         set).
+     */
+    protected String getProtocolProperty(String name) {
+        // the name we're given is the least qualified part of the name. We
+        // construct the full property name
+        // using the protocol ("pop3").
+        String fullName = "mail." + protocol + "." + name;
+        return getSessionProperty(fullName);
+    }
+
+    /**
+     * Get a property associated with this mail session.
+     *
+     * @param name
+     *            The name of the property.
+     *
+     * @return The property value (returns null if the property has not been
+     *         set).
+     */
+    protected String getSessionProperty(String name) {
+        return session.getProperty(name);
+    }
+}
diff --git a/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/POP3Constants.java b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/POP3Constants.java
new file mode 100644
index 0000000..57aed7e
--- /dev/null
+++ b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/POP3Constants.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.geronimo.javamail.store.pop3;
+
+/**
+ * Defines a few constants that are used throught the implementation.
+ * 
+ * @version $Rev$ $Date$
+ */
+
+public interface POP3Constants {
+    public final static String SPACE = " ";
+
+    public final static String CRLF = "\r\n";
+
+    public final static int LF = '\n';
+
+    public final static int CR = '\r';
+
+    public final static int DOT = '.';
+
+    public final static int OK = 0;
+
+    public final static int ERR = 1;
+    
+    public final static int CHALLENGE = 2;
+}
\ No newline at end of file
diff --git a/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/POP3Folder.java b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/POP3Folder.java
new file mode 100644
index 0000000..042c170
--- /dev/null
+++ b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/POP3Folder.java
@@ -0,0 +1,346 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.geronimo.javamail.store.pop3;
+
+import java.util.Vector;
+
+import javax.mail.FetchProfile;
+import javax.mail.Flags;
+import javax.mail.Folder;
+import javax.mail.Message;
+import javax.mail.MessagingException;
+import javax.mail.MethodNotSupportedException;
+import javax.mail.Session;
+import javax.mail.Store;
+import javax.mail.URLName;
+import javax.mail.event.ConnectionEvent;
+
+import org.apache.geronimo.javamail.store.pop3.message.POP3Message;
+import org.apache.geronimo.javamail.store.pop3.message.POP3MessageFactory;
+import org.apache.geronimo.javamail.store.pop3.response.POP3ResponseFactory;
+import org.apache.geronimo.javamail.store.pop3.response.POP3StatusResponse;
+
+/**
+ * The POP3 implementation of the javax.mail.Folder Note that only INBOX is
+ * supported in POP3
+ * <p>
+ * <url>http://www.faqs.org/rfcs/rfc1939.html</url>
+ * </p>
+ * 
+ * @see javax.mail.Folder
+ * 
+ * @version $Rev$ $Date$
+ */
+public class POP3Folder extends Folder {
+
+    private boolean isFolderOpen = false;
+
+    private int mode;
+
+    private POP3Connection pop3Con;
+
+    private int msgCount;
+
+    private Session session;
+
+    /**
+     * Vector is synchronized so choose over the other Collection impls This is
+     * initialized on open A chache will save the expensive operation of
+     * retrieving the message again from the server.
+     */
+    private Vector msgCache;
+
+    protected POP3Folder(Store store, URLName url) {
+        super(store);
+    }
+
+    protected POP3Folder(Store store, Session session, POP3Connection pop3Con) {
+        super(store);
+        this.pop3Con = pop3Con;
+        this.session = session;
+    }
+
+    public String getName() {
+        return "INBOX";
+    }
+
+    public String getFullName() {
+        return "INBOX";
+    }
+
+    /**
+     * Never return "this" as the parent folder. Somebody not familliar with
+     * POP3 may do something like while(getParent() != null) or something
+     * simmilar which will result in an infinte loop
+     */
+    public Folder getParent() throws MessagingException {
+        throw new MethodNotSupportedException("INBOX is the root folder");
+    }
+
+    public boolean exists() throws MessagingException {
+        // INBOX always exists at the backend
+        return true;
+    }
+
+    public Folder[] list(String pattern) throws MessagingException {
+        throw new MethodNotSupportedException("Only INBOX is supported in POP3, no sub folders");
+    }
+
+    /**
+     * No sub folders, hence there is no notion of a seperator
+     */
+    public char getSeparator() throws MessagingException {
+        throw new MethodNotSupportedException("Only INBOX is supported in POP3, no sub folders");
+    }
+
+    public int getType() throws MessagingException {
+        return HOLDS_MESSAGES;
+    }
+
+    public boolean create(int type) throws MessagingException {
+        throw new MethodNotSupportedException("Only INBOX is supported in POP3, no sub folders");
+    }
+
+    public boolean hasNewMessages() throws MessagingException {
+        throw new MethodNotSupportedException("POP3 doesn't support this operation");
+    }
+
+    public Folder getFolder(String name) throws MessagingException {
+        throw new MethodNotSupportedException("Only INBOX is supported in POP3, no sub folders");
+    }
+
+    public boolean delete(boolean recurse) throws MessagingException {
+        throw new MethodNotSupportedException("Only INBOX is supported in POP3 and INBOX cannot be deleted");
+    }
+
+    public boolean renameTo(Folder f) throws MessagingException {
+        throw new MethodNotSupportedException("Only INBOX is supported in POP3 and INBOX cannot be renamed");
+    }
+
+    /**
+     * @see javax.mail.Folder#open(int)
+     */
+    public void open(int mode) throws MessagingException {
+        // Can only be performed on a closed folder
+        checkClosed();
+
+        try {
+
+            POP3StatusResponse res = (POP3StatusResponse) POP3ResponseFactory.getStatusResponse(pop3Con
+                    .sendCommand(POP3CommandFactory.getCOMMAND_STAT()));
+
+            // I am not checking for the res == null condition as the
+            // try catch block will handle it.
+
+            this.mode = mode;
+            this.isFolderOpen = true;
+            this.msgCount = res.getNumMessages();
+            // JavaMail API has no method in Folder to expose the total
+            // size (no of bytes) of the mail drop;
+
+            // NB:  We use the actual message number to access the messages from 
+            // the cache, which is origin 1.  Vectors are origin 0, so we add one additional 
+            // element and burn the 
+            msgCache = new Vector(msgCount + 1);
+            msgCache.setSize(msgCount + 1);
+
+        } catch (Exception e) {
+            throw new MessagingException("Unable to execute STAT command", e);
+        }
+
+        notifyConnectionListeners(ConnectionEvent.OPENED);
+    }
+
+    public void close(boolean expunge) throws MessagingException {
+        // Can only be performed on an open folder
+        checkOpen();
+
+        try {
+            if (mode == READ_WRITE) {
+                // find all messages marked deleted and issue DELE commands
+                POP3Message m;
+                // NB: the first element in the cache is not used.
+                for (int i = 1; i < msgCache.size(); i++) {
+                    if ((m = (POP3Message) msgCache.elementAt(i)) != null) {
+                        if (m.isSet(Flags.Flag.DELETED)) {
+                            try {
+                                pop3Con.sendCommand(POP3CommandFactory.getCOMMAND_DELE(i + 1));
+                            } catch (Exception e) {
+                                throw new MessagingException("Exception deleting message no [" + (i + 1)
+                                        + "] during close", e);
+                            }
+                        }
+                    }
+                }
+            }
+
+            try {
+                pop3Con.sendCommand(POP3CommandFactory.getCOMMAND_QUIT());
+            } catch (Exception e) {
+                // doesn't really care about the response
+            }
+            // dosn't need a catch block here, but added incase something goes
+            // wrong
+            // so that the finnaly is garunteed to execute in such a case.
+        } finally {
+            try {
+                pop3Con.close();
+            } catch (Exception e) {
+                // doesn't really care about the response
+                // all we can do is to set the reference explicitly to null
+                pop3Con = null;
+            }
+
+            /*
+             * The message numbers depend on the mail drop if the connection is
+             * closed, then purge the cache
+             */
+            msgCache = null;
+            isFolderOpen = false;
+            notifyConnectionListeners(ConnectionEvent.CLOSED);
+        }
+    }
+
+    public boolean isOpen() {
+        return isFolderOpen;
+    }
+
+    public Flags getPermanentFlags() {
+        // unfortunately doesn't have a throws clause for this method
+        // throw new MethodNotSupportedException("POP3 doesn't support permanent
+        // flags");
+
+        // Better than returning null, save the extra condition from a user to
+        // check for null
+        // and avoids a NullPointerException for the careless.
+        return new Flags();
+    }
+
+    public int getMessageCount() throws MessagingException {
+        return msgCount;
+    }
+
+    /**
+     * Checks wether the message is in cache, if not will create a new message
+     * object and return it.
+     * 
+     * @see javax.mail.Folder#getMessage(int)
+     */
+    public Message getMessage(int msgNum) throws MessagingException {
+        // Can only be performed on an Open folder
+        checkOpen();
+        if (msgNum < 1 || msgNum > getMessageCount()) {
+            throw new MessagingException("Invalid Message number");
+        }
+
+        Message msg = null;
+        try {
+            msg = (Message) msgCache.elementAt(msgNum);
+        } catch (RuntimeException e) {
+            session.getDebugOut().println("Message not in cache");
+        }
+        if (msg == null) {
+            msg = POP3MessageFactory.createMessage(this, session, pop3Con, msgNum);
+            msgCache.setElementAt(msg, msgNum);
+        }
+
+        return msg;
+    }
+
+    public void appendMessages(Message[] msgs) throws MessagingException {
+        throw new MethodNotSupportedException("Message appending is not supported in POP3");
+
+    }
+
+    public Message[] expunge() throws MessagingException {
+        throw new MethodNotSupportedException("Expunge is not supported in POP3");
+    }
+
+    public int getMode() throws IllegalStateException {
+        // Can only be performed on an Open folder
+        checkOpen();
+        return mode;
+    }
+
+    /**
+     * @see javax.mail.Folder#fetch(javax.mail.Message[],
+     *      javax.mail.FetchProfile)
+     * 
+     * The JavaMail API recommends that this method be overrident to provide a
+     * meaningfull implementation.
+     */
+    public void fetch(Message[] msgs, FetchProfile fp) throws MessagingException {
+        // Can only be performed on an Open folder
+        checkOpen();
+        for (int i = 0; i < msgs.length; i++) {
+            Message msg = msgs[i];
+            if (msg == null) {
+                msg = POP3MessageFactory.createMessage(this, session, pop3Con, i);
+            }
+            if (fp.contains(FetchProfile.Item.ENVELOPE)) {
+                msg = POP3MessageFactory.createMessageWithEvelope((POP3Message) msg);
+            }
+
+            if (fp.contains(FetchProfile.Item.CONTENT_INFO)) {
+                msg = POP3MessageFactory.createMessageWithContentInfo((POP3Message) msg);
+            }
+
+            if (fp.contains(FetchProfile.Item.FLAGS)) {
+                msg = POP3MessageFactory.createMessageWithFlags((POP3Message) msg);
+            }
+
+            msgs[i] = msg;
+        }
+    }
+
+    /**
+     * Below is a list of covinience methods that avoid repeated checking for a
+     * value and throwing an exception
+     */
+
+    /** Ensure the folder is open */
+    private void checkOpen() throws IllegalStateException {
+        if (!isFolderOpen) {
+            throw new IllegalStateException("Folder is not Open");
+        }
+    }
+
+    /** Ensure the folder is not open */
+    private void checkClosed() throws IllegalStateException {
+        if (isFolderOpen) {
+            throw new IllegalStateException("Folder is Open");
+        }
+    }
+
+    /**
+     * @see javax.mail.Folder#notifyMessageChangedListeners(int,
+     *      javax.mail.Message)
+     * 
+     * this method is protected and cannot be used outside of Folder, therefore
+     * had to explicitly expose it via a method in POP3Folder, so that
+     * POP3Message has access to it
+     * 
+     * Bad design on the part of the Java Mail API.
+     */
+    public void notifyMessageChangedListeners(int type, Message m) {
+        super.notifyMessageChangedListeners(type, m);
+    }
+
+}
diff --git a/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/POP3Response.java b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/POP3Response.java
new file mode 100644
index 0000000..197ca20
--- /dev/null
+++ b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/POP3Response.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.geronimo.javamail.store.pop3;
+
+import java.io.InputStream;
+
+/**
+ * An abstraction for POP3 Response
+ * 
+ * @see org.apache.geronimo.javamail.store.pop3.response.POP3ResponseFactory
+ * @see org.apache.geronimo.javamail.store.pop3.response.DefaultPOP3Response
+ * @see org.apache.geronimo.javamail.store.pop3.response.POP3StatusResponse
+ * 
+ * @version $Rev$ $Date$
+ */
+public interface POP3Response {
+
+    /**
+     * Returns the response OK, CHALLENGE or ERR
+     * <ul>
+     * <li>OK --> +OK in pop3 spec
+     * <li>CHALLENGE --> + in pop3 spec
+     * <li>ERR --> -ERR in pop3 spec
+     * </ul>
+     */
+    public int getStatus();
+
+    /**
+     * this corresponds to the line with the status however the status will be
+     * removed and the remainder is returned. Ex. "+OK 132 3023673" is the first
+     * line of response for a STAT command this method will return "132 3023673"
+     * 
+     * So any subsequent process can parse the params 132 as no of msgs and
+     * 3023674 as the size.
+     * 
+     * @see org.apache.geronimo.javamail.store.pop3.response.POP3StatusResponse
+     */
+    public String getFirstLine();
+
+    /**
+     * This way we are not restricting anybody as InputStream.class is the most
+     * basic type to represent an inputstream and ppl can decorate it anyway
+     * they want, for ex BufferedInputStream or as an InputStreamReader allowing
+     * maximum flexibility in using it.
+     */
+    public InputStream getData();
+}
diff --git a/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/POP3ResponseBuilder.java b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/POP3ResponseBuilder.java
new file mode 100644
index 0000000..7b0ab4a
--- /dev/null
+++ b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/POP3ResponseBuilder.java
@@ -0,0 +1,136 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.geronimo.javamail.store.pop3;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import javax.mail.MessagingException;
+import javax.mail.Session;
+
+import org.apache.geronimo.javamail.store.pop3.response.POP3ResponseFactory;
+
+;
+
+/**
+ * Builds a basic response out of the input stream received by the connection.
+ * Performs only two basic functions
+ * <ul>
+ * <li>Extrats the status code</li>
+ * <li>If multi-line response then extract the data as an input stream</li>
+ * </ul>
+ * 
+ * @version $Rev$ $Date$
+ */
+
+public final class POP3ResponseBuilder implements POP3Constants {
+
+    public static POP3Response buildResponse(Session session, BufferedReader reader, boolean isMultiLineResponse)
+            throws MessagingException {
+
+        int status = ERR;
+        InputStream data = null;
+
+        String line;
+        try {
+            line = reader.readLine();
+        } catch (IOException e) {
+            throw new MessagingException("Error in receving response");
+        }
+        if (line == null || line.trim().equals("")) {
+            if (session.getDebug()) {
+                session.getDebugOut().println("Empty Response");
+            }
+            throw new MessagingException("Empty Response");
+        }
+        if (session.getDebug()) {
+            session.getDebugOut().println("Response From Server " + line);
+        }
+
+        if (line.startsWith("+OK")) {
+            status = OK;
+            line = removeStatusField(line);
+            if (isMultiLineResponse) {
+                data = getMultiLineResponse(session, reader);
+            }
+        } else if (line.startsWith("-ERR")) {
+            status = ERR;
+            line = removeStatusField(line);
+        }else if (line.startsWith("+")) {
+        	status = CHALLENGE;
+        	line = removeStatusField(line);
+        	if (isMultiLineResponse) {
+        		data = getMultiLineResponse(session, reader);
+        	}
+        } else {
+            throw new MessagingException("Unexpected response: " + line);
+        }
+
+        return POP3ResponseFactory.getDefaultResponse(status, line, data);
+    }
+
+    private static String removeStatusField(String line) {
+        return line.substring(line.indexOf(SPACE) + 1);
+    }
+
+    /**
+     * This could be a multiline response
+     */
+    private static InputStream getMultiLineResponse(Session session, BufferedReader reader) throws MessagingException {
+
+        int byteRead = -1;
+        int lastByteRead = LF;
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        try {
+            while ((byteRead = reader.read()) >= 0) {
+                // We are checking for the end of a multiline response
+                // the format is .CRLF
+
+                // checking for the DOT and CR
+                if (lastByteRead == DOT && byteRead == CR) {
+                    byteRead = reader.read();
+                    // now checking for the LF of the second CRLF
+                    if (byteRead == LF) {
+                        // end of response
+                        break;
+                    }
+                }
+
+                out.write(byteRead);
+                lastByteRead = byteRead;
+            }
+
+            if (session.getDebug()) {
+                session.getDebugOut().println("\n============================ Response Content==================\n");
+                session.getDebugOut().write(out.toByteArray());
+                session.getDebugOut().println("\n==============================================================\n");
+            }
+
+        } catch (IOException e) {
+            throw new MessagingException("Error processing a multi-line response", e);
+        }
+
+        return new ByteArrayInputStream(out.toByteArray());
+    }
+
+}
diff --git a/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/POP3Store.java b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/POP3Store.java
new file mode 100644
index 0000000..5caed89
--- /dev/null
+++ b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/POP3Store.java
@@ -0,0 +1,240 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.geronimo.javamail.store.pop3;
+
+import javax.mail.AuthenticationFailedException;
+import javax.mail.Folder;
+import javax.mail.MessagingException;
+import javax.mail.Session;
+import javax.mail.Store;
+import javax.mail.URLName;
+
+/**
+ * POP3 implementation of javax.mail.Store POP protocol spec is implemented in
+ * org.apache.geronimo.javamail.store.pop3.POP3Connection
+ * 
+ * @version $Rev$ $Date$
+ */
+
+public class POP3Store extends Store {
+
+    private POP3Connection pop3Con;
+
+    protected static final int DEFAULT_MAIL_POP3_PORT = 110;
+    private boolean sslConnection;
+    private int defaultPort;
+    
+    private String protocol;
+    public POP3Store(Session session, URLName name) {
+        this(session, name, "pop3", DEFAULT_MAIL_POP3_PORT, false);
+    }
+
+    /**
+     * Common constructor used by the POP3Store and POP3SSLStore classes
+     * to do common initialization of defaults.
+     *
+     * @param session
+     *            The host session instance.
+     * @param name
+     *            The URLName of the target.
+     * @param protocol
+     *            The protocol type ("pop3"). This helps us in
+     *            retrieving protocol-specific session properties.
+     * @param defaultPort
+     *            The default port used by this protocol. For pop3, this will
+     *            be 110. The default for pop3 with ssl is 995.
+     * @param sslConnection
+     *            Indicates whether an SSL connection should be used to initial
+     *            contact the server. This is different from the STARTTLS
+     *            support, which switches the connection to SSL after the
+     *            initial startup.
+     */
+    protected POP3Store(Session session, URLName name, String protocol, int defaultPort, boolean sslConnection) {
+        super(session, name);
+        this.protocol = protocol;
+
+        // these are defaults based on what the superclass specifies.
+        this.sslConnection = sslConnection;
+        this.defaultPort = defaultPort;
+
+    }
+    /**
+     * @see javax.mail.Store#getDefaultFolder()
+     * 
+     * There is only INBOX supported in POP3 so the default folder is inbox
+     */
+    public Folder getDefaultFolder() throws MessagingException {
+        return getFolder("INBOX");
+    }
+
+    /**
+     * @see javax.mail.Store#getFolder(java.lang.String)
+     */
+    public Folder getFolder(String name) throws MessagingException {
+
+        checkConnectionStatus();
+
+        if (!"INBOX".equalsIgnoreCase(name)) {
+            throw new MessagingException("Only INBOX is supported in POP3");
+        }
+        return new POP3Folder(this, session, pop3Con);
+    }
+
+    /**
+     * @see javax.mail.Store#getFolder(javax.mail.URLName)
+     */
+    public Folder getFolder(URLName url) throws MessagingException {
+        return getFolder(url.getFile());
+    }
+
+    /**
+     * @see javax.mail.Service#protocolConnect(java.lang.String, int,
+     *      java.lang.String, java.lang.String)
+     */
+    protected synchronized boolean protocolConnect(String host, int portNum, String user, String passwd)
+            throws MessagingException {
+
+        // Never store the user, passwd for security reasons
+
+        // if these values are null, no connection attempt should be made
+        if (host == null || passwd == null || user == null) {
+            return false;
+        }
+
+        // validate port num
+        if (portNum < 1) {
+            String portstring = session.getProperty("mail.pop3.port");
+            if (portstring != null) {
+                try {
+                    portNum = Integer.parseInt(portstring);
+                } catch (NumberFormatException e) {
+                    portNum = defaultPort;
+                }
+            }
+        }
+
+        /*
+         * Obtaining a connection to the server.
+         * 
+         */
+        pop3Con = new POP3Connection(this.session, host, portNum, sslConnection, protocol);
+        try {
+            pop3Con.open();
+        } catch (Exception e) {
+            throw new MessagingException("Connection failed", e);
+        }
+
+        /*
+         * Sending the USER command with username
+         * 
+         */
+        POP3Response resUser = null;
+        try {
+            resUser = pop3Con.sendCommand(POP3CommandFactory.getCOMMAND_USER(user));
+        } catch (Exception e) {
+            throw new MessagingException("Connection failed", e);
+        }
+
+        if (POP3Constants.ERR == resUser.getStatus()) {
+
+            /*
+             * Authentication failed so sending QUIT
+             * 
+             */
+            try {
+                pop3Con.sendCommand(POP3CommandFactory.getCOMMAND_QUIT());
+            } catch (Exception e) {
+                // We don't care about the response or if any error happens
+                // just trying to comply with the spec.
+                // Most likely the server would have terminated the connection
+                // by now.
+            }
+
+            throw new AuthenticationFailedException(resUser.getFirstLine());
+        }
+
+        /*
+         * Sending the PASS command with password
+         * 
+         */
+        POP3Response resPwd = null;
+        try {
+            resPwd = pop3Con.sendCommand(POP3CommandFactory.getCOMMAND_PASS(passwd));
+        } catch (Exception e) {
+            throw new MessagingException("Connection failed", e);
+        }
+
+        if (POP3Constants.ERR == resPwd.getStatus()) {
+
+            /*
+             * Authentication failed so sending QUIT
+             * 
+             */
+            try {
+                pop3Con.sendCommand(POP3CommandFactory.getCOMMAND_QUIT());
+            } catch (Exception e) {
+                // We don't care about the response or if any error happens
+                // just trying to comply with the spec.
+                // Most likely the server would have terminated the connection
+                // by now.
+            }
+
+            throw new AuthenticationFailedException(resPwd.getFirstLine());
+        }
+
+        return true;
+    }
+
+    /**
+     * @see javax.mail.Service#isConnected()
+     */
+    public boolean isConnected() {
+        POP3Response res = null;
+        try {
+            res = pop3Con.sendCommand(POP3CommandFactory.getCOMMAND_NOOP());
+        } catch (Exception e) {
+            return false;
+        }
+
+        return (POP3Constants.OK == res.getStatus());
+    }
+
+    /**
+     * @see javax.mail.Service#close()
+     */
+    public void close() throws MessagingException {
+        // This is done to ensure proper event notification.
+        super.close();
+        try {
+            pop3Con.close();
+        } catch (Exception e) {
+            // A message is already set at the connection level
+            // unfortuantely there is no constructor that takes only
+            // the root exception
+            new MessagingException("", e);
+        }
+    }
+
+    private void checkConnectionStatus() throws MessagingException {
+        if (!this.isConnected()) {
+            throw new MessagingException("Not connected ");
+        }
+    }
+}
diff --git a/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/message/POP3Message.java b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/message/POP3Message.java
new file mode 100644
index 0000000..c8183ec
--- /dev/null
+++ b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/message/POP3Message.java
@@ -0,0 +1,263 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.geronimo.javamail.store.pop3.message;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Enumeration;
+
+import javax.mail.Flags;
+import javax.mail.Folder;
+import javax.mail.IllegalWriteException;
+import javax.mail.MessagingException;
+import javax.mail.Session;
+import javax.mail.event.MessageChangedEvent;
+import javax.mail.internet.InternetHeaders;
+import javax.mail.internet.MimeMessage;
+
+import org.apache.geronimo.javamail.store.pop3.POP3CommandFactory;
+import org.apache.geronimo.javamail.store.pop3.POP3Connection;
+import org.apache.geronimo.javamail.store.pop3.POP3Folder;
+import org.apache.geronimo.javamail.store.pop3.POP3Response;
+import org.apache.geronimo.javamail.store.pop3.response.POP3ListResponse;
+import org.apache.geronimo.javamail.store.pop3.response.POP3ResponseFactory;
+
+/**
+ * POP3 implementation of javax.mail.internet.MimeMessage
+ * 
+ * Only the most basic information is given and Message objects created here is
+ * a light-weight reference to the actual Message As per the JavaMail spec items
+ * from the actual message will get filled up on demand
+ * 
+ * If some other items are obtained from the server as a result of one call,
+ * then the other details are also processed and filled in. For ex if RETR is
+ * called then header information will also be processed in addition to the
+ * content
+ * 
+ * @version $Rev$ $Date$
+ */
+public class POP3Message extends MimeMessage {
+
+    private POP3Connection pop3Con;
+
+    private int msgSize = -1;
+
+    private int headerSize = -1;
+
+    // We can't use header bcos it's already initialize to
+    // to an empty InternetHeader
+    private InputStream rawHeaders;
+
+    // used to force loading of headers again
+    private boolean loadHeaders = true;
+
+    // to get accessed to the debug setting and log
+    private Session session;
+
+    protected POP3Message(Folder folder, int msgnum, Session session, POP3Connection pop3Con) {
+        super(folder, msgnum);
+        this.pop3Con = pop3Con;
+        this.session = session;
+    }
+
+    /**
+     * @see javax.mail.internet.MimeMessage#getContentStream()
+     */
+    protected InputStream getContentStream() throws MessagingException {
+        POP3Response msgResponse = null;
+        try {
+            msgResponse = pop3Con.sendCommand(POP3CommandFactory.getCOMMAND_RETR(msgnum));
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        loadHeaders = true;
+        loadHeaders(msgResponse.getData());
+        loadContent(msgResponse.getData());
+
+        return contentStream;
+    }
+
+    public void setFlags(Flags newFlags, boolean set) throws MessagingException {
+        Flags oldFlags = (Flags) flags.clone();
+        super.setFlags(newFlags, set);
+
+        if (!flags.equals(oldFlags)) {
+            ((POP3Folder) folder).notifyMessageChangedListeners(MessageChangedEvent.FLAGS_CHANGED, this);
+        }
+    }
+
+    protected void loadHeaders(InputStream in) throws MessagingException {
+        if (loadHeaders || rawHeaders == null) {
+            rawHeaders = in;
+            headers = new InternetHeaders(rawHeaders);
+            loadHeaders = false;
+        }
+    }
+
+    protected void loadContent(InputStream stream) throws MessagingException {
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        try {
+            int byteRead = stream.read();
+            int lastByte = -1;
+            for (; byteRead > 0;) {
+                if (byteRead == ' ' && lastByte == '\n') {
+                    break;
+                }
+                lastByte = byteRead;
+                byteRead = stream.read();
+            }
+
+            for (; stream.available() > 0;) {
+                out.write(stream.read());
+            }
+
+            contentStream = new ByteArrayInputStream(out.toByteArray());
+            msgSize = contentStream.available();
+
+        } catch (IOException e) {
+
+            throw new MessagingException("Error loading content info", e);
+        }
+    }
+
+    public int getSize() throws MessagingException {
+        if (msgSize >= 0) {
+            return msgSize;
+        }
+        try {
+
+            if (msgSize < 0) {
+                if (rawHeaders == null) {
+                    loadHeaders();
+                }
+                POP3ListResponse res = (POP3ListResponse) POP3ResponseFactory.getListResponse(pop3Con
+                        .sendCommand(POP3CommandFactory.getCOMMAND_LIST(msgnum)));
+                msgSize = res.getSize() - headerSize;
+            }
+            return msgSize;
+        } catch (MessagingException ex) {
+            throw new MessagingException("error getting size", ex);
+        }
+    }
+
+    /**
+     * notice that we pass zero as the no of lines from the message,as it
+     * doesn't serv any purpose to get only a certain number of lines.
+     * 
+     * However this maybe important if a mail client only shows 3 or 4 lines of
+     * the message in the list and then when the user clicks they would load the
+     * message on demand.
+     * 
+     */
+    protected void loadHeaders() throws MessagingException {
+        POP3Response msgResponse = null;
+        try {
+
+            msgResponse = pop3Con.sendCommand(POP3CommandFactory.getCOMMAND_TOP(msgnum, 0));
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        loadHeaders(msgResponse.getData());
+    }
+
+    /***************************************************************************
+     * Following is a set of methods that deal with headers I have tried to use
+     * the bare minimum
+     * 
+     * Used sun's POP3 impl & JavaMail API as a guide in decided which methods
+     * are important.
+     **************************************************************************/
+
+    public String[] getHeader(String name) throws MessagingException {
+        if (rawHeaders == null)
+            loadHeaders();
+        return headers.getHeader(name);
+    }
+
+    public String getHeader(String name, String delimiter) throws MessagingException {
+        if (headers == null)
+            loadHeaders();
+        return headers.getHeader(name, delimiter);
+    }
+
+    public Enumeration getAllHeaders() throws MessagingException {
+        if (headers == null)
+            loadHeaders();
+        return headers.getAllHeaders();
+    }
+
+    public Enumeration getMatchingHeaders(String[] names) throws MessagingException {
+        if (headers == null)
+            loadHeaders();
+        return headers.getMatchingHeaders(names);
+    }
+
+    public Enumeration getNonMatchingHeaders(String[] names) throws MessagingException {
+        if (headers == null)
+            loadHeaders();
+        return headers.getNonMatchingHeaders(names);
+    }
+
+    public Enumeration getAllHeaderLines() throws MessagingException {
+        if (headers == null)
+            loadHeaders();
+        return headers.getAllHeaderLines();
+    }
+
+    public Enumeration getMatchingHeaderLines(String[] names) throws MessagingException {
+        if (headers == null)
+            loadHeaders();
+        return headers.getMatchingHeaderLines(names);
+    }
+
+    public Enumeration getNonMatchingHeaderLines(String[] names) throws MessagingException {
+        if (headers == null)
+            loadHeaders();
+        return headers.getNonMatchingHeaderLines(names);
+    }
+
+    // the following are overrides for header modification methods. These
+    // messages are read only,
+    // so the headers cannot be modified.
+    public void addHeader(String name, String value) throws MessagingException {
+        throw new IllegalWriteException("POP3 messages are read-only");
+    }
+
+    public void setHeader(String name, String value) throws MessagingException {
+        throw new IllegalWriteException("POP3 messages are read-only");
+    }
+
+    public void removeHeader(String name) throws MessagingException {
+        throw new IllegalWriteException("POP3 messages are read-only");
+    }
+
+    public void addHeaderLine(String line) throws MessagingException {
+        throw new IllegalWriteException("POP3 messages are read-only");
+    }
+
+    /**
+     * We cannot modify these messages
+     */
+    public void saveChanges() throws MessagingException {
+        throw new IllegalWriteException("POP3 messages are read-only");
+    }
+}
diff --git a/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/message/POP3MessageFactory.java b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/message/POP3MessageFactory.java
new file mode 100644
index 0000000..7c8d493
--- /dev/null
+++ b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/message/POP3MessageFactory.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.geronimo.javamail.store.pop3.message;
+
+import javax.mail.Message;
+import javax.mail.MessagingException;
+import javax.mail.Session;
+import javax.mail.internet.MimeMessage.RecipientType;
+
+import org.apache.geronimo.javamail.store.pop3.POP3Connection;
+import org.apache.geronimo.javamail.store.pop3.POP3Folder;
+
+/**
+ * Fctory class to create POP3Messages based on the fetch profile
+ * 
+ * @version $Rev$ $Date$
+ */
+public final class POP3MessageFactory {
+
+    /**
+     * Creates a basic method with no items, the items will be loaded on demand
+     * 
+     * @param folder
+     * @param session
+     * @param pop3Con
+     * @param msgNum
+     * @return
+     */
+    public static Message createMessage(POP3Folder folder, Session session, POP3Connection pop3Con, int msgNum) {
+        return new POP3Message(folder, msgNum, session, pop3Con);
+    }
+
+    /**
+     * Created in response to <cpde>FetchProfile.ENVELOPE</code>
+     */
+    public static Message createMessageWithEvelope(POP3Message msg) throws MessagingException {
+        msg.getAllHeaders();
+        msg.getSender();
+        msg.getSentDate();
+        msg.getSubject();
+        msg.getReplyTo();
+        msg.getReceivedDate();
+        msg.getRecipients(RecipientType.TO);
+
+        return msg;
+    }
+
+    /**
+     * Created in response to <code>FetchProfile.CONTENT_INFO</code>
+     */
+    public static Message createMessageWithContentInfo(POP3Message msg) throws MessagingException {
+        msg.getContentType();
+        msg.getDisposition();
+        msg.getDescription();
+        msg.getSize();
+        msg.getLineCount();
+
+        return msg;
+    }
+
+    /**
+     * Created in response to <code>FetchProfile.FLAGS</code>
+     */
+    public static Message createMessageWithFlags(POP3Message msg) throws MessagingException {
+        msg.getFlags();
+        return msg;
+    }
+
+}
diff --git a/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/message/POP3MessageWithContentInfo.java b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/message/POP3MessageWithContentInfo.java
new file mode 100644
index 0000000..17ca20d
--- /dev/null
+++ b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/message/POP3MessageWithContentInfo.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.geronimo.javamail.store.pop3.message;
+
+import javax.mail.Folder;
+import javax.mail.MessagingException;
+import javax.mail.Session;
+
+import org.apache.geronimo.javamail.store.pop3.POP3Connection;
+
+/**
+ * light-weight Message object will be created in response to
+ * FetchProfile.CONTENT_INFO other details will be filled on demand *
+ * 
+ * @version $Rev$ $Date$
+ * 
+ */
+
+public class POP3MessageWithContentInfo extends POP3Message {
+
+    public POP3MessageWithContentInfo(Folder folder, int msgnum, Session session, POP3Connection pop3Con)
+            throws MessagingException {
+        super(folder, msgnum, null, pop3Con);
+        this.getContentType();
+        this.getDisposition();
+        this.getDescription();
+        this.getSize();
+        this.getLineCount();
+    }
+
+}
diff --git a/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/message/POP3MessageWithEnvelope.java b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/message/POP3MessageWithEnvelope.java
new file mode 100644
index 0000000..bc05d39
--- /dev/null
+++ b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/message/POP3MessageWithEnvelope.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.geronimo.javamail.store.pop3.message;
+
+import javax.mail.Folder;
+import javax.mail.MessagingException;
+import javax.mail.Session;
+
+import org.apache.geronimo.javamail.store.pop3.POP3Connection;
+
+/**
+ * light-weight Message object will be created in response to
+ * FetchProfile.ENVELOPE other details will be filled on demand *
+ * 
+ * @version $Rev$ $Date$
+ */
+
+public class POP3MessageWithEnvelope extends POP3Message {
+
+    protected POP3MessageWithEnvelope(Folder folder, int msgnum, Session session, POP3Connection pop3Con)
+            throws MessagingException {
+        super(folder, msgnum, session, pop3Con);
+        this.getAllHeaders();
+        this.getSender();
+        this.getSentDate();
+        this.getSubject();
+        this.getReplyTo();
+        this.getReceivedDate();
+        this.getRecipients(RecipientType.TO);
+    }
+}
diff --git a/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/message/POP3MessageWithFlags.java b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/message/POP3MessageWithFlags.java
new file mode 100644
index 0000000..2c6ac64
--- /dev/null
+++ b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/message/POP3MessageWithFlags.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.geronimo.javamail.store.pop3.message;
+
+import javax.mail.Folder;
+import javax.mail.MessagingException;
+import javax.mail.Session;
+
+import org.apache.geronimo.javamail.store.pop3.POP3Connection;
+
+/**
+ * light-weight Message object will be created in response to FetchProfile.FLAGS
+ * other details will be filled on demand *
+ * 
+ * @version $Rev$ $Date$
+ */
+
+public class POP3MessageWithFlags extends POP3Message {
+
+    protected POP3MessageWithFlags(Folder folder, int msgnum, Session session, POP3Connection pop3Con)
+            throws MessagingException {
+        super(folder, msgnum, session, pop3Con);
+        this.getFlags();
+    }
+}
diff --git a/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/response/DefaultPOP3Response.java b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/response/DefaultPOP3Response.java
new file mode 100644
index 0000000..43e33ed
--- /dev/null
+++ b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/response/DefaultPOP3Response.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.geronimo.javamail.store.pop3.response;
+
+import java.io.InputStream;
+
+import org.apache.geronimo.javamail.store.pop3.POP3Constants;
+import org.apache.geronimo.javamail.store.pop3.POP3Response;
+
+/**
+ * This class provides the basic implementation for the POP3Response.
+ * 
+ * @see org.apache.geronimo.javamail.store.pop3.POP3Response
+ * @version $Rev$ $Date$
+ */
+
+public class DefaultPOP3Response implements POP3Response, POP3Constants {
+
+    private int status = ERR;
+
+    private String firstLine;
+
+    private InputStream data;
+
+    DefaultPOP3Response(int status, String firstLine, InputStream data) {
+        this.status = status;
+        this.firstLine = firstLine;
+        this.data = data;
+    }
+
+    public int getStatus() {
+        return status;
+    }
+
+    public InputStream getData() {
+        return data;
+    }
+
+    public String getFirstLine() {
+        return firstLine;
+    }
+
+}
diff --git a/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/response/POP3ListResponse.java b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/response/POP3ListResponse.java
new file mode 100644
index 0000000..448322a
--- /dev/null
+++ b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/response/POP3ListResponse.java
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.geronimo.javamail.store.pop3.response;
+
+import java.util.Vector;
+
+import javax.mail.MessagingException;
+
+import org.apache.geronimo.javamail.store.pop3.POP3Response;
+
+/**
+ * This class adds functionality to the basic response by parsing the reply for
+ * LIST command and obtaining specific information about the msgnum and the
+ * size. It could be for one or more msgs depending on wether a msg number was
+ * passed or not into the LIST command
+ * 
+ * @see org.apache.geronimo.javamail.store.pop3.POP3Response
+ * @see org.apache.geronimo.javamail.store.pop3.response.DefaultPOP3Response
+ * 
+ * @version $Rev$ $Date$
+ */
+
+public class POP3ListResponse extends DefaultPOP3Response {
+
+    private int msgnum = 0;
+
+    private int size = 0;
+
+    private Vector multipleMsgs = null;
+
+    POP3ListResponse(POP3Response baseRes) throws MessagingException {
+        super(baseRes.getStatus(), baseRes.getFirstLine(), baseRes.getData());
+
+        // if ERR not worth proceeding any further
+        if (OK == getStatus()) {
+
+            // if data == null, then it mean it's a single line response
+            if (baseRes.getData() == null) {
+                String[] args = getFirstLine().split(SPACE);
+                try {
+                    msgnum = Integer.parseInt(args[0]);
+                } catch (NumberFormatException e) {
+                    throw new MessagingException("Invalid response for STAT command", e);
+                }
+                try {
+                    size = Integer.parseInt(args[1]);
+                } catch (NumberFormatException e) {
+                    throw new MessagingException("Invalid response for STAT command", e);
+                }
+            } else {
+                int totalMsgs = 0;
+                String[] args = getFirstLine().split(SPACE);
+                try {
+                    totalMsgs = Integer.parseInt(args[0]);
+                } catch (NumberFormatException e) {
+                    throw new MessagingException("Invalid response for STAT command", e);
+                }
+                multipleMsgs = new Vector(totalMsgs);
+                multipleMsgs.setSize(totalMsgs);
+                // Todo : multi-line response parsing
+            }
+
+        }
+    }
+
+    public int getMessageNumber() {
+        return msgnum;
+    }
+
+    public int getSize() {
+        return size;
+    }
+
+    /**
+     * Messages can be accessed by multipleMsgs.getElementAt(msgnum)
+     * 
+     */
+    public Vector getMultipleMessageDetails() {
+        return multipleMsgs;
+    }
+
+}
diff --git a/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/response/POP3ResponseFactory.java b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/response/POP3ResponseFactory.java
new file mode 100644
index 0000000..5412746
--- /dev/null
+++ b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/response/POP3ResponseFactory.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.geronimo.javamail.store.pop3.response;
+
+import java.io.InputStream;
+
+import javax.mail.MessagingException;
+
+import org.apache.geronimo.javamail.store.pop3.POP3Constants;
+import org.apache.geronimo.javamail.store.pop3.POP3Response;
+
+/**
+ * This factory provides a uniform way of handling the creation of response
+ * objects.
+ * 
+ * @version $Rev$ $Date$
+ */
+
+public final class POP3ResponseFactory implements POP3Constants {
+
+    public static POP3Response getDefaultResponse(int status, String line, InputStream data) {
+        return new DefaultPOP3Response(status, line, data);
+    }
+
+    public static POP3Response getStatusResponse(POP3Response baseRes) throws MessagingException {
+        return new POP3StatusResponse(baseRes);
+    }
+
+    public static POP3Response getListResponse(POP3Response baseRes) throws MessagingException {
+        return new POP3StatusResponse(baseRes);
+    }
+
+}
diff --git a/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/response/POP3StatusResponse.java b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/response/POP3StatusResponse.java
new file mode 100644
index 0000000..4fceb84
--- /dev/null
+++ b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/response/POP3StatusResponse.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.geronimo.javamail.store.pop3.response;
+
+import javax.mail.MessagingException;
+
+import org.apache.geronimo.javamail.store.pop3.POP3Response;
+
+/**
+ * This class adds functionality to the basic response by parsing the status
+ * line and obtaining specific information about num of msgs and the size
+ * 
+ * @see org.apache.geronimo.javamail.store.pop3.POP3Response
+ * @see org.apache.geronimo.javamail.store.pop3.response.DefaultPOP3Response
+ * 
+ * @version $Rev$ $Date$
+ */
+
+public class POP3StatusResponse extends DefaultPOP3Response {
+
+    private int numMessages = 0;
+
+    private int size = 0;
+
+    POP3StatusResponse(POP3Response baseRes) throws MessagingException {
+        super(baseRes.getStatus(), baseRes.getFirstLine(), baseRes.getData());
+
+        // if ERR not worth proceeding any further
+        if (OK == getStatus()) {
+            String[] args = getFirstLine().split(SPACE);
+            try {
+                numMessages = Integer.parseInt(args[0]);
+            } catch (NumberFormatException e) {
+                throw new MessagingException("Invalid response for STAT command", e);
+            }
+            try {
+                size = Integer.parseInt(args[1]);
+            } catch (NumberFormatException e) {
+                throw new MessagingException("Invalid response for STAT command", e);
+            }
+        }
+    }
+
+    public int getNumMessages() {
+        return numMessages;
+    }
+
+    public int getSize() {
+        return size;
+    }
+
+}
diff --git a/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/transport/nntp/NNTPConnection.java b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/transport/nntp/NNTPConnection.java
new file mode 100644
index 0000000..a8ca219
--- /dev/null
+++ b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/transport/nntp/NNTPConnection.java
@@ -0,0 +1,1081 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.geronimo.javamail.transport.nntp;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.util.HashMap;
+import java.util.List;
+import java.util.StringTokenizer;
+
+import javax.mail.Message;
+import javax.mail.MessagingException;
+import javax.mail.Session;
+
+import org.apache.geronimo.javamail.authentication.ClientAuthenticator;
+import org.apache.geronimo.javamail.authentication.CramMD5Authenticator;
+import org.apache.geronimo.javamail.authentication.DigestMD5Authenticator;
+import org.apache.geronimo.javamail.authentication.LoginAuthenticator;
+import org.apache.geronimo.javamail.authentication.PlainAuthenticator;
+import org.apache.geronimo.javamail.util.MIMEOutputStream;
+import org.apache.geronimo.javamail.util.TraceInputStream;
+import org.apache.geronimo.javamail.util.TraceOutputStream;
+import org.apache.geronimo.mail.util.Base64;
+import org.apache.geronimo.mail.util.SessionUtil;
+
+/**
+ * Simple implementation of NNTP transport. Just does plain RFC977-ish delivery.
+ * <p/> There is no way to indicate failure for a given recipient (it's possible
+ * to have a recipient address rejected). The sun impl throws exceptions even if
+ * others successful), but maybe we do a different way... <p/>
+ * 
+ * @version $Rev$ $Date$
+ */
+public class NNTPConnection {
+
+    /**
+     * constants for EOL termination
+     */
+    protected static final char CR = '\r';
+
+    protected static final char LF = '\n';
+
+    /**
+     * property keys for protocol properties.
+     */
+    protected static final String MAIL_NNTP_AUTH = "auth";
+
+    protected static final String MAIL_NNTP_PORT = "port";
+
+    protected static final String MAIL_NNTP_TIMEOUT = "timeout";
+
+    protected static final String MAIL_NNTP_SASL_REALM = "sasl.realm";
+
+    protected static final String MAIL_NNTP_FACTORY_CLASS = "socketFactory.class";
+
+    protected static final String MAIL_NNTP_FACTORY_FALLBACK = "fallback";
+
+    protected static final String MAIL_NNTP_LOCALADDRESS = "localaddress";
+
+    protected static final String MAIL_NNTP_LOCALPORT = "localport";
+
+    protected static final String MAIL_NNTP_QUITWAIT = "quitwait";
+
+    protected static final String MAIL_NNTP_FACTORY_PORT = "socketFactory.port";
+
+    protected static final String MAIL_NNTP_ENCODE_TRACE = "encodetrace";
+
+    protected static final int MIN_MILLIS = 1000 * 60;
+
+    protected static final int TIMEOUT = MIN_MILLIS * 5;
+
+    protected static final String DEFAULT_MAIL_HOST = "localhost";
+
+    protected static final int DEFAULT_NNTP_PORT = 119;
+
+    protected static final String AUTHENTICATION_PLAIN = "PLAIN";
+
+    protected static final String AUTHENTICATION_LOGIN = "LOGIN";
+
+    protected static final String AUTHENTICATION_CRAMMD5 = "CRAM-MD5";
+
+    protected static final String AUTHENTICATION_DIGESTMD5 = "DIGEST-MD5";
+
+    // the protocol in use (either nntp or nntp-post).
+    String protocol;
+
+    // the target host
+    protected String host;
+
+    // the target server port.
+    protected int port;
+
+    // the connection socket...can be a plain socket or SSLSocket, if TLS is
+    // being used.
+    protected Socket socket;
+
+    // input stream used to read data. If Sasl is in use, this might be other
+    // than the
+    // direct access to the socket input stream.
+    protected InputStream inputStream;
+
+    // the test reader wrapped around the input stream.
+    protected BufferedReader in;
+
+    // the other end of the connection pipeline.
+    protected OutputStream outputStream;
+
+    // does the server support posting?
+    protected boolean postingAllowed = true;
+
+    // the username we connect with
+    protected String username;
+
+    // the authentication password.
+    protected String password;
+
+    // the target SASL realm (normally null unless explicitly set or we have an
+    // authentication mechanism that
+    // requires it.
+    protected String realm;
+
+    // the last response line received from the server.
+    protected NNTPReply lastServerResponse = null;
+
+    // our attached session
+    protected Session session;
+
+    // our session provided debug output stream.
+    protected PrintStream debugStream;
+
+    // our debug flag (passed from the hosting transport)
+    protected boolean debug;
+
+    // list of authentication mechanisms supported by the server
+    protected HashMap serverAuthenticationMechanisms;
+
+    // map of server extension arguments
+    protected HashMap serverExtensionArgs;
+
+    // the welcome string from the server.
+    protected String welcomeString = null;
+
+    /**
+     * Normal constructor for an NNTPConnection() object.
+     * 
+     * @param session
+     *            The attached session.
+     * @param host
+     *            The target host name of the NNTP server.
+     * @param port
+     *            The target listening port of the server. Defaults to 119 if
+     *            the port is specified as -1.
+     * @param username
+     *            The login user name (can be null unless authentication is
+     *            required).
+     * @param password
+     *            Password associated with the userid account. Can be null if
+     *            authentication is not required.
+     * @param debug
+     *            The session debug flag.
+     */
+    public NNTPConnection(String protocol, Session session, String host, int port, String username, String password,
+            boolean debug) {
+        this.protocol = protocol;
+        this.session = session;
+        this.host = host;
+        this.port = port;
+        this.username = username;
+        this.password = password;
+        this.debug = debug;
+
+        // get our debug output.
+        debugStream = session.getDebugOut();
+    }
+
+    /**
+     * Connect to the server and do the initial handshaking.
+     * 
+     * @exception MessagingException
+     */
+    public void connect() throws MessagingException {
+        try {
+
+            // create socket and connect to server.
+            getConnection();
+
+            // receive welcoming message
+            getWelcome();
+
+        } catch (IOException e) {
+            if (debug) {
+                debugOut("I/O exception establishing connection", e);
+            }
+            throw new MessagingException("Connection error", e);
+        }
+    }
+
+    /**
+     * Close the connection. On completion, we'll be disconnected from the
+     * server and unable to send more data.
+     * 
+     * @exception MessagingException
+     */
+    public void close() throws MessagingException {
+        // if we're already closed, get outta here.
+        if (socket == null) {
+            return;
+        }
+        try {
+            // say goodbye
+            sendQuit();
+        } finally {
+            // and close up the connection. We do this in a finally block to
+            // make sure the connection
+            // is shut down even if quit gets an error.
+            closeServerConnection();
+        }
+    }
+
+    /**
+     * Create a transport connection object and connect it to the target server.
+     * 
+     * @exception MessagingException
+     */
+    protected void getConnection() throws IOException {
+        // We might have been passed a socket to connect with...if not, we need
+        // to create one of the correct type.
+        if (socket == null) {
+            getConnectedSocket();
+        }
+        // if we already have a socket, get some information from it and
+        // override what we've been passed.
+        else {
+            port = socket.getPort();
+            host = socket.getInetAddress().getHostName();
+        }
+
+        // now set up the input/output streams.
+        inputStream = new TraceInputStream(socket.getInputStream(), debugStream, debug, getBooleanProperty(
+                MAIL_NNTP_ENCODE_TRACE, false));
+        ;
+        outputStream = new TraceOutputStream(socket.getOutputStream(), debugStream, debug, getBooleanProperty(
+                MAIL_NNTP_ENCODE_TRACE, false));
+
+        // get a reader to read the input as lines
+        in = new BufferedReader(new InputStreamReader(inputStream));
+    }
+
+    /**
+     * Close the server connection at termination.
+     */
+    public void closeServerConnection() {
+        try {
+            socket.close();
+        } catch (IOException ignored) {
+        }
+
+        socket = null;
+        inputStream = null;
+        outputStream = null;
+        in = null;
+    }
+
+    /**
+     * Creates a connected socket
+     * 
+     * @exception MessagingException
+     */
+    public void getConnectedSocket() throws IOException {
+        if (debug) {
+            debugOut("Attempting plain socket connection to server " + host + ":" + port);
+        }
+
+        // the socket factory can be specified via a session property. By
+        // default, we just directly
+        // instantiate a socket without using a factor.
+        String socketFactory = getProperty(MAIL_NNTP_FACTORY_CLASS);
+
+        // there are several protocol properties that can be set to tune the
+        // created socket. We need to
+        // retrieve those bits before creating the socket.
+        int timeout = getIntProperty(MAIL_NNTP_TIMEOUT, -1);
+        InetAddress localAddress = null;
+        // see if we have a local address override.
+        String localAddrProp = getProperty(MAIL_NNTP_LOCALADDRESS);
+        if (localAddrProp != null) {
+            localAddress = InetAddress.getByName(localAddrProp);
+        }
+
+        // check for a local port...default is to allow socket to choose.
+        int localPort = getIntProperty(MAIL_NNTP_LOCALPORT, 0);
+
+        socket = null;
+
+        // if there is no socket factory defined (normal), we just create a
+        // socket directly.
+        if (socketFactory == null) {
+            socket = new Socket(host, port, localAddress, localPort);
+        }
+
+        else {
+            try {
+                int socketFactoryPort = getIntProperty(MAIL_NNTP_FACTORY_PORT, -1);
+
+                // we choose the port used by the socket based on overrides.
+                Integer portArg = new Integer(socketFactoryPort == -1 ? port : socketFactoryPort);
+
+                // use the current context loader to resolve this.
+                ClassLoader loader = Thread.currentThread().getContextClassLoader();
+                Class factoryClass = loader.loadClass(socketFactory);
+
+                // done indirectly, we need to invoke the method using
+                // reflection.
+                // This retrieves a factory instance.
+                Method getDefault = factoryClass.getMethod("getDefault", new Class[0]);
+                Object defFactory = getDefault.invoke(new Object(), new Object[0]);
+
+                // now that we have the factory, there are two different
+                // createSocket() calls we use,
+                // depending on whether we have a localAddress override.
+
+                if (localAddress != null) {
+                    // retrieve the createSocket(String, int, InetAddress, int)
+                    // method.
+                    Class[] createSocketSig = new Class[] { String.class, Integer.TYPE, InetAddress.class, Integer.TYPE };
+                    Method createSocket = factoryClass.getMethod("createSocket", createSocketSig);
+
+                    Object[] createSocketArgs = new Object[] { host, portArg, localAddress, new Integer(localPort) };
+                    socket = (Socket) createSocket.invoke(defFactory, createSocketArgs);
+                } else {
+                    // retrieve the createSocket(String, int) method.
+                    Class[] createSocketSig = new Class[] { String.class, Integer.TYPE };
+                    Method createSocket = factoryClass.getMethod("createSocket", createSocketSig);
+
+                    Object[] createSocketArgs = new Object[] { host, portArg };
+                    socket = (Socket) createSocket.invoke(defFactory, createSocketArgs);
+                }
+            } catch (Throwable e) {
+                // if a socket factor is specified, then we may need to fall
+                // back to a default. This behavior
+                // is controlled by (surprise) more session properties.
+                if (getBooleanProperty(MAIL_NNTP_FACTORY_FALLBACK, false)) {
+                    if (debug) {
+                        debugOut("First plain socket attempt faile, falling back to default factory", e);
+                    }
+                    socket = new Socket(host, port, localAddress, localPort);
+                }
+                // we have an exception. We're going to throw an IOException,
+                // which may require unwrapping
+                // or rewrapping the exception.
+                else {
+                    // we have an exception from the reflection, so unwrap the
+                    // base exception
+                    if (e instanceof InvocationTargetException) {
+                        e = ((InvocationTargetException) e).getTargetException();
+                    }
+
+                    if (debug) {
+                        debugOut("Plain socket creation failure", e);
+                    }
+
+                    // throw this as an IOException, with the original exception
+                    // attached.
+                    IOException ioe = new IOException("Error connecting to " + host + ", " + port);
+                    ioe.initCause(e);
+                    throw ioe;
+                }
+            }
+        }
+
+        if (timeout >= 0) {
+            socket.setSoTimeout(timeout);
+        }
+    }
+
+    /**
+     * Get the servers welcome blob from the wire....
+     */
+    public void getWelcome() throws MessagingException {
+        NNTPReply line = getReply();
+
+        //
+        if (line.isError()) {
+            throw new MessagingException("Error connecting to news server: " + line.getMessage());
+        }
+
+        // remember we can post.
+        if (line.getCode() == NNTPReply.POSTING_ALLOWED) {
+            postingAllowed = true;
+        } else {
+            postingAllowed = false;
+        }
+
+        // the NNTP store will want to use the welcome string, so save it.
+        welcomeString = line.getMessage();
+
+        // find out what extensions this server supports.
+        getExtensions();
+    }
+
+    /**
+     * Sends the QUIT message and receieves the response
+     */
+    public void sendQuit() throws MessagingException {
+        // there's yet another property that controls whether we should wait for
+        // a
+        // reply for a QUIT command. If on, just send the command and get outta
+        // here.
+        if (getBooleanProperty(MAIL_NNTP_QUITWAIT, false)) {
+            sendLine("QUIT");
+        } else {
+            // handle as a real command...we're going to ignore the response.
+            sendCommand("QUIT");
+        }
+    }
+
+    /**
+     * Tell the server to switch to a named group.
+     * 
+     * @param name
+     *            The name of the target group.
+     * 
+     * @return The server response to the GROUP command.
+     */
+    public NNTPReply selectGroup(String name) throws MessagingException {
+        // send the GROUP command
+        return sendCommand("GROUP " + name);
+    }
+
+    /**
+     * Ask the server what extensions it supports.
+     * 
+     * @return True if the command was accepted ok, false for any errors.
+     * @exception MessagingException
+     */
+    protected void getExtensions() throws MessagingException {
+        NNTPReply reply = sendCommand("LIST EXTENSIONS", NNTPReply.EXTENSIONS_SUPPORTED);
+
+        // we get a 202 code back. The first line is just a greeting, and
+        // extensions are deliverd as data
+        // lines terminated with a "." line.
+        if (reply.getCode() != NNTPReply.EXTENSIONS_SUPPORTED) {
+            return;
+        }
+
+        // get a fresh extension mapping table.
+        serverExtensionArgs = new HashMap();
+        serverAuthenticationMechanisms = new HashMap();
+
+        // get the extension data lines.
+        List extensions = reply.getData();
+
+        // process all of the continuation lines
+        for (int i = 0; i < extensions.size(); i++) {
+            // go process the extention
+            processExtension((String) extensions.get(i));
+        }
+    }
+
+    /**
+     * Process an extension string passed back as the EHLP response.
+     * 
+     * @param extension
+     *            The string value of the extension (which will be of the form
+     *            "NAME arguments").
+     */
+    protected void processExtension(String extension) {
+        String extensionName = extension.toUpperCase();
+        String argument = "";
+
+        int delimiter = extension.indexOf(' ');
+        // if we have a keyword with arguments, parse them out and add to the
+        // argument map.
+        if (delimiter != -1) {
+            extensionName = extension.substring(0, delimiter).toUpperCase();
+            argument = extension.substring(delimiter + 1);
+        }
+
+        // add this to the map so it can be tested later.
+        serverExtensionArgs.put(extensionName, argument);
+
+        // process a few special ones that don't require extra parsing.
+        // AUTHINFO is entered in as a auth mechanism.
+        if (extensionName.equals("AUTHINFO")) {
+            serverAuthenticationMechanisms.put("AUTHINFO", "AUTHINFO");
+        }
+        // special case for some older servers.
+        else if (extensionName.equals("SASL")) {
+            // The security mechanisms are blank delimited tokens.
+            StringTokenizer tokenizer = new StringTokenizer(argument);
+
+            while (tokenizer.hasMoreTokens()) {
+                String mechanism = tokenizer.nextToken().toUpperCase();
+                serverAuthenticationMechanisms.put(mechanism, mechanism);
+            }
+        }
+    }
+
+    /**
+     * Retrieve any argument information associated with a extension reported
+     * back by the server on the EHLO command.
+     * 
+     * @param name
+     *            The name of the target server extension.
+     * 
+     * @return Any argument passed on a server extension. Returns null if the
+     *         extension did not include an argument or the extension was not
+     *         supported.
+     */
+    public String extensionParameter(String name) {
+        if (serverExtensionArgs != null) {
+            return (String) serverExtensionArgs.get(name);
+        }
+        return null;
+    }
+
+    /**
+     * Tests whether the target server supports a named extension.
+     * 
+     * @param name
+     *            The target extension name.
+     * 
+     * @return true if the target server reported on the EHLO command that is
+     *         supports the targer server, false if the extension was not
+     *         supported.
+     */
+    public boolean supportsExtension(String name) {
+        // this only returns null if we don't have this extension
+        return extensionParameter(name) != null;
+    }
+
+    /**
+     * Determine if the target server supports a given authentication mechanism.
+     * 
+     * @param mechanism
+     *            The mechanism name.
+     * 
+     * @return true if the server EHLO response indicates it supports the
+     *         mechanism, false otherwise.
+     */
+    protected boolean supportsAuthentication(String mechanism) {
+        return serverAuthenticationMechanisms.get(mechanism) != null;
+    }
+
+    /**
+     * Sends the data in the message down the socket. This presumes the server
+     * is in the right place and ready for getting the DATA message and the data
+     * right place in the sequence
+     */
+    public synchronized void sendPost(Message msg) throws MessagingException {
+
+        // send the POST command
+        NNTPReply line = sendCommand("POST");
+
+        if (line.getCode() != NNTPReply.SEND_ARTICLE) {
+            throw new MessagingException("Server rejected POST command: " + line);
+        }
+
+        // we've received permission to send the data, so ask the message to
+        // write itself out.
+        try {
+            // the data content has two requirements we need to meet by
+            // filtering the
+            // output stream. Requirement 1 is to conicalize any line breaks.
+            // All line
+            // breaks will be transformed into properly formed CRLF sequences.
+            //
+            // Requirement 2 is to perform byte-stuff for any line that begins
+            // with a "."
+            // so that data is not confused with the end-of-data marker (a
+            // "\r\n.\r\n" sequence.
+            //
+            // The MIME output stream performs those two functions on behalf of
+            // the content
+            // writer.
+            OutputStream mimeOut = new MIMEOutputStream(outputStream);
+
+            msg.writeTo(mimeOut);
+            mimeOut.flush();
+        } catch (IOException e) {
+            throw new MessagingException("I/O error posting message", e);
+        } catch (MessagingException e) {
+            throw new MessagingException("Exception posting message", e);
+        }
+
+        // now to finish, we send a CRLF sequence, followed by a ".".
+        sendLine("");
+        sendLine(".");
+
+        // use a longer time out here to give the server time to process the
+        // data.
+        line = new NNTPReply(receiveLine());
+
+        if (line.getCode() != NNTPReply.POSTED_OK) {
+            throw new MessagingException("Server rejected POST command: " + line);
+        }
+    }
+
+    /**
+     * Issue a command and retrieve the response. If the given success indicator
+     * is received, the command is returning a longer response, terminated by a
+     * "crlf.crlf" sequence. These lines are attached to the reply.
+     * 
+     * @param command
+     *            The command to issue.
+     * @param success
+     *            The command reply that indicates additional data should be
+     *            retrieved.
+     * 
+     * @return The command reply.
+     */
+    public synchronized NNTPReply sendCommand(String command, int success) throws MessagingException {
+        NNTPReply reply = sendCommand(command);
+        if (reply.getCode() == success) {
+            reply.retrieveData(in);
+        }
+        return reply;
+    }
+
+    /**
+     * Send a command to the server, returning the first response line back as a
+     * reply.
+     * 
+     * @param data
+     *            The data to send.
+     * 
+     * @return A reply object with the reply line.
+     * @exception MessagingException
+     */
+    public NNTPReply sendCommand(String data) throws MessagingException {
+        sendLine(data);
+        NNTPReply reply = getReply();
+        // did the server just inform us we need to authenticate? The spec
+        // allows this
+        // response to be sent at any time, so we need to try to authenticate
+        // and then retry the command.
+        if (reply.getCode() == NNTPReply.AUTHINFO_REQUIRED || reply.getCode() == NNTPReply.AUTHINFO_SIMPLE_REQUIRED) {
+            if (debug) {
+                debugOut("Authentication required received from server.");
+            }
+            // authenticate with the server, if necessary
+            processAuthentication(reply.getCode());
+            // if we've safely authenticated, we can reissue the command and
+            // process the response.
+            sendLine(data);
+            reply = getReply();
+        }
+        return reply;
+    }
+
+    /**
+     * Send a command to the server, returning the first response line back as a
+     * reply.
+     * 
+     * @param data
+     *            The data to send.
+     * 
+     * @return A reply object with the reply line.
+     * @exception MessagingException
+     */
+    public NNTPReply sendAuthCommand(String data) throws MessagingException {
+        sendLine(data);
+        return getReply();
+    }
+
+    /**
+     * Sends a message down the socket and terminates with the appropriate CRLF
+     */
+    public void sendLine(String data) throws MessagingException {
+        if (socket == null || !socket.isConnected()) {
+            throw new MessagingException("no connection");
+        }
+        try {
+            outputStream.write(data.getBytes());
+            outputStream.write(CR);
+            outputStream.write(LF);
+            outputStream.flush();
+        } catch (IOException e) {
+            throw new MessagingException(e.toString());
+        }
+    }
+
+    /**
+     * Get a reply line for an NNTP command.
+     * 
+     * @return An NNTP reply object from the stream.
+     */
+    public NNTPReply getReply() throws MessagingException {
+        lastServerResponse = new NNTPReply(receiveLine());
+        return lastServerResponse;
+    }
+
+    /**
+     * Retrieve the last response received from the NNTP server.
+     * 
+     * @return The raw response string (including the error code) returned from
+     *         the NNTP server.
+     */
+    public String getLastServerResponse() {
+        if (lastServerResponse == null) {
+            return "";
+        }
+        return lastServerResponse.getReply();
+    }
+
+    /**
+     * Receives one line from the server. A line is a sequence of bytes
+     * terminated by a CRLF
+     * 
+     * @return the line from the server as String
+     */
+    public String receiveLine() throws MessagingException {
+        if (socket == null || !socket.isConnected()) {
+            throw new MessagingException("no connection");
+        }
+
+        try {
+            String line = in.readLine();
+            if (line == null) {
+                throw new MessagingException("Unexpected end of stream");
+            }
+            return line;
+        } catch (IOException e) {
+            throw new MessagingException("Error reading from server", e);
+        }
+    }
+
+    /**
+     * Retrieve the SASL realm used for DIGEST-MD5 authentication. This will
+     * either be explicitly set, or retrieved using the mail.nntp.sasl.realm
+     * session property.
+     * 
+     * @return The current realm information (which can be null).
+     */
+    public String getSASLRealm() {
+        // if the realm is null, retrieve it using the realm session property.
+        if (realm == null) {
+            realm = getProperty(MAIL_NNTP_SASL_REALM);
+        }
+        return realm;
+    }
+
+    /**
+     * Explicitly set the SASL realm used for DIGEST-MD5 authenticaiton.
+     * 
+     * @param name
+     *            The new realm name.
+     */
+    public void setSASLRealm(String name) {
+        realm = name;
+    }
+
+    /**
+     * Authenticate with the server, if necessary (or possible).
+     */
+    protected void processAuthentication(int request) throws MessagingException {
+        // we need to authenticate, but we don't have userid/password
+        // information...fail this
+        // immediately.
+        if (username == null || password == null) {
+            throw new MessagingException("Server requires user authentication");
+        }
+
+        if (request == NNTPReply.AUTHINFO_SIMPLE_REQUIRED) {
+            processAuthinfoSimple();
+        } else {
+            if (!processAuthinfoSasl()) {
+                processAuthinfoUser();
+            }
+        }
+    }
+
+    /**
+     * Process an AUTHINFO SIMPLE command. Not widely used, but if the server
+     * asks for it, we can respond.
+     * 
+     * @exception MessagingException
+     */
+    protected void processAuthinfoSimple() throws MessagingException {
+        NNTPReply reply = sendAuthCommand("AUTHINFO SIMPLE");
+        if (reply.getCode() != NNTPReply.AUTHINFO_CONTINUE) {
+            throw new MessagingException("Error authenticating with server using AUTHINFO SIMPLE");
+        }
+        reply = sendAuthCommand(username + " " + password);
+        if (reply.getCode() != NNTPReply.AUTHINFO_ACCEPTED) {
+            throw new MessagingException("Error authenticating with server using AUTHINFO SIMPLE");
+        }
+    }
+
+    /**
+     * Process AUTHINFO GENERIC. Right now, this appears not to be widely used
+     * and information on how the conversations are handled for different auth
+     * types is lacking, so right now, this just returns false to force the
+     * userid/password form to be used.
+     * 
+     * @return Always returns false.
+     * @exception MessagingException
+     */
+    protected boolean processAuthinfoGeneric() throws MessagingException {
+        return false;
+    }
+
+    /**
+     * Process AUTHINFO SASL.
+     * 
+     * @return Returns true if the server support a SASL authentication
+     *         mechanism and accepted reponse challenges.
+     * @exception MessagingException
+     */
+    protected boolean processAuthinfoSasl() throws MessagingException {
+        ClientAuthenticator authenticator = null;
+
+        // now go through the progression of mechanisms we support, from the
+        // most secure to the
+        // least secure.
+
+        if (supportsAuthentication(AUTHENTICATION_DIGESTMD5)) {
+            authenticator = new DigestMD5Authenticator(host, username, password, getSASLRealm());
+        } else if (supportsAuthentication(AUTHENTICATION_CRAMMD5)) {
+            authenticator = new CramMD5Authenticator(username, password);
+        } else if (supportsAuthentication(AUTHENTICATION_LOGIN)) {
+            authenticator = new LoginAuthenticator(username, password);
+        } else if (supportsAuthentication(AUTHENTICATION_PLAIN)) {
+            authenticator = new PlainAuthenticator(username, password);
+        } else {
+            // can't find a mechanism we support in common
+            return false;
+        }
+
+        if (debug) {
+            debugOut("Authenticating for user: " + username + " using " + authenticator.getMechanismName());
+        }
+
+        // if the authenticator has some initial data, we compose a command
+        // containing the initial data.
+        if (authenticator.hasInitialResponse()) {
+            StringBuffer command = new StringBuffer();
+            // the auth command initiates the handshaking.
+            command.append("AUTHINFO SASL ");
+            // and tell the server which mechanism we're using.
+            command.append(authenticator.getMechanismName());
+            command.append(" ");
+            // and append the response data
+            command.append(new String(Base64.encode(authenticator.evaluateChallenge(null))));
+            // send the command now
+            sendLine(command.toString());
+        }
+        // we just send an auth command with the command type.
+        else {
+            StringBuffer command = new StringBuffer();
+            // the auth command initiates the handshaking.
+            command.append("AUTHINFO SASL");
+            // and tell the server which mechanism we're using.
+            command.append(authenticator.getMechanismName());
+            // send the command now
+            sendLine(command.toString());
+        }
+
+        // now process the challenge sequence. We get a 235 response back when
+        // the server accepts the
+        // authentication, and a 334 indicates we have an additional challenge.
+        while (true) {
+            // get the next line, and if it is an error response, return now.
+            NNTPReply line = getReply();
+
+            // if we get a completion return, we've passed muster, so give an
+            // authentication response.
+            if (line.getCode() == NNTPReply.AUTHINFO_ACCEPTED || line.getCode() == NNTPReply.AUTHINFO_ACCEPTED_FINAL) {
+                if (debug) {
+                    debugOut("Successful SMTP authentication");
+                }
+                return true;
+            }
+            // we have an additional challenge to process.
+            else if (line.getCode() == NNTPReply.AUTHINFO_CHALLENGE) {
+                // Does the authenticator think it is finished? We can't answer
+                // an additional challenge,
+                // so fail this.
+                if (authenticator.isComplete()) {
+                    if (debug) {
+                        debugOut("Extra authentication challenge " + line);
+                    }
+                    return false;
+                }
+
+                // we're passed back a challenge value, Base64 encoded.
+                byte[] challenge = Base64.decode(line.getMessage().getBytes());
+
+                // have the authenticator evaluate and send back the encoded
+                // response.
+                sendLine(new String(Base64.encode(authenticator.evaluateChallenge(challenge))));
+            }
+            // completion or challenge are the only responses we know how to
+            // handle. Anything else must
+            // be a failure.
+            else {
+                if (debug) {
+                    debugOut("Authentication failure " + line);
+                }
+                return false;
+            }
+        }
+    }
+
+    /**
+     * Process an AUTHINFO USER command. Most common form of NNTP
+     * authentication.
+     * 
+     * @exception MessagingException
+     */
+    protected void processAuthinfoUser() throws MessagingException {
+        NNTPReply reply = sendAuthCommand("AUTHINFO USER " + username);
+        // accepted without a password (uncommon, but allowed), we're done
+        if (reply.getCode() == NNTPReply.AUTHINFO_ACCEPTED) {
+            return;
+        }
+        // the only other non-error response is continue.
+        if (reply.getCode() != NNTPReply.AUTHINFO_CONTINUE) {
+            throw new MessagingException("Error authenticating with server using AUTHINFO USER: " + reply);
+        }
+        // now send the password. We expect an accepted response.
+        reply = sendAuthCommand("AUTHINFO PASS " + password);
+        if (reply.getCode() != NNTPReply.AUTHINFO_ACCEPTED) {
+            throw new MessagingException("Error authenticating with server using AUTHINFO SIMPLE");
+        }
+    }
+
+    /**
+     * Internal debug output routine.
+     * 
+     * @param value
+     *            The string value to output.
+     */
+    protected void debugOut(String message) {
+        debugStream.println("NNTPTransport DEBUG: " + message);
+    }
+
+    /**
+     * Internal debugging routine for reporting exceptions.
+     * 
+     * @param message
+     *            A message associated with the exception context.
+     * @param e
+     *            The received exception.
+     */
+    protected void debugOut(String message, Throwable e) {
+        debugOut("Received exception -> " + message);
+        debugOut("Exception message -> " + e.getMessage());
+        e.printStackTrace(debugStream);
+    }
+
+    /**
+     * Indicate whether posting is allowed for a given server.
+     * 
+     * @return True if the server allows posting, false if the server is
+     *         read-only.
+     */
+    public boolean isPostingAllowed() {
+        return postingAllowed;
+    }
+
+    /**
+     * Retrieve the welcome string sent back from the server.
+     * 
+     * @return The server provided welcome string.
+     */
+    public String getWelcomeString() {
+        return welcomeString;
+    }
+
+    /**
+     * Return the server host for this connection.
+     * 
+     * @return The String name of the server host.
+     */
+    public String getHost() {
+        return host;
+    }
+
+    /**
+     * Get a property associated with this mail protocol.
+     * 
+     * @param name
+     *            The name of the property.
+     * 
+     * @return The property value (returns null if the property has not been
+     *         set).
+     */
+    String getProperty(String name) {
+        // the name we're given is the least qualified part of the name. We
+        // construct the full property name
+        // using the protocol (either "nntp" or "nntp-post").
+        String fullName = "mail." + protocol + "." + name;
+        return session.getProperty(fullName);
+    }
+
+    /**
+     * Get a property associated with this mail session. Returns the provided
+     * default if it doesn't exist.
+     * 
+     * @param name
+     *            The name of the property.
+     * @param defaultValue
+     *            The default value to return if the property doesn't exist.
+     * 
+     * @return The property value (returns defaultValue if the property has not
+     *         been set).
+     */
+    String getProperty(String name, String defaultValue) {
+        // the name we're given is the least qualified part of the name. We
+        // construct the full property name
+        // using the protocol (either "nntp" or "nntp-post").
+        String fullName = "mail." + protocol + "." + name;
+        return SessionUtil.getProperty(session, fullName, defaultValue);
+    }
+
+    /**
+     * Get a property associated with this mail session as an integer value.
+     * Returns the default value if the property doesn't exist or it doesn't
+     * have a valid int value.
+     * 
+     * @param name
+     *            The name of the property.
+     * @param defaultValue
+     *            The default value to return if the property doesn't exist.
+     * 
+     * @return The property value converted to an int.
+     */
+    int getIntProperty(String name, int defaultValue) {
+        // the name we're given is the least qualified part of the name. We
+        // construct the full property name
+        // using the protocol (either "nntp" or "nntp-post").
+        String fullName = "mail." + protocol + "." + name;
+        return SessionUtil.getIntProperty(session, fullName, defaultValue);
+    }
+
+    /**
+     * Get a property associated with this mail session as an boolean value.
+     * Returns the default value if the property doesn't exist or it doesn't
+     * have a valid int value.
+     * 
+     * @param name
+     *            The name of the property.
+     * @param defaultValue
+     *            The default value to return if the property doesn't exist.
+     * 
+     * @return The property value converted to a boolean
+     */
+    boolean getBooleanProperty(String name, boolean defaultValue) {
+        // the name we're given is the least qualified part of the name. We
+        // construct the full property name
+        // using the protocol (either "nntp" or "nntp-post").
+        String fullName = "mail." + protocol + "." + name;
+        return SessionUtil.getBooleanProperty(session, fullName, defaultValue);
+    }
+}
diff --git a/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/transport/nntp/NNTPReply.java b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/transport/nntp/NNTPReply.java
new file mode 100644
index 0000000..6328b92
--- /dev/null
+++ b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/transport/nntp/NNTPReply.java
@@ -0,0 +1,240 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.geronimo.javamail.transport.nntp;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.mail.MessagingException;
+
+/**
+ * Util class to represent a reply from a NNTP server
+ * 
+ * @version $Rev$ $Date$
+ */
+public class NNTPReply {
+    // general server responses
+    public static final int POSTING_ALLOWED = 200;
+
+    public static final int NO_POSTING_ALLOWED = 201;
+
+    public static final int EXTENSIONS_SUPPORTED = 202;
+
+    public static final int SERVICE_DISCONTINUED = 400;
+
+    public static final int COMMAND_NOT_RECOGNIZED = 500;
+
+    public static final int COMMAND_SYNTAX_ERROR = 501;
+
+    public static final int PERMISSION_DENIED = 502;
+
+    public static final int PROGRAM_FAULT = 503;
+
+    // article responses
+    public static final int ARTICLE_FOLLOWS = 220;
+
+    public static final int HEAD_FOLLOWS = 221;
+
+    public static final int BODY_FOLLOWS = 222;
+
+    public static final int REQUEST_TEXT_SEPARATELY = 223;
+
+    public static final int OVERVIEW_FOLLOWS = 224;
+
+    public static final int NEW_ARTICLES_FOLLOWS = 230;
+
+    public static final int NEW_GROUPS_FOLLOWS = 231;
+
+    public static final int ARTICLE_TRANSFERRED = 235;
+
+    public static final int NO_NEWSGROUP_SELECTED = 412;
+
+    public static final int NO_ARTICLE_SELECTED = 420;
+
+    public static final int NO_ARTICLE_NUMBER = 423;
+
+    public static final int NO_ARTICLE_FOUND = 430;
+
+    // group responses
+    public static final int GROUP_SELECTED = 211;
+
+    public static final int NO_SUCH_NEWSGROUP = 411;
+
+    // post responses
+    public static final int POSTED_OK = 240;
+
+    public static final int SEND_ARTICLE = 340;
+
+    public static final int POSTING_NOT_ALLOWED = 440;
+
+    public static final int POSTING_FAILED = 441;
+
+    // quit responses
+    public static final int CLOSING_CONNECTION = 205;
+
+    // authentication responses
+    public static final int AUTHINFO_ACCEPTED = 250;
+
+    public static final int AUTHINFO_ACCEPTED_FINAL = 251;
+
+    public static final int AUTHINFO_CONTINUE = 350;
+
+    public static final int AUTHINFO_CHALLENGE = 350;
+
+    public static final int AUTHINFO_SIMPLE_REJECTED = 402;
+
+    public static final int AUTHENTICATION_ACCEPTED = 281;
+
+    public static final int MORE_AUTHENTICATION_REQUIRED = 381;
+
+    public static final int AUTHINFO_REQUIRED = 480;
+
+    public static final int AUTHINFO_SIMPLE_REQUIRED = 450;
+
+    public static final int AUTHENTICATION_REJECTED = 482;
+
+    // list active reponses
+    public static final int LIST_FOLLOWS = 215;
+
+    // The original reply string
+    private final String reply;
+
+    // returned message code
+    private final int code;
+
+    // the returned message text
+    private final String message;
+
+    // data associated with a long response command.
+    private ArrayList data;
+
+    NNTPReply(String s) throws MessagingException {
+        // save the reply
+        reply = s;
+
+        // In a normal response, the first 3 must be the return code. However,
+        // the response back from a QUIT command is frequently a null string.
+        // Therefore, if the result is
+        // too short, just default the code to -1 and use the entire text for
+        // the message.
+        if (s == null || s.length() < 3) {
+            code = -1;
+            message = s;
+            return;
+        }
+
+        try {
+            code = Integer.parseInt(s.substring(0, 3));
+
+            // message should be separated by a space OR a continuation
+            // character if this is a
+            // multi-line response.
+            if (s.length() > 4) {
+                message = s.substring(4);
+            } else {
+                message = "";
+            }
+        } catch (NumberFormatException e) {
+            throw new MessagingException("error in parsing reply code", e);
+        }
+    }
+
+    /**
+     * Retrieve data associated with a multi-line reponse from a server stream.
+     * 
+     * @param in
+     *            The reader that's the source of the additional lines.
+     * 
+     * @exception IOException
+     */
+    public void retrieveData(BufferedReader in) throws MessagingException {
+        try {
+            data = new ArrayList();
+
+            String line = in.readLine();
+            // read until the end of file or until we see the end of data
+            // marker.
+            while (line != null && !line.equals(".")) {
+                // this line is not the terminator, but it may have been byte
+                // stuffed. If it starts with
+                // '.', throw away the leading one.
+                if (line.startsWith(".")) {
+                    line = line.substring(1);
+                }
+
+                // just add the line to the list
+                data.add(line);
+                line = in.readLine();
+            }
+        } catch (IOException e) {
+            throw new MessagingException("Error reading message reply", e);
+        }
+    }
+
+    /**
+     * Retrieve the long-command data from this response.
+     * 
+     * @return The data list. Returns null if there is no associated data.
+     */
+    public List getData() {
+        return data;
+    }
+
+    /**
+     * Return the code value associated with the reply.
+     * 
+     * @return The integer code associated with the reply.
+     */
+    public int getCode() {
+        return this.code;
+    }
+
+    /**
+     * Get the message text associated with the reply.
+     * 
+     * @return The string value of the message from the reply.
+     */
+    public String getMessage() {
+        return this.message;
+    }
+
+    /**
+     * Retrieve the raw reply string for the reponse.
+     * 
+     * @return The original reply string from the server.
+     */
+    public String getReply() {
+        return reply;
+    }
+
+    /**
+     * Indicates if reply is an error condition
+     */
+    boolean isError() {
+        // error codes are all above 400
+        return code >= 400;
+    }
+
+    public String toString() {
+        return "CODE = " + getCode() + " : MSG = " + getMessage();
+    }
+}
diff --git a/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/transport/nntp/NNTPTransport.java b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/transport/nntp/NNTPTransport.java
new file mode 100644
index 0000000..9e953eb
--- /dev/null
+++ b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/transport/nntp/NNTPTransport.java
@@ -0,0 +1,374 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.geronimo.javamail.transport.nntp;
+
+import java.io.PrintStream;
+import java.util.ArrayList;
+
+import javax.mail.Address;
+import javax.mail.Message;
+import javax.mail.MessagingException;
+import javax.mail.Session;
+import javax.mail.Transport;
+import javax.mail.URLName;
+import javax.mail.event.TransportEvent;
+import javax.mail.internet.InternetAddress;
+import javax.mail.internet.MimeMessage;
+import javax.mail.internet.NewsAddress;
+
+import org.apache.geronimo.mail.util.SessionUtil;
+
+/**
+ * Simple implementation of NNTP transport. Just does plain RFC977-ish delivery.
+ * <p/> There is no way to indicate failure for a given recipient (it's possible
+ * to have a recipient address rejected). The sun impl throws exceptions even if
+ * others successful), but maybe we do a different way... <p/>
+ * 
+ * @version $Rev$ $Date$
+ */
+public class NNTPTransport extends Transport {
+
+    /**
+     * property keys for protocol properties.
+     */
+    protected static final String NNTP_AUTH = "auth";
+
+    protected static final String NNTP_PORT = "port";
+
+    protected static final String NNTP_FROM = "from";
+
+    protected static final String protocol = "nntp-post";
+
+    protected static final int DEFAULT_NNTP_PORT = 119;
+
+    // our active connection object (shared code with the NNTPStore).
+    protected NNTPConnection connection;
+
+    // our session provided debug output stream.
+    protected PrintStream debugStream;
+
+    /**
+     * Normal constructor for an NNTPTransport() object. This constructor is
+     * used to build a transport instance for the "smtp" protocol.
+     * 
+     * @param session
+     *            The attached session.
+     * @param name
+     *            An optional URLName object containing target information.
+     */
+    public NNTPTransport(Session session, URLName name) {
+        super(session, name);
+
+        // get our debug output.
+        debugStream = session.getDebugOut();
+    }
+
+    /**
+     * Do the protocol connection for an NNTP transport. This handles server
+     * authentication, if possible. Returns false if unable to connect to the
+     * server.
+     * 
+     * @param host
+     *            The target host name.
+     * @param port
+     *            The server port number.
+     * @param user
+     *            The authentication user (if any).
+     * @param password
+     *            The server password. Might not be sent directly if more
+     *            sophisticated authentication is used.
+     * 
+     * @return true if we were able to connect to the server properly, false for
+     *         any failures.
+     * @exception MessagingException
+     */
+    protected boolean protocolConnect(String host, int port, String username, String password)
+            throws MessagingException {
+        if (debug) {
+            debugOut("Connecting to server " + host + ":" + port + " for user " + username);
+        }
+
+        // first check to see if we need to authenticate. If we need this, then
+        // we must have a username and
+        // password specified. Failing this may result in a user prompt to
+        // collect the information.
+        boolean mustAuthenticate = SessionUtil.getBooleanProperty(session, NNTP_AUTH, false);
+
+        // if we need to authenticate, and we don't have both a userid and
+        // password, then we fail this
+        // immediately. The Service.connect() method will try to obtain the user
+        // information and retry the
+        // connection one time.
+        if (mustAuthenticate && (username == null || password == null)) {
+            return false;
+        }
+
+        // if the port is defaulted, then see if we have something configured in
+        // the session.
+        // if not configured, we just use the default default.
+        if (port == -1) {
+            // check for a property and fall back on the default if it's not
+            // set.
+            port = SessionUtil.getIntProperty(session, NNTP_PORT, DEFAULT_NNTP_PORT);
+        }
+
+        // create socket and connect to server.
+        connection = new NNTPConnection(protocol, session, host, port, username, password, debug);
+        connection.connect();
+
+        // we're going to return success here, but in truth, the server may end
+        // up asking for our
+        // bonafides at any time, and we'll be expected to authenticate then.
+        return true;
+    }
+
+    /**
+     * Send a message to multiple addressees.
+     * 
+     * @param message
+     *            The message we're sending.
+     * @param addresses
+     *            An array of addresses to send to.
+     * 
+     * @exception MessagingException
+     */
+    public void sendMessage(Message message, Address[] addresses) throws MessagingException {
+        if (!isConnected()) {
+            throw new IllegalStateException("Not connected");
+        }
+
+        if (!connection.isPostingAllowed()) {
+            throw new MessagingException("Posting disabled for host server");
+        }
+        // don't bother me w/ null messages or no addreses
+        if (message == null) {
+            throw new MessagingException("Null message");
+        }
+
+        // NNTP only handles instances of MimeMessage, not the more general
+        // message case.
+        if (!(message instanceof MimeMessage)) {
+            throw new MessagingException("NNTP can only send MimeMessages");
+        }
+
+        // need to sort the from value out from a variety of sources.
+        InternetAddress from = null;
+
+        Address[] fromAddresses = message.getFrom();
+
+        // If the message has a From address set, we just use that. Otherwise,
+        // we set a From using
+        // the property version, if available.
+        if (fromAddresses == null || fromAddresses.length == 0) {
+            // the from value can be set explicitly as a property
+            String defaultFrom = session.getProperty(NNTP_FROM);
+            if (defaultFrom == null) {
+                message.setFrom(new InternetAddress(defaultFrom));
+            }
+        }
+
+        // we must have a message list.
+        if (addresses == null || addresses.length == 0) {
+            throw new MessagingException("Null or empty address array");
+        }
+
+        boolean haveGroup = false;
+
+        // enforce the requirement that all of the targets are NewsAddress
+        // instances.
+        for (int i = 0; i < addresses.length; i++) {
+            if (!(addresses[i] instanceof NewsAddress)) {
+                System.out.println("Illegal address is of class " + addresses[i].getClass());
+                throw new MessagingException("Illegal NewsAddress " + addresses[i]);
+            }
+        }
+
+        // event notifcation requires we send lists of successes and failures
+        // broken down by category.
+        // The categories are:
+        //
+        // 1) addresses successfully processed.
+        // 2) addresses deemed valid, but had a processing failure that
+        // prevented sending.
+        // 3) addressed deemed invalid (basically all other processing
+        // failures).
+        ArrayList sentAddresses = new ArrayList();
+        ArrayList unsentAddresses = new ArrayList();
+        ArrayList invalidAddresses = new ArrayList();
+
+        boolean sendFailure = false;
+
+        // now try to post this message to the different news groups.
+        for (int i = 0; i < addresses.length; i++) {
+            try {
+                // select the target news group
+                NNTPReply reply = connection.selectGroup(((NewsAddress) addresses[i]).getNewsgroup());
+
+                if (reply.getCode() != NNTPReply.GROUP_SELECTED) {
+                    invalidAddresses.add(addresses[i]);
+                    sendFailure = true;
+                } else {
+                    // send data
+                    connection.sendPost(message);
+                    sentAddresses.add(addresses[i]);
+                }
+            } catch (MessagingException e) {
+                unsentAddresses.add(addresses[i]);
+                sendFailure = true;
+            }
+        }
+
+        // create our lists for notification and exception reporting from this
+        // point on.
+        Address[] sent = (Address[]) sentAddresses.toArray(new Address[0]);
+        Address[] unsent = (Address[]) unsentAddresses.toArray(new Address[0]);
+        Address[] invalid = (Address[]) invalidAddresses.toArray(new Address[0]);
+
+        if (sendFailure) {
+            // did we deliver anything at all?
+            if (sent.length == 0) {
+                // notify of the error.
+                notifyTransportListeners(TransportEvent.MESSAGE_NOT_DELIVERED, sent, unsent, invalid, message);
+            } else {
+                // notify that we delivered at least part of this
+                notifyTransportListeners(TransportEvent.MESSAGE_PARTIALLY_DELIVERED, sent, unsent, invalid, message);
+            }
+
+            throw new MessagingException("Error posting NNTP message");
+        }
+
+        // notify our listeners of successful delivery.
+        notifyTransportListeners(TransportEvent.MESSAGE_DELIVERED, sent, unsent, invalid, message);
+    }
+
+    /**
+     * Close the connection. On completion, we'll be disconnected from the
+     * server and unable to send more data.
+     * 
+     * @exception MessagingException
+     */
+    public void close() throws MessagingException {
+        // This is done to ensure proper event notification.
+        super.close();
+        connection.close();
+        connection = null;
+    }
+
+    /**
+     * Internal debug output routine.
+     * 
+     * @param value
+     *            The string value to output.
+     */
+    protected void debugOut(String message) {
+        debugStream.println("NNTPTransport DEBUG: " + message);
+    }
+
+    /**
+     * Internal debugging routine for reporting exceptions.
+     * 
+     * @param message
+     *            A message associated with the exception context.
+     * @param e
+     *            The received exception.
+     */
+    protected void debugOut(String message, Throwable e) {
+        debugOut("Received exception -> " + message);
+        debugOut("Exception message -> " + e.getMessage());
+        e.printStackTrace(debugStream);
+    }
+
+    /**
+     * Get a property associated with this mail protocol.
+     * 
+     * @param name
+     *            The name of the property.
+     * 
+     * @return The property value (returns null if the property has not been
+     *         set).
+     */
+    String getProperty(String name) {
+        // the name we're given is the least qualified part of the name. We
+        // construct the full property name
+        // using the protocol (either "nntp" or "nntp-post").
+        String fullName = "mail." + protocol + "." + name;
+        return session.getProperty(fullName);
+    }
+
+    /**
+     * Get a property associated with this mail session. Returns the provided
+     * default if it doesn't exist.
+     * 
+     * @param name
+     *            The name of the property.
+     * @param defaultValue
+     *            The default value to return if the property doesn't exist.
+     * 
+     * @return The property value (returns defaultValue if the property has not
+     *         been set).
+     */
+    String getProperty(String name, String defaultValue) {
+        // the name we're given is the least qualified part of the name. We
+        // construct the full property name
+        // using the protocol (either "nntp" or "nntp-post").
+        String fullName = "mail." + protocol + "." + name;
+        return SessionUtil.getProperty(session, fullName, defaultValue);
+    }
+
+    /**
+     * Get a property associated with this mail session as an integer value.
+     * Returns the default value if the property doesn't exist or it doesn't
+     * have a valid int value.
+     * 
+     * @param name
+     *            The name of the property.
+     * @param defaultValue
+     *            The default value to return if the property doesn't exist.
+     * 
+     * @return The property value converted to an int.
+     */
+    int getIntProperty(String name, int defaultValue) {
+        // the name we're given is the least qualified part of the name. We
+        // construct the full property name
+        // using the protocol (either "nntp" or "nntp-post").
+        String fullName = "mail." + protocol + "." + name;
+        return SessionUtil.getIntProperty(session, fullName, defaultValue);
+    }
+
+    /**
+     * Get a property associated with this mail session as an boolean value.
+     * Returns the default value if the property doesn't exist or it doesn't
+     * have a valid int value.
+     * 
+     * @param name
+     *            The name of the property.
+     * @param defaultValue
+     *            The default value to return if the property doesn't exist.
+     * 
+     * @return The property value converted to a boolean
+     */
+    boolean getBooleanProperty(String name, boolean defaultValue) {
+        // the name we're given is the least qualified part of the name. We
+        // construct the full property name
+        // using the protocol (either "nntp" or "nntp-post").
+        String fullName = "mail." + protocol + "." + name;
+        return SessionUtil.getBooleanProperty(session, fullName, defaultValue);
+    }
+}
diff --git a/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/transport/nntp/StringListInputStream.java b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/transport/nntp/StringListInputStream.java
new file mode 100644
index 0000000..62dab16
--- /dev/null
+++ b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/transport/nntp/StringListInputStream.java
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.geronimo.javamail.transport.nntp;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.List;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class StringListInputStream extends InputStream {
+    // the list of lines we're reading from
+    protected List lines;
+
+    // the next line to process.
+    protected int nextLine = 0;
+
+    // current buffer of bytes to read from
+    byte[] buffer;
+
+    // current offset within the buffer;
+    int offset;
+
+    // indicator that we've left off at a split between the CR and LF of a line
+    // break.
+    boolean atLineBreak = false;
+
+    public StringListInputStream(List lines) throws IOException {
+        this.lines = lines;
+        nextLine = 0;
+        buffer = null;
+        offset = 0;
+        atLineBreak = false;
+
+        // if we have at least one line in the list, get the bytes now.
+        if (lines.size() > 0) {
+            nextBuffer();
+        }
+    }
+
+    /**
+     * Just override the single byte read version, which handles all of the
+     * lineend markers correctly.
+     * 
+     * @return The next byte from the stream or -1 if we've hit the EOF.
+     */
+    public int read() throws IOException {
+        // leave off at the split between a line?
+        if (atLineBreak) {
+            // flip this off and return the second line end character. Also step
+            // to the next line.
+            atLineBreak = false;
+            nextBuffer();
+            return '\n';
+        }
+        // gone past the end? Got an EOF
+        if (buffer == null) {
+            return -1;
+        }
+
+        // reach the end of the line?
+        if (offset >= buffer.length) {
+            // we're now working on a virtual linebreak
+            atLineBreak = true;
+            return '\r';
+        }
+        // just return the next byte
+        return buffer[offset++];
+
+    }
+
+    /**
+     * Step to the next buffer of string data.
+     * 
+     * @exception IOException
+     */
+    protected void nextBuffer() throws IOException {
+        // give an eof check.
+        if (nextLine >= lines.size()) {
+            buffer = null;
+        } else {
+            try {
+                String next = (String) lines.get(nextLine++);
+                buffer = next.getBytes("US-ASCII");
+
+            } catch (UnsupportedEncodingException e) {
+                throw new IOException("Invalid string encoding");
+            }
+        }
+
+        offset = 0;
+    }
+}
diff --git a/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/transport/smtp/MalformedSMTPReplyException.java b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/transport/smtp/MalformedSMTPReplyException.java
new file mode 100644
index 0000000..ad1b7fa
--- /dev/null
+++ b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/transport/smtp/MalformedSMTPReplyException.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.geronimo.javamail.transport.smtp;
+
+/**
+ * Exception for when a SMTP reply string has a problem
+ * 
+ * @version $Rev$ $Date$
+ */
+class MalformedSMTPReplyException extends Exception {
+    MalformedSMTPReplyException() {
+        super();
+    }
+
+    MalformedSMTPReplyException(String msg) {
+        super(msg);
+    }
+
+    MalformedSMTPReplyException(String msg, Exception t) {
+        super(msg, t);
+    }
+}
diff --git a/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/transport/smtp/SMTPAddressFailedException.java b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/transport/smtp/SMTPAddressFailedException.java
new file mode 100644
index 0000000..bfd2699
--- /dev/null
+++ b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/transport/smtp/SMTPAddressFailedException.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.geronimo.javamail.transport.smtp;
+
+import javax.mail.MessagingException;
+import javax.mail.internet.InternetAddress;
+
+public class SMTPAddressFailedException extends MessagingException {
+    // the failing address
+    InternetAddress addr;
+
+    // the failing command
+    protected String cmd;
+
+    // the error code for the failure
+    protected int rc;
+
+    /**
+     * Constructor for an SMTPAddressFailingException.
+     * 
+     * @param addr
+     *            The failing address.
+     * @param cmd
+     *            The failing command string.
+     * @param rc
+     *            The error code for the command.
+     * @param err
+     *            An error message for the exception.
+     */
+    SMTPAddressFailedException(InternetAddress addr, java.lang.String cmd, int rc, java.lang.String err) {
+        super(err);
+        this.cmd = cmd;
+        this.rc = rc;
+        this.addr = addr;
+    }
+
+    /**
+     * Get the failing command string for the exception.
+     * 
+     * @return The string value of the failing command.
+     */
+    public String getCommand() {
+        return cmd;
+    }
+
+    /**
+     * The failing command return code.
+     * 
+     * @return The failure return code.
+     */
+    public int getReturnCode() {
+        return rc;
+    }
+
+    /**
+     * Retrieve the internet address associated with this exception.
+     * 
+     * @return The provided InternetAddress object.
+     */
+    public InternetAddress getAddress() {
+        return addr;
+    }
+}
diff --git a/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/transport/smtp/SMTPAddressSucceededException.java b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/transport/smtp/SMTPAddressSucceededException.java
new file mode 100644
index 0000000..990d656
--- /dev/null
+++ b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/transport/smtp/SMTPAddressSucceededException.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.geronimo.javamail.transport.smtp;
+
+import javax.mail.MessagingException;
+import javax.mail.internet.InternetAddress;
+
+public class SMTPAddressSucceededException extends MessagingException {
+    // the succeeding address
+    InternetAddress addr;
+
+    // the failing command
+    protected String cmd;
+
+    // the error code for the failure
+    protected int rc;
+
+    /**
+     * Constructor for an SMTPAddressSucceededException.
+     * 
+     * @param addr
+     *            The succeeding address.
+     * @param cmd
+     *            The succeeding command string.
+     * @param rc
+     *            The error code for the command.
+     * @param err
+     *            An error message for the exception.
+     */
+    SMTPAddressSucceededException(InternetAddress addr, java.lang.String cmd, int rc, java.lang.String err) {
+        super(err);
+        this.cmd = cmd;
+        this.rc = rc;
+        this.addr = addr;
+    }
+
+    /**
+     * Get the failing command string for the exception.
+     * 
+     * @return The string value of the failing command.
+     */
+    public String getCommand() {
+        return cmd;
+    }
+
+    /**
+     * The failing command return code.
+     * 
+     * @return The failure return code.
+     */
+    public int getReturnCode() {
+        return rc;
+    }
+
+    /**
+     * Retrieve the internet address associated with this exception.
+     * 
+     * @return The provided InternetAddress object.
+     */
+    public InternetAddress getAddress() {
+        return addr;
+    }
+}
diff --git a/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/transport/smtp/SMTPMessage.java b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/transport/smtp/SMTPMessage.java
new file mode 100644
index 0000000..91d8c95
--- /dev/null
+++ b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/transport/smtp/SMTPMessage.java
@@ -0,0 +1,237 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.geronimo.javamail.transport.smtp;
+
+import java.io.InputStream;
+
+import javax.mail.MessagingException;
+import javax.mail.Session;
+import javax.mail.internet.MimeMessage;
+
+public class SMTPMessage extends MimeMessage {
+
+    // never notify
+    public static final int NOTIFY_NEVER = -1;
+
+    // notify of successful deliveries.
+    public static final int NOTIFY_SUCCESS = 1;
+
+    // notify of delivery failures.
+    public static final int NOTIFY_FAILURE = 2;
+
+    // notify of delivery delays
+    public static final int NOTIFY_DELAY = 4;
+
+    // return full message with status notifications
+    public static final int RETURN_FULL = 1;
+
+    // return only message headers with status notifications
+    public static final int RETURN_HDRS = 2;
+
+    // support 8BitMime encodings
+    protected boolean allow8bitMIME = false;
+
+    // a from address specified in the message envelope. Overrides other from
+    // sources.
+    protected String envelopeFrom = null;
+
+    // an option string to append to the MAIL command on sending.
+    protected String mailExtension = null;
+
+    // SMTP mail notification options if DSN is supported.
+    protected int notifyOptions = 0;
+
+    // DSN return option notification values.
+    protected int returnOption = 0;
+
+    // allow sending if some addresses give errors.
+    protected boolean sendPartial = false;
+
+    // an RFC 2554 AUTH= value.
+    protected String submitter = null;
+
+    /**
+     * Default (and normal) constructor for an SMTPMessage.
+     * 
+     * @param session
+     *            The hosting Javamail Session.
+     */
+    public SMTPMessage(Session session) {
+        // this is a simple one.
+        super(session);
+    }
+
+    /**
+     * Construct an SMTPMessage instance by reading and parsing the data from
+     * the provided InputStream. The InputStream will be left positioned at the
+     * end of the message data on constructor completion.
+     * 
+     * @param session
+     *            The hosting Javamail Session.
+     */
+    public SMTPMessage(Session session, InputStream source) throws MessagingException {
+        // this is a simple one.
+        super(session, source);
+    }
+
+    /**
+     * Construct an SMTPMimeMessage from another source MimeMessage object. The
+     * new object and the old object are independent of each other.
+     * 
+     * @param source
+     *            The source MimeMessage object.
+     */
+    public SMTPMessage(MimeMessage source) throws MessagingException {
+        super(source);
+    }
+
+    /**
+     * Change the allow8BitMime attribute for the message.
+     * 
+     * @param a
+     *            The new setting.
+     */
+    public void setAllow8bitMIME(boolean a) {
+        allow8bitMIME = a;
+    }
+
+    /**
+     * Retrieve the current 8bitMIME attribute.
+     * 
+     * @return The current attribute value.
+     */
+    public boolean getAllow8bitMIME() {
+        return allow8bitMIME;
+    }
+
+    /**
+     * Change the envelopeFrom attribute for the message.
+     * 
+     * @param from
+     *            The new setting.
+     */
+    public void setEnvelopeFrom(String from) {
+        envelopeFrom = from;
+    }
+
+    /**
+     * Retrieve the current evelopeFrom attribute.
+     * 
+     * @return The current attribute value.
+     */
+    public String getEnvelopeFrom() {
+        return envelopeFrom;
+    }
+
+    /**
+     * Change the mailExtension attribute for the message.
+     * 
+     * @param e
+     *            The new setting.
+     */
+    public void setMailExtension(String e) {
+        mailExtension = e;
+    }
+
+    /**
+     * Retrieve the current mailExtension attribute.
+     * 
+     * @return The current attribute value.
+     */
+    public String getMailExtension() {
+        return mailExtension;
+    }
+
+    /**
+     * Change the notifyOptions attribute for the message.
+     * 
+     * @param options
+     *            The new setting.
+     */
+    public void setNotifyOptions(int options) {
+        notifyOptions = options;
+    }
+
+    /**
+     * Retrieve the current notifyOptions attribute.
+     * 
+     * @return The current attribute value.
+     */
+    public int getNotifyOptions() {
+        return notifyOptions;
+    }
+
+    /**
+     * Change the returnOptions attribute for the message.
+     * 
+     * @param option
+     *            The new setting.
+     */
+    public void setReturnOption(int option) {
+        returnOption = option;
+    }
+
+    /**
+     * Retrieve the current returnOption attribute.
+     * 
+     * @return The current attribute value.
+     */
+    public int getReturnOption() {
+        return returnOption;
+    }
+
+    /**
+     * Change the sendPartial attribute for the message.
+     * 
+     * @param a
+     *            The new setting.
+     */
+    public void setSendPartial(boolean a) {
+        sendPartial = a;
+    }
+
+    /**
+     * Retrieve the current sendPartial attribute.
+     * 
+     * @return The current attribute value.
+     */
+    public boolean getSendPartial() {
+        return sendPartial;
+    }
+
+    /**
+     * Change the submitter attribute for the message.
+     * 
+     * @param s
+     *            The new setting.
+     */
+    public void setSubmitter(String s) {
+        submitter = s;
+    }
+
+    /**
+     * Retrieve the current submitter attribute.
+     * 
+     * @return The current attribute value.
+     */
+    public String getSubmitter() {
+        return submitter;
+    }
+}
diff --git a/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/transport/smtp/SMTPReply.java b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/transport/smtp/SMTPReply.java
new file mode 100644
index 0000000..dbee40e
--- /dev/null
+++ b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/transport/smtp/SMTPReply.java
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.geronimo.javamail.transport.smtp;
+
+/**
+ * Util class to represent a reply from a SMTP server
+ * 
+ * @version $Rev$ $Date$
+ */
+class SMTPReply {
+    // The original reply string
+    private final String reply;
+
+    // returned message code
+    private final int code;
+
+    // the returned message text
+    private final String message;
+
+    // indicates that this is a continuation response
+    private boolean continued;
+
+    SMTPReply(String s) throws MalformedSMTPReplyException {
+        // save the reply
+        reply = s;
+
+        // In a normal response, the first 3 must be the return code. However,
+        // the response back from a QUIT command is frequently a null string.
+        // Therefore, if the result is
+        // too short, just default the code to -1 and use the entire text for
+        // the message.
+        if (s == null || s.length() < 3) {
+            code = -1;
+            message = s;
+            return;
+        }
+
+        try {
+            continued = false;
+            code = Integer.parseInt(s.substring(0, 3));
+
+            // message should be separated by a space OR a continuation
+            // character if this is a
+            // multi-line response.
+            if (s.length() > 4) {
+                //
+                if (s.charAt(3) == '-') {
+                    continued = true;
+                }
+                message = s.substring(4);
+            } else {
+                message = "";
+            }
+        } catch (NumberFormatException e) {
+            throw new MalformedSMTPReplyException("error in parsing code", e);
+        }
+    }
+
+    /**
+     * Return the code value associated with the reply.
+     * 
+     * @return The integer code associated with the reply.
+     */
+    public int getCode() {
+        return this.code;
+    }
+
+    /**
+     * Get the message text associated with the reply.
+     * 
+     * @return The string value of the message from the reply.
+     */
+    public String getMessage() {
+        return this.message;
+    }
+
+    /**
+     * Retrieve the raw reply string for the reponse.
+     * 
+     * @return The original reply string from the server.
+     */
+    public String getReply() {
+        return reply;
+    }
+
+    /**
+     * Indicates if reply is an error condition
+     */
+    boolean isError() {
+        // error codes are all above 400
+        return code >= 400;
+    }
+
+    /**
+     * Indicates whether this response is flagged as part of a multiple line
+     * response.
+     * 
+     * @return true if the response has multiple lines, false if this is the
+     *         last line of the response.
+     */
+    public boolean isContinued() {
+        return continued;
+    }
+
+    public String toString() {
+        return "CODE = " + getCode() + " : MSG = " + getMessage();
+    }
+}
diff --git a/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/transport/smtp/SMTPSTransport.java b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/transport/smtp/SMTPSTransport.java
new file mode 100644
index 0000000..0794065
--- /dev/null
+++ b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/transport/smtp/SMTPSTransport.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.geronimo.javamail.transport.smtp;
+
+import javax.mail.Session;
+import javax.mail.URLName;
+
+public class SMTPSTransport extends SMTPTransport {
+    /**
+     * @param session
+     * @param name
+     */
+    public SMTPSTransport(Session session, URLName name) {
+        super(session, name, "smtps", 465, true);
+    }
+}
diff --git a/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/transport/smtp/SMTPSendFailedException.java b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/transport/smtp/SMTPSendFailedException.java
new file mode 100644
index 0000000..20ff08a
--- /dev/null
+++ b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/transport/smtp/SMTPSendFailedException.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.geronimo.javamail.transport.smtp;
+
+import javax.mail.Address;
+import javax.mail.SendFailedException;
+
+public class SMTPSendFailedException extends SendFailedException {
+    // the failing command
+    protected String cmd;
+
+    // the error code for the failure
+    protected int rc;
+
+    /**
+     * Constructor for an SMTPSendFaileException.
+     * 
+     * @param cmd
+     *            The failing command string.
+     * @param rc
+     *            The error code for the failing command.
+     * @param err
+     *            An error message for the exception.
+     * @param ex
+     *            Any associated nested exception.
+     * @param vs
+     *            An array of valid, sent addresses.
+     * @param vus
+     *            An array of addresses that were valid, but were unsent.
+     * @param inv
+     *            An array of addresses deemed invalid.
+     */
+    SMTPSendFailedException(java.lang.String cmd, int rc, java.lang.String err, java.lang.Exception ex, Address[] vs,
+            Address[] vus, Address[] inv) {
+        super(err, ex, vs, vus, inv);
+        this.cmd = cmd;
+        this.rc = rc;
+    }
+
+    /**
+     * Get the failing command string for the exception.
+     * 
+     * @return The string value of the failing command.
+     */
+    public String getCommand() {
+        return cmd;
+    }
+
+    /**
+     * The failing command return code.
+     * 
+     * @return The failure return code.
+     */
+    public int getReturnCode() {
+        return rc;
+    }
+}
diff --git a/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/transport/smtp/SMTPTransport.java b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/transport/smtp/SMTPTransport.java
new file mode 100644
index 0000000..7b96d52
--- /dev/null
+++ b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/transport/smtp/SMTPTransport.java
@@ -0,0 +1,2362 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.geronimo.javamail.transport.smtp;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.io.UnsupportedEncodingException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.StringTokenizer;
+
+import javax.mail.Address;
+import javax.mail.AuthenticationFailedException;
+import javax.mail.Message;
+import javax.mail.MessagingException;
+import javax.mail.Session;
+import javax.mail.Transport;
+import javax.mail.URLName;
+import javax.mail.event.TransportEvent;
+import javax.mail.internet.InternetAddress;
+import javax.mail.internet.MimeMessage;
+import javax.net.ssl.SSLSocket;
+
+import org.apache.geronimo.javamail.authentication.ClientAuthenticator;
+import org.apache.geronimo.javamail.authentication.CramMD5Authenticator;
+import org.apache.geronimo.javamail.authentication.DigestMD5Authenticator;
+import org.apache.geronimo.javamail.authentication.LoginAuthenticator;
+import org.apache.geronimo.javamail.authentication.PlainAuthenticator;
+import org.apache.geronimo.javamail.util.MIMEOutputStream;
+import org.apache.geronimo.javamail.util.TraceInputStream;
+import org.apache.geronimo.javamail.util.TraceOutputStream;
+import org.apache.geronimo.mail.util.Base64;
+import org.apache.geronimo.mail.util.XText;
+
+/**
+ * Simple implementation of SMTP transport. Just does plain RFC821-ish delivery.
+ * <p/> Supported properties : <p/>
+ * <ul>
+ * <li> mail.host : to set the server to deliver to. Default = localhost</li>
+ * <li> mail.smtp.port : to set the port. Default = 25</li>
+ * <li> mail.smtp.locahost : name to use for HELO/EHLO - default getHostName()</li>
+ * </ul>
+ * <p/> There is no way to indicate failure for a given recipient (it's possible
+ * to have a recipient address rejected). The sun impl throws exceptions even if
+ * others successful), but maybe we do a different way... <p/> TODO : lots.
+ * ESMTP, user/pass, indicate failure, etc...
+ *
+ * @version $Rev$ $Date$
+ */
+public class SMTPTransport extends Transport {
+
+    /**
+     * constants for EOL termination
+     */
+    protected static final char CR = '\r';
+
+    protected static final char LF = '\n';
+
+    /**
+     * property keys for top level session properties.
+     */
+    protected static final String MAIL_LOCALHOST = "mail.localhost";
+
+    protected static final String MAIL_SSLFACTORY_CLASS = "mail.SSLSocketFactory.class";
+
+    /**
+     * property keys for protocol properties. The actual property name will be
+     * appended with "mail." + protocol + ".", where the protocol is either
+     * "smtp" or "smtps".
+     */
+    protected static final String MAIL_SMTP_AUTH = "auth";
+
+    protected static final String MAIL_SMTP_PORT = "port";
+
+    protected static final String MAIL_SMTP_LOCALHOST = "localhost";
+
+    protected static final String MAIL_SMTP_TIMEOUT = "timeout";
+
+    protected static final String MAIL_SMTP_SASL_REALM = "sasl.realm";
+
+    protected static final String MAIL_SMTP_TLS = "starttls.enable";
+
+    protected static final String MAIL_SMTP_FACTORY_CLASS = "socketFactory.class";
+
+    protected static final String MAIL_SMTP_FACTORY_FALLBACK = "socketFactory.fallback";
+
+    protected static final String MAIL_SMTP_FACTORY_PORT = "socketFactory.port";
+
+    protected static final String MAIL_SMTP_REPORT_SUCCESS = "reportsuccess";
+
+    protected static final String MAIL_SMTP_STARTTLS_ENABLE = "starttls.enable";
+
+    protected static final String MAIL_SMTP_DSN_NOTIFY = "dsn.notify";
+
+    protected static final String MAIL_SMTP_SENDPARTIAL = "sendpartial";
+
+    protected static final String MAIL_SMTP_LOCALADDRESS = "localaddress";
+
+    protected static final String MAIL_SMTP_LOCALPORT = "localport";
+
+    protected static final String MAIL_SMTP_QUITWAIT = "quitwait";
+
+    protected static final String MAIL_SMTP_FROM = "from";
+
+    protected static final String MAIL_SMTP_DSN_RET = "dsn.ret";
+
+    protected static final String MAIL_SMTP_SUBMITTER = "submitter";
+
+    protected static final String MAIL_SMTP_EXTENSION = "mailextension";
+
+    protected static final String MAIL_SMTP_EHLO = "ehlo";
+
+    protected static final String MAIL_SMTP_ENCODE_TRACE = "encodetrace";
+
+    protected static final int MIN_MILLIS = 1000 * 60;
+
+    protected static final int TIMEOUT = MIN_MILLIS * 5;
+
+    protected static final String DEFAULT_MAIL_HOST = "localhost";
+
+    protected static final int DEFAULT_MAIL_SMTP_PORT = 25;
+
+    protected static final int DEFAULT_MAIL_SMTPS_PORT = 465;
+
+    // SMTP reply codes
+    protected static final int SERVICE_READY = 220;
+
+    protected static final int SERVICE_CLOSING = 221;
+
+    protected static final int AUTHENTICATION_COMPLETE = 235;
+
+    protected static final int COMMAND_ACCEPTED = 250;
+
+    protected static final int ADDRESS_NOT_LOCAL = 251;
+
+    protected static final int AUTHENTICATION_CHALLENGE = 334;
+
+    protected static final int START_MAIL_INPUT = 354;
+
+    protected static final int SERVICE_NOT_AVAILABLE = 421;
+
+    protected static final int MAILBOX_BUSY = 450;
+
+    protected static final int PROCESSING_ERROR = 451;
+
+    protected static final int INSUFFICIENT_STORAGE = 452;
+
+    protected static final int COMMAND_SYNTAX_ERROR = 500;
+
+    protected static final int PARAMETER_SYNTAX_ERROR = 501;
+
+    protected static final int COMMAND_NOT_IMPLEMENTED = 502;
+
+    protected static final int INVALID_COMMAND_SEQUENCE = 503;
+
+    protected static final int COMMAND_PARAMETER_NOT_IMPLEMENTED = 504;
+
+    protected static final int MAILBOX_NOT_FOUND = 550;
+
+    protected static final int USER_NOT_LOCAL = 551;
+
+    protected static final int MAILBOX_FULL = 552;
+
+    protected static final int INVALID_MAILBOX = 553;
+
+    protected static final int TRANSACTION_FAILED = 553;
+
+    protected static final String AUTHENTICATION_PLAIN = "PLAIN";
+
+    protected static final String AUTHENTICATION_LOGIN = "LOGIN";
+
+    protected static final String AUTHENTICATION_CRAMMD5 = "CRAM-MD5";
+
+    protected static final String AUTHENTICATION_DIGESTMD5 = "DIGEST-MD5";
+
+    // the protocol we're working with. This will be either "smtp" or "smtps".
+    protected String protocol;
+
+    // the target host
+    protected String host;
+
+    // the default port to use for this protocol (differs between "smtp" and
+    // "smtps").
+    protected int defaultPort;
+
+    // the target server port.
+    protected int port;
+
+    // the connection socket...can be a plain socket or SSLSocket, if TLS is
+    // being used.
+    protected Socket socket;
+
+    // our local host name
+    protected String localHost;
+
+    // input stream used to read data. If Sasl is in use, this might be other
+    // than the
+    // direct access to the socket input stream.
+    protected InputStream inputStream;
+
+    // the other end of the connection pipeline.
+    protected OutputStream outputStream;
+
+    // list of authentication mechanisms supported by the server
+    protected HashMap serverAuthenticationMechanisms;
+
+    // map of server extension arguments
+    protected HashMap serverExtensionArgs;
+
+    // do we report success after completion of each mail send.
+    protected boolean reportSuccess;
+
+    // does the server support transport level security?
+    protected boolean serverTLS = false;
+
+    // is TLS enabled on our part?
+    protected boolean useTLS = false;
+
+    // do we use SSL for our initial connection?
+    protected boolean sslConnection = false;
+
+    // the username we connect with
+    protected String username;
+
+    // the authentication password.
+    protected String password;
+
+    // the target SASL realm (normally null unless explicitly set or we have an
+    // authentication mechanism that
+    // requires it.
+    protected String realm;
+
+    // the last response line received from the server.
+    protected SMTPReply lastServerResponse = null;
+
+    // our session provided debug output stream.
+    protected PrintStream debugStream;
+
+    /**
+     * Normal constructor for an SMTPTransport() object. This constructor is
+     * used to build a transport instance for the "smtp" protocol.
+     *
+     * @param session
+     *            The attached session.
+     * @param name
+     *            An optional URLName object containing target information.
+     */
+    public SMTPTransport(Session session, URLName name) {
+        this(session, name, "smtp", DEFAULT_MAIL_SMTP_PORT, false);
+    }
+
+    /**
+     * Common constructor used by the SMTPTransport and SMTPSTransport classes
+     * to do common initialization of defaults.
+     *
+     * @param session
+     *            The host session instance.
+     * @param name
+     *            The URLName of the target.
+     * @param protocol
+     *            The protocol type (either "smtp" or "smtps". This helps us in
+     *            retrieving protocol-specific session properties.
+     * @param defaultPort
+     *            The default port used by this protocol. For "smtp", this will
+     *            be 25. The default for "smtps" is 465.
+     * @param sslConnection
+     *            Indicates whether an SSL connection should be used to initial
+     *            contact the server. This is different from the STARTTLS
+     *            support, which switches the connection to SSL after the
+     *            initial startup.
+     */
+    protected SMTPTransport(Session session, URLName name, String protocol, int defaultPort, boolean sslConnection) {
+        super(session, name);
+        this.protocol = protocol;
+
+        // these are defaults based on what the superclass specifies.
+        this.defaultPort = defaultPort;
+        this.sslConnection = sslConnection;
+        // check to see if we need to throw an exception after a send operation.
+        reportSuccess = isProtocolPropertyTrue(MAIL_SMTP_REPORT_SUCCESS);
+        // and also check for TLS enablement.
+        useTLS = isProtocolPropertyTrue(MAIL_SMTP_STARTTLS_ENABLE);
+
+        // get our debug output.
+        debugStream = session.getDebugOut();
+    }
+
+    /**
+     * Connect to a server using an already created socket. This connection is
+     * just like any other connection, except we will not create a new socket.
+     *
+     * @param socket
+     *            The socket connection to use.
+     */
+    public void connect(Socket socket) throws MessagingException {
+        this.socket = socket;
+        super.connect();
+    }
+
+    /**
+     * Do the protocol connection for an SMTP transport. This handles server
+     * authentication, if possible. Returns false if unable to connect to the
+     * server.
+     *
+     * @param host
+     *            The target host name.
+     * @param port
+     *            The server port number.
+     * @param user
+     *            The authentication user (if any).
+     * @param password
+     *            The server password. Might not be sent directly if more
+     *            sophisticated authentication is used.
+     *
+     * @return true if we were able to connect to the server properly, false for
+     *         any failures.
+     * @exception MessagingException
+     */
+    protected boolean protocolConnect(String host, int port, String username, String password)
+            throws MessagingException {
+        if (debug) {
+            debugOut("Connecting to server " + host + ":" + port + " for user " + username);
+        }
+
+        // now check to see if we need to authenticate. If we need this, then
+        // we must have a username and
+        // password specified. Failing this may result in a user prompt to
+        // collect the information.
+        boolean mustAuthenticate = isProtocolPropertyTrue(MAIL_SMTP_AUTH);
+
+        // if we need to authenticate, and we don't have both a userid and
+        // password, then we fail this
+        // immediately. The Service.connect() method will try to obtain the user
+        // information and retry the
+        // connection one time.
+        if (mustAuthenticate && (username == null || password == null)) {
+            return false;
+        }
+
+        // if the port is defaulted, then see if we have something configured in
+        // the session.
+        // if not configured, we just use the default default.
+        if (port == -1) {
+            // take the default first.
+            port = defaultPort;
+            String configuredPort = getProtocolProperty(MAIL_SMTP_PORT);
+            if (configuredPort != null) {
+                port = Integer.parseInt(configuredPort);
+            }
+        }
+    	
+    	// Before we do anything, let's make sure that we succesfully received a host
+    	if ( host == null ) {
+    		host = DEFAULT_MAIL_HOST;
+    	}
+
+        try {
+
+            // create socket and connect to server.
+            getConnection(host, port, username, password);
+
+            // receive welcoming message
+            if (!getWelcome()) {
+                throw new MessagingException("Error in getting welcome msg");
+            }
+
+            // say hello
+            if (!sendHandshake()) {
+                throw new MessagingException("Error in saying EHLO to server");
+            }
+
+            // authenticate with the server, if necessary
+            if (!processAuthentication()) {
+                if (debug) {
+                    debugOut("User authentication failure");
+                }
+                throw new AuthenticationFailedException("Error authenticating with server");
+            }
+        } catch (IOException e) {
+            if (debug) {
+                debugOut("I/O exception establishing connection", e);
+            }
+            throw new MessagingException("Connection error", e);
+        }
+        return true;
+    }
+
+    /**
+     * Send a message to multiple addressees.
+     *
+     * @param message
+     *            The message we're sending.
+     * @param addresses
+     *            An array of addresses to send to.
+     *
+     * @exception MessagingException
+     */
+    public void sendMessage(Message message, Address[] addresses) throws MessagingException {
+        if (!isConnected()) {
+            throw new IllegalStateException("Not connected");
+        }
+        // don't bother me w/ null messages or no addreses
+        if (message == null) {
+            throw new MessagingException("Null message");
+        }
+
+        // SMTP only handles instances of MimeMessage, not the more general
+        // message case.
+        if (!(message instanceof MimeMessage)) {
+            throw new MessagingException("SMTP can only send MimeMessages");
+        }
+
+        // we must have a message list.
+        if (addresses == null || addresses.length == 0) {
+            throw new MessagingException("Null or empty address array");
+        }
+
+        boolean haveGroup = false;
+
+        // enforce the requirement that all of the targets are InternetAddress
+        // instances.
+        for (int i = 0; i < addresses.length; i++) {
+            if (addresses[i] instanceof InternetAddress) {
+                // and while we're here, see if we have a groups in the address
+                // list. If we do, then
+                // we're going to need to expand these before sending.
+                if (((InternetAddress) addresses[i]).isGroup()) {
+                    haveGroup = true;
+                }
+            } else {
+                throw new MessagingException("Illegal InternetAddress " + addresses[i]);
+            }
+        }
+
+        // did we find a group? Time to expand this into our full target list.
+        if (haveGroup) {
+            addresses = expandGroups(addresses);
+        }
+
+        SendStatus[] stats = new SendStatus[addresses.length];
+
+        // create our lists for notification and exception reporting.
+        Address[] sent = null;
+        Address[] unsent = null;
+        Address[] invalid = null;
+
+        try {
+            // send sender first. If this failed, send a failure notice of the
+            // event, using the full list of
+            // addresses as the unsent, and nothing for the rest.
+            if (!sendMailFrom(message)) {
+                unsent = addresses;
+                sent = new Address[0];
+                invalid = new Address[0];
+                // notify of the error.
+                notifyTransportListeners(TransportEvent.MESSAGE_NOT_DELIVERED, sent, unsent, invalid, message);
+
+                // include the reponse information here.
+                SMTPReply last = lastServerResponse;
+                // now send an "uber-exception" to indicate the failure.
+                throw new SMTPSendFailedException("MAIL FROM", last.getCode(), last.getMessage(), null, sent, unsent,
+                        invalid);
+            }
+
+            String dsn = null;
+
+            // there's an optional notification argument that can be added to
+            // MAIL TO. See if we've been
+            // provided with one.
+
+            // an SMTPMessage object is the first source
+            if (message instanceof SMTPMessage) {
+                // get the notification options
+                int options = ((SMTPMessage) message).getNotifyOptions();
+
+                switch (options) {
+                // a zero value indicates nothing is set.
+                case 0:
+                    break;
+
+                case SMTPMessage.NOTIFY_NEVER:
+                    dsn = "NEVER";
+                    break;
+
+                case SMTPMessage.NOTIFY_SUCCESS:
+                    dsn = "SUCCESS";
+                    break;
+
+                case SMTPMessage.NOTIFY_FAILURE:
+                    dsn = "FAILURE";
+                    break;
+
+                case SMTPMessage.NOTIFY_DELAY:
+                    dsn = "DELAY";
+                    break;
+
+                // now for combinations...there are few enough combinations here
+                // that we can just handle this in the switch statement rather
+                // than have to
+                // concatentate everything together.
+                case (SMTPMessage.NOTIFY_SUCCESS + SMTPMessage.NOTIFY_FAILURE):
+                    dsn = "SUCCESS,FAILURE";
+                    break;
+
+                case (SMTPMessage.NOTIFY_SUCCESS + SMTPMessage.NOTIFY_DELAY):
+                    dsn = "SUCCESS,DELAY";
+                    break;
+
+                case (SMTPMessage.NOTIFY_FAILURE + SMTPMessage.NOTIFY_DELAY):
+                    dsn = "FAILURE,DELAY";
+                    break;
+
+                case (SMTPMessage.NOTIFY_SUCCESS + SMTPMessage.NOTIFY_FAILURE + SMTPMessage.NOTIFY_DELAY):
+                    dsn = "SUCCESS,FAILURE,DELAY";
+                    break;
+                }
+            }
+
+            // if still null, grab a property value (yada, yada, yada...)
+            if (dsn == null) {
+                dsn = getProtocolProperty(MAIL_SMTP_DSN_NOTIFY);
+            }
+
+            // we need to know about any failures once we've gone through the
+            // complete list, so keep a
+            // failure flag.
+            boolean sendFailure = false;
+
+            // event notifcation requires we send lists of successes and
+            // failures broken down by category.
+            // The categories are:
+            //
+            // 1) addresses successfully processed.
+            // 2) addresses deemed valid, but had a processing failure that
+            // prevented sending.
+            // 3) addressed deemed invalid (basically all other processing
+            // failures).
+            ArrayList sentAddresses = new ArrayList();
+            ArrayList unsentAddresses = new ArrayList();
+            ArrayList invalidAddresses = new ArrayList();
+
+            // Now we add a MAIL TO record for each recipient. At this point, we
+            // just collect
+            for (int i = 0; i < addresses.length; i++) {
+                InternetAddress target = (InternetAddress) addresses[i];
+
+                // write out the record now.
+                SendStatus status = sendRcptTo(target, dsn);
+                stats[i] = status;
+
+                switch (status.getStatus()) {
+                // successfully sent
+                case SendStatus.SUCCESS:
+                    sentAddresses.add(target);
+                    break;
+
+                // we have an invalid address of some sort, or a general sending
+                // error (which we'll
+                // interpret as due to an invalid address.
+                case SendStatus.INVALID_ADDRESS:
+                case SendStatus.GENERAL_ERROR:
+                    sendFailure = true;
+                    invalidAddresses.add(target);
+                    break;
+
+                // good address, but this was a send failure.
+                case SendStatus.SEND_FAILURE:
+                    sendFailure = true;
+                    unsentAddresses.add(target);
+                    break;
+                }
+            }
+
+            // if we had a send failure, then we need to check if we allow
+            // partial sends. If not allowed,
+            // we abort the send operation now.
+            if (sendFailure) {
+                // now see how we're configured for this send operation.
+                boolean partialSends = false;
+
+                // this can be attached directly to the message.
+                if (message instanceof SMTPMessage) {
+                    partialSends = ((SMTPMessage) message).getSendPartial();
+                }
+
+                // if still false on the message object, check for a property
+                // version also
+                if (!partialSends) {
+                    partialSends = isProtocolPropertyTrue(MAIL_SMTP_SENDPARTIAL);
+                }
+
+                // if we're not allowing partial successes or we've failed on
+                // all of the addresses, it's
+                // time to abort.
+                if (!partialSends || sentAddresses.isEmpty()) {
+                    // we send along the valid and invalid address lists on the
+                    // notifications and
+                    // exceptions.
+                    // however, since we're aborting the entire send, the
+                    // successes need to become
+                    // members of the failure list.
+                    unsentAddresses.addAll(sentAddresses);
+
+                    // this one is empty.
+                    sent = new Address[0];
+                    unsent = (Address[]) unsentAddresses.toArray(new Address[0]);
+                    invalid = (Address[]) invalidAddresses.toArray(new Address[0]);
+
+                    // go reset our connection so we can process additional
+                    // sends.
+                    resetConnection();
+
+                    // get a list of chained exceptions for all of the failures.
+                    MessagingException failures = generateExceptionChain(stats, false);
+
+                    // now send an "uber-exception" to indicate the failure.
+                    throw new SMTPSendFailedException("MAIL TO", 0, "Invalid Address", failures, sent, unsent, invalid);
+                }
+            }
+
+            try {
+                // try to send the data
+                sendData(message);
+            } catch (MessagingException e) {
+                // If there's an error at this point, this is a complete
+                // delivery failure.
+                // we send along the valid and invalid address lists on the
+                // notifications and
+                // exceptions.
+                // however, since we're aborting the entire send, the successes
+                // need to become
+                // members of the failure list.
+                unsentAddresses.addAll(sentAddresses);
+
+                // this one is empty.
+                sent = new Address[0];
+                unsent = (Address[]) unsentAddresses.toArray(new Address[0]);
+                invalid = (Address[]) invalidAddresses.toArray(new Address[0]);
+                // notify of the error.
+                notifyTransportListeners(TransportEvent.MESSAGE_NOT_DELIVERED, sent, unsent, invalid, message);
+                // send a send failure exception.
+                throw new SMTPSendFailedException("DATA", 0, "Send failure", e, sent, unsent, invalid);
+            }
+
+            // create our lists for notification and exception reporting from
+            // this point on.
+            sent = (Address[]) sentAddresses.toArray(new Address[0]);
+            unsent = (Address[]) unsentAddresses.toArray(new Address[0]);
+            invalid = (Address[]) invalidAddresses.toArray(new Address[0]);
+
+            // if sendFailure is true, we had an error during the address phase,
+            // but we had permission to
+            // process this as a partial send operation. Now that the data has
+            // been sent ok, it's time to
+            // report the partial failure.
+            if (sendFailure) {
+                // notify our listeners of the partial delivery.
+                notifyTransportListeners(TransportEvent.MESSAGE_PARTIALLY_DELIVERED, sent, unsent, invalid, message);
+
+                // get a list of chained exceptions for all of the failures (and
+                // the successes, if reportSuccess has been
+                // turned on).
+                MessagingException failures = generateExceptionChain(stats, getReportSuccess());
+
+                // now send an "uber-exception" to indicate the failure.
+                throw new SMTPSendFailedException("MAIL TO", 0, "Invalid Address", failures, sent, unsent, invalid);
+            }
+
+            // notify our listeners of successful delivery.
+            notifyTransportListeners(TransportEvent.MESSAGE_DELIVERED, sent, unsent, invalid, message);
+
+            // we've not had any failures, but we've been asked to report
+            // success as an exception. Do
+            // this now.
+            if (reportSuccess) {
+                // generate the chain of success exceptions (we already know
+                // there are no failure ones to report).
+                MessagingException successes = generateExceptionChain(stats, reportSuccess);
+                if (successes != null) {
+                    throw successes;
+                }
+            }
+        } catch (SMTPSendFailedException e) {
+            // if this is a send failure, we've already handled
+            // notifications....just rethrow it.
+            throw e;
+        } catch (MessagingException e) {
+            // notify of the error.
+            notifyTransportListeners(TransportEvent.MESSAGE_NOT_DELIVERED, sent, unsent, invalid, message);
+            throw e;
+        }
+    }
+
+    /**
+     * Close the connection. On completion, we'll be disconnected from the
+     * server and unable to send more data.
+     *
+     * @exception MessagingException
+     */
+    public void close() throws MessagingException {
+        // if we're already closed, get outta here.
+        if (socket == null) {
+            return;
+        }
+        try {
+            // say goodbye
+            sendQuit();
+        } finally {
+            // and close up the connection. We do this in a finally block to
+            // make sure the connection
+            // is shut down even if quit gets an error.
+            closeServerConnection();
+        }
+    }
+
+    /**
+     * Turn a series of send status items into a chain of exceptions indicating
+     * the state of each send operation.
+     *
+     * @param stats
+     *            The list of SendStatus items.
+     * @param reportSuccess
+     *            Indicates whether we should include the report success items.
+     *
+     * @return The head of a chained list of MessagingExceptions.
+     */
+    protected MessagingException generateExceptionChain(SendStatus[] stats, boolean reportSuccess) {
+        MessagingException current = null;
+
+        for (int i = 0; i < stats.length; i++) {
+            SendStatus status = stats[i];
+
+            if (status != null) {
+                MessagingException nextException = stats[i].getException(reportSuccess);
+                // if there's an exception associated with this status, chain it
+                // up with the rest.
+                if (nextException != null) {
+                    if (current == null) {
+                        current = nextException;
+                    } else {
+                        current.setNextException(nextException);
+                        current = nextException;
+                    }
+                }
+            }
+        }
+        return current;
+    }
+
+    /**
+     * Reset the server connection after an error.
+     *
+     * @exception MessagingException
+     */
+    protected void resetConnection() throws MessagingException {
+        // we want the caller to retrieve the last response responsbile for
+        // requiring the reset, so save and
+        // restore that info around the reset.
+        SMTPReply last = lastServerResponse;
+
+        // send a reset command.
+        SMTPReply line = sendCommand("RSET");
+
+        // if this did not reset ok, just close the connection
+        if (line.getCode() != COMMAND_ACCEPTED) {
+            close();
+        }
+        // restore this.
+        lastServerResponse = last;
+    }
+
+    /**
+     * Expand the address list by converting any group addresses into single
+     * address targets.
+     *
+     * @param addresses
+     *            The input array of addresses.
+     *
+     * @return The expanded array of addresses.
+     * @exception MessagingException
+     */
+    protected Address[] expandGroups(Address[] addresses) throws MessagingException {
+        ArrayList expandedAddresses = new ArrayList();
+
+        // run the list looking for group addresses, and add the full group list
+        // to our targets.
+        for (int i = 0; i < addresses.length; i++) {
+            InternetAddress address = (InternetAddress) addresses[i];
+            // not a group? Just copy over to the other list.
+            if (!address.isGroup()) {
+                expandedAddresses.add(address);
+            } else {
+                // get the group address and copy each member of the group into
+                // the expanded list.
+                InternetAddress[] groupAddresses = address.getGroup(true);
+                for (int j = 1; j < groupAddresses.length; j++) {
+                    expandedAddresses.add(groupAddresses[j]);
+                }
+            }
+        }
+
+        // convert back into an array.
+        return (Address[]) expandedAddresses.toArray(new Address[0]);
+    }
+
+    /**
+     * Create a transport connection object and connect it to the target server.
+     *
+     * @param host
+     *            The target server host.
+     * @param port
+     *            The connection port.
+     *
+     * @exception MessagingException
+     */
+    protected void getConnection(String host, int port, String username, String password) throws IOException {
+        this.host = host;
+        this.port = port;
+        this.username = username;
+        this.password = password;
+        // and see if STARTTLS is enabled.
+        useTLS = isProtocolPropertyTrue(MAIL_SMTP_TLS);
+        serverAuthenticationMechanisms = new HashMap();
+        // We might have been passed a socket to connect with...if not, we need
+        // to create one of the correct type.
+        if (socket == null) {
+            // if this is the "smtps" protocol, we start with an SSLSocket
+            if (sslConnection) {
+                getConnectedSSLSocket();
+            } else {
+                getConnectedSocket();
+            }
+        }
+        // if we already have a socket, get some information from it and
+        // override what we've been passed.
+        else {
+            port = socket.getPort();
+            host = socket.getInetAddress().getHostName();
+        }
+        // now set up the input/output streams.
+        inputStream = new TraceInputStream(socket.getInputStream(), debugStream, debug,
+                isProtocolPropertyTrue(MAIL_SMTP_ENCODE_TRACE));
+        ;
+        outputStream = new TraceOutputStream(socket.getOutputStream(), debugStream, debug,
+                isProtocolPropertyTrue(MAIL_SMTP_ENCODE_TRACE));
+    }
+
+    /**
+     * Get a property associated with this mail protocol.
+     *
+     * @param name
+     *            The name of the property.
+     *
+     * @return The property value (returns null if the property has not been
+     *         set).
+     */
+    protected String getProtocolProperty(String name) {
+        // the name we're given is the least qualified part of the name. We
+        // construct the full property name
+        // using the protocol (either "smtp" or "smtps").
+        String fullName = "mail." + protocol + "." + name;
+        return getSessionProperty(fullName);
+    }
+
+    /**
+     * Get a property associated with this mail session.
+     *
+     * @param name
+     *            The name of the property.
+     *
+     * @return The property value (returns null if the property has not been
+     *         set).
+     */
+    protected String getSessionProperty(String name) {
+        return session.getProperty(name);
+    }
+
+    /**
+     * Get a property associated with this mail session. Returns the provided
+     * default if it doesn't exist.
+     *
+     * @param name
+     *            The name of the property.
+     * @param defaultValue
+     *            The default value to return if the property doesn't exist.
+     *
+     * @return The property value (returns defaultValue if the property has not
+     *         been set).
+     */
+    protected String getSessionProperty(String name, String defaultValue) {
+        String result = session.getProperty(name);
+        if (result == null) {
+            return defaultValue;
+        }
+        return result;
+    }
+
+    /**
+     * Get a property associated with this mail session. Returns the provided
+     * default if it doesn't exist.
+     *
+     * @param name
+     *            The name of the property.
+     * @param defaultValue
+     *            The default value to return if the property doesn't exist.
+     *
+     * @return The property value (returns defaultValue if the property has not
+     *         been set).
+     */
+    protected String getProtocolProperty(String name, String defaultValue) {
+        // the name we're given is the least qualified part of the name. We
+        // construct the full property name
+        // using the protocol (either "smtp" or "smtps").
+        String fullName = "mail." + protocol + "." + name;
+        return getSessionProperty(fullName, defaultValue);
+    }
+
+    /**
+     * Get a property associated with this mail session as an integer value.
+     * Returns the default value if the property doesn't exist or it doesn't
+     * have a valid int value.
+     *
+     * @param name
+     *            The name of the property.
+     * @param defaultValue
+     *            The default value to return if the property doesn't exist.
+     *
+     * @return The property value converted to an int.
+     */
+    protected int getIntSessionProperty(String name, int defaultValue) {
+        String result = getSessionProperty(name);
+        if (result != null) {
+            try {
+                // convert into an int value.
+                return Integer.parseInt(result);
+            } catch (NumberFormatException e) {
+            }
+        }
+        // return default value if it doesn't exist is isn't convertable.
+        return defaultValue;
+    }
+
+    /**
+     * Get a property associated with this mail session as an integer value.
+     * Returns the default value if the property doesn't exist or it doesn't
+     * have a valid int value.
+     *
+     * @param name
+     *            The name of the property.
+     * @param defaultValue
+     *            The default value to return if the property doesn't exist.
+     *
+     * @return The property value converted to an int.
+     */
+    protected int getIntProtocolProperty(String name, int defaultValue) {
+        // the name we're given is the least qualified part of the name. We
+        // construct the full property name
+        // using the protocol (either "smtp" or "smtps").
+        String fullName = "mail." + protocol + "." + name;
+        return getIntSessionProperty(fullName, defaultValue);
+    }
+
+    /**
+     * Process a session property as a boolean value, returning either true or
+     * false.
+     *
+     * @return True if the property value is "true". Returns false for any other
+     *         value (including null).
+     */
+    protected boolean isProtocolPropertyTrue(String name) {
+        // the name we're given is the least qualified part of the name. We
+        // construct the full property name
+        // using the protocol (either "smtp" or "smtps").
+        String fullName = "mail." + protocol + "." + name;
+        return isSessionPropertyTrue(fullName);
+    }
+
+    /**
+     * Process a session property as a boolean value, returning either true or
+     * false.
+     *
+     * @return True if the property value is "true". Returns false for any other
+     *         value (including null).
+     */
+    protected boolean isSessionPropertyTrue(String name) {
+        String property = session.getProperty(name);
+        if (property != null) {
+            return property.equals("true");
+        }
+        return false;
+    }
+
+    /**
+     * Process a session property as a boolean value, returning either true or
+     * false.
+     *
+     * @return True if the property value is "false". Returns false for other
+     *         value (including null).
+     */
+    protected boolean isSessionPropertyFalse(String name) {
+        String property = session.getProperty(name);
+        if (property != null) {
+            return property.equals("false");
+        }
+        return false;
+    }
+
+    /**
+     * Process a session property as a boolean value, returning either true or
+     * false.
+     *
+     * @return True if the property value is "false". Returns false for other
+     *         value (including null).
+     */
+    protected boolean isProtocolPropertyFalse(String name) {
+        // the name we're given is the least qualified part of the name. We
+        // construct the full property name
+        // using the protocol (either "smtp" or "smtps").
+        String fullName = "mail." + protocol + "." + name;
+        return isSessionPropertyTrue(fullName);
+    }
+
+    /**
+     * Close the server connection at termination.
+     */
+    protected void closeServerConnection() {
+        try {
+            socket.close();
+        } catch (IOException ignored) {
+        }
+
+        socket = null;
+        inputStream = null;
+        outputStream = null;
+    }
+
+    /**
+     * Creates a connected socket
+     *
+     * @exception MessagingException
+     */
+    protected void getConnectedSocket() throws IOException {
+        if (debug) {
+            debugOut("Attempting plain socket connection to server " + host + ":" + port);
+        }
+
+        // the socket factory can be specified via a session property. By
+        // default, we just directly
+        // instantiate a socket without using a factor.
+        String socketFactory = getProtocolProperty(MAIL_SMTP_FACTORY_CLASS);
+
+        // there are several protocol properties that can be set to tune the
+        // created socket. We need to
+        // retrieve those bits before creating the socket.
+        int timeout = getIntProtocolProperty(MAIL_SMTP_TIMEOUT, -1);
+        InetAddress localAddress = null;
+        // see if we have a local address override.
+        String localAddrProp = getProtocolProperty(MAIL_SMTP_LOCALADDRESS);
+        if (localAddrProp != null) {
+            localAddress = InetAddress.getByName(localAddrProp);
+        }
+
+        // check for a local port...default is to allow socket to choose.
+        int localPort = getIntProtocolProperty(MAIL_SMTP_LOCALPORT, 0);
+
+        socket = null;
+
+        // if there is no socket factory defined (normal), we just create a
+        // socket directly.
+        if (socketFactory == null) {
+            socket = new Socket(host, port, localAddress, localPort);
+        }
+
+        else {
+            try {
+                int socketFactoryPort = getIntProtocolProperty(MAIL_SMTP_FACTORY_PORT, -1);
+
+                // we choose the port used by the socket based on overrides.
+                Integer portArg = new Integer(socketFactoryPort == -1 ? port : socketFactoryPort);
+
+                // use the current context loader to resolve this.
+                ClassLoader loader = Thread.currentThread().getContextClassLoader();
+                Class factoryClass = loader.loadClass(socketFactory);
+
+                // done indirectly, we need to invoke the method using
+                // reflection.
+                // This retrieves a factory instance.
+                Method getDefault = factoryClass.getMethod("getDefault", new Class[0]);
+                Object defFactory = getDefault.invoke(new Object(), new Object[0]);
+
+                // now that we have the factory, there are two different
+                // createSocket() calls we use,
+                // depending on whether we have a localAddress override.
+
+                if (localAddress != null) {
+                    // retrieve the createSocket(String, int, InetAddress, int)
+                    // method.
+                    Class[] createSocketSig = new Class[] { String.class, Integer.TYPE, InetAddress.class, Integer.TYPE };
+                    Method createSocket = factoryClass.getMethod("createSocket", createSocketSig);
+
+                    Object[] createSocketArgs = new Object[] { host, portArg, localAddress, new Integer(localPort) };
+                    socket = (Socket) createSocket.invoke(defFactory, createSocketArgs);
+                } else {
+                    // retrieve the createSocket(String, int) method.
+                    Class[] createSocketSig = new Class[] { String.class, Integer.TYPE };
+                    Method createSocket = factoryClass.getMethod("createSocket", createSocketSig);
+
+                    Object[] createSocketArgs = new Object[] { host, portArg };
+                    socket = (Socket) createSocket.invoke(defFactory, createSocketArgs);
+                }
+            } catch (Throwable e) {
+                // if a socket factor is specified, then we may need to fall
+                // back to a default. This behavior
+                // is controlled by (surprise) more session properties.
+                if (isProtocolPropertyTrue(MAIL_SMTP_FACTORY_FALLBACK)) {
+                    if (debug) {
+                        debugOut("First plain socket attempt faile, falling back to default factory", e);
+                    }
+                    socket = new Socket(host, port, localAddress, localPort);
+                }
+                // we have an exception. We're going to throw an IOException,
+                // which may require unwrapping
+                // or rewrapping the exception.
+                else {
+                    // we have an exception from the reflection, so unwrap the
+                    // base exception
+                    if (e instanceof InvocationTargetException) {
+                        e = ((InvocationTargetException) e).getTargetException();
+                    }
+
+                    if (debug) {
+                        debugOut("Plain socket creation failure", e);
+                    }
+
+                    // throw this as an IOException, with the original exception
+                    // attached.
+                    IOException ioe = new IOException("Error connecting to " + host + ", " + port);
+                    ioe.initCause(e);
+                    throw ioe;
+                }
+            }
+        }
+
+        if (timeout >= 0) {
+            socket.setSoTimeout(timeout);
+        }
+    }
+
+    /**
+     * Creates a connected SSL socket for an initial SSL connection.
+     *
+     * @exception MessagingException
+     */
+    protected void getConnectedSSLSocket() throws IOException {
+        if (debug) {
+            debugOut("Attempting SSL socket connection to server " + host + ":" + port);
+        }
+        // the socket factory can be specified via a protocol property, a
+        // session property, and if all else
+        // fails (which it usually does), we fall back to the standard factory
+        // class.
+        String socketFactory = getProtocolProperty(MAIL_SMTP_FACTORY_CLASS, getSessionProperty(MAIL_SSLFACTORY_CLASS,
+                "javax.net.ssl.SSLSocketFactory"));
+
+        // there are several protocol properties that can be set to tune the
+        // created socket. We need to
+        // retrieve those bits before creating the socket.
+        int timeout = getIntProtocolProperty(MAIL_SMTP_TIMEOUT, -1);
+        InetAddress localAddress = null;
+        // see if we have a local address override.
+        String localAddrProp = getProtocolProperty(MAIL_SMTP_LOCALADDRESS);
+        if (localAddrProp != null) {
+            localAddress = InetAddress.getByName(localAddrProp);
+        }
+
+        // check for a local port...default is to allow socket to choose.
+        int localPort = getIntProtocolProperty(MAIL_SMTP_LOCALPORT, 0);
+
+        socket = null;
+
+        // if there is no socket factory defined (normal), we just create a
+        // socket directly.
+        if (socketFactory == null) {
+            socket = new Socket(host, port, localAddress, localPort);
+        }
+
+        else {
+            // we'll try this with potentially two different factories if we're
+            // allowed to fall back.
+            boolean fallback = isProtocolPropertyTrue(MAIL_SMTP_FACTORY_FALLBACK);
+
+            while (true) {
+                try {
+                    if (debug) {
+                        debugOut("Creating SSL socket using factory " + socketFactory);
+                    }
+
+                    int socketFactoryPort = getIntProtocolProperty(MAIL_SMTP_FACTORY_PORT, -1);
+
+                    // we choose the port used by the socket based on overrides.
+                    Integer portArg = new Integer(socketFactoryPort == -1 ? port : socketFactoryPort);
+
+                    // use the current context loader to resolve this.
+                    ClassLoader loader = Thread.currentThread().getContextClassLoader();
+                    Class factoryClass = loader.loadClass(socketFactory);
+
+                    // done indirectly, we need to invoke the method using
+                    // reflection.
+                    // This retrieves a factory instance.
+                    Method getDefault = factoryClass.getMethod("getDefault", new Class[0]);
+                    Object defFactory = getDefault.invoke(new Object(), new Object[0]);
+
+                    // now that we have the factory, there are two different
+                    // createSocket() calls we use,
+                    // depending on whether we have a localAddress override.
+
+                    if (localAddress != null) {
+                        // retrieve the createSocket(String, int, InetAddress,
+                        // int) method.
+                        Class[] createSocketSig = new Class[] { String.class, Integer.TYPE, InetAddress.class,
+                                Integer.TYPE };
+                        Method createSocket = factoryClass.getMethod("createSocket", createSocketSig);
+
+                        Object[] createSocketArgs = new Object[] { host, portArg, localAddress, new Integer(localPort) };
+                        socket = (Socket) createSocket.invoke(defFactory, createSocketArgs);
+                    } else {
+                        // retrieve the createSocket(String, int) method.
+                        Class[] createSocketSig = new Class[] { String.class, Integer.TYPE };
+                        Method createSocket = factoryClass.getMethod("createSocket", createSocketSig);
+
+                        Object[] createSocketArgs = new Object[] { host, portArg };
+                        socket = (Socket) createSocket.invoke(defFactory, createSocketArgs);
+                    }
+                    // now break out and configure the socket.
+                    break;
+                } catch (Throwable e) {
+                    // if we're allowed to fallback, then use the default
+                    // factory and try this again. We only
+                    // allow this to happen once.
+                    if (fallback) {
+                        if (debug) {
+                            debugOut("First attempt at creating SSL socket failed, falling back to default factory");
+                        }
+                        socketFactory = "javax.net.ssl.SSLSocketFactory";
+                        fallback = false;
+                        continue;
+                    }
+                    // we have an exception. We're going to throw an
+                    // IOException, which may require unwrapping
+                    // or rewrapping the exception.
+                    else {
+                        // we have an exception from the reflection, so unwrap
+                        // the base exception
+                        if (e instanceof InvocationTargetException) {
+                            e = ((InvocationTargetException) e).getTargetException();
+                        }
+
+                        if (debug) {
+                            debugOut("Failure creating SSL socket", e);
+                        }
+
+                        // throw this as an IOException, with the original
+                        // exception attached.
+                        IOException ioe = new IOException("Error connecting to " + host + ", " + port);
+                        ioe.initCause(e);
+                        throw ioe;
+                    }
+                }
+            }
+        }
+
+        if (timeout >= 0) {
+            socket.setSoTimeout(timeout);
+        }
+    }
+
+    /**
+     * Switch the connection to using TLS level security, switching to an SSL
+     * socket.
+     */
+    protected void getConnectedTLSSocket() throws MessagingException {
+        if (debug) {
+            debugOut("Attempting to negotiate STARTTLS with server " + host);
+        }
+        // tell the server of our intention to start a TLS session
+        SMTPReply line = sendCommand("STARTTLS");
+
+        if (line.getCode() != SERVICE_READY) {
+            if (debug) {
+                debugOut("STARTTLS command rejected by SMTP server " + host);
+            }
+            throw new MessagingException("Unable to make TLS server connection");
+        }
+        // it worked, now switch the socket into TLS mode
+        try {
+
+            // we use the same target and port as the current connection.
+            String host = socket.getInetAddress().getHostName();
+            int port = socket.getPort();
+
+            // the socket factory can be specified via a session property. By
+            // default, we use
+            // the native SSL factory.
+            String socketFactory = getProtocolProperty(MAIL_SMTP_FACTORY_CLASS, "javax.net.ssl.SSLSocketFactory");
+
+            // use the current context loader to resolve this.
+            ClassLoader loader = Thread.currentThread().getContextClassLoader();
+            Class factoryClass = loader.loadClass(socketFactory);
+
+            // done indirectly, we need to invoke the method using reflection.
+            // This retrieves a factory instance.
+            Method getDefault = factoryClass.getMethod("getDefault", new Class[0]);
+            Object defFactory = getDefault.invoke(new Object(), new Object[0]);
+
+            // now we need to invoke createSocket()
+            Class[] createSocketSig = new Class[] { Socket.class, String.class, Integer.TYPE, Boolean.TYPE };
+            Method createSocket = factoryClass.getMethod("createSocket", createSocketSig);
+
+            Object[] createSocketArgs = new Object[] { socket, host, new Integer(port), Boolean.TRUE };
+
+            // and finally create the socket
+            Socket sslSocket = (Socket) createSocket.invoke(defFactory, createSocketArgs);
+
+            // if this is an instance of SSLSocket (very common), try setting
+            // the protocol to be
+            // "TLSv1". If this is some other class because of a factory
+            // override, we'll just have to
+            // accept that things will work.
+            if (sslSocket instanceof SSLSocket) {
+                ((SSLSocket) sslSocket).setEnabledProtocols(new String[] { "TLSv1" });
+                ((SSLSocket) sslSocket).setUseClientMode(true);
+                ((SSLSocket) sslSocket).startHandshake();
+            }
+
+            // and finally, as a last step, replace our input streams with the
+            // secure ones.
+            // now set up the input/output streams.
+            inputStream = new TraceInputStream(sslSocket.getInputStream(), debugStream, debug,
+                    isProtocolPropertyTrue(MAIL_SMTP_ENCODE_TRACE));
+            ;
+            outputStream = new TraceOutputStream(sslSocket.getOutputStream(), debugStream, debug,
+                    isProtocolPropertyTrue(MAIL_SMTP_ENCODE_TRACE));
+            // this is our active socket now
+            socket = sslSocket;
+
+        } catch (Exception e) {
+            if (debug) {
+                debugOut("Failure attempting to convert connection to TLS", e);
+            }
+            throw new MessagingException("Unable to convert connection to SSL", e);
+        }
+    }
+
+    /**
+     * Get the servers welcome blob from the wire....
+     */
+    protected boolean getWelcome() throws MessagingException {
+        SMTPReply line = getReply();
+        return !line.isError();
+    }
+
+    /**
+     * Sends the data in the message down the socket. This presumes the server
+     * is in the right place and ready for getting the DATA message and the data
+     * right place in the sequence
+     */
+    protected void sendData(Message msg) throws MessagingException {
+
+        // send the DATA command
+        SMTPReply line = sendCommand("DATA");
+
+        if (line.isError()) {
+            throw new MessagingException("Error issuing SMTP 'DATA' command: " + line);
+        }
+
+        // now the data... I could look at the type, but
+        try {
+            // the data content has two requirements we need to meet by
+            // filtering the
+            // output stream. Requirement 1 is to conicalize any line breaks.
+            // All line
+            // breaks will be transformed into properly formed CRLF sequences.
+            //
+            // Requirement 2 is to perform byte-stuff for any line that begins
+            // with a "."
+            // so that data is not confused with the end-of-data marker (a
+            // "\r\n.\r\n" sequence.
+            //
+            // The MIME output stream performs those two functions on behalf of
+            // the content
+            // writer.
+            OutputStream mimeOut = new MIMEOutputStream(outputStream);
+
+            msg.writeTo(mimeOut);
+            mimeOut.flush();
+        } catch (IOException e) {
+            throw new MessagingException(e.toString());
+        } catch (MessagingException e) {
+            throw new MessagingException(e.toString());
+        }
+
+        // now to finish, we send a CRLF sequence, followed by a ".".
+        sendLine("");
+        sendLine(".");
+
+        // use a longer time out here to give the server time to process the
+        // data.
+        try {
+            line = new SMTPReply(receiveLine(TIMEOUT * 2));
+        } catch (MalformedSMTPReplyException e) {
+            throw new MessagingException(e.toString());
+        } catch (MessagingException e) {
+            throw new MessagingException(e.toString());
+        }
+
+        if (line.isError()) {
+            throw new MessagingException("Error issuing SMTP 'DATA' command: " + line);
+        }
+    }
+
+    /**
+     * Sends the QUIT message and receieves the response
+     */
+    protected void sendQuit() throws MessagingException {
+        // there's yet another property that controls whether we should wait for
+        // a reply for a QUIT command. If true, we're suppposed to wait for a response 
+        // from the QUIT command.  Otherwise we just send the QUIT and bail.  The default 
+        // is "false"
+        if (isProtocolPropertyTrue(MAIL_SMTP_QUITWAIT)) {
+            // handle as a real command...we're going to ignore the response.
+            sendCommand("QUIT");
+        } else {
+            // just send the command without waiting for a response. 
+            sendLine("QUIT");
+        }
+    }
+
+    /**
+     * Sets a receiver address for the current message
+     *
+     * @param addr
+     *            The target address.
+     * @param dsn
+     *            An optional notification address appended to the MAIL command.
+     *
+     * @return The status for this particular send operation.
+     * @exception MessagingException
+     */
+    protected SendStatus sendRcptTo(InternetAddress addr, String dsn) throws MessagingException {
+        // compose the command using the fixed up email address. Normally, this
+        // involves adding
+        // "<" and ">" around the address.
+
+        StringBuffer command = new StringBuffer();
+
+        // compose the first part of the command
+        command.append("RCPT TO: ");
+        command.append(fixEmailAddress(addr.getAddress()));
+
+        // if we have DSN information, append it to the command.
+        if (dsn != null) {
+            command.append(" NOTIFY=");
+            command.append(dsn);
+        }
+
+        // get a string version of this command.
+        String commandString = command.toString();
+
+        SMTPReply line = sendCommand(commandString);
+
+        switch (line.getCode()) {
+        // these two are both successful transmissions
+        case COMMAND_ACCEPTED:
+        case ADDRESS_NOT_LOCAL:
+            // we get out of here with the status information.
+            return new SendStatus(SendStatus.SUCCESS, addr, commandString, line);
+
+        // these are considered invalid address errors
+        case PARAMETER_SYNTAX_ERROR:
+        case INVALID_COMMAND_SEQUENCE:
+        case MAILBOX_NOT_FOUND:
+        case INVALID_MAILBOX:
+        case USER_NOT_LOCAL:
+            // we get out of here with the status information.
+            return new SendStatus(SendStatus.INVALID_ADDRESS, addr, commandString, line);
+
+        // the command was valid, but something went wrong in the server.
+        case SERVICE_NOT_AVAILABLE:
+        case MAILBOX_BUSY:
+        case PROCESSING_ERROR:
+        case INSUFFICIENT_STORAGE:
+        case MAILBOX_FULL:
+            // we get out of here with the status information.
+            return new SendStatus(SendStatus.SEND_FAILURE, addr, commandString, line);
+
+        // everything else is considered really bad...
+        default:
+            // we get out of here with the status information.
+            return new SendStatus(SendStatus.GENERAL_ERROR, addr, commandString, line);
+        }
+    }
+
+    /**
+     * Set the sender for this mail.
+     *
+     * @param message
+     *            The message we're sending.
+     *
+     * @exception MessagingException
+     */
+    protected boolean sendMailFrom(Message message) throws MessagingException {
+
+        // need to sort the from value out from a variety of sources.
+        String from = null;
+
+        // first potential source is from the message itself, if it's an
+        // instance of SMTPMessage.
+        if (message instanceof SMTPMessage) {
+            from = ((SMTPMessage) message).getEnvelopeFrom();
+        }
+
+        // if not available from the message, check the protocol property next
+        if (from == null || from.length() == 0) {
+            // the from value can be set explicitly as a property
+            from = getProtocolProperty(MAIL_SMTP_FROM);
+        }
+
+        // if not there, see if we have something in the message header.
+        if (from == null || from.length() == 0) {
+            Address[] fromAddresses = message.getFrom();
+
+            // if we have some addresses in the header, then take the first one
+            // as our From: address
+            if (fromAddresses != null && fromAddresses.length > 0) {
+                from = ((InternetAddress) fromAddresses[0]).getAddress();
+            }
+            // get what the InternetAddress class believes to be the local
+            // address.
+            else {
+                InternetAddress local = InternetAddress.getLocalAddress(session);
+                if (local != null) {
+                    from = local.getAddress(); 
+                }
+            }
+        }
+
+        if (from == null || from.length() == 0) {
+            throw new MessagingException("no FROM address");
+        }
+
+        StringBuffer command = new StringBuffer();
+
+        // start building up the command
+        command.append("MAIL FROM: ");
+        command.append(fixEmailAddress(from));
+
+        // does this server support Delivery Status Notification? Then we may
+        // need to add some extra to the command.
+        if (supportsExtension("DSN")) {
+            String returnNotification = null;
+
+            // the return notification stuff might be set as value on the
+            // message object itself.
+            if (message instanceof SMTPMessage) {
+                // we need to convert the option into a string value.
+                switch (((SMTPMessage) message).getReturnOption()) {
+                case SMTPMessage.RETURN_FULL:
+                    returnNotification = "FULL";
+                    break;
+
+                case SMTPMessage.RETURN_HDRS:
+                    returnNotification = "HDRS";
+                    break;
+                }
+            }
+
+            // if not obtained from the message object, it can also be set as a
+            // property.
+            if (returnNotification == null) {
+                // the DSN value is set by yet another property.
+                returnNotification = getProtocolProperty(MAIL_SMTP_DSN_RET);
+            }
+
+            // if we have a target, add the notification stuff to our FROM
+            // command.
+            if (returnNotification != null) {
+                command.append(" RET=");
+                command.append(returnNotification);
+            }
+        }
+
+        // if this server supports AUTH and we have submitter information, then
+        // we also add the
+        // "AUTH=" keyword to the MAIL FROM command (see RFC 2554).
+
+        if (supportsExtension("AUTH")) {
+            String submitter = null;
+
+            // another option that can be specified on the message object.
+            if (message instanceof SMTPMessage) {
+                submitter = ((SMTPMessage) message).getSubmitter();
+            }
+            // if not part of the object, try for a propery version.
+            if (submitter == null) {
+                // we only send the extra keyword is a submitter is specified.
+                submitter = getProtocolProperty(MAIL_SMTP_SUBMITTER);
+            }
+            // we have one...add the keyword, plus the submitter info in xtext
+            // format (defined by RFC 1891).
+            if (submitter != null) {
+                command.append(" AUTH=");
+                try {
+                    // add this encoded
+                    command.append(new String(XText.encode(submitter.getBytes("US-ASCII"))));
+                } catch (UnsupportedEncodingException e) {
+                    throw new MessagingException("Invalid submitter value " + submitter);
+                }
+            }
+        }
+
+        String extension = null;
+
+        // now see if we need to add any additional extension info to this
+        // command. The extension is not
+        // checked for validity. That's the reponsibility of the caller.
+        if (message instanceof SMTPMessage) {
+            extension = ((SMTPMessage) message).getMailExtension();
+        }
+        // this can come either from the object or from a set property.
+        if (extension == null) {
+            extension = getProtocolProperty(MAIL_SMTP_EXTENSION);
+        }
+
+        // have something real to add?
+        if (extension != null && extension.length() != 0) {
+            // tack this on the end with a blank delimiter.
+            command.append(' ');
+            command.append(extension);
+        }
+
+        // and finally send the command
+        SMTPReply line = sendCommand(command.toString());
+
+        // 250 response indicates success.
+        return line.getCode() == COMMAND_ACCEPTED;
+    }
+
+    /**
+     * Send a command to the server, returning the first response line back as a
+     * reply.
+     *
+     * @param data
+     *            The data to send.
+     *
+     * @return A reply object with the reply line.
+     * @exception MessagingException
+     */
+    protected SMTPReply sendCommand(String data) throws MessagingException {
+        sendLine(data);
+        return getReply();
+    }
+
+    /**
+     * Sends a message down the socket and terminates with the appropriate CRLF
+     */
+    protected void sendLine(String data) throws MessagingException {
+        if (socket == null || !socket.isConnected()) {
+            throw new MessagingException("no connection");
+        }
+        try {
+            System.out.println(">>>>>Sending data " + data + "<<<<<<");
+            outputStream.write(data.getBytes());
+            outputStream.write(CR);
+            outputStream.write(LF);
+            outputStream.flush();
+        } catch (IOException e) {
+            throw new MessagingException(e.toString());
+        }
+    }
+
+    /**
+     * Receives one line from the server. A line is a sequence of bytes
+     * terminated by a CRLF
+     *
+     * @return the line from the server as String
+     */
+    protected String receiveLine() throws MessagingException {
+        return receiveLine(TIMEOUT);
+    }
+
+    /**
+     * Get a reply line for an SMTP command.
+     *
+     * @return An SMTP reply object from the stream.
+     */
+    protected SMTPReply getReply() throws MessagingException {
+        try {
+            lastServerResponse = new SMTPReply(receiveLine());
+        } catch (MalformedSMTPReplyException e) {
+            throw new MessagingException(e.toString());
+        } catch (MessagingException e) {
+            throw e;
+        }
+        return lastServerResponse;
+    }
+
+    /**
+     * Retrieve the last response received from the SMTP server.
+     *
+     * @return The raw response string (including the error code) returned from
+     *         the SMTP server.
+     */
+    public String getLastServerResponse() {
+        if (lastServerResponse == null) {
+            return "";
+        }
+        return lastServerResponse.getReply();
+    }
+
+    /**
+     * Receives one line from the server. A line is a sequence of bytes
+     * terminated by a CRLF
+     *
+     * @return the line from the server as String
+     */
+    protected String receiveLine(int delayMillis) throws MessagingException {
+        if (socket == null || !socket.isConnected()) {
+            throw new MessagingException("no connection");
+        }
+
+        int timeout = 0;
+
+        try {
+            // for now, read byte for byte, looking for a CRLF
+            timeout = socket.getSoTimeout();
+
+            socket.setSoTimeout(delayMillis);
+
+            StringBuffer buff = new StringBuffer();
+
+            int c;
+            boolean crFound = false, lfFound = false;
+
+            while ((c = inputStream.read()) != -1 && crFound == false && lfFound == false) {
+                // we're looking for a CRLF sequence, so mark each one as seen.
+                // Any other
+                // character gets appended to the end of the buffer.
+                if (c == CR) {
+                    crFound = true;
+                } else if (c == LF) {
+                    lfFound = true;
+                } else {
+                    buff.append((char) c);
+                }
+            }
+
+            String line = buff.toString();
+            return line;
+
+        } catch (SocketException e) {
+            throw new MessagingException(e.toString());
+        } catch (IOException e) {
+            throw new MessagingException(e.toString());
+        } finally {
+            try {
+                socket.setSoTimeout(timeout);
+            } catch (SocketException e) {
+                // ignore - was just trying to do the decent thing...
+            }
+        }
+    }
+
+    /**
+     * Convert an InternetAddress into a form sendable on an SMTP mail command.
+     * InternetAddress.getAddress() generally returns just the address portion
+     * of the full address, minus route address markers. We need to ensure we
+     * have an address with '<' and '>' delimiters.
+     *
+     * @param mail
+     *            The mail address returned from InternetAddress.getAddress().
+     *
+     * @return A string formatted for sending.
+     */
+    protected String fixEmailAddress(String mail) {
+        if (mail.charAt(0) == '<') {
+            return mail;
+        }
+        return "<" + mail + ">";
+    }
+
+    /**
+     * Start the handshake process with the server, including setting up and
+     * TLS-level work. At the completion of this task, we should be ready to
+     * authenticate with the server, if needed.
+     */
+    protected boolean sendHandshake() throws MessagingException {
+        // check to see what sort of initial handshake we need to make.
+        boolean useEhlo = !isProtocolPropertyFalse(MAIL_SMTP_EHLO);
+
+        // if we're to use Ehlo, send it and then fall back to just a HELO
+        // message if it fails.
+        if (useEhlo) {
+            if (!sendEhlo()) {
+                sendHelo();
+            }
+        } else {
+            // send the initial hello response.
+            sendHelo();
+        }
+
+        if (useTLS) {
+            // if we've been told to use TLS, and this server doesn't support
+            // it, then this is a failure
+            if (!serverTLS) {
+                throw new MessagingException("Server doesn't support required transport level security");
+            }
+            // if the server supports TLS, then use it for the connection.
+            // on our connection.
+            getConnectedTLSSocket();
+
+            // some servers (gmail is one that I know of) only send a STARTTLS
+            // extension message on the
+            // first EHLO command. Now that we have the TLS handshaking
+            // established, we need to send a
+            // second EHLO message to retrieve the AUTH records from the server.
+            serverAuthenticationMechanisms.clear();
+            if (!sendEhlo()) {
+                throw new MessagingException("Failure sending EHLO command to SMTP server");
+            }
+        }
+
+        // this worked.
+        return true;
+    }
+
+    /**
+     * Send the EHLO command to the SMTP server.
+     *
+     * @return True if the command was accepted ok, false for any errors.
+     * @exception SMTPTransportException
+     * @exception MalformedSMTPReplyException
+     * @exception MessagingException
+     */
+    protected boolean sendEhlo() throws MessagingException {
+        sendLine("EHLO " + getLocalHost());
+
+        SMTPReply line = getReply();
+
+        // we get a 250 code back. The first line is just a greeting, and
+        // extensions are identifed on
+        // continuations. If this fails, then we'll try once more with HELO to
+        // establish bona fides.
+        if (line.getCode() != COMMAND_ACCEPTED) {
+            return false;
+        }
+
+        // get a fresh extension mapping table.
+        serverExtensionArgs = new HashMap();
+
+        // process all of the continuation lines
+        while (line.isContinued()) {
+            // get the next line
+            line = getReply();
+            if (line.getCode() != COMMAND_ACCEPTED) {
+                // all EHLO failures go back to the HELO failback step.
+                return false;
+            }
+            // go process the extention
+            processExtension(line.getMessage());
+        }
+        return true;
+    }
+
+    /**
+     * Send the HELO command to the SMTP server.
+     *
+     * @exception MessagingException
+     */
+    protected void sendHelo() throws MessagingException {
+        sendLine("HELO " + getLocalHost());
+
+        SMTPReply line = getReply();
+
+        // we get a 250 code back. The first line is just a greeting, and
+        // extensions are identifed on
+        // continuations. If this fails, then we'll try once more with HELO to
+        // establish bona fides.
+        if (line.getCode() != COMMAND_ACCEPTED) {
+            throw new MessagingException("Failure sending HELO command to SMTP server");
+        }
+    }
+
+    /**
+     * Retrieve the local client host name.
+     *
+     * @return The string version of the local host name.
+     * @exception SMTPTransportException
+     */
+    public String getLocalHost() throws MessagingException {
+        if (localHost == null) {
+
+            try {
+                localHost = InetAddress.getLocalHost().getHostName();
+            } catch (UnknownHostException e) {
+                // fine, we're misconfigured - ignore
+            }
+
+            if (localHost == null) {
+                localHost = getProtocolProperty(MAIL_SMTP_LOCALHOST);
+            }
+
+            if (localHost == null) {
+                localHost = getSessionProperty(MAIL_LOCALHOST);
+            }
+
+            if (localHost == null) {
+                throw new MessagingException("Can't get local hostname. "
+                        + " Please correctly configure JDK/DNS or set mail.smtp.localhost");
+            }
+        }
+
+        return localHost;
+    }
+
+    /**
+     * Return the current reportSuccess property.
+     *
+     * @return The current reportSuccess property.
+     */
+    public boolean getReportSuccess() {
+        return reportSuccess;
+    }
+
+    /**
+     * Set a new value for the reportSuccess property.
+     *
+     * @param report
+     *            The new setting.
+     */
+    public void setReportSuccess(boolean report) {
+        reportSuccess = report;
+    }
+
+    /**
+     * Return the current startTLS property.
+     *
+     * @return The current startTLS property.
+     */
+    public boolean getStartTLS() {
+        return reportSuccess;
+    }
+
+    /**
+     * Set a new value for the startTLS property.
+     *
+     * @param start
+     *            The new setting.
+     */
+    public void setStartTLS(boolean start) {
+        useTLS = start;
+    }
+
+    /**
+     * Retrieve the SASL realm used for DIGEST-MD5 authentication. This will
+     * either be explicitly set, or retrieved using the mail.smtp.sasl.realm
+     * session property.
+     *
+     * @return The current realm information (which can be null).
+     */
+    public String getSASLRealm() {
+        // if the realm is null, retrieve it using the realm session property.
+        if (realm == null) {
+            realm = getProtocolProperty(MAIL_SMTP_SASL_REALM);
+        }
+        return realm;
+    }
+
+    /**
+     * Explicitly set the SASL realm used for DIGEST-MD5 authenticaiton.
+     *
+     * @param name
+     *            The new realm name.
+     */
+    public void setSASLRealm(String name) {
+        realm = name;
+    }
+
+    /**
+     * Explicitly set the local host information.
+     *
+     * @param localHost
+     *            The new localHost name.
+     */
+    public void setLocalHost(String localHost) {
+        this.localHost = localHost;
+    }
+
+    /**
+     * Process an extension string passed back as the EHLP response.
+     *
+     * @param extension
+     *            The string value of the extension (which will be of the form
+     *            "NAME arguments").
+     */
+    protected void processExtension(String extension) {
+        String extensionName = extension.toUpperCase();
+        String argument = "";
+
+        int delimiter = extension.indexOf(' ');
+        // if we have a keyword with arguments, parse them out and add to the
+        // argument map.
+        if (delimiter != -1) {
+            extensionName = extension.substring(0, delimiter).toUpperCase();
+            argument = extension.substring(delimiter + 1);
+        }
+
+        // add this to the map so it can be tested later.
+        serverExtensionArgs.put(extensionName, argument);
+
+        // process a few special ones that don't require extra parsing.
+        // AUTH and AUTH=LOGIN are handled the same
+        if (extensionName.equals("AUTH")) {
+            // if we don't have an argument on AUTH, this means LOGIN.
+            if (argument == null) {
+                serverAuthenticationMechanisms.put("LOGIN", "LOGIN");
+            } else {
+                // The security mechanisms are blank delimited tokens.
+                StringTokenizer tokenizer = new StringTokenizer(argument);
+
+                while (tokenizer.hasMoreTokens()) {
+                    String mechanism = tokenizer.nextToken().toUpperCase();
+                    serverAuthenticationMechanisms.put(mechanism, mechanism);
+                }
+            }
+        }
+        // special case for some older servers.
+        else if (extensionName.equals("AUTH=LOGIN")) {
+            serverAuthenticationMechanisms.put("LOGIN", "LOGIN");
+        }
+        // does this support transport level security?
+        else if (extensionName.equals("STARTTLS")) {
+            // flag this for later
+            serverTLS = true;
+        }
+    }
+
+    /**
+     * Retrieve any argument information associated with a extension reported
+     * back by the server on the EHLO command.
+     *
+     * @param name
+     *            The name of the target server extension.
+     *
+     * @return Any argument passed on a server extension. Returns null if the
+     *         extension did not include an argument or the extension was not
+     *         supported.
+     */
+    public String extensionParameter(String name) {
+        if (serverExtensionArgs != null) {
+            return (String) serverExtensionArgs.get(name);
+        }
+        return null;
+    }
+
+    /**
+     * Tests whether the target server supports a named extension.
+     *
+     * @param name
+     *            The target extension name.
+     *
+     * @return true if the target server reported on the EHLO command that is
+     *         supports the targer server, false if the extension was not
+     *         supported.
+     */
+    public boolean supportsExtension(String name) {
+        // this only returns null if we don't have this extension
+        return extensionParameter(name) != null;
+    }
+
+    /**
+     * Determine if the target server supports a given authentication mechanism.
+     *
+     * @param mechanism
+     *            The mechanism name.
+     *
+     * @return true if the server EHLO response indicates it supports the
+     *         mechanism, false otherwise.
+     */
+    protected boolean supportsAuthentication(String mechanism) {
+        return serverAuthenticationMechanisms.get(mechanism) != null;
+    }
+
+    /**
+     * Authenticate with the server, if necessary (or possible).
+     *
+     * @return true if we are ok to proceed, false for an authentication
+     *         failures.
+     */
+    protected boolean processAuthentication() throws MessagingException {
+        // no authentication defined?
+        if (!isProtocolPropertyTrue(MAIL_SMTP_AUTH)) {
+            return true;
+        }
+
+        // we need to authenticate, but we don't have userid/password
+        // information...fail this
+        // immediately.
+        if (username == null || password == null) {
+            return false;
+        }
+
+        ClientAuthenticator authenticator = null;
+
+        // now go through the progression of mechanisms we support, from the
+        // most secure to the
+        // least secure.
+
+        if (supportsAuthentication(AUTHENTICATION_DIGESTMD5)) {
+            authenticator = new DigestMD5Authenticator(host, username, password, getSASLRealm());
+        } else if (supportsAuthentication(AUTHENTICATION_CRAMMD5)) {
+            authenticator = new CramMD5Authenticator(username, password);
+        } else if (supportsAuthentication(AUTHENTICATION_LOGIN)) {
+            authenticator = new LoginAuthenticator(username, password);
+        } else if (supportsAuthentication(AUTHENTICATION_PLAIN)) {
+            authenticator = new PlainAuthenticator(username, password);
+        } else {
+            // can't find a mechanism we support in common
+            return false;
+        }
+
+        if (debug) {
+            debugOut("Authenticating for user: " + username + " using " + authenticator.getMechanismName());
+        }
+
+        // if the authenticator has some initial data, we compose a command
+        // containing the initial data.
+        if (authenticator.hasInitialResponse()) {
+            StringBuffer command = new StringBuffer();
+            // the auth command initiates the handshaking.
+            command.append("AUTH ");
+            // and tell the server which mechanism we're using.
+            command.append(authenticator.getMechanismName());
+            command.append(" ");
+            // and append the response data
+            command.append(new String(Base64.encode(authenticator.evaluateChallenge(null))));
+            // send the command now
+            sendLine(command.toString());
+        }
+        // we just send an auth command with the command type.
+        else {
+            StringBuffer command = new StringBuffer();
+            // the auth command initiates the handshaking.
+            command.append("AUTH ");
+            // and tell the server which mechanism we're using.
+            command.append(authenticator.getMechanismName());
+            // send the command now
+            sendLine(command.toString());
+        }
+
+        // now process the challenge sequence. We get a 235 response back when
+        // the server accepts the
+        // authentication, and a 334 indicates we have an additional challenge.
+        while (true) {
+            // get the next line, and if it is an error response, return now.
+            SMTPReply line;
+            try {
+                line = new SMTPReply(receiveLine());
+            } catch (MalformedSMTPReplyException e) {
+                throw new MessagingException(e.toString());
+            } catch (MessagingException e) {
+                throw e;
+            }
+
+            // if we get a completion return, we've passed muster, so give an
+            // authentication response.
+            if (line.getCode() == AUTHENTICATION_COMPLETE) {
+                if (debug) {
+                    debugOut("Successful SMTP authentication");
+                }
+                return true;
+            }
+            // we have an additional challenge to process.
+            else if (line.getCode() == AUTHENTICATION_CHALLENGE) {
+                // Does the authenticator think it is finished? We can't answer
+                // an additional challenge,
+                // so fail this.
+                if (authenticator.isComplete()) {
+                    return false;
+                }
+
+                // we're passed back a challenge value, Base64 encoded.
+                byte[] challenge = Base64.decode(line.getMessage().getBytes());
+
+                // have the authenticator evaluate and send back the encoded
+                // response.
+                sendLine(new String(Base64.encode(authenticator.evaluateChallenge(challenge))));
+            }
+            // completion or challenge are the only responses we know how to
+            // handle. Anything else must
+            // be a failure.
+            else {
+                if (debug) {
+                    debugOut("Authentication failure " + line);
+                }
+                return false;
+            }
+        }
+    }
+
+    /**
+     * Simple holder class for the address/send status duple, as we can have
+     * mixed success for a set of addresses and a message
+     */
+    public class SendStatus {
+        public final static int SUCCESS = 0;
+
+        public final static int INVALID_ADDRESS = 1;
+
+        public final static int SEND_FAILURE = 2;
+
+        public final static int GENERAL_ERROR = 3;
+
+        // the status type of the send operation.
+        int status;
+
+        // the address associated with this status
+        InternetAddress address;
+
+        // the command string send to the server.
+        String cmd;
+
+        // the reply from the server.
+        SMTPReply reply;
+
+        /**
+         * Constructor for a SendStatus item.
+         *
+         * @param s
+         *            The status type.
+         * @param a
+         *            The address this is the status for.
+         * @param c
+         *            The command string associated with this status.
+         * @param r
+         *            The reply information from the server.
+         */
+        public SendStatus(int s, InternetAddress a, String c, SMTPReply r) {
+            this.cmd = c;
+            this.status = s;
+            this.address = a;
+            this.reply = r;
+        }
+
+        /**
+         * Get the status information for this item.
+         *
+         * @return The current status code.
+         */
+        public int getStatus() {
+            return this.status;
+        }
+
+        /**
+         * Retrieve the InternetAddress object associated with this send
+         * operation.
+         *
+         * @return The associated address object.
+         */
+        public InternetAddress getAddress() {
+            return this.address;
+        }
+
+        /**
+         * Retrieve the reply information associated with this send operati
+         *
+         * @return The SMTPReply object received for the operation.
+         */
+        public SMTPReply getReply() {
+            return reply;
+        }
+
+        /**
+         * Get the command string sent for this send operation.
+         *
+         * @return The command string for the MAIL TO command sent to the
+         *         server.
+         */
+        public String getCommand() {
+            return cmd;
+        }
+
+        /**
+         * Get an exception object associated with this send operation. There is
+         * a mechanism for reporting send success via a send operation, so this
+         * will be either a success or failure exception.
+         *
+         * @param reportSuccess
+         *            Indicates if we want success operations too.
+         *
+         * @return A newly constructed exception object.
+         */
+        public MessagingException getException(boolean reportSuccess) {
+            if (status != SUCCESS) {
+                return new SMTPAddressFailedException(address, cmd, reply.getCode(), reply.getMessage());
+            } else {
+                if (reportSuccess) {
+                    return new SMTPAddressSucceededException(address, cmd, reply.getCode(), reply.getMessage());
+                }
+            }
+            return null;
+        }
+    }
+
+    /**
+     * Internal debug output routine.
+     *
+     * @param value
+     *            The string value to output.
+     */
+    protected void debugOut(String message) {
+        debugStream.println("SMTPTransport DEBUG: " + message);
+    }
+
+    /**
+     * Internal debugging routine for reporting exceptions.
+     *
+     * @param message
+     *            A message associated with the exception context.
+     * @param e
+     *            The received exception.
+     */
+    protected void debugOut(String message, Throwable e) {
+        debugOut("Received exception -> " + message);
+        debugOut("Exception message -> " + e.getMessage());
+        e.printStackTrace(debugStream);
+    }
+}
diff --git a/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/transport/smtp/SMTPTransportException.java b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/transport/smtp/SMTPTransportException.java
new file mode 100644
index 0000000..636bbde
--- /dev/null
+++ b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/transport/smtp/SMTPTransportException.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.geronimo.javamail.transport.smtp;
+
+/**
+ * General purpose Exception
+ * 
+ * @version $Id$
+ */
+class SMTPTransportException extends Exception {
+
+    SMTPTransportException() {
+        super();
+    }
+
+    SMTPTransportException(String s) {
+        super(s);
+    }
+
+    SMTPTransportException(String s, Exception t) {
+        super(s, t);
+    }
+
+    SMTPTransportException(Exception t) {
+        super("SMTP Transport error", t);
+    }
+}
diff --git a/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/util/MIMEOutputStream.java b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/util/MIMEOutputStream.java
new file mode 100644
index 0000000..0409331
--- /dev/null
+++ b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/util/MIMEOutputStream.java
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.geronimo.javamail.util;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * An implementation of an OutputStream that performs MIME linebreak
+ * canonicalization and "byte-stuff" so that data content does not get mistaken
+ * for a message data-end marker (CRLF.CRLF)l
+ * 
+ * @version $Rev$ $Date$
+ */
+public class MIMEOutputStream extends OutputStream {
+
+    // the wrappered output stream.
+    protected OutputStream out;
+
+    // last character we handled...used to recongnize line breaks.
+    protected int lastWrite = -1;
+
+    // a flag to indicate we've just processed a line break. This is used for
+    // byte stuffing purposes. This
+    // is initially true, because if the first character of the content is a
+    // period, we need to byte-stuff
+    // immediately.
+    protected boolean atLineBreak = true;
+
+    /**
+     * Create an output stream that writes to the target output stream.
+     * 
+     * @param out
+     *            The wrapped output stream.
+     */
+    public MIMEOutputStream(OutputStream out) {
+        this.out = out;
+    }
+
+    // in order for this to work, we only need override the single character
+    // form, as the others
+    // funnel through this one by default.
+    public void write(int ch) throws IOException {
+        // if this is a CR character, always write out a full sequence, and
+        // remember that we just did this.
+        if (ch == '\r') {
+            out.write((byte) '\r');
+            out.write((byte) '\n');
+            // we've just taken a break;
+            atLineBreak = true;
+        }
+        // if this is a new line, then we need to determine if this is a loner
+        // or part of a CRLF sequence.
+        else if (ch == '\n') {
+            // is this a lone ranger?
+            if (lastWrite != '\r') {
+                // write the full CRLF sequence.
+                out.write((byte) '\r');
+                out.write((byte) '\n');
+            }
+            // regardless of whether we wrote something or not, we're still at a
+            // line break.
+            atLineBreak = true;
+        }
+        // potential byte-stuffing situation?
+        else if (ch == '.') {
+            // ok, this is a potential stuff situation. Did we just have a line
+            // break? Double up the character.
+            if (atLineBreak) {
+                out.write('.');
+            }
+            out.write('.');
+            atLineBreak = false;
+        } else {
+            // just write this out and flip the linebreak flag.
+            out.write(ch);
+            atLineBreak = false;
+        }
+        // remember this last one for CRLF tracking purposes.
+        lastWrite = ch;
+    }
+}
diff --git a/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/util/TraceInputStream.java b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/util/TraceInputStream.java
new file mode 100644
index 0000000..39ee6dd
--- /dev/null
+++ b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/util/TraceInputStream.java
@@ -0,0 +1,121 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.geronimo.javamail.util;
+
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.apache.geronimo.mail.util.QuotedPrintableEncoderStream;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class TraceInputStream extends FilterInputStream {
+    // the current debug setting
+    protected boolean debug = false;
+
+    // the target trace output stream.
+    protected OutputStream traceStream;
+
+    /**
+     * Construct a debug trace stream.
+     * 
+     * @param in
+     *            The source input stream.
+     * @param traceStream
+     *            The side trace stream to which trace data gets written.
+     * @param encode
+     *            Indicates whether we wish the Trace data to be Q-P encoded.
+     */
+    public TraceInputStream(InputStream in, OutputStream traceStream, boolean debug, boolean encode) {
+        super(in);
+        this.debug = debug;
+        if (encode) {
+            this.traceStream = new QuotedPrintableEncoderStream(traceStream);
+        } else {
+            this.traceStream = traceStream;
+        }
+    }
+
+    /**
+     * Set the current setting of the debug trace stream debug flag.
+     * 
+     * @param d
+     *            The new debug flag settings.
+     */
+    public void setDebug(boolean d) {
+        debug = d;
+    }
+
+    /**
+     * Reads up to <code>len</code> bytes of data from this input stream into
+     * an array of bytes. This method blocks until some input is available.
+     * <p>
+     * This method simply performs <code>in.read(b, off, len)</code> and
+     * returns the result.
+     * 
+     * @param b
+     *            the buffer into which the data is read.
+     * @param off
+     *            the start offset of the data.
+     * @param len
+     *            the maximum number of bytes read.
+     * @return the total number of bytes read into the buffer, or
+     *         <code>-1</code> if there is no more data because the end of the
+     *         stream has been reached.
+     * @exception IOException
+     *                if an I/O error occurs.
+     * @see java.io.FilterInputStream#in
+     */
+    public int read(byte b[], int off, int len) throws IOException {
+        int count = in.read(b, off, len);
+        if (debug && count > 0) {
+            traceStream.write(b, off, count);
+        }
+        return count;
+    }
+
+    /**
+     * Reads the next byte of data from this input stream. The value byte is
+     * returned as an <code>int</code> in the range <code>0</code> to
+     * <code>255</code>. If no byte is available because the end of the
+     * stream has been reached, the value <code>-1</code> is returned. This
+     * method blocks until input data is available, the end of the stream is
+     * detected, or an exception is thrown.
+     * <p>
+     * This method simply performs <code>in.read()</code> and returns the
+     * result.
+     * 
+     * @return the next byte of data, or <code>-1</code> if the end of the
+     *         stream is reached.
+     * @exception IOException
+     *                if an I/O error occurs.
+     * @see java.io.FilterInputStream#in
+     */
+    public int read() throws IOException {
+        int b = in.read();
+        if (debug) {
+            traceStream.write(b);
+        }
+        return b;
+    }
+}
diff --git a/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/util/TraceOutputStream.java b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/util/TraceOutputStream.java
new file mode 100644
index 0000000..94cebd8
--- /dev/null
+++ b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/util/TraceOutputStream.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.geronimo.javamail.util;
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.apache.geronimo.mail.util.QuotedPrintableEncoderStream;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class TraceOutputStream extends FilterOutputStream {
+    // the current debug setting
+    protected boolean debug = false;
+
+    // the target trace output stream.
+    protected OutputStream traceStream;
+
+    /**
+     * Construct a debug trace stream.
+     *
+     * @param out
+     *            The target out put stream.
+     * @param traceStream
+     *            The side trace stream to which trace data gets written.
+     * @param encode
+     *            Indicates whether we wish the Trace data to be Q-P encoded.
+     */
+    public TraceOutputStream(OutputStream out, OutputStream traceStream, boolean debug, boolean encode) {
+        super(out);
+        this.debug = debug;
+        if (encode) {
+            this.traceStream = new QuotedPrintableEncoderStream(traceStream);
+        } else {
+            this.traceStream = traceStream;
+        }
+    }
+
+    /**
+     * Set the current setting of the debug trace stream debug flag.
+     *
+     * @param d
+     *            The new debug flag settings.
+     */
+    public void setDebug(boolean d) {
+        debug = d;
+    }
+
+
+    /**
+     * Writes the specified <code>byte</code> to this output stream.
+     * <p>
+     * The <code>write</code> method of <code>FilterOutputStream</code>
+     * calls the <code>write</code> method of its underlying output stream,
+     * that is, it performs <tt>out.write(b)</tt>.
+     * <p>
+     * Implements the abstract <tt>write</tt> method of <tt>OutputStream</tt>.
+     *
+     * @param b
+     *            the <code>byte</code>.
+     * @exception IOException
+     *                if an I/O error occurs.
+     */
+    public void write(int b) throws IOException {
+        if (debug) {
+            traceStream.write(b);
+        }
+        super.write(b);
+    }
+}
diff --git a/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/resources/META-INF/javamail.default.address.map b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/resources/META-INF/javamail.default.address.map
new file mode 100644
index 0000000..3bba278
--- /dev/null
+++ b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/resources/META-INF/javamail.default.address.map
@@ -0,0 +1,34 @@
+##

+## Licensed to the Apache Software Foundation (ASF) under one

+## or more contributor license agreements.  See the NOTICE file

+## distributed with this work for additional information

+## regarding copyright ownership.  The ASF licenses this file

+## to you under the Apache License, Version 2.0 (the

+## "License"); you may not use this file except in compliance

+## with the License.  You may obtain a copy of the License at

+##

+##  http://www.apache.org/licenses/LICENSE-2.0

+##

+## Unless required by applicable law or agreed to in writing,

+## software distributed under the License is distributed on an

+## "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY

+## KIND, either express or implied.  See the License for the

+## specific language governing permissions and limitations

+## under the License.

+##

+

+##

+## $Rev: 437934 $ $Date: 2006-08-28 20:27:42 -0700 (Mon, 28 Aug 2006) $

+##

+

+#

+# This file configures the default behaviour of JavaMail. DO NOT EDIT.

+# Create a new file /META-INF/javamail.address.map and put

+# the same format lines in there.

+#

+# Note that you can't override these defaults, merely add to them.

+#

+# $Rev: 351866 $ $Date: 2005-12-02 20:12:14 -0500 (Fri, 02 Dec 2005) $

+#

+rfc822=smtp

+news=nntp

diff --git a/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/resources/META-INF/javamail.default.providers b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/resources/META-INF/javamail.default.providers
new file mode 100644
index 0000000..fb8ee77
--- /dev/null
+++ b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/resources/META-INF/javamail.default.providers
@@ -0,0 +1,38 @@
+##

+## Licensed to the Apache Software Foundation (ASF) under one

+## or more contributor license agreements.  See the NOTICE file

+## distributed with this work for additional information

+## regarding copyright ownership.  The ASF licenses this file

+## to you under the Apache License, Version 2.0 (the

+## "License"); you may not use this file except in compliance

+## with the License.  You may obtain a copy of the License at

+##

+##  http://www.apache.org/licenses/LICENSE-2.0

+##

+## Unless required by applicable law or agreed to in writing,

+## software distributed under the License is distributed on an

+## "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY

+## KIND, either express or implied.  See the License for the

+## specific language governing permissions and limitations

+## under the License.

+##

+

+##

+## $Rev: 437934 $ $Date: 2006-08-28 20:27:42 -0700 (Mon, 28 Aug 2006) $

+##

+

+#

+# This file configures the default behaviour of JavaMail. DO NOT EDIT.

+# Create a new file /META-INF/javamail.providers and put

+# the same format lines in there.

+#

+# Note that you can't override these defaults, merely add to them.

+#

+# $Rev: 398634 $ $Date: 2006-05-01 12:56:06 -0400 (Mon, 01 May 2006) $

+#

+protocol=smtp; type=transport; class=org.apache.geronimo.javamail.transport.smtp.SMTPTransport; vendor=Apache Software Foundation; version=1.0

+protocol=smtps; type=transport; class=org.apache.geronimo.javamail.transport.smtp.SMTPTSransport; vendor=Apache Software Foundation; version=1.0

+protocol=nntp-post; type=transport; class=org.apache.geronimo.javamail.transport.nntp.NNTPTransport; vendor=Apache Software Foundation; version=1.0

+protocol=nntp; type=store; class=org.apache.geronimo.javamail.store.nntp.NNTPStore; vendor=Apache Software Foundation; version=1.0

+protocol=pop3; type=store; class=org.apache.geronimo.javamail.store.pop3.POP3Store; vendor=Apache Software Foundation; version=1.0

+

diff --git a/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/site/site.xml b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/site/site.xml
new file mode 100644
index 0000000..80f99dd
--- /dev/null
+++ b/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/site/site.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+    
+     http://www.apache.org/licenses/LICENSE-2.0
+    
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+-->
+
+<!-- $Rev$ $Date$ -->
+
+<project name="${project.name}">
+    
+    <body>
+        
+        ${parentProject}
+        
+        ${modules}
+        
+        ${reports}
+        
+    </body>
+
+</project>
+
+
diff --git a/geronimo-javamail_1.4/pom.xml b/geronimo-javamail_1.4/pom.xml
new file mode 100644
index 0000000..cc60809
--- /dev/null
+++ b/geronimo-javamail_1.4/pom.xml
@@ -0,0 +1,142 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+-->
+
+<!-- $Rev$ $Date$ -->
+
+<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>
+
+    <parent>
+        <groupId>org.apache.geronimo.genesis.config</groupId>
+        <artifactId>project-config</artifactId>
+        <version>1.1</version>
+    </parent>
+
+    <groupId>org.apache.geronimo.javamail</groupId>
+    <artifactId>geronimo-javamail-1.4</artifactId>
+    <name>Geronimo JavaMail</name>
+    <packaging>pom</packaging>
+
+    <version>1.2</version>
+
+    <description>
+        Geronimmo JavaMail provider.
+    </description>
+
+    <scm>
+        <connection>scm:svn:http://svn.apache.org/repos/asf/geronimo/javamail/tags/geronimo-javamail_1.4-1.2</connection>
+        <developerConnection>scm:svn:https://svn.apache.org/repos/asf/geronimo/javamail/tags/geronimo-javamail_1.4-1.2</developerConnection>
+        <url>http://svn.apache.org/viewvc/geronimo/javamail/tags/geronimo-javamail_1.4-1.2</url>
+    </scm>
+
+    <dependencyManagement>
+        <dependencies>
+
+            <dependency>
+                <groupId>org.apache.geronimo.specs</groupId>
+                <artifactId>geronimo-activation_1.1_spec</artifactId>
+                <version>1.0</version>
+            </dependency>
+
+            <dependency>
+                <groupId>org.apache.geronimo.specs</groupId>
+                <artifactId>geronimo-javamail_1.4_spec</artifactId>
+                <version>1.1</version>
+            </dependency>
+
+        </dependencies>
+    </dependencyManagement>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <source>1.4</source>
+                    <target>1.4</target>
+                </configuration>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-idea-plugin</artifactId>
+                <configuration>
+                    <jdkName>1.4</jdkName>
+                    <linkModules>true</linkModules>
+                </configuration>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-release-plugin</artifactId>
+                <configuration>
+                    <tagBase>https://svn.apache.org/repos/asf/geronimo/javamail/tags</tagBase>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <repositories>
+        <!--
+        NOTE: The default repositories are picked up by Genesis, but need to
+              specify where Genesis lives to pick it up + any additional repositories.
+        -->
+
+        <repository>
+            <id>apache-snapshots</id>
+            <name>Apache Snapshots Repository</name>
+            <url>http://people.apache.org/repo/m2-snapshot-repository</url>
+            <layout>default</layout>
+            <snapshots>
+                <enabled>true</enabled>
+                <updatePolicy>daily</updatePolicy>
+                <checksumPolicy>ignore</checksumPolicy>
+            </snapshots>
+            <releases>
+                <enabled>false</enabled>
+            </releases>
+        </repository>
+    </repositories>
+
+    <distributionManagement>
+        <site>
+            <id>geronimo-website</id>
+            <url>scp://people.apache.org/www/geronimo.apache.org/maven/javamail</url>
+        </site>
+    </distributionManagement>
+
+    <profiles>
+        <profile>
+            <id>default</id>
+
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+
+            <modules>
+                <module>geronimo-javamail_1.4_provider</module>
+                <module>geronimo-javamail_1.4_mail</module>
+            </modules>
+        </profile>
+    </profiles>
+
+</project>