* 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:
parent
74ddcac51f
commit
b47bd3cf51
|
@ -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
|
|
@ -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
|
Loading…
Reference in New Issue