#include "NGImap4ResponseParser.h"
#include "NGImap4Support.h"
+#include "NGImap4Envelope.h"
+#include "NGImap4EnvelopeAddress.h"
#include "imCommon.h"
// TODO(hh): code is now prepared for last-exception, but currently it just
@interface NGImap4ResponseParser(ParsingPrivates)
- (BOOL)_parseNumberUntaggedResponse:(NGMutableHashMap *)result_;
- (NSDictionary *)_parseBodyContent;
+
+- (NSData *)_parseData;
+
+- (BOOL)_parseQuotaResponseIntoHashMap:(NGMutableHashMap *)result_;
+- (void)_parseContinuationResponseIntoHashMap:(NGMutableHashMap *)result_;
+- (BOOL)_parseListOrLSubResponseIntoHashMap:(NGMutableHashMap *)result_;
+- (BOOL)_parseCapabilityResponseIntoHashMap:(NGMutableHashMap *)result_;
+- (BOOL)_parseSearchResponseIntoHashMap:(NGMutableHashMap *)result_;
+- (BOOL)_parseSortResponseIntoHashMap:(NGMutableHashMap *)result_;
+- (BOOL)_parseQuotaRootResponseIntoHashMap:(NGMutableHashMap *)result_;
+- (BOOL)_parseStatusResponseIntoHashMap:(NGMutableHashMap *)result_;
+- (BOOL)_parseByeUntaggedResponseIntoHashMap:(NGMutableHashMap *)result_;
+
+- (NSArray *)_parseThread;
+
@end
@implementation NGImap4ResponseParser
-#define __la(__SELF__, __LACNT) \
+#define __la(__SELF__, __PEEKPOS) \
((__SELF__->la == NULL) \
- ? [__SELF__->buffer la:__LACNT]\
- : __SELF__->la(__SELF__->buffer, @selector(la:), __LACNT))
+ ? [__SELF__->buffer la:__PEEKPOS]\
+ : __SELF__->la(__SELF__->buffer, @selector(la:), __PEEKPOS))
static __inline__ int _la(NGImap4ResponseParser *self, unsigned _laCnt) {
- char c = __la(self, _laCnt);
+ register unsigned char c = __la(self, _laCnt);
- if (c == '\r')
- return _la(self, _laCnt + 1);
+ return (c == '\r')
+ ? _la(self, _laCnt + 1)
+ : c;
+}
+static __inline__ BOOL _matchesString(NGImap4ResponseParser *self,
+ unsigned char *s)
+{
+ register unsigned int i;
- return c;
+ for (i = 0; s[i] != '\0'; i++) {
+ if (_la(self, i) != s[i])
+ return NO;
+ }
+ return YES;
}
static NSDictionary *_parseBody(NGImap4ResponseParser *self);
NGMutableHashMap *result_);
static void _parseUntaggedResponse(NGImap4ResponseParser *self,
NGMutableHashMap *result_);
-static BOOL _parseByeUntaggedResponse(NGImap4ResponseParser *self,
- NGMutableHashMap *result_);
static NSArray *_parseFlagArray(NGImap4ResponseParser *self);
static BOOL _parseFlagsUntaggedResponse(NGImap4ResponseParser *self,
NGMutableHashMap *result_);
NGMutableHashMap *result_);
static BOOL _parseThreadResponse(NGImap4ResponseParser *self,
NGMutableHashMap *result_);
-static BOOL _parseStatusResponse(NGImap4ResponseParser *self,
- NGMutableHashMap *result_);
-static BOOL _parseListOrLSubResponse(NGImap4ResponseParser *self,
- NGMutableHashMap *result_);
-static BOOL _parseCapabilityResponse(NGImap4ResponseParser *self,
- NGMutableHashMap *result_);
-static BOOL _parseSearchResponse(NGImap4ResponseParser *self,
- NGMutableHashMap *result_);
-static NSArray *_parseThread(NGImap4ResponseParser *self);
-static BOOL _parseSortResponse(NGImap4ResponseParser *self,
- NGMutableHashMap *result_);
static NSNumber *_parseUnsigned(NGImap4ResponseParser *self);
static NSString *_parseUntil(NGImap4ResponseParser *self, char _c);
static NSString *_parseUntil2(NGImap4ResponseParser *self, char _c1, char _c2);
-static __inline__ void _match(NGImap4ResponseParser *self, char _match);
+
+static __inline__ NSException *_consumeIfMatch
+ (NGImap4ResponseParser *self, unsigned char _m);
static __inline__ void _consume(NGImap4ResponseParser *self, unsigned _cnt);
-static void _parseContinuationResponse(NGImap4ResponseParser *self,
- NGMutableHashMap *result_);
-static NSData *_parseData(NGImap4ResponseParser *self);
static void _parseSieveRespone(NGImap4ResponseParser *self,
NGMutableHashMap *result_);
static NSString *_parseContentSieveResponse(NGImap4ResponseParser *self);
static NSString *_parseStringSieveResponse(NGImap4ResponseParser *self);
-static BOOL _parseQuotaResponse(NGImap4ResponseParser *self,
- NGMutableHashMap *result_);
-static BOOL _parseQuotaRootResponse(NGImap4ResponseParser *self,
- NGMutableHashMap *result_);
-
static unsigned int LaSize = 4097;
static unsigned UseMemoryMappedData = 0;
static unsigned Imap4MMDataBoundary = 0;
static NSStringEncoding defCStringEncoding;
static NSNumber *YesNum = nil;
static NSNumber *NoNum = nil;
+static NSNull *null = nil;
+ (void)initialize {
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
if (didInit) return;
didInit = YES;
+ null = [[NSNull null] retain];
+
encoding = [NGMimePartParser defaultHeaderFieldEncoding];
defCStringEncoding = [NSString defaultCStringEncoding];
}
}
else if (l0 == '+') { /* starting with a '+'? */
- _parseContinuationResponse(self, result);
+ [self _parseContinuationResponseIntoHashMap:result];
endOfCommand = YES;
}
else if (isdigit(l0)) {
return;
}
-static NSData *_parseData(NGImap4ResponseParser *self) {
- // TODO: split up method
- NSData *result;
- unsigned size;
- NSNumber *n;
-
- if (_la(self, 0) != '{')
- return nil;
-
- if (debugDataOn) [self logWithFormat:@"parse data ..."];
-
- /* got header */
- result = nil;
-
- _consume(self, 1);
- if ((n = _parseUnsigned(self)) == nil) {
- NSException *e;
-
- e = [[NGImap4ParserException alloc]
- initWithFormat:@"expect a number between {}"];
- [self setLastException:[e autorelease]];
- return nil;
- }
- if (debugDataOn) [self logWithFormat:@" parse data: %@", n];
- _match(self, '}');
- _match(self, '\n');
-
- size = [n intValue];
-
- if (UseMemoryMappedData && (size > Imap4MMDataBoundary)) {
- static NSProcessInfo *Pi = nil;
- NGFileStream *stream;
-
- unsigned char buf[LaSize + 2];
- unsigned char tmpBuf[LaSize + 2];
- unsigned wasRead = 0;
- NSString *path;
- signed char lastChar; // must be signed
+- (NSData *)_parseDataToFile:(unsigned)_size {
+ // TODO: move to own method
+ // TODO: do not use NGFileStream but just fopen/fwrite
+ static NSProcessInfo *Pi = nil;
+ NGFileStream *stream;
+ NSData *result;
+ unsigned char buf[LaSize + 2];
+ unsigned char tmpBuf[LaSize + 2];
+ unsigned wasRead = 0;
+ NSString *path;
+ signed char lastChar; // must be signed
- if (debugDataOn) [self logWithFormat:@" using memory mapped data ..."];
+ if (debugDataOn) [self logWithFormat:@" using memory mapped data ..."];
- if (Pi == nil)
- Pi = [[NSProcessInfo processInfo] retain];
+ if (Pi == nil)
+ Pi = [[NSProcessInfo processInfo] retain];
- path = [Pi temporaryFileName];
- stream = [NGFileStream alloc]; /* extra line to keep gcc happy */
- stream = [stream initWithPath:path];
+ path = [Pi temporaryFileName];
+ stream = [NGFileStream alloc]; /* extra line to keep gcc happy */
+ stream = [stream initWithPath:path];
- if (![stream openInMode:NGFileWriteOnly]) {
- NSException *e;
+ if (![stream openInMode:NGFileWriteOnly]) {
+ NSException *e;
- e = [[NGImap4ParserException alloc]
- initWithFormat:@"Could not open temporary file %@", path];
- [self setLastException:[e autorelease]];
- return nil;
- }
+ e = [[NGImap4ParserException alloc]
+ initWithFormat:@"Could not open temporary file %@", path];
+ [self setLastException:[e autorelease]];
+ return nil;
+ }
- lastChar = -1;
- while (wasRead < size) {
- unsigned readCnt, bufCnt, tmpSize, cnt, tmpBufCnt;
+ lastChar = -1;
+ while (wasRead < _size) {
+ unsigned readCnt, bufCnt, tmpSize, cnt, tmpBufCnt;
- bufCnt = 0;
+ bufCnt = 0;
- if (lastChar != -1) {
- buf[bufCnt++] = lastChar;
- lastChar = -1;
- }
+ if (lastChar != -1) {
+ buf[bufCnt++] = lastChar;
+ lastChar = -1;
+ }
- [self->buffer la:(size - wasRead < LaSize)
- ? (size - wasRead)
- : LaSize];
+ [self->buffer la:(_size - wasRead < LaSize)
+ ? (_size - wasRead)
+ : LaSize];
- readCnt = [self->buffer readBytes:buf+bufCnt count:size - wasRead];
+ readCnt = [self->buffer readBytes:buf+bufCnt count:_size - wasRead];
- wasRead+=readCnt;
- bufCnt +=readCnt;
+ wasRead+=readCnt;
+ bufCnt +=readCnt;
- tmpSize = bufCnt - 1;
- cnt = 0;
- tmpBufCnt = 0;
+ tmpSize = bufCnt - 1;
+ cnt = 0;
+ tmpBufCnt = 0;
- while (cnt < tmpSize) {
- if ((buf[cnt] == '\r') && (buf[cnt+1] == '\n')) {
- cnt++;
- }
- tmpBuf[tmpBufCnt++] = buf[cnt++];
- }
- if (cnt < bufCnt) {
- lastChar = buf[cnt];
- }
- [stream writeBytes:tmpBuf count:tmpBufCnt];
+ while (cnt < tmpSize) {
+ if ((buf[cnt] == '\r') && (buf[cnt+1] == '\n')) {
+ cnt++;
}
- if (lastChar != -1)
- [stream writeBytes:&lastChar count:1];
-
- [stream close];
- [stream release]; stream = nil;
- result = [NSData dataWithContentsOfMappedFile:path];
- [[NSFileManager defaultManager] removeFileAtPath:path handler:nil];
-
- return result;
+ tmpBuf[tmpBufCnt++] = buf[cnt++];
+ }
+ if (cnt < bufCnt) {
+ lastChar = buf[cnt];
+ }
+ [stream writeBytes:tmpBuf count:tmpBufCnt];
}
+ if (lastChar != -1)
+ [stream writeBytes:&lastChar count:1];
+
+ [stream close];
+ [stream release]; stream = nil;
+ result = [NSData dataWithContentsOfMappedFile:path];
+ [[NSFileManager defaultManager] removeFileAtPath:path handler:nil];
- if (size == 0) {
- [self logWithFormat:@"ERROR(%s): got content size '0'!",
- __PRETTY_FUNCTION__];
- return nil;
- }
- else {
- unsigned char *buf = NULL;
- unsigned wasRead = 0;
- unsigned char *tmpBuf;
- unsigned cnt, tmpBufCnt, tmpSize;
+ return result;
+}
+- (NSData *)_parseDataIntoRAM:(unsigned)_size {
+ /* parses data into a RAM buffer (NSData) */
+ unsigned char *buf = NULL;
+ unsigned char *tmpBuf;
+ unsigned wasRead = 0;
+ unsigned cnt, tmpBufCnt, tmpSize;
+ NSData *result;
- buf = calloc(size + 10, sizeof(char));
+ buf = calloc(_size + 10, sizeof(char));
- while (wasRead < size) {
- [self->buffer la:(size - wasRead < LaSize) ? (size - wasRead) : LaSize];
+ while (wasRead < _size) {
+ [self->buffer la:(_size - wasRead < LaSize) ? (_size - wasRead) : LaSize];
- wasRead += [self->buffer readBytes:(buf + wasRead)
- count:(size - wasRead)];
- }
-
- /* normalize response \r\n -> \n */
+ wasRead += [self->buffer readBytes:(buf + wasRead) count:(_size-wasRead)];
+ }
+
+ /* normalize response \r\n -> \n */
- tmpBuf = calloc(size + 10, sizeof(char));
- cnt = 0;
- tmpBufCnt = 0;
- tmpSize = size == 0 ? 0 : size - 1;
- while (tmpBufCnt < tmpSize && cnt < size) {
- if ((buf[cnt] == '\r') && (buf[cnt + 1] == '\n'))
- cnt++; /* skip \r */
+ tmpBuf = calloc(_size + 10, sizeof(char));
+ cnt = 0;
+ tmpBufCnt = 0;
+ tmpSize = _size == 0 ? 0 : _size - 1;
+ while (tmpBufCnt < tmpSize && cnt < _size) {
+ if ((buf[cnt] == '\r') && (buf[cnt + 1] == '\n'))
+ cnt++; /* skip \r */
- tmpBuf[tmpBufCnt] = buf[cnt];
- tmpBufCnt++;
- cnt++;
- }
- if (cnt < size) {
- tmpBuf[tmpBufCnt] = buf[cnt];
- tmpBufCnt++;
- cnt++;
- }
+ tmpBuf[tmpBufCnt] = buf[cnt];
+ tmpBufCnt++;
+ cnt++;
+ }
+ if (cnt < _size) {
+ tmpBuf[tmpBufCnt] = buf[cnt];
+ tmpBufCnt++;
+ cnt++;
+ }
- result = [NSData dataWithBytesNoCopy:tmpBuf length:tmpBufCnt];
+ result = [NSData dataWithBytesNoCopy:tmpBuf length:tmpBufCnt];
- if (buf != NULL) free(buf); buf = NULL;
- return result;
+ if (buf != NULL) free(buf); buf = NULL;
+ return result;
+}
+- (NSData *)_parseData {
+ /*
+ parses:
+ { <uint> } \n
+ */
+ // TODO: split up method
+ NSData *result;
+ unsigned size;
+ NSNumber *sizeNum;
+
+ if (_la(self, 0) != '{')
+ return nil;
+
+ if (debugDataOn) [self logWithFormat:@"parse data ..."];
+
+ /* got header */
+ result = nil;
+
+ _consume(self, 1);
+ if ((sizeNum = _parseUnsigned(self)) == nil) {
+ NSException *e;
+
+ e = [[NGImap4ParserException alloc]
+ initWithFormat:@"expect a number between {}"];
+ [self setLastException:[e autorelease]];
+ return nil;
}
+ if (debugDataOn) [self logWithFormat:@" parse data, size: %@", sizeNum];
+ _consumeIfMatch(self, '}');
+ _consumeIfMatch(self, '\n');
+
+ if ((size = [sizeNum intValue]) == 0) {
+ [self logWithFormat:@"ERROR(%s): got content size '0'!",
+ __PRETTY_FUNCTION__];
+ return nil;
+ }
+
+ if (UseMemoryMappedData && (size > Imap4MMDataBoundary))
+ return [self _parseDataToFile:size];
+
+ return [self _parseDataIntoRAM:size];
}
static int _parseTaggedResponse(NGImap4ResponseParser *self,
return -1;
}
- _match(self, ' ');
+ _consumeIfMatch(self, ' ');
res = [_parseUntil(self, ' ') lowercaseString];
if (_la(self, 0) == '[') { /* Found flag like [READ-ONLY] */
_consume(self, 1);
// TODO: is it really required by IMAP4 that responses are uppercase?
// TODO: apparently this code *breaks* with lowercase detection on!
unsigned char l0, l1 = 0;
- _match(self, '*');
- _match(self, ' ');
+ _consumeIfMatch(self, '*');
+ _consumeIfMatch(self, ' ');
l0 = _la(self, 0);
switch (l0) {
l1 = _la(self, 1);
if (l1 == 'A' && _parseBadUntaggedResponse(self, result_)) // la: 3
return;
- if (l1 == 'Y' && _parseByeUntaggedResponse(self, result_)) // la: 3
+ if (l1 == 'Y' && [self _parseByeUntaggedResponseIntoHashMap:result_]) // 3
return;
break;
case 'C':
- if (_parseCapabilityResponse(self, result_)) // la: 10
+ if ([self _parseCapabilityResponseIntoHashMap:result_]) // la: 10
return;
break;
-
+
case 'F':
if (_parseFlagsUntaggedResponse(self, result_)) // la: 5
return;
break;
case 'L':
- if (_parseListOrLSubResponse(self, result_)) // la: 4
+ if ([self _parseListOrLSubResponseIntoHashMap:result_]) // la: 4
return;
break;
case 'S':
switch (_la(self, 1)) {
case 'O': // SORT
- if (_parseSortResponse(self, result_)) // la: 4
+ if ([self _parseSortResponseIntoHashMap:result_]) // la: 4
return;
break;
case 'E': // SEARCH
- if (_parseSearchResponse(self, result_)) // la: 5
+ if ([self _parseSearchResponseIntoHashMap:result_]) // la: 5
return;
break;
case 'T': // STATUS
- if (_parseStatusResponse(self, result_)) // la: 6
+ if ([self _parseStatusResponseIntoHashMap:result_]) // la: 6
/* eg "* STATUS INBOX (MESSAGES 0 RECENT 0 UNSEEN 0)" */
return;
break;
break;
case 'Q':
- if (_parseQuotaResponse(self, result_)) // la: 6
+ if ([self _parseQuotaResponseIntoHashMap:result_]) // la: 6
return;
- if (_parseQuotaRootResponse(self, result_)) // la: 10
+ if ([self _parseQuotaRootResponseIntoHashMap:result_]) // la: 10
return;
break;
[self logWithFormat:@"%s: no matching tag specifier?", __PRETTY_FUNCTION__];
}
-static void _parseContinuationResponse(NGImap4ResponseParser *self,
- NGMutableHashMap *result_) {
- _match(self, '+');
- _match(self, ' ');
-
+- (void)_parseContinuationResponseIntoHashMap:(NGMutableHashMap *)result_ {
+ _consumeIfMatch(self, '+');
+ _consumeIfMatch(self, ' ');
+
[result_ addObject:YesNum forKey:@"ContinuationResponse"];
[result_ addObject:_parseUntil(self, '\n') forKey:@"description"];
}
-static BOOL _parseListOrLSubResponse(NGImap4ResponseParser *self,
- NGMutableHashMap *result_) {
- if (((_la(self, 0) == 'L')
- && (_la(self, 1) == 'I')
- && (_la(self, 2) == 'S')
- && (_la(self, 3) == 'T')
- && (_la(self, 4) == ' ')) ||
- ((_la(self, 0) == 'L')
- && (_la(self, 1) == 'S')
- && (_la(self, 2) == 'U')
- && (_la(self, 3) == 'B')
- && (_la(self, 4) == ' '))) {
- NSArray *flags = nil;
- NSString *delim = nil;
- NSString *name = nil;
- NSDictionary *d;
-
- _consume(self, 5);
-
- flags = _parseFlagArray(self);
-
- _match(self, ' ');
-
- if (_la(self, 0) == '"') {
- _match(self, '"');
- delim = _parseUntil(self, '"');
- _match(self, ' ');
- }
- else {
- _parseUntil(self, ' ');
- delim = nil;
- }
- if (_la(self, 0) == '"') {
- _consume(self, 1);
- name = _parseUntil(self, '"');
- _parseUntil(self, '\n');
- }
- else {
- name = _parseUntil(self, '\n');
- }
+- (NSString *)_parseQuotedString {
+ /* parse a quoted string, eg '"' */
+ if (_la(self, 0) != '"')
+ return nil;
+ _consume(self, 1);
+ return _parseUntil(self, '"');
+}
+- (void)_consumeOptionalSpace {
+ if (_la(self, 0) == ' ') _consume(self, 1);
+}
- d = [[NSDictionary alloc] initWithObjectsAndKeys:
- name, @"folderName",
- flags, @"flags",
- delim, @"delimiter", nil];
- [result_ addObject:d forKey:@"list"];
- [d release];
- return YES;
+- (BOOL)_parseListOrLSubResponseIntoHashMap:(NGMutableHashMap *)result_ {
+ NSArray *flags = nil;
+ NSString *delim = nil;
+ NSString *name = nil;
+ NSDictionary *d;
+
+ if (!_matchesString(self, "LIST ") && !_matchesString(self, "LSUB "))
+ return NO;
+
+ _consume(self, 5); /* consume 'LIST ' or 'LSUB ' */
+ flags = _parseFlagArray(self);
+ _consumeIfMatch(self, ' ');
+
+ if (_la(self, 0) == '"') {
+ delim = [self _parseQuotedString];
+ _consumeIfMatch(self, ' ');
}
- return NO;
+ else {
+ _parseUntil(self, ' ');
+ delim = nil;
+ }
+ if (_la(self, 0) == '"') {
+ name = [self _parseQuotedString];
+ _parseUntil(self, '\n');
+ }
+ else
+ name = _parseUntil(self, '\n');
+
+ d = [[NSDictionary alloc] initWithObjectsAndKeys:
+ name, @"folderName",
+ flags, @"flags",
+ delim, @"delimiter", nil];
+ [result_ addObject:d forKey:@"list"];
+ [d release];
+ return YES;
}
-static BOOL _parseCapabilityResponse(NGImap4ResponseParser *self,
- NGMutableHashMap *result_) {
- if ((_la(self, 0) == 'C')
- && (_la(self, 1) == 'A')
- && (_la(self, 2) == 'P')
- && (_la(self, 3) == 'A')
- && (_la(self, 4) == 'B')
- && (_la(self, 5) == 'I')
- && (_la(self, 6) == 'L')
- && (_la(self, 7) == 'I')
- && (_la(self, 8) == 'T')
- && (_la(self, 9) == 'Y')
- && (_la(self, 10) == ' ')) {
- NSString *caps;
-
- caps = _parseUntil(self, '\n');
- {
- NSEnumerator *enumerator;
- id obj;
- NSMutableArray *array;
- NSArray *tmp;
-
- array = [[NSMutableArray alloc] initWithCapacity:16];
-
- enumerator = [[caps componentsSeparatedByString:@" "] objectEnumerator];
- while ((obj = [enumerator nextObject])) {
- [array addObject:[obj lowercaseString]];
- }
- tmp = [array shallowCopy];
- [result_ addObject:tmp forKey:@"capability"];
+- (BOOL)_parseCapabilityResponseIntoHashMap:(NGMutableHashMap *)result_ {
+ NSString *caps;
+ NSEnumerator *enumerator;
+ id obj;
+ NSMutableArray *array;
+ NSArray *tmp;
+
+ if (!_matchesString(self, "CAPABILITY "))
+ return NO;
- [array release]; array = nil;
- [tmp release]; tmp = nil;
- }
- return YES;
- }
- return NO;
-}
+ caps = _parseUntil(self, '\n');
-static BOOL _parseSearchResponse(NGImap4ResponseParser *self,
- NGMutableHashMap *result_) {
- if ((_la(self, 0) == 'S')
- && (_la(self, 1) == 'E')
- && (_la(self, 2) == 'A')
- && (_la(self, 3) == 'R')
- && (_la(self, 4) == 'C')
- && (_la(self, 5) == 'H')) {
+ array = [[NSMutableArray alloc] initWithCapacity:16];
- NSMutableArray *msn = nil;
+ enumerator = [[caps componentsSeparatedByString:@" "] objectEnumerator];
+ while ((obj = [enumerator nextObject]) != nil)
+ [array addObject:[obj lowercaseString]];
+
+ tmp = [array copy];
+ [result_ addObject:tmp forKey:@"capability"];
+
+ [array release]; array = nil;
+ [tmp release]; tmp = nil;
+ return YES;
+}
- _consume(self, 6);
+- (BOOL)_parseSearchResponseIntoHashMap:(NGMutableHashMap *)result_ {
+ NSMutableArray *msn = nil;
+
+ if (!_matchesString(self, "SEARCH "))
+ return NO;
- msn = [NSMutableArray arrayWithCapacity:128];
+ _consume(self, 6);
- while (_la(self, 0) == ' ') {
+ msn = [NSMutableArray arrayWithCapacity:128];
+
+ while (_la(self, 0) == ' ') {
_consume(self, 1);
[msn addObject:_parseUnsigned(self)];
- }
- _parseUntil(self, '\n');
- [result_ addObject:msn forKey:@"search"];
- return YES;
}
- return NO;
+ _parseUntil(self, '\n');
+ [result_ addObject:msn forKey:@"search"];
+ return YES;
}
-static BOOL _parseSortResponse(NGImap4ResponseParser *self,
- NGMutableHashMap *result_) {
- if ((_la(self, 0) == 'S')
- && (_la(self, 1) == 'O')
- && (_la(self, 2) == 'R')
- && (_la(self, 3) == 'T')) {
-
- NSMutableArray *msn = nil;
-
- _consume(self, 4);
+- (BOOL)_parseSortResponseIntoHashMap:(NGMutableHashMap *)result_ {
+ NSMutableArray *msn = nil;
+
+ if (!_matchesString(self, "SORT"))
+ return NO;
+
+ _consume(self, 4);
- msn = [NSMutableArray arrayWithCapacity:128];
+ msn = [NSMutableArray arrayWithCapacity:128];
- while (_la(self, 0) == ' ') {
- _consume(self, 1);
- [msn addObject:_parseUnsigned(self)];
- }
- _parseUntil(self, '\n');
- [result_ addObject:msn forKey:@"sort"];
- return YES;
+ while (_la(self, 0) == ' ') {
+ _consume(self, 1);
+ [msn addObject:_parseUnsigned(self)];
}
- return NO;
+ _parseUntil(self, '\n');
+ [result_ addObject:msn forKey:@"sort"];
+ return YES;
}
-static BOOL _parseQuotaResponse(NGImap4ResponseParser *self,
- NGMutableHashMap *result_) {
- if ((_la(self, 0) == 'Q')
- && (_la(self, 1) == 'U')
- && (_la(self, 2) == 'O')
- && (_la(self, 3) == 'T')
- && (_la(self, 4) == 'A')
- && (_la(self, 5) == ' ')) {
+- (BOOL)_parseQuotaResponseIntoHashMap:(NGMutableHashMap *)result_ {
+ NSString *qRoot;
+ NSMutableDictionary *parse;
+ NSMutableDictionary *quota;
- NSString *qRoot;
- NSMutableDictionary *parse;
- NSMutableDictionary *quota;
+ if (!_matchesString(self, "QUOTA "))
+ return NO;
- _consume(self, 6);
+ _consume(self, 6);
- quota = [result_ objectForKey:@"quota"];
+ quota = [result_ objectForKey:@"quota"];
- if (!quota) {
+ if (!quota) {
quota = [NSMutableDictionary dictionaryWithCapacity:2];
[result_ setObject:quota forKey:@"quota"];
- }
+ }
- parse = [NSMutableDictionary dictionaryWithCapacity:3];
- qRoot = _parseUntil2(self, ' ', '\n');
+ parse = [NSMutableDictionary dictionaryWithCapacity:3];
+ qRoot = _parseUntil2(self, ' ', '\n');
- if (_la(self, 0) == ' ') {
+ if (_la(self, 0) == ' ') {
_consume(self, 1);
if (_la(self, 0) == '(') {
}
}
[quota setObject:parse forKey:qRoot];
- }
- _parseUntil(self, '\n');
-
- return YES;
}
- return NO;
+ _parseUntil(self, '\n');
+
+ return YES;
}
-static BOOL _parseQuotaRootResponse(NGImap4ResponseParser *self,
- NGMutableHashMap *result_)
-{
- if ((_la(self, 0) == 'Q')
- && (_la(self, 1) == 'U')
- && (_la(self, 2) == 'O')
- && (_la(self, 3) == 'T')
- && (_la(self, 4) == 'A')
- && (_la(self, 5) == 'R')
- && (_la(self, 6) == 'O')
- && (_la(self, 7) == 'O')
- && (_la(self, 8) == 'T')
- && (_la(self, 9) == ' ')) {
- NSString *folderName, *folderRoot;
- NSMutableDictionary *dict;
+- (BOOL)_parseQuotaRootResponseIntoHashMap:(NGMutableHashMap *)result_ {
+ NSString *folderName, *folderRoot;
+ NSMutableDictionary *dict;
+
+ if (!_matchesString(self, "QUOTAROOT "))
+ return NO;
- _consume(self, 10);
+ _consume(self, 10);
- dict = [result_ objectForKey:@"quotaRoot"];
+ dict = [result_ objectForKey:@"quotaRoot"];
- if (!dict) {
- dict = [NSMutableDictionary dictionaryWithCapacity:2];
- [result_ setObject:dict forKey:@"quotaRoot"];
- }
- if (_la(self, 0) == '"') {
- _consume(self , 1);
- folderName = _parseUntil(self, '"');
- }
- else {
- folderName = _parseUntil2(self, '\n', ' ');
- }
- if (_la(self, 0) == ' ') {
- _consume(self, 1);
- folderRoot = _parseUntil(self, '\n');
- }
- else {
- _consume(self, 1);
- folderRoot = nil;
- }
- if ([folderName length] && [folderRoot length]) {
- [dict setObject:folderRoot forKey:folderName];
- }
- return YES;
+ if (!dict) {
+ dict = [NSMutableDictionary dictionaryWithCapacity:2];
+ [result_ setObject:dict forKey:@"quotaRoot"];
}
- return NO;
+ if (_la(self, 0) == '"') {
+ _consume(self , 1);
+ folderName = _parseUntil(self, '"');
+ }
+ else {
+ folderName = _parseUntil2(self, '\n', ' ');
+ }
+ if (_la(self, 0) == ' ') {
+ _consume(self, 1);
+ folderRoot = _parseUntil(self, '\n');
+ }
+ else {
+ _consume(self, 1);
+ folderRoot = nil;
+ }
+ if ([folderName length] && [folderRoot length]) {
+ [dict setObject:folderRoot forKey:folderName];
+ }
+ return YES;
}
-static NSArray *_parseThread(NGImap4ResponseParser *self) {
+- (NSArray *)_parseThread {
NSMutableArray *array;
NSNumber *msg;
array = [NSMutableArray arrayWithCapacity:64];
- if (_la(self, 0) == '(') {
+ if (_la(self, 0) == '(')
_consume(self, 1);
- }
+
while (1) {
if (_la(self, 0) == '(') {
- id a;
- a = _parseThread(self);
- [array addObject:a];
+ NSArray *a;
+
+ a = [self _parseThread];
+ if (a != nil) [array addObject:a];
}
else if ((msg = _parseUnsigned(self))) {
[array addObject:msg];
else if (_la(self, 0) == ' ')
_consume(self, 1);
}
- _match(self, ')');
+ _consumeIfMatch(self, ')');
return array;
}
msn = [NSMutableArray arrayWithCapacity:64];
while ((_la(self, 0) == '(')) {
NSArray *array;
- if ((array = _parseThread(self)))
+
+ if ((array = [self _parseThread]) != nil)
[msn addObject:array];
}
_parseUntil(self, '\n');
return NO;
}
-static BOOL _parseStatusResponse(NGImap4ResponseParser *self,
- NGMutableHashMap *result_) {
- if ((_la(self, 0) == 'S')
- && (_la(self, 1) == 'T')
- && (_la(self, 2) == 'A')
- && (_la(self, 3) == 'T')
- && (_la(self, 4) == 'U')
- && (_la(self, 5) == 'S')
- && (_la(self, 6) == ' ')) {
- NSString *name = nil;
- NSMutableDictionary *flags = nil;
- NSDictionary *d;
+- (BOOL)_parseStatusResponseIntoHashMap:(NGMutableHashMap *)result_ {
+ NSString *name = nil;
+ NSMutableDictionary *flags = nil;
+ NSDictionary *d;
- _consume(self, 7);
+ if (!_matchesString(self, "STATUS "))
+ return NO;
- if (_la(self, 0) == '"') {
- _consume(self, 1);
- name = _parseUntil(self, '"');
- _match(self, ' ');
- }
- else {
- name = _parseUntil(self, ' ');
- }
- _match(self, '(');
- flags = [NSMutableDictionary dictionaryWithCapacity:8];
+ _consume(self, 7);
+
+ if (_la(self, 0) == '"') {
+ _consume(self, 1);
+ name = _parseUntil(self, '"');
+ _consumeIfMatch(self, ' ');
+ }
+ else {
+ name = _parseUntil(self, ' ');
+ }
+ _consumeIfMatch(self, '(');
+ flags = [NSMutableDictionary dictionaryWithCapacity:8];
- while (_la(self, 0) != ')') {
- NSString *key = _parseUntil(self, ' ');
- id value = _parseUntil2(self, ' ', ')');
+ while (_la(self, 0) != ')') {
+ NSString *key = _parseUntil(self, ' ');
+ id value = _parseUntil2(self, ' ', ')');
- if (_la(self, 0) == ' ')
- _consume(self, 1);
+ if (_la(self, 0) == ' ')
+ _consume(self, 1);
- [flags setObject:[NumClass numberWithInt:[value intValue]]
- forKey:[key lowercaseString]];
- }
- _match(self, ')');
- _parseUntil(self, '\n');
+ [flags setObject:[NumClass numberWithInt:[value intValue]]
+ forKey:[key lowercaseString]];
+ }
+ _consumeIfMatch(self, ')');
+ _parseUntil(self, '\n');
+
+ d = [[NSDictionary alloc] initWithObjectsAndKeys:
+ name, @"folderName",
+ flags, @"flags", nil];
+ [result_ addObject:d forKey:@"status"];
+ [d release];
+ return YES;
+}
- d = [[NSDictionary alloc] initWithObjectsAndKeys:
- name, @"folderName",
- flags, @"flags", nil];
- [result_ addObject:d forKey:@"status"];
- [d release];
- return YES;
+- (BOOL)_parseByeUntaggedResponseIntoHashMap:(NGMutableHashMap *)result_ {
+ NSString *reason;
+
+ if (!_matchesString(self, "BYE "))
+ return NO;
+
+ _consume(self, 4);
+ reason = _parseUntil(self, '\n');
+ [result_ addObject:reason forKey:@"bye"];
+ return YES;
+}
+
+- (id)_parseQuotedStringOrNIL {
+ if (_la(self, 0) == '"')
+ return [self _parseQuotedString];
+ if (_matchesString(self, "NIL")) {
+ _consume(self, 3);
+ return null;
}
- return NO;
+ return nil;
}
-static BOOL _parseByeUntaggedResponse(NGImap4ResponseParser *self,
- NGMutableHashMap *result_) {
- if ((_la(self, 0) == 'B')
- && (_la(self, 1) == 'Y')
- && (_la(self, 2) == 'E')
- && (_la(self, 3) == ' ')) {
- NSString *reason = nil;
-
- _consume(self, 4);
- reason = _parseUntil(self, '\n');
- [result_ addObject:reason forKey:@"bye"];
- return YES;
+- (NGImap4EnvelopeAddress *)_parseEnvelopeAddressStructure {
+ /*
+ Note: returns retained object!
+
+ Order:
+ personal name
+ SMTP@at-domain-list(source route)
+ mailbox name
+ hostname
+ eg:
+ (NIL NIL "helge.hess" "opengroupware.org")
+ */
+ NGImap4EnvelopeAddress *address;
+ NSString *pname, *route, *mailbox, *host;
+
+ if (_la(self, 0) != '(') {
+ if (_matchesString(self, "NIL")) {
+ _consume(self, 3);
+ return (id)[null retain];
+ }
+ return nil;
}
- return NO;
+ _consume(self, 1); // '('
+
+ pname = [[self _parseQuotedStringOrNIL] copy]; [self _consumeOptionalSpace];
+ route = [[self _parseQuotedStringOrNIL] copy]; [self _consumeOptionalSpace];
+ mailbox = [[self _parseQuotedStringOrNIL] copy];[self _consumeOptionalSpace];
+ host = [[self _parseQuotedStringOrNIL] copy]; [self _consumeOptionalSpace];
+
+ if (_la(self, 0) != ')') {
+ [self logWithFormat:@"WARNING: IMAP4 envelope "
+ @"address not properly closed (c0=%c,c1=%c): %@",
+ _la(self, 0), _la(self, 1), self->serverResponseDebug];
+ }
+ else
+ _consume(self, 1);
+
+ address = [[NGImap4EnvelopeAddress alloc] initWithPersonalName:pname
+ sourceRoute:route mailbox:mailbox
+ host:host];
+ return address;
+}
+
+- (NSArray *)_parseEnvelopeAddressStructures {
+ NSMutableArray *ma;
+
+ if (_la(self, 0) != '(') {
+ if (_matchesString(self, "NIL")) {
+ _consume(self, 3);
+ return (id)[null retain];
+ }
+ return nil;
+ }
+ _consume(self, 1); // '('
+
+ ma = nil;
+ while (_la(self, 0) != ')') {
+ NGImap4EnvelopeAddress *address;
+
+ if ((address = [self _parseEnvelopeAddressStructure]) == nil)
+ continue; // TODO: should we stop parsing?
+ if (![address isNotNull])
+ continue;
+
+ if (ma == nil) ma = [NSMutableArray arrayWithCapacity:4];
+ [ma addObject:address];
+ [address release]; /* the parse returns a retained object! */
+ }
+
+ if (_la(self, 0) != ')') {
+ [self logWithFormat:
+ @"WARNING: IMAP4 envelope address not properly closed!"];
+ }
+ else
+ _consume(self, 1);
+ return ma;
+}
+
+- (id)_parseEnvelope {
+ /*
+ http://www.hunnysoft.com/rfc/rfc3501.html
+
+ envelope = "(" env-date SP env-subject SP env-from SP env-sender SP
+ env-reply-to SP env-to SP env-cc SP env-bcc SP
+ env-in-reply-to SP env-message-id ")"
+
+ * 1189 FETCH (UID 1189 ENVELOPE
+ ("Tue, 22 Jun 2004 08:42:01 -0500" ""
+ (("Jeff Glaspie" NIL "jeff" "glaspie.org"))
+ (("Jeff Glaspie" NIL "jeff" "glaspie.org"))
+ (("Jeff Glaspie" NIL "jeff" "glaspie.org"))
+ ((NIL NIL "helge.hess" "opengroupware.org"))
+ NIL NIL NIL
+ "<20040622134354.F11133CEB14@mail.opengroupware.org>"
+ )
+ )
+ */
+ static NGMimeRFC822DateHeaderFieldParser *dateParser = nil;
+ NGImap4Envelope *env;
+ NSString *dateStr;
+ id tmp;
+
+ if (dateParser == nil)
+ dateParser = [[NGMimeRFC822DateHeaderFieldParser alloc] init];
+
+ if (_la(self, 0) != '(')
+ return nil;
+ _consume(self, 1);
+
+ env = [[[NGImap4Envelope alloc] init] autorelease];
+
+ /* parse date */
+
+ dateStr = [self _parseQuotedStringOrNIL];
+ [self _consumeOptionalSpace];
+ if ([dateStr isNotNull])
+ env->date = [[dateParser parseValue:dateStr ofHeaderField:nil] retain];
+
+ /* parse subject */
+
+ if ((tmp = [self _parseQuotedStringOrNIL]))
+ env->subject = [tmp isNotNull] ? [tmp copy] : nil;
+ [self _consumeOptionalSpace];
+
+ /* parse addresses */
+
+ if ((tmp = [self _parseEnvelopeAddressStructures]) != nil)
+ env->from = [tmp isNotNull] ? [[tmp lastObject] copy] : nil;
+ [self _consumeOptionalSpace];
+ if ((tmp = [self _parseEnvelopeAddressStructures]) != nil)
+ env->sender = [tmp isNotNull] ? [[tmp lastObject] copy] : nil;
+ [self _consumeOptionalSpace];
+ if ((tmp = [self _parseEnvelopeAddressStructures]) != nil)
+ env->replyTo = [tmp isNotNull] ? [[tmp lastObject] copy] : nil;
+ [self _consumeOptionalSpace];
+
+ if ((tmp = [self _parseEnvelopeAddressStructures]) != nil)
+ env->to = [tmp isNotNull] ? [tmp copy] : nil;
+ [self _consumeOptionalSpace];
+ if ((tmp = [self _parseEnvelopeAddressStructures]) != nil)
+ env->cc = [tmp isNotNull] ? [tmp copy] : nil;
+ [self _consumeOptionalSpace];
+ if ((tmp = [self _parseEnvelopeAddressStructures]) != nil)
+ env->bcc = [tmp isNotNull] ? [tmp copy] : nil;
+ [self _consumeOptionalSpace];
+
+ if ((tmp = [self _parseQuotedStringOrNIL]))
+ env->inReplyTo = [tmp isNotNull] ? [tmp copy] : nil;
+ [self _consumeOptionalSpace];
+ if ((tmp = [self _parseQuotedStringOrNIL]))
+ env->msgId = [tmp isNotNull] ? [tmp copy] : nil;
+ [self _consumeOptionalSpace];
+
+ if (_la(self, 0) != ')')
+ [self logWithFormat:@"WARNING: IMAP4 envelope not properly closed!"];
+ else
+ _consume(self, 1);
+
+ return env;
}
- (BOOL)_parseNumberUntaggedResponse:(NGMutableHashMap *)result_ {
- NSNumber *number = nil;
+ NSNumber *number;
NSString *key = nil;
if ((number = _parseUnsigned(self)) == nil)
return NO;
- _match(self, ' ');
+ _consumeIfMatch(self, ' ');
- if ((_la(self, 0) == 'F')
- && (_la(self, 1) == 'E')
- && (_la(self, 2) == 'T')
- && (_la(self, 3) == 'C')
- && (_la(self, 4) == 'H')
- && (_la(self, 5) == ' ')) { /* got a fetch response (fetch request) */
+ if (_matchesString(self, "FETCH ")) {
/* eg: "FETCH (FLAGS (\Seen) UID 5 RFC822.HEADER {2903}" */
- NSMutableDictionary *fetch = nil;
-
+ NSMutableDictionary *fetch;
+
fetch = [[NSMutableDictionary alloc] initWithCapacity:10];
- _consume(self, 6);
- _match(self, '(');
- while (_la(self, 0) != ')') {
+ _consume(self, 6); /* "FETCH " */
+ _consumeIfMatch(self, '(');
+ while (_la(self, 0) != ')') { /* until closing parent */
NSString *key = nil;
key = [_parseUntil(self, ' ') lowercaseString];
data = [str dataUsingEncoding:defCStringEncoding];
}
else
- data = _parseData(self);
+ data = [self _parseData];
if (data != nil) [fetch setObject:data forKey:key];
}
+ else if ([key isEqualToString:@"envelope"]) {
+ id envelope;
+
+ if ((envelope = [self _parseEnvelope]) != nil)
+ [fetch setObject:envelope forKey:key];
+ else
+ [self logWithFormat:@"ERROR: could not parse envelope!"];
+ }
else {
NSException *e;
-
+
e = [[NGImap4ParserException alloc] initWithFormat:
- @"unsupported fetch %@", key];
+ @"unsupported fetch key: %@",
+ key];
[self setLastException:[e autorelease]];
return NO;
}
if (_la(self, 0) == ' ')
_consume(self, 1);
}
- if (fetch) {
- [fetch setObject:number forKey:@"msn"];
+ if (fetch != nil) {
+ [fetch setObject:number forKey:@"msn"];
[result_ addObject:fetch forKey:@"fetch"];
_consume(self, 1); /* consume ')' */
- _match(self, '\n');
+ _consumeIfMatch(self, '\n');
}
else { /* no correct fetch line */
_parseUntil(self, '\n');
}
-
+
[fetch release]; fetch = nil;
-
return YES;
}
NSString *str;
NSData *data;
- if ((data = _parseData(self)) == nil)
+ if ((data = [self _parseData]) == nil)
return NO;
str = [[StrClass alloc] initWithData:data encoding:defCStringEncoding];
if ((str = _parseStringSieveResponse(self)))
return str;
- if ((data = _parseData(self)) == nil)
+ if ((data = [self _parseData]) == nil)
return nil;
return [[[StrClass alloc] initWithData:data encoding:defCStringEncoding]
NSData *data;
if (debugDataOn) [self logWithFormat:@"parse body decode string"];
- data = _parseData(self);
+ data = [self _parseData];
if (_decode)
data = [data decodeQuotedPrintableValueOfMIMEHeaderField:nil];
_consume(self, 1);
key = _parseBodyString(self, YES);
- _match(self, ' ');
+ _consumeIfMatch(self, ' ');
value = _parseBodyDecodeString(self, YES, YES);
[list setObject:value forKey:[key lowercaseString]];
}
- _match(self, ')');
+ _consumeIfMatch(self, ')');
}
else {
NSString *str;
static NSArray *_parseAddressStructure(NGImap4ResponseParser *self) {
NSString *personalName, *sourceRoute, *mailboxName, *hostName;
- _match(self, '(');
+ _consumeIfMatch(self, '(');
personalName = _parseBodyString(self, YES);
- _match(self, ' ');
+ _consumeIfMatch(self, ' ');
sourceRoute = _parseBodyString(self, YES);
- _match(self, ' ');
+ _consumeIfMatch(self, ' ');
mailboxName = _parseBodyString(self, YES);
- _match(self, ' ');
+ _consumeIfMatch(self, ' ');
hostName = _parseBodyString(self, YES);
- _match(self, ')');
+ _consumeIfMatch(self, ')');
return [NSDictionary dictionaryWithObjectsAndKeys:
personalName, @"personalName",
sourceRoute, @"sourceRoute",
NSMutableDictionary *dict;
type = [_parseBodyString(self, YES) lowercaseString];
- _match(self, ' ');
+ _consumeIfMatch(self, ' ');
subtype = _parseBodyString(self, YES);
- _match(self, ' ');
+ _consumeIfMatch(self, ' ');
parameterList = _parseBodyParameterList(self);
- _match(self, ' ');
+ _consumeIfMatch(self, ' ');
bodyId = _parseBodyString(self, YES);
- _match(self, ' ');
+ _consumeIfMatch(self, ' ');
description = _parseBodyString(self, YES);
- _match(self, ' ');
+ _consumeIfMatch(self, ' ');
encoding = _parseBodyString(self, YES);
- _match(self, ' ');
+ _consumeIfMatch(self, ' ');
bodysize = _parseBodyString(self, YES);
dict = [NSMutableDictionary dictionaryWithObjectsAndKeys:
bodysize, @"size", nil];
if ([type isEqualToString:@"text"]) {
- _match(self, ' ');
+ _consumeIfMatch(self, ' ');
[dict setObject:_parseBodyString(self, YES) forKey:@"lines"];
}
else if ([type isEqualToString:@"message"]) {
if (_la(self, 0) != ')') {
- _match(self, ' ');
- _match(self, '(');
+ _consumeIfMatch(self, ' ');
+ _consumeIfMatch(self, '(');
[dict setObject:_parseBodyString(self, YES) forKey:@"date"];
- _match(self, ' ');
+ _consumeIfMatch(self, ' ');
[dict setObject:_parseBodyString(self, YES) forKey:@"subject"];
- _match(self, ' ');
+ _consumeIfMatch(self, ' ');
[dict setObject:_parseParenthesizedAddressList(self) forKey:@"from"];
- _match(self, ' ');
+ _consumeIfMatch(self, ' ');
[dict setObject:_parseParenthesizedAddressList(self) forKey:@"sender"];
- _match(self, ' ');
+ _consumeIfMatch(self, ' ');
[dict setObject:_parseParenthesizedAddressList(self)
forKey:@"reply-to"];
- _match(self, ' ');
+ _consumeIfMatch(self, ' ');
[dict setObject:_parseParenthesizedAddressList(self) forKey:@"to"];
- _match(self, ' ');
+ _consumeIfMatch(self, ' ');
[dict setObject:_parseParenthesizedAddressList(self) forKey:@"cc"];
- _match(self, ' ');
+ _consumeIfMatch(self, ' ');
[dict setObject:_parseParenthesizedAddressList(self) forKey:@"bcc"];
- _match(self, ' ');
+ _consumeIfMatch(self, ' ');
[dict setObject:_parseBodyString(self, YES) forKey:@"in-reply-to"];
- _match(self, ' ');
+ _consumeIfMatch(self, ' ');
[dict setObject:_parseBodyString(self, YES) forKey:@"messageId"];
- _match(self, ')');
- _match(self, ' ');
+ _consumeIfMatch(self, ')');
+ _consumeIfMatch(self, ' ');
[dict setObject:_parseBody(self) forKey:@"body"];
- _match(self, ' ');
+ _consumeIfMatch(self, ' ');
[dict setObject:_parseBodyString(self, YES) forKey:@"bodyLines"];
}
}
while (_la(self, 0) == '(') {
[parts addObject:_parseBody(self)];
}
- _match(self, ' ');
+ _consumeIfMatch(self, ' ');
kind = _parseBodyString(self, YES);
return [NSDictionary dictionaryWithObjectsAndKeys:
parts, @"parts",
static NSDictionary *_parseBody(NGImap4ResponseParser *self) {
NSDictionary *result;
- _match(self, '(');
+ _consumeIfMatch(self, '(');
if (_la(self, 0) == '(') {
result = _parseMultipartBody(self);
data = [str dataUsingEncoding:defCStringEncoding];
}
else
- data = _parseData(self);
+ data = [self _parseData];
if (data == nil) {
[self logWithFormat:@"ERROR(%s): got no data.", __PRETTY_FUNCTION__];
static NSArray *_parseFlagArray(NGImap4ResponseParser *self) {
NSString *flags;
- _match(self, '(');
+ _consumeIfMatch(self, '(');
flags = _parseUntil(self, ')');
if ([flags length] == 0) {
&& (_la(self, 5) == ' ')) {
_consume(self, 6);
[result_ addObject:_parseFlagArray(self) forKey:@"flags"];
- _match(self, '\n');
+ _consumeIfMatch(self, '\n');
return YES;
}
return NO;
[NSMutableString stringWithCString:buf length:1024];
else {
NSString *s;
+
s = [(NSString *)[StrClass alloc] initWithCString:buf length:1024];
[str appendString:s];
[s release];
}
}
+- (NSException *)exceptionForFailedMatch:(unsigned char)_match
+ got:(unsigned char)_avail
+{
+ NSException *e;
+
+ e = [NGImap4ParserException alloc];
+ if (self->debug) {
+ e = [e initWithFormat:@"unexpected char <%c> expected <%c> <%@>",
+ _avail, _match, self->serverResponseDebug];
+ }
+ else {
+ e = [e initWithFormat:@"unexpected char <%c> expected <%c>",
+ _avail, _match];
+ }
+ return [e autorelease];
+}
-static __inline__ void _match(NGImap4ResponseParser *self, char _match) {
+static __inline__ NSException *_consumeIfMatch(NGImap4ResponseParser *self,
+ unsigned char _match)
+{
+ NSException *e;
+
if (_la(self,0) == _match) {
_consume(self, 1);
- return;
- }
- {
- NSException *e;
- if (self->debug) {
- e = [[NGImap4ParserException alloc]
- initWithFormat:@"unexpected char <%c> "
- @"expected <%c> <%@>",
- _la(self, 0), _match,
- self->serverResponseDebug];
- }
- else {
- e = [[NGImap4ParserException alloc]
- initWithFormat:@"unexpected char <%c> "
- @"expected <%c>",
- _la(self, 0), _match];
- }
- e = [e autorelease];
- [self setLastException:e];
+ return nil;
}
+
+ e = [self exceptionForFailedMatch:_match got:_la(self, 0)];
+ [self setLastException:e];
+ return e;
}
static __inline__ void _consume(NGImap4ResponseParser *self, unsigned _cnt) {