/* message */
- (id)fetchParts:(NSArray *)_parts {
+ // TODO: explain what it does
+ /*
+ Called by -fetchPlainTextParts:
+ */
return [[self imap4Connection] fetchURL:[self imap4URL] parts:_parts];
}
/* bulk fetching of plain/text content */
- (BOOL)shouldFetchPartOfType:(NSString *)_type subtype:(NSString *)_subtype {
+ /*
+ This method decides which parts are 'prefetched' for display. Those are
+ usually text parts (the set is currently hardcoded in this method ...).
+ */
_type = [_type lowercaseString];
_subtype = [_subtype lowercaseString];
- if ([_type isEqualToString:@"text"]) {
- if ([_subtype isEqualToString:@"plain"])
- return YES;
-
- if ([_subtype isEqualToString:@"calendar"]) /* we also fetch calendars */
- return YES;
- }
-
- if ([_type isEqualToString:@"application"]) {
- if ([_subtype isEqualToString:@"pgp-signature"])
- return YES;
- }
-
- return NO;
+ return (([_type isEqualToString:@"text"]
+ && ([_subtype isEqualToString:@"plain"]
+ || [_subtype isEqualToString:@"html"]
+ || [_subtype isEqualToString:@"calendar"]))
+ || ([_type isEqualToString:@"application"]
+ && ([_subtype isEqualToString:@"pgp-signature"]
+ || [_subtype hasPrefix:@"x-vnd.kolab."])));
}
- (void)addRequiredKeysOfStructure:(id)_info path:(NSString *)_p
toArray:(NSMutableArray *)_keys
recurse:(BOOL)_recurse
{
+ /*
+ This is used to collect the set of IMAP4 fetch-keys required to fetch
+ the basic parts of the body structure. That is, to fetch all parts which
+ are displayed 'inline' in a single IMAP4 fetch.
+
+ The method calls itself recursively to walk the body structure.
+ */
NSArray *parts;
unsigned i, count;
BOOL fetchPart;
id body;
+ /* Note: if the part itself doesn't qualify, we still check subparts */
fetchPart = [self shouldFetchPartOfType:[_info valueForKey:@"type"]
subtype:[_info valueForKey:@"subtype"]];
if (fetchPart) {
NSString *sp;
id childInfo;
- sp = [_p length] > 0
+ sp = ([_p length] > 0)
? [_p stringByAppendingFormat:@".%d", i + 1]
: [NSString stringWithFormat:@"%d", i + 1];
}
- (NSArray *)plainTextContentFetchKeys {
+ /*
+ The name is not 100% correct. The method returns all body structure fetch
+ keys which are marked by the -shouldFetchPartOfType:subtype: method.
+ */
NSMutableArray *ma;
ma = [NSMutableArray arrayWithCapacity:4];
}
- (NSDictionary *)fetchPlainTextParts:(NSArray *)_fetchKeys {
+ // TODO: is the name correct or does it also fetch other parts?
NSMutableDictionary *flatContents;
unsigned i, count;
id result;
result = [self fetchParts:_fetchKeys];
result = [result valueForKey:@"RawResponse"]; // hackish
-
+
// Note: -valueForKey: doesn't work!
result = [(NSDictionary *)result objectForKey:@"fetch"];
objectForKey:@"data"];
if (![data isNotNull]) {
- [self debugWithFormat:@"got no data fork key: %@", key];
+ [self errorWithFormat:@"got no data for key: %@", key];
continue;
}
-
+
if ([key isEqualToString:@"body[text]"])
- key = @""; // see key collector
+ key = @""; // see key collector for explanation (TODO: where?)
else if ([key hasPrefix:@"body["]) {
NSRange r;
/* convert parts to strings */
-- (NSString *)stringForData:(NSData *)_data partInfo:(NSDictionary *)_info {
- NSString *charset;
- NSString *s;
+- (NSString *)stringForData:(NSData *)_data partInfo:(NSDictionary *)_info
+{
+ NSString *charset, *encoding, *s;
+ NSData *mailData;
if (![_data isNotNull])
return nil;
s = nil;
-
- charset = [[_info valueForKey:@"parameterList"] valueForKey:@"charset"];
- if ([charset isNotNull] && [charset length] > 0)
- s = [NSString stringWithData:_data usingEncodingNamed:charset];
-
- if (s == nil) { /* no charset provided, fall back to UTF-8 */
- s = [[NSString alloc] initWithData:_data encoding:NSUTF8StringEncoding];
- s = [s autorelease];
- }
-
+
+ encoding = [[_info objectForKey:@"encoding"] lowercaseString];
+
+ if ([encoding isEqualToString: @"7bit"]
+ || [encoding isEqualToString: @"8bit"])
+ mailData = _data;
+ else if ([encoding isEqualToString: @"base64"])
+ mailData = [_data dataByDecodingBase64];
+ else if ([encoding isEqualToString: @"quoted-printable"])
+ mailData = [_data dataByDecodingQuotedPrintable];
+
+ charset = [[_info valueForKey:@"parameterList"] valueForKey: @"charset"];
+ if (![charset length])
+ {
+ s = [[NSString alloc] initWithData:mailData encoding:NSUTF8StringEncoding];
+ [s autorelease];
+ }
+ else
+ s = [NSString stringWithData: mailData
+ usingEncodingNamed: charset];
+
return s;
}
return nil;
}
+- (NSException *) moveToFolderNamed: (NSString *) folderName
+ inContext: (id)_ctx
+{
+ /*
+ Trashing is three actions:
+ a) copy to trash folder
+ b) mark mail as deleted
+ c) expunge folder
+
+ In case b) or c) fails, we can't do anything because IMAP4 doesn't tell us
+ the ID used in the trash folder.
+ */
+ SOGoMailAccounts *destFolder;
+ NSEnumerator *folders;
+ NSString *currentFolderName, *reason;
+ NSException *error;
+
+ // TODO: check for safe HTTP method
+
+ destFolder = [self mailAccountsFolder];
+ folders = [[folderName componentsSeparatedByString: @"/"] objectEnumerator];
+ currentFolderName = [folders nextObject];
+ currentFolderName = [folders nextObject];
+
+ while (currentFolderName)
+ {
+ destFolder = [destFolder lookupName: currentFolderName
+ inContext: _ctx
+ acquire: NO];
+ if ([destFolder isKindOfClass: [NSException class]])
+ return (NSException *) destFolder;
+ currentFolderName = [folders nextObject];
+ }
+
+ if (!([destFolder isKindOfClass: [SOGoMailFolder class]]
+ && [destFolder isNotNull]))
+ {
+ reason = [NSString stringWithFormat: @"Did not find folder name '%@'!",
+ folderName];
+ return [NSException exceptionWithHTTPStatus:500 /* Server Error */
+ reason: reason];
+ }
+ [destFolder flushMailCaches];
+
+ /* a) copy */
+
+ error = [self davCopyToTargetObject: destFolder
+ newName:@"fakeNewUnusedByIMAP4" /* autoassigned */
+ inContext:_ctx];
+ if (error != nil) return error;
+
+ /* b) mark deleted */
+
+ error = [[self imap4Connection] markURLDeleted: [self imap4URL]];
+ if (error != nil) return error;
+
+ /* c) expunge */
+
+ error = [[self imap4Connection] expungeAtURL:[[self container] imap4URL]];
+ if (error != nil) return error; // TODO: unflag as deleted?
+ [self flushMailCaches];
+
+ return nil;
+}
+
- (NSException *)delete {
/*
Note: delete is different to DELETEAction: for mails! The 'delete' runs
/* some mail classification */
+- (BOOL)isKolabObject {
+ NSDictionary *h;
+
+ if ((h = [self mailHeaders]) != nil)
+ return [[h objectForKey:@"x-kolab-type"] isNotEmpty];
+
+ // TODO: we could check the body structure?
+
+ return NO;
+}
+
- (BOOL)isMailingListMail {
NSDictionary *h;
*/
return mailETag;
}
+- (int)zlGenerationCount {
+ return 0; /* mails never change */
+}
+
+/* Outlook mail tagging */
+
+- (NSString *)outlookMessageClass {
+ NSString *type;
+
+ if ((type = [[self mailHeaders] objectForKey:@"x-kolab-type"]) != nil) {
+ if ([type isEqualToString:@"application/x-vnd.kolab.contact"])
+ return @"IPM.Contact";
+ if ([type isEqualToString:@"application/x-vnd.kolab.task"])
+ return @"IPM.Task";
+ if ([type isEqualToString:@"application/x-vnd.kolab.event"])
+ return @"IPM.Appointment";
+ if ([type isEqualToString:@"application/x-vnd.kolab.note"])
+ return @"IPM.Note";
+ if ([type isEqualToString:@"application/x-vnd.kolab.journal"])
+ return @"IPM.Journal";
+ }
+
+ return @"IPM.Message"; /* email, default class */
+}
/* debugging */