/* * The contents of this file are subject to the Netscape Public License * Version 1.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.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.imap4; import java.io.*; import java.net.*; import java.util.*; import java.lang.reflect.*; import netscape.messaging.IO; /** *The IMAP4Client exposes the IMAP4 client level API. *The client uses this class for communicating with a *server using the IMAP4 protocol. *

The IMAP4Client class contains API calls that map *directly to the client commands defined in RFC 2060, *as well as other API calls that are necessary to establish *and maintain an IMAP4 session. *

For a list of the IMAP4 RFCs referenced in the *Messaging Access SDK and their URLs, *see "Where to Find More Information" in "About This Book." */ public class IMAP4Client { /** * Creates an IMAP4_Client object. * Default constructor for an IMAP4_Client object; * takes an IMAP4 response sink as a parameter. *@param in_sink Response sink that implements the IIMAP4Sink interface. * @see IIMAP4Sink#taggedLine * @see IIMAP4Sink#error */ public IMAP4Client(IIMAP4Sink in_sink) { //Internal objects m_IOHandler = new IO(); m_systemPreferences = new SystemPreferences(); m_systemPreferences.setIO(m_IOHandler); m_pendingCommands = new Vector(); m_commandVector = new Vector(); m_firstResponse = true; m_tagID = 0; m_processBlock = new Integer(1); //Parsing variables m_sink = in_sink; m_dispatcher = null; m_serverResponse = new StringBuffer(); m_tokens = new StringBuffer[3]; m_parserArgs = new StringBuffer[1]; for(int i = 0; i < m_tokens.length; i++) { m_tokens[i] = new StringBuffer(); } //Note: The hashtables are static so there is only one set of hashtables //for n connections and are defined under data members } /////////////////////////////////////////////// //General Commands /** * Connects to the IMAP server in_IMAPHost using the default port (143). *

To specify the connection port, use the *form of connect that takes a port number. * @param in_IMAPHost Name of the host server to connect to. * @return Boolean true if successfully connected, false otherwise. * @exception IOException If an I/O error occurs. * @see #connect * @see #disconnect * @see IIMAP4Sink#taggedLine * @see IIMAP4Sink#error * @see IIMAP4Sink#ok */ public synchronized boolean connect(String in_IMAPHost) throws IOException { return connect(in_IMAPHost, m_systemPreferences.getPort()); } /** * Connects to the IMAP server in_IMAPHost through the specified port. *

To use the default connection port, use the *form of connect that takes only the server name. * @param in_IMAPHost Name of the host server to connect to. * @param in_portNumber Number of the port to connect to. * @return Boolean value: true if successfully connected, false otherwise. * @exception IOException If an I/O error occurs. * @see #disconnect * @see IIMAP4Sink#taggedLine * @see IIMAP4Sink#error * @see IIMAP4Sink#ok */ public synchronized boolean connect(String in_IMAPHost, int in_portNumber) throws IOException { //Check parameters if(in_IMAPHost == null) { throw new IllegalArgumentException(); } //Establish the IMAP connection m_IOHandler.connect(in_IMAPHost, in_portNumber); m_IOHandler.setTimeout(m_systemPreferences.getTimeout()); m_dispatcher = new Dispatcher(m_IOHandler, m_sink, m_pendingCommands, m_systemPreferences); m_pendingCommands.addElement(IGlobals.connect); m_firstResponse = true; m_tagID = 0; return 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 #connect * @see IIMAP4Sink#bye * @see IIMAP4Sink#taggedLine * @see IIMAP4Sink#error */ public synchronized void disconnect() throws IOException { m_IOHandler.disconnect(); m_pendingCommands.removeAllElements(); m_tagID = 0; } /** * Processes the server responses for all API commands executed prior to * the invocation of this command. * Calls the appropriate functions on the response * sink to relay the results back to the user. * Checks for responses that are buffered at the socket. * It then reads, parses, and pushes the data through the IIMAP4Sink. * NOTE: Synchronized internally. * @exception IOException If an I/O error occurs. * @see IIMAP4Sink#taggedLine * @see IIMAP4Sink#error */ public void processResponses() throws IOException { synchronized (m_processBlock) { String l_responseType = null; Method l_responseParser = null; char l_tokenChar; //Parse server response and route data through the appropriate //methods in the response sink while(!m_pendingCommands.isEmpty()) { m_IOHandler.readLine(m_serverResponse); //Special case for connect if(m_firstResponse) { m_pendingCommands.removeElement(IGlobals.connect); } //Initialize the tokens array for(int i = 0; i < m_tokens.length; i++) { m_tokens[i].setLength(0); } //Tokenize the server response for(int i = 0, j = 0; i < m_serverResponse.length(); i++) { l_tokenChar = m_serverResponse.charAt(i); if(l_tokenChar == ' ') { j++; if(j >= m_tokens.length) { break; } else { continue; } } m_tokens[j].append(l_tokenChar); } //Parse the server response and dispatch to appropriate method on sink try { l_responseType = resolveType(m_tokens); if(l_responseType.equals(IGlobals.BYE)) { m_pendingCommands.removeAllElements(); m_tagID = 0; } m_parserArgs[0] = m_serverResponse; l_responseParser = (Method)m_responseMapper.get(l_responseType); l_responseParser.invoke(m_dispatcher, m_parserArgs); } catch(InvocationTargetException e) { Throwable l_exception = e.getTargetException(); if(l_exception instanceof IMAP4ServerException) { throw (IMAP4ServerException)l_exception; } else if(l_exception instanceof IMAP4Exception) { throw (IMAP4Exception)l_exception; } l_exception.printStackTrace(); throw (new IOException(IGlobals.DispatcherError + " " + l_exception.getMessage())); } catch(IllegalAccessException e) { throw (new IOException(IGlobals.DispatcherError + " " + e.getMessage())); } } } } /** * Sends the specified command to the IMAP4 server. * Allows the developer to extend the default commands supplied * through the SDK. Equivalent to writing the value of * the in_command parameter directly to the socket. * Do not prepend a tag to the command; this is added * internally. The server response is pushed back through * IIMAP4Sink.rawResponse(). *

Success does not indicate successful completion of the * command, just that it has been invoked on the IMAP4 Server. * Finding out if the command executed successfully requires * using IMAP4Sink.rawResponse() to examine the returned data. * @param in_command The command to send to the IMAP server. * @return String The tag associated with the command; * FALSE otherwise. * @exception IOException If an I/O error occurs. * @see IIMAP4Sink#rawResponse * @see IIMAP4Sink#rawResponse * @see IIMAP4Sink#taggedLine * @see IIMAP4Sink#error */ public synchronized String sendCommand(String in_command) throws IOException { //Check parameters if(in_command == null) { throw new IllegalArgumentException(); } m_commandVector.addElement(in_command); return sendCommand(m_commandVector); } /** * Sends in_command to the IMAP server. * Allows the developer to extend the default commands supplied * through the SDK. Equivalent to writing the value of * the in_command parameter directly to the socket. * Do not prepend a tag to the command; this is added * internally. The server response is pushed back through * IIMAP4Sink.rawResponse(). *

Success does not indicate successful completion of the * command, just that it has been invoked on the IMAP4 Server. * Finding out if the command executed successfully requires * using IMAP4Sink.rawResponse() to examine the returned data. * @param in_command The command to send to the IMAP server * @return String The tag associated with the command; * FALSE otherwise. * @exception IOException If an I/O error occurs. * @see IIMAP4Sink#rawResponse * @see IIMAP4Sink#taggedLine * @see IIMAP4Sink#error */ protected synchronized String sendCommand(Vector in_command) throws IOException { String l_tag = null; //Prepend a tag id and send the command m_tagID++; l_tag = String.valueOf(m_tagID); in_command.insertElementAt(IGlobals.Space, 0); in_command.insertElementAt(l_tag, 0); in_command.addElement(IGlobals.CarriageNewLine); try { m_IOHandler.write(in_command); m_IOHandler.flush(); m_pendingCommands.addElement(l_tag); in_command.removeAllElements(); } catch(IOException e) { in_command.removeAllElements(); throw e; } return l_tag; } /** * Retrieves the system preferences. * @return SystemPreferences Object representing the system preferences. * @see IIMAP4Sink#taggedLine * @see IIMAP4Sink#error */ public synchronized SystemPreferences getSystemPreferences() { return m_systemPreferences; } /** * Sets a new response sink to route the server responses to. * @param in_sink New IMAP4 response sink to use. * @exception IOException If an I/O error occurs. * @see #getResponseSink * @see IIMAP4Sink#taggedLine * @see IIMAP4Sink#error */ public synchronized void setResponseSink(IIMAP4Sink in_sink) throws IOException { synchronized (m_processBlock) { //Check parameters if(in_sink == null) { throw new IllegalArgumentException(); } m_sink = in_sink; m_dispatcher.setResponseSink(m_sink); } } /** * Retrieves the response sink. * @return IIMAP4Sink Response sink to which to route server responses. * @see #setResponseSink * @see IIMAP4Sink#taggedLine * @see IIMAP4Sink#error */ public synchronized IIMAP4Sink getResponseSink() { return m_sink; } //////////////////////////////////////////////////// //The non-authenticated state /** * Logs in to server with user name and password. * Identifies the client to the server and carries the plain text password * authenticating this user. Logs in the user after establishing a connection * to the IMAP4 server, which passes the user * password over the network in the clear. * @param in_user The user name. * @param in_password The password of this user. * @return String The tag associated with this command. * @exception IOException If an I/O error occurs. * @see #logout * @see IIMAP4Sink#taggedLine * @see IIMAP4Sink#error * @see IIMAP4Sink#bye */ public synchronized String login(String in_user, String in_password) throws IOException { //Check parameters if((in_user == null) || (in_password == null)) { throw new IllegalArgumentException(); } m_commandVector.addElement(IGlobals.LOGIN); m_commandVector.addElement(in_user); m_commandVector.addElement(IGlobals.Space); m_commandVector.addElement(in_password); return sendCommand(m_commandVector); } /** * Informs the server that the client is done with the connection. * Closes the connection and logs the user out. * @return String The tag associated with this command. * @exception IOException If an I/O error occurs. * @see #logout * @see IIMAP4Sink#taggedLine * @see IIMAP4Sink#error * @see IIMAP4Sink#bye */ public synchronized String logout() throws IOException { //Ending session.. close connection m_commandVector.addElement(IGlobals.LOGOUT); return sendCommand(m_commandVector); } /** * Gets positive server response; does not affect IMAP4 session. *

The 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. *

Can be used to poll for mail or get new mail. Most servers look for * new mail whenever they receive a command. This method does nothing * in itself, so it is ideal for triggering this server action. *

Resets the autologout timer inside the server; may change the * number of messages in a mailbox (due to new mail). * May change or update the status of messages in mailboxes. * Not needed by applications that do something and disconnect at once. * @return String The tag associated with this command. * @exception IOException If an I/O error occurs. * @see IIMAP4Sink#noop * @see IIMAP4Sink#exists * @see IIMAP4Sink#expunge * @see IIMAP4Sink#recent * @see IIMAP4Sink#fetchStart * @see IIMAP4Sink#fetchFlags * @see IIMAP4Sink#fetchEnd * @see IIMAP4Sink#taggedLine * @see IIMAP4Sink#error */ public synchronized String noop() throws IOException { m_commandVector.addElement(IGlobals.NOOP); return sendCommand(m_commandVector); } /** * Requests a listing of capabilities that the server supports. * @return String The tag associated with this command. * @exception IOException If an I/O error occurs. * @see #connect * @see IIMAP4Sink#capability * @see IIMAP4Sink#taggedLine * @see IIMAP4Sink#error */ public synchronized String capability() throws IOException { m_commandVector.addElement(IGlobals.CAPABILITY); return sendCommand(m_commandVector); } //////////////////////////////////////////////////// //The authenticated state /** * Appends the literal argument as a new message to the end of the * specified destination mailbox. * @param in_mailbox Mailbox name. * @param in_optFlags Parenthesized list of optional flags. * @param in_optDateTime Optional date/time string. * @param in_inputLiteralStream The message literal. * @return String The command object and the (available) server response. * @exception IOException If an I/O error occurs. * @see IIMAP4Sink#taggedLine * @see IIMAP4Sink#error */ public synchronized String append(String in_mailbox, String in_optFlags, String in_optDateTime, InputStream in_inputLiteralStream) throws IOException { //Check parameters if((in_mailbox == null) || (in_inputLiteralStream == null)) { throw new IllegalArgumentException(); } if(m_dispatcher == null) { throw new IOException(errNotConnected); } //Note: This is a command continuation request. The subsequent response //to the server's request for the message data is handled in Dispatcher.plus String l_flags = in_optFlags; String l_dateTime = in_optDateTime; int l_totalSize = in_inputLiteralStream.available(); //Format the optional parameters if(l_flags != null) { formatOptionalParam(l_flags); } else { l_flags = IGlobals.EmptyString; } if(l_dateTime != null) { formatOptionalParam(l_dateTime); } else { l_dateTime = IGlobals.EmptyString; } //Pass the input stream to the dispatcher m_dispatcher.setAppendStream(in_inputLiteralStream); //Issue the command m_commandVector.addElement(IGlobals.APPEND); m_commandVector.addElement(IGlobals.Space); m_commandVector.addElement(in_mailbox); m_commandVector.addElement(IGlobals.Space); m_commandVector.addElement(l_flags); m_commandVector.addElement(l_dateTime); m_commandVector.addElement(IGlobals.LCurly); m_commandVector.addElement(String.valueOf(l_totalSize)); m_commandVector.addElement(IGlobals.RCurly); try { return sendCommand(m_commandVector); } catch(IOException e) { m_commandVector.removeAllElements(); throw e; } } /** * Creates a new mailbox with the given name. * Be careful to use the correct separator character if you want to create * a directory on the server side. * You cannot and should not CREATE an INBOX. * It is always there. * @param in_mailbox Name of the mailbox to create. * @return String The tag associated with this command. * @exception IOException If an I/O error occurs. * @see IIMAP4Sink#taggedLine * @see IIMAP4Sink#error */ public synchronized String create(String in_mailbox) throws IOException { //Check parameters if(in_mailbox == null) { throw new IllegalArgumentException(); } m_commandVector.addElement(IGlobals.CREATE); m_commandVector.addElement(in_mailbox); return sendCommand(m_commandVector); } /** * PERMANENTLY removes the mailbox with the given name. * You can not and should not DELETE the INBOX. The system needs it. * @param in_mailbox Name of the mailbox to delete. * @return String The tag associated with this command * @exception IOException If an I/O error occurs. * @see IIMAP4Sink#taggedLine * @see IIMAP4Sink#error */ public synchronized String delete(String in_mailbox) throws IOException { //Check parameters if(in_mailbox == null) { throw new IllegalArgumentException(); } m_commandVector.addElement(IGlobals.DELETE); m_commandVector.addElement(in_mailbox); return sendCommand(m_commandVector); } /** * Selects a server mailbox for read-only access. * @param in_mailbox Name of the mailbox to examine. * @return String The tag associated with this command. * @exception IOException If an I/O error occurs. * @see IIMAP4Sink#flags * @see IIMAP4Sink#exists * @see IIMAP4Sink#recent * @see IIMAP4Sink#ok * @see IIMAP4Sink#taggedLine * @see IIMAP4Sink#error */ public synchronized String examine(String in_mailbox) throws IOException { //Check parameters if(in_mailbox == null) { throw new IllegalArgumentException(); } m_commandVector.addElement(IGlobals.EXAMINE); m_commandVector.addElement(IGlobals.Space); m_commandVector.addElement(in_mailbox); return sendCommand(m_commandVector); } /** * Lists a subset of all names available * to the client. * @param in_refName String with reference name (prefix). * @param in_mailbox String with mailbox name (suffix). * @return String The tag associated with this command. * @exception IOException If an I/O error occurs. * @see IIMAP4Sink#list * @see IIMAP4Sink#taggedLine * @see IIMAP4Sink#error */ public synchronized String list(String in_refName, String in_mailbox) throws IOException { //Check parameters if((in_refName == null) || (in_mailbox == null)) { throw new IllegalArgumentException(); } m_commandVector.addElement(IGlobals.LIST); m_commandVector.addElement(IGlobals.Space); m_commandVector.addElement(in_refName); m_commandVector.addElement(IGlobals.Space); m_commandVector.addElement(in_mailbox); return sendCommand(m_commandVector); } /** * Return a subset of all names that the user has * declared as being "active" or "subscribed". * @param in_refName Reference name. * @param in_mailbox Mailbox name with possible wildcards. * @return String The tag associated with this command. * @exception IOException If an I/O error occurs. * @see IIMAP4Sink#lsub * @see IIMAP4Sink#taggedLine * @see IIMAP4Sink#error */ public synchronized String lsub(String in_refName, String in_mailbox) throws IOException { //Check parameters if((in_refName == null) || (in_mailbox == null)) { throw new IllegalArgumentException(); } m_commandVector.addElement(IGlobals.LSUB); m_commandVector.addElement(IGlobals.Space); m_commandVector.addElement(in_refName); m_commandVector.addElement(IGlobals.Space); m_commandVector.addElement(in_mailbox); return sendCommand(m_commandVector); } /** * Renames a mailbox. * @param in_currentMB String with the mailbox's current name. * @param in_newMB String with the mailbox's new name. * @return String The tag associated with this command * @exception IOException If an I/O error occurs. * @see IIMAP4Sink#taggedLine * @see IIMAP4Sink#error */ public synchronized String rename(String in_currentMB,String in_newMB) throws IOException { //Check parameters if((in_currentMB == null) || (in_newMB == null)) { throw new IllegalArgumentException(); } m_commandVector.addElement(IGlobals.RENAME); m_commandVector.addElement(in_currentMB); m_commandVector.addElement(IGlobals.Space); m_commandVector.addElement(in_newMB); return sendCommand(m_commandVector); } /** * Selects a mailbox on the server. * @param in_mailbox Name of the mailbox. * @return String The tag associated with this command. * @exception IOException If an I/O error occurs. * @see #examine * @see IIMAP4Sink#flags * @see IIMAP4Sink#exists * @see IIMAP4Sink#recent * @see IIMAP4Sink#ok * @see IIMAP4Sink#taggedLine * @see IIMAP4Sink#error */ public synchronized String select(String in_mailbox) throws IOException { //Check parameters if(in_mailbox == null) { throw new IllegalArgumentException(); } m_commandVector.addElement(IGlobals.SELECT); m_commandVector.addElement(IGlobals.Space); m_commandVector.addElement(in_mailbox); return sendCommand(m_commandVector); } /** * Requests the status of the specified mailbox. * Requests one or more types of status, as specified by the data items parameter. * @param in_mailbox Name of the mailbox. * @param in_statusData Status data items. * @return String The tag associated with this command * @exception IOException If an I/O error occurs. * @see IIMAP4Sink#statusMessages * @see IIMAP4Sink#statusRecent * @see IIMAP4Sink#statusUidnext * @see IIMAP4Sink#statusUidvalidity * @see IIMAP4Sink#statusUnseen * @see IIMAP4Sink#taggedLine * @see IIMAP4Sink#error */ public synchronized String status(String in_mailbox, String in_statusData) throws IOException { //Check parameters if((in_mailbox == null) || (in_statusData == null)) { throw new IllegalArgumentException(); } m_commandVector.addElement(IGlobals.STATUS); m_commandVector.addElement(IGlobals.Space); m_commandVector.addElement(in_mailbox); m_commandVector.addElement(IGlobals.Space); m_commandVector.addElement(in_statusData); return sendCommand(m_commandVector); } /** * Adds the specified mailbox name to the server's set of "active" or * "subscribed" mailboxes. * This list is accessed through the lsub method. * @param in_mailbox Name of the mailbox to add. * @return String The tag associated with this command * @exception IOException If an I/O error occurs. * @see #unsubscribe * @see #lsub * @see IIMAP4Sink#taggedLine * @see IIMAP4Sink#error */ public synchronized String subscribe(String in_mailbox) throws IOException { //Check parameters if(in_mailbox == null) { throw new IllegalArgumentException(); } m_commandVector.addElement(IGlobals.SUBSCRIBE); m_commandVector.addElement(IGlobals.Space); m_commandVector.addElement(in_mailbox); return sendCommand(m_commandVector); } /** * Removes the specified mailbox name from the server's set of "active" or * "subscribed" mailboxes. * @param in_mailbox Name of the mailbox to remove. * @return String The tag associated with this command * @exception IOException If an I/O error occurs. * @see #unsubscribe * @see IIMAP4Sink#taggedLine * @see IIMAP4Sink#error */ public synchronized String unsubscribe(String in_mailbox) throws IOException { //Check parameters if(in_mailbox == null) { throw new IllegalArgumentException(); } m_commandVector.addElement(IGlobals.UNSUBSCRIBE); m_commandVector.addElement(IGlobals.Space); m_commandVector.addElement(in_mailbox); return sendCommand(m_commandVector); } //////////////////////////////////////////////////// //The selected state /** * Requests a checkpoint of the currently selected mailbox. * Also flushes existing mailbox states to disk. * Actual method behavior depends on server implementation. * @return String The tag associated with this command. * @exception IOException If an I/O error occurs. * @see IIMAP4Sink#taggedLine * @see IIMAP4Sink#error */ public synchronized String check() throws IOException { m_commandVector.addElement(IGlobals.CHECK); return sendCommand(m_commandVector); } /** * Closes the mailbox. * Also removes any messages marked with the \Deleted flag. * The session moves to the mailbox that contained the closed mailbox. * The expunge() method removes messages without closing. * @return String The tag associated with this command. * @exception IOException If an I/O error occurs. * @see #expunge * @see IIMAP4Sink#taggedLine * @see IIMAP4Sink#error */ public synchronized String close() throws IOException { m_commandVector.addElement(IGlobals.CLOSE); return sendCommand(m_commandVector); } /** * Copies the specified message(s) from the currently selected mailbox * to the end of the specified destination mailbox. * @param in_msgSet Message numbers to copy (for example, "1:2"). * @param in_mailbox Name of target mailbox. * @return String The tag associated with this command. * @exception IOException If an I/O error occurs. * @see #uidCopy * @see IIMAP4Sink#taggedLine * @see IIMAP4Sink#error */ public synchronized String copy(String in_msgSet, String in_mailbox) throws IOException { //Check parameters if((in_msgSet == null) || (in_mailbox == null)) { throw new IllegalArgumentException(); } m_commandVector.addElement(IGlobals.COPY); m_commandVector.addElement(IGlobals.Space); m_commandVector.addElement(in_msgSet); m_commandVector.addElement(IGlobals.Space); m_commandVector.addElement(in_mailbox); return sendCommand(m_commandVector); } /** * Removes all messages flagged for deletion. * Deletes all the messages in the * mailbox with the \Deleted flag set. As described in RFC 1730, if there is * an EXPUNGEd message, all messages with higher numbers are automatically * decremented by one. This happens BEFORE this call returns, * so, immediately following this call, you should think about * what the state of the message cache might be. * @return String The tag associated with this command * @exception IOException If an I/O error occurs. * @see IIMAP4Sink#expunge * @see #delete * @see IIMAP4Sink#taggedLine * @see IIMAP4Sink#error */ public synchronized String expunge() throws IOException { m_commandVector.addElement(IGlobals.EXPUNGE); return sendCommand(m_commandVector); } /** * Retrieves data specified by the fetch criteria for the given message * set and returns the corresponding message sequence numbers. * @param in_msgSet Message set. * @param in_fetchCriteria Message data item names. * @return String The tag associated with this command. * @exception IOException If an I/O error occurs. * @see #uidFetch * @see IIMAP4Sink#fetchStart * @see IIMAP4Sink#fetchEnd * @see IIMAP4Sink#fetchSize * @see IIMAP4Sink#fetchData * @see IIMAP4Sink#fetchFlags * @see IIMAP4Sink#fetchBodyStructure * @see IIMAP4Sink#fetchEnvelope * @see IIMAP4Sink#fetchInternalDate * @see IIMAP4Sink#fetchHeader * @see IIMAP4Sink#fetchUid * @see IIMAP4Sink#taggedLine * @see IIMAP4Sink#error */ public synchronized String fetch(String in_msgSet, String in_fetchCriteria) throws IOException { //Check parameters if((in_msgSet == null) || (in_fetchCriteria == null)) { throw new IllegalArgumentException(); } m_commandVector.addElement(IGlobals.FETCH); m_commandVector.addElement(IGlobals.Space); m_commandVector.addElement(in_msgSet); m_commandVector.addElement(IGlobals.Space); m_commandVector.addElement(in_fetchCriteria); return sendCommand(m_commandVector); } /** * Searches the mailbox for messages that match the given search * criteria. * @param in_criteria Search criteria consisting of one or more search keys. * @return String The tag associated with this command. * @exception IOException If an I/O error occurs. * @see IIMAP4Sink#search * @see #uidSearch * @see IIMAP4Sink#searchStart * @see IIMAP4Sink#search * @see IIMAP4Sink#searchEnd * @see IIMAP4Sink#taggedLine * @see IIMAP4Sink#error */ public synchronized String search(String in_criteria) throws IOException { //Check parameters if(in_criteria == null) { throw new IllegalArgumentException(); } m_commandVector.addElement(IGlobals.SEARCH); m_commandVector.addElement(IGlobals.Space); m_commandVector.addElement(in_criteria); return sendCommand(m_commandVector); } /** * Alters data associated with a message in the mailbox. * @param in_msgSet Message set. * @param in_dataItem Message data item name. * @param in_value Value for the message data item. * @return String The tag associated with this command. * @exception IOException If an I/O error occurs. * @see #uidStore * @see IIMAP4Sink#taggedLine * @see IIMAP4Sink#error */ public synchronized String store(String in_msgSet, String in_dataItem, String in_value) throws IOException { //Check parameters if((in_msgSet == null) || (in_dataItem == null) || (in_value == null)) { throw new IllegalArgumentException(); } m_commandVector.addElement(IGlobals.STORE); m_commandVector.addElement(IGlobals.Space); m_commandVector.addElement(in_msgSet); m_commandVector.addElement(IGlobals.Space); m_commandVector.addElement(in_dataItem); m_commandVector.addElement(IGlobals.Space); m_commandVector.addElement(in_value); return sendCommand(m_commandVector); } /** * Copies messages specified with a unique identifier. * Performs a UID COPY. * @param in_msgSet Message numbers to copy (for example, "1:2"). * @param in_mailbox Name of target mailbox. * @return String The tag associated with this command. * @exception IOException If an I/O error occurs. * @see #copy * @see IIMAP4Sink#taggedLine * @see IIMAP4Sink#error */ public synchronized String uidCopy(String in_msgSet, String in_mailbox) throws IOException { //Check parameters if((in_msgSet == null) || (in_mailbox == null)) { throw new IllegalArgumentException(); } m_commandVector.addElement(IGlobals.UID); m_commandVector.addElement(IGlobals.Space); m_commandVector.addElement(IGlobals.COPY); m_commandVector.addElement(IGlobals.Space); m_commandVector.addElement(in_msgSet); m_commandVector.addElement(IGlobals.Space); m_commandVector.addElement(in_mailbox); return sendCommand(m_commandVector); } /** * Fetches messages specified with a unique identifier * and retrieve the corresponding unique identifiers. * Performs a UID FETCH. * @param in_msgSet The message set * @param in_fetchCriteria The message data item names * @return String The tag associated with this command * @exception IOException If an I/O error occurs. * @see #fetch * @see IIMAP4Sink#fetchStart * @see IIMAP4Sink#fetchEnd * @see IIMAP4Sink#fetchSize * @see IIMAP4Sink#fetchData * @see IIMAP4Sink#fetchFlags * @see IIMAP4Sink#fetchBodyStructure * @see IIMAP4Sink#fetchEnvelope * @see IIMAP4Sink#fetchInternalDate * @see IIMAP4Sink#fetchHeader * @see IIMAP4Sink#fetchUid * @see IIMAP4Sink#taggedLine * @see IIMAP4Sink#error */ public synchronized String uidFetch(String in_msgSet, String in_fetchCriteria) throws IOException { //Check parameters if((in_msgSet == null) || (in_fetchCriteria == null)) { throw new IllegalArgumentException(); } m_commandVector.addElement(IGlobals.UID); m_commandVector.addElement(IGlobals.Space); m_commandVector.addElement(IGlobals.FETCH); m_commandVector.addElement(IGlobals.Space); m_commandVector.addElement(in_msgSet); m_commandVector.addElement(IGlobals.Space); m_commandVector.addElement(in_fetchCriteria); return sendCommand(m_commandVector); } /** * * Stores a message specified by a unique identifier. * Performs a UID STORE. * @param in_set Message set. * @param in_dataItem Message data item name. * @param in_value Value for the message data item. * @return String The tag associated with this command. * @exception IOException If an I/O error occurs. * @see #store * @see IIMAP4Sink#taggedLine * @see IIMAP4Sink#error */ public synchronized String uidStore(String in_msgSet, String in_dataItem, String in_value) throws IOException { //Check parameters if((in_msgSet == null) || (in_dataItem == null) || (in_value == null)) { throw new IllegalArgumentException(); } m_commandVector.addElement(IGlobals.UID); m_commandVector.addElement(IGlobals.Space); m_commandVector.addElement(IGlobals.STORE); m_commandVector.addElement(IGlobals.Space); m_commandVector.addElement(in_msgSet); m_commandVector.addElement(IGlobals.Space); m_commandVector.addElement(in_dataItem); m_commandVector.addElement(IGlobals.Space); m_commandVector.addElement(in_value); return sendCommand(m_commandVector); } /** * Searches the mailbox for messages that match the search criteria * and retrieves the corresponding unique identifiers. * Performs a UID SEARCH. * @param in_criteria Search criteria consisting of one or more search keys. * @return String The tag associated with this command * @exception IOException If an I/O error occurs. * @see #search * @see IIMAP4Sink#searchStart * @see IIMAP4Sink#search * @see IIMAP4Sink#searchEnd * @see IIMAP4Sink#taggedLine * @see IIMAP4Sink#error */ public synchronized String uidSearch(String in_criteria) throws IOException { //Check parameters if(in_criteria == null) { throw new IllegalArgumentException(); } m_commandVector.addElement(IGlobals.UID); m_commandVector.addElement(IGlobals.Space); m_commandVector.addElement(IGlobals.SEARCH); m_commandVector.addElement(IGlobals.Space); m_commandVector.addElement(in_criteria); return sendCommand(m_commandVector); } //////////////////////////////////////////////////////////////////// // Extended Commands //////////////////////////////////////////////////////////////////// /** * Retrieves the prefixes of namespaces used by a server for * personal mailboxes, other user's mailboxes, and shared mailboxes. * @return String The tag associated with this command. * @exception IOException If an I/O error occurs. * @see IIMAP4Sink#nameSpaceStart * @see IIMAP4Sink#nameSpacePersonal * @see IIMAP4Sink#nameSpaceOtherUsers * @see IIMAP4Sink#nameSpaceShared * @see IIMAP4Sink#nameSpaceEnd * @see IIMAP4Sink#taggedLine * @see IIMAP4Sink#error */ public synchronized String nameSpace() throws IOException { m_commandVector.addElement(IGlobals.NAMESPACE); return sendCommand(m_commandVector); } /** * Changes the access control list on the specified mailbox. * The specified identifier is granted permissions specified in the * third argument * @param in_mailbox Name of the mailbox. * @param in_authId Authentication identifier. * @param in_accessRight Access rights modification. * @return String The tag associated with this command. * @exception IOException If an I/O error occurs. * @see deleteACL * @see #getACL * @see IIMAP4Sink#taggedLine * @see IIMAP4Sink#error */ public synchronized String setACL(String in_mailbox, String in_authId, String in_accessRight) throws IOException { //Check parameters if((in_mailbox == null) || (in_authId == null) || (in_accessRight == null)) { throw new IllegalArgumentException(); } m_commandVector.addElement(IGlobals.SETACL); m_commandVector.addElement(IGlobals.Space); m_commandVector.addElement(in_mailbox); m_commandVector.addElement(IGlobals.Space); m_commandVector.addElement(in_authId); m_commandVector.addElement(IGlobals.Space); m_commandVector.addElement(in_accessRight); return sendCommand(m_commandVector); } /** * Removes an pair for the specified identifier * from the access control list for the specified mailbox. * @param in_mailbox Name of the mailbox. * @param in_authId Authentication identifier. * @return String The tag associated with this command * @exception IOException If an I/O error occurs. * @see setACL * @see #getACL * @see IIMAP4Sink#taggedLine * @see IIMAP4Sink#error */ public synchronized String deleteACL(String in_mailbox, String in_authId) throws IOException { //Check parameters if((in_mailbox == null) || (in_authId == null)) { throw new IllegalArgumentException(); } m_commandVector.addElement(IGlobals.DELETEACL); m_commandVector.addElement(IGlobals.Space); m_commandVector.addElement(in_mailbox); m_commandVector.addElement(IGlobals.Space); m_commandVector.addElement(in_authId); return sendCommand(m_commandVector); } /** * Retrieves the access control list for the mailbox in an untagged ACL reply. * @param in_mailbox Name of the mailbox. * @return String The tag associated with this command. * @exception IOException If an I/O error occurs. * @see deleteACL * @see #setACL * @see IIMAP4Sink#aclStart * @see IIMAP4Sink#aclIdentifierRight * @see IIMAP4Sink#aclEnd * @see IIMAP4Sink#taggedLine * @see IIMAP4Sink#error */ public synchronized String getACL(String in_mailbox) throws IOException { //Check parameters if(in_mailbox == null) { throw new IllegalArgumentException(); } m_commandVector.addElement(IGlobals.GETACL); m_commandVector.addElement(IGlobals.Space); m_commandVector.addElement(in_mailbox); return sendCommand(m_commandVector); } /** * Retrieves the access control list for mailbox in an untagged ACL reply. * @param in_mailbox Name of the mailbox. * @param in_authId Authentication identifier. * @return String The tag associated with this command. * @exception IOException If an I/O error occurs. * @see #myRights * @see IIMAP4Sink#listRightsStart * @see IIMAP4Sink#listRightsRequiredRights * @see IIMAP4Sink#listRightsOptionalRights * @see IIMAP4Sink#listRightsEnd * @see IIMAP4Sink#taggedLine * @see IIMAP4Sink#error */ public synchronized String listRights(String in_mailbox, String in_authId) throws IOException { //Check parameters if((in_mailbox == null) || (in_authId == null)) { throw new IllegalArgumentException(); } m_commandVector.addElement(IGlobals.LISTRIGHTS); m_commandVector.addElement(IGlobals.Space); m_commandVector.addElement(in_mailbox); m_commandVector.addElement(IGlobals.Space); m_commandVector.addElement(in_authId); return sendCommand(m_commandVector); } /** * Retrieves the user’s rights to the specified mailbox. * @param in_mailbox Name of the mailbox. * @return String The tag associated with this command. * @exception IOException If an I/O error occurs. * @see #listRights * @see IIMAP4Sink#myRights * @see IIMAP4Sink#taggedLine * @see IIMAP4Sink#error */ public synchronized String myRights(String in_mailbox) throws IOException { //Check parameters if(in_mailbox == null) { throw new IllegalArgumentException(); } m_commandVector.addElement(IGlobals.MYRIGHTS); m_commandVector.addElement(IGlobals.Space); m_commandVector.addElement(in_mailbox); return sendCommand(m_commandVector); } //////////////////////////////////////////////////////////////////// // Internal Methods //////////////////////////////////////////////////////////////////// /** * Returns the token that represents the server response type. * @param in_tokens The first 3 tokens in the current server response line * @return StringBuffer The server response type or null if unknown type * @see IIMAP4Sink#taggedLine * @see IIMAP4Sink#error */ private String resolveType(StringBuffer in_tokens[]) { int l_token1Length = in_tokens[1].length(); int l_token2Length = in_tokens[2].length(); if((in_tokens[0].length() < 1) || (l_token1Length < 1)) { return IGlobals.UNKNOWN; } if('*' == in_tokens[0].charAt(0)) { switch(in_tokens[1].charAt(0)) { case 'C': { if( (l_token1Length > 9) && (in_tokens[1].charAt(1) == 'A') && (in_tokens[1].charAt(2) == 'P') && (in_tokens[1].charAt(3) == 'A') && (in_tokens[1].charAt(4) == 'B') && (in_tokens[1].charAt(5) == 'I') && (in_tokens[1].charAt(6) == 'L') && (in_tokens[1].charAt(7) == 'I') && (in_tokens[1].charAt(8) == 'T') && (in_tokens[1].charAt(9) == 'Y')) { return IGlobals.CAPABILITY; } break; } case 'L': { if( (l_token1Length > 9) && (in_tokens[1].charAt(1) == 'I') && (in_tokens[1].charAt(2) == 'S') && (in_tokens[1].charAt(3) == 'T') && (in_tokens[1].charAt(4) == 'R') && (in_tokens[1].charAt(5) == 'I') && (in_tokens[1].charAt(6) == 'G') && (in_tokens[1].charAt(7) == 'H') && (in_tokens[1].charAt(8) == 'T') && (in_tokens[1].charAt(9) == 'S')) { return IGlobals.LISTRIGHTS; } else if( (l_token1Length > 3) && (in_tokens[1].charAt(1) == 'I') && (in_tokens[1].charAt(2) == 'S') && (in_tokens[1].charAt(3) == 'T')) { return IGlobals.LIST; } else if((l_token1Length > 3) && (in_tokens[1].charAt(1) == 'S') && (in_tokens[1].charAt(2) == 'U') && (in_tokens[1].charAt(3) == 'B')) { return IGlobals.LSUB; } break; } case 'S': { if( (l_token1Length > 5) && (in_tokens[1].charAt(1) == 'T') && (in_tokens[1].charAt(2) == 'A') && (in_tokens[1].charAt(3) == 'T') && (in_tokens[1].charAt(4) == 'U') && (in_tokens[1].charAt(5) == 'S')) { return IGlobals.STATUS; } else if((l_token1Length > 5) && (in_tokens[1].charAt(1) == 'E') && (in_tokens[1].charAt(2) == 'A') && (in_tokens[1].charAt(3) == 'R') && (in_tokens[1].charAt(4) == 'C') && (in_tokens[1].charAt(5) == 'H')) { return IGlobals.SEARCH; } break; } case 'F': { if( (l_token1Length > 4) && (in_tokens[1].charAt(1) == 'L') && (in_tokens[1].charAt(2) == 'A') && (in_tokens[1].charAt(3) == 'G') && (in_tokens[1].charAt(4) == 'S')) { return IGlobals.FLAGS; } break; } case 'O': { if( (l_token1Length > 1) && (in_tokens[1].charAt(1) == 'K')) { return IGlobals.OK; } break; } case 'N': { if( (l_token1Length > 1) && (in_tokens[1].charAt(1) == 'O')) { return IGlobals.NO; } else if( (l_token1Length > 8) && (in_tokens[1].charAt(1) == 'A') && (in_tokens[1].charAt(2) == 'M') && (in_tokens[1].charAt(3) == 'E') && (in_tokens[1].charAt(4) == 'S') && (in_tokens[1].charAt(5) == 'P') && (in_tokens[1].charAt(6) == 'A') && (in_tokens[1].charAt(7) == 'C') && (in_tokens[1].charAt(8) == 'E')) { return IGlobals.NAMESPACE; } break; } case 'P': { if( (l_token1Length > 6) && (in_tokens[1].charAt(1) == 'R') && (in_tokens[1].charAt(2) == 'E') && (in_tokens[1].charAt(3) == 'A') && (in_tokens[1].charAt(4) == 'U') && (in_tokens[1].charAt(5) == 'T') && (in_tokens[1].charAt(6) == 'H')) { return IGlobals.PREAUTH; } break; } case 'B': { if( (l_token1Length > 2) && (in_tokens[1].charAt(1) == 'Y') && (in_tokens[1].charAt(2) == 'E')) { return IGlobals.BYE; } else if((l_token1Length > 2) && (in_tokens[1].charAt(1) == 'A') && (in_tokens[1].charAt(2) == 'D')) { return IGlobals.BAD; } break; } case 'A': { if( (l_token1Length > 2) && (in_tokens[1].charAt(1) == 'C') && (in_tokens[1].charAt(2) == 'L')) { return IGlobals.ACL; } break; } case 'M': { if( (l_token1Length > 7) && (in_tokens[1].charAt(1) == 'Y') && (in_tokens[1].charAt(2) == 'R') && (in_tokens[1].charAt(3) == 'I') && (in_tokens[1].charAt(4) == 'G') && (in_tokens[1].charAt(5) == 'H') && (in_tokens[1].charAt(6) == 'T') && (in_tokens[1].charAt(7) == 'S')) { return IGlobals.MYRIGHTS; } break; } default: { break; } } switch(in_tokens[2].charAt(0)) { case 'R': { if( (l_token2Length > 5) && (in_tokens[2].charAt(1) == 'E') && (in_tokens[2].charAt(2) == 'C') && (in_tokens[2].charAt(3) == 'E') && (in_tokens[2].charAt(4) == 'N') && (in_tokens[2].charAt(5) == 'T')) { return IGlobals.RECENT; } break; } case 'E': { if( (l_token2Length > 5) && (in_tokens[2].charAt(1) == 'X') && (in_tokens[2].charAt(2) == 'I') && (in_tokens[2].charAt(3) == 'S') && (in_tokens[2].charAt(4) == 'T') && (in_tokens[2].charAt(5) == 'S')) { return IGlobals.EXISTS; } else if((l_token2Length > 6) && (in_tokens[2].charAt(1) == 'X') && (in_tokens[2].charAt(2) == 'P') && (in_tokens[2].charAt(3) == 'U') && (in_tokens[2].charAt(4) == 'N') && (in_tokens[2].charAt(5) == 'G') && (in_tokens[2].charAt(6) == 'E')) { return IGlobals.EXPUNGE; } break; } case 'F': { if( (l_token2Length > 4) && (in_tokens[2].charAt(1) == 'E') && (in_tokens[2].charAt(2) == 'T') && (in_tokens[2].charAt(3) == 'C') && (in_tokens[2].charAt(4) == 'H')) { return IGlobals.FETCH; } break; } default: { break; } } } else if('+' == in_tokens[0].charAt(0)) { return IGlobals.PLUS; } else { //Check for valid tag int l_tag = m_dispatcher.atoi(in_tokens[0], 0, in_tokens[0].length()); if((l_tag > 0) && (l_tag <= m_tagID)) { switch(in_tokens[1].charAt(0)) { case 'O': { if( (l_token1Length > 1) && (in_tokens[1].charAt(1) == 'K')) { return IGlobals.taggedOK; } break; } case 'N': { if( (l_token1Length > 1) && (in_tokens[1].charAt(1) == 'O')) { return IGlobals.taggedNO; } break; } case 'B': { if( (l_token1Length > 2) && (in_tokens[1].charAt(1) == 'A') && (in_tokens[1].charAt(2) == 'D')) { return IGlobals.taggedBAD; } break; } default: { break; } } } } return IGlobals.UNKNOWN; } /** * Format optional parameters for command string construction. * @param out_optParam The optional parameter. * @see IIMAP4Sink#taggedLine * @see IIMAP4Sink#error */ private void formatOptionalParam(String out_optParam) { //Format the flag string out_optParam.trim(); if(out_optParam.length() > 0) { out_optParam += IGlobals.Space; } } //////////////////////////////////////////////////////////////////////// //Data members //////////////////////////////////////////////////////////////////////// /**A vector of strings representing the command*/ private Vector m_commandVector; /**The IO object*/ private IO m_IOHandler; /**Flags*/ private boolean m_firstResponse; /**The system preferences*/ private SystemPreferences m_systemPreferences; /** The tag id*/ private int m_tagID; protected Vector m_pendingCommands; /** Variables to assist the parsing of the server response */ private StringBuffer[] m_tokens; private StringBuffer m_serverResponse; private StringBuffer m_parserArgs[]; /** Used to synchronize processResponses block*/ private IIMAP4Sink m_sink; private Integer m_processBlock; public final static int ENVELOPE_DATE = 0; public final static int ENVELOPE_SUBJECT = 1; public final static int ENVELOPE_FROM = 2; public final static int ENVELOPE_SENDER = 3; public final static int ENVELOPE_REPLYTO = 4; public final static int ENVELOPE_TO = 5; public final static int ENVELOPE_CC = 6; public final static int ENVELOPE_BCC = 7; public final static int ENVELOPE_INREPLYTO = 8; public final static int ENVELOPE_MESSAGEID = 9; /** The dispatcher*/ private Dispatcher m_dispatcher; /** String constants*/ protected final static String errNotConnected = new String("Must connect to server"); /** Hashtables for mapping of response types to appropriate sink methods*/ private static Hashtable m_responseMapper; private static Class[] m_args = new Class[1]; static { try { //The hash tables m_responseMapper = new Hashtable(); //Initialize arguments m_args[0] = StringBuffer.class; //Command completed methods m_responseMapper.put(IGlobals.taggedOK, Dispatcher.class.getMethod("taggedLine", m_args)); m_responseMapper.put(IGlobals.taggedBAD, Dispatcher.class.getMethod("error", m_args)); m_responseMapper.put(IGlobals.taggedNO, Dispatcher.class.getMethod("error", m_args)); m_responseMapper.put(IGlobals.BAD, Dispatcher.class.getMethod("untaggedError", m_args)); m_responseMapper.put(IGlobals.NO, Dispatcher.class.getMethod("untaggedError", m_args)); m_responseMapper.put(IGlobals.OK, Dispatcher.class.getMethod("ok", m_args)); m_responseMapper.put(IGlobals.CAPABILITY, Dispatcher.class.getMethod("capability", m_args)); m_responseMapper.put(IGlobals.LIST, Dispatcher.class.getMethod("list", m_args)); m_responseMapper.put(IGlobals.LSUB, Dispatcher.class.getMethod("lsub", m_args)); m_responseMapper.put(IGlobals.STATUS, Dispatcher.class.getMethod("status", m_args)); m_responseMapper.put(IGlobals.SEARCH, Dispatcher.class.getMethod("search", m_args)); m_responseMapper.put(IGlobals.FLAGS, Dispatcher.class.getMethod("flags", m_args)); m_responseMapper.put(IGlobals.BYE, Dispatcher.class.getMethod("bye", m_args)); m_responseMapper.put(IGlobals.RECENT, Dispatcher.class.getMethod("recent", m_args)); m_responseMapper.put(IGlobals.EXISTS, Dispatcher.class.getMethod("exists", m_args)); m_responseMapper.put(IGlobals.EXPUNGE, Dispatcher.class.getMethod("expunge", m_args)); m_responseMapper.put(IGlobals.FETCH, Dispatcher.class.getMethod("fetch", m_args)); m_responseMapper.put(IGlobals.PLUS, Dispatcher.class.getMethod("plus", m_args)); m_responseMapper.put(IGlobals.NAMESPACE, Dispatcher.class.getMethod("namespace", m_args)); m_responseMapper.put(IGlobals.ACL, Dispatcher.class.getMethod("acl", m_args)); m_responseMapper.put(IGlobals.LISTRIGHTS, Dispatcher.class.getMethod("listrights", m_args)); m_responseMapper.put(IGlobals.MYRIGHTS, Dispatcher.class.getMethod("myrights", m_args)); m_responseMapper.put(IGlobals.UNKNOWN, Dispatcher.class.getMethod("rawResponse", m_args)); } catch(NoSuchMethodException e) { System.out.println("Fatal error: can't load functions " + e.getMessage()); } } }