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

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

Added: geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/authentication/DigestMD5Authenticator.java
URL: http://svn.apache.org/viewvc/geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/authentication/DigestMD5Authenticator.java?rev=1882306&view=auto
==============================================================================
--- geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/authentication/DigestMD5Authenticator.java (added)
+++ geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/authentication/DigestMD5Authenticator.java Wed Oct  7 15:58:39 2020
@@ -0,0 +1,628 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.geronimo.javamail.authentication;
+
+import java.io.UnsupportedEncodingException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.util.ArrayList;
+
+import javax.mail.AuthenticationFailedException;
+import javax.mail.MessagingException;
+
+import org.apache.geronimo.mail.util.Base64;
+import org.apache.geronimo.mail.util.Hex;
+
+/**
+ * Process a DIGEST-MD5 authentication, using the challenge/response mechanisms.
+ */
+public class DigestMD5Authenticator implements ClientAuthenticator {
+
+    protected static final int AUTHENTICATE_CLIENT = 0;
+
+    protected static final int AUTHENTICATE_SERVER = 1;
+
+    protected static final int AUTHENTICATION_COMPLETE = 2;
+
+    // the host server name
+    protected String host;
+
+    // the user we're authenticating
+    protected String username;
+
+    // the user's password (the "shared secret")
+    protected String password;
+
+    // the target login realm
+    protected String realm;
+
+    // our message digest for processing the challenges.
+    MessageDigest digest;
+
+    // the string we send to the server on the first challenge.
+    protected String clientResponse;
+
+    // the response back from an authentication challenge.
+    protected String authenticationResponse = null;
+
+    // our list of realms received from the server (normally just one).
+    protected ArrayList realms;
+
+    // the nonce value sent from the server
+    protected String nonce;
+
+    // indicates whether we've gone through the entire challenge process.
+    protected int stage = AUTHENTICATE_CLIENT;
+
+    /**
+     * Main constructor.
+     *
+     * @param host
+     *            The server host name.
+     * @param username
+     *            The login user name.
+     * @param password
+     *            The login password.
+     * @param realm
+     *            The target login realm (can be null).
+     */
+    public DigestMD5Authenticator(String host, String username, String password, String realm) {
+        this.host = host;
+        this.username = username;
+        this.password = password;
+        this.realm = realm;
+    }
+
+    /**
+     * Respond to the hasInitialResponse query. This mechanism does not have an
+     * initial response.
+     *
+     * @return Always returns false.
+     */
+    public boolean hasInitialResponse() {
+        return false;
+    }
+
+    /**
+     * Indicate whether the challenge/response process is complete.
+     *
+     * @return True if the last challenge has been processed, false otherwise.
+     */
+    public boolean isComplete() {
+        return stage == AUTHENTICATION_COMPLETE;
+    }
+
+    /**
+     * Retrieve the authenticator mechanism name.
+     *
+     * @return Always returns the string "DIGEST-MD5"
+     */
+    public String getMechanismName() {
+        return "DIGEST-MD5";
+    }
+
+    /**
+     * Evaluate a DIGEST-MD5 login challenge, returning the a result string that
+     * should satisfy the clallenge.
+     *
+     * @param challenge
+     *            The decoded challenge data, as a string.
+     *
+     * @return A formatted challege response, as an array of bytes.
+     * @exception MessagingException
+     */
+    public byte[] evaluateChallenge(byte[] challenge) throws MessagingException {
+
+        // DIGEST-MD5 authentication goes in two stages. First state involves us
+        // validating with the
+        // server, the second stage is the server validating with us, using the
+        // shared secret.
+        switch (stage) {
+        // stage one of the process.
+        case AUTHENTICATE_CLIENT: {
+            // get the response and advance the processing stage.
+            byte[] response = authenticateClient(challenge);
+            stage = AUTHENTICATE_SERVER;
+            return response;
+        }
+
+        // stage two of the process.
+        case AUTHENTICATE_SERVER: {
+            // get the response and advance the processing stage to completed.
+            byte[] response = authenticateServer(challenge);
+            stage = AUTHENTICATION_COMPLETE;
+            return response;
+        }
+
+        // should never happen.
+        default:
+            throw new MessagingException("Invalid LOGIN challenge");
+        }
+    }
+
+    /**
+     * Evaluate a DIGEST-MD5 login server authentication challenge, returning
+     * the a result string that should satisfy the clallenge.
+     *
+     * @param challenge
+     *            The decoded challenge data, as a string.
+     *
+     * @return A formatted challege response, as an array of bytes.
+     * @exception MessagingException
+     */
+    public byte[] authenticateServer(byte[] challenge) throws MessagingException {
+        // parse the challenge string and validate.
+        if (!parseChallenge(challenge)) {
+            return null;
+        }
+
+        try {
+            // like all of the client validation steps, the following is order
+            // critical.
+            // first add in the URI information.
+            digest.update((":smtp/" + host).getBytes("US-ASCII"));
+            // now mix in the response we sent originally
+            String responseString = clientResponse + new String(Hex.encode(digest.digest()), "US-ASCII");
+            digest.update(responseString.getBytes("US-ASCII"));
+
+            // now convert that into a hex encoded string.
+            String validationText = new String(Hex.encode(digest.digest()), "US-ASCII");
+
+            // 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);
+
+        try {
+            // and get this as a base64 encoded string.
+            String cnonce = new String(Base64.encode(cnonceBytes), "US-ASCII");
+
+            // Now the digest computation part. This gets a bit tricky, and must be
+            // done in strict order.
+            // 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()), "US-ASCII") + ":" + 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()), "US-ASCII");
+            // 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()), "US-ASCII");
+
+            // 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 = null;
+        try {
+            parser = new DigestParser(new String(challenge, "US-ASCII"));
+        } catch (UnsupportedEncodingException ex) {
+        }
+
+        // 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;
+        }
+
+    }
+}

Added: geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/authentication/LoginAuthenticator.java
URL: http://svn.apache.org/viewvc/geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/authentication/LoginAuthenticator.java?rev=1882306&view=auto
==============================================================================
--- geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/authentication/LoginAuthenticator.java (added)
+++ geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/authentication/LoginAuthenticator.java Wed Oct  7 15:58:39 2020
@@ -0,0 +1,140 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.geronimo.javamail.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");
+    }
+}

Added: geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/authentication/PlainAuthenticator.java
URL: http://svn.apache.org/viewvc/geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/authentication/PlainAuthenticator.java?rev=1882306&view=auto
==============================================================================
--- geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/authentication/PlainAuthenticator.java (added)
+++ geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/authentication/PlainAuthenticator.java Wed Oct  7 15:58:39 2020
@@ -0,0 +1,126 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.geronimo.javamail.authentication;
+
+import java.io.UnsupportedEncodingException;
+
+import javax.mail.MessagingException;
+
+//Implements RFC 4616 PLAIN SASL mechanism
+//See also RFC 3501, section 6.2.2"
+//an RFC 2595, section 6"
+public class PlainAuthenticator implements ClientAuthenticator {
+
+    // the sasl authzid we're authenticating
+    protected String authzid;
+
+    // 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 authzid
+     *            SASL authenticationid (optional)
+     * @param username
+     *            The login user name.
+     * @param password
+     *            The login password.
+     */
+    public PlainAuthenticator(String authzid, String username, String password) {
+        this.authzid = authzid;
+        this.username = username;
+        this.password = password;
+    }
+
+     /**
+      * Constructor without authzid
+      *
+      * @param username
+      *            The login user name.
+      * @param password
+      *            The login password.
+      */
+     public PlainAuthenticator(String username, String password) {
+         this(null, username, 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 challenge.
+     * 
+     * @param challenge
+     *            For PLAIN Authentication there is no challenge (so this is unused)
+     * 
+     * @return A formatted challenge response, as an array of bytes.
+     * @exception MessagingException
+     */
+    public byte[] evaluateChallenge(byte[] challenge) throws MessagingException {
+        try {
+
+            String result = "\0"+username+"\0"+password;
+
+            if(authzid != null && authzid.length() > 0) {
+                result = authzid+result;
+            }
+
+            complete = true;
+            return result.getBytes("UTF-8");
+
+        } catch (UnsupportedEncodingException e) {
+            // got an error, fail this
+            throw new MessagingException("Invalid encoding");
+        }
+    }
+}

Added: geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/authentication/SASLAuthenticator.java
URL: http://svn.apache.org/viewvc/geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/authentication/SASLAuthenticator.java?rev=1882306&view=auto
==============================================================================
--- geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/authentication/SASLAuthenticator.java (added)
+++ geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/authentication/SASLAuthenticator.java Wed Oct  7 15:58:39 2020
@@ -0,0 +1,174 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.geronimo.javamail.authentication;
+
+import java.io.UnsupportedEncodingException ;
+import java.util.Map;        
+import java.util.Properties; 
+
+import javax.mail.MessagingException;
+
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.sasl.Sasl; 
+import javax.security.sasl.SaslClient; 
+import javax.security.sasl.SaslException; 
+import javax.security.sasl.RealmCallback; 
+import javax.security.sasl.RealmChoiceCallback; 
+
+public class SASLAuthenticator implements ClientAuthenticator, CallbackHandler {
+    // The realm we're authenticating within 
+    protected String realm; 
+    // the user we're authenticating
+    protected String username;
+    // the user's password (the "shared secret")
+    protected String password;
+    // the authenticator we're proxying 
+    protected SaslClient authenticator; 
+
+    protected boolean complete = false;
+
+    /**
+     * Main constructor.
+     * 
+     * @param username
+     *            The login user name.
+     * @param password
+     *            The login password.
+     */
+    public SASLAuthenticator(String[] mechanisms, Properties properties, String protocol, String host, String realm, 
+            String authorizationID, String username, String password) throws MessagingException {
+        this.realm = realm; 
+        this.username = username;
+        this.password = password;
+        try {
+            authenticator = Sasl.createSaslClient(mechanisms, authorizationID, protocol, host, (Map)properties, 
+                this); 
+        } catch (SaslException e) {
+        } 
+    }
+    
+    
+    /**
+     * Respond to the hasInitialResponse query. We defer this to the Sasl client.  
+     * 
+     * @return The SaslClient response to the same query. 
+     */
+    public boolean hasInitialResponse() {
+        return authenticator.hasInitialResponse(); 
+    }
+
+    /**
+     * Indicate whether the challenge/response process is complete.
+     * 
+     * @return True if the last challenge has been processed, false otherwise.
+     */
+    public boolean isComplete() {
+        return authenticator.hasInitialResponse(); 
+    }
+
+    /**
+     * Retrieve the authenticator mechanism name.
+     * 
+     * @return Always returns the string "PLAIN"
+     */
+    public String getMechanismName() {
+        // the authenticator selects this for us. 
+        return authenticator.getMechanismName(); 
+    }
+
+    /**
+     * Evaluate a login challenge, returning the a result string that
+     * should satisfy the clallenge.  This is forwarded to the 
+     * SaslClient, which will use the CallBackHandler to retrieve the 
+     * information it needs for the given protocol. 
+     * 
+     * @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 {
+        // for an initial response challenge, there's no challenge date.  The SASL 
+        // client still expects a byte array argument. 
+        if (challenge == null) {
+            challenge = new byte[0];
+        }
+        
+        try {
+            return authenticator.evaluateChallenge(challenge);
+        } catch (SaslException e) {
+            // got an error, fail this
+            throw new MessagingException("Error performing SASL validation", e);
+        }
+    }
+    
+    public void handle(Callback[] callBacks) {
+        for (int i = 0; i < callBacks.length; i++) {
+            Callback callBack = callBacks[i]; 
+            // requesting the user name 
+            if (callBack instanceof NameCallback) {
+                ((NameCallback)callBack).setName(username); 
+            }
+            // need the password 
+            else if (callBack instanceof PasswordCallback) {
+                ((PasswordCallback)callBack).setPassword(password.toCharArray()); 
+            }
+            // direct request for the realm information 
+            else if (callBack instanceof RealmCallback) {
+                RealmCallback realmCallback = (RealmCallback)callBack; 
+                // we might not have a realm, so use the default from the 
+                // callback item 
+                if (realm == null) {
+                    realmCallback.setText(realmCallback.getDefaultText()); 
+                }
+                else { 
+                    realmCallback.setText(realm); 
+                }
+            }
+            // asked to select the realm information from a list 
+            else if (callBack instanceof RealmChoiceCallback) {
+                RealmChoiceCallback realmCallback = (RealmChoiceCallback)callBack; 
+                // if we don't have a realm, just tell it to use the default 
+                if (realm == null) {
+                    realmCallback.setSelectedIndex(realmCallback.getDefaultChoice()); 
+                }
+                else {
+                    // locate our configured one in the list 
+                    String[] choices = realmCallback.getChoices(); 
+
+                    for (int j = 0; j < choices.length; j++) {
+                        // set the index to any match and get out of here. 
+                        if (choices[j].equals(realm)) {
+                            realmCallback.setSelectedIndex(j); 
+                            break; 
+                        }
+                    }
+                    // NB:  If there was no match, we don't set anything.  
+                    // this should cause an authentication failure. 
+                }
+            }
+        }
+    }
+}
+

Added: geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/authentication/XOAUTH2Authenticator.java
URL: http://svn.apache.org/viewvc/geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/authentication/XOAUTH2Authenticator.java?rev=1882306&view=auto
==============================================================================
--- geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/authentication/XOAUTH2Authenticator.java (added)
+++ geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/authentication/XOAUTH2Authenticator.java Wed Oct  7 15:58:39 2020
@@ -0,0 +1,102 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.geronimo.javamail.authentication;
+
+import javax.mail.MessagingException;
+import java.io.UnsupportedEncodingException;
+import java.util.Properties;
+
+public class XOAUTH2Authenticator implements ClientAuthenticator {
+
+    //The user we're authenticating
+    protected String username;
+    //The user's password (the "shared secret")
+    protected String password;
+    protected boolean complete = false;
+
+    /**
+     * Main constructor.
+     *
+     * @param username The login user name.
+     * @param password The Oauth2 token.
+     */
+    public XOAUTH2Authenticator(String[] mechanisms, Properties properties, String protocol, String host, String realm,
+                                String authorizationID, String username, String password) throws MessagingException {
+        this.username = username;
+        this.password = password;
+    }
+
+    /**
+     * Respond to the hasInitialResponse query.
+     *
+     * @return The SaslClient response to the same query.
+     */
+    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 Will returns the string "XOAUTH2"
+     */
+    public String getMechanismName() {
+        return "XOAUTH2";
+    }
+
+    /**
+     * Evaluate a login challenge, returning the a result string that
+     * should satisfy the challenge.  This use the CallBackHandler to retrieve the
+     * information it needs for the given protocol.
+     *
+     * @param challenge The decoded challenge data, as byte array.
+     * @return A formatted challenge response, as an array of bytes.
+     * @throws MessagingException
+     */
+    public byte[] evaluateChallenge(final byte[] challenge) throws MessagingException{
+        if (complete) {
+            return new byte[0];
+        }
+
+        final String response = new StringBuilder()
+                .append("user=")
+                .append(this.username)
+                .append("\001auth=Bearer ")
+                .append(this.password)
+                .append("\001\001")
+                .toString();
+
+
+        complete = true;
+
+        try {
+            return response.getBytes("UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            throw new MessagingException("Invalid encoding");
+        }
+    }
+}

Added: geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/handlers/AbstractImageHandler.java
URL: http://svn.apache.org/viewvc/geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/handlers/AbstractImageHandler.java?rev=1882306&view=auto
==============================================================================
--- geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/handlers/AbstractImageHandler.java (added)
+++ geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/handlers/AbstractImageHandler.java Wed Oct  7 15:58:39 2020
@@ -0,0 +1,91 @@
+/**
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.geronimo.javamail.handlers;
+
+import java.awt.Graphics2D;
+import java.awt.Image;
+import java.awt.datatransfer.DataFlavor;  
+import java.awt.datatransfer.UnsupportedFlavorException;
+import java.awt.image.BufferedImage;
+import java.awt.image.RenderedImage;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Iterator;
+import javax.activation.ActivationDataFlavor; 
+import javax.activation.DataContentHandler;
+import javax.activation.DataSource;
+import javax.activation.UnsupportedDataTypeException;
+import javax.imageio.IIOImage;
+import javax.imageio.ImageIO;
+import javax.imageio.ImageReader;
+import javax.imageio.ImageWriter;
+import javax.imageio.stream.ImageInputStream;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class AbstractImageHandler implements DataContentHandler {
+    private final ActivationDataFlavor flavour;
+
+    public AbstractImageHandler(ActivationDataFlavor flavour) {
+        this.flavour = flavour;
+    }
+
+    public DataFlavor[] getTransferDataFlavors() {
+        return new DataFlavor[]{flavour};
+    }
+
+    public Object getTransferData(DataFlavor dataFlavor, DataSource dataSource) throws UnsupportedFlavorException, IOException {
+        return flavour.equals(dataFlavor) ? getContent(dataSource) : null;
+    }
+
+    public Object getContent(DataSource ds) throws IOException {
+        Iterator i = ImageIO.getImageReadersByMIMEType(flavour.getMimeType());
+        if (!i.hasNext()) {
+            throw new UnsupportedDataTypeException("Unknown image type " + flavour.getMimeType());
+        }
+        ImageReader reader = (ImageReader) i.next();
+        ImageInputStream iis = ImageIO.createImageInputStream(ds.getInputStream());
+        reader.setInput(iis);
+        return reader.read(0);
+    }
+
+    public void writeTo(Object obj, String mimeType, OutputStream os) throws IOException {
+        Iterator i = ImageIO.getImageWritersByMIMEType(flavour.getMimeType());
+        if (!i.hasNext()) {
+            throw new UnsupportedDataTypeException("Unknown image type " + flavour.getMimeType());
+        }
+        ImageWriter writer = (ImageWriter) i.next();
+        writer.setOutput(ImageIO.createImageOutputStream(os));
+
+        if (obj instanceof RenderedImage) {
+            writer.write((RenderedImage) obj);
+        } else if (obj instanceof BufferedImage) {
+            BufferedImage buffered = (BufferedImage) obj;
+            writer.write(new IIOImage(buffered.getRaster(), null, null));
+        } else if (obj instanceof Image) {
+            Image image = (Image) obj;
+            BufferedImage buffered = new BufferedImage(image.getWidth(null), image.getHeight(null), BufferedImage.TYPE_INT_ARGB);
+            Graphics2D graphics = buffered.createGraphics();
+            graphics.drawImage(image, 0, 0, null, null);
+            writer.write(new IIOImage(buffered.getRaster(), null, null));
+        } else {
+            throw new UnsupportedDataTypeException("Unknown image type " + obj.getClass().getName());
+        }
+        os.flush();
+    }
+}

Added: geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/handlers/AbstractTextHandler.java
URL: http://svn.apache.org/viewvc/geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/handlers/AbstractTextHandler.java?rev=1882306&view=auto
==============================================================================
--- geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/handlers/AbstractTextHandler.java (added)
+++ geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/handlers/AbstractTextHandler.java Wed Oct  7 15:58:39 2020
@@ -0,0 +1,134 @@
+/**
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.geronimo.javamail.handlers;
+
+import java.awt.datatransfer.DataFlavor;
+import java.awt.datatransfer.UnsupportedFlavorException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Reader;
+import java.io.UnsupportedEncodingException;
+
+import javax.activation.DataContentHandler;
+import javax.activation.DataSource;
+
+import javax.mail.internet.ContentType; 
+import javax.mail.internet.MimeUtility;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class AbstractTextHandler implements DataContentHandler {
+    private final DataFlavor flavour;
+
+    public AbstractTextHandler(DataFlavor flavour) {
+        this.flavour = flavour;
+    }
+
+    public DataFlavor[] getTransferDataFlavors() {
+        return new DataFlavor[] {flavour};
+    }
+
+    public Object getTransferData(DataFlavor dataFlavor, DataSource dataSource) throws UnsupportedFlavorException, IOException {
+        return flavour.equals(dataFlavor) ? getContent(dataSource) : null;
+    }
+
+    /**
+     * Read the content from the DataSource and transform 
+     * it into a text object (String). 
+     * 
+     * @param ds     The source DataSource.
+     * 
+     * @return The content object. 
+     * @exception IOException
+     */
+    public Object getContent(DataSource ds) throws IOException {
+        InputStream is = ds.getInputStream(); 
+        InputStreamReader reader;
+        // process any encoding to make sure the chars get transformed into the 
+        // correct byte types. 
+        try {
+            String charset = getCharSet(ds.getContentType());
+            reader = new InputStreamReader(is, charset);
+        } catch (Exception ex) {
+            throw new UnsupportedEncodingException(ex.toString());
+        }
+        StringBuffer result = new StringBuffer(1024);
+        char[] buffer = new char[32768];
+        int count;
+        while ((count = reader.read(buffer)) > 0) {
+            result.append(buffer, 0, count);
+        }
+        return result.toString();
+    }
+
+    
+    /**
+     * Write an object of "our" type out to the provided 
+     * output stream.  The content type might modify the 
+     * result based on the content type parameters. 
+     * 
+     * @param object The object to write.
+     * @param contentType
+     *               The content mime type, including parameters.
+     * @param outputstream
+     *               The target output stream.
+     * 
+     * @throws IOException
+     */
+    public void writeTo(Object o, String contentType, OutputStream outputstream) throws IOException {
+        String s;
+        if (o instanceof String) {
+            s = (String) o;
+        } else if (o != null) {
+            s = o.toString();
+        } else {
+            return;
+        }
+        // process any encoding to make sure the chars get transformed into the 
+        // correct byte types. 
+        OutputStreamWriter writer;
+        try {
+            String charset = getCharSet(contentType);
+            writer = new OutputStreamWriter(outputstream, charset);
+        } catch (Exception ex) {
+            ex.printStackTrace(); 
+            throw new UnsupportedEncodingException(ex.toString());
+        }
+        writer.write(s);
+        writer.flush();
+    }
+    
+
+    /**
+     * get the character set from content type
+     * @param contentType
+     * @return
+     * @throws ParseException
+     */
+    protected String getCharSet(String contentType) throws Exception {
+        ContentType type = new ContentType(contentType);
+        String charset = type.getParameter("charset");
+        if (charset == null) {
+            charset = "us-ascii";
+        }
+        return MimeUtility.javaCharset(charset);
+    }
+}

Added: geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/handlers/ImageGifHandler.java
URL: http://svn.apache.org/viewvc/geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/handlers/ImageGifHandler.java?rev=1882306&view=auto
==============================================================================
--- geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/handlers/ImageGifHandler.java (added)
+++ geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/handlers/ImageGifHandler.java Wed Oct  7 15:58:39 2020
@@ -0,0 +1,29 @@
+/**
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.geronimo.javamail.handlers;
+
+import java.awt.Image;
+import javax.activation.ActivationDataFlavor;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class ImageGifHandler extends AbstractImageHandler {
+    public ImageGifHandler() {
+        super(new ActivationDataFlavor(Image.class, "image/gif", "GIF Image"));
+    }
+}

Added: geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/handlers/ImageJpegHandler.java
URL: http://svn.apache.org/viewvc/geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/handlers/ImageJpegHandler.java?rev=1882306&view=auto
==============================================================================
--- geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/handlers/ImageJpegHandler.java (added)
+++ geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/handlers/ImageJpegHandler.java Wed Oct  7 15:58:39 2020
@@ -0,0 +1,29 @@
+/**
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.geronimo.javamail.handlers;
+
+import java.awt.Image;
+import javax.activation.ActivationDataFlavor;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class ImageJpegHandler extends AbstractImageHandler {
+    public ImageJpegHandler() {
+        super(new ActivationDataFlavor(Image.class, "image/jpeg", "JPEG Image"));
+    }
+}

Added: geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/handlers/MultipartHandler.java
URL: http://svn.apache.org/viewvc/geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/handlers/MultipartHandler.java?rev=1882306&view=auto
==============================================================================
--- geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/handlers/MultipartHandler.java (added)
+++ geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/handlers/MultipartHandler.java Wed Oct  7 15:58:39 2020
@@ -0,0 +1,65 @@
+/**
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.geronimo.javamail.handlers;
+
+import java.awt.datatransfer.DataFlavor;
+import java.awt.datatransfer.UnsupportedFlavorException;
+import java.io.IOException;
+import java.io.OutputStream;
+import javax.activation.ActivationDataFlavor;
+import javax.activation.DataContentHandler;
+import javax.activation.DataSource;
+import javax.mail.MessagingException;
+import javax.mail.internet.MimeMultipart;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class MultipartHandler implements DataContentHandler {
+    private final DataFlavor flavour;
+
+    public MultipartHandler() {
+        flavour = new ActivationDataFlavor(MimeMultipart.class, "multipart/mixed", "Multipart MIME");
+    }
+
+    public DataFlavor[] getTransferDataFlavors() {
+        return new DataFlavor[]{flavour};
+    }
+
+    public Object getTransferData(DataFlavor df, DataSource ds) throws UnsupportedFlavorException, IOException {
+        return flavour.equals(df) ? getContent(ds) : null;
+    }
+
+    public Object getContent(DataSource ds) throws IOException {
+        try {
+            return new MimeMultipart(ds);
+        } catch (MessagingException e) {
+            throw (IOException) new IOException(e.getMessage()).initCause(e);
+        }
+    }
+
+    public void writeTo(Object obj, String mimeType, OutputStream os) throws IOException {
+        if (obj instanceof MimeMultipart) {
+            MimeMultipart mp = (MimeMultipart) obj;
+            try {
+                mp.writeTo(os);
+            } catch (MessagingException e) {
+                throw (IOException) new IOException(e.getMessage()).initCause(e);
+            }
+        }
+    }
+}

Added: geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/handlers/RFC822MessageHandler.java
URL: http://svn.apache.org/viewvc/geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/handlers/RFC822MessageHandler.java?rev=1882306&view=auto
==============================================================================
--- geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/handlers/RFC822MessageHandler.java (added)
+++ geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/handlers/RFC822MessageHandler.java Wed Oct  7 15:58:39 2020
@@ -0,0 +1,120 @@
+/**
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.geronimo.javamail.handlers;
+
+import java.awt.datatransfer.DataFlavor;
+import java.awt.datatransfer.UnsupportedFlavorException;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Properties;
+
+import javax.activation.ActivationDataFlavor;
+import javax.activation.DataContentHandler;
+import javax.activation.DataSource;
+import javax.mail.Message;
+import javax.mail.MessageAware;
+import javax.mail.MessageContext;
+import javax.mail.MessagingException;
+import javax.mail.Session;
+import javax.mail.internet.MimeMessage;
+
+/**
+ * Content handler for RFC-822 compliant messages.
+ * @version $Rev$ $Date$
+ */
+public class RFC822MessageHandler implements DataContentHandler {
+    // the data flavor defines what this looks like, and is fixed once the
+    // handler is instantiated
+    protected final DataFlavor flavour;
+
+    public RFC822MessageHandler() {
+        flavour = new ActivationDataFlavor(Message.class, "message/rfc822", "Message");
+    }
+
+    /**
+     * Return all of the flavors processed by this handler.  This
+     * is just the singleton flavor.
+     *
+     * @return An array of the transfer flavors supported by this handler.
+     */
+    public DataFlavor[] getTransferDataFlavors() {
+        return new DataFlavor[] { flavour };
+    }
+
+    /**
+     * Retrieve the transfer data from the data source, but
+     * only if the requested flavor matches what we support.
+     *
+     * @param df     The requested data flavor.
+     * @param ds     The source DataSource.
+     *
+     * @return The extracted content object, or null if there is a
+     *         mismatch of flavors.
+     * @exception UnsupportedFlavorException
+     * @exception IOException
+     */
+    public Object getTransferData(DataFlavor df, DataSource ds) throws UnsupportedFlavorException, IOException {
+        return flavour.equals(df) ? getContent(ds) : null;
+    }
+
+    /**
+     * Extract the RFC822 Message content from a DataSource.
+     *
+     * @param ds     The source data source.
+     *
+     * @return An extracted MimeMessage object.
+     * @exception IOException
+     */
+    public Object getContent(DataSource ds) throws IOException {
+        try {
+            // creating a MimeMessage instance requires a session.  If the DataSource
+            // is a MessageAware one, we can get the session information from the MessageContext.
+            // This is generally the case, but if it is not available, then just retrieve
+            // the default instance and use it.
+            if (ds instanceof MessageAware) {
+                MessageContext context = ((MessageAware)ds).getMessageContext();
+                return new MimeMessage(context.getSession(), ds.getInputStream());
+            }
+            else {
+                return new MimeMessage(Session.getDefaultInstance(new Properties(), null), ds.getInputStream());
+            }
+        } catch (MessagingException e) {
+            throw (IOException) new IOException(e.getMessage()).initCause(e);
+        }
+    }
+
+    /**
+     * Write an RFC 822 message object out to an output stream.
+     *
+     * @param obj      The source message object.
+     * @param mimeType The target mimetype
+     * @param os       The target output stream.
+     *
+     * @exception IOException
+     */
+    public void writeTo(Object obj, String mimeType, OutputStream os) throws IOException {
+        // we only handle message instances here
+        if (obj instanceof Message) {
+            Message message = (Message) obj;
+            try {
+                message.writeTo(os);
+            } catch (MessagingException e) {
+                throw (IOException) new IOException(e.getMessage()).initCause(e);
+            }
+        }
+    }
+}

Added: geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/handlers/TextHtmlHandler.java
URL: http://svn.apache.org/viewvc/geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/handlers/TextHtmlHandler.java?rev=1882306&view=auto
==============================================================================
--- geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/handlers/TextHtmlHandler.java (added)
+++ geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/handlers/TextHtmlHandler.java Wed Oct  7 15:58:39 2020
@@ -0,0 +1,29 @@
+/**
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.geronimo.javamail.handlers;
+
+import javax.activation.ActivationDataFlavor;
+
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class TextHtmlHandler extends AbstractTextHandler {
+    public TextHtmlHandler() {
+        super(new ActivationDataFlavor(String.class, "text/html", "HTML Text"));
+    }
+}

Added: geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/handlers/TextPlainHandler.java
URL: http://svn.apache.org/viewvc/geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/handlers/TextPlainHandler.java?rev=1882306&view=auto
==============================================================================
--- geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/handlers/TextPlainHandler.java (added)
+++ geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/handlers/TextPlainHandler.java Wed Oct  7 15:58:39 2020
@@ -0,0 +1,29 @@
+/**
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.geronimo.javamail.handlers;
+
+import javax.activation.ActivationDataFlavor;
+
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class TextPlainHandler extends AbstractTextHandler {
+    public TextPlainHandler() {
+        super(new ActivationDataFlavor(String.class, "text/plain", "Plain Text"));
+    }
+}

Added: geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/handlers/TextXmlHandler.java
URL: http://svn.apache.org/viewvc/geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/handlers/TextXmlHandler.java?rev=1882306&view=auto
==============================================================================
--- geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/handlers/TextXmlHandler.java (added)
+++ geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/handlers/TextXmlHandler.java Wed Oct  7 15:58:39 2020
@@ -0,0 +1,29 @@
+/**
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.geronimo.javamail.handlers;
+
+import javax.activation.ActivationDataFlavor;
+
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class TextXmlHandler extends AbstractTextHandler {
+    public TextXmlHandler() {
+        super(new ActivationDataFlavor(String.class, "text/xml", "XML Text"));
+    }
+}

Added: geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/store/imap/ACL.java
URL: http://svn.apache.org/viewvc/geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/store/imap/ACL.java?rev=1882306&view=auto
==============================================================================
--- geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/store/imap/ACL.java (added)
+++ geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/store/imap/ACL.java Wed Oct  7 15:58:39 2020
@@ -0,0 +1,93 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.geronimo.javamail.store.imap;
+
+/**
+ * A named access control list for IMAP resources.  
+ */
+public class ACL implements Cloneable {
+    /**
+     * The name of the resource this ACL applies to.
+     */
+    private String name; 
+    /**
+     * The rights associated with this resource.
+     */
+    private Rights rights;
+    
+    /**
+     * Create an ACL for a resource.  The ACL will have an empty Rights set.
+     * 
+     * @param name   The name of the resource.
+     */
+    public ACL(String name) {
+        this.name = name; 
+        this.rights = new Rights(); 
+    }
+    
+    /**
+     * Create a named ACL instance with an initial Rights set.
+     * 
+     * @param name   The name of the resouce this ACL applies to.
+     * @param rights The Rights associated with this resource.
+     */
+    public ACL(String name, Rights rights) {
+        this.name = name; 
+        this.rights = rights;  
+    }
+    
+    /**
+     * Get the ACL name.
+     * 
+     * @return The string name of the ACL.
+     */
+    public String getName() {
+        return name; 
+    }
+    
+    /**
+     * Get the Rights associated with this ACL.
+     * 
+     * @return The Rights set supported for this resource.
+     */
+    public Rights getRights() {
+        return rights; 
+    }
+    
+    /**
+     * Set a new set of Rights for this ACL instance.
+     * 
+     * @param rights The new Rights set.
+     */
+    public void setRights(Rights rights) {
+        this.rights = rights;         
+    }
+    
+    
+    /**
+     * Creates and returns a copy of this object. 
+     * 
+     * @return A cloned copy of this object.  This is a deep 
+     *         copy, given that a new Rights set is also created.
+     * @exception CloneNotSupportedException
+     */
+    protected Object clone() throws CloneNotSupportedException {
+        return new ACL(name, new Rights(rights)); 
+    }
+}

Added: geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/store/imap/IMAPAttachedMessage.java
URL: http://svn.apache.org/viewvc/geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/store/imap/IMAPAttachedMessage.java?rev=1882306&view=auto
==============================================================================
--- geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/store/imap/IMAPAttachedMessage.java (added)
+++ geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/store/imap/IMAPAttachedMessage.java Wed Oct  7 15:58:39 2020
@@ -0,0 +1,125 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.geronimo.javamail.store.imap;
+
+import javax.activation.DataHandler;
+
+import javax.mail.Flags;
+import javax.mail.MessagingException;
+import javax.mail.MethodNotSupportedException;
+
+import org.apache.geronimo.javamail.store.imap.connection.IMAPEnvelope;
+import org.apache.geronimo.javamail.store.imap.connection.IMAPBodyStructure;
+
+/**
+ * A nested message attachement inside of another 
+ * IMAP message.  This is a less-functional version 
+ * of the top-level message.
+ */
+public class IMAPAttachedMessage extends IMAPMessage {
+    // the parent enclosing message.
+    protected IMAPMessage parent;
+
+    /**
+     * Constructor for an attached message part.
+     * 
+     * @param parent   The parent message (outer-most message).
+     * @param section  The section identifier for this embedded part
+     *                 in IMAP section format.  This will identify
+     *                 the part hierarchy used to locate this part within
+     *                 the message.
+     * @param envelope The Envelope that describes this part.
+     * @param bodyStructure
+     *                 The Body structure element that describes this part.
+     */
+    public IMAPAttachedMessage(IMAPMessage parent, String section, IMAPEnvelope envelope, IMAPBodyStructure bodyStructure) {
+        super((IMAPFolder)parent.getFolder(), parent.store, parent.getMessageNumber(), parent.sequenceNumber);
+        this.parent = parent;
+        // sets the subset we're looking for 
+        this.section = section;
+        // the envelope and body structure are loaded from the server by the parent 
+        this.envelope = envelope;
+        this.bodyStructure = bodyStructure;
+    }
+
+    /**
+     * Check if this message is still valid.  This is 
+     * delegated to the outer-most message.
+     * 
+     * @exception MessagingException
+     */
+    protected void checkValidity() throws MessagingException {
+        parent.checkValidity();
+    }
+
+    /**
+     * Check if the outer-most message has been expunged.
+     * 
+     * @return true if the message has been expunged.
+     */
+    public boolean isExpunged() {
+        return parent.isExpunged();
+    }
+
+    /**
+     * Get the size of this message part.
+     * 
+     * @return The estimate size of this message part, in bytes.
+     */
+    public int getSize() {
+        return bodyStructure.bodySize;
+    }
+
+    
+    /**
+     * Return a copy the flags associated with this message.
+     *
+     * @return a copy of the flags for this message
+     * @throws MessagingException if there was a problem accessing the Store
+     */
+    public Flags getFlags() throws MessagingException {
+        return parent.getFlags(); 
+    }
+
+
+    /**
+     * Check whether the supplied flag is set.
+     * The default implementation checks the flags returned by {@link #getFlags()}.
+     *
+     * @param flag the flags to check for
+     * @return true if the flags is set
+     * @throws MessagingException if there was a problem accessing the Store
+     */
+    public boolean isSet(Flags.Flag flag) throws MessagingException {
+        // load the flags, if needed 
+        return parent.isSet(flag); 
+    }
+
+    /**
+     * Set or clear a flag value.
+     *
+     * @param flags  The set of flags to effect.
+     * @param set    The value to set the flag to (true or false).
+     *
+     * @exception MessagingException
+     */
+    public void setFlags(Flags flag, boolean set) throws MessagingException {
+        throw new MethodNotSupportedException("Flags cannot be set on message attachements"); 
+    }
+}
+