From 09a3866d02aaf56d4f35a1e6623fe071172c2d24 Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Fri, 27 Jun 2008 14:49:28 +0000 Subject: [PATCH 01/18] Monotone-Parent: 2b4777c3ca432b7b604c9d23a47dae304ddd83b7 Monotone-Revision: 5092b7192c90ae7b78ecc0442ac5ec1841d30a8d Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2008-06-27T14:49:28 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 10 ++ SoObjects/Appointments/SOGoFreeBusyObject.m | 185 +++++++++++--------- 2 files changed, 109 insertions(+), 86 deletions(-) diff --git a/ChangeLog b/ChangeLog index aef19faa4..1ace0efc7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,13 @@ +2008-06-27 Wolfgang Sourdeau + + * SoObjects/Appointments/SOGoFreeBusyObject.m ([SOGoFreeBusyObject + -contentAsString]): add the ending interval to "today" instead of + to the start date. + ([SOGoFreeBusyObject + -contentAsStringWithMethod:methodfrom:_startDateto:_endDate]): new + method that accepts a method parameter to specify in the returned + iCalendar object. + 2008-06-23 Wolfgang Sourdeau * SoObjects/SOGo/SOGoObject.m ([SOGoObject -labelForKey:key]): diff --git a/SoObjects/Appointments/SOGoFreeBusyObject.m b/SoObjects/Appointments/SOGoFreeBusyObject.m index 3bdd2f25d..c6681983a 100644 --- a/SoObjects/Appointments/SOGoFreeBusyObject.m +++ b/SoObjects/Appointments/SOGoFreeBusyObject.m @@ -71,6 +71,89 @@ static unsigned int freebusyRangeEnd = 0; } } +- (iCalPerson *) iCalPersonWithUID: (NSString *) uid +{ + iCalPerson *person; + LDAPUserManager *um; + NSDictionary *contactInfos; + + um = [LDAPUserManager sharedUserManager]; + contactInfos = [um contactInfosForUserWithUIDorEmail: uid]; + + person = [iCalPerson new]; + [person autorelease]; + [person setCn: [contactInfos objectForKey: @"cn"]]; + [person setEmail: [contactInfos objectForKey: @"c_email"]]; + + return person; +} + +/* Private API */ +- (iCalFreeBusyType) _fbTypeForEventStatus: (NSNumber *) eventStatus +{ + unsigned int status; + iCalFreeBusyType fbType; + + status = [eventStatus unsignedIntValue]; + if (status == 0) + fbType = iCalFBBusyTentative; + else if (status == 1) + fbType = iCalFBBusy; + else + fbType = iCalFBFree; + + return fbType; +} + +- (NSString *) iCalStringForFreeBusyInfos: (NSArray *) _infos + withMethod: (NSString *) method + from: (NSCalendarDate *) _startDate + to: (NSCalendarDate *) _endDate +{ + NSString *uid; + NSEnumerator *events; + iCalCalendar *calendar; + iCalFreeBusy *freebusy; + NSDictionary *info; + iCalFreeBusyType type; + + uid = [container ownerInContext: context]; + + calendar = [iCalCalendar groupWithTag: @"vcalendar"]; + [calendar setProdID: @"//Inverse groupe conseil/SOGo 0.9"]; + [calendar setVersion: @"2.0"]; + if (method) + [calendar setMethod: method]; + + freebusy = [iCalFreeBusy groupWithTag: @"vfreebusy"]; + [freebusy addToAttendees: [self iCalPersonWithUID: uid]]; + [freebusy setTimeStampAsDate: [NSCalendarDate calendarDate]]; + [freebusy setStartDate: _startDate]; + [freebusy setEndDate: _endDate]; + + /* ORGANIZER - strictly required but missing for now */ + + /* ATTENDEE */ +// person = [self iCalPersonWithUid: uid]; +// [person setTag: @"ATTENDEE"]; +// [ms appendString: [person versitString]]; + + /* FREEBUSY */ + events = [_infos objectEnumerator]; + while ((info = [events nextObject])) + if ([[info objectForKey: @"c_isopaque"] boolValue]) + { + type = [self _fbTypeForEventStatus: [info objectForKey: @"c_status"]]; + [freebusy addFreeBusyFrom: [info objectForKey: @"startDate"] + to: [info objectForKey: @"endDate"] + type: type]; + } + + [calendar setUniqueChild: freebusy]; + + return [calendar versitString]; +} + - (NSString *) contentAsString { NSCalendarDate *today, *startDate, *endDate; @@ -82,20 +165,30 @@ static unsigned int freebusyRangeEnd = 0; startDate = [today dateByAddingYears: 0 months: 0 days: -freebusyRangeStart hours: 0 minutes: 0 seconds: 0]; - endDate = [startDate dateByAddingYears: 0 months: 0 days: freebusyRangeEnd - hours: 0 minutes: 0 seconds: 0]; + endDate = [today dateByAddingYears: 0 months: 0 days: freebusyRangeEnd + hours: 0 minutes: 0 seconds: 0]; return [self contentAsStringFrom: startDate to: endDate]; } -- (NSString *) contentAsStringFrom: (NSCalendarDate *) _startDate - to: (NSCalendarDate *) _endDate +- (NSString *) contentAsStringWithMethod: (NSString *) method + from: (NSCalendarDate *) _startDate + to: (NSCalendarDate *) _endDate { NSArray *infos; - infos = [self fetchFreeBusyInfosFrom:_startDate to:_endDate]; + infos = [self fetchFreeBusyInfosFrom: _startDate to: _endDate]; - return [self iCalStringForFreeBusyInfos:infos from:_startDate to:_endDate]; + return [self iCalStringForFreeBusyInfos: infos withMethod: method + from: _startDate to: _endDate]; +} + +- (NSString *) contentAsStringFrom: (NSCalendarDate *) _startDate + to: (NSCalendarDate *) _endDate +{ + return [self contentAsStringWithMethod: nil + from: _startDate + to: _endDate]; } - (NSArray *) fetchFreeBusyInfosFrom: (NSCalendarDate *) startDate @@ -130,86 +223,6 @@ static unsigned int freebusyRangeEnd = 0; return [self contentAsString]; } -/* Private API */ -- (iCalFreeBusyType) _fbTypeForEventStatus: (NSNumber *) eventStatus -{ - unsigned int status; - iCalFreeBusyType fbType; - - status = [eventStatus unsignedIntValue]; - if (status == 0) - fbType = iCalFBBusyTentative; - else if (status == 1) - fbType = iCalFBBusy; - else - fbType = iCalFBFree; - - return fbType; -} - -- (iCalPerson *) iCalPersonWithUID: (NSString *) uid -{ - iCalPerson *person; - LDAPUserManager *um; - NSDictionary *contactInfos; - - um = [LDAPUserManager sharedUserManager]; - contactInfos = [um contactInfosForUserWithUIDorEmail: uid]; - - person = [iCalPerson new]; - [person autorelease]; - [person setCn: [contactInfos objectForKey: @"cn"]]; - [person setEmail: [contactInfos objectForKey: @"c_email"]]; - - return person; -} - -- (NSString *) iCalStringForFreeBusyInfos: (NSArray *) _infos - from: (NSCalendarDate *) _startDate - to: (NSCalendarDate *) _endDate -{ - NSString *uid; - NSEnumerator *events; - iCalCalendar *calendar; - iCalFreeBusy *freebusy; - NSDictionary *info; - iCalFreeBusyType type; - - uid = [container ownerInContext: context]; - - calendar = [iCalCalendar groupWithTag: @"vcalendar"]; - [calendar setProdID: @"//Inverse groupe conseil/SOGo 0.9"]; - [calendar setVersion: @"2.0"]; - - freebusy = [iCalFreeBusy groupWithTag: @"vfreebusy"]; - [freebusy addToAttendees: [self iCalPersonWithUID: uid]]; - [freebusy setTimeStampAsDate: [NSCalendarDate calendarDate]]; - [freebusy setStartDate: _startDate]; - [freebusy setEndDate: _endDate]; - - /* ORGANIZER - strictly required but missing for now */ - - /* ATTENDEE */ -// person = [self iCalPersonWithUid: uid]; -// [person setTag: @"ATTENDEE"]; -// [ms appendString: [person versitString]]; - - /* FREEBUSY */ - events = [_infos objectEnumerator]; - while ((info = [events nextObject])) - if ([[info objectForKey: @"c_isopaque"] boolValue]) - { - type = [self _fbTypeForEventStatus: [info objectForKey: @"c_status"]]; - [freebusy addFreeBusyFrom: [info objectForKey: @"startDate"] - to: [info objectForKey: @"endDate"] - type: type]; - } - - [calendar setUniqueChild: freebusy]; - - return [calendar versitString]; -} - /* deliver content without need for view method */ - (id) GETAction: (id)_ctx From 975f39355e95e5bf50a6dad5fa43b94bc0196db0 Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Mon, 30 Jun 2008 14:08:10 +0000 Subject: [PATCH 02/18] Monotone-Parent: 5092b7192c90ae7b78ecc0442ac5ec1841d30a8d Monotone-Revision: 644595c11e58a7c604c223b28fa3c197272777e2 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2008-06-30T14:08:10 Monotone-Branch: ca.inverse.sogo --- SOPE/GDLContentStore/ChangeLog | 5 +++++ SOPE/GDLContentStore/EOAdaptorChannel+GCS.h | 3 ++- SOPE/GDLContentStore/EOAdaptorChannel+GCS.m | 17 ++++++++++++++++- 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/SOPE/GDLContentStore/ChangeLog b/SOPE/GDLContentStore/ChangeLog index bc33ea28d..2404379eb 100644 --- a/SOPE/GDLContentStore/ChangeLog +++ b/SOPE/GDLContentStore/ChangeLog @@ -1,3 +1,8 @@ +2008-06-30 Wolfgang Sourdeau + + * EOAdaptorChannel+GCS.m ([EOAdaptorChannel + -dropTables:tableNames]): new method that drops a list of tables. + 2008-06-23 Wolfgang Sourdeau * GCSFolderManager.m ([GCSFolderManager diff --git a/SOPE/GDLContentStore/EOAdaptorChannel+GCS.h b/SOPE/GDLContentStore/EOAdaptorChannel+GCS.h index da28e551a..4adaacc2f 100644 --- a/SOPE/GDLContentStore/EOAdaptorChannel+GCS.h +++ b/SOPE/GDLContentStore/EOAdaptorChannel+GCS.h @@ -33,7 +33,8 @@ @interface EOAdaptorChannel(GCS) -- (BOOL)tableExistsWithName:(NSString *)_tableName; +- (BOOL) tableExistsWithName: (NSString *) _tableName; +- (void) dropTables: (NSArray *) tableNames; @end diff --git a/SOPE/GDLContentStore/EOAdaptorChannel+GCS.m b/SOPE/GDLContentStore/EOAdaptorChannel+GCS.m index 23e156176..dca32501d 100644 --- a/SOPE/GDLContentStore/EOAdaptorChannel+GCS.m +++ b/SOPE/GDLContentStore/EOAdaptorChannel+GCS.m @@ -24,7 +24,8 @@ @implementation EOAdaptorChannel(GCS) -- (BOOL)tableExistsWithName:(NSString *)_tableName { +- (BOOL) tableExistsWithName: (NSString *) _tableName +{ NSException *ex; NSString *sql; BOOL didOpen; @@ -47,4 +48,18 @@ return ex != nil ? NO : YES; } +- (void) dropTables: (NSArray *) tableNames +{ + unsigned int count, max; + NSString *sql; + + max = [tableNames count]; + for (count = 0; count < max; count++) + { + sql = [NSString stringWithFormat: @"DROP TABLE %@", + [tableNames objectAtIndex: count]]; + [self evaluateExpressionX: sql]; + } +} + @end /* EOAdaptorChannel(GCS) */ From e389f53590bccb47971081c017e967b540289b39 Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Mon, 30 Jun 2008 14:10:45 +0000 Subject: [PATCH 03/18] Monotone-Parent: 644595c11e58a7c604c223b28fa3c197272777e2 Monotone-Revision: f6d0d3d72013771c7544d3498ed9510e4a4a9ff2 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2008-06-30T14:10:45 Monotone-Branch: ca.inverse.sogo --- SOPE/GDLContentStore/ChangeLog | 6 ++++++ SOPE/GDLContentStore/GCSFolder.m | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/SOPE/GDLContentStore/ChangeLog b/SOPE/GDLContentStore/ChangeLog index 2404379eb..02d175214 100644 --- a/SOPE/GDLContentStore/ChangeLog +++ b/SOPE/GDLContentStore/ChangeLog @@ -1,5 +1,11 @@ 2008-06-30 Wolfgang Sourdeau + * GCSFolder.m ([GCSFolder -recordOfEntryWithName:name]) + ([GCSFolder -writeContent:_contenttoName:_namebaseVersion:_baseVersion]) + ([GCSFolder -fetchFields:fieldsfetchSpecification:spec]): don't + use the "as" keyword when specifying multiple tables, so that + Oracle doesn't choke on the queries. + * EOAdaptorChannel+GCS.m ([EOAdaptorChannel -dropTables:tableNames]): new method that drops a list of tables. diff --git a/SOPE/GDLContentStore/GCSFolder.m b/SOPE/GDLContentStore/GCSFolder.m index 544381f82..2a1e87b1b 100644 --- a/SOPE/GDLContentStore/GCSFolder.m +++ b/SOPE/GDLContentStore/GCSFolder.m @@ -449,7 +449,7 @@ static NSArray *contentFieldNames = nil; [sql appendString: @"*"]; [sql appendString:@" FROM "]; if (requirement == bothTableRequired) - [sql appendFormat: @"%@ as a, %@ as b", + [sql appendFormat: @"%@ a, %@ b", [self quickTableName], [self storeTableName]]; else { From 7c2be6f0d31f156faae0d3106758306c7c01f73b Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Mon, 30 Jun 2008 14:12:10 +0000 Subject: [PATCH 04/18] Monotone-Parent: f6d0d3d72013771c7544d3498ed9510e4a4a9ff2 Monotone-Revision: 597389f8260d0d6fdfa812be82b4f10f924e5c8b Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2008-06-30T14:12:10 Monotone-Branch: ca.inverse.sogo --- SOPE/GDLContentStore/ChangeLog | 5 + SOPE/GDLContentStore/GCSFolderManager.m | 489 +++++++++++++----------- 2 files changed, 265 insertions(+), 229 deletions(-) diff --git a/SOPE/GDLContentStore/ChangeLog b/SOPE/GDLContentStore/ChangeLog index 02d175214..457007ce3 100644 --- a/SOPE/GDLContentStore/ChangeLog +++ b/SOPE/GDLContentStore/ChangeLog @@ -1,5 +1,10 @@ 2008-06-30 Wolfgang Sourdeau + * GCSFolderManager.m ([GCSFolderManager + -createFolderOfType:_typewithName:_nameatPath:_path]): wrap the + creation queries in a transaction, to ensure the creation of + tables is atomic. + * GCSFolder.m ([GCSFolder -recordOfEntryWithName:name]) ([GCSFolder -writeContent:_contenttoName:_namebaseVersion:_baseVersion]) ([GCSFolder -fetchFields:fieldsfetchSpecification:spec]): don't diff --git a/SOPE/GDLContentStore/GCSFolderManager.m b/SOPE/GDLContentStore/GCSFolderManager.m index c7ec78fc8..b5fc7ee2f 100644 --- a/SOPE/GDLContentStore/GCSFolderManager.m +++ b/SOPE/GDLContentStore/GCSFolderManager.m @@ -29,16 +29,17 @@ #import "EOAdaptorChannel+GCS.h" #import "common.h" #import +#import #import #import /* Required database schema: - - c_path - c_path1, path2, path3... [quickPathCount times] - c_foldername + + c_path + c_path1, path2, path3... [quickPathCount times] + c_foldername TODO: - add a local cache? @@ -68,12 +69,12 @@ static NSCharacterSet *asciiAlphaNumericCS = nil; NSUserDefaults *ud = [NSUserDefaults standardUserDefaults]; int seed; - seed = ([[NSDate date] timeIntervalSince1970] + seed = ((unsigned int) [[NSDate date] timeIntervalSince1970] + [[NSProcessInfo processInfo] processIdentifier]); srand (seed); - debugOn = [ud boolForKey:@"GCSFolderManagerDebugEnabled"]; - debugSQLGen = [ud boolForKey:@"GCSFolderManagerSQLDebugEnabled"]; + debugOn = [ud boolForKey: @"GCSFolderManagerDebugEnabled"]; + debugSQLGen = [ud boolForKey: @"GCSFolderManagerSQLDebugEnabled"]; emptyArray = [[NSArray alloc] init]; if (!asciiAlphaNumericCS) { @@ -112,7 +113,8 @@ static NSCharacterSet *asciiAlphaNumericCS = nil; return fm; } -- (NSDictionary *)loadDefaultFolderTypes { +- (NSDictionary *) loadDefaultFolderTypes +{ NSMutableDictionary *typeMap; NSArray *types; unsigned i, count; @@ -136,7 +138,7 @@ static NSCharacterSet *asciiAlphaNumericCS = nil; typeObject = [[GCSFolderType alloc] initWithFolderTypeName:type]; [self logWithFormat:@" %@: %s", - type, [typeObject isNotNull] ? "OK" : "FAIL"]; + type, [typeObject isNotNull] ? "OK" : "FAIL"]; [typeMap setObject:typeObject forKey:type]; [typeObject release]; } @@ -147,13 +149,13 @@ static NSCharacterSet *asciiAlphaNumericCS = nil; - (id)initWithFolderInfoLocation:(NSURL *)_url { if (_url == nil) { [self logWithFormat:@"ERROR(%s): missing folder info url!", - __PRETTY_FUNCTION__]; + __PRETTY_FUNCTION__]; [self release]; return nil; } if ((self = [super init])) { - self->channelManager = [[GCSChannelManager defaultChannelManager] retain]; - self->folderInfoLocation = [_url retain]; + channelManager = [[GCSChannelManager defaultChannelManager] retain]; + folderInfoLocation = [_url retain]; if ([[self folderInfoTableName] length] == 0) { [self logWithFormat:@"ERROR(%s): missing tablename in URL: %@", @@ -163,22 +165,23 @@ static NSCharacterSet *asciiAlphaNumericCS = nil; } /* register default folder types */ - self->nameToType = [[self loadDefaultFolderTypes] copy]; + nameToType = [[self loadDefaultFolderTypes] copy]; } return self; } -- (void)dealloc { - [self->nameToType release]; - [self->folderInfoLocation release]; - [self->channelManager release]; +- (void) dealloc +{ + [nameToType release]; + [folderInfoLocation release]; + [channelManager release]; [super dealloc]; } /* accessors */ - (NSURL *)folderInfoLocation { - return self->folderInfoLocation; + return folderInfoLocation; } - (NSString *)folderInfoTableName { @@ -188,14 +191,14 @@ static NSCharacterSet *asciiAlphaNumericCS = nil; /* connection */ - (GCSChannelManager *)channelManager { - return self->channelManager; + return channelManager; } - (EOAdaptorChannel *)acquireOpenChannel { EOAdaptorChannel *ch; ch = [[self channelManager] acquireOpenChannelForURL: - [self folderInfoLocation]]; + [self folderInfoLocation]]; return ch; } - (void)releaseChannel:(EOAdaptorChannel *)_channel { @@ -226,7 +229,7 @@ static NSCharacterSet *asciiAlphaNumericCS = nil; if ((ex = [channel evaluateExpressionX:_sql]) != nil) { [self logWithFormat:@"ERROR(%s): cannot execute\n SQL '%@':\n %@", - __PRETTY_FUNCTION__, _sql, ex]; + __PRETTY_FUNCTION__, _sql, ex]; [self releaseChannel:channel]; return nil; } @@ -256,14 +259,14 @@ static NSCharacterSet *asciiAlphaNumericCS = nil; folderTypeName = [_record objectForKey:@"c_folder_type"]; if (![folderTypeName isNotNull]) { [self logWithFormat:@"ERROR(%s): missing type in folder: %@", - __PRETTY_FUNCTION__, _record]; + __PRETTY_FUNCTION__, _record]; return nil; } if ((folderType = [self folderTypeWithName:folderTypeName]) == nil) { [self logWithFormat: @"ERROR(%s): could not resolve type '%@' of folder: %@", - __PRETTY_FUNCTION__, - folderTypeName, [_record valueForKey:@"c_path"]]; + __PRETTY_FUNCTION__, + folderTypeName, [_record valueForKey:@"c_path"]]; return nil; } @@ -277,7 +280,7 @@ static NSCharacterSet *asciiAlphaNumericCS = nil; : nil; if (location == nil) { [self logWithFormat:@"ERROR(%s): missing folder location in record: %@", - __PRETTY_FUNCTION__, _record]; + __PRETTY_FUNCTION__, _record]; return nil; } @@ -288,7 +291,7 @@ static NSCharacterSet *asciiAlphaNumericCS = nil; if (quickLocation == nil) { [self logWithFormat:@"WARNING(%s): missing quick location in record: %@", - __PRETTY_FUNCTION__, _record]; + __PRETTY_FUNCTION__, _record]; } locationString = [_record objectForKey:@"c_acl_location"]; @@ -308,7 +311,7 @@ static NSCharacterSet *asciiAlphaNumericCS = nil; /* path SQL */ - (NSString *)generateSQLWhereForInternalNames:(NSArray *)_names - exactMatch:(BOOL)_beExact orDirectSubfolderMatch:(BOOL)_directSubs + exactMatch:(BOOL)_beExact orDirectSubfolderMatch:(BOOL)_directSubs { /* generates a WHERE qualifier for matching the "quick" entries */ NSMutableString *sql; @@ -316,7 +319,7 @@ static NSCharacterSet *asciiAlphaNumericCS = nil; if ((count = [_names count]) == 0) { [self debugWithFormat:@"WARNING(%s): passed in empty name array!", - __PRETTY_FUNCTION__]; + __PRETTY_FUNCTION__]; return @"1 = 2"; } @@ -374,7 +377,7 @@ static NSCharacterSet *asciiAlphaNumericCS = nil; } - (NSString *)generateSQLPathFetchForInternalNames:(NSArray *)_names - exactMatch:(BOOL)_beExact orDirectSubfolderMatch:(BOOL)_directSubs + exactMatch:(BOOL)_beExact orDirectSubfolderMatch:(BOOL)_directSubs { /* fetches the 'path' subset for a given quick-names */ NSMutableString *sql; @@ -404,109 +407,142 @@ static NSCharacterSet *asciiAlphaNumericCS = nil; return YES; } -- (NSString *)internalNameFromPath:(NSString *)_path { +- (NSString *) internalNameFromPath: (NSString *) _path +{ + NSString *name; + // TODO: ensure proper path and SQL escaping! - - if (![self _isStandardizedPath:_path]) { - [self debugWithFormat:@"%s: not a standardized path: '%@'", + + if ([self _isStandardizedPath:_path]) + { + if ([_path hasSuffix:@"/"] && [_path length] > 1) + name = [_path substringToIndex: ([_path length] - 1)]; + else + name = _path; + } + else + { + [self debugWithFormat:@"%s: not a standardized path: '%@'", __PRETTY_FUNCTION__, _path]; - return nil; - } + name = nil; + } - if ([_path hasSuffix:@"/"] && [_path length] > 1) - _path = [_path substringToIndex:([_path length] - 1)]; - - return _path; + return name; } -- (NSArray *)internalNamesFromPath:(NSString *)_path { + +- (NSArray *) internalNamesFromPath: (NSString *) _path +{ NSString *fname; NSArray *fnames; - - if ((fname = [self internalNameFromPath:_path]) == nil) - return nil; - - if ([fname hasPrefix:@"/"]) - fname = [fname substringFromIndex:1]; - - fnames = [fname componentsSeparatedByString:@"/"]; - if ([fnames count] == 0) - return nil; + + fname = [self internalNameFromPath: _path]; + if (fname) + { + if ([fname hasPrefix:@"/"]) + fname = [fname substringFromIndex: 1]; + fnames = [fname componentsSeparatedByString:@"/"]; + if ([fnames count] == 0) + fnames = nil; + } + else + fnames = nil; return fnames; } -- (NSString *)pathFromInternalName:(NSString *)_name { + +- (NSString *) pathFromInternalName: (NSString *) _name +{ /* for incomplete pathes, like '/Users/helge/' */ return _name; } -- (NSString *)pathPartFromInternalName:(NSString *)_name { + +- (NSString *) pathPartFromInternalName: (NSString *) _name +{ /* for incomplete pathes, like 'Users/' */ return _name; } -- (NSDictionary *)filterRecords:(NSArray *)_records forPath:(NSString *)_path { - unsigned i, count; +- (NSDictionary *) filterRecords: (NSArray *) _records + forPath: (NSString *) _path +{ NSString *name; - - if (_records == nil) return nil; - if ((name = [self internalNameFromPath:_path]) == nil) return nil; - - for (i = 0, count = [_records count]; i < count; i++) { - NSDictionary *record; - NSString *recName; - - record = [_records objectAtIndex:i]; - recName = [record objectForKey:GCSPathRecordName]; + NSDictionary *record, *matchRecord; + NSString *recName; + unsigned int i, count; + + matchRecord = nil; + + if (_records) + { + name = [self internalNameFromPath: _path]; + if (name) + { + count = [_records count]; + i = 0; + while (!matchRecord && i < count) + { + record = [_records objectAtIndex: i]; + recName = [record objectForKey: GCSPathRecordName]; #if 0 - [self logWithFormat:@"check '%@' vs '%@' (%@)...", - name, recName, [_records objectAtIndex:i]]; + [self logWithFormat:@"check '%@' vs '%@' (%@)...", + name, recName, [_records objectAtIndex:i]]; #endif - - if ([name isEqualToString:recName]) - return [_records objectAtIndex:i]; - } - return nil; + + if ([name isEqualToString: recName]) + matchRecord = record; + else + i++; + } + } + } + + return matchRecord; } -- (BOOL)folderExistsAtPath:(NSString *)_path { - NSString *fname; +- (BOOL) folderExistsAtPath: (NSString *) _path +{ + NSString *fname, *sname, *sql; NSArray *fnames, *records; - NSString *sql; - unsigned count; + unsigned int count; + NSDictionary *record; + BOOL result; - if ((fnames = [self internalNamesFromPath:_path]) == nil) { + fnames = [self internalNamesFromPath: _path]; + if (fnames) + { + sql = [self generateSQLPathFetchForInternalNames: fnames + exactMatch: YES + orDirectSubfolderMatch: NO]; + if ([sql length]) + { + records = [self performSQL: sql]; + if (records) + { + count = [records count]; + if (count) + { + fname = [self internalNameFromPath: _path]; + if (count == 1) + { + record = [records objectAtIndex: 0]; + sname = [record objectForKey: GCSPathRecordName]; + result = [fname isEqualToString: sname]; + } + else + [self logWithFormat: @"records: %@", records]; + } + } + else + [self logWithFormat:@"ERROR(%s): executing SQL failed: '%@'", + __PRETTY_FUNCTION__, sql]; + } + else + [self debugWithFormat:@"got no SQL for names: %@", fnames]; + } + else [self debugWithFormat:@"got no internal names for path: '%@'", _path]; - return NO; - } - sql = [self generateSQLPathFetchForInternalNames:fnames - exactMatch:YES orDirectSubfolderMatch:NO]; - if ([sql length] == 0) { - [self debugWithFormat:@"got no SQL for names: %@", fnames]; - return NO; - } - - if ((records = [self performSQL:sql]) == nil) { - [self logWithFormat:@"ERROR(%s): executing SQL failed: '%@'", - __PRETTY_FUNCTION__, sql]; - return NO; - } - - if ((count = [records count]) == 0) - return NO; - - fname = [self internalNameFromPath:_path]; - if (count == 1) { - NSDictionary *record; - NSString *sname; - - record = [records objectAtIndex:0]; - sname = [record objectForKey:GCSPathRecordName]; - return [fname isEqualToString:sname]; - } - - [self logWithFormat:@"records: %@", records]; - - return NO; + return result; } - (NSArray *)listSubFoldersAtPath:(NSString *)_path recursive:(BOOL)_recursive{ @@ -530,7 +566,7 @@ static NSCharacterSet *asciiAlphaNumericCS = nil; if ((records = [self performSQL:sql]) == nil) { [self logWithFormat:@"ERROR(%s): executing SQL failed: '%@'", - __PRETTY_FUNCTION__, sql]; + __PRETTY_FUNCTION__, sql]; return nil; } @@ -567,7 +603,8 @@ static NSCharacterSet *asciiAlphaNumericCS = nil; return result; } -- (GCSFolder *)folderAtPath:(NSString *)_path { +- (GCSFolder *) folderAtPath: (NSString *) _path +{ NSMutableString *sql; NSArray *fnames, *records; NSString *ws; @@ -600,7 +637,7 @@ static NSCharacterSet *asciiAlphaNumericCS = nil; if ((records = [self performSQL:sql]) == nil) { [self logWithFormat:@"ERROR(%s): executing SQL failed: '%@'", - __PRETTY_FUNCTION__, sql]; + __PRETTY_FUNCTION__, sql]; return nil; } @@ -615,7 +652,7 @@ static NSCharacterSet *asciiAlphaNumericCS = nil; } [self logWithFormat:@"ERROR(%s): more than one row for path: '%@'", - __PRETTY_FUNCTION__, _path]; + __PRETTY_FUNCTION__, _path]; return nil; } @@ -654,128 +691,117 @@ static NSCharacterSet *asciiAlphaNumericCS = nil; newUID, randInc & 0xfff, (unsigned int) rand()]; } -- (NSException *)createFolderOfType:(NSString *)_type - withName:(NSString*)_name atPath:(NSString *)_path +- (NSException *) _reallyCreateFolderWithName: (NSString *) folderName + andFolderType: (NSString *) folderType + andType: (GCSFolderType *) ftype + andChannel: (EOAdaptorChannel + *) channel + atPath: (NSString *) path { - // TBD: badly broken, needs to be wrapped in a transaction. - // TBD: would be best to perform all operations as a single SQL statement. - GCSFolderType *ftype; - NSString *tableName, *quickTableName, *aclTableName; - NSString *baseURL, *pathElement; - EOAdaptorChannel *channel; - NSEnumerator *pathElements; - NSMutableArray *paths; - NSException *error; - NSString *sql; + NSException *error; + NSString *baseURL, *tableName, *quickTableName, *aclTableName, *sql; + EOAdaptorContext *aContext; + NSMutableArray *paths; - paths = [[NSMutableArray alloc] initWithCapacity: 5]; - - pathElements = [[_path componentsSeparatedByString: @"/"] objectEnumerator]; - while ((pathElement = [pathElements nextObject]) != nil) { - NSString *p = [[NSString alloc] initWithFormat: @"'%@'", pathElement]; - [paths addObject: p]; - [p release]; p = nil; - } + paths + = [NSMutableArray arrayWithArray: [path componentsSeparatedByString: @"/"]]; while ([paths count] < 5) [paths addObject: @"NULL"]; + + tableName = [self baseTableNameWithUID: [paths objectAtIndex: 2]]; + + aContext = [channel adaptorContext]; + [aContext beginTransaction]; + error = [channel createGCSFolderTableWithName: tableName]; + if (!error) + { + quickTableName = [tableName stringByAppendingString: @"_quick"]; + sql = [ftype sqlQuickCreateWithTableName: quickTableName]; + error = [channel evaluateExpressionX: sql]; + if (!error) + { + aclTableName = [tableName stringByAppendingString: @"_acl"]; + error = [channel createGCSFolderACLTableWithName: aclTableName]; + if (!error) + { + // TBD: fix SQL injection issues + baseURL + = [[folderInfoLocation absoluteString] stringByDeletingLastPathComponent]; + + sql = [NSString stringWithFormat: @"INSERT INTO %@" + @" (c_path, c_path1, c_path2, c_path3, c_path4," + @" c_foldername, c_location, c_quick_location," + @" c_acl_location, c_folder_type)" + @" VALUES ('%@', '%@', '%@', '%@', '%@', '%@', '%@/%@'," + @" '%@/%@', '%@/%@', '%@')", + [self folderInfoTableName], path, + [paths objectAtIndex: 1], [paths objectAtIndex: 2], + [paths objectAtIndex: 3], [paths objectAtIndex: 4], + [folderName stringByReplacingString: @"'" withString: @"''"], + baseURL, tableName, + baseURL, quickTableName, + baseURL, aclTableName, + folderType]; + error = [channel evaluateExpressionX: sql]; + } + } + } + + if (error) + [aContext rollbackTransaction]; + else + [aContext commitTransaction]; + + return error; +} + +- (NSException *) createFolderOfType: (NSString *) _type + withName: (NSString*) _name + atPath: (NSString *) _path +{ + // TBD: would be best to perform all operations as a single SQL statement. + GCSFolderType *ftype; + EOAdaptorChannel *channel; + NSException *error; // TBD: fix SQL injection issue! - sql = [NSString stringWithFormat: @"SELECT * FROM %@ WHERE c_path = '%@'", - [self folderInfoTableName], _path]; - if ([[self performSQL: sql] isNotEmpty]) { - return [NSException exceptionWithName:@"GCSExitingFolder" - reason:@"a folder already exists at that path" - userInfo:nil]; - } - if ((ftype = [self folderTypeWithName:_type]) == nil) { - return [NSException exceptionWithName:@"GCSMissingFolderType" - reason:@"missing folder type"userInfo:nil]; - } - if ((channel = [self acquireOpenChannel]) == nil) { - return [NSException exceptionWithName:@"GCSNoChannel" - reason:@"could not open channel" - userInfo:nil]; - } + if ([self folderExistsAtPath: _path]) + error = [NSException exceptionWithName: @"GCSExitingFolder" + reason: @"a folder already exists at that path" + userInfo: nil]; + else + { + ftype = [self folderTypeWithName:_type]; + if (ftype) + { + channel = [self acquireOpenChannel]; + if (channel) + { + error = [self _reallyCreateFolderWithName: _name + andFolderType: _type + andType: ftype andChannel: channel + atPath: _path]; + if (error && [self folderExistsAtPath: _path]) + error = nil; - tableName = [self baseTableNameWithUID: [paths objectAtIndex: 2]]; - quickTableName = [tableName stringByAppendingString: @"_quick"]; - aclTableName = [tableName stringByAppendingString: @"_acl"]; - - sql = [@"DROP TABLE " stringByAppendingString:quickTableName]; - if ((error = [channel evaluateExpressionX:sql]) != nil) - ; // 'DROP TABLE' is allowed to fail (DROP IF EXISTS is not in PG<8.2) - - sql = [@"DROP TABLE " stringByAppendingString:tableName]; - if ((error = [channel evaluateExpressionX:sql]) != nil) - ; // 'DROP TABLE' is allowed to fail (DROP IF EXISTS is not in PG<8.2) - - sql = [@"DROP TABLE " stringByAppendingString:aclTableName]; - if ((error = [channel evaluateExpressionX:sql]) != nil) - ; // 'DROP TABLE' is allowed to fail (DROP IF EXISTS is not in PG<8.2) - - if ((error = [channel createGCSFolderTableWithName: tableName]) != nil) - return error; - - sql = [ftype sqlQuickCreateWithTableName: quickTableName]; - if (debugSQLGen) [self logWithFormat:@"quick-Create: %@", sql]; - - if ((error = [channel evaluateExpressionX:sql]) != nil) { - /* 'rollback' TBD: wrap in proper tx */ - sql = [@"DROP TABLE " stringByAppendingString:tableName]; - if ((error = [channel evaluateExpressionX:sql]) != nil) { - [self warnWithFormat:@"failed to drop freshly created table: %@", - tableName]; + [self releaseChannel: channel]; + } + else + error = [NSException exceptionWithName: @"GCSNoChannel" + reason: @"could not open channel" + userInfo: nil]; + } + else + error = [NSException exceptionWithName: @"GCSMissingFolderType" + reason: @"missing folder type" + userInfo: nil]; } - - return error; - } - if (debugSQLGen) [self logWithFormat:@"acl-Create: %@", sql]; - if ((error = [channel createGCSFolderACLTableWithName: aclTableName]) - != nil) { - /* 'rollback' TBD: wrap in proper tx */ - sql = [@"DROP TABLE " stringByAppendingString:quickTableName]; - if ((error = [channel evaluateExpressionX:sql]) != nil) { - [self warnWithFormat:@"failed to drop freshly created table: %@", - tableName]; - } - sql = [@"DROP TABLE " stringByAppendingString:tableName]; - if ((error = [channel evaluateExpressionX:sql]) != nil) { - [self warnWithFormat:@"failed to drop freshly created table: %@", - tableName]; - } - - return error; - } - - // TBD: fix SQL injection issues - baseURL - = [[folderInfoLocation absoluteString] stringByDeletingLastPathComponent]; - - sql = [NSString stringWithFormat: @"INSERT INTO %@" - @" (c_path, c_path1, c_path2, c_path3, c_path4," - @" c_foldername, c_location, c_quick_location," - @" c_acl_location, c_folder_type)" - @" VALUES ('%@', %@, %@, %@, %@, '%@', '%@/%@'," - @" '%@/%@', '%@/%@', '%@')", - [self folderInfoTableName], _path, - [paths objectAtIndex: 1], [paths objectAtIndex: 2], - [paths objectAtIndex: 3], [paths objectAtIndex: 4], - [_name stringByReplacingString: @"'" withString: @"''"], - baseURL, tableName, - baseURL, quickTableName, - baseURL, aclTableName, - _type]; - if ((error = [channel evaluateExpressionX:sql]) != nil) - return error; - - [paths release]; paths = nil; - [self releaseChannel: channel]; - - return nil; + return error; } -- (NSException *)deleteFolderAtPath:(NSString *)_path { +- (NSException *) deleteFolderAtPath: (NSString *) _path +{ GCSFolder *folder; NSArray *fnames; NSString *sql, *ws; @@ -816,7 +842,8 @@ static NSCharacterSet *asciiAlphaNumericCS = nil; /* folder types */ -- (GCSFolderType *)folderTypeWithName:(NSString *)_name { +- (GCSFolderType *) folderTypeWithName: (NSString *) _name +{ NSString *specificName; GCSFolderType *type; @@ -825,37 +852,41 @@ static NSCharacterSet *asciiAlphaNumericCS = nil; specificName = [NSString stringWithFormat: @"%@-%@", _name, [folderInfoLocation scheme]]; - type = [self->nameToType objectForKey: [specificName lowercaseString]]; + type = [nameToType objectForKey: [specificName lowercaseString]]; if (!type) - type = [self->nameToType objectForKey:[_name lowercaseString]]; + type = [nameToType objectForKey:[_name lowercaseString]]; return type; } /* cache management */ -- (void)reset { +- (void) reset +{ /* does nothing in the moment, but we need a way to signal refreshes */ } /* debugging */ -- (BOOL)isDebuggingEnabled { +- (BOOL) isDebuggingEnabled +{ return debugOn; } /* description */ -- (NSString *)description { +- (NSString *) description +{ NSMutableString *ms; ms = [NSMutableString stringWithCapacity:256]; [ms appendFormat:@"<0x%p[%@]:", self, NSStringFromClass([self class])]; - [ms appendFormat:@" url=%@", [self->folderInfoLocation absoluteString]]; + [ms appendFormat:@" url=%@", [folderInfoLocation absoluteString]]; [ms appendFormat:@" channel-manager=0x%p", [self channelManager]]; [ms appendString:@">"]; + return ms; } From 3e01dc899fc9a5b52424bf4a2105d07e95c14f39 Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Mon, 30 Jun 2008 14:12:32 +0000 Subject: [PATCH 05/18] Monotone-Parent: 597389f8260d0d6fdfa812be82b4f10f924e5c8b Monotone-Revision: dce96f1e164f7e8d2e52f854cb9287574b9ded6d Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2008-06-30T14:12:32 Monotone-Branch: ca.inverse.sogo --- UI/MainUI/OCSFolderInfo-oracle.sql | 4 ++-- UI/MainUI/OCSFolderInfo.sql | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/UI/MainUI/OCSFolderInfo-oracle.sql b/UI/MainUI/OCSFolderInfo-oracle.sql index 3167282b3..bab345c38 100644 --- a/UI/MainUI/OCSFolderInfo-oracle.sql +++ b/UI/MainUI/OCSFolderInfo-oracle.sql @@ -3,8 +3,8 @@ -- CREATE TABLE @{tableName} ( - c_folder_id INTEGER PRIMARY KEY, - c_path VARCHAR(255) NOT NULL, -- the full path to the folder + c_folder_id INTEGER, + c_path VARCHAR(255) PRIMARY KEY, -- the full path to the folder c_path1 VARCHAR(255) NOT NULL, -- parts (for fast queries) c_path2 VARCHAR(255) NULL, -- parts (for fast queries) c_path3 VARCHAR(255) NULL, -- parts (for fast queries) diff --git a/UI/MainUI/OCSFolderInfo.sql b/UI/MainUI/OCSFolderInfo.sql index 548b71a10..e0bdf5b1a 100644 --- a/UI/MainUI/OCSFolderInfo.sql +++ b/UI/MainUI/OCSFolderInfo.sql @@ -5,7 +5,7 @@ CREATE TABLE @{tableName} ( c_folder_id SERIAL, - c_path VARCHAR(255) NOT NULL, -- the full path to the folder + c_path VARCHAR(255) PRIMARY KEY, -- the full path to the folder c_path1 VARCHAR(255) NOT NULL, -- parts (for fast queries) c_path2 VARCHAR(255) NULL, -- parts (for fast queries) c_path3 VARCHAR(255) NULL, -- parts (for fast queries) From f6e7d5c3a7fefce0e3ccebaed30aecc2c68320ff Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Mon, 30 Jun 2008 14:15:42 +0000 Subject: [PATCH 06/18] Monotone-Parent: dce96f1e164f7e8d2e52f854cb9287574b9ded6d Monotone-Revision: a0b176b29a24b7fe33ae8e1eb04d9838a299deaa Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2008-06-30T14:15:42 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 9 +++++++++ SoObjects/Contacts/SOGoContactLDAPFolder.m | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 1ace0efc7..da035913c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +2008-06-30 Wolfgang Sourdeau + + * SoObjects/Contacts/SOGoContactLDAPFolder.m + ([SOGoContactLDAPFolder + -lookupName:objectNameinContext:lookupContextacquire:acquire]): + use lookupContactentryWithUIDorEmail to make sure we use the ID + field specified in the user defaults to match the username, and + that the users can be found using their email. + 2008-06-27 Wolfgang Sourdeau * SoObjects/Appointments/SOGoFreeBusyObject.m ([SOGoFreeBusyObject diff --git a/SoObjects/Contacts/SOGoContactLDAPFolder.m b/SoObjects/Contacts/SOGoContactLDAPFolder.m index ce1253360..e01d08af5 100644 --- a/SoObjects/Contacts/SOGoContactLDAPFolder.m +++ b/SoObjects/Contacts/SOGoContactLDAPFolder.m @@ -164,7 +164,7 @@ if (!obj) { - ldifEntry = [ldapSource lookupContactEntry: objectName]; + ldifEntry = [ldapSource lookupContactEntryWithUIDorEmail: objectName]; obj = ((ldifEntry) ? [SOGoContactLDIFEntry contactEntryWithName: objectName withLDIFEntry: ldifEntry From d0c53ef1ac925b14253d1ba17c73b2a36e914caa Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Mon, 30 Jun 2008 14:50:47 +0000 Subject: [PATCH 07/18] Monotone-Parent: a0b176b29a24b7fe33ae8e1eb04d9838a299deaa Monotone-Revision: c4d0ee7e69c541a6b4b799dae55d03d6b5b67a6a Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2008-06-30T14:50:47 Monotone-Branch: ca.inverse.sogo --- SOPE/GDLContentStore/GCSFolderManager.m | 2 ++ 1 file changed, 2 insertions(+) diff --git a/SOPE/GDLContentStore/GCSFolderManager.m b/SOPE/GDLContentStore/GCSFolderManager.m index b5fc7ee2f..c6ec4c781 100644 --- a/SOPE/GDLContentStore/GCSFolderManager.m +++ b/SOPE/GDLContentStore/GCSFolderManager.m @@ -507,6 +507,8 @@ static NSCharacterSet *asciiAlphaNumericCS = nil; NSDictionary *record; BOOL result; + result = NO; + fnames = [self internalNamesFromPath: _path]; if (fnames) { From f50e9b4d0f617827e9f9473c5ad2bacac0c09a54 Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Mon, 30 Jun 2008 15:15:29 +0000 Subject: [PATCH 08/18] Monotone-Parent: c4d0ee7e69c541a6b4b799dae55d03d6b5b67a6a Monotone-Revision: effc0ea5c248c71e6b7996a01b73ce09b7609780 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2008-06-30T15:15:29 Monotone-Branch: ca.inverse.sogo --- SOPE/GDLContentStore/GCSFolderManager.m | 57 ++++++++++++------------- 1 file changed, 28 insertions(+), 29 deletions(-) diff --git a/SOPE/GDLContentStore/GCSFolderManager.m b/SOPE/GDLContentStore/GCSFolderManager.m index c6ec4c781..ddc8f3e90 100644 --- a/SOPE/GDLContentStore/GCSFolderManager.m +++ b/SOPE/GDLContentStore/GCSFolderManager.m @@ -710,42 +710,41 @@ static NSCharacterSet *asciiAlphaNumericCS = nil; while ([paths count] < 5) [paths addObject: @"NULL"]; - tableName = [self baseTableNameWithUID: [paths objectAtIndex: 2]]; - aContext = [channel adaptorContext]; [aContext beginTransaction]; - error = [channel createGCSFolderTableWithName: tableName]; + + tableName = [self baseTableNameWithUID: [paths objectAtIndex: 2]]; + quickTableName = [tableName stringByAppendingString: @"_quick"]; + aclTableName = [tableName stringByAppendingString: @"_acl"]; + + // TBD: fix SQL injection issues + baseURL + = [[folderInfoLocation absoluteString] stringByDeletingLastPathComponent]; + + sql = [NSString stringWithFormat: @"INSERT INTO %@" + @" (c_path, c_path1, c_path2, c_path3, c_path4," + @" c_foldername, c_location, c_quick_location," + @" c_acl_location, c_folder_type)" + @" VALUES ('%@', '%@', '%@', '%@', '%@', '%@', '%@/%@'," + @" '%@/%@', '%@/%@', '%@')", + [self folderInfoTableName], path, + [paths objectAtIndex: 1], [paths objectAtIndex: 2], + [paths objectAtIndex: 3], [paths objectAtIndex: 4], + [folderName stringByReplacingString: @"'" withString: @"''"], + baseURL, tableName, + baseURL, quickTableName, + baseURL, aclTableName, + folderType]; + error = [channel evaluateExpressionX: sql]; if (!error) { - quickTableName = [tableName stringByAppendingString: @"_quick"]; - sql = [ftype sqlQuickCreateWithTableName: quickTableName]; - error = [channel evaluateExpressionX: sql]; + error = [channel createGCSFolderTableWithName: tableName]; if (!error) { - aclTableName = [tableName stringByAppendingString: @"_acl"]; - error = [channel createGCSFolderACLTableWithName: aclTableName]; + sql = [ftype sqlQuickCreateWithTableName: quickTableName]; + error = [channel evaluateExpressionX: sql]; if (!error) - { - // TBD: fix SQL injection issues - baseURL - = [[folderInfoLocation absoluteString] stringByDeletingLastPathComponent]; - - sql = [NSString stringWithFormat: @"INSERT INTO %@" - @" (c_path, c_path1, c_path2, c_path3, c_path4," - @" c_foldername, c_location, c_quick_location," - @" c_acl_location, c_folder_type)" - @" VALUES ('%@', '%@', '%@', '%@', '%@', '%@', '%@/%@'," - @" '%@/%@', '%@/%@', '%@')", - [self folderInfoTableName], path, - [paths objectAtIndex: 1], [paths objectAtIndex: 2], - [paths objectAtIndex: 3], [paths objectAtIndex: 4], - [folderName stringByReplacingString: @"'" withString: @"''"], - baseURL, tableName, - baseURL, quickTableName, - baseURL, aclTableName, - folderType]; - error = [channel evaluateExpressionX: sql]; - } + error = [channel createGCSFolderACLTableWithName: aclTableName]; } } From 843b7d36169e2112394600f959acb85db1d05e6b Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Mon, 30 Jun 2008 18:31:03 +0000 Subject: [PATCH 09/18] Monotone-Parent: effc0ea5c248c71e6b7996a01b73ce09b7609780 Monotone-Revision: a004f12155b916af0dd5336aa31347b570b5395b Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2008-06-30T18:31:03 Monotone-Branch: ca.inverse.sogo --- SOPE/NGCards/ChangeLog | 6 ++++++ SOPE/NGCards/iCalFreeBusy.h | 3 +++ SOPE/NGCards/iCalFreeBusy.m | 14 ++++++++++++++ 3 files changed, 23 insertions(+) diff --git a/SOPE/NGCards/ChangeLog b/SOPE/NGCards/ChangeLog index 3263427f1..d789a1376 100644 --- a/SOPE/NGCards/ChangeLog +++ b/SOPE/NGCards/ChangeLog @@ -1,3 +1,9 @@ +2008-06-30 Wolfgang Sourdeau + + * iCalFreeBusy.m ([iCalFreeBusy + -fillStartDate:startDateandEndDate:endDate]): new method created + to avoid enlarging the code. + 2008-04-02 Wolfgang Sourdeau * iCalRecurrenceCalculator.m ([iCalRecurrenceCalculator diff --git a/SOPE/NGCards/iCalFreeBusy.h b/SOPE/NGCards/iCalFreeBusy.h index 8e744025f..d5ac71275 100644 --- a/SOPE/NGCards/iCalFreeBusy.h +++ b/SOPE/NGCards/iCalFreeBusy.h @@ -43,6 +43,9 @@ typedef enum iCalFreeBusyType - (NSCalendarDate *) endDate; - (BOOL) hasEndDate; +- (void) fillStartDate: (NSCalendarDate **) startDate + andEndDate: (NSCalendarDate **) endDate; + - (void) addFreeBusyFrom: (NSCalendarDate *) start to: (NSCalendarDate *) end type: (iCalFreeBusyType) type; diff --git a/SOPE/NGCards/iCalFreeBusy.m b/SOPE/NGCards/iCalFreeBusy.m index 17fd8e41d..74b25a2c1 100644 --- a/SOPE/NGCards/iCalFreeBusy.m +++ b/SOPE/NGCards/iCalFreeBusy.m @@ -62,6 +62,20 @@ return ([[self childrenWithTag: @"dtend"] count] > 0); } +- (void) fillStartDate: (NSCalendarDate **) startDate + andEndDate: (NSCalendarDate **) endDate +{ + if ([self hasStartDate]) + *startDate = [self startDate]; + else + *startDate = nil; + + if ([self hasEndDate]) + *endDate = [self endDate]; + else + *endDate = nil; +} + - (NSString *) _freeBusyTypeString: (iCalFreeBusyType) type { NSString *typeString; From b86a466fb7a6582a28ad04398c1d3404a315e13c Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Mon, 30 Jun 2008 18:31:29 +0000 Subject: [PATCH 10/18] Monotone-Parent: a004f12155b916af0dd5336aa31347b570b5395b Monotone-Revision: bd220b05886ea464c192ac169ed8f63316357a12 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2008-06-30T18:31:29 Monotone-Branch: ca.inverse.sogo --- SoObjects/SOGo/SOGoObject.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/SoObjects/SOGo/SOGoObject.h b/SoObjects/SOGo/SOGoObject.h index c6285d0a2..f6b04cc7d 100644 --- a/SoObjects/SOGo/SOGoObject.h +++ b/SoObjects/SOGo/SOGoObject.h @@ -134,6 +134,8 @@ - (NSString *) httpURLForAdvisoryToUser: (NSString *) uid; - (NSString *) resourceURLForAdvisoryToUser: (NSString *) uid; +- (NSArray *) davComplianceClassesInContext: (WOContext *) localContext; + /* dav acls */ - (SOGoWebDAVValue *) davCurrentUserPrivilegeSet; From ea81b6155f8387e675a926935a5f34f28e4fbb61 Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Mon, 30 Jun 2008 18:31:38 +0000 Subject: [PATCH 11/18] Monotone-Parent: bd220b05886ea464c192ac169ed8f63316357a12 Monotone-Revision: e12ee6acd551d70f45f22939355f0439c3590e24 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2008-06-30T18:31:38 Monotone-Branch: ca.inverse.sogo --- SoObjects/SOGo/DAVReportMap.plist | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/SoObjects/SOGo/DAVReportMap.plist b/SoObjects/SOGo/DAVReportMap.plist index 31defaf11..acffb2ec4 100644 --- a/SoObjects/SOGo/DAVReportMap.plist +++ b/SoObjects/SOGo/DAVReportMap.plist @@ -10,8 +10,10 @@ "{urn:ietf:params:xml:ns:carddav}supported-collation-set" = davSupportedCollectionSet; - /* WebDAV ACL */ + /* DeltaV */ "{DAV:}principal-match" = davPrincipalMatch; + "{DAV:}principal-property-search" = davPrincipalPropertySearch; + "{DAV:}principal-search-property-set" = davPrincipalSearchPropertySet; /* Inverse DAV */ "{urn:inverse:params:xml:ns:inverse-dav}collection-query" From 8e84b21ce92a2623ced6bf6f1bf1c92a0552ec1c Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Mon, 30 Jun 2008 18:31:50 +0000 Subject: [PATCH 12/18] Monotone-Parent: e12ee6acd551d70f45f22939355f0439c3590e24 Monotone-Revision: a67cdac2314fad2d44d7a6d3d3388e7476568ac4 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2008-06-30T18:31:50 Monotone-Branch: ca.inverse.sogo --- SoObjects/SOGo/SOGoUserFolder.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/SoObjects/SOGo/SOGoUserFolder.h b/SoObjects/SOGo/SOGoUserFolder.h index d2bd564ab..795a3379b 100644 --- a/SoObjects/SOGo/SOGoUserFolder.h +++ b/SoObjects/SOGo/SOGoUserFolder.h @@ -52,8 +52,8 @@ - (NSDictionary *) foldersOfType: (NSString *) type matchingUID: (NSString *) uid; -/* TODO: not implemented, bad bad */ -// - (id)lookupFreeBusyObject; +- (id) freeBusyObject: (NSString *) _key + inContext: (WOContext *) _ctx; @end From 37ccac74a4293bbaf50e9193f9351c3ccfec71be Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Mon, 30 Jun 2008 18:41:32 +0000 Subject: [PATCH 13/18] Monotone-Parent: a67cdac2314fad2d44d7a6d3d3388e7476568ac4 Monotone-Revision: 17ae35d5a041728462df76d24e7144981ef58862 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2008-06-30T18:41:32 Monotone-Branch: ca.inverse.sogo --- SoObjects/Appointments/SOGoFreeBusyObject.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/SoObjects/Appointments/SOGoFreeBusyObject.h b/SoObjects/Appointments/SOGoFreeBusyObject.h index 174e5dacb..4816d242d 100644 --- a/SoObjects/Appointments/SOGoFreeBusyObject.h +++ b/SoObjects/Appointments/SOGoFreeBusyObject.h @@ -43,6 +43,9 @@ - (NSString *) contentAsStringFrom: (NSCalendarDate *) _startDate to: (NSCalendarDate *) _endDate; +- (NSString *) contentAsStringWithMethod: (NSString *) method + from: (NSCalendarDate *) _startDate + to: (NSCalendarDate *) _endDate; - (NSArray *) fetchFreeBusyInfosFrom: (NSCalendarDate *) _startDate to: (NSCalendarDate *) _endDate; From acd7801f5cc55b7cc218ca164b5ade0aad2a91bd Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Mon, 30 Jun 2008 18:49:46 +0000 Subject: [PATCH 14/18] Monotone-Parent: 17ae35d5a041728462df76d24e7144981ef58862 Monotone-Revision: 696e29a05659691fb76dc0e1831843aae357bb91 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2008-06-30T18:49:46 Monotone-Branch: ca.inverse.sogo --- SoObjects/SOGo/SOGoParentFolder.m | 36 ++++++++++++++++++------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/SoObjects/SOGo/SOGoParentFolder.m b/SoObjects/SOGo/SOGoParentFolder.m index af6929f63..dbbcf34fd 100644 --- a/SoObjects/SOGo/SOGoParentFolder.m +++ b/SoObjects/SOGo/SOGoParentFolder.m @@ -57,36 +57,42 @@ static SoSecurityManager *sm = nil; if (!webdavAclManager) { webdavAclManager = [SOGoWebDAVAclManager new]; - [webdavAclManager registerDAVPermission: davElement (@"read", @"DAV:") + [webdavAclManager registerDAVPermission: davElement (@"read", + XMLNS_WEBDAV) abstract: YES withEquivalent: nil - asChildOf: davElement (@"all", @"DAV:")]; - [webdavAclManager registerDAVPermission: davElement (@"read-current-user-privilege-set", @"DAV:") + asChildOf: davElement (@"all", XMLNS_WEBDAV)]; + [webdavAclManager registerDAVPermission: + davElement (@"read-current-user-privilege-set", + XMLNS_WEBDAV) abstract: NO withEquivalent: SoPerm_WebDAVAccess - asChildOf: davElement (@"read", @"DAV:")]; - [webdavAclManager registerDAVPermission: davElement (@"write", @"DAV:") + asChildOf: davElement (@"read", XMLNS_WEBDAV)]; + [webdavAclManager registerDAVPermission: davElement (@"write", + XMLNS_WEBDAV) abstract: YES withEquivalent: nil - asChildOf: davElement (@"all", @"DAV:")]; - [webdavAclManager registerDAVPermission: davElement (@"bind", @"DAV:") + asChildOf: davElement (@"all", XMLNS_WEBDAV)]; + [webdavAclManager registerDAVPermission: davElement (@"bind", + XMLNS_WEBDAV) abstract: NO withEquivalent: SoPerm_AddFolders - asChildOf: davElement (@"write", @"DAV:")]; - [webdavAclManager registerDAVPermission: davElement (@"unbind", @"DAV:") + asChildOf: davElement (@"write", XMLNS_WEBDAV)]; + [webdavAclManager registerDAVPermission: davElement (@"unbind", + XMLNS_WEBDAV) abstract: NO withEquivalent: SoPerm_DeleteObjects - asChildOf: davElement (@"write", @"DAV:")]; + asChildOf: davElement (@"write", XMLNS_WEBDAV)]; [webdavAclManager - registerDAVPermission: davElement (@"write-properties", @"DAV:") + registerDAVPermission: davElement (@"write-properties", XMLNS_WEBDAV) abstract: YES withEquivalent: nil - asChildOf: davElement (@"write", @"DAV:")]; + asChildOf: davElement (@"write", XMLNS_WEBDAV)]; [webdavAclManager - registerDAVPermission: davElement (@"write-content", @"DAV:") + registerDAVPermission: davElement (@"write-content", XMLNS_WEBDAV) abstract: YES withEquivalent: nil - asChildOf: davElement (@"write", @"DAV:")]; + asChildOf: davElement (@"write", XMLNS_WEBDAV)]; } return webdavAclManager; @@ -209,7 +215,7 @@ static SoSecurityManager *sm = nil; [self ownerInContext: context], gcsFolderType]; [self _fetchPersonalFolders: sql withChannel: fc]; - [cm releaseChannel: fc]; + [cm releaseChannel: fc];340 2057 // sql = [sql stringByAppendingFormat:@" WHERE %@ = '%@'", // uidColumnName, [self uid]]; } From 7f7ca2d3cedc211e7715564cdd7455810405c295 Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Mon, 30 Jun 2008 18:52:20 +0000 Subject: [PATCH 15/18] Monotone-Parent: 696e29a05659691fb76dc0e1831843aae357bb91 Monotone-Revision: b307b838dc1265da074caf92873c4e57fe4cade1 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2008-06-30T18:52:20 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 7 + .../Appointments/SOGoAppointmentFolder.m | 450 ++++++++++++++---- 2 files changed, 358 insertions(+), 99 deletions(-) diff --git a/ChangeLog b/ChangeLog index da035913c..0011c1dbe 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,12 @@ 2008-06-30 Wolfgang Sourdeau + * SoObjects/Appointments/SOGoAppointmentFolder.m + ([SOGoAppointmentFolder -davComplianceClassesInContext:]): append + "calendar-schedule" to the list. + ([SOGoAppointmentFolder -POSTAction:localContext]): new method to + handle POST CalDAV requests. + ([SOGoAppointmentFolder -davCollectionTag]): new method. + * SoObjects/Contacts/SOGoContactLDAPFolder.m ([SOGoContactLDAPFolder -lookupName:objectNameinContext:lookupContextacquire:acquire]): diff --git a/SoObjects/Appointments/SOGoAppointmentFolder.m b/SoObjects/Appointments/SOGoAppointmentFolder.m index bae8fa3d1..591ecbe0f 100644 --- a/SoObjects/Appointments/SOGoAppointmentFolder.m +++ b/SoObjects/Appointments/SOGoAppointmentFolder.m @@ -37,8 +37,11 @@ #import #import #import +#import #import +#import #import +#import #import #import #import @@ -58,10 +61,12 @@ #import #import #import +#import #import #import "SOGoAppointmentObject.h" #import "SOGoAppointmentFolders.h" +#import "SOGoFreeBusyObject.h" #import "SOGoTaskObject.h" #import "SOGoAppointmentFolder.h" @@ -76,8 +81,8 @@ @implementation SOGoAppointmentFolder -static NGLogger *logger = nil; -static NSNumber *sharedYes = nil; +static NGLogger *logger = nil; +static NSNumber *sharedYes = nil; static NSArray *reportQueryFields = nil; static Class sogoAppointmentFolderKlass = Nil; @@ -123,128 +128,126 @@ static Class sogoAppointmentFolderKlass = Nil; + (SOGoWebDAVAclManager *) webdavAclManager { SOGoWebDAVAclManager *webdavAclManager = nil; - NSString *nsCD, *nsD, *nsI; + NSString *nsI; if (!webdavAclManager) { - nsD = @"DAV:"; - nsCD = @"urn:ietf:params:xml:ns:caldav"; nsI = @"urn:inverse:params:xml:ns:inverse-dav"; webdavAclManager = [SOGoWebDAVAclManager new]; - [webdavAclManager registerDAVPermission: davElement (@"read", nsD) + [webdavAclManager registerDAVPermission: davElement (@"read", XMLNS_WEBDAV) abstract: YES withEquivalent: SoPerm_WebDAVAccess - asChildOf: davElement (@"all", nsD)]; - [webdavAclManager registerDAVPermission: davElement (@"read-current-user-privilege-set", nsD) + asChildOf: davElement (@"all", XMLNS_WEBDAV)]; + [webdavAclManager registerDAVPermission: davElement (@"read-current-user-privilege-set", XMLNS_WEBDAV) abstract: YES withEquivalent: SoPerm_WebDAVAccess - asChildOf: davElement (@"read", nsD)]; - [webdavAclManager registerDAVPermission: davElement (@"read-free-busy", nsD) + asChildOf: davElement (@"read", XMLNS_WEBDAV)]; + [webdavAclManager registerDAVPermission: davElement (@"read-free-busy", XMLNS_WEBDAV) abstract: NO withEquivalent: SoPerm_AccessContentsInformation - asChildOf: davElement (@"read", nsD)]; - [webdavAclManager registerDAVPermission: davElement (@"write", nsD) + asChildOf: davElement (@"read", XMLNS_WEBDAV)]; + [webdavAclManager registerDAVPermission: davElement (@"write", XMLNS_WEBDAV) abstract: YES withEquivalent: nil - asChildOf: davElement (@"all", nsD)]; - [webdavAclManager registerDAVPermission: davElement (@"bind", nsD) + asChildOf: davElement (@"all", XMLNS_WEBDAV)]; + [webdavAclManager registerDAVPermission: davElement (@"bind", XMLNS_WEBDAV) abstract: NO withEquivalent: SoPerm_AddDocumentsImagesAndFiles - asChildOf: davElement (@"write", nsD)]; + asChildOf: davElement (@"write", XMLNS_WEBDAV)]; [webdavAclManager registerDAVPermission: davElement (@"schedule", - nsCD) + XMLNS_CALDAV) abstract: NO withEquivalent: nil - asChildOf: davElement (@"bind", nsD)]; + asChildOf: davElement (@"bind", XMLNS_WEBDAV)]; [webdavAclManager registerDAVPermission: davElement (@"schedule-post", - nsCD) + XMLNS_CALDAV) abstract: NO withEquivalent: nil - asChildOf: davElement (@"schedule", nsCD)]; + asChildOf: davElement (@"schedule", XMLNS_CALDAV)]; [webdavAclManager registerDAVPermission: - davElement (@"schedule-post-vevent", nsCD) + davElement (@"schedule-post-vevent", XMLNS_CALDAV) abstract: NO withEquivalent: nil - asChildOf: davElement (@"schedule-post", nsCD)]; + asChildOf: davElement (@"schedule-post", XMLNS_CALDAV)]; [webdavAclManager registerDAVPermission: - davElement (@"schedule-post-vtodo", nsCD) + davElement (@"schedule-post-vtodo", XMLNS_CALDAV) abstract: NO withEquivalent: nil - asChildOf: davElement (@"schedule-post", nsCD)]; + asChildOf: davElement (@"schedule-post", XMLNS_CALDAV)]; [webdavAclManager registerDAVPermission: - davElement (@"schedule-post-vjournal", nsCD) + davElement (@"schedule-post-vjournal", XMLNS_CALDAV) abstract: NO withEquivalent: nil - asChildOf: davElement (@"schedule-post", nsCD)]; + asChildOf: davElement (@"schedule-post", XMLNS_CALDAV)]; [webdavAclManager registerDAVPermission: - davElement (@"schedule-post-vfreebusy", nsCD) + davElement (@"schedule-post-vfreebusy", XMLNS_CALDAV) abstract: NO withEquivalent: nil - asChildOf: davElement (@"schedule-post", nsCD)]; + asChildOf: davElement (@"schedule-post", XMLNS_CALDAV)]; [webdavAclManager registerDAVPermission: davElement (@"schedule-deliver", - nsCD) + XMLNS_CALDAV) abstract: NO withEquivalent: nil - asChildOf: davElement (@"schedule", nsCD)]; + asChildOf: davElement (@"schedule", XMLNS_CALDAV)]; [webdavAclManager registerDAVPermission: - davElement (@"schedule-deliver-vevent", nsCD) + davElement (@"schedule-deliver-vevent", XMLNS_CALDAV) abstract: NO withEquivalent: nil - asChildOf: davElement (@"schedule-deliver", nsCD)]; + asChildOf: davElement (@"schedule-deliver", XMLNS_CALDAV)]; [webdavAclManager registerDAVPermission: - davElement (@"schedule-deliver-vtodo", nsCD) + davElement (@"schedule-deliver-vtodo", XMLNS_CALDAV) abstract: NO withEquivalent: nil - asChildOf: davElement (@"schedule-deliver", nsCD)]; + asChildOf: davElement (@"schedule-deliver", XMLNS_CALDAV)]; [webdavAclManager registerDAVPermission: - davElement (@"schedule-deliver-vjournal", nsCD) + davElement (@"schedule-deliver-vjournal", XMLNS_CALDAV) abstract: NO withEquivalent: nil - asChildOf: davElement (@"schedule-deliver", nsCD)]; + asChildOf: davElement (@"schedule-deliver", XMLNS_CALDAV)]; [webdavAclManager registerDAVPermission: - davElement (@"schedule-deliver-vfreebusy", nsCD) + davElement (@"schedule-deliver-vfreebusy", XMLNS_CALDAV) abstract: NO withEquivalent: nil - asChildOf: davElement (@"schedule-deliver", nsCD)]; + asChildOf: davElement (@"schedule-deliver", XMLNS_CALDAV)]; [webdavAclManager registerDAVPermission: davElement (@"schedule-respond", - nsCD) + XMLNS_CALDAV) abstract: NO withEquivalent: nil - asChildOf: davElement (@"schedule", nsCD)]; + asChildOf: davElement (@"schedule", XMLNS_CALDAV)]; [webdavAclManager registerDAVPermission: - davElement (@"schedule-respond-vevent", nsCD) + davElement (@"schedule-respond-vevent", XMLNS_CALDAV) abstract: NO withEquivalent: nil - asChildOf: davElement (@"schedule-respond", nsCD)]; + asChildOf: davElement (@"schedule-respond", XMLNS_CALDAV)]; [webdavAclManager registerDAVPermission: - davElement (@"schedule-respond-vtodo", nsCD) + davElement (@"schedule-respond-vtodo", XMLNS_CALDAV) abstract: NO withEquivalent: nil - asChildOf: davElement (@"schedule-respond", nsCD)]; - [webdavAclManager registerDAVPermission: davElement (@"unbind", nsD) + asChildOf: davElement (@"schedule-respond", XMLNS_CALDAV)]; + [webdavAclManager registerDAVPermission: davElement (@"unbind", XMLNS_WEBDAV) abstract: NO withEquivalent: SoPerm_DeleteObjects - asChildOf: davElement (@"write", nsD)]; + asChildOf: davElement (@"write", XMLNS_WEBDAV)]; [webdavAclManager - registerDAVPermission: davElement (@"write-properties", nsD) + registerDAVPermission: davElement (@"write-properties", XMLNS_WEBDAV) abstract: YES withEquivalent: SoPerm_ChangePermissions /* hackish */ - asChildOf: davElement (@"write", nsD)]; + asChildOf: davElement (@"write", XMLNS_WEBDAV)]; [webdavAclManager - registerDAVPermission: davElement (@"write-content", nsD) + registerDAVPermission: davElement (@"write-content", XMLNS_WEBDAV) abstract: YES withEquivalent: nil - asChildOf: davElement (@"write", nsD)]; + asChildOf: davElement (@"write", XMLNS_WEBDAV)]; [webdavAclManager registerDAVPermission: davElement (@"admin", nsI) abstract: YES withEquivalent: nil - asChildOf: davElement (@"all", nsD)]; - [webdavAclManager registerDAVPermission: davElement (@"read-acl", nsD) + asChildOf: davElement (@"all", XMLNS_WEBDAV)]; + [webdavAclManager registerDAVPermission: davElement (@"read-acl", XMLNS_WEBDAV) abstract: YES withEquivalent: SOGoPerm_ReadAcls asChildOf: davElement (@"admin", nsI)]; - [webdavAclManager registerDAVPermission: davElement (@"write-acl", nsD) + [webdavAclManager registerDAVPermission: davElement (@"write-acl", XMLNS_WEBDAV) abstract: YES withEquivalent: SoPerm_ChangePermissions asChildOf: davElement (@"admin", nsI)]; @@ -926,7 +929,7 @@ _selectorForProperty (NSString *property) namespace = [property substringFromRange: NSMakeRange (1, nsEnd.location - 1)]; nodeName = [property substringFromIndex: nsEnd.location + 1]; - if ([namespace isEqualToString: @"urn:ietf:params:xml:ns:caldav"]) + if ([namespace isEqualToString: XMLNS_CALDAV]) nsRep = @"C"; else nsRep = @"D"; @@ -1033,26 +1036,18 @@ _selectorForProperty (NSString *property) free (values); if ([properties200 count]) - { - [propstats addObject: [NSDictionary dictionaryWithObjectsAndKeys: - properties200, @"properties", - @"HTTP/1.1 200 OK", @"status", - nil]]; - [properties200 autorelease]; - } - else - [properties200 release]; + [propstats addObject: [NSDictionary dictionaryWithObjectsAndKeys: + properties200, @"properties", + @"HTTP/1.1 200 OK", @"status", + nil]]; + [properties200 release]; if ([properties404 count]) - { - [propstats addObject: [NSDictionary dictionaryWithObjectsAndKeys: - properties404, @"properties", - @"HTTP/1.1 404 Not Found", @"status", + [propstats addObject: [NSDictionary dictionaryWithObjectsAndKeys: + properties404, @"properties", + @"HTTP/1.1 404 Not Found", @"status", nil]]; - [properties404 autorelease]; - } - else - [properties404 release]; + [properties404 release]; // NSLog (@"/_propstats:ofObject:: %@", [NSDate date]); @@ -1635,6 +1630,219 @@ _selectorForProperty (NSString *property) return obj; } +- (NSDictionary *) freebusyResponseForRecipient: (NSString *) recipient + withUser: (SOGoUser *) user + andCalendarData: (NSString *) calendarData +{ + NSDictionary *response; + NSMutableArray *content; + + content = [NSMutableArray new]; + + [content addObject: davElementWithContent (@"recipient", XMLNS_CALDAV, recipient)]; + if (user) + { + [content addObject: davElementWithContent (@"request-status", XMLNS_CALDAV, + @"2.0;Success")]; + [content addObject: davElementWithContent (@"calendar-data", XMLNS_CALDAV, + calendarData)]; + } + else + [content addObject: + davElementWithContent (@"request-status", XMLNS_CALDAV, + @"3.7;Invalid Calendar User")]; + response = davElementWithContent (@"response", XMLNS_CALDAV, content); + [content release]; + + return response; +} + +- (NSDictionary *) caldavFreeBusyRequestOnRecipient: (NSString *) recipient + from: (NSCalendarDate *) start + to: (NSCalendarDate *) to +{ + LDAPUserManager *um; + SOGoUser *user; + NSString *lRecipient, *login, *calendarData; + SOGoFreeBusyObject *freebusy; + + user = nil; + calendarData = nil; + + lRecipient = [recipient lowercaseString]; + if ([lRecipient hasPrefix: @"mailto:"]) + { + um = [LDAPUserManager sharedUserManager]; + login = [um getUIDForEmail: [lRecipient substringFromIndex: 7]]; + if ([login length]) + { + user = [SOGoUser userWithLogin: login roles: nil]; + freebusy = [[user homeFolderInContext: context] + freeBusyObject: @"freebusy.ifb" + inContext: context]; + calendarData = [freebusy contentAsStringWithMethod: @"REPLY" + from: start to: to]; + } + } + + return [self freebusyResponseForRecipient: recipient + withUser: user + andCalendarData: calendarData]; +} + +- (NSDictionary *) caldavFreeBusyRequest: (iCalFreeBusy *) freebusy + from: (NSString *) originator + to: (NSArray *) recipients +{ + NSDictionary *responseElement; + NSMutableArray *elements; + NSString *recipient; + unsigned int count, max; + NSCalendarDate *startDate, *endDate; + + elements = [NSMutableArray new]; + [freebusy fillStartDate: &startDate andEndDate: &endDate]; + max = [recipients count]; + for (count = 0; count < max; count++) + { + recipient = [recipients objectAtIndex: count]; + [elements addObject: [self caldavFreeBusyRequestOnRecipient: recipient + from: startDate to: endDate]]; + } + + responseElement = davElementWithContent (@"schedule-response", + XMLNS_CALDAV, elements); + [elements release]; + + return responseElement; +} + +- (void) _saveCalDAVEvent: (iCalEvent *) event +{ + NSString *filename, *iCalString; + SOGoAppointmentObject *apt; + + filename = [NSString stringWithFormat: @"%@.ics", [event uid]]; + apt = [self lookupName: filename inContext: context acquire: NO]; + if ([apt isKindOfClass: [NSException class]]) + { + iCalString = [[event parent] versitString]; + apt = [self _createChildComponentWithName: filename + andContent: iCalString]; + } + [apt saveComponent: event]; +} + +- (NSDictionary *) caldavEventRequest: (iCalEvent *) event + from: (NSString *) originator + to: (NSArray *) recipients +{ + NSDictionary *responseElement; + NSMutableArray *elements, *content; + NSString *recipient; + unsigned int count, max; + + [self _saveCalDAVEvent: event]; + + elements = [NSMutableArray new]; + max = [recipients count]; + for (count = 0; count < max; count++) + { + /* this is a fake success status */ + recipient = [recipients objectAtIndex: count]; + content = [NSMutableArray new]; + [content addObject: davElementWithContent (@"recipient", XMLNS_CALDAV, recipient)]; + [content addObject: davElementWithContent (@"request-status", XMLNS_CALDAV, + @"2.0;Success")]; + [elements addObject: davElementWithContent (@"response", XMLNS_CALDAV, content)]; + [content release]; + } + + responseElement = davElementWithContent (@"schedule-response", + XMLNS_CALDAV, elements); + [elements release]; + + return responseElement; +} + +- (WOResponse *) _caldavScheduleResponse: (NSDictionary *) tags +{ + WOResponse *response; + + response = [context response]; + if (tags) + { + [response setStatus: 200]; + [response appendContentString:@"\r\n"]; + [response setHeader: @"application/xml; charset=utf-8" + forKey: @"Content-Type"]; + [response appendContentString: + [tags asWebDavStringWithNamespaces: nil]]; + } + else + [response setStatus: 415]; + + return response; +} + +- (WOResponse *) caldavScheduleRequest: (WORequest *) rq + withCalendar: (iCalCalendar *) calendar +{ + NSString *tag, *originator; + NSArray *recipients, *elements; + iCalEntityObject *element; + NSDictionary *tags; + + elements = [calendar allObjects]; + if ([elements count]) + { + element = [elements objectAtIndex: 0]; + originator = [rq headerForKey: @"originator"]; + recipients = [[rq headerForKey: @"recipient"] + componentsSeparatedByString: @", "]; + tag = [[element tag] uppercaseString]; + if ([tag isEqualToString: @"VFREEBUSY"]) + tags = [self caldavFreeBusyRequest: (iCalFreeBusy *) element + from: originator + to: recipients]; + else if ([tag isEqualToString: @"VEVENT"]) + tags = [self caldavEventRequest: (iCalEvent *) element + from: originator + to: recipients]; + else + tags = nil; +#warning needs to handle errors + } + + return [self _caldavScheduleResponse: tags]; +} + +- (id) POSTAction: (id) localContext +{ + id obj; + NSString *cType; + WORequest *rq; + iCalCalendar *calendar; + + obj = nil; + + rq = [localContext request]; + if ([rq isSoWebDAVRequest]) + { + cType = [rq headerForKey: @"content-type"]; + if ([cType hasPrefix: @"text/calendar"]) + { + calendar + = [iCalCalendar parseSingleFromSource: [rq contentAsString]]; + obj = [self caldavScheduleRequest: rq + withCalendar: calendar]; + } + } + + return obj; +} + - (NSArray *) davComplianceClassesInContext: (id)_ctx { NSMutableArray *classes; @@ -1646,8 +1854,8 @@ _selectorForProperty (NSString *property) primaryClasses = [super davComplianceClassesInContext: _ctx]; if (primaryClasses) [classes addObjectsFromArray: primaryClasses]; - [classes addObject: @"access-control"]; [classes addObject: @"calendar-access"]; + [classes addObject: @"calendar-schedule"]; return classes; } @@ -1679,6 +1887,45 @@ _selectorForProperty (NSString *property) return colType; } +- (NSString *) davCollectionTag +{ + NSArray *records; + GCSFolder *folder; + static EOFetchSpecification *cTagSpec = nil; + EOSortOrdering *ordering; + NSNumber *lastModified; + NSString *cTag; + + folder = [self ocsFolder]; + if (!cTagSpec) + { + ordering = [EOSortOrdering sortOrderingWithKey: @"c_lastmodified" + selector: EOCompareDescending]; + cTagSpec = [EOFetchSpecification + fetchSpecificationWithEntityName: [folder folderName] + qualifier: nil + sortOrderings: [NSArray arrayWithObject: ordering]]; + [cTagSpec retain]; + } + + records = [folder fetchFields: [NSArray arrayWithObject: @"c_lastmodified"] + fetchSpecification: cTagSpec]; + if ([records count]) + { + lastModified = [[records objectAtIndex: 0] objectForKey: @"c_lastmodified"]; + cTag = [lastModified stringValue]; + } + else + cTag = @"-1"; + + return cTag; +} + +- (NSString *) davDescription +{ + return @""; +} + /* vevent UID handling */ - (NSString *) resourceNameForEventUID: (NSString *)_u @@ -2036,45 +2283,50 @@ _selectorForProperty (NSString *property) NSMutableArray *uids; LDAPUserManager *um; unsigned i, count; + iCalPerson *person; + NSString *email; + NSString *uid; - if (_persons == nil) - return nil; - - count = [_persons count]; - uids = [NSMutableArray arrayWithCapacity:count + 1]; - um = [LDAPUserManager sharedUserManager]; - - for (i = 0; i < count; i++) + if (_persons) { - iCalPerson *person; - NSString *email; - NSString *uid; - - person = [_persons objectAtIndex:i]; - email = [person rfc822Email]; - if ([email isNotNull]) - uid = [um getUIDForEmail:email]; - else - uid = nil; - - if (!uid) - uid = (NSString *) [NSNull null]; - [uids addObject: uid]; + count = [_persons count]; + uids = [NSMutableArray arrayWithCapacity:count + 1]; + um = [LDAPUserManager sharedUserManager]; + + for (i = 0; i < count; i++) + { + person = [_persons objectAtIndex:i]; + email = [person rfc822Email]; + if ([email isNotNull]) + uid = [um getUIDForEmail:email]; + else + uid = nil; + + if (!uid) + uid = (NSString *) [NSNull null]; + [uids addObject: uid]; + } } + else + uids = nil; return uids; } -- (NSArray *)lookupCalendarFoldersForICalPerson: (NSArray *) _persons - inContext: (id) _ctx +- (NSArray *) lookupCalendarFoldersForICalPerson: (NSArray *) _persons + inContext: (id) _ctx { /* Note: can return NSNull objects in the array! */ - NSArray *uids; + NSArray *uids, *folders; - if ((uids = [self uidsFromICalPersons:_persons]) == nil) - return nil; + uids = [self uidsFromICalPersons: _persons]; + if (uids) + folders = [self lookupCalendarFoldersForUIDs: uids + inContext: _ctx]; + else + folders = nil; - return [self lookupCalendarFoldersForUIDs:uids inContext:_ctx]; + return folders; } // - (id) lookupGroupFolderForUIDs: (NSArray *) _uids From a616391e06fb151e02167174f10b707b32695bbf Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Mon, 30 Jun 2008 18:53:12 +0000 Subject: [PATCH 16/18] Monotone-Parent: b307b838dc1265da074caf92873c4e57fe4cade1 Monotone-Revision: 00cc1c566eeb4f26c3d21e3a3543d990d20dcd75 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2008-06-30T18:53:12 Monotone-Branch: ca.inverse.sogo --- .../Appointments/SOGoAppointmentFolders.m | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/SoObjects/Appointments/SOGoAppointmentFolders.m b/SoObjects/Appointments/SOGoAppointmentFolders.m index 46d68b6d0..d3f2e6364 100644 --- a/SoObjects/Appointments/SOGoAppointmentFolders.m +++ b/SoObjects/Appointments/SOGoAppointmentFolders.m @@ -44,6 +44,24 @@ return [self labelForKey: @"Personal Calendar"]; } +#warning THIS CAUSES LIGHTNING TO FAIL (that's why its commented out) +// - (NSArray *) davComplianceClassesInContext: (id)_ctx +// { +// NSMutableArray *classes; +// NSArray *primaryClasses; + +// classes = [NSMutableArray new]; +// [classes autorelease]; + +// primaryClasses = [super davComplianceClassesInContext: _ctx]; +// if (primaryClasses) +// [classes addObjectsFromArray: primaryClasses]; +// [classes addObject: @"calendar-access"]; +// [classes addObject: @"calendar-schedule"]; + +// return classes; +// } + // /* CalDAV support */ // - (NSArray *) davComplianceClassesInContext: (WOContext *) localContext // { From 669d8b1128c37d833d15d2c0d892046106eacf39 Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Mon, 30 Jun 2008 18:54:55 +0000 Subject: [PATCH 17/18] Monotone-Parent: 00cc1c566eeb4f26c3d21e3a3543d990d20dcd75 Monotone-Revision: b20bb1a79c74d239030ea69ed5a863b7bfad609d Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2008-06-30T18:54:55 Monotone-Branch: ca.inverse.sogo --- .../SOGoUserFolder+Appointments.m | 119 +++++++++--------- 1 file changed, 59 insertions(+), 60 deletions(-) diff --git a/SoObjects/Appointments/SOGoUserFolder+Appointments.m b/SoObjects/Appointments/SOGoUserFolder+Appointments.m index fa890c750..3eacc3acc 100644 --- a/SoObjects/Appointments/SOGoUserFolder+Appointments.m +++ b/SoObjects/Appointments/SOGoUserFolder+Appointments.m @@ -51,80 +51,79 @@ return addresses; } +/* CalDAV support */ +- (NSArray *) davCalendarHomeSet +{ + /* + + http://cal.example.com/home/bernard/calendars/ + -// /* CalDAV support */ -// - (NSArray *) davCalendarHomeSet -// { -// /* -// -// http://cal.example.com/home/bernard/calendars/ -// + Note: this is the *container* for calendar collections, not the + collections itself. So for use its the home folder, the + public folder and the groups folder. + */ + NSArray *tag; + SOGoAppointmentFolders *parent; -// Note: this is the *container* for calendar collections, not the -// collections itself. So for use its the home folder, the -// public folder and the groups folder. -// */ -// NSArray *tag; -// SOGoAppointmentFolders *parent; + parent = [self privateCalendars: @"Calendar" inContext: context]; + tag = [NSArray arrayWithObjects: @"href", @"DAV:", @"D", + [[parent davURL] path], nil]; -// parent = [self privateCalendars: @"Calendar" inContext: context]; -// tag = [NSArray arrayWithObjects: @"href", @"DAV:", @"D", -// [parent davURL], nil]; + return [NSArray arrayWithObject: tag]; +} -// return [NSArray arrayWithObject: tag]; -// } +- (NSArray *) davCalendarScheduleInboxURL +{ + NSArray *tag; + SOGoAppointmentFolders *parent; -// - (NSArray *) davCalendarScheduleInboxURL -// { -// NSArray *tag; -// SOGoAppointmentFolders *parent; + parent = [self privateCalendars: @"Calendar" inContext: context]; + tag = [NSArray arrayWithObjects: @"href", @"DAV:", @"D", + [NSString stringWithFormat: @"%@personal/", [[parent davURL] path]], + nil]; -// parent = [self privateCalendars: @"Calendar" inContext: context]; -// tag = [NSArray arrayWithObjects: @"href", @"DAV:", @"D", -// [NSString stringWithFormat: @"%@personal/", [parent davURL]], -// nil]; + return [NSArray arrayWithObject: tag]; +} -// return [NSArray arrayWithObject: tag]; -// } +- (NSString *) davCalendarScheduleOutboxURL +{ + NSArray *tag; + SOGoAppointmentFolders *parent; -// - (NSString *) davCalendarScheduleOutboxURL -// { -// NSArray *tag; -// SOGoAppointmentFolders *parent; + parent = [self privateCalendars: @"Calendar" inContext: context]; + tag = [NSArray arrayWithObjects: @"href", @"DAV:", @"D", + [NSString stringWithFormat: @"%@personal/", [[parent davURL] path]], + nil]; -// parent = [self privateCalendars: @"Calendar" inContext: context]; -// tag = [NSArray arrayWithObjects: @"href", @"DAV:", @"D", -// [NSString stringWithFormat: @"%@personal/", [parent davURL]], -// nil]; + return [NSArray arrayWithObject: tag]; +} -// return [NSArray arrayWithObject: tag]; -// } +- (NSString *) davDropboxHomeURL +{ + NSArray *tag; + SOGoAppointmentFolders *parent; -// - (NSString *) davDropboxHomeURL -// { -// NSArray *tag; -// SOGoAppointmentFolders *parent; + parent = [self privateCalendars: @"Calendar" inContext: context]; + tag = [NSArray arrayWithObjects: @"href", @"DAV:", @"D", + [NSString stringWithFormat: @"%@personal/", [[parent davURL] path]], + nil]; -// parent = [self privateCalendars: @"Calendar" inContext: context]; -// tag = [NSArray arrayWithObjects: @"href", @"DAV:", @"D", -// [NSString stringWithFormat: @"%@personal/", [parent davURL]], -// nil]; + return [NSArray arrayWithObject: tag]; +} -// return [NSArray arrayWithObject: tag]; -// } +- (NSString *) davNotificationsURL +{ + NSArray *tag; + SOGoAppointmentFolders *parent; -// - (NSString *) davNotificationsURL -// { -// NSArray *tag; -// SOGoAppointmentFolders *parent; + parent = [self privateCalendars: @"Calendar" inContext: context]; + tag = [NSArray arrayWithObjects: @"href", @"DAV:", @"D", + [NSString stringWithFormat: @"%@personal/", [[parent davURL] path]], + nil]; -// parent = [self privateCalendars: @"Calendar" inContext: context]; -// tag = [NSArray arrayWithObjects: @"href", @"DAV:", @"D", -// [NSString stringWithFormat: @"%@personal/", [parent davURL]], -// nil]; - -// return [NSArray arrayWithObject: tag]; -// } + return [NSArray arrayWithObject: tag]; +} @end From 68cef03a0664b44fac4e9e7402cc475546272e46 Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Mon, 30 Jun 2008 18:55:45 +0000 Subject: [PATCH 18/18] Monotone-Parent: b20bb1a79c74d239030ea69ed5a863b7bfad609d Monotone-Revision: 4ffb0fb9d1bad2a11d7f36633c52d54218fbde52 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2008-06-30T18:55:45 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 7 +++++++ SoObjects/SOGo/SOGoParentFolder.m | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 0011c1dbe..b876f4ae6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,12 @@ 2008-06-30 Wolfgang Sourdeau + * SoObjects/Appointments/SOGoUserFolder+Appointments.m ([SOGoUserFolder -davCalendarHomeSet]) + ([SOGoUserFolder -davCalendarScheduleInboxURL]) + ([SOGoUserFolder -davCalendarScheduleOutboxURL]) + ([SOGoUserFolder -davDropboxHomeURL]) + ([SOGoUserFolder -davNotificationsURL]): new methods for + supporting the caldav-schedule draft extension to CalDAV. + * SoObjects/Appointments/SOGoAppointmentFolder.m ([SOGoAppointmentFolder -davComplianceClassesInContext:]): append "calendar-schedule" to the list. diff --git a/SoObjects/SOGo/SOGoParentFolder.m b/SoObjects/SOGo/SOGoParentFolder.m index dbbcf34fd..b6548dff1 100644 --- a/SoObjects/SOGo/SOGoParentFolder.m +++ b/SoObjects/SOGo/SOGoParentFolder.m @@ -215,7 +215,7 @@ static SoSecurityManager *sm = nil; [self ownerInContext: context], gcsFolderType]; [self _fetchPersonalFolders: sql withChannel: fc]; - [cm releaseChannel: fc];340 2057 + [cm releaseChannel: fc]; // sql = [sql stringByAppendingFormat:@" WHERE %@ = '%@'", // uidColumnName, [self uid]]; }