You are viewing a plain text version of this content. The canonical link for it is here.
Posted to scm@geronimo.apache.org by jl...@apache.org on 2020/10/07 15:58:41 UTC
svn commit: r1882306 [12/17] - in
/geronimo/javamail/trunk/geronimo-javamail_1.6: ./
geronimo-javamail_1.6_mail/ geronimo-javamail_1.6_mail/src/
geronimo-javamail_1.6_mail/src/site/ geronimo-javamail_1.6_provider/
geronimo-javamail_1.6_provider/src/ ge...
Added: geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/POP3Store.java
URL: http://svn.apache.org/viewvc/geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/POP3Store.java?rev=1882306&view=auto
==============================================================================
--- geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/POP3Store.java (added)
+++ geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/POP3Store.java Wed Oct 7 15:58:39 2020
@@ -0,0 +1,359 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.geronimo.javamail.store.pop3;
+
+import java.io.PrintStream;
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.mail.AuthenticationFailedException;
+import javax.mail.Folder;
+import javax.mail.MessagingException;
+import javax.mail.Session;
+import javax.mail.Store;
+import javax.mail.URLName;
+
+import org.apache.geronimo.javamail.store.pop3.connection.POP3Connection;
+import org.apache.geronimo.javamail.store.pop3.connection.POP3ConnectionPool;
+import org.apache.geronimo.javamail.util.ProtocolProperties;
+
+/**
+ * POP3 implementation of javax.mail.Store POP protocol spec is implemented in
+ * org.apache.geronimo.javamail.store.pop3.POP3Connection
+ *
+ * @version $Rev$ $Date$
+ */
+
+public class POP3Store extends Store {
+ protected static final int DEFAULT_POP3_PORT = 110;
+ protected static final int DEFAULT_POP3_SSL_PORT = 995;
+
+
+ // our accessor for protocol properties and the holder of
+ // protocol-specific information
+ protected ProtocolProperties props;
+ // our connection object
+ protected POP3ConnectionPool connectionPool;
+ // our session provided debug output stream.
+ protected PrintStream debugStream;
+ // the debug flag
+ protected boolean debug;
+ // the root folder
+ protected POP3RootFolder root;
+ // until we're connected, we're closed
+ boolean closedForBusiness = true;
+ protected LinkedList openFolders = new LinkedList();
+
+
+ public POP3Store(Session session, URLName name) {
+ this(session, name, "pop3", DEFAULT_POP3_PORT, false);
+ }
+
+ /**
+ * Common constructor used by the POP3Store and POP3SSLStore classes
+ * to do common initialization of defaults.
+ *
+ * @param session
+ * The host session instance.
+ * @param name
+ * The URLName of the target.
+ * @param protocol
+ * The protocol type ("pop3"). This helps us in
+ * retrieving protocol-specific session properties.
+ * @param defaultPort
+ * The default port used by this protocol. For pop3, this will
+ * be 110. The default for pop3 with ssl is 995.
+ * @param sslConnection
+ * Indicates whether an SSL connection should be used to initial
+ * contact the server. This is different from the STARTTLS
+ * support, which switches the connection to SSL after the
+ * initial startup.
+ */
+ protected POP3Store(Session session, URLName name, String protocol, int defaultPort, boolean sslConnection) {
+ super(session, name);
+
+ // create the protocol property holder. This gives an abstraction over the different
+ // flavors of the protocol.
+ props = new ProtocolProperties(session, protocol, sslConnection, defaultPort);
+
+ // get our debug settings
+ debugStream = session.getDebugOut();
+ debug = session.getDebug();
+ // the connection pool manages connections for the stores, folder, and message usage.
+ connectionPool = new POP3ConnectionPool(this, props);
+ }
+
+
+ /**
+ * Return a Folder object that represents the root of the namespace for the current user.
+ *
+ * Note that in some store configurations (such as IMAP4) the root folder might
+ * not be the INBOX folder.
+ *
+ * @return the root Folder
+ * @throws MessagingException if there was a problem accessing the store
+ */
+ public Folder getDefaultFolder() throws MessagingException {
+ checkConnectionStatus();
+ // if no root yet, create a root folder instance.
+ if (root == null) {
+ return new POP3RootFolder(this);
+ }
+ return root;
+ }
+
+ /**
+ * Return the Folder corresponding to the given name.
+ * The folder might 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 Folder getFolder(String name) throws MessagingException {
+ return getDefaultFolder().getFolder(name);
+ }
+
+
+ /**
+ * 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 url
+ *
+ * @return the corresponding folder
+ * @throws MessagingException
+ * if there was a problem accessing the store
+ */
+ public Folder getFolder(URLName url) throws MessagingException {
+ return getDefaultFolder().getFolder(url.getFile());
+ }
+
+
+ /**
+ * @see javax.mail.Service#protocolConnect(String, int,
+ * String, String)
+ */
+ protected synchronized boolean protocolConnect(String host, int port, String username, String password) throws MessagingException {
+
+ if (debug) {
+ debugOut("Connecting to server " + host + ":" + port + " for user " + username);
+ }
+
+ // the connection pool handles all of the details here.
+ if (connectionPool.protocolConnect(host, port, username, password))
+ {
+ // the store is now open
+ closedForBusiness = false;
+ return true;
+ }
+ return false;
+ }
+
+
+ /**
+ * Get a connection for the store.
+ *
+ * @return The request connection object.
+ * @exception MessagingException
+ */
+ protected POP3Connection getConnection() throws MessagingException {
+ return connectionPool.getConnection();
+ }
+
+ /**
+ * Return a connection back to the connection pool after
+ * it has been used for a request.
+ *
+ * @param connection The return connection.
+ *
+ * @exception MessagingException
+ */
+ protected void releaseConnection(POP3Connection connection) throws MessagingException {
+ connectionPool.releaseConnection(connection);
+ }
+
+ /**
+ * Get a connection object for a folder to use.
+ *
+ * @param folder The requesting folder (always the inbox for POP3).
+ *
+ * @return An active POP3Connection.
+ * @exception MessagingException
+ */
+ synchronized POP3Connection getFolderConnection(POP3Folder folder) throws MessagingException {
+ POP3Connection connection = connectionPool.getConnection();
+ openFolders.add(folder);
+ return connection;
+ }
+
+ /**
+ * Release a connection object after a folder is
+ * finished with a request.
+ *
+ * @param folder The requesting folder.
+ * @param connection
+ *
+ * @exception MessagingException
+ */
+ synchronized void releaseFolderConnection(POP3Folder folder, POP3Connection connection) throws MessagingException {
+ openFolders.remove(folder);
+ // return this back to the pool
+ connectionPool.releaseConnection(connection);
+ }
+
+ /**
+ * Close all open folders. We have a small problem here with a race condition. There's no safe, single
+ * synchronization point for us to block creation of new folders while we're closing. So we make a copy of
+ * the folders list, close all of those folders, and keep repeating until we're done.
+ */
+ protected void closeOpenFolders() {
+ // we're no longer accepting additional opens. Any folders that open after this point will get an
+ // exception trying to get a connection.
+ closedForBusiness = true;
+
+ while (true) {
+ List folders = null;
+
+ // grab our lock, copy the open folders reference, and null this out. Once we see a null
+ // open folders ref, we're done closing.
+ synchronized(connectionPool) {
+ folders = openFolders;
+ openFolders = new LinkedList();
+ }
+
+ // null folder, we're done
+ if (folders.isEmpty()) {
+ return;
+ }
+ // now close each of the open folders.
+ for (int i = 0; i < folders.size(); i++) {
+ POP3Folder folder = (POP3Folder)folders.get(i);
+ try {
+ folder.close(false);
+ } catch (MessagingException e) {
+ }
+ }
+ }
+ }
+
+
+ /**
+ * @see javax.mail.Service#isConnected()
+ */
+ public boolean isConnected() {
+ // the connect() method of the super class checks here first. If the connected flag
+ // is off, then it's too early for use to try to get a connection and verify we still
+ // have a live one.
+ if (!super.isConnected()) {
+ return false;
+ }
+ try {
+ POP3Connection connection = getConnection();
+ // a null connection likely means we had a failure establishing a
+ // new connection to the POP3 server.
+ if (connection == null) {
+ return false;
+ }
+ try {
+ // make sure the server is really there
+ connection.pingServer();
+ return true;
+ }
+ finally {
+ // return the connection to the pool when finished
+ if (connection != null) {
+ releaseConnection(connection);
+ }
+ }
+ } catch (MessagingException e) {
+ }
+ return false;
+ }
+
+ /**
+ * Close the store, and any open folders associated with the
+ * store.
+ *
+ * @exception MessagingException
+ */
+ public synchronized void close() throws MessagingException{
+ // if already closed, nothing to do.
+ if (closedForBusiness) {
+ return;
+ }
+
+ // close the folders first, then shut down the Store.
+ closeOpenFolders();
+
+ connectionPool.close();
+ connectionPool = null;
+
+ // make sure we do the superclass close operation first so
+ // notification events get broadcast properly.
+ super.close();
+ }
+
+ /**
+ * Check the status of our connection.
+ *
+ * @exception MessagingException
+ */
+ private void checkConnectionStatus() throws MessagingException {
+ if (!this.isConnected()) {
+ throw new MessagingException("Not connected ");
+ }
+ }
+
+ /**
+ * Internal debug output routine.
+ *
+ * @param value The string value to output.
+ */
+ void debugOut(String message) {
+ debugStream.println("POP3Store DEBUG: " + message);
+ }
+
+ /**
+ * Internal debugging routine for reporting exceptions.
+ *
+ * @param message A message associated with the exception context.
+ * @param e The received exception.
+ */
+ void debugOut(String message, Throwable e) {
+ debugOut("Received exception -> " + message);
+ debugOut("Exception message -> " + e.getMessage());
+ e.printStackTrace(debugStream);
+ }
+
+ /**
+ * Finalizer to perform IMAPStore() cleanup when
+ * no longer in use.
+ *
+ * @exception Throwable
+ */
+ protected void finalize() throws Throwable {
+ super.finalize();
+ close();
+ }
+}
Added: geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/connection/POP3Connection.java
URL: http://svn.apache.org/viewvc/geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/connection/POP3Connection.java?rev=1882306&view=auto
==============================================================================
--- geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/connection/POP3Connection.java (added)
+++ geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/connection/POP3Connection.java Wed Oct 7 15:58:39 2020
@@ -0,0 +1,693 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.geronimo.javamail.store.pop3.connection;
+
+import java.io.*;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.StringTokenizer;
+
+import javax.mail.MessagingException;
+import javax.mail.internet.InternetHeaders;
+
+import org.apache.geronimo.javamail.authentication.AuthenticatorFactory;
+import org.apache.geronimo.javamail.authentication.ClientAuthenticator;
+import org.apache.geronimo.javamail.store.imap.connection.IMAPResponseStream;
+import org.apache.geronimo.javamail.store.pop3.POP3Constants;
+import org.apache.geronimo.javamail.util.CommandFailedException;
+import org.apache.geronimo.javamail.util.InvalidCommandException;
+import org.apache.geronimo.javamail.util.MIMEInputReader;
+import org.apache.geronimo.javamail.util.MailConnection;
+import org.apache.geronimo.javamail.util.ProtocolProperties;
+import org.apache.geronimo.mail.util.Base64;
+import org.apache.geronimo.mail.util.Hex;
+
+/**
+ * Simple implementation of POP3 transport.
+ *
+ * @version $Rev$ $Date$
+ */
+public class POP3Connection extends MailConnection implements POP3Constants {
+
+ static final protected String MAIL_APOP_ENABLED = "apop.enable";
+ static final protected String MAIL_AUTH_ENABLED = "auth.enable";
+ static final protected String MAIL_RESET_QUIT = "rsetbeforequit";
+ static final protected String MAIL_DISABLE_TOP = "disabletop";
+ //static final protected String MAIL_FORGET_TOP = "forgettopheaders"; //TODO forgettopheaders
+
+ // the initial greeting string, which might be required for APOP authentication.
+ protected String greeting;
+ // is use of the AUTH command enabled
+ protected boolean authEnabled;
+ // is use of APOP command enabled
+ protected boolean apopEnabled;
+ // input reader wrapped around the socket input stream
+ protected BufferedReader reader;
+ // output writer wrapped around the socket output stream.
+ protected PrintWriter writer;
+ // this connection was closed unexpectedly
+ protected boolean closed;
+ // indicates whether this connection is currently logged in. Once
+ // we send a QUIT, we're finished.
+ protected boolean loggedIn;
+ // indicates whether we need to avoid using the TOP command
+ // when retrieving headers
+ protected boolean topDisabled = false;
+ // is TLS enabled on our part?
+ protected boolean useTLS = false;
+ // is TLS required on our part?
+ protected boolean requireTLS = false;
+
+ /**
+ * Normal constructor for an POP3Connection() object.
+ *
+ * @param props The protocol properties abstraction containing our
+ * property modifiers.
+ */
+ public POP3Connection(ProtocolProperties props) {
+ super(props);
+
+ // get our login properties flags
+ authEnabled = props.getBooleanProperty(MAIL_AUTH_ENABLED, false);
+ apopEnabled = props.getBooleanProperty(MAIL_APOP_ENABLED, false);
+ topDisabled = props.getBooleanProperty(MAIL_DISABLE_TOP, false);
+ // and also check for TLS enablement.
+ useTLS = props.getBooleanProperty(MAIL_STARTTLS_ENABLE, false);
+ // and also check if TLS is required.
+ requireTLS = props.getBooleanProperty(MAIL_STARTTLS_REQUIRED, false);
+
+ }
+
+
+ /**
+ * Connect to the server and do the initial handshaking.
+ *
+ * @exception MessagingException
+ */
+ public boolean protocolConnect(String host, int port, String authid, String realm, String username, String password) throws MessagingException {
+ this.serverHost = host;
+ this.serverPort = port;
+ this.realm = realm;
+ this.authid = authid;
+ this.username = username;
+ this.password = password;
+
+ try {
+ // create socket and connect to server.
+ getConnection();
+ // consume the welcome line
+ getWelcome();
+
+ // if we're not already using an SSL connection, and we have permission to issue STARTTLS or its even required
+ // try to setup a SSL connection
+ if (!sslConnection && (useTLS || requireTLS)) {
+
+ // tell the server of our intention to start a TLS session
+ POP3Response starttlsResponse = null;
+ try {
+ starttlsResponse = sendCommand("STLS");
+ } catch (CommandFailedException e) {
+
+ }
+
+ //if the server does not support TLS check if its required.
+ //If true then throw an error, if not establish a non SSL connection
+ if(requireTLS && (starttlsResponse == null || starttlsResponse.isError())) {
+ throw new MessagingException("Server doesn't support required transport level security");
+ } else if(starttlsResponse != null && starttlsResponse.getStatus() == POP3Response.OK) {
+ // The connection is then handled by the superclass level.
+ getConnectedTLSSocket();
+ } else {
+ if (debug) {
+ debugOut("STARTTLS is enabled but not required and server does not support it. So we establish a connection without transport level security");
+ }
+ }
+ }
+
+ getConnection();
+
+ // go login with the server
+ if (login())
+ {
+ loggedIn = true;
+ return true;
+ }
+ return false;
+ } catch (IOException e) {
+ if (debug) {
+ debugOut("I/O exception establishing connection", e);
+ }
+ throw new MessagingException("Connection error", e);
+ }
+ }
+
+
+ /**
+ * Create a transport connection object and connect it to the
+ * target server.
+ *
+ * @exception MessagingException
+ */
+ protected void getConnection() throws MessagingException
+ {
+ try {
+ // do all of the non-protocol specific set up. This will get our socket established
+ // and ready use.
+ super.getConnection();
+ } catch (IOException e) {
+ throw new MessagingException("Unable to obtain a connection to the POP3 server", e);
+ }
+
+ // The POP3 protocol is inherently a string-based protocol, so we get
+ // string readers/writers for the connection streams. Note that we explicitly
+ // set the encoding to ensure that an inappropriate native encoding is not picked up.
+ try {
+ reader = new BufferedReader(new InputStreamReader(inputStream, "ISO8859-1"));
+ writer = new PrintWriter(new OutputStreamWriter(new BufferedOutputStream(outputStream), "ISO8859-1"));
+ } catch (UnsupportedEncodingException e) {
+ }
+ }
+
+ protected void getWelcome() throws IOException {
+ // just read the line and consume it. If debug is
+ // enabled, there I/O stream will be traced
+ greeting = reader.readLine();
+ }
+
+ public String toString() {
+ return "POP3Connection host: " + serverHost + " port: " + serverPort;
+ }
+
+
+ /**
+ * 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
+ logout();
+ } 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();
+ // get rid of our response processor too.
+ reader = null;
+ writer = null;
+ }
+ }
+
+
+ /**
+ * Tag this connection as having been closed by the
+ * server. This will not be returned to the
+ * connection pool.
+ */
+ public void setClosed() {
+ closed = true;
+ }
+
+ /**
+ * Test if the connection has been forcibly closed.
+ *
+ * @return True if the server disconnected the connection.
+ */
+ public boolean isClosed() {
+ return closed;
+ }
+
+ protected POP3Response sendCommand(String cmd) throws MessagingException {
+ return sendCommand(cmd, false);
+ }
+
+ protected POP3Response sendMultiLineCommand(String cmd) throws MessagingException {
+ return sendCommand(cmd, true);
+ }
+
+ protected synchronized POP3Response sendCommand(String cmd, boolean multiLine) throws MessagingException {
+ if (socket.isConnected()) {
+ {
+ // NOTE: We don't use println() because it uses the platform concept of a newline rather
+ // than using CRLF, which is required by the POP3 protocol.
+ writer.write(cmd);
+ writer.write("\r\n");
+ writer.flush();
+
+ POP3Response response = buildResponse(multiLine);
+ if (response.isError()) {
+ throw new CommandFailedException("Error issuing POP3 command: " + cmd);
+ }
+ return response;
+ }
+ }
+ throw new MessagingException("Connection to Mail Server is lost, connection " + this.toString());
+ }
+
+ /**
+ * Build a POP3Response item from the response stream.
+ *
+ * @param isMultiLineResponse
+ * If true, this command is expecting multiple lines back from the server.
+ *
+ * @return A POP3Response item with all of the command response data.
+ * @exception MessagingException
+ */
+ protected POP3Response buildResponse(boolean isMultiLineResponse) throws MessagingException {
+ int status = ERR;
+ byte[] data = null;
+
+ String line;
+ //MIMEInputReader source = new MIMEInputReader(reader); //TODO unused
+
+ try {
+ line = reader.readLine();
+ } catch (IOException e) {
+ throw new MessagingException("Error in receving response");
+ }
+
+ if (line == null || line.trim().equals("")) {
+ throw new MessagingException("Empty Response");
+ }
+
+ if (line.startsWith("+OK")) {
+ status = OK;
+ line = removeStatusField(line);
+ if (isMultiLineResponse) {
+ data = getMultiLineResponse();
+ }
+ } else if (line.startsWith("-ERR")) {
+ status = ERR;
+ line = removeStatusField(line);
+ }else if (line.startsWith("+")) {
+ status = CHALLENGE;
+ line = removeStatusField(line);
+ if (isMultiLineResponse) {
+ data = getMultiLineResponse();
+ }
+ } else {
+ throw new MessagingException("Unexpected response: " + line);
+ }
+ return new POP3Response(status, line, data);
+ }
+
+ private static String removeStatusField(String line) {
+ return line.substring(line.indexOf(SPACE) + 1);
+ }
+
+ /**
+ * This could be a multiline response
+ */
+ private byte[] getMultiLineResponse() throws MessagingException {
+
+ MIMEInputReader source = new MIMEInputReader(reader);
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+ // it's more efficient to do this a buffer at a time.
+ // the MIMEInputReader takes care of the byte-stuffing and
+ // ".\r\n" input terminator for us.
+ try {
+ OutputStreamWriter outWriter = new OutputStreamWriter(out, "ISO8859-1");
+ char buffer[] = new char[500];
+ try {
+ int charsRead = -1;
+ while ((charsRead = source.read(buffer)) >= 0) {
+ outWriter.write(buffer, 0, charsRead);
+ }
+ outWriter.flush();
+ } catch (IOException e) {
+ throw new MessagingException("Error processing a multi-line response", e);
+ }
+ } catch (UnsupportedEncodingException e) {
+ }
+ return out.toByteArray();
+ }
+
+
+ /**
+ * Retrieve the raw message content from the POP3
+ * server. This is all of the message data, including
+ * the header.
+ *
+ * @param sequenceNumber
+ * The message sequence number.
+ *
+ * @return A byte array containing all of the message data.
+ * @exception MessagingException
+ */
+ public byte[] retrieveMessageData(int sequenceNumber) throws MessagingException {
+ POP3Response msgResponse = sendMultiLineCommand("RETR " + sequenceNumber);
+ // we want the data directly in this case.
+ return msgResponse.getData();
+ }
+
+ /**
+ * Retrieve the message header information for a given
+ * message, returned as an input stream suitable
+ * for loading the message data.
+ *
+ * @param sequenceNumber
+ * The server sequence number for the message.
+ *
+ * @return An inputstream that can be used to read the message
+ * data.
+ * @exception MessagingException
+ */
+ public ByteArrayInputStream retrieveMessageHeaders(int sequenceNumber) throws MessagingException {
+ POP3Response msgResponse;
+
+ // some POP3 servers don't correctly implement TOP, so this can be disabled. If
+ // we can't use TOP, then use RETR and retrieve everything. We can just hand back
+ // the stream, as the header loading routine will stop at the first
+ // null line.
+ if (topDisabled) {
+ msgResponse = sendMultiLineCommand("RETR " + sequenceNumber);
+ }
+ else {
+ msgResponse = sendMultiLineCommand("TOP " + sequenceNumber + " 0");
+ }
+
+ // just load the returned message data as a set of headers
+ return msgResponse.getContentStream();
+ }
+
+ /**
+ * Retrieve the total message size from the mail
+ * server. This is the size of the headers plus
+ * the size of the message content.
+ *
+ * @param sequenceNumber
+ * The message sequence number.
+ *
+ * @return The full size of the message.
+ * @exception MessagingException
+ */
+ public int retrieveMessageSize(int sequenceNumber) throws MessagingException {
+ POP3Response msgResponse = sendCommand("LIST " + sequenceNumber);
+ // Convert this into the parsed response type we need.
+ POP3ListResponse list = new POP3ListResponse(msgResponse);
+ // this returns the total message size
+ return list.getSize();
+ }
+
+ /**
+ * Retrieve the mail drop status information.
+ *
+ * @return An object representing the returned mail drop status.
+ * @exception MessagingException
+ */
+ public POP3StatusResponse retrieveMailboxStatus() throws MessagingException {
+ // issue the STAT command and return this into a status response
+ return new POP3StatusResponse(sendCommand("STAT"));
+ }
+
+
+ /**
+ * Retrieve the UID for an individual message.
+ *
+ * @param sequenceNumber
+ * The target message sequence number.
+ *
+ * @return The string UID maintained by the server.
+ * @exception MessagingException
+ */
+ public String retrieveMessageUid(int sequenceNumber) throws MessagingException {
+ POP3Response msgResponse = sendCommand("UIDL " + sequenceNumber);
+
+ String message = msgResponse.getFirstLine();
+ // the UID is everything after the blank separating the message number and the UID.
+ // there's not supposed to be anything else on the message, but trim it of whitespace
+ // just to be on the safe side.
+ return message.substring(message.indexOf(' ') + 1).trim();
+ }
+
+
+ /**
+ * Delete a single message from the mail server.
+ *
+ * @param sequenceNumber
+ * The sequence number of the message to delete.
+ *
+ * @exception MessagingException
+ */
+ public void deleteMessage(int sequenceNumber) throws MessagingException {
+ // just issue the command...we ignore the command response
+ sendCommand("DELE " + sequenceNumber);
+ }
+
+ /**
+ * Logout from the mail server. This sends a QUIT
+ * command, which will likely sever the mail connection.
+ *
+ * @exception MessagingException
+ */
+ public void logout() throws MessagingException {
+ // we may have already sent the QUIT command
+ if (!loggedIn) {
+ return;
+ }
+ // just issue the command...we ignore the command response
+ sendCommand("QUIT");
+ loggedIn = false;
+ }
+
+ /**
+ * Perform a reset on the mail server.
+ *
+ * @exception MessagingException
+ */
+ public void reset() throws MessagingException {
+ // some mail servers mark retrieved messages for deletion
+ // automatically. This will reset the read flags before
+ // we go through normal cleanup.
+ if (props.getBooleanProperty(MAIL_RESET_QUIT, false)) {
+ // just send an RSET command first
+ sendCommand("RSET");
+ }
+ }
+
+ /**
+ * Ping the mail server to see if we still have an active connection.
+ *
+ * @exception MessagingException thrown if we do not have an active connection.
+ */
+ public void pingServer() throws MessagingException {
+ // just issue the command...we ignore the command response
+ sendCommand("NOOP");
+ }
+
+ /**
+ * Login to the mail server, using whichever method is
+ * configured. This will try multiple methods, if allowed,
+ * in decreasing levels of security.
+ *
+ * @return true if the login was successful.
+ * @exception MessagingException
+ */
+ public synchronized boolean login() throws MessagingException {
+ // permitted to use the AUTH command?
+ if (authEnabled) {
+ try {
+ // go do the SASL thing
+ return processSaslAuthentication();
+ } catch (MessagingException e) {
+ // Any error here means fall back to the next mechanism
+ }
+ }
+
+ if (apopEnabled) {
+ try {
+ // go do the SASL thing
+ return processAPOPAuthentication();
+ } catch (MessagingException e) {
+ // Any error here means fall back to the next mechanism
+ }
+ }
+
+ try {
+ // do the tried and true login processing.
+ return processLogin();
+ } catch (MessagingException e) {
+ }
+ // everything failed...can't get in
+ return false;
+ }
+
+
+ /**
+ * Process a basic LOGIN operation, using the
+ * plain test USER/PASS command combo.
+ *
+ * @return true if we logged successfully.
+ * @exception MessagingException
+ */
+ public boolean processLogin() throws MessagingException {
+ // start by sending the USER command, followed by
+ // the PASS command
+ sendCommand("USER " + username);
+ sendCommand("PASS " + password);
+ return true; // we're in
+ }
+
+ /**
+ * Process logging in using the APOP command. Only
+ * works on servers that give a timestamp value
+ * in the welcome response.
+ *
+ * @return true if the login was accepted.
+ * @exception MessagingException
+ */
+ public boolean processAPOPAuthentication() throws MessagingException {
+ int timeStart = greeting.indexOf('<');
+ // if we didn't get an APOP challenge on the greeting, throw an exception
+ // the main login processor will swallow that and fall back to the next
+ // mechanism
+ if (timeStart == -1) {
+ throw new MessagingException("POP3 Server does not support APOP");
+ }
+ int timeEnd = greeting.indexOf('>');
+ String timeStamp = greeting.substring(timeStart, timeEnd + 1);
+
+ // we create the digest password using the timestamp value sent to use
+ // concatenated with the password.
+ String digestPassword = timeStamp + password;
+
+ byte[] digest;
+
+ try {
+ // create a digest value from the password.
+ MessageDigest md = MessageDigest.getInstance("MD5");
+ digest = md.digest(digestPassword.getBytes("iso-8859-1"));
+ } catch (NoSuchAlgorithmException e) {
+ // this shouldn't happen, but if it does, we'll just try a plain
+ // login.
+ throw new MessagingException("Unable to create MD5 digest", e);
+ } catch (UnsupportedEncodingException e) {
+ // this shouldn't happen, but if it does, we'll just try a plain
+ // login.
+ throw new MessagingException("Unable to create MD5 digest", e);
+ }
+ // this will throw an exception if it gives an error failure
+ sendCommand("APOP " + username + " " + new String(Hex.encode(digest)));
+ // no exception, we must have passed
+ return true;
+ }
+
+
+ /**
+ * Process SASL-type authentication.
+ *
+ * @return Returns true if the server support a SASL authentication mechanism and
+ * accepted reponse challenges.
+ * @exception MessagingException
+ */
+ protected boolean processSaslAuthentication() throws MessagingException {
+ // if unable to get an appropriate authenticator, just fail it.
+ ClientAuthenticator authenticator = getSaslAuthenticator();
+ if (authenticator == null) {
+ throw new MessagingException("Unable to obtain SASL authenticator");
+ }
+
+ // go process the login.
+ return processLogin(authenticator);
+ }
+
+ /**
+ * Attempt to retrieve a SASL authenticator for this
+ * protocol.
+ *
+ * @return A SASL authenticator, or null if a suitable one
+ * was not located.
+ */
+ protected ClientAuthenticator getSaslAuthenticator() {
+ return AuthenticatorFactory.getAuthenticator(props, selectSaslMechanisms(), serverHost, username, password, authid, realm);
+ }
+
+
+ /**
+ * Process a login using the provided authenticator object.
+ *
+ * NB: This method is synchronized because we have a multi-step process going on
+ * here. No other commands should be sent to the server until we complete.
+ *
+ * @return Returns true if the server support a SASL authentication mechanism and
+ * accepted reponse challenges.
+ * @exception MessagingException
+ */
+ protected synchronized boolean processLogin(ClientAuthenticator authenticator) throws MessagingException {
+ if (debug) {
+ debugOut("Authenticating for user: " + username + " using " + authenticator.getMechanismName());
+ }
+
+ POP3Response response = sendCommand("AUTH " + authenticator.getMechanismName());
+
+ // now process the challenge sequence. We get a continuation response back for each stage of the
+ // authentication, and finally an OK when everything passes muster.
+ while (true) {
+ // this should be a continuation reply, if things are still good.
+ if (response.isChallenge()) {
+ // we're passed back a challenge value, Base64 encoded.
+ byte[] challenge = response.decodeChallengeResponse();
+
+ try {
+ String responseString = new String(Base64.encode(authenticator.evaluateChallenge(challenge)), "US-ASCII");
+
+ // have the authenticator evaluate and send back the encoded response.
+ response = sendCommand(responseString);
+ } catch (UnsupportedEncodingException ex) {
+ }
+ }
+ else {
+ // there are only two choices here, OK or a continuation. OK means
+ // we've passed muster and are in.
+ return true;
+ }
+ }
+ }
+
+
+ /**
+ * Merge the configured SASL mechanisms with the capabilities that the
+ * server has indicated it supports, returning a merged list that can
+ * be used for selecting a mechanism.
+ *
+ * @return A List representing the intersection of the configured list and the
+ * capabilities list.
+ */
+ protected List selectSaslMechanisms() {
+ // just return the set that have been explicity permitted
+ return getSaslMechanisms();
+ }
+}
+
Added: geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/connection/POP3ConnectionPool.java
URL: http://svn.apache.org/viewvc/geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/connection/POP3ConnectionPool.java?rev=1882306&view=auto
==============================================================================
--- geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/connection/POP3ConnectionPool.java (added)
+++ geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/connection/POP3ConnectionPool.java Wed Oct 7 15:58:39 2020
@@ -0,0 +1,224 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.geronimo.javamail.store.pop3.connection;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.StringTokenizer;
+
+import javax.mail.MessagingException;
+import javax.mail.Session;
+import javax.mail.Store;
+
+import javax.mail.StoreClosedException;
+
+import org.apache.geronimo.javamail.store.pop3.POP3Store;
+import org.apache.geronimo.javamail.util.ProtocolProperties;
+
+public class POP3ConnectionPool {
+
+ protected static final String MAIL_PORT = "port";
+
+ protected static final String MAIL_SASL_REALM = "sasl.realm";
+ protected static final String MAIL_AUTHORIZATIONID = "sasl.authorizationid";
+
+ protected static final String DEFAULT_MAIL_HOST = "localhost";
+
+ // Our hosting Store instance
+ protected POP3Store store;
+ // our Protocol abstraction
+ protected ProtocolProperties props;
+ // POP3 is not nearly as multi-threaded as IMAP. We really just have a single folder,
+ // plus the Store, but the Store doesn't really talk to the server very much. We only
+ // hold one connection available, and on the off chance there is a situation where
+ // we need to create a new one, we'll authenticate on demand. The one case where
+ // I know this might be an issue is a folder checking back with the Store to see it if
+ // it is still connected.
+ protected POP3Connection availableConnection;
+
+ // our debug flag
+ protected boolean debug;
+
+ // the target host
+ protected String host;
+ // the target server port.
+ protected int port;
+ // the username we connect with
+ protected String username;
+ // the authentication password.
+ protected String password;
+ // the SASL realm name
+ protected String realm;
+ // the authorization id.
+ protected String authid;
+ // Turned on when the store is closed for business.
+ protected boolean closed = false;
+
+ /**
+ * Create a connection pool associated with a give POP3Store instance. The
+ * connection pool manages handing out connections for both the Store and
+ * Folder and Message usage.
+ *
+ * @param store The Store we're creating the pool for.
+ * @param props The protocol properties abstraction we use.
+ */
+ public POP3ConnectionPool(POP3Store store, ProtocolProperties props) {
+ this.store = store;
+ this.props = props;
+ }
+
+
+ /**
+ * Manage the initial connection to the POP3 server. This is the first
+ * point where we obtain the information needed to make an actual server
+ * connection. Like the Store protocolConnect method, we return false
+ * if there's any sort of authentication difficulties.
+ *
+ * @param host The host of the mail server.
+ * @param port The mail server connection port.
+ * @param user The connection user name.
+ * @param password The connection password.
+ *
+ * @return True if we were able to connect and authenticate correctly.
+ * @exception MessagingException
+ */
+ public synchronized boolean protocolConnect(String host, int port, String username, String password) throws MessagingException {
+ // NOTE: We don't check for the username/password being null at this point. It's possible that
+ // the server will send back a PREAUTH response, which means we don't need to go through login
+ // processing. We'll need to check the capabilities response after we make the connection to decide
+ // if logging in is necesssary.
+
+ // save this for subsequent connections. All pool connections will use this info.
+ // 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 = props.getIntProperty(MAIL_PORT, props.getDefaultPort());
+ // it's possible that -1 might have been explicitly set, so one last check.
+ if (port == -1) {
+ port = props.getDefaultPort();
+ }
+ }
+
+ // Before we do anything, let's make sure that we succesfully received a host
+ if ( host == null ) {
+ host = DEFAULT_MAIL_HOST;
+ }
+
+ this.host = host;
+ this.port = port;
+ this.username = username;
+ this.password = password;
+
+ // make sure we have the realm information
+ realm = props.getProperty(MAIL_SASL_REALM);
+ // get an authzid value, if we have one. The default is to use the username.
+ authid = props.getProperty(MAIL_AUTHORIZATIONID, username);
+
+ // go create a connection and just add it to the pool. If there is an authenticaton error,
+ // return the connect failure, and we may end up trying again.
+ availableConnection = createPoolConnection();
+ if (availableConnection == null) {
+ return false;
+ }
+ // we're connected, authenticated, and ready to go.
+ return true;
+ }
+
+ /**
+ * Creates an authenticated pool connection and adds it to
+ * the connection pool. If there is an existing connection
+ * already in the pool, this returns without creating a new
+ * connection.
+ *
+ * @exception MessagingException
+ */
+ protected POP3Connection createPoolConnection() throws MessagingException {
+ POP3Connection connection = new POP3Connection(props);
+ if (!connection.protocolConnect(host, port, authid, realm, username, password)) {
+ // we only add live connections to the pool. Sever the connections and
+ // allow it to go free.
+ connection.closeServerConnection();
+ return null;
+ }
+ // just return this connection
+ return connection;
+ }
+
+
+ /**
+ * Get a connection from the pool. We try to retrieve a live
+ * connection, but we test the connection's liveness before
+ * returning one. If we don't have a viable connection in
+ * the pool, we'll create a new one. The returned connection
+ * will be in the authenticated state already.
+ *
+ * @return A POP3Connection object that is connected to the server.
+ */
+ public synchronized POP3Connection getConnection() throws MessagingException {
+ // if we have an available one (common when opening the INBOX), just return it
+ POP3Connection connection = availableConnection;
+
+ if (connection != null) {
+ availableConnection = null;
+ return connection;
+ }
+ // we need an additional connection...rare, but it can happen if we've closed the INBOX folder.
+ return createPoolConnection();
+ }
+
+
+ /**
+ * Return a connection to the connection pool.
+ *
+ * @param connection The connection getting returned.
+ *
+ * @exception MessagingException
+ */
+ public synchronized void releaseConnection(POP3Connection connection) throws MessagingException
+ {
+ // we're generally only called if the store needed to talk to the server and
+ // then returned the connection to the pool. So it's pretty likely that we'll just cache this
+ if (availableConnection == null) {
+ availableConnection = connection;
+ }
+ else {
+ // got too many connections created...not sure how, but get rid of this one.
+ connection.close();
+ }
+ }
+
+
+ /**
+ * Close the entire connection pool.
+ *
+ * @exception MessagingException
+ */
+ public synchronized void close() throws MessagingException {
+ // we'll on have the single connection in reserver
+ if (availableConnection != null) {
+ availableConnection.close();
+ availableConnection = null;
+ }
+ // turn out the lights, hang the closed sign on the wall.
+ closed = true;
+ }
+}
+
Added: geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/connection/POP3ListResponse.java
URL: http://svn.apache.org/viewvc/geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/connection/POP3ListResponse.java?rev=1882306&view=auto
==============================================================================
--- geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/connection/POP3ListResponse.java (added)
+++ geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/connection/POP3ListResponse.java Wed Oct 7 15:58:39 2020
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.geronimo.javamail.store.pop3.connection;
+
+import java.io.ByteArrayInputStream;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.mail.MessagingException;
+
+/**
+ * This class adds functionality to the basic response by parsing the reply for
+ * LIST command and obtaining specific information about the msgnum and the
+ * size. It could be for one or more msgs depending on wether a msg number was
+ * passed or not into the LIST command
+ *
+ * @see org.apache.geronimo.javamail.store.pop3.POP3Response
+ * @see org.apache.geronimo.javamail.store.pop3.response.DefaultPOP3Response
+ *
+ * @version $Rev$ $Date$
+ */
+
+public class POP3ListResponse extends POP3Response {
+
+ private int msgnum = 0;
+
+ private int size = 0;
+
+ private List multipleMsgs = null;
+
+ POP3ListResponse(POP3Response baseRes) throws MessagingException {
+ super(baseRes.getStatus(), baseRes.getFirstLine(), baseRes.getData());
+
+ // if ERR not worth proceeding any further
+ if (OK == getStatus()) {
+
+ // if data == null, then it mean it's a single line response
+ if (baseRes.getData() == null) {
+ String[] args = getFirstLine().split(SPACE);
+ try {
+ msgnum = Integer.parseInt(args[0]);
+ } catch (NumberFormatException e) {
+ throw new MessagingException("Invalid response for LIST command", e);
+ }
+ try {
+ size = Integer.parseInt(args[1]);
+ } catch (NumberFormatException e) {
+ throw new MessagingException("Invalid response for LIST command", e);
+ }
+ } else {
+ int totalMsgs = 0;
+ String[] args = getFirstLine().split(SPACE);
+ try {
+ totalMsgs = Integer.parseInt(args[0]);
+ } catch (NumberFormatException e) {
+ throw new MessagingException("Invalid response for LIST command", e);
+ }
+ multipleMsgs = new ArrayList(totalMsgs);
+ // Todo : multi-line response parsing
+ }
+
+ }
+ }
+
+ public int getMessageNumber() {
+ return msgnum;
+ }
+
+ public int getSize() {
+ return size;
+ }
+
+ /**
+ * Messages can be accessed by multipleMsgs.getElementAt(msgnum)
+ *
+ */
+ public List getMultipleMessageDetails() {
+ return multipleMsgs;
+ }
+
+}
Added: geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/connection/POP3Response.java
URL: http://svn.apache.org/viewvc/geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/connection/POP3Response.java?rev=1882306&view=auto
==============================================================================
--- geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/connection/POP3Response.java (added)
+++ geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/connection/POP3Response.java Wed Oct 7 15:58:39 2020
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.geronimo.javamail.store.pop3.connection;
+
+import java.io.ByteArrayInputStream;
+
+import org.apache.geronimo.javamail.store.pop3.POP3Constants;
+
+import org.apache.geronimo.mail.util.Base64;
+
+/**
+ * This class provides the basic implementation for the POP3Response.
+ *
+ * @see org.apache.geronimo.javamail.store.pop3.POP3Response
+ * @version $Rev$ $Date$
+ */
+
+public class POP3Response implements POP3Constants {
+
+ private int status = ERR;
+
+ private String firstLine;
+
+ private byte[] data;
+
+ POP3Response(int status, String firstLine, byte []data) {
+ this.status = status;
+ this.firstLine = firstLine;
+ this.data = data;
+ }
+
+ public int getStatus() {
+ return status;
+ }
+
+ public byte[] getData() {
+ return data;
+ }
+
+ public ByteArrayInputStream getContentStream() {
+ return new ByteArrayInputStream(data);
+ }
+
+ public String getFirstLine() {
+ return firstLine;
+ }
+
+ public boolean isError() {
+ return status == ERR;
+ }
+
+ public boolean isChallenge() {
+ return status == CHALLENGE;
+ }
+
+ /**
+ * Decode the message portion of a continuation challenge response.
+ *
+ * @return The byte array containing the decoded data.
+ */
+ public byte[] decodeChallengeResponse()
+ {
+ // the challenge response is a base64 encoded string...
+ return Base64.decode(firstLine.trim());
+ }
+
+}
+
Added: geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/connection/POP3StatusResponse.java
URL: http://svn.apache.org/viewvc/geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/connection/POP3StatusResponse.java?rev=1882306&view=auto
==============================================================================
--- geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/connection/POP3StatusResponse.java (added)
+++ geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/connection/POP3StatusResponse.java Wed Oct 7 15:58:39 2020
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.geronimo.javamail.store.pop3.connection;
+
+import javax.mail.MessagingException;
+
+/**
+ * This class adds functionality to the basic response by parsing the status
+ * line and obtaining specific information about num of msgs and the size
+ *
+ * @see org.apache.geronimo.javamail.store.pop3.POP3Response
+ * @see org.apache.geronimo.javamail.store.pop3.response.DefaultPOP3Response
+ *
+ * @version $Rev$ $Date$
+ */
+
+public class POP3StatusResponse extends POP3Response {
+
+ private int numMessages = 0;
+
+ private int size = 0;
+
+ POP3StatusResponse(POP3Response baseRes) throws MessagingException {
+ super(baseRes.getStatus(), baseRes.getFirstLine(), baseRes.getData());
+
+ // if ERR not worth proceeding any further
+ if (OK == getStatus()) {
+ String[] args = getFirstLine().split(SPACE);
+ try {
+ numMessages = Integer.parseInt(args[0]);
+ } catch (NumberFormatException e) {
+ throw new MessagingException("Invalid response for STAT command", e);
+ }
+ try {
+ size = Integer.parseInt(args[1]);
+ } catch (NumberFormatException e) {
+ throw new MessagingException("Invalid response for STAT command", e);
+ }
+ }
+ }
+
+ public int getNumMessages() {
+ return numMessages;
+ }
+
+ public int getSize() {
+ return size;
+ }
+
+}
Added: geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/transport/nntp/NNTPConnection.java
URL: http://svn.apache.org/viewvc/geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/transport/nntp/NNTPConnection.java?rev=1882306&view=auto
==============================================================================
--- geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/transport/nntp/NNTPConnection.java (added)
+++ geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/transport/nntp/NNTPConnection.java Wed Oct 7 15:58:39 2020
@@ -0,0 +1,728 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.geronimo.javamail.transport.nntp;
+
+import java.io.BufferedReader;
+import java.io.BufferedOutputStream;
+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.PrintWriter;
+import java.io.UnsupportedEncodingException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.StringTokenizer;
+
+import javax.mail.Message;
+import javax.mail.MessagingException;
+import javax.mail.Session;
+
+import org.apache.geronimo.javamail.authentication.ClientAuthenticator;
+import org.apache.geronimo.javamail.authentication.AuthenticatorFactory;
+import org.apache.geronimo.javamail.util.MailConnection;
+import org.apache.geronimo.javamail.util.MIMEOutputStream;
+import org.apache.geronimo.javamail.util.ProtocolProperties;
+import org.apache.geronimo.mail.util.Base64;
+import org.apache.geronimo.mail.util.SessionUtil;
+
+/**
+ * Simple implementation of NNTP transport. Just does plain RFC977-ish delivery.
+ *
+ * @version $Rev$ $Date$
+ */
+public class NNTPConnection extends MailConnection {
+
+ /**
+ * constants for EOL termination
+ */
+ protected static final char CR = '\r';
+
+ protected static final char LF = '\n';
+
+ /**
+ * property keys for protocol properties.
+ */
+ protected static final int DEFAULT_NNTP_PORT = 119;
+ // does the server support posting?
+ protected boolean postingAllowed = true;
+
+ // different authentication mechanisms
+ protected boolean authInfoUserAllowed = false;
+ protected boolean authInfoSaslAllowed = false;
+
+ // the last response line received from the server.
+ protected NNTPReply lastServerResponse = null;
+
+ // the welcome string from the server.
+ protected String welcomeString = null;
+
+ // input reader wrapped around the socket input stream
+ protected BufferedReader reader;
+ // output writer wrapped around the socket output stream.
+ protected PrintWriter writer;
+
+ /**
+ * Normal constructor for an NNTPConnection() object.
+ *
+ * @param props The property bundle for this protocol instance.
+ */
+ public NNTPConnection(ProtocolProperties props) {
+ super(props);
+ }
+
+
+ /**
+ * Connect to the server and do the initial handshaking.
+ *
+ * @param host The target host name.
+ * @param port The target port
+ * @param username The connection username (can be null)
+ * @param password The authentication password (can be null).
+ *
+ * @return true if we were able to obtain a connection and
+ * authenticate.
+ * @exception MessagingException
+ */
+ public boolean protocolConnect(String host, int port, String username, String password) throws MessagingException {
+ super.protocolConnect(host, port, username, password);
+ // create socket and connect to server.
+ getConnection();
+
+ // receive welcoming message
+ getWelcome();
+
+ return true;
+ }
+
+
+ /**
+ * Create a transport connection object and connect it to the
+ * target server.
+ *
+ * @exception MessagingException
+ */
+ protected void getConnection() throws MessagingException
+ {
+ try {
+ // do all of the non-protocol specific set up. This will get our socket established
+ // and ready use.
+ super.getConnection();
+ } catch (IOException e) {
+ throw new MessagingException("Unable to obtain a connection to the NNTP server", e);
+ }
+
+ // The NNTP protocol is inherently a string-based protocol, so we get
+ // string readers/writers for the connection streams. Note that we explicitly
+ // set the encoding to ensure that an inappropriate native encoding is not picked up.
+ try {
+ reader = new BufferedReader(new InputStreamReader(inputStream, "ISO8859-1"));
+ writer = new PrintWriter(new OutputStreamWriter(new BufferedOutputStream(outputStream), "ISO8859-1"));
+ } catch (UnsupportedEncodingException 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();
+ // get rid of our response processor too.
+ reader = null;
+ writer = null;
+ }
+ }
+
+ public String toString() {
+ return "NNTPConnection host: " + serverHost + " port: " + serverPort;
+ }
+
+
+ /**
+ * 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;
+ }
+
+ // the NNTP store will want to use the welcome string, so save it.
+ welcomeString = line.getMessage();
+
+ // find out what extensions this server supports.
+ getExtensions();
+ }
+
+
+ /**
+ * Sends the QUIT message and receieves the response
+ */
+ public void sendQuit() throws MessagingException {
+ sendLine("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 delivered as data
+ // lines terminated with a "." line.
+ if (reply.getCode() != NNTPReply.EXTENSIONS_SUPPORTED) {
+ return;
+ }
+
+ // get a fresh extension mapping table.
+ capabilities = new HashMap();
+ authentications = new ArrayList();
+
+ // 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 LIST EXTENSIONS 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.
+ capabilities.put(extensionName, argument);
+
+ // we need to determine which authentication mechanisms are supported here
+ if (extensionName.equals("AUTHINFO")) {
+ StringTokenizer tokenizer = new StringTokenizer(argument);
+
+ while (tokenizer.hasMoreTokens()) {
+ // we only know how to do USER or SASL
+ String mechanism = tokenizer.nextToken().toUpperCase();
+ if (mechanism.equals("SASL")) {
+ authInfoSaslAllowed = true;
+ }
+ else if (mechanism.equals("USER")) {
+ authInfoUserAllowed = true;
+ }
+ }
+ }
+ // 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();
+ authentications.add(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 (capabilities != null) {
+ return (String) capabilities.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;
+ }
+
+
+ /**
+ * 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 synchronized 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.
+ MIMEOutputStream mimeOut = new MIMEOutputStream(outputStream);
+
+ msg.writeTo(mimeOut);
+
+ // now to finish, we send a CRLF sequence, followed by a ".".
+ mimeOut.writeSMTPTerminator();
+ // and flush the data to send it along
+ mimeOut.flush();
+ } catch (IOException e) {
+ throw new MessagingException("I/O error posting message", e);
+ } catch (MessagingException e) {
+ throw new MessagingException("Exception posting message", e);
+ }
+
+ // 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 synchronized NNTPReply sendCommand(String command, int success) throws MessagingException {
+ NNTPReply reply = sendCommand(command);
+ if (reply.getCode() == success) {
+ reply.retrieveData(reader);
+ }
+ 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) {
+ 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 (socket == null || !socket.isConnected()) {
+ throw new MessagingException("no connection");
+ }
+ try {
+ outputStream.write(data.getBytes("ISO8859-1"));
+ 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 = reader.readLine();
+ if (line == null) {
+ throw new MessagingException("Unexpected end of stream");
+ }
+ return line;
+ } catch (IOException e) {
+ throw new MessagingException("Error reading from server", e);
+ }
+ }
+
+
+ /**
+ * 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 (!processSaslAuthentication()) {
+ 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 SASL-type authentication.
+ *
+ * @return Returns true if the server support a SASL authentication mechanism and
+ * accepted reponse challenges.
+ * @exception MessagingException
+ */
+ protected boolean processSaslAuthentication() throws MessagingException {
+ // only do this if permitted
+ if (!authInfoSaslAllowed) {
+ return false;
+ }
+ // if unable to get an appropriate authenticator, just fail it.
+ ClientAuthenticator authenticator = getSaslAuthenticator();
+ if (authenticator == null) {
+ throw new MessagingException("Unable to obtain SASL authenticator");
+ }
+
+ // go process the login.
+ return processLogin(authenticator);
+ }
+
+ /**
+ * Attempt to retrieve a SASL authenticator for this
+ * protocol.
+ *
+ * @return A SASL authenticator, or null if a suitable one
+ * was not located.
+ */
+ protected ClientAuthenticator getSaslAuthenticator() {
+ return AuthenticatorFactory.getAuthenticator(props, selectSaslMechanisms(), serverHost, username, password, authid, realm);
+ }
+
+
+ /**
+ * Process a login using the provided authenticator object.
+ *
+ * NB: This method is synchronized because we have a multi-step process going on
+ * here. No other commands should be sent to the server until we complete.
+ *
+ * @return Returns true if the server support a SASL authentication mechanism and
+ * accepted reponse challenges.
+ * @exception MessagingException
+ */
+ protected synchronized boolean processLogin(ClientAuthenticator authenticator) throws MessagingException {
+ 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
+ try {
+ command.append(new String(Base64.encode(authenticator.evaluateChallenge(null)), "US-ASCII"));
+ } catch (UnsupportedEncodingException e) {
+ }
+ // 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) {
+ 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()) {
+ debugOut("Extra authentication challenge " + line);
+ return false;
+ }
+
+ // we're passed back a challenge value, Base64 encoded.
+ try {
+ byte[] challenge = Base64.decode(line.getMessage().getBytes("ISO8859-1"));
+
+ // have the authenticator evaluate and send back the encoded
+ // response.
+ sendLine(new String(Base64.encode(authenticator.evaluateChallenge(challenge)), "US-ASCII"));
+ } catch (UnsupportedEncodingException e) {
+ }
+ }
+ // completion or challenge are the only responses we know how to
+ // handle. Anything else must
+ // be a failure.
+ else {
+ debugOut("Authentication failure " + line);
+ return false;
+ }
+ }
+ }
+
+
+ /**
+ * Process an AUTHINFO USER command. Most common form of NNTP
+ * authentication.
+ *
+ * @exception MessagingException
+ */
+ protected void processAuthinfoUser() throws MessagingException {
+ // only do this if allowed by the server
+ if (!authInfoUserAllowed) {
+ return;
+ }
+ 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");
+ }
+ }
+
+
+ /**
+ * 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;
+ }
+
+ /**
+ * Retrieve the welcome string sent back from the server.
+ *
+ * @return The server provided welcome string.
+ */
+ public String getWelcomeString() {
+ return welcomeString;
+ }
+}