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:
commit
0ecc42f252
33
ChangeLog
33
ChangeLog
|
@ -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]):
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -33,7 +33,8 @@
|
|||
|
||||
@interface EOAdaptorChannel(GCS)
|
||||
|
||||
- (BOOL)tableExistsWithName:(NSString *)_tableName;
|
||||
- (BOOL) tableExistsWithName: (NSString *) _tableName;
|
||||
- (void) dropTables: (NSArray *) tableNames;
|
||||
|
||||
@end
|
||||
|
||||
|
|
|
@ -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) */
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
// {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -164,7 +164,7 @@
|
|||
|
||||
if (!obj)
|
||||
{
|
||||
ldifEntry = [ldapSource lookupContactEntry: objectName];
|
||||
ldifEntry = [ldapSource lookupContactEntryWithUIDorEmail: objectName];
|
||||
obj = ((ldifEntry)
|
||||
? [SOGoContactLDIFEntry contactEntryWithName: objectName
|
||||
withLDIFEntry: ldifEntry
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -134,6 +134,8 @@
|
|||
- (NSString *) httpURLForAdvisoryToUser: (NSString *) uid;
|
||||
- (NSString *) resourceURLForAdvisoryToUser: (NSString *) uid;
|
||||
|
||||
- (NSArray *) davComplianceClassesInContext: (WOContext *) localContext;
|
||||
|
||||
/* dav acls */
|
||||
- (SOGoWebDAVValue *) davCurrentUserPrivilegeSet;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue