* Work in progress of an IMAP response parser that will replace weak and

error prone parsing method that is currently utilized by the IMAP module.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@43052 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler 2011-10-31 23:33:22 +00:00
parent 74ddcac51f
commit b47bd3cf51
2 changed files with 563 additions and 0 deletions

View File

@ -0,0 +1,413 @@
/*
* Copyright 2011, Axel Dörfler, axeld@pinc-software.de.
* Distributed under the terms of the MIT License.
*/
#include "Response.h"
namespace IMAP {
ArgumentList::ArgumentList()
:
fArguments(5, true)
{
}
ArgumentList::~ArgumentList()
{
}
BString
ArgumentList::StringAt(int32 index) const
{
if (index >= 0 && index < fArguments.CountItems()) {
if (StringArgument* argument = dynamic_cast<StringArgument*>(
fArguments.ItemAt(index)))
return argument->String();
}
return "";
}
bool
ArgumentList::IsStringAt(int32 index) const
{
if (index >= 0 && index < fArguments.CountItems()) {
if (dynamic_cast<StringArgument*>(fArguments.ItemAt(index)) != NULL)
return true;
}
return false;
}
bool
ArgumentList::EqualsAt(int32 index, const char* string) const
{
return StringAt(index).ICompare(string);
}
const ArgumentList&
ArgumentList::ListAt(int32 index) const
{
if (index >= 0 && index < fArguments.CountItems()) {
if (ListArgument* argument = dynamic_cast<ListArgument*>(
fArguments.ItemAt(index)))
return argument->List();
}
static ArgumentList empty(0, true);
return empty;
}
bool
ArgumentList::IsListAt(int32 index) const
{
if (index >= 0 && index < fArguments.CountItems()) {
if (ListArgument* argument = dynamic_cast<ListArgument*>(
fArguments.ItemAt(index)))
return true;
}
return false;
}
bool
ArgumentList::IsListAt(int32 index, char kind) const
{
if (index >= 0 && index < fArguments.CountItems()) {
if (ListArgument* argument = dynamic_cast<ListArgument*>(
fArguments.ItemAt(index)))
return argument->Kind() == kind;
}
return false;
}
int32
ArgumentList::IntegerAt(int32 index) const
{
return atoi(StringAt(index).String());
}
bool
ArgumentList::IsIntegerAt(int32 index) const
{
BString string = StringAt(index);
for (int32 i = 0; i < string.Length(); i++) {
if (!isdigit(string.ByteAt(i)))
return false;
}
return string.Length() > 0;
}
// #pragma mark -
Argument::Argument()
{
}
Argument::~Argument()
{
}
/*static*/ BString
Argument::ToString(const ArgumentList& arguments)
{
BString string;
for (int32 i = 0; i < arguments.CountItems(); i++) {
if (i > 0)
string += ", ";
string += arguments.ItemAt(i)->ToString();
}
return string;
}
bool
Argument::Contains(const ArgumentList& arguments, const char* string) const
{
for (int32 i = 0; i < arguments.CountItems(); i++) {
if (StringArgument* argument = dynamic_cast<StringArgument*>(
arguments.ItemAt(i))) {
if (argument->String().ICompare(string))
return true;
}
}
return false;
}
// #pragma mark -
ListArgument::ListArgument()
:
fList(5, true)
{
}
BString
ListArgument::ToString() const
{
BString string("(");
string += Argument::ToString(response.Arguments());
string += ")";
return string;
}
// #pragma mark -
StringArgument::StringArgument(const BString& string)
:
fString(string)
{
}
BString
StringArgument::ToString() const
{
return fString;
}
// #pragma mark -
ParseException::ParseException()
:
fMessage(NULL)
{
}
ParseException::ParseException(const char* message)
:
fMessage(message)
{
}
ParseException::~ParseException()
{
}
// #pragma mark -
ExpectedParseException::ExpectedParseException(char expected, char instead)
{
snprintf(fBuffer, sizeof(fBuffer), "Expected \"%c\", but got \"%c\"!",
expected, instead);
fMessage = fBuffer;
}
// #pragma mark -
Response::Response()
:
fTag(0),
fArguments(5, true),
fContinued(false)
{
}
Response::~Response()
{
}
void
Response::SetTo(const char* line) throw(ParseException)
{
MakeEmpty();
fTag = 0;
fContinued = false;
if (line[0] == '*') {
// Untagged response
Consume(line, '*');
Consume(line, ' ');
} else if (line[0] == '+') {
// Continuation
Consume(line, '+');
fContinued = true;
} else {
// Tagged response
Consume(line, 'A');
fTag = strtoul(line, (char**)&line, 10);
if (line == NULL)
ParseException("Invalid tag!");
Consume(line, ' ');
}
char c = ParseLine(this, line);
if (c != '\0')
throw ExpectedParseException('\0', c);
}
bool
Response::IsCommand(const char* command) const
{
return IsStringAt(0, command);
}
char
Response::ParseLine(ArgumentList& arguments, const char*& line)
{
while (line[0] != '\0') {
char c = line[0];
switch (c) {
case '(':
ParseList(arguments, line, '(', ')');
break;
case '[':
ParseList(arguments, line, '[', ']');
break;
case ')':
case ']':
Consume(line, c);
return c;
case '"':
ParseQuoted(arguments, line);
break;
case '{':
ParseLiteral(arguments, line);
break;
case ' ':
case '\t':
// whitespace
Consume(line, c);
break;
case '\r':
Consume(line, '\r');
Consume(line, '\n');
return '\0';
case '\n':
Consume(line, '\n');
return '\0';
default:
ParseString(arguments, line);
break;
}
}
return '\0';
}
void
Response::Consume(const char*& line, char c)
{
if (line[0] != c)
throw ExpectedParseException(c, line[0]);
line++;
}
void
Response::ParseList(ArgumentList& arguments, const char*& line, char start,
char end)
{
Consume(line, start);
ListArgument* argument = new ListArgument(start);
arguments.AddItem(argument);
char c = ParseLine(argument->List(), line);
if (c != end)
throw ExpectedParseException(end, c);
}
void
Response::ParseQuoted(ArgumentList& arguments, const char*& line)
{
Consume(line, '"');
BString string;
char* output = string.LockBuffer(strlen(line));
int32 index = 0;
while (line[0] != '\0') {
char c = line[0];
if (c == '\\') {
line++;
if (line[0] == '\0')
break;
} else if (c == '"') {
line++;
output[index] = '\0';
string.UnlockBuffer(index);
arguments.AddItem(new StringArgument(string));
return;
}
output[index++] = c;
line++;
}
throw ParseException("Unexpected end of qouted string!");
}
void
Response::ParseLiteral(ArgumentList& arguments, const char*& line)
{
// TODO!
throw ParseException("Literals are not yet supported!");
}
void
Response::ParseString(ArgumentList& arguments, const char*& line)
{
arguments.AddItem(new StringArgument(ExtractString(line)));
}
BString
Response::ExtractString(const char*& line)
{
const char* start = line;
while (line[0] != '\0') {
char c = line[0];
if (c <= ' ' || strchr("()[]{}\"", c) != NULL)
return BString(start, line - start);
line++;
}
throw ParseException("Unexpected end of string");
}
} // namespace IMAP

View File

@ -0,0 +1,150 @@
/*
* Copyright 2011, Axel Dörfler, axeld@pinc-software.de.
* Distributed under the terms of the MIT License.
*/
#ifndef RESPONSE_H
#define RESPONSE_H
#include <stdexcept>
#include <ObjectList.h>
#include <String.h>
namespace IMAP {
class Argument;
class ArgumentList {
public:
ArgumentList();
~ArgumentList();
size_t CountItems()
{ return (size_t)fArguments.CountItems(); }
Argument& ItemAt(size_t index) const
{ return *fArguments.ItemAt(index); }
void MakeEmpty()
{ fArguments.MakeEmpty(); }
bool AddItem(Argument* argument)
{ return fArguments.AddItem(argument); }
Argument* RemoveItem(size_t index)
{ return fArguments.RemoveItemAt(index); }
BString ToString() const;
bool Contains(const char* string) const;
BString StringAt(int32 index) const;
bool IsStringAt(int32 index) const;
bool EqualsAt(int32 index,
const char* string) const;
const ArgumentList& ListAt(int32 index) const;
bool IsListAt(int32 index) const;
bool IsListAt(int32 index, char kind) const;
int32 IntegerAt(int32 index) const;
bool IsIntegerAt(int32 index) const;
private:
BObjectList<Argument> fArguments;
};
class Argument {
public:
Argument();
virtual ~Argument();
virtual BString ToString() const = 0;
};
class ListArgument : public Argument {
public:
ListArgument(char kind);
ArgumentList& List() { return fList; }
char Kind() { return fKind; }
virtual BString ToString() const;
private:
ArgumentList fList;
char fKind;
};
class StringArgument : public Argument {
public:
StringArgument(const BString& string);
const BString& String() { return fString; }
virtual BString ToString() const;
private:
BString fString;
};
class ParseException : public std::exception {
public:
ParseException();
ParseException(const char* message);
virtual ~ParseException();
const char* Message() const { return fMessage; }
protected:
const char* fMessage;
};
class ExpectedParseException : ParseException {
public:
ExpectedParseException(char expected,
char instead);
protected:
char fBuffer[64];
};
class Response : public ArgumentList {
public:
Response();
~Response();
void SetTo(const char* line) throw(ParseException);
bool IsUntagged() const { return fTag == 0; }
int32 Tag() const { return fTag; }
bool IsCommand(const char* command) const;
bool IsContinued() const { return fContinued; }
protected:
char ParseLine(ArgumentList& arguments,
const char*& line);
void Consume(const char*& line, char c);
void ParseList(ArgumentList& arguments,
const char*& line, char start, char end);
void ParseQuoted(ArgumentList& arguments,
const char*& line);
void ParseLiteral(ArgumentList& arguments,
const char*& line);
void ParseString(ArgumentList& arguments,
const char*& line);
BString ExtractString(const char*& line);
protected:
int32 fTag;
bool fContinued;
};
} // namespace IMAP
#endif // RESPONSE_H