From 95a443d89a87b03a27f9f1fa166a4ff681522680 Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Fri, 29 Jun 2012 17:59:38 +0000 Subject: [PATCH 1/8] Monotone-Parent: 3f8608c82d9c379cc9e0fea6ffe853cc1949a24b Monotone-Revision: 905276f295d6f28a6946297f6a7af9ad60f71842 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-06-29T17:59:38 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 8 + OpenChange/EOQualifier+MAPI.h | 4 +- OpenChange/EOQualifier+MAPI.m | 30 +- OpenChange/GCSSpecialQueries+OpenChange.h | 34 ++ OpenChange/GCSSpecialQueries+OpenChange.m | 102 ++++ OpenChange/GNUmakefile | 34 +- OpenChange/MAPIStoreCalendarAttachment.m | 6 +- OpenChange/MAPIStoreCalendarMessage.m | 4 +- OpenChange/MAPIStoreContactsMessage.m | 4 +- OpenChange/MAPIStoreContext.m | 33 +- ...BaseContext.h => MAPIStoreDBBaseContext.h} | 12 +- OpenChange/MAPIStoreDBBaseContext.m | 116 +++++ ...APIStoreFSFolder.h => MAPIStoreDBFolder.h} | 12 +- ...APIStoreFSFolder.m => MAPIStoreDBFolder.m} | 87 ++-- ...FolderTable.h => MAPIStoreDBFolderTable.h} | 12 +- ...FolderTable.m => MAPIStoreDBFolderTable.m} | 8 +- ...IStoreFSMessage.h => MAPIStoreDBMessage.h} | 14 +- ...IStoreFSMessage.m => MAPIStoreDBMessage.m} | 108 +++- ...ssageTable.h => MAPIStoreDBMessageTable.h} | 12 +- ...ssageTable.m => MAPIStoreDBMessageTable.m} | 16 +- OpenChange/MAPIStoreFAIMessage.h | 4 +- OpenChange/MAPIStoreFAIMessageTable.h | 4 +- OpenChange/MAPIStoreFSBaseContext.m | 79 --- OpenChange/MAPIStoreFallbackContext.h | 4 +- OpenChange/MAPIStoreFallbackContext.m | 15 +- OpenChange/MAPIStoreFolder.h | 21 +- OpenChange/MAPIStoreFolder.m | 190 ++++--- OpenChange/MAPIStoreGCSFolder.h | 2 +- OpenChange/MAPIStoreGCSFolder.m | 12 +- OpenChange/MAPIStoreMailAttachment.h | 2 + OpenChange/MAPIStoreMailAttachment.m | 9 +- OpenChange/MAPIStoreMailFolder.h | 2 +- OpenChange/MAPIStoreMailFolder.m | 36 +- OpenChange/MAPIStoreMailMessage.m | 6 +- OpenChange/MAPIStoreMailMessageTable.m | 2 +- OpenChange/MAPIStoreMailVolatileMessage.h | 4 +- OpenChange/MAPIStoreMailVolatileMessage.m | 184 +++++-- OpenChange/MAPIStoreMessage.h | 4 +- OpenChange/MAPIStoreMessage.m | 12 +- OpenChange/MAPIStoreMessageTable.h | 1 + OpenChange/MAPIStoreMessageTable.m | 57 +++ OpenChange/MAPIStoreNotesContext.h | 4 +- OpenChange/MAPIStoreNotesFolder.h | 4 +- OpenChange/MAPIStoreNotesMessage.h | 4 +- OpenChange/MAPIStoreNotesMessage.m | 17 - OpenChange/MAPIStoreObject.h | 44 +- OpenChange/MAPIStoreObject.m | 224 ++------ OpenChange/MAPIStorePermissionsTable.m | 4 +- OpenChange/MAPIStoreSOGoObject.h | 89 ++++ OpenChange/MAPIStoreSOGoObject.m | 255 +++++++++ OpenChange/MAPIStoreTable.h | 2 - OpenChange/MAPIStoreTable.m | 55 -- OpenChange/MAPIStoreUserContext.h | 7 + OpenChange/MAPIStoreUserContext.m | 77 ++- OpenChange/MAPIStoreVolatileMessage.h | 37 -- OpenChange/MAPIStoreVolatileMessage.m | 208 -------- OpenChange/NSObject+PropertyList.m | 182 +++++++ ...{SOGoMAPIFSFolder.h => SOGoMAPIDBFolder.h} | 41 +- OpenChange/SOGoMAPIDBFolder.m | 402 +++++++++++++++ OpenChange/SOGoMAPIDBObject.h | 83 +++ OpenChange/SOGoMAPIDBObject.m | 483 ++++++++++++++++++ OpenChange/SOGoMAPIFSFolder.m | 439 ---------------- OpenChange/SOGoMAPIFSMessage.h | 47 -- OpenChange/SOGoMAPIFSMessage.m | 238 --------- ...MAPIVolatileMessage.h => SOGoMAPIObject.h} | 26 +- ...MAPIVolatileMessage.m => SOGoMAPIObject.m} | 50 +- OpenChange/plreader.m | 151 +----- 67 files changed, 2606 insertions(+), 1873 deletions(-) create mode 100644 OpenChange/GCSSpecialQueries+OpenChange.h create mode 100644 OpenChange/GCSSpecialQueries+OpenChange.m rename OpenChange/{MAPIStoreFSBaseContext.h => MAPIStoreDBBaseContext.h} (76%) create mode 100644 OpenChange/MAPIStoreDBBaseContext.m rename OpenChange/{MAPIStoreFSFolder.h => MAPIStoreDBFolder.h} (78%) rename OpenChange/{MAPIStoreFSFolder.m => MAPIStoreDBFolder.m} (75%) rename OpenChange/{MAPIStoreFSFolderTable.h => MAPIStoreDBFolderTable.h} (76%) rename OpenChange/{MAPIStoreFSFolderTable.m => MAPIStoreDBFolderTable.m} (85%) rename OpenChange/{MAPIStoreFSMessage.h => MAPIStoreDBMessage.h} (74%) rename OpenChange/{MAPIStoreFSMessage.m => MAPIStoreDBMessage.m} (59%) rename OpenChange/{MAPIStoreFSMessageTable.h => MAPIStoreDBMessageTable.h} (76%) rename OpenChange/{MAPIStoreFSMessageTable.m => MAPIStoreDBMessageTable.m} (92%) delete mode 100644 OpenChange/MAPIStoreFSBaseContext.m create mode 100644 OpenChange/MAPIStoreSOGoObject.h create mode 100644 OpenChange/MAPIStoreSOGoObject.m delete mode 100644 OpenChange/MAPIStoreVolatileMessage.h delete mode 100644 OpenChange/MAPIStoreVolatileMessage.m create mode 100644 OpenChange/NSObject+PropertyList.m rename OpenChange/{SOGoMAPIFSFolder.h => SOGoMAPIDBFolder.h} (52%) create mode 100644 OpenChange/SOGoMAPIDBFolder.m create mode 100644 OpenChange/SOGoMAPIDBObject.h create mode 100644 OpenChange/SOGoMAPIDBObject.m delete mode 100644 OpenChange/SOGoMAPIFSFolder.m delete mode 100644 OpenChange/SOGoMAPIFSMessage.h delete mode 100644 OpenChange/SOGoMAPIFSMessage.m rename OpenChange/{SOGoMAPIVolatileMessage.h => SOGoMAPIObject.h} (63%) rename OpenChange/{SOGoMAPIVolatileMessage.m => SOGoMAPIObject.m} (58%) diff --git a/ChangeLog b/ChangeLog index f749e20f6..a2d6e0ffe 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2012-06-29 Wolfgang Sourdeau + + * OpenChange/SOGoMAPIDBObject.m: new class module that replaced + SOGoMAPIFSMessage. + + * OpenChange/SOGoMAPIDBFolder.m: new class module that replaced + SOGoMAPIFSFolder. + 2012-06-28 Wolfgang Sourdeau * SoObjects/SOGo/SOGoObject.m (-initWithName:inContainer:): make diff --git a/OpenChange/EOQualifier+MAPI.h b/OpenChange/EOQualifier+MAPI.h index cbe65b915..72f368b63 100644 --- a/OpenChange/EOQualifier+MAPI.h +++ b/OpenChange/EOQualifier+MAPI.h @@ -25,11 +25,11 @@ #import -@class SOGoMAPIVolatileMessage; +@class SOGoMAPIDBObject; @interface EOQualifier (MAPIStoreRestrictions) -- (BOOL) evaluateMAPIVolatileMessage: (SOGoMAPIVolatileMessage *) message; +- (BOOL) evaluateSOGoMAPIDBObject: (SOGoMAPIDBObject *) object; @end diff --git a/OpenChange/EOQualifier+MAPI.m b/OpenChange/EOQualifier+MAPI.m index 0584e2789..8707dd788 100644 --- a/OpenChange/EOQualifier+MAPI.m +++ b/OpenChange/EOQualifier+MAPI.m @@ -28,28 +28,28 @@ #import -#import "SOGoMAPIVolatileMessage.h" +#import "EOBitmaskQualifier.h" +#import "SOGoMAPIDBObject.h" #import "EOQualifier+MAPI.h" -#import "EOBitmaskQualifier.h" @implementation EOQualifier (MAPIStoreRestrictions) -- (BOOL) _evaluateMAPIVolatileMessageProperties: (NSDictionary *) properties +- (BOOL) _evaluateSOGoMAPIDBObject: (NSDictionary *) properties { [self subclassResponsibility: _cmd]; return NO; } -- (BOOL) evaluateMAPIVolatileMessage: (SOGoMAPIVolatileMessage *) message +- (BOOL) evaluateSOGoMAPIDBObject: (SOGoMAPIDBObject *) object { NSDictionary *properties; BOOL rc; - [self logWithFormat: @"evaluating message '%@'", message]; + [self logWithFormat: @"evaluating object '%@'", object]; - properties = [message properties]; - rc = [self _evaluateMAPIVolatileMessageProperties: properties]; + properties = [object properties]; + rc = [self _evaluateSOGoMAPIDBObject: properties]; [self logWithFormat: @" evaluation result: %d", rc]; @@ -60,7 +60,7 @@ @implementation EOAndQualifier (MAPIStoreRestrictionsPrivate) -- (BOOL) _evaluateMAPIVolatileMessageProperties: (NSDictionary *) properties +- (BOOL) _evaluateSOGoMAPIDBObject: (NSDictionary *) properties { NSUInteger i; BOOL rc; @@ -69,7 +69,7 @@ for (i = 0; rc && i < count; i++) rc = [[qualifiers objectAtIndex: i] - _evaluateMAPIVolatileMessageProperties: properties]; + _evaluateSOGoMAPIDBObject: properties]; return rc; } @@ -78,7 +78,7 @@ @implementation EOOrQualifier (MAPIStoreRestrictionsPrivate) -- (BOOL) _evaluateMAPIVolatileMessageProperties: (NSDictionary *) properties +- (BOOL) _evaluateSOGoMAPIDBObject: (NSDictionary *) properties { NSUInteger i; BOOL rc; @@ -87,7 +87,7 @@ for (i = 0; !rc && i < count; i++) rc = [[qualifiers objectAtIndex: i] - _evaluateMAPIVolatileMessageProperties: properties]; + _evaluateSOGoMAPIDBObject: properties]; return rc; } @@ -96,9 +96,9 @@ @implementation EONotQualifier (MAPIStoreRestrictionsPrivate) -- (BOOL) _evaluateMAPIVolatileMessageProperties: (NSDictionary *) properties +- (BOOL) _evaluateSOGoMAPIDBObject: (NSDictionary *) properties { - return ![qualifier _evaluateMAPIVolatileMessageProperties: properties]; + return ![qualifier _evaluateSOGoMAPIDBObject: properties]; } @end @@ -107,7 +107,7 @@ typedef BOOL (*EOComparator) (id, SEL, id); -- (BOOL) _evaluateMAPIVolatileMessageProperties: (NSDictionary *) properties +- (BOOL) _evaluateSOGoMAPIDBObject: (NSDictionary *) properties { id finalKey; id propValue; @@ -136,7 +136,7 @@ typedef BOOL (*EOComparator) (id, SEL, id); @implementation EOBitmaskQualifier (MAPIStoreRestrictionsPrivate) -- (BOOL) _evaluateMAPIVolatileMessageProperties: (NSDictionary *) properties +- (BOOL) _evaluateSOGoMAPIDBObject: (NSDictionary *) properties { NSNumber *propTag; id propValue; diff --git a/OpenChange/GCSSpecialQueries+OpenChange.h b/OpenChange/GCSSpecialQueries+OpenChange.h new file mode 100644 index 000000000..4caaca54b --- /dev/null +++ b/OpenChange/GCSSpecialQueries+OpenChange.h @@ -0,0 +1,34 @@ +/* GCSSpecialQueries+OpenChange.h - this file is part of SOGo + * + * Copyright (C) 2012 Inverse inc + * + * Author: Wolfgang Sourdeau + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef GCSSPECIALQUERIES_OPENCHANGE_H +#define GCSSPECIALQUERIES_OPENCHANGE_H + +#import + +@interface GCSSpecialQueries (OpenChangeHelpers) + +- (NSString *) createOpenChangeFSTableWithName: (NSString *) tableName; + +@end + +#endif /* GCSSPECIALQUERIES_OPENCHANGE_H */ diff --git a/OpenChange/GCSSpecialQueries+OpenChange.m b/OpenChange/GCSSpecialQueries+OpenChange.m new file mode 100644 index 000000000..aa336bf99 --- /dev/null +++ b/OpenChange/GCSSpecialQueries+OpenChange.m @@ -0,0 +1,102 @@ +/* GCSSpecialQueries+OpenChange.m - this file is part of SOGo + * + * Copyright (C) 2012 Inverse inc + * + * Author: Wolfgang Sourdeau + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#import + +#import "GCSSpecialQueries+OpenChange.h" + +@interface GCSPostgreSQLSpecialQueries (OpenChangeHelpers) +@end + +@interface GCSMySQLSpecialQueries (OpenChangeHelpers) +@end + +@interface GCSOracleSpecialQueries (OpenChangeHelpers) +@end + +@implementation GCSSpecialQueries (OpenChangeHelpers) + +- (NSString *) createOpenChangeFSTableWithName: (NSString *) tableName +{ + [self subclassResponsibility: _cmd]; + + return nil; +} + +@end + +@implementation GCSPostgreSQLSpecialQueries (OpenChangeHelpers) + +- (NSString *) createOpenChangeFSTableWithName: (NSString *) tableName +{ + static NSString *sqlFolderFormat + = (@"CREATE TABLE %@ (" + @" c_path VARCHAR(255) PRIMARY KEY," + @" c_type SMALLINT NOT NULL," + @" c_creationdate INT4 NOT NULL," + @" c_lastmodified INT4 NOT NULL," + @" c_version INT4 NOT NULL DEFAULT 0," + @" c_deleted SMALLINT NOT NULL DEFAULT 0," + @" c_content TEXT)"); + + return [NSString stringWithFormat: sqlFolderFormat, tableName]; +} + +@end + +@implementation GCSMySQLSpecialQueries (OpenChangeHelpers) + +- (NSString *) createOpenChangeFSTableWithName: (NSString *) tableName +{ + static NSString *sqlFolderFormat + = (@"CREATE TABLE %@ (" + @" c_path VARCHAR(255) PRIMARY KEY," + @" c_type TINYINT NOT NULL," + @" c_creationdate INT NOT NULL," + @" c_lastmodified INT NOT NULL," + @" c_version INT NOT NULL DEFAULT 0," + @" c_deleted TINYINT NOT NULL DEFAULT 0," + @" c_content TEXT)"); + + return [NSString stringWithFormat: sqlFolderFormat, tableName]; +} + +@end + +@implementation GCSOracleSpecialQueries (OpenChangeHelpers) + +- (NSString *) createOpenChangeFSTableWithName: (NSString *) tableName +{ + static NSString *sqlFolderFormat + = (@"CREATE TABLE %@ (" + @" c_path VARCHAR2(255) PRIMARY KEY," + @" c_type SMALLINT NOT NULL," + @" c_creationdate INT4 NOT NULL," + @" c_lastmodified INT4 NOT NULL," + @" c_version INT4 NOT NULL DEFAULT 0," + @" c_deleted SMALLINT NOT NULL DEFAULT 0," + @" c_content CLOB)"); + + return [NSString stringWithFormat: sqlFolderFormat, tableName]; +} + +@end diff --git a/OpenChange/GNUmakefile b/OpenChange/GNUmakefile index 0f3fb1a39..6f4803ba9 100644 --- a/OpenChange/GNUmakefile +++ b/OpenChange/GNUmakefile @@ -41,9 +41,11 @@ $(SOGOBACKEND)_OBJC_FILES += \ MAPIStoreSamDBUtils.m \ MAPIStoreUserContext.m \ \ - SOGoMAPIVolatileMessage.m \ - SOGoMAPIFSFolder.m \ - SOGoMAPIFSMessage.m \ + SOGoMAPIObject.m \ + \ + SOGoMAPIDBObject.m \ + SOGoMAPIDBMessage.m \ + SOGoMAPIDBFolder.m \ \ MAPIStoreAppointmentWrapper.m \ MAPIStoreAttachment.m \ @@ -53,18 +55,17 @@ $(SOGOBACKEND)_OBJC_FILES += \ MAPIStoreFolder.m \ MAPIStoreMessage.m \ MAPIStoreObject.m \ + MAPIStoreSOGoObject.m \ MAPIStoreTable.m \ MAPIStoreMessageTable.m \ MAPIStoreFolderTable.m \ MAPIStorePermissionsTable.m \ \ - MAPIStoreVolatileMessage.m \ - \ - MAPIStoreFSBaseContext.m \ - MAPIStoreFSFolder.m \ - MAPIStoreFSFolderTable.m \ - MAPIStoreFSMessage.m \ - MAPIStoreFSMessageTable.m \ + MAPIStoreDBBaseContext.m \ + MAPIStoreDBFolder.m \ + MAPIStoreDBFolderTable.m \ + MAPIStoreDBMessage.m \ + MAPIStoreDBMessageTable.m \ \ MAPIStoreFAIMessage.m \ MAPIStoreFAIMessageTable.m \ @@ -113,7 +114,10 @@ $(SOGOBACKEND)_OBJC_FILES += \ NSValue+MAPIStore.m \ \ EOBitmaskQualifier.m \ - EOQualifier+MAPI.m \ + \ + GCSSpecialQueries+OpenChange.m\ + \ + EOQualifier+MAPI.m $(SOGOBACKEND)_RESOURCE_FILES += \ @@ -145,7 +149,13 @@ PLREADER_TOOL = plreader $(PLREADER_TOOL)_OBJC_FILES += \ plreader.m \ -TEST_TOOL_NAME += $(PLREADER_TOOL) +DBMSGREADER_TOOL = dbmsgreader +$(DBMSGREADER_TOOL)_OBJC_FILES += \ + dbmsgreader.m + +$(DBMSGREADER_TOOL)_LIB_DIRS += -L../SoObjects/SOGo/ -lSOGo -lNGObjWeb + +TEST_TOOL_NAME += $(PLREADER_TOOL) $(DBMSGREADER_TOOL) ### cflags and libs LIBMAPI_CFLAGS = $(shell pkg-config libmapi --cflags) diff --git a/OpenChange/MAPIStoreCalendarAttachment.m b/OpenChange/MAPIStoreCalendarAttachment.m index 63aa68df8..1ba40ac17 100644 --- a/OpenChange/MAPIStoreCalendarAttachment.m +++ b/OpenChange/MAPIStoreCalendarAttachment.m @@ -67,10 +67,10 @@ { MAPIStoreEmbeddedMessage *msg; - if (isNew) - msg = nil; - else + // if (isNew) msg = nil; + // else + // msg = nil; return msg; } diff --git a/OpenChange/MAPIStoreCalendarMessage.m b/OpenChange/MAPIStoreCalendarMessage.m index daa9cc83d..5d2eb48a6 100644 --- a/OpenChange/MAPIStoreCalendarMessage.m +++ b/OpenChange/MAPIStoreCalendarMessage.m @@ -1111,9 +1111,7 @@ newAid = [[self attachmentKeys] count]; newAttachment = [MAPIStoreCalendarAttachment - mapiStoreObjectWithSOGoObject: nil - inContainer: self]; - [newAttachment setIsNew: YES]; + mapiStoreObjectInContainer: self]; [newAttachment setAID: newAid]; newKey = [NSString stringWithFormat: @"%ul", newAid]; [attachmentParts setObject: newAttachment diff --git a/OpenChange/MAPIStoreContactsMessage.m b/OpenChange/MAPIStoreContactsMessage.m index c8af5c05b..99f297ea1 100644 --- a/OpenChange/MAPIStoreContactsMessage.m +++ b/OpenChange/MAPIStoreContactsMessage.m @@ -35,6 +35,7 @@ #import #import +#import "MAPIStoreAttachment.h" #import "MAPIStoreContactsAttachment.h" #import "MAPIStoreContactsFolder.h" #import "MAPIStorePropertySelectors.h" @@ -767,8 +768,7 @@ || [encoding isEqualToString: @"BASE64"]) { attachment = [MAPIStoreContactsAttachment - mapiStoreObjectWithSOGoObject: nil - inContainer: self]; + mapiStoreObjectInContainer: self]; [attachment setAID: 0]; [attachment setPhoto: photo]; [attachmentParts setObject: attachment forKey: @"photo"]; diff --git a/OpenChange/MAPIStoreContext.m b/OpenChange/MAPIStoreContext.m index 1817658ea..a3704289b 100644 --- a/OpenChange/MAPIStoreContext.m +++ b/OpenChange/MAPIStoreContext.m @@ -28,11 +28,9 @@ #import #import +#import #import -#import "SOGoMAPIFSFolder.h" -#import "SOGoMAPIFSMessage.h" - #import "MAPIStoreAttachment.h" // #import "MAPIStoreAttachmentTable.h" #import "MAPIStoreFallbackContext.h" @@ -433,25 +431,29 @@ static inline NSURL *CompleteURLFromMapistoreURI (const char *uri) [self ensureContextFolder]; currentFolder = [self rootSOGoFolder]; + [containersBag addObject: currentFolder]; path = [contextUrl path]; if ([path hasPrefix: @"/"]) path = [path substringFromIndex: 1]; if ([path hasSuffix: @"/"]) path = [path substringToIndex: [path length] - 1]; - pathComponents = [path componentsSeparatedByString: @"/"]; - max = [pathComponents count]; - for (count = 0; currentFolder && count < max; count++) + if ([path length] > 0) { - [woContext setClientObject: currentFolder]; - currentFolder - = [currentFolder lookupName: [pathComponents objectAtIndex: count] - inContext: woContext + pathComponents = [path componentsSeparatedByString: @"/"]; + max = [pathComponents count]; + for (count = 0; currentFolder && count < max; count++) + { + [woContext setClientObject: currentFolder]; + currentFolder = [currentFolder + lookupName: [pathComponents objectAtIndex: count] + inContext: woContext acquire: NO]; - if ([currentFolder isKindOfClass: SOGoObjectK]) /* class common to all - SOGo folder types */ - [containersBag addObject: currentFolder]; - else - currentFolder = nil; + if ([currentFolder isKindOfClass: SOGoObjectK]) /* class common to all + SOGo folder types */ + [containersBag addObject: currentFolder]; + else + currentFolder = nil; + } } if (currentFolder) @@ -460,7 +462,6 @@ static inline NSURL *CompleteURLFromMapistoreURI (const char *uri) mapiStoreObjectWithSOGoObject: currentFolder inContainer: nil]; [baseFolder setContext: self]; - *folderPtr = baseFolder; rc = MAPISTORE_SUCCESS; } diff --git a/OpenChange/MAPIStoreFSBaseContext.h b/OpenChange/MAPIStoreDBBaseContext.h similarity index 76% rename from OpenChange/MAPIStoreFSBaseContext.h rename to OpenChange/MAPIStoreDBBaseContext.h index ecc63d1e6..62f5bd489 100644 --- a/OpenChange/MAPIStoreFSBaseContext.h +++ b/OpenChange/MAPIStoreDBBaseContext.h @@ -1,6 +1,6 @@ -/* MAPIStoreFSBaseContext.h - this file is part of SOGo +/* MAPIStoreDBBaseContext.h - this file is part of SOGo * - * Copyright (C) 2010 Inverse inc. + * Copyright (C) 2012 Inverse inc. * * Author: Wolfgang Sourdeau * @@ -20,13 +20,13 @@ * Boston, MA 02111-1307, USA. */ -#ifndef MAPISTOREFSBASECONTEXT_H -#define MAPISTOREFSBASECONTEXT_H +#ifndef MAPISTOREDBBASECONTEXT_H +#define MAPISTOREDBBASECONTEXT_H #import "MAPIStoreContext.h" -@interface MAPIStoreFSBaseContext : MAPIStoreContext +@interface MAPIStoreDBBaseContext : MAPIStoreContext @end -#endif /* MAPISTOREFSBASECONTEXT_H */ +#endif /* MAPISTOREDBBASECONTEXT_H */ diff --git a/OpenChange/MAPIStoreDBBaseContext.m b/OpenChange/MAPIStoreDBBaseContext.m new file mode 100644 index 000000000..d82538083 --- /dev/null +++ b/OpenChange/MAPIStoreDBBaseContext.m @@ -0,0 +1,116 @@ +/* MAPIStoreDBBaseContext.m - this file is part of SOGo + * + * Copyright (C) 2012 Inverse inc. + * + * Author: Wolfgang Sourdeau + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* A generic parent class for all context that will store their data on the + disk in the form of a plist. */ + +#import +#import +#import + +#import + +#import "MAPIStoreDBFolder.h" +#import "MAPIStoreMapping.h" +#import "MAPIStoreUserContext.h" +#import "SOGoMAPIDBFolder.h" + +#import "MAPIStoreDBBaseContext.h" + +#undef DEBUG +#include + +static Class MAPIStoreDBFolderK; + +@implementation MAPIStoreDBBaseContext + ++ (void) initialize +{ + MAPIStoreDBFolderK = [MAPIStoreDBFolder class]; +} + ++ (NSString *) MAPIModuleName +{ + return nil; +} + +- (Class) MAPIStoreFolderClass +{ + return MAPIStoreDBFolderK; +} + +- (void) ensureContextFolder +{ + SOGoMAPIDBFolder *currentFolder; + NSArray *parts; + NSMutableArray *folders; + NSString *folderName; + NSUInteger count, max; + + parts = [[contextUrl path] componentsSeparatedByString: @"/"]; + max = [parts count]; + folders = [NSMutableArray arrayWithCapacity: max]; + + /* build the folder chain */ + currentFolder = [self rootSOGoFolder]; + [folders addObject: currentFolder]; + for (count = 1; count < max; count++) + { + folderName = [parts objectAtIndex: count]; + if ([folderName length] > 0) + { + currentFolder = [SOGoMAPIDBFolder objectWithName: folderName + inContainer: currentFolder]; + [folders addObject: currentFolder]; + } + } + + /* ensure each folder in the chain actually exists, so that it becomes + "listable" in further operations */ + max = [folders count]; + for (count = 0; count < max; count++) + { + currentFolder = [folders objectAtIndex: count]; + [currentFolder reloadIfNeeded]; + if ([currentFolder isNew]) + [currentFolder save]; + } +} + +- (id) rootSOGoFolder +{ + SOGoMAPIDBFolder *folder; + + [userContext ensureFolderTableExists]; + + folder = [SOGoMAPIDBFolder objectWithName: [isa MAPIModuleName] + inContainer: nil]; + [folder setTableUrl: [userContext folderTableURL]]; + // [folder reloadIfNeeded]; + + /* we don't need to set the "path prefix" of the folder since the module + name is used as the label for the top folder */ + + return folder; +} + +@end diff --git a/OpenChange/MAPIStoreFSFolder.h b/OpenChange/MAPIStoreDBFolder.h similarity index 78% rename from OpenChange/MAPIStoreFSFolder.h rename to OpenChange/MAPIStoreDBFolder.h index 75e3f08e5..2415af934 100644 --- a/OpenChange/MAPIStoreFSFolder.h +++ b/OpenChange/MAPIStoreDBFolder.h @@ -1,6 +1,6 @@ -/* MAPIStoreFSFolder.h - this file is part of SOGo +/* MAPIStoreDBFolder.h - this file is part of SOGo * - * Copyright (C) 2011 Inverse inc + * Copyright (C) 2012 Inverse inc * * Author: Wolfgang Sourdeau * @@ -20,14 +20,14 @@ * Boston, MA 02111-1307, USA. */ -#ifndef MAPISTOREFSFOLDER_H -#define MAPISTOREFSFOLDER_H +#ifndef MAPISTOREDBFOLDER_H +#define MAPISTOREDBFOLDER_H #import "MAPIStoreFolder.h" -@interface MAPIStoreFSFolder : MAPIStoreFolder +@interface MAPIStoreDBFolder : MAPIStoreFolder @end -#endif /* MAPISTOREFSFOLDER_H */ +#endif /* MAPISTOREDBFOLDER_H */ diff --git a/OpenChange/MAPIStoreFSFolder.m b/OpenChange/MAPIStoreDBFolder.m similarity index 75% rename from OpenChange/MAPIStoreFSFolder.m rename to OpenChange/MAPIStoreDBFolder.m index 68f57545f..641b9dc59 100644 --- a/OpenChange/MAPIStoreFSFolder.m +++ b/OpenChange/MAPIStoreDBFolder.m @@ -1,4 +1,4 @@ -/* MAPIStoreFSFolder.m - this file is part of SOGo +/* MAPIStoreDBFolder.m - this file is part of SOGo * * Copyright (C) 2011 Inverse inc * @@ -31,15 +31,15 @@ #import #import "EOQualifier+MAPI.h" #import "MAPIStoreContext.h" -#import "MAPIStoreFSFolderTable.h" -#import "MAPIStoreFSMessage.h" -#import "MAPIStoreFSMessageTable.h" +#import "MAPIStoreDBFolderTable.h" +#import "MAPIStoreDBMessage.h" +#import "MAPIStoreDBMessageTable.h" #import "MAPIStoreTypes.h" #import "MAPIStoreUserContext.h" -#import "SOGoMAPIFSFolder.h" -#import "SOGoMAPIFSMessage.h" +#import "SOGoMAPIDBFolder.h" +#import "SOGoMAPIDBMessage.h" -#import "MAPIStoreFSFolder.h" +#import "MAPIStoreDBFolder.h" #undef DEBUG #include @@ -57,39 +57,34 @@ static NSString *MAPIStoreRightCreateSubfolders = @"RightsCreateSubfolders"; static NSString *MAPIStoreRightFolderOwner = @"RightsFolderOwner"; static NSString *MAPIStoreRightFolderContact = @"RightsFolderContact"; -@implementation MAPIStoreFSFolder +@implementation MAPIStoreDBFolder + (void) initialize { EOKeyValueQualifierK = [EOKeyValueQualifier class]; } +- (void) setupAuxiliaryObjects +{ + [super setupAuxiliaryObjects]; + ASSIGN (sogoObject, dbFolder); +} + - (MAPIStoreMessageTable *) messageTable { - return [MAPIStoreFSMessageTable tableForContainer: self]; + return [MAPIStoreDBMessageTable tableForContainer: self]; } - (MAPIStoreFolderTable *) folderTable { - return [MAPIStoreFSFolderTable tableForContainer: self]; + return [MAPIStoreDBFolderTable tableForContainer: self]; } - (enum mapistore_error) createFolder: (struct SRow *) aRow withFID: (uint64_t) newFID andKey: (NSString **) newKeyP { - NSString *newKey, *urlString; - NSURL *childURL; - SOGoMAPIFSFolder *childFolder; - - newKey = [NSString stringWithFormat: @"0x%.16"PRIx64, (unsigned long long) newFID]; - - urlString = [NSString stringWithFormat: @"%@/%@", [self url], newKey]; - childURL = [NSURL URLWithString: [urlString stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding]]; - childFolder = [SOGoMAPIFSFolder folderWithURL: childURL - andTableType: MAPISTORE_MESSAGE_TABLE]; - [childFolder ensureDirectory]; - *newKeyP = newKey; + *newKeyP = [NSString stringWithFormat: @"0x%.16"PRIx64, (unsigned long long) newFID]; return MAPISTORE_SUCCESS; } @@ -97,14 +92,15 @@ static NSString *MAPIStoreRightFolderContact = @"RightsFolderContact"; - (MAPIStoreMessage *) createMessage { MAPIStoreMessage *newMessage; - SOGoMAPIFSMessage *fsObject; + SOGoMAPIDBMessage *fsObject; NSString *newKey; newKey = [NSString stringWithFormat: @"%@.plist", [SOGoObject globallyUniqueObjectId]]; - fsObject = [SOGoMAPIFSMessage objectWithName: newKey + fsObject = [SOGoMAPIDBMessage objectWithName: newKey inContainer: sogoObject]; - newMessage = [MAPIStoreFSMessage mapiStoreObjectWithSOGoObject: fsObject + [fsObject setObjectType: MAPIDBObjectTypeMessage]; + newMessage = [MAPIStoreDBMessage mapiStoreObjectWithSOGoObject: fsObject inContainer: self]; return newMessage; @@ -119,9 +115,10 @@ static NSString *MAPIStoreRightFolderContact = @"RightsFolderContact"; ownerUser = [[self userContext] sogoUser]; if ([[context activeUser] isEqual: ownerUser] || [self subscriberCanReadMessages]) - keys = [(SOGoMAPIFSFolder *) sogoObject - toOneRelationshipKeysMatchingQualifier: qualifier - andSortOrderings: sortOrderings]; + keys = [(SOGoMAPIDBFolder *) sogoObject childKeysOfType: MAPIDBObjectTypeMessage + includeDeleted: NO + matchingQualifier: qualifier + andSortOrderings: sortOrderings]; else keys = [NSArray array]; @@ -131,39 +128,17 @@ static NSString *MAPIStoreRightFolderContact = @"RightsFolderContact"; - (NSArray *) folderKeysMatchingQualifier: (EOQualifier *) qualifier andSortOrderings: (NSArray *) sortOrderings { - NSArray *entries; - NSMutableArray *filteredEntries; - NSUInteger count, max; - MAPIStoreFSFolder *subfolder; - SOGoMAPIFSMessage *propertiesMessage; - NSString *subfolderKey; - - entries = [(SOGoMAPIFSFolder *) sogoObject toManyRelationshipKeys]; - if (qualifier) - { - max = [entries count]; - filteredEntries = [NSMutableArray arrayWithCapacity: max]; - for (count = 0; count < max; count++) - { - subfolderKey = [entries objectAtIndex: count]; - subfolder = [self lookupFolder: subfolderKey]; - propertiesMessage = [subfolder propertiesMessage]; - if ([qualifier evaluateMAPIVolatileMessage: propertiesMessage]) - [filteredEntries addObject: subfolderKey]; - } - entries = filteredEntries; - } - if (sortOrderings) - [self errorWithFormat: @"sort orderings are not used for folders"]; - - return entries; + return [dbFolder childKeysOfType: MAPIDBObjectTypeFolder + includeDeleted: NO + matchingQualifier: qualifier + andSortOrderings: sortOrderings]; } - (NSDate *) lastMessageModificationTime { NSUInteger count, max; NSDate *date, *fileDate; - MAPIStoreFSMessage *msg; + MAPIStoreDBMessage *msg; NSArray *messageKeys; messageKeys = [self messageKeys]; @@ -189,7 +164,7 @@ static NSString *MAPIStoreRightFolderContact = @"RightsFolderContact"; - (SOGoFolder *) aclFolder { - return propsFolder; + return sogoObject; } - (NSArray *) rolesForExchangeRights: (uint32_t) rights diff --git a/OpenChange/MAPIStoreFSFolderTable.h b/OpenChange/MAPIStoreDBFolderTable.h similarity index 76% rename from OpenChange/MAPIStoreFSFolderTable.h rename to OpenChange/MAPIStoreDBFolderTable.h index f5a5dfb4e..afc3018e2 100644 --- a/OpenChange/MAPIStoreFSFolderTable.h +++ b/OpenChange/MAPIStoreDBFolderTable.h @@ -1,6 +1,6 @@ -/* MAPIStoreFSFolderTable.h - this file is part of SOGo +/* MAPIStoreDBFolderTable.h - this file is part of SOGo * - * Copyright (C) 2011 Inverse inc + * Copyright (C) 2012 Inverse inc * * Author: Wolfgang Sourdeau * @@ -20,12 +20,12 @@ * Boston, MA 02111-1307, USA. */ -#ifndef MAPISTOREFSFOLDERTABLE_H -#define MAPISTOREFSFOLDERTABLE_H +#ifndef MAPISTOREDBFOLDERTABLE_H +#define MAPISTOREDBFOLDERTABLE_H #import "MAPIStoreFolderTable.h" -@interface MAPIStoreFSFolderTable : MAPIStoreFolderTable +@interface MAPIStoreDBFolderTable : MAPIStoreFolderTable @end -#endif /* MAPISTOREFSFOLDERTABLE_H */ +#endif /* MAPISTOREDBFOLDERTABLE_H */ diff --git a/OpenChange/MAPIStoreFSFolderTable.m b/OpenChange/MAPIStoreDBFolderTable.m similarity index 85% rename from OpenChange/MAPIStoreFSFolderTable.m rename to OpenChange/MAPIStoreDBFolderTable.m index a834c5fb4..01a40b7f0 100644 --- a/OpenChange/MAPIStoreFSFolderTable.m +++ b/OpenChange/MAPIStoreDBFolderTable.m @@ -1,6 +1,6 @@ -/* MAPIStoreFSFolderTable.m - this file is part of SOGo +/* MAPIStoreDBFolderTable.m - this file is part of SOGo * - * Copyright (C) 2011 Inverse inc + * Copyright (C) 2012 Inverse inc * * Author: Wolfgang Sourdeau * @@ -24,9 +24,9 @@ #import "MAPIStoreTypes.h" -#import "MAPIStoreFSFolderTable.h" +#import "MAPIStoreDBFolderTable.h" -@implementation MAPIStoreFSFolderTable +@implementation MAPIStoreDBFolderTable - (NSString *) backendIdentifierForProperty: (enum MAPITAGS) property { diff --git a/OpenChange/MAPIStoreFSMessage.h b/OpenChange/MAPIStoreDBMessage.h similarity index 74% rename from OpenChange/MAPIStoreFSMessage.h rename to OpenChange/MAPIStoreDBMessage.h index 20084a4f2..532f53a8e 100644 --- a/OpenChange/MAPIStoreFSMessage.h +++ b/OpenChange/MAPIStoreDBMessage.h @@ -1,6 +1,6 @@ -/* MAPIStoreFSMessage.h - this file is part of SOGo +/* MAPIStoreDBMessage.h - this file is part of SOGo * - * Copyright (C) 2011 Inverse inc + * Copyright (C) 2012 Inverse inc * * Author: Wolfgang Sourdeau * @@ -20,12 +20,12 @@ * Boston, MA 02111-1307, USA. */ -#ifndef MAPISTOREFSMESSAGE_H -#define MAPISTOREFSMESSAGE_H +#ifndef MAPISTOREDBMESSAGE_H +#define MAPISTOREDBMESSAGE_H -#import "MAPIStoreVolatileMessage.h" +#import "MAPIStoreMessage.h" -@interface MAPIStoreFSMessage : MAPIStoreVolatileMessage +@interface MAPIStoreDBMessage : MAPIStoreMessage @end -#endif /* MAPISTOREFSMESSAGE_H */ +#endif /* MAPISTOREDBMESSAGE_H */ diff --git a/OpenChange/MAPIStoreFSMessage.m b/OpenChange/MAPIStoreDBMessage.m similarity index 59% rename from OpenChange/MAPIStoreFSMessage.m rename to OpenChange/MAPIStoreDBMessage.m index 7b7939884..945b41c8e 100644 --- a/OpenChange/MAPIStoreFSMessage.m +++ b/OpenChange/MAPIStoreDBMessage.m @@ -1,4 +1,4 @@ -/* MAPIStoreFSMessage.m - this file is part of SOGo +/* MAPIStoreDBMessage.m - this file is part of SOGo * * Copyright (C) 2011 Inverse inc * @@ -21,24 +21,25 @@ */ #import +#import #import #import #import #import "MAPIStoreContext.h" #import "MAPIStorePropertySelectors.h" -#import "SOGoMAPIFSMessage.h" +#import "SOGoMAPIDBMessage.h" -#import "MAPIStoreFSFolder.h" -#import "MAPIStoreFSMessage.h" +#import "MAPIStoreDBFolder.h" +#import "MAPIStoreDBMessage.h" #import "MAPIStoreTypes.h" -#import "NSData+MAPIStore.h" +#import "NSObject+MAPIStore.h" #undef DEBUG #include #include -@implementation MAPIStoreFSMessage +@implementation MAPIStoreDBMessage + (int) getAvailableProperties: (struct SPropTagArray **) propertiesP inMemCtx: (TALLOC_CTX *) memCtx @@ -60,13 +61,96 @@ /* FIXME (hack): append a few undocumented properties that can be added to FAI messages */ for (count = 0; count < 8; count++) - properties->aulPropTag[MAPIStoreSupportedPropertiesCount+count] = faiProperties[count]; + properties->aulPropTag[MAPIStoreSupportedPropertiesCount+count] + = faiProperties[count]; *propertiesP = properties; return MAPISTORE_SUCCESS; } +- (id) initWithSOGoObject: (id) newSOGoObject + inContainer: (MAPIStoreObject *) newContainer +{ + if ((self = [super initWithSOGoObject: newSOGoObject + inContainer: newContainer])) + { + [properties release]; + properties = [newSOGoObject properties]; + [properties retain]; + } + + return self; +} + +- (uint64_t) objectVersion +{ + NSNumber *versionNbr; + uint64_t objectVersion; + + [(SOGoMAPIDBMessage *) sogoObject reloadIfNeeded]; + versionNbr = [properties objectForKey: @"version"]; + if (versionNbr) + objectVersion = [versionNbr unsignedLongLongValue]; + else + objectVersion = ULLONG_MAX; + + return objectVersion; +} + +- (int) getProperties: (struct mapistore_property_data *) data + withTags: (enum MAPITAGS *) tags + andCount: (uint16_t) columnCount + inMemCtx: (TALLOC_CTX *) memCtx +{ + [sogoObject reloadIfNeeded]; + + return [super getProperties: data + withTags: tags + andCount: columnCount + inMemCtx: memCtx]; +} + +- (int) getProperty: (void **) data + withTag: (enum MAPITAGS) propTag + inMemCtx: (TALLOC_CTX *) memCtx +{ + id value; + int rc; + + value = [properties objectForKey: MAPIPropertyKey (propTag)]; + if (value) + rc = [value getValue: data forTag: propTag inMemCtx: memCtx]; + else + rc = [super getProperty: data withTag: propTag inMemCtx: memCtx]; + + return rc; +} + +- (int) getPidTagSubject: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + id value; + int rc; + + value = [properties + objectForKey: MAPIPropertyKey (PidTagNormalizedSubject)]; + if (value) + rc = [value getValue: data forTag: PidTagNormalizedSubject + inMemCtx: memCtx]; + else + rc = MAPISTORE_ERR_NOT_FOUND; + + return rc; +} + +- (void) addProperties: (NSDictionary *) newNewProperties +{ + [sogoObject reloadIfNeeded]; + + [super addProperties: newNewProperties]; +} + - (void) save { uint64_t newVersion; @@ -76,13 +160,11 @@ newVersion = exchange_globcnt ([[self context] getNewChangeNumber] >> 16); [properties setObject: [NSNumber numberWithUnsignedLongLong: newVersion] - forKey: @"version"]; + forKey: @"version"]; [self logWithFormat: @"%d props in dict", [properties count]]; - [sogoObject appendProperties: properties]; [sogoObject save]; - [properties removeAllObjects]; } - (BOOL) _messageIsFreeBusy @@ -91,7 +173,7 @@ /* This is a HACK until we figure out how to determine a message position in the mailbox hierarchy.... (missing: folderid and role) */ - msgClass = [[sogoObject properties] + msgClass = [properties objectForKey: MAPIPropertyKey (PR_MESSAGE_CLASS_UNICODE)]; return [msgClass isEqualToString: @"IPM.Microsoft.ScheduleData.FreeBusy"]; @@ -115,12 +197,12 @@ - (NSDate *) creationTime { - return [sogoObject creationTime]; + return [sogoObject creationDate]; } - (NSDate *) lastModificationTime { - return [sogoObject lastModificationTime]; + return [sogoObject lastModified]; } @end diff --git a/OpenChange/MAPIStoreFSMessageTable.h b/OpenChange/MAPIStoreDBMessageTable.h similarity index 76% rename from OpenChange/MAPIStoreFSMessageTable.h rename to OpenChange/MAPIStoreDBMessageTable.h index 452c8ffdb..4b9f66480 100644 --- a/OpenChange/MAPIStoreFSMessageTable.h +++ b/OpenChange/MAPIStoreDBMessageTable.h @@ -1,6 +1,6 @@ -/* MAPIStoreFSMessageTable.h - this file is part of SOGo +/* MAPIStoreDBMessageTable.h - this file is part of SOGo * - * Copyright (C) 2010 Inverse inc + * Copyright (C) 2012 Inverse inc * * Author: Wolfgang Sourdeau * @@ -20,12 +20,12 @@ * Boston, MA 02111-1307, USA. */ -#ifndef MAPISTOREFSMESSAGETABLE_H -#define MAPISTOREFSMESSAGETABLE_H +#ifndef MAPISTOREDBMESSAGETABLE_H +#define MAPISTOREDBMESSAGETABLE_H #import "MAPIStoreMessageTable.h" -@interface MAPIStoreFSMessageTable : MAPIStoreMessageTable +@interface MAPIStoreDBMessageTable : MAPIStoreMessageTable @end -#endif /* MAPISTOREFSMESSAGETABLE_H */ +#endif /* MAPISTOREDBMESSAGETABLE_H */ diff --git a/OpenChange/MAPIStoreFSMessageTable.m b/OpenChange/MAPIStoreDBMessageTable.m similarity index 92% rename from OpenChange/MAPIStoreFSMessageTable.m rename to OpenChange/MAPIStoreDBMessageTable.m index 7c8504c67..9f791d68a 100644 --- a/OpenChange/MAPIStoreFSMessageTable.m +++ b/OpenChange/MAPIStoreDBMessageTable.m @@ -1,6 +1,6 @@ -/* MAPIStoreFSMessageTable.m - this file is part of SOGo +/* MAPIStoreDBMessageTable.m - this file is part of SOGo * - * Copyright (C) 2010 Inverse inc + * Copyright (C) 2012 Inverse inc * * Author: Wolfgang Sourdeau * @@ -25,25 +25,25 @@ #import #import "MAPIStoreTypes.h" -#import "MAPIStoreFSMessage.h" +#import "MAPIStoreDBMessage.h" -#import "MAPIStoreFSMessageTable.h" +#import "MAPIStoreDBMessageTable.h" #undef DEBUG #include -static Class MAPIStoreFSMessageK = Nil; +static Class MAPIStoreDBMessageK = Nil; -@implementation MAPIStoreFSMessageTable +@implementation MAPIStoreDBMessageTable + (void) initialize { - MAPIStoreFSMessageK = [MAPIStoreFSMessage class]; + MAPIStoreDBMessageK = [MAPIStoreDBMessage class]; } + (Class) childObjectClass { - return MAPIStoreFSMessageK; + return MAPIStoreDBMessageK; } - (NSString *) backendIdentifierForProperty: (enum MAPITAGS) property diff --git a/OpenChange/MAPIStoreFAIMessage.h b/OpenChange/MAPIStoreFAIMessage.h index 4ac10e143..fd4d08930 100644 --- a/OpenChange/MAPIStoreFAIMessage.h +++ b/OpenChange/MAPIStoreFAIMessage.h @@ -23,9 +23,9 @@ #ifndef MAPISTOREFAIMESSAGE_H #define MAPISTOREFAIMESSAGE_H -#import "MAPIStoreFSMessage.h" +#import "MAPIStoreDBMessage.h" -@interface MAPIStoreFAIMessage : MAPIStoreFSMessage +@interface MAPIStoreFAIMessage : MAPIStoreDBMessage @end #endif /* MAPISTOREFAIMESSAGE_H */ diff --git a/OpenChange/MAPIStoreFAIMessageTable.h b/OpenChange/MAPIStoreFAIMessageTable.h index 70e12f43f..0aa6d7230 100644 --- a/OpenChange/MAPIStoreFAIMessageTable.h +++ b/OpenChange/MAPIStoreFAIMessageTable.h @@ -23,9 +23,9 @@ #ifndef MAPISTOREFAIMESSAGETABLE_H #define MAPISTOREFAIMESSAGETABLE_H -#import "MAPIStoreFSMessageTable.h" +#import "MAPIStoreDBMessageTable.h" -@interface MAPIStoreFAIMessageTable : MAPIStoreFSMessageTable +@interface MAPIStoreFAIMessageTable : MAPIStoreDBMessageTable @end #endif /* MAPISTOREFAIMESSAGETABLE_H */ diff --git a/OpenChange/MAPIStoreFSBaseContext.m b/OpenChange/MAPIStoreFSBaseContext.m deleted file mode 100644 index a3e448082..000000000 --- a/OpenChange/MAPIStoreFSBaseContext.m +++ /dev/null @@ -1,79 +0,0 @@ -/* MAPIStoreFSBaseContext.m - this file is part of SOGo - * - * Copyright (C) 2010 Inverse inc. - * - * Author: Wolfgang Sourdeau - * - * This file is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3, or (at your option) - * any later version. - * - * This file is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -/* A generic parent class for all context that will store their data on the - disk in the form of a plist. */ - -#import -#import - -#import - -#import "MAPIStoreFSFolder.h" -#import "MAPIStoreMapping.h" -#import "MAPIStoreUserContext.h" -#import "SOGoMAPIFSFolder.h" - -#import "MAPIStoreFSBaseContext.h" - -#undef DEBUG -#include - -static Class MAPIStoreFSFolderK; - -@implementation MAPIStoreFSBaseContext - -+ (void) initialize -{ - MAPIStoreFSFolderK = [MAPIStoreFSFolder class]; -} - -+ (NSString *) MAPIModuleName -{ - return nil; -} - -- (Class) MAPIStoreFolderClass -{ - return MAPIStoreFSFolderK; -} - -- (void) ensureContextFolder -{ - SOGoMAPIFSFolder *contextFolder; - - contextFolder = [SOGoMAPIFSFolder folderWithURL: contextUrl - andTableType: MAPISTORE_MESSAGE_TABLE]; - [contextFolder ensureDirectory]; -} - -- (id) rootSOGoFolder -{ - NSString *urlString; - - urlString = [NSString stringWithFormat: @"sogo://%@@%@/", - [userContext username], [isa MAPIModuleName]]; - return [SOGoMAPIFSFolder folderWithURL: [NSURL URLWithString: urlString] - andTableType: MAPISTORE_MESSAGE_TABLE]; -} - -@end diff --git a/OpenChange/MAPIStoreFallbackContext.h b/OpenChange/MAPIStoreFallbackContext.h index da8e03c0d..eaf992bb7 100644 --- a/OpenChange/MAPIStoreFallbackContext.h +++ b/OpenChange/MAPIStoreFallbackContext.h @@ -23,9 +23,9 @@ #ifndef MAPISTOREFALLBACKCONTEXT_H #define MAPISTOREFALLBACKCONTEXT_H -#import "MAPIStoreFSBaseContext.h" +#import "MAPIStoreDBBaseContext.h" -@interface MAPIStoreFallbackContext : MAPIStoreFSBaseContext +@interface MAPIStoreFallbackContext : MAPIStoreDBBaseContext @end diff --git a/OpenChange/MAPIStoreFallbackContext.m b/OpenChange/MAPIStoreFallbackContext.m index c5270d055..37da58d7d 100644 --- a/OpenChange/MAPIStoreFallbackContext.m +++ b/OpenChange/MAPIStoreFallbackContext.m @@ -26,7 +26,7 @@ #import "MAPIStoreUserContext.h" #import "NSString+MAPIStore.h" -#import "SOGoMAPIFSFolder.h" +#import "SOGoMAPIDBFolder.h" #import "MAPIStoreFallbackContext.h" @@ -51,10 +51,11 @@ inMemCtx: (TALLOC_CTX *) memCtx { struct mapistore_contexts_list *firstContext = NULL, *context; - SOGoMAPIFSFolder *root; + SOGoMAPIDBFolder *root; NSArray *names; NSUInteger count, max; NSString *baseURL, *url, *name; + MAPIStoreUserContext *userContext; baseURL = [NSString stringWithFormat: @"sogo://%@@fallback/", userName]; @@ -67,11 +68,15 @@ DLIST_ADD_END (firstContext, context, void); - /* Maybe emsmdbp_provisioning should be fixed in order to only take the uri returned above to avoid deleting its entries... */ - root = [SOGoMAPIFSFolder folderWithURL: [NSURL URLWithString: baseURL] - andTableType: MAPISTORE_MESSAGE_TABLE]; + root = [SOGoMAPIDBFolder objectWithName: [self MAPIModuleName] + inContainer: nil]; + [root setOwner: userName]; + userContext = [MAPIStoreUserContext userContextWithUsername: userName + andTDBIndexing: indexingTdb]; + [userContext ensureFolderTableExists]; + [root setTableUrl: [userContext folderTableURL]]; names = [root toManyRelationshipKeys]; max = [names count]; for (count = 0; count < max; count++) diff --git a/OpenChange/MAPIStoreFolder.h b/OpenChange/MAPIStoreFolder.h index 41b3bdd61..b29a93d3d 100644 --- a/OpenChange/MAPIStoreFolder.h +++ b/OpenChange/MAPIStoreFolder.h @@ -38,29 +38,34 @@ @class MAPIStoreMessageTable; @class MAPIStorePermissionsTable; @class SOGoFolder; -@class SOGoMAPIFSFolder; -@class SOGoMAPIFSMessage; +@class SOGoMAPIDBFolder; +@class SOGoMAPIDBMessage; -#import "MAPIStoreObject.h" +#import "MAPIStoreSOGoObject.h" -@interface MAPIStoreFolder : MAPIStoreObject +@interface MAPIStoreFolder : MAPIStoreSOGoObject { MAPIStoreContext *context; // NSArray *messageKeys; // NSArray *faiMessageKeys; // NSArray *folderKeys; - SOGoMAPIFSFolder *faiFolder; - SOGoMAPIFSFolder *propsFolder; - SOGoMAPIFSMessage *propsMessage; + SOGoMAPIDBFolder *dbFolder; + // SOGoMAPIDBFolder *faiFolder; + // SOGoMAPIDBFolder *propsFolder; + // SOGoMAPIDBMessage *propsMessage; } - (void) setContext: (MAPIStoreContext *) newContext; +- (void) setupAuxiliaryObjects; + +- (SOGoMAPIDBFolder *) dbFolder; + - (NSArray *) activeMessageTables; - (NSArray *) activeFAIMessageTables; -- (SOGoMAPIFSMessage *) propertiesMessage; +// - (SOGoMAPIDBMessage *) propertiesMessage; - (id) lookupMessageByURL: (NSString *) messageURL; - (id) lookupFolderByURL: (NSString *) folderURL; diff --git a/OpenChange/MAPIStoreFolder.m b/OpenChange/MAPIStoreFolder.m index 3e480f11d..f4128695d 100644 --- a/OpenChange/MAPIStoreFolder.m +++ b/OpenChange/MAPIStoreFolder.m @@ -48,8 +48,8 @@ #import "NSDate+MAPIStore.h" #import "NSString+MAPIStore.h" #import "NSObject+MAPIStore.h" -#import "SOGoMAPIFSFolder.h" -#import "SOGoMAPIFSMessage.h" +#import "SOGoMAPIDBFolder.h" +#import "SOGoMAPIDBMessage.h" #include @@ -79,33 +79,67 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe // messageKeys = nil; // faiMessageKeys = nil; // folderKeys = nil; - faiFolder = nil; + dbFolder = nil; context = nil; - propsFolder = nil; - propsMessage = nil; + // propsFolder = nil; + // propsMessage = nil; } return self; } -- (void) _setupAuxiliaryObjects +- (void) setupAuxiliaryObjects { - NSURL *propsURL; - NSString *urlString; + NSURL *folderURL; + NSMutableString *pathPrefix; + NSString *path, *folderName; + NSArray *parts; + NSUInteger lastPartIdx; + MAPIStoreUserContext *userContext; + + folderURL = [NSURL URLWithString: [self url]]; + path = [folderURL path]; + path = [path substringFromIndex: 1]; + if ([path length] > 0) + { + parts = [path componentsSeparatedByString: @"/"]; + lastPartIdx = [parts count] - 1; + if ([path hasSuffix: @"/"]) + lastPartIdx--; + folderName = [parts objectAtIndex: lastPartIdx]; + } + else + folderName = [folderURL host]; + + userContext = [self userContext]; + [userContext ensureFolderTableExists]; + + ASSIGN (dbFolder, + [SOGoMAPIDBFolder objectWithName: folderName + inContainer: [container dbFolder]]); + [dbFolder setTableUrl: [userContext folderTableURL]]; + if (!container && [path length] > 0) + { + pathPrefix = [NSMutableString stringWithCapacity: 64]; + [pathPrefix appendFormat: @"/%@", [folderURL host]]; + parts = [parts subarrayWithRange: NSMakeRange (0, lastPartIdx)]; + if ([parts count] > 0) + [pathPrefix appendFormat: @"/%@", [parts componentsJoinedByString: @"/"]]; + [dbFolder setPathPrefix: pathPrefix]; + } + [dbFolder reloadIfNeeded]; + + /* propsMessage and self share the same properties dictionary */ + // ASSIGN (propsMessage, + // [SOGoMAPIDBMessage objectWithName: @"properties.plist" + // inContainer: dbFolder]); + // [propsMessage setObjectType: MAPIDBObjectTypeInternal]; + // [propsMessage reloadIfNeeded]; + [properties release]; + properties = [dbFolder properties]; + [properties retain]; - urlString = [[self url] stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding]; - propsURL = [NSURL URLWithString: urlString]; - [self logWithFormat: @"_setupAuxiliaryObjects: %@", propsURL]; - ASSIGN (faiFolder, - [SOGoMAPIFSFolder folderWithURL: propsURL - andTableType: MAPISTORE_FAI_TABLE]); - ASSIGN (propsFolder, - [SOGoMAPIFSFolder folderWithURL: propsURL - andTableType: MAPISTORE_FOLDER_TABLE]); - ASSIGN (propsMessage, - [SOGoMAPIFSMessage objectWithName: @"properties.plist" - inContainer: propsFolder]); [self setupVersionsMessage]; } @@ -119,7 +153,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe inContainer: newContainer]) && newContainer) { - [self _setupAuxiliaryObjects]; + [self setupAuxiliaryObjects]; } return self; @@ -129,13 +163,13 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe { ASSIGN (context, newContext); if (newContext) - [self _setupAuxiliaryObjects]; + [self setupAuxiliaryObjects]; } - (MAPIStoreContext *) context { if (!context) - [self setContext: [container context]]; + [self setContext: (MAPIStoreContext *) [container context]]; return context; } @@ -145,29 +179,31 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe // [messageKeys release]; // [faiMessageKeys release]; // [folderKeys release]; - [propsMessage release]; - [propsFolder release]; - [faiFolder release]; + // [propsMessage release]; + [dbFolder release]; [context release]; [super dealloc]; } +- (SOGoMAPIDBFolder *) dbFolder +{ + return dbFolder; +} + /* backend interface */ -- (SOGoMAPIFSMessage *) propertiesMessage -{ - return propsMessage; -} +// - (SOGoMAPIDBMessage *) propertiesMessage +// { +// return propsMessage; +// } - (uint64_t) objectVersion { NSNumber *value; - NSDictionary *props; uint64_t cn; - props = [propsMessage properties]; - value = [props objectForKey: MAPIPropertyKey (PidTagChangeNumber)]; + value = [properties objectForKey: MAPIPropertyKey (PidTagChangeNumber)]; if (value) cn = [value unsignedLongLongValue]; else @@ -175,10 +211,10 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe [self logWithFormat: @"no value for PidTagChangeNumber, adding one now"]; cn = [[self context] getNewChangeNumber]; value = [NSNumber numberWithUnsignedLongLong: cn]; - props = [NSDictionary dictionaryWithObject: value - forKey: MAPIPropertyKey (PidTagChangeNumber)]; - [propsMessage appendProperties: props]; - [propsMessage save]; + + [properties setObject: value + forKey: MAPIPropertyKey (PidTagChangeNumber)]; + [dbFolder save]; } return cn >> 16; @@ -186,21 +222,24 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe - (id) lookupFolder: (NSString *) folderKey { - MAPIStoreFolder *childFolder = nil; + MAPIStoreFolder *childFolder; SOGoFolder *sogoFolder; WOContext *woContext; if ([[self folderKeys] containsObject: folderKey]) { woContext = [[self userContext] woContext]; - sogoFolder = [sogoObject lookupName: folderKey - inContext: woContext + sogoFolder = [sogoObject lookupName: folderKey inContext: woContext acquire: NO]; - [sogoFolder setContext: woContext]; if (sogoFolder && ![sogoFolder isKindOfClass: NSExceptionK]) - childFolder = [isa mapiStoreObjectWithSOGoObject: sogoFolder - inContainer: self]; + { + [sogoFolder setContext: woContext]; + childFolder = [isa mapiStoreObjectWithSOGoObject: sogoFolder + inContainer: self]; + } } + else + childFolder = nil; return childFolder; } @@ -264,9 +303,9 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe { if ([[self faiMessageKeys] containsObject: messageKey]) { - msgObject = [faiFolder lookupName: messageKey - inContext: nil - acquire: NO]; + msgObject = [dbFolder lookupName: messageKey + inContext: nil + acquire: NO]; childMessage = [MAPIStoreFAIMessageK mapiStoreObjectWithSOGoObject: msgObject inContainer: self]; @@ -383,9 +422,8 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe - (int) deleteFolder { - [propsMessage delete]; - [propsFolder delete]; - [faiFolder delete]; + // [propsMessage delete]; + [dbFolder delete]; [self cleanupCaches]; @@ -1004,7 +1042,11 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe /* TODO: this should no longer be required once mapistore v2 API is in place, when we can then do this from -dealloc below */ + [dbFolder reloadIfNeeded]; + propsCopy = [newProperties mutableCopy]; + [propsCopy autorelease]; + currentProp = bannedProps; while (*currentProp) { @@ -1012,9 +1054,8 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe currentProp++; } - [propsMessage appendProperties: propsCopy]; - [propsMessage save]; - [propsCopy release]; + [properties addEntriesFromDictionary: propsCopy]; + [dbFolder save]; } - (NSArray *) messageKeys @@ -1039,9 +1080,10 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe - (NSArray *) faiMessageKeysMatchingQualifier: (EOQualifier *) qualifier andSortOrderings: (NSArray *) sortOrderings { - return [faiFolder - toOneRelationshipKeysMatchingQualifier: qualifier - andSortOrderings: sortOrderings]; + return [dbFolder childKeysOfType: MAPIDBObjectTypeFAI + includeDeleted: NO + matchingQualifier: qualifier + andSortOrderings: sortOrderings]; } - (NSArray *) faiMessageKeys @@ -1287,6 +1329,19 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe return MAPISTORE_SUCCESS; } +- (int) getProperties: (struct mapistore_property_data *) data + withTags: (enum MAPITAGS *) tags + andCount: (uint16_t) columnCount + inMemCtx: (TALLOC_CTX *) memCtx +{ + [dbFolder reloadIfNeeded]; + + return [super getProperties: data + withTags: tags + andCount: columnCount + inMemCtx: memCtx]; +} + - (int) getProperty: (void **) data withTag: (enum MAPITAGS) propTag inMemCtx: (TALLOC_CTX *) memCtx @@ -1294,8 +1349,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe int rc; id value; - value = [[propsMessage properties] - objectForKey: MAPIPropertyKey (propTag)]; + value = [properties objectForKey: MAPIPropertyKey (propTag)]; if (value) rc = [value getValue: data forTag: propTag inMemCtx: memCtx]; else @@ -1307,13 +1361,15 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe - (MAPIStoreMessage *) _createAssociatedMessage { MAPIStoreMessage *newMessage; - SOGoMAPIFSMessage *fsObject; + SOGoMAPIDBMessage *dbObject; NSString *newKey; newKey = [NSString stringWithFormat: @"%@.plist", [SOGoObject globallyUniqueObjectId]]; - fsObject = [SOGoMAPIFSMessage objectWithName: newKey inContainer: faiFolder]; - newMessage = [MAPIStoreFAIMessageK mapiStoreObjectWithSOGoObject: fsObject + dbObject = [SOGoMAPIDBMessage objectWithName: newKey inContainer: dbFolder]; + [dbObject setObjectType: MAPIDBObjectTypeFAI]; + [dbObject setIsNew: YES]; + newMessage = [MAPIStoreFAIMessageK mapiStoreObjectWithSOGoObject: dbObject inContainer: self]; return newMessage; @@ -1328,9 +1384,15 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe newMessage = [self _createAssociatedMessage]; else newMessage = [self createMessage]; - [newMessage setIsNew: YES]; + /* FIXME: this is ugly as the specifics of message creation should all be + delegated to subclasses */ + if ([newMessage respondsToSelector: @selector (setIsNew:)]) + [newMessage setIsNew: YES]; woContext = [[self userContext] woContext]; - [[newMessage sogoObject] setContext: woContext]; + /* FIXME: this is ugly too as the specifics of message creation should all + be delegated to subclasses */ + if ([newMessage respondsToSelector: @selector (sogoObject:)]) + [[newMessage sogoObject] setContext: woContext]; return newMessage; } @@ -1598,12 +1660,12 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe - (NSDate *) creationTime { - return [propsMessage creationTime]; + return [dbFolder creationDate]; } - (NSDate *) lastModificationTime { - return [propsMessage lastModificationTime]; + return [dbFolder lastModified]; } /* subclasses */ diff --git a/OpenChange/MAPIStoreGCSFolder.h b/OpenChange/MAPIStoreGCSFolder.h index 0d0c1e6df..d889b78a9 100644 --- a/OpenChange/MAPIStoreGCSFolder.h +++ b/OpenChange/MAPIStoreGCSFolder.h @@ -34,7 +34,7 @@ @interface MAPIStoreGCSFolder : MAPIStoreFolder { - SOGoMAPIFSMessage *versionsMessage; + SOGoMAPIDBMessage *versionsMessage; NSArray *activeUserRoles; EOQualifier *componentQualifier; } diff --git a/OpenChange/MAPIStoreGCSFolder.m b/OpenChange/MAPIStoreGCSFolder.m index e5f6bbd65..b94f10ad5 100644 --- a/OpenChange/MAPIStoreGCSFolder.m +++ b/OpenChange/MAPIStoreGCSFolder.m @@ -40,7 +40,7 @@ #import "NSData+MAPIStore.h" #import "NSDate+MAPIStore.h" #import "NSString+MAPIStore.h" -#import "SOGoMAPIFSMessage.h" +#import "SOGoMAPIDBMessage.h" #import "MAPIStoreGCSFolder.h" @@ -71,8 +71,9 @@ static Class NSNumberK; - (void) setupVersionsMessage { ASSIGN (versionsMessage, - [SOGoMAPIFSMessage objectWithName: @"versions.plist" - inContainer: propsFolder]); + [SOGoMAPIDBMessage objectWithName: @"versions.plist" + inContainer: dbFolder]); + [versionsMessage setObjectType: MAPIDBObjectTypeInternal]; } - (void) dealloc @@ -288,7 +289,8 @@ static Class NSNumberK; forKey: @"PredecessorChangeList"]; [changeList release]; } - [changeList setObject: globCnt forKey: guid]; + [changeList setObject: globCnt + forKey: guid]; } - (EOQualifier *) componentQualifier @@ -349,6 +351,7 @@ static Class NSNumberK; [sortOrdering retain]; } + [versionsMessage reloadIfNeeded]; currentProperties = [versionsMessage properties]; lastModificationDate = [currentProperties objectForKey: @"SyncLastModificationDate"]; @@ -451,7 +454,6 @@ static Class NSNumberK; forKey: @"SyncLastSynchronisationDate"]; [currentProperties setObject: lastModificationDate forKey: @"SyncLastModificationDate"]; - [versionsMessage appendProperties: currentProperties]; [versionsMessage save]; } } diff --git a/OpenChange/MAPIStoreMailAttachment.h b/OpenChange/MAPIStoreMailAttachment.h index 5fded594c..f9f0011e9 100644 --- a/OpenChange/MAPIStoreMailAttachment.h +++ b/OpenChange/MAPIStoreMailAttachment.h @@ -30,9 +30,11 @@ @interface MAPIStoreMailAttachment : MAPIStoreAttachment { NSDictionary *bodyInfo; + SOGoMailBodyPart *bodyPart; } - (void) setBodyInfo: (NSDictionary *) newBodyInfo; +- (void) setBodyPart: (SOGoMailBodyPart *) newBodyPart; @end diff --git a/OpenChange/MAPIStoreMailAttachment.m b/OpenChange/MAPIStoreMailAttachment.m index 7b9641548..6834b8c1d 100644 --- a/OpenChange/MAPIStoreMailAttachment.m +++ b/OpenChange/MAPIStoreMailAttachment.m @@ -52,6 +52,7 @@ if ((self = [super init])) { bodyInfo = nil; + bodyPart = nil; } return self; @@ -60,6 +61,7 @@ - (void) dealloc { [bodyInfo release]; + [bodyPart release]; [super dealloc]; } @@ -68,6 +70,11 @@ ASSIGN (bodyInfo, newBodyInfo); } +- (void) setBodyPart: (SOGoMailBodyPart *) newBodyPart +{ + ASSIGN (bodyPart, newBodyPart); +} + - (int) getPidTagAttachMethod: (void **) data inMemCtx: (TALLOC_CTX *) memCtx { @@ -187,7 +194,7 @@ - (int) getPidTagAttachDataBinary: (void **) data inMemCtx: (TALLOC_CTX *) memCtx { - *data = [[sogoObject fetchBLOBWithPeek: YES] asBinaryInMemCtx: memCtx]; + *data = [[bodyPart fetchBLOBWithPeek: YES] asBinaryInMemCtx: memCtx]; return MAPISTORE_SUCCESS; } diff --git a/OpenChange/MAPIStoreMailFolder.h b/OpenChange/MAPIStoreMailFolder.h index 7e47e8c09..ab6430489 100644 --- a/OpenChange/MAPIStoreMailFolder.h +++ b/OpenChange/MAPIStoreMailFolder.h @@ -36,7 +36,7 @@ @interface MAPIStoreMailFolder : MAPIStoreFolder { - SOGoMAPIFSMessage *versionsMessage; + SOGoMAPIDBMessage *versionsMessage; } - (BOOL) ensureFolderExists; diff --git a/OpenChange/MAPIStoreMailFolder.m b/OpenChange/MAPIStoreMailFolder.m index f5cfe3340..8fffd8d9d 100644 --- a/OpenChange/MAPIStoreMailFolder.m +++ b/OpenChange/MAPIStoreMailFolder.m @@ -54,9 +54,8 @@ #import "MAPIStoreTypes.h" #import "NSData+MAPIStore.h" #import "NSString+MAPIStore.h" -#import "SOGoMAPIFSMessage.h" +#import "SOGoMAPIDBMessage.h" -#import "SOGoMAPIVolatileMessage.h" #import "MAPIStoreMailVolatileMessage.h" #import "MAPIStoreMailFolder.h" @@ -97,8 +96,9 @@ static Class SOGoMailFolderK, MAPIStoreOutboxFolderK; - (void) setupVersionsMessage { ASSIGN (versionsMessage, - [SOGoMAPIFSMessage objectWithName: @"versions.plist" - inContainer: propsFolder]); + [SOGoMAPIDBMessage objectWithName: @"versions.plist" + inContainer: dbFolder]); + [versionsMessage setObjectType: MAPIDBObjectTypeInternal]; } - (BOOL) ensureFolderExists @@ -119,6 +119,9 @@ static Class SOGoMailFolderK, MAPIStoreOutboxFolderK; && ![[(SOGoMailFolder *) sogoObject displayName] isEqualToString: newDisplayName]) { + [NSException raise: @"MAPIStoreIOException" + format: @"renaming a mail folder via OpenChange is" + @" currently a bad idea"]; [(SOGoMailFolder *) sogoObject renameTo: newDisplayName]; propsCopy = [newProperties mutableCopy]; [propsCopy removeObjectForKey: key]; @@ -489,10 +492,8 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data) now = [NSCalendarDate date]; [now setTimeZone: utcTZ]; - currentProperties = [[versionsMessage properties] mutableCopy]; - if (!currentProperties) - currentProperties = [NSMutableDictionary new]; - [currentProperties autorelease]; + [versionsMessage reloadIfNeeded]; + currentProperties = [versionsMessage properties]; messages = [currentProperties objectForKey: @"Messages"]; if (!messages) { @@ -613,7 +614,6 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data) ti = [NSNumber numberWithDouble: [now timeIntervalSince1970]]; [currentProperties setObject: ti forKey: @"SyncLastSynchronisationDate"]; - [versionsMessage appendProperties: currentProperties]; [versionsMessage save]; } @@ -1004,20 +1004,16 @@ _parseCOPYUID (NSString *line, NSArray **destUIDsP) - (MAPIStoreMessage *) createMessage { - MAPIStoreMailVolatileMessage *newMessage; - SOGoMAPIVolatileMessage *newObject; + SOGoMAPIObject *childObject; - newObject = [SOGoMAPIVolatileMessage - objectWithName: [SOGoObject globallyUniqueObjectId] - inContainer: sogoObject]; - newMessage - = [MAPIStoreMailVolatileMessage mapiStoreObjectWithSOGoObject: newObject - inContainer: self]; - - return newMessage; + childObject = [SOGoMAPIObject objectWithName: [SOGoMAPIObject + globallyUniqueObjectId] + inContainer: sogoObject]; + return [MAPIStoreMailVolatileMessage + mapiStoreObjectWithSOGoObject: childObject + inContainer: self]; } - - (NSArray *) rolesForExchangeRights: (uint32_t) rights { NSMutableArray *roles; diff --git a/OpenChange/MAPIStoreMailMessage.m b/OpenChange/MAPIStoreMailMessage.m index 85081c2db..8e8070ae2 100644 --- a/OpenChange/MAPIStoreMailMessage.m +++ b/OpenChange/MAPIStoreMailMessage.m @@ -373,7 +373,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data) if (uid) { changeNumber = [(MAPIStoreMailFolder *) container - changeNumberForMessageUID: uid]; + changeNumberForMessageUID: uid]; if (!changeNumber) { [self warnWithFormat: @"attempting to get change number" @@ -1529,8 +1529,8 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data) if (currentPart) { attachment = [MAPIStoreMailAttachment - mapiStoreObjectWithSOGoObject: currentPart - inContainer: self]; + mapiStoreObjectInContainer: self]; + [attachment setBodyPart: currentPart]; [attachment setBodyInfo: [attachmentParts objectForKey: childKey]]; [attachment setAID: [[self attachmentKeys] indexOfObject: childKey]]; } diff --git a/OpenChange/MAPIStoreMailMessageTable.m b/OpenChange/MAPIStoreMailMessageTable.m index 62dc406fd..c1561bbd5 100644 --- a/OpenChange/MAPIStoreMailMessageTable.m +++ b/OpenChange/MAPIStoreMailMessageTable.m @@ -332,7 +332,7 @@ static Class MAPIStoreMailMessageK, NSDataK, NSStringK; if (!fetchedCoreInfos) { fetchedCoreInfos = YES; - [(SOGoMailFolder *) [container sogoObject] + [(SOGoMailFolder *) [(MAPIStoreMailFolder *) container sogoObject] prefetchCoreInfosForMessageKeys: [self restrictedChildKeys]]; } diff --git a/OpenChange/MAPIStoreMailVolatileMessage.h b/OpenChange/MAPIStoreMailVolatileMessage.h index 82959cc6f..53a51847f 100644 --- a/OpenChange/MAPIStoreMailVolatileMessage.h +++ b/OpenChange/MAPIStoreMailVolatileMessage.h @@ -23,9 +23,9 @@ #ifndef MAPISTOREMAILVOLATILEMESSAGE_H #define MAPISTOREMAILVOLATILEMESSAGE_H -#import "MAPIStoreVolatileMessage.h" +#import "MAPIStoreMessage.h" -@interface MAPIStoreMailVolatileMessage : MAPIStoreVolatileMessage +@interface MAPIStoreMailVolatileMessage : MAPIStoreMessage - (int) submitWithFlags: (enum SubmitFlags) flags; diff --git a/OpenChange/MAPIStoreMailVolatileMessage.m b/OpenChange/MAPIStoreMailVolatileMessage.m index 3a3d66be3..b3bb2145f 100644 --- a/OpenChange/MAPIStoreMailVolatileMessage.m +++ b/OpenChange/MAPIStoreMailVolatileMessage.m @@ -51,6 +51,7 @@ #import #import "MAPIStoreAttachment.h" +#import "MAPIStoreAttachmentTable.h" #import "MAPIStoreContext.h" #import "MAPIStoreMailFolder.h" #import "MAPIStoreMIME.h" @@ -60,7 +61,7 @@ #import "NSData+MAPIStore.h" #import "NSObject+MAPIStore.h" #import "NSString+MAPIStore.h" -#import "SOGoMAPIVolatileMessage.h" +#import "SOGoMAPIObject.h" #import "MAPIStoreMailVolatileMessage.h" @@ -68,6 +69,8 @@ #include #include +static Class NSNumberK = Nil; + static NSString *recTypes[] = { @"orig", @"to", @"cc", @"bcc" }; // @@ -242,6 +245,106 @@ static NSString *recTypes[] = { @"orig", @"to", @"cc", @"bcc" }; @implementation MAPIStoreMailVolatileMessage ++ (void) initialize +{ + NSNumberK = [NSNumber class]; +} + +- (id) initWithSOGoObject: (id) newSOGoObject + inContainer: (MAPIStoreObject *) newContainer +{ + if ((self = [super initWithSOGoObject: newSOGoObject + inContainer: newContainer])) + { + ASSIGN (properties, [sogoObject properties]); + } + + return self; +} + +- (void) addProperties: (NSDictionary *) newProperties +{ + [super addProperties: newProperties]; + [sogoObject adjustLastModified]; +} + +- (BOOL) canGetProperty: (enum MAPITAGS) propTag +{ + return ([super canGetProperty: propTag] + || [properties objectForKey: MAPIPropertyKey (propTag)] != nil); +} + +- (uint64_t) objectVersion +{ + NSNumber *version; + + version = [properties objectForKey: @"version"]; + + return (version + ? exchange_globcnt ([version unsignedLongLongValue]) + : ULLONG_MAX); +} + +- (int) getPidTagSubject: (void **) data inMemCtx: (TALLOC_CTX *) memCtx +{ + /* if we get here, it means that the properties file didn't contain a + relevant value */ + return [self getEmptyString: data inMemCtx: memCtx]; +} + +- (int) getPidTagMessageClass: (void **) data inMemCtx: (TALLOC_CTX *) memCtx +{ + *data = [@"IPM.Note" asUnicodeInMemCtx: memCtx]; + + return MAPISTORE_SUCCESS; +} + +- (int) getPidTagChangeKey: (void **) data inMemCtx: (TALLOC_CTX *) memCtx +{ + NSData *changeKey; + int rc; + + changeKey = [properties objectForKey: MAPIPropertyKey (PR_CHANGE_KEY)]; + if (changeKey) + { + *data = [changeKey asBinaryInMemCtx: memCtx]; + rc = MAPISTORE_SUCCESS; + } + else + rc = [super getPidTagChangeKey: data inMemCtx: memCtx]; + + return rc; +} + +- (NSArray *) attachmentsKeysMatchingQualifier: (EOQualifier *) qualifier + andSortOrderings: (NSArray *) sortOrderings +{ + NSDictionary *attachments; + + attachments = [properties objectForKey: @"attachments"]; + + return [attachments allKeys]; +} + +- (NSDate *) creationTime +{ + return [sogoObject creationDate]; +} + +- (NSDate *) lastModificationTime +{ + return [sogoObject lastModified]; +} + +- (id) lookupAttachment: (NSString *) childKey +{ + NSDictionary *attachments; + + attachments = [properties objectForKey: @"attachments"]; + + return [attachments objectForKey: childKey]; +} + - (void) getMessageData: (struct mapistore_message **) dataPtr inMemCtx: (TALLOC_CTX *) memCtx { @@ -258,9 +361,11 @@ static NSString *recTypes[] = { @"orig", @"to", @"cc", @"bcc" }; samCtx = [[self context] connectionInfo]->sam_ctx; - [super getMessageData: &msgData inMemCtx: memCtx]; + // [super getMessageData: &msgData inMemCtx: memCtx]; - allRecipients = [[sogoObject properties] objectForKey: @"recipients"]; + msgData = talloc_zero (memCtx, struct mapistore_message); + + allRecipients = [properties objectForKey: @"recipients"]; msgData->columns = set_SPropTagArray (msgData, 9, PR_OBJECT_TYPE, PR_DISPLAY_TYPE, @@ -660,10 +765,13 @@ MakeTextPartBody (NSDictionary *mailProperties, NSDictionary *attachmentParts, return textBody; } +// static id +// MakeMessageBody (NSDictionary *mailProperties, NSDictionary *attachmentParts, +// NSString **contentType) static id -MakeMessageBody (NSDictionary *mailProperties, NSDictionary *attachmentParts, - NSString **contentType) +MakeMessageBody (NSDictionary *mailProperties, NSString **contentType) { + NSDictionary *attachmentParts; id messageBody, textBody; NSString *textContentType; NSArray *parts; @@ -671,6 +779,7 @@ MakeMessageBody (NSDictionary *mailProperties, NSDictionary *attachmentParts, NGMutableHashMap *headers; NSUInteger count, max; + attachmentParts = [mailProperties objectForKey: @"attachments"]; textBody = MakeTextPartBody (mailProperties, attachmentParts, &textContentType); @@ -707,22 +816,20 @@ MakeMessageBody (NSDictionary *mailProperties, NSDictionary *attachmentParts, - (NGMimeMessage *) _generateMessage { - NSDictionary *mailProperties; NSString *contentType; NGMimeMessage *message; NGMutableHashMap *headers; id messageBody; - mailProperties = [sogoObject properties]; - headers = [[NGMutableHashMap alloc] initWithCapacity: 16]; - FillMessageHeadersFromProperties (headers, mailProperties, + FillMessageHeadersFromProperties (headers, properties, [[self context] connectionInfo]); message = [[NGMimeMessage alloc] initWithHeader: headers]; [message autorelease]; [headers release]; - messageBody = MakeMessageBody (mailProperties, attachmentParts, &contentType); + messageBody = MakeMessageBody (properties, &contentType); + // messageBody = MakeMessageBody (mailProperties, attachmentParts, &contentType); if (messageBody) { [headers setObject: contentType forKey: @"content-type"]; @@ -775,7 +882,7 @@ MakeMessageBody (NSDictionary *mailProperties, NSDictionary *attachmentParts, - (int) submitWithFlags: (enum SubmitFlags) flags { - NSDictionary *mailProperties, *recipients; + NSDictionary *recipients; NSData *messageData; NSMutableArray *recipientEmails; NSArray *list; @@ -785,19 +892,17 @@ MakeMessageBody (NSDictionary *mailProperties, NSDictionary *attachmentParts, // SOGoMailFolder *sentFolder; SOGoDomainDefaults *dd; NSException *error; - MAPIStoreMapping *mapping; + // MAPIStoreMapping *mapping; - mailProperties = [sogoObject properties]; - msgClass = [mailProperties objectForKey: MAPIPropertyKey (PidTagMessageClass)]; + msgClass = [properties objectForKey: MAPIPropertyKey (PidTagMessageClass)]; if ([msgClass isEqualToString: @"IPM.Note"]) /* we skip invitation replies */ { /* send mail */ messageData = [self _generateMailDataWithBcc: NO]; - mailProperties = [sogoObject properties]; recipientEmails = [NSMutableArray arrayWithCapacity: 32]; - recipients = [mailProperties objectForKey: @"recipients"]; + recipients = [properties objectForKey: @"recipients"]; for (count = 0; count < 3; count++) { recId = recTypes[count]; @@ -819,11 +924,11 @@ MakeMessageBody (NSDictionary *mailProperties, NSDictionary *attachmentParts, if (error) [self logWithFormat: @"an error occurred: '%@'", error]; - mapping = [self mapping]; - [mapping unregisterURLWithID: [self objectId]]; - [self setIsNew: NO]; - [properties removeAllObjects]; - [[self container] cleanupCaches]; + // mapping = [self mapping]; + // [mapping unregisterURLWithID: [self objectId]]; + // [self setIsNew: NO]; + // [properties removeAllObjects]; + [(MAPIStoreMailFolder *) [self container] cleanupCaches]; } else [self logWithFormat: @"skipping submit of message with class '%@'", @@ -834,14 +939,14 @@ MakeMessageBody (NSDictionary *mailProperties, NSDictionary *attachmentParts, - (void) save { - NSString *folderName, *flag, *newIdString; + NSString *folderName, *flag, *newIdString, *messageKey; NSData *changeKey, *messageData; NGImap4Connection *connection; NGImap4Client *client; SOGoMailFolder *containerFolder; NSDictionary *result, *responseResult; - MAPIStoreMapping *mapping; - uint64_t mid; + // MAPIStoreMapping *mapping; + // uint64_t mid; messageData = [self _generateMailDataWithBcc: YES]; @@ -860,21 +965,24 @@ MakeMessageBody (NSDictionary *mailProperties, NSDictionary *attachmentParts, flag = [responseResult objectForKey: @"flag"]; newIdString = [[flag componentsSeparatedByString: @" "] objectAtIndex: 2]; - mid = [self objectId]; - mapping = [self mapping]; - [mapping unregisterURLWithID: mid]; - [sogoObject setNameInContainer: [NSString stringWithFormat: @"%@.eml", newIdString]]; - [mapping registerURL: [self url] withID: mid]; - } + // mid = [self objectId]; + // mapping = [self mapping]; + // [mapping unregisterURLWithID: mid]; + // [sogoObject setNameInContainer: ]; + messageKey = [NSString stringWithFormat: @"%@.eml", newIdString]; + // [mapping registerURL: [NSString stringWithFormat: @"%@%@", + // [(MAPIStoreMailFolder *) container url], messageKey] + // withID: mid]; - /* synchronise the cache and update the change key with the one provided by - the client */ - [(MAPIStoreMailFolder *) container synchroniseCache]; - changeKey = [[sogoObject properties] - objectForKey: MAPIPropertyKey (PR_CHANGE_KEY)]; - if (changeKey) - [(MAPIStoreMailFolder *) container - setChangeKey: changeKey forMessageWithKey: [self nameInContainer]]; + /* synchronise the cache and update the change key with the one provided + by the client */ + [(MAPIStoreMailFolder *) container synchroniseCache]; + changeKey = [properties objectForKey: MAPIPropertyKey (PR_CHANGE_KEY)]; + if (changeKey) + [(MAPIStoreMailFolder *) container + setChangeKey: changeKey + forMessageWithKey: messageKey]; + } } @end diff --git a/OpenChange/MAPIStoreMessage.h b/OpenChange/MAPIStoreMessage.h index d1494c060..b7ba24ae7 100644 --- a/OpenChange/MAPIStoreMessage.h +++ b/OpenChange/MAPIStoreMessage.h @@ -35,9 +35,9 @@ @class MAPIStoreAttachmentTable; @class MAPIStoreFolder; -#import "MAPIStoreObject.h" +#import "MAPIStoreSOGoObject.h" -@interface MAPIStoreMessage : MAPIStoreObject +@interface MAPIStoreMessage : MAPIStoreSOGoObject { NSArray *attachmentKeys; NSMutableDictionary *attachmentParts; diff --git a/OpenChange/MAPIStoreMessage.m b/OpenChange/MAPIStoreMessage.m index 35ea1036d..0d42eb048 100644 --- a/OpenChange/MAPIStoreMessage.m +++ b/OpenChange/MAPIStoreMessage.m @@ -36,6 +36,7 @@ #import "MAPIStoreAttachmentTable.h" #import "MAPIStoreContext.h" #import "MAPIStoreFolder.h" +#import "MAPIStoreMessageTable.h" #import "MAPIStorePropertySelectors.h" #import "MAPIStoreSamDBUtils.h" #import "MAPIStoreTypes.h" @@ -116,7 +117,6 @@ rtf2html (NSData *compressedRTF) @interface SOGoObject (MAPIStoreProtocol) -- (NSString *) davEntityTag; - (NSString *) davContentLength; @end @@ -304,6 +304,7 @@ rtf2html (NSData *compressedRTF) NSData *htmlData, *rtfData; static NSNumber *htmlKey = nil, *rtfKey = nil; + /* we intercept any RTF content and convert it to HTML */ [super addProperties: newNewProperties]; if (!htmlKey) @@ -339,10 +340,8 @@ rtf2html (NSData *compressedRTF) newAid = [[self attachmentKeys] count]; - newAttachment = [MAPIStoreAttachment - mapiStoreObjectWithSOGoObject: nil - inContainer: self]; - [newAttachment setIsNew: YES]; + newAttachment = [MAPIStoreAttachment mapiStoreObjectInContainer: self]; + // [newAttachment setIsNew: YES]; [newAttachment setAID: newAid]; newKey = [NSString stringWithFormat: @"%ul", newAid]; [attachmentParts setObject: newAttachment @@ -497,7 +496,6 @@ rtf2html (NSData *compressedRTF) [[containerTables objectAtIndex: count] notifyChangesForChild: self]; [self setIsNew: NO]; - [properties removeAllObjects]; [container cleanupCaches]; rc = MAPISTORE_SUCCESS; } @@ -792,7 +790,7 @@ rtf2html (NSData *compressedRTF) - (int) getPidTagOriginalMessageClass: (void **) data inMemCtx: (TALLOC_CTX *) memCtx { - return [self getPidTagMessageClass: data inMemCtx: memCtx]; + return [self getProperty: data withTag: PidTagMessageClass inMemCtx: memCtx]; } - (int) getPidTagHasAttachments: (void **) data diff --git a/OpenChange/MAPIStoreMessageTable.h b/OpenChange/MAPIStoreMessageTable.h index 7c3935bba..0779afaef 100644 --- a/OpenChange/MAPIStoreMessageTable.h +++ b/OpenChange/MAPIStoreMessageTable.h @@ -28,6 +28,7 @@ @interface MAPIStoreMessageTable : MAPIStoreTable - (void) setSortOrder: (const struct SSortOrderSet *) set; +- (void) notifyChangesForChild: (MAPIStoreMessage *) child; @end diff --git a/OpenChange/MAPIStoreMessageTable.m b/OpenChange/MAPIStoreMessageTable.m index 33703dc23..3c08e2bab 100644 --- a/OpenChange/MAPIStoreMessageTable.m +++ b/OpenChange/MAPIStoreMessageTable.m @@ -27,6 +27,7 @@ #import #import +#import "MAPIStoreContext.h" #import "MAPIStoreFolder.h" #import "MAPIStoreTypes.h" #import "NSData+MAPIStore.h" @@ -83,4 +84,60 @@ return [(MAPIStoreFolder *) container lookupMessage: childKey]; } +- (void) notifyChangesForChild: (MAPIStoreMessage *) child +{ + NSUInteger currentChildRow, newChildRow; + NSArray *list; + NSString *childName; + struct mapistore_table_notification_parameters notif_parameters; + struct mapistore_context *mstoreCtx; + + mstoreCtx = [[(MAPIStoreFolder *) container context] + connectionInfo]->mstore_ctx; + + notif_parameters.table_type = tableType; + notif_parameters.handle = handleId; + notif_parameters.folder_id = [(MAPIStoreFolder *) container objectId]; + notif_parameters.object_id = [child objectId]; + notif_parameters.instance_id = 0; /* TODO: always 0 ? */ + + childName = [child nameInContainer]; + list = [self restrictedChildKeys]; + currentChildRow = [list indexOfObject: childName]; + notif_parameters.row_id = currentChildRow; + + [self cleanupCaches]; + list = [self restrictedChildKeys]; + newChildRow = [list indexOfObject: childName]; + + if (currentChildRow == NSNotFound) + { + if (newChildRow != NSNotFound) + { + notif_parameters.row_id = newChildRow; + mapistore_push_notification (mstoreCtx, + MAPISTORE_TABLE, + MAPISTORE_OBJECT_CREATED, + ¬if_parameters); + } + } + else + { + if (newChildRow == NSNotFound) + mapistore_push_notification (mstoreCtx, + MAPISTORE_TABLE, + MAPISTORE_OBJECT_DELETED, + ¬if_parameters); + else + { + /* the fact that the row order has changed has no impact here */ + notif_parameters.row_id = newChildRow; + mapistore_push_notification (mstoreCtx, + MAPISTORE_TABLE, + MAPISTORE_OBJECT_MODIFIED, + ¬if_parameters); + } + } +} + @end diff --git a/OpenChange/MAPIStoreNotesContext.h b/OpenChange/MAPIStoreNotesContext.h index ad4a581e1..3f44d575d 100644 --- a/OpenChange/MAPIStoreNotesContext.h +++ b/OpenChange/MAPIStoreNotesContext.h @@ -23,9 +23,9 @@ #ifndef MAPISTORENOTESCONTEXT_H #define MAPISTORENOTESCONTEXT_H -#import "MAPIStoreFSBaseContext.h" +#import "MAPIStoreDBBaseContext.h" -@interface MAPIStoreNotesContext : MAPIStoreFSBaseContext +@interface MAPIStoreNotesContext : MAPIStoreDBBaseContext @end diff --git a/OpenChange/MAPIStoreNotesFolder.h b/OpenChange/MAPIStoreNotesFolder.h index de2eb2748..9baebab27 100644 --- a/OpenChange/MAPIStoreNotesFolder.h +++ b/OpenChange/MAPIStoreNotesFolder.h @@ -23,9 +23,9 @@ #ifndef MAPISTORENOTESFOLDER_H #define MAPISTORENOTESFOLDER_H -#import "MAPIStoreFSFolder.h" +#import "MAPIStoreDBFolder.h" -@interface MAPIStoreNotesFolder : MAPIStoreFSFolder +@interface MAPIStoreNotesFolder : MAPIStoreDBFolder @end #endif /* MAPISTORENOTESFOLDER_H */ diff --git a/OpenChange/MAPIStoreNotesMessage.h b/OpenChange/MAPIStoreNotesMessage.h index 81adfc0dc..d4ec7e1ba 100644 --- a/OpenChange/MAPIStoreNotesMessage.h +++ b/OpenChange/MAPIStoreNotesMessage.h @@ -23,9 +23,9 @@ #ifndef MAPISTORENOTESMESSAGE_H #define MAPISTORENOTESMESSAGE_H -#import "MAPIStoreFSMessage.h" +#import "MAPIStoreDBMessage.h" -@interface MAPIStoreNotesMessage : MAPIStoreFSMessage +@interface MAPIStoreNotesMessage : MAPIStoreDBMessage @end #endif /* MAPISTORENOTESMESSAGE_H */ diff --git a/OpenChange/MAPIStoreNotesMessage.m b/OpenChange/MAPIStoreNotesMessage.m index d59d9a2f8..7cac637b8 100644 --- a/OpenChange/MAPIStoreNotesMessage.m +++ b/OpenChange/MAPIStoreNotesMessage.m @@ -53,21 +53,4 @@ return MAPISTORE_SUCCESS; } -- (int) getPidTagSubject: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - id value; - int rc; - - value = [[sogoObject properties] - objectForKey: MAPIPropertyKey (PidTagNormalizedSubject)]; - if (value) - rc = [value getValue: data forTag: PidTagNormalizedSubject - inMemCtx: memCtx]; - else - rc = MAPISTORE_ERR_NOT_FOUND; - - return rc; -} - @end diff --git a/OpenChange/MAPIStoreObject.h b/OpenChange/MAPIStoreObject.h index cb7ed7809..709396e9f 100644 --- a/OpenChange/MAPIStoreObject.h +++ b/OpenChange/MAPIStoreObject.h @@ -33,56 +33,38 @@ @class NSMutableArray; @class NSMutableDictionary; -@class EOQualifier; - @class MAPIStoreContext; -@class MAPIStoreFolder; @class MAPIStoreMapping; -@class MAPIStoreTable; @class MAPIStoreUserContext; +@class MAPIStoreSOGoObject; @interface MAPIStoreObject : NSObject { const IMP *classGetters; NSMutableArray *parentContainersBag; - MAPIStoreObject *container; - id sogoObject; + id container; NSMutableDictionary *properties; - BOOL isNew; } -+ (id) mapiStoreObjectWithSOGoObject: (id) newSOGoObject - inContainer: (MAPIStoreObject *) newContainer; ++ (id) mapiStoreObjectInContainer: (MAPIStoreObject *) newContainer; +- (id) initInContainer: (MAPIStoreObject *) newContainer; + + (int) getAvailableProperties: (struct SPropTagArray **) propertiesP inMemCtx: (TALLOC_CTX *) memCtx; -- (id) initWithSOGoObject: (id) newSOGoObject - inContainer: (MAPIStoreObject *) newFolder; - -- (void) setIsNew: (BOOL) newIsNew; -- (BOOL) isNew; - -- (NSString *) nameInContainer; - -- (id) sogoObject; - (MAPIStoreObject *) container; - (MAPIStoreContext *) context; - (MAPIStoreUserContext *) userContext; - (MAPIStoreMapping *) mapping; -- (void) cleanupCaches; - -- (uint64_t) objectId; -- (NSString *) url; - /* properties */ - (BOOL) canGetProperty: (enum MAPITAGS) propTag; - (void) addProperties: (NSDictionary *) newProperties; -- (NSDictionary *) properties; +- (NSMutableDictionary *) properties; /* ops */ - (int) getAvailableProperties: (struct SPropTagArray **) propertiesP @@ -104,26 +86,12 @@ fromGlobCnt: (uint64_t) objectCnt inMemCtx: (TALLOC_CTX *) memCtx; -/* implemented getters */ -- (int) getPidTagDisplayName: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx; -- (int) getPidTagSearchKey: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx; -- (int) getPidTagGenerateExchangeViews: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx; -- (int) getPidTagParentSourceKey: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx; -- (int) getPidTagSourceKey: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx; -- (int) getPidTagChangeKey: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx; - (int) getPidTagCreationTime: (void **) data inMemCtx: (TALLOC_CTX *) memCtx; - (int) getPidTagLastModificationTime: (void **) data inMemCtx: (TALLOC_CTX *) memCtx; /* subclasses */ -- (uint64_t) objectVersion; - (NSDate *) creationTime; - (NSDate *) lastModificationTime; diff --git a/OpenChange/MAPIStoreObject.m b/OpenChange/MAPIStoreObject.m index e3d379d6b..ac782cdc8 100644 --- a/OpenChange/MAPIStoreObject.m +++ b/OpenChange/MAPIStoreObject.m @@ -58,13 +58,11 @@ static Class NSExceptionK, MAPIStoreFolderK; MAPIStoreFolderK = [MAPIStoreFolder class]; } -+ (id) mapiStoreObjectWithSOGoObject: (id) newSOGoObject - inContainer: (MAPIStoreObject *) newContainer ++ (id) mapiStoreObjectInContainer: (MAPIStoreObject *) newContainer { id newObject; - newObject = [[self alloc] initWithSOGoObject: newSOGoObject - inContainer: newContainer]; + newObject = [[self alloc] initInContainer: newContainer]; [newObject autorelease]; return newObject; @@ -106,22 +104,18 @@ static Class NSExceptionK, MAPIStoreFolderK; classGetters = (IMP *) MAPIStorePropertyGettersForClass (isa); parentContainersBag = [NSMutableArray new]; container = nil; - sogoObject = nil; properties = [NSMutableDictionary new]; - isNew = NO; } - [self logWithFormat: @"-init"]; + // [self logWithFormat: @"-init"]; return self; } -- (id) initWithSOGoObject: (id) newSOGoObject - inContainer: (MAPIStoreObject *) newContainer +- (id) initInContainer: (MAPIStoreObject *) newContainer { if ((self = [self init])) { - ASSIGN (sogoObject, newSOGoObject); ASSIGN (container, newContainer); } @@ -130,42 +124,21 @@ static Class NSExceptionK, MAPIStoreFolderK; - (void) dealloc { - [self logWithFormat: @"-dealloc"]; - [sogoObject release]; + // [self logWithFormat: @"-dealloc"]; [properties release]; [parentContainersBag release]; [container release]; [super dealloc]; } -- (void) setIsNew: (BOOL) newIsNew -{ - isNew = newIsNew; -} - -- (BOOL) isNew -{ - return isNew; -} - -- (id) sogoObject -{ - return sogoObject; -} - - (MAPIStoreObject *) container { return container; } -- (NSString *) nameInContainer -{ - return [sogoObject nameInContainer]; -} - - (MAPIStoreContext *) context { - return [container context]; + return (MAPIStoreContext *) [container context]; } - (MAPIStoreUserContext *) userContext @@ -178,47 +151,14 @@ static Class NSExceptionK, MAPIStoreFolderK; return [[self userContext] mapping]; } -- (void) cleanupCaches -{ -} - /* helpers */ -- (uint64_t) objectId -{ - uint64_t objectId; - - if ([container isKindOfClass: MAPIStoreFolderK]) - objectId = [(MAPIStoreFolder *) container - idForObjectWithKey: [sogoObject nameInContainer]]; - else - { - [self errorWithFormat: @"%s: container is not a folder", __PRETTY_FUNCTION__]; - objectId = (uint64_t) -1; - } - - return objectId; -} - -- (NSString *) url -{ - NSString *containerURL, *format; - - containerURL = [container url]; - if ([containerURL hasSuffix: @"/"]) - format = @"%@%@"; - else - format = @"%@/%@"; - - return [NSString stringWithFormat: format, - containerURL, [self nameInContainer]]; -} - (void) addProperties: (NSDictionary *) newNewProperties { [properties addEntriesFromDictionary: newNewProperties]; } -- (NSDictionary *) properties +- (NSMutableDictionary *) properties { return properties; } @@ -248,124 +188,8 @@ static Class NSExceptionK, MAPIStoreFolderK; return rc; } -/* helper getters */ -- (NSData *) getReplicaKeyFromGlobCnt: (uint64_t) objectCnt -{ - struct mapistore_connection_info *connInfo; - NSMutableData *replicaKey; - char buffer[6]; - NSUInteger count; - - connInfo = [[self context] connectionInfo]; - - for (count = 0; count < 6; count++) - { - buffer[count] = objectCnt & 0xff; - objectCnt >>= 8; - } - - replicaKey = [NSMutableData dataWithCapacity: 22]; - [replicaKey appendBytes: &connInfo->replica_guid - length: sizeof (struct GUID)]; - [replicaKey appendBytes: buffer length: 6]; - - return replicaKey; -} - -- (int) getReplicaKey: (void **) data - fromGlobCnt: (uint64_t) objectCnt - inMemCtx: (TALLOC_CTX *) memCtx -{ - *data = [[self getReplicaKeyFromGlobCnt: objectCnt] asBinaryInMemCtx: memCtx]; - - return MAPISTORE_SUCCESS; -} - -/* getters */ -- (int) getPidTagDisplayName: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - *data = [[sogoObject displayName] asUnicodeInMemCtx: memCtx]; - - return MAPISTORE_SUCCESS; -} - -- (int) getPidTagSearchKey: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - NSString *stringValue; - - stringValue = [sogoObject nameInContainer]; - *data = [[stringValue dataUsingEncoding: NSASCIIStringEncoding] - asBinaryInMemCtx: memCtx]; - - return MAPISTORE_SUCCESS; -} - -- (int) getPidTagGenerateExchangeViews: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - return [self getNo: data inMemCtx: memCtx]; -} - -- (int) getPidTagParentSourceKey: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - return [self getReplicaKey: data fromGlobCnt: [container objectId] >> 16 - inMemCtx: memCtx]; -} - -- (int) getPidTagSourceKey: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - return [self getReplicaKey: data fromGlobCnt: [self objectId] >> 16 - inMemCtx: memCtx]; -} - -- (uint64_t) objectVersion -{ - [self subclassResponsibility: _cmd]; - - return ULLONG_MAX; -} - -- (int) getPidTagChangeKey: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - int rc; - uint64_t obVersion; - - obVersion = [self objectVersion]; - if (obVersion == ULLONG_MAX) - rc = MAPISTORE_ERR_NOT_FOUND; - else - rc = [self getReplicaKey: data fromGlobCnt: obVersion - inMemCtx: memCtx]; - - return rc; -} - -- (int) getPidTagChangeNumber: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - int rc; - uint64_t obVersion; - - obVersion = [self objectVersion]; - if (obVersion == ULLONG_MAX) - rc = MAPISTORE_ERR_NOT_FOUND; - else - { - *data = MAPILongLongValue (memCtx, ((obVersion << 16) - | 0x0001)); - rc = MAPISTORE_SUCCESS; - } - - return rc; -} - - (int) getPidTagCreationTime: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx + inMemCtx: (TALLOC_CTX *) memCtx { *data = [[self creationTime] asFileTimeInMemCtx: memCtx]; @@ -474,6 +298,38 @@ static Class NSExceptionK, MAPIStoreFolderK; return MAPISTORE_SUCCESS; } +- (NSData *) getReplicaKeyFromGlobCnt: (uint64_t) objectCnt +{ + struct mapistore_connection_info *connInfo; + NSMutableData *replicaKey; + char buffer[6]; + NSUInteger count; + + connInfo = [[self context] connectionInfo]; + + for (count = 0; count < 6; count++) + { + buffer[count] = objectCnt & 0xff; + objectCnt >>= 8; + } + + replicaKey = [NSMutableData dataWithCapacity: 22]; + [replicaKey appendBytes: &connInfo->replica_guid + length: sizeof (struct GUID)]; + [replicaKey appendBytes: buffer length: 6]; + + return replicaKey; +} + +- (int) getReplicaKey: (void **) data + fromGlobCnt: (uint64_t) objectCnt + inMemCtx: (TALLOC_CTX *) memCtx +{ + *data = [[self getReplicaKeyFromGlobCnt: objectCnt] asBinaryInMemCtx: memCtx]; + + return MAPISTORE_SUCCESS; +} + /* subclasses */ - (NSDate *) creationTime { diff --git a/OpenChange/MAPIStorePermissionsTable.m b/OpenChange/MAPIStorePermissionsTable.m index 6d4c57c55..2385ee8a5 100644 --- a/OpenChange/MAPIStorePermissionsTable.m +++ b/OpenChange/MAPIStorePermissionsTable.m @@ -50,7 +50,7 @@ MAPIStorePermissionEntry *newEntry; newEntry = [[self alloc] initWithUserId: newUserId andMemberId: newMemberId - forFolder: newFolder]; + forFolder: newFolder]; [newEntry autorelease]; return newEntry; @@ -60,7 +60,7 @@ andMemberId: (uint64_t) newMemberId forFolder: (MAPIStoreFolder *) newFolder { - if ((self = [self initWithSOGoObject: nil inContainer: newFolder])) + if ((self = [self initInContainer: newFolder])) { ASSIGN (userId, newUserId); memberId = newMemberId; diff --git a/OpenChange/MAPIStoreSOGoObject.h b/OpenChange/MAPIStoreSOGoObject.h new file mode 100644 index 000000000..90524558f --- /dev/null +++ b/OpenChange/MAPIStoreSOGoObject.h @@ -0,0 +1,89 @@ +/* MAPIStoreObject.h - this file is part of SOGo + * + * Copyright (C) 2011 Inverse inc + * + * Author: Wolfgang Sourdeau + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef MAPISTORESOGOOBJECT_H +#define MAPISTORESOGOOBJECT_H + +#include + +#import "MAPIStoreObject.h" + +@class NSDate; +@class NSData; +@class NSString; +@class NSMutableArray; +@class NSMutableDictionary; + +@class EOQualifier; + +@class MAPIStoreContext; +@class MAPIStoreFolder; +@class MAPIStoreMapping; +@class MAPIStoreTable; +@class MAPIStoreUserContext; + +@interface MAPIStoreSOGoObject : MAPIStoreObject +{ + id sogoObject; + BOOL isNew; +} + ++ (id) mapiStoreObjectWithSOGoObject: (id) newSOGoObject + inContainer: (MAPIStoreObject *) newContainer; + +- (id) initWithSOGoObject: (id) newSOGoObject + inContainer: (MAPIStoreObject *) newFolder; + +- (void) setIsNew: (BOOL) newIsNew; +- (BOOL) isNew; + +- (id) sogoObject; + +- (NSString *) nameInContainer; + +- (MAPIStoreObject *) container; + +- (void) cleanupCaches; + +- (uint64_t) objectId; +- (NSString *) url; + +/* implemented getters */ +- (int) getPidTagDisplayName: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx; +- (int) getPidTagSearchKey: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx; +- (int) getPidTagGenerateExchangeViews: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx; +- (int) getPidTagParentSourceKey: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx; +- (int) getPidTagSourceKey: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx; +- (int) getPidTagChangeKey: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx; + +/* subclasses */ +- (uint64_t) objectVersion; + +@end + +#endif /* MAPISTORESOGOOBJECT_H */ diff --git a/OpenChange/MAPIStoreSOGoObject.m b/OpenChange/MAPIStoreSOGoObject.m new file mode 100644 index 000000000..d2d3b4790 --- /dev/null +++ b/OpenChange/MAPIStoreSOGoObject.m @@ -0,0 +1,255 @@ +/* MAPIStoreObject.m - this file is part of SOGo + * + * Copyright (C) 2011 Inverse inc + * + * Author: Wolfgang Sourdeau + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#import +#import +#import +#import +#import +#import +#import +#import + +#import "MAPIStoreContext.h" +#import "MAPIStoreFolder.h" +#import "MAPIStorePropertySelectors.h" +#import "MAPIStoreTypes.h" +#import "MAPIStoreUserContext.h" +#import "NSDate+MAPIStore.h" +#import "NSData+MAPIStore.h" +#import "NSObject+MAPIStore.h" +#import "NSString+MAPIStore.h" + +#import "MAPIStoreSOGoObject.h" + +#undef DEBUG +#include +#include +#include +#include +#include + +@implementation MAPIStoreSOGoObject + +static Class MAPIStoreFolderK; + ++ (void) initialize +{ + MAPIStoreFolderK = [MAPIStoreFolder class]; +} + ++ (id) mapiStoreObjectWithSOGoObject: (id) newSOGoObject + inContainer: (MAPIStoreObject *) newContainer +{ + id newObject; + + newObject = [[self alloc] initWithSOGoObject: newSOGoObject + inContainer: newContainer]; + [newObject autorelease]; + + return newObject; +} + +- (id) init +{ + if ((self = [super init])) + { + sogoObject = nil; + isNew = NO; + } + + [self logWithFormat: @"-init"]; + + return self; +} + +- (id) initWithSOGoObject: (id) newSOGoObject + inContainer: (MAPIStoreObject *) newContainer +{ + if ((self = [self initInContainer: newContainer])) + { + ASSIGN (sogoObject, newSOGoObject); + } + + return self; +} + +- (void) dealloc +{ + // [self logWithFormat: @"-dealloc"]; + [sogoObject release]; + [super dealloc]; +} + +- (void) setIsNew: (BOOL) newIsNew +{ + isNew = newIsNew; +} + +- (BOOL) isNew +{ + return isNew; +} + +- (id) sogoObject +{ + return sogoObject; +} + +- (MAPIStoreObject *) container +{ + return container; +} + +- (NSString *) nameInContainer +{ + return [sogoObject nameInContainer]; +} + +- (void) cleanupCaches +{ +} + +/* helpers */ +- (uint64_t) objectId +{ + uint64_t objectId; + + if ([container isKindOfClass: MAPIStoreFolderK]) + objectId = [(MAPIStoreFolder *) container + idForObjectWithKey: [sogoObject nameInContainer]]; + else + { + [self errorWithFormat: @"%s: container is not a folder", __PRETTY_FUNCTION__]; + objectId = (uint64_t) -1; + } + + return objectId; +} + +- (NSString *) url +{ + NSString *containerURL, *format; + + containerURL = (NSString *) [container url]; + if ([containerURL hasSuffix: @"/"]) + format = @"%@%@"; + else + format = @"%@/%@"; + + return [NSString stringWithFormat: format, + containerURL, [self nameInContainer]]; +} + +/* getters */ +- (int) getPidTagDisplayName: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + *data = [[sogoObject displayName] asUnicodeInMemCtx: memCtx]; + + return MAPISTORE_SUCCESS; +} + +- (int) getPidTagSearchKey: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + NSString *stringValue; + + stringValue = [sogoObject nameInContainer]; + *data = [[stringValue dataUsingEncoding: NSASCIIStringEncoding] + asBinaryInMemCtx: memCtx]; + + return MAPISTORE_SUCCESS; +} + +- (int) getPidTagGenerateExchangeViews: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self getNo: data inMemCtx: memCtx]; +} + +- (int) getPidTagParentSourceKey: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self getReplicaKey: data fromGlobCnt: [container objectId] >> 16 + inMemCtx: memCtx]; +} + +- (int) getPidTagSourceKey: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self getReplicaKey: data fromGlobCnt: [self objectId] >> 16 + inMemCtx: memCtx]; +} + +/* helper getters */ +- (int) getPidTagChangeKey: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + int rc; + uint64_t obVersion; + + obVersion = [self objectVersion]; + if (obVersion == ULLONG_MAX) + rc = MAPISTORE_ERR_NOT_FOUND; + else + rc = [self getReplicaKey: data fromGlobCnt: obVersion + inMemCtx: memCtx]; + + return rc; +} + +- (int) getPidTagChangeNumber: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + int rc; + uint64_t obVersion; + + obVersion = [self objectVersion]; + if (obVersion == ULLONG_MAX) + rc = MAPISTORE_ERR_NOT_FOUND; + else + { + *data = MAPILongLongValue (memCtx, ((obVersion << 16) + | 0x0001)); + rc = MAPISTORE_SUCCESS; + } + + return rc; +} + +/* subclasses */ +- (uint64_t) objectVersion +{ + [self subclassResponsibility: _cmd]; + + return ULLONG_MAX; +} + +/* logging */ +- (NSString *) loggingPrefix +{ + return [NSString stringWithFormat:@"<%@:%p:%@>", + NSStringFromClass (isa), self, [self nameInContainer]]; +} + +@end diff --git a/OpenChange/MAPIStoreTable.h b/OpenChange/MAPIStoreTable.h index fbf13a680..7695c79f0 100644 --- a/OpenChange/MAPIStoreTable.h +++ b/OpenChange/MAPIStoreTable.h @@ -100,8 +100,6 @@ typedef enum { - (int) getRowCount: (uint32_t *) countP withQueryType: (enum mapistore_query_type) queryType; -- (void) notifyChangesForChild: (MAPIStoreObject *) child; - /* helpers */ - (SEL) operatorFromRestrictionOperator: (uint32_t) resOp; diff --git a/OpenChange/MAPIStoreTable.m b/OpenChange/MAPIStoreTable.m index 620b8f248..b47ce8a72 100644 --- a/OpenChange/MAPIStoreTable.m +++ b/OpenChange/MAPIStoreTable.m @@ -874,61 +874,6 @@ static Class NSDataK, NSStringK; return MAPISTORE_SUCCESS; } -- (void) notifyChangesForChild: (MAPIStoreObject *) child -{ - NSUInteger currentChildRow, newChildRow; - NSArray *list; - NSString *childName; - struct mapistore_table_notification_parameters notif_parameters; - struct mapistore_context *mstoreCtx; - - mstoreCtx = [[container context] connectionInfo]->mstore_ctx; - - notif_parameters.table_type = tableType; - notif_parameters.handle = handleId; - notif_parameters.folder_id = [container objectId]; - notif_parameters.object_id = [child objectId]; - notif_parameters.instance_id = 0; /* TODO: always 0 ? */ - - childName = [child nameInContainer]; - list = [self restrictedChildKeys]; - currentChildRow = [list indexOfObject: childName]; - notif_parameters.row_id = currentChildRow; - - [self cleanupCaches]; - list = [self restrictedChildKeys]; - newChildRow = [list indexOfObject: childName]; - - if (currentChildRow == NSNotFound) - { - if (newChildRow != NSNotFound) - { - notif_parameters.row_id = newChildRow; - mapistore_push_notification (mstoreCtx, - MAPISTORE_TABLE, - MAPISTORE_OBJECT_CREATED, - ¬if_parameters); - } - } - else - { - if (newChildRow == NSNotFound) - mapistore_push_notification (mstoreCtx, - MAPISTORE_TABLE, - MAPISTORE_OBJECT_DELETED, - ¬if_parameters); - else - { - /* the fact that the row order has changed has no impact here */ - notif_parameters.row_id = newChildRow; - mapistore_push_notification (mstoreCtx, - MAPISTORE_TABLE, - MAPISTORE_OBJECT_MODIFIED, - ¬if_parameters); - } - } -} - /* subclasses */ - (NSString *) backendIdentifierForProperty: (enum MAPITAGS) property { diff --git a/OpenChange/MAPIStoreUserContext.h b/OpenChange/MAPIStoreUserContext.h index 5cdd38710..70f142b3d 100644 --- a/OpenChange/MAPIStoreUserContext.h +++ b/OpenChange/MAPIStoreUserContext.h @@ -28,6 +28,7 @@ @class NSMutableDictionary; @class NSString; @class NSTimeZone; +@class NSURL; @class WOContext; @@ -52,6 +53,9 @@ MAPIStoreMapping *mapping; + BOOL userDbTableExists; + NSURL *folderTableURL; + WOContext *woContext; MAPIStoreAuthenticator *authenticator; } @@ -71,8 +75,11 @@ - (NSDictionary *) rootFolders; +- (NSURL *) folderTableURL; - (MAPIStoreMapping *) mapping; +- (void) ensureFolderTableExists; + /* SOGo hacky magic */ - (void) activateWithUser: (SOGoUser *) activeUser; - (MAPIStoreAuthenticator *) authenticator; diff --git a/OpenChange/MAPIStoreUserContext.m b/OpenChange/MAPIStoreUserContext.m index dcb3cfb72..bf5d97f0c 100644 --- a/OpenChange/MAPIStoreUserContext.m +++ b/OpenChange/MAPIStoreUserContext.m @@ -6,7 +6,7 @@ * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) + * the Free Software Foundation; either version 3, or (at your option) * any later version. * * This file is distributed in the hope that it will be useful, @@ -23,17 +23,21 @@ #import #import #import +#import #import #import #import +#import +#import #import #import #import #import +#import "GCSSpecialQueries+OpenChange.h" #import "MAPIApplication.h" #import "MAPIStoreAuthenticator.h" #import "MAPIStoreMapping.h" @@ -80,6 +84,9 @@ static NSMapTable *contextsTable = nil; mapping = nil; + userDbTableExists = NO; + folderTableURL = nil; + authenticator = nil; woContext = [WOContext contextWithRequest: nil]; [woContext retain]; @@ -117,6 +124,8 @@ static NSMapTable *contextsTable = nil; [authenticator release]; [mapping release]; + [folderTableURL release]; + [sogoUser release]; [contextsTable removeObjectForKey: username]; @@ -213,6 +222,72 @@ static NSMapTable *contextsTable = nil; return mapping; } + +/* OpenChange db table */ + +- (NSURL *) folderTableURL +{ + NSString *urlString, *ocFSTableName; + NSMutableArray *parts; + SOGoUser *user; + + if (!folderTableURL) + { + user = [self sogoUser]; + urlString = [[user domainDefaults] folderInfoURL]; + parts = [[urlString componentsSeparatedByString: @"/"] + mutableCopy]; + [parts autorelease]; + if ([parts count] == 5) + { + /* If "OCSFolderInfoURL" is properly configured, we must have 5 + parts in this url. */ + ocFSTableName = [NSString stringWithFormat: @"socfs_%@", username]; + [parts replaceObjectAtIndex: 4 withObject: ocFSTableName]; + folderTableURL + = [NSURL URLWithString: [parts componentsJoinedByString: @"/"]]; + [folderTableURL retain]; + } + else + [NSException raise: @"MAPIStoreIOException" + format: @"'OCSFolderInfoURL' is not set"]; + } + + return folderTableURL; +} + +- (void) ensureFolderTableExists +{ + GCSChannelManager *cm; + EOAdaptorChannel *channel; + NSString *tableName, *query; + GCSSpecialQueries *queries; + + [self folderTableURL]; + + cm = [GCSChannelManager defaultChannelManager]; + channel = [cm acquireOpenChannelForURL: folderTableURL]; + + /* FIXME: make use of [EOChannelAdaptor describeTableNames] instead */ + tableName = [[folderTableURL path] lastPathComponent]; + if ([channel evaluateExpressionX: + [NSString stringWithFormat: @"SELECT count(*) FROM %@", + tableName]]) + { + queries = [channel specialQueries]; + query = [queries createOpenChangeFSTableWithName: tableName]; + if ([channel evaluateExpressionX: query]) + [NSException raise: @"MAPIStoreIOException" + format: @"could not create special table '%@'", tableName]; + } + else + [channel cancelFetch]; + + + [cm releaseChannel: channel]; +} + +/* SOGo context objects */ - (WOContext *) woContext { return woContext; diff --git a/OpenChange/MAPIStoreVolatileMessage.h b/OpenChange/MAPIStoreVolatileMessage.h deleted file mode 100644 index 71e02c5f8..000000000 --- a/OpenChange/MAPIStoreVolatileMessage.h +++ /dev/null @@ -1,37 +0,0 @@ -/* MAPIStoreVolatileMessage.h - this file is part of SOGo - * - * Copyright (C) 2011 Inverse inc - * - * Author: Wolfgang Sourdeau - * - * This file is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3, or (at your option) - * any later version. - * - * This file is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifndef MAPISTOREVOLATILEMESSAGE_H -#define MAPISTOREVOLATILEMESSAGE_H - -#import "MAPIStoreMessage.h" - -@interface MAPIStoreVolatileMessage : MAPIStoreMessage -{ - BOOL fetchedAttachments; - NSDate *creationTime; - NSDate *lastModificationTime; -} - -@end - -#endif /* MAPISTOREVOLATILEMESSAGE_H */ diff --git a/OpenChange/MAPIStoreVolatileMessage.m b/OpenChange/MAPIStoreVolatileMessage.m deleted file mode 100644 index b248ef12a..000000000 --- a/OpenChange/MAPIStoreVolatileMessage.m +++ /dev/null @@ -1,208 +0,0 @@ -/* MAPIStoreVolatileMessage.m - this file is part of SOGo - * - * Copyright (C) 2011 Inverse inc - * - * Author: Wolfgang Sourdeau - * - * This file is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3, or (at your option) - * any later version. - * - * This file is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import - -#import "MAPIStoreContext.h" -#import "MAPIStoreMailFolder.h" -#import "MAPIStoreMapping.h" -#import "MAPIStoreTypes.h" -#import "NSData+MAPIStore.h" -#import "NSObject+MAPIStore.h" -#import "NSString+MAPIStore.h" -#import "SOGoMAPIVolatileMessage.h" - -#import "MAPIStoreVolatileMessage.h" - -#undef DEBUG -#include -#include - -Class NSNumberK; - -@implementation MAPIStoreVolatileMessage - -+ (void) initialize -{ - NSNumberK = [NSNumber class]; -} - -- (id) init -{ - if ((self = [super init])) - { - fetchedAttachments = NO; - ASSIGN (creationTime, [NSDate date]); - lastModificationTime = [creationTime copy]; - } - - return self; -} - -- (void) dealloc -{ - [creationTime release]; - [lastModificationTime release]; - [super dealloc]; -} - -- (void) addProperties: (NSDictionary *) newProperties -{ - [super addProperties: newProperties]; - [sogoObject appendProperties: properties]; - [properties removeAllObjects]; - ASSIGN (lastModificationTime, [NSDate date]); -} - -- (BOOL) canGetProperty: (enum MAPITAGS) propTag -{ - return ([super canGetProperty: propTag] - || [[sogoObject properties] objectForKey: MAPIPropertyKey (propTag)]); -} - -- (uint64_t) objectVersion -{ - NSNumber *version; - - version = [[sogoObject properties] objectForKey: @"version"]; - - return (version - ? exchange_globcnt ([version unsignedLongLongValue]) - : ULLONG_MAX); -} - -- (int) getProperty: (void **) data - withTag: (enum MAPITAGS) propTag - inMemCtx: (TALLOC_CTX *) memCtx -{ - id value; - int rc; - - value = [[sogoObject properties] objectForKey: MAPIPropertyKey (propTag)]; - if (value) - rc = [value getValue: data forTag: propTag inMemCtx: memCtx]; - else - rc = [super getProperty: data withTag: propTag inMemCtx: memCtx]; - - return rc; -} - -- (int) getPidTagSubject: (void **) data inMemCtx: (TALLOC_CTX *) memCtx -{ - /* if we get here, it means that the properties file didn't contain a - relevant value */ - return [self getEmptyString: data inMemCtx: memCtx]; -} - -- (int) getPidTagMessageClass: (void **) data inMemCtx: (TALLOC_CTX *) memCtx -{ - *data = [@"IPM.Note" asUnicodeInMemCtx: memCtx]; - - return MAPISTORE_SUCCESS; -} - -- (int) getPidTagChangeKey: (void **) data inMemCtx: (TALLOC_CTX *) memCtx -{ - NSData *changeKey; - int rc; - - changeKey = [[sogoObject properties] - objectForKey: MAPIPropertyKey (PR_CHANGE_KEY)]; - if (changeKey) - { - *data = [changeKey asBinaryInMemCtx: memCtx]; - rc = MAPISTORE_SUCCESS; - } - else - rc = [super getPidTagChangeKey: data inMemCtx: memCtx]; - - return rc; -} - -- (NSArray *) attachmentsKeysMatchingQualifier: (EOQualifier *) qualifier - andSortOrderings: (NSArray *) sortOrderings -{ - NSDictionary *attachments; - NSArray *keys; - NSString *key, *newKey; - NSUInteger count, max, aid; - MAPIStoreAttachment *attachment; - - if (!fetchedAttachments) - { - attachments = [[sogoObject properties] objectForKey: @"attachments"]; - keys = [attachments allKeys]; - max = [keys count]; - if (max > 0) - { - aid = [keys count]; - for (count = 0; count < max; count++) - { - key = [keys objectAtIndex: count]; - attachment = [attachments objectForKey: key]; - newKey = [NSString stringWithFormat: @"%ul", (aid + count)]; - [attachmentParts setObject: attachment forKey: newKey]; - } - } - fetchedAttachments = YES; - } - - return [super attachmentKeysMatchingQualifier: qualifier - andSortOrderings: sortOrderings]; -} - -- (NSDate *) creationTime -{ - return creationTime; -} - -- (NSDate *) lastModificationTime -{ - return lastModificationTime; -} - -- (id) lookupAttachment: (NSString *) childKey -{ - return [attachmentParts objectForKey: childKey]; -} - -- (void) save -{ - [self subclassResponsibility: _cmd]; -} - -@end diff --git a/OpenChange/NSObject+PropertyList.m b/OpenChange/NSObject+PropertyList.m new file mode 100644 index 000000000..9f9762508 --- /dev/null +++ b/OpenChange/NSObject+PropertyList.m @@ -0,0 +1,182 @@ +/* dbmsgdump.m - this file is part of SOGo + * + * Copyright (C) 2011 Inverse inc + * + * Author: Wolfgang Sourdeau + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* A format-agnostic property list dumper. + Usage: dbmsgdump [filename] */ + +#import +#import +#import +#import +#import +#import +#import +#import +#import + +const char *indentationStep = " "; + +@interface NSObject (plext) + +- (void) displayWithIndentation: (NSInteger) anInt; + +@end + +@implementation NSObject (plext) + +- (void) _outputIndentation: (NSInteger) anInt +{ + NSInteger i; + + for (i = 0; i < anInt; i++) + printf ("%s", indentationStep); +} + +- (void) displayWithIndentation: (NSInteger) anInt +{ + printf ("(%s) %s", + [NSStringFromClass (isa) UTF8String], + [[self description] UTF8String]); +} + +@end + +@implementation NSDictionary (plext) + +- (void) displayKey: (NSString *) key + withIndentation: (NSInteger) anInt +{ + [self _outputIndentation: anInt]; + + printf ("%s ", [[key description] UTF8String]); + if ([key isKindOfClass: [NSValue class]]) + printf ("(%s: 0x%.8x) ", [(NSValue *) key objCType], [key intValue]); + + printf ("= "); +} + +- (void) displayWithIndentation: (NSInteger) anInt +{ + NSUInteger i, max; + NSArray *keys; + NSInteger subIndent; + NSString *key; + + keys = [self allKeys]; + max = [keys count]; + + printf ("{ (%ld) items\n", (long) max); + + subIndent = anInt + 1; + + for (i = 0; i < max; i++) + { + key = [keys objectAtIndex: i]; + [self displayKey: key withIndentation: subIndent]; + [[self objectForKey: key] displayWithIndentation: subIndent]; + if (i < (max - 1)) + printf (","); + printf ("\n"); + } + + [self _outputIndentation: anInt]; + printf ("}"); +} + +@end + +@implementation NSArray (plext) + +- (void) displayCount: (NSUInteger) count + withIndentation: (NSInteger) anInt +{ + [self _outputIndentation: anInt]; + printf ("%lu = ", (unsigned long) count); +} + +- (void) displayWithIndentation: (NSInteger) anInt +{ + NSUInteger i, max; + NSInteger subIndent; + + max = [self count]; + + printf ("[ (%ld) items\n", (long) max); + + subIndent = anInt + 1; + + for (i = 0; i < max; i++) + { + [self displayCount: i withIndentation: subIndent]; + [[self objectAtIndex: i] displayWithIndentation: subIndent]; + if (i < (max - 1)) + printf (","); + printf ("\n"); + } + + [self _outputIndentation: anInt]; + printf ("]"); +} + +@end + +static void +OCDumpPListData (NSData *content) +{ + NSDictionary *d; + NSPropertyListFormat format; + NSString *error = nil; + const char *formatName; + + d = [NSPropertyListSerialization propertyListFromData: content + mutabilityOption: NSPropertyListImmutable + format: &format + errorDescription: &error]; + if (d) + { + switch (format) + { + case NSPropertyListOpenStepFormat: + formatName = "OpenStep"; + break; + case NSPropertyListXMLFormat_v1_0: + formatName = "XML"; + break; + case NSPropertyListBinaryFormat_v1_0: + formatName = "Binary"; + break; + case NSPropertyListGNUstepFormat: + formatName = "GNUstep"; + break; + case NSPropertyListGNUstepBinaryFormat: + formatName = "GNUstep binary"; + break; + default: formatName = "unknown"; + } + + printf ("File format is: %s\n", formatName); + [d displayWithIndentation: 0]; + printf ("\n"); + } + else + printf ("an error occurred: %s\n", [error UTF8String]); +} diff --git a/OpenChange/SOGoMAPIFSFolder.h b/OpenChange/SOGoMAPIDBFolder.h similarity index 52% rename from OpenChange/SOGoMAPIFSFolder.h rename to OpenChange/SOGoMAPIDBFolder.h index 244094129..e2d29058f 100644 --- a/OpenChange/SOGoMAPIFSFolder.h +++ b/OpenChange/SOGoMAPIDBFolder.h @@ -1,6 +1,6 @@ -/* SOGoMAPIFSFolder.h - this file is part of SOGo +/* SOGoMAPIDBFolder.h - this file is part of SOGo * - * Copyright (C) 2010 Inverse inc. + * Copyright (C) 2012 Inverse inc. * * Author: Wolfgang Sourdeau * @@ -20,41 +20,38 @@ * Boston, MA 02111-1307, USA. */ -#ifndef SOGOMAPIFSFOLDER_H -#define SOGOMAPIFSFOLDER_H +#ifndef SOGOMAPIDBFOLDER_H +#define SOGOMAPIDBFOLDER_H -#import +#import "SOGoMAPIDBObject.h" @class NSArray; +@class NSMutableString; @class NSString; @class NSURL; @class EOQualifier; -@class SOGoMAPIFSMessage; +@class SOGoMAPIDBMessage; -@interface SOGoMAPIFSFolder : SOGoFolder +@interface SOGoMAPIDBFolder : SOGoMAPIDBObject { - NSString *directory; - BOOL directoryIsSane; + NSString *pathPrefix; /* for root folders */ + SOGoMAPIDBObject *aclMessage; } -+ (id) folderWithURL: (NSURL *) url - andTableType: (uint8_t) tableType; -- (id) initWithURL: (NSURL *) url - andTableType: (uint8_t) tableType; +- (void) setPathPrefix: (NSString *) newPathPrefix; -- (NSString *) directory; +- (NSMutableString *) pathForChild: (NSString *) childName; -- (SOGoMAPIFSMessage *) newMessage; -- (void) ensureDirectory; +- (NSArray *) toOneRelationshipKeys; +- (NSArray *) toManyRelationshipKeys; -- (NSCalendarDate *) creationTime; -- (NSCalendarDate *) lastModificationTime; - -- (NSArray *) toOneRelationshipKeysMatchingQualifier: (EOQualifier *) qualifier - andSortOrderings: (NSArray *) sortOrderings; +- (NSArray *) childKeysOfType: (MAPIDBObjectType) type + includeDeleted: (BOOL) includeDeleted + matchingQualifier: (EOQualifier *) qualifier + andSortOrderings: (NSArray *) sortOrderings; @end -#endif /* SOGOMAPIFSFOLDER_H */ +#endif /* SOGOMAPIDBFOLDER_H */ diff --git a/OpenChange/SOGoMAPIDBFolder.m b/OpenChange/SOGoMAPIDBFolder.m new file mode 100644 index 000000000..ddd6086a6 --- /dev/null +++ b/OpenChange/SOGoMAPIDBFolder.m @@ -0,0 +1,402 @@ +/* SOGoMAPIDBFolder.m - this file is part of SOGo + * + * Copyright (C) 2012 Inverse inc. + * + * Author: Wolfgang Sourdeau + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#import +#import +#import +#import +#import +#import +#import +#import + +#import +#import +#import +// #import + +#import +#import +#import +#import "EOQualifier+MAPI.h" +#import "GCSSpecialQueries+OpenChange.h" +#import "SOGoMAPIDBMessage.h" + +#import "SOGoMAPIDBFolder.h" + +#undef DEBUG +#include +#include +#include +#include +#include +#include +#include + +Class SOGoMAPIDBObjectK = Nil; + +@implementation SOGoMAPIDBFolder + ++ (void) initialize +{ + SOGoMAPIDBObjectK = [SOGoMAPIDBObject class]; +} + +- (id) init +{ + if ((self = [super init])) + { + pathPrefix = nil; + } + + return self; +} + +- (id) initWithName: (NSString *) name inContainer: (id) newContainer +{ + if ((self = [super initWithName: name inContainer: newContainer])) + { + objectType = MAPIDBObjectTypeFolder; + aclMessage = [SOGoMAPIDBObject objectWithName: @"permissions" + inContainer: self]; + [aclMessage setObjectType: MAPIDBObjectTypeInternal]; + [aclMessage retain]; + } + + return self; +} + +- (void) dealloc +{ + [aclMessage release]; + [pathPrefix release]; + [super dealloc]; +} + +- (BOOL) isFolderish +{ + return YES; +} + +- (void) setPathPrefix: (NSString *) newPathPrefix +{ + ASSIGN (pathPrefix, newPathPrefix); +} + +- (NSMutableString *) pathForChild: (NSString *) childName +{ + NSMutableString *path; + + path = [self path]; + [path appendFormat: @"/%@", childName]; + + return path; +} + +- (NSMutableString *) path +{ + NSMutableString *path; + + path = [super path]; + if (pathPrefix) + [path insertString: pathPrefix atIndex: 0]; + + return path; +} + +// - (SOGoMAPIDBMessage *) newMessage +// { +// NSString *newFilename; + +// newFilename = [NSString stringWithFormat: @"%@.plist", +// [SOGoObject globallyUniqueObjectId]]; + +// return [SOGoMAPIDBMessage objectWithName: filename inContainer: self]; +// } + +- (NSArray *) childKeysOfType: (MAPIDBObjectType) type + includeDeleted: (BOOL) includeDeleted + matchingQualifier: (EOQualifier *) qualifier + andSortOrderings: (NSArray *) sortOrderings +{ + NSMutableArray *childKeys; + NSMutableString *sql// , *qualifierClause + ; + NSString *childPathPrefix, *childPath, *childKey; + NSMutableArray *whereClause; + NSArray *records; + NSDictionary *record; + NSUInteger childPathPrefixLen, count, max; + SOGoMAPIDBObject *currentChild; + + /* query construction */ + sql = [NSMutableString stringWithCapacity: 256]; + [sql appendFormat: @"SELECT * FROM %@", [self tableName]]; + + whereClause = [NSMutableArray arrayWithCapacity: 2]; + childPathPrefix = [NSString stringWithFormat: @"%@/", [self path]]; + [whereClause addObject: [NSString stringWithFormat: @"c_path LIKE '%@%%'", + childPathPrefix]]; + [whereClause addObject: [NSString stringWithFormat: @"c_type = %d", type]]; + if (!includeDeleted) + [whereClause addObject: @"c_deleted = 0"]; + + [sql appendFormat: @" WHERE %@", + [whereClause componentsJoinedByString: @" AND "]]; + + /* results */ + records = [self performSQLQuery: sql]; + if (records) + { + max = [records count]; + childKeys = [NSMutableArray arrayWithCapacity: max]; + childPathPrefixLen = [childPathPrefix length]; + for (count = 0; count < max; count++) + { + record = [records objectAtIndex: count]; + childPath = [record objectForKey: @"c_path"]; + childKey = [childPath substringFromIndex: childPathPrefixLen]; + if ([childKey rangeOfString: @"/"].location == NSNotFound) + { + if (qualifier) + { + currentChild = [SOGoMAPIDBObject objectWithName: childKey + inContainer: self]; + [currentChild setupFromRecord: record]; + if ([qualifier evaluateSOGoMAPIDBObject: currentChild]) + [childKeys addObject: childKey]; + } + else + [childKeys addObject: childKey]; + } + } + } + else + childKeys = nil; + + return childKeys; +} + +- (NSArray *) toManyRelationshipKeys +{ + return [self childKeysOfType: MAPIDBObjectTypeFolder + includeDeleted: NO + matchingQualifier: nil + andSortOrderings: nil]; +} + +- (NSArray *) toOneRelationshipKeys +{ + return [self childKeysOfType: MAPIDBObjectTypeMessage + includeDeleted: NO + matchingQualifier: nil + andSortOrderings: nil]; +} + +// - (NSArray *) toOneRelationshipKeysMatchingQualifier: (EOQualifier *) qualifier +// andSortOrderings: (NSArray *) sortOrderings +// { +// NSArray *allKeys; +// NSMutableArray *keys; +// NSUInteger count, max; +// NSString *messageKey; +// SOGoMAPIDBMessage *message; + +// if (sortOrderings) +// [self warnWithFormat: @"sorting is not handled yet"]; + +// allKeys = [self toOneRelationshipKeys]; +// if (qualifier) +// { +// [self logWithFormat: @"%s: getting restricted FAI keys", __PRETTY_FUNCTION__]; +// max = [allKeys count]; +// keys = [NSMutableArray arrayWithCapacity: max]; +// for (count = 0; count < max; count++) +// { +// messageKey = [allKeys objectAtIndex: count]; +// message = [self lookupName: messageKey +// inContext: nil +// acquire: NO]; +// if ([qualifier evaluateMAPIVolatileMessage: message]) +// [keys addObject: messageKey]; +// } +// } +// else +// keys = (NSMutableArray *) allKeys; + +// return keys; +// } + +- (id) lookupName: (NSString *) childName + inContext: (WOContext *) woContext + acquire: (BOOL) acquire +{ + id object; + Class objectClass; + NSString *childPath; + NSDictionary *record; + + childPath = [self pathForChild: childName]; + record = [self lookupRecord: childPath newerThanVersion: -1]; + if (record) + { + if ([[record objectForKey: @"c_type"] intValue] == MAPIDBObjectTypeFolder) + objectClass = isa; + else + objectClass = SOGoMAPIDBObjectK; + + object = [objectClass objectWithName: childName + inContainer: self]; + [object setupFromRecord: record]; + } + else + object = nil; + + return object; +} + +- (id) lookupFolder: (NSString *) folderName + inContext: (WOContext *) woContext +{ + id object; + + object = [SOGoMAPIDBFolder objectWithName: folderName + inContainer: self]; + [object reloadIfNeeded]; + + return object; +} + +// - (id) _fileAttributeForKey: (NSString *) key +// { +// NSDictionary *attributes; + +// attributes = [[NSFileManager defaultManager] +// fileAttributesAtPath: directory +// traverseLink: NO]; + +// return [attributes objectForKey: key]; +// } + +// - (NSCalendarDate *) creationTime +// { +// return [self _fileAttributeForKey: NSFileCreationDate]; +// } + +// - (NSCalendarDate *) lastModificationTime +// { +// return [self _fileAttributeForKey: NSFileModificationDate]; +// } + +- (NSException *) delete +{ + [self notImplemented: _cmd]; + + // NSFileManager *fm; + // NSException *error; + + // fm = [NSFileManager defaultManager]; + + // if (![fm removeFileAtPath: directory handler: NULL]) + // error = [NSException exceptionWithName: @"MAPIStoreIOException" + // reason: @"could not delete folder" + // userInfo: nil]; + // else + // error = nil; + + // return error; + return nil; +} + +/* acl */ +- (NSString *) defaultUserID +{ + return @"default"; +} + +- (NSMutableDictionary *) _aclEntries +{ + NSMutableDictionary *aclEntries; + + [aclMessage reloadIfNeeded]; + aclEntries = [aclMessage properties]; + if (![aclEntries objectForKey: @"users"]) + [aclEntries setObject: [NSMutableArray array] forKey: @"users"]; + if (![aclEntries objectForKey: @"entries"]) + [aclEntries setObject: [NSMutableDictionary dictionary] + forKey: @"entries"]; + + return aclEntries; +} + +- (void) addUserInAcls: (NSString *) user +{ + NSMutableDictionary *acl; + NSMutableArray *users; + + acl = [self _aclEntries]; + users = [acl objectForKey: @"users"]; + [users addObjectUniquely: user]; + [aclMessage save]; +} + +- (void) removeAclsForUsers: (NSArray *) oldUsers +{ + NSDictionary *acl; + NSMutableDictionary *entries; + NSMutableArray *users; + + acl = [self _aclEntries]; + entries = [acl objectForKey: @"entries"]; + [entries removeObjectsForKeys: oldUsers]; + users = [acl objectForKey: @"users"]; + [users removeObjectsInArray: oldUsers]; + [aclMessage save]; +} + +- (NSArray *) aclUsers +{ + return [[self _aclEntries] objectForKey: @"users"]; +} + +- (NSArray *) aclsForUser: (NSString *) uid +{ + NSDictionary *entries; + + entries = [[self _aclEntries] objectForKey: @"entries"]; + + return [entries objectForKey: uid]; +} + +- (void) setRoles: (NSArray *) roles + forUser: (NSString *) uid +{ + NSMutableDictionary *acl; + NSMutableDictionary *entries; + + acl = [self _aclEntries]; + entries = [acl objectForKey: @"entries"]; + [entries setObject: roles forKey: uid]; + [aclMessage save]; +} + +@end diff --git a/OpenChange/SOGoMAPIDBObject.h b/OpenChange/SOGoMAPIDBObject.h new file mode 100644 index 000000000..ea0a7a58a --- /dev/null +++ b/OpenChange/SOGoMAPIDBObject.h @@ -0,0 +1,83 @@ +/* SOGoMAPIDBObject.h - this file is part of SOGo + * + * Copyright (C) 2012 Inverse inc + * + * Author: Wolfgang Sourdeau + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef SOGOMAPIDBOBJECT_H +#define SOGOMAPIDBOBJECT_H + +#import "SOGoMAPIObject.h" + +@class NSArray; +@class NSMutableDictionary; +@class NSMutableString; +@class NSString; +@class NSURL; + +@class EOAdaptor; + +typedef enum { + MAPIDBObjectTypeFolder = 1, + MAPIDBObjectTypeMessage = 2, + MAPIDBObjectTypeFAI = 3, + MAPIDBObjectTypeInternal = 99 /* object = property list */ +} MAPIDBObjectType; + +@interface SOGoMAPIDBObject : SOGoMAPIObject +{ + NSURL *tableUrl; + + BOOL initialized; /* safe guard */ + MAPIDBObjectType objectType; + NSInteger version; + BOOL deleted; +} + +/* actions */ +- (void) setupFromRecord: (NSDictionary *) record; + +- (void) reloadIfNeeded; +- (void) save; + +/* accessors */ +- (NSMutableString *) path; /* full filename */ + +- (void) setTableUrl: (NSURL *) newTableUrl; +- (NSURL *) tableUrl; +- (NSString *) tableName; + +- (NSArray *) performSQLQuery: (NSString *) sql; +- (NSDictionary *) lookupRecord: (NSString *) path + newerThanVersion: (NSInteger) startVersion; + +- (void) setObjectType: (MAPIDBObjectType) newObjectType; +- (MAPIDBObjectType) objectType; /* message, fai, folder */ + +/* automatically set from actions */ +- (BOOL) deleted; + +/* db helpers */ +- (EOAdaptor *) tableChannelAdaptor; +- (NSArray *) performSQLQuery: (NSString *) sql; + + +@end + +#endif /* SOGOMAPIDBOBJECT_H */ diff --git a/OpenChange/SOGoMAPIDBObject.m b/OpenChange/SOGoMAPIDBObject.m new file mode 100644 index 000000000..b80af8fc5 --- /dev/null +++ b/OpenChange/SOGoMAPIDBObject.m @@ -0,0 +1,483 @@ +/* SOGoMAPIDBObject.m - this file is part of SOGo + * + * Copyright (C) 2012 Inverse inc + * + * Author: Wolfgang Sourdeau + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import + +#import "GCSSpecialQueries+OpenChange.h" +#import "MAPIStoreTypes.h" +#import "SOGoMAPIDBFolder.h" + +#import "SOGoMAPIDBObject.h" + +static EOAttribute *textColumn = nil; + +@implementation SOGoMAPIDBObject + ++ (void) initialize +{ + NSDictionary *description; + + if (!textColumn) + { + /* TODO: this is a hack for providing an EOAttribute definition that is + compatible with all the backends that we support. We should make use + of EOModel instead. */ + description = [NSDictionary dictionaryWithObjectsAndKeys: + @"c_textfield", @"columnName", + @"VARCHAR", @"externalType", + nil]; + textColumn = [EOAttribute attributeFromPropertyList: description]; + [textColumn retain]; + } +} + +/* + = (@"CREATE TABLE %@ (" + @" c_path VARCHAR(255) PRIMARY KEY," + @" c_type VARCHAR(20) NOT NULL," + @" c_creationdate INT4 NOT NULL," + @" c_lastmodified INT4 NOT NULL," + @" c_version INT4 NOT NULL DEFAULT 0," + @" c_deleted SMALLINT NOT NULL DEFAULT 0," +*/ + +/* indexes: + c_path (primary key) + c_counter + c_path, c_type + c_path, c_creationdate */ + +- (id) init +{ + if ((self = [super init])) + { + tableUrl = nil; + initialized = NO; + objectType = -1; + deleted = NO; + version = 0; + } + + return self; +} + +- (void) dealloc +{ + [tableUrl release]; + [super dealloc]; +} + +- (void) setTableUrl: (NSURL *) newTableUrl +{ + ASSIGN (tableUrl, newTableUrl); +} + +- (NSURL *) tableUrl +{ + if (!tableUrl) + { + tableUrl = [container tableUrl]; + [tableUrl retain]; + if (!tableUrl) + [NSException raise: @"MAPIStoreIOException" + format: @"table url is not set for object '%@'", self]; + } + + return tableUrl; +} + +- (NSString *) tableName +{ + NSArray *parts; + + [self tableUrl]; + parts = [[tableUrl path] componentsSeparatedByString: @"/"]; + + return [parts lastObject]; +} + +- (void) setupFromRecord: (NSDictionary *) record +{ + NSInteger intValue; + NSString *propsValue, *error; + NSDictionary *newValues; + NSPropertyListFormat format; + + objectType = [[record objectForKey: @"c_type"] intValue]; + intValue = [[record objectForKey: @"c_creationdate"] intValue]; + ASSIGN (creationDate, + [NSCalendarDate + dateWithTimeIntervalSince1970: (NSTimeInterval) intValue]); + intValue = [[record objectForKey: @"c_lastmodified"] intValue]; + ASSIGN (lastModified, + [NSCalendarDate + dateWithTimeIntervalSince1970: (NSTimeInterval) intValue]); + deleted = ([[record objectForKey: @"c_deleted"] intValue] > 0); + version = [[record objectForKey: @"c_version"] intValue]; + propsValue = [record objectForKey: @"c_content"]; + if ([propsValue isNotNull]) + { + newValues = [NSPropertyListSerialization propertyListFromData: [propsValue dataByDecodingBase64] + mutabilityOption: NSPropertyListMutableContainers + format: &format + errorDescription: &error]; + [properties addEntriesFromDictionary: newValues]; + // [properties addEntriesFromDictionary: [propsValue + // objectFromJSONString]]; + } + else + [properties removeAllObjects]; + + initialized = YES; +} + +/* accessors */ +- (NSMutableString *) path +{ + NSMutableString *path; + + if (container) + path = [container pathForChild: nameInContainer]; + else + path = [NSMutableString stringWithFormat: @"/%@", nameInContainer]; + + if ([path rangeOfString: @"//"].location != NSNotFound) + [NSException raise: @"MAPIStoreIOException" + format: @"object path has not been properly set for" + " folder '%@' (%@)", + self, path]; + + return path; +} + +- (void) setObjectType: (MAPIDBObjectType) newObjectType +{ + objectType = newObjectType; +} + +- (MAPIDBObjectType) objectType /* message, fai, folder */ +{ + return objectType; +} + +- (NSCalendarDate *) creationDate +{ + if (!initialized) + [NSException raise: @"MAPIStoreIOException" + format: @"record has not been initialized: %@", self]; + + return creationDate; +} + +- (NSCalendarDate *) lastModified +{ + if (!initialized) + [NSException raise: @"MAPIStoreIOException" + format: @"record has not been initialized: %@", self]; + + return lastModified; +} + +- (BOOL) deleted +{ + return deleted; +} + +- (Class) mapistoreMessageClass +{ + NSString *className, *mapiMsgClass; + + switch (objectType) + { + case MAPIDBObjectTypeMessage: + mapiMsgClass = [properties + objectForKey: MAPIPropertyKey (PidTagMessageClass)]; + if (mapiMsgClass) + { + if ([mapiMsgClass isEqualToString: @"IPM.StickyNote"]) + className = @"MAPIStoreNotesMessage"; + else + className = @"MAPIStoreDBMessage"; + [self logWithFormat: @"PidTagMessageClass = '%@', returning '%@'", + mapiMsgClass, className]; + } + else + { + [self warnWithFormat: @"PidTagMessageClass is not set, falling back" + @" to 'MAPIStoreDBMessage'"]; + className = @"MAPIStoreDBMessage"; + } + break; + case MAPIDBObjectTypeFAI: + className = @"MAPIStoreFAIMessage"; + break; + default: + [NSException raise: @"MAPIStoreIOException" + format: @"message class should not be queried for objects" + @" of type '%d'", objectType]; + } + + return NSClassFromString (className); +} + +/* actions */ +- (EOAdaptor *) tableChannelAdaptor +{ + GCSChannelManager *cm; + EOAdaptor *adaptor; + EOAdaptorChannel *channel; + + cm = [GCSChannelManager defaultChannelManager]; + channel = [cm acquireOpenChannelForURL: [self tableUrl]]; + adaptor = [[channel adaptorContext] adaptor]; + [cm releaseChannel: channel]; + + return adaptor; +} + +- (NSArray *) performSQLQuery: (NSString *) sql +{ + NSMutableArray *records; + GCSChannelManager *cm; + EOAdaptorChannel *channel; + NSException *error; + NSArray *attrs; + NSDictionary *record; + + cm = [GCSChannelManager defaultChannelManager]; + channel = [cm acquireOpenChannelForURL: [self tableUrl]]; + + error = [channel evaluateExpressionX: sql]; + if (error) + { + records = nil; + [self logWithFormat: + @"an exception occurred when executing query '%@'", + sql]; + [self logWithFormat: @"exception is '%@'", error]; + } + else + { + records = [NSMutableArray arrayWithCapacity: 256]; + attrs = [channel describeResults: NO]; + while ((record = [channel fetchAttributes: attrs withZone: NULL])) + [records addObject: record]; + } + [cm releaseChannel: channel]; + + return records; +} + +- (NSDictionary *) lookupRecord: (NSString *) path + newerThanVersion: (NSInteger) startVersion +{ + NSDictionary *record; + NSArray *records; + NSString *tableName, *pathValue; + NSMutableString *sql; + EOAdaptor *adaptor; + + if ([path hasSuffix: @"/"]) + [NSException raise: @"MAPIStoreIOException" + format: @"path ends with a slash: %@", path]; + + tableName = [self tableName]; + adaptor = [self tableChannelAdaptor]; + pathValue = [adaptor formatValue: path + forAttribute: textColumn]; + + /* query */ + sql = [NSMutableString stringWithFormat: + @"SELECT * FROM %@ WHERE c_path = %@", + tableName, pathValue]; + if (startVersion > -1) + [sql appendFormat: @" AND c_version > %d", startVersion]; + + /* execution */ + records = [self performSQLQuery: sql]; + if ([records count] > 0) + record = [records objectAtIndex: 0]; + else + record = nil; + + return record; +} + +- (void) reloadIfNeeded +{ + /* if object is uninitialized: reload without condition, otherwise, load if + c_version > :version */ + NSDictionary *record; + + if (initialized) + { + if (!isNew) + { + record = [self lookupRecord: [self path] + newerThanVersion: version]; + if (record) + [self setupFromRecord: record]; + } + } + else + { + record = [self lookupRecord: [self path] + newerThanVersion: -1]; + if (record) + { + [self setupFromRecord: record]; + isNew = NO; + } + else + isNew = YES; + initialized = YES; + } +} + +- (NSException *) delete +{ + deleted = YES; + [properties removeAllObjects]; + [self save]; + + return nil; +} + +- (void) save +{ + NSString *sql; + NSData *content; + NSCalendarDate *now; + GCSChannelManager *cm; + EOAdaptor *adaptor; + EOAdaptorChannel *channel; + NSInteger creationDateValue, lastModifiedValue, deletedValue; + NSString *tableName, *pathValue, *propsValue; + NSException *result; + + if (!initialized) + [NSException raise: @"MAPIStoreIOException" + format: @"record has not been initialized: %@", self]; + + cm = [GCSChannelManager defaultChannelManager]; + + channel = [cm acquireOpenChannelForURL: [self tableUrl]]; + + tableName = [self tableName]; + + now = [NSCalendarDate date]; + ASSIGN (lastModified, now); + + /* +- (NSException *)insertRowX:(NSDictionary *)_row forEntity:(EOEntity *)_entity; +- (NSException *)updateRowX:(NSDictionary*)aRow + describedByQualifier:(EOSQLQualifier*)aQualifier; + */ + + adaptor = [[channel adaptorContext] adaptor]; + pathValue = [adaptor formatValue: [self path] + forAttribute: textColumn]; + + lastModifiedValue = (NSInteger) [lastModified timeIntervalSince1970]; + + if (objectType == -1) + [NSException raise: @"MAPIStoreIOException" + format: @"object type has not been set for object '%@'", + self]; + + if ([properties count] > 0) + { + content = [NSPropertyListSerialization + dataFromPropertyList: properties + format: NSPropertyListBinaryFormat_v1_0 + errorDescription: NULL]; + propsValue = [adaptor formatValue: [content stringByEncodingBase64] + forAttribute: textColumn]; + } + else + propsValue = @"NULL"; + + if (isNew) + { + ASSIGN (creationDate, now); + creationDateValue = (NSInteger) [creationDate timeIntervalSince1970]; + sql = [NSString stringWithFormat: + (@"INSERT INTO %@" + @" (c_path, c_type, c_creationdate, c_lastmodified," + @" c_deleted, c_version, c_content)" + @" VALUES (%@, %d, %d, %d, 0, 0, %@" + @")"), + tableName, + pathValue, objectType, + creationDateValue, lastModifiedValue, + propsValue]; + isNew = NO; + } + else + { + version++; + deletedValue = (deleted ? 1 : 0); + sql = [NSString stringWithFormat: + (@"UPDATE %@" + @" SET c_lastmodified = %d, c_deleted = %d," + @" c_version = %d, c_content = %@" + @" WHERE c_path = %@"), + tableName, + lastModifiedValue, deletedValue, version, propsValue, + pathValue]; + } + + result = [channel evaluateExpressionX: sql]; + if (result) + [self errorWithFormat: @"could not insert/update record for record %@" + @" in %@: %@", pathValue, tableName, result]; + // @" c_path VARCHAR(255) PRIMARY KEY," + // @" c_type SMALLINT NOT NULL," + // @" c_creationdate INT4 NOT NULL," + // @" c_lastmodified INT4 NOT NULL," + // @" c_deleted SMALLINT NOT NULL DEFAULT 0," + // @" c_content BLOB"); + + [cm releaseChannel: channel]; +} + +@end diff --git a/OpenChange/SOGoMAPIFSFolder.m b/OpenChange/SOGoMAPIFSFolder.m deleted file mode 100644 index b26e0ea9d..000000000 --- a/OpenChange/SOGoMAPIFSFolder.m +++ /dev/null @@ -1,439 +0,0 @@ -/* SOGoMAPIFSFolder.m - this file is part of SOGo - * - * Copyright (C) 2010 Inverse inc. - * - * Author: Wolfgang Sourdeau - * - * This file is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3, or (at your option) - * any later version. - * - * This file is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#import -#import -#import -#import -#import -#import -#import -#import -#import - -#import -#import - -#import "EOQualifier+MAPI.h" -#import "SOGoMAPIFSMessage.h" - -#import "SOGoMAPIFSFolder.h" - -#undef DEBUG -#include -#include -#include -#include -#include -#include -#include - -static NSString *privateDir = nil; - -@implementation SOGoMAPIFSFolder - -+ (void) initialize -{ - struct loadparm_context *lpCtx; - const char *cPrivateDir; - - if (!privateDir) - { - lpCtx = loadparm_init_global (true); - cPrivateDir = lpcfg_private_dir (lpCtx); - privateDir = [NSString stringWithUTF8String: cPrivateDir]; - [privateDir retain]; - } -} - -+ (id) folderWithURL: (NSURL *) url - andTableType: (uint8_t) tableType -{ - SOGoMAPIFSFolder *newFolder; - - newFolder = [[self alloc] initWithURL: url - andTableType: tableType]; - [newFolder autorelease]; - - return newFolder; -} - -- (id) init -{ - if ((self = [super init])) - { - directory = nil; - directoryIsSane = NO; - } - - return self; -} - -- (void) dealloc -{ - [directory release]; - [super dealloc]; -} - -- (id) initWithURL: (NSURL *) url - andTableType: (uint8_t) tableType -{ - NSString *path, *username, *tableParticle; - - if ((self = [self init])) - { - if (tableType == MAPISTORE_MESSAGE_TABLE) - tableParticle = @"message"; - else if (tableType == MAPISTORE_FAI_TABLE) - tableParticle = @"fai"; - else if (tableType == MAPISTORE_FOLDER_TABLE) - tableParticle = @"folder"; - else - { - [NSException raise: @"MAPIStoreIOException" - format: @"unsupported table type: %d", tableType]; - tableParticle = nil; - } - - path = [url path]; - if (![path hasSuffix: @"/"]) - path = [NSString stringWithFormat: @"%@/", path]; - username = [url user]; - directory = [NSString stringWithFormat: @"%@/mapistore/SOGo/%@/%@/%@%@", - privateDir, username, tableParticle, - [url host], path]; - [self setOwner: username]; - [self logWithFormat: @"directory: %@", directory]; - [directory retain]; - ASSIGN (nameInContainer, [path stringByDeletingLastPathComponent]); - } - - return self; -} - -- (id) initWithName: (NSString *) newName - inContainer: (id) newContainer -{ - if ((self = [super initWithName: newName inContainer: newContainer])) - { - directory = [[newContainer directory] - stringByAppendingPathComponent: newName]; - [directory retain]; - } - - return self; -} - -- (NSString *) directory -{ - return directory; -} - -- (SOGoMAPIFSMessage *) newMessage -{ - NSString *filename; - - filename = [NSString stringWithFormat: @"%@.plist", - [SOGoObject globallyUniqueObjectId]]; - - return [SOGoMAPIFSMessage objectWithName: filename inContainer: self]; -} - -- (void) ensureDirectory -{ - NSFileManager *fm; - NSDictionary *attributes; - BOOL isDir; - - if (!directory) - [NSException raise: @"MAPIStoreIOException" - format: @"directory is nil"]; - - fm = [NSFileManager defaultManager]; - if ([fm fileExistsAtPath: directory isDirectory: &isDir]) - { - if (!isDir) - [NSException raise: @"MAPIStoreIOException" - format: @"object at path '%@' is not a directory", - directory]; - } - else - { - attributes - = [NSDictionary dictionaryWithObject: [NSNumber numberWithInt: 0700] - forKey: NSFilePosixPermissions]; - [fm createDirectoryAtPath: directory - attributes: attributes]; - } - - directoryIsSane = YES; -} - -- (NSArray *) _objectsInDirectory: (BOOL) dirs -{ - NSFileManager *fm; - NSArray *contents; - NSMutableArray *files; - NSUInteger count, max; - NSString *file, *fullName; - BOOL isDir; - - if (!directoryIsSane) - [self ensureDirectory]; - - fm = [NSFileManager defaultManager]; - contents = [fm directoryContentsAtPath: directory]; - max = [contents count]; - files = [NSMutableArray arrayWithCapacity: max]; - for (count = 0; count < max; count++) - { - file = [contents objectAtIndex: count]; - if (![file isEqualToString: @"permissions.plist"]) - { - fullName = [directory stringByAppendingPathComponent: file]; - if ([fm fileExistsAtPath: fullName - isDirectory: &isDir] - && dirs == isDir) - [files addObject: file]; - } - } - - return files; -} - -- (NSArray *) toManyRelationshipKeys -{ - return [self _objectsInDirectory: YES]; -} - -- (NSArray *) toOneRelationshipKeys -{ - return [self _objectsInDirectory: NO]; -} - -- (NSArray *) toOneRelationshipKeysMatchingQualifier: (EOQualifier *) qualifier - andSortOrderings: (NSArray *) sortOrderings -{ - NSArray *allKeys; - NSMutableArray *keys; - NSUInteger count, max; - NSString *messageKey; - SOGoMAPIFSMessage *message; - - if (sortOrderings) - [self warnWithFormat: @"sorting is not handled yet"]; - - allKeys = [self toOneRelationshipKeys]; - if (qualifier) - { - [self logWithFormat: @"%s: getting restricted FAI keys", __PRETTY_FUNCTION__]; - max = [allKeys count]; - keys = [NSMutableArray arrayWithCapacity: max]; - for (count = 0; count < max; count++) - { - messageKey = [allKeys objectAtIndex: count]; - message = [self lookupName: messageKey - inContext: nil - acquire: NO]; - if ([qualifier evaluateMAPIVolatileMessage: message]) - [keys addObject: messageKey]; - } - } - else - keys = (NSMutableArray *) allKeys; - - return keys; -} - -- (id) lookupName: (NSString *) fileName - inContext: (WOContext *) woContext - acquire: (BOOL) acquire -{ - NSFileManager *fm; - NSString *fullName; - id object; - BOOL isDir; - - if (!directoryIsSane) - [self ensureDirectory]; - - fm = [NSFileManager defaultManager]; - fullName = [directory stringByAppendingPathComponent: fileName]; - if ([fm fileExistsAtPath: fullName - isDirectory: &isDir]) - { - if (isDir) - object = [isa objectWithName: fileName - inContainer: self]; - else - object = [SOGoMAPIFSMessage objectWithName: fileName - inContainer: self]; - } - else - object = nil; - - return object; -} - -- (id) _fileAttributeForKey: (NSString *) key -{ - NSDictionary *attributes; - - attributes = [[NSFileManager defaultManager] - fileAttributesAtPath: directory - traverseLink: NO]; - - return [attributes objectForKey: key]; -} - -- (NSCalendarDate *) creationTime -{ - return [self _fileAttributeForKey: NSFileCreationDate]; -} - -- (NSCalendarDate *) lastModificationTime -{ - return [self _fileAttributeForKey: NSFileModificationDate]; -} - -- (NSException *) delete -{ - NSFileManager *fm; - NSException *error; - - fm = [NSFileManager defaultManager]; - - if (![fm removeFileAtPath: directory handler: NULL]) - error = [NSException exceptionWithName: @"MAPIStoreIOException" - reason: @"could not delete folder" - userInfo: nil]; - else - error = nil; - - return error; -} - -/* acl */ -- (NSString *) defaultUserID -{ - return @"default"; -} - -- (NSMutableDictionary *) _aclEntries -{ - NSMutableDictionary *aclEntries; - NSData *content; - NSString *error, *filename; - NSPropertyListFormat format; - - filename = [directory stringByAppendingPathComponent: @"permissions.plist"]; - content = [NSData dataWithContentsOfFile: filename]; - if (content) - aclEntries = [NSPropertyListSerialization propertyListFromData: content - mutabilityOption: NSPropertyListMutableContainers - format: &format - errorDescription: &error]; - else - aclEntries = nil; - if (!aclEntries) - { - aclEntries = [NSMutableDictionary dictionary]; - [aclEntries setObject: [NSMutableArray array] forKey: @"users"]; - [aclEntries setObject: [NSMutableDictionary dictionary] - forKey: @"entries"]; - } - - return aclEntries; -} - -- (void) _saveAcl: (NSDictionary *) acl -{ - NSString *filename; - NSData *content; - - filename = [directory stringByAppendingPathComponent: @"permissions.plist"]; - [self ensureDirectory]; - - if (acl) - content = [NSPropertyListSerialization - dataFromPropertyList: acl - format: NSPropertyListBinaryFormat_v1_0 - errorDescription: NULL]; - else - content = [NSData data]; - if (![content writeToFile: filename atomically: NO]) - [NSException raise: @"MAPIStoreIOException" - format: @"could not save acl"]; -} - -- (void) addUserInAcls: (NSString *) user -{ - NSMutableDictionary *acl; - NSMutableArray *users; - - acl = [self _aclEntries]; - users = [acl objectForKey: @"users"]; - [users addObjectUniquely: user]; - [self _saveAcl: acl]; -} - -- (void) removeAclsForUsers: (NSArray *) oldUsers -{ - NSDictionary *acl; - NSMutableDictionary *entries; - NSMutableArray *users; - - acl = [self _aclEntries]; - entries = [acl objectForKey: @"entries"]; - [entries removeObjectsForKeys: oldUsers]; - users = [acl objectForKey: @"users"]; - [users removeObjectsInArray: oldUsers]; - [self _saveAcl: acl]; -} - -- (NSArray *) aclUsers -{ - return [[self _aclEntries] objectForKey: @"users"]; -} - -- (NSArray *) aclsForUser: (NSString *) uid -{ - NSDictionary *entries; - - entries = [[self _aclEntries] objectForKey: @"entries"]; - - return [entries objectForKey: uid]; -} - -- (void) setRoles: (NSArray *) roles - forUser: (NSString *) uid -{ - NSMutableDictionary *acl; - NSMutableDictionary *entries; - - acl = [self _aclEntries]; - entries = [acl objectForKey: @"entries"]; - [entries setObject: roles forKey: uid]; - [self _saveAcl: acl]; -} - -@end diff --git a/OpenChange/SOGoMAPIFSMessage.h b/OpenChange/SOGoMAPIFSMessage.h deleted file mode 100644 index ec6494938..000000000 --- a/OpenChange/SOGoMAPIFSMessage.h +++ /dev/null @@ -1,47 +0,0 @@ -/* SOGoMAPIFSMessage.h - this file is part of SOGo - * - * Copyright (C) 2010 Inverse inc. - * - * Author: Wolfgang Sourdeau - * - * This file is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3, or (at your option) - * any later version. - * - * This file is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifndef SOGOMAPIFSMESSAGE_H -#define SOGOMAPIFSMESSAGE_H - -#import "SOGoMAPIVolatileMessage.h" - -@class NSDate; -@class NSString; - -@interface SOGoMAPIFSMessage : SOGoMAPIVolatileMessage -{ - NSString *completeFilename; - NSUInteger inode; - NSData *lastModificationTime; -} - -- (void) save; - -- (NSString *) completeFilename; - -- (NSDate *) creationTime; -- (NSDate *) lastModificationTime; - -@end - -#endif /* SOGOMAPIFSMESSAGE_H */ diff --git a/OpenChange/SOGoMAPIFSMessage.m b/OpenChange/SOGoMAPIFSMessage.m deleted file mode 100644 index 8a854fa1d..000000000 --- a/OpenChange/SOGoMAPIFSMessage.m +++ /dev/null @@ -1,238 +0,0 @@ -/* SOGoMAPIFSMessage.m - this file is part of SOGo - * - * Copyright (C) 2010 Inverse inc. - * - * Author: Wolfgang Sourdeau - * - * This file is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3, or (at your option) - * any later version. - * - * This file is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#import -#import -#import -#import -#import -#import -#import - -#import - -#import "SOGoMAPIFSFolder.h" - -#import "SOGoMAPIFSMessage.h" - -@implementation SOGoMAPIFSMessage - -- (id) init -{ - if ((self = [super init])) - { - completeFilename = nil; - inode = 0; - lastModificationTime = nil; - } - - return self; -} - -- (void) dealloc -{ - [completeFilename release]; - [super dealloc]; -} - -- (Class) mapistoreMessageClass -{ - NSArray *dirMembers; - NSString *className; - - /* FIXME: this method is a bit dirty */ - dirMembers = [[container directory] componentsSeparatedByString: @"/"]; - if ([dirMembers containsObject: @"fai"]) /* should not occur as FAI message - are instantiated directly in - MAPIStoreFolder */ - className = @"MAPIStoreFAIMessage"; - else if ([dirMembers containsObject: @"notes"]) - className = @"MAPIStoreNotesMessage"; - else - className = @"MAPIStoreFSMessage"; - - return NSClassFromString (className); -} - -- (NSString *) completeFilename -{ - if (!completeFilename) - { - completeFilename = [[container directory] - stringByAppendingPathComponent: nameInContainer]; - [completeFilename retain]; - } - - return completeFilename; -} - -- (BOOL) _readFileChangesDataWithDate: (NSDate **) newLMTime - andInode: (NSUInteger *) newInode -{ - BOOL rc; - NSDictionary *attributes; - - attributes = [[NSFileManager defaultManager] - fileAttributesAtPath: [self completeFilename] - traverseLink: NO]; - if (attributes) - { - *newLMTime = [attributes fileModificationDate]; - *newInode = [attributes fileSystemFileNumber]; - rc = YES; - } - else - rc = NO; - - return rc; -} - -- (BOOL) _checkFileChangesDataWithDate: (NSDate **) newLMTime - andInode: (NSUInteger *) newInode -{ - BOOL hasChanged = NO; - NSDate *lastLMTime; - NSUInteger lastInode; - - if ([self _readFileChangesDataWithDate: &lastLMTime - andInode: &lastInode]) - { - if (inode != lastInode - || ![lastModificationTime isEqual: lastLMTime]) - { - if (lastLMTime) - *newLMTime = lastLMTime; - if (newInode) - *newInode = lastInode; - hasChanged = YES; - } - } - - return hasChanged; -} - -- (NSMutableDictionary *) properties -{ - NSData *content; - NSString *error; - NSPropertyListFormat format; - NSDate *lastLMTime; - NSUInteger lastInode; - - if ([self _checkFileChangesDataWithDate: &lastLMTime - andInode: &lastInode]) - { - [self logWithFormat: @"file '%@' new or modified: rereading properties", - [self completeFilename]]; - [properties release]; - properties = nil; - content = [NSData dataWithContentsOfFile: [self completeFilename]]; - if (content) - { - properties = [NSPropertyListSerialization propertyListFromData: content - mutabilityOption: NSPropertyListMutableContainers - format: &format - errorDescription: &error]; - [properties retain]; - if (!properties) - [self logWithFormat: @"an error occurred during deserialization" - @" of message: '%@'", error]; - } - ASSIGN (lastModificationTime, lastLMTime); - inode = lastInode; - } - - return [super properties]; -} - -- (void) save -{ - NSData *content; - NSDate *lastLMTime; - NSUInteger lastInode; - - [container ensureDirectory]; - - // [self logWithFormat: @"%d props in whole dict", [properties count]]; - - content = [NSPropertyListSerialization - dataFromPropertyList: [self properties] - format: NSPropertyListBinaryFormat_v1_0 - errorDescription: NULL]; - if (![content writeToFile: [self completeFilename] atomically: YES]) - [NSException raise: @"MAPIStoreIOException" - format: @"could not save message"]; - - [self _readFileChangesDataWithDate: &lastLMTime andInode: &lastInode]; - ASSIGN (lastModificationTime, lastLMTime); - inode = lastInode; - // [self logWithFormat: @"fs message written to '%@'", [self completeFilename]]; -} - -- (NSString *) davEntityTag -{ - NSDate *lm; - - lm = [self lastModificationTime]; - - return [NSString stringWithFormat: @"%d", (int) [lm timeIntervalSince1970]]; -} - -- (NSException *) delete -{ - NSFileManager *fm; - NSException *error; - - fm = [NSFileManager defaultManager]; - - if (![fm removeFileAtPath: [self completeFilename] handler: NULL]) - error = [NSException exceptionWithName: @"MAPIStoreIOException" - reason: @"could not delete message" - userInfo: nil]; - else - error = nil; - - return error; -} - -- (id) _fileAttributeForKey: (NSString *) key -{ - NSDictionary *attributes; - - attributes = [[NSFileManager defaultManager] - fileAttributesAtPath: [self completeFilename] - traverseLink: NO]; - - return [attributes objectForKey: key]; -} - -- (NSDate *) creationTime -{ - return [self _fileAttributeForKey: NSFileCreationDate]; -} - -- (NSDate *) lastModificationTime -{ - return [self _fileAttributeForKey: NSFileModificationDate]; -} - -@end diff --git a/OpenChange/SOGoMAPIVolatileMessage.h b/OpenChange/SOGoMAPIObject.h similarity index 63% rename from OpenChange/SOGoMAPIVolatileMessage.h rename to OpenChange/SOGoMAPIObject.h index 00596fef6..fbebaf641 100644 --- a/OpenChange/SOGoMAPIVolatileMessage.h +++ b/OpenChange/SOGoMAPIObject.h @@ -1,12 +1,12 @@ -/* SOGoMAPIVolatileMessage.h - this file is part of SOGo +/* SOGoMAPIObject.h - this file is part of SOGo * - * Copyright (C) 2011 Inverse inc + * Copyright (C) 2012 Inverse inc * * Author: Wolfgang Sourdeau * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3, or (at your option) + * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This file is distributed in the hope that it will be useful, @@ -20,22 +20,30 @@ * Boston, MA 02111-1307, USA. */ -#ifndef SOGOMAPIVOLATILEMESSAGE_H -#define SOGOMAPIVOLATILEMESSAGE_H +#ifndef SOGOMAPIOBJECT_H +#define SOGOMAPIOBJECT_H #import -@class NSDictionary; @class NSMutableDictionary; -@interface SOGoMAPIVolatileMessage : SOGoObject +@interface SOGoMAPIObject : SOGoObject { + BOOL isNew; NSMutableDictionary *properties; + NSCalendarDate *creationDate; + NSCalendarDate *lastModified; } +- (void) setIsNew: (BOOL) newIsNew; +- (BOOL) isNew; + +- (void) adjustLastModified; + - (NSMutableDictionary *) properties; -- (void) appendProperties: (NSDictionary *) newProperties; +- (NSCalendarDate *) creationDate; +- (NSCalendarDate *) lastModified; @end -#endif /* SOGOMAPIVOLATILEMESSAGE_H */ +#endif /* SOGOMAPIOBJECT_H */ diff --git a/OpenChange/SOGoMAPIVolatileMessage.m b/OpenChange/SOGoMAPIObject.m similarity index 58% rename from OpenChange/SOGoMAPIVolatileMessage.m rename to OpenChange/SOGoMAPIObject.m index b46a81c61..ec8a9ae52 100644 --- a/OpenChange/SOGoMAPIVolatileMessage.m +++ b/OpenChange/SOGoMAPIObject.m @@ -1,6 +1,6 @@ -/* SOGoMAPIVolatileMessage.m - this file is part of SOGo +/* SOGoMAPIObject.m - this file is part of SOGo * - * Copyright (C) 2011 Inverse inc + * Copyright (C) 2012 Inverse inc * * Author: Wolfgang Sourdeau * @@ -20,17 +20,19 @@ * Boston, MA 02111-1307, USA. */ -#import +#import "SOGoMAPIObject.h" -#import "SOGoMAPIVolatileMessage.h" - -@implementation SOGoMAPIVolatileMessage +@implementation SOGoMAPIObject - (id) init { if ((self = [super init])) { - properties = nil; + isNew = NO; + creationDate = [NSCalendarDate date]; + [creationDate retain]; + lastModified = [creationDate copy]; + properties = [NSMutableDictionary new]; } return self; @@ -38,21 +40,45 @@ - (void) dealloc { + [creationDate release]; + [lastModified release]; [properties release]; [super dealloc]; } +- (void) setIsNew: (BOOL) newIsNew +{ + isNew = newIsNew; +} + +- (BOOL) isNew +{ + return isNew; +} + +- (void) adjustLastModified +{ + ASSIGN (lastModified, [NSCalendarDate date]); +} + +- (BOOL) isFolderish +{ + return NO; +} + - (NSMutableDictionary *) properties { - if (!properties) - properties = [NSMutableDictionary new]; - return properties; } -- (void) appendProperties: (NSDictionary *) newProperties +- (NSCalendarDate *) creationDate { - [[self properties] addEntriesFromDictionary: newProperties]; + return creationDate; +} + +- (NSCalendarDate *) lastModified +{ + return lastModified; } @end diff --git a/OpenChange/plreader.m b/OpenChange/plreader.m index 68c7a81e5..b027a987c 100644 --- a/OpenChange/plreader.m +++ b/OpenChange/plreader.m @@ -23,164 +23,19 @@ /* A format-agnostic property list dumper. Usage: plreader [filename] */ -#import #import -#import -#import +#import #import -#import -#import -#import -#import -const char *indentationStep = " "; - -@interface NSObject (plext) - -- (void) displayWithIndentation: (NSInteger) anInt; - -@end - -@implementation NSObject (plext) - -- (void) _outputIndentation: (NSInteger) anInt -{ - NSInteger i; - - for (i = 0; i < anInt; i++) - printf ("%s", indentationStep); -} - -- (void) displayWithIndentation: (NSInteger) anInt -{ - printf ("(%s) %s", - [NSStringFromClass (isa) UTF8String], - [[self description] UTF8String]); -} - -@end - -@implementation NSDictionary (plext) - -- (void) displayKey: (NSString *) key - withIndentation: (NSInteger) anInt -{ - [self _outputIndentation: anInt]; - - printf ("%s ", [[key description] UTF8String]); - if ([key isKindOfClass: [NSValue class]]) - printf ("(%s: 0x%.8x) ", [(NSValue *) key objCType], [key intValue]); - - printf ("= "); -} - -- (void) displayWithIndentation: (NSInteger) anInt -{ - NSUInteger i, max; - NSArray *keys; - NSInteger subIndent; - NSString *key; - - keys = [self allKeys]; - max = [keys count]; - - printf ("{ (%ld) items\n", (long) max); - - subIndent = anInt + 1; - - for (i = 0; i < max; i++) - { - key = [keys objectAtIndex: i]; - [self displayKey: key withIndentation: subIndent]; - [[self objectForKey: key] displayWithIndentation: subIndent]; - if (i < (max - 1)) - printf (","); - printf ("\n"); - } - - [self _outputIndentation: anInt]; - printf ("}"); -} - -@end - -@implementation NSArray (plext) - -- (void) displayCount: (NSUInteger) count - withIndentation: (NSInteger) anInt -{ - [self _outputIndentation: anInt]; - printf ("%lu = ", (unsigned long) count); -} - -- (void) displayWithIndentation: (NSInteger) anInt -{ - NSUInteger i, max; - NSInteger subIndent; - - max = [self count]; - - printf ("[ (%ld) items\n", (long) max); - - subIndent = anInt + 1; - - for (i = 0; i < max; i++) - { - [self displayCount: i withIndentation: subIndent]; - [[self objectAtIndex: i] displayWithIndentation: subIndent]; - if (i < (max - 1)) - printf (","); - printf ("\n"); - } - - [self _outputIndentation: anInt]; - printf ("]"); -} - -@end +#import "NSObject+PropertyList.m" static void PLReaderDumpPListFile (NSString *filename) { NSData *content; - NSDictionary *d; - NSPropertyListFormat format; - NSString *error = nil; - const char *formatName; content = [NSData dataWithContentsOfFile: filename]; - d = [NSPropertyListSerialization propertyListFromData: content - mutabilityOption: NSPropertyListImmutable - format: &format - errorDescription: &error]; - if (d) - { - switch (format) - { - case NSPropertyListOpenStepFormat: - formatName = "OpenStep"; - break; - case NSPropertyListXMLFormat_v1_0: - formatName = "XML"; - break; - case NSPropertyListBinaryFormat_v1_0: - formatName = "Binary"; - break; - case NSPropertyListGNUstepFormat: - formatName = "GNUstep"; - break; - case NSPropertyListGNUstepBinaryFormat: - formatName = "GNUstep binary"; - break; - default: formatName = "unknown"; - } - - printf ("File format is: %s\n", formatName); - [d displayWithIndentation: 0]; - printf ("\n"); - } - else - printf ("an error occurred: %s\n", [error UTF8String]); + OCDumpPListData (content); } int main() From a783d81528b67a1dcbdc2f19ed065684308b7a6d Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Fri, 29 Jun 2012 19:09:02 +0000 Subject: [PATCH 2/8] Monotone-Parent: 905276f295d6f28a6946297f6a7af9ad60f71842 Monotone-Revision: 4ecb95aab8f686702ff3eb186a97b1bf1f3b5531 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-06-29T19:09:02 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 4 ++++ OpenChange/MAPIStoreAttachment.h | 1 + OpenChange/MAPIStoreAttachment.m | 23 ++++++++++++++--------- OpenChange/MAPIStoreSOGo.m | 6 +++--- 4 files changed, 22 insertions(+), 12 deletions(-) diff --git a/ChangeLog b/ChangeLog index a2d6e0ffe..440ab3de1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,9 @@ 2012-06-29 Wolfgang Sourdeau + * OpenChange/MAPIStoreSOGo.m + (sogo_message_attachment_open_embedded_message): added the "mode" + parameter. + * OpenChange/SOGoMAPIDBObject.m: new class module that replaced SOGoMAPIFSMessage. diff --git a/OpenChange/MAPIStoreAttachment.h b/OpenChange/MAPIStoreAttachment.h index f9716f7e7..1d4576572 100644 --- a/OpenChange/MAPIStoreAttachment.h +++ b/OpenChange/MAPIStoreAttachment.h @@ -37,6 +37,7 @@ - (uint32_t) AID; - (int) openEmbeddedMessage: (MAPIStoreEmbeddedMessage **) messagePtr + inMode: (enum OpenEmbeddedMessage_OpenModeFlags) mode withMID: (uint64_t *) mid withMAPIStoreMsg: (struct mapistore_message **) mapistoreMsgPtr inMemCtx: (TALLOC_CTX *) memCtx; diff --git a/OpenChange/MAPIStoreAttachment.m b/OpenChange/MAPIStoreAttachment.m index 59c8832df..86944436b 100644 --- a/OpenChange/MAPIStoreAttachment.m +++ b/OpenChange/MAPIStoreAttachment.m @@ -91,6 +91,7 @@ } - (int) openEmbeddedMessage: (MAPIStoreEmbeddedMessage **) messagePtr + inMode: (enum OpenEmbeddedMessage_OpenModeFlags) mode withMID: (uint64_t *) mid withMAPIStoreMsg: (struct mapistore_message **) mapistoreMsgPtr inMemCtx: (TALLOC_CTX *) memCtx @@ -103,19 +104,23 @@ mapping = [self mapping]; - attMessage = [self openEmbeddedMessage]; - if (attMessage) + if (mode == MAPI_CREATE) + attMessage = [self createEmbeddedMessage]; + else { - *mid = [mapping idFromURL: [attMessage url]]; - *messagePtr = attMessage; - *mapistoreMsgPtr = mapistoreMsg; + // if (attMessage) + // [mapping registerURL: [attMessage url] + // withID: *mid]; + attMessage = [self openEmbeddedMessage]; + if (attMessage) + { + *mid = [mapping idFromURL: [attMessage url]]; + *messagePtr = attMessage; + *mapistoreMsgPtr = mapistoreMsg; + } } // else if (flags == MAPI_CREATE) // { - // attMessage = [self createEmbeddedMessage]; - // if (attMessage) - // [mapping registerURL: [attMessage url] - // withID: *mid]; // } return (attMessage ? MAPISTORE_SUCCESS : MAPISTORE_ERROR); diff --git a/OpenChange/MAPIStoreSOGo.m b/OpenChange/MAPIStoreSOGo.m index 1a5fc64d0..0a474aba7 100644 --- a/OpenChange/MAPIStoreSOGo.m +++ b/OpenChange/MAPIStoreSOGo.m @@ -963,9 +963,8 @@ sogo_message_submit (void *message_object, enum SubmitFlags flags) static enum mapistore_error sogo_message_attachment_open_embedded_message -(void *attachment_object, - TALLOC_CTX *mem_ctx, void **message_object, - uint64_t *midP, +(void *attachment_object, enum OpenEmbeddedMessage_OpenModeFlags mode, + TALLOC_CTX *mem_ctx, void **message_object, uint64_t *midP, struct mapistore_message **msg) { struct MAPIStoreTallocWrapper *wrapper; @@ -983,6 +982,7 @@ sogo_message_attachment_open_embedded_message GSRegisterCurrentThread (); pool = [NSAutoreleasePool new]; rc = [attachment openEmbeddedMessage: &message + inMode: mode withMID: midP withMAPIStoreMsg: msg inMemCtx: mem_ctx]; From 3c6ed20f53cca33227007fd32b724f00a571cc5d Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Fri, 29 Jun 2012 19:25:31 +0000 Subject: [PATCH 3/8] Missing files Monotone-Parent: 0c4f43a27b60ff9951c52b8ab1d9309d5fc9f209 Monotone-Revision: 2eae9f47dfec8f0fc9908ebce5e7d249f21120bc Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-06-29T19:25:31 Monotone-Branch: ca.inverse.sogo --- OpenChange/SOGoMAPIDBMessage.h | 34 +++++++++++++++++++ OpenChange/SOGoMAPIDBMessage.m | 61 ++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+) create mode 100644 OpenChange/SOGoMAPIDBMessage.h create mode 100644 OpenChange/SOGoMAPIDBMessage.m diff --git a/OpenChange/SOGoMAPIDBMessage.h b/OpenChange/SOGoMAPIDBMessage.h new file mode 100644 index 000000000..29909a60a --- /dev/null +++ b/OpenChange/SOGoMAPIDBMessage.h @@ -0,0 +1,34 @@ +/* SOGoMAPIDBMessage.h - this file is part of SOGo + * + * Copyright (C) 2012 Inverse inc. + * + * Author: Wolfgang Sourdeau + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef SOGOMAPIDBMESSAGE_H +#define SOGOMAPIDBMESSAGE_H + +#import "SOGoMAPIDBObject.h" + +@class NSDate; +@class NSString; + +@interface SOGoMAPIDBMessage : SOGoMAPIDBObject +@end + +#endif /* SOGOMAPIDBMESSAGE_H */ diff --git a/OpenChange/SOGoMAPIDBMessage.m b/OpenChange/SOGoMAPIDBMessage.m new file mode 100644 index 000000000..f17093be1 --- /dev/null +++ b/OpenChange/SOGoMAPIDBMessage.m @@ -0,0 +1,61 @@ +/* SOGoMAPIDBMessage.m - this file is part of SOGo + * + * Copyright (C) 2012 Inverse inc. + * + * Author: Wolfgang Sourdeau + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#import +#import +#import +#import +#import +#import +#import + +#import + +#import "SOGoMAPIDBFolder.h" + +#import "SOGoMAPIDBMessage.h" + +@implementation SOGoMAPIDBMessage + +- (Class) mapistoreMessageClass +{ + // NSArray *dirMembers; + NSString *className; + + [NSException raise: @"whereisthisusedexception" + format: @"this exception should be triggered only for tracing"]; + // /* FIXME: this method is a bit dirty */ + // dirMembers = [[container directory] componentsSeparatedByString: @"/"]; + // if ([dirMembers containsObject: @"fai"]) /* should not occur as FAI message + // are instantiated directly in + // MAPIStoreFolder */ + // className = @"MAPIStoreFAIMessage"; + // else if ([dirMembers containsObject: @"notes"]) + // className = @"MAPIStoreNotesMessage"; + // else + // className = @"MAPIStoreDBMessage"; + + className = @"nimportequoi"; + return NSClassFromString (className); +} + +@end From 6987cba3c251db3ca2456488e95652d2f93d84bd Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Fri, 29 Jun 2012 19:37:15 +0000 Subject: [PATCH 4/8] Missing files Monotone-Parent: 2eae9f47dfec8f0fc9908ebce5e7d249f21120bc Monotone-Revision: 53c7bfdc2c357c162e23401ab5600d2cb4a1483e Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-06-29T19:37:15 Monotone-Branch: ca.inverse.sogo --- OpenChange/dbmsgreader.m | 109 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 OpenChange/dbmsgreader.m diff --git a/OpenChange/dbmsgreader.m b/OpenChange/dbmsgreader.m new file mode 100644 index 000000000..b4b4332b3 --- /dev/null +++ b/OpenChange/dbmsgreader.m @@ -0,0 +1,109 @@ +/* dbmsgreader.m - this file is part of SOGo + * + * Copyright (C) 2011 Inverse inc + * + * Author: Wolfgang Sourdeau + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* A format-agnostic property list readerer. + Usage: dbmsgreader [username] [filename] */ + +#import +#import +#import +#import +#import +#import +#import +#import +#import + +#import "MAPIStoreUserContext.h" +#import "SOGoMAPIDBObject.h" + +#import "NSObject+PropertyList.m" + +Class MAPIStoreUserContextK, SOGoMAPIDBObjectK; + +static void +DbDumpObject (NSString *username, NSString *path) +{ + id ctx; + NSData *content; + id dbobject; + NSDictionary *record; + + ctx = [MAPIStoreUserContextK userContextWithUsername: username + andTDBIndexing: NULL]; + dbobject = [SOGoMAPIDBObjectK new]; + [dbobject setTableUrl: [ctx folderTableURL]]; + record = [dbobject lookupRecord: path newerThanVersion: -1]; + if (record) + { + content = [[record objectForKey: @"c_content"] dataByDecodingBase64]; + OCDumpPListData (content); + } + else + NSLog (@"record not found"); + + [dbobject release]; +} + +int main (int argc, char *argv[], char *envp[]) +{ + NSAutoreleasePool *pool; + SOGoProductLoader *loader; + NSUserDefaults *ud; + SoProductRegistry *registry; + NSArray *arguments; + + /* Here we work around a bug in GNUstep which decodes XML user + defaults using the system encoding rather than honouring + the encoding specified in the file. */ + putenv ("GNUSTEP_STRING_ENCODING=NSUTF8StringEncoding"); + + pool = [NSAutoreleasePool new]; + + [SOGoSystemDefaults sharedSystemDefaults]; + + /* We force the plugin to base its configuration on the SOGo tree. */ + ud = [NSUserDefaults standardUserDefaults]; + [ud registerDefaults: [ud persistentDomainForName: @"sogod"]]; + + [NSProcessInfo initializeWithArguments: argv + count: argc + environment: envp]; + + registry = [SoProductRegistry sharedProductRegistry]; + [registry scanForProductsInDirectory: SOGO_BUNDLES_DIR]; + + loader = [SOGoProductLoader productLoader]; + [loader loadProducts: [NSArray arrayWithObject: BACKEND_BUNDLE_NAME]]; + + MAPIStoreUserContextK = NSClassFromString (@"MAPIStoreUserContext"); + SOGoMAPIDBObjectK = NSClassFromString (@"SOGoMAPIDBObject"); + + arguments = [[NSProcessInfo processInfo] arguments]; + if ([arguments count] > 2) + DbDumpObject ([arguments objectAtIndex: 1], + [arguments objectAtIndex: 2]); + + [pool release]; + + return 0; +} From 0a2244c2cb6fdd2c47cd41218ed746f118a920d8 Mon Sep 17 00:00:00 2001 From: Jean Raby Date: Fri, 29 Jun 2012 20:17:20 +0000 Subject: [PATCH 5/8] * SoObjects/SOGo/WORequest+SOGo.[mh] (-isAndroid): new method to detect whether the request has been performed from an android client * SoObjects/Contacts/SOGoContactFolders.m (appendSystemSources): Let sogo append system sources if the request comes from an android client even if its user agent matches the IPhoneAddressBook Monotone-Parent: a4ef73c2ad79c8da8d8e0c93767ab06e14bc846b Monotone-Revision: 228f7d730cde5a5046b0885c1ac02649ee2b7c05 Monotone-Author: jraby@inverse.ca Monotone-Date: 2012-06-29T20:17:20 --- ChangeLog | 10 ++++++++++ SoObjects/Contacts/SOGoContactFolders.m | 6 ++++-- SoObjects/SOGo/WORequest+SOGo.h | 1 + SoObjects/SOGo/WORequest+SOGo.m | 11 +++++++++++ 4 files changed, 26 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index d87b63f59..334c14772 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,13 @@ +2012-06-29 Jean Raby + + * SoObjects/SOGo/WORequest+SOGo.[mh] + (-isAndroid): new method to detect whether the + request has been performed from an android client + + * SoObjects/Contacts/SOGoContactFolders.m (appendSystemSources): + Let sogo append system sources if the request comes from an android + client even if its user agent matches the IPhoneAddressBook + 2012-06-27 Jean Raby * SoObjects/Appointments/SOGoAppointmentObject.m diff --git a/SoObjects/Contacts/SOGoContactFolders.m b/SoObjects/Contacts/SOGoContactFolders.m index 537d7a08a..57aa27ab4 100644 --- a/SoObjects/Contacts/SOGoContactFolders.m +++ b/SoObjects/Contacts/SOGoContactFolders.m @@ -127,7 +127,8 @@ SOGoContactSourceFolder *currentFolder; SOGoUser *currentUser; - if (![[context request] isIPhoneAddressBookApp]) + if (! ([[context request] isIPhoneAddressBookApp] && + ![[context request] isAndroid])) { currentUser = [context activeUser]; if (activeUserIsOwner @@ -271,7 +272,8 @@ NSArray *sourceIDs; NSString *domain, *srcDisplayName; - if ([[context request] isIPhoneAddressBookApp]) + if ([[context request] isIPhoneAddressBookApp] && + ![[context request] isAndroid]) { currentUser = [context activeUser]; if (activeUserIsOwner diff --git a/SoObjects/SOGo/WORequest+SOGo.h b/SoObjects/SOGo/WORequest+SOGo.h index 3983c5a80..f749d183c 100644 --- a/SoObjects/SOGo/WORequest+SOGo.h +++ b/SoObjects/SOGo/WORequest+SOGo.h @@ -36,6 +36,7 @@ - (BOOL) isICal4; - (BOOL) isMacOSXAddressBookApp; - (BOOL) isIPhoneAddressBookApp; +- (BOOL) isAndroid; @end diff --git a/SoObjects/SOGo/WORequest+SOGo.m b/SoObjects/SOGo/WORequest+SOGo.m index c7b76fbbb..b972f22e2 100644 --- a/SoObjects/SOGo/WORequest+SOGo.m +++ b/SoObjects/SOGo/WORequest+SOGo.m @@ -176,4 +176,15 @@ [[cc userAgent] rangeOfString: @"dataaccessd/1.0"].location != NSNotFound); // Seen on iOS 5.0.1 on iPad } +- (BOOL) isAndroid +{ + WEClientCapabilities *cc; + + cc = [self clientCapabilities]; + + // CardDAV-Sync (Android) (like iOS/5.0.1 (9A405) dataaccessd/1.0) gzip + return ([[cc userAgent] rangeOfString: @"Android"].location != NSNotFound); +} + + @end From 13bad20737c7ac33a8092610555df01e093a392b Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Sat, 30 Jun 2012 07:30:44 +0000 Subject: [PATCH 6/8] Monotone-Parent: 53c7bfdc2c357c162e23401ab5600d2cb4a1483e Monotone-Revision: fc5a6818c0ee3f04b21668c4b2008a9549fb34f7 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-06-30T07:30:44 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 6 +++ OpenChange/MAPIStoreSOGo.m | 66 ++++++++++++++++----------------- OpenChange/NSObject+MAPIStore.h | 2 +- OpenChange/NSObject+MAPIStore.m | 4 +- 4 files changed, 42 insertions(+), 36 deletions(-) diff --git a/ChangeLog b/ChangeLog index 884a80698..089d771c0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2012-06-30 Wolfgang Sourdeau + + * OpenChange/NSObject+MAPIStore.h: renamed + MAPIStoreTallocWrapper.MAPIStoreSOGoObject to .instance, to avoid + confusion in certain versions of GCC with our new class type. + 2012-06-29 Wolfgang Sourdeau * OpenChange/MAPIStoreSOGo.m diff --git a/OpenChange/MAPIStoreSOGo.m b/OpenChange/MAPIStoreSOGo.m index 0a474aba7..d7ba5c387 100644 --- a/OpenChange/MAPIStoreSOGo.m +++ b/OpenChange/MAPIStoreSOGo.m @@ -273,7 +273,7 @@ sogo_context_get_path(void *backend_object, TALLOC_CTX *mem_ctx, if (backend_object) { wrapper = backend_object; - context = wrapper->MAPIStoreSOGoObject; + context = wrapper->instance; GSRegisterCurrentThread (); pool = [NSAutoreleasePool new]; rc = [context getPath: path ofFMID: fmid inMemCtx: mem_ctx]; @@ -303,7 +303,7 @@ sogo_context_get_root_folder(void *backend_object, TALLOC_CTX *mem_ctx, if (backend_object) { wrapper = backend_object; - context = wrapper->MAPIStoreSOGoObject; + context = wrapper->instance; GSRegisterCurrentThread (); pool = [NSAutoreleasePool new]; rc = [context getRootFolder: &folder withFID: fid]; @@ -342,7 +342,7 @@ sogo_folder_open_folder(void *folder_object, TALLOC_CTX *mem_ctx, uint64_t fid, if (folder_object) { wrapper = folder_object; - folder = wrapper->MAPIStoreSOGoObject; + folder = wrapper->instance; GSRegisterCurrentThread (); pool = [NSAutoreleasePool new]; rc = [folder openFolder: &childFolder withFID: fid]; @@ -382,7 +382,7 @@ sogo_folder_create_folder(void *folder_object, TALLOC_CTX *mem_ctx, if (folder_object) { wrapper = folder_object; - folder = wrapper->MAPIStoreSOGoObject; + folder = wrapper->instance; GSRegisterCurrentThread (); pool = [NSAutoreleasePool new]; rc = [folder createFolder: &childFolder withRow: aRow andFID: fid]; @@ -421,7 +421,7 @@ sogo_folder_delete(void *folder_object) if (folder_object) { wrapper = folder_object; - folder = wrapper->MAPIStoreSOGoObject; + folder = wrapper->instance; GSRegisterCurrentThread (); pool = [NSAutoreleasePool new]; rc = [folder deleteFolder]; @@ -449,7 +449,7 @@ sogo_folder_get_child_count(void *folder_object, enum mapistore_table_type table if (folder_object) { wrapper = folder_object; - folder = wrapper->MAPIStoreSOGoObject; + folder = wrapper->instance; GSRegisterCurrentThread (); pool = [NSAutoreleasePool new]; rc = [folder getChildCount: child_count ofTableType: table_type]; @@ -481,7 +481,7 @@ sogo_folder_open_message(void *folder_object, if (folder_object) { wrapper = folder_object; - folder = wrapper->MAPIStoreSOGoObject; + folder = wrapper->instance; GSRegisterCurrentThread (); pool = [NSAutoreleasePool new]; rc = [folder openMessage: &message @@ -519,7 +519,7 @@ sogo_folder_create_message(void *folder_object, if (folder_object) { wrapper = folder_object; - folder = wrapper->MAPIStoreSOGoObject; + folder = wrapper->instance; GSRegisterCurrentThread (); pool = [NSAutoreleasePool new]; rc = [folder createMessage: &message @@ -551,7 +551,7 @@ sogo_folder_delete_message(void *folder_object, uint64_t mid, uint8_t flags) if (folder_object) { wrapper = folder_object; - folder = wrapper->MAPIStoreSOGoObject; + folder = wrapper->instance; GSRegisterCurrentThread (); pool = [NSAutoreleasePool new]; rc = [folder deleteMessageWithMID: mid andFlags: flags]; @@ -584,10 +584,10 @@ sogo_folder_move_copy_messages(void *folder_object, if (folder_object) { wrapper = folder_object; - targetFolder = wrapper->MAPIStoreSOGoObject; + targetFolder = wrapper->instance; wrapper = source_folder_object; - sourceFolder = wrapper->MAPIStoreSOGoObject; + sourceFolder = wrapper->instance; GSRegisterCurrentThread (); pool = [NSAutoreleasePool new]; @@ -623,7 +623,7 @@ sogo_folder_get_deleted_fmids(void *folder_object, TALLOC_CTX *mem_ctx, if (folder_object) { wrapper = folder_object; - folder = wrapper->MAPIStoreSOGoObject; + folder = wrapper->instance; GSRegisterCurrentThread (); pool = [NSAutoreleasePool new]; rc = [folder getDeletedFMIDs: fmidsp @@ -658,7 +658,7 @@ sogo_folder_open_table(void *folder_object, TALLOC_CTX *mem_ctx, if (folder_object) { wrapper = folder_object; - folder = wrapper->MAPIStoreSOGoObject; + folder = wrapper->instance; GSRegisterCurrentThread (); pool = [NSAutoreleasePool new]; rc = [folder getTable: &table @@ -693,7 +693,7 @@ sogo_folder_modify_permissions(void *folder_object, uint8_t flags, if (folder_object) { wrapper = folder_object; - folder = wrapper->MAPIStoreSOGoObject; + folder = wrapper->instance; GSRegisterCurrentThread (); pool = [NSAutoreleasePool new]; rc = [folder modifyPermissions: permissions @@ -725,7 +725,7 @@ sogo_message_get_message_data(void *message_object, if (message_object) { wrapper = message_object; - message = wrapper->MAPIStoreSOGoObject; + message = wrapper->instance; GSRegisterCurrentThread (); pool = [NSAutoreleasePool new]; [message getMessageData: msg_dataP @@ -756,7 +756,7 @@ sogo_message_create_attachment (void *message_object, TALLOC_CTX *mem_ctx, void if (message_object) { wrapper = message_object; - message = wrapper->MAPIStoreSOGoObject; + message = wrapper->instance; GSRegisterCurrentThread (); pool = [NSAutoreleasePool new]; rc = [message createAttachment: &attachment inAID: aidp]; @@ -789,7 +789,7 @@ sogo_message_open_attachment (void *message_object, TALLOC_CTX *mem_ctx, if (message_object) { wrapper = message_object; - message = wrapper->MAPIStoreSOGoObject; + message = wrapper->instance; GSRegisterCurrentThread (); pool = [NSAutoreleasePool new]; rc = [message getAttachment: &attachment withAID: aid]; @@ -821,7 +821,7 @@ sogo_message_get_attachment_table (void *message_object, TALLOC_CTX *mem_ctx, vo if (message_object) { wrapper = message_object; - message = wrapper->MAPIStoreSOGoObject; + message = wrapper->instance; GSRegisterCurrentThread (); pool = [NSAutoreleasePool new]; rc = [message getAttachmentTable: &table @@ -856,7 +856,7 @@ sogo_message_modify_recipients (void *message_object, if (message_object) { wrapper = message_object; - message = wrapper->MAPIStoreSOGoObject; + message = wrapper->instance; GSRegisterCurrentThread (); pool = [NSAutoreleasePool new]; rc = [message modifyRecipientsWithRecipients: recipients @@ -887,7 +887,7 @@ sogo_message_set_read_flag (void *message_object, uint8_t flag) if (message_object) { wrapper = message_object; - message = wrapper->MAPIStoreSOGoObject; + message = wrapper->instance; GSRegisterCurrentThread (); pool = [NSAutoreleasePool new]; rc = [message setReadFlag: flag]; @@ -916,7 +916,7 @@ sogo_message_save (void *message_object) if (message_object) { wrapper = message_object; - message = wrapper->MAPIStoreSOGoObject; + message = wrapper->instance; GSRegisterCurrentThread (); pool = [NSAutoreleasePool new]; rc = [message saveMessage]; @@ -945,7 +945,7 @@ sogo_message_submit (void *message_object, enum SubmitFlags flags) if (message_object) { wrapper = message_object; - message = wrapper->MAPIStoreSOGoObject; + message = wrapper->instance; GSRegisterCurrentThread (); pool = [NSAutoreleasePool new]; rc = [message submitWithFlags: flags]; @@ -978,7 +978,7 @@ sogo_message_attachment_open_embedded_message if (attachment_object) { wrapper = attachment_object; - attachment = wrapper->MAPIStoreSOGoObject; + attachment = wrapper->instance; GSRegisterCurrentThread (); pool = [NSAutoreleasePool new]; rc = [attachment openEmbeddedMessage: &message @@ -1012,7 +1012,7 @@ static enum mapistore_error sogo_table_get_available_properties(void *table_obje if (table_object) { wrapper = table_object; - table = wrapper->MAPIStoreSOGoObject; + table = wrapper->instance; GSRegisterCurrentThread (); pool = [NSAutoreleasePool new]; rc = [table getAvailableProperties: propertiesP inMemCtx: mem_ctx]; @@ -1040,7 +1040,7 @@ sogo_table_set_columns (void *table_object, uint16_t count, enum MAPITAGS *prope if (table_object) { wrapper = table_object; - table = wrapper->MAPIStoreSOGoObject; + table = wrapper->instance; GSRegisterCurrentThread (); pool = [NSAutoreleasePool new]; rc = [table setColumns: properties @@ -1069,7 +1069,7 @@ sogo_table_set_restrictions (void *table_object, struct mapi_SRestriction *restr if (table_object) { wrapper = table_object; - table = wrapper->MAPIStoreSOGoObject; + table = wrapper->instance; GSRegisterCurrentThread (); pool = [NSAutoreleasePool new]; [table setRestrictions: restrictions]; @@ -1100,7 +1100,7 @@ sogo_table_set_sort_order (void *table_object, struct SSortOrderSet *sort_order, if (table_object) { wrapper = table_object; - table = wrapper->MAPIStoreSOGoObject; + table = wrapper->instance; GSRegisterCurrentThread (); pool = [NSAutoreleasePool new]; [table setSortOrder: sort_order]; @@ -1133,7 +1133,7 @@ sogo_table_get_row (void *table_object, TALLOC_CTX *mem_ctx, if (table_object) { wrapper = table_object; - table = wrapper->MAPIStoreSOGoObject; + table = wrapper->instance; GSRegisterCurrentThread (); pool = [NSAutoreleasePool new]; rc = [table getRow: data withRowID: row_id andQueryType: query_type @@ -1164,7 +1164,7 @@ sogo_table_get_row_count (void *table_object, if (table_object) { wrapper = table_object; - table = wrapper->MAPIStoreSOGoObject; + table = wrapper->instance; GSRegisterCurrentThread (); pool = [NSAutoreleasePool new]; rc = [table getRowCount: row_countp @@ -1193,7 +1193,7 @@ sogo_table_handle_destructor (void *table_object, uint32_t handle_id) if (table_object) { wrapper = table_object; - table = wrapper->MAPIStoreSOGoObject; + table = wrapper->instance; GSRegisterCurrentThread (); pool = [NSAutoreleasePool new]; [table destroyHandle: handle_id]; @@ -1223,7 +1223,7 @@ static enum mapistore_error sogo_properties_get_available_properties(void *objec if (object) { wrapper = object; - propObject = wrapper->MAPIStoreSOGoObject; + propObject = wrapper->instance; GSRegisterCurrentThread (); pool = [NSAutoreleasePool new]; rc = [propObject getAvailableProperties: propertiesP inMemCtx: mem_ctx]; @@ -1254,7 +1254,7 @@ sogo_properties_get_properties (void *object, if (object) { wrapper = object; - propObject = wrapper->MAPIStoreSOGoObject; + propObject = wrapper->instance; GSRegisterCurrentThread (); pool = [NSAutoreleasePool new]; rc = [propObject getProperties: data withTags: properties @@ -1284,7 +1284,7 @@ sogo_properties_set_properties (void *object, struct SRow *aRow) if (object) { wrapper = object; - propObject = wrapper->MAPIStoreSOGoObject; + propObject = wrapper->instance; GSRegisterCurrentThread (); pool = [NSAutoreleasePool new]; rc = [propObject addPropertiesFromRow: aRow]; diff --git a/OpenChange/NSObject+MAPIStore.h b/OpenChange/NSObject+MAPIStore.h index 19254456f..2e532eb8d 100644 --- a/OpenChange/NSObject+MAPIStore.h +++ b/OpenChange/NSObject+MAPIStore.h @@ -29,7 +29,7 @@ struct MAPIStoreTallocWrapper { - id MAPIStoreSOGoObject; + id instance; }; @interface NSObject (MAPIStoreTallocHelpers) diff --git a/OpenChange/NSObject+MAPIStore.m b/OpenChange/NSObject+MAPIStore.m index 475395755..c291990f4 100644 --- a/OpenChange/NSObject+MAPIStore.m +++ b/OpenChange/NSObject+MAPIStore.m @@ -50,7 +50,7 @@ MAPIStoreTallocWrapperDestroy (void *data) pool = [NSAutoreleasePool new]; wrapper = data; // NSLog (@"destroying wrapped object (wrapper: %p; object: %p)...\n", wrapper, wrapper->MAPIStoreSOGoObject); - [wrapper->MAPIStoreSOGoObject release]; + [wrapper->instance release]; [pool release]; GSUnregisterCurrentThread (); @@ -63,7 +63,7 @@ MAPIStoreTallocWrapperDestroy (void *data) wrapper = talloc_zero (tallocCtx, struct MAPIStoreTallocWrapper); talloc_set_destructor ((void *) wrapper, MAPIStoreTallocWrapperDestroy); - wrapper->MAPIStoreSOGoObject = self; + wrapper->instance = self; [self retain]; // NSLog (@"returning wrapper: %p; object: %p", wrapper, self); From 674fb1081cc25690609752aa9d11757e9e137cae Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Sun, 1 Jul 2012 20:55:54 +0000 Subject: [PATCH 7/8] Monotone-Parent: b4eb1c4ecb6343d34ed9700a15e35cd1c9dc33af Monotone-Revision: 0ad1e10fb7587b8444bd502de4908d1aabf70c96 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-07-01T20:55:54 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 5 +++++ OpenChange/NSDate+MAPIStore.h | 6 +++++- OpenChange/NSDate+MAPIStore.m | 21 ++++++++++++++++++++- 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index 69098850d..0787be8c6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2012-07-01 Wolfgang Sourdeau + + * OpenChange/NSDate+MAPIStore.m (NSDateCompare): new comparison + function for sorting array of NSDate instances. + 2012-06-30 Wolfgang Sourdeau * OpenChange/NSObject+MAPIStore.h: renamed diff --git a/OpenChange/NSDate+MAPIStore.h b/OpenChange/NSDate+MAPIStore.h index 3c7809f79..0a01a2ddc 100644 --- a/OpenChange/NSDate+MAPIStore.h +++ b/OpenChange/NSDate+MAPIStore.h @@ -25,9 +25,11 @@ #import +@class NSCalendarDate; + @interface NSDate (MAPIStoreDataTypes) -+ (id) dateFromMinutesSince1601: (uint32_t) minutes; ++ (NSCalendarDate *) dateFromMinutesSince1601: (uint32_t) minutes; - (uint32_t) asMinutesSince1601; + (id) dateFromFileTime: (const struct FILETIME *) timeValue; @@ -37,4 +39,6 @@ @end +NSComparisonResult NSDateCompare (NSDate *date1, NSDate *date2, void *); + #endif /* NSCALENDARDATE+MAPISTORE_H */ diff --git a/OpenChange/NSDate+MAPIStore.m b/OpenChange/NSDate+MAPIStore.m index eb873978b..1e631b82b 100644 --- a/OpenChange/NSDate+MAPIStore.m +++ b/OpenChange/NSDate+MAPIStore.m @@ -51,7 +51,7 @@ _setupRefDate () timeZone: [NSTimeZone timeZoneWithName: @"UTC"]]; } -+ (id) dateFromMinutesSince1601: (uint32_t) minutes ++ (NSCalendarDate *) dateFromMinutesSince1601: (uint32_t) minutes { NSCalendarDate *result; @@ -129,3 +129,22 @@ _setupRefDate () } @end + +NSComparisonResult +NSDateCompare (NSDate *date1, NSDate *date2, void *ctx) +{ + NSTimeInterval secs1, secs2; + NSComparisonResult result; + + secs1 = [date1 timeIntervalSince1970]; + secs2 = [date2 timeIntervalSince1970]; + if (secs1 == secs2) + result = NSOrderedSame; + else if (secs1 < secs2) + result = NSOrderedAscending; + else + result = NSOrderedDescending; + + return result; +} + From b3acd2e2add97681606d1ee811282adf38c3bcdc Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Sun, 1 Jul 2012 20:58:46 +0000 Subject: [PATCH 8/8] Monotone-Parent: 0ad1e10fb7587b8444bd502de4908d1aabf70c96 Monotone-Revision: a6b977673a1cc94b1269e19c469101dca9fd17bc Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-07-01T20:58:46 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 8 ++ ...Native Microsoft Outlook Configuration.odt | Bin 27917 -> 27917 bytes OpenChange/MAPIStoreAppointmentWrapper.m | 86 ++++++++---------- OpenChange/MAPIStoreRecurrenceUtils.h | 11 ++- OpenChange/MAPIStoreRecurrenceUtils.m | 48 ++++++++-- 5 files changed, 98 insertions(+), 55 deletions(-) diff --git a/ChangeLog b/ChangeLog index 0787be8c6..43598829b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,13 @@ 2012-07-01 Wolfgang Sourdeau + * OpenChange/MAPIStoreRecurrenceUtils.m + (-setupRecurrenceWithMasterEntity:fromRecurrencePattern:): add + exception dates to master entity based on the + "DeletedInstanceDates" member of the struct. + (-fillRecurrencePattern:withEvent:inTimeZone:inMemCtx:): new name + for fillRecurrencePattern:withStartDate:andEndDate:, add exception + dates to struct. + * OpenChange/NSDate+MAPIStore.m (NSDateCompare): new comparison function for sorting array of NSDate instances. diff --git a/Documentation/SOGo Native Microsoft Outlook Configuration.odt b/Documentation/SOGo Native Microsoft Outlook Configuration.odt index a350de392a22f7c89e179aeacef624755b9329af..3761cc8329c4bc0bb68370d8af7f6d7cb5d83a0a 100644 GIT binary patch delta 19554 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 delta 19545 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 diff --git a/OpenChange/MAPIStoreAppointmentWrapper.m b/OpenChange/MAPIStoreAppointmentWrapper.m index 7862980aa..d306e3c8f 100644 --- a/OpenChange/MAPIStoreAppointmentWrapper.m +++ b/OpenChange/MAPIStoreAppointmentWrapper.m @@ -28,11 +28,8 @@ #import #import #import -#import #import #import -#import -#import #import #import #import @@ -580,7 +577,7 @@ static NSCharacterSet *hexCharacterSet = nil; } - (int) getPidTagMessageClass: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx + inMemCtx: (TALLOC_CTX *) memCtx { const char *className; @@ -633,7 +630,7 @@ static NSCharacterSet *hexCharacterSet = nil; } - (int) getPidTagStartDate: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx + inMemCtx: (TALLOC_CTX *) memCtx { NSCalendarDate *dateValue; NSInteger offset; @@ -827,7 +824,7 @@ static NSCharacterSet *hexCharacterSet = nil; /* sender (organizer) */ - (int) getPidTagSenderEmailAddress: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx + inMemCtx: (TALLOC_CTX *) memCtx { return [self _getEmailAddress: data forICalPerson: [event organizer] @@ -835,7 +832,7 @@ static NSCharacterSet *hexCharacterSet = nil; } - (int) getPidTagSenderAddressType: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx + inMemCtx: (TALLOC_CTX *) memCtx { return [self _getAddrType: data forICalPerson: [event organizer] @@ -843,7 +840,7 @@ static NSCharacterSet *hexCharacterSet = nil; } - (int) getPidTagSenderName: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx + inMemCtx: (TALLOC_CTX *) memCtx { return [self _getName: data forICalPerson: [event organizer] @@ -851,7 +848,7 @@ static NSCharacterSet *hexCharacterSet = nil; } - (int) getPidTagSenderEntryId: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx + inMemCtx: (TALLOC_CTX *) memCtx { return [self _getEntryId: data forICalPerson: [event organizer] @@ -860,7 +857,7 @@ static NSCharacterSet *hexCharacterSet = nil; /* attendee */ - (int) getPidTagReceivedByEmailAddress: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx + inMemCtx: (TALLOC_CTX *) memCtx { return [self _getEmailAddress: data forICalPerson: [event userAsAttendee: user] @@ -868,7 +865,7 @@ static NSCharacterSet *hexCharacterSet = nil; } - (int) getPidTagReceivedByAddressType: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx + inMemCtx: (TALLOC_CTX *) memCtx { return [self _getAddrType: data forICalPerson: [event userAsAttendee: user] @@ -876,7 +873,7 @@ static NSCharacterSet *hexCharacterSet = nil; } - (int) getPidTagReceivedByName: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx + inMemCtx: (TALLOC_CTX *) memCtx { return [self _getName: data forICalPerson: [event userAsAttendee: user] @@ -884,7 +881,7 @@ static NSCharacterSet *hexCharacterSet = nil; } - (int) getPidTagReceivedByEntryId: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx + inMemCtx: (TALLOC_CTX *) memCtx { return [self _getEntryId: data forICalPerson: [event userAsAttendee: user] @@ -893,7 +890,7 @@ static NSCharacterSet *hexCharacterSet = nil; /* /attendee */ - (int) getPidTagEndDate: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx + inMemCtx: (TALLOC_CTX *) memCtx { NSCalendarDate *dateValue; NSInteger offset; @@ -995,7 +992,8 @@ static NSCharacterSet *hexCharacterSet = nil; return [self getPidLidLocation: data inMemCtx: memCtx]; } -- (int) getPidLidServerProcessed: (void **) data inMemCtx: (TALLOC_CTX *) memCtx +- (int) getPidLidServerProcessed: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx { /* TODO: we need to check whether the event has been processed internally by SOGo or if it was received only by mail. We only assume the SOGo case @@ -1003,7 +1001,8 @@ static NSCharacterSet *hexCharacterSet = nil; return [self getYes: data inMemCtx: memCtx]; } -- (int) getPidLidServerProcessingActions: (void **) data inMemCtx: (TALLOC_CTX *) memCtx +- (int) getPidLidServerProcessingActions: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx { *data = MAPILongValue (memCtx, 0x00000010 /* cpsCreatedOnPrincipal */ @@ -1020,14 +1019,14 @@ static NSCharacterSet *hexCharacterSet = nil; } - (int) getPidTagSensitivity: (void **) data // not implemented, depends on CLASS - inMemCtx: (TALLOC_CTX *) memCtx + inMemCtx: (TALLOC_CTX *) memCtx { // normal = 0, personal?? = 1, private = 2, confidential = 3 return [self getLongZero: data inMemCtx: memCtx]; } - (int) getPidTagImportance: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx + inMemCtx: (TALLOC_CTX *) memCtx { uint32_t v; if ([[event priority] isEqualToString: @"9"]) @@ -1043,7 +1042,7 @@ static NSCharacterSet *hexCharacterSet = nil; } - (int) getPidTagBody: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx + inMemCtx: (TALLOC_CTX *) memCtx { int rc = MAPISTORE_SUCCESS; NSString *stringValue; @@ -1074,29 +1073,6 @@ static NSCharacterSet *hexCharacterSet = nil; return MAPISTORE_SUCCESS; } -static void -_fillAppointmentRecurrencePattern (struct AppointmentRecurrencePattern *arp, - NSCalendarDate *startDate, NSTimeInterval duration, - NSCalendarDate * endDate, iCalRecurrenceRule *rule) -{ - uint32_t startMinutes; - - [rule fillRecurrencePattern: &arp->RecurrencePattern - withStartDate: startDate andEndDate: endDate]; - arp->ReaderVersion2 = 0x00003006; - arp->WriterVersion2 = 0x00003009; - - startMinutes = ([startDate hourOfDay] * 60 + [startDate minuteOfHour]); - arp->StartTimeOffset = startMinutes; - arp->EndTimeOffset = startMinutes + (uint32_t) (duration / 60); - - arp->ExceptionCount = 0; - arp->ReservedBlock1Size = 0; - - /* Currently ignored in property.idl: - arp->ReservedBlock2Size = 0; */ -} - - (struct SBinary_short *) _computeAppointmentRecurInMemCtx: (TALLOC_CTX *) memCtx { @@ -1105,6 +1081,7 @@ _fillAppointmentRecurrencePattern (struct AppointmentRecurrencePattern *arp, struct SBinary_short *sBin; NSCalendarDate *firstStartDate; iCalRecurrenceRule *rule; + NSUInteger startMinutes; rule = [[event recurrenceRules] objectAtIndex: 0]; @@ -1114,10 +1091,27 @@ _fillAppointmentRecurrencePattern (struct AppointmentRecurrencePattern *arp, [firstStartDate setTimeZone: timeZone]; arp = talloc_zero (memCtx, struct AppointmentRecurrencePattern); - _fillAppointmentRecurrencePattern (arp, firstStartDate, - [event durationAsTimeInterval], - [event lastPossibleRecurrenceStartDate], - rule); + [rule fillRecurrencePattern: &arp->RecurrencePattern + withEvent: event + inTimeZone: timeZone + inMemCtx: arp]; + arp->ReaderVersion2 = 0x00003006; + arp->WriterVersion2 = 0x00003009; + + startMinutes = ([firstStartDate hourOfDay] * 60 + + [firstStartDate minuteOfHour]); + arp->StartTimeOffset = startMinutes; + arp->EndTimeOffset = (startMinutes + + (NSUInteger) ([event durationAsTimeInterval] + / 60)); + + arp->ExceptionCount = 0; + arp->ReservedBlock1Size = 0; + + /* Currently ignored in property.idl: arp->ReservedBlock2Size = 0; */ + + + /* convert struct to blob */ sBin = talloc_zero (memCtx, struct SBinary_short); bin = set_AppointmentRecurrencePattern (sBin, arp); sBin->cb = bin->cb; diff --git a/OpenChange/MAPIStoreRecurrenceUtils.h b/OpenChange/MAPIStoreRecurrenceUtils.h index 4fdf8c54e..a72a23af8 100644 --- a/OpenChange/MAPIStoreRecurrenceUtils.h +++ b/OpenChange/MAPIStoreRecurrenceUtils.h @@ -23,10 +23,14 @@ #ifndef MAPISTORERECURRENCEUTILS_H #define MAPISTORERECURRENCEUTILS_H +#include + #import #import -@class NSCalendarDate; +@class NSTimeZone; + +@class iCalEvent; @class iCalRepeatableEntityObject; @class iCalRecurrenceRule; @@ -44,8 +48,9 @@ @interface iCalRecurrenceRule (MAPIStoreRecurrence) - (void) fillRecurrencePattern: (struct RecurrencePattern *) rp - withStartDate: (NSCalendarDate *) startDate - andEndDate: (NSCalendarDate *) endDate; + withEvent: (iCalEvent *) event + inTimeZone: (NSTimeZone *) timeZone + inMemCtx: (TALLOC_CTX *) memCtx; @end diff --git a/OpenChange/MAPIStoreRecurrenceUtils.m b/OpenChange/MAPIStoreRecurrenceUtils.m index 09d6ce793..00ddf437a 100644 --- a/OpenChange/MAPIStoreRecurrenceUtils.m +++ b/OpenChange/MAPIStoreRecurrenceUtils.m @@ -21,17 +21,22 @@ */ #import +#import #import #import #import +#import +#import +#import #import #import -#import +#import #import "NSDate+MAPIStore.h" #import "MAPIStoreRecurrenceUtils.h" +#import "MAPIStoreTypes.h" #include #include @@ -43,7 +48,7 @@ - (void) setupRecurrenceWithMasterEntity: (iCalRepeatableEntityObject *) entity fromRecurrencePattern: (struct RecurrencePattern *) rp { - NSCalendarDate *startDate, *olEndDate, *untilDate; + NSCalendarDate *startDate, *olEndDate, *untilDate, *exDate; NSString *monthDay, *month; iCalRecurrenceRule *rule; iCalByDayMask *byDayMask; @@ -203,6 +208,17 @@ [self errorWithFormat: @"invalid value for EndType: %.4x", rp->EndType]; } + + /* exception dates */ + for (count = 0; count < rp->DeletedInstanceCount; count++) + { + exDate + = [NSDate dateFromMinutesSince1601: rp->DeletedInstanceDates[count]]; + exDate = [exDate hour: [startDate hourOfDay] + minute: [startDate minuteOfHour] + second: [startDate secondOfMinute]]; + [entity addToExceptionDates: exDate]; + } } @end @@ -210,17 +226,24 @@ @implementation iCalRecurrenceRule (MAPIStoreRecurrence) - (void) fillRecurrencePattern: (struct RecurrencePattern *) rp - withStartDate: (NSCalendarDate *) startDate - andEndDate: (NSCalendarDate *) endDate + withEvent: (iCalEvent *) event + inTimeZone: (NSTimeZone *) timeZone + inMemCtx: (TALLOC_CTX *) memCtx { iCalRecurrenceFrequency freq; iCalByDayMask *byDayMask; NSString *byMonthDay, *bySetPos; - NSCalendarDate *untilDate, *beginOfWeek, *minimumDate, *moduloDate, *midnight; + NSCalendarDate *startDate, *endDate, *untilDate, *beginOfWeek, *minimumDate, *moduloDate, *midnight; iCalWeekOccurrences *days; - NSInteger dayOfWeek, repeatInterval, repeatCount, count, firstOccurrence; + NSInteger dayOfWeek, repeatInterval, repeatCount, count, firstOccurrence, max; uint32_t nbrMonths, mask; + NSArray *exDates; + startDate = [event firstRecurrenceStartDate]; + [startDate setTimeZone: timeZone]; + endDate = [event lastPossibleRecurrenceStartDate]; + [endDate setTimeZone: timeZone]; + rp->ReaderVersion = 0x3004; rp->WriterVersion = 0x3004; @@ -370,6 +393,19 @@ [self errorWithFormat: @"rule for an event that never occurs"]; } } + + + exDates = [[event exceptionDatesWithTimeZone: utcTZ] + sortedArrayUsingFunction: NSDateCompare + context: NULL]; + max = [exDates count]; + rp->DeletedInstanceCount = max; + rp->DeletedInstanceDates = talloc_array (memCtx, uint32_t, max); + for (count = 0; count < max; count++) + { + startDate = [[exDates objectAtIndex: count] hour: 0 minute: 0 second: 0]; + *(rp->DeletedInstanceDates + count) = [startDate asMinutesSince1601]; + } } @end