geronimo-scm mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From jlaskow...@apache.org
Subject svn commit: r383095 - in /geronimo/trunk/modules/javamail-transport/src: java/org/apache/geronimo/javamail/transport/nntp/ resources/META-INF/
Date Sat, 04 Mar 2006 11:55:44 GMT
Author: jlaskowski
Date: Sat Mar  4 03:55:42 2006
New Revision: 383095

URL: http://svn.apache.org/viewcvs?rev=383095&view=rev
Log:
GERONIMO-1675 Add NNTP transport to the javamail package

Thanks Rick!

NOTE: It requires the latest version of geronimo-javamail_1.3.1_spec-1.1-SNAPSHOT.jar. Will have to ask how ot publish it.

Added:
    geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/nntp/
    geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/nntp/NNTPConnection.java   (with props)
    geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/nntp/NNTPReply.java   (with props)
    geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/nntp/NNTPTransport.java   (with props)
Modified:
    geronimo/trunk/modules/javamail-transport/src/resources/META-INF/javamail.default.address.map   (contents, props changed)
    geronimo/trunk/modules/javamail-transport/src/resources/META-INF/javamail.default.providers   (contents, props changed)

Added: geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/nntp/NNTPConnection.java
URL: http://svn.apache.org/viewcvs/geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/nntp/NNTPConnection.java?rev=383095&view=auto
==============================================================================
--- geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/nntp/NNTPConnection.java (added)
+++ geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/nntp/NNTPConnection.java Sat Mar  4 03:55:42 2006
@@ -0,0 +1,948 @@
+/**
+ *
+ * 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 org.apache.geronimo.javamail.transport.nntp;
+
+import java.io.IOException;
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintStream;
+import java.io.Reader;
+import java.io.UnsupportedEncodingException;
+import java.io.Writer;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.HashMap;
+import java.util.StringTokenizer;
+
+import javax.mail.Address;
+import javax.mail.AuthenticationFailedException;
+import javax.mail.Message;
+import javax.mail.MessagingException;
+import javax.mail.Session;
+import javax.mail.Transport;
+import javax.mail.URLName;
+import javax.mail.event.TransportEvent;
+import javax.mail.internet.NewsAddress;
+import javax.mail.internet.MimeMessage;
+
+import org.apache.geronimo.javamail.authentication.ClientAuthenticator;
+import org.apache.geronimo.javamail.authentication.CramMD5Authenticator;
+import org.apache.geronimo.javamail.authentication.DigestMD5Authenticator;
+import org.apache.geronimo.javamail.authentication.LoginAuthenticator;
+import org.apache.geronimo.javamail.authentication.PlainAuthenticator;
+import org.apache.geronimo.mail.util.Base64;
+import org.apache.geronimo.mail.util.SessionUtil;
+import org.apache.geronimo.javamail.util.MIMEOutputStream;
+
+/**
+ * Simple implementation of NNTP transport.  Just does plain RFC977-ish
+ * delivery.
+ * <p/>
+ * There is no way to indicate failure for a given recipient (it's possible to have a
+ * recipient address rejected).  The sun impl throws exceptions even if others are successful,
+ * but maybe we do a different way...
+ * <p/>
+ *
+ * @version $Rev$ $Date$
+ */
+public class NNTPConnection  {
+
+    /**
+     * constants for EOL termination
+     */
+    protected static final char CR = '\r';
+    protected static final char LF = '\n';
+
+    /**
+     * property keys for protocol properties.
+     */
+    protected static final String MAIL_NNTP_AUTH = "mail.nntp.auth";
+    protected static final String MAIL_NNTP_PORT = "mail.nntp.port";
+    protected static final String MAIL_NNTP_LOCALHOST = "mail.nntp.localhost";
+    protected static final String MAIL_NNTP_TIMEOUT = "mail.nntp.timeout";
+    protected static final String MAIL_NNTP_SASL_REALM = "mail.nntp.sasl.realm";
+    protected static final String MAIL_NNTP_FACTORY_CLASS = "mail.nntp.socketFactory.class";
+    protected static final String MAIL_NNTP_FACTORY_FALLBACK = "socketFactory.fallback";
+    protected static final String MAIL_NNTP_LOCALADDRESS = "localaddress";
+    protected static final String MAIL_NNTP_LOCALPORT = "localport";
+    protected static final String MAIL_NNTP_QUITWAIT = "quitwait";
+    protected static final String MAIL_NNTP_FACTORY_PORT = "socketFactory.port";
+
+    protected static final int MIN_MILLIS = 1000 * 60;
+    protected static final int TIMEOUT = MIN_MILLIS * 5;
+    protected static final String DEFAULT_MAIL_HOST = "localhost";
+    protected static final int DEFAULT_NNTP_PORT = 119;
+
+    protected static final String AUTHENTICATION_PLAIN = "PLAIN";
+    protected static final String AUTHENTICATION_LOGIN = "LOGIN";
+    protected static final String AUTHENTICATION_CRAMMD5 = "CRAM-MD5";
+    protected static final String AUTHENTICATION_DIGESTMD5 = "DIGEST-MD5";
+
+    // the target host
+    protected String host;
+    // the target server port.
+    protected int port;
+
+    // the connection socket...can be a plain socket or SSLSocket, if TLS is being used.
+    protected Socket socket;
+
+    // our local host name
+    protected String localHost;
+
+    // input stream used to read data.  If Sasl is in use, this might be other than the
+    // direct access to the socket input stream.
+    protected InputStream inputStream;
+    // the test reader wrapped around the input stream.
+    protected BufferedReader in;
+    // the other end of the connection pipeline.
+    protected OutputStream outputStream;
+
+    // does the server support posting?
+    protected boolean postingAllowed = true;
+
+    // the username we connect with
+    protected String username;
+    // the authentication password.
+    protected String password;
+    // the target SASL realm (normally null unless explicitly set or we have an authentication mechanism that
+    // requires it.
+    protected String realm;
+
+    // the last response line received from the server.
+    protected NNTPReply lastServerResponse = null;
+
+    // our attached session
+    protected Session session;
+    // our session provided debug output stream.
+    protected PrintStream debugStream;
+    // our debug flag (passed from the hosting transport)
+    protected boolean debug;
+
+    // list of authentication mechanisms supported by the server
+    protected HashMap serverAuthenticationMechanisms;
+    // map of server extension arguments
+    protected HashMap serverExtensionArgs;
+
+    /**
+     * Normal constructor for an NNTPConnection() object.
+     *
+     * @param session  The attached session.
+     * @param host     The target host name of the NNTP server.
+     * @param port     The target listening port of the server.  Defaults to 119 if
+     *                 the port is specified as -1.
+     * @param username The login user name (can be null unless authentication is
+     *                 required).
+     * @param password Password associated with the userid account.  Can be null if
+     *                 authentication is not required.
+     * @param debug    The session debug flag.
+     */
+    public NNTPConnection(Session session, String host, int port, String username, String password, boolean debug) {
+        this.session = session;
+        this.host = host;
+        this.port = port;
+        this.username = username;
+        this.password = password;
+        this.debug = debug;
+
+        // get our debug output.
+        debugStream = session.getDebugOut();
+    }
+
+
+    /**
+     * Connect to the server and do the initial handshaking.
+     *
+     * @exception MessagingException
+     */
+    public void connect() throws MessagingException {
+        try {
+
+            // create socket and connect to server.
+            getConnection();
+
+            // receive welcoming message
+            getWelcome();
+
+        } catch (IOException e) {
+            if (debug) {
+                debugOut("I/O exception establishing connection", e);
+            }
+            throw new MessagingException("Connection error", e);
+        }
+    }
+
+
+    /**
+     * Close the connection.  On completion, we'll be disconnected from
+     * the server and unable to send more data.
+     *
+     * @exception MessagingException
+     */
+    public void close() throws MessagingException {
+        // if we're already closed, get outta here.
+        if (socket == null) {
+            return;
+        }
+        try {
+            // say goodbye
+            sendQuit();
+        } finally {
+            // and close up the connection.  We do this in a finally block to make sure the connection
+            // is shut down even if quit gets an error.
+            closeServerConnection();
+        }
+    }
+
+
+    /**
+     * Create a transport connection object and connect it to the
+     * target server.
+     *
+     * @exception MessagingException
+     */
+    protected void getConnection() throws IOException
+    {
+        // We might have been passed a socket to connect with...if not, we need to create one of the correct type.
+        if (socket == null) {
+            getConnectedSocket();
+        }
+        // if we already have a socket, get some information from it and override what we've been passed.
+        else {
+            port = socket.getPort();
+            host = socket.getInetAddress().getHostName();
+        }
+        // now set up the input/output streams.
+        inputStream = socket.getInputStream();
+        outputStream = socket.getOutputStream();
+
+        // get a reader to read the input as lines
+        in = new BufferedReader(new InputStreamReader(inputStream));
+    }
+
+    /**
+     * Close the server connection at termination.
+     */
+    public void closeServerConnection()
+    {
+        try {
+            socket.close();
+        } catch (IOException ignored) {
+        }
+
+        socket = null;
+        inputStream = null;
+        outputStream = null;
+        in = null;
+    }
+
+
+    /**
+     * Creates a connected socket
+     *
+     * @exception MessagingException
+     */
+    public void getConnectedSocket() throws IOException {
+        if (debug) {
+            debugOut("Attempting plain socket connection to server " + host + ":" + port);
+        }
+
+
+        // the socket factory can be specified via a session property.  By default, we just directly
+        // instantiate a socket without using a factor.
+        String socketFactory = session.getProperty(MAIL_NNTP_FACTORY_CLASS);
+
+        // there are several protocol properties that can be set to tune the created socket.  We need to
+        // retrieve those bits before creating the socket.
+        int timeout = SessionUtil.getIntProperty(session, MAIL_NNTP_TIMEOUT, -1);
+        InetAddress localAddress = null;
+        // see if we have a local address override.
+        String localAddrProp = session.getProperty(MAIL_NNTP_LOCALADDRESS);
+        if (localAddrProp != null) {
+            localAddress = InetAddress.getByName(localAddrProp);
+        }
+
+        // check for a local port...default is to allow socket to choose.
+        int localPort = SessionUtil.getIntProperty(session, MAIL_NNTP_LOCALPORT, 0);
+
+        socket = null;
+
+        // if there is no socket factory defined (normal), we just create a socket directly.
+        if (socketFactory == null) {
+            socket = new Socket(host, port, localAddress, localPort);
+        }
+
+        else {
+            try {
+                int socketFactoryPort = SessionUtil.getIntProperty(session, MAIL_NNTP_FACTORY_PORT, -1);
+
+                // we choose the port used by the socket based on overrides.
+                Integer portArg = new Integer(socketFactoryPort == -1 ? port : socketFactoryPort);
+
+                // use the current context loader to resolve this.
+                ClassLoader loader = Thread.currentThread().getContextClassLoader();
+                Class factoryClass = loader.loadClass(socketFactory);
+
+                // done indirectly, we need to invoke the method using reflection.
+                // This retrieves a factory instance.
+                Method getDefault = factoryClass.getMethod("getDefault", new Class[0]);
+                Object defFactory = getDefault.invoke(new Object(), new Object[0]);
+
+                // now that we have the factory, there are two different createSocket() calls we use,
+                // depending on whether we have a localAddress override.
+
+                if (localAddress != null) {
+                    // retrieve the createSocket(String, int, InetAddress, int) method.
+                    Class[] createSocketSig = new Class[] { String.class, Integer.TYPE, InetAddress.class, Integer.TYPE };
+                    Method createSocket = factoryClass.getMethod("createSocket", createSocketSig);
+
+                    Object[] createSocketArgs = new Object[] { host, portArg, localAddress, new Integer(localPort) };
+                    socket = (Socket)createSocket.invoke(defFactory, createSocketArgs);
+                }
+                else {
+                    // retrieve the createSocket(String, int) method.
+                    Class[] createSocketSig = new Class[] { String.class, Integer.TYPE };
+                    Method createSocket = factoryClass.getMethod("createSocket", createSocketSig);
+
+                    Object[] createSocketArgs = new Object[] { host, portArg };
+                    socket = (Socket)createSocket.invoke(defFactory, createSocketArgs);
+                }
+            } catch (Throwable e) {
+                // if a socket factor is specified, then we may need to fall back to a default.  This behavior
+                // is controlled by (surprise) more session properties.
+                if (SessionUtil.getBooleanProperty(session, MAIL_NNTP_FACTORY_FALLBACK, false)) {
+                    if (debug) {
+                        debugOut("First plain socket attempt faile, falling back to default factory", e);
+                    }
+                    socket = new Socket(host, port, localAddress, localPort);
+                }
+                // we have an exception.  We're going to throw an IOException, which may require unwrapping
+                // or rewrapping the exception.
+                else {
+                    // we have an exception from the reflection, so unwrap the base exception
+                    if (e instanceof InvocationTargetException) {
+                        e = ((InvocationTargetException)e).getTargetException();
+                    }
+
+                    if (debug) {
+                        debugOut("Plain socket creation failure", e);
+                    }
+
+                    // throw this as an IOException, with the original exception attached.
+                    IOException ioe = new IOException("Error connecting to " + host + ", " + port);
+                    ioe.initCause(e);
+                    throw ioe;
+                }
+            }
+        }
+
+        if (timeout >= 0) {
+            socket.setSoTimeout(timeout);
+        }
+    }
+
+    /**
+     * Get the servers welcome blob from the wire....
+     */
+    public void getWelcome() throws MessagingException {
+        NNTPReply line = getReply();
+
+        //
+        if (line.isError()) {
+            throw new MessagingException("Error connecting to news server: " + line.getMessage());
+        }
+
+        // remember we can post.
+        if (line.getCode() == NNTPReply.POSTING_ALLOWED) {
+            postingAllowed = true;
+        }
+        else {
+            postingAllowed = false;
+        }
+
+        // find out what extensions this server supports.
+        getExtensions();
+    }
+
+    /**
+     * Sends the QUIT message and receieves the response
+     */
+    public void sendQuit() throws MessagingException {
+        // there's yet another property that controls whether we should wait for a
+        // reply for a QUIT command.  If on, just send the command and get outta here.
+        if (SessionUtil.getBooleanProperty(session, MAIL_NNTP_QUITWAIT, false)) {
+            sendLine("QUIT");
+        }
+        else {
+            // handle as a real command...we're going to ignore the response.
+            sendCommand("QUIT");
+        }
+    }
+
+    /**
+     * Tell the server to switch to a named group.
+     *
+     * @param name   The name of the target group.
+     *
+     * @return The server response to the GROUP command.
+     */
+    public NNTPReply selectGroup(String name) throws MessagingException {
+        // send the GROUP command
+        return sendCommand("GROUP " + name);
+    }
+
+
+    /**
+     * Ask the server what extensions it supports.
+     *
+     * @return True if the command was accepted ok, false for any errors.
+     * @exception MessagingException
+     */
+    protected void getExtensions() throws MessagingException {
+        NNTPReply reply = sendCommand("LIST EXTENSIONS", NNTPReply.EXTENSIONS_SUPPORTED);
+
+        // we get a 202 code back.  The first line is just a greeting, and extensions are deliverd as data
+        // lines terminated with a "." line.
+        if (reply.getCode() != NNTPReply.EXTENSIONS_SUPPORTED) {
+            return;
+        }
+
+        // get a fresh extension mapping table.
+        serverExtensionArgs = new HashMap();
+        serverAuthenticationMechanisms = new HashMap();
+
+        // get the extension data lines.
+        List extensions = reply.getData();
+
+        // process all of the continuation lines
+        for (int i = 0; i < extensions.size(); i++) {
+            // go process the extention
+            processExtension((String)extensions.get(i));
+        }
+    }
+
+
+    /**
+     * Process an extension string passed back as the EHLP response.
+     *
+     * @param extension The string value of the extension (which will be of the form
+     *                  "NAME arguments").
+     */
+    protected void processExtension(String extension)
+    {
+        String extensionName = extension.toUpperCase();
+        String argument = "";
+
+        int delimiter = extension.indexOf(' ');
+        // if we have a keyword with arguments, parse them out and add to the argument map.
+        if (delimiter != -1) {
+            extensionName = extension.substring(0, delimiter).toUpperCase();
+            argument = extension.substring(delimiter + 1);
+        }
+
+        // add this to the map so it can be tested later.
+        serverExtensionArgs.put(extensionName, argument);
+
+        // process a few special ones that don't require extra parsing.
+        // AUTHINFO is entered in as a auth mechanism.
+        if (extensionName.equals("AUTHINFO")) {
+            serverAuthenticationMechanisms.put("AUTHINFO", "AUTHINFO");
+        }
+        // special case for some older servers.
+        else if (extensionName.equals("SASL")) {
+            // The security mechanisms are blank delimited tokens.
+            StringTokenizer tokenizer = new StringTokenizer(argument);
+
+            while (tokenizer.hasMoreTokens()) {
+                String mechanism = tokenizer.nextToken().toUpperCase();
+                serverAuthenticationMechanisms.put(mechanism, mechanism);
+            }
+        }
+    }
+
+
+    /**
+     * Retrieve any argument information associated with a extension
+     * reported back by the server on the EHLO command.
+     *
+     * @param name   The name of the target server extension.
+     *
+     * @return Any argument passed on a server extension.  Returns null if
+     *         the extension did not include an argument or the extension
+     *         was not supported.
+     */
+    public String extensionParameter(String name) {
+        if (serverExtensionArgs != null) {
+            return (String)serverExtensionArgs.get(name);
+        }
+        return null;
+    }
+
+    /**
+     * Tests whether the target server supports a named extension.
+     *
+     * @param name   The target extension name.
+     *
+     * @return true if the target server reported on the EHLO command that
+     *         is supports the targer server, false if the extension was not
+     *         supported.
+     */
+    public boolean supportsExtension(String name) {
+        // this only returns null if we don't have this extension
+        return extensionParameter(name) != null;
+    }
+
+
+    /**
+     * Determine if the target server supports a given authentication
+     * mechanism.
+     *
+     * @param mechanism The mechanism name.
+     *
+     * @return true if the server EHLO response indicates it supports the
+     *         mechanism, false otherwise.
+     */
+    protected boolean supportsAuthentication(String mechanism)
+    {
+       return serverAuthenticationMechanisms.get(mechanism) != null;
+    }
+
+
+    /**
+     * Sends the data in the message down the socket.  This presumes the
+     * server is in the right place and ready for getting the DATA message
+     * and the data right place in the sequence
+     */
+    public void sendPost(Message msg) throws MessagingException {
+
+        // send the POST command
+        NNTPReply line = sendCommand("POST");
+
+        if (line.getCode() != NNTPReply.SEND_ARTICLE) {
+            throw new MessagingException("Server rejected POST command: " + line);
+        }
+
+        // we've received permission to send the data, so ask the message to write itself out.
+        try {
+            // the data content has two requirements we need to meet by filtering the
+            // output stream.  Requirement 1 is to conicalize any line breaks.  All line
+            // breaks will be transformed into properly formed CRLF sequences.
+            //
+            // Requirement 2 is to perform byte-stuff for any line that begins with a "."
+            // so that data is not confused with the end-of-data marker (a "\r\n.\r\n" sequence.
+            //
+            // The MIME output stream performs those two functions on behalf of the content
+            // writer.
+            OutputStream mimeOut = new MIMEOutputStream(outputStream);
+
+            msg.writeTo(mimeOut);
+            mimeOut.flush();
+        } catch (IOException e) {
+            throw new MessagingException("I/O error posting message", e);
+        } catch (MessagingException e) {
+            throw new MessagingException("Exception posting message", e);
+        }
+
+        // now to finish, we send a CRLF sequence, followed by a ".".
+        sendLine("");
+        sendLine(".");
+
+        // use a longer time out here to give the server time to process the data.
+		line = new NNTPReply(receiveLine());
+
+        if (line.getCode() != NNTPReply.POSTED_OK) {
+            throw new MessagingException("Server rejected POST command: " + line);
+        }
+    }
+
+    /**
+     * Issue a command and retrieve the response.  If the given
+     * success indicator is received, the command is returning a longer
+     * response, terminated by a "crlf.crlf" sequence.  These lines
+     * are attached to the reply.
+     *
+     * @param command The command to issue.
+     * @param success The command reply that indicates additional data should be
+     *                retrieved.
+     *
+     * @return The command reply.
+     */
+    public NNTPReply sendCommand(String command, int success) throws MessagingException {
+        NNTPReply reply = sendCommand(command);
+        if (reply.getCode() == success) {
+            reply.retrieveData(in);
+        }
+        return reply;
+    }
+
+
+    /**
+     * Send a command to the server, returning the first response line
+     * back as a reply.
+     *
+     * @param data   The data to send.
+     *
+     * @return A reply object with the reply line.
+     * @exception MessagingException
+     */
+    public NNTPReply sendCommand(String data) throws MessagingException {
+        sendLine(data);
+        NNTPReply reply = getReply();
+        // did the server just inform us we need to authenticate?  The spec allows this
+        // response to be sent at any time, so we need to try to authenticate and then retry the command.
+        if (reply.getCode() == NNTPReply.AUTHINFO_REQUIRED || reply.getCode() == NNTPReply.AUTHINFO_SIMPLE_REQUIRED) {
+            if (debug) {
+                debugOut("Authentication required received from server.");
+            }
+            // authenticate with the server, if necessary
+            processAuthentication(reply.getCode());
+            // if we've safely authenticated, we can reissue the command and process the response.
+            sendLine(data);
+            reply = getReply();
+        }
+        return reply;
+    }
+
+    /**
+     * Send a command to the server, returning the first response line
+     * back as a reply.
+     *
+     * @param data   The data to send.
+     *
+     * @return A reply object with the reply line.
+     * @exception MessagingException
+     */
+    public NNTPReply sendAuthCommand(String data) throws MessagingException {
+        sendLine(data);
+        return getReply();
+    }
+
+
+    /**
+     * Sends a  message down the socket and terminates with the
+     * appropriate CRLF
+     */
+    public void sendLine(String data) throws MessagingException {
+        if (debug) {
+            debugOut("sending line to server >>>" + data + "<<<");
+        }
+        if (socket == null || !socket.isConnected()) {
+            throw new MessagingException("no connection");
+        }
+        try {
+            outputStream.write(data.getBytes());
+            outputStream.write(CR);
+            outputStream.write(LF);
+            outputStream.flush();
+        } catch (IOException e) {
+            throw new MessagingException(e.toString());
+        }
+    }
+
+    /**
+     * Get a reply line for an NNTP command.
+     *
+     * @return An NNTP reply object from the stream.
+     */
+    public NNTPReply getReply() throws MessagingException {
+		lastServerResponse = new NNTPReply(receiveLine());
+        return lastServerResponse;
+    }
+
+    /**
+     * Retrieve the last response received from the NNTP server.
+     *
+     * @return The raw response string (including the error code) returned
+     *         from the NNTP server.
+     */
+    public String getLastServerResponse() {
+        if (lastServerResponse == null) {
+            return "";
+        }
+        return lastServerResponse.getReply();
+    }
+
+
+
+    /**
+     * Receives one line from the server.  A line is a sequence of bytes
+     * terminated by a CRLF
+     *
+     * @return the line from the server as String
+     */
+    public String receiveLine() throws MessagingException {
+        if (socket == null || !socket.isConnected()) {
+            throw new MessagingException("no connection");
+        }
+
+        try {
+            String line = in.readLine();
+            if (line == null) {
+                throw new MessagingException("Unexpected end of stream");
+            }
+            return line;
+        } catch (IOException e) {
+            throw new MessagingException("Error reading from server", e);
+        }
+    }
+
+
+    /**
+     * Retrieve the SASL realm used for DIGEST-MD5 authentication.
+     * This will either be explicitly set, or retrieved using the
+     * mail.nntp.sasl.realm session property.
+     *
+     * @return The current realm information (which can be null).
+     */
+    public String getSASLRealm() {
+        // if the realm is null, retrieve it using the realm session property.
+        if (realm == null) {
+            realm = session.getProperty(MAIL_NNTP_SASL_REALM);
+        }
+        return realm;
+    }
+
+
+    /**
+     * Explicitly set the SASL realm used for DIGEST-MD5 authenticaiton.
+     *
+     * @param name   The new realm name.
+     */
+    public void setSASLRealm(String name) {
+        realm = name;
+    }
+
+
+    /**
+     * Authenticate with the server, if necessary (or possible).
+     */
+    protected void processAuthentication(int request) throws MessagingException
+    {
+        // we need to authenticate, but we don't have userid/password information...fail this
+        // immediately.
+        if (username == null || password == null) {
+            throw new MessagingException("Server requires user authentication");
+        }
+
+        if (request == NNTPReply.AUTHINFO_SIMPLE_REQUIRED) {
+            processAuthinfoSimple();
+        }
+        else {
+            if (!processAuthinfoSasl()) {
+                processAuthinfoUser();
+            }
+        }
+    }
+
+
+    /**
+     * Process an AUTHINFO SIMPLE command.  Not widely used, but if
+     * the server asks for it, we can respond.
+     *
+     * @exception MessagingException
+     */
+    protected void processAuthinfoSimple() throws MessagingException {
+        NNTPReply reply = sendAuthCommand("AUTHINFO SIMPLE");
+        if (reply.getCode() != NNTPReply.AUTHINFO_CONTINUE) {
+            throw new MessagingException("Error authenticating with server using AUTHINFO SIMPLE");
+        }
+        reply = sendAuthCommand(username + " " + password);
+        if (reply.getCode() != NNTPReply.AUTHINFO_ACCEPTED) {
+            throw new MessagingException("Error authenticating with server using AUTHINFO SIMPLE");
+        }
+    }
+
+
+    /**
+     * Process AUTHINFO GENERIC.  Right now, this appears not to be
+     * widely used and information on how the conversations are
+     * handled for different auth types is lacking, so right now,
+     * this just returns false to force the userid/password form
+     * to be used.
+     *
+     * @return Always returns false.
+     * @exception MessagingException
+     */
+    protected boolean processAuthinfoGeneric() throws MessagingException {
+        return false;
+    }
+
+
+    /**
+     * Process AUTHINFO SASL.
+     *
+     * @return Returns true if the server support a SASL authentication mechanism and
+     * accepted reponse challenges.
+     * @exception MessagingException
+     */
+    protected boolean processAuthinfoSasl() throws MessagingException {
+        ClientAuthenticator authenticator = null;
+
+        // now go through the progression of mechanisms we support, from the most secure to the
+        // least secure.
+
+        if (supportsAuthentication(AUTHENTICATION_DIGESTMD5)) {
+            authenticator = new DigestMD5Authenticator(host, username, password, getSASLRealm());
+        }
+        else if (supportsAuthentication(AUTHENTICATION_CRAMMD5)) {
+            authenticator = new CramMD5Authenticator(username, password);
+        }
+        else if (supportsAuthentication(AUTHENTICATION_LOGIN)) {
+            authenticator = new LoginAuthenticator(username, password);
+        }
+        else if (supportsAuthentication(AUTHENTICATION_PLAIN)) {
+            authenticator = new PlainAuthenticator(username, password);
+        }
+        else {
+            // can't find a mechanism we support in common
+            return false;
+        }
+
+        if (debug) {
+            debugOut("Authenticating for user: " + username + " using " + authenticator.getMechanismName());
+        }
+
+        // if the authenticator has some initial data, we compose a command containing the initial data.
+        if (authenticator.hasInitialResponse()) {
+            StringBuffer command = new StringBuffer();
+            // the auth command initiates the handshaking.
+            command.append("AUTHINFO SASL ");
+            // and tell the server which mechanism we're using.
+            command.append(authenticator.getMechanismName());
+            command.append(" ");
+            // and append the response data
+            command.append(new String(Base64.encode(authenticator.evaluateChallenge(null))));
+            // send the command now
+            sendLine(command.toString());
+        }
+        // we just send an auth command with the command type.
+        else {
+            StringBuffer command = new StringBuffer();
+            // the auth command initiates the handshaking.
+            command.append("AUTHINFO SASL");
+            // and tell the server which mechanism we're using.
+            command.append(authenticator.getMechanismName());
+            // send the command now
+            sendLine(command.toString());
+        }
+
+        // now process the challenge sequence.  We get a 235 response back when the server accepts the
+        // authentication, and a 334 indicates we have an additional challenge.
+        while (true) {
+            // get the next line, and if it is an error response, return now.
+            NNTPReply line = getReply();
+
+            // if we get a completion return, we've passed muster, so give an authentication response.
+            if (line.getCode() == NNTPReply.AUTHINFO_ACCEPTED || line.getCode() == NNTPReply.AUTHINFO_ACCEPTED_FINAL) {
+                if (debug) {
+                    debugOut("Successful SMTP authentication");
+                }
+                return true;
+            }
+            // we have an additional challenge to process.
+            else if (line.getCode() == NNTPReply.AUTHINFO_CHALLENGE) {
+                // Does the authenticator think it is finished?  We can't answer an additional challenge,
+                // so fail this.
+                if (authenticator.isComplete()) {
+                    if (debug) {
+                        debugOut("Extra authentication challenge " + line);
+                    }
+                    return false;
+                }
+
+                // we're passed back a challenge value, Base64 encoded.
+                byte[] challenge = Base64.decode(line.getMessage().getBytes());
+
+                // have the authenticator evaluate and send back the encoded response.
+                sendLine(new String(Base64.encode(authenticator.evaluateChallenge(challenge))));
+            }
+            // completion or challenge are the only responses we know how to handle.  Anything else must
+            // be a failure.
+            else {
+                if (debug) {
+                    debugOut("Authentication failure " + line);
+                }
+                return false;
+            }
+        }
+    }
+
+
+    /**
+     * Process an AUTHINFO USER command.  Most common form of NNTP authentication.
+     *
+     * @exception MessagingException
+     */
+    protected void processAuthinfoUser() throws MessagingException {
+        NNTPReply reply = sendAuthCommand("AUTHINFO USER " + username);
+        // accepted without a password (uncommon, but allowed), we're done
+        if (reply.getCode() == NNTPReply.AUTHINFO_ACCEPTED) {
+            return;
+        }
+        // the only other non-error response is continue.
+        if (reply.getCode() != NNTPReply.AUTHINFO_CONTINUE) {
+            throw new MessagingException("Error authenticating with server using AUTHINFO USER: " + reply);
+        }
+        // now send the password.  We expect an accepted response.
+        reply = sendAuthCommand("AUTHINFO PASS " + password);
+        if (reply.getCode() != NNTPReply.AUTHINFO_ACCEPTED) {
+            throw new MessagingException("Error authenticating with server using AUTHINFO SIMPLE");
+        }
+    }
+
+    /**
+     * Internal debug output routine.
+     *
+     * @param value  The string value to output.
+     */
+    protected void debugOut(String message) {
+        debugStream.println("NNTPTransport DEBUG: " + message);
+    }
+
+    /**
+     * Internal debugging routine for reporting exceptions.
+     *
+     * @param message A message associated with the exception context.
+     * @param e       The received exception.
+     */
+    protected void debugOut(String message, Throwable e) {
+        debugOut("Received exception -> " + message);
+        debugOut("Exception message -> " + e.getMessage());
+        e.printStackTrace(debugStream);
+    }
+
+
+    /**
+     * Indicate whether posting is allowed for a given server.
+     *
+     * @return True if the server allows posting, false if the server is
+     *         read-only.
+     */
+    public boolean isPostingAllowed() {
+        return postingAllowed;
+    }
+}
+

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

Propchange: geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/nntp/NNTPConnection.java
------------------------------------------------------------------------------
    svn:keywords = Rev Date

Added: geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/nntp/NNTPReply.java
URL: http://svn.apache.org/viewcvs/geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/nntp/NNTPReply.java?rev=383095&view=auto
==============================================================================
--- geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/nntp/NNTPReply.java (added)
+++ geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/nntp/NNTPReply.java Sat Mar  4 03:55:42 2006
@@ -0,0 +1,197 @@
+/**
+ *
+ * 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 org.apache.geronimo.javamail.transport.nntp;
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.BufferedReader;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.mail.MessagingException;
+
+/**
+ * Util class to represent a reply from a NNTP server
+ *
+ * @version $Rev$ $Date$
+ */
+class NNTPReply {
+    // general server responses
+    public static final int POSTING_ALLOWED = 200;
+    public static final int NO_POSTING_ALLOWED = 201;
+    public static final int EXTENSIONS_SUPPORTED = 202;
+
+    public static final int SERVICE_DISCONTINUED = 400;
+    public static final int COMMAND_NOT_RECOGNIZED = 500;
+    public static final int COMMAND_SYNTAX_ERROR = 501;
+    public static final int PERMISSION_DENIED = 502;
+    public static final int PROGRAM_FAULT = 503;
+
+    // article responses
+    public static final int ARTICLE_FOLLOWS   = 220;
+    public static final int HEAD_FOLLOWS      = 221;
+    public static final int BODY_FOLLOWS      = 222;
+    public static final int REQUEST_TEXT_SEPARATELY = 223;
+    public static final int OVERVIEW_FOLLOWS  = 224;
+    public static final int NEW_ARTICLES_FOLLOWS  = 230;
+    public static final int NEW_GROUPS_FOLLOWS    = 231;
+    public static final int ARTICLE_TRANSFERRED   = 235;
+
+    public static final int NO_NEWSGROUP_SELECTED = 412;
+    public static final int NO_ARTICLE_SELECTED = 420;
+    public static final int NO_ARTICLE_NUMBER   = 423;
+    public static final int NO_ARTICLE_FOUND    = 430;
+
+    // group responses
+    public static final int GROUP_SELECTED = 211;
+    public static final int NO_SUCH_NEWSGROUP = 411;
+
+    // post responses
+    public static final int POSTED_OK = 240;
+    public static final int SEND_ARTICLE = 340;
+    public static final int POSTING_NOT_ALLOWED = 440;
+    public static final int POSTING_FAILED = 441;
+
+    // quit responses
+    public static final int CLOSING_CONNECTION = 205;
+
+    // authentication responses
+    public static final int AUTHINFO_ACCEPTED        = 250;
+    public static final int AUTHINFO_ACCEPTED_FINAL  = 251;
+    public static final int AUTHINFO_CONTINUE        = 350;
+    public static final int AUTHINFO_CHALLENGE       = 350;
+    public static final int AUTHINFO_SIMPLE_REJECTED = 402;
+    public static final int AUTHENTICATION_ACCEPTED = 281;
+    public static final int MORE_AUTHENTICATION_REQUIRED = 381;
+    public static final int AUTHINFO_REQUIRED = 480;
+    public static final int AUTHINFO_SIMPLE_REQUIRED = 450;
+    public static final int AUTHENTICATION_REJECTED = 482;
+
+    // The original reply string
+    private final String reply;
+    // returned message code
+    private final int code;
+    // the returned message text
+    private final String message;
+    // data associated with a long response command.
+    private ArrayList data;
+
+    NNTPReply(String s) throws MessagingException {
+        // save the reply
+        reply = s;
+
+        // In a normal response, the first 3 must be the return code.  However,
+        // the response back from a QUIT command is frequently a null string.  Therefore, if the result is
+        // too short, just default the code to -1 and use the entire text for the message.
+        if (s == null || s.length() < 3) {
+            code = -1;
+            message = s;
+            return;
+        }
+
+        try {
+            code = Integer.parseInt(s.substring(0, 3));
+
+            // message should be separated by a space OR a continuation character if this is a
+            // multi-line response.
+            if (s.length() > 4) {
+                message = s.substring(4);
+            } else {
+                message = "";
+            }
+        } catch (NumberFormatException e) {
+            throw new MessagingException("error in parsing reply code", e);
+        }
+    }
+
+    /**
+     * Retrieve data associated with a multi-line reponse from
+     * a server stream.
+     *
+     * @param in     The reader that's the source of the additional lines.
+     *
+     * @exception IOException
+     */
+    public void retrieveData(BufferedReader in) throws MessagingException {
+        try {
+            data = new ArrayList();
+
+            String line = in.readLine();
+
+            // read until the end of file or until we see the end of data marker.
+            while (line != null && !line.equals(".")) {
+                // just add the line to the list
+                // TODO:  figure out if we need to handle "stuffing".
+                data.add(line);
+                line = in.readLine();
+            }
+        } catch (IOException e) {
+            throw new MessagingException("Error reading message reply", e);
+        }
+    }
+
+    /**
+     * Retrieve the long-command data from this response.
+     *
+     * @return The data list.  Returns null if there is no associated data.
+     */
+    public List getData() {
+        return data;
+    }
+
+
+    /**
+     * Return the code value associated with the reply.
+     *
+     * @return The integer code associated with the reply.
+     */
+    public int getCode() {
+        return this.code;
+    }
+
+    /**
+     * Get the message text associated with the reply.
+     *
+     * @return The string value of the message from the reply.
+     */
+    public String getMessage() {
+        return this.message;
+    }
+
+    /**
+     * Retrieve the raw reply string for the reponse.
+     *
+     * @return The original reply string from the server.
+     */
+    public String getReply() {
+        return reply;
+    }
+
+
+    /**
+     * Indicates if reply is an error condition
+     */
+    boolean isError() {
+        // error codes are all above 400
+        return code >= 400;
+    }
+
+    public String toString() {
+        return "CODE = " + getCode() + " : MSG = " + getMessage();
+    }
+}

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

Propchange: geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/nntp/NNTPReply.java
------------------------------------------------------------------------------
    svn:keywords = Rev Date

Added: geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/nntp/NNTPTransport.java
URL: http://svn.apache.org/viewcvs/geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/nntp/NNTPTransport.java?rev=383095&view=auto
==============================================================================
--- geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/nntp/NNTPTransport.java (added)
+++ geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/nntp/NNTPTransport.java Sat Mar  4 03:55:42 2006
@@ -0,0 +1,279 @@
+/**
+ *
+ * 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 org.apache.geronimo.javamail.transport.nntp;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintStream;
+import java.io.Reader;
+import java.io.UnsupportedEncodingException;
+import java.io.Writer;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.StringTokenizer;
+
+import javax.mail.Address;
+import javax.mail.AuthenticationFailedException;
+import javax.mail.Message;
+import javax.mail.MessagingException;
+import javax.mail.Session;
+import javax.mail.Transport;
+import javax.mail.URLName;
+import javax.mail.event.TransportEvent;
+import javax.mail.internet.NewsAddress;
+import javax.mail.internet.MimeMessage;
+
+import org.apache.geronimo.mail.util.SessionUtil;
+
+/**
+ * Simple implementation of NNTP transport.  Just does plain RFC977-ish
+ * delivery.
+ * <p/>
+ * There is no way to indicate failure for a given recipient (it's possible to have a
+ * recipient address rejected).  The sun impl throws exceptions even if others are successful,
+ * but maybe we do a different way...
+ * <p/>
+ *
+ * @version $Rev$ $Date$
+ */
+public class NNTPTransport extends Transport {
+
+    /**
+     * property keys for protocol properties.  The actual property name will
+     * be appended with "mail." + protocol + ".", where the protocol is either
+     * "smtp" or "smtps".
+     */
+    protected static final String MAIL_NNTP_AUTH = "auth";
+    protected static final String MAIL_NNTP_PORT = "port";
+    protected static final String MAIL_NNTP_TIMEOUT = "timeout";
+
+    protected static final int DEFAULT_NNTP_PORT = 119;
+
+    // our active connection object (shared code with the NNTPStore).
+    protected NNTPConnection connection;
+
+    // our session provided debug output stream.
+    protected PrintStream debugStream;
+
+
+    /**
+     * Normal constructor for an NNTPTransport() object.  This
+     * constructor is used to build a transport instance for the
+     * "smtp" protocol.
+     *
+     * @param session The attached session.
+     * @param name    An optional URLName object containing target information.
+     */
+    public NNTPTransport(Session session, URLName name) {
+        super(session, name);
+
+        // get our debug output.
+        debugStream = session.getDebugOut();
+    }
+
+
+    /**
+     * Do the protocol connection for an NNTP transport.  This
+     * handles server authentication, if possible.  Returns false if
+     * unable to connect to the server.
+     *
+     * @param host     The target host name.
+     * @param port     The server port number.
+     * @param user     The authentication user (if any).
+     * @param password The server password.  Might not be sent directly if more
+     *                 sophisticated authentication is used.
+     *
+     * @return true if we were able to connect to the server properly, false
+     *         for any failures.
+     * @exception MessagingException
+     */
+    protected boolean protocolConnect(String host, int port, String username, String password) throws MessagingException
+    {
+        if (debug) {
+            debugOut("Connecting to server " + host + ":" + port + " for user " + username);
+        }
+
+        // first check to see if we need to authenticate.  If we need this, then we must have a username and
+        // password specified.  Failing this may result in a user prompt to collect the information.
+        boolean mustAuthenticate = SessionUtil.getBooleanProperty(session, MAIL_NNTP_AUTH, false);
+
+        // if we need to authenticate, and we don't have both a userid and password, then we fail this
+        // immediately.  The Service.connect() method will try to obtain the user information and retry the
+        // connection one time.
+        if (mustAuthenticate && (username == null || password == null)) {
+            return false;
+        }
+
+
+        // if the port is defaulted, then see if we have something configured in the session.
+        // if not configured, we just use the default default.
+        if (port == -1) {
+            // check for a property and fall back on the default if it's not set.
+            port = SessionUtil.getIntProperty(session, MAIL_NNTP_PORT, DEFAULT_NNTP_PORT);
+        }
+
+
+        // create socket and connect to server.
+        connection = new NNTPConnection(session, host, port, username, password, debug);
+        connection.connect();
+
+        // we're going to return success here, but in truth, the server may end up asking for our
+        // bonafides at any time, and we'll be expected to authenticate then.
+        return true;
+    }
+
+    /**
+     * Send a message to multiple addressees.
+     *
+     * @param message   The message we're sending.
+     * @param addresses An array of addresses to send to.
+     *
+     * @exception MessagingException
+     */
+    public void sendMessage(Message message, Address[] addresses) throws MessagingException {
+        if (!isConnected()) {
+            throw new IllegalStateException("Not connected");
+        }
+
+        if (!connection.isPostingAllowed()) {
+            throw new MessagingException("Posting disabled for host server");
+        }
+        // don't bother me w/ null messages or no addreses
+        if (message == null) {
+            throw new MessagingException("Null message");
+        }
+
+        // NNTP only handles instances of MimeMessage, not the more general message case.
+        if (!(message instanceof MimeMessage)) {
+            throw new MessagingException("NNTP can only send MimeMessages");
+        }
+
+        // we must have a message list.
+        if (addresses == null || addresses.length == 0) {
+            throw new MessagingException("Null or empty address array");
+        }
+
+        boolean haveGroup = false;
+
+        // enforce the requirement that all of the targets are NewsAddress instances.
+        for (int i = 0; i < addresses.length; i++) {
+            if (!(addresses[i] instanceof NewsAddress)) {
+                System.out.println("Illegal address is of class " + addresses[i].getClass());
+                throw new MessagingException("Illegal NewsAddress " + addresses[i]);
+            }
+        }
+
+
+        // event notifcation requires we send lists of successes and failures broken down by category.
+        // The categories are:
+        //
+        // 1) addresses successfully processed.
+        // 2) addresses deemed valid, but had a processing failure that prevented sending.
+        // 3) addressed deemed invalid (basically all other processing failures).
+        ArrayList sentAddresses = new ArrayList();
+        ArrayList unsentAddresses = new ArrayList();
+        ArrayList invalidAddresses = new ArrayList();
+
+        boolean sendFailure = false;
+
+        // now try to post this message to the different news groups.
+        for (int i = 0; i < addresses.length; i++) {
+            try {
+                // select the target news group
+                NNTPReply reply = connection.selectGroup(((NewsAddress)addresses[i]).getNewsgroup());
+
+                if (reply.getCode() != NNTPReply.GROUP_SELECTED) {
+                    invalidAddresses.add(addresses[i]);
+                    sendFailure = true;
+                }
+                else {
+                    // send data
+                    connection.sendPost(message);
+                    sentAddresses.add(addresses[i]);
+                }
+            } catch (MessagingException e) {
+                unsentAddresses.add(addresses[i]);
+                sendFailure = true;
+            }
+        }
+
+        // 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]);
+
+        if (sendFailure) {
+            // did we deliver anything at all?
+            if (sent.length == 0) {
+                // notify of the error.
+                notifyTransportListeners(TransportEvent.MESSAGE_NOT_DELIVERED, sent, unsent, invalid, message);
+            }
+            else {
+                // notify that we delivered at least part of this
+                notifyTransportListeners(TransportEvent.MESSAGE_PARTIALLY_DELIVERED, sent, unsent, invalid, message);
+            }
+
+            throw new MessagingException("Error posting NNTP message");
+        }
+
+        // notify our listeners of successful delivery.
+        notifyTransportListeners(TransportEvent.MESSAGE_DELIVERED, sent, unsent, invalid, message);
+    }
+
+
+    /**
+     * Close the connection.  On completion, we'll be disconnected from
+     * the server and unable to send more data.
+     *
+     * @exception MessagingException
+     */
+    public void close() throws MessagingException {
+        connection.close();
+        connection = null;
+    }
+
+    /**
+     * Internal debug output routine.
+     *
+     * @param value  The string value to output.
+     */
+    protected void debugOut(String message) {
+        debugStream.println("NNTPTransport DEBUG: " + message);
+    }
+
+    /**
+     * Internal debugging routine for reporting exceptions.
+     *
+     * @param message A message associated with the exception context.
+     * @param e       The received exception.
+     */
+    protected void debugOut(String message, Throwable e) {
+        debugOut("Received exception -> " + message);
+        debugOut("Exception message -> " + e.getMessage());
+        e.printStackTrace(debugStream);
+    }
+}

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

Propchange: geronimo/trunk/modules/javamail-transport/src/java/org/apache/geronimo/javamail/transport/nntp/NNTPTransport.java
------------------------------------------------------------------------------
    svn:keywords = Rev Date

Modified: geronimo/trunk/modules/javamail-transport/src/resources/META-INF/javamail.default.address.map
URL: http://svn.apache.org/viewcvs/geronimo/trunk/modules/javamail-transport/src/resources/META-INF/javamail.default.address.map?rev=383095&r1=383094&r2=383095&view=diff
==============================================================================
--- geronimo/trunk/modules/javamail-transport/src/resources/META-INF/javamail.default.address.map (original)
+++ geronimo/trunk/modules/javamail-transport/src/resources/META-INF/javamail.default.address.map Sat Mar  4 03:55:42 2006
@@ -1,9 +1,30 @@
+##
+##
+##   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.
+##
+
+##
+## $Rev$ $Date$
+##
+
+#
 # This file configures the default behaviour of JavaMail. DO NOT EDIT.
 # Create a new file /META-INF/javamail.address.map and put
 # the same format lines in there.
 #
 # Note that you can't override these defaults, merely add to them.
 #
-# $Rev$ $Date$
-#
 rfc822=smtp
+news=nntp

Propchange: geronimo/trunk/modules/javamail-transport/src/resources/META-INF/javamail.default.address.map
------------------------------------------------------------------------------
--- svn:keywords (original)
+++ svn:keywords Sat Mar  4 03:55:42 2006
@@ -1 +1 @@
-author date id rev
+Rev Date

Modified: geronimo/trunk/modules/javamail-transport/src/resources/META-INF/javamail.default.providers
URL: http://svn.apache.org/viewcvs/geronimo/trunk/modules/javamail-transport/src/resources/META-INF/javamail.default.providers?rev=383095&r1=383094&r2=383095&view=diff
==============================================================================
--- geronimo/trunk/modules/javamail-transport/src/resources/META-INF/javamail.default.providers (original)
+++ geronimo/trunk/modules/javamail-transport/src/resources/META-INF/javamail.default.providers Sat Mar  4 03:55:42 2006
@@ -1,11 +1,33 @@
+##
+##
+##   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.
+##
+
+##
+## $Rev$ $Date$
+##
+
+#
 # This file configures the default behaviour of JavaMail. DO NOT EDIT.
 # Create a new file /META-INF/javamail.providers and put
 # the same format lines in there.
 #
 # Note that you can't override these defaults, merely add to them.
 #
-# $Rev$ $Date$
-#
 protocol=smtp; type=transport; class=org.apache.geronimo.javamail.transport.smtp.SMTPTransport; vendor=Apache Software Foundation; version=1.0
 protocol=smtps; type=transport; class=org.apache.geronimo.javamail.transport.smtp.SMTPTSransport; vendor=Apache Software Foundation; version=1.0
-protocol=pop3; type=store; class=org.apache.geronimo.javamail.store.pop3.POP3Store; vendor=Apache Software Foundation; version=1.0
\ No newline at end of file
+protocol=nntp; type=transport; class=org.apache.geronimo.javamail.transport.nntp.NNTPTransport; vendor=Apache Software Foundation; version=1.0
+protocol=pop3; type=store; class=org.apache.geronimo.javamail.store.pop3.POP3Store; vendor=Apache Software Foundation; version=1.0
+

Propchange: geronimo/trunk/modules/javamail-transport/src/resources/META-INF/javamail.default.providers
------------------------------------------------------------------------------
--- svn:keywords (original)
+++ svn:keywords Sat Mar  4 03:55:42 2006
@@ -1 +1 @@
-author date id rev
+Rev Date



Mime
View raw message