From 1a47b783c883480137181da114513a6719ec3101 Mon Sep 17 00:00:00 2001 From: helge Date: Tue, 5 Oct 2004 22:26:01 +0000 Subject: [PATCH] added uid caching to mailer manager git-svn-id: http://svn.opengroupware.org/SOGo/trunk@364 d1b88da0-ebda-0310-925b-ed51d893ca5b --- SOGo/SoObjects/Mailer/ChangeLog | 13 ++ SOGo/SoObjects/Mailer/GNUmakefile | 17 +- .../Mailer/SOGoMailConnectionEntry.h | 68 +++++++ .../Mailer/SOGoMailConnectionEntry.m | 98 +++++++++ SOGo/SoObjects/Mailer/SOGoMailFolder.m | 1 - SOGo/SoObjects/Mailer/SOGoMailManager.h | 2 +- SOGo/SoObjects/Mailer/SOGoMailManager.m | 191 +++++++----------- SOGo/SoObjects/Mailer/Version | 2 +- 8 files changed, 266 insertions(+), 126 deletions(-) create mode 100644 SOGo/SoObjects/Mailer/SOGoMailConnectionEntry.h create mode 100644 SOGo/SoObjects/Mailer/SOGoMailConnectionEntry.m diff --git a/SOGo/SoObjects/Mailer/ChangeLog b/SOGo/SoObjects/Mailer/ChangeLog index ac1e4187..fd6f649b 100644 --- a/SOGo/SoObjects/Mailer/ChangeLog +++ b/SOGo/SoObjects/Mailer/ChangeLog @@ -1,3 +1,16 @@ +2004-10-06 Helge Hess + + * v0.9.26 + + * SOGoMailConnectionEntry.m: added caching of sorted UIDs sets + + * SOGoMailManager.m: moved SOGoMailConnectionEntry class to own file + +2004-10-05 Helge Hess + + * SOGoMailManager: removed range argument from -fetchUIDsInURL:.., + since IMAP4 doesn't support ranges anyway ... (v0.9.25) + 2004-10-04 Helge Hess * SOGoMailBodyPart.m: improved MIME type generation (v0.9.24) diff --git a/SOGo/SoObjects/Mailer/GNUmakefile b/SOGo/SoObjects/Mailer/GNUmakefile index a372e001..2dc7d244 100644 --- a/SOGo/SoObjects/Mailer/GNUmakefile +++ b/SOGo/SoObjects/Mailer/GNUmakefile @@ -7,16 +7,17 @@ BUNDLE_NAME = Mailer Mailer_PRINCIPAL_CLASS = SOGoMailerProduct Mailer_OBJC_FILES += \ - Product.m \ + Product.m \ \ - SOGoMailManager.m \ + SOGoMailManager.m \ + SOGoMailConnectionEntry.m \ \ - SOGoMailBaseObject.m \ - SOGoMailAccounts.m \ - SOGoMailAccount.m \ - SOGoMailFolder.m \ - SOGoMailObject.m \ - SOGoMailBodyPart.m \ + SOGoMailBaseObject.m \ + SOGoMailAccounts.m \ + SOGoMailAccount.m \ + SOGoMailFolder.m \ + SOGoMailObject.m \ + SOGoMailBodyPart.m \ Mailer_RESOURCE_FILES += \ Version \ diff --git a/SOGo/SoObjects/Mailer/SOGoMailConnectionEntry.h b/SOGo/SoObjects/Mailer/SOGoMailConnectionEntry.h new file mode 100644 index 00000000..69970fb8 --- /dev/null +++ b/SOGo/SoObjects/Mailer/SOGoMailConnectionEntry.h @@ -0,0 +1,68 @@ +/* + 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 __SOGo_SOGoMailConnectionEntry_H__ +#define __SOGo_SOGoMailConnectionEntry_H__ + +#import + +/* + SOGoMailConnectionEntry + + A cached connection to an IMAP4 server plus some cached objects. +*/ + +@class NSString, NSDate, NSArray, NSDictionary, NSURL; +@class NGImap4Client; + +@interface SOGoMailConnectionEntry : NSObject +{ +@public + NGImap4Client *client; + NSString *password; + NSDate *creationTime; + NSDictionary *subfolders; + + /* uids cache */ + NSArray *cachedUIDs; + NSURL *uidFolderURL; + id uidSortOrdering; +} + +- (id)initWithClient:(NGImap4Client *)_client password:(NSString *)_pwd; + +/* accessors */ + +- (NGImap4Client *)client; +- (BOOL)isValidPassword:(NSString *)_pwd; + +- (NSDate *)creationTime; + +- (void)cacheHierarchyResults:(NSDictionary *)_hierarchy; +- (NSDictionary *)cachedHierarchyResults; + +- (id)cachedUIDsForURL:(NSURL *)_url qualifier:(id)_q sortOrdering:(id)_so; +- (void)cacheUIDs:(NSArray *)_uids forURL:(NSURL *)_url + qualifier:(id)_q sortOrdering:(id)_so; + +@end + +#endif /* __SOGo_SOGoMailConnectionEntry_H__ */ diff --git a/SOGo/SoObjects/Mailer/SOGoMailConnectionEntry.m b/SOGo/SoObjects/Mailer/SOGoMailConnectionEntry.m new file mode 100644 index 00000000..bb2d486b --- /dev/null +++ b/SOGo/SoObjects/Mailer/SOGoMailConnectionEntry.m @@ -0,0 +1,98 @@ +/* + 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 "SOGoMailConnectionEntry.h" +#include "common.h" + +@implementation SOGoMailConnectionEntry + +- (id)initWithClient:(NGImap4Client *)_client password:(NSString *)_pwd { + if (_client == nil || _pwd == nil) { + [self release]; + return nil; + } + + if ((self = [super init])) { + self->client = [_client retain]; + self->password = [_pwd copy]; + + self->creationTime = [[NSDate alloc] init]; + } + return self; +} +- (id)init { + return [self initWithClient:nil password:nil]; +} + +- (void)dealloc { + [self->cachedUIDs release]; + [self->uidFolderURL release]; + [self->uidSortOrdering release]; + [self->creationTime release]; + [self->subfolders release]; + [self->password release]; + [self->client release]; + [super dealloc]; +} + +/* accessors */ + +- (NGImap4Client *)client { + return self->client; +} +- (BOOL)isValidPassword:(NSString *)_pwd { + return [self->password isEqualToString:_pwd]; +} + +- (NSDate *)creationTime { + return self->creationTime; +} + +- (void)cacheHierarchyResults:(NSDictionary *)_hierarchy { + ASSIGNCOPY(self->subfolders, _hierarchy); +} +- (NSDictionary *)cachedHierarchyResults { + return self->subfolders; +} + +- (id)cachedUIDsForURL:(NSURL *)_url qualifier:(id)_q sortOrdering:(id)_so { + if (_q != nil) + return nil; + if (![_so isEqual:self->uidSortOrdering]) + return nil; + if (![self->uidFolderURL isEqual:_url]) + return nil; + + return self->cachedUIDs; +} + +- (void)cacheUIDs:(NSArray *)_uids forURL:(NSURL *)_url + qualifier:(id)_q sortOrdering:(id)_so +{ + if (_q != nil) + return; + + ASSIGNCOPY(self->uidSortOrdering, _so); + ASSIGNCOPY(self->uidFolderURL, _url); + ASSIGNCOPY(self->cachedUIDs, _uids); +} + +@end /* SOGoMailConnectionEntry */ diff --git a/SOGo/SoObjects/Mailer/SOGoMailFolder.m b/SOGo/SoObjects/Mailer/SOGoMailFolder.m index 5025b049..b158dc5f 100644 --- a/SOGo/SoObjects/Mailer/SOGoMailFolder.m +++ b/SOGo/SoObjects/Mailer/SOGoMailFolder.m @@ -45,7 +45,6 @@ - (NSArray *)fetchUIDsMatchingQualifier:(id)_q sortOrdering:(id)_so { return [[self mailManager] fetchUIDsInURL:[self imap4URL] qualifier:_q sortOrdering:_so - range:NSMakeRange(0, 100000) password:[self imap4Password]]; } diff --git a/SOGo/SoObjects/Mailer/SOGoMailManager.h b/SOGo/SoObjects/Mailer/SOGoMailManager.h index 36a40994..3eb874a2 100644 --- a/SOGo/SoObjects/Mailer/SOGoMailManager.h +++ b/SOGo/SoObjects/Mailer/SOGoMailManager.h @@ -55,7 +55,7 @@ /* messages */ - (NSArray *)fetchUIDsInURL:(NSURL *)_url qualifier:(id)_q - sortOrdering:(id)_so range:(NSRange)_range password:(NSString *)_pwd; + sortOrdering:(id)_so password:(NSString *)_pwd; - (NSArray *)fetchUIDs:(NSArray *)_uids inURL:(NSURL *)_url parts:(NSArray *)_parts password:(NSString *)_pwd; diff --git a/SOGo/SoObjects/Mailer/SOGoMailManager.m b/SOGo/SoObjects/Mailer/SOGoMailManager.m index 7f46fce6..b3c507cf 100644 --- a/SOGo/SoObjects/Mailer/SOGoMailManager.m +++ b/SOGo/SoObjects/Mailer/SOGoMailManager.m @@ -20,30 +20,10 @@ */ #include "SOGoMailManager.h" +#include "SOGoMailConnectionEntry.h" #include "common.h" -@interface SOGoMailConnectionEntry : NSObject -{ -@public - NGImap4Client *client; - NSString *password; - NSDate *creationTime; - NSDictionary *subfolders; -} - -- (id)initWithClient:(NGImap4Client *)_client password:(NSString *)_pwd; - -/* accessors */ - -- (NGImap4Client *)client; -- (BOOL)isValidPassword:(NSString *)_pwd; - -- (NSDate *)creationTime; - -- (void)cacheHierarchyResults:(NSDictionary *)_hierarchy; -- (NSDictionary *)cachedHierarchyResults; - -@end +// TODO: need a way to refresh caches on get mail! @implementation SOGoMailManager @@ -119,6 +99,46 @@ static NSTimeInterval PoolScanInterval = 5 * 60; [self->urlToEntry count]]; } +- (id)entryForURL:(NSURL *)_url password:(NSString *)_pwd { + /* + Three cases: + a) not yet connected => create new entry and connect + b) connected, correct password => return cached entry + c) connected, different password => try to recreate entry + */ + SOGoMailConnectionEntry *entry; + NGImap4Client *client; + + /* check cache */ + + if ((entry = [self entryForURL:_url]) != nil) { + if ([entry isValidPassword:_pwd]) { + if (debugCache) + [self logWithFormat:@"valid password, reusing cache entry ..."]; + return entry; + } + + /* different password, password could have changed! */ + if (debugCache) + [self logWithFormat:@"different password than cached entry: %@", _url]; + entry = nil; + } + else + [self debugWithFormat:@"no connection cached yet for url: %@", _url]; + + /* try to login */ + + client = [entry isValidPassword:_pwd] + ? [entry client] + : [self imap4ClientForURL:_url password:_pwd]; + + if (client == nil) + return nil; + + /* sideeffect of -imap4ClientForURL:password: is to create a cache entry */ + return [self entryForURL:_url]; +} + /* client object */ - (NGImap4Client *)imap4ClientForURL:(NSURL *)_url password:(NSString *)_pwd { @@ -273,43 +293,26 @@ static NSTimeInterval PoolScanInterval = 5 * 60; - (NSArray *)subfoldersForURL:(NSURL *)_url password:(NSString *)_pwd { // TODO: add caching SOGoMailConnectionEntry *entry; - NGImap4Client *client; NSDictionary *result; if (debugKeys) [self debugWithFormat:@"subfolders for URL: %@ ...",[_url absoluteString]]; - /* check cache */ - - if ((entry = [self entryForURL:_url]) != nil) { - if ([entry isValidPassword:_pwd]) { - NSDictionary *allFolders; - - if (debugCache) - [self logWithFormat:@"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 */ + /* check connection cache */ - client = [entry isValidPassword:_pwd] - ? [entry client] - : [self imap4ClientForURL:_url password:_pwd]; - if (client == nil) + 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 = [client list:@"INBOX" pattern:@"*"]; + result = [[entry client] list:@"INBOX" pattern:@"*"]; if (![[result valueForKey:@"result"] boolValue]) { [self logWithFormat:@"ERROR: listing of folder failed!"]; return nil; @@ -336,25 +339,37 @@ static NSTimeInterval PoolScanInterval = 5 * 60; /* messages */ - (NSArray *)fetchUIDsInURL:(NSURL *)_url qualifier:(id)_qualifier - sortOrdering:(id)_so range:(NSRange)_range password:(NSString *)_pwd + sortOrdering:(id)_so password:(NSString *)_pwd { /* sortOrdering can be an NSString, an EOSortOrdering or an array of EOS. */ - NGImap4Client *client; + SOGoMailConnectionEntry *entry; NSDictionary *result; NSArray *uids; - if ((client = [self imap4ClientForURL:_url password:_pwd]) == nil) + /* check connection cache */ + + if ((entry = [self entryForURL:_url password:_pwd]) == nil) return nil; - result = [client select:[self imap4FolderNameForURL:_url]]; + /* check cache */ + + uids = [entry cachedUIDsForURL:_url qualifier:_qualifier sortOrdering:_so]; + if (uids != nil) { + [self logWithFormat:@"REUSE UID CACHE!"]; + return [uids isNotNull] ? uids : nil; + } + + /* select folder and fetch */ + + result = [[entry client] select:[self imap4FolderNameForURL:_url]]; if (![[result valueForKey:@"result"] boolValue]) { [self logWithFormat:@"ERROR: could not select URL: %@: %@", _url, result]; return nil; } - result = [client sort:_so qualifier:_qualifier encoding:@"UTF-8"]; + result = [[entry client] sort:_so qualifier:_qualifier encoding:@"UTF-8"]; if (![[result valueForKey:@"result"] boolValue]) { [self logWithFormat:@"ERROR: could not sort contents of URL: %@", _url]; return nil; @@ -366,14 +381,9 @@ static NSTimeInterval PoolScanInterval = 5 * 60; return nil; } - // TODO: improve to use a single range call? - if (_range.location > 0) { - uids = [uids subarrayWithRange:NSMakeRange(_range.location, - [uids count]-_range.location)]; - } - if ([uids count] > _range.length && _range.length != 0) - uids = [uids subarrayWithRange:NSMakeRange(0, _range.length)]; + /* cache */ + [entry cacheUIDs:uids forURL:_url qualifier:_qualifier sortOrdering:_so]; return uids; } @@ -416,10 +426,10 @@ static NSTimeInterval PoolScanInterval = 5 * 60; /* fetch parts */ -#warning 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" + // 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]) { @@ -427,6 +437,7 @@ static NSTimeInterval PoolScanInterval = 5 * 60; [_uids count],_url]; return nil; } + //[self logWithFormat:@"RESULT: %@", result]; return (id)result; } @@ -510,53 +521,3 @@ static NSTimeInterval PoolScanInterval = 5 * 60; } @end /* SOGoMailManager */ - -@implementation SOGoMailConnectionEntry - -- (id)initWithClient:(NGImap4Client *)_client password:(NSString *)_pwd { - if (_client == nil || _pwd == nil) { - [self release]; - return nil; - } - - if ((self = [super init])) { - self->client = [_client retain]; - self->password = [_pwd copy]; - - self->creationTime = [[NSDate alloc] init]; - } - return self; -} -- (id)init { - return [self initWithClient:nil password:nil]; -} - -- (void)dealloc { - [self->creationTime release]; - [self->subfolders release]; - [self->password release]; - [self->client release]; - [super dealloc]; -} - -/* accessors */ - -- (NGImap4Client *)client { - return self->client; -} -- (BOOL)isValidPassword:(NSString *)_pwd { - return [self->password isEqualToString:_pwd]; -} - -- (NSDate *)creationTime { - return self->creationTime; -} - -- (void)cacheHierarchyResults:(NSDictionary *)_hierarchy { - ASSIGNCOPY(self->subfolders, _hierarchy); -} -- (NSDictionary *)cachedHierarchyResults { - return self->subfolders; -} - -@end /* SOGoMailConnectionEntry */ diff --git a/SOGo/SoObjects/Mailer/Version b/SOGo/SoObjects/Mailer/Version index a281cca1..9c8dad69 100644 --- a/SOGo/SoObjects/Mailer/Version +++ b/SOGo/SoObjects/Mailer/Version @@ -1,3 +1,3 @@ # $Id$ -SUBMINOR_VERSION:=24 +SUBMINOR_VERSION:=26 -- 2.39.5