/*
- Copyright (C) 2004 SKYRIX Software AG
+ Copyright (C) 2004-2005 SKYRIX Software AG
This file is part of OpenGroupware.org.
*/
#include "SOGoMailConnectionEntry.h"
+#include "SOGoMailboxInfo.h"
#include "common.h"
@implementation SOGoMailConnectionEntry
+static BOOL debugOn = NO;
+static BOOL debugCache = NO;
+static BOOL debugKeys = NO;
+static BOOL alwaysSelect = NO;
+static BOOL onlyFetchInbox = NO;
+static NSString *imap4Separator = nil;
+
++ (void)initialize {
+ NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
+
+ debugOn = [ud boolForKey:@"SOGoEnableIMAP4Debug"];
+ debugCache = [ud boolForKey:@"SOGoEnableIMAP4CacheDebug"];
+ alwaysSelect = [ud boolForKey:@"SOGoAlwaysSelectIMAP4Folder"];
+ if (debugOn) NSLog(@"Note: SOGoEnableIMAP4Debug is enabled!");
+ if (alwaysSelect)
+ NSLog(@"WARNING: 'SOGoAlwaysSelectIMAP4Folder' enabled (slow down)");
+
+ imap4Separator = [[ud stringForKey:@"SOGoIMAP4StringSeparator"] copy];
+ if ([imap4Separator length] == 0)
+ imap4Separator = @"/";
+ NSLog(@"Note(SOGoMailManager): using '%@' as the IMAP4 folder separator.",
+ imap4Separator);
+}
+
- (id)initWithClient:(NGImap4Client *)_client password:(NSString *)_pwd {
if (_client == nil || _pwd == nil) {
[self release];
ASSIGN(self->cachedUIDs, nil);
}
+/* IMAP4 path/url processing methods */
+
+NSArray *SOGoMailGetDirectChildren(NSArray *_array, NSString *_fn) {
+ /*
+ Scans string '_array' for strings which start with the string in '_fn'.
+ Then split on '/'.
+ */
+ NSMutableArray *ma;
+ unsigned i, count, prefixlen;
+
+ if ((count = [_array count]) < 2)
+ /* one entry is the folder itself, so we need at least two */
+ return [NSArray array];
+
+#if __APPLE__
+ // TODO: somehow results are different on OSX
+ prefixlen = [_fn isEqualToString:@""] ? 0 : [_fn length] + 1;
+#else
+ prefixlen = [_fn isEqualToString:@"/"] ? 1 : [_fn length] + 1;
+#endif
+ ma = [NSMutableArray arrayWithCapacity:count];
+ for (i = 0; i < count; i++) {
+ NSString *p;
+
+ p = [_array objectAtIndex:i];
+ if ([p length] <= prefixlen)
+ continue;
+ if (prefixlen != 0 && ![p hasPrefix:_fn])
+ continue;
+
+ /* cut of common part */
+ p = [p substringFromIndex:prefixlen];
+
+ /* check whether the path is a sub-subfolder path */
+ if ([p rangeOfString:@"/"].length > 0)
+ continue;
+
+ [ma addObject:p];
+ }
+
+ [ma sortUsingSelector:@selector(compare:)];
+ return ma;
+}
+
+NSArray *SOGoMailExtractSubfolders(NSURL *_url, NSDictionary *_result) {
+ NSString *folderName;
+ NSDictionary *result;
+ NSArray *names;
+ NSArray *flags;
+
+ /* Note: the result is normalized, that is, it contains / as the separator */
+ folderName = [_url path];
+#if __APPLE__
+ /* normalized results already have the / in front on libFoundation?! */
+ if ([folderName hasPrefix:@"/"])
+ folderName = [folderName substringFromIndex:1];
+#endif
+
+ result = [_result valueForKey:@"list"];
+
+ /* Cyrus already tells us whether we need to check for children */
+ flags = [result objectForKey:folderName];
+ if ([flags containsObject:@"hasnochildren"]) {
+ if (debugKeys)
+ NSLog(@"%s: folder %@ has no children.", __PRETTY_FUNCTION__,folderName);
+ return nil;
+ }
+
+ if (debugKeys) {
+ NSLog(@"%s: all keys %@: %@", __PRETTY_FUNCTION__, folderName,
+ [[result allKeys] componentsJoinedByString:@", "]);
+ }
+
+ names = SOGoMailGetDirectChildren([result allKeys], folderName);
+ if (debugKeys) {
+ NSLog(@"%s: subfolders of '%@': %@", __PRETTY_FUNCTION__, folderName,
+ [names componentsJoinedByString:@","]);
+ }
+ return names;
+}
+
+- (NSArray *)_getDirectChildren:(NSArray *)_array folderName:(NSString *)_fn {
+ return SOGoMailGetDirectChildren(_array, _fn);
+}
+- (NSArray *)extractSubfoldersForURL:(NSURL *)_url
+ fromResultSet:(NSDictionary *)_result
+{
+ return SOGoMailExtractSubfolders(_url, _result);
+}
+
+- (NSString *)imap4Separator {
+ // TODO: make server specific ivar!
+ return imap4Separator;
+}
+
+- (NSString *)imap4FolderNameForURL:(NSURL *)_url removeFileName:(BOOL)_delfn {
+ /* a bit hackish, but should be OK */
+ NSString *folderName;
+ NSArray *names;
+
+ if (_url == nil)
+ return nil;
+
+ folderName = [_url path];
+ if ([folderName length] == 0)
+ return nil;
+ if ([folderName characterAtIndex:0] == '/')
+ folderName = [folderName substringFromIndex:1];
+
+ if (_delfn) folderName = [folderName stringByDeletingLastPathComponent];
+
+ if ([[self imap4Separator] isEqualToString:@"/"])
+ return folderName;
+
+ names = [folderName pathComponents];
+ return [names componentsJoinedByString:[self imap4Separator]];
+}
+- (NSString *)imap4FolderNameForURL:(NSURL *)_url {
+ return [self imap4FolderNameForURL:_url removeFileName:NO];
+}
+
+- (NSArray *)extractFoldersFromResultSet:(NSDictionary *)_result {
+ /* Note: the result is normalized, that is, it contains / as the separator */
+ return [[_result valueForKey:@"list"] allKeys];
+}
+
+/* folder selections */
+
+- (BOOL)selectFolder:(id)_url {
+ NSDictionary *result;
+ NSString *newFolder;
+
+ newFolder = [_url isKindOfClass:[NSURL class]]
+ ? [self imap4FolderNameForURL:_url]
+ : _url;
+
+ if (!alwaysSelect) {
+ if ([[[self client] selectedFolderName] isEqualToString:newFolder])
+ return YES;
+ }
+
+ result = [[self client] select:newFolder];
+ if (![[result valueForKey:@"result"] boolValue]) {
+ [self errorWithFormat:@"could not select URL: %@: %@", _url, result];
+ return NO;
+ }
+
+ return YES;
+}
+
+- (BOOL)isPermissionDeniedResult:(id)_result {
+ if ([[_result valueForKey:@"result"] intValue] != 0)
+ return NO;
+
+ return [[_result valueForKey:@"reason"]
+ isEqualToString:@"Permission denied"];
+}
+
+/* folder operations */
+
+- (NSArray *)subfoldersForURL:(NSURL *)_url {
+ NSDictionary *result;
+
+ /* check hierarchy cache */
+
+ if ((result = [self cachedHierarchyResults]) != nil)
+ return [self extractSubfoldersForURL:_url fromResultSet:result];
+
+ [self debugWithFormat:@" no folders cached yet .."];
+
+ /* fetch _all_ folders */
+
+ result = [[self client] list:(onlyFetchInbox ? @"INBOX" : @"*")
+ pattern:@"*"];
+ if (![[result valueForKey:@"result"] boolValue]) {
+ [self errorWithFormat:@"listing of folder failed!"];
+ return nil;
+ }
+
+ /* cache results */
+
+ if ([result isNotNull]) {
+ [self cacheHierarchyResults:result];
+ if (debugCache) {
+ [self logWithFormat:@"cached results in entry %@: 0x%08X(%d)",
+ self, result, [result count]];
+ }
+ }
+
+ /* extract list */
+
+ return [self extractSubfoldersForURL:_url fromResultSet:result];
+}
+
+- (NSArray *)allFoldersForURL:(NSURL *)_url {
+ NSDictionary *result;
+
+ /* check hierarchy cache */
+
+ if ((result = [self cachedHierarchyResults]) != nil)
+ return [self extractFoldersFromResultSet:result];
+
+ [self debugWithFormat:@" no folders cached yet .."];
+
+ /* fetch _all_ folders */
+
+ result = [[self client] list:@"INBOX" pattern:@"*"];
+ if (![[result valueForKey:@"result"] boolValue]) {
+ [self logWithFormat:@"ERROR: listing of folder failed!"];
+ return nil;
+ }
+
+ /* cache results */
+
+ if ([result isNotNull]) {
+ [self cacheHierarchyResults:result];
+ if (debugCache) {
+ [self logWithFormat:@"cached results in entry %@: 0x%08X(%d)",
+ self, result, [result count]];
+ }
+ }
+
+ /* extract list */
+ return [self extractFoldersFromResultSet:result];
+}
+
+/* message operations */
+
+- (NSArray *)fetchUIDsInURL:(NSURL *)_url qualifier:(id)_qualifier
+ sortOrdering:(id)_so
+{
+ /*
+ sortOrdering can be an NSString, an EOSortOrdering or an array of EOS.
+ */
+ NSDictionary *result;
+ NSArray *uids;
+
+ /* check cache */
+
+ uids = [self cachedUIDsForURL:_url qualifier:_qualifier sortOrdering:_so];
+ if (uids != nil) {
+ if (debugCache) [self logWithFormat:@"reusing uid cache!"];
+ return [uids isNotNull] ? uids : nil;
+ }
+
+ /* select folder and fetch */
+
+ if (![self selectFolder:_url])
+ return nil;
+
+ result = [[self client] sort:_so qualifier:_qualifier encoding:@"UTF-8"];
+ if (![[result valueForKey:@"result"] boolValue]) {
+ [self errorWithFormat:@"could not sort contents of URL: %@", _url];
+ return nil;
+ }
+
+ uids = [result valueForKey:@"sort"];
+ if (![uids isNotNull]) {
+ [self errorWithFormat:@"got no UIDs for URL: %@: %@", _url, result];
+ return nil;
+ }
+
+ /* cache */
+
+ [self cacheUIDs:uids forURL:_url qualifier:_qualifier sortOrdering:_so];
+ return uids;
+}
+
+- (NSArray *)fetchUIDs:(NSArray *)_uids inURL:(NSURL *)_url
+ parts:(NSArray *)_parts
+{
+ // currently returns a dict?!
+ /*
+ Allowed fetch keys:
+ UID
+ BODY.PEEK[<section>]<<partial>>
+ BODY [this is the bodystructure, supported]
+ BODYSTRUCTURE [not supported yet!]
+ ENVELOPE [this is a parsed header, but does not include type]
+ FLAGS
+ INTERNALDATE
+ RFC822
+ RFC822.HEADER
+ RFC822.SIZE
+ RFC822.TEXT
+ */
+ NSDictionary *result;
+
+ if (_uids == nil)
+ return nil;
+ if ([_uids count] == 0)
+ return nil; // TODO: might break empty folders?! return a dict!
+
+ /* select folder */
+
+ if (![self selectFolder:_url])
+ return nil;
+
+ /* fetch parts */
+
+ // TODO: split uids into batches, otherwise Cyrus will complain
+ // => not really important because we batch before (in the sort)
+ // if the list is too long, we get a:
+ // "* BYE Fatal error: word too long"
+
+ result = [[self client] fetchUids:_uids parts:_parts];
+ if (![[result valueForKey:@"result"] boolValue]) {
+ [self errorWithFormat:@"could not fetch %d uids for url: %@",
+ [_uids count],_url];
+ return nil;
+ }
+
+ //[self logWithFormat:@"RESULT: %@", result];
+ return (id)result;
+}
+
+- (id)fetchURL:(NSURL *)_url parts:(NSArray *)_parts {
+ // currently returns a dict
+ NSDictionary *result;
+ NSString *uid;
+
+ if (![_url isNotNull]) return nil;
+
+ /* select folder */
+
+ uid = [self imap4FolderNameForURL:_url removeFileName:YES];
+ if (![self selectFolder:uid])
+ return nil;
+
+ /* fetch parts */
+
+ uid = [[_url path] lastPathComponent];
+
+ result = [client fetchUids:[NSArray arrayWithObject:uid] parts:_parts];
+ if (![[result valueForKey:@"result"] boolValue]) {
+ [self errorWithFormat:@"could not fetch url: %@", _url];
+ return nil;
+ }
+ //[self logWithFormat:@"RESULT: %@", result];
+ return (id)result;
+}
+
+- (NSData *)fetchContentOfBodyPart:(NSString *)_partId atURL:(NSURL *)_url {
+ NSString *key;
+ NSArray *parts;
+ id result, fetch, body;
+
+ if (_partId == nil) return nil;
+
+ key = [@"body[" stringByAppendingString:_partId];
+ key = [key stringByAppendingString:@"]"];
+ parts = [NSArray arrayWithObjects:&key count:1];
+
+ /* fetch */
+
+ result = [self fetchURL:_url parts:parts];
+
+ /* process results */
+
+ result = [result objectForKey:@"fetch"];
+ if ([result count] == 0) { /* did not find part */
+ [self errorWithFormat:@"did not find part: %@", _partId];
+ return nil;
+ }
+
+ fetch = [result objectAtIndex:0];
+ if ((body = [fetch objectForKey:@"body"]) == nil) {
+ [self errorWithFormat:@"did not find body in response: %@", result];
+ return nil;
+ }
+
+ if ((result = [body objectForKey:@"data"]) == nil) {
+ [self errorWithFormat:@"did not find data in body: %@", fetch];
+ return nil;
+ }
+ return result;
+}
+
+/* message flags */
+
+- (NSException *)addOrRemove:(BOOL)_flag flags:(id)_f toURL:(NSURL *)_url {
+ id result;
+
+ if (![_url isNotNull]) return nil;
+ if (![_f isNotNull]) return nil;
+
+ if (![_f isKindOfClass:[NSArray class]])
+ _f = [NSArray arrayWithObjects:&_f count:1];
+
+ /* select folder */
+
+ if (![self selectFolder:
+ [self imap4FolderNameForURL:_url removeFileName:YES]]) {
+ return [NSException exceptionWithHTTPStatus:404 /* server error */
+ reason:@"could not select IMAP4 folder"];
+ }
+
+ /* store flags */
+
+ result = [[self client] storeUid:[[[_url path] lastPathComponent] intValue]
+ add:[NSNumber numberWithBool:_flag]
+ flags:_f];
+ if (![[result valueForKey:@"result"] boolValue]) {
+ unsigned int status;
+ NSString *r;
+
+ r = [result valueForKey:@"reason"];
+ if ([r isEqualToString:@"Permission denied"]) {
+ /* different for each server?, no error codes in IMAP4 ... */
+ status = 403 /* Forbidden */;
+ }
+ else
+ status = 500 /* internal server error */;
+
+ [self logWithFormat:@"DEBUG: fail result %@", result];
+
+ r = [@"Failed to add flag to IMAP4 message: " stringByAppendingString:r];
+
+ return [NSException exceptionWithHTTPStatus:status /* server error */
+ reason:r];
+ }
+ /* result contains 'fetch' key with the current flags */
+ return nil;
+}
+- (NSException *)addFlags:(id)_f toURL:(NSURL *)_u {
+ return [self addOrRemove:YES flags:_f toURL:_u];
+}
+- (NSException *)removeFlags:(id)_f toURL:(NSURL *)_u {
+ return [self addOrRemove:NO flags:_f toURL:_u];
+}
+
+- (NSException *)markURLDeleted:(NSURL *)_url {
+ return [self addOrRemove:YES flags:@"Deleted" toURL:_url];
+}
+
+- (NSException *)addFlags:(id)_f toAllMessagesInURL:(NSURL *)_url {
+ id result;
+
+ if (![_url isNotNull]) return nil;
+ if (![_f isNotNull]) return nil;
+
+ if (![_f isKindOfClass:[NSArray class]])
+ _f = [NSArray arrayWithObjects:&_f count:1];
+
+ /* select folder */
+
+ if (![self selectFolder:[self imap4FolderNameForURL:_url]]) {
+ return [NSException exceptionWithHTTPStatus:404 /* not found */
+ reason:@"could not select IMAP4 folder"];
+ }
+
+ /* fetch all sequence numbers */
+
+ result = [client searchWithQualifier:nil /* means: ALL */];
+ if (![[result valueForKey:@"result"] boolValue]) {
+ return [NSException exceptionWithHTTPStatus:500 /* server error */
+ reason:@"could not search in IMAP4 folder"];
+ }
+
+ result = [result valueForKey:@"search"];
+ if ([result count] == 0) /* no messages in there, nothin' to be done */
+ return nil;
+
+ /* store flags */
+
+ result = [[self client] storeFlags:_f forMSNs:result addOrRemove:YES];
+ if (![[result valueForKey:@"result"] boolValue]) {
+ return [NSException exceptionWithHTTPStatus:500 /* server error */
+ reason:@"could not change flags in IMAP4 folder"];
+ }
+
+ return nil;
+}
+
+/* posting new data */
+
+- (NSException *)postData:(NSData *)_data flags:(id)_f
+ toFolderURL:(NSURL *)_url
+{
+ id result;
+
+ if (![_url isNotNull]) return nil;
+ if (![_f isNotNull]) _f = [NSArray array];
+
+ if (![_f isKindOfClass:[NSArray class]])
+ _f = [NSArray arrayWithObjects:&_f count:1];
+
+ result = [[self client] append:_data
+ toFolder:[self imap4FolderNameForURL:_url]
+ withFlags:_f];
+ if (![[result valueForKey:@"result"] boolValue]) {
+ [self logWithFormat:@"DEBUG: fail result %@", result];
+ return [NSException exceptionWithHTTPStatus:500 /* server error */
+ reason:@"failed to store message to IMAP4 message"];
+ }
+ /* result contains 'fetch' key with the current flags */
+
+ // TODO: need to flush any caches?
+ return nil;
+}
+
+/* operations */
+
+- (NSException *)expungeAtURL:(NSURL *)_url {
+ NSString *p;
+ id result;
+
+ /* select folder */
+
+ p = [self imap4FolderNameForURL:_url removeFileName:NO];
+ if (![self selectFolder:p]) {
+ return [NSException exceptionWithHTTPStatus:501 /* Server Error */
+ reason:@"could not select IMAP4 folder!"];
+ }
+
+ /* expunge */
+
+ result = [[self client] expunge];
+
+ if (![[result valueForKey:@"result"] boolValue]) {
+ [self errorWithFormat:@"could not expunge url: %@", _url];
+ return nil;
+ }
+ //[self logWithFormat:@"RESULT: %@", result];
+ return nil;
+}
+
+/* copying and moving */
+
+- (NSException *)copyMailURL:(NSURL *)_srcurl toFolderURL:(NSURL *)_desturl {
+ NSString *srcname, *destname;
+ unsigned uid;
+ id result;
+
+ /* names */
+
+ srcname = [self imap4FolderNameForURL:_srcurl removeFileName:YES];
+ uid = [[[_srcurl path] lastPathComponent] unsignedIntValue];
+ destname = [self imap4FolderNameForURL:_desturl];
+
+ /* select source folder */
+
+ if (![self selectFolder:srcname]) {
+ return [NSException exceptionWithHTTPStatus:404 /* Not Found */
+ reason:@"did not find source folder"];
+ }
+
+ /* copy */
+
+ result = [[self client] copyUid:uid toFolder:destname];
+ if (![[result valueForKey:@"result"] boolValue]) {
+ return [NSException exceptionWithHTTPStatus:500 /* Server Error */
+ reason:@"copy operation failed"];
+ }
+
+ // TODO: need to flush some caches?
+
+ return nil;
+}
+
+/* managing folders */
+
+- (BOOL)doesMailboxExistAtURL:(NSURL *)_url {
+ NSString *folderName;
+ id result;
+
+ /* check in hierarchy cache */
+
+ if ((result = [self cachedHierarchyResults]) != nil) {
+ result = [result objectForKey:@"list"];
+ return ([result objectForKey:[_url path]] != nil) ? YES : NO;
+ }
+
+ /* check using IMAP4 select */
+ // TODO: we should probably just fetch the whole hierarchy?
+
+ folderName = [self imap4FolderNameForURL:_url];
+ result = [[self client] select:folderName];
+ if (![[result valueForKey:@"result"] boolValue])
+ return NO;
+
+ return YES;
+}
+
+- (id)infoForMailboxAtURL:(NSURL *)_url {
+ SOGoMailboxInfo *info;
+ NSString *folderName;
+ id result;
+
+ folderName = [self imap4FolderNameForURL:_url];
+ result = [[self client] select:folderName];
+ if (![[result valueForKey:@"result"] boolValue]) {
+ return [NSException exceptionWithHTTPStatus:404 /* Not Found */
+ reason:@"did not find IMAP4 folder (select failed)"];
+ }
+
+ info = [[SOGoMailboxInfo alloc] initWithURL:_url folderName:folderName
+ selectDictionary:result];
+ return [info autorelease];
+}
+
+- (NSException *)createMailbox:(NSString *)_mailbox atURL:(NSURL *)_url {
+ NSString *newPath;
+ id result;
+
+ /* construct path */
+
+ newPath = [self imap4FolderNameForURL:_url];
+ newPath = [newPath stringByAppendingString:[self imap4Separator]];
+ newPath = [newPath stringByAppendingString:_mailbox];
+
+ /* create */
+
+ result = [[self client] create:newPath];
+ if ([self isPermissionDeniedResult:result]) {
+ return [NSException exceptionWithHTTPStatus:403 /* forbidden */
+ reason:@"creation of folders not allowed"];
+ }
+ else if ([[result valueForKey:@"result"] intValue] == 0) {
+ return [NSException exceptionWithHTTPStatus:500 /* server error */
+ reason:[result valueForKey:@"reason"]];
+ }
+
+ [self flushFolderHierarchyCache];
+ // [self debugWithFormat:@"created mailbox: %@: %@", newPath, result];
+ return nil;
+}
+
+- (NSException *)deleteMailboxAtURL:(NSURL *)_url {
+ NSString *path;
+ id result;
+
+ /* delete */
+
+ path = [self imap4FolderNameForURL:_url];
+ result = [[self client] delete:path];
+
+ if ([self isPermissionDeniedResult:result]) {
+ return [NSException exceptionWithHTTPStatus:403 /* forbidden */
+ reason:@"creation of folders not allowed"];
+ }
+ else if ([[result valueForKey:@"result"] intValue] == 0) {
+ return [NSException exceptionWithHTTPStatus:500 /* server error */
+ reason:[result valueForKey:@"reason"]];
+ }
+
+ [self flushFolderHierarchyCache];
+#if 0
+ [self debugWithFormat:@"delete mailbox %@: %@", _url, result];
+#endif
+ return nil;
+}
+
+- (NSException *)moveMailboxAtURL:(NSURL *)_srcurl toURL:(NSURL *)_desturl {
+ NSString *srcname, *destname;
+ id result;
+
+ /* rename */
+
+ srcname = [self imap4FolderNameForURL:_srcurl];
+ destname = [self imap4FolderNameForURL:_desturl];
+
+ result = [[self client] rename:srcname to:destname];
+
+ if ([self isPermissionDeniedResult:result]) {
+ return [NSException exceptionWithHTTPStatus:403 /* forbidden */
+ reason:@"creation of folders not allowed"];
+ }
+ else if ([[result valueForKey:@"result"] intValue] == 0) {
+ return [NSException exceptionWithHTTPStatus:500 /* server error */
+ reason:[result valueForKey:@"reason"]];
+ }
+
+ [self flushFolderHierarchyCache];
+#if 0
+ [self debugWithFormat:@"renamed mailbox %@: %@", _srcurl, result];
+#endif
+ return nil;
+}
+
+/* ACLs */
+
+- (NSDictionary *)aclForMailboxAtURL:(NSURL *)_url {
+ /*
+ Returns a mapping of uid => permission strings, eg:
+ guizmo.g = lrs;
+ root = lrswipcda;
+ */
+ NSString *folderName;
+ id result;
+
+ folderName = [self imap4FolderNameForURL:_url];
+ result = [[self client] getACL:folderName];
+ if (![[result valueForKey:@"result"] boolValue]) {
+ [self logWithFormat:@"ERROR: getacl failed: %@", result];
+ return [NSException exceptionWithHTTPStatus:404 /* Not Found */
+ reason:@"did not find ACL for IMAP4 folder"];
+ }
+
+ return [result valueForKey:@"acl"];
+}
+
+- (NSString *)myRightsForMailboxAtURL:(NSURL *)_url {
+ NSString *folderName;
+ id result;
+
+ /* check cache */
+
+ if ((result = [self cachedMyRightsForURL:_url]) != nil)
+ return result;
+
+ /* run IMAP4 op */
+
+ folderName = [self imap4FolderNameForURL:_url];
+ result = [[self client] myRights:folderName];
+ if (![[result valueForKey:@"result"] boolValue]) {
+ [self logWithFormat:@"ERROR: myrights failed: %@", result];
+ return [NSException exceptionWithHTTPStatus:404 /* Not Found */
+ reason:@"did not find myrights for IMAP4 folder"];
+ }
+
+ /* cache results */
+
+ if ((result = [result valueForKey:@"myrights"]) != nil)
+ [self cacheMyRights:result forURL:_url];
+ return result;
+}
+
@end /* SOGoMailConnectionEntry */
#include "SOGoMailManager.h"
#include "SOGoMailConnectionEntry.h"
-#include "SOGoMailboxInfo.h"
#include "common.h"
/*
static BOOL debugCache = NO;
static BOOL debugKeys = NO;
static BOOL poolingOff = NO;
-static BOOL alwaysSelect = NO;
-static BOOL onlyFetchInbox = NO;
static NSTimeInterval PoolScanInterval = 5 * 60 /* every five minutes */;
-static NSString *imap4Separator = nil;
+ (void)initialize {
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
debugOn = [ud boolForKey:@"SOGoEnableIMAP4Debug"];
debugCache = [ud boolForKey:@"SOGoEnableIMAP4CacheDebug"];
poolingOff = [ud boolForKey:@"SOGoDisableIMAP4Pooling"];
- alwaysSelect = [ud boolForKey:@"SOGoAlwaysSelectIMAP4Folder"];
if (debugOn) NSLog(@"Note: SOGoEnableIMAP4Debug is enabled!");
if (poolingOff) NSLog(@"WARNING: IMAP4 connection pooling is disabled!");
-
- if (alwaysSelect)
- NSLog(@"WARNING: 'SOGoAlwaysSelectIMAP4Folder' enabled (slow down)");
-
- imap4Separator = [[ud stringForKey:@"SOGoIMAP4StringSeparator"] copy];
- if ([imap4Separator length] == 0)
- imap4Separator = @"/";
- NSLog(@"Note(SOGoMailManager): using '%@' as the IMAP4 folder separator.",
- imap4Separator);
}
+ (id)defaultMailManager {
return [self entryForURL:_url];
}
+- (NSException *)errorForMissingEntryAtURL:(NSURL *)_url {
+ // TODO: improve
+ return [NSException exceptionWithHTTPStatus:404 /* Not Found */
+ reason:@"Did not find mail URL"];
+}
+
/* client object */
- (NGImap4Client *)imap4ClientForURL:(NSURL *)_url password:(NSString *)_pwd {
[entry flushMailCaches];
}
-- (BOOL)selectFolder:(id)_url inClient:(NGImap4Client *)_client {
- NSDictionary *result;
- NSString *newFolder;
-
- newFolder = [_url isKindOfClass:[NSURL class]]
- ? [self imap4FolderNameForURL:_url]
- : _url;
-
- if (!alwaysSelect) {
- if ([[_client selectedFolderName] isEqualToString:newFolder])
- return YES;
- }
-
- result = [_client select:newFolder];
- if (![[result valueForKey:@"result"] boolValue]) {
- [self errorWithFormat:@"could not select URL: %@: %@", _url, result];
- return NO;
- }
-
- return YES;
-}
-
/* folder hierarchy */
-- (NSArray *)_getDirectChildren:(NSArray *)_array folderName:(NSString *)_fn {
- /*
- Scans string '_array' for strings which start with the string in '_fn'.
- Then split on '/'.
- */
- NSMutableArray *ma;
- unsigned i, count, prefixlen;
-
- if ((count = [_array count]) < 2)
- /* one entry is the folder itself, so we need at least two */
- return [NSArray array];
-
-#if __APPLE__
- // TODO: somehow results are different on OSX
- prefixlen = [_fn isEqualToString:@""] ? 0 : [_fn length] + 1;
-#else
- prefixlen = [_fn isEqualToString:@"/"] ? 1 : [_fn length] + 1;
-#endif
- ma = [NSMutableArray arrayWithCapacity:count];
- for (i = 0; i < count; i++) {
- NSString *p;
-
- p = [_array objectAtIndex:i];
- if ([p length] <= prefixlen)
- continue;
- if (prefixlen != 0 && ![p hasPrefix:_fn])
- continue;
-
- /* cut of common part */
- p = [p substringFromIndex:prefixlen];
-
- /* check whether the path is a sub-subfolder path */
- if ([p rangeOfString:@"/"].length > 0)
- continue;
-
- [ma addObject:p];
- }
-
- [ma sortUsingSelector:@selector(compare:)];
- return ma;
-}
-
-- (NSString *)imap4Separator {
- return imap4Separator;
-}
-
-- (NSString *)imap4FolderNameForURL:(NSURL *)_url removeFileName:(BOOL)_delfn {
- /* a bit hackish, but should be OK */
- NSString *folderName;
- NSArray *names;
-
- if (_url == nil)
- return nil;
-
- folderName = [_url path];
- if ([folderName length] == 0)
- return nil;
- if ([folderName characterAtIndex:0] == '/')
- folderName = [folderName substringFromIndex:1];
-
- if (_delfn) folderName = [folderName stringByDeletingLastPathComponent];
-
- if ([[self imap4Separator] isEqualToString:@"/"])
- return folderName;
-
- names = [folderName pathComponents];
- return [names componentsJoinedByString:[self imap4Separator]];
-}
-- (NSString *)imap4FolderNameForURL:(NSURL *)_url {
- return [self imap4FolderNameForURL:_url removeFileName:NO];
-}
-
-- (NSArray *)extractSubfoldersForURL:(NSURL *)_url
- fromResultSet:(NSDictionary *)_result
-{
- NSString *folderName;
- NSDictionary *result;
- NSArray *names;
- NSArray *flags;
-
- /* Note: the result is normalized, that is, it contains / as the separator */
- folderName = [_url path];
-#if __APPLE__
- /* normalized results already have the / in front on libFoundation?! */
- if ([folderName hasPrefix:@"/"])
- folderName = [folderName substringFromIndex:1];
-#endif
-
- result = [_result valueForKey:@"list"];
-
- /* Cyrus already tells us whether we need to check for children */
- flags = [result objectForKey:folderName];
- if ([flags containsObject:@"hasnochildren"]) {
- if (debugKeys)
- [self logWithFormat:@"folder %@ has no children.", folderName];
- return nil;
- }
-
- if (debugKeys) {
- [self logWithFormat:@"all keys %@: %@", folderName,
- [[result allKeys] componentsJoinedByString:@", "]];
- }
-
- names = [self _getDirectChildren:[result allKeys] folderName:folderName];
- if (debugKeys) {
- [self logWithFormat:@"subfolders of '%@': %@", folderName,
- [names componentsJoinedByString:@","]];
- }
- return names;
-}
-
-- (NSArray *)extractFoldersFromResultSet:(NSDictionary *)_result {
- /* Note: the result is normalized, that is, it contains / as the separator */
- return [[_result valueForKey:@"list"] allKeys];
-}
-
- (NSArray *)subfoldersForURL:(NSURL *)_url password:(NSString *)_pwd {
SOGoMailConnectionEntry *entry;
- NSDictionary *result;
if (debugKeys)
[self debugWithFormat:@"subfolders for URL: %@ ...",[_url absoluteString]];
if ((entry = [self entryForURL:_url password:_pwd]) == nil)
return nil;
- /* check hierarchy cache */
-
- if ((result = [entry cachedHierarchyResults]) != nil)
- return [self extractSubfoldersForURL:_url fromResultSet:result];
-
- [self debugWithFormat:@" no folders cached yet .."];
-
- /* fetch _all_ folders */
-
- result = [[entry client] list:(onlyFetchInbox ? @"INBOX" : @"*")
- pattern:@"*"];
- if (![[result valueForKey:@"result"] boolValue]) {
- [self errorWithFormat:@"listing of folder failed!"];
- return nil;
- }
-
- /* cache results */
-
- if ([result isNotNull]) {
- if (entry == nil) /* required in case the entry was not setup */
- entry = [self entryForURL:_url];
-
- [entry cacheHierarchyResults:result];
- if (debugCache) {
- [self logWithFormat:@"cached results in entry %@: 0x%08X(%d)",
- entry, result, [result count]];
- }
- }
-
- /* extract list */
-
- return [self extractSubfoldersForURL:_url fromResultSet:result];
+ return [entry subfoldersForURL:_url];
}
- (NSArray *)allFoldersForURL:(NSURL *)_url password:(NSString *)_pwd {
SOGoMailConnectionEntry *entry;
- NSDictionary *result;
-
+
if (debugKeys)
[self debugWithFormat:@"folders for URL: %@ ...",[_url absoluteString]];
if ((entry = [self entryForURL:_url password:_pwd]) == nil)
return nil;
- /* check hierarchy cache */
-
- if ((result = [entry cachedHierarchyResults]) != nil)
- return [self extractFoldersFromResultSet:result];
-
- [self debugWithFormat:@" no folders cached yet .."];
-
- /* fetch _all_ folders */
-
- result = [[entry client] list:@"INBOX" pattern:@"*"];
- if (![[result valueForKey:@"result"] boolValue]) {
- [self logWithFormat:@"ERROR: listing of folder failed!"];
- return nil;
- }
-
- /* cache results */
-
- if ([result isNotNull]) {
- if (entry == nil) /* required in case the entry was not setup */
- entry = [self entryForURL:_url];
-
- [entry cacheHierarchyResults:result];
- if (debugCache) {
- [self logWithFormat:@"cached results in entry %@: 0x%08X(%d)",
- entry, result, [result count]];
- }
- }
-
- /* extract list */
- return [self extractFoldersFromResultSet:result];
+ return [entry allFoldersForURL:_url];
}
/* messages */
sortOrdering can be an NSString, an EOSortOrdering or an array of EOS.
*/
SOGoMailConnectionEntry *entry;
- NSDictionary *result;
- NSArray *uids;
/* check connection cache */
-
if ((entry = [self entryForURL:_url password:_pwd]) == nil)
return nil;
- /* check cache */
-
- uids = [entry cachedUIDsForURL:_url qualifier:_qualifier sortOrdering:_so];
- if (uids != nil) {
- if (debugCache) [self logWithFormat:@"reusing uid cache!"];
- return [uids isNotNull] ? uids : nil;
- }
-
- /* select folder and fetch */
-
- if (![self selectFolder:_url inClient:[entry client]])
- return nil;
-
- result = [[entry client] sort:_so qualifier:_qualifier encoding:@"UTF-8"];
- if (![[result valueForKey:@"result"] boolValue]) {
- [self errorWithFormat:@"could not sort contents of URL: %@", _url];
- return nil;
- }
-
- uids = [result valueForKey:@"sort"];
- if (![uids isNotNull]) {
- [self errorWithFormat:@"got no UIDs for URL: %@: %@", _url, result];
- return nil;
- }
-
- /* cache */
-
- [entry cacheUIDs:uids forURL:_url qualifier:_qualifier sortOrdering:_so];
- return uids;
+ return [entry fetchUIDsInURL:_url qualifier:_qualifier sortOrdering:_so];
}
- (NSArray *)fetchUIDs:(NSArray *)_uids inURL:(NSURL *)_url
RFC822.SIZE
RFC822.TEXT
*/
- NGImap4Client *client;
- NSDictionary *result;
+ SOGoMailConnectionEntry *entry;
if (_uids == nil)
return nil;
if ([_uids count] == 0)
return nil; // TODO: might break empty folders?! return a dict!
- if ((client = [self imap4ClientForURL:_url password:_pwd]) == nil)
- return nil;
-
- /* select folder */
-
- if (![self selectFolder:_url inClient:client])
- return nil;
-
- /* fetch parts */
-
- // TODO: split uids into batches, otherwise Cyrus will complain
- // => not really important because we batch before (in the sort)
- // if the list is too long, we get a:
- // "* BYE Fatal error: word too long"
-
- result = [client fetchUids:_uids parts:_parts];
- if (![[result valueForKey:@"result"] boolValue]) {
- [self errorWithFormat:@"could not fetch %d uids for url: %@",
- [_uids count],_url];
+ /* check connection cache */
+ if ((entry = [self entryForURL:_url password:_pwd]) == nil)
return nil;
- }
- //[self logWithFormat:@"RESULT: %@", result];
- return (id)result;
+ return [entry fetchUIDs:_uids inURL:_url parts:_parts];
}
- (NSException *)expungeAtURL:(NSURL *)_url password:(NSString *)_pwd {
- NGImap4Client *client;
- NSString *p;
- id result;
-
- if ((client = [self imap4ClientForURL:_url password:_pwd]) == nil)
- return nil; // TODO: return error?
-
- /* select folder */
-
- p = [self imap4FolderNameForURL:_url removeFileName:NO];
- if (![self selectFolder:p inClient:client])
- return nil;
+ SOGoMailConnectionEntry *entry;
- /* expunge */
+ if ((entry = [self entryForURL:_url password:_pwd]) == nil)
+ return [self errorForMissingEntryAtURL:_url];
- result = [client expunge];
-
- if (![[result valueForKey:@"result"] boolValue]) {
- [self errorWithFormat:@"could not expunge url: %@", _url];
- return nil;
- }
- //[self logWithFormat:@"RESULT: %@", result];
- return nil;
+ return [entry expungeAtURL:_url];
}
- (id)fetchURL:(NSURL *)_url parts:(NSArray *)_parts password:(NSString *)_pwd{
- // currently returns a dict
- NGImap4Client *client;
- NSDictionary *result;
- NSString *uid;
+ SOGoMailConnectionEntry *entry;
if (![_url isNotNull]) return nil;
+ if ((entry = [self entryForURL:_url password:_pwd]) == nil)
+ return [self errorForMissingEntryAtURL:_url];
- if ((client = [self imap4ClientForURL:_url password:_pwd]) == nil)
- return nil;
-
- /* select folder */
-
- if (![self selectFolder:[self imap4FolderNameForURL:_url removeFileName:YES]
- inClient:client])
- return nil;
-
- /* fetch parts */
-
- uid = [[_url path] lastPathComponent];
-
- result = [client fetchUids:[NSArray arrayWithObject:uid] parts:_parts];
- if (![[result valueForKey:@"result"] boolValue]) {
- [self errorWithFormat:@"could not fetch url: %@", _url];
- return nil;
- }
- //[self logWithFormat:@"RESULT: %@", result];
- return (id)result;
+ return [entry fetchURL:_url parts:_parts];
}
- (NSData *)fetchContentOfBodyPart:(NSString *)_partId
atURL:(NSURL *)_url password:(NSString *)_pwd
{
- NSString *key;
- NSArray *parts;
- id result, fetch, body;
-
- if (_partId == nil) return nil;
-
- key = [@"body[" stringByAppendingString:_partId];
- key = [key stringByAppendingString:@"]"];
- parts = [NSArray arrayWithObjects:&key count:1];
-
- /* fetch */
-
- result = [self fetchURL:_url parts:parts password:_pwd];
-
- /* process results */
-
- result = [result objectForKey:@"fetch"];
- if ([result count] == 0) { /* did not find part */
- [self errorWithFormat:@"did not find part: %@", _partId];
- return nil;
- }
-
- fetch = [result objectAtIndex:0];
- if ((body = [fetch objectForKey:@"body"]) == nil) {
- [self errorWithFormat:@"did not find body in response: %@", result];
- return nil;
- }
-
- if ((result = [body objectForKey:@"data"]) == nil) {
- [self errorWithFormat:@"did not find data in body: %@", fetch];
- return nil;
- }
- return result;
+ SOGoMailConnectionEntry *entry;
+
+ if ((entry = [self entryForURL:_url password:_pwd]) == nil)
+ return nil; // TODO: improve?
+
+ return [entry fetchContentOfBodyPart:_partId atURL:_url];
}
- (NSException *)addOrRemove:(BOOL)_flag flags:(id)_f
toURL:(NSURL *)_url password:(NSString *)_p
{
- NGImap4Client *client;
- id result;
-
- if (![_url isNotNull]) return nil;
- if (![_f isNotNull]) return nil;
-
- if (![_f isKindOfClass:[NSArray class]])
- _f = [NSArray arrayWithObjects:&_f count:1];
-
- /* get client */
+ SOGoMailConnectionEntry *entry;
- if ((client = [self imap4ClientForURL:_url password:_p]) == nil)
- // TODO: return 401?
- return nil;
-
- /* select folder */
-
- if (![self selectFolder:[self imap4FolderNameForURL:_url removeFileName:YES]
- inClient:client]) {
- return [NSException exceptionWithHTTPStatus:404 /* server error */
- reason:@"could not select IMAP4 folder"];
- return nil;
- }
-
- /* store flags */
-
- result = [client storeUid:[[[_url path] lastPathComponent] intValue]
- add:[NSNumber numberWithBool:_flag]
- flags:_f];
- if (![[result valueForKey:@"result"] boolValue]) {
- unsigned int status;
- NSString *r;
-
- r = [result valueForKey:@"reason"];
- if ([r isEqualToString:@"Permission denied"]) {
- /* different for each server?, no error codes in IMAP4 ... */
- status = 403 /* Forbidden */;
- }
- else
- status = 500 /* internal server error */;
-
- [self logWithFormat:@"DEBUG: fail result %@", result];
+ if ((entry = [self entryForURL:_url password:_p]) == nil)
+ return [self errorForMissingEntryAtURL:_url];
- r = [@"Failed to add flag to IMAP4 message: " stringByAppendingString:r];
-
- return [NSException exceptionWithHTTPStatus:status /* server error */
- reason:r];
- }
- /* result contains 'fetch' key with the current flags */
- return nil;
+ return [entry addOrRemove:_flag flags:_f toURL:_url];
}
- (NSException *)addFlags:(id)_f toURL:(NSURL *)_u password:(NSString *)_p {
return [self addOrRemove:YES flags:_f toURL:_u password:_p];
- (NSException *)postData:(NSData *)_data flags:(id)_f
toFolderURL:(NSURL *)_url password:(NSString *)_p
{
- NGImap4Client *client;
- id result;
-
+ SOGoMailConnectionEntry *entry;
+
if (![_url isNotNull]) return nil;
- if (![_f isNotNull]) _f = [NSArray array];
- if ((client = [self imap4ClientForURL:_url password:_p]) == nil)
- return nil;
+ if ((entry = [self entryForURL:_url password:_p]) == nil)
+ return [self errorForMissingEntryAtURL:_url];
- if (![_f isKindOfClass:[NSArray class]])
- _f = [NSArray arrayWithObjects:&_f count:1];
-
- result = [client append:_data
- toFolder:[self imap4FolderNameForURL:_url]
- withFlags:_f];
- if (![[result valueForKey:@"result"] boolValue]) {
- [self logWithFormat:@"DEBUG: fail result %@", result];
- return [NSException exceptionWithHTTPStatus:500 /* server error */
- reason:@"failed to store message to IMAP4 message"];
- }
- /* result contains 'fetch' key with the current flags */
-
- // TODO: need to flush any caches?
- return nil;
+ return [entry postData:_data flags:_f toFolderURL:_url];
}
- (NSException *)copyMailURL:(NSURL *)_srcurl toFolderURL:(NSURL *)_desturl
password:(NSString *)_pwd
{
SOGoMailConnectionEntry *entry;
- NSString *srcname, *destname;
- unsigned uid;
- id result;
/* check connection cache */
- if ((entry = [self entryForURL:_srcurl password:_pwd]) == nil) {
- // TODO: better to use an auth exception?
- return [NSException exceptionWithHTTPStatus:404 /* Not Found */
- reason:@"did not find IMAP4 folder (no entry)"];
- }
+ if ((entry = [self entryForURL:_srcurl password:_pwd]) == nil)
+ return [self errorForMissingEntryAtURL:_srcurl];
/* check whether URLs are on different servers */
reason:@"source and destination on different servers"];
}
- /* names */
-
- srcname = [self imap4FolderNameForURL:_srcurl removeFileName:YES];
- uid = [[[_srcurl path] lastPathComponent] unsignedIntValue];
- destname = [self imap4FolderNameForURL:_desturl];
-
- /* select source folder */
-
- if (![self selectFolder:srcname inClient:[entry client]]) {
- return [NSException exceptionWithHTTPStatus:404 /* Not Found */
- reason:@"did not find source folder"];
- }
-
- /* copy */
-
- result = [[entry client] copyUid:uid toFolder:destname];
- if (![[result valueForKey:@"result"] boolValue]) {
- return [NSException exceptionWithHTTPStatus:500 /* Server Error */
- reason:@"copy operation failed"];
- }
-
- // TODO: need to flush some caches?
-
- return nil;
+ return [entry copyMailURL:_srcurl toFolderURL:_desturl];
}
/* managing folders */
- (BOOL)doesMailboxExistAtURL:(NSURL *)_url password:(NSString *)_pwd {
SOGoMailConnectionEntry *entry;
- NSString *folderName;
- id result;
if ((entry = [self entryForURL:_url password:_pwd]) == nil)
return NO;
- /* check in hierarchy cache */
-
- if ((result = [entry cachedHierarchyResults]) != nil) {
- result = [result objectForKey:@"list"];
- return ([result objectForKey:[_url path]] != nil) ? YES : NO;
- }
-
- /* check using IMAP4 select */
- // TODO: we should probably just fetch the whole hierarchy?
-
- folderName = [self imap4FolderNameForURL:_url];
- result = [[entry client] select:folderName];
- if (![[result valueForKey:@"result"] boolValue])
- return NO;
-
- return YES;
+ return [entry doesMailboxExistAtURL:_url];
}
- (id)infoForMailboxAtURL:(NSURL *)_url password:(NSString *)_pwd {
SOGoMailConnectionEntry *entry;
- SOGoMailboxInfo *info;
- NSString *folderName;
- id result;
-
- if ((entry = [self entryForURL:_url password:_pwd]) == nil) {
- // TODO: better to use an auth exception?
- return [NSException exceptionWithHTTPStatus:404 /* Not Found */
- reason:@"did not find IMAP4 folder (no entry)"];
- }
- folderName = [self imap4FolderNameForURL:_url];
- result = [[entry client] select:folderName];
- if (![[result valueForKey:@"result"] boolValue]) {
- return [NSException exceptionWithHTTPStatus:404 /* Not Found */
- reason:@"did not find IMAP4 folder (select failed)"];
- }
-
- info = [[SOGoMailboxInfo alloc] initWithURL:_url folderName:folderName
- selectDictionary:result];
- return [info autorelease];
+ if ((entry = [self entryForURL:_url password:_pwd]) == nil)
+ return [self errorForMissingEntryAtURL:_url];
+
+ return [entry infoForMailboxAtURL:_url];
}
- (NSException *)createMailbox:(NSString *)_mailbox atURL:(NSURL *)_url
password:(NSString *)_pwd
{
SOGoMailConnectionEntry *entry;
- NSString *newPath;
- id result;
-
- /* check connection cache */
- if ((entry = [self entryForURL:_url password:_pwd]) == nil) {
- // TODO: better to use an auth exception?
- return [NSException exceptionWithHTTPStatus:404 /* Not Found */
- reason:@"did not find IMAP4 folder (no entry)"];
- }
+ /* check connection cache */
+ if ((entry = [self entryForURL:_url password:_pwd]) == nil)
+ return [self errorForMissingEntryAtURL:_url];
- /* construct path */
-
- newPath = [self imap4FolderNameForURL:_url];
- newPath = [newPath stringByAppendingString:[self imap4Separator]];
- newPath = [newPath stringByAppendingString:_mailbox];
-
- /* create */
-
- result = [[entry client] create:newPath];
- if ([self isPermissionDeniedResult:result]) {
- return [NSException exceptionWithHTTPStatus:403 /* forbidden */
- reason:@"creation of folders not allowed"];
- }
- else if ([[result valueForKey:@"result"] intValue] == 0) {
- return [NSException exceptionWithHTTPStatus:500 /* server error */
- reason:[result valueForKey:@"reason"]];
- }
-
- [entry flushFolderHierarchyCache];
- // [self debugWithFormat:@"created mailbox: %@: %@", newPath, result];
- return nil;
+ return [entry createMailbox:_mailbox atURL:_url];
}
- (NSException *)deleteMailboxAtURL:(NSURL *)_url password:(NSString *)_pwd {
SOGoMailConnectionEntry *entry;
- NSString *path;
- id result;
-
- /* check connection cache */
- if ((entry = [self entryForURL:_url password:_pwd]) == nil) {
- // TODO: better to use an auth exception?
- return [NSException exceptionWithHTTPStatus:404 /* Not Found */
- reason:@"did not find IMAP4 folder (no entry)"];
- }
-
- /* delete */
-
- path = [self imap4FolderNameForURL:_url];
- result = [[entry client] delete:path];
+ /* check connection cache */
- if ([self isPermissionDeniedResult:result]) {
- return [NSException exceptionWithHTTPStatus:403 /* forbidden */
- reason:@"creation of folders not allowed"];
- }
- else if ([[result valueForKey:@"result"] intValue] == 0) {
- return [NSException exceptionWithHTTPStatus:500 /* server error */
- reason:[result valueForKey:@"reason"]];
- }
+ if ((entry = [self entryForURL:_url password:_pwd]) == nil)
+ return [self errorForMissingEntryAtURL:_url];
- [entry flushFolderHierarchyCache];
-#if 0
- [self debugWithFormat:@"delete mailbox %@: %@", _url, result];
-#endif
- return nil;
+ return [entry deleteMailboxAtURL:_url];
}
- (NSException *)moveMailboxAtURL:(NSURL *)_srcurl toURL:(NSURL *)_desturl
password:(NSString *)_pwd
{
SOGoMailConnectionEntry *entry;
- NSString *srcname, *destname;
- id result;
/* check connection cache */
- if ((entry = [self entryForURL:_srcurl password:_pwd]) == nil) {
- // TODO: better to use an auth exception?
- return [NSException exceptionWithHTTPStatus:404 /* Not Found */
- reason:@"did not find IMAP4 folder (no entry)"];
- }
+ if ((entry = [self entryForURL:_srcurl password:_pwd]) == nil)
+ return [self errorForMissingEntryAtURL:_srcurl];
/* check whether URLs are on different servers */
reason:@"source and destination on different servers"];
}
- /* rename */
-
- srcname = [self imap4FolderNameForURL:_srcurl];
- destname = [self imap4FolderNameForURL:_desturl];
-
- result = [[entry client] rename:srcname to:destname];
-
- if ([self isPermissionDeniedResult:result]) {
- return [NSException exceptionWithHTTPStatus:403 /* forbidden */
- reason:@"creation of folders not allowed"];
- }
- else if ([[result valueForKey:@"result"] intValue] == 0) {
- return [NSException exceptionWithHTTPStatus:500 /* server error */
- reason:[result valueForKey:@"reason"]];
- }
-
- [entry flushFolderHierarchyCache];
-#if 0
- [self debugWithFormat:@"renamed mailbox %@: %@", _srcurl, result];
-#endif
- return nil;
+ return [entry moveMailboxAtURL:_srcurl toURL:_desturl];
}
- (NSDictionary *)aclForMailboxAtURL:(NSURL *)_url password:(NSString *)_pwd {
root = lrswipcda;
*/
SOGoMailConnectionEntry *entry;
- NSString *folderName;
- id result;
- if ((entry = [self entryForURL:_url password:_pwd]) == nil) {
- // TODO: better to use an auth exception?
- return [NSException exceptionWithHTTPStatus:404 /* Not Found */
- reason:@"did not find IMAP4 folder (no entry)"];
- }
-
- folderName = [self imap4FolderNameForURL:_url];
- result = [[entry client] getACL:folderName];
- if (![[result valueForKey:@"result"] boolValue]) {
- [self logWithFormat:@"ERROR: getacl failed: %@", result];
- return [NSException exceptionWithHTTPStatus:404 /* Not Found */
- reason:@"did not find ACL for IMAP4 folder"];
- }
+ if ((entry = [self entryForURL:_url password:_pwd]) == nil)
+ return (id)[self errorForMissingEntryAtURL:_url];
- return [result valueForKey:@"acl"];
+ return [entry aclForMailboxAtURL:_url];
}
- (NSString *)myRightsForMailboxAtURL:(NSURL *)_url password:(NSString *)_pwd {
SOGoMailConnectionEntry *entry;
- NSString *folderName;
- id result;
-
- if ((entry = [self entryForURL:_url password:_pwd]) == nil) {
- // TODO: better to use an auth exception?
- return [NSException exceptionWithHTTPStatus:404 /* Not Found */
- reason:@"did not find IMAP4 folder (no entry)"];
- }
-
- /* check cache */
- if ((result = [entry cachedMyRightsForURL:_url]) != nil)
- return result;
-
- /* run IMAP4 op */
-
- folderName = [self imap4FolderNameForURL:_url];
- result = [[entry client] myRights:folderName];
- if (![[result valueForKey:@"result"] boolValue]) {
- [self logWithFormat:@"ERROR: myrights failed: %@", result];
- return [NSException exceptionWithHTTPStatus:404 /* Not Found */
- reason:@"did not find myrights for IMAP4 folder"];
- }
+ if ((entry = [self entryForURL:_url password:_pwd]) == nil)
+ return (id)[self errorForMissingEntryAtURL:_url];
- /* cache results */
-
- if ((result = [result valueForKey:@"myrights"]) != nil)
- [entry cacheMyRights:result forURL:_url];
- return result;
+ return [entry myRightsForMailboxAtURL:_url];
}
/* bulk flag adding (eg used for empty/trash) */
- (NSException *)addFlags:(id)_f toAllMessagesInURL:(NSURL *)_url
password:(NSString *)_p
{
- NGImap4Client *client;
- id result;
+ SOGoMailConnectionEntry *entry;
if (![_url isNotNull]) return nil;
if (![_f isNotNull]) return nil;
-
- if (![_f isKindOfClass:[NSArray class]])
- _f = [NSArray arrayWithObjects:&_f count:1];
-
- /* get client */
-
- if ((client = [self imap4ClientForURL:_url password:_p]) == nil)
- // TODO: return 401?
- return nil;
-
- /* select folder */
-
- if (![self selectFolder:[self imap4FolderNameForURL:_url] inClient:client]) {
- return [NSException exceptionWithHTTPStatus:404 /* not found */
- reason:@"could not select IMAP4 folder"];
- }
-
- /* fetch all sequence numbers */
- result = [client searchWithQualifier:nil /* means: ALL */];
- if (![[result valueForKey:@"result"] boolValue]) {
- return [NSException exceptionWithHTTPStatus:500 /* server error */
- reason:@"could not search in IMAP4 folder"];
- }
-
- result = [result valueForKey:@"search"];
- if ([result count] == 0) /* no messages in there, nothin' to be done */
- return nil;
-
- /* store flags */
-
- result = [client storeFlags:_f forMSNs:result addOrRemove:YES];
- if (![[result valueForKey:@"result"] boolValue]) {
- return [NSException exceptionWithHTTPStatus:500 /* server error */
- reason:@"could not change flags in IMAP4 folder"];
- }
+ if ((entry = [self entryForURL:_url password:_p]) == nil)
+ return [self errorForMissingEntryAtURL:_url];
- return nil;
+ return [entry addFlags:_f toAllMessagesInURL:_url];
}
/* debugging */