You are viewing a plain text version of this content. The canonical link for it is here.
Posted to scm@geronimo.apache.org by ri...@apache.org on 2006/07/14 12:02:29 UTC

svn commit: r421852 [3/15] - in /geronimo/specs/trunk: ./ geronimo-spec-j2ee/ geronimo-spec-javamail-1.3.1/ geronimo-spec-javamail-1.3.1/src/ geronimo-spec-javamail-1.4/ geronimo-spec-javamail-1.4/src/ geronimo-spec-javamail-1.4/src/main/ geronimo-spec...

Added: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/Provider.java
URL: http://svn.apache.org/viewvc/geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/Provider.java?rev=421852&view=auto
==============================================================================
--- geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/Provider.java (added)
+++ geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/Provider.java Fri Jul 14 03:02:19 2006
@@ -0,0 +1,86 @@
+/**
+ *
+ * Copyright 2003-2006 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 javax.mail;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class Provider {
+    /**
+     * A enumeration inner class that defines Provider types.
+     */
+    public static class Type {
+        /**
+         * A message store provider such as POP3 or IMAP4.
+         */
+        public static final Type STORE = new Type();
+
+        /**
+         * A message transport provider such as SMTP.
+         */
+        public static final Type TRANSPORT = new Type();
+
+        private Type() {
+        }
+    }
+
+    private final String className;
+    private final String protocol;
+    private final Type type;
+    private final String vendor;
+    private final String version;
+
+    public Provider(Type type, String protocol, String className, String vendor, String version) {
+        this.protocol = protocol;
+        this.className = className;
+        this.type = type;
+        this.vendor = vendor;
+        this.version = version;
+    }
+
+    public String getClassName() {
+        return className;
+    }
+
+    public String getProtocol() {
+        return protocol;
+    }
+
+    public Type getType() {
+        return type;
+    }
+
+    public String getVendor() {
+        return vendor;
+    }
+
+    public String getVersion() {
+        return version;
+    }
+
+    public String toString() {
+        return "protocol="
+                + protocol
+                + "; type="
+                + type
+                + "; class="
+                + className
+                + (vendor == null ? "" : "; vendor=" + vendor)
+                + (version == null ? "" : ";version=" + version);
+    }
+}

Propchange: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/Provider.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/Provider.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/Provider.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/Quota.java
URL: http://svn.apache.org/viewvc/geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/Quota.java?rev=421852&view=auto
==============================================================================
--- geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/Quota.java (added)
+++ geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/Quota.java Fri Jul 14 03:02:19 2006
@@ -0,0 +1,128 @@
+/**
+ *
+ * Copyright 2006 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 javax.mail;
+
+
+/**
+ * A representation of a Quota item for a given quota root.
+ *
+ * @version $Rev$ $Date$
+ */
+
+public class Quota {
+    /**
+     * The name of the quota root.
+     */
+    public String quotaRoot;
+
+    /**
+     * The resources associated with this quota root.
+     */
+    public Resource[] resources;
+
+
+    /**
+     * Create a Quota with the given name and not resources.
+     *
+     * @param quotaRoot The quota root name.
+     */
+    public Quota(String quotaRoot) {
+        this.quotaRoot = quotaRoot;
+    }
+
+    /**
+     * Set a limit value for a resource.  If the resource is not
+     * current associated with this Quota, a new Resource item is
+     * added to the resources list.
+     *
+     * @param name   The target resource name.
+     * @param limit  The new limit value for the resource.
+     */
+    public void setResourceLimit(String name, long limit) {
+        Resource target = findResource(name);
+        target.limit = limit;
+    }
+
+    /**
+     * Locate a particular named resource, adding one to the list
+     * if it does not exist.
+     *
+     * @param name   The target resource name.
+     *
+     * @return A Resource item for this named resource (either existing or new).
+     */
+    private Resource findResource(String name) {
+        // no resources yet?  Make it so.
+        if (resources == null) {
+            Resource target = new Resource(name, 0, 0);
+            resources = new Resource[] { target };
+            return target;
+        }
+
+        // see if this one exists and return it.
+        for (int i = 0; i < resources.length; i++) {
+            Resource current = resources[i];
+            if (current.name.equalsIgnoreCase(name)) {
+                return current;
+            }
+        }
+
+        // have to extend the array...this is a pain.
+        Resource[] newResources = new Resource[resources.length + 1];
+        System.arraycopy(resources, 0, newResources, 0, resources.length);
+        Resource target = new Resource(name, 0, 0);
+        newResources[resources.length] = target;
+        resources = newResources;
+        return target;
+    }
+
+
+
+    /**
+     * A representation of a given resource definition.
+     */
+    public static class Resource {
+        /**
+         * The resource name.
+         */
+        public String name;
+        /**
+         * The current resource usage.
+         */
+        public long usage;
+        /**
+         * The limit value for this resource.
+         */
+        public long limit;
+
+
+        /**
+         * Construct a Resource object from the given name and usage/limit
+         * information.
+         *
+         * @param name   The Resource name.
+         * @param usage  The current resource usage.
+         * @param limit  The Resource limit value.
+         */
+        public Resource(String name, long usage, long limit) {
+            this.name = name;
+            this.usage = usage;
+            this.limit = limit;
+        }
+    }
+}

Propchange: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/Quota.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/Quota.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/Quota.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/QuotaAwareStore.java
URL: http://svn.apache.org/viewvc/geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/QuotaAwareStore.java?rev=421852&view=auto
==============================================================================
--- geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/QuotaAwareStore.java (added)
+++ geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/QuotaAwareStore.java Fri Jul 14 03:02:19 2006
@@ -0,0 +1,46 @@
+/**
+ *
+ * Copyright 2006 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 javax.mail;
+
+
+/**
+ * An interface for Store implementations to support the IMAP RFC 2087 Quota extension.
+ *
+ * @version $Rev$ $Date$
+ */
+
+public interface QuotaAwareStore {
+    /**
+     * Get the quotas for the specified root element.
+     *
+     * @param root   The root name for the quota information.
+     *
+     * @return An array of Quota objects defined for the root.
+     */
+    public Quota[] getQuota(String root);
+
+    /**
+     * Set a quota item.  The root contained in the Quota item identies
+     * the quota target.
+     *
+     * @param quota  The source quota item.
+     */
+    public void setQuota(Quota quota);
+}
+
+

Propchange: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/QuotaAwareStore.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/QuotaAwareStore.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/QuotaAwareStore.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/ReadOnlyFolderException.java
URL: http://svn.apache.org/viewvc/geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/ReadOnlyFolderException.java?rev=421852&view=auto
==============================================================================
--- geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/ReadOnlyFolderException.java (added)
+++ geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/ReadOnlyFolderException.java Fri Jul 14 03:02:19 2006
@@ -0,0 +1,38 @@
+/**
+ *
+ * Copyright 2003-2006 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 javax.mail;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class ReadOnlyFolderException extends MessagingException {
+    private transient Folder _folder;
+
+    public ReadOnlyFolderException(Folder folder) {
+        this(folder, "Folder not found: " + folder.getName());
+    }
+
+    public ReadOnlyFolderException(Folder folder, String message) {
+        super(message);
+        _folder = folder;
+    }
+
+    public Folder getFolder() {
+        return _folder;
+    }
+}

Propchange: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/ReadOnlyFolderException.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/ReadOnlyFolderException.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/ReadOnlyFolderException.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/SendFailedException.java
URL: http://svn.apache.org/viewvc/geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/SendFailedException.java?rev=421852&view=auto
==============================================================================
--- geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/SendFailedException.java (added)
+++ geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/SendFailedException.java Fri Jul 14 03:02:19 2006
@@ -0,0 +1,62 @@
+/**
+ *
+ * Copyright 2003-2006 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 javax.mail;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class SendFailedException extends MessagingException {
+    protected transient Address invalid[];
+    protected transient Address validSent[];
+    protected transient Address validUnsent[];
+
+    public SendFailedException() {
+        super();
+    }
+
+    public SendFailedException(String message) {
+        super(message);
+    }
+
+    public SendFailedException(String message, Exception cause) {
+        super(message, cause);
+    }
+
+    public SendFailedException(String message,
+                               Exception cause,
+                               Address[] validSent,
+                               Address[] validUnsent,
+                               Address[] invalid) {
+        this(message, cause);
+        this.invalid = invalid;
+        this.validSent = validSent;
+        this.validUnsent = validUnsent;
+    }
+
+    public Address[] getValidSentAddresses() {
+        return validSent;
+    }
+
+    public Address[] getValidUnsentAddresses() {
+        return validUnsent;
+    }
+
+    public Address[] getInvalidAddresses() {
+        return invalid;
+    }
+}

Propchange: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/SendFailedException.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/SendFailedException.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/SendFailedException.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/Service.java
URL: http://svn.apache.org/viewvc/geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/Service.java?rev=421852&view=auto
==============================================================================
--- geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/Service.java (added)
+++ geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/Service.java Fri Jul 14 03:02:19 2006
@@ -0,0 +1,387 @@
+/**
+ *
+ * Copyright 2003-2006 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 javax.mail;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.Vector;
+
+import javax.mail.event.ConnectionEvent;
+import javax.mail.event.ConnectionListener;
+import javax.mail.event.MailEvent;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public abstract class Service {
+    /**
+     * The session from which this service was created.
+     */
+    protected Session session;
+    /**
+     * The URLName of this service
+     */
+    protected URLName url;
+    /**
+     * Debug flag for this service, set from the Session's debug flag.
+     */
+    protected boolean debug;
+
+    private boolean connected;
+    private final Vector connectionListeners = new Vector(2);
+    private final EventQueue queue = new EventQueue();
+
+    /**
+     * Construct a new Service.
+     * @param session the session from which this service was created
+     * @param url the URLName of this service
+     */
+    protected Service(Session session, URLName url) {
+        this.session = session;
+        this.url = url;
+        this.debug = session.getDebug();
+    }
+
+    /**
+     * A generic connect method that takes no parameters allowing subclasses
+     * to implement an appropriate authentication scheme.
+     * The default implementation calls <code>connect(null, null, null)</code>
+     * @throws AuthenticationFailedException if authentication fails
+     * @throws MessagingException for other failures
+     */
+    public void connect() throws MessagingException {
+        connect(null, null, null);
+    }
+
+    /**
+     * Connect to the specified host using a simple username/password authenticaion scheme
+     * and the default port.
+     * The default implementation calls <code>connect(host, -1, user, password)</code>
+     *
+     * @param host the host to connect to
+     * @param user the user name
+     * @param password the user's password
+     * @throws AuthenticationFailedException if authentication fails
+     * @throws MessagingException for other failures
+     */
+    public void connect(String host, String user, String password) throws MessagingException {
+        connect(host, -1, user, password);
+    }
+
+    /**
+     * Connect to the specified host using a simple username/password authenticaion scheme
+     * and the default host and port.
+     * The default implementation calls <code>connect(host, -1, user, password)</code>
+     *
+     * @param user the user name
+     * @param password the user's password
+     * @throws AuthenticationFailedException if authentication fails
+     * @throws MessagingException for other failures
+     */
+    public void connect(String user, String password) throws MessagingException {
+        connect(null, -1, user, password);
+    }
+
+    /**
+     * Connect to the specified host at the specified port using a simple username/password authenticaion scheme.
+     *
+     * If this Service is already connected, an IllegalStateException is thrown.
+     *
+     * @param host the host to connect to
+     * @param port the port to connect to; pass -1 to use the default for the protocol
+     * @param user the user name
+     * @param password the user's password
+     * @throws AuthenticationFailedException if authentication fails
+     * @throws MessagingException for other failures
+     * @throws IllegalStateException if this service is already connected
+     */
+    public void connect(String host, int port, String user, String password) throws MessagingException {
+
+        if (isConnected()) {
+            throw new IllegalStateException("Already connected");
+        }
+
+        // before we try to connect, we need to derive values for some parameters that may not have
+        // been explicitly specified.  For example, the normal connect() method leaves us to derive all
+        // of these from other sources.  Some of the values are derived from our URLName value, others
+        // from session parameters.  We need to go through all of these to develop a set of values we
+        // can connect with.
+
+        // this is the protocol we're connecting with.  We use this largely to derive configured values from
+        // session properties.
+        String protocol = null;
+
+        // if we're working with the URL form, then we can retrieve the protocol from the URL.
+        if (url != null) {
+            protocol = url.getProtocol();
+        }
+
+        // now try to derive values for any of the arguments we've been given as defaults
+        if (host == null) {
+            // first choice is from the url, if we have
+            if (url != null) {
+                host = url.getHost();
+                // it is possible that this could return null (rare).  If it does, try to get a
+                // value from a protocol specific session variable.
+                if (host == null) {
+                    host = session.getProperty("mail." + protocol + ".host");
+                }
+            }
+            // this may still be null...get the global mail property
+            if (host == null) {
+                host = session.getProperty("mail.host");
+            }
+        }
+
+        // ok, go after userid information next.
+        if (user == null) {
+            // first choice is from the url, if we have
+            if (url != null) {
+                user = url.getUsername();
+                // make sure we get the password from the url, if we can.
+                if (password == null) {
+                    password = url.getPassword();
+                }
+                // user still null?  We have several levels of properties to try yet
+                if (user == null) {
+                    user = session.getProperty("mail." + protocol + ".user");
+                }
+            }
+
+            // this may still be null...get the global mail property
+            if (user == null) {
+                user = session.getProperty("mail.user");
+            }
+
+            // finally, we try getting the system defined user name
+            try {
+                user = System.getProperty("user.name");
+            } catch (SecurityException e) {
+                // we ignore this, and just us a null username.
+            }
+        }
+        // if we have an explicitly given user name, we need to see if this matches the url one and
+        // grab the password from there.
+        else {
+            if (url != null && user.equals(url.getUsername())) {
+                password = url.getPassword();
+            }
+        }
+
+        // we need to update the URLName associated with this connection once we have all of the information,
+        // which means we also need to propogate the file portion of the URLName if we have this form when
+        // we start.
+        String file = null;
+        if (url != null) {
+            file = url.getFile();
+        }
+
+        // see if we have cached security information to use.  If this is not cached, we'll save it
+        // after we successfully connect.
+        boolean cachePassword = false;
+
+
+        // still have a null password to this point, and using a url form?
+        if (password == null && url != null) {
+            // construct a new URL, filling in any pieces that may have been explicitly specified.
+            setURLName(new URLName(protocol, host, port, file, user, password));
+            // now see if we have a saved password from a previous request.
+            PasswordAuthentication cachedPassword = session.getPasswordAuthentication(getURLName());
+
+            // if we found a saved one, see if we need to get any the pieces from here.
+            if (cachedPassword != null) {
+                // not even a resolved userid?  Then use both bits.
+                if (user == null) {
+                    user = cachedPassword.getUserName();
+                    password = cachedPassword.getPassword();
+                }
+                // our user name must match the cached name to be valid.
+                else if (user.equals(cachedPassword.getUserName())) {
+                    password = cachedPassword.getPassword();
+                }
+            }
+            else
+            {
+                // nothing found in the cache, so we need to save this if we can connect successfully.
+                cachePassword = true;
+            }
+        }
+
+        // we've done our best up to this point to obtain all of the information needed to make the
+        // connection.  Now we pass this off to the protocol handler to see if it works.  If we get a
+        // connection failure, we may need to prompt for a password before continuing.
+        try {
+            connected = protocolConnect(host, port, user, password);
+        }
+        catch (AuthenticationFailedException e) {
+        }
+
+        if (!connected) {
+            InetAddress ipAddress = null;
+
+            try {
+                ipAddress = InetAddress.getByName(host);
+            } catch (UnknownHostException e) {
+            }
+
+            // now ask the session to try prompting for a password.
+            PasswordAuthentication promptPassword = session.requestPasswordAuthentication(ipAddress, port, protocol, null, user);
+
+            // if we were able to obtain new information from the session, then try again using the
+            // provided information .
+            if (promptPassword != null) {
+                user = promptPassword.getUserName();
+                password = promptPassword.getPassword();
+            }
+
+            connected = protocolConnect(host, port, user, password);
+        }
+
+
+        // if we're still not connected, then this is an exception.
+        if (!connected) {
+            throw new AuthenticationFailedException();
+        }
+
+        // the URL name needs to reflect the most recent information.
+        setURLName(new URLName(protocol, host, port, file, user, password));
+
+        // we need to update the global password cache with this information.
+        if (cachePassword) {
+            session.setPasswordAuthentication(getURLName(), new PasswordAuthentication(user, password));
+        }
+
+        // we're now connected....broadcast this to any interested parties.
+        setConnected(connected);
+        notifyConnectionListeners(ConnectionEvent.OPENED);
+    }
+
+    /**
+     * Attempt the protocol-specific connection; subclasses should override this to establish
+     * a connection in the appropriate manner.
+     *
+     * This method should return true if the connection was established.
+     * It may return false to cause the {@link #connect(String, int, String, String)} method to
+     * reattempt the connection after trying to obtain user and password information from the user.
+     * Alternatively it may throw a AuthenticatedFailedException to abandon the conection attempt.
+     *
+     * @param host
+     * @param port
+     * @param user
+     * @param password
+     * @return
+     * @throws AuthenticationFailedException if authentication fails
+     * @throws MessagingException for other failures
+     */
+    protected boolean protocolConnect(String host, int port, String user, String password) throws MessagingException {
+        return false;
+    }
+
+    /**
+     * Check if this service is currently connected.
+     * The default implementation simply returns the value of a private boolean field;
+     * subclasses may wish to override this method to verify the physical connection.
+     *
+     * @return true if this service is connected
+     */
+    public boolean isConnected() {
+        return connected;
+    }
+
+    /**
+     * Notification to subclasses that the connection state has changed.
+     * This method is called by the connect() and close() methods to indicate state change;
+     * subclasses should also call this method if the connection is automatically closed
+     * for some reason.
+     *
+     * @param connected the connection state
+     */
+    protected void setConnected(boolean connected) {
+        this.connected = connected;
+    }
+
+    /**
+     * Close this service and terminate its physical connection.
+     * The default implementation simply calls setConnected(false) and then
+     * sends a CLOSED event to all registered ConnectionListeners.
+     * Subclasses overriding this method should still ensure it is closed; they should
+     * also ensure that it is called if the connection is closed automatically, for
+     * for example in a finalizer.
+     *
+     *@throws MessagingException if there were errors closing; the connection is still closed
+     */
+    public void close() throws MessagingException {
+        setConnected(false);
+        notifyConnectionListeners(ConnectionEvent.CLOSED);
+    }
+
+    /**
+     * Return a copy of the URLName representing this service with the password and file information removed.
+     *
+     * @return the URLName for this service
+     */
+    public URLName getURLName() {
+
+        return url == null ? null : new URLName(url.getProtocol(), url.getHost(), url.getPort(), null, url.getUsername(), null);
+    }
+
+    /**
+     * Set the url field.
+     * @param url the new value
+     */
+    protected void setURLName(URLName url) {
+        this.url = url;
+    }
+
+    public void addConnectionListener(ConnectionListener listener) {
+        connectionListeners.add(listener);
+    }
+
+    public void removeConnectionListener(ConnectionListener listener) {
+        connectionListeners.remove(listener);
+    }
+
+    protected void notifyConnectionListeners(int type) {
+        queue.queueEvent(new ConnectionEvent(this, type), connectionListeners);
+    }
+
+    public String toString() {
+        return url == null ? super.toString() : url.toString();
+    }
+
+    protected void queueEvent(MailEvent event, Vector listeners) {
+        queue.queueEvent(event, listeners);
+    }
+
+    protected void finalize() throws Throwable {
+        queue.stop();
+        connectionListeners.clear();
+        super.finalize();
+    }
+
+
+    /**
+     * Package scope utility method to allow Message instances
+     * access to the Service's session.
+     *
+     * @return The Session the service is associated with.
+     */
+    Session getSession() {
+        return session;
+    }
+}

Propchange: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/Service.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/Service.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/Service.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/Session.java
URL: http://svn.apache.org/viewvc/geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/Session.java?rev=421852&view=auto
==============================================================================
--- geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/Session.java (added)
+++ geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/Session.java Fri Jul 14 03:02:19 2006
@@ -0,0 +1,797 @@
+/**
+ *
+ * Copyright 2003-2006 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 javax.mail;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.PrintStream;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.net.InetAddress;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.StringTokenizer;
+import java.util.WeakHashMap;
+
+
+/**
+ * OK, so we have a final class in the API with a heck of a lot of implementation required...
+ * let's try and figure out what it is meant to do.
+ * <p/>
+ * It is supposed to collect together properties and defaults so that they can be
+ * shared by multiple applications on a desktop; with process isolation and no
+ * real concept of shared memory, this seems challenging. These properties and
+ * defaults rely on system properties, making management in a app server harder,
+ * and on resources loaded from "mail.jar" which may lead to skew between
+ * differnet independent implementations of this API.
+ *
+ * @version $Rev$ $Date$
+ */
+public final class Session {
+    private static final Class[] PARAM_TYPES = {Session.class, URLName.class};
+    private static final WeakHashMap addressMapsByClassLoader = new WeakHashMap();
+    private static Session DEFAULT_SESSION;
+
+    private Map passwordAuthentications = new HashMap();
+
+    private final Properties properties;
+    private final Authenticator authenticator;
+    private boolean debug;
+    private PrintStream debugOut = System.out;
+
+    private static final WeakHashMap providersByClassLoader = new WeakHashMap();
+
+    /**
+     * No public constrcutor allowed.
+     */
+    private Session(Properties properties, Authenticator authenticator) {
+        this.properties = properties;
+        this.authenticator = authenticator;
+        debug = Boolean.valueOf(properties.getProperty("mail.debug")).booleanValue();
+    }
+
+    /**
+     * Create a new session initialized with the supplied properties which uses the supplied authenticator.
+     * Clients should ensure the properties listed in Appendix A of the JavaMail specification are
+     * set as the defaults are unlikey to work in most scenarios; particular attention should be given
+     * to:
+     * <ul>
+     * <li>mail.store.protocol</li>
+     * <li>mail.transport.protocol</li>
+     * <li>mail.host</li>
+     * <li>mail.user</li>
+     * <li>mail.from</li>
+     * </ul>
+     *
+     * @param properties    the session properties
+     * @param authenticator an authenticator for callbacks to the user
+     * @return a new session
+     */
+    public static Session getInstance(Properties properties, Authenticator authenticator) {
+        return new Session(new Properties(properties), authenticator);
+    }
+
+    /**
+     * Create a new session initialized with the supplied properties with no authenticator.
+     *
+     * @param properties the session properties
+     * @return a new session
+     * @see #getInstance(java.util.Properties, Authenticator)
+     */
+    public static Session getInstance(Properties properties) {
+        return getInstance(properties, null);
+    }
+
+    /**
+     * Get the "default" instance assuming no authenticator is required.
+     *
+     * @param properties the session properties
+     * @return if "default" session
+     * @throws SecurityException if the does not have permission to access the default session
+     */
+    public synchronized static Session getDefaultInstance(Properties properties) {
+        return getDefaultInstance(properties, null);
+    }
+
+    /**
+     * Get the "default" session.
+     * If there is not current "default", a new Session is created and installed as the default.
+     *
+     * @param properties
+     * @param authenticator
+     * @return if "default" session
+     * @throws SecurityException if the does not have permission to access the default session
+     */
+    public synchronized static Session getDefaultInstance(Properties properties, Authenticator authenticator) {
+        if (DEFAULT_SESSION == null) {
+            DEFAULT_SESSION = getInstance(properties, authenticator);
+        } else {
+            if (authenticator != DEFAULT_SESSION.authenticator) {
+                if (authenticator == null || DEFAULT_SESSION.authenticator == null || authenticator.getClass().getClassLoader() != DEFAULT_SESSION.authenticator.getClass().getClassLoader()) {
+                    throw new SecurityException();
+                }
+            }
+            // todo we should check with the SecurityManager here as well
+        }
+        return DEFAULT_SESSION;
+    }
+
+    /**
+     * Enable debugging for this session.
+     * Debugging can also be enabled by setting the "mail.debug" property to true when
+     * the session is being created.
+     *
+     * @param debug the debug setting
+     */
+    public void setDebug(boolean debug) {
+        this.debug = debug;
+    }
+
+    /**
+     * Get the debug setting for this session.
+     *
+     * @return the debug setting
+     */
+    public boolean getDebug() {
+        return debug;
+    }
+
+    /**
+     * Set the output stream where debug information should be sent.
+     * If set to null, System.out will be used.
+     *
+     * @param out the stream to write debug information to
+     */
+    public void setDebugOut(PrintStream out) {
+        debugOut = out == null ? System.out : out;
+    }
+
+    /**
+     * Return the debug output stream.
+     *
+     * @return the debug output stream
+     */
+    public PrintStream getDebugOut() {
+        return debugOut;
+    }
+
+    /**
+     * Return the list of providers available to this application.
+     * This method searches for providers that are defined in the javamail.providers
+     * and javamail.default.providers resources available through the current context
+     * classloader, or if that is not available, the classloader that loaded this class.
+     * <p/>
+     * As searching for providers is potentially expensive, this implementation maintains
+     * a WeakHashMap of providers indexed by ClassLoader.
+     *
+     * @return an array of providers
+     */
+    public Provider[] getProviders() {
+        ProviderInfo info = getProviderInfo();
+        return (Provider[]) info.all.toArray(new Provider[info.all.size()]);
+    }
+
+    /**
+     * Return the provider for a specific protocol.
+     * This implementation initially looks in the Session properties for an property with the name
+     * "mail.<protocol>.class"; if found it attempts to create an instance of the class named in that
+     * property throwing a NoSuchProviderException if the class cannot be loaded.
+     * If this property is not found, it searches the providers returned by {@link #getProviders()}
+     * for a entry for the specified protocol.
+     *
+     * @param protocol the protocol to get a provider for
+     * @return a provider for that protocol
+     * @throws NoSuchProviderException
+     */
+    public Provider getProvider(String protocol) throws NoSuchProviderException {
+        ProviderInfo info = getProviderInfo();
+        Provider provider = null;
+        String providerName = properties.getProperty("mail." + protocol + ".class");
+        if (providerName != null) {
+            provider = (Provider) info.byClassName.get(providerName);
+            if (debug) {
+                writeDebug("DEBUG: new provider loaded: " + provider.toString());
+            }
+        }
+
+        // if not able to locate this by class name, just grab a registered protocol.
+        if (provider == null) {
+            provider = (Provider) info.byProtocol.get(protocol);
+        }
+
+        if (provider == null) {
+            throw new NoSuchProviderException("Unable to locate provider for protocol: " + protocol);
+        }
+        if (debug) {
+            writeDebug("DEBUG: getProvider() returning provider " + provider.toString());
+        }
+        return provider;
+    }
+
+    /**
+     * Make the supplied Provider the default for its protocol.
+     *
+     * @param provider the new default Provider
+     * @throws NoSuchProviderException
+     */
+    public void setProvider(Provider provider) throws NoSuchProviderException {
+        ProviderInfo info = getProviderInfo();
+        info.byProtocol.put(provider.getProtocol(), provider);
+    }
+
+    /**
+     * Return a Store for the default protocol defined by the mail.store.protocol property.
+     *
+     * @return the store for the default protocol
+     * @throws NoSuchProviderException
+     */
+    public Store getStore() throws NoSuchProviderException {
+        String protocol = properties.getProperty("mail.store.protocol");
+        if (protocol == null) {
+            throw new NoSuchProviderException("mail.store.protocol property is not set");
+        }
+        return getStore(protocol);
+    }
+
+    /**
+     * Return a Store for the specified protocol.
+     *
+     * @param protocol the protocol to get a Store for
+     * @return a Store
+     * @throws NoSuchProviderException if no provider is defined for the specified protocol
+     */
+    public Store getStore(String protocol) throws NoSuchProviderException {
+        Provider provider = getProvider(protocol);
+        return getStore(provider);
+    }
+
+    /**
+     * Return a Store for the protocol specified in the given URL
+     *
+     * @param url the URL of the Store
+     * @return a Store
+     * @throws NoSuchProviderException if no provider is defined for the specified protocol
+     */
+    public Store getStore(URLName url) throws NoSuchProviderException {
+        return (Store) getService(getProvider(url.getProtocol()), url);
+    }
+
+    /**
+     * Return the Store specified by the given provider.
+     *
+     * @param provider the provider to create from
+     * @return a Store
+     * @throws NoSuchProviderException if there was a problem creating the Store
+     */
+    public Store getStore(Provider provider) throws NoSuchProviderException {
+        if (Provider.Type.STORE != provider.getType()) {
+            throw new NoSuchProviderException("Not a Store Provider: " + provider);
+        }
+        return (Store) getService(provider, null);
+    }
+
+    /**
+     * Return a closed folder for the supplied URLName, or null if it cannot be obtained.
+     * <p/>
+     * The scheme portion of the URL is used to locate the Provider and create the Store;
+     * the returned Store is then used to obtain the folder.
+     *
+     * @param name the location of the folder
+     * @return the requested folder, or null if it is unavailable
+     * @throws NoSuchProviderException if there is no provider
+     * @throws MessagingException      if there was a problem accessing the Store
+     */
+    public Folder getFolder(URLName name) throws MessagingException {
+        Store store = getStore(name);
+        return store.getFolder(name);
+    }
+
+    /**
+     * Return a Transport for the default protocol specified by the
+     * <code>mail.transport.protocol</code> property.
+     *
+     * @return a Transport
+     * @throws NoSuchProviderException
+     */
+    public Transport getTransport() throws NoSuchProviderException {
+        String protocol = properties.getProperty("mail.transport.protocol");
+        if (protocol == null) {
+            throw new NoSuchProviderException("mail.transport.protocol property is not set");
+        }
+        return getTransport(protocol);
+    }
+
+    /**
+     * Return a Transport for the specified protocol.
+     *
+     * @param protocol the protocol to use
+     * @return a Transport
+     * @throws NoSuchProviderException
+     */
+    public Transport getTransport(String protocol) throws NoSuchProviderException {
+        Provider provider = getProvider(protocol);
+        return getTransport(provider);
+    }
+
+    /**
+     * Return a transport for the protocol specified in the URL.
+     *
+     * @param name the URL whose scheme specifies the protocol
+     * @return a Transport
+     * @throws NoSuchProviderException
+     */
+    public Transport getTransport(URLName name) throws NoSuchProviderException {
+        return (Transport) getService(getProvider(name.getProtocol()), name);
+    }
+
+    /**
+     * Return a transport for the protocol associated with the type of this address.
+     *
+     * @param address the address we are trying to deliver to
+     * @return a Transport
+     * @throws NoSuchProviderException
+     */
+    public Transport getTransport(Address address) throws NoSuchProviderException {
+        String type = address.getType();
+        // load the address map from the resource files.
+        Map addressMap = getAddressMap();
+        String protocolName = (String)addressMap.get(type);
+        if (protocolName == null) {
+            throw new NoSuchProviderException("No provider for address type " + type);
+        }
+        return getTransport(protocolName);
+    }
+
+    /**
+     * Return the Transport specified by a Provider
+     *
+     * @param provider the defining Provider
+     * @return a Transport
+     * @throws NoSuchProviderException
+     */
+    public Transport getTransport(Provider provider) throws NoSuchProviderException {
+        return (Transport) getService(provider, null);
+    }
+
+    /**
+     * Set the password authentication associated with a URL.
+     *
+     * @param name          the url
+     * @param authenticator the authenticator
+     */
+    public void setPasswordAuthentication(URLName name, PasswordAuthentication authenticator) {
+        if (authenticator == null) {
+            passwordAuthentications.remove(name);
+        } else {
+            passwordAuthentications.put(name, authenticator);
+        }
+    }
+
+    /**
+     * Get the password authentication associated with a URL
+     *
+     * @param name the URL
+     * @return any authenticator for that url, or null if none
+     */
+    public PasswordAuthentication getPasswordAuthentication(URLName name) {
+        return (PasswordAuthentication) passwordAuthentications.get(name);
+    }
+
+    /**
+     * Call back to the application supplied authenticator to get the needed username add password.
+     *
+     * @param host            the host we are trying to connect to, may be null
+     * @param port            the port on that host
+     * @param protocol        the protocol trying to be used
+     * @param prompt          a String to show as part of the prompt, may be null
+     * @param defaultUserName the default username, may be null
+     * @return the authentication information collected by the authenticator; may be null
+     */
+    public PasswordAuthentication requestPasswordAuthentication(InetAddress host, int port, String protocol, String prompt, String defaultUserName) {
+        if (authenticator == null) {
+            return null;
+        }
+        return authenticator.authenticate(host, port, protocol, prompt, defaultUserName);
+    }
+
+    /**
+     * Return the properties object for this Session; this is a live collection.
+     *
+     * @return the properties for the Session
+     */
+    public Properties getProperties() {
+        return properties;
+    }
+
+    /**
+     * Return the specified property.
+     *
+     * @param property the property to get
+     * @return its value, or null if not present
+     */
+    public String getProperty(String property) {
+        return getProperties().getProperty(property);
+    }
+
+
+    /**
+     * Add a provider to the Session managed provider list.
+     *
+     * @param provider The new provider to add.
+     */
+    public synchronized void addProvider(Provider provider) {
+        ProviderInfo info = getProviderInfo();
+        info.addProvider(provider);
+    }
+
+
+
+    /**
+     * Add a mapping between an address type and a protocol used
+     * to process that address type.
+     *
+     * @param addressType
+     *                 The address type identifier.
+     * @param protocol The protocol name mapping.
+     */
+    public void setProtocolForAddress(String addressType, String protocol) {
+        Map addressMap = getAddressMap();
+
+        // no protocol specified is a removal
+        if (protocol == null) {
+            addressMap.remove(addressType);
+        }
+        else {
+            addressMap.put(addressType, protocol);
+        }
+    }
+
+
+    private Service getService(Provider provider, URLName name) throws NoSuchProviderException {
+        try {
+            ClassLoader cl = getClassLoader();
+            Class clazz = cl.loadClass(provider.getClassName());
+            Constructor ctr = clazz.getConstructor(PARAM_TYPES);
+            return (Service) ctr.newInstance(new Object[]{this, name});
+        } catch (ClassNotFoundException e) {
+            throw (NoSuchProviderException) new NoSuchProviderException("Unable to load class for provider: " + provider).initCause(e);
+        } catch (NoSuchMethodException e) {
+            throw (NoSuchProviderException) new NoSuchProviderException("Provider class does not have a constructor(Session, URLName): " + provider).initCause(e);
+        } catch (InstantiationException e) {
+            throw (NoSuchProviderException) new NoSuchProviderException("Unable to instantiate provider class: " + provider).initCause(e);
+        } catch (IllegalAccessException e) {
+            throw (NoSuchProviderException) new NoSuchProviderException("Unable to instantiate provider class: " + provider).initCause(e);
+        } catch (InvocationTargetException e) {
+            throw (NoSuchProviderException) new NoSuchProviderException("Exception from constructor of provider class: " + provider).initCause(e.getCause());
+        }
+    }
+
+    private ProviderInfo getProviderInfo() {
+        ClassLoader cl = getClassLoader();
+        ProviderInfo info = (ProviderInfo) providersByClassLoader.get(cl);
+        if (info == null) {
+            info = loadProviders(cl);
+        }
+        return info;
+    }
+
+    private Map getAddressMap() {
+        ClassLoader cl = getClassLoader();
+        Map addressMap = (Map)addressMapsByClassLoader.get(cl);
+        if (addressMap == null) {
+            addressMap = loadAddressMap(cl);
+        }
+        return addressMap;
+    }
+
+
+    /**
+     * Resolve a class loader used to resolve context resources.  The
+     * class loader used is either a current thread context class
+     * loader (if set), the class loader used to load an authenticator
+     * we've been initialized with, or the class loader used to load
+     * this class instance (which may be a subclass of Session).
+     *
+     * @return The class loader used to load resources.
+     */
+    private ClassLoader getClassLoader() {
+        ClassLoader cl = Thread.currentThread().getContextClassLoader();
+        if (cl == null) {
+            if (authenticator != null) {
+                cl = authenticator.getClass().getClassLoader();
+            }
+            else {
+                cl = this.getClass().getClassLoader();
+            }
+        }
+        return cl;
+    }
+
+    private ProviderInfo loadProviders(ClassLoader cl) {
+        // we create a merged map from reading all of the potential address map entries.  The locations
+        // searched are:
+        //   1.   java.home/lib/javamail.address.map
+        //   2. META-INF/javamail.address.map
+        //   3. META-INF/javamail.default.address.map
+        //
+        ProviderInfo info = new ProviderInfo();
+
+        // make sure this is added to the global map.
+        providersByClassLoader.put(cl, info);
+
+
+        // NOTE:  Unlike the addressMap, we process these in the defined order.  The loading routine
+        // will not overwrite entries if they already exist in the map.
+
+        try {
+            Enumeration e = cl.getResources("META-INF/javamail.default.providers");
+            while (e.hasMoreElements()) {
+                URL url = (URL) e.nextElement();
+                if (debug) {
+                    writeDebug("Loading javamail.default.providers from " + url.toString());
+                }
+
+                InputStream is = url.openStream();
+                try {
+                    loadProviders(info, is);
+                } finally{
+                    is.close();
+                }
+            }
+        } catch (SecurityException e) {
+            // ignore
+        } catch (IOException e) {
+            // ignore
+        }
+
+
+        try {
+            File file = new File(System.getProperty("java.home"), "lib/javamail.providers");
+            InputStream is = new FileInputStream(file);
+            try {
+                loadProviders(info, is);
+                if (debug) {
+                    writeDebug("Loaded lib/javamail.providers from " + file.toString());
+                }
+            } finally{
+                is.close();
+            }
+        } catch (SecurityException e) {
+            // ignore
+        } catch (IOException e) {
+            // ignore
+        }
+
+        try {
+            Enumeration e = cl.getResources("META-INF/javamail.providers");
+            while (e.hasMoreElements()) {
+                URL url = (URL) e.nextElement();
+                if (debug) {
+                    writeDebug("Loading META-INF/javamail.providers from " + url.toString());
+                }
+                InputStream is = url.openStream();
+                try {
+                    loadProviders(info, is);
+                } finally{
+                    is.close();
+                }
+            }
+        } catch (SecurityException e) {
+            // ignore
+        } catch (IOException e) {
+            // ignore
+        }
+
+        return info;
+    }
+
+    private void loadProviders(ProviderInfo info, InputStream is) throws IOException {
+        BufferedReader reader = new BufferedReader(new InputStreamReader(is));
+        String line;
+        while ((line = reader.readLine()) != null) {
+            StringTokenizer tok = new StringTokenizer(line, ";");
+            String protocol = null;
+            Provider.Type type = null;
+            String className = null;
+            String vendor = null;
+            String version = null;
+            while (tok.hasMoreTokens()) {
+                String property = tok.nextToken();
+                int index = property.indexOf('=');
+                if (index == -1) {
+                    continue;
+                }
+                String key = property.substring(0, index).trim().toLowerCase();
+                String value = property.substring(index+1).trim();
+                if (protocol == null && "protocol".equals(key)) {
+                    protocol = value;
+                } else if (type == null && "type".equals(key)) {
+                    if ("store".equals(value)) {
+                        type = Provider.Type.STORE;
+                    } else if ("transport".equals(value)) {
+                        type = Provider.Type.TRANSPORT;
+                    }
+                } else if (className == null && "class".equals(key)) {
+                    className = value;
+                } else if ("vendor".equals(key)) {
+                    vendor = value;
+                } else if ("version".equals(key)) {
+                    version = value;
+                }
+            }
+            if (protocol == null || type == null || className == null) {
+                //todo should we log a warning?
+                continue;
+            }
+
+            if (debug) {
+                writeDebug("DEBUG: loading new provider protocol=" + protocol + ", className=" + className + ", vendor=" + vendor + ", version=" + version);
+            }
+            Provider provider = new Provider(type, protocol, className, vendor, version);
+            // add to the info list.
+            info.addProvider(provider);
+        }
+    }
+
+    /**
+     * Load up an address map associated with a using class loader
+     * instance.
+     *
+     * @param cl     The class loader used to resolve the address map.
+     *
+     * @return A map containing the entries associated with this classloader
+     *         instance.
+     */
+    private static Map loadAddressMap(ClassLoader cl) {
+        // we create a merged map from reading all of the potential address map entries.  The locations
+        // searched are:
+        //   1.   java.home/lib/javamail.address.map
+        //   2. META-INF/javamail.address.map
+        //   3. META-INF/javamail.default.address.map
+        //
+        // if all of the above searches fail, we just set up some "default" defaults.
+
+        // the format of the address.map file is defined as a property file.  We can cheat and
+        // just use Properties.load() to read in the files.
+        Properties addressMap = new Properties();
+
+        // add this to the tracking map.
+        addressMapsByClassLoader.put(cl, addressMap);
+
+        // NOTE:  We are reading these resources in reverse order of what's cited above.  This allows
+        // user defined entries to overwrite default entries if there are similarly named items.
+
+        try {
+            Enumeration e = cl.getResources("META-INF/javamail.default.address.map");
+            while (e.hasMoreElements()) {
+                URL url = (URL) e.nextElement();
+                InputStream is = url.openStream();
+                try {
+                    // load as a property file
+                    addressMap.load(is);
+                } finally{
+                    is.close();
+                }
+            }
+        } catch (SecurityException e) {
+            // ignore
+        } catch (IOException e) {
+            // ignore
+        }
+
+
+        try {
+            Enumeration e = cl.getResources("META-INF/javamail.address.map");
+            while (e.hasMoreElements()) {
+                URL url = (URL) e.nextElement();
+                InputStream is = url.openStream();
+                try {
+                    // load as a property file
+                    addressMap.load(is);
+                } finally{
+                    is.close();
+                }
+            }
+        } catch (SecurityException e) {
+            // ignore
+        } catch (IOException e) {
+            // ignore
+        }
+
+
+        try {
+            File file = new File(System.getProperty("java.home"), "lib/javamail.address.map");
+            InputStream is = new FileInputStream(file);
+            try {
+                // load as a property file
+                addressMap.load(is);
+            } finally{
+                is.close();
+            }
+        } catch (SecurityException e) {
+            // ignore
+        } catch (IOException e) {
+            // ignore
+        }
+
+        try {
+            Enumeration e = cl.getResources("META-INF/javamail.address.map");
+            while (e.hasMoreElements()) {
+                URL url = (URL) e.nextElement();
+                InputStream is = url.openStream();
+                try {
+                    // load as a property file
+                    addressMap.load(is);
+                } finally{
+                    is.close();
+                }
+            }
+        } catch (SecurityException e) {
+            // ignore
+        } catch (IOException e) {
+            // ignore
+        }
+
+
+        // if unable to load anything, at least create the MimeMessage-smtp protocol mapping.
+        if (addressMap.isEmpty()) {
+            addressMap.put("rfc822", "smtp");
+        }
+
+        return addressMap;
+    }
+
+    /**
+     * Private convenience routine for debug output.
+     *
+     * @param msg    The message to write out to the debug stream.
+     */
+    private void writeDebug(String msg) {
+        debugOut.println(msg);
+    }
+
+
+    private static class ProviderInfo {
+        private final Map byClassName = new HashMap();
+        private final Map byProtocol = new HashMap();
+        private final List all = new ArrayList();
+
+        public void addProvider(Provider provider) {
+            String className = provider.getClassName();
+
+            if (!byClassName.containsKey(className)) {
+                byClassName.put(className, provider);
+            }
+
+            String protocol = provider.getProtocol();
+            if (!byProtocol.containsKey(protocol)) {
+                byProtocol.put(protocol, provider);
+            }
+            all.add(provider);
+        }
+    }
+}

Propchange: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/Session.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/Session.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/Session.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/Store.java
URL: http://svn.apache.org/viewvc/geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/Store.java?rev=421852&view=auto
==============================================================================
--- geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/Store.java (added)
+++ geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/Store.java Fri Jul 14 03:02:19 2006
@@ -0,0 +1,142 @@
+/**
+ *
+ * Copyright 2003-2006 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 javax.mail;
+
+import java.util.Vector;
+import javax.mail.event.FolderEvent;
+import javax.mail.event.FolderListener;
+import javax.mail.event.StoreEvent;
+import javax.mail.event.StoreListener;
+
+/**
+ * Abstract class that represents a message store.
+ *
+ * @version $Rev$ $Date$
+ */
+public abstract class Store extends Service {
+    private static final Folder[] FOLDER_ARRAY = new Folder[0];
+    private final Vector folderListeners = new Vector(2);
+    private final Vector storeListeners = new Vector(2);
+
+    /**
+     * Constructor specifying session and url of this store.
+     * Subclasses MUST provide a constructor with this signature.
+     *
+     * @param session the session associated with this store
+     * @param name the URL of the store
+     */
+    protected Store(Session session, URLName name) {
+        super(session, name);
+    }
+
+    /**
+     * Retutn a Folder object that represents the root of the namespace for the current user.
+     *
+     * Note that in some store configurations (such as IMAP4) then the root folder may
+     * not be the INBOX folder.
+     *
+     * @return the root Folder
+     * @throws MessagingException if there was a problem accessing the store
+     */
+    public abstract Folder getDefaultFolder() throws MessagingException;
+
+    /**
+     * Return the Folder corresponding to the given name.
+     * The folder may not physically exist; the {@link Folder#exists()} method can be used
+     * to determine if it is real.
+     * @param name the name of the Folder to return
+     * @return the corresponding folder
+     * @throws MessagingException if there was a problem accessing the store
+     */
+    public abstract Folder getFolder(String name) throws MessagingException;
+
+    /**
+     * Return the folder identified by the URLName; the URLName must refer to this Store.
+     * Implementations may use the {@link URLName#getFile()} method to determined the folder name.
+     *
+     * @param name the folder to return
+     * @return the corresponding folder
+     * @throws MessagingException if there was a problem accessing the store
+     */
+    public abstract Folder getFolder(URLName name) throws MessagingException;
+
+    /**
+     * Return the root folders of the personal namespace belonging to the current user.
+     *
+     * The default implementation simply returns an array containing the folder returned by {@link #getDefaultFolder()}.
+     * @return the root folders of the user's peronal namespaces
+     * @throws MessagingException if there was a problem accessing the store
+     */
+    public Folder[] getPersonalNamespaces() throws MessagingException {
+        return new Folder[]{getDefaultFolder()};
+    }
+
+    /**
+     * Return the root folders of the personal namespaces belonging to the supplied user.
+     *
+     * The default implementation simply returns an empty array.
+     *
+     * @param user the user whose namespaces should be returned
+     * @return the root folders of the given user's peronal namespaces
+     * @throws MessagingException if there was a problem accessing the store
+     */
+    public Folder[] getUserNamespaces(String user) throws MessagingException {
+        return FOLDER_ARRAY;
+    }
+
+    /**
+     * Return the root folders of namespaces that are intended to be shared between users.
+     *
+     * The default implementation simply returns an empty array.
+     * @return the root folders of all shared namespaces
+     * @throws MessagingException if there was a problem accessing the store
+     */
+    public Folder[] getSharedNamespaces() throws MessagingException {
+        return FOLDER_ARRAY;
+    }
+
+
+    public void addStoreListener(StoreListener listener) {
+        storeListeners.add(listener);
+    }
+
+    public void removeStoreListener(StoreListener listener) {
+        storeListeners.remove(listener);
+    }
+
+    protected void notifyStoreListeners(int type, String message) {
+        queueEvent(new StoreEvent(this, type, message), storeListeners);
+    }
+
+
+    public void addFolderListener(FolderListener listener) {
+        folderListeners.add(listener);
+    }
+
+    public void removeFolderListener(FolderListener listener) {
+        folderListeners.remove(listener);
+    }
+
+    protected void notifyFolderListeners(int type, Folder folder) {
+        queueEvent(new FolderEvent(this, folder, type), folderListeners);
+    }
+
+    protected void notifyFolderRenamedListeners(Folder oldFolder, Folder newFolder) {
+        queueEvent(new FolderEvent(this, oldFolder, newFolder, FolderEvent.RENAMED), folderListeners);
+    }
+}

Propchange: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/Store.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/Store.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/Store.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/StoreClosedException.java
URL: http://svn.apache.org/viewvc/geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/StoreClosedException.java?rev=421852&view=auto
==============================================================================
--- geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/StoreClosedException.java (added)
+++ geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/StoreClosedException.java Fri Jul 14 03:02:19 2006
@@ -0,0 +1,38 @@
+/**
+ *
+ * Copyright 2003-2006 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 javax.mail;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class StoreClosedException extends MessagingException {
+    private transient Store _store;
+
+    public StoreClosedException(Store store) {
+        super();
+        _store = store;
+    }
+
+    public StoreClosedException(Store store, String message) {
+        super(message);
+    }
+
+    public Store getStore() {
+        return _store;
+    }
+}

Propchange: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/StoreClosedException.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/StoreClosedException.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/StoreClosedException.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/Transport.java
URL: http://svn.apache.org/viewvc/geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/Transport.java?rev=421852&view=auto
==============================================================================
--- geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/Transport.java (added)
+++ geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/Transport.java Fri Jul 14 03:02:19 2006
@@ -0,0 +1,204 @@
+/**
+ *
+ * Copyright 2003-2006 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 javax.mail;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Vector;
+import javax.mail.event.TransportEvent;
+import javax.mail.event.TransportListener;
+
+/**
+ * Abstract class modeling a message transport.
+ *
+ * @version $Rev$ $Date$
+ */
+public abstract class Transport extends Service {
+    /**
+     * Send a message to all recipient addresses it contains (as returned by {@link Message#getAllRecipients()})
+     * using message transports appropriate for each address. Message addresses are checked during submission,
+     * but there is no guarantee that the ultimate address is valid or that the message will ever be delivered.
+     * <p/>
+     * {@link Message#saveChanges()} will be called before the message is actually sent.
+     *
+     * @param message the message to send
+     * @throws MessagingException if there was a problem sending the message
+     */
+    public static void send(Message message) throws MessagingException {
+        send(message, message.getAllRecipients());
+    }
+
+    /**
+     * Send a message to all addresses provided irrespective of any recipients contained in the message itself
+     * using message transports appropriate for each address. Message addresses are checked during submission,
+     * but there is no guarantee that the ultimate address is valid or that the message will ever be delivered.
+     * <p/>
+     * {@link Message#saveChanges()} will be called before the message is actually sent.
+     *
+     * @param message   the message to send
+     * @param addresses the addesses to send to
+     * @throws MessagingException if there was a problem sending the message
+     */
+    public static void send(Message message, Address[] addresses) throws MessagingException {
+        Session session = message.session;
+        Map msgsByTransport = new HashMap();
+        for (int i = 0; i < addresses.length; i++) {
+            Address address = addresses[i];
+            Transport transport = session.getTransport(address);
+            List addrs = (List) msgsByTransport.get(transport);
+            if (addrs == null) {
+                addrs = new ArrayList();
+                msgsByTransport.put(transport, addrs);
+            }
+            addrs.add(address);
+        }
+
+        message.saveChanges();
+
+        // Since we might be sending to multiple protocols, we need to catch and process each exception
+        // when we send and then throw a new SendFailedException when everything is done.  Unfortunately, this
+        // also means unwrapping the information in any SendFailedExceptions we receive and building
+        // composite failed list.
+        MessagingException chainedException = null;
+        ArrayList sentAddresses = new ArrayList();
+        ArrayList unsentAddresses = new ArrayList();
+        ArrayList invalidAddresses = new ArrayList();
+
+
+        for (Iterator i = msgsByTransport.entrySet().iterator(); i.hasNext();) {
+            Map.Entry entry = (Map.Entry) i.next();
+            Transport transport = (Transport) entry.getKey();
+            List addrs = (List) entry.getValue();
+            try {
+                // we MUST connect to the transport before attempting to send.
+                transport.connect();
+                transport.sendMessage(message, (Address[]) addrs.toArray(new Address[addrs.size()]));
+                // if we have to throw an exception because of another failure, these addresses need to
+                // be in the valid list.  Since we succeeded here, we can add these now.
+                sentAddresses.addAll(addrs);
+            } catch (SendFailedException e) {
+                // a true send failure.  The exception contains a wealth of information about
+                // the failures, including a potential chain of exceptions explaining what went wrong.  We're
+                // going to send a new one of these, so we need to merge the information.
+
+                // add this to our exception chain
+                if (chainedException == null) {
+                    chainedException = e;
+                }
+                else {
+                    chainedException.setNextException(e);
+                }
+
+                // now extract each of the address categories from
+                Address[] exAddrs = e.getValidSentAddresses();
+                if (exAddrs != null) {
+                    for (int j = 0; j < exAddrs.length; j++) {
+                        sentAddresses.add(exAddrs[j]);
+                    }
+                }
+
+                exAddrs = e.getValidUnsentAddresses();
+                if (exAddrs != null) {
+                    for (int j = 0; j < exAddrs.length; j++) {
+                        unsentAddresses.add(exAddrs[j]);
+                    }
+                }
+
+                exAddrs = e.getInvalidAddresses();
+                if (exAddrs != null) {
+                    for (int j = 0; j < exAddrs.length; j++) {
+                        invalidAddresses.add(exAddrs[j]);
+                    }
+                }
+
+            } catch (MessagingException e) {
+                // add this to our exception chain
+                if (chainedException == null) {
+                    chainedException = e;
+                }
+                else {
+                    chainedException.setNextException(e);
+                }
+            }
+            finally {
+                transport.close();
+            }
+        }
+
+        // if we have an exception chain then we need to throw a new exception giving the failure
+        // information.
+        if (chainedException != null) {
+            // if we're only sending to a single transport (common), and we received a SendFailedException
+            // as a result, then we have a fully formed exception already.  Rather than wrap this in another
+            // exception, we can just rethrow the one we have.
+            if (msgsByTransport.size() == 1 && chainedException instanceof SendFailedException) {
+                throw chainedException;
+            }
+
+            // create our lists for notification and exception reporting from this point on.
+            Address[] sent = (Address[])sentAddresses.toArray(new Address[0]);
+            Address[] unsent = (Address[])unsentAddresses.toArray(new Address[0]);
+            Address[] invalid = (Address[])invalidAddresses.toArray(new Address[0]);
+
+            throw new SendFailedException("Send failure", chainedException, sent, unsent, invalid);
+        }
+    }
+
+    /**
+     * Constructor taking Session and URLName parameters required for {@link Service#Service(Session, URLName)}.
+     *
+     * @param session the Session this transport is for
+     * @param name    the location this transport is for
+     */
+    public Transport(Session session, URLName name) {
+        super(session, name);
+    }
+
+    /**
+     * Send a message to the supplied addresses using this transport; if any of the addresses are
+     * invalid then a {@link SendFailedException} is thrown. Whether the message is actually sent
+     * to any of the addresses is undefined.
+     * <p/>
+     * Unlike the static {@link #send(Message, Address[])} method, {@link Message#saveChanges()} is
+     * not called. A {@link TransportEvent} will be sent to registered listeners once the delivery
+     * attempt has been made.
+     *
+     * @param message   the message to send
+     * @param addresses list of addresses to send it to
+     * @throws SendFailedException if the send failed
+     * @throws MessagingException  if there was a problem sending the message
+     */
+    public abstract void sendMessage(Message message, Address[] addresses) throws MessagingException;
+
+    private Vector transportListeners = new Vector();
+
+    public void addTransportListener(TransportListener listener) {
+        transportListeners.add(listener);
+    }
+
+    public void removeTransportListener(TransportListener listener) {
+        transportListeners.remove(listener);
+    }
+
+    protected void notifyTransportListeners(int type, Address[] validSent, Address[] validUnsent, Address[] invalid, Message message) {
+        queueEvent(new TransportEvent(this, type, validSent, validUnsent, invalid, message), transportListeners);
+    }
+}

Propchange: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/Transport.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/Transport.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/Transport.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/UIDFolder.java
URL: http://svn.apache.org/viewvc/geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/UIDFolder.java?rev=421852&view=auto
==============================================================================
--- geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/UIDFolder.java (added)
+++ geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/UIDFolder.java Fri Jul 14 03:02:19 2006
@@ -0,0 +1,50 @@
+/**
+ *
+ * Copyright 2003-2006 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 javax.mail;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public interface UIDFolder {
+    /**
+     * A special value than can be passed as the <code>end</code> parameter to
+     * {@link Folder#getMessages(int, int)} to indicate the last message in this folder.
+     */
+    public static final long LASTUID = -1;
+
+    public abstract long getUIDValidity() throws MessagingException;
+
+    public abstract Message getMessageByUID(long uid)
+            throws MessagingException;
+
+    public abstract Message[] getMessagesByUID(long start, long end)
+            throws MessagingException;
+
+    public abstract Message[] getMessagesByUID(long[] ids)
+            throws MessagingException;
+
+    public abstract long getUID(Message message) throws MessagingException;
+
+    public static class FetchProfileItem extends FetchProfile.Item {
+        public static final FetchProfileItem UID = new FetchProfileItem("Uid");
+
+        protected FetchProfileItem(String name) {
+            super(name);
+        }
+    }
+}

Propchange: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/UIDFolder.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/UIDFolder.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/UIDFolder.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain