dmose%mozilla.org 9fc7780ead updating license boilerplate to xPL v1.1
git-svn-id: svn://10.0.0.236/trunk@52523 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-02 06:20:05 +00:00

1563 lines
51 KiB
Java

/*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (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.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is the Netscape Messaging Access SDK Version 3.5 code,
* released on or about June 15, 1998. *
* The Initial Developer of the Original Code is Netscape Communications
* Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s): ______________________________________.
*/
/*
* Copyright (c) 1997 and 1998 Netscape Communications Corporation
* (http://home.netscape.com/misc/trademarks.html)
*/
package netscape.messaging.pop3;
import java.io.IOException;
import java.io.InputStream;
import java.util.Vector;
import netscape.messaging.*;
/**
*The POP3Client class represents the POP3 client.
*The client uses this class for communicating with a
*server using the POP3 protocol.
*The POP3 client conforms to the client specifications
*described in RFC 1939. For the URL,
*see "Where to Find More Information" in "About This Book."
*@author derekt@netscape.com
*@version 1.0
*/
public class POP3Client
{
///////////////////////////////////////////////////////////////////////////
// Internal class used for socket I/O.
///////////////////////////////////////////////////////////////////////////
private IO m_io;
///////////////////////////////////////////////////////////////////////////
// Data member used for C-runtime equivalent routines.
///////////////////////////////////////////////////////////////////////////
private Common m_common;
///////////////////////////////////////////////////////////////////////////
// Data member used to store the timeout value.
///////////////////////////////////////////////////////////////////////////
private int m_timeout;
///////////////////////////////////////////////////////////////////////////
// Boolean variable to determine if we can send another command.
///////////////////////////////////////////////////////////////////////////
private boolean m_mustProcess;
///////////////////////////////////////////////////////////////////////////
// Callback data members.
///////////////////////////////////////////////////////////////////////////
private Vector m_pendingList;
private IPOP3Sink m_notifySink;
///////////////////////////////////////////////////////////////////////////
// Reused response objects.
///////////////////////////////////////////////////////////////////////////
private StringBuffer m_responseLine;
private StringBuffer m_responseMessage;
private int m_currentMessageNumber;
///////////////////////////////////////////////////////////////////////////
// Byte arrays that are reused for commands and responses.
///////////////////////////////////////////////////////////////////////////
private byte[] m_response;
private byte[] m_messageData;
private int m_messageDataSize;
private int m_overFlowSize;
private byte[] m_itoaBuffer;
private byte[] m_tokenBuffer;
private boolean m_multiLineState;
///////////////////////////////////////////////////////////////////////////
// Constants.
///////////////////////////////////////////////////////////////////////////
private final static int m_defaultPort = 110;
private final static int m_minChunkSize = 1024;
private final static int m_maxReplyLine = 512;
private final static byte[] m_dele = { 'D', 'E', 'L', 'E', ' ' };
private final static byte[] m_list = { 'L', 'I', 'S', 'T', ' ' };
private final static byte[] m_noop = { 'N', 'O', 'O', 'P' };
private final static byte[] m_pass = { 'P', 'A', 'S', 'S', ' ' };
private final static byte[] m_quit = { 'Q', 'U', 'I', 'T' };
private final static byte[] m_rset = { 'R', 'S', 'E', 'T' };
private final static byte[] m_retr = { 'R', 'E', 'T', 'R', ' ' };
private final static byte[] m_stat = { 'S', 'T', 'A', 'T' };
private final static byte[] m_top = { 'T', 'O', 'P', ' ' };
private final static byte[] m_uidl = { 'U', 'I', 'D', 'L', ' ' };
private final static byte[] m_user = { 'U', 'S', 'E', 'R', ' ' };
private final static byte[] m_xauthlist = { 'X', 'A', 'U', 'T', 'H', 'L', 'I', 'S', 'T', ' ' };
private final static byte[] m_xsender = { 'X', 'S', 'E', 'N', 'D', 'E', 'R', ' ' };
private final static byte[] m_space = { ' ' };
///////////////////////////////////////////////////////////////////////////
// Command types for POP3.
///////////////////////////////////////////////////////////////////////////
private final static int CONN = 0;
private final static int DELE = 1;
private final static int LISTA = 2;
private final static int LIST = 3;
private final static int NOOP = 4;
private final static int PASS = 5;
private final static int QUIT = 6;
private final static int RSET = 7;
private final static int RETR = 8;
private final static int SENDCOMMANDA = 9;
private final static int SENDCOMMAND = 10;
private final static int STAT = 11;
private final static int TOP = 12;
private final static int UIDLA = 13;
private final static int UIDL = 14;
private final static int USER = 15;
private final static int XAUTHLISTA = 16;
private final static int XAUTHLIST = 17;
private final static int XSENDER = 18;
private final static Integer CONN_OBJ = new Integer(CONN);
private final static Integer DELE_OBJ = new Integer(DELE);
private final static Integer LISTA_OBJ = new Integer(LISTA);
private final static Integer LIST_OBJ = new Integer(LIST);
private final static Integer NOOP_OBJ = new Integer(NOOP);
private final static Integer PASS_OBJ = new Integer(PASS);
private final static Integer QUIT_OBJ = new Integer(QUIT);
private final static Integer RSET_OBJ = new Integer(RSET);
private final static Integer RETR_OBJ = new Integer(RETR);
private final static Integer SENDCOMMANDA_OBJ = new Integer(SENDCOMMANDA);
private final static Integer SENDCOMMAND_OBJ = new Integer(SENDCOMMAND);
private final static Integer STAT_OBJ = new Integer(STAT);
private final static Integer TOP_OBJ = new Integer(TOP);
private final static Integer UIDLA_OBJ = new Integer(UIDLA);
private final static Integer UIDL_OBJ = new Integer(UIDL);
private final static Integer USER_OBJ = new Integer(USER);
private final static Integer XAUTHLISTA_OBJ = new Integer(XAUTHLISTA);
private final static Integer XAUTHLIST_OBJ = new Integer(XAUTHLIST);
private final static Integer XSENDER_OBJ = new Integer(XSENDER);
///////////////////////////////////////////////////////////////////////////
// Error messages for POP3.
///////////////////////////////////////////////////////////////////////////
private final static String errProcessResponses = new String("Must call processResponses()");
private final static String errParse = new String("Error while parsing response");
/**
*Constructor for POP3Client that takes a POP3Sink as a parameter.
*@param in_sink Response sink that implements the IPOP3Sink interface.
*@see #IPOP3Sink
*/
public POP3Client( IPOP3Sink in_sink )
{
///////////////////////////////////////////////////////////////////////
// Variables that get initialized once in the constructor.
///////////////////////////////////////////////////////////////////////
m_io = new IO();
m_common = new Common();
m_timeout = 0;
m_pendingList = new Vector();
m_notifySink = in_sink;
m_response = new byte[m_maxReplyLine];
m_itoaBuffer = new byte[m_maxReplyLine];
m_tokenBuffer = new byte[m_maxReplyLine];
m_messageData = new byte[m_minChunkSize];
m_responseLine = new StringBuffer( m_maxReplyLine );
m_responseMessage = new StringBuffer( m_maxReplyLine );
///////////////////////////////////////////////////////////////////////
// Variables that get initialized in the constructor and on every new
// connection.
///////////////////////////////////////////////////////////////////////
m_multiLineState = false;
}
///////////////////////////////////////////////////////////////////////////
// Protocol commands
///////////////////////////////////////////////////////////////////////////
/**
*Connects to the server using the default port.
*<p>To specify the connection port, use the
*form of connect that takes a port number.
*@param in_server Name of the host server to connect to.
*@exception IOException If an I/O error occurs.
*@see IPOP3Sink#connect
*@see IPOP3Sink#error
*@see #quit
*@see #connect
*/
public synchronized void connect( String in_server ) throws IOException
{
connect( in_server, m_defaultPort );
}
/**
*Connects to the server using the specified port.
*<p>To use the default connection port, use the
*form of connect that takes only the server name.
*@param in_server Name of the host server to connect to.
*@param in_port Number of the port to connect to.
*@exception IOException If an I/O error occurs.
*@see IPOP3Sink#connect
*@see IPOP3Sink#error
*@see #quit
*@see #connect
*/
public synchronized void connect( String in_server, int in_port ) throws IOException
{
///////////////////////////////////////////////////////////////////////
// Error checking before proceeding with command.
///////////////////////////////////////////////////////////////////////
if ( in_server == null || in_port < 0 )
{
throw new IllegalArgumentException();
}
m_multiLineState = false;
m_pendingList.removeAllElements();
m_io.connect( in_server, in_port, m_messageData.length, 1024 );
m_io.setTimeout( m_timeout );
m_pendingList.addElement( CONN_OBJ );
m_mustProcess = true;
}
/**
*Deletes a message.
*Marks a message for deletion on the server;
*actually deleted when quit() is called.
*@param in_messageNumber Number of the message to delete.
*@exception IOException If an IO error occurs.
*@see IPOP3Sink#delete
*@see IPOP3Sink#error
*@see #quit
*/
public synchronized void delete( int in_messageNumber ) throws IOException
{
///////////////////////////////////////////////////////////////////////
// Error checking before proceeding with command.
///////////////////////////////////////////////////////////////////////
if ( m_mustProcess )
{
throw new POP3Exception(errProcessResponses);
}
m_io.write( m_dele );
m_io.send( m_itoaBuffer, 0, m_common.itoa(in_messageNumber, m_itoaBuffer), false );
m_pendingList.addElement( DELE_OBJ );
m_mustProcess = true;
}
/**
*Closes the socket connection with the server.
*Can be used to perform a high level "Cancel" while
*retrieving a message.
*NOTE: Do not call processResponses() after this method.
*@exception IOException If an I/O error occurs.
*@see IPOP3Sink#delete
*@see #quit
*/
public synchronized void disconnect() throws IOException
{
m_io.disconnect();
}
/**
*Lists all messages.
*POP3 includes two forms of LIST. This form goes through all
*the messages in the mailbox and generates a list of messages.
*The other form takes the message number of the message to
*list as an argument.
*@exception IOException If an I/O error occurs.
*@see IPOP3Sink#listStart
*@see IPOP3Sink#list
*@see IPOP3Sink#listComplete
*@see IPOP3Sink#error
*@see #list
*/
public synchronized void list() throws IOException
{
///////////////////////////////////////////////////////////////////////
// Error checking before proceeding with command.
///////////////////////////////////////////////////////////////////////
if ( m_mustProcess )
{
throw new POP3Exception(errProcessResponses);
}
m_io.send( m_list, false );
m_pendingList.addElement( LIST_OBJ );
m_mustProcess = true;
}
/**
*List a message.
*POP3 includes two forms of LIST.
*This form takes the message number of the message to list
*as an argument. The other form goes through all the messages
*in the mailbox and generates a list of messages.
*@param in_messageNumber The message number to list.
*@exception IOException If an I/O error occurs.
*@see IPOP3Sink#listStart
*@see IPOP3Sink#list
*@see IPOP3Sink#listComplete
*@see IPOP3Sink#error
*@see #list
*/
public synchronized void list( int in_messageNumber ) throws IOException
{
///////////////////////////////////////////////////////////////////////
// Error checking before proceeding with command.
///////////////////////////////////////////////////////////////////////
if ( m_mustProcess )
{
throw new POP3Exception(errProcessResponses);
}
m_io.write( m_list );
m_io.send( m_itoaBuffer, 0, m_common.itoa(in_messageNumber, m_itoaBuffer), false );
m_pendingList.addElement( LISTA_OBJ );
m_mustProcess = true;
}
/**
*Gets positive server response; does not affect POP3 session.
*Issues Noop command.
*<P>Server responds to commands with a "still here" response.
*Sending the noop method does nothing except force this server response.
*Can be used to maintain server connection, perhaps being issued
*at timed intervals to make sure that the server is still active.
*Not needed by applications that do not need to maintain the connection.
*@exception IOException If an I/O error occurs.
*@see IPOP3Sink#noop
*@see IPOP3Sink#error
*/
public synchronized void noop() throws IOException
{
///////////////////////////////////////////////////////////////////////
// Error checking before proceeding with command.
///////////////////////////////////////////////////////////////////////
if ( m_mustProcess )
{
throw new POP3Exception(errProcessResponses);
}
m_io.send( m_noop, false );
m_pendingList.addElement( NOOP_OBJ );
m_mustProcess = true;
}
/**
*Enters a password.
*Identifies the user password; on success, the POP3
*session enters the Transaction state.
*@param in_password User password.
*@exception IOException If an I/O error occurs.
*@see IPOP3Sink#pass
*@see IPOP3Sink#error
*@see #user
*/
public synchronized void pass( String in_password ) throws IOException
{
///////////////////////////////////////////////////////////////////////
// Error checking before proceeding with command.
///////////////////////////////////////////////////////////////////////
if ( in_password == null )
{
throw new IllegalArgumentException();
}
if ( m_mustProcess )
{
throw new POP3Exception(errProcessResponses);
}
m_io.write( m_pass );
m_io.send( in_password, false );
m_pendingList.addElement( PASS_OBJ );
m_mustProcess = true;
}
/**
*Closes the connection with the server and expunges any deleted messages.
*Ends the session, purging deleted messages and logging the user
*out from the POP3 server. If issued in Authentication state,
*server closes connections. If issued in Transaction state,
*server goes into Update state and deletes marked messages,
*then quits.
*@exception IOException If an I/O error occurs.
*@see IPO3Sink#quit
*@see IPOP3Sink#error
*@see #delete
*/
public synchronized void quit() throws IOException
{
///////////////////////////////////////////////////////////////////////
// Error checking before proceeding with command.
///////////////////////////////////////////////////////////////////////
if ( m_mustProcess )
{
throw new POP3Exception(errProcessResponses);
}
m_io.send( m_quit, false );
m_pendingList.addElement( QUIT_OBJ );
m_mustProcess = true;
}
/**
*Resets the state of the server by undeleting any deleted messages.
*Cancels the current mail transfer and all current processes,
*discards data, and clears all states.
*@exception IOException If an I/O error occurs.
*@see IPOP3Sink#reset
*@see IPOP3Sink#error
*/
public synchronized void reset() throws IOException
{
///////////////////////////////////////////////////////////////////////
// Error checking before proceeding with command.
///////////////////////////////////////////////////////////////////////
if ( m_mustProcess )
{
throw new POP3Exception(errProcessResponses);
}
m_io.send( m_rset, false );
m_pendingList.addElement( RSET_OBJ );
m_mustProcess = true;
}
/**
*Retrieves a message.
*@param in_messageNumber Number of message to retrieve.
*@exception IOException If an I/O error occurs.
*@see IPOP3Sink#retrieve
*@see IPOP3Sink#retrieveComplete
*@see IPOP3Sink#retrieveStart
*@see IPOP3Sink#error
*/
public synchronized void retrieve( int in_messageNumber ) throws IOException
{
///////////////////////////////////////////////////////////////////////
// Error checking before proceeding with command.
///////////////////////////////////////////////////////////////////////
if ( m_mustProcess )
{
throw new POP3Exception(errProcessResponses);
}
m_io.write( m_retr );
m_io.send(m_itoaBuffer, 0, m_common.itoa(in_messageNumber, m_itoaBuffer), false);
m_pendingList.addElement( RETR_OBJ );
m_currentMessageNumber = in_messageNumber;
m_mustProcess = true;
}
/**
*Sends an unsupported command to the server.
*Sends commands that are not supported by the Messaging Access SDK
*implementation of POP3.
*NOTE: This method is primarily intended to support extensions
*to the protocol to meet client application needs.
*@param in_command Raw command to send to the server.
*@param in_multiLine Identifies whether response is multiline or not.
*@exception IOException If an I/O error occurs.
*@see IPOP3Sink#sendCommand
*@see IPOP3Sink#sendCommandStart
*@see IPOP3Sink#sendCommandComplete
*@see IPOP3Sink#error
*/
public synchronized void sendCommand( String in_command, boolean in_multiLine ) throws IOException
{
///////////////////////////////////////////////////////////////////////
// Error checking before proceeding with command.
///////////////////////////////////////////////////////////////////////
if ( in_command == null )
{
throw new IllegalArgumentException();
}
if ( m_mustProcess )
{
throw new POP3Exception(errProcessResponses);
}
m_io.send( in_command, false );
if ( in_multiLine )
{
m_pendingList.addElement( SENDCOMMAND_OBJ );
}
else
{
m_pendingList.addElement( SENDCOMMANDA_OBJ );
}
m_mustProcess = true;
}
/**
*Gets the status of the mail drop.
*Returns the number of messages and octet size of the mail drop.
*Always returns message octet count.
*@exception IOException If an I/O error occurs.
*@see IPOP3Sink#stat
*@see IPOP3Sink#error
*/
public synchronized void stat() throws IOException
{
///////////////////////////////////////////////////////////////////////
// Error checking before proceeding with command.
///////////////////////////////////////////////////////////////////////
if ( m_mustProcess )
{
throw new POP3Exception(errProcessResponses);
}
m_io.send( m_stat, false );
m_pendingList.addElement( STAT_OBJ );
m_mustProcess = true;
}
/**
*Retrieves headers plus the specified number of lines from the body.
*@param in_messageNumber Number of message to retrieve.
*@param in_lines Number of lines of the message body to return.
*@exception IOException If an I/O error occurs.
*@see IPOP3Sink#topStart
*@see IPOP3Sink#top
*@see IPOP3Sink#topComplete
*@see IPOP3Sink#error
*/
public synchronized void top( int in_messageNumber, int in_lines ) throws IOException
{
///////////////////////////////////////////////////////////////////////
// Error checking before proceeding with command.
///////////////////////////////////////////////////////////////////////
if ( m_mustProcess )
{
throw new POP3Exception(errProcessResponses);
}
m_io.write( m_top );
m_io.write( m_itoaBuffer, 0, m_common.itoa(in_messageNumber, m_itoaBuffer) );
m_io.write( m_space );
m_io.send( m_itoaBuffer, 0, m_common.itoa(in_lines, m_itoaBuffer), false );
m_pendingList.addElement( TOP_OBJ );
m_currentMessageNumber = in_messageNumber;
m_mustProcess = true;
}
/**
*Lists all messages with their unique-ids.
*POP3 includes two forms of uidList(). This form goes through all
*the messages in the mailbox and generates a list of messages
*with their unique ids.
*The other form takes the message number of the message to
*list as an argument.
*@exception IOException If an I/O error occurs.
*@see IPOP3Sink#uidList
*@see IPOP3Sink#uidListStart
*@see IPOP3Sink#uidListComplete
*@see IPOP3Sink#error
*/
public synchronized void uidList() throws IOException
{
///////////////////////////////////////////////////////////////////////
// Error checking before proceeding with command.
///////////////////////////////////////////////////////////////////////
if ( m_mustProcess )
{
throw new POP3Exception(errProcessResponses);
}
m_io.send( m_uidl, false );
m_pendingList.addElement( UIDL_OBJ );
m_mustProcess = true;
}
/**
*Lists a message with its unique id.
*POP3 includes two forms of uidList(). This form takes the message number of the message to
*list with its unique id as an argument.
*The other form goes through all
*the messages in the mailbox and generates a list of messages.
*@param in_messageNumber Number of message for which to return a unique id. .
*@exception IOException If an I/O error occurs.
*@see IPOP3Sink#uidList
*@see IPOP3Sink#uidListStart
*@see IPOP3Sink#uidListComplete
*@see IPOP3Sink#error
*/
public synchronized void uidList( int in_messageNumber ) throws IOException
{
///////////////////////////////////////////////////////////////////////
// Error checking before proceeding with command.
///////////////////////////////////////////////////////////////////////
if ( m_mustProcess )
{
throw new POP3Exception(errProcessResponses);
}
m_io.write( m_uidl );
m_io.send( m_itoaBuffer, 0, m_common.itoa(in_messageNumber, m_itoaBuffer), false );
m_pendingList.addElement( UIDLA_OBJ );
m_mustProcess = true;
}
/**
*Enters a username.
*Identifies the user or mail drop by name to the server;
*the server returns a known or unknown response.
*@param in_user Name of the user's maildrop.
*@exception IOException If an I/O error occurs.
*@see IPOP3Sink#user
*@see IPOP3Sink#error
*@see #pass
*/
public synchronized void user( String in_user ) throws IOException
{
///////////////////////////////////////////////////////////////////////
// Error checking before proceeding with command.
///////////////////////////////////////////////////////////////////////
if ( in_user == null )
{
throw new IllegalArgumentException();
}
if ( m_mustProcess )
{
throw new POP3Exception(errProcessResponses);
}
m_io.write( m_user );
m_io.send( in_user, false );
m_pendingList.addElement( USER_OBJ );
m_mustProcess = true;
}
/**
*Returns a list of authenticated users.
*@exception IOException If an I/O error occurs.
*@see IPOP3Sink#xAuthList
*@see IPOP3Sink#xAuthListStart
*@see IPOP3Sink#xAuthListComplete
*@see IPOP3Sink#error
*/
public synchronized void xAuthList() throws IOException
{
///////////////////////////////////////////////////////////////////////
// Error checking before proceeding with command.
///////////////////////////////////////////////////////////////////////
if ( m_mustProcess )
{
throw new POP3Exception(errProcessResponses);
}
m_io.send( m_xauthlist, false );
m_pendingList.addElement( XAUTHLIST_OBJ );
m_mustProcess = true;
}
/**
*Returns a list of authenticated users for a specified message.
*@param in_messageNumber The message number.
*@exception IOException If an I/O error occurs.
*@see IPOP3Sink#xAuthList
*@see IPOP3Sink#xAuthListStart
*@see IPOP3Sink#xAuthListComplete
*@see IPOP3Sink#error
*/
public synchronized void xAuthList( int in_messageNumber ) throws IOException
{
///////////////////////////////////////////////////////////////////////
// Error checking before proceeding with command.
///////////////////////////////////////////////////////////////////////
if ( m_mustProcess )
{
throw new POP3Exception(errProcessResponses);
}
m_io.write( m_xauthlist );
m_io.send(m_itoaBuffer, 0, m_common.itoa(in_messageNumber, m_itoaBuffer), false);
m_pendingList.addElement( XAUTHLISTA_OBJ );
m_mustProcess = true;
}
/**
*Gets the email address of the sender of the specified message.
*Gets the email address of the sender of the specified message.
*Sends the XSENDER [arg] POP3 protocol command.
*@param in_messageNumber Number of the message.
*@exception IOException If an I/O error occurs.
*@see IPOP3Sink#xSender
*@see IPOP3Sink#error
*/
public synchronized void xSender( int in_messageNumber ) throws IOException
{
///////////////////////////////////////////////////////////////////////
// Error checking before proceeding with command.
///////////////////////////////////////////////////////////////////////
if ( m_mustProcess )
{
throw new POP3Exception(errProcessResponses);
}
m_io.write( m_xsender );
m_io.send(m_itoaBuffer, 0, m_common.itoa(in_messageNumber, m_itoaBuffer), false);
m_pendingList.addElement( XSENDER_OBJ );
m_mustProcess = true;
}
/**
*Reads in responses from the server and invokes the appropriate sink methods.
*Processes the server responses for API commands. Invokes the
*callback methods provided by the user for all responses that
*are available at the time of execution.
*NOTE: If a timeout occurs, the user can continue by calling processResponses() again.
*@return int The number of responses processed.
*@exception POP3ServerException If a server response error occurs.
*@exception InterruptedIOException If a time-out occurs.
*@exception IOException If an I/O error occurs.
*@see IPOP3Sink#error
*/
public void processResponses() throws IOException
{
synchronized(m_responseMessage)
{
try
{
Integer l_commandType;
m_io.flush();
///////////////////////////////////////////////////////////////////
// Prcoess as many of the responses as possible.
///////////////////////////////////////////////////////////////////
while ( !m_pendingList.isEmpty() )
{
l_commandType = (Integer)m_pendingList.firstElement();
switch( l_commandType.intValue() )
{
case CONN:
parseConnect();
break;
case DELE:
parseDelete();
break;
case LISTA:
parseListA();
break;
case LIST:
parseList();
break;
case NOOP:
parseNoop();
break;
case PASS:
parsePass();
break;
case QUIT:
parseQuit();
m_io.disconnect();
break;
case RSET:
parseRset();
break;
case RETR:
parseRetr();
break;
case SENDCOMMANDA:
parseSendCommandA();
break;
case SENDCOMMAND:
parseSendCommand();
break;
case STAT:
parseStat();
break;
case TOP:
parseTop();
break;
case UIDLA:
parseUidlA();
break;
case UIDL:
parseUidl();
break;
case USER:
parseUser();
break;
case XAUTHLISTA:
parseXAuthListA();
break;
case XAUTHLIST:
parseXAuthList();
break;
case XSENDER:
parseXSender();
break;
}
m_pendingList.removeElementAt( 0 );
}
m_mustProcess = false;
}
catch( POP3ServerException e )
{
m_mustProcess = false;
m_pendingList.removeElementAt( 0 );
throw e;
}
}
}
///////////////////////////////////////////////////////////////////////////
// Preferences
///////////////////////////////////////////////////////////////////////////
/**
*Sets the size of the data chunks that are passed to the response sink
*for the retrieve() callback. The chunk size must be >= 1024.
*@param in_chunkSize Size of chunk used for sending messages using
*the send method. Minimum chunk size: 1024. Default = 1K.
*@exception IOException If an I/O error occurs.
*@see #retrieve
*/
public synchronized void setChunkSize( int in_chunkSize )
{
synchronized(m_responseMessage)
{
if ( in_chunkSize < m_minChunkSize )
{
throw new IllegalArgumentException();
}
m_messageData = new byte[in_chunkSize];
}
}
/**
*Registers a new response sink, overriding the one passed into the
*constructor or any set afterwards.
*Server reponses are routed to this new response sink.
*@param in_responseSink New POP3 response sink to use.
*@exception IOException If an I/O error occurs.
*/
public synchronized void setResponseSink( IPOP3Sink in_responseSink )
{
synchronized(m_responseMessage)
{
if ( in_responseSink == null )
{
throw new NullPointerException();
}
m_notifySink = in_responseSink;
}
}
/**
*Sets the amount of time allowed to wait before
*returning control to the user.
*@param in_timeout Time-out period to set in milliseconds.
*Values, in milliseconds:
*<ul>
*<li>0 = infinite time-out (default)
*<li>-1 = no waiting
*<li>> 0 = length of time-out period
</ul>
*@exception IOException If an I/O error occurs.
*@see #processResponses
*/
public synchronized void setTimeout( int in_timeout ) throws IOException
{
synchronized(m_responseMessage)
{
if ( in_timeout < -1 )
{
throw new IllegalArgumentException();
}
m_timeout = in_timeout;
if ( m_io != null )
{
m_io.setTimeout( m_timeout );
}
}
}
///////////////////////////////////////////////////////////////////////////
// Internal functions
///////////////////////////////////////////////////////////////////////////
/**
*Separates the response code from the message part of the response.
*/
private final boolean setStatusInfo(
byte[] in_response,
int in_byteCount,
StringBuffer out_responseMessage ) throws IOException
{
boolean l_responseStatus;
if ( in_byteCount < 3 )
{
throw new POP3Exception(errParse);
}
if ( '+' == (char)in_response[0] &&
'O' == (char)in_response[1] &&
'K' == (char)in_response[2] )
{
l_responseStatus = true;
}
else
{
l_responseStatus = false;
}
if ( in_byteCount > 4 )
{
m_responseMessage.setLength( 0 );
for ( int count = 4; count < in_byteCount; count++ )
{
m_responseMessage.append( (char)m_response[count] );
}
}
else
{
m_responseMessage.setLength( 0 );
}
return l_responseStatus;
}
/**
*Copies the next token into the specified byte array.
*/
private final int getNextToken(
byte[] in_parseLine,
int in_offset,
byte[] out_token ) throws IOException
{
int i;
int n = 0;
for ( i = in_offset; Character.isSpaceChar((char)in_parseLine[i]); i++ )
{
}
while ( i < in_parseLine.length &&
Character.isDigit((char)in_parseLine[i]) )
{
out_token[n++] = in_parseLine[i++];
}
out_token[n] = '\0';
return i;
}
/**
*Copies the bytes in a byte array into a StringBuffer.
*/
private final void toStringBuffer( StringBuffer out_buffer,
byte[] in_buffer,
int offset,
int in_length )
{
out_buffer.setLength( in_length );
for ( int i = 0; i < in_length; i++ )
{
out_buffer.setCharAt( i, (char)in_buffer[offset+i] );
}
}
///////////////////////////////////////////////////////////////////////////
// Methods for parsing the responses and invoking sink notifications.
///////////////////////////////////////////////////////////////////////////
private final void parseConnect() throws IOException
{
int l_byteCount = m_io.readLine( m_response );
if ( setStatusInfo( m_response, l_byteCount, m_responseMessage ) )
{
m_notifySink.connect( m_responseMessage );
}
else
{
m_notifySink.error( m_responseMessage );
}
}
private final void parseDelete() throws IOException
{
int l_byteCount = m_io.readLine( m_response );
if ( setStatusInfo( m_response, l_byteCount, m_responseMessage ) )
{
m_notifySink.delete( m_responseMessage );
}
else
{
m_notifySink.error( m_responseMessage );
}
}
private final void parseListA() throws IOException
{
int l_offset = 3;
int l_messageNumber;
int l_octetCount;
int l_byteCount = m_io.readLine( m_response );
if ( setStatusInfo( m_response, l_byteCount, m_responseMessage ) )
{
m_notifySink.listStart();
l_offset = getNextToken( m_response, l_offset, m_tokenBuffer );
l_messageNumber = m_common.atoi( m_tokenBuffer );
l_offset = getNextToken( m_response, l_offset, m_tokenBuffer );
l_octetCount = m_common.atoi( m_tokenBuffer );
m_notifySink.list( l_messageNumber, l_octetCount );
m_notifySink.listComplete();
}
else
{
m_notifySink.error( m_responseMessage );
}
}
private final void parseList() throws IOException
{
int l_offset = 0;
int l_messageNumber;
int l_octetCount;
int l_byteCount;
boolean l_responseStatus;
if ( m_multiLineState == false )
{
l_byteCount = m_io.readLine( m_response );
if ( setStatusInfo(m_response, l_byteCount, m_responseMessage) )
{
m_notifySink.listStart();
}
else
{
m_notifySink.error( m_responseMessage );
return;
}
m_multiLineState = true;
}
l_byteCount = m_io.readLine( m_response );
while( ('.' != (char)m_response[0]) || ( l_byteCount != 1 ) )
{
l_offset = getNextToken( m_response, l_offset, m_tokenBuffer );
l_messageNumber = m_common.atoi( m_tokenBuffer );
l_offset = getNextToken( m_response, l_offset, m_tokenBuffer );
l_octetCount = m_common.atoi( m_tokenBuffer );
l_offset = 0;
m_notifySink.list( l_messageNumber, l_octetCount );
l_byteCount = m_io.readLine( m_response );
}
m_multiLineState = false;
m_notifySink.listComplete();
}
private final void parseNoop() throws IOException
{
int l_byteCount = m_io.readLine( m_response );
if ( setStatusInfo( m_response, l_byteCount, m_responseMessage ) )
{
m_notifySink.noop();
}
else
{
m_notifySink.error( m_responseMessage );
}
}
private final void parsePass() throws IOException
{
int l_byteCount = m_io.readLine( m_response );
if ( setStatusInfo( m_response, l_byteCount, m_responseMessage ) )
{
m_notifySink.pass( m_responseMessage );
}
else
{
m_notifySink.error( m_responseMessage );
}
}
private final void parseQuit() throws IOException
{
int l_byteCount = m_io.readLine( m_response );
if ( setStatusInfo( m_response, l_byteCount, m_responseMessage ) )
{
m_notifySink.quit( m_responseMessage );
}
else
{
m_notifySink.error( m_responseMessage );
}
}
private final void parseRset() throws IOException
{
int l_byteCount = m_io.readLine( m_response );
if ( setStatusInfo( m_response, l_byteCount, m_responseMessage ) )
{
m_notifySink.reset( m_responseMessage );
}
else
{
m_notifySink.error( m_responseMessage );
}
}
private final void parseRetr() throws IOException
{
int l_nextByte;
int l_byteCount;
int l_curByteCount;
int l_octetCount;
boolean l_responseStatus;
if ( m_multiLineState == false )
{
l_byteCount = m_io.readLine( m_response );
if ( setStatusInfo(m_response, l_byteCount, m_responseMessage) )
{
int ret_offset = getNextToken( m_response, 3, m_tokenBuffer );
if (ret_offset != 3) // there was an octetCount after +OK
{
l_octetCount = m_common.atoi (m_tokenBuffer);
m_notifySink.retrieveStart (m_currentMessageNumber, l_octetCount);
}
else
{
m_notifySink.retrieveStart (m_currentMessageNumber, 0);
}
}
else
{
m_notifySink.error( m_responseMessage );
return;
}
m_multiLineState = true;
m_messageDataSize = 0;
}
m_io.readLine( m_responseLine );
m_responseLine.append( '\r' );
m_responseLine.append( '\n' );
l_byteCount = m_responseLine.length();
while( ( m_responseLine.charAt(0) != '.') || ( l_byteCount != 3 ) )
{
l_curByteCount = 0;
if ( m_responseLine.charAt(0) == '.' )
{
l_curByteCount++;
}
while ( l_curByteCount < l_byteCount )
{
if ( m_messageDataSize == m_messageData.length )
{
m_notifySink.retrieve( m_messageData );
m_messageDataSize = 0;
}
m_messageData[m_messageDataSize++] = (byte)m_responseLine.charAt(l_curByteCount++);
}
m_io.readLine( m_responseLine );
m_responseLine.append( '\r' );
m_responseLine.append( '\n' );
l_byteCount = m_responseLine.length();
}
if ( m_messageDataSize > 0 )
{
byte[] l_buffer = new byte[m_messageDataSize];
for ( int i = 0; i < m_messageDataSize; i++ )
{
l_buffer[i] = m_messageData[i];
}
m_notifySink.retrieve( l_buffer );
}
m_multiLineState = false;
m_notifySink.retrieveComplete();
}
private final void parseSendCommandA() throws IOException
{
int l_byteCount = m_io.readLine( m_response );
if ( setStatusInfo( m_response, l_byteCount, m_responseMessage ) )
{
m_notifySink.sendCommandStart();
m_notifySink.sendCommand( m_responseMessage );
m_notifySink.sendCommandComplete();
}
else
{
m_notifySink.error( m_responseMessage );
}
}
private final void parseSendCommand() throws IOException
{
int l_byteCount;
boolean l_responseStatus;
if ( m_multiLineState == false )
{
l_byteCount = m_io.readLine( m_response );
if ( setStatusInfo(m_response, l_byteCount, m_responseMessage) )
{
m_notifySink.listStart();
}
else
{
m_notifySink.error( m_responseMessage );
return;
}
m_multiLineState = true;
}
l_byteCount = m_io.readLine( m_response );
while( ('.' != (char)m_response[0]) && ( l_byteCount != 1 ) )
{
toStringBuffer( m_responseMessage, m_response, 0, l_byteCount );
m_notifySink.sendCommand( m_responseMessage );
m_io.readLine( m_response );
}
m_multiLineState = false;
m_notifySink.sendCommandComplete();
}
private final void parseStat() throws IOException
{
int l_offset = 3;
int l_messageCount;
int l_octetCount;
int l_byteCount = m_io.readLine( m_response );
if ( setStatusInfo(m_response, l_byteCount, m_responseMessage) )
{
l_offset = getNextToken( m_response, l_offset, m_tokenBuffer );
l_messageCount = m_common.atoi( m_tokenBuffer );
l_offset = getNextToken( m_response, l_offset, m_tokenBuffer );
l_octetCount = m_common.atoi( m_tokenBuffer );
m_notifySink.stat( l_messageCount, l_octetCount );
}
else
{
m_notifySink.error( m_responseMessage );
}
}
private final void parseTop() throws IOException
{
int l_byteCount;
boolean l_responseStatus;
if ( m_multiLineState == false )
{
l_byteCount = m_io.readLine( m_response );
if ( setStatusInfo(m_response, l_byteCount, m_responseMessage) )
{
m_notifySink.topStart( m_currentMessageNumber );
}
else
{
m_notifySink.error( m_responseMessage );
return;
}
m_multiLineState = true;
}
l_byteCount = m_io.readLine( m_response );
while( ('.' != (char)m_response[0]) && ( l_byteCount != 1 ) )
{
if ( '.' != (char)m_response[0] )
{
toStringBuffer( m_responseMessage, m_response, 0, l_byteCount );
}
else
{
toStringBuffer( m_responseMessage, m_response, 1, l_byteCount - 1 );
}
m_notifySink.top( m_responseMessage );
l_byteCount = m_io.readLine( m_response );
}
m_multiLineState = false;
m_notifySink.topComplete();
}
private final void parseUidlA() throws IOException
{
int l_offset = 3;
int l_messageNumber;
int l_byteCount = m_io.readLine( m_response );
if ( setStatusInfo(m_response, l_byteCount, m_responseMessage) )
{
m_notifySink.uidListStart();
l_offset = getNextToken( m_response, l_offset, m_tokenBuffer );
l_messageNumber = m_common.atoi( m_tokenBuffer );
getNextToken( m_response, l_offset, m_tokenBuffer );
toStringBuffer( m_responseMessage, m_tokenBuffer, 0, l_byteCount - l_offset );
m_notifySink.uidList( l_messageNumber, m_responseMessage );
m_notifySink.uidListComplete();
}
else
{
m_notifySink.error( m_responseMessage );
}
}
private final void parseUidl() throws IOException
{
int l_offset = 0;
int l_byteCount;
int l_messageNumber;
boolean l_responseStatus;
if ( m_multiLineState == false )
{
l_byteCount = m_io.readLine( m_response );
if ( setStatusInfo(m_response, l_byteCount, m_responseMessage) )
{
m_notifySink.uidListStart();
}
else
{
m_notifySink.error( m_responseMessage );
return;
}
m_multiLineState = true;
}
l_byteCount = m_io.readLine( m_response );
while( ('.' != (char)m_response[0]) && ( l_byteCount != 1 ) )
{
l_offset = getNextToken( m_response, l_offset, m_tokenBuffer );
l_messageNumber = m_common.atoi( m_tokenBuffer );
getNextToken( m_response, l_offset, m_tokenBuffer );
toStringBuffer( m_responseMessage, m_tokenBuffer, 0, l_byteCount - l_offset );
l_offset = 0;
m_notifySink.uidList( l_messageNumber, m_responseMessage );
l_byteCount = m_io.readLine( m_response );
}
m_multiLineState = false;
m_notifySink.uidListComplete();
}
private final void parseUser() throws IOException
{
int l_byteCount = m_io.readLine( m_response );
if ( setStatusInfo(m_response, l_byteCount, m_responseMessage) )
{
m_notifySink.user( m_responseMessage );
}
else
{
m_notifySink.error( m_responseMessage );
}
}
private final void parseXAuthListA() throws IOException
{
int l_offset = 3;
int l_octetCount;
int l_messageNumber;
int l_byteCount = m_io.readLine( m_response );
if ( setStatusInfo(m_response, l_byteCount, m_responseMessage) )
{
m_notifySink.xAuthListStart();
l_offset = getNextToken( m_response, l_offset, m_tokenBuffer );
l_messageNumber = m_common.atoi( m_tokenBuffer );
l_offset = getNextToken( m_response, l_offset, m_tokenBuffer );
l_octetCount = m_common.atoi( m_tokenBuffer );
getNextToken( m_response, l_offset, m_tokenBuffer );
toStringBuffer( m_responseMessage, m_tokenBuffer, 0, l_byteCount - l_offset );
m_notifySink.xAuthList( l_messageNumber, l_octetCount, m_responseMessage );
m_notifySink.xAuthListComplete();
}
else
{
m_notifySink.error( m_responseMessage );
}
}
private final void parseXAuthList() throws IOException
{
int l_offset = 0;
int l_byteCount;
int l_octetCount;
int l_messageNumber;
boolean l_responseStatus;
if ( m_multiLineState == false )
{
l_byteCount = m_io.readLine( m_response );
if ( setStatusInfo(m_response, l_byteCount, m_responseMessage) )
{
m_notifySink.xAuthListStart();
}
else
{
m_notifySink.error( m_responseMessage );
return;
}
m_multiLineState = true;
}
l_byteCount = m_io.readLine( m_response );
while( ('.' != (char)m_response[0]) && ( l_byteCount != 1 ) )
{
l_offset = getNextToken( m_response, l_offset, m_tokenBuffer );
l_messageNumber = m_common.atoi( m_tokenBuffer );
l_offset = getNextToken( m_response, l_offset, m_tokenBuffer );
l_octetCount = m_common.atoi( m_tokenBuffer );
getNextToken( m_response, l_offset, m_tokenBuffer );
toStringBuffer( m_responseMessage, m_tokenBuffer, 0, l_byteCount - l_offset );
l_offset = 0;
m_notifySink.xAuthList( l_messageNumber, l_octetCount, m_responseMessage );
l_byteCount = m_io.readLine( m_response );
}
m_multiLineState = false;
m_notifySink.xAuthListComplete();
}
private final void parseXSender() throws IOException
{
int l_byteCount = m_io.readLine( m_response );
if ( setStatusInfo(m_response, l_byteCount, m_responseMessage) )
{
m_notifySink.xSender( m_responseMessage );
}
else
{
m_notifySink.error( m_responseMessage );
}
}
}