+2005-01-31 Helge Hess <helge.hess@opengroupware.org>
+
+ * NGImap4: added support for creating envelope objects from body
+ structure dictionaries (v4.5.209)
+
+2005-01-30 Helge Hess <helge.hess@opengroupware.org>
+
+ * NGImap4: code cleanup in response normalizer (v4.5.208)
+
+ * NGImap4: some fix in the Sieve script upload (v4.5.207)
+
2005-01-30 Helge Hess <helge.hess@skyrix.com>
* v4.5.206
+2005-01-31 Helge Hess <helge.hess@opengroupware.org>
+
+ * NGImap4Envelope.m, NGImap4EnvelopeAddress.m: added support for
+ bodystructure dictionaries
+
+2005-01-30 Helge Hess <helge.hess@opengroupware.org>
+
+ * NGImap4ResponseNormalizer.m: code cleanup
+
+ * NGSieveClient.m: removed a superflous "{" in the script-put call
+
2005-01-30 Helge Hess <helge.hess@skyrix.com>
* NGImap4Client.m: minor code cleanups
Wraps the raw envelope as parsed from an IMAP4 fetch response.
*/
-@class NSString, NSArray, NSCalendarDate;
+@class NSString, NSArray, NSCalendarDate, NSDictionary;
@class NGImap4EnvelopeAddress;
@interface NGImap4Envelope : NSObject
sender:(id)_sender replyTo:(id)_replyTo
to:(NSArray *)_to cc:(NSArray *)_cc bcc:(NSArray *)_bcc;
+- (id)initWithBodyStructureInfo:(NSDictionary *)_info;
+
/* accessors */
- (NSCalendarDate *)date;
#include "NGImap4Envelope.h"
#include "NGImap4EnvelopeAddress.h"
+#include <NGMime/NGMimeHeaderFieldParser.h>
#include "imCommon.h"
@implementation NGImap4Envelope
+static NGMimeRFC822DateHeaderFieldParser *dateParser = nil;
+
++ (void)initialize {
+ dateParser = [[NGMimeRFC822DateHeaderFieldParser alloc] init];
+}
+
- (id)newEnvelopeAddressForEMail:(id)_email {
if (![_email isNotNull])
return nil;
if ([_email isKindOfClass:[NGImap4EnvelopeAddress class]])
return [_email copy];
+
+ if ([_email isKindOfClass:[NSDictionary class]]) {
+ /*
+ A body structure dictionary, contains those keys:
+ hostName, mailboxName, personalName, sourceRoute
+ */
+ return [[NGImap4EnvelopeAddress alloc] initWithBodyStructureInfo:_email];
+ }
_email = [_email stringValue];
if ([_email length] == 0)
return self;
}
+- (id)initWithBodyStructureInfo:(NSDictionary *)_info {
+ id lFrom, lReplyTo, lDate;
+
+ if (![_info isNotNull]) {
+ [self release];
+ return nil;
+ }
+
+ lFrom = [_info valueForKey:@"from"];
+ lReplyTo = [_info valueForKey:@"reply-to"];
+
+ if ([lFrom isKindOfClass:[NSArray class]])
+ lFrom = [lFrom count] > 0 ? [lFrom objectAtIndex:0] : nil;
+ if ([lReplyTo isKindOfClass:[NSArray class]])
+ lReplyTo = [lReplyTo count] > 0 ? [lReplyTo objectAtIndex:0] : nil;
+
+ self = [self initWithMessageID:[_info valueForKey:@"messageId"]
+ subject:[_info valueForKey:@"subject"]
+ sender:lFrom replyTo:lReplyTo
+ to:[_info valueForKey:@"to"]
+ cc:[_info valueForKey:@"cc"]
+ bcc:[_info valueForKey:@"bcc"]];
+ if (self == nil) return nil;
+
+ /* extended ivars */
+
+ self->inReplyTo = [[_info valueForKey:@"in-reply-to"] copy];
+
+ if ([(lDate = [_info valueForKey:@"date"]) isNotNull]) {
+ if ([lDate isKindOfClass:[NSDate class]])
+ self->date = [lDate copy];
+ else
+ self->date = [[dateParser parseValue:lDate ofHeaderField:@"date"] copy];
+ }
+
+ return self;
+}
+
- (void)dealloc {
[self->date release];
[self->subject release];
response.
*/
-@class NSString;
+@class NSString, NSDictionary;
@interface NGImap4EnvelopeAddress : NSObject < NSCopying >
{
- (id)initWithPersonalName:(NSString *)_pname sourceRoute:(NSString *)_route
mailbox:(NSString *)_mbox host:(NSString *)_host;
+
- (id)initWithString:(NSString *)_str;
+- (id)initWithBodyStructureInfo:(NSDictionary *)_info;
+
/* accessors */
- (NSString *)personalName;
mailbox:_str host:nil];
}
+- (id)initWithBodyStructureInfo:(NSDictionary *)_info {
+ if (![_info isNotNull]) {
+ [self release];
+ return nil;
+ }
+ return [self initWithPersonalName:[_info valueForKey:@"personalName"]
+ sourceRoute:[_info valueForKey:@"sourceRoute"]
+ mailbox:[_info valueForKey:@"mailboxName"]
+ host:[_info valueForKey:@"hostName"]];
+}
+
- (void)dealloc {
[self->personalName release];
[self->sourceRoute release];
map = [NGMutableHashMap hashMapWithCapacity:4];
- if ((value = [_dict objectForKey:@"subject"])) {
+ if ([(value = [_dict objectForKey:@"subject"]) isNotNull])
[map setObject:value forKey:Fields->subject];
- }
- if ((value = [_dict objectForKey:@"messageId"])) {
+
+ if ([(value = [_dict objectForKey:@"messageId"]) isNotNull])
[map setObject:value forKey:Fields->messageID];
- }
- if ((value = [_dict objectForKey:@"in-reply-to"])) {
+
+ if ([(value = [_dict objectForKey:@"in-reply-to"]) isNotNull])
[map setObject:value forKey:@"in-reply-to"];
- }
- if ((value = [_dict objectForKey:Fields->contentLength])) {
+
+ if ([(value = [_dict objectForKey:Fields->contentLength]) isNotNull])
[map setObject:value forKey:Fields->contentLength];
- }
- if ((value = [_dict objectForKey:@"date"])) {
+
+ if ([(value = [_dict objectForKey:@"date"]) isNotNull]) {
NSCalendarDate *d;
if ((d = [NSCalendarDate calendarDateWithRfc822DateString:value]))
dictionary for each response to the 'fetch' key of the normalized response
dictionary (as retrieved by 'normalizeResponse')
*/
-- (NSDictionary *)normalizeFetchResponse:(NGHashMap *)_map {
- /*
- Raw Sample (Courier):
- C[0x8b4e754]: 27 uid fetch 635 (body)
- S[0x8c8b4e4]: * 627 FETCH (UID 635 BODY
- ("text" "plain" ("charset" "iso-8859-1" "format" "flowed")
- NIL NIL "8bit" 2474 51))
- S[0x8c8b4e4]: * 627 FETCH (FLAGS (\Seen))
- S[0x8c8b4e4]: 27 OK FETCH completed.
- - this results in two result records (one for UID and one for FLAGS)
- TODO: should we coalesce?
-
- Raw Sample (Cyrus):
- C[0x8c8ec64]: 14 uid fetch 20199 (body)
- S[0x8da46a4]: * 93 FETCH (UID 20199 BODY
- ((("TEXT" "PLAIN" ("CHARSET" "utf-8") NIL "signed data" "7BIT" 691 17)
- ("APPLICATION" "PKCS7-SIGNATURE" ("NAME" "smime.p7s") NIL
- "signature" "BASE64" 2936) "SIGNED")
- ("TEXT" "PLAIN" ("CHARSET" "us-ascii") NIL NIL "7BIT" 146 4)
- "MIXED"))
- S[0x8da46a4]: 14 OK Completed
- - UID key is mapped to 'uid'
- - BODY key is mapped to a nested body structure
- - MSN is added for the '93'? (TODO: make sure this is the case)
-
- Sample returns (not for the above code!):
- {
- // other message stuff
- fetch = (
- {
- header = < NSData containing the header >;
- size = 3314;
- uid = 20187;
- msn = 72;
- flags = ( answered, deleted, seen );
- },
- ... for each fetch message ...
- )
- }
- */
- NSMutableDictionary *result;
- id obj;
- NSEnumerator *enumerator;
- NSMutableArray *fetchResponseRecords;
-
- // TODO: describe what the generic normalize does.
- // Q: do we need to run this before the following section or can we
- // call this method just before [result setObject:...] ? (I guess
- // the latter, because 'result' is not accessed, but who knows
- // about side effects in this JR cruft :-( )
- result = [self normalizeResponse:_map];
-
- fetchResponseRecords = [[NSMutableArray alloc] initWithCapacity:512];
-
- /* walk over each response tag which is keyed by 'fetch' in the hashmap */
- enumerator = [_map objectEnumeratorForKey:@"fetch"];
- while ((obj = [enumerator nextObject])) {
+- (NSDictionary *)normalizeFetchResponsePart:(id)obj {
// TODO: shouldn't we use a specific object instead of NSDict for that?
NSDictionary *entry;
NSEnumerator *keyEnum;
// TODO: this should add some error handling wrt the count?
// TODO: this could return multiple values for the same key?! => fix that
- while ((key = [keyEnum nextObject]) && (count < 9)) {
+ while (((key = [keyEnum nextObject]) != nil) && (count < 9)) {
unsigned klen;
unichar c;
? [[DictClass alloc] initWithObjects:values forKeys:keys count:count]
: nil;
- if (entry == nil)
+ return entry; /* returns retained object! */
+}
+- (NSDictionary *)normalizeFetchResponse:(NGHashMap *)_map {
+ /*
+ Raw Sample (Courier):
+ C[0x8b4e754]: 27 uid fetch 635 (body)
+ S[0x8c8b4e4]: * 627 FETCH (UID 635 BODY
+ ("text" "plain" ("charset" "iso-8859-1" "format" "flowed")
+ NIL NIL "8bit" 2474 51))
+ S[0x8c8b4e4]: * 627 FETCH (FLAGS (\Seen))
+ S[0x8c8b4e4]: 27 OK FETCH completed.
+ - this results in two result records (one for UID and one for FLAGS)
+ TODO: should we coalesce?
+
+ Raw Sample (Cyrus):
+ C[0x8c8ec64]: 14 uid fetch 20199 (body)
+ S[0x8da46a4]: * 93 FETCH (UID 20199 BODY
+ ((("TEXT" "PLAIN" ("CHARSET" "utf-8") NIL "signed data" "7BIT" 691 17)
+ ("APPLICATION" "PKCS7-SIGNATURE" ("NAME" "smime.p7s") NIL
+ "signature" "BASE64" 2936) "SIGNED")
+ ("TEXT" "PLAIN" ("CHARSET" "us-ascii") NIL NIL "7BIT" 146 4)
+ "MIXED"))
+ S[0x8da46a4]: 14 OK Completed
+ - UID key is mapped to 'uid'
+ - BODY key is mapped to a nested body structure
+ - MSN is added for the '93'? (TODO: make sure this is the case)
+
+ Sample returns (not for the above code!):
+ {
+ // other message stuff
+ fetch = (
+ {
+ header = < NSData containing the header >;
+ size = 3314;
+ uid = 20187;
+ msn = 72;
+ flags = ( answered, deleted, seen );
+ },
+ ... for each fetch message ...
+ )
+ }
+ */
+ NSMutableDictionary *result;
+ id obj;
+ NSEnumerator *enumerator;
+ NSMutableArray *fetchResponseRecords;
+
+ // TODO: describe what the generic normalize does.
+ // Q: do we need to run this before the following section or can we
+ // call this method just before [result setObject:...] ? (I guess
+ // the latter, because 'result' is not accessed, but who knows
+ // about side effects in this JR cruft :-( )
+ result = [self normalizeResponse:_map];
+
+ fetchResponseRecords = [[NSMutableArray alloc] initWithCapacity:512];
+
+ /* walk over each response tag which is keyed by 'fetch' in the hashmap */
+ enumerator = [_map objectEnumeratorForKey:@"fetch"];
+ while ((obj = [enumerator nextObject]) != nil) {
+ NSDictionary *entry;
+
+ if ((entry = [self normalizeFetchResponsePart:obj]) == nil)
continue;
[fetchResponseRecords addObject:entry];
mailbox name
hostname
eg:
- (NIL NIL "helge.hess" "opengroupware.org")
+ ("Helge Hess" NIL "helge.hess" "opengroupware.org")
*/
NGImap4EnvelopeAddress *address;
NSString *pname, *route, *mailbox, *host;
}
- (NSArray *)_parseEnvelopeAddressStructures {
+ /*
+ Parses an array of envelopes, most common:
+ ((NIL NIL "users-admin" "opengroupware.org"))
+ (just one envelope in the array)
+ */
NSMutableArray *ma;
if (_la(self, 0) != '(') {
s = @"PUTSCRIPT \"";
s = [s stringByAppendingString:_name];
- s = [s stringByAppendingString:@"\" {"];
+ s = [s stringByAppendingString:@"\" "];
s = [s stringByAppendingFormat:@"{%d+}\r\n%@", [_script length], _script];
s = [s stringByAppendingString:@"\r\n"];
map = [self processCommand:s];
MAJOR_VERSION:=4
MINOR_VERSION:=5
-SUBMINOR_VERSION:=206
+SUBMINOR_VERSION:=209
# v4.2.149 requires libNGStreams v4.2.34