Got rid of the ConnectionReader, and implemented parsing literals.
* The parser now works on a BDataIO instead. It's now even a bit simpler overall. * Implemented ParseLiteral() - the default implementation just reads the data into a string. Thanks to BDataIO this was now very simple to do. * Tested the code: imap_tester can now successfully read mails (using the raw command only, at the moment).
This commit is contained in:
parent
37d26ae5e2
commit
a517047070
src/add-ons/mail_daemon/inbound_protocols/imap/imap_lib
@ -19,7 +19,6 @@ typedef std::vector<BString> StringList;
|
|||||||
namespace IMAP {
|
namespace IMAP {
|
||||||
|
|
||||||
|
|
||||||
class ConnectionReader;
|
|
||||||
class HandlerListener;
|
class HandlerListener;
|
||||||
|
|
||||||
|
|
||||||
|
@ -23,132 +23,6 @@
|
|||||||
namespace IMAP {
|
namespace IMAP {
|
||||||
|
|
||||||
|
|
||||||
ConnectionReader::ConnectionReader()
|
|
||||||
:
|
|
||||||
fSocket(NULL)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
ConnectionReader::SetTo(BSocket& socket)
|
|
||||||
{
|
|
||||||
fSocket = &socket;
|
|
||||||
fBufferedSocket = new BBufferedDataIO(socket, 32768, false, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
ConnectionReader::GetNextLine(BString& line, bigtime_t timeout,
|
|
||||||
int32 maxUnfinishedLine)
|
|
||||||
{
|
|
||||||
line.SetTo((const char*)NULL, 0);
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
status_t status = _GetNextDataBunch(line, timeout);
|
|
||||||
if (status == B_OK)
|
|
||||||
return status;
|
|
||||||
if (status == B_NAME_NOT_FOUND) {
|
|
||||||
if (maxUnfinishedLine < 0 || line.Length() < maxUnfinishedLine)
|
|
||||||
continue;
|
|
||||||
else
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
return B_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
ConnectionReader::FinishLine(BString& line)
|
|
||||||
{
|
|
||||||
while (true) {
|
|
||||||
status_t status = _GetNextDataBunch(line, B_INFINITE_TIMEOUT);
|
|
||||||
if (status == B_OK)
|
|
||||||
return status;
|
|
||||||
if (status == B_NAME_NOT_FOUND)
|
|
||||||
continue;
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
return B_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
ConnectionReader::ReadToStream(int32 size, BDataIO& out)
|
|
||||||
{
|
|
||||||
const int32 kBunchSize = 1024; // 1Kb
|
|
||||||
char buffer[kBunchSize];
|
|
||||||
|
|
||||||
int32 readSize = size - fStringBuffer.Length();
|
|
||||||
int32 readed = fStringBuffer.Length();
|
|
||||||
if (readSize < 0) {
|
|
||||||
readed = size;
|
|
||||||
}
|
|
||||||
out.Write(fStringBuffer.String(), readed);
|
|
||||||
fStringBuffer.Remove(0, readed);
|
|
||||||
|
|
||||||
while (readSize > 0) {
|
|
||||||
int32 bunchSize = readSize < kBunchSize ? readSize : kBunchSize;
|
|
||||||
int nReaded = fBufferedSocket->Read(buffer, bunchSize);
|
|
||||||
if (nReaded < 0)
|
|
||||||
return B_ERROR;
|
|
||||||
readSize -= nReaded;
|
|
||||||
out.Write(buffer, nReaded);
|
|
||||||
}
|
|
||||||
return B_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
ConnectionReader::_GetNextDataBunch(BString& line, bigtime_t timeout,
|
|
||||||
uint32 maxNewLength)
|
|
||||||
{
|
|
||||||
if (_ExtractTillEndOfLine(line))
|
|
||||||
return B_OK;
|
|
||||||
|
|
||||||
char buffer[maxNewLength];
|
|
||||||
//
|
|
||||||
// if (timeout != B_INFINITE_TIMEOUT) {
|
|
||||||
// status_t status = fSocket->WaitForReadable(timeout);
|
|
||||||
// if (status != B_OK)
|
|
||||||
// return status;
|
|
||||||
// }
|
|
||||||
|
|
||||||
int nReaded = fBufferedSocket->Read(buffer, maxNewLength);
|
|
||||||
if (nReaded <= 0)
|
|
||||||
return B_ERROR;
|
|
||||||
|
|
||||||
fStringBuffer.SetTo(buffer, nReaded);
|
|
||||||
if (_ExtractTillEndOfLine(line))
|
|
||||||
return B_OK;
|
|
||||||
return B_NAME_NOT_FOUND;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool
|
|
||||||
ConnectionReader::_ExtractTillEndOfLine(BString& out)
|
|
||||||
{
|
|
||||||
int32 endPos = fStringBuffer.FindFirst('\n');
|
|
||||||
if (endPos == B_ERROR) {
|
|
||||||
endPos = fStringBuffer.FindFirst(xEOF);
|
|
||||||
if (endPos == B_ERROR) {
|
|
||||||
out += fStringBuffer;
|
|
||||||
fStringBuffer.SetTo((const char*)NULL, 0);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
out.Append(fStringBuffer, endPos + 1);
|
|
||||||
fStringBuffer.Remove(0, endPos + 1);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// #pragma mark -
|
|
||||||
|
|
||||||
|
|
||||||
Protocol::Protocol()
|
Protocol::Protocol()
|
||||||
:
|
:
|
||||||
fSocket(NULL),
|
fSocket(NULL),
|
||||||
@ -209,7 +83,6 @@ Protocol::Connect(const BNetworkAddress& address, const char* username,
|
|||||||
|
|
||||||
TRACE("Login\n");
|
TRACE("Login\n");
|
||||||
|
|
||||||
fConnectionReader.SetTo(*fSocket);
|
|
||||||
fIsConnected = true;
|
fIsConnected = true;
|
||||||
|
|
||||||
LoginCommand login(username, password);
|
LoginCommand login(username, password);
|
||||||
@ -400,7 +273,7 @@ status_t
|
|||||||
Protocol::HandleResponse(bigtime_t timeout, bool disconnectOnTimeout)
|
Protocol::HandleResponse(bigtime_t timeout, bool disconnectOnTimeout)
|
||||||
{
|
{
|
||||||
status_t commandStatus = B_OK;
|
status_t commandStatus = B_OK;
|
||||||
IMAP::ResponseParser parser(fConnectionReader);
|
IMAP::ResponseParser parser(*fBufferedSocket);
|
||||||
IMAP::Response response;
|
IMAP::Response response;
|
||||||
|
|
||||||
bool done = false;
|
bool done = false;
|
||||||
@ -437,7 +310,7 @@ Protocol::HandleResponse(bigtime_t timeout, bool disconnectOnTimeout)
|
|||||||
} else
|
} else
|
||||||
printf("Unknown tag S: %s\n", response.ToString().String());
|
printf("Unknown tag S: %s\n", response.ToString().String());
|
||||||
}
|
}
|
||||||
} catch (IMAP::ParseException& exception) {
|
} catch (ParseException& exception) {
|
||||||
printf("Error during parsing: %s\n", exception.Message());
|
printf("Error during parsing: %s\n", exception.Message());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,39 +36,6 @@ typedef BObjectList<Handler> HandlerList;
|
|||||||
typedef std::map<int32, Command*> CommandIDMap;
|
typedef std::map<int32, Command*> CommandIDMap;
|
||||||
|
|
||||||
|
|
||||||
// TODO: throw this class away, and just use a BBufferedDataIO instead.
|
|
||||||
class ConnectionReader {
|
|
||||||
public:
|
|
||||||
ConnectionReader();
|
|
||||||
|
|
||||||
void SetTo(BSocket& socket);
|
|
||||||
|
|
||||||
/*! Try to read line. If no end of line is found at least
|
|
||||||
minUnfinishedLine characters are returned. */
|
|
||||||
status_t GetNextLine(BString& line,
|
|
||||||
bigtime_t timeout = kIMAP4ClientTimeout,
|
|
||||||
int32 maxUnfinishedLine = -1);
|
|
||||||
/*! Read data and append it to line till the end of file is
|
|
||||||
reached. */
|
|
||||||
status_t FinishLine(BString& line);
|
|
||||||
|
|
||||||
status_t ReadToStream(int32 size, BDataIO& out);
|
|
||||||
|
|
||||||
private:
|
|
||||||
/*! Try to read till the end of line is reached. To do so maximal
|
|
||||||
maxNewLength bytes are read from the server if needed. */
|
|
||||||
status_t _GetNextDataBunch(BString& line,
|
|
||||||
bigtime_t timeout,
|
|
||||||
uint32 maxNewLength = 256);
|
|
||||||
bool _ExtractTillEndOfLine(BString& out);
|
|
||||||
|
|
||||||
private:
|
|
||||||
BSocket* fSocket;
|
|
||||||
BBufferedDataIO* fBufferedSocket;
|
|
||||||
BString fStringBuffer;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class FolderInfo {
|
class FolderInfo {
|
||||||
public:
|
public:
|
||||||
FolderInfo()
|
FolderInfo()
|
||||||
@ -144,7 +111,6 @@ private:
|
|||||||
protected:
|
protected:
|
||||||
BSocket* fSocket;
|
BSocket* fSocket;
|
||||||
BBufferedDataIO* fBufferedSocket;
|
BBufferedDataIO* fBufferedSocket;
|
||||||
ConnectionReader fConnectionReader;
|
|
||||||
|
|
||||||
HandlerList fHandlerList;
|
HandlerList fHandlerList;
|
||||||
CommandList fAfterQuackCommands;
|
CommandList fAfterQuackCommands;
|
||||||
|
@ -8,9 +8,6 @@
|
|||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include "Protocol.h"
|
|
||||||
// TODO: remove again once the ConnectionReader is out
|
|
||||||
|
|
||||||
|
|
||||||
#define TRACE_IMAP
|
#define TRACE_IMAP
|
||||||
#ifdef TRACE_IMAP
|
#ifdef TRACE_IMAP
|
||||||
@ -206,15 +203,26 @@ StringArgument::ToString() const
|
|||||||
|
|
||||||
|
|
||||||
ParseException::ParseException()
|
ParseException::ParseException()
|
||||||
:
|
|
||||||
fMessage(NULL)
|
|
||||||
{
|
{
|
||||||
|
fBuffer[0] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ParseException::ParseException(const char* message)
|
ParseException::ParseException(const char* format, ...)
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
va_start(args, format);
|
||||||
|
vsnprintf(fBuffer, sizeof(fBuffer), format, args);
|
||||||
|
va_end(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
|
StreamException::StreamException(status_t status)
|
||||||
:
|
:
|
||||||
fMessage(message)
|
ParseException("Error from stream: %s", status)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -224,9 +232,19 @@ ParseException::ParseException(const char* message)
|
|||||||
|
|
||||||
ExpectedParseException::ExpectedParseException(char expected, char instead)
|
ExpectedParseException::ExpectedParseException(char expected, char instead)
|
||||||
{
|
{
|
||||||
snprintf(fBuffer, sizeof(fBuffer), "Expected \"%c\", but got \"%c\"!",
|
char bufferA[8];
|
||||||
expected, instead);
|
char bufferB[8];
|
||||||
fMessage = fBuffer;
|
snprintf(fBuffer, sizeof(fBuffer), "Expected %s, but got %s instead!",
|
||||||
|
CharToString(bufferA, sizeof(bufferA), expected),
|
||||||
|
CharToString(bufferB, sizeof(bufferB), instead));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const char*
|
||||||
|
ExpectedParseException::CharToString(char* buffer, size_t size, char c)
|
||||||
|
{
|
||||||
|
snprintf(buffer, size, isprint(c) ? "\"%c\"" : "(%x)", c);
|
||||||
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -249,7 +267,8 @@ LiteralHandler::~LiteralHandler()
|
|||||||
Response::Response()
|
Response::Response()
|
||||||
:
|
:
|
||||||
fTag(0),
|
fTag(0),
|
||||||
fContinuation(false)
|
fContinuation(false),
|
||||||
|
fHasNextChar(false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -260,33 +279,29 @@ Response::~Response()
|
|||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
Response::Parse(ConnectionReader& reader, const char* line,
|
Response::Parse(BDataIO& stream, LiteralHandler* handler) throw(ParseException)
|
||||||
LiteralHandler* handler) throw(ParseException)
|
|
||||||
{
|
{
|
||||||
MakeEmpty();
|
MakeEmpty();
|
||||||
fReader = &reader;
|
|
||||||
fLiteralHandler = handler;
|
fLiteralHandler = handler;
|
||||||
fTag = 0;
|
fTag = 0;
|
||||||
fContinuation = false;
|
fContinuation = false;
|
||||||
|
fHasNextChar = false;
|
||||||
|
|
||||||
if (line[0] == '*') {
|
char begin = Next(stream);
|
||||||
|
if (begin == '*') {
|
||||||
// Untagged response
|
// Untagged response
|
||||||
Consume(line, '*');
|
Consume(stream, ' ');
|
||||||
Consume(line, ' ');
|
} else if (begin == '+') {
|
||||||
} else if (line[0] == '+') {
|
|
||||||
// Continuation
|
// Continuation
|
||||||
Consume(line, '+');
|
|
||||||
fContinuation = true;
|
fContinuation = true;
|
||||||
} else {
|
} else if (begin == 'A') {
|
||||||
// Tagged response
|
// Tagged response
|
||||||
Consume(line, 'A');
|
fTag = ExtractNumber(stream);
|
||||||
fTag = strtoul(line, (char**)&line, 10);
|
Consume(stream, ' ');
|
||||||
if (line == NULL)
|
} else
|
||||||
ParseException("Invalid tag!");
|
throw ParseException("Unexpected response begin");
|
||||||
Consume(line, ' ');
|
|
||||||
}
|
|
||||||
|
|
||||||
char c = ParseLine(*this, line);
|
char c = ParseLine(*this, stream);
|
||||||
if (c != '\0')
|
if (c != '\0')
|
||||||
throw ExpectedParseException('\0', c);
|
throw ExpectedParseException('\0', c);
|
||||||
}
|
}
|
||||||
@ -300,44 +315,47 @@ Response::IsCommand(const char* command) const
|
|||||||
|
|
||||||
|
|
||||||
char
|
char
|
||||||
Response::ParseLine(ArgumentList& arguments, const char*& line)
|
Response::ParseLine(ArgumentList& arguments, BDataIO& stream)
|
||||||
{
|
{
|
||||||
while (line[0] != '\0') {
|
while (true) {
|
||||||
char c = line[0];
|
char c = Peek(stream);
|
||||||
|
if (c == '\0')
|
||||||
|
break;
|
||||||
|
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case '(':
|
case '(':
|
||||||
ParseList(arguments, line, '(', ')');
|
ParseList(arguments, stream, '(', ')');
|
||||||
break;
|
break;
|
||||||
case '[':
|
case '[':
|
||||||
ParseList(arguments, line, '[', ']');
|
ParseList(arguments, stream, '[', ']');
|
||||||
break;
|
break;
|
||||||
case ')':
|
case ')':
|
||||||
case ']':
|
case ']':
|
||||||
Consume(line, c);
|
Consume(stream, c);
|
||||||
return c;
|
return c;
|
||||||
case '"':
|
case '"':
|
||||||
ParseQuoted(arguments, line);
|
ParseQuoted(arguments, stream);
|
||||||
break;
|
break;
|
||||||
case '{':
|
case '{':
|
||||||
ParseLiteral(arguments, line);
|
ParseLiteral(arguments, stream);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ' ':
|
case ' ':
|
||||||
case '\t':
|
case '\t':
|
||||||
// whitespace
|
// whitespace
|
||||||
Consume(line, c);
|
Consume(stream, c);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '\r':
|
case '\r':
|
||||||
Consume(line, '\r');
|
Consume(stream, '\r');
|
||||||
Consume(line, '\n');
|
Consume(stream, '\n');
|
||||||
return '\0';
|
return '\0';
|
||||||
case '\n':
|
case '\n':
|
||||||
Consume(line, '\n');
|
Consume(stream, '\n');
|
||||||
return '\0';
|
return '\0';
|
||||||
|
|
||||||
default:
|
default:
|
||||||
ParseString(arguments, line);
|
ParseString(arguments, stream);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -347,55 +365,38 @@ Response::ParseLine(ArgumentList& arguments, const char*& line)
|
|||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
Response::Consume(const char*& line, char c)
|
Response::ParseList(ArgumentList& arguments, BDataIO& stream, char start,
|
||||||
{
|
|
||||||
if (line[0] != c)
|
|
||||||
throw ExpectedParseException(c, line[0]);
|
|
||||||
|
|
||||||
line++;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
Response::ParseList(ArgumentList& arguments, const char*& line, char start,
|
|
||||||
char end)
|
char end)
|
||||||
{
|
{
|
||||||
Consume(line, start);
|
Consume(stream, start);
|
||||||
|
|
||||||
ListArgument* argument = new ListArgument(start);
|
ListArgument* argument = new ListArgument(start);
|
||||||
arguments.AddItem(argument);
|
arguments.AddItem(argument);
|
||||||
|
|
||||||
char c = ParseLine(argument->List(), line);
|
char c = ParseLine(argument->List(), stream);
|
||||||
if (c != end)
|
if (c != end)
|
||||||
throw ExpectedParseException(end, c);
|
throw ExpectedParseException(end, c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
Response::ParseQuoted(ArgumentList& arguments, const char*& line)
|
Response::ParseQuoted(ArgumentList& arguments, BDataIO& stream)
|
||||||
{
|
{
|
||||||
Consume(line, '"');
|
Consume(stream, '"');
|
||||||
|
|
||||||
BString string;
|
BString string;
|
||||||
char* output = string.LockBuffer(strlen(line));
|
while (true) {
|
||||||
int32 index = 0;
|
char c = Next(stream);
|
||||||
|
|
||||||
while (line[0] != '\0') {
|
|
||||||
char c = line[0];
|
|
||||||
if (c == '\\') {
|
if (c == '\\') {
|
||||||
line++;
|
c = Next(stream);
|
||||||
if (line[0] == '\0')
|
|
||||||
break;
|
|
||||||
} else if (c == '"') {
|
} else if (c == '"') {
|
||||||
line++;
|
|
||||||
output[index] = '\0';
|
|
||||||
string.UnlockBuffer(index);
|
|
||||||
arguments.AddItem(new StringArgument(string));
|
arguments.AddItem(new StringArgument(string));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (c == '\0')
|
||||||
|
break;
|
||||||
|
|
||||||
output[index++] = c;
|
string += c;
|
||||||
line++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
throw ParseException("Unexpected end of qouted string!");
|
throw ParseException("Unexpected end of qouted string!");
|
||||||
@ -403,62 +404,144 @@ Response::ParseQuoted(ArgumentList& arguments, const char*& line)
|
|||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
Response::ParseLiteral(ArgumentList& arguments, const char*& line)
|
Response::ParseLiteral(ArgumentList& arguments, BDataIO& stream)
|
||||||
{
|
{
|
||||||
Consume(line, '{');
|
Consume(stream, '{');
|
||||||
off_t size = atoll(ExtractString(line));
|
size_t size = ExtractNumber(stream);
|
||||||
Consume(line, '}');
|
Consume(stream, '}');
|
||||||
Consume(line, '\r');
|
Consume(stream, '\r');
|
||||||
Consume(line, '\n');
|
Consume(stream, '\n');
|
||||||
|
|
||||||
if (fLiteralHandler != NULL)
|
if (fLiteralHandler != NULL)
|
||||||
fLiteralHandler->HandleLiteral(*fReader, size);
|
fLiteralHandler->HandleLiteral(stream, size);
|
||||||
else {
|
else {
|
||||||
// The default implementation just throws the data away
|
// The default implementation just adds the data as a string
|
||||||
BMallocIO stream;
|
TRACE("Trying to read literal with %" B_PRIuSIZE " bytes.\n", size);
|
||||||
TRACE("Trying to read literal with %llu bytes.\n", size);
|
BString string;
|
||||||
status_t status = fReader->ReadToStream(size, stream);
|
char* buffer = string.LockBuffer(size);
|
||||||
if (status == B_OK) {
|
if (buffer == NULL) {
|
||||||
TRACE("LITERAL: %-*s\n", (int)size, (char*)stream.Buffer());
|
throw ParseException("Not enough memory for literal of %"
|
||||||
} else
|
B_PRIuSIZE " bytes.", size);
|
||||||
TRACE("Reading literal failed: %s\n", strerror(status));
|
}
|
||||||
|
|
||||||
|
size_t totalRead = 0;
|
||||||
|
while (totalRead < size) {
|
||||||
|
ssize_t bytesRead = stream.Read(buffer + totalRead,
|
||||||
|
size - totalRead);
|
||||||
|
if (bytesRead == 0)
|
||||||
|
throw ParseException("Unexpected end of literal");
|
||||||
|
if (bytesRead < 0)
|
||||||
|
throw StreamException(bytesRead);
|
||||||
|
|
||||||
|
totalRead += bytesRead;
|
||||||
|
}
|
||||||
|
|
||||||
|
string.UnlockBuffer(size);
|
||||||
|
arguments.AddItem(new StringArgument(string));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
Response::ParseString(ArgumentList& arguments, const char*& line)
|
Response::ParseString(ArgumentList& arguments, BDataIO& stream)
|
||||||
{
|
{
|
||||||
arguments.AddItem(new StringArgument(ExtractString(line)));
|
arguments.AddItem(new StringArgument(ExtractString(stream)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
BString
|
BString
|
||||||
Response::ExtractString(const char*& line)
|
Response::ExtractString(BDataIO& stream)
|
||||||
{
|
{
|
||||||
const char* start = line;
|
BString string;
|
||||||
|
|
||||||
// TODO: parse modified UTF-7 as described in RFC 3501, 5.1.3
|
// TODO: parse modified UTF-7 as described in RFC 3501, 5.1.3
|
||||||
while (line[0] != '\0') {
|
while (true) {
|
||||||
char c = line[0];
|
char c = Peek(stream);
|
||||||
|
if (c == '\0')
|
||||||
|
break;
|
||||||
if (c <= ' ' || strchr("()[]{}\"", c) != NULL)
|
if (c <= ' ' || strchr("()[]{}\"", c) != NULL)
|
||||||
return BString(start, line - start);
|
return string;
|
||||||
|
|
||||||
line++;
|
string += Next(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw ParseException("Unexpected end of string");
|
throw ParseException("Unexpected end of string");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
size_t
|
||||||
|
Response::ExtractNumber(BDataIO& stream)
|
||||||
|
{
|
||||||
|
BString string = ExtractString(stream);
|
||||||
|
|
||||||
|
const char* end;
|
||||||
|
size_t number = strtoul(string.String(), (char**)&end, 10);
|
||||||
|
if (end == NULL || end[0] != '\0')
|
||||||
|
ParseException("Invalid number!");
|
||||||
|
|
||||||
|
return number;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
Response::Consume(BDataIO& stream, char expected)
|
||||||
|
{
|
||||||
|
char c = Next(stream);
|
||||||
|
if (c != expected)
|
||||||
|
throw ExpectedParseException(expected, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
char
|
||||||
|
Response::Next(BDataIO& stream)
|
||||||
|
{
|
||||||
|
if (fHasNextChar) {
|
||||||
|
fHasNextChar = false;
|
||||||
|
return fNextChar;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Read(stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
char
|
||||||
|
Response::Peek(BDataIO& stream)
|
||||||
|
{
|
||||||
|
if (fHasNextChar)
|
||||||
|
return fNextChar;
|
||||||
|
|
||||||
|
fNextChar = Read(stream);
|
||||||
|
fHasNextChar = true;
|
||||||
|
|
||||||
|
return fNextChar;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
char
|
||||||
|
Response::Read(BDataIO& stream)
|
||||||
|
{
|
||||||
|
char c;
|
||||||
|
ssize_t bytesRead = stream.Read(&c, 1);
|
||||||
|
if (bytesRead == 1) {
|
||||||
|
printf("%c", c);
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bytesRead == 0)
|
||||||
|
throw ParseException("Unexpected end of string");
|
||||||
|
|
||||||
|
throw StreamException(bytesRead);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// #pragma mark -
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
ResponseParser::ResponseParser(ConnectionReader& reader)
|
ResponseParser::ResponseParser(BDataIO& stream)
|
||||||
:
|
:
|
||||||
fLiteralHandler(NULL)
|
fLiteralHandler(NULL)
|
||||||
{
|
{
|
||||||
SetTo(reader);
|
SetTo(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -468,9 +551,9 @@ ResponseParser::~ResponseParser()
|
|||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
ResponseParser::SetTo(ConnectionReader& reader)
|
ResponseParser::SetTo(BDataIO& stream)
|
||||||
{
|
{
|
||||||
fReader = &reader;
|
fStream = &stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -485,16 +568,7 @@ status_t
|
|||||||
ResponseParser::NextResponse(Response& response, bigtime_t timeout)
|
ResponseParser::NextResponse(Response& response, bigtime_t timeout)
|
||||||
throw(ParseException)
|
throw(ParseException)
|
||||||
{
|
{
|
||||||
BString line;
|
response.Parse(*fStream, fLiteralHandler);
|
||||||
status_t status = fReader->GetNextLine(line, timeout);
|
|
||||||
if (status != B_OK) {
|
|
||||||
TRACE("S: read error %s", line.String());
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
TRACE("S: %s", line.String());
|
|
||||||
response.Parse(*fReader, line, fLiteralHandler);
|
|
||||||
|
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
|
#include <DataIO.h>
|
||||||
#include <ObjectList.h>
|
#include <ObjectList.h>
|
||||||
#include <String.h>
|
#include <String.h>
|
||||||
|
|
||||||
@ -16,7 +17,6 @@ namespace IMAP {
|
|||||||
|
|
||||||
|
|
||||||
class Argument;
|
class Argument;
|
||||||
class ConnectionReader;
|
|
||||||
|
|
||||||
|
|
||||||
class ArgumentList : public BObjectList<Argument> {
|
class ArgumentList : public BObjectList<Argument> {
|
||||||
@ -82,12 +82,18 @@ private:
|
|||||||
class ParseException : public std::exception {
|
class ParseException : public std::exception {
|
||||||
public:
|
public:
|
||||||
ParseException();
|
ParseException();
|
||||||
ParseException(const char* message);
|
ParseException(const char* format, ...);
|
||||||
|
|
||||||
const char* Message() const { return fMessage; }
|
const char* Message() const { return fBuffer; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
const char* fMessage;
|
char fBuffer[64];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class StreamException : public ParseException {
|
||||||
|
public:
|
||||||
|
StreamException(status_t status);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -97,7 +103,7 @@ public:
|
|||||||
char instead);
|
char instead);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
char fBuffer[64];
|
const char* CharToString(char* buffer, size_t size, char c);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -106,8 +112,8 @@ public:
|
|||||||
LiteralHandler();
|
LiteralHandler();
|
||||||
virtual ~LiteralHandler();
|
virtual ~LiteralHandler();
|
||||||
|
|
||||||
virtual void HandleLiteral(ConnectionReader& reader,
|
virtual void HandleLiteral(BDataIO& stream,
|
||||||
off_t length) = 0;
|
size_t length) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -116,50 +122,60 @@ public:
|
|||||||
Response();
|
Response();
|
||||||
~Response();
|
~Response();
|
||||||
|
|
||||||
void Parse(ConnectionReader& reader,
|
void Parse(BDataIO& stream, LiteralHandler* handler)
|
||||||
const char* line, LiteralHandler* handler)
|
|
||||||
throw(ParseException);
|
throw(ParseException);
|
||||||
|
|
||||||
bool IsUntagged() const { return fTag == 0; }
|
bool IsUntagged() const { return fTag == 0; }
|
||||||
int32 Tag() const { return fTag; }
|
uint32 Tag() const { return fTag; }
|
||||||
bool IsCommand(const char* command) const;
|
bool IsCommand(const char* command) const;
|
||||||
bool IsContinuation() const { return fContinuation; }
|
bool IsContinuation() const { return fContinuation; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
char ParseLine(ArgumentList& arguments,
|
char ParseLine(ArgumentList& arguments,
|
||||||
const char*& line);
|
BDataIO& stream);
|
||||||
void Consume(const char*& line, char c);
|
|
||||||
void ParseList(ArgumentList& arguments,
|
void ParseList(ArgumentList& arguments,
|
||||||
const char*& line, char start, char end);
|
BDataIO& stream, char start, char end);
|
||||||
void ParseQuoted(ArgumentList& arguments,
|
void ParseQuoted(ArgumentList& arguments,
|
||||||
const char*& line);
|
BDataIO& stream);
|
||||||
void ParseLiteral(ArgumentList& arguments,
|
void ParseLiteral(ArgumentList& arguments,
|
||||||
const char*& line);
|
BDataIO& stream);
|
||||||
void ParseString(ArgumentList& arguments,
|
void ParseString(ArgumentList& arguments,
|
||||||
const char*& line);
|
BDataIO& stream);
|
||||||
BString ExtractString(const char*& line);
|
|
||||||
|
BString ExtractString(BDataIO& stream);
|
||||||
|
size_t ExtractNumber(BDataIO& stream);
|
||||||
|
|
||||||
|
void Consume(BDataIO& stream, char c);
|
||||||
|
|
||||||
|
char Next(BDataIO& stream);
|
||||||
|
char Peek(BDataIO& stream);
|
||||||
|
char Read(BDataIO& stream);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
ConnectionReader* fReader;
|
|
||||||
LiteralHandler* fLiteralHandler;
|
LiteralHandler* fLiteralHandler;
|
||||||
int32 fTag;
|
uint32 fTag;
|
||||||
bool fContinuation;
|
bool fContinuation;
|
||||||
|
bool fHasNextChar;
|
||||||
|
char fNextChar;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class ResponseParser {
|
class ResponseParser {
|
||||||
public:
|
public:
|
||||||
ResponseParser(ConnectionReader& reader);
|
ResponseParser(BDataIO& stream);
|
||||||
~ResponseParser();
|
~ResponseParser();
|
||||||
|
|
||||||
void SetTo(ConnectionReader& reader);
|
void SetTo(BDataIO& stream);
|
||||||
void SetLiteralHandler(LiteralHandler* handler);
|
void SetLiteralHandler(LiteralHandler* handler);
|
||||||
|
|
||||||
status_t NextResponse(Response& response,
|
status_t NextResponse(Response& response,
|
||||||
bigtime_t timeout) throw(ParseException);
|
bigtime_t timeout) throw(ParseException);
|
||||||
|
|
||||||
|
private:
|
||||||
|
ResponseParser(const ResponseParser& other);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
ConnectionReader* fReader;
|
BDataIO* fStream;
|
||||||
LiteralHandler* fLiteralHandler;
|
LiteralHandler* fLiteralHandler;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user