You are viewing a plain text version of this content. The canonical link for it is here.
Posted to scm@geronimo.apache.org by js...@apache.org on 2006/06/01 06:36:06 UTC

svn commit: r410741 [5/8] - in /geronimo/trunk: applications/console-core/src/java/org/apache/geronimo/console/util/ applications/console-framework/src/java/org/apache/geronimo/console/servlet/ applications/console-standard/ applications/console-standa...

Modified: geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/authentication/DigestMD5Authenticator.java
URL: http://svn.apache.org/viewvc/geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/authentication/DigestMD5Authenticator.java?rev=410741&r1=410740&r2=410741&view=diff
==============================================================================
--- geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/authentication/DigestMD5Authenticator.java (original)
+++ geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/authentication/DigestMD5Authenticator.java Wed May 31 21:35:48 2006
@@ -1,622 +1,622 @@
-/**
- *
- * 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.AuthenticationFailedException;
-import javax.mail.MessagingException;
-
-import org.apache.geronimo.mail.util.Base64;
-import org.apache.geronimo.mail.util.Hex;
-
-/**
- * Process a DIGEST-MD5 authentication, using the challenge/response mechanisms.
- */
-public class DigestMD5Authenticator implements ClientAuthenticator {
-
-    protected static final int AUTHENTICATE_CLIENT = 0;
-
-    protected static final int AUTHENTICATE_SERVER = 1;
-
-    protected static final int AUTHENTICATION_COMPLETE = 2;
-
-    // the host server name
-    protected String host;
-
-    // the user we're authenticating
-    protected String username;
-
-    // the user's password (the "shared secret")
-    protected String password;
-
-    // the target login realm
-    protected String realm;
-
-    // our message digest for processing the challenges.
-    MessageDigest digest;
-
-    // the string we send to the server on the first challenge.
-    protected String clientResponse;
-
-    // the response back from an authentication challenge.
-    protected String authenticationResponse = null;
-
-    // our list of realms received from the server (normally just one).
-    protected ArrayList realms;
-
-    // the nonce value sent from the server
-    protected String nonce;
-
-    // indicates whether we've gone through the entire challenge process.
-    protected int stage = AUTHENTICATE_CLIENT;
-
-    /**
-     * Main constructor.
-     * 
-     * @param host
-     *            The server host name.
-     * @param username
-     *            The login user name.
-     * @param password
-     *            The login password.
-     * @param realm
-     *            The target login realm (can be null).
-     */
-    public DigestMD5Authenticator(String host, String username, String password, String realm) {
-        this.host = host;
-        this.username = username;
-        this.password = password;
-        this.realm = realm;
-    }
-
-    /**
-     * Respond to the hasInitialResponse query. This mechanism does not have an
-     * initial response.
-     * 
-     * @return Always returns false.
-     */
-    public boolean hasInitialResponse() {
-        return false;
-    }
-
-    /**
-     * Indicate whether the challenge/response process is complete.
-     * 
-     * @return True if the last challenge has been processed, false otherwise.
-     */
-    public boolean isComplete() {
-        return stage == AUTHENTICATION_COMPLETE;
-    }
-
-    /**
-     * Retrieve the authenticator mechanism name.
-     * 
-     * @return Always returns the string "DIGEST-MD5"
-     */
-    public String getMechanismName() {
-        return "DIGEST-MD5";
-    }
-
-    /**
-     * Evaluate a DIGEST-MD5 login challenge, returning the a result string that
-     * should satisfy the clallenge.
-     * 
-     * @param challenge
-     *            The decoded challenge data, as a string.
-     * 
-     * @return A formatted challege response, as an array of bytes.
-     * @exception MessagingException
-     */
-    public byte[] evaluateChallenge(byte[] challenge) throws MessagingException {
-
-        // DIGEST-MD5 authentication goes in two stages. First state involves us
-        // validating with the
-        // server, the second stage is the server validating with us, using the
-        // shared secret.
-        switch (stage) {
-        // stage one of the process.
-        case AUTHENTICATE_CLIENT: {
-            // get the response and advance the processing stage.
-            byte[] response = authenticateClient(challenge);
-            stage = AUTHENTICATE_SERVER;
-            return response;
-        }
-
-        // stage two of the process.
-        case AUTHENTICATE_SERVER: {
-            // get the response and advance the processing stage to completed.
-            byte[] response = authenticateServer(challenge);
-            stage = AUTHENTICATION_COMPLETE;
-            return response;
-        }
-
-        // should never happen.
-        default:
-            throw new MessagingException("Invalid LOGIN challenge");
-        }
-    }
-
-    /**
-     * Evaluate a DIGEST-MD5 login server authentication challenge, returning
-     * the a result string that should satisfy the clallenge.
-     * 
-     * @param challenge
-     *            The decoded challenge data, as a string.
-     * 
-     * @return A formatted challege response, as an array of bytes.
-     * @exception MessagingException
-     */
-    public byte[] authenticateServer(byte[] challenge) throws MessagingException {
-        // parse the challenge string and validate.
-        if (!parseChallenge(challenge)) {
-            return null;
-        }
-
-        try {
-            // like all of the client validation steps, the following is order
-            // critical.
-            // first add in the URI information.
-            digest.update((":smtp/" + host).getBytes("US-ASCII"));
-            // now mix in the response we sent originally
-            String responseString = clientResponse + new String(Hex.encode(digest.digest()));
-            digest.update(responseString.getBytes("US-ASCII"));
-
-            // now convert that into a hex encoded string.
-            String validationText = new String(Hex.encode(digest.digest()));
-
-            // if everything went well, this calculated value should match what
-            // we got back from the server.
-            // our response back is just a null string....
-            if (validationText.equals(authenticationResponse)) {
-                return new byte[0];
-            }
-            throw new AuthenticationFailedException("Invalid DIGEST-MD5 response from server");
-        } catch (UnsupportedEncodingException e) {
-            throw new MessagingException("Invalid character encodings");
-        }
-
-    }
-
-    /**
-     * Evaluate a DIGEST-MD5 login client authentication challenge, returning
-     * the a result string that should satisfy the clallenge.
-     * 
-     * @param challenge
-     *            The decoded challenge data, as a string.
-     * 
-     * @return A formatted challege response, as an array of bytes.
-     * @exception MessagingException
-     */
-    public byte[] authenticateClient(byte[] challenge) throws MessagingException {
-        // parse the challenge string and validate.
-        if (!parseChallenge(challenge)) {
-            return null;
-        }
-
-        SecureRandom randomGenerator;
-        // before doing anything, make sure we can get the required crypto
-        // support.
-        try {
-            randomGenerator = new SecureRandom();
-            digest = MessageDigest.getInstance("MD5");
-        } catch (NoSuchAlgorithmException e) {
-            throw new MessagingException("Unable to access cryptography libraries");
-        }
-
-        // if not configured for a realm, take the first realm from the list, if
-        // any
-        if (realm == null) {
-            // if not handed any realms, just use the host name.
-            if (realms.isEmpty()) {
-                realm = host;
-            } else {
-                // pretty arbitrary at this point, so just use the first one.
-                realm = (String) realms.get(0);
-            }
-        }
-
-        // use secure random to generate a collection of bytes. that is our
-        // cnonce value.
-        byte[] cnonceBytes = new byte[32];
-
-        randomGenerator.nextBytes(cnonceBytes);
-        // and get this as a base64 encoded string.
-        String cnonce = new String(Base64.encode(cnonceBytes));
-
-        // Now the digest computation part. This gets a bit tricky, and must be
-        // done in strict order.
-
-        try {
-            // this identifies where we're logging into.
-            String idString = username + ":" + realm + ":" + password;
-            // we get a digest for this string, then use the digest for the
-            // first stage
-            // of the next digest operation.
-            digest.update(digest.digest(idString.getBytes("US-ASCII")));
-
-            // now we add the nonce strings to the digest.
-            String nonceString = ":" + nonce + ":" + cnonce;
-            digest.update(nonceString.getBytes("US-ASCII"));
-
-            // hex encode this digest, and add on the string values
-            // NB, we only support "auth" for the quality of protection value
-            // (qop). We save this in an
-            // instance variable because we'll need this to validate the
-            // response back from the server.
-            clientResponse = new String(Hex.encode(digest.digest())) + ":" + nonce + ":00000001:" + cnonce + ":auth:";
-
-            // now we add in identification values to the hash.
-            String authString = "AUTHENTICATE:smtp/" + host;
-            digest.update(authString.getBytes("US-ASCII"));
-
-            // this gets added on to the client response
-            String responseString = clientResponse + new String(Hex.encode(digest.digest()));
-            // and this gets fed back into the digest
-            digest.update(responseString.getBytes("US-ASCII"));
-
-            // and FINALLY, the challege digest is hex encoded for sending back
-            // to the server (whew).
-            String challengeResponse = new String(Hex.encode(digest.digest()));
-
-            // now finally build the keyword/value part of the challenge
-            // response. These can be
-            // in any order.
-            StringBuffer response = new StringBuffer();
-
-            response.append("username=\"");
-            response.append(username);
-            response.append("\"");
-
-            response.append(",realm=\"");
-            response.append(realm);
-            response.append("\"");
-
-            // we only support auth qop values, and the nonce-count (nc) is
-            // always 1.
-            response.append(",qop=auth");
-            response.append(",nc=00000001");
-
-            response.append(",nonce=\"");
-            response.append(nonce);
-            response.append("\"");
-
-            response.append(",cnonce=\"");
-            response.append(cnonce);
-            response.append("\"");
-
-            response.append(",digest-uri=\"smtp/");
-            response.append(host);
-            response.append("\"");
-
-            response.append(",response=");
-            response.append(challengeResponse);
-
-            return response.toString().getBytes("US-ASCII");
-
-        } catch (UnsupportedEncodingException e) {
-            throw new MessagingException("Invalid character encodings");
-        }
-    }
-
-    /**
-     * Parse the challege string, pulling out information required for our
-     * challenge response.
-     * 
-     * @param challenge
-     *            The challenge data.
-     * 
-     * @return true if there were no errors parsing the string, false otherwise.
-     * @exception MessagingException
-     */
-    protected boolean parseChallenge(byte[] challenge) throws MessagingException {
-        realms = new ArrayList();
-
-        DigestParser parser = new DigestParser(new String(challenge));
-
-        // parse the entire string...but we ignore everything but the options we
-        // support.
-        while (parser.hasMore()) {
-            NameValuePair pair = parser.parseNameValuePair();
-
-            String name = pair.name;
-
-            // realm to add to our list?
-            if (name.equalsIgnoreCase("realm")) {
-                realms.add(pair.value);
-            }
-            // we need the nonce to evaluate the client challenge.
-            else if (name.equalsIgnoreCase("nonce")) {
-                nonce = pair.value;
-            }
-            // rspauth is the challenge replay back, which allows us to validate
-            // that server is also legit.
-            else if (name.equalsIgnoreCase("rspauth")) {
-                authenticationResponse = pair.value;
-            }
-        }
-
-        return true;
-    }
-
-    /**
-     * Inner class for parsing a DIGEST-MD5 challenge string, which is composed
-     * of "name=value" pairs, separated by "," characters.
-     */
-    class DigestParser {
-        // the challenge we're parsing
-        String challenge;
-
-        // length of the challenge
-        int length;
-
-        // current parsing position
-        int position;
-
-        /**
-         * Normal constructor.
-         * 
-         * @param challenge
-         *            The challenge string to be parsed.
-         */
-        public DigestParser(String challenge) {
-            this.challenge = challenge;
-            this.length = challenge.length();
-            position = 0;
-        }
-
-        /**
-         * Test if there are more values to parse.
-         * 
-         * @return true if we've not reached the end of the challenge string,
-         *         false if the challenge has been completely consumed.
-         */
-        private boolean hasMore() {
-            return position < length;
-        }
-
-        /**
-         * Return the character at the current parsing position.
-         * 
-         * @return The string character for the current parse position.
-         */
-        private char currentChar() {
-            return challenge.charAt(position);
-        }
-
-        /**
-         * step forward to the next character position.
-         */
-        private void nextChar() {
-            position++;
-        }
-
-        /**
-         * Skip over any white space characters in the challenge string.
-         */
-        private void skipSpaces() {
-            while (position < length && Character.isWhitespace(currentChar())) {
-                position++;
-            }
-        }
-
-        /**
-         * Parse a quoted string used with a name/value pair, accounting for
-         * escape characters embedded within the string.
-         * 
-         * @return The string value of the character string.
-         */
-        private String parseQuotedValue() {
-            // we're here because we found the starting double quote. Step over
-            // it and parse to the closing
-            // one.
-            nextChar();
-
-            StringBuffer value = new StringBuffer();
-
-            while (hasMore()) {
-                char ch = currentChar();
-
-                // is this an escape char?
-                if (ch == '\\') {
-                    // step past this, and grab the following character
-                    nextChar();
-                    // we have an invalid quoted string....
-                    if (!hasMore()) {
-                        return null;
-                    }
-                    value.append(currentChar());
-                }
-                // end of the string?
-                else if (ch == '"') {
-                    // step over this so the caller doesn't process it.
-                    nextChar();
-                    // return the constructed string.
-                    return value.toString();
-                } else {
-                    // step over the character and contine with the next
-                    // characteer1
-                    value.append(ch);
-                }
-                nextChar();
-            }
-            /* fell off the end without finding a closing quote! */
-            return null;
-        }
-
-        /**
-         * Parse a token value used with a name/value pair.
-         * 
-         * @return The string value of the token. Returns null if nothing is
-         *         found up to the separater.
-         */
-        private String parseTokenValue() {
-
-            StringBuffer value = new StringBuffer();
-
-            while (hasMore()) {
-                char ch = currentChar();
-                switch (ch) {
-                // process the token separators.
-                case ' ':
-                case '\t':
-                case '(':
-                case ')':
-                case '<':
-                case '>':
-                case '@':
-                case ',':
-                case ';':
-                case ':':
-                case '\\':
-                case '"':
-                case '/':
-                case '[':
-                case ']':
-                case '?':
-                case '=':
-                case '{':
-                case '}':
-                    // no token characters found? this is bad.
-                    if (value.length() == 0) {
-                        return null;
-                    }
-                    // return the accumulated characters.
-                    return value.toString();
-
-                default:
-                    // is this a control character? That's a delimiter (likely
-                    // invalid for the next step,
-                    // but it is a token terminator.
-                    if (ch < 32 || ch > 127) {
-                        // no token characters found? this is bad.
-                        if (value.length() == 0) {
-                            return null;
-                        }
-                        // return the accumulated characters.
-                        return value.toString();
-                    }
-                    value.append(ch);
-                    break;
-                }
-                // step to the next character.
-                nextChar();
-            }
-            // no token characters found? this is bad.
-            if (value.length() == 0) {
-                return null;
-            }
-            // return the accumulated characters.
-            return value.toString();
-        }
-
-        /**
-         * Parse out a name token of a name/value pair.
-         * 
-         * @return The string value of the name.
-         */
-        private String parseName() {
-            // skip to the value start
-            skipSpaces();
-
-            // the name is a token.
-            return parseTokenValue();
-        }
-
-        /**
-         * Parse out a a value of a name/value pair.
-         * 
-         * @return The string value associated with the name.
-         */
-        private String parseValue() {
-            // skip to the value start
-            skipSpaces();
-
-            // start of a quoted string?
-            if (currentChar() == '"') {
-                // parse it out as a string.
-                return parseQuotedValue();
-            }
-            // the value must be a token.
-            return parseTokenValue();
-        }
-
-        /**
-         * Parse a name/value pair in an DIGEST-MD5 string.
-         * 
-         * @return A NameValuePair object containing the two parts of the value.
-         * @exception MessagingException
-         */
-        public NameValuePair parseNameValuePair() throws MessagingException {
-            // get the name token
-            String name = parseName();
-            if (name == null) {
-                throw new MessagingException("Name syntax error");
-            }
-
-            // the name should be followed by an "=" sign
-            if (!hasMore() || currentChar() != '=') {
-                throw new MessagingException("Name/value pair syntax error");
-            }
-
-            // step over the equals
-            nextChar();
-
-            // now get the value part
-            String value = parseValue();
-            if (value == null) {
-                throw new MessagingException("Name/value pair syntax error");
-            }
-
-            // skip forward to the terminator, which should either be the end of
-            // the line or a ","
-            skipSpaces();
-            // all that work, only to have a syntax error at the end (sigh)
-            if (hasMore()) {
-                if (currentChar() != ',') {
-                    throw new MessagingException("Name/value pair syntax error");
-                }
-                // step over, and make sure we position ourselves at either the
-                // end or the first
-                // real character for parsing the next name/value pair.
-                nextChar();
-                skipSpaces();
-            }
-            return new NameValuePair(name, value);
-        }
-    }
-
-    /**
-     * Simple inner class to represent a name/value pair.
-     */
-    public class NameValuePair {
-        public String name;
-
-        public String value;
-
-        NameValuePair(String name, String value) {
-            this.name = name;
-            this.value = value;
-        }
-
-    }
-}
+/**
+ *
+ * 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.AuthenticationFailedException;
+import javax.mail.MessagingException;
+
+import org.apache.geronimo.mail.util.Base64;
+import org.apache.geronimo.mail.util.Hex;
+
+/**
+ * Process a DIGEST-MD5 authentication, using the challenge/response mechanisms.
+ */
+public class DigestMD5Authenticator implements ClientAuthenticator {
+
+    protected static final int AUTHENTICATE_CLIENT = 0;
+
+    protected static final int AUTHENTICATE_SERVER = 1;
+
+    protected static final int AUTHENTICATION_COMPLETE = 2;
+
+    // the host server name
+    protected String host;
+
+    // the user we're authenticating
+    protected String username;
+
+    // the user's password (the "shared secret")
+    protected String password;
+
+    // the target login realm
+    protected String realm;
+
+    // our message digest for processing the challenges.
+    MessageDigest digest;
+
+    // the string we send to the server on the first challenge.
+    protected String clientResponse;
+
+    // the response back from an authentication challenge.
+    protected String authenticationResponse = null;
+
+    // our list of realms received from the server (normally just one).
+    protected ArrayList realms;
+
+    // the nonce value sent from the server
+    protected String nonce;
+
+    // indicates whether we've gone through the entire challenge process.
+    protected int stage = AUTHENTICATE_CLIENT;
+
+    /**
+     * Main constructor.
+     * 
+     * @param host
+     *            The server host name.
+     * @param username
+     *            The login user name.
+     * @param password
+     *            The login password.
+     * @param realm
+     *            The target login realm (can be null).
+     */
+    public DigestMD5Authenticator(String host, String username, String password, String realm) {
+        this.host = host;
+        this.username = username;
+        this.password = password;
+        this.realm = realm;
+    }
+
+    /**
+     * Respond to the hasInitialResponse query. This mechanism does not have an
+     * initial response.
+     * 
+     * @return Always returns false.
+     */
+    public boolean hasInitialResponse() {
+        return false;
+    }
+
+    /**
+     * Indicate whether the challenge/response process is complete.
+     * 
+     * @return True if the last challenge has been processed, false otherwise.
+     */
+    public boolean isComplete() {
+        return stage == AUTHENTICATION_COMPLETE;
+    }
+
+    /**
+     * Retrieve the authenticator mechanism name.
+     * 
+     * @return Always returns the string "DIGEST-MD5"
+     */
+    public String getMechanismName() {
+        return "DIGEST-MD5";
+    }
+
+    /**
+     * Evaluate a DIGEST-MD5 login challenge, returning the a result string that
+     * should satisfy the clallenge.
+     * 
+     * @param challenge
+     *            The decoded challenge data, as a string.
+     * 
+     * @return A formatted challege response, as an array of bytes.
+     * @exception MessagingException
+     */
+    public byte[] evaluateChallenge(byte[] challenge) throws MessagingException {
+
+        // DIGEST-MD5 authentication goes in two stages. First state involves us
+        // validating with the
+        // server, the second stage is the server validating with us, using the
+        // shared secret.
+        switch (stage) {
+        // stage one of the process.
+        case AUTHENTICATE_CLIENT: {
+            // get the response and advance the processing stage.
+            byte[] response = authenticateClient(challenge);
+            stage = AUTHENTICATE_SERVER;
+            return response;
+        }
+
+        // stage two of the process.
+        case AUTHENTICATE_SERVER: {
+            // get the response and advance the processing stage to completed.
+            byte[] response = authenticateServer(challenge);
+            stage = AUTHENTICATION_COMPLETE;
+            return response;
+        }
+
+        // should never happen.
+        default:
+            throw new MessagingException("Invalid LOGIN challenge");
+        }
+    }
+
+    /**
+     * Evaluate a DIGEST-MD5 login server authentication challenge, returning
+     * the a result string that should satisfy the clallenge.
+     * 
+     * @param challenge
+     *            The decoded challenge data, as a string.
+     * 
+     * @return A formatted challege response, as an array of bytes.
+     * @exception MessagingException
+     */
+    public byte[] authenticateServer(byte[] challenge) throws MessagingException {
+        // parse the challenge string and validate.
+        if (!parseChallenge(challenge)) {
+            return null;
+        }
+
+        try {
+            // like all of the client validation steps, the following is order
+            // critical.
+            // first add in the URI information.
+            digest.update((":smtp/" + host).getBytes("US-ASCII"));
+            // now mix in the response we sent originally
+            String responseString = clientResponse + new String(Hex.encode(digest.digest()));
+            digest.update(responseString.getBytes("US-ASCII"));
+
+            // now convert that into a hex encoded string.
+            String validationText = new String(Hex.encode(digest.digest()));
+
+            // if everything went well, this calculated value should match what
+            // we got back from the server.
+            // our response back is just a null string....
+            if (validationText.equals(authenticationResponse)) {
+                return new byte[0];
+            }
+            throw new AuthenticationFailedException("Invalid DIGEST-MD5 response from server");
+        } catch (UnsupportedEncodingException e) {
+            throw new MessagingException("Invalid character encodings");
+        }
+
+    }
+
+    /**
+     * Evaluate a DIGEST-MD5 login client authentication challenge, returning
+     * the a result string that should satisfy the clallenge.
+     * 
+     * @param challenge
+     *            The decoded challenge data, as a string.
+     * 
+     * @return A formatted challege response, as an array of bytes.
+     * @exception MessagingException
+     */
+    public byte[] authenticateClient(byte[] challenge) throws MessagingException {
+        // parse the challenge string and validate.
+        if (!parseChallenge(challenge)) {
+            return null;
+        }
+
+        SecureRandom randomGenerator;
+        // before doing anything, make sure we can get the required crypto
+        // support.
+        try {
+            randomGenerator = new SecureRandom();
+            digest = MessageDigest.getInstance("MD5");
+        } catch (NoSuchAlgorithmException e) {
+            throw new MessagingException("Unable to access cryptography libraries");
+        }
+
+        // if not configured for a realm, take the first realm from the list, if
+        // any
+        if (realm == null) {
+            // if not handed any realms, just use the host name.
+            if (realms.isEmpty()) {
+                realm = host;
+            } else {
+                // pretty arbitrary at this point, so just use the first one.
+                realm = (String) realms.get(0);
+            }
+        }
+
+        // use secure random to generate a collection of bytes. that is our
+        // cnonce value.
+        byte[] cnonceBytes = new byte[32];
+
+        randomGenerator.nextBytes(cnonceBytes);
+        // and get this as a base64 encoded string.
+        String cnonce = new String(Base64.encode(cnonceBytes));
+
+        // Now the digest computation part. This gets a bit tricky, and must be
+        // done in strict order.
+
+        try {
+            // this identifies where we're logging into.
+            String idString = username + ":" + realm + ":" + password;
+            // we get a digest for this string, then use the digest for the
+            // first stage
+            // of the next digest operation.
+            digest.update(digest.digest(idString.getBytes("US-ASCII")));
+
+            // now we add the nonce strings to the digest.
+            String nonceString = ":" + nonce + ":" + cnonce;
+            digest.update(nonceString.getBytes("US-ASCII"));
+
+            // hex encode this digest, and add on the string values
+            // NB, we only support "auth" for the quality of protection value
+            // (qop). We save this in an
+            // instance variable because we'll need this to validate the
+            // response back from the server.
+            clientResponse = new String(Hex.encode(digest.digest())) + ":" + nonce + ":00000001:" + cnonce + ":auth:";
+
+            // now we add in identification values to the hash.
+            String authString = "AUTHENTICATE:smtp/" + host;
+            digest.update(authString.getBytes("US-ASCII"));
+
+            // this gets added on to the client response
+            String responseString = clientResponse + new String(Hex.encode(digest.digest()));
+            // and this gets fed back into the digest
+            digest.update(responseString.getBytes("US-ASCII"));
+
+            // and FINALLY, the challege digest is hex encoded for sending back
+            // to the server (whew).
+            String challengeResponse = new String(Hex.encode(digest.digest()));
+
+            // now finally build the keyword/value part of the challenge
+            // response. These can be
+            // in any order.
+            StringBuffer response = new StringBuffer();
+
+            response.append("username=\"");
+            response.append(username);
+            response.append("\"");
+
+            response.append(",realm=\"");
+            response.append(realm);
+            response.append("\"");
+
+            // we only support auth qop values, and the nonce-count (nc) is
+            // always 1.
+            response.append(",qop=auth");
+            response.append(",nc=00000001");
+
+            response.append(",nonce=\"");
+            response.append(nonce);
+            response.append("\"");
+
+            response.append(",cnonce=\"");
+            response.append(cnonce);
+            response.append("\"");
+
+            response.append(",digest-uri=\"smtp/");
+            response.append(host);
+            response.append("\"");
+
+            response.append(",response=");
+            response.append(challengeResponse);
+
+            return response.toString().getBytes("US-ASCII");
+
+        } catch (UnsupportedEncodingException e) {
+            throw new MessagingException("Invalid character encodings");
+        }
+    }
+
+    /**
+     * Parse the challege string, pulling out information required for our
+     * challenge response.
+     * 
+     * @param challenge
+     *            The challenge data.
+     * 
+     * @return true if there were no errors parsing the string, false otherwise.
+     * @exception MessagingException
+     */
+    protected boolean parseChallenge(byte[] challenge) throws MessagingException {
+        realms = new ArrayList();
+
+        DigestParser parser = new DigestParser(new String(challenge));
+
+        // parse the entire string...but we ignore everything but the options we
+        // support.
+        while (parser.hasMore()) {
+            NameValuePair pair = parser.parseNameValuePair();
+
+            String name = pair.name;
+
+            // realm to add to our list?
+            if (name.equalsIgnoreCase("realm")) {
+                realms.add(pair.value);
+            }
+            // we need the nonce to evaluate the client challenge.
+            else if (name.equalsIgnoreCase("nonce")) {
+                nonce = pair.value;
+            }
+            // rspauth is the challenge replay back, which allows us to validate
+            // that server is also legit.
+            else if (name.equalsIgnoreCase("rspauth")) {
+                authenticationResponse = pair.value;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Inner class for parsing a DIGEST-MD5 challenge string, which is composed
+     * of "name=value" pairs, separated by "," characters.
+     */
+    class DigestParser {
+        // the challenge we're parsing
+        String challenge;
+
+        // length of the challenge
+        int length;
+
+        // current parsing position
+        int position;
+
+        /**
+         * Normal constructor.
+         * 
+         * @param challenge
+         *            The challenge string to be parsed.
+         */
+        public DigestParser(String challenge) {
+            this.challenge = challenge;
+            this.length = challenge.length();
+            position = 0;
+        }
+
+        /**
+         * Test if there are more values to parse.
+         * 
+         * @return true if we've not reached the end of the challenge string,
+         *         false if the challenge has been completely consumed.
+         */
+        private boolean hasMore() {
+            return position < length;
+        }
+
+        /**
+         * Return the character at the current parsing position.
+         * 
+         * @return The string character for the current parse position.
+         */
+        private char currentChar() {
+            return challenge.charAt(position);
+        }
+
+        /**
+         * step forward to the next character position.
+         */
+        private void nextChar() {
+            position++;
+        }
+
+        /**
+         * Skip over any white space characters in the challenge string.
+         */
+        private void skipSpaces() {
+            while (position < length && Character.isWhitespace(currentChar())) {
+                position++;
+            }
+        }
+
+        /**
+         * Parse a quoted string used with a name/value pair, accounting for
+         * escape characters embedded within the string.
+         * 
+         * @return The string value of the character string.
+         */
+        private String parseQuotedValue() {
+            // we're here because we found the starting double quote. Step over
+            // it and parse to the closing
+            // one.
+            nextChar();
+
+            StringBuffer value = new StringBuffer();
+
+            while (hasMore()) {
+                char ch = currentChar();
+
+                // is this an escape char?
+                if (ch == '\\') {
+                    // step past this, and grab the following character
+                    nextChar();
+                    // we have an invalid quoted string....
+                    if (!hasMore()) {
+                        return null;
+                    }
+                    value.append(currentChar());
+                }
+                // end of the string?
+                else if (ch == '"') {
+                    // step over this so the caller doesn't process it.
+                    nextChar();
+                    // return the constructed string.
+                    return value.toString();
+                } else {
+                    // step over the character and contine with the next
+                    // characteer1
+                    value.append(ch);
+                }
+                nextChar();
+            }
+            /* fell off the end without finding a closing quote! */
+            return null;
+        }
+
+        /**
+         * Parse a token value used with a name/value pair.
+         * 
+         * @return The string value of the token. Returns null if nothing is
+         *         found up to the separater.
+         */
+        private String parseTokenValue() {
+
+            StringBuffer value = new StringBuffer();
+
+            while (hasMore()) {
+                char ch = currentChar();
+                switch (ch) {
+                // process the token separators.
+                case ' ':
+                case '\t':
+                case '(':
+                case ')':
+                case '<':
+                case '>':
+                case '@':
+                case ',':
+                case ';':
+                case ':':
+                case '\\':
+                case '"':
+                case '/':
+                case '[':
+                case ']':
+                case '?':
+                case '=':
+                case '{':
+                case '}':
+                    // no token characters found? this is bad.
+                    if (value.length() == 0) {
+                        return null;
+                    }
+                    // return the accumulated characters.
+                    return value.toString();
+
+                default:
+                    // is this a control character? That's a delimiter (likely
+                    // invalid for the next step,
+                    // but it is a token terminator.
+                    if (ch < 32 || ch > 127) {
+                        // no token characters found? this is bad.
+                        if (value.length() == 0) {
+                            return null;
+                        }
+                        // return the accumulated characters.
+                        return value.toString();
+                    }
+                    value.append(ch);
+                    break;
+                }
+                // step to the next character.
+                nextChar();
+            }
+            // no token characters found? this is bad.
+            if (value.length() == 0) {
+                return null;
+            }
+            // return the accumulated characters.
+            return value.toString();
+        }
+
+        /**
+         * Parse out a name token of a name/value pair.
+         * 
+         * @return The string value of the name.
+         */
+        private String parseName() {
+            // skip to the value start
+            skipSpaces();
+
+            // the name is a token.
+            return parseTokenValue();
+        }
+
+        /**
+         * Parse out a a value of a name/value pair.
+         * 
+         * @return The string value associated with the name.
+         */
+        private String parseValue() {
+            // skip to the value start
+            skipSpaces();
+
+            // start of a quoted string?
+            if (currentChar() == '"') {
+                // parse it out as a string.
+                return parseQuotedValue();
+            }
+            // the value must be a token.
+            return parseTokenValue();
+        }
+
+        /**
+         * Parse a name/value pair in an DIGEST-MD5 string.
+         * 
+         * @return A NameValuePair object containing the two parts of the value.
+         * @exception MessagingException
+         */
+        public NameValuePair parseNameValuePair() throws MessagingException {
+            // get the name token
+            String name = parseName();
+            if (name == null) {
+                throw new MessagingException("Name syntax error");
+            }
+
+            // the name should be followed by an "=" sign
+            if (!hasMore() || currentChar() != '=') {
+                throw new MessagingException("Name/value pair syntax error");
+            }
+
+            // step over the equals
+            nextChar();
+
+            // now get the value part
+            String value = parseValue();
+            if (value == null) {
+                throw new MessagingException("Name/value pair syntax error");
+            }
+
+            // skip forward to the terminator, which should either be the end of
+            // the line or a ","
+            skipSpaces();
+            // all that work, only to have a syntax error at the end (sigh)
+            if (hasMore()) {
+                if (currentChar() != ',') {
+                    throw new MessagingException("Name/value pair syntax error");
+                }
+                // step over, and make sure we position ourselves at either the
+                // end or the first
+                // real character for parsing the next name/value pair.
+                nextChar();
+                skipSpaces();
+            }
+            return new NameValuePair(name, value);
+        }
+    }
+
+    /**
+     * Simple inner class to represent a name/value pair.
+     */
+    public class NameValuePair {
+        public String name;
+
+        public String value;
+
+        NameValuePair(String name, String value) {
+            this.name = name;
+            this.value = value;
+        }
+
+    }
+}

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

Modified: geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/authentication/LoginAuthenticator.java
URL: http://svn.apache.org/viewvc/geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/authentication/LoginAuthenticator.java?rev=410741&r1=410740&r2=410741&view=diff
==============================================================================
--- geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/authentication/LoginAuthenticator.java (original)
+++ geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/authentication/LoginAuthenticator.java Wed May 31 21:35:48 2006
@@ -1,138 +1,138 @@
-/**
- *
- * 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;
-
-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");
-    }
-}
+/**
+ *
+ * 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;
+
+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

Modified: geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/authentication/PlainAuthenticator.java
URL: http://svn.apache.org/viewvc/geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/authentication/PlainAuthenticator.java?rev=410741&r1=410740&r2=410741&view=diff
==============================================================================
--- geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/authentication/PlainAuthenticator.java (original)
+++ geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/authentication/PlainAuthenticator.java Wed May 31 21:35:48 2006
@@ -1,110 +1,110 @@
-/**
- *
- * 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;
-
-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");
-        }
-    }
-}
+/**
+ *
+ * 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;
+
+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/SMTPAddressFailedException.java
URL: http://svn.apache.org/viewvc/geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/smtp/SMTPAddressFailedException.java?rev=410741&r1=410740&r2=410741&view=diff
==============================================================================
--- geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/smtp/SMTPAddressFailedException.java (original)
+++ geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/smtp/SMTPAddressFailedException.java Wed May 31 21:35:48 2006
@@ -1,78 +1,78 @@
-/**
- *
- * 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.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;
-    }
-}
+/**
+ *
+ * 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.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

Modified: geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/smtp/SMTPAddressSucceededException.java
URL: http://svn.apache.org/viewvc/geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/smtp/SMTPAddressSucceededException.java?rev=410741&r1=410740&r2=410741&view=diff
==============================================================================
--- geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/smtp/SMTPAddressSucceededException.java (original)
+++ geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/smtp/SMTPAddressSucceededException.java Wed May 31 21:35:48 2006
@@ -1,78 +1,78 @@
-/**
- *
- * 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.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;
-    }
-}
+/**
+ *
+ * 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.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

Modified: geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/smtp/SMTPMessage.java
URL: http://svn.apache.org/viewvc/geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/smtp/SMTPMessage.java?rev=410741&r1=410740&r2=410741&view=diff
==============================================================================
--- geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/smtp/SMTPMessage.java (original)
+++ geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/smtp/SMTPMessage.java Wed May 31 21:35:48 2006
@@ -1,235 +1,235 @@
-/**
- *
- * 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.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;
-    }
-}
+/**
+ *
+ * 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.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;
+    }
+}