You are viewing a plain text version of this content. The canonical link for it is here.
Posted to scm@geronimo.apache.org by jl...@apache.org on 2020/10/07 15:58:41 UTC

svn commit: r1882306 [14/17] - in /geronimo/javamail/trunk/geronimo-javamail_1.6: ./ geronimo-javamail_1.6_mail/ geronimo-javamail_1.6_mail/src/ geronimo-javamail_1.6_mail/src/site/ geronimo-javamail_1.6_provider/ geronimo-javamail_1.6_provider/src/ ge...

Added: geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/transport/smtp/SMTPMessage.java
URL: http://svn.apache.org/viewvc/geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/transport/smtp/SMTPMessage.java?rev=1882306&view=auto
==============================================================================
--- geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/transport/smtp/SMTPMessage.java (added)
+++ geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/transport/smtp/SMTPMessage.java Wed Oct  7 15:58:39 2020
@@ -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;
+    }
+}

Added: geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/transport/smtp/SMTPReply.java
URL: http://svn.apache.org/viewvc/geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/transport/smtp/SMTPReply.java?rev=1882306&view=auto
==============================================================================
--- geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/transport/smtp/SMTPReply.java (added)
+++ geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/transport/smtp/SMTPReply.java Wed Oct  7 15:58:39 2020
@@ -0,0 +1,208 @@
+/*
+ * 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.util.ArrayList;
+import java.util.List;
+
+/**
+ * Util class to represent a reply from a SMTP server
+ * 
+ * @version $Rev$ $Date$
+ */
+class SMTPReply {
+    // SMTP reply codes
+    public static final int SERVICE_READY = 220;
+
+    public static final int SERVICE_CLOSING = 221;
+
+    public static final int AUTHENTICATION_COMPLETE = 235;
+
+    public static final int COMMAND_ACCEPTED = 250;
+
+    public static final int ADDRESS_NOT_LOCAL = 251;
+
+    public static final int AUTHENTICATION_CHALLENGE = 334;
+
+    public static final int START_MAIL_INPUT = 354;
+
+    public static final int SERVICE_NOT_AVAILABLE = 421;
+
+    public static final int MAILBOX_BUSY = 450;
+
+    public static final int PROCESSING_ERROR = 451;
+
+    public static final int INSUFFICIENT_STORAGE = 452;
+
+    public static final int COMMAND_SYNTAX_ERROR = 500;
+
+    public static final int PARAMETER_SYNTAX_ERROR = 501;
+
+    public static final int COMMAND_NOT_IMPLEMENTED = 502;
+
+    public static final int INVALID_COMMAND_SEQUENCE = 503;
+
+    public static final int COMMAND_PARAMETER_NOT_IMPLEMENTED = 504;
+
+    public static final int MAILBOX_NOT_FOUND = 550;
+
+    public static final int USER_NOT_LOCAL = 551;
+
+    public static final int MAILBOX_FULL = 552;
+
+    public static final int INVALID_MAILBOX = 553;
+
+    public static final int TRANSACTION_FAILED = 553;
+    
+    // The original reply string
+    private final String reply;
+
+    // returned message code
+    private final int code;
+
+    // the returned message text
+    private final String message;
+    
+    // additional returned lines from a continued response 
+    private List lines; 
+
+    // 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);
+        }
+    }
+    
+    /**
+     * Add a line to a continued response.  This will 
+     * update the continued status if the end of the 
+     * response is reached. 
+     * 
+     * @param line   The line to add.
+     */
+    public void addLine(String line) {
+        if (lines == null) {
+            lines = new ArrayList(); 
+            lines.add(message); 
+        }
+        // mark if we're still continued 
+        continued = line.charAt(3) == '-';
+        // add the line to the list 
+        lines.add(line.substring(4)); 
+    }
+    
+    /**
+     * Get the list of all of the lines associated with 
+     * this reply. 
+     * 
+     * @return A List containing all lines associated with this
+     *         reply.
+     */
+    public List getLines() {
+        if (lines == null) {
+            lines = new ArrayList(); 
+            lines.add(message); 
+        }
+        return lines;
+    }
+    
+
+    /**
+     * 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();
+    }
+}

Added: geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/transport/smtp/SMTPSTransport.java
URL: http://svn.apache.org/viewvc/geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/transport/smtp/SMTPSTransport.java?rev=1882306&view=auto
==============================================================================
--- geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/transport/smtp/SMTPSTransport.java (added)
+++ geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/transport/smtp/SMTPSTransport.java Wed Oct  7 15:58:39 2020
@@ -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);
+    }
+}

Added: geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/transport/smtp/SMTPSendFailedException.java
URL: http://svn.apache.org/viewvc/geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/transport/smtp/SMTPSendFailedException.java?rev=1882306&view=auto
==============================================================================
--- geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/transport/smtp/SMTPSendFailedException.java (added)
+++ geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/transport/smtp/SMTPSendFailedException.java Wed Oct  7 15:58:39 2020
@@ -0,0 +1,75 @@
+/*
+ * 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(
+        String cmd, int rc, String err, 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;
+    }
+}

Added: geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/transport/smtp/SMTPTransport.java
URL: http://svn.apache.org/viewvc/geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/transport/smtp/SMTPTransport.java?rev=1882306&view=auto
==============================================================================
--- geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/transport/smtp/SMTPTransport.java (added)
+++ geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/transport/smtp/SMTPTransport.java Wed Oct  7 15:58:39 2020
@@ -0,0 +1,656 @@
+/*
+ * 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.UnsupportedEncodingException;
+import java.net.Socket; 
+import java.util.ArrayList;
+
+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.mail.internet.MimeMultipart;     
+import javax.mail.internet.MimePart;     
+
+import org.apache.geronimo.javamail.util.ProtocolProperties; 
+import org.apache.geronimo.javamail.transport.smtp.SMTPConnection.SendStatus; 
+
+/**
+ * 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 {
+    /**
+     * 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_DSN_NOTIFY = "dsn.notify";
+    protected static final String MAIL_SMTP_SENDPARTIAL = "sendpartial";
+    protected static final String MAIL_SMTP_EXTENSION = "mailextension";
+    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;
+
+
+    // do we use SSL for our initial connection?
+    protected boolean sslConnection = false;
+    
+    // our accessor for protocol properties and the holder of 
+    // protocol-specific information 
+    protected ProtocolProperties props; 
+    // our active connection object 
+    protected SMTPConnection connection;
+
+    // the last response line received from the server.
+    protected SMTPReply lastServerResponse = null;
+
+    /**
+     * 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);
+        
+        // create the protocol property holder.  This gives an abstraction over the different 
+        // flavors of the protocol. 
+        props = new ProtocolProperties(session, protocol, sslConnection, defaultPort); 
+        // the connection manages connection for the transport 
+        connection = new SMTPConnection(props); 
+    }
+
+    
+    /**
+     * 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 {
+        connection.connect(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 {
+        // the connection pool handles all of the details here. 
+        return connection.protocolConnect(host, port, username, password);
+    }
+
+    /**
+     * 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 reportSuccess = getReportSuccess(); 
+        
+        // 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 = props.getBooleanProperty(MAIL_SMTP_SENDPARTIAL, false);
+        }
+
+        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 (!connection.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 = connection.getLastServerResponse(); 
+                // now send an "uber-exception" to indicate the failure.
+                throw new SMTPSendFailedException("MAIL FROM", last.getCode(), last.getMessage(), null, sent, unsent,
+                        invalid);
+            }
+
+            // get the additional notification status, if available 
+            String dsn = getDeliveryStatusNotification(message);
+
+            // 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 = connection.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) {
+                // 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.
+                    connection.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
+                connection.sendData((MimeMessage)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, reportSuccess);
+
+                // 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;
+        }
+    }
+    
+    
+    /**
+     * Determine what delivery status notification should
+     * be added to the RCPT TO: command. 
+     * 
+     * @param message The message we're sending.
+     * 
+     * @return The string NOTIFY= value to add to the command. 
+     */
+    protected String getDeliveryStatusNotification(Message message) {
+        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 = props.getProperty(MAIL_SMTP_DSN_NOTIFY);
+        }
+        return dsn; 
+    }
+    
+
+
+    /**
+     * 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();
+        // NB:  We reuse the connection if asked to reconnect 
+        connection.close();
+    }
+    
+    
+    /**
+     * 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;
+    }
+
+    /**
+     * 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]);
+    }
+    
+
+    /**
+     * Retrieve the local client host name.
+     *
+     * @return The string version of the local host name.
+     * @exception SMTPTransportException
+     */
+    public String getLocalHost() throws MessagingException {
+        return connection.getLocalHost(); 
+    }
+
+    
+    /**
+     * Explicitly set the local host information.
+     *
+     * @param localHost
+     *            The new localHost name.
+     */
+    public void setLocalHost(String localHost) {
+        connection.setLocalHost(localHost); 
+    }
+
+    
+    /**
+     * Return the current reportSuccess property.
+     *
+     * @return The current reportSuccess property.
+     */
+    public boolean getReportSuccess() {
+        return connection.getReportSuccess(); 
+    }
+
+    /**
+     * Set a new value for the reportSuccess property.
+     *
+     * @param report
+     *            The new setting.
+     */
+    public void setReportSuccess(boolean report) {
+        connection.setReportSuccess(report); 
+    }
+
+    /**
+     * Return the current startTLS property.
+     *
+     * @return The current startTLS property.
+     */
+    public boolean getStartTLS() {
+        return connection.getStartTLS(); 
+    }
+
+    /**
+     * Set a new value for the startTLS property.
+     *
+     * @param start
+     *            The new setting.
+     */
+    public void setStartTLS(boolean start) {
+        connection.setStartTLS(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() {
+        return connection.getSASLRealm(); 
+    }
+
+    /**
+     * Explicitly set the SASL realm used for DIGEST-MD5 authenticaiton.
+     *
+     * @param name
+     *            The new realm name.
+     */
+    public void setSASLRealm(String name) {
+        connection.setSASLRealm(name); 
+    }
+}

Added: geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/transport/smtp/SMTPTransportException.java
URL: http://svn.apache.org/viewvc/geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/transport/smtp/SMTPTransportException.java?rev=1882306&view=auto
==============================================================================
--- geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/transport/smtp/SMTPTransportException.java (added)
+++ geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/transport/smtp/SMTPTransportException.java Wed Oct  7 15:58:39 2020
@@ -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);
+    }
+}

Added: geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/util/CommandFailedException.java
URL: http://svn.apache.org/viewvc/geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/util/CommandFailedException.java?rev=1882306&view=auto
==============================================================================
--- geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/util/CommandFailedException.java (added)
+++ geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/util/CommandFailedException.java Wed Oct  7 15:58:39 2020
@@ -0,0 +1,35 @@
+/**
+ * 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 javax.mail.MessagingException; 
+
+public class CommandFailedException extends MessagingException {
+    public CommandFailedException() {
+        super();
+    }
+
+    public CommandFailedException(String message) {
+        super(message);
+    }
+
+    public CommandFailedException(String message, Exception cause) {
+        super(message, cause);
+    }
+}
+

Added: geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/util/ConnectionException.java
URL: http://svn.apache.org/viewvc/geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/util/ConnectionException.java?rev=1882306&view=auto
==============================================================================
--- geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/util/ConnectionException.java (added)
+++ geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/util/ConnectionException.java Wed Oct  7 15:58:39 2020
@@ -0,0 +1,35 @@
+/**
+ * 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 javax.mail.MessagingException; 
+
+public class ConnectionException extends MessagingException {
+    public ConnectionException() {
+        super();
+    }
+
+    public ConnectionException(String message) {
+        super(message);
+    }
+
+    public ConnectionException(String message, Exception cause) {
+        super(message, cause);
+    }
+}
+

Added: geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/util/CountingOutputStream.java
URL: http://svn.apache.org/viewvc/geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/util/CountingOutputStream.java?rev=1882306&view=auto
==============================================================================
--- geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/util/CountingOutputStream.java (added)
+++ geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/util/CountingOutputStream.java Wed Oct  7 15:58:39 2020
@@ -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.util;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * An implementation of an OutputStream just counts
+ * the number of bytes written to the stream. 
+ * @version $Rev$ $Date$
+ */
+public class CountingOutputStream extends OutputStream {
+    // the counting accumulator 
+    int count = 0; 
+
+    // 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 {
+        // just increment the count 
+        count++; 
+    }
+    
+    
+    /**
+     * Get the current accumulator total for this stream. 
+     * 
+     * @return The current count. 
+     */
+    public int getCount() {
+        return count; 
+    }
+    
+    
+    /**
+     * Reset the counter to zero. 
+     */
+    public void reset() {
+        count = 0; 
+    }
+}
+

Added: geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/util/InvalidCommandException.java
URL: http://svn.apache.org/viewvc/geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/util/InvalidCommandException.java?rev=1882306&view=auto
==============================================================================
--- geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/util/InvalidCommandException.java (added)
+++ geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/util/InvalidCommandException.java Wed Oct  7 15:58:39 2020
@@ -0,0 +1,35 @@
+/**
+ * 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 javax.mail.MessagingException; 
+
+public class InvalidCommandException extends MessagingException {
+    public InvalidCommandException() {
+        super();
+    }
+
+    public InvalidCommandException(String message) {
+        super(message);
+    }
+
+    public InvalidCommandException(String message, Exception cause) {
+        super(message, cause);
+    }
+}
+

Added: geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/util/MIMEInputReader.java
URL: http://svn.apache.org/viewvc/geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/util/MIMEInputReader.java?rev=1882306&view=auto
==============================================================================
--- geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/util/MIMEInputReader.java (added)
+++ geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/util/MIMEInputReader.java Wed Oct  7 15:58:39 2020
@@ -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.util;
+
+import java.io.IOException;
+import java.io.Reader; 
+
+/**
+ * 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 MIMEInputReader extends Reader {
+
+    // the wrappered output stream.
+    protected Reader source;
+
+    // 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;
+    // we've hit the terminating marker on the data
+    protected boolean endOfData = false; 
+    
+
+    /**
+     * Create an input reader that reads from the source input reader  
+     * 
+     * @param out
+     *            The wrapped Reader        
+     */
+    public MIMEInputReader(Reader source) {
+        this.source = source; 
+    }
+    
+    /**
+     * Concrete implementation of the Reader read() 
+     * abstract method.  This appears to be the only 
+     * abstract method, so all of the other reads must 
+     * funnel through this method. 
+     * 
+     * @param buffer The buffer to fill.
+     * @param off    The offset to start adding characters.
+     * @param len    The number of requested characters.
+     * 
+     * @return The actual count of characters read.  Returns -1 
+     *         if we hit an EOF without reading any characters.
+     * @exception IOException
+     */
+    public int read(char buffer[], int off, int len) throws IOException {
+        // we've been asked for nothing, we'll return nothing. 
+        if (len == 0) {
+            return 0; 
+        }
+        
+        // have we hit the end of data?  Return a -1 indicator
+        if (endOfData) {
+            return -1; 
+        }
+        
+        // number of bytes read 
+        int bytesRead = 0; 
+        
+        int lastRead; 
+        
+        while (bytesRead < len && (lastRead = source.read()) >= 0) {
+            // We are checking for the end of a multiline response
+            // the format is .CRLF
+            
+            // we also have to check for byte-stuffing situation 
+            // where we remove a leading period.  
+            if (atLineBreak && lastRead == '.') {
+                // step to the next character 
+                lastRead = source.read();
+                // we have ".CR"...this is our end of stream 
+                // marker.  Consume the LF from the reader and return 
+                if (lastRead == '\r') {
+                    source.read(); 
+                    // no more reads from this point. 
+                    endOfData = true; 
+                    break; 
+                }
+                // the next character SHOULD be a ".".  We swallow the first 
+                // dot and just write the next character to the buffer 
+                atLineBreak = false; 
+            }
+            else if (lastRead == '\n') {
+                // hit an end-of-line marker?
+                // remember we just had a line break 
+                atLineBreak = true; 
+            }
+            else 
+            {
+                // something other than a line break character 
+                atLineBreak = false; 
+            }
+            // add the character to the buffer 
+            buffer[off++] = (char)lastRead; 
+            bytesRead++; 
+        }
+        
+        // we must have had an EOF condition of some sort 
+        if (bytesRead == 0) {
+            return -1; 
+        }
+        // return the actual length read in 
+        return bytesRead; 
+    }
+    
+     /**
+      * Close the stream.  This is a NOP for this stream.  
+      * 
+      * @exception IOException
+      */
+     public void close() throws IOException {
+         // does nothing 
+     }
+}
+

Added: geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/util/MIMEOutputStream.java
URL: http://svn.apache.org/viewvc/geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/util/MIMEOutputStream.java?rev=1882306&view=auto
==============================================================================
--- geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/util/MIMEOutputStream.java (added)
+++ geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/util/MIMEOutputStream.java Wed Oct  7 15:58:39 2020
@@ -0,0 +1,132 @@
+/*
+ * 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;
+    }
+    
+    
+    /**
+     * Force the stream to be terminated at a line break. 
+     * This is generally in preparation for the transport to 
+     * write out an end-of-data marker, which generally 
+     * needs to be preceded by a CRLF sequence. 
+     * 
+     * @exception IOException
+     */
+    public void forceTerminatingLineBreak() throws IOException {
+        if (!atLineBreak) {
+            out.write((byte) '\r');
+            out.write((byte) '\n');
+            // we've just taken a break;
+            atLineBreak = true;
+        }
+    }
+    
+    
+    /**
+     * Write out the SMTP terminator to the output stream. 
+     * This ensures that we don't write out an extra 
+     * CRLF if the data terminates with that value.  
+     * 
+     * @exception IOException
+     */
+    public void writeSMTPTerminator() throws IOException {
+        forceTerminatingLineBreak(); 
+        out.write('.'); 
+        out.write('\r'); 
+        out.write('\n'); 
+    }
+}