]> err.no Git - sope/commitdiff
added support for envelope responses
authorhelge <helge@e4a50df8-12e2-0310-a44c-efbce7f8a7e3>
Fri, 1 Oct 2004 21:16:52 +0000 (21:16 +0000)
committerhelge <helge@e4a50df8-12e2-0310-a44c-efbce7f8a7e3>
Fri, 1 Oct 2004 21:16:52 +0000 (21:16 +0000)
git-svn-id: http://svn.opengroupware.org/SOPE/trunk@211 e4a50df8-12e2-0310-a44c-efbce7f8a7e3

sope-mime/NGImap4/ChangeLog
sope-mime/NGImap4/GNUmakefile
sope-mime/NGImap4/NGImap4Client.m
sope-mime/NGImap4/NGImap4Envelope.h [new file with mode: 0644]
sope-mime/NGImap4/NGImap4Envelope.m [new file with mode: 0644]
sope-mime/NGImap4/NGImap4EnvelopeAddress.h [new file with mode: 0644]
sope-mime/NGImap4/NGImap4EnvelopeAddress.m [new file with mode: 0644]
sope-mime/NGImap4/NGImap4ResponseParser.m
sope-mime/NGMime/NGMimeHeaderFieldParser.h
sope-mime/Version

index 5e87e1799a3ec2ef688f83d333c1e623dd3e042d..430f479c338d5d053b5a4b0a11bf0c274ab1f413 100644 (file)
@@ -1,21 +1,26 @@
+2004-10-01  Helge Hess  <helge.hess@opengroupware.org>
+       
+       * NGImap4ResponseParser.m: added support for envelope responses, major
+         code cleanups (v4.3.184)
+       
 2004-09-30  Helge Hess  <helge.hess@opengroupware.org>
-
+       
        * v4.3.183
-
+       
        * NGImap4Folder.m: use new sort API
-
+       
        * NGImap4Client.m: deprecated -sort:qualifier: in favor of
          -sort:qualifier:encoding:, code cleanups
-
+       
 2004-09-29  Helge Hess  <helge.hess@skyrix.com>
-
+       
        * NGImap4Client.m: improved -description (v4.3.181)
-
+       
 2004-09-21  Marcus Mueller  <znek@mulle-kybernetik.com>
-
+       
        * imCommon.h: Fixed duplicate interface declarations by renaming
          them. I believe this was an Xcode only issue. (v4.3.180)
-
+       
 2004-09-07  Helge Hess  <helge.hess@skyrix.com>
 
        * NGImap4ResponseParser.m: added some sanity checks for unexpected
index e628ab98577db65b54247d28f709e44037f13faf..67bd0fa5b1d48d405d051576d24b41bc4f7be1d5 100644 (file)
@@ -18,6 +18,8 @@ NGImap4_HEADER_FILES = \
        NGImap4DataSource.h     \
        NSString+Imap4.h        \
        NGSieveClient.h         \
+       NGImap4Envelope.h       \
+       NGImap4EnvelopeAddress.h\
 
 NGImap4_OBJC_FILES = \
        NGImap4ResponseParser.m         \
@@ -41,6 +43,8 @@ NGImap4_OBJC_FILES = \
        NGImap4FolderMailRegistry.m     \
        NGImap4FolderFlags.m            \
        NGImap4ResponseNormalizer.m     \
+       NGImap4Envelope.m               \
+       NGImap4EnvelopeAddress.m        \
 
 -include GNUmakefile.preamble
 include $(GNUSTEP_MAKEFILES)/subproject.make
index 0511a259c9ce82b1ccba776bf1fad6e5805542af..4006e48f825a8403baec4184570717cf0fa1c6ed 100644 (file)
@@ -694,15 +694,20 @@ static BOOL         ImapDebugEnabled   = NO;
 }
 
 - (NSDictionary *)fetchUids:(NSArray *)_uids parts:(NSArray *)_parts {
+  /*
+    eg: 'UID FETCH 1189,1325,1326 ([TODO])'
+  */
   NSAutoreleasePool *pool;
   NSString          *cmd;
   NSDictionary      *result;
+  NSString          *uidsStr, *partsStr;
   id fetchres;
   
   pool = [[NSAutoreleasePool alloc] init];
-  cmd  = [NSString stringWithFormat:@"uid fetch %@ (%@)",
-                  [self _uidsJoinedForFetchCmd:_uids],
-                  [self _partsJoinedForFetchCmd:_parts]];
+  
+  uidsStr  = [self _uidsJoinedForFetchCmd:_uids];
+  partsStr = [self _partsJoinedForFetchCmd:_parts];
+  cmd  = [NSString stringWithFormat:@"uid fetch %@ (%@)", uidsStr, partsStr];
   
   fetchres = [self processCommand:cmd];
   result   = [[self->normer normalizeFetchResponse:fetchres] retain];
diff --git a/sope-mime/NGImap4/NGImap4Envelope.h b/sope-mime/NGImap4/NGImap4Envelope.h
new file mode 100644 (file)
index 0000000..e44bf91
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+  Copyright (C) 2004 SKYRIX Software AG
+
+  This file is part of OpenGroupware.org.
+
+  OGo is free software; you can redistribute it and/or modify it under
+  the terms of the GNU Lesser General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+
+  OGo is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+  License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with OGo; see the file COPYING.  If not, write to the
+  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+  02111-1307, USA.
+*/
+
+#ifndef __NGImap4_NGImap4Envelope_H__
+#define __NGImap4_NGImap4Envelope_H__
+
+#import <Foundation/NSObject.h>
+
+/*
+  NGImap4Envelope
+  
+  Wraps the raw envelope as parsed from an IMAP4 fetch response.
+*/
+
+@class NSString, NSArray, NSCalendarDate;
+@class NGImap4EnvelopeAddress;
+
+@interface NGImap4Envelope : NSObject
+{
+@public
+  NSCalendarDate         *date;
+  NSString               *subject;
+  NSString               *inReplyTo;
+  NSString               *msgId;
+  NGImap4EnvelopeAddress *from;
+  NGImap4EnvelopeAddress *sender;
+  NGImap4EnvelopeAddress *replyTo;
+  NSArray *to;
+  NSArray *cc;
+  NSArray *bcc;
+}
+
+/* accessors */
+
+- (NSCalendarDate *)date;
+- (NSString *)subject;
+- (NSString *)inReplyTo;
+- (NSString *)messageID;
+- (NGImap4EnvelopeAddress *)from;
+- (NGImap4EnvelopeAddress *)sender;
+- (NGImap4EnvelopeAddress *)replyTo;
+- (NSArray *)to;
+- (NSArray *)cc;
+- (NSArray *)bcc;
+
+/* derived accessors */
+
+- (BOOL)hasTo;
+- (BOOL)hasCC;
+- (BOOL)hasBCC;
+
+@end
+
+#endif /* __NGImap4_NGImap4Envelope_H__ */
diff --git a/sope-mime/NGImap4/NGImap4Envelope.m b/sope-mime/NGImap4/NGImap4Envelope.m
new file mode 100644 (file)
index 0000000..8e2bb08
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+  Copyright (C) 2004 SKYRIX Software AG
+
+  This file is part of OpenGroupware.org.
+
+  OGo is free software; you can redistribute it and/or modify it under
+  the terms of the GNU Lesser General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+
+  OGo is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+  License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with OGo; see the file COPYING.  If not, write to the
+  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+  02111-1307, USA.
+*/
+
+#include "NGImap4Envelope.h"
+#include "NGImap4EnvelopeAddress.h"
+#include "imCommon.h"
+
+@implementation NGImap4Envelope
+
+- (void)dealloc {
+  [self->date      release];
+  [self->subject   release];
+  [self->inReplyTo release];
+  [self->msgId     release];
+  [self->from      release];
+  [self->sender    release];
+  [self->replyTo   release];
+  [self->to        release];
+  [self->cc        release];
+  [self->bcc       release];
+  [super dealloc];
+}
+
+/* accessors */
+
+- (NSCalendarDate *)date {
+  return self->date;
+}
+- (NSString *)subject {
+  return self->subject;
+}
+- (NSString *)inReplyTo {
+  return self->inReplyTo;
+}
+- (NSString *)messageID {
+  return self->msgId;
+}
+- (NGImap4EnvelopeAddress *)from {
+  return self->from;
+}
+- (NGImap4EnvelopeAddress *)sender {
+  return self->sender;
+}
+- (NGImap4EnvelopeAddress *)replyTo {
+  return self->replyTo;
+}
+- (NSArray *)to {
+  return self->to;
+}
+- (NSArray *)cc {
+  return self->cc;
+}
+- (NSArray *)bcc {
+  return self->bcc;
+}
+
+/* derived accessors */
+
+- (BOOL)hasTo {
+  return [self->to count] > 0 ? YES : NO;
+}
+- (BOOL)hasCC {
+  return [self->cc count] > 0 ? YES : NO;
+}
+- (BOOL)hasBCC {
+  return [self->bcc count] > 0 ? YES : NO;
+}
+
+/* description */
+
+- (NSString *)description {
+  NSMutableString *ms;
+  
+  ms = [NSMutableString stringWithCapacity:128];
+  [ms appendFormat:@"<0x%08X[%@]:", self, NSStringFromClass([self class])];
+
+  if (self->date)      [ms appendFormat:@" date='%@'",      self->date];
+  if (self->subject)   [ms appendFormat:@" subject='%@'",   self->subject];
+  if (self->msgId)     [ms appendFormat:@" msgid='%@'",     self->msgId];
+  if (self->inReplyTo) [ms appendFormat:@" inreplyto='%@'", self->inReplyTo];
+  
+  if (self->from)   [ms appendFormat:@" from=%@",   [self->from   email]];
+  if (self->sender) [ms appendFormat:@" sender=%@", [self->sender email]];
+  
+  if (self->to)  [ms appendFormat:@" to=%@",  self->to];
+  if (self->cc)  [ms appendFormat:@" cc=%@",  self->cc];
+  if (self->bcc) [ms appendFormat:@" bcc=%@", self->bcc];
+  
+  [ms appendString:@">"];
+  return ms;
+}
+
+@end /* NGImap4Envelope */
diff --git a/sope-mime/NGImap4/NGImap4EnvelopeAddress.h b/sope-mime/NGImap4/NGImap4EnvelopeAddress.h
new file mode 100644 (file)
index 0000000..923faf9
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+  Copyright (C) 2004 SKYRIX Software AG
+
+  This file is part of OpenGroupware.org.
+
+  OGo is free software; you can redistribute it and/or modify it under
+  the terms of the GNU Lesser General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+
+  OGo is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+  License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with OGo; see the file COPYING.  If not, write to the
+  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+  02111-1307, USA.
+*/
+
+#ifndef __NGImap4_NGImap4EnvelopeAddress_H__
+#define __NGImap4_NGImap4EnvelopeAddress_H__
+
+#import <Foundation/NSObject.h>
+
+/*
+  NGImap4EnvelopeAddress
+
+  Wraps the raw mail address in the envelope as parsed from an IMAP4 fetch
+  response.
+*/
+
+@class NSString;
+
+@interface NGImap4EnvelopeAddress : NSObject < NSCopying >
+{
+@public
+  NSString *personalName;
+  NSString *sourceRoute;
+  NSString *mailbox;
+  NSString *host;
+}
+
+- (id)initWithPersonalName:(NSString *)_pname sourceRoute:(NSString *)_route
+  mailbox:(NSString *)_mbox host:(NSString *)_host;
+
+/* accessors */
+
+- (NSString *)personalName;
+- (NSString *)sourceRoute;
+- (NSString *)mailbox;
+- (NSString *)host;
+
+/* derived accessors */
+
+- (NSString *)baseEMail;
+- (NSString *)email;
+
+@end
+
+#endif /* __NGImap4_NGImap4EnvelopeAddress_H__ */
diff --git a/sope-mime/NGImap4/NGImap4EnvelopeAddress.m b/sope-mime/NGImap4/NGImap4EnvelopeAddress.m
new file mode 100644 (file)
index 0000000..bfb4d4f
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+  Copyright (C) 2004 SKYRIX Software AG
+
+  This file is part of OpenGroupware.org.
+
+  OGo is free software; you can redistribute it and/or modify it under
+  the terms of the GNU Lesser General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+
+  OGo is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+  License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with OGo; see the file COPYING.  If not, write to the
+  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+  02111-1307, USA.
+*/
+
+#include "NGImap4EnvelopeAddress.h"
+#include "imCommon.h"
+
+@implementation NGImap4EnvelopeAddress
+
+- (id)initWithPersonalName:(NSString *)_pname sourceRoute:(NSString *)_route
+  mailbox:(NSString *)_mbox host:(NSString *)_host
+{
+  /* Note: we expect NSNull for unset values! */
+  if (_pname == nil || _route == nil || _mbox == nil || _host == nil) {
+    [self release];
+    return nil;
+  }
+  
+  if ((self = [super init])) {
+    if ([_pname isNotNull]) self->personalName = [_pname copy];
+    if ([_route isNotNull]) self->sourceRoute  = [_route copy];
+    if ([_mbox  isNotNull]) self->mailbox      = [_mbox  copy];
+    if ([_host  isNotNull]) self->host         = [_host  copy];
+  }
+  return self;
+}
+- (id)init {
+  return [self initWithPersonalName:nil sourceRoute:nil mailbox:nil host:nil];
+}
+
+- (void)dealloc {
+  [self->personalName release];
+  [self->sourceRoute  release];
+  [self->mailbox      release];
+  [self->host         release];
+  [super dealloc];
+}
+
+/* accessors */
+
+- (NSString *)personalName {
+  return self->personalName;
+}
+- (NSString *)sourceRoute {
+  return self->sourceRoute;
+}
+- (NSString *)mailbox {
+  return self->mailbox;
+}
+- (NSString *)host {
+  return self->host;
+}
+
+/* NSCopying */
+
+- (id)copyWithZone:(NSZone *)_zone {
+  /* we are immutable */
+  return [self retain];
+}
+
+/* derived accessors */
+
+- (NSString *)baseEMail {
+  NSString *t;
+  
+  if ([self->mailbox length] == 0)
+    return nil;
+  if ([self->host length] == 0)
+    return self->mailbox;
+  
+  t = [self->mailbox stringByAppendingString:@"@"];
+  return [t stringByAppendingString:self->host];
+}
+- (NSString *)email {
+  NSString *t;
+  
+  if ([self->personalName length] == 0)
+    return [self baseEMail];
+  if ((t = [self baseEMail]) == nil)
+    return self->personalName;
+  
+  t = [[self->personalName
+           stringByAppendingString:@" <"] stringByAppendingString:t];
+  return [t stringByAppendingString:@">"];
+}
+
+/* description */
+
+- (NSString *)description {
+  NSMutableString *ms;
+  
+  ms = [NSMutableString stringWithCapacity:128];
+  [ms appendFormat:@"<0x%08X[%@]:", self, NSStringFromClass([self class])];
+  
+  if (self->personalName) [ms appendFormat:@" name='%@'", self->personalName];
+  if (self->sourceRoute)  [ms appendFormat:@" source='%@'", self->sourceRoute];
+  if (self->mailbox)      [ms appendFormat:@" mailbox='%@'", self->mailbox];
+  if (self->host)         [ms appendFormat:@" host='%@'",    self->host];
+  
+  [ms appendString:@">"];
+  return ms;
+}
+
+@end /* NGImap4EnvelopeAddress */
index 046a70a107754dda2766daba371eb4497f153dc1..c03db7bdaa0501dce445b0575ba55c64bd9c4ea6 100644 (file)
@@ -21,6 +21,8 @@
 
 #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);
@@ -62,8 +89,6 @@ static int _parseTaggedResponse(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_);
@@ -75,25 +100,13 @@ static BOOL _parseNoUntaggedResponse(NGImap4ResponseParser *self,
                                      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_);
@@ -108,11 +121,6 @@ static BOOL _parseNoSieveResponse(NGImap4ResponseParser *self,
 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;
@@ -124,6 +132,7 @@ static Class            NumClass      = Nil;
 static NSStringEncoding defCStringEncoding;
 static NSNumber         *YesNum = nil;
 static NSNumber         *NoNum  = nil;
+static NSNull           *null   = nil;
 
 + (void)initialize {
   NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
@@ -131,6 +140,8 @@ static NSNumber         *NoNum  = nil;
   if (didInit) return;
   didInit = YES;
 
+  null = [[NSNull null] retain];
+  
   encoding = [NGMimePartParser defaultHeaderFieldEncoding];
   defCStringEncoding = [NSString defaultCStringEncoding];
   
@@ -271,7 +282,7 @@ static NSNumber         *NoNum  = nil;
       }
     }
     else if (l0 == '+') { /* starting with a '+'? */
-      _parseContinuationResponse(self, result);
+      [self _parseContinuationResponseIntoHashMap:result];
       endOfCommand = YES;
     }
     else if (isdigit(l0)) {
@@ -307,154 +318,163 @@ static void _parseSieveRespone(NGImap4ResponseParser *self,
     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,
@@ -483,7 +503,7 @@ 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);
@@ -510,8 +530,8 @@ static void _parseUntaggedResponse(NGImap4ResponseParser *self,
   // 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) {
@@ -519,22 +539,22 @@ static void _parseUntaggedResponse(NGImap4ResponseParser *self,
     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;
 
@@ -555,15 +575,15 @@ static void _parseUntaggedResponse(NGImap4ResponseParser *self,
   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;
@@ -576,9 +596,9 @@ static void _parseUntaggedResponse(NGImap4ResponseParser *self,
     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;
 
@@ -594,182 +614,147 @@ static void _parseUntaggedResponse(NGImap4ResponseParser *self,
   [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) == '(') {
@@ -801,75 +786,63 @@ static BOOL _parseQuotaResponse(NGImap4ResponseParser *self,
         }
       }
       [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];
@@ -882,7 +855,7 @@ static NSArray *_parseThread(NGImap4ResponseParser *self) {
     else if (_la(self, 0) == ' ')
       _consume(self, 1);
   }
-  _match(self, ')');
+  _consumeIfMatch(self, ')');
   return array;
 }
 
@@ -910,7 +883,8 @@ static BOOL _parseThreadResponse(NGImap4ResponseParser *self,
     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');
@@ -920,94 +894,249 @@ static BOOL _parseThreadResponse(NGImap4ResponseParser *self,
   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];
@@ -1042,33 +1171,41 @@ static BOOL _parseByeUntaggedResponse(NGImap4ResponseParser *self,
           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;
   }
 
@@ -1112,7 +1249,7 @@ static BOOL _parseDataSieveResponse(NGImap4ResponseParser *self,
   NSString *str;
   NSData   *data;
 
-  if ((data = _parseData(self)) == nil)
+  if ((data = [self _parseData]) == nil)
     return NO;
   
   str = [[StrClass alloc] initWithData:data encoding:defCStringEncoding];
@@ -1165,7 +1302,7 @@ static NSString *_parseContentSieveResponse(NGImap4ResponseParser *self) {
   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]
@@ -1195,7 +1332,7 @@ static NSString *_parseBodyDecodeString(NGImap4ResponseParser *self,
     NSData *data;
     
     if (debugDataOn) [self logWithFormat:@"parse body decode string"];
-    data = _parseData(self);
+    data = [self _parseData];
 
     if (_decode)
       data = [data decodeQuotedPrintableValueOfMIMEHeaderField:nil];
@@ -1249,12 +1386,12 @@ static NSDictionary *_parseBodyParameterList(NGImap4ResponseParser *self)
         _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;
@@ -1271,15 +1408,15 @@ static NSDictionary *_parseBodyParameterList(NGImap4ResponseParser *self)
 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",
@@ -1317,17 +1454,17 @@ static NSDictionary *_parseSingleBody(NGImap4ResponseParser *self) {
   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:
@@ -1340,37 +1477,37 @@ static NSDictionary *_parseSingleBody(NGImap4ResponseParser *self) {
                               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"];
     }
   }
@@ -1386,7 +1523,7 @@ static NSDictionary *_parseMultipartBody(NGImap4ResponseParser *self) {
   while (_la(self, 0) == '(') {
     [parts addObject:_parseBody(self)];
   }
-  _match(self, ' ');
+  _consumeIfMatch(self, ' ');
   kind = _parseBodyString(self, YES);
   return [NSDictionary dictionaryWithObjectsAndKeys:
                        parts,        @"parts",
@@ -1397,7 +1534,7 @@ static NSDictionary *_parseMultipartBody(NGImap4ResponseParser *self) {
 static NSDictionary *_parseBody(NGImap4ResponseParser *self) {
   NSDictionary *result;
 
-  _match(self, '(');
+  _consumeIfMatch(self, '(');
 
   if (_la(self, 0) == '(') {
     result = _parseMultipartBody(self);
@@ -1429,7 +1566,7 @@ static NSDictionary *_parseBody(NGImap4ResponseParser *self) {
     data = [str dataUsingEncoding:defCStringEncoding];
   }
   else 
-    data = _parseData(self);
+    data = [self _parseData];
   
   if (data == nil) {
     [self logWithFormat:@"ERROR(%s): got no data.", __PRETTY_FUNCTION__];
@@ -1442,7 +1579,7 @@ static NSDictionary *_parseBody(NGImap4ResponseParser *self) {
 static NSArray *_parseFlagArray(NGImap4ResponseParser *self) {
   NSString *flags;
   
-  _match(self, '(');
+  _consumeIfMatch(self, '(');
   
   flags = _parseUntil(self, ')');
   if ([flags length] == 0) {
@@ -1464,7 +1601,7 @@ static BOOL _parseFlagsUntaggedResponse(NGImap4ResponseParser *self,
       && (_la(self, 5) == ' ')) {
     _consume(self, 6);
     [result_ addObject:_parseFlagArray(self) forKey:@"flags"];
-    _match(self, '\n');
+    _consumeIfMatch(self, '\n');
     return YES;
   }
   return NO;
@@ -1664,6 +1801,7 @@ static NSString *_parseUntil2(NGImap4ResponseParser *self, char _c1, char _c2){
                  [NSMutableString stringWithCString:buf length:1024];
       else {
         NSString *s;
+       
         s = [(NSString *)[StrClass alloc] initWithCString:buf length:1024];
         [str appendString:s];
         [s release];
@@ -1687,30 +1825,36 @@ static NSString *_parseUntil2(NGImap4ResponseParser *self, char _c1, char _c2){
   }
 }
 
+- (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) {
index e4b191ec1f8bc04789a7766d80603eb2393fab4b..d4c9a2f3708959496db30952340cb52287c83de6 100644 (file)
@@ -18,7 +18,6 @@
   Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
   02111-1307, USA.
 */
-// $Id$
 
 #ifndef __NGMime_NGHeaderFieldParser_H__
 #define __NGMime_NGHeaderFieldParser_H__
index 5f3604ff0936634e26fdfb8b1364e83ad89bd6b5..5fc17adf6d20ed8c1c83df97a6b1e50a8a533333 100644 (file)
@@ -2,6 +2,6 @@
 
 MAJOR_VERSION:=4
 MINOR_VERSION:=3
-SUBMINOR_VERSION:=183
+SUBMINOR_VERSION:=184
 
 # v4.2.149 requires libNGStreams v4.2.34