/* * 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.imap4; import java.io.*; import java.net.*; import java.util.*; import netscape.messaging.IO; /** * Dispatcher parses incoming data and dispatches it to the appropriate * method on the sink */ class Dispatcher { /**Create a Dispatcher object*/ public Dispatcher( IO in_io, IIMAP4Sink in_sink, Vector in_pendingCommands, SystemPreferences in_systemPrefs) { int l_index = 0, i = 0; //Internal variables m_io = in_io; m_sink = in_sink; m_pendingCommands = in_pendingCommands; m_systemPrefs = in_systemPrefs; //Re-usable buffers m_data = new byte[m_systemPrefs.getBlockSize()]; m_buffers = new StringBuffer[5]; m_fieldValues = new StringBuffer[10]; for(l_index = 0; l_index < m_buffers.length; l_index++) { m_buffers[l_index] = new StringBuffer(); } //Note: The starting point for the index is dependent on how many //of the StringBuffers in m_buffers are used while processing //ENVELOPE.. only used in fetchEnvelope for(i = 0, l_index = 2; l_index < m_buffers.length; i++, l_index++) { m_fieldValues[i] = m_buffers[l_index]; } for(; i < m_fieldValues.length; i++) { m_fieldValues[i] = new StringBuffer(); } m_readHeader = false; m_appendStream = null; } /////////////////////////////////////////////////////////////////////////////// //Parsing methods for each server response type /** * Indicates the end of response(s) for successful commands * @param in_data The data to be parsed */ public void taggedLine(StringBuffer in_data) throws IMAP4Exception { parseTaggedLine(in_data); m_pendingCommands.removeElement((m_buffers[0]).toString()); m_sink.taggedLine(m_buffers[0], m_buffers[1], m_buffers[2]); } /** * Indicates the response for unsuccessful commands (error responses) * @param in_data The data to be parsed */ public void error(StringBuffer in_data) throws IMAP4Exception { parseTaggedLine(in_data); m_pendingCommands.removeElement((m_buffers[0]).toString()); m_sink.error(m_buffers[0], m_buffers[1], m_buffers[2]); } /** * Indicates an untagged error for NO and BAD * @param in_data The data to be parsed */ public void untaggedError(StringBuffer in_data) throws IMAP4Exception { int l_begin = 0, l_end = -1; //Initialize buffers for(int i = 0; i < m_buffers.length; i++) { m_buffers[i].setLength(0); } //Skip over the * l_end = indexOf(in_data, ' ', l_begin); if(l_end == -1) { throw new IMAP4Exception(IGlobals.UnexpectedServerData); } l_end++; l_begin = l_end; //This error is untagged m_buffers[0].append(IGlobals.UNTAGGED); //Store the status: NO or BAD l_end = storeSegment(in_data, m_buffers[1], ' ', l_begin); if(l_end == -1) { throw new IMAP4Exception(IGlobals.UnexpectedServerData); } l_end++; l_begin = l_end; //Store the human-readable information if((l_begin == -1) || (l_end == -1)) { l_begin = 0; l_begin = indexOf(in_data, ' ', l_begin); if(l_begin == -1) { throw new IMAP4Exception(IGlobals.UnexpectedServerData); } l_begin = indexOf(in_data, ' ', l_begin + 1); if(l_begin == -1) { throw new IMAP4Exception(IGlobals.UnexpectedServerData); } l_begin++; } else { l_begin = indexOf(in_data, ' ', l_end); if(l_begin == -1) { throw new IMAP4Exception(IGlobals.UnexpectedServerData); } l_begin++; } m_buffers[2].setLength(0); for(int i = l_begin; i < in_data.length(); i++) { m_buffers[2].append(in_data.charAt(i)); } m_sink.error(m_buffers[0], m_buffers[1], m_buffers[2]); } /** * Parses an unsolicited ok response * @param in_data The data to be parsed * @param IMAP4Exception A parsing error occured */ public void ok(StringBuffer in_data) throws IMAP4Exception { int l_begin = 0, l_end = -1; //Retrieve the response code m_buffers[0].setLength(0); l_begin = indexOf(in_data, '[', l_begin); if(l_begin != -1) { l_end = indexOf(in_data, ']', l_begin); if(l_end != -1) { for(int i = l_begin; i < (l_end + 1); i++) { m_buffers[0].append(in_data.charAt(i)); } } } //Store the human-readable information if((l_begin == -1) || (l_end == -1)) { l_begin = 0; l_begin = indexOf(in_data, ' ', l_begin); if(l_begin == -1) { throw new IMAP4Exception(IGlobals.UnexpectedServerData); } l_begin = indexOf(in_data, ' ', l_begin + 1); if(l_begin == -1) { throw new IMAP4Exception(IGlobals.UnexpectedServerData); } l_begin++; } else { l_begin = indexOf(in_data, ' ', l_end); if(l_begin == -1) { throw new IMAP4Exception(IGlobals.UnexpectedServerData); } l_begin++; } m_buffers[1].setLength(0); for(int i = l_begin; i < in_data.length(); i++) { m_buffers[1].append(in_data.charAt(i)); } m_sink.ok(m_buffers[0], m_buffers[1]); } /** * Parses the tagged line * @param in_data The data to be parsed */ protected void parseTaggedLine(StringBuffer in_data) throws IMAP4Exception { int l_begin = 0, l_end = -1; //Initialize buffers for(int i = 0; i < m_buffers.length; i++) { m_buffers[i].setLength(0); } //Store the tag l_end = storeSegment(in_data, m_buffers[0], ' ', l_begin); if(l_end == -1) { throw new IMAP4Exception(IGlobals.UnexpectedServerData); } l_end++; l_begin = l_end; //Store the status l_end = storeSegment(in_data, m_buffers[1], ' ', l_begin); if(l_end == -1) { throw new IMAP4Exception(IGlobals.UnexpectedServerData); } l_end++; l_begin = l_end; //Store the reason l_end = in_data.length(); for(int i = l_begin; i < l_end; i++) { m_buffers[2].append(in_data.charAt(i)); } } /** * The capability of the IMAP server * @param in_data The data to be parsed */ public void capability(StringBuffer in_data) throws IMAP4Exception { int l_begin = 0, l_end = -1; //Skip over the * l_end = indexOf(in_data, ' ', l_begin); if(l_end == -1) { throw new IMAP4Exception(IGlobals.UnexpectedServerData); } l_end++; l_begin = l_end; //Skip over CAPABILITY l_end = indexOf(in_data, ' ', l_begin); if(l_end == -1) { throw new IMAP4Exception(IGlobals.UnexpectedServerData); } l_end++; l_begin = l_end; //The rest is the capability listing m_buffers[0].setLength(0); l_end = in_data.length(); for(int i = l_begin; i < l_end; i++) { m_buffers[0].append(in_data.charAt(i)); } m_sink.capability(m_buffers[0]); } /** * Mailboxes that matched the search criteria * @param in_data The data to be parsed */ public void list(StringBuffer in_data) { m_buffers[0].setLength(0); m_buffers[1].setLength(0); m_buffers[2].setLength(0); extractMailboxInfo(in_data, m_buffers[0], m_buffers[1], m_buffers[2]); m_sink.list(m_buffers[0], m_buffers[1], m_buffers[2]); } /** * Subscribed or active mailboxes that matched the search criteria * @param in_data The data to be parsed */ public void lsub(StringBuffer in_data) { m_buffers[0].setLength(0); m_buffers[1].setLength(0); m_buffers[2].setLength(0); extractMailboxInfo(in_data, m_buffers[0], m_buffers[1], m_buffers[2]); m_sink.lsub(m_buffers[0], m_buffers[1], m_buffers[2]); } /** * The response to a status command * @param in_data The data to be parsed */ public void status(StringBuffer in_data) throws IMAP4Exception { int l_begin = 0, l_end = -1; //Skip over the * l_end = indexOf(in_data, ' ', l_begin); if(l_end == -1) { throw new IMAP4Exception(IGlobals.UnexpectedServerData); } l_end++; l_begin = l_end; //Skip over STATUS l_end = indexOf(in_data, ' ', l_begin); if(l_end == -1) { throw new IMAP4Exception(IGlobals.UnexpectedServerData); } l_end++; l_begin = l_end; //Skip the opening parenthesis l_end = indexOf(in_data, '(', l_begin); if(l_end == -1) { throw new IMAP4Exception(IGlobals.UnexpectedServerData); } l_end++; l_begin = l_end; while(l_begin < in_data.length()) { //Extract the data items m_buffers[0].setLength(0); m_buffers[1].setLength(0); l_begin = skipWhite(in_data, l_begin); l_end = storeSegment(in_data, m_buffers[0], ' ', l_begin); if(l_end == -1) { break; } l_end++; l_begin = l_end; //Extract the value of the data item l_begin = skipWhite(in_data, l_begin); l_end = storeSegment(in_data, m_buffers[1], ' ', l_begin); if(l_end == -1) { l_end = storeSegment(in_data, m_buffers[1], ')', l_begin); if(l_end == -1) { throw new IMAP4Exception(IGlobals.UnexpectedServerData); } } l_end++; l_begin = l_end; switch(resolveStatusDataItem(m_buffers[0])) { case IGlobals.StatusMessages: { m_sink.statusMessages(atoi(m_buffers[1], 0, m_buffers[1].length())); break; } case IGlobals.StatusRecent: { m_sink.statusRecent(atoi(m_buffers[1], 0, m_buffers[1].length())); break; } case IGlobals.StatusUidNext: { m_sink.statusUidnext(atoi(m_buffers[1], 0, m_buffers[1].length())); break; } case IGlobals.StatusUidValidity: { m_sink.statusUidvalidity(atoi(m_buffers[1], 0, m_buffers[1].length())); break; } case IGlobals.StatusUnSeen: { m_sink.statusUnseen(atoi(m_buffers[1], 0, m_buffers[1].length())); break; } default: { break; } } } } /** * The messages that matched the search criteria * @param in_data The data to be parsed */ public void search(StringBuffer in_data) throws IMAP4Exception { int l_begin = 0, l_end = -1; Object l_reference = null; //Skip over the * l_end = indexOf(in_data, ' ', l_begin); if(l_end == -1) { throw new IMAP4Exception(IGlobals.UnexpectedServerData); } l_end++; l_begin = l_end; //Skip over SEARCH l_end = indexOf(in_data, ' ', l_begin); if(l_end == -1) { l_end =in_data.length(); l_begin = l_end; } else { l_end++; l_begin = l_end; } l_reference = m_sink.searchStart(); while(l_begin < in_data.length()) { //Extract the message number m_buffers[0].setLength(0); l_end = storeSegment(in_data, m_buffers[0], ' ', l_begin); if(l_end == -1) { for(int i = l_begin; i < in_data.length(); i++) { m_buffers[0].append(in_data.charAt(i)); } l_end = in_data.length(); l_begin = l_end; } else { l_end++; l_begin = l_end; } m_sink.search(l_reference, atoi(m_buffers[0], 0, m_buffers[0].length())); } m_sink.searchEnd(l_reference); } /** * The flags that are applicable for the selected mailbox * @param in_data The data to be parsed */ public void flags(StringBuffer in_data) throws IMAP4Exception { int l_begin = 0, l_end = -1; m_buffers[0].setLength(0); //Skip over the * l_end = indexOf(in_data, ' ', l_begin); if(l_end == -1) { throw new IMAP4Exception(IGlobals.UnexpectedServerData); } l_end++; l_begin = l_end; //Skip over FLAGS l_end = indexOf(in_data, ' ', l_begin); if(l_end == -1) { throw new IMAP4Exception(IGlobals.UnexpectedServerData); } l_end++; l_begin = l_end; l_end = matchBrackets(in_data, m_buffers[0], l_begin); if(l_end == -1) { throw new IMAP4Exception(IGlobals.UnexpectedServerData); } l_end++; l_begin = l_end; m_sink.flags(m_buffers[0]); } /** * The total number of messages in the selected mailbox * @param in_data The data to be parsed */ public void exists(StringBuffer in_data) { m_sink.exists(getNumber(in_data)); } /** * The message number of the expunged message * @param in_data The data to be parsed */ public void expunge(StringBuffer in_data) { m_sink.expunge(getNumber(in_data)); } /** * The total number of messages with the \Recent flag set * @param in_data The data to be parsed */ public void recent(StringBuffer in_data) { m_sink.recent(getNumber(in_data)); } /** * The reason why the connection was closed * @param in_data The data to be parsed */ public void bye(StringBuffer in_data) throws IMAP4Exception { int l_begin = 0, l_end = -1; m_buffers[0].setLength(0); //Skip over the * l_end = indexOf(in_data, ' ', l_begin); if(l_end == -1) { throw new IMAP4Exception(IGlobals.UnexpectedServerData); } l_end++; l_begin = l_end; //Skip over BYE l_end = indexOf(in_data, ' ', l_begin); if(l_end == -1) { throw new IMAP4Exception(IGlobals.UnexpectedServerData); } l_end++; l_begin = l_end; //The rest is the capability listing l_end = in_data.length(); for(int i = l_begin; i < l_end; i++) { m_buffers[0].append(in_data.charAt(i)); } m_sink.bye(m_buffers[0]); } /** * Parse the fetch data and pass into the appropriate data sink * @param in_data The data to be parsed. "* msg FETCH (....)" */ public void fetch(StringBuffer in_data) throws IOException { int l_begin = 0, l_end = -1, l_msg = 0; Object l_reference = null; boolean l_readName = true; for(int i=0; i l_totalSize) { l_readChunk = l_totalSize - l_bytesRead; l_dataChunklet = new byte[l_readChunk]; } else { l_readChunk = m_systemPrefs.getBlockSize(); l_dataChunklet = m_data; } //Read the chunk in l_readChunk = m_io.read(l_dataChunklet, l_readChunk); if(l_readChunk == -1) { throw new IOException(IGlobals.ConnectionLost); } l_bytesRead += l_readChunk; //Parse the field and value out if(m_readHeader) { int l_fieldIndex = -1; for(int j = 0; j < l_readChunk; j++) { j = readHeaderLine(l_dataChunklet, j, m_buffers[2]); if(j == -1) { break; } if( (m_buffers[2].charAt(0) == '\r') && (m_buffers[2].charAt(1) == '\n')) { m_readHeader = false; break; } l_fieldIndex = getFieldName(m_buffers[2], 0, m_buffers[3]); if(l_fieldIndex == -1) { throw new IMAP4Exception(IGlobals.UnexpectedServerData); } l_fieldIndex++; getFieldValue(m_buffers[2], l_fieldIndex, m_buffers[4]); m_sink.fetchHeader(l_reference, m_buffers[3], m_buffers[4]); m_buffers[2].setLength(0); m_buffers[3].setLength(0); m_buffers[4].setLength(0); } } m_sink.fetchData(l_reference, l_dataChunklet, l_bytesRead, l_totalSize); } m_io.readLine(in_data); l_begin = 0; break; } case IGlobals.Rfc822Size: { l_end = storeSegment(in_data, m_buffers[1], ' ', l_begin); if(l_end == -1) { l_end = storeSegment(in_data, m_buffers[1], ')', l_begin); if(l_end == -1) { throw new IMAP4Exception(IGlobals.UnexpectedServerData); } } l_end++; l_begin = l_end; m_sink.fetchSize(l_reference, atoi(m_buffers[1], 0, m_buffers[1].length())); m_buffers[1].setLength(0); break; } case IGlobals.Uid: { l_end = storeSegment(in_data, m_buffers[1], ' ', l_begin); if(l_end == -1) { l_end = storeSegment(in_data, m_buffers[1], ')', l_begin); if(l_end == -1) { throw new IMAP4Exception(IGlobals.UnexpectedServerData); } } l_end++; l_begin = l_end; m_sink.fetchUid(l_reference, atoi(m_buffers[1], 0, m_buffers[1].length())); m_buffers[1].setLength(0); break; } case IGlobals.Flags: { m_buffers[1].setLength(0); l_end = matchBrackets(in_data, m_buffers[1], l_begin); if(l_end == -1) { throw new IMAP4Exception(IGlobals.UnexpectedServerData); } l_end++; l_begin = l_end; m_sink.fetchFlags(l_reference, m_buffers[1]); m_buffers[1].setLength(0); break; } case IGlobals.Envelope: { //Get the closed value of the entire envelope int l_fieldBegin = 0, l_fieldEnd = -1, l_fieldIndex = -1; m_buffers[1].setLength(0); for(int i = 0; i < m_fieldValues.length; i++) { m_fieldValues[i].setLength(0); } l_end = matchBrackets(in_data, m_buffers[1], l_begin); if(l_end == -1) { throw new IMAP4Exception(IGlobals.UnexpectedServerData); } l_end++; l_begin = l_end; //Extract the date l_fieldBegin++; //Skip the opening bracket l_fieldEnd = matchFieldQuotes(m_buffers[1], m_fieldValues[0], l_fieldBegin); if(l_fieldEnd == -1) { throw new IMAP4Exception(IGlobals.UnexpectedServerData); } l_fieldEnd++; l_fieldBegin = l_fieldEnd; //Extract the subject l_fieldEnd = matchFieldQuotes(m_buffers[1], m_fieldValues[1], l_fieldBegin); if(l_fieldEnd == -1) { throw new IMAP4Exception(IGlobals.UnexpectedServerData); } l_fieldEnd++; l_fieldBegin = l_fieldEnd; for(int i = 2; i < 8; i++) { //Extract the from, sender, reply-to, to, cc, bcc l_fieldEnd = matchFieldBrackets(m_buffers[1], m_fieldValues[i], l_fieldBegin); if(l_fieldEnd == -1) { throw new IMAP4Exception(IGlobals.UnexpectedServerData); } l_fieldEnd++; l_fieldBegin = l_fieldEnd; } //Extract the in-reply-to l_fieldEnd = matchFieldQuotes(m_buffers[1], m_fieldValues[8], l_fieldBegin); if(l_fieldEnd == -1) { throw new IMAP4Exception(IGlobals.UnexpectedServerData); } l_fieldEnd++; l_fieldBegin = l_fieldEnd; //Extract the message-id l_fieldEnd = matchFieldQuotes(m_buffers[1], m_fieldValues[9], l_fieldBegin); if(l_fieldEnd == -1) { throw new IMAP4Exception(IGlobals.UnexpectedServerData); } l_fieldEnd++; l_fieldBegin = l_fieldEnd; m_sink.fetchEnvelope(l_reference, m_fieldValues); break; } case IGlobals.InternalDate: { l_end = indexOf(in_data, '"', l_begin); if(l_end == -1) { throw new IMAP4Exception(IGlobals.UnexpectedServerData); } l_end++; l_begin = l_end; l_end = storeSegment(in_data, m_buffers[1], '"', l_begin); if(l_end == -1) { throw new IMAP4Exception(IGlobals.UnexpectedServerData); } l_end++; l_begin = l_end; m_sink.fetchInternalDate(l_reference, m_buffers[1]); m_buffers[1].setLength(0); break; } default: { break; } } } m_sink.fetchEnd(l_reference); } /** * Parse the data continuation request * @param in_data The data to be parsed */ public void plus(StringBuffer in_data) throws IOException { //Wait for the continuation response //Send out message data int l_bytesRead = 0, l_readChunk = 0, l_totalSize; if(m_appendStream == null) { throw new IMAP4Exception(IGlobals.UnexpectedServerData); } l_totalSize = m_appendStream.available(); while(l_bytesRead < l_totalSize) { //Determine the size of the next chunk to read if((l_bytesRead + m_systemPrefs.getBlockSize()) > l_totalSize) { l_readChunk = l_totalSize - l_bytesRead; } else { l_readChunk = m_systemPrefs.getBlockSize(); } //Read the chunk in m_appendStream.read(m_data, 0, l_readChunk); l_bytesRead += l_readChunk; //Write the data out m_io.write(m_data, 0, l_readChunk); } m_io.write(IGlobals.CarriageNewLine); m_io.flush(); } /** * Parse the namespace data * @param in_data The data to be parsed */ public void namespace(StringBuffer in_data) throws IMAP4Exception { int l_begin = 0, l_end = -1; int l_beginNS = 0, l_endNS = -1; Object l_reference = null; //Skip over the * l_end = indexOf(in_data, ' ', l_begin); if(l_end == -1) { throw new IMAP4Exception(IGlobals.UnexpectedServerData); } l_end++; l_begin = l_end; //Skip over NAMESPACE l_end = indexOf(in_data, ' ', l_begin); if(l_end == -1) { throw new IMAP4Exception(IGlobals.UnexpectedServerData); } l_end++; l_begin = l_end; l_reference = m_sink.nameSpaceStart(); //Extract the personal namespace m_buffers[0].setLength(0); l_end = matchFieldBrackets(in_data, m_buffers[0], l_begin); if(l_end == -1) { throw new IMAP4Exception(IGlobals.UnexpectedServerData); } l_end++; l_begin = l_end; //Extract each field l_beginNS = 1; while(l_beginNS < m_buffers[0].length()) { m_buffers[1].setLength(0); l_endNS = matchFieldBrackets(m_buffers[0], m_buffers[1], l_beginNS); if(l_endNS == -1) { break; } l_endNS++; l_beginNS = l_endNS; m_sink.nameSpacePersonal(l_reference, m_buffers[1]); } //Extract the other user's namespace m_buffers[0].setLength(0); l_end = matchFieldBrackets(in_data, m_buffers[0], l_begin); if(l_end == -1) { throw new IMAP4Exception(IGlobals.UnexpectedServerData); } l_end++; l_begin = l_end; //Extract each field l_beginNS = 1; while(l_beginNS < m_buffers[0].length()) { m_buffers[1].setLength(0); l_endNS = matchFieldBrackets(m_buffers[0], m_buffers[1], l_beginNS); if(l_endNS == -1) { break; } l_endNS++; l_beginNS = l_endNS; m_sink.nameSpaceOtherUsers(l_reference, m_buffers[1]); } //Extract the shared namespace l_end = matchFieldBrackets(in_data, m_buffers[0], l_begin); if(l_end == -1) { throw new IMAP4Exception(IGlobals.UnexpectedServerData); } l_end++; l_begin = l_end; //Extract each field l_beginNS = 1; while(l_beginNS < m_buffers[0].length()) { m_buffers[1].setLength(0); l_endNS = matchFieldBrackets(m_buffers[0], m_buffers[1], l_beginNS); if(l_endNS == -1) { break; } l_endNS++; l_beginNS = l_endNS; m_sink.nameSpaceShared(l_reference, m_buffers[1]); } m_sink.nameSpaceEnd(l_reference); } /** * Parse the acl data * @param in_data The data to be parsed */ public void acl(StringBuffer in_data) throws IMAP4Exception { int l_begin = 0, l_end = -1; Object l_reference = null; //Skip over the * l_end = indexOf(in_data, ' ', l_begin); if(l_end == -1) { throw new IMAP4Exception(IGlobals.UnexpectedServerData); } l_end++; l_begin = l_end; //Skip over ACL l_end = indexOf(in_data, ' ', l_begin); if(l_end == -1) { throw new IMAP4Exception(IGlobals.UnexpectedServerData); } l_end++; l_begin = l_end; //Get the mailbox name m_buffers[0].setLength(0); l_end = storeSegment(in_data, m_buffers[0], ' ', l_begin); if(l_end == -1) { throw new IMAP4Exception(IGlobals.UnexpectedServerData); } l_end++; l_begin = l_end; l_reference = m_sink.aclStart(m_buffers[0]); while(l_begin < in_data.length()) { //Get identifier m_buffers[0].setLength(0); l_end = storeSegment(in_data, m_buffers[0], ' ', l_begin); if(l_end == -1) { break; } l_end++; l_begin = l_end; //Get the rights m_buffers[1].setLength(0); l_end = storeSegment(in_data, m_buffers[1], ' ', l_begin); if(l_end == -1) { l_end = storeSegment(in_data, m_buffers[1], '\n', l_begin); if(l_end == -1) { break; } } l_end++; l_begin = l_end; m_sink.aclIdentifierRight(l_reference, m_buffers[0], m_buffers[1]); } m_sink.aclEnd(l_reference); } /** * Parse the listrights data * @param in_data The data to be parsed */ public void listrights(StringBuffer in_data) throws IMAP4Exception { int l_begin = 0, l_end = -1; Object l_reference = null; //Skip over the * l_end = indexOf(in_data, ' ', l_begin); if(l_end == -1) { throw new IMAP4Exception(IGlobals.UnexpectedServerData); } l_end++; l_begin = l_end; //Skip over LISTRIGHTS l_end = indexOf(in_data, ' ', l_begin); if(l_end == -1) { throw new IMAP4Exception(IGlobals.UnexpectedServerData); } l_end++; l_begin = l_end; //Get the mailbox name m_buffers[0].setLength(0); l_end = storeSegment(in_data, m_buffers[0], ' ', l_begin); if(l_end == -1) { throw new IMAP4Exception(IGlobals.UnexpectedServerData); } l_end++; l_begin = l_end; //Get the identifier m_buffers[1].setLength(0); l_end = storeSegment(in_data, m_buffers[1], ' ', l_begin); if(l_end == -1) { throw new IMAP4Exception(IGlobals.UnexpectedServerData); } l_end++; l_begin = l_end; l_reference = m_sink.listRightsStart(m_buffers[0], m_buffers[1]); //Get the required rights m_buffers[0].setLength(0); l_end = storeSegment(in_data, m_buffers[0], ' ', l_begin); if(l_end == -1) { throw new IMAP4Exception(IGlobals.UnexpectedServerData); } l_end++; l_begin = l_end; m_sink.listRightsRequiredRights(l_reference, m_buffers[0]); while(l_begin < in_data.length()) { //Get the optional rights m_buffers[0].setLength(0); l_end = storeSegment(in_data, m_buffers[0], ' ', l_begin); if(l_end == -1) { l_end = storeSegment(in_data, m_buffers[0], '\n', l_begin); if(l_end == -1) { break; } } l_end++; l_begin = l_end; m_sink.listRightsOptionalRights(l_reference, m_buffers[0]); } m_sink.listRightsEnd(l_reference); } /** * Parse the myrights data * @param in_data The data to be parsed */ public void myrights(StringBuffer in_data) throws IMAP4Exception { int l_begin = 0, l_end = -1; //Skip over the * l_end = indexOf(in_data, ' ', l_begin); if(l_end == -1) { throw new IMAP4Exception(IGlobals.UnexpectedServerData); } l_end++; l_begin = l_end; //Skip over MYRIGHTS l_end = indexOf(in_data, ' ', l_begin); if(l_end == -1) { throw new IMAP4Exception(IGlobals.UnexpectedServerData); } l_end++; l_begin = l_end; //Get the mailbox name m_buffers[0].setLength(0); l_end = storeSegment(in_data, m_buffers[0], ' ', l_begin); if(l_end == -1) { throw new IMAP4Exception(IGlobals.UnexpectedServerData); } l_end++; l_begin = l_end; //Get the rights m_buffers[1].setLength(0); for(int i = l_begin; i < in_data.length(); i++) { m_buffers[1].append(in_data.charAt(i)); } m_sink.myRights(m_buffers[0], m_buffers[1]); } /** * Raw (unparsed) data is pushed into here. Note: Matches up with sendCommand * and will also be used as a default method to push information that doesn't * belong anywhere else * @param in_data The data to be parsed */ public void rawResponse(StringBuffer in_data) { m_sink.rawResponse(in_data); } /////////////////////////////////////////////////////////////////////////////// //General methods /** * Sets the append stream * @param in_iStream The message data */ public void setAppendStream(InputStream in_iStream) { m_appendStream = in_iStream; } /** * Sets the sink * @param in_iStream The message data */ public void setResponseSink(IIMAP4Sink in_sink) { m_sink = in_sink; } /////////////////////////////////////////////////////////////////////////////// //Parsing Utilities /** * Finds the first occurrence of in_find after in_index in in_src * @param in_src The StringBuffer to search through * @param in_find The character to find * @param in_index The to begin the search from * @return int The index of in_find or -1 if not found */ public int indexOf(StringBuffer in_src, char in_find, int in_index) { for(int i = in_index; i < in_src.length(); i++) { if(in_src.charAt(i) == in_find) { //Found in_find return i; } } return -1; } /** * Returns the index of the next position that is not white space * @param in_src The StringBuffer to search through * @param in_start The index to begin the search from * @return int The index of the next non-white space */ public int skipWhite(StringBuffer in_src, int in_start) { for(int i = in_start; i < in_src.length(); i++) { if(in_src.charAt(i) != ' ') { //Found in_find return i; } } return -1; } /** * Reads a line of the header into out_line * @param in_src The data to search through * @param in_index The index to begin searching from * @param out_field The storage for the line * @returns int The index of the end of the line (\r,\n,\r\n) or -1 if * end of line not found */ public int readHeaderLine(byte[] in_src, int in_index, StringBuffer out_line) { char l_nextChar; for(int i = in_index; i < in_src.length; i++) { l_nextChar = (char)in_src[i]; out_line.append(l_nextChar); if(l_nextChar == '\n') { if((i+1) < in_src.length) { l_nextChar = (char)in_src[i + 1]; } if((l_nextChar != '\t')&&(l_nextChar != ' ')) { return i; } } } return -1; } /** * Extracts the next field name after in_index and places it in out_field * @param in_src The data to search through * @param in_index The index to begin searching from * @param out_field The storage for the field name * @returns int The index of the last position of the field name (index of :) * or -1 if the name is not complete */ public int getFieldName(StringBuffer in_src, int in_index, StringBuffer out_field) { char l_nextChar; for(int i = in_index; i < in_src.length(); i++) { l_nextChar = in_src.charAt(i); if(l_nextChar == ':') { return i; } else { out_field.append(l_nextChar); } } return -1; } /** * Copies all data from in_index in in_src to out_value * @param in_src The data to search through * @param in_index The index to begin searching from * @param out_value The storage for the field value */ public void getFieldValue(StringBuffer in_src, int in_index, StringBuffer out_value) { int l_index = in_index; l_index = skipWhite(in_src, l_index); for(int i = l_index; i < in_src.length(); i++) { //Don't store the new line at the end of the field value if( ((i + 1) < in_src.length()) && ((i + 2) == in_src.length()) && (in_src.charAt(i) == '\r') && (in_src.charAt(i + 1) == '\n')) { return; } out_value.append(in_src.charAt(i)); } } /** * Finds the first occurrence of in_find after in_index in in_src * @param in_src The StringBuffer to search through * @param in_dest The StringBuffer to store the block in_index to in_find * @param in_find The character to find * @param in_index The to begin the search from * @return int The index of in_find or -1 if not found */ public int storeSegment(StringBuffer in_src, StringBuffer in_dest, char in_find, int in_index) { int l_initialLength = in_dest.length(); for(int i = in_index; i < in_src.length(); i++) { if(in_src.charAt(i) == in_find) { //Found in_find return i; } else { in_dest.append(in_src.charAt(i)); } } in_dest.setLength(l_initialLength); return -1; } /** * Same as matchBrackets, but also counts NIL value as a bracketed value * @param in_data The data to be parsed * @param in_value The data within the brackets * @param in_start The index to begin parsing from * @return int The index where the quoted value ended or -1 on error */ public int matchFieldQuotes(StringBuffer in_data, StringBuffer in_value, int in_start) { int l_index = in_start; int l_fieldEnd = -1, l_fieldBegin = -1; l_index = skipWhite(in_data, l_index); if( (in_data.charAt(l_index) == 'N') && (in_data.charAt(l_index + 1) == 'I') && (in_data.charAt(l_index + 2) == 'L')) { in_value.append('N'); in_value.append('I'); in_value.append('L'); return (l_index + 2); } //Extract the quoted value l_fieldEnd = indexOf(in_data, '"', in_start); if(l_fieldEnd == -1) { return -1; } l_fieldEnd++; l_fieldBegin = l_fieldEnd; l_fieldEnd = storeSegment(in_data, in_value, '"', l_fieldBegin); if(l_fieldEnd == -1) { return -1; } return l_fieldEnd; } /** * Extract the number out the server response * @param in_data Server response of the form * 233 EXISTS * @return int The number at the second token position or -1 if n/a */ protected int getNumber(StringBuffer in_data) { int l_begin = 0, l_end = -1; //Skip over the * l_end = indexOf(in_data, ' ', l_begin); if(l_end == -1) { return -1; } l_end++; l_begin = l_end; //Get the number of messages l_end = indexOf(in_data, ' ', l_begin); if(l_end == -1) { return -1; } return atoi(in_data, l_begin, l_end); } /** * Resolves the value of in_dataItem without allocating space for a new String * @param in_dataItem The fetch data item * @return int A constant representing the data item */ protected int resolveFetchDataItem(StringBuffer in_dataItem) { int l_dataLength = in_dataItem.length(); if(l_dataLength < 1) { return IGlobals.Unknown; } switch(in_dataItem.charAt(0)) { case 'B': { if( (l_dataLength > 12) && (in_dataItem.charAt(1) == 'O') && (in_dataItem.charAt(2) == 'D') && (in_dataItem.charAt(3) == 'Y') && (in_dataItem.charAt(4) == 'S') && (in_dataItem.charAt(5) == 'T') && (in_dataItem.charAt(6) == 'R') && (in_dataItem.charAt(7) == 'U') && (in_dataItem.charAt(8) == 'C') && (in_dataItem.charAt(9) == 'T') && (in_dataItem.charAt(10) == 'U') && (in_dataItem.charAt(11) == 'R') && (in_dataItem.charAt(12) == 'E')) { return IGlobals.BodyStructure; } else if((l_dataLength > 3) && (in_dataItem.charAt(1) == 'O') && (in_dataItem.charAt(2) == 'D') && (in_dataItem.charAt(3) == 'Y')) { if( (l_dataLength > 4) && (in_dataItem.charAt(4) == '[')) { if( (in_dataItem.charAt(5) == ']') || (in_dataItem.charAt(5) == '0') || (in_dataItem.charAt(5) == 'H')) { m_readHeader = true; } } else if((l_dataLength > 10) && (in_dataItem.charAt(4) == '.') && (in_dataItem.charAt(5) == 'P') && (in_dataItem.charAt(6) == 'E') && (in_dataItem.charAt(7) == 'E') && (in_dataItem.charAt(8) == 'K') && (in_dataItem.charAt(9) == '[')) { if( (in_dataItem.charAt(10) == ']') || (in_dataItem.charAt(10) == '0') || (in_dataItem.charAt(10) == 'H')) { m_readHeader = true; } } return IGlobals.Message; } break; } case 'R': { if( (l_dataLength > 5) && (in_dataItem.charAt(1) == 'F') && (in_dataItem.charAt(2) == 'C') && (in_dataItem.charAt(3) == '8') && (in_dataItem.charAt(4) == '2') && (in_dataItem.charAt(5) == '2')) { if( (l_dataLength > 10) && (in_dataItem.charAt(6) == '.') && (in_dataItem.charAt(7) == 'S') && (in_dataItem.charAt(8) == 'I') && (in_dataItem.charAt(9) == 'Z') && (in_dataItem.charAt(10) == 'E')) { return IGlobals.Rfc822Size; } else if((l_dataLength > 10) && (in_dataItem.charAt(6) == '.') && (in_dataItem.charAt(7) == 'T') && (in_dataItem.charAt(8) == 'E') && (in_dataItem.charAt(9) == 'X') && (in_dataItem.charAt(10) == 'T')) { return IGlobals.Rfc822Text; } else if((l_dataLength > 12) && (in_dataItem.charAt(6) == '.') && (in_dataItem.charAt(7) == 'H') && (in_dataItem.charAt(8) == 'E') && (in_dataItem.charAt(9) == 'A') && (in_dataItem.charAt(10) == 'D') && (in_dataItem.charAt(11) == 'E') && (in_dataItem.charAt(12) == 'R')) { m_readHeader = true; return IGlobals.Message; } m_readHeader = true; return IGlobals.Message; } break; } case 'U': { if( (l_dataLength > 2) && (in_dataItem.charAt(1) == 'I') && (in_dataItem.charAt(2) == 'D')) { return IGlobals.Uid; } break; } case 'F': { if( (l_dataLength > 4) && (in_dataItem.charAt(1) == 'L') && (in_dataItem.charAt(2) == 'A') && (in_dataItem.charAt(3) == 'G') && (in_dataItem.charAt(4) == 'S')) { return IGlobals.Flags; } break; } case 'E': { if( (l_dataLength > 7) && (in_dataItem.charAt(1) == 'N') && (in_dataItem.charAt(2) == 'V') && (in_dataItem.charAt(3) == 'E') && (in_dataItem.charAt(4) == 'L') && (in_dataItem.charAt(5) == 'O') && (in_dataItem.charAt(6) == 'P') && (in_dataItem.charAt(7) == 'E')) { return IGlobals.Envelope; } break; } case 'I': { if( (l_dataLength > 11) && (in_dataItem.charAt(1) == 'N') && (in_dataItem.charAt(2) == 'T') && (in_dataItem.charAt(3) == 'E') && (in_dataItem.charAt(4) == 'R') && (in_dataItem.charAt(5) == 'N') && (in_dataItem.charAt(6) == 'A') && (in_dataItem.charAt(7) == 'L') && (in_dataItem.charAt(8) == 'D') && (in_dataItem.charAt(9) == 'A') && (in_dataItem.charAt(10) == 'T') && (in_dataItem.charAt(11) == 'E')) { return IGlobals.InternalDate; } break; } default: { break; } } return IGlobals.Unknown; } /** * Resolves the value of in_dataItem without allocating space for a new String * @param in_dataItem The status data item * @return int A constant representing the data item */ protected int resolveStatusDataItem(StringBuffer in_dataItem) { int l_dataLength = in_dataItem.length(); if(l_dataLength < 1) { return IGlobals.Unknown; } switch(in_dataItem.charAt(0)) { case 'M': { if( (l_dataLength > 7) && (in_dataItem.charAt(1) == 'E') && (in_dataItem.charAt(2) == 'S') && (in_dataItem.charAt(3) == 'S') && (in_dataItem.charAt(4) == 'A') && (in_dataItem.charAt(5) == 'G') && (in_dataItem.charAt(6) == 'E') && (in_dataItem.charAt(7) == 'S')) { return IGlobals.StatusMessages; } break; } case 'R': { if( (l_dataLength > 5) && (in_dataItem.charAt(1) == 'E') && (in_dataItem.charAt(2) == 'C') && (in_dataItem.charAt(3) == 'E') && (in_dataItem.charAt(4) == 'N') && (in_dataItem.charAt(5) == 'T')) { return IGlobals.StatusRecent; } break; } case 'U': { if( (l_dataLength > 6) && (in_dataItem.charAt(1) == 'I') && (in_dataItem.charAt(2) == 'D') && (in_dataItem.charAt(3) == 'N') && (in_dataItem.charAt(4) == 'E') && (in_dataItem.charAt(5) == 'X') && (in_dataItem.charAt(6) == 'T')) { return IGlobals.StatusUidNext; } else if((l_dataLength > 10) && (in_dataItem.charAt(1) == 'I') && (in_dataItem.charAt(2) == 'D') && (in_dataItem.charAt(3) == 'V') && (in_dataItem.charAt(4) == 'A') && (in_dataItem.charAt(5) == 'L') && (in_dataItem.charAt(6) == 'I') && (in_dataItem.charAt(7) == 'D') && (in_dataItem.charAt(8) == 'I') && (in_dataItem.charAt(9) == 'T') && (in_dataItem.charAt(10) == 'Y')) { return IGlobals.StatusUidValidity; } else if((l_dataLength > 5) && (in_dataItem.charAt(1) == 'N') && (in_dataItem.charAt(2) == 'S') && (in_dataItem.charAt(3) == 'E') && (in_dataItem.charAt(4) == 'E') && (in_dataItem.charAt(5) == 'N')) { return IGlobals.StatusUnSeen; } break; } default: { break; } } return IGlobals.Unknown; } /** * Convert the ascii number to an int * @param in_data The buffer with the number to convert * @param in_begin The beginning of the number * @param in_end The end of the number * @return int The number */ protected int atoi(StringBuffer in_data, int in_begin, int in_end) { int i; int n; int sign; for ( i = in_begin; Character.isSpaceChar(in_data.charAt(i)); i++ ) { } sign = (in_data.charAt(i) == '-') ? -1 : 1; if ( in_data.charAt(i) == '+' || in_data.charAt(i) == '-' ) { i++; } for ( n = 0; (i < in_end) && Character.isDigit(in_data.charAt(i)); i++ ) { n = 10 * n + ( in_data.charAt(i) - '0'); } return sign * n; } protected int itoa( int in_number, byte[] io_string ) { int i; int sign; if ((sign = in_number) < 0) { in_number = -in_number; } i = 0; do { io_string[i++] = (byte)(in_number % 10 + '0'); } while ((in_number /= 10) > 0); if ( sign < 0 ) { io_string[i] = '-'; } reverse(io_string, i ); return i; } protected void reverse( byte[] io_string, int in_length ) { int c, i, j; for ( i = 0, j = in_length - 1; i < j; i++, j-- ) { c = io_string[i]; io_string[i] = io_string[j]; io_string[j] = (byte)c; } } /** * Same as matchBrackets, but also counts NIL value as a bracketed value * @param in_data The data to be parsed * @param in_value The data within the brackets * @param in_start The index to begin parsing from * @return int The index where the bracketed value ended or -1 on error */ public int matchFieldBrackets(StringBuffer in_data, StringBuffer in_value, int in_start) { int l_index = in_start; l_index = skipWhite(in_data, l_index); if( (in_data.charAt(l_index) == 'N') && (in_data.charAt(l_index + 1) == 'I') && (in_data.charAt(l_index + 2) == 'L')) { in_value.append('N'); in_value.append('I'); in_value.append('L'); return (l_index + 2); } return matchBrackets(in_data, in_value, in_start); } /** * Stores the value of the next closed set of brackets within in_value * @param in_data The data to be parsed * @param in_value The data within the brackets * @param in_start The index to begin parsing from * @return int The index where the bracketed value ended or -1 on error */ public int matchBrackets(StringBuffer in_data, StringBuffer in_value, int in_start) { //Indexes used to help find the bracketed value int l_nIndex = -1; int l_nLeftBraces = 0; //Flag to indicate when the Left brace at l_nLeft should be recounted boolean l_bRecount = false; //Verify the first position is a left bracket l_nIndex = indexOf(in_data, '(', in_start); if(l_nIndex == -1) { return -1; } in_value.append(in_data.charAt(l_nIndex)); l_nIndex++; l_nLeftBraces++; for(; l_nIndex < in_data.length(); l_nIndex++) { in_value.append(in_data.charAt(l_nIndex)); if(in_data.charAt(l_nIndex) == '(') { l_nLeftBraces++; } else if(in_data.charAt(l_nIndex) == ')') { l_nLeftBraces--; if(l_nLeftBraces < 1) { //Found the closing bracket return l_nIndex; } } } in_value.setLength(0); return -1; } /** * Extracts the mailbox info * @param in_data The data to parse * @param out_buffer1 Storage for the attributes * @param out_buffer2 Storage for the delimiter * @param out_buffer3 Storage for the name */ public void extractMailboxInfo( StringBuffer in_data, StringBuffer out_buffer1, StringBuffer out_buffer2, StringBuffer out_buffer3) { int l_begin = 0, l_end = -1; //Skip over the * l_end = indexOf(in_data, ' ', l_begin); if(l_end == -1) { return; } l_end++; l_begin = l_end; //Skip over LIST l_end = indexOf(in_data, ' ', l_begin); if(l_end == -1) { return; } l_end++; l_begin = l_end; //Extract the attributes l_end = matchBrackets(in_data, m_buffers[0], l_begin); if(l_end == -1) { return; } l_end++; l_begin = l_end; //Extract the delimiter l_end = indexOf(in_data, '"', l_begin); if(l_end == -1) { return; } l_end++; l_begin = l_end; l_end = storeSegment(in_data, m_buffers[1], '"', l_begin); if(l_end == -1) { return; } l_end++; l_begin = l_end; //Extract the name: The rest is the name of the mailbox l_begin = skipWhite(in_data, l_begin); l_end = in_data.length(); for(int i = l_begin; i < l_end; i++) { m_buffers[2].append(in_data.charAt(i)); } } //Data members /////////////////////////////////////////////////////////////////////// /** The io object*/ private IO m_io; /** The data sink*/ private IIMAP4Sink m_sink; /* Re-usable buffers for data sink*/ private StringBuffer[] m_buffers; /** Re-usable buffer for fetchData*/ private byte[] m_data; /** Storage for envelope field values;*/ private StringBuffer[] m_fieldValues; /** The message data*/ private InputStream m_appendStream; /** The commands awaiting responses*/ private Vector m_pendingCommands; /** The system preferences*/ private SystemPreferences m_systemPrefs; /** Parsing flags*/ private boolean m_readHeader; }