merge of '46d1c12eedb8177f788cf76fdb4e73b766c27617'

and '4ffb0fb9d1bad2a11d7f36633c52d54218fbde52'

Monotone-Parent: 46d1c12eedb8177f788cf76fdb4e73b766c27617
Monotone-Parent: 4ffb0fb9d1bad2a11d7f36633c52d54218fbde52
Monotone-Revision: 5352455491daf79d879c8976262af4ecc972d346

Monotone-Author: wsourdeau@inverse.ca
Monotone-Date: 2008-06-30T18:57:07
Monotone-Branch: ca.inverse.sogo
This commit is contained in:
Wolfgang Sourdeau 2008-06-30 18:57:07 +00:00
commit 0ecc42f252
21 changed files with 899 additions and 484 deletions

View file

@ -1,3 +1,36 @@
2008-06-30 Wolfgang Sourdeau <wsourdeau@inverse.ca>
* 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.
([SOGoAppointmentFolder -POSTAction:localContext]): new method to
handle POST CalDAV requests.
([SOGoAppointmentFolder -davCollectionTag]): new method.
* 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 <wsourdeau@inverse.ca>
* 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 <wsourdeau@inverse.ca>
* SoObjects/SOGo/SOGoObject.m ([SOGoObject -labelForKey:key]):

View file

@ -1,3 +1,19 @@
2008-06-30 Wolfgang Sourdeau <wsourdeau@inverse.ca>
* 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
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.
2008-06-23 Wolfgang Sourdeau <wsourdeau@inverse.ca>
* GCSFolderManager.m ([GCSFolderManager

View file

@ -33,7 +33,8 @@
@interface EOAdaptorChannel(GCS)
- (BOOL)tableExistsWithName:(NSString *)_tableName;
- (BOOL) tableExistsWithName: (NSString *) _tableName;
- (void) dropTables: (NSArray *) tableNames;
@end

View file

@ -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) */

View file

@ -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
{

View file

@ -29,16 +29,17 @@
#import "EOAdaptorChannel+GCS.h"
#import "common.h"
#import <GDLAccess/EOAdaptorChannel.h>
#import <GDLAccess/EOAdaptorContext.h>
#import <NGExtensions/NGResourceLocator.h>
#import <unistd.h>
/*
Required database schema:
<arbitary table>
c_path
c_path1, path2, path3... [quickPathCount times]
c_foldername
<arbitary table>
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,144 @@ 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) {
result = NO;
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 +568,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 +605,8 @@ static NSCharacterSet *asciiAlphaNumericCS = nil;
return result;
}
- (GCSFolder *)folderAtPath:(NSString *)_path {
- (GCSFolder *) folderAtPath: (NSString *) _path
{
NSMutableString *sql;
NSArray *fnames, *records;
NSString *ws;
@ -600,7 +639,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 +654,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 +693,116 @@ 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
<GCSEOAdaptorChannel> *) 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 <GCSEOAdaptorChannel> *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"];
// 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];
}
aContext = [channel adaptorContext];
[aContext beginTransaction];
tableName = [self baseTableNameWithUID: [paths objectAtIndex: 2]];
quickTableName = [tableName stringByAppendingString: @"_quick"];
aclTableName = [tableName stringByAppendingString: @"_acl"];
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];
}
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 ('%@', %@, %@, %@, %@, '%@', '%@/%@',"
@" VALUES ('%@', '%@', '%@', '%@', '%@', '%@', '%@/%@',"
@" '%@/%@', '%@/%@', '%@')",
[self folderInfoTableName], _path,
[self folderInfoTableName], path,
[paths objectAtIndex: 1], [paths objectAtIndex: 2],
[paths objectAtIndex: 3], [paths objectAtIndex: 4],
[_name stringByReplacingString: @"'" withString: @"''"],
[folderName stringByReplacingString: @"'" withString: @"''"],
baseURL, tableName,
baseURL, quickTableName,
baseURL, aclTableName,
_type];
if ((error = [channel evaluateExpressionX:sql]) != nil)
return error;
folderType];
error = [channel evaluateExpressionX: sql];
if (!error)
{
error = [channel createGCSFolderTableWithName: tableName];
if (!error)
{
sql = [ftype sqlQuickCreateWithTableName: quickTableName];
error = [channel evaluateExpressionX: sql];
if (!error)
error = [channel createGCSFolderACLTableWithName: aclTableName];
}
}
[paths release]; paths = nil;
[self releaseChannel: channel];
if (error)
[aContext rollbackTransaction];
else
[aContext commitTransaction];
return nil;
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 <GCSEOAdaptorChannel> *channel;
NSException *error;
// TBD: fix SQL injection issue!
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;
[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;
}
- (NSException *)deleteFolderAtPath:(NSString *)_path {
- (NSException *) deleteFolderAtPath: (NSString *) _path
{
GCSFolder *folder;
NSArray *fnames;
NSString *sql, *ws;
@ -816,7 +843,8 @@ static NSCharacterSet *asciiAlphaNumericCS = nil;
/* folder types */
- (GCSFolderType *)folderTypeWithName:(NSString *)_name {
- (GCSFolderType *) folderTypeWithName: (NSString *) _name
{
NSString *specificName;
GCSFolderType *type;
@ -825,37 +853,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;
}

View file

@ -1,3 +1,9 @@
2008-06-30 Wolfgang Sourdeau <wsourdeau@inverse.ca>
* iCalFreeBusy.m ([iCalFreeBusy
-fillStartDate:startDateandEndDate:endDate]): new method created
to avoid enlarging the code.
2008-04-02 Wolfgang Sourdeau <wsourdeau@inverse.ca>
* iCalRecurrenceCalculator.m ([iCalRecurrenceCalculator

View file

@ -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;

View file

@ -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;

View file

@ -37,8 +37,11 @@
#import <GDLContentStore/GCSFolder.h>
#import <DOM/DOMNode.h>
#import <DOM/DOMProtocols.h>
#import <EOControl/EOFetchSpecification.h>
#import <EOControl/EOQualifier.h>
#import <EOControl/EOSortOrdering.h>
#import <NGCards/iCalCalendar.h>
#import <NGCards/iCalFreeBusy.h>
#import <NGCards/iCalDateTime.h>
#import <NGCards/iCalPerson.h>
#import <NGCards/iCalRecurrenceCalculator.h>
@ -58,10 +61,12 @@
#import <SOGo/LDAPUserManager.h>
#import <SOGo/SOGoPermissions.h>
#import <SOGo/SOGoUser.h>
#import <SOGo/SOGoUserFolder.h>
#import <SOGo/SOGoWebDAVAclManager.h>
#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:@"<?xml version=\"1.0\""
@" encoding=\"utf-8\"?>\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

View file

@ -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
// {

View file

@ -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;

View file

@ -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

View file

@ -51,80 +51,79 @@
return addresses;
}
/* CalDAV support */
- (NSArray *) davCalendarHomeSet
{
/*
<C:calendar-home-set xmlns:D="DAV:"
xmlns:C="urn:ietf:params:xml:ns:caldav">
<D:href>http://cal.example.com/home/bernard/calendars/</D:href>
</C:calendar-home-set>
// /* CalDAV support */
// - (NSArray *) davCalendarHomeSet
// {
// /*
// <C:calendar-home-set xmlns:D="DAV:"
// xmlns:C="urn:ietf:params:xml:ns:caldav">
// <D:href>http://cal.example.com/home/bernard/calendars/</D:href>
// </C:calendar-home-set>
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

View file

@ -164,7 +164,7 @@
if (!obj)
{
ldifEntry = [ldapSource lookupContactEntry: objectName];
ldifEntry = [ldapSource lookupContactEntryWithUIDorEmail: objectName];
obj = ((ldifEntry)
? [SOGoContactLDIFEntry contactEntryWithName: objectName
withLDIFEntry: ldifEntry

View file

@ -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"

View file

@ -134,6 +134,8 @@
- (NSString *) httpURLForAdvisoryToUser: (NSString *) uid;
- (NSString *) resourceURLForAdvisoryToUser: (NSString *) uid;
- (NSArray *) davComplianceClassesInContext: (WOContext *) localContext;
/* dav acls */
- (SOGoWebDAVValue *) davCurrentUserPrivilegeSet;

View file

@ -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;

View file

@ -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

View file

@ -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)

View file

@ -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)