2024 lines
61 KiB
Java
2024 lines
61 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.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<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;
|
|
|
|
//Store the message number in m_buffers[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.fetchStart(atoi(m_buffers[0], 0, m_buffers[0].length()));
|
|
|
|
//Skip over FETCH
|
|
l_end = indexOf(in_data, ' ', l_begin);
|
|
if(l_end == -1)
|
|
{
|
|
throw new IMAP4Exception(IGlobals.UnexpectedServerData);
|
|
}
|
|
l_end++;
|
|
l_begin = l_end;
|
|
|
|
//Skip the first bracket
|
|
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 item
|
|
l_begin = skipWhite(in_data, l_begin);
|
|
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;
|
|
|
|
//Extract the value of the data item
|
|
l_begin = skipWhite(in_data, l_begin);
|
|
|
|
switch(resolveFetchDataItem(m_buffers[0]))
|
|
{
|
|
case IGlobals.BodyStructure:
|
|
{
|
|
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.fetchBodyStructure(l_reference, m_buffers[1]);
|
|
m_buffers[1].setLength(0);
|
|
break;
|
|
}
|
|
case IGlobals.Message:
|
|
case IGlobals.Rfc822Text:
|
|
{
|
|
//Extract the size of the message block
|
|
int l_totalSize = 0;
|
|
int l_bytesRead = 0;
|
|
int l_readChunk = 0;
|
|
int l_index = -1;
|
|
byte[] l_dataChunklet = null;
|
|
|
|
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_totalSize = atoi(m_buffers[1], 0, m_buffers[1].length());
|
|
|
|
//Read in a block of l_totalSize from the IO
|
|
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;
|
|
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 delimeter
|
|
* @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 delimeter
|
|
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;
|
|
|
|
|
|
}
|