geronimo-scm mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From bsny...@apache.org
Subject svn commit: r375375 [1/3] - in /geronimo/trunk/modules/javamail-transport/src: java/org/apache/geronimo/javamail/authentication/ java/org/apache/geronimo/javamail/transport/smtp/ resources/META-INF/
Date Mon, 06 Feb 2006 21:16:32 GMT
Author: bsnyder
Date: Mon Feb  6 13:16:28 2006
New Revision: 375375

URL: http://svn.apache.org/viewcvs?rev=375375&view=rev
Log:
Added patch for GERONIMO-1593.

Added:
    geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/authentication/
    geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/authentication/ClientAuthenticator.java   (with props)
    geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/authentication/CramMD5Authenticator.java   (with props)
    geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/authentication/DigestMD5Authenticator.java   (with props)
    geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/authentication/LoginAuthenticator.java   (with props)
    geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/authentication/PlainAuthenticator.java   (with props)
    geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/smtp/SMTPAddressFailedException.java   (with props)
    geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/smtp/SMTPAddressSucceededException.java   (with props)
    geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/smtp/SMTPMessage.java   (with props)
    geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/smtp/SMTPSTransport.java   (with props)
    geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/smtp/SMTPSendFailedException.java   (with props)
Modified:
    geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/smtp/MalformedSMTPReplyException.java
    geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/smtp/SMTPReply.java
    geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/smtp/SMTPTransport.java
    geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/smtp/SMTPTransportException.java
    geronimo/trunk/modules/javamail-transport/src/resources/META-INF/javamail.default.providers

Added: geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/authentication/ClientAuthenticator.java
URL: http://svn.apache.org/viewcvs/geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/authentication/ClientAuthenticator.java?rev=375375&view=auto
==============================================================================
--- geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/authentication/ClientAuthenticator.java (added)
+++ geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/authentication/ClientAuthenticator.java Mon Feb  6 13:16:28 2006
@@ -0,0 +1,84 @@
+/**
+ *
+ * Copyright 2003-2005 The Apache Software Foundation
+ *
+ *  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.
+ */
+
+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();
+}

Propchange: geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/authentication/ClientAuthenticator.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/authentication/ClientAuthenticator.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Propchange: geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/authentication/ClientAuthenticator.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/authentication/CramMD5Authenticator.java
URL: http://svn.apache.org/viewcvs/geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/authentication/CramMD5Authenticator.java?rev=375375&view=auto
==============================================================================
--- geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/authentication/CramMD5Authenticator.java (added)
+++ geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/authentication/CramMD5Authenticator.java Mon Feb  6 13:16:28 2006
@@ -0,0 +1,167 @@
+/**
+ *
+ * Copyright 2003-2005 The Apache Software Foundation
+ *
+ *  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.
+ */
+
+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.Base64;
+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
+    }
+}
+

Propchange: geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/authentication/CramMD5Authenticator.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/authentication/CramMD5Authenticator.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Propchange: geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/authentication/CramMD5Authenticator.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/authentication/DigestMD5Authenticator.java
URL: http://svn.apache.org/viewcvs/geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/authentication/DigestMD5Authenticator.java?rev=375375&view=auto
==============================================================================
--- geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/authentication/DigestMD5Authenticator.java (added)
+++ geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/authentication/DigestMD5Authenticator.java Mon Feb  6 13:16:28 2006
@@ -0,0 +1,600 @@
+/**
+ *
+ * Copyright 2003-2005 The Apache Software Foundation
+ *
+ *  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.
+ */
+
+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.MessagingException;
+import javax.mail.AuthenticationFailedException;
+
+import org.apache.geronimo.mail.util.Base64;
+import org.apache.geronimo.mail.util.Hex;
+
+import sun.security.provider.MD5;
+
+/**
+ * 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;
+        }
+
+    }
+}

Propchange: geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/authentication/DigestMD5Authenticator.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/authentication/DigestMD5Authenticator.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Propchange: geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/authentication/DigestMD5Authenticator.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/authentication/LoginAuthenticator.java
URL: http://svn.apache.org/viewcvs/geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/authentication/LoginAuthenticator.java?rev=375375&view=auto
==============================================================================
--- geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/authentication/LoginAuthenticator.java (added)
+++ geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/authentication/LoginAuthenticator.java Mon Feb  6 13:16:28 2006
@@ -0,0 +1,134 @@
+/**
+ *
+ * Copyright 2003-2005 The Apache Software Foundation
+ *
+ *  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.
+ */
+
+package org.apache.geronimo.javamail.authentication;
+
+import java.io.UnsupportedEncodingException;
+
+import javax.mail.MessagingException;
+
+import org.apache.geronimo.mail.util.Base64;
+
+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");
+    }
+}
+

Propchange: geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/authentication/LoginAuthenticator.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/authentication/LoginAuthenticator.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Propchange: geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/authentication/LoginAuthenticator.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/authentication/PlainAuthenticator.java
URL: http://svn.apache.org/viewcvs/geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/authentication/PlainAuthenticator.java?rev=375375&view=auto
==============================================================================
--- geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/authentication/PlainAuthenticator.java (added)
+++ geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/authentication/PlainAuthenticator.java Mon Feb  6 13:16:28 2006
@@ -0,0 +1,109 @@
+/**
+ *
+ * Copyright 2003-2005 The Apache Software Foundation
+ *
+ *  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.
+ */
+
+package org.apache.geronimo.javamail.authentication;
+
+import java.io.UnsupportedEncodingException;
+
+import javax.mail.MessagingException;
+
+import org.apache.geronimo.mail.util.Base64;
+
+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");
+        }
+    }
+}
+
+

Propchange: geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/authentication/PlainAuthenticator.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/authentication/PlainAuthenticator.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Propchange: geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/authentication/PlainAuthenticator.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/smtp/MalformedSMTPReplyException.java
URL: http://svn.apache.org/viewcvs/geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/smtp/MalformedSMTPReplyException.java?rev=375375&r1=375374&r2=375375&view=diff
==============================================================================
--- geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/smtp/MalformedSMTPReplyException.java (original)
+++ geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/smtp/MalformedSMTPReplyException.java Mon Feb  6 13:16:28 2006
@@ -31,7 +31,7 @@
         super(msg);
     }
 
-    MalformedSMTPReplyException(String msg, Throwable t) {
+    MalformedSMTPReplyException(String msg, Exception t) {
         super(msg, t);
     }
 }

Added: geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/smtp/SMTPAddressFailedException.java
URL: http://svn.apache.org/viewcvs/geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/smtp/SMTPAddressFailedException.java?rev=375375&view=auto
==============================================================================
--- geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/smtp/SMTPAddressFailedException.java (added)
+++ geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/smtp/SMTPAddressFailedException.java Mon Feb  6 13:16:28 2006
@@ -0,0 +1,76 @@
+/**
+ *
+ * Copyright 2003-2005 The Apache Software Foundation
+ *
+ *  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.
+ */
+
+package org.apache.geronimo.javamail.transport.smtp;
+
+import javax.mail.MessagingException;
+import javax.mail.Address;
+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;
+    }
+}
+
+

Propchange: geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/smtp/SMTPAddressFailedException.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/smtp/SMTPAddressFailedException.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Propchange: geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/smtp/SMTPAddressFailedException.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/smtp/SMTPAddressSucceededException.java
URL: http://svn.apache.org/viewcvs/geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/smtp/SMTPAddressSucceededException.java?rev=375375&view=auto
==============================================================================
--- geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/smtp/SMTPAddressSucceededException.java (added)
+++ geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/smtp/SMTPAddressSucceededException.java Mon Feb  6 13:16:28 2006
@@ -0,0 +1,76 @@
+/**
+ *
+ * Copyright 2003-2005 The Apache Software Foundation
+ *
+ *  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.
+ */
+
+package org.apache.geronimo.javamail.transport.smtp;
+
+import javax.mail.MessagingException;
+import javax.mail.Address;
+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;
+    }
+}
+
+

Propchange: geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/smtp/SMTPAddressSucceededException.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/smtp/SMTPAddressSucceededException.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Propchange: geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/smtp/SMTPAddressSucceededException.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/smtp/SMTPMessage.java
URL: http://svn.apache.org/viewcvs/geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/smtp/SMTPMessage.java?rev=375375&view=auto
==============================================================================
--- geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/smtp/SMTPMessage.java (added)
+++ geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/smtp/SMTPMessage.java Mon Feb  6 13:16:28 2006
@@ -0,0 +1,224 @@
+/**
+ *
+ * Copyright 2003-2005 The Apache Software Foundation
+ *
+ *  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.
+ */
+
+package org.apache.geronimo.javamail.transport.smtp;
+
+import java.io.InputStream;
+
+import javax.mail.internet.MimeMessage;
+import javax.mail.MessagingException;
+import javax.mail.Session;
+
+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;
+    }
+}

Propchange: geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/smtp/SMTPMessage.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/smtp/SMTPMessage.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Propchange: geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/smtp/SMTPMessage.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/smtp/SMTPReply.java
URL: http://svn.apache.org/viewcvs/geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/smtp/SMTPReply.java?rev=375375&r1=375374&r2=375375&view=diff
==============================================================================
--- geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/smtp/SMTPReply.java (original)
+++ geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/smtp/SMTPReply.java Mon Feb  6 13:16:28 2006
@@ -23,22 +23,39 @@
  * @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;
-    private final char firstChar;
+    // indicates that this is a continuation response
+    private boolean continued;
 
     SMTPReply(String s) throws MalformedSMTPReplyException {
-        // first three must be the return code
+        // 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) {
-            throw new MalformedSMTPReplyException("Too Short! : " + s);
+            code = -1;
+            message = s;
+            return;
         }
 
         try {
-            firstChar = s.charAt(0);
+            continued = false;
             code = Integer.parseInt(s.substring(0, 3));
 
-            // message should be separated by a space
+            // 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 = "";
@@ -48,23 +65,52 @@
         }
     }
 
-    int getCode() {
+    /**
+     * Return the code value associated with the reply.
+     *
+     * @return The integer code associated with the reply.
+     */
+    public int getCode() {
         return this.code;
     }
 
-    String getMessage() {
+    /**
+     * 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() {
-        if (firstChar == '5' || firstChar == '4') {
-            return true;
-        }
+        // error codes are all above 400
+        return code >= 400;
+    }
 
-        return false;
+
+    /**
+     * 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() {

Added: geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/smtp/SMTPSTransport.java
URL: http://svn.apache.org/viewcvs/geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/smtp/SMTPSTransport.java?rev=375375&view=auto
==============================================================================
--- geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/smtp/SMTPSTransport.java (added)
+++ geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/smtp/SMTPSTransport.java Mon Feb  6 13:16:28 2006
@@ -0,0 +1,34 @@
+/**
+ *
+ * Copyright 2003-2005 The Apache Software Foundation
+ *
+ *  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.
+ */
+
+package org.apache.geronimo.javamail.transport.smtp;
+
+import javax.mail.MessagingException;
+import javax.mail.Session;
+import javax.mail.Transport;
+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);
+    }
+}

Propchange: geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/smtp/SMTPSTransport.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/smtp/SMTPSTransport.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Propchange: geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/smtp/SMTPSTransport.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/smtp/SMTPSendFailedException.java
URL: http://svn.apache.org/viewcvs/geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/smtp/SMTPSendFailedException.java?rev=375375&view=auto
==============================================================================
--- geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/smtp/SMTPSendFailedException.java (added)
+++ geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/smtp/SMTPSendFailedException.java Mon Feb  6 13:16:28 2006
@@ -0,0 +1,65 @@
+/**
+ *
+ * Copyright 2003-2005 The Apache Software Foundation
+ *
+ *  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.
+ */
+
+package org.apache.geronimo.javamail.transport.smtp;
+
+import javax.mail.SendFailedException;
+import javax.mail.Address;
+
+
+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;
+    }
+}
+

Propchange: geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/smtp/SMTPSendFailedException.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/smtp/SMTPSendFailedException.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Propchange: geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/smtp/SMTPSendFailedException.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain



Mime
View raw message