- (NSDate *)creationTime;
+- (void)cacheHierarchyResults:(NSDictionary *)_hierarchy;
+- (NSDictionary *)cachedHierarchyResults;
+
@end
@implementation SOGoMailManager
[_url scheme], [_url user], [_url host], [_url port]];
}
+- (SOGoMailConnectionEntry *)entryForURL:(NSURL *)_url {
+ if (_url == nil)
+ return nil;
+
+ return [self->urlToEntry objectForKey:[self cacheKeyForURL:_url]];
+}
+- (void)cacheEntry:(SOGoMailConnectionEntry *)_entry forURL:(NSURL *)_url {
+ if (_entry == nil) _entry = (id)[NSNull null];
+ [self->urlToEntry setObject:_entry forKey:[self cacheKeyForURL:_url]];
+}
+
- (void)_garbageCollect:(NSTimer *)_timer {
// TODO: scan for old IMAP4 channels
[self debugWithFormat:@"should collect IMAP4 channels (%d active)",
SOGoMailConnectionEntry *entry;
NGImap4Client *client;
NSDictionary *result;
- NSString *cacheKey;
if (_url == nil)
return nil;
- cacheKey = [self cacheKeyForURL:_url];
+ /* check connection pool */
- if ((entry = [self->urlToEntry objectForKey:cacheKey]) != nil) {
+ if ((entry = [self entryForURL:_url]) != nil) {
if ([entry isValidPassword:_pwd]) {
[self debugWithFormat:@"reused IMAP4 connection for URL: %@", _url];
return [entry client];
entry = nil;
}
+ /* setup connection and attempt login */
+
if ((client = [NGImap4Client clientWithURL:_url]) == nil)
return nil;
[self logWithFormat:@"ERROR: IMAP4 login failed!"];
return nil;
}
-
+
[self debugWithFormat:@"created new IMAP4 connection for URL: %@", _url];
+
+ /* cache connection in pool */
+
entry = [[SOGoMailConnectionEntry alloc] initWithClient:client
password:_pwd];
- [self->urlToEntry setObject:entry forKey:cacheKey];
+ [self cacheEntry:entry forURL:_url];
[entry release]; entry = nil;
+
return client;
}
/* folder hierarchy */
- (NSArray *)_getDirectChildren:(NSArray *)_array folderName:(NSString *)_fn {
- // TODO: we should get the full list of folders _once_ and work on that
- // (we could cache it in the context)
NSMutableArray *ma;
unsigned i, count, prefixlen;
p = [_array objectAtIndex:i];
if ([p length] <= prefixlen)
continue;
+ if (![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;
[self imap4Separator]];
}
+- (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 ([folderName hasPrefix:@"/"])
+ folderName = [folderName substringFromIndex:1];
+
+ result = [_result valueForKey:@"list"];
+
+ /* Cyrus already tells us whether we need to check for children */
+ flags = [result objectForKey:folderName];
+ if ([flags containsObject:@"hasnochildren"])
+ return nil;
+
+ names = [self _getDirectChildren:[result allKeys] folderName:folderName];
+#if 0
+ [self debugWithFormat:@"subfolders of %@: %@", folderName,
+ [names componentsJoinedByString:@","]];
+#endif
+ return names;
+}
+
- (NSArray *)subfoldersForURL:(NSURL *)_url password:(NSString *)_pwd {
// TODO: add caching
+ SOGoMailConnectionEntry *entry;
NGImap4Client *client;
NSDictionary *result;
- NSString *folderName;
- if ((client = [self imap4ClientForURL:_url password:_pwd]) == nil)
+ /* check cache */
+
+ if ((entry = [self entryForURL:_url]) != nil) {
+ if ([entry isValidPassword:_pwd]) {
+ NSDictionary *allFolders;
+
+ [self debugWithFormat:@"valid password, reusing folder cache .."];
+ if ((allFolders = [entry cachedHierarchyResults]) != nil)
+ return [self extractSubfoldersForURL:_url fromResultSet:allFolders];
+
+ [self debugWithFormat:@" no folders cached yet .."];
+ }
+
+ /* different password, password could have changed! */
+ entry = nil;
+ }
+ else
+ [self debugWithFormat:@"no connection cached yet for url: %@", _url];
+
+ /* get client */
+
+ client = [entry isValidPassword:_pwd]
+ ? [entry client]
+ : [self imap4ClientForURL:_url password:_pwd];
+ if (client == nil)
return nil;
- folderName = [self imap4FolderNameForURL:_url];
+ /* fetch _all_ folders */
- /* maybe we want to use a cache over here */
- result = [client list:folderName pattern:@"*"];
+ result = [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];
+ [self debugWithFormat:@"cached results in entry %@: %@", entry, result];
+ }
+
/* extract list */
- result = [result valueForKey:@"list"];
- return [self _getDirectChildren:[result allKeys] folderName:folderName];
+
+ return [self extractSubfoldersForURL:_url fromResultSet:result];
}
/* debugging */
return self->creationTime;
}
+- (void)cacheHierarchyResults:(NSDictionary *)_hierarchy {
+ ASSIGNCOPY(self->subfolders, _hierarchy);
+}
+- (NSDictionary *)cachedHierarchyResults {
+ return self->subfolders;
+}
+
@end /* SOGoMailConnectionEntry */