From 81f335088850182319383ec75856fb9a559c82c1 Mon Sep 17 00:00:00 2001 From: Francis Lachapelle Date: Thu, 15 Dec 2011 13:36:10 +0000 Subject: [PATCH 01/68] Fixed version file to 2.0.0. Monotone-Parent: e58b519a51a061b80853202ec91f07ec470a2bf0 Monotone-Revision: 46c94a711dad0272d530ac34edf0d0a9918b7510 Monotone-Author: flachapelle@inverse.ca Monotone-Date: 2011-12-15T13:36:10 Monotone-Branch: ca.inverse.sogo --- Version | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Version b/Version index 640430005..09503ebca 100644 --- a/Version +++ b/Version @@ -2,6 +2,6 @@ # This file is included by library makefiles to set the version information # of the executable. -MAJOR_VERSION=1 -MINOR_VERSION=3 -SUBMINOR_VERSION=12 +MAJOR_VERSION=2 +MINOR_VERSION=0 +SUBMINOR_VERSION=0 From dcc991ca4e1044795fe6327451492730e8da5199 Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Thu, 22 Dec 2011 21:13:53 +0000 Subject: [PATCH 02/68] Monotone-Parent: 9f17d2137546528e36f23d8a37a6bbdb69bc3a3f Monotone-Revision: 441881b5757d734efa6d8072844aa767ef1709e2 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2011-12-22T21:13:53 Monotone-Branch: ca.inverse.sogo --- OpenChange/GNUmakefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/OpenChange/GNUmakefile b/OpenChange/GNUmakefile index 7452c39fc..a28abc6d3 100644 --- a/OpenChange/GNUmakefile +++ b/OpenChange/GNUmakefile @@ -172,12 +172,12 @@ LIBMAPISTORE_CFLAGS = $(shell pkg-config libmapistore --cflags) -DSAMBA_PREFIX=" LIBMAPISTORE_LIBS = $(shell pkg-config libmapistore --libs) -lmapiproxy $(MAPISTORESOGO)_INSTALL_DIR = $(DESTDIR)/$(SAMBA_LIB_DIR)/mapistore_backends -$(MAPISTORESOGO)_LDFLAGS += \ +$(MAPISTORESOGO)_LIB_DIRS += \ -L../SoObjects/SOGo/SOGo.framework/ -lSOGo \ $(LIBMAPI_LIBS) \ $(LIBMAPISTORE_LIBS) -$(SOGOBACKEND)_LDFLAGS += \ +$(SOGOBACKEND)_LIB_DIRS += \ -L../OGoContentStore/$(GNUSTEP_OBJ_DIR)/ -lOGoContentStore \ -L../SoObjects/SOGo/SOGo.framework/ -lSOGo \ $(LIBMAPI_LIBS) \ From 094e5899bc7e877a2fadff84d877b280e266b2cc Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Fri, 30 Dec 2011 19:32:02 +0000 Subject: [PATCH 03/68] Monotone-Parent: ed89880eae2839f1f4714817c94287a44216b46f Monotone-Revision: e9388e969d7f55642fe7910f621ef54be2512a3a Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2011-12-30T19:32:02 Monotone-Branch: ca.inverse.sogo --- Tools/GNUmakefile | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Tools/GNUmakefile b/Tools/GNUmakefile index 920ad2d82..c1bf7785b 100644 --- a/Tools/GNUmakefile +++ b/Tools/GNUmakefile @@ -4,6 +4,7 @@ include ../config.make include $(GNUSTEP_MAKEFILES)/common.make include ../Version +### SOGO_TOOL = sogo-tool $(SOGO_TOOL)_INSTALL_DIR = $(SOGO_ADMIN_TOOLS) $(SOGO_TOOL)_OBJC_FILES += \ @@ -18,7 +19,9 @@ $(SOGO_TOOL)_OBJC_FILES += \ SOGoToolRenameUser.m \ SOGoToolUserPreferences.m \ SOGoToolExpireAutoReply.m +TOOL_NAME += $(SOGO_TOOL) +### SOGO_SLAPD_SOCKD = sogo-slapd-sockd $(SOGO_SLAPD_SOCKD)_INSTALL_DIR = $(SOGO_ADMIN_TOOLS) $(SOGO_SLAPD_SOCKD)_OBJC_FILES += \ @@ -27,15 +30,17 @@ $(SOGO_SLAPD_SOCKD)_OBJC_FILES += \ SOGoSockD.m \ SOGoSockDScanner.m \ SOGoSockDOperation.m \ +TOOL_NAME += $(SOGO_SLAPD_SOCKD) +### SOGO_EALARMS_NOTIFY = sogo-ealarms-notify $(SOGO_EALARMS_NOTIFY)_INSTALL_DIR = $(SOGO_ADMIN_TOOLS) $(SOGO_EALARMS_NOTIFY)_OBJC_FILES += \ sogo-ealarms-notify.m \ \ SOGoEAlarmsNotifier.m +TOOL_NAME += $(SOGO_EALARMS_NOTIFY) -TOOL_NAME = $(SOGO_TOOL) $(SOGO_SLAPD_SOCKD) $(SOGO_EALARMS_NOTIFY) -include GNUmakefile.preamble include $(GNUSTEP_MAKEFILES)/tool.make From 540237248cc282ebc3ad4bef3c9b1b32422847d7 Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Fri, 30 Dec 2011 19:44:42 +0000 Subject: [PATCH 04/68] Monotone-Parent: e9388e969d7f55642fe7910f621ef54be2512a3a Monotone-Revision: 28ce727aaf05b14f23fbcec914f8b827755f3483 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2011-12-30T19:44:42 Monotone-Branch: ca.inverse.sogo --- SOPE/NGCards/CardElement.m | 2 -- 1 file changed, 2 deletions(-) diff --git a/SOPE/NGCards/CardElement.m b/SOPE/NGCards/CardElement.m index ec4d7404f..7064c81f9 100644 --- a/SOPE/NGCards/CardElement.m +++ b/SOPE/NGCards/CardElement.m @@ -460,8 +460,6 @@ _orderedValuesAreVoid (NSArray *orderedValues) NSMutableArray *orderedValues; NSUInteger count, max; - result = YES; - keys = [values allKeys]; max = [keys count]; for (count = 0; result && count < max; count++) From 843be30a45c905ac54ac900c2dbf4f2edd42b33f Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Fri, 30 Dec 2011 19:45:58 +0000 Subject: [PATCH 05/68] Monotone-Parent: 28ce727aaf05b14f23fbcec914f8b827755f3483 Monotone-Revision: ec02ae62fee05e50774a4da52cc17e4811526340 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2011-12-30T19:45:58 Monotone-Branch: ca.inverse.sogo --- SOPE/NGCards/CardGroup.h | 5 ++++- SOPE/NGCards/CardGroup.m | 30 ++++++++++++++++++++++++++++++ SOPE/NGCards/ChangeLog | 6 ++++++ 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/SOPE/NGCards/CardGroup.h b/SOPE/NGCards/CardGroup.h index 4535f041d..117d00c54 100644 --- a/SOPE/NGCards/CardGroup.h +++ b/SOPE/NGCards/CardGroup.h @@ -46,12 +46,15 @@ - (CardElement *) uniqueChildWithTag: (NSString *) aTag; - (void) setUniqueChild: (CardElement *) aChild; +- (NSMutableArray *) children; + - (void) addChild: (CardElement *) aChild; - (void) addChildren: (NSArray *) someChildren; - (void) removeChild: (CardElement *) aChild; - (void) removeChildren: (NSArray *) someChildren; -- (NSMutableArray *) children; +- (void) cleanupEmptyChildren; + - (CardElement *) firstChildWithTag: (NSString *) aTag; - (NSArray *) childrenWithTag: (NSString *) aTag; - (NSArray *) childrenWithAttribute: (NSString *) anAttribute diff --git a/SOPE/NGCards/CardGroup.m b/SOPE/NGCards/CardGroup.m index c6ef8bb62..d19b1a321 100644 --- a/SOPE/NGCards/CardGroup.m +++ b/SOPE/NGCards/CardGroup.m @@ -142,6 +142,19 @@ static NGCardsSaxHandler *sax = nil; return nil; } +- (BOOL) isVoid +{ + BOOL isVoid = YES; + NSUInteger count, max; + + max = [children count]; + for (count = 0; isVoid && count < max; count++) + if (![[children objectAtIndex: count] isVoid]) + isVoid = NO; + + return isVoid; +} + - (void) addChild: (CardElement *) aChild { Class mappedClass; @@ -366,6 +379,23 @@ static NGCardsSaxHandler *sax = nil; [self addChild: newChild]; } +- (void) cleanupEmptyChildren +{ + NSUInteger max; + NSInteger count; + CardElement *child; + + max = [children count]; + for (count = max - 1; count > -1; count--) + { + child = [children objectAtIndex: count]; + if ([child isKindOfClass: [CardGroup class]]) + [(CardGroup *) child cleanupEmptyChildren]; + if ([child isVoid]) + [children removeObjectAtIndex: count]; + } +} + - (NSString *) description { NSMutableString *str; diff --git a/SOPE/NGCards/ChangeLog b/SOPE/NGCards/ChangeLog index b78570076..5b111eefd 100644 --- a/SOPE/NGCards/ChangeLog +++ b/SOPE/NGCards/ChangeLog @@ -1,3 +1,9 @@ +2011-12-30 Wolfgang Sourdeau + + * CardGroup.m (-isVoid): overriden method. + (-cleanupEmptyChildren): make use of "isVoid" to detect and remove + empty children. + 2011-11-21 Francis Lachapelle * iCalTimeZone.m (+knownTimeZoneNames): ignore files that don't From 4b4af4d32328c5df1e911b0125348b66ca9eca30 Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Fri, 30 Dec 2011 19:46:52 +0000 Subject: [PATCH 06/68] Monotone-Parent: ec02ae62fee05e50774a4da52cc17e4811526340 Monotone-Revision: d5e2b98ae15729726ed7a81b845e37f7657dcb8e Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2011-12-30T19:46:52 Monotone-Branch: ca.inverse.sogo --- SOPE/NGCards/ChangeLog | 2 ++ SOPE/NGCards/NGVCard.m | 2 ++ 2 files changed, 4 insertions(+) diff --git a/SOPE/NGCards/ChangeLog b/SOPE/NGCards/ChangeLog index 5b111eefd..8c8fe0b52 100644 --- a/SOPE/NGCards/ChangeLog +++ b/SOPE/NGCards/ChangeLog @@ -1,5 +1,7 @@ 2011-12-30 Wolfgang Sourdeau + * NGVCard.m (-initWithUid:): initialize "CLASS" and "PROFILE". + * CardGroup.m (-isVoid): overriden method. (-cleanupEmptyChildren): make use of "isVoid" to detect and remove empty children. diff --git a/SOPE/NGCards/NGVCard.m b/SOPE/NGCards/NGVCard.m index 97bcc92a0..db3833c6e 100644 --- a/SOPE/NGCards/NGVCard.m +++ b/SOPE/NGCards/NGVCard.m @@ -50,6 +50,8 @@ [self setTag: @"vcard"]; [self setUid: _uid]; [self setVersion: @"3.0"]; + [self setVClass: @"PUBLIC"]; + [self setProfile: @"VCARD"]; } return self; From 03e49d5985e3eac3c23abcf06b4e3e00be6ee394 Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Fri, 30 Dec 2011 20:10:32 +0000 Subject: [PATCH 07/68] Monotone-Parent: d5e2b98ae15729726ed7a81b845e37f7657dcb8e Monotone-Revision: 77e874f5910fd1b3a30173bdae0bfa2544be6c07 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2011-12-30T20:10:32 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 6 ++++++ SoObjects/Contacts/SOGoFolder+CardDAV.m | 8 ++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/ChangeLog b/ChangeLog index 9e05548a2..3d0be3178 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2011-12-30 Wolfgang Sourdeau + + * SoObjects/Contacts/SOGoFolder+CardDAV.m (_isValidFilter:): make + use of the lowercase instance of the string, which was erroneously + ignored previously. + 2011-12-29 Ludovic Marcotte * SoObjects/SOGo/SOGoSQLUserProfile.m (_sqlJsonRepresentation:): diff --git a/SoObjects/Contacts/SOGoFolder+CardDAV.m b/SoObjects/Contacts/SOGoFolder+CardDAV.m index 2f62f1641..dfb761dfa 100644 --- a/SoObjects/Contacts/SOGoFolder+CardDAV.m +++ b/SoObjects/Contacts/SOGoFolder+CardDAV.m @@ -118,10 +118,10 @@ newString = [theString lowercaseString]; - return ([theString isEqualToString: @"sn"] - || [theString isEqualToString: @"givenname"] - || [theString isEqualToString: @"mail"] - || [theString isEqualToString: @"telephonenumber"]); + return ([newString isEqualToString: @"sn"] + || [newString isEqualToString: @"givenname"] + || [newString isEqualToString: @"mail"] + || [newString isEqualToString: @"telephonenumber"]); } - (NSDictionary *) _parseContactFilter: (id ) filterElement From 486f0f7ad17dae3d537649ec2618700cfec0d780 Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Fri, 30 Dec 2011 20:29:36 +0000 Subject: [PATCH 08/68] Monotone-Parent: 77e874f5910fd1b3a30173bdae0bfa2544be6c07 Monotone-Revision: 995b68ade85d0d884cdf8f6fc87e55f0c376a80c Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2011-12-30T20:29:36 Monotone-Branch: ca.inverse.sogo --- Tests/Integration/webdavlib.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/Tests/Integration/webdavlib.py b/Tests/Integration/webdavlib.py index eee5eb243..46bc6d317 100644 --- a/Tests/Integration/webdavlib.py +++ b/Tests/Integration/webdavlib.py @@ -30,6 +30,7 @@ import sys xmlns_dav = "DAV:" xmlns_caldav = "urn:ietf:params:xml:ns:caldav" +xmlns_carddav = "urn:ietf:params:xml:ns:carddav" xmlns_inversedav = "urn:inverse:params:xml:ns:inverse-dav" url_re = None @@ -401,6 +402,25 @@ class CalDAVCalendarQuery(WebDAVREPORT): filter_node.append(cal_filter_node) self.top_node.append(filter_node) +class CardDAVAddressBookQuery(WebDAVREPORT): + def __init__(self, url, properties, searchProperty = None, searchValue = None): + WebDAVQuery.__init__(self, url) + query_tag = self.ns_mgr.register("addressbook-query", xmlns_carddav) + ns_key = self.ns_mgr.xmlns[xmlns_carddav] + self.top_node = _WD_XMLTreeElement(query_tag) + if properties is not None and len(properties) > 0: + self._initProperties(properties) + + if searchProperty is not None: + filter_node = _WD_XMLTreeElement("%s:filter" % ns_key) + self.top_node.append(filter_node) + propfilter_node = _WD_XMLTreeElement("%s:prop-filter" % ns_key, { "name": searchProperty }) + filter_node.append(propfilter_node) + match_node = _WD_XMLTreeElement("%s:text-match" % ns_key, + { "collation": "i;unicasemap", "match-type": "starts-with" }) + propfilter_node.append(match_node) + match_node.appendSubtree(None, searchValue) + class MailDAVMailQuery(WebDAVREPORT): def __init__(self, url, properties, filters = None, sort = None, ascending = True): From 4ba4c622bc4ea0a8b0ad3e136862e6b407ab56a1 Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Fri, 30 Dec 2011 20:39:07 +0000 Subject: [PATCH 09/68] Monotone-Parent: 995b68ade85d0d884cdf8f6fc87e55f0c376a80c Monotone-Revision: 61fe02b6c3bc4f0aa3797d661d014d8c256b0de9 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2011-12-30T20:39:07 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 94 +++ SoObjects/Contacts/GNUmakefile | 11 +- SoObjects/Contacts/NGVCard+SOGo.h | 6 +- SoObjects/Contacts/NGVCard+SOGo.m | 691 ++++++++++++++--- SoObjects/Contacts/NGVList+SOGo.m | 4 +- SoObjects/Contacts/NSDictionary+LDIF.h | 34 + SoObjects/Contacts/NSDictionary+LDIF.m | 111 +++ SoObjects/Contacts/NSString+LDIF.h | 34 + SoObjects/Contacts/NSString+LDIF.m | 67 ++ SoObjects/Contacts/SOGoContactEntryPhoto.h | 5 - SoObjects/Contacts/SOGoContactEntryPhoto.m | 29 +- SoObjects/Contacts/SOGoContactGCSEntry.m | 42 +- SoObjects/Contacts/SOGoContactGCSFolder.m | 1 + SoObjects/Contacts/SOGoContactLDIFEntry.h | 5 +- SoObjects/Contacts/SOGoContactLDIFEntry.m | 232 ++---- SoObjects/Contacts/SOGoContactObject.h | 21 +- SoObjects/Contacts/SOGoContactSourceFolder.h | 12 +- SoObjects/Contacts/SOGoContactSourceFolder.m | 64 +- SoObjects/SOGo/GNUmakefile | 2 + SoObjects/SOGo/LDAPSource.h | 13 + SoObjects/SOGo/LDAPSource.m | 769 ++++++++++++------- SoObjects/SOGo/NSDictionary+Utilities.h | 3 +- SoObjects/SOGo/NSDictionary+Utilities.m | 82 -- SoObjects/SOGo/NSString+Utilities.h | 2 - SoObjects/SOGo/NSString+Utilities.m | 38 - SoObjects/SOGo/SOGoSource.h | 9 +- SoObjects/SOGo/SQLSource.m | 59 +- Tools/SOGoSockDOperation.m | 9 +- Tools/SOGoToolBackup.m | 11 +- UI/Contacts/UIxContactActions.m | 2 + UI/Contacts/UIxContactEditor.h | 10 +- UI/Contacts/UIxContactEditor.m | 652 +++------------- UI/Contacts/UIxContactFolderActions.m | 4 +- UI/Contacts/UIxContactView.m | 27 +- UI/Contacts/product.plist | 18 +- UI/Templates/ContactsUI/UIxContactEditor.wox | 291 ++++--- UI/WebServerResources/ContactsUI.js | 9 +- UI/WebServerResources/UIxContactEditor.css | 6 + UI/WebServerResources/UIxContactEditor.js | 102 +-- 39 files changed, 2041 insertions(+), 1540 deletions(-) create mode 100644 SoObjects/Contacts/NSDictionary+LDIF.h create mode 100644 SoObjects/Contacts/NSDictionary+LDIF.m create mode 100644 SoObjects/Contacts/NSString+LDIF.h create mode 100644 SoObjects/Contacts/NSString+LDIF.m diff --git a/ChangeLog b/ChangeLog index 3d0be3178..371e3a5c7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,99 @@ 2011-12-30 Wolfgang Sourdeau + * UI/WebServerResources/UIxContactEditor.js + (validateContactEditor): the birth date is validated slightly + differently, by enabling empty and 2-digit years as well as single + digits months and days. + + * UI/Contacts/UIxContactView.m (-defaultAction, -dealloc): retain + and release the "card" ivar. + (_formattedURL:): the url should be displayed only if it is + non-nil AND non-empty. + (-vcardAction): removed useless method. + + * UI/Contacts/UIxContactEditor.m (init): removed the "snapshot", + "preferredEmail", "card", "photosURL" and "contactCategories" ivars. + (-ldifRecord): new getter that proxy the invocation to the client + object, but by taking the contactEmail and contactFN url + parameters. Replaces the "-snapshot" getter, since the LDIF record + is now directly edited. + (-addressBooksList): the client object container class is no + longer taken into account when fetching the current user's permissions. + (-supportCategories, -supportPhotos): new getters that enables the + categories and photo tabs. + (-setJsonContactCategories:, -jsonContactCategories): now make + use of the special "vcardcategories" parameter found in the + contact LDIF record. + + * SoObjects/SOGo/SQLSource.m (_lookupContactEntry:considerEmail:): + enhanced to copy the "c_XX" fields to unprefixed equivalents. + + * SoObjects/Contacts/NSString+LDIF.m (mustEncodeLDIFValue): new + method, replacing "_isLDIFSafe" in a clearly public manner. + + * SoObjects/SOGo/LDAPSource.m (-[NGLdapEntry _asDictionary]): new + utility method to convert an entry into a "SOGo LDIF record". + (-[NGLdapAttribute _asArrayOrString]): new utility method to + convert an LDAP attribute into a string or an array of strings + when the attribute has one or more values. + (-initFromUDSource:inDomain:): handle the new "modifiers", + "mapping" and "objectClasses" configuration keys, used when the + source instance is used as an addressbook. All LDAP fields are now + converted to lowercase. + (_searchAttributes): removed method as the special "*" attribute + is not costly enough to justify its existence, thereby reducing + code complexity. + (-lookupContactEntry:, -lookupContactEntryWithUIDorEmail:) + (-lookupLoginByDN:): merged common code in the new + -_lookupLDAPEntry: method, that accepts an EOQualifier as argument. + (--addContactEntry:withID:, -updateContactEntry: and + removeContactEntryWithID:): new methods for editing addressbook + sources. + + * SoObjects/Contacts/SOGoContactSourceFolder.m (-source): new + getter for the "source" ivar. + (-lookupName:inContext:acquire:): accept the creation of new LDIF + entries via web methods ending with "AsContact". + (-saveLDIFEntry:, -deleteLDIFEntry:): new proxy methods for the + new SOGoSource -addContactEntry:withID:, -updateContactEntry: and + removeContactEntryWithID: methods, enabling the creation, + modification and deletion of LDAP contacts. + (-aclsForUser:): implemented method based on the array returned by + -[ modifiers]. + + * SoObjects/Contacts/SOGoContactLDIFEntry.m (-vCard): the vcard is + now generated automatically from the LDIF record of the entry, + using the new method provided by NGVCard+SOGo. + (-aclsForUser:): new overriden method similar to the + implementation from SOGoContentObject. + + * SoObjects/Contacts/SOGoContactGCSEntry.m (-setLDIFRecord) + (-ldifRecord, -hasPhoto): new accessors required by the + SOGoContactObject protocol. + (-lookupName:inContext:acquire:): return the only photo element + when the "photo" key is requested, if present in the card. + (-save): now returns an NSException, instead of void. + + * SoObjects/Contacts/SOGoContactEntryPhoto.m + (+entryPhotoWithID:inContainer:, -setPhotoID:): removed methods, + since there can only be one PHOTO element per contact. + (-photo): simplified thanks to the constraint mentionned above. + + * SoObjects/Contacts/NSDictionary+LDIF.m (-ldifRecordAsString): + new method implementing the code previously found in + SOGo/NSDictionary+Utilities.m, in order to produce a textual + representation of an LDIF record. + + * SoObjects/Contacts/NSDictionary+LDIF.[hm]: new category module. + + * SoObjects/Contacts/NGVCard+SOGo.m (-asLDIFRecord): new method + implementing the conversion code previously found in + UIxContactEditor, in order to produce an "LDIF record" in the form + of an NSDictionary matching the inetOrgPerson and + mozillaAbPersonAlpha object classes. + (-updateFromLDIFRecord:) reciprocal method to "-asLDIFRecord", + with conversion code moved from UIxContactEditor. + * SoObjects/Contacts/SOGoFolder+CardDAV.m (_isValidFilter:): make use of the lowercase instance of the string, which was erroneously ignored previously. diff --git a/SoObjects/Contacts/GNUmakefile b/SoObjects/Contacts/GNUmakefile index 3e3ac2ed2..af5ed91f5 100644 --- a/SoObjects/Contacts/GNUmakefile +++ b/SoObjects/Contacts/GNUmakefile @@ -9,17 +9,20 @@ Contacts_PRINCIPAL_CLASS = SOGoContactsProduct Contacts_OBJC_FILES = \ Product.m \ - NGVCard+SOGo.m \ - NGVList+SOGo.m \ + NGVCard+SOGo.m \ + NGVList+SOGo.m \ SOGoFolder+CardDAV.m \ SOGoContactFolders.m \ SOGoContactGCSEntry.m \ SOGoContactGCSList.m \ SOGoContactGCSFolder.m \ SOGoContactLDIFEntry.m \ - SOGoContactSourceFolder.m \ - SOGoUserFolder+Contacts.m \ + SOGoContactSourceFolder.m \ + SOGoUserFolder+Contacts.m \ SOGoContactEntryPhoto.m \ + \ + NSDictionary+LDIF.m \ + NSString+LDIF.m Contacts_RESOURCE_FILES += \ product.plist \ diff --git a/SoObjects/Contacts/NGVCard+SOGo.h b/SoObjects/Contacts/NGVCard+SOGo.h index a758d86c8..49dcd55d0 100644 --- a/SoObjects/Contacts/NGVCard+SOGo.h +++ b/SoObjects/Contacts/NGVCard+SOGo.h @@ -25,9 +25,13 @@ #import +@class NSDictionary; +@class NSMutableDictionary; + @interface NGVCard (SOGoExtensions) -- (NSString *) ldifString; +- (void) updateFromLDIFRecord: (NSDictionary *) ldifRecord; +- (NSMutableDictionary *) asLDIFRecord; @end diff --git a/SoObjects/Contacts/NGVCard+SOGo.m b/SoObjects/Contacts/NGVCard+SOGo.m index 811ade06d..da86be3fa 100644 --- a/SoObjects/Contacts/NGVCard+SOGo.m +++ b/SoObjects/Contacts/NGVCard+SOGo.m @@ -21,153 +21,610 @@ */ #import +#import #import #import #import -#import +#import +#import + +#import "NSDictionary+LDIF.h" #import "NGVCard+SOGo.h" +/* +objectclass ( 2.5.6.6 NAME 'person' + DESC 'RFC2256: a person' + SUP top STRUCTURAL + MUST ( sn $ cn ) + MAY ( userPassword $ telephoneNumber $ seeAlso $ description ) ) + +objectclass ( 2.5.6.7 NAME 'organizationalPerson' + DESC 'RFC2256: an organizational person' + SUP person STRUCTURAL + MAY ( title $ x121Address $ registeredAddress $ destinationIndicator $ + preferredDeliveryMethod $ telexNumber $ teletexTerminalIdentifier $ + telephoneNumber $ internationaliSDNNumber $ + facsimileTelephoneNumber $ street $ postOfficeBox $ postalCode $ + postalAddress $ physicalDeliveryOfficeName $ ou $ st $ l ) ) + +objectclass ( 2.16.840.1.113730.3.2.2 + NAME 'inetOrgPerson' + DESC 'RFC2798: Internet Organizational Person' + SUP organizationalPerson + STRUCTURAL + MAY ( + audio $ businessCategory $ carLicense $ departmentNumber $ + displayName $ employeeNumber $ employeeType $ givenName $ + homePhone $ homePostalAddress $ initials $ jpegPhoto $ + labeledURI $ mail $ manager $ mobile $ o $ pager $ + photo $ roomNumber $ secretary $ uid $ userCertificate $ + x500uniqueIdentifier $ preferredLanguage $ + userSMIMECertificate $ userPKCS12 ) + ) + +objectclass ( 1.3.6.1.4.1.13769.9.1 NAME 'mozillaAbPersonAlpha' + SUP top AUXILIARY + MUST ( cn ) + MAY( c $ + description $ + displayName $ + facsimileTelephoneNumber $ + givenName $ + homePhone $ + l $ + mail $ + mobile $ + mozillaCustom1 $ + mozillaCustom2 $ + mozillaCustom3 $ + mozillaCustom4 $ + mozillaHomeCountryName $ + mozillaHomeLocalityName $ + mozillaHomePostalCode $ + mozillaHomeState $ + mozillaHomeStreet $ + mozillaHomeStreet2 $ + mozillaHomeUrl $ + mozillaNickname $ + mozillaSecondEmail $ + mozillaUseHtmlMail $ + mozillaWorkStreet2 $ + mozillaWorkUrl $ + nsAIMid $ + o $ + ou $ + pager $ + postalCode $ + postOfficeBox $ + sn $ + st $ + street $ + telephoneNumber $ + title ) ) + + additional vcard fields: +"vcardCategories" + +test contact (export from tb): + +dn:: Y249UHLDqW5vbSBOb20sbWFpbD1hZHIxQGVsZS5jb20= +objectclass: top +objectclass: person +objectclass: organizationalPerson +objectclass: inetOrgPerson +objectclass: mozillaAbPersonAlpha +givenName:: UHLDqW5vbQ== +sn: Nom +cn:: UHLDqW5vbSBOb20= +mozillaNickname: Surnom +mail: adr1@ele.com +mozillaSecondEmail: adralt@ele.com +nsAIMid: pseudo aim +modifytimestamp: 1324509379 +telephoneNumber: travail +homePhone: dom +facsimiletelephonenumber: fax +pager: pager +mobile: port +mozillaHomeStreet:: YWRyMSBwcml2w6ll +mozillaHomeStreet2:: YWRyMiBwcml2w6ll +mozillaHomeLocalityName: ville/locallite +mozillaHomeState:: w6l0YXQvcHJvdg== +mozillaHomePostalCode: codepos +mozillaHomeCountryName: pays +street: adr1 pro +mozillaWorkStreet2: adr2 pro +l: ville pro +st: etat pro +postalCode: codepro +c: payspro +title: fonction pro +ou: service pro +o: soc pro +mozillaWorkUrl: webpro +mozillaHomeUrl: web +birthyear: 1946 +birthmonth: 12 +birthday: 04 +mozillaCustom1: d1 +mozillaCustom2: d2 +mozillaCustom3: d3 +mozillaCustom4: d4 +description: notes + +convention: +- our "LDIF records" are inetOrgPerson + mozillaAbPersonAlpha + a few custom + fields (categories) +- all keys are lowercase + + */ + @implementation NGVCard (SOGoExtensions) -- (NSString *) ldifString +/* LDIF -> VCARD */ +- (CardElement *) _elementWithTag: (NSString *) elementTag + ofType: (NSString *) type { - NSMutableString *rc; - NSString *buffer; - NSArray *array; - NSMutableArray *marray; - NSMutableDictionary *entry; + NSArray *elements; CardElement *element; - id tmp; - entry = [NSMutableDictionary dictionary]; + elements = [self childrenWithTag: elementTag + andAttribute: @"type" havingValue: type]; + if ([elements count] > 0) + element = [elements objectAtIndex: 0]; + else + { + element = [CardElement elementWithTag: elementTag]; + [element addType: type]; + [self addChild: element]; + } - [entry setObject: [NSString stringWithFormat: @"cn=%@,mail=%@", - [self fn], [self preferredEMail]] - forKey: @"dn"]; - [entry setObject: [NSArray arrayWithObjects: @"top", @"person", - @"organizationalPerson", @"inetOrgPerson", - @"mozillaAbPersonObsolete", nil] - forKey: @"objectclass"]; + return element; +} + +- (void) _setPhoneValues: (NSDictionary *) ldifRecord +{ + CardElement *phone; + + phone = [self _elementWithTag: @"tel" ofType: @"work"]; + [phone setSingleValue: [ldifRecord objectForKey: @"telephonenumber"] forKey: @""]; + phone = [self _elementWithTag: @"tel" ofType: @"home"]; + [phone setSingleValue: [ldifRecord objectForKey: @"homephone"] forKey: @""]; + phone = [self _elementWithTag: @"tel" ofType: @"cell"]; + [phone setSingleValue: [ldifRecord objectForKey: @"mobile"] forKey: @""]; + phone = [self _elementWithTag: @"tel" ofType: @"fax"]; + [phone setSingleValue: [ldifRecord objectForKey: @"facsimiletelephonenumber"] + forKey: @""]; + phone = [self _elementWithTag: @"tel" ofType: @"pager"]; + [phone setSingleValue: [ldifRecord objectForKey: @"pager"] forKey: @""]; +} + +- (void) _setEmails: (NSDictionary *) ldifRecord +{ + CardElement *mail, *homeMail; + + mail = [self _elementWithTag: @"email" ofType: @"work"]; + [mail setSingleValue: [ldifRecord objectForKey: @"mail"] forKey: @""]; + homeMail = [self _elementWithTag: @"email" ofType: @"home"]; + [homeMail setSingleValue: [ldifRecord objectForKey: @"mozillasecondemail"] forKey: @""]; + [[self uniqueChildWithTag: @"x-mozilla-html"] + setSingleValue: [ldifRecord objectForKey: @"mozillausehtmlmail"] + forKey: @""]; +} + +- (void) updateFromLDIFRecord: (NSDictionary *) ldifRecord +{ + CardElement *element; + NSArray *units; + NSInteger year, yearOfToday, month, day; + NSCalendarDate *now; + NSString *ou; + + [self setNWithFamily: [ldifRecord objectForKey: @"sn"] + given: [ldifRecord objectForKey: @"givenname"] + additional: nil prefixes: nil suffixes: nil]; + [self setNickname: [ldifRecord objectForKey: @"mozillanickname"]]; + [self setFn: [ldifRecord objectForKey: @"displayname"]]; + [self setTitle: [ldifRecord objectForKey: @"title"]]; + + element = [self _elementWithTag: @"adr" ofType: @"home"]; + [element setSingleValue: [ldifRecord objectForKey: @"mozillahomestreet2"] + atIndex: 1 forKey: @""]; + [element setSingleValue: [ldifRecord objectForKey: @"mozillahomestreet"] + atIndex: 2 forKey: @""]; + [element setSingleValue: [ldifRecord objectForKey: @"mozillahomelocalityname"] + atIndex: 3 forKey: @""]; + [element setSingleValue: [ldifRecord objectForKey: @"mozillahomestate"] + atIndex: 4 forKey: @""]; + [element setSingleValue: [ldifRecord objectForKey: @"mozillahomepostalcode"] + atIndex: 5 forKey: @""]; + [element setSingleValue: [ldifRecord objectForKey: @"mozillahomecountryname"] + atIndex: 6 forKey: @""]; + + element = [self _elementWithTag: @"adr" ofType: @"work"]; + [element setSingleValue: [ldifRecord objectForKey: @"mozillaworkstreet2"] + atIndex: 1 forKey: @""]; + [element setSingleValue: [ldifRecord objectForKey: @"street"] + atIndex: 2 forKey: @""]; + [element setSingleValue: [ldifRecord objectForKey: @"l"] + atIndex: 3 forKey: @""]; + [element setSingleValue: [ldifRecord objectForKey: @"st"] + atIndex: 4 forKey: @""]; + [element setSingleValue: [ldifRecord objectForKey: @"postalcode"] + atIndex: 5 forKey: @""]; + [element setSingleValue: [ldifRecord objectForKey: @"c"] + atIndex: 6 forKey: @""]; + + ou = [ldifRecord objectForKey: @"ou"]; + if (ou) + units = [NSArray arrayWithObject: ou]; + else + units = nil; + [self setOrg: [ldifRecord objectForKey: @"o"] + units: units]; + + [self _setPhoneValues: ldifRecord]; + [self _setEmails: ldifRecord]; + [[self _elementWithTag: @"url" ofType: @"home"] + setSingleValue: [ldifRecord objectForKey: @"mozillahomeurl"] forKey: @""]; + [[self _elementWithTag: @"url" ofType: @"work"] + setSingleValue: [ldifRecord objectForKey: @"mozillaworkurl"] forKey: @""]; + + [[self uniqueChildWithTag: @"x-aim"] + setSingleValue: [ldifRecord objectForKey: @"nsaimid"] + forKey: @""]; + + now = [NSCalendarDate date]; + year = [[ldifRecord objectForKey: @"birthyear"] intValue]; + if (year < 100) + { + yearOfToday = [now yearOfCommonEra]; + if (year == 0) + year = yearOfToday; + else if (yearOfToday < (year + 2000)) + year += 1900; + else + year += 2000; + } + month = [[ldifRecord objectForKey: @"birthmonth"] intValue]; + day = [[ldifRecord objectForKey: @"birthday"] intValue]; + + if (year && month && day) + [self setBday: [NSString stringWithFormat: @"%.4d-%.2d-%.2d", + year, month, day]]; + else + [self setBday: @""]; + + [self setNote: [ldifRecord objectForKey: @"description"]]; + [self setCategories: [ldifRecord objectForKey: @"vcardcategories"]]; + + [self cleanupEmptyChildren]; +} + +/* VCARD -> LDIF */ +- (NSString *) _simpleValueForType: (NSString *) aType + inArray: (NSArray *) anArray + excluding: (NSString *) aTypeToExclude +{ + NSArray *elements; + NSString *value; + + elements = [anArray cardElementsWithAttribute: @"type" + havingValue: aType]; + value = nil; + + if ([elements count] > 0) + { + CardElement *ce; + int i; + + for (i = 0; i < [elements count]; i++) + { + ce = [elements objectAtIndex: i]; + value = [ce flattenedValuesForKey: @""]; + + if (!aTypeToExclude) + break; + + if (![ce hasAttribute: @"type" havingValue: aTypeToExclude]) + break; + + value = nil; + } + } + + return value; +} + +- (void) _setValue: (NSString *) key + to: (NSString *) aValue + inLDIFRecord: (NSMutableDictionary *) ldifRecord +{ + if (!aValue) + aValue = @""; + + [ldifRecord setObject: aValue forKey: key]; +} + +- (void) _setupEmailFieldsInLDIFRecord: (NSMutableDictionary *) ldifRecord +{ + NSArray *elements; + NSString *workMail, *homeMail, *potential; + unsigned int max; + + elements = [self childrenWithTag: @"email"]; + max = [elements count]; + workMail = [self _simpleValueForType: @"work" + inArray: elements excluding: nil]; + homeMail = [self _simpleValueForType: @"home" + inArray: elements excluding: nil]; + + if (max > 0) + { + potential = [[elements objectAtIndex: 0] flattenedValuesForKey: @""]; + if (!workMail) + { + if (homeMail && homeMail == potential) + { + if (max > 1) + workMail = [[elements objectAtIndex: 1] flattenedValuesForKey: @""]; + } + else + workMail = potential; + } + if (!homeMail && max > 1) + { + if (workMail && workMail == potential) + homeMail = [[elements objectAtIndex: 1] flattenedValuesForKey: @""]; + else + homeMail = potential; + } + } + + [self _setValue: @"mail" to: workMail inLDIFRecord: ldifRecord]; + [self _setValue: @"mozillasecondemail" to: homeMail inLDIFRecord: ldifRecord]; + + [self _setValue: @"mozillausehtmlmail" + to: [[self uniqueChildWithTag: @"x-mozilla-html"] + flattenedValuesForKey: @""] + inLDIFRecord: ldifRecord]; +} + +- (void) _setupOrgFieldsInLDIFRecord: (NSMutableDictionary *) ldifRecord +{ + NSMutableArray *orgServices; + CardElement *org; + NSString *service; + NSUInteger count, max; + + org = [self org]; + [self _setValue: @"o" + to: [org flattenedValueAtIndex: 0 forKey: @""] + inLDIFRecord: ldifRecord]; + max = [[org valuesForKey: @""] count]; + if (max > 1) + { + orgServices = [NSMutableArray arrayWithCapacity: max]; + for (count = 1; count < max; count++) + { + service = [org flattenedValueAtIndex: count forKey: @""]; + if ([service length] > 0) + [orgServices addObject: service]; + } + + [self _setValue: @"ou" + to: [orgServices componentsJoinedByString: @", "] + inLDIFRecord: ldifRecord]; + } +} + +- (NSMutableDictionary *) asLDIFRecord +{ + NSArray *elements, *categories; + CardElement *element; + NSMutableDictionary *ldifRecord; + NSCalendarDate *birthDay; + NSString *dn, *stringValue, *stringValue2; + + ldifRecord = [NSMutableDictionary dictionaryWithCapacity: 32]; + [ldifRecord setObject: [NSArray arrayWithObjects: @"top", @"inetOrgPerson", + @"mozillaAbPersonAlpha", nil] + forKey: @"objectClass"]; element = [self n]; - tmp = [element flattenedValueAtIndex: 1 forKey: @""]; - if ([tmp length] > 0) - [entry setObject: tmp forKey: @"givenName"]; - - tmp = [element flattenedValueAtIndex: 0 forKey: @""]; - if ([tmp length] > 0) - [entry setObject: tmp forKey: @"sn"]; + [self _setValue: @"sn" + to: [element flattenedValueAtIndex: 0 forKey: @""] + inLDIFRecord: ldifRecord]; + [self _setValue: @"givenname" + to: [element flattenedValueAtIndex: 1 forKey: @""] + inLDIFRecord: ldifRecord]; + [self _setValue: @"displayname" to: [self fn] + inLDIFRecord: ldifRecord]; + [self _setValue: @"mozillanickname" to: [self nickname] + inLDIFRecord: ldifRecord]; - tmp = [self fn]; - if (tmp) - [entry setObject: tmp forKey: @"cn"]; - - tmp = [self preferredEMail]; - if (tmp) - [entry setObject: tmp forKey: @"mail"]; - - [entry setObject: @"0Z" forKey: @"modifytimestamp"]; + elements = [self childrenWithTag: @"tel"]; + // We do this (exclude FAX) in order to avoid setting the WORK number as the FAX + // one if we do see the FAX field BEFORE the WORK number. + [self _setValue: @"telephonenumber" + to: [self _simpleValueForType: @"work" inArray: elements + excluding: @"fax"] + inLDIFRecord: ldifRecord]; + [self _setValue: @"homephone" + to: [self _simpleValueForType: @"home" inArray: elements + excluding: @"fax"] + inLDIFRecord: ldifRecord]; + [self _setValue: @"mobile" + to: [self _simpleValueForType: @"cell" inArray: elements + excluding: nil] + inLDIFRecord: ldifRecord]; + [self _setValue: @"facsimiletelephonenumber" + to: [self _simpleValueForType: @"fax" inArray: elements + excluding: nil] + inLDIFRecord: ldifRecord]; + [self _setValue: @"pager" + to: [self _simpleValueForType: @"pager" inArray: elements + excluding: nil] + inLDIFRecord: ldifRecord]; - buffer = [self nickname]; - if (buffer && [buffer length] > 0) - [entry setObject: buffer forKey: @"mozillaNickname"]; + // If we don't have a "home" and "work" phone number but + // we have a "voice" one defined, we set it to the "work" value + // This can happen when we have : + // VERSION:2.1 + // N:name;surname;;;; + // TEL;VOICE;HOME: + // TEL;VOICE;WORK: + // TEL;PAGER: + // TEL;FAX;WORK: + // TEL;CELL:514 123 1234 + // TEL;VOICE:450 456 6789 + // ADR;HOME:;;;;;; + // ADR;WORK:;;;;;; + // ADR:;;;;;; + if ([[ldifRecord objectForKey: @"telephonenumber"] length] == 0 && + [[ldifRecord objectForKey: @"homephone"] length] == 0 && + [elements count] > 0) + [self _setValue: @"telephonenumber" + to: [self _simpleValueForType: @"voice" inArray: elements + excluding: nil] + inLDIFRecord: ldifRecord]; - marray = [NSMutableArray arrayWithArray: [self childrenWithTag: @"email"]]; - [marray removeObjectsInArray: [self childrenWithTag: @"email" - andAttribute: @"type" - havingValue: @"pref"]]; - if ([marray count]) + [self _setupEmailFieldsInLDIFRecord: ldifRecord]; + + [self _setValue: @"nsaimid" + to: [[self uniqueChildWithTag: @"x-aim"] + flattenedValuesForKey: @""] + inLDIFRecord: ldifRecord]; + + elements = [self childrenWithTag: @"adr" + andAttribute: @"type" havingValue: @"work"]; + if (elements && [elements count] > 0) { - buffer = [[marray objectAtIndex: [marray count]-1] - flattenedValuesForKey: @""]; - - if ([buffer caseInsensitiveCompare: [self preferredEMail]] != NSOrderedSame) - [entry setObject: buffer forKey: @"mozillaSecondEmail"]; + element = [elements objectAtIndex: 0]; + [self _setValue: @"mozillaworkstreet2" + to: [element flattenedValueAtIndex: 1 forKey: @""] + inLDIFRecord: ldifRecord]; + [self _setValue: @"street" + to: [element flattenedValueAtIndex: 2 forKey: @""] + inLDIFRecord: ldifRecord]; + [self _setValue: @"l" + to: [element flattenedValueAtIndex: 3 forKey: @""] + inLDIFRecord: ldifRecord]; + [self _setValue: @"st" + to: [element flattenedValueAtIndex: 4 forKey: @""] + inLDIFRecord: ldifRecord]; + [self _setValue: @"postalcode" + to: [element flattenedValueAtIndex: 5 forKey: @""] + inLDIFRecord: ldifRecord]; + [self _setValue: @"c" + to: [element flattenedValueAtIndex: 6 forKey: @""] + inLDIFRecord: ldifRecord]; } - array = [self childrenWithTag: @"tel" andAttribute: @"type" havingValue: @"home"]; - if ([array count]) - [entry setObject: [[array objectAtIndex: 0] flattenedValuesForKey: @""] - forKey: @"homePhone"]; - array = [self childrenWithTag: @"tel" andAttribute: @"type" havingValue: @"fax"]; - if ([array count]) - [entry setObject: [[array objectAtIndex: 0] flattenedValuesForKey: @""] - forKey: @"fax"]; - array = [self childrenWithTag: @"tel" andAttribute: @"type" havingValue: @"cell"]; - if ([array count]) - [entry setObject: [[array objectAtIndex: 0] flattenedValuesForKey: @""] - forKey: @"mobile"]; - array = [self childrenWithTag: @"tel" andAttribute: @"type" havingValue: @"pager"]; - if ([array count]) - [entry setObject: [[array objectAtIndex: 0] flattenedValuesForKey: @""] - forKey: @"pager"]; - - array = [self childrenWithTag: @"adr" andAttribute: @"type" havingValue: @"home"]; - if ([array count]) + elements = [self childrenWithTag: @"adr" + andAttribute: @"type" havingValue: @"home"]; + if (elements && [elements count] > 0) { - tmp = [array objectAtIndex: 0]; - [entry setObject: [tmp flattenedValueAtIndex: 1 forKey: @""] - forKey: @"mozillaHomeStreet2"]; - [entry setObject: [tmp flattenedValueAtIndex: 2 forKey: @""] - forKey: @"homeStreet"]; - [entry setObject: [tmp flattenedValueAtIndex: 3 forKey: @""] - forKey: @"mozillaHomeLocalityName"]; - [entry setObject: [tmp flattenedValueAtIndex: 4 forKey: @""] - forKey: @"mozillaHomeState"]; - [entry setObject: [tmp flattenedValueAtIndex: 5 forKey: @""] - forKey: @"mozillaHomePostalCode"]; - [entry setObject: [tmp flattenedValueAtIndex: 6 forKey: @""] - forKey: @"mozillaHomeCountryName"]; + element = [elements objectAtIndex: 0]; + [self _setValue: @"mozillahomestreet2" + to: [element flattenedValueAtIndex: 1 forKey: @""] + inLDIFRecord: ldifRecord]; + [self _setValue: @"mozillahomestreet" + to: [element flattenedValueAtIndex: 2 forKey: @""] + inLDIFRecord: ldifRecord]; + [self _setValue: @"mozillahomelocalityname" + to: [element flattenedValueAtIndex: 3 forKey: @""] + inLDIFRecord: ldifRecord]; + [self _setValue: @"mozillahomestate" + to: [element flattenedValueAtIndex: 4 forKey: @""] + inLDIFRecord: ldifRecord]; + [self _setValue: @"mozillahomepostalcode" + to: [element flattenedValueAtIndex: 5 forKey: @""] + inLDIFRecord: ldifRecord]; + [self _setValue: @"mozillahomecountryname" + to: [element flattenedValueAtIndex: 6 forKey: @""] + inLDIFRecord: ldifRecord]; } - element = [self org]; - tmp = [element flattenedValueAtIndex: 0 forKey: @""]; - if ([tmp length] > 0) - [entry setObject: tmp forKey: @"o"]; - - array = [self childrenWithTag: @"adr" andAttribute: @"type" havingValue: @"work"]; - if ([array count]) + elements = [self childrenWithTag: @"url"]; + [self _setValue: @"mozillaworkurl" + to: [self _simpleValueForType: @"work" inArray: elements + excluding: nil] + inLDIFRecord: ldifRecord]; + [self _setValue: @"mozillahomeurl" + to: [self _simpleValueForType: @"home" inArray: elements + excluding: nil] + inLDIFRecord: ldifRecord]; + + // If we don't have a "work" or "home" URL but we still have + // an URL field present, let's add it to the "home" value + if ([[ldifRecord objectForKey: @"mozillaworkurl"] length] == 0 && + [[ldifRecord objectForKey: @"mozillahomeurl"] length] == 0 && + [elements count] > 0) + [self _setValue: @"mozillahomeurl" + to: [[elements objectAtIndex: 0] + flattenedValuesForKey: @""] + inLDIFRecord: ldifRecord]; + // If we do have a "work" URL but no "home" URL but two + // values URLs present, let's add the second one as the home URL + else if ([[ldifRecord objectForKey: @"mozillaworkurl"] length] > 0 && + [[ldifRecord objectForKey: @"mozillahomeurl"] length] == 0 && + [elements count] > 1) { - tmp = [array objectAtIndex: 0]; - [entry setObject: [tmp flattenedValueAtIndex: 1 forKey: @""] - forKey: @"mozillaWorkStreet2"]; - [entry setObject: [tmp flattenedValueAtIndex: 2 forKey: @""] - forKey: @"street"]; - [entry setObject: [tmp flattenedValueAtIndex: 3 forKey: @""] - forKey: @"l"]; - [entry setObject: [tmp flattenedValueAtIndex: 4 forKey: @""] - forKey: @"st"]; - [entry setObject: [tmp flattenedValueAtIndex: 5 forKey: @""] - forKey: @"postalCode"]; - [entry setObject: [tmp flattenedValueAtIndex: 6 forKey: @""] - forKey: @"c"]; + int i; + + for (i = 0; i < [elements count]; i++) + { + if ([[[elements objectAtIndex: i] flattenedValuesForKey: @""] + caseInsensitiveCompare: [ldifRecord objectForKey: @"mozillaworkurl"]] != NSOrderedSame) + { + [self _setValue: @"mozillahomeurl" + to: [[elements objectAtIndex: i] + flattenedValuesForKey: @""] + inLDIFRecord: ldifRecord]; + break; + } + } } + + [self _setValue: @"title" to: [self title] inLDIFRecord: ldifRecord]; + [self _setupOrgFieldsInLDIFRecord: ldifRecord]; - array = [self childrenWithTag: @"tel" andAttribute: @"type" havingValue: @"work"]; - if ([array count]) - [entry setObject: [[array objectAtIndex: 0] flattenedValuesForKey: @""] - forKey: @"telephoneNumber"]; + categories = [self categories]; + if ([categories count] > 0) + [ldifRecord setValue: categories forKey: @"vcardcategories"]; - array = [self childrenWithTag: @"url" andAttribute: @"type" havingValue: @"work"]; - if ([array count]) - [entry setObject: [[array objectAtIndex: 0] flattenedValuesForKey: @""] - forKey: @"workurl"]; + birthDay = [[self bday] asCalendarDate]; + if (birthDay) + { + stringValue = [NSString stringWithFormat: @"%.4d", [birthDay yearOfCommonEra]]; + [self _setValue: @"birthyear" to: stringValue inLDIFRecord: ldifRecord]; + stringValue = [NSString stringWithFormat: @"%.2d", [birthDay monthOfYear]]; + [self _setValue: @"birthmonth" to: stringValue inLDIFRecord: ldifRecord]; + stringValue = [NSString stringWithFormat: @"%.2d", [birthDay dayOfMonth]]; + [self _setValue: @"birthday" to: stringValue inLDIFRecord: ldifRecord]; + } + [self _setValue: @"description" to: [self note] inLDIFRecord: ldifRecord]; - array = [self childrenWithTag: @"url" andAttribute: @"type" havingValue: @"home"]; - if ([array count]) - [entry setObject: [[array objectAtIndex: 0] flattenedValuesForKey: @""] - forKey: @"homeurl"]; + stringValue = [ldifRecord objectForKey: @"displayname"]; + stringValue2 = [ldifRecord objectForKey: @"mail"]; + if ([stringValue length] > 0) + { + if ([stringValue2 length] > 0) + dn = [NSString stringWithFormat: @"cn=%@,mail=%@", + stringValue, stringValue2]; + else + dn = [NSString stringWithFormat: @"cn=%@", stringValue]; + } + else if ([stringValue2 length] > 0) + dn = [NSString stringWithFormat: @"mail=%@", stringValue2]; + else + dn = @""; + [ldifRecord setObject: dn forKey: @"dn"]; - tmp = [self note]; - if (tmp && [tmp length]) - [entry setObject: tmp forKey: @"description"]; - - rc = [NSMutableString stringWithString: [entry userRecordAsLDIFEntry]]; - [rc appendFormat: @"\n"]; - - return rc; + return ldifRecord; } @end /* NGVCard */ diff --git a/SoObjects/Contacts/NGVList+SOGo.m b/SoObjects/Contacts/NGVList+SOGo.m index f0bc5fd46..4e6fd7c78 100644 --- a/SoObjects/Contacts/NGVList+SOGo.m +++ b/SoObjects/Contacts/NGVList+SOGo.m @@ -27,7 +27,7 @@ #import -#import +#import "NSDictionary+LDIF.h" #import "NGVList+SOGo.h" @@ -65,7 +65,7 @@ } [entry setObject: members forKey: @"member"]; - rc = [NSMutableString stringWithString: [entry userRecordAsLDIFEntry]]; + rc = [NSMutableString stringWithString: [entry ldifRecordAsString]]; [rc appendFormat: @"\n"]; return rc; diff --git a/SoObjects/Contacts/NSDictionary+LDIF.h b/SoObjects/Contacts/NSDictionary+LDIF.h new file mode 100644 index 000000000..1fcae50dc --- /dev/null +++ b/SoObjects/Contacts/NSDictionary+LDIF.h @@ -0,0 +1,34 @@ +/* NSDictionary+LDIF.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 NSDICTIONARY_LDIF_H +#define NSDICTIONARY_LDIF_H + +#import + +@interface NSDictionary (SOGoLDIF) + +- (NSString *) ldifRecordAsString; + +@end + +#endif /* NSDICTIONARY_LDIF_H */ diff --git a/SoObjects/Contacts/NSDictionary+LDIF.m b/SoObjects/Contacts/NSDictionary+LDIF.m new file mode 100644 index 000000000..f475c0fc5 --- /dev/null +++ b/SoObjects/Contacts/NSDictionary+LDIF.m @@ -0,0 +1,111 @@ +/* NSDictionary+LDIF.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 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. + */ + +#import +#import +#import + +#import "NSString+LDIF.h" + +#import "NSDictionary+LDIF.h" + +@implementation NSDictionary (SOGoLDIF) + +- (void) _appendLDIFKey: (NSString *) key + value: (NSString *) value + toString: (NSMutableString *) ldifString +{ + if ([value isKindOfClass: [NSString class]] && [value length] > 0) + { + if ([value mustEncodeLDIFValue]) + [ldifString appendFormat: @"%@:: %@\n", + key, [value stringByEncodingBase64]]; + else + [ldifString appendFormat: @"%@: %@\n", key, value]; + } +} + +- (void) _appendLDIFKey: (NSString *) key + toString: (NSMutableString *) ldifString +{ + id value; + int count, max; + + value = [self objectForKey: key]; + if ([value isKindOfClass: [NSArray class]]) + { + max = [value count]; + for (count = 0; count < max; count++) + [self _appendLDIFKey: key value: [value objectAtIndex: count] + toString: ldifString]; + } + else + [self _appendLDIFKey: key value: [self objectForKey: key] + toString: ldifString]; +} + +- (void) _appendObjectClassesToString: (NSMutableString *) ldifString +{ + NSEnumerator *classes; + NSString *currentClass; + NSArray *objectClass; + + objectClass = [self objectForKey: @"objectClass"]; + if ([objectClass isKindOfClass: [NSString class]]) + [self _appendLDIFKey: @"objectClass" value: (NSString *) objectClass + toString: ldifString]; + else + { + classes = [objectClass objectEnumerator]; + while ((currentClass = [classes nextObject])) + [self _appendLDIFKey: @"objectClass" value: currentClass + toString: ldifString]; + } +} + +- (NSString *) ldifRecordAsString +{ + NSArray *keys; + NSMutableString *ldifString; + NSUInteger count, max; + NSString *currentKey; + +// {CalendarAccess = YES; MailAccess = YES; c_cn = "Wolfgang Sourdeau"; c_emails = ("wolfgang@test.com"); c_name = "wolfgang@test.com"; c_uid = "wolfgang@test.com"; cn = "wolfgang@test.com"; displayName = "Wolfgang Sourdeau"; dn = "cn=wolfgang@test.com,ou=evariste,o=inverse.ca"; givenName = Wolfgang; mail = "wolfgang@test.com"; objectClass = organizationalPerson; sn = Sourdeau; } + + ldifString = [NSMutableString string]; + [self _appendLDIFKey: @"dn" toString: ldifString]; + [self _appendObjectClassesToString: ldifString]; + + keys = [self allKeys]; + max = [keys count]; + for (count = 0; count < max; count++) + { + currentKey = [keys objectAtIndex: count]; + if (!([currentKey hasPrefix: @"objectClass"] + || [currentKey isEqualToString: @"dn"])) + [self _appendLDIFKey: currentKey toString: ldifString]; + } + + return ldifString; +} + +@end diff --git a/SoObjects/Contacts/NSString+LDIF.h b/SoObjects/Contacts/NSString+LDIF.h new file mode 100644 index 000000000..5f6c0f1da --- /dev/null +++ b/SoObjects/Contacts/NSString+LDIF.h @@ -0,0 +1,34 @@ +/* NSString+LDIF.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 NSSTRING_LDIF_H +#define NSSTRING_LDIF_H + +#import + +@interface NSString (SOGoLDIF) + +- (BOOL) mustEncodeLDIFValue; + +@end + +#endif /* NSSTRING_LDIF_H */ diff --git a/SoObjects/Contacts/NSString+LDIF.m b/SoObjects/Contacts/NSString+LDIF.m new file mode 100644 index 000000000..91593a446 --- /dev/null +++ b/SoObjects/Contacts/NSString+LDIF.m @@ -0,0 +1,67 @@ +/* NSString+LDIF.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 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. + */ + +#import + +#import "NSString+LDIF.h" + +@implementation NSString (SOGoLDIF) + +static NSMutableCharacterSet *safeLDIFChars = nil; +static NSMutableCharacterSet *safeLDIFStartChars = nil; + +- (void) _initSafeLDIFChars +{ + safeLDIFChars = [NSMutableCharacterSet new]; + [safeLDIFChars addCharactersInRange: NSMakeRange (0x01, 9)]; + [safeLDIFChars addCharactersInRange: NSMakeRange (0x0b, 2)]; + [safeLDIFChars addCharactersInRange: NSMakeRange (0x0e, 114)]; + + safeLDIFStartChars = [safeLDIFChars mutableCopy]; + [safeLDIFStartChars removeCharactersInString: @" :<"]; +} + +- (BOOL) mustEncodeLDIFValue +{ + int count, max; + BOOL rc; + + if (!safeLDIFChars) + [self _initSafeLDIFChars]; + + rc = NO; + + max = [self length]; + if (max > 0) + { + if ([safeLDIFStartChars characterIsMember: [self characterAtIndex: 0]]) + for (count = 1; !rc && count < max; count++) + rc = ![safeLDIFChars + characterIsMember: [self characterAtIndex: count]]; + else + rc = YES; + } + + return rc; +} + +@end diff --git a/SoObjects/Contacts/SOGoContactEntryPhoto.h b/SoObjects/Contacts/SOGoContactEntryPhoto.h index e35eb2cd7..6c44d0757 100644 --- a/SoObjects/Contacts/SOGoContactEntryPhoto.h +++ b/SoObjects/Contacts/SOGoContactEntryPhoto.h @@ -30,11 +30,6 @@ int photoID; } -+ (id) entryPhotoWithID: (int) photoId - inContainer: (id) container; - -- (void) setPhotoID: (int) newPhotoID; - - (NSString *) davContentType; @end diff --git a/SoObjects/Contacts/SOGoContactEntryPhoto.m b/SoObjects/Contacts/SOGoContactEntryPhoto.m index d90750460..7f571ca98 100644 --- a/SoObjects/Contacts/SOGoContactEntryPhoto.m +++ b/SoObjects/Contacts/SOGoContactEntryPhoto.m @@ -35,36 +35,9 @@ @implementation SOGoContactEntryPhoto -+ (id) entryPhotoWithID: (int) photoID - inContainer: (id) container -{ - id photo; - - photo - = [super objectWithName: [NSString stringWithFormat: @"photo%d", photoID] - inContainer: container]; - [photo setPhotoID: photoID]; - - return photo; -} - -- (void) setPhotoID: (int) newPhotoID -{ - photoID = newPhotoID; -} - - (NGVCardPhoto *) photo { - NGVCardPhoto *photo; - NSArray *photoElements; - - photoElements = [[container vCard] childrenWithTag: @"photo"]; - if ([photoElements count] > photoID) - photo = [photoElements objectAtIndex: photoID]; - else - photo = nil; - - return photo; + return (NGVCardPhoto *) [[container vCard] firstChildWithTag: @"photo"]; } - (id) GETAction: (WOContext *) localContext diff --git a/SoObjects/Contacts/SOGoContactGCSEntry.m b/SoObjects/Contacts/SOGoContactGCSEntry.m index 61cb32708..09c18ebbd 100644 --- a/SoObjects/Contacts/SOGoContactGCSEntry.m +++ b/SoObjects/Contacts/SOGoContactGCSEntry.m @@ -19,11 +19,15 @@ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ + #import +#import #import #import +#import +#import "NGVCard+SOGo.h" #import "SOGoContactEntryPhoto.h" #import "SOGoContactGCSEntry.h" @@ -62,6 +66,21 @@ return card; } +- (void) setLDIFRecord: (NSDictionary *) newLDIFRecord +{ + [[self vCard] updateFromLDIFRecord: newLDIFRecord]; +} + +- (NSDictionary *) ldifRecord +{ + return [[self vCard] asLDIFRecord]; +} + +- (BOOL) hasPhoto +{ + return ([[self vCard] firstChildWithTag: @"photo"] != nil); +} + /* actions */ - (id) lookupName: (NSString *) lookupName @@ -69,16 +88,12 @@ acquire: (BOOL) acquire { id obj; - int photoIndex; - NSArray *photoElements; - if ([lookupName hasPrefix: @"photo"]) + if ([lookupName isEqualToString: @"photo"]) { - photoElements = [[self vCard] childrenWithTag: @"photo"]; - photoIndex = [[lookupName substringFromIndex: 5] intValue]; - if (photoIndex > -1 && photoIndex < [photoElements count]) - obj = [SOGoContactEntryPhoto entryPhotoWithID: photoIndex - inContainer: self]; + if ([self hasPhoto]) + obj = [SOGoContactEntryPhoto objectWithName: lookupName + inContainer: self]; else obj = nil; } @@ -127,13 +142,16 @@ /* specialized actions */ -- (void) save +- (NSException *) save { - NGVCard *vcard; + NSException *result; - vcard = [self vCard]; + if (card) + result = [self saveContentString: [card versitString]]; + else + result = nil; /* TODO: we should probably return an exception instead */ - [self saveContentString: [vcard versitString]]; + return result; } - (NSException *) saveContentString: (NSString *) newContent diff --git a/SoObjects/Contacts/SOGoContactGCSFolder.m b/SoObjects/Contacts/SOGoContactGCSFolder.m index 73114b811..9fa758e75 100644 --- a/SoObjects/Contacts/SOGoContactGCSFolder.m +++ b/SoObjects/Contacts/SOGoContactGCSFolder.m @@ -45,6 +45,7 @@ #import #import #import +#import #import "SOGoContactGCSEntry.h" #import "SOGoContactGCSList.h" diff --git a/SoObjects/Contacts/SOGoContactLDIFEntry.h b/SoObjects/Contacts/SOGoContactLDIFEntry.h index d89b13d30..e2625e665 100644 --- a/SoObjects/Contacts/SOGoContactLDIFEntry.h +++ b/SoObjects/Contacts/SOGoContactLDIFEntry.h @@ -31,8 +31,8 @@ @interface SOGoContactLDIFEntry : SOGoObject { + BOOL isNew; NSDictionary *ldifEntry; - NGVCard *vcard; } + (SOGoContactLDIFEntry *) contactEntryWithName: (NSString *) newName @@ -42,6 +42,9 @@ withLDIFEntry: (NSDictionary *) newEntry inContainer: (id) newContainer; +- (BOOL) isNew; +- (void) setIsNew: (BOOL) newIsNew; + - (NSString *) davEntityTag; @end diff --git a/SoObjects/Contacts/SOGoContactLDIFEntry.m b/SoObjects/Contacts/SOGoContactLDIFEntry.m index 17d562709..f4b09e800 100644 --- a/SoObjects/Contacts/SOGoContactLDIFEntry.m +++ b/SoObjects/Contacts/SOGoContactLDIFEntry.m @@ -29,9 +29,13 @@ #import #import +#import +#import +#import "NGVCard+SOGo.h" #import "SOGoContactGCSEntry.h" #import "SOGoContactLDIFEntry.h" +#import "SOGoContactSourceFolder.h" @implementation SOGoContactLDIFEntry @@ -42,8 +46,8 @@ SOGoContactLDIFEntry *entry; entry = [[self alloc] initWithName: newName - withLDIFEntry: newEntry - inContainer: newContainer]; + withLDIFEntry: newEntry + inContainer: newContainer]; [entry autorelease]; return entry; @@ -56,7 +60,7 @@ if ((self = [self initWithName: newName inContainer: newContainer])) { ASSIGN (ldifEntry, newEntry); - vcard = nil; + isNew = NO; } return self; @@ -64,169 +68,34 @@ - (void) dealloc { - [vcard release]; [ldifEntry release]; [super dealloc]; } +- (BOOL) isNew +{ + return isNew; +} + +- (void) setIsNew: (BOOL) newIsNew +{ + isNew = newIsNew; +} + - (NSString *) contentAsString { return [[self vCard] versitString]; } -- (void) _setPhonesOfVCard: (NGVCard *) vCard -{ - NSString *info; - - info = [ldifEntry objectForKey: @"telephonenumber"]; - if (info) - [vCard addTel: info - types: [NSArray arrayWithObjects: @"work", @"voice", @"pref", nil]]; - info = [ldifEntry objectForKey: @"homephone"]; - if (info) - [vCard addTel: info - types: [NSArray arrayWithObjects: @"home", @"voice", nil]]; - info = [ldifEntry objectForKey: @"fax"]; - if (info) - [vCard addTel: info - types: [NSArray arrayWithObjects: @"work", @"fax", nil]]; - info = [ldifEntry objectForKey: @"pager"]; - if (info) - [vCard addTel: info - types: [NSArray arrayWithObjects: @"pager", nil]]; - info = [ldifEntry objectForKey: @"mobile"]; - if (info) - [vCard addTel: info - types: [NSArray arrayWithObjects: @"cell", @"voice", nil]]; - -// telephoneNumber: work phone -// homePhone: home phone -// fax: fax phone -// pager: page phone -// mobile: mobile phone - -} - - (NGVCard *) vCard { - NSString *info, *surname, *streetAddress, *location, *region, *postalCode, *country, *org, *orgunit; - CardElement *element; - unsigned int count; + NGVCard *vcard; - if (!vcard) - { - vcard = [[NGVCard alloc] initWithUid: [self nameInContainer]]; - [vcard setVClass: @"PUBLIC"]; - [vcard setProdID: [NSString - stringWithFormat: @"-//Inverse inc./SOGo %@//EN", - SOGoVersion]]; - [vcard setProfile: @"VCARD"]; - info = [ldifEntry objectForKey: @"c_cn"]; - if (![info length]) - { - info = [ldifEntry objectForKey: @"displayname"]; - if (![info length]) - info = [ldifEntry objectForKey: @"cn"]; - } - [vcard setFn: info]; - surname = [ldifEntry objectForKey: @"sn"]; - if (!surname) - surname = [ldifEntry objectForKey: @"surname"]; - [vcard setNWithFamily: surname - given: [ldifEntry objectForKey: @"givenname"] - additional: nil - prefixes: nil - suffixes: nil]; - info = [ldifEntry objectForKey: @"title"]; - if (info) - [vcard setTitle: info]; - info = [ldifEntry objectForKey: @"mozillanickname"]; - if (info) - [vcard setNickname: info]; - - /* If "c_info" is defined, we set as the NOTE value in order for - Thunderbird (or any other CardDAV client) to display it. */ - info = [ldifEntry objectForKey: @"c_info"]; - if (![info length]) - info = [ldifEntry objectForKey: @"description"]; - if ([info length]) - [vcard setNote: info]; - - info = [ldifEntry objectForKey: @"mail"]; - if (info) - [vcard addEmail: info - types: [NSArray arrayWithObjects: @"internet", @"pref", nil]]; - [self _setPhonesOfVCard: vcard]; - - streetAddress = [ldifEntry objectForKey: @"street"]; - if (!streetAddress) - streetAddress = [ldifEntry objectForKey: @"streetaddress"]; - - location = [ldifEntry objectForKey: @"l"]; - if (!location) - location = [ldifEntry objectForKey: @"locality"]; - - region = [ldifEntry objectForKey: @"st"]; - if (!region) - region = [ldifEntry objectForKey: @"region"]; - - postalCode = [ldifEntry objectForKey: @"postalcode"]; - if (!postalCode) - postalCode = [ldifEntry objectForKey: @"zip"]; - - country = [ldifEntry objectForKey: @"c"]; - if (!country) - country = [ldifEntry objectForKey: @"countryname"]; - - element = [CardElement elementWithTag: @"adr"]; - [element setValue: 0 ofAttribute: @"type" to: @"work"]; - - if (streetAddress) - [element setSingleValue: streetAddress atIndex: 2 forKey: @""]; - if (location) - [element setSingleValue: location atIndex: 3 forKey: @""]; - if (region) - [element setSingleValue: region atIndex: 4 forKey: @""]; - if (postalCode) - [element setSingleValue: postalCode atIndex: 5 forKey: @""]; - if (country) - [element setSingleValue: country atIndex: 6 forKey: @""]; - - if (streetAddress || location || region || postalCode || country) - [vcard addChild: element]; - - // We handle the org/orgunit stuff - element = [CardElement elementWithTag: @"org"]; - org = [ldifEntry objectForKey: @"o"]; - orgunit = [ldifEntry objectForKey: @"ou"]; - if (!orgunit) - orgunit = [ldifEntry objectForKey: @"orgunit"]; - - if (org) - [element setSingleValue: org atIndex: 0 forKey: @""]; - if (orgunit) - [element setSingleValue: orgunit atIndex: 1 forKey: @""]; - - if (org || orgunit) - [vcard addChild: element]; - - info = [ldifEntry objectForKey: @"calFBURL"]; - if (info) - [vcard addChildWithTag: @"FBURL" - types: nil - singleValue: info]; - for (count = 1; count < 5; count++) - { - info = [ldifEntry objectForKey: - [NSString stringWithFormat: @"mozillacustom%d", - count]]; - if (info) - [vcard addChildWithTag: [NSString stringWithFormat: @"CUSTOM%d", - count] - types: nil - singleValue: info]; - } - } + vcard = [NGVCard cardWithUid: [self nameInContainer]]; + [vcard setProdID: [NSString + stringWithFormat: @"-//Inverse inc./SOGo %@//EN", + SOGoVersion]]; + [vcard updateFromLDIFRecord: [self ldifRecord]]; return vcard; } @@ -236,6 +105,21 @@ return NO; } +- (void) setLDIFRecord: (NSDictionary *) newLDIFRecord +{ + ASSIGN (ldifEntry, newLDIFRecord); +} + +- (NSDictionary *) ldifRecord +{ + return ldifEntry; +} + +- (BOOL) hasPhoto +{ + return NO; +} + - (NSString *) davEntityTag { unsigned int hash; @@ -251,13 +135,47 @@ return @"text/x-vcard"; } -- (NSArray *) aclsForUser: (NSString *) uid +- (NSException *) save { - return nil; + return [(SOGoContactSourceFolder *) container saveLDIFEntry: self]; } -- (void) save +- (NSException *) delete { + return [(SOGoContactSourceFolder *) container deleteLDIFEntry: self]; +} + +/* acl */ + +- (NSArray *) aclsForUser: (NSString *) uid +{ + NSMutableArray *acls; + NSArray *containerAcls; + + acls = [NSMutableArray array]; + /* this is unused... */ +// ownAcls = [container aclsForUser: uid +// forObjectAtPath: [self pathArrayToSOGoObject]]; +// [acls addObjectsFromArray: ownAcls]; + containerAcls = [container aclsForUser: uid]; + if ([containerAcls count] > 0) + { + [acls addObjectsFromArray: containerAcls]; + /* The creation of an object is actually a "modification" to an + unexisting object. When the object is new, we give the + "ObjectCreator" the "ObjectModifier" role temporarily while we + disallow the "ObjectModifier" users to modify them, unless they are + ObjectCreators too. */ + if (isNew) + { + if ([containerAcls containsObject: SOGoRole_ObjectCreator]) + [acls addObject: SOGoRole_ObjectEditor]; + else + [acls removeObject: SOGoRole_ObjectEditor]; + } + } + + return acls; } /* DAV */ diff --git a/SoObjects/Contacts/SOGoContactObject.h b/SoObjects/Contacts/SOGoContactObject.h index 74b911540..6b718eefa 100644 --- a/SoObjects/Contacts/SOGoContactObject.h +++ b/SoObjects/Contacts/SOGoContactObject.h @@ -22,25 +22,20 @@ #ifndef __Contacts_SOGoContactObject_H__ #define __Contacts_SOGoContactObject_H__ -/* - SOGoContactObject - - Represents a single contact. This SOPE controller object manages all the - attendee storages (that is, it might store into multiple folders for meeting - appointments!). - - Note: SOGoContactObject do not need to exist yet. They can also be "new" - appointments with an externally generated unique key. -*/ - @class NSDictionary; -@class NSString; @class NGVCard; @protocol SOGoContactObject - (NGVCard *) vCard; -- (void) save; +- (BOOL) hasPhoto; + +/* web editing */ +- (void) setLDIFRecord: (NSDictionary *) newLDIFRecord; +- (NSDictionary *) ldifRecord; + +- (NSException *) save; +- (NSException *) delete; @end diff --git a/SoObjects/Contacts/SOGoContactSourceFolder.h b/SoObjects/Contacts/SOGoContactSourceFolder.h index 5cde076b7..09effd47d 100644 --- a/SoObjects/Contacts/SOGoContactSourceFolder.h +++ b/SoObjects/Contacts/SOGoContactSourceFolder.h @@ -26,13 +26,14 @@ #import "SOGoContactFolder.h" #import "SOGoFolder+CardDAV.h" -@class NSMutableDictionary; +#import -#import "../SOGo/SOGoSource.h" +@class NSMutableDictionary; +@class SOGoContactLDIFEntry; @interface SOGoContactSourceFolder : SOGoFolder { - id source; + id source; NSMutableDictionary *childRecords; } @@ -42,7 +43,10 @@ - (id) initWithName: (NSString *) newName andDisplayName: (NSString *) newDisplayName inContainer: (id) newContainer; -- (void) setSource: (id) newSource; +- (void) setSource: (id ) newSource; + +- (NSException *) saveLDIFEntry: (SOGoContactLDIFEntry *) ldifEntry; +- (NSException *) deleteLDIFEntry: (SOGoContactLDIFEntry *) ldifEntry; @end diff --git a/SoObjects/Contacts/SOGoContactSourceFolder.m b/SoObjects/Contacts/SOGoContactSourceFolder.m index cc25d0d76..ba576aa8f 100644 --- a/SoObjects/Contacts/SOGoContactSourceFolder.m +++ b/SoObjects/Contacts/SOGoContactSourceFolder.m @@ -38,6 +38,7 @@ #import #import +#import #import #import #import @@ -96,11 +97,16 @@ [super dealloc]; } -- (void) setSource: (id) newSource +- (void) setSource: (id ) newSource { ASSIGN (source, newSource); } +- (id ) source +{ + return source; +} + - (NSString *) groupDavResourceType { return @"vcard-collection"; @@ -126,7 +132,9 @@ acquire: (BOOL) acquire { NSDictionary *ldifEntry; - id obj; + SOGoContactLDIFEntry *obj; + NSString *url; + BOOL isNew = NO; /* first check attributes directly bound to the application */ obj = [super lookupName: objectName inContext: lookupContext acquire: NO]; @@ -139,11 +147,24 @@ ldifEntry = [source lookupContactEntry: objectName]; if (ldifEntry) [childRecords setObject: ldifEntry forKey: objectName]; + else if ([self isValidContentName: objectName]) + { + url = [[[lookupContext request] uri] urlWithoutParameters]; + if ([url hasSuffix: @"AsContact"]) + { + ldifEntry = [NSMutableDictionary dictionary]; + isNew = YES; + } + } } if (ldifEntry) - obj = [SOGoContactLDIFEntry contactEntryWithName: objectName - withLDIFEntry: ldifEntry - inContainer: self]; + { + obj = [SOGoContactLDIFEntry contactEntryWithName: objectName + withLDIFEntry: ldifEntry + inContainer: self]; + if (isNew) + [obj setIsNew: YES]; + } else obj = [NSException exceptionWithHTTPStatus: 404]; } @@ -156,6 +177,19 @@ return [source allEntryIDs]; } +- (NSException *) saveLDIFEntry: (SOGoContactLDIFEntry *) ldifEntry +{ + return (([ldifEntry isNew]) + ? [source addContactEntry: [ldifEntry ldifRecord] + withID: [ldifEntry nameInContainer]] + : [source updateContactEntry: [ldifEntry ldifRecord]]); +} + +- (NSException *) deleteLDIFEntry: (SOGoContactLDIFEntry *) ldifEntry +{ + return [source removeContactEntryWithID: [ldifEntry nameInContainer]]; +} + - (NSDictionary *) _flattenedRecord: (NSDictionary *) oldRecord { NSMutableDictionary *newRecord; @@ -324,10 +358,26 @@ return [NSArray arrayWithObject: SoRole_Authenticated]; } -/* TODO: this might change one day when we support LDAP acls */ - (NSArray *) aclsForUser: (NSString *) uid { - return nil; + NSArray *acls, *modifiers; + static NSArray *modifierRoles = nil; + + if (!modifierRoles) + modifierRoles = [[NSArray alloc] initWithObjects: @"Owner", + @"ObjectViewer", + @"ObjectEditor", @"ObjectCreator", + @"ObjectEraser", nil]; + + modifiers = [source modifiers]; + if ([modifiers containsObject: uid]) + acls = [modifierRoles copy]; + else + acls = [NSArray new]; + + [acls autorelease]; + + return acls; } @end diff --git a/SoObjects/SOGo/GNUmakefile b/SoObjects/SOGo/GNUmakefile index 5a3a05a79..c099e3c49 100644 --- a/SoObjects/SOGo/GNUmakefile +++ b/SoObjects/SOGo/GNUmakefile @@ -28,6 +28,7 @@ SOGo_HEADER_FILES = \ \ SOGoUserManager.h \ LDAPSource.h \ + LDAPSourceSchema.h \ SQLSource.h \ SOGoUserProfile.h \ SOGoDateFormatter.h \ @@ -97,6 +98,7 @@ SOGo_OBJC_FILES = \ SOGoStartupLogger.m \ SOGoUserManager.m \ LDAPSource.m \ + LDAPSourceSchema.m \ SQLSource.m \ SOGoUserProfile.m \ SOGoSQLUserProfile.m \ diff --git a/SoObjects/SOGo/LDAPSource.h b/SoObjects/SOGo/LDAPSource.h index 0f7929527..64fd9c02d 100644 --- a/SoObjects/SOGo/LDAPSource.h +++ b/SoObjects/SOGo/LDAPSource.h @@ -30,6 +30,7 @@ #include "SOGoSource.h" #include "SOGoConstants.h" +@class LDAPSourceSchema; @class NSDictionary; @class NSString; @class NGLdapConnection; @@ -52,6 +53,7 @@ NSString *_scope; NSString *baseDN; + LDAPSourceSchema *schema; NSString *IDField; // the first part of a user DN NSString *CNField; NSString *UIDField; @@ -63,6 +65,9 @@ NSString *domain; NSString *contactInfoAttribute; + NSDictionary *contactMapping; + NSArray *contactObjectClasses; + NSDictionary *modulesConstraints; NSMutableArray *searchAttributes; @@ -74,6 +79,9 @@ /* resources handling */ NSString *kindField; NSString *multipleBookingsField; + + /* ACL */ + NSArray *modifiers; } - (void) setBindDN: (NSString *) newBindDN @@ -95,6 +103,11 @@ kindField: (NSString *) newKindField andMultipleBookingsField: (NSString *) newMultipleBookingsField; +/* This enable the convertion of a contact entry with inetOrgPerson and mozillaAbPerson + to and from an LDAP record */ +- (void) setContactMapping: (NSDictionary *) newMapping + andObjectClasses: (NSArray *) newObjectClasses; + - (NGLdapEntry *) lookupGroupEntryByUID: (NSString *) theUID; - (NGLdapEntry *) lookupGroupEntryByEmail: (NSString *) theEmail; - (NGLdapEntry *) lookupGroupEntryByAttribute: (NSString *) theAttribute diff --git a/SoObjects/SOGo/LDAPSource.m b/SoObjects/SOGo/LDAPSource.m index abb45190e..27ee22fe8 100644 --- a/SoObjects/SOGo/LDAPSource.m +++ b/SoObjects/SOGo/LDAPSource.m @@ -35,6 +35,7 @@ #import #import +#import "LDAPSourceSchema.h" #import "NSArray+Utilities.h" #import "NSString+Utilities.h" #import "SOGoDomainDefaults.h" @@ -44,89 +45,76 @@ #import "../../Main/SOGo.h" +static Class NSStringK; + #define SafeLDAPCriteria(x) [[[x stringByReplacingString: @"\\" withString: @"\\\\"] \ stringByReplacingString: @"'" withString: @"\\'"] \ stringByReplacingString: @"%" withString: @"%%"] -static NSArray *commonSearchFields; + +@interface NGLdapAttribute (SOGoLDAP) + +- (id) _asArrayOrString; + +@end + +@implementation NGLdapAttribute (SOGoLDAP) + +- (id) _asArrayOrString +{ + id value; + NSArray *arrayValue; + + arrayValue = [self allStringValues]; + if ([arrayValue count] == 1) + value = [arrayValue objectAtIndex: 0]; + else + value = arrayValue; + + return value; +} + +@end + +@interface NGLdapEntry (SOGoLDAP) + +- (NSMutableDictionary *) _asDictionary; + +@end + +@implementation NGLdapEntry (SOGoLDAP) + +- (NSMutableDictionary *) _asDictionary +{ + NSMutableDictionary *ldapRecord; + NSDictionary *ldapAttributes; + NSArray *keys; + NSString *key; + NSUInteger count, max; + id value; + + ldapAttributes = [self attributes]; + keys = [ldapAttributes allKeys]; + max = [keys count]; + + ldapRecord = [NSMutableDictionary dictionaryWithCapacity: max]; + for (count = 0; count < max; count++) + { + key = [keys objectAtIndex: count]; + value = [[ldapAttributes objectForKey: key] _asArrayOrString]; + if (value) + [ldapRecord setObject: value forKey: [key lowercaseString]]; + } + + return ldapRecord; +} + +@end @implementation LDAPSource + (void) initialize { - if (!commonSearchFields) - { - commonSearchFields = [NSArray arrayWithObjects: - @"title", - @"company", - @"o", - @"displayname", - @"modifytimestamp", - @"mozillahomestate", - @"mozillahomeurl", - @"homeurl", - @"st", - @"region", - @"mozillacustom2", - @"custom2", - @"mozillahomecountryname", - @"description", - @"notes", - @"department", - @"departmentnumber", - @"ou", - @"orgunit", - @"mobile", - @"cellphone", - @"carphone", - @"mozillacustom1", - @"custom1", - @"mozillanickname", - @"xmozillanickname", - @"mozillaworkurl", - @"workurl", - @"fax", - @"facsimiletelephonenumber", - @"telephonenumber", - @"mozillahomestreet", - @"mozillasecondemail", - @"xmozillasecondemail", - @"mozillacustom4", - @"custom4", - @"nsaimid", - @"nscpaimscreenname", - @"street", - @"streetaddress", - @"postofficebox", - @"homephone", - @"cn", - @"commonname", - @"givenname", - @"mozillahomepostalcode", - @"mozillahomelocalityname", - @"mozillaworkstreet2", - @"mozillausehtmlmail", - @"xmozillausehtmlmail", - @"mozillahomestreet2", - @"postalcode", - @"zip", - @"c", - @"countryname", - @"pager", - @"pagerphone", - @"mail", - @"sn", - @"surname", - @"mozillacustom3", - @"custom3", - @"l", - @"locality", - @"birthyear", - @"serialnumber", - @"calfburl", - @"proxyaddresses", - nil]; - [commonSearchFields retain]; - } + NSStringK = [NSString class]; } // @@ -162,11 +150,13 @@ static NSArray *commonSearchFields; domain = nil; baseDN = nil; + schema = nil; IDField = @"cn"; /* the first part of a user DN */ CNField = @"cn"; UIDField = @"uid"; mailFields = [NSArray arrayWithObject: @"mail"]; [mailFields retain]; + contactMapping = nil; searchFields = [NSArray arrayWithObjects: @"sn", @"displayname", @"telephonenumber", nil]; [searchFields retain]; IMAPHostField = nil; @@ -182,6 +172,7 @@ static NSArray *commonSearchFields; multipleBookingsField = nil; _dnCache = [[NSMutableDictionary alloc] init]; + modifiers = nil; } return self; @@ -192,6 +183,7 @@ static NSArray *commonSearchFields; // - (void) dealloc { + [schema release]; [bindDN release]; [password release]; [sourceBindDN release]; @@ -202,6 +194,7 @@ static NSArray *commonSearchFields; [IDField release]; [CNField release]; [UIDField release]; + [contactMapping release]; [mailFields release]; [searchFields release]; [IMAPHostField release]; @@ -216,6 +209,7 @@ static NSArray *commonSearchFields; [_dnCache release]; [kindField release]; [multipleBookingsField release]; + [modifiers release]; [super dealloc]; } @@ -251,6 +245,9 @@ static NSArray *commonSearchFields; kindField: [udSource objectForKey: @"KindFieldName"] andMultipleBookingsField: [udSource objectForKey: @"MultipleBookingsFieldName"]]; + [self setContactMapping: [udSource objectForKey: @"mapping"] + andObjectClasses: [udSource objectForKey: @"objectClasses"]]; + if ([sourceDomain length]) { dd = [SOGoDomainDefaults defaultsForDomain: sourceDomain]; @@ -286,6 +283,8 @@ static NSArray *commonSearchFields; if ([udSource objectForKey: @"passwordPolicy"]) passwordPolicy = [[udSource objectForKey: @"passwordPolicy"] boolValue]; + + ASSIGN (modifiers, [udSource objectForKey: @"modifiers"]); } return self; @@ -345,15 +344,15 @@ andMultipleBookingsField: (NSString *) newMultipleBookingsField { ASSIGN(baseDN, [newBaseDN lowercaseString]); if (newIDField) - ASSIGN(IDField, newIDField); + ASSIGN(IDField, [newIDField lowercaseString]); if (newCNField) - ASSIGN(CNField, newCNField); + ASSIGN(CNField, [newCNField lowercaseString]); if (newUIDField) - ASSIGN(UIDField, newUIDField); + ASSIGN(UIDField, [newUIDField lowercaseString]); if (newIMAPHostField) - ASSIGN(IMAPHostField, newIMAPHostField); + ASSIGN(IMAPHostField, [newIMAPHostField lowercaseString]); if (newIMAPLoginField) - ASSIGN(IMAPLoginField, newIMAPLoginField); + ASSIGN(IMAPLoginField, [newIMAPLoginField lowercaseString]); if (newMailFields) ASSIGN(mailFields, newMailFields); if (newSearchFields) @@ -382,9 +381,16 @@ andMultipleBookingsField: (NSString *) newMultipleBookingsField } } if (newKindField) - ASSIGN(kindField, newKindField); + ASSIGN(kindField, [newKindField lowercaseString]); if (newMultipleBookingsField) - ASSIGN(multipleBookingsField, newMultipleBookingsField); + ASSIGN(multipleBookingsField, [newMultipleBookingsField lowercaseString]); +} + +- (void) setContactMapping: (NSDictionary *) newMapping + andObjectClasses: (NSArray *) newObjectClasses +{ + ASSIGN (contactMapping, newMapping); + ASSIGN (contactObjectClasses, newObjectClasses); } // @@ -438,7 +444,9 @@ andMultipleBookingsField: (NSString *) newMultipleBookingsField } NS_HANDLER { - NSLog(@"Could not bind to the LDAP server %@ (%d) using the bind DN: %@", hostname, port, bindDN); + [self errorWithFormat: @"Could not bind to the LDAP server %@ (%d)" + @" using the bind DN: %@", + hostname, port, bindDN]; ldapConnection = nil; } NS_ENDHANDLER; @@ -738,44 +746,6 @@ andMultipleBookingsField: (NSString *) newMultipleBookingsField return fields; } -- (NSArray *) _searchAttributes -{ - if (!searchAttributes) - { - searchAttributes = [NSMutableArray new]; - [searchAttributes addObject: @"objectClass"]; - if (CNField) - [searchAttributes addObject: CNField]; - if (UIDField) - [searchAttributes addObject: UIDField]; - [searchAttributes addObjectsFromArray: mailFields]; - [searchAttributes addObjectsFromArray: [self _constraintsFields]]; - [searchAttributes addObjectsFromArray: commonSearchFields]; - [searchAttributes addObjectUniquely: IDField]; - - // Add SOGoLDAPContactInfoAttribute from user defaults - if ([contactInfoAttribute length]) - [searchAttributes addObjectUniquely: contactInfoAttribute]; - - // Add IMAP hostname from user defaults - if ([IMAPHostField length]) - [searchAttributes addObjectUniquely: IMAPHostField]; - - // Add IMAP login from user defaults - if ([IMAPLoginField length]) - [searchAttributes addObjectUniquely: IMAPLoginField]; - - // Add the resources handling attributes - if ([kindField length]) - [searchAttributes addObjectUniquely: kindField]; - - if ([multipleBookingsField length]) - [searchAttributes addObjectUniquely: multipleBookingsField]; - } - - return searchAttributes; -} - - (NSArray *) allEntryIDs { NSEnumerator *entries; @@ -814,7 +784,7 @@ andMultipleBookingsField: (NSString *) newMultipleBookingsField } - (void) _fillEmailsOfEntry: (NGLdapEntry *) ldapEntry - intoContactEntry: (NSMutableDictionary *) contactEntry + intoLDIFRecord: (NSMutableDictionary *) ldifRecord { NSEnumerator *emailFields; NSString *currentFieldName, *ldapValue; @@ -829,27 +799,27 @@ andMultipleBookingsField: (NSString *) newMultipleBookingsField allStringValues]; [emails addObjectsFromArray: allValues]; } - [contactEntry setObject: emails forKey: @"c_emails"]; + [ldifRecord setObject: emails forKey: @"c_emails"]; [emails release]; if (IMAPHostField) { ldapValue = [[ldapEntry attributeWithName: IMAPHostField] stringValueAtIndex: 0]; if ([ldapValue length] > 0) - [contactEntry setObject: ldapValue forKey: @"c_imaphostname"]; + [ldifRecord setObject: ldapValue forKey: @"c_imaphostname"]; } if (IMAPLoginField) { ldapValue = [[ldapEntry attributeWithName: IMAPLoginField] stringValueAtIndex: 0]; if ([ldapValue length] > 0) - [contactEntry setObject: ldapValue forKey: @"c_imaplogin"]; + [ldifRecord setObject: ldapValue forKey: @"c_imaplogin"]; } } - (void) _fillConstraints: (NGLdapEntry *) ldapEntry forModule: (NSString *) module - intoContactEntry: (NSMutableDictionary *) contactEntry + intoLDIFRecord: (NSMutableDictionary *) ldifRecord { NSDictionary *constraints; NSEnumerator *matches; @@ -875,22 +845,95 @@ andMultipleBookingsField: (NSString *) newMultipleBookingsField } } - [contactEntry setObject: [NSNumber numberWithBool: result] - forKey: [NSString stringWithFormat: @"%@Access", module]]; + [ldifRecord setObject: [NSNumber numberWithBool: result] + forKey: [NSString stringWithFormat: @"%@Access", module]]; +} + +/* conversion LDAP -> SOGo inetOrgPerson entry */ +- (void) _applyContactMappingToResult: (NSMutableDictionary *) ldifRecord +{ + NSArray *sourceFields; + NSArray *keys; + NSString *key, *field, *value; + NSUInteger count, max, fieldCount, fieldMax; + BOOL filled; + + keys = [contactMapping allKeys]; + max = [keys count]; + for (count = 0; count < max; count++) + { + key = [keys objectAtIndex: count]; + sourceFields = [contactMapping objectForKey: key]; + if ([sourceFields isKindOfClass: NSStringK]) + sourceFields = [NSArray arrayWithObject: sourceFields]; + fieldMax = [sourceFields count]; + filled = NO; + for (fieldCount = 0; + !filled && fieldCount < fieldMax; + fieldCount++) + { + field = [[sourceFields objectAtIndex: fieldCount] lowercaseString]; + value = [ldifRecord objectForKey: field]; + if (value) + { + [ldifRecord setObject: value forKey: [key lowercaseString]]; + filled = YES; + } + } + } +} + +/* conversion SOGo inetOrgPerson entry -> LDAP */ +- (void) _applyContactMappingToOutput: (NSMutableDictionary *) ldifRecord +{ + NSArray *sourceFields; + NSArray *keys; + NSString *key, *lowerKey, *field, *value; + NSUInteger count, max, fieldCount, fieldMax; + + if (contactObjectClasses) + [ldifRecord setObject: contactObjectClasses + forKey: @"objectclass"]; + + keys = [contactMapping allKeys]; + max = [keys count]; + for (count = 0; count < max; count++) + { + key = [keys objectAtIndex: count]; + lowerKey = [key lowercaseString]; + value = [ldifRecord objectForKey: lowerKey]; + if ([value length] > 0) + { + sourceFields = [contactMapping objectForKey: key]; + if ([sourceFields isKindOfClass: NSStringK]) + sourceFields = [NSArray arrayWithObject: sourceFields]; + + fieldMax = [sourceFields count]; + for (fieldCount = 0; fieldCount < fieldMax; fieldCount++) + { + field = [[sourceFields objectAtIndex: fieldCount] + lowercaseString]; + [ldifRecord setObject: value forKey: field]; + } + } + } } - (NSDictionary *) _convertLDAPEntryToContact: (NGLdapEntry *) ldapEntry { - NSMutableDictionary *contactEntry; - NSEnumerator *attributes; - NSString *currentAttribute, *value; + NSMutableDictionary *ldifRecord; + NSString *value; + static NSArray *resourceKinds = nil; NSMutableArray *classes; id o; - contactEntry = [NSMutableDictionary dictionary]; - [contactEntry setObject: [ldapEntry dn] forKey: @"dn"]; - attributes = [[self _searchAttributes] objectEnumerator]; + if (!resourceKinds) + resourceKinds = [[NSArray alloc] initWithObjects: @"location", @"thing", + @"group", nil]; + ldifRecord = [ldapEntry _asDictionary]; + [ldifRecord setObject: [ldapEntry dn] forKey: @"dn"]; + // We get our objectClass attribute values. We lowercase // everything for ease of search after. o = [ldapEntry objectClasses]; @@ -909,9 +952,6 @@ andMultipleBookingsField: (NSString *) newMultipleBookingsField if (classes) { - [contactEntry setObject: classes - forKey: @"objectclasses"]; - // We check if our entry is a group. If so, we set the // 'isGroup' custom attribute. if ([classes containsObject: @"group"] || @@ -919,63 +959,48 @@ andMultipleBookingsField: (NSString *) newMultipleBookingsField [classes containsObject: @"groupofuniquenames"] || [classes containsObject: @"posixgroup"]) { - [contactEntry setObject: [NSNumber numberWithInt: 1] - forKey: @"isGroup"]; + [ldifRecord setObject: [NSNumber numberWithInt: 1] + forKey: @"isGroup"]; } // We check if our entry is a resource. We also support // determining resources based on the KindFieldName attribute // value - see below. else if ([classes containsObject: @"calendarresource"]) { - [contactEntry setObject: [NSNumber numberWithInt: 1] - forKey: @"isResource"]; + [ldifRecord setObject: [NSNumber numberWithInt: 1] + forKey: @"isResource"]; } } - while ((currentAttribute = [attributes nextObject])) + // We check if that entry corresponds to a resource. For this, + // kindField must be defined and it must hold one of those values + // + // location + // thing + // group + // + if ([kindField length] > 0) { - value = [[ldapEntry attributeWithName: currentAttribute] - stringValueAtIndex: 0]; + value = [ldifRecord objectForKey: [kindField lowercaseString]]; + if ([value isKindOfClass: NSStringK] + && [resourceKinds containsObject: value]) + [ldifRecord setObject: [NSNumber numberWithInt: 1] + forKey: @"isResource"]; + } - // It's important here to set our attributes' key in lowercase. - if (value) - { - currentAttribute = [currentAttribute lowercaseString]; - [contactEntry setObject: value forKey: currentAttribute]; - - // We check if that entry corresponds to a resource. For this, - // kindField must be defined and it must hold one of those values - // - // location - // thing - // group - // - if (kindField && - [kindField caseInsensitiveCompare: currentAttribute] == NSOrderedSame) - { - if ([value caseInsensitiveCompare: @"location"] == NSOrderedSame || - [value caseInsensitiveCompare: @"thing"] == NSOrderedSame || - [value caseInsensitiveCompare: @"group"] == NSOrderedSame) - { - [contactEntry setObject: [NSNumber numberWithInt: 1] - forKey: @"isResource"]; - } - } - // We check for the number of simultanous bookings that is allowed. - // A value of 0 means that there's no limit. - if (multipleBookingsField && - [multipleBookingsField caseInsensitiveCompare: currentAttribute] == NSOrderedSame) - { - [contactEntry setObject: [NSNumber numberWithInt: [value intValue]] - forKey: @"numberOfSimultaneousBookings"]; - } - } + // We check for the number of simultanous bookings that is allowed. + // A value of 0 means that there's no limit. + if ([multipleBookingsField length] > 0) + { + value = [ldifRecord objectForKey: [multipleBookingsField lowercaseString]]; + [ldifRecord setObject: [NSNumber numberWithInt: [value intValue]] + forKey: @"numberOfSimultaneousBookings"]; } value = [[ldapEntry attributeWithName: IDField] stringValueAtIndex: 0]; if (!value) value = @""; - [contactEntry setObject: value forKey: @"c_name"]; + [ldifRecord setObject: value forKey: @"c_name"]; value = [[ldapEntry attributeWithName: UIDField] stringValueAtIndex: 0]; if (!value) value = @""; @@ -984,11 +1009,14 @@ andMultipleBookingsField: (NSString *) newMultipleBookingsField // Eventually, we could check at this point if the entry is a group // and prefix the UID with a "@" // } - [contactEntry setObject: value forKey: @"c_uid"]; + [ldifRecord setObject: value forKey: @"c_uid"]; value = [[ldapEntry attributeWithName: CNField] stringValueAtIndex: 0]; if (!value) value = @""; - [contactEntry setObject: value forKey: @"c_cn"]; + [ldifRecord setObject: value forKey: @"c_cn"]; + /* if "displayName" is not set, we use CNField because it must exist */ + if (![ldifRecord objectForKey: @"displayname"]) + [ldifRecord setObject: value forKey: @"displayname"]; if (contactInfoAttribute) { @@ -999,21 +1027,24 @@ andMultipleBookingsField: (NSString *) newMultipleBookingsField } else value = @""; - [contactEntry setObject: value forKey: @"c_info"]; + [ldifRecord setObject: value forKey: @"c_info"]; if (domain) value = domain; else value = @""; - [contactEntry setObject: value forKey: @"c_domain"]; + [ldifRecord setObject: value forKey: @"c_domain"]; - [self _fillEmailsOfEntry: ldapEntry intoContactEntry: contactEntry]; + [self _fillEmailsOfEntry: ldapEntry intoLDIFRecord: ldifRecord]; [self _fillConstraints: ldapEntry forModule: @"Calendar" - intoContactEntry: (NSMutableDictionary *) contactEntry]; + intoLDIFRecord: (NSMutableDictionary *) ldifRecord]; [self _fillConstraints: ldapEntry forModule: @"Mail" - intoContactEntry: (NSMutableDictionary *) contactEntry]; + intoLDIFRecord: (NSMutableDictionary *) ldifRecord]; - return contactEntry; + if (contactMapping) + [self _applyContactMappingToResult: ldifRecord]; + + return ldifRecord; } - (NSArray *) fetchContactsMatching: (NSString *) match @@ -1031,7 +1062,8 @@ andMultipleBookingsField: (NSString *) newMultipleBookingsField { ldapConnection = [self _ldapConnection]; qualifier = [self _qualifierForFilter: match]; - attributes = [self _searchAttributes]; + // attributes = [self _searchAttributes]; + attributes = [NSArray arrayWithObject: @"*"]; if ([_scope caseInsensitiveCompare: @"BASE"] == NSOrderedSame) entries = [ldapConnection baseSearchAtBaseDN: baseDN @@ -1053,83 +1085,76 @@ andMultipleBookingsField: (NSString *) newMultipleBookingsField return contacts; } +- (NGLdapEntry *) _lookupLDAPEntry: (EOQualifier *) qualifier +{ + NGLdapConnection *ldapConnection; + NSArray *attributes; + NSEnumerator *entries; + + // attributes = [self _searchAttributes]; + ldapConnection = [self _ldapConnection]; + if (!schema) + { + schema = [LDAPSourceSchema new]; + [schema readSchemaFromConnection: ldapConnection]; + } + attributes = [NSArray arrayWithObject: @"*"]; + + if ([_scope caseInsensitiveCompare: @"BASE"] == NSOrderedSame) + entries = [ldapConnection baseSearchAtBaseDN: baseDN + qualifier: qualifier + attributes: attributes]; + else if ([_scope caseInsensitiveCompare: @"ONE"] == NSOrderedSame) + entries = [ldapConnection flatSearchAtBaseDN: baseDN + qualifier: qualifier + attributes: attributes]; + else + entries = [ldapConnection deepSearchAtBaseDN: baseDN + qualifier: qualifier + attributes: attributes]; + + return [entries nextObject]; +} + - (NSDictionary *) lookupContactEntry: (NSString *) theID { NGLdapEntry *ldapEntry; - NGLdapConnection *ldapConnection; - NSEnumerator *entries; EOQualifier *qualifier; - NSArray *attributes; NSString *s; - NSDictionary *contactEntry; + NSDictionary *ldifRecord; - contactEntry = nil; + ldifRecord = nil; if ([theID length] > 0) { - ldapConnection = [self _ldapConnection]; s = [NSString stringWithFormat: @"(%@='%@')", IDField, SafeLDAPCriteria(theID)]; qualifier = [EOQualifier qualifierWithQualifierFormat: s]; - attributes = [self _searchAttributes]; - - if ([_scope caseInsensitiveCompare: @"BASE"] == NSOrderedSame) - entries = [ldapConnection baseSearchAtBaseDN: baseDN - qualifier: qualifier - attributes: attributes]; - else if ([_scope caseInsensitiveCompare: @"ONE"] == NSOrderedSame) - entries = [ldapConnection flatSearchAtBaseDN: baseDN - qualifier: qualifier - attributes: attributes]; - else - entries = [ldapConnection deepSearchAtBaseDN: baseDN - qualifier: qualifier - attributes: attributes]; - - ldapEntry = [entries nextObject]; + ldapEntry = [self _lookupLDAPEntry: qualifier]; if (ldapEntry) - contactEntry = [self _convertLDAPEntryToContact: ldapEntry]; + ldifRecord = [self _convertLDAPEntryToContact: ldapEntry]; } - return contactEntry; + return ldifRecord; } - (NSDictionary *) lookupContactEntryWithUIDorEmail: (NSString *) uid { - NGLdapConnection *ldapConnection; NGLdapEntry *ldapEntry; - NSEnumerator *entries; EOQualifier *qualifier; - NSArray *attributes; - NSDictionary *contactEntry; + NSDictionary *ldifRecord; - contactEntry = nil; + ldifRecord = nil; if ([uid length] > 0) { - ldapConnection = [self _ldapConnection]; qualifier = [self _qualifierForUIDFilter: uid]; - attributes = [self _searchAttributes]; - - if ([_scope caseInsensitiveCompare: @"BASE"] == NSOrderedSame) - entries = [ldapConnection baseSearchAtBaseDN: baseDN - qualifier: qualifier - attributes: attributes]; - else if ([_scope caseInsensitiveCompare: @"ONE"] == NSOrderedSame) - entries = [ldapConnection flatSearchAtBaseDN: baseDN - qualifier: qualifier - attributes: attributes]; - else - entries = [ldapConnection deepSearchAtBaseDN: baseDN - qualifier: qualifier - attributes: attributes]; - - ldapEntry = [entries nextObject]; + ldapEntry = [self _lookupLDAPEntry: qualifier]; if (ldapEntry) - contactEntry = [self _convertLDAPEntryToContact: ldapEntry]; + ldifRecord = [self _convertLDAPEntryToContact: ldapEntry]; } - return contactEntry; + return ldifRecord; } - (NSString *) lookupLoginByDN: (NSString *) theDN @@ -1171,43 +1196,24 @@ andMultipleBookingsField: (NSString *) newMultipleBookingsField - (NGLdapEntry *) lookupGroupEntryByAttribute: (NSString *) theAttribute andValue: (NSString *) theValue { - NSMutableArray *attributes; - NSEnumerator *entries; EOQualifier *qualifier; NSString *s; - NGLdapConnection *ldapConnection; NGLdapEntry *ldapEntry; if ([theValue length] > 0) { - ldapConnection = [self _ldapConnection]; - s = [NSString stringWithFormat: @"(%@='%@')", theAttribute, SafeLDAPCriteria(theValue)]; qualifier = [EOQualifier qualifierWithQualifierFormat: s]; // We look for additional attributes - the ones related to group // membership - attributes = [NSMutableArray arrayWithArray: [self _searchAttributes]]; - [attributes addObject: @"member"]; - [attributes addObject: @"uniqueMember"]; - [attributes addObject: @"memberUid"]; - [attributes addObject: @"memberOf"]; - - if ([_scope caseInsensitiveCompare: @"BASE"] == NSOrderedSame) - entries = [ldapConnection baseSearchAtBaseDN: baseDN - qualifier: qualifier - attributes: attributes]; - else if ([_scope caseInsensitiveCompare: @"ONE"] == NSOrderedSame) - entries = [ldapConnection flatSearchAtBaseDN: baseDN - qualifier: qualifier - attributes: attributes]; - else - entries = [ldapConnection deepSearchAtBaseDN: baseDN - qualifier: qualifier - attributes: attributes]; - - ldapEntry = [entries nextObject]; + // attributes = [NSMutableArray arrayWithArray: [self _searchAttributes]]; + // [attributes addObject: @"member"]; + // [attributes addObject: @"uniqueMember"]; + // [attributes addObject: @"memberUid"]; + // [attributes addObject: @"memberOf"]; + ldapEntry = [self _lookupLDAPEntry: qualifier]; } else ldapEntry = nil; @@ -1225,4 +1231,247 @@ andMultipleBookingsField: (NSString *) newMultipleBookingsField return baseDN; } +- (NSArray *) modifiers +{ + return modifiers; +} + +static NSArray * +_convertRecordToLDAPAttributes (LDAPSourceSchema *schema, NSDictionary *ldifRecord) +{ + /* convert resulting record to NGLdapEntry: + - ignore fields with empty values + - ignore extra fields + - use correct case for LDAP attribute matching classes */ + NSMutableArray *attributes; + NGLdapAttribute *attribute; + NSArray *classes, *fields, *values; + NSString *field, *lowerField, *value; + NSUInteger count, max, valueCount, valueMax; + + attributes = [NSMutableArray new]; + + classes = [ldifRecord objectForKey: @"objectclass"]; + if ([classes isKindOfClass: NSStringK]) + classes = [NSArray arrayWithObject: classes]; + fields = [schema fieldsForClasses: classes]; + + max = [fields count]; + for (count = 0; count < max; count++) + { + attribute = nil; + field = [fields objectAtIndex: count]; + lowerField = [field lowercaseString]; + if (![lowerField isEqualToString: @"dn"]) + { + values = [ldifRecord objectForKey: lowerField]; + if ([values isKindOfClass: NSStringK]) + values = [NSArray arrayWithObject: values]; + valueMax = [values count]; + for (valueCount = 0; valueCount < valueMax; valueCount++) + { + value = [values objectAtIndex: valueCount]; + if ([value length] > 0) + { + if (!attribute) + { + attribute = [[NGLdapAttribute alloc] + initWithAttributeName: field]; + [attributes addObject: attribute]; + [attribute release]; + } + [attribute addStringValue: value]; + } + } + } + } + + return attributes; +} + +- (NSException *) addContactEntry: (NSDictionary *) roLdifRecord + withID: (NSString *) aId +{ + NSException *result = nil; + NGLdapEntry *newEntry; + NSMutableDictionary *ldifRecord; + NSArray *attributes; + NSString *dn, *cnValue; + NGLdapConnection *ldapConnection; + + if ([aId length] > 0) + { + ldapConnection = [self _ldapConnection]; + if (!schema) + { + schema = [LDAPSourceSchema new]; + [schema readSchemaFromConnection: ldapConnection]; + } + + ldifRecord = [roLdifRecord mutableCopy]; + [ldifRecord autorelease]; + [ldifRecord setObject: aId forKey: UIDField]; + + /* if CN is not set, we use aId because it must exist */ + if (![ldifRecord objectForKey: CNField]) + { + cnValue = [ldifRecord objectForKey: @"displayname"]; + if ([cnValue length] == 0) + cnValue = aId; + [ldifRecord setObject: aId forKey: @"cn"]; + } + + [self _applyContactMappingToOutput: ldifRecord]; + + /* since the id might have changed due to the mapping above, we + reload the record ID */ + aId = [ldifRecord objectForKey: UIDField]; + dn = [NSString stringWithFormat: @"%@=%@,%@", IDField, aId, baseDN]; + attributes = _convertRecordToLDAPAttributes (schema, ldifRecord); + + newEntry = [[NGLdapEntry alloc] initWithDN: dn + attributes: attributes]; + [newEntry autorelease]; + [attributes release]; + NS_DURING + { + [ldapConnection addEntry: newEntry]; + } + NS_HANDLER + { + result = localException; + } + NS_ENDHANDLER; + } + else + [self errorWithFormat: @"no value for id field '%@'", IDField]; + + return result; +} + +static NSArray * +_makeLDAPChanges (NGLdapConnection *ldapConnection, + NSString *dn, NSArray *attributes) +{ + NSMutableArray *changes, *attributeNames, *origAttributeNames; + NGLdapEntry *origEntry; + NSArray *values; + NGLdapAttribute *attribute, *origAttribute; + NSString *name; + NSDictionary *origAttributes; + NSUInteger count, max, valueCount, valueMax; + BOOL allStrings; + + /* additions and modifications */ + origEntry = [ldapConnection entryAtDN: dn + attributes: [NSArray arrayWithObject: @"*"]]; + origAttributes = [origEntry attributes]; + + max = [attributes count]; + changes = [NSMutableArray arrayWithCapacity: max]; + attributeNames = [NSMutableArray arrayWithCapacity: max]; + for (count = 0; count < max; count++) + { + attribute = [attributes objectAtIndex: count]; + name = [attribute attributeName]; + [attributeNames addObject: name]; + origAttribute = [origAttributes objectForKey: name]; + if (origAttribute) + { + if (![origAttribute isEqual: attribute]) + [changes + addObject: [NGLdapModification replaceModification: attribute]]; + } + else + [changes addObject: [NGLdapModification addModification: attribute]]; + } + + /* deletions */ + origAttributeNames = [[origAttributes allKeys] mutableCopy]; + [origAttributeNames autorelease]; + [origAttributeNames removeObjectsInArray: attributeNames]; + max = [origAttributeNames count]; + for (count = 0; count < max; count++) + { + name = [origAttributeNames objectAtIndex: count]; + origAttribute = [origAttributes objectForKey: name]; + /* the attribute must only have string values, otherwise it will anyway + be missing from the new record */ + allStrings = YES; + values = [origAttribute allValues]; + valueMax = [values count]; + for (valueCount = 0; allStrings && valueCount < valueMax; valueCount++) + if (![[values objectAtIndex: valueCount] isKindOfClass: NSStringK]) + allStrings = NO; + if (allStrings) + [changes + addObject: [NGLdapModification deleteModification: origAttribute]]; + } + + return changes; +} + +- (NSException *) updateContactEntry: (NSDictionary *) roLdifRecord +{ + NSException *result = nil; + NSString *dn; + NSMutableDictionary *ldifRecord; + NSArray *attributes, *changes; + NGLdapConnection *ldapConnection; + + dn = [roLdifRecord objectForKey: @"dn"]; + if ([dn length] > 0) + { + ldapConnection = [self _ldapConnection]; + if (!schema) + { + schema = [LDAPSourceSchema new]; + [schema readSchemaFromConnection: ldapConnection]; + } + + ldifRecord = [roLdifRecord mutableCopy]; + [ldifRecord autorelease]; + [self _applyContactMappingToOutput: ldifRecord]; + attributes = _convertRecordToLDAPAttributes (schema, ldifRecord); + + changes = _makeLDAPChanges (ldapConnection, dn, attributes); + + NS_DURING + { + [ldapConnection modifyEntryWithDN: dn + changes: changes]; + } + NS_HANDLER + { + result = localException; + } + NS_ENDHANDLER; + } + else + [self errorWithFormat: @"expected dn for modified record"]; + + return result; +} + +- (NSException *) removeContactEntryWithID: (NSString *) aId +{ + NSException *result = nil; + NGLdapConnection *ldapConnection; + NSString *dn; + + ldapConnection = [self _ldapConnection]; + dn = [NSString stringWithFormat: @"%@=%@,%@", IDField, aId, baseDN]; + NS_DURING + { + [ldapConnection removeEntryWithDN: dn]; + } + NS_HANDLER + { + result = localException; + } + NS_ENDHANDLER; + + return result; +} + @end diff --git a/SoObjects/SOGo/NSDictionary+Utilities.h b/SoObjects/SOGo/NSDictionary+Utilities.h index c8e08ea7c..92fe50f34 100644 --- a/SoObjects/SOGo/NSDictionary+Utilities.h +++ b/SoObjects/SOGo/NSDictionary+Utilities.h @@ -35,8 +35,7 @@ - (NSString *) jsonRepresentation; - (NSString *) keysWithFormat: (NSString *) keyFormat; -// LDIF methods -- (NSString *) userRecordAsLDIFEntry; +- (NSComparisonResult) caseInsensitiveDisplayNameCompare: (NSDictionary *) theDictionary; @end diff --git a/SoObjects/SOGo/NSDictionary+Utilities.m b/SoObjects/SOGo/NSDictionary+Utilities.m index c38dce316..79e0c6c6d 100644 --- a/SoObjects/SOGo/NSDictionary+Utilities.m +++ b/SoObjects/SOGo/NSDictionary+Utilities.m @@ -26,12 +26,8 @@ #import #import #import -#import - -#import #import "NSArray+Utilities.h" -#import "NSObject+Utilities.h" #import "NSString+Utilities.h" #import "NSDictionary+Utilities.h" @@ -109,84 +105,6 @@ return [[self objectForKey: @"cn"] caseInsensitiveCompare: [theDictionary objectForKey: @"cn"]]; } -// LDIF Methods -#warning We might want to support more than just strings here -- (void) _appendLDIFKey: (NSString *) key - value: (NSString *) value - toString: (NSMutableString *) ldifString -{ - if ([value isKindOfClass: [NSString class]]) - { - if ([value _isLDIFSafe]) - [ldifString appendFormat: @"%@: %@\n", key, value]; - else - [ldifString appendFormat: @"%@:: %@\n", - key, [value stringByEncodingBase64]]; - } -} - -- (void) _appendLDIFKey: (NSString *) key - toString: (NSMutableString *) ldifString -{ - id value; - int count, max; - - value = [self objectForKey: key]; - if ([value isKindOfClass: [NSArray class]]) - { - max = [value count]; - for (count = 0; count < max; count++) - [self _appendLDIFKey: key value: [value objectAtIndex: count] - toString: ldifString]; - } - else - [self _appendLDIFKey: key value: [self objectForKey: key] - toString: ldifString]; -} - -- (void) _appendObjectClassesToString: (NSMutableString *) ldifString -{ - NSEnumerator *classes; - NSString *currentClass; - - classes = [[self objectForKey: @"objectClasses"] objectEnumerator]; - while ((currentClass = [classes nextObject])) - [self _appendLDIFKey: @"objectClass" value: currentClass - toString: ldifString]; -} - -- (NSString *) userRecordAsLDIFEntry -{ - NSMutableString *ldifString; - NSEnumerator *keys; - NSString *currentKey; - -// {CalendarAccess = YES; MailAccess = YES; c_cn = "Wolfgang Sourdeau"; c_emails = ("wolfgang@test.com"); c_name = "wolfgang@test.com"; c_uid = "wolfgang@test.com"; cn = "wolfgang@test.com"; displayName = "Wolfgang Sourdeau"; dn = "cn=wolfgang@test.com,ou=evariste,o=inverse.ca"; givenName = Wolfgang; mail = "wolfgang@test.com"; objectClass = organizationalPerson; sn = Sourdeau; } - - ldifString = [NSMutableString string]; - [self _appendLDIFKey: @"dn" toString: ldifString]; - [self _appendObjectClassesToString: ldifString]; - - keys = [[self allKeys] objectEnumerator]; - while ((currentKey = [keys nextObject])) - { - if (!([currentKey isEqualToString: @"CalendarAccess"] - || [currentKey isEqualToString: @"MailAccess"] - || [currentKey isEqualToString: @"ContactAccess"] - || [currentKey hasPrefix: @"objectClass"] - || [currentKey hasPrefix: @"c_"] - || [currentKey isEqualToString: @"dn"] - || [currentKey isEqualToString: @"isGroup"] - || [currentKey isEqualToString: @"isResource"] - || [currentKey isEqualToString: @"numberOfSimultaneousBookings"] - || [currentKey isEqualToString: @"canAuthenticate"])) - [self _appendLDIFKey: currentKey toString: ldifString]; - } - - return ldifString; -} - - @end @implementation NSMutableDictionary (SOGoDictionaryUtilities) diff --git a/SoObjects/SOGo/NSString+Utilities.h b/SoObjects/SOGo/NSString+Utilities.h index 78af47b7b..53bf190b9 100644 --- a/SoObjects/SOGo/NSString+Utilities.h +++ b/SoObjects/SOGo/NSString+Utilities.h @@ -62,8 +62,6 @@ - (int) timeValue; -- (BOOL) _isLDIFSafe; - - (BOOL) isJSONString; - (id) objectFromJSONString; diff --git a/SoObjects/SOGo/NSString+Utilities.m b/SoObjects/SOGo/NSString+Utilities.m index 094dd595d..c9079c4bd 100644 --- a/SoObjects/SOGo/NSString+Utilities.m +++ b/SoObjects/SOGo/NSString+Utilities.m @@ -486,44 +486,6 @@ static int cssEscapingCount; return time; } -static NSMutableCharacterSet *safeLDIFChars = nil; -static NSMutableCharacterSet *safeLDIFStartChars = nil; - -- (void) _initSafeLDIFChars -{ - safeLDIFChars = [NSMutableCharacterSet new]; - [safeLDIFChars addCharactersInRange: NSMakeRange (0x01, 9)]; - [safeLDIFChars addCharactersInRange: NSMakeRange (0x0b, 2)]; - [safeLDIFChars addCharactersInRange: NSMakeRange (0x0e, 114)]; - - safeLDIFStartChars = [safeLDIFChars mutableCopy]; - [safeLDIFStartChars removeCharactersInString: @" :<"]; -} - -- (BOOL) _isLDIFSafe -{ - int count, max; - BOOL rc; - - if (!safeLDIFChars) - [self _initSafeLDIFChars]; - - rc = YES; - - max = [self length]; - if (max > 0) - { - if ([safeLDIFStartChars characterIsMember: [self characterAtIndex: 0]]) - for (count = 1; rc && count < max; count++) - rc = [safeLDIFChars - characterIsMember: [self characterAtIndex: count]]; - else - rc = NO; - } - - return rc; -} - - (BOOL) isJSONString { NSDictionary *jsonData; diff --git a/SoObjects/SOGo/SOGoSource.h b/SoObjects/SOGo/SOGoSource.h index 7fd04fd46..3c8f90380 100644 --- a/SoObjects/SOGo/SOGoSource.h +++ b/SoObjects/SOGo/SOGoSource.h @@ -28,9 +28,10 @@ #import "SOGoConstants.h" @class NSDictionary; +@class NSException; @class NSString; -@protocol SOGoSource +@protocol SOGoSource + (id) sourceFromUDSource: (NSDictionary *) udSource inDomain: (NSString *) domain; @@ -57,6 +58,12 @@ - (NSArray *) allEntryIDs; - (NSArray *) fetchContactsMatching: (NSString *) filter; - (NSString *) sourceID; +- (NSArray *) modifiers; + +- (NSException *) addContactEntry: (NSDictionary *) roLdifRecord + withID: (NSString *) aId; +- (NSException *) updateContactEntry: (NSDictionary *) ldifRecord; +- (NSException *) removeContactEntryWithID: (NSString *) aId; @end diff --git a/SoObjects/SOGo/SQLSource.m b/SoObjects/SOGo/SQLSource.m index 84777c8ab..6950e2d7e 100644 --- a/SoObjects/SOGo/SQLSource.m +++ b/SoObjects/SOGo/SQLSource.m @@ -22,8 +22,9 @@ */ #import -#import #import +#import +#import #import #import #import @@ -379,6 +380,7 @@ { NSMutableDictionary *response; NSMutableArray *qualifiers; + NSArray *fieldNames; EOAdaptorChannel *channel; EOQualifier *loginQualifier, *qualifier; GCSChannelManager *cm; @@ -463,6 +465,16 @@ [response autorelease]; [channel cancelFetch]; + /* Convert all c_ fields to obtain their ldif equivalent */ + fieldNames = [response allKeys]; + for (i = 0; i < [fieldNames count]; i++) + { + field = [fieldNames objectAtIndex: i]; + if ([field hasPrefix: @"c_"]) + [response setObject: [response objectForKey: field] + forKey: [field substringFromIndex: 2]]; + } + // We have to do this here since we do not manage modules // constraints right now over a SQL backend. [response setObject: [NSNumber numberWithBool: YES] forKey: @"CalendarAccess"]; @@ -684,4 +696,49 @@ return _sourceID; } +- (NSArray *) modifiers +{ + return nil; +} + +- (NSException *) addContactEntry: (NSDictionary *) roLdifRecord + withID: (NSString *) aId +{ + NSString *reason; + + reason = [NSString stringWithFormat: @"method '%@' is not available" + @" for class '%@'", NSStringFromSelector (_cmd), + NSStringFromClass (isa)]; + + return [NSException exceptionWithName: @"SQLSourceIOException" + reason: reason + userInfo: nil]; +} + +- (NSException *) updateContactEntry: (NSDictionary *) roLdifRecord +{ + NSString *reason; + + reason = [NSString stringWithFormat: @"method '%@' is not available" + @" for class '%@'", NSStringFromSelector (_cmd), + NSStringFromClass (isa)]; + + return [NSException exceptionWithName: @"SQLSourceIOException" + reason: reason + userInfo: nil]; +} + +- (NSException *) removeContactEntryWithID: (NSString *) aId +{ + NSString *reason; + + reason = [NSString stringWithFormat: @"method '%@' is not available" + @" for class '%@'", NSStringFromSelector (_cmd), + NSStringFromClass (isa)]; + + return [NSException exceptionWithName: @"SQLSourceIOException" + reason: reason + userInfo: nil]; +} + @end diff --git a/Tools/SOGoSockDOperation.m b/Tools/SOGoSockDOperation.m index 6e7057a34..62d4d8226 100644 --- a/Tools/SOGoSockDOperation.m +++ b/Tools/SOGoSockDOperation.m @@ -29,6 +29,7 @@ #import #import #import +#import #import #import #import @@ -120,13 +121,13 @@ Class SOGoContactSourceFolderKlass = Nil; value = [entry objectForKey: key]; if ([value isKindOfClass: [NSString class]] && [value length] > 0) { - if ([value _isLDIFSafe]) - [result appendFormat: @"%@: %@\n", - [key substringFromIndex: 2], value]; - else + if ([value mustEncodeLDIFValue]) [result appendFormat: @"%@:: %@\n", [key substringFromIndex: 2], [value stringByEncodingBase64]]; + else + [result appendFormat: @"%@: %@\n", + [key substringFromIndex: 2], value]; } } [result appendString: @"\n"]; diff --git a/Tools/SOGoToolBackup.m b/Tools/SOGoToolBackup.m index 5b2c6588e..8dd7ea04a 100644 --- a/Tools/SOGoToolBackup.m +++ b/Tools/SOGoToolBackup.m @@ -37,11 +37,12 @@ #import #import #import -#import +#import #import #import #import #import +#import #import "SOGoTool.h" @@ -61,6 +62,12 @@ @implementation SOGoToolBackup ++ (void) initialize +{ + [[SOGoProductLoader productLoader] + loadProducts: [NSArray arrayWithObject: @"Contacts.SOGo"]]; +} + + (NSString *) command { return @"backup"; @@ -376,7 +383,7 @@ userEntry = [currentSource lookupContactEntry: uid]; if (userEntry) { - [userRecord setObject: [userEntry userRecordAsLDIFEntry] + [userRecord setObject: [userEntry ldifRecordAsString] forKey: @"ldif_record"]; done = YES; } diff --git a/UI/Contacts/UIxContactActions.m b/UI/Contacts/UIxContactActions.m index 03f5f6a07..aa4cc7138 100644 --- a/UI/Contacts/UIxContactActions.m +++ b/UI/Contacts/UIxContactActions.m @@ -52,6 +52,8 @@ categories = [[self categories] mutableCopy]; [categories autorelease]; + if (!categories) + categories = [NSMutableArray array]; if (set) { if (![categories containsObject: category]) diff --git a/UI/Contacts/UIxContactEditor.h b/UI/Contacts/UIxContactEditor.h index f0998f2f2..e01c07767 100644 --- a/UI/Contacts/UIxContactEditor.h +++ b/UI/Contacts/UIxContactEditor.h @@ -27,22 +27,18 @@ @class NSString; @class NSMutableDictionary; -@class NGVCard; - @class SOGoContactFolder; @interface UIxContactEditor : UIxComponent { id addressBookItem; - NSString *preferredEmail; NSString *item; - NGVCard *card; - NSMutableArray *photosURL; - NSMutableDictionary *snapshot; /* contains the values for editing */ + NSMutableDictionary *ldifRecord; /* contains the values for editing */ SOGoContactFolder *componentAddressBook; - NSArray *contactCategories; } +- (NSMutableDictionary *) ldifRecord; + - (void) setAddressBookItem: (id) _item; - (id) addressBookItem; diff --git a/UI/Contacts/UIxContactEditor.m b/UI/Contacts/UIxContactEditor.m index dfbc48644..73a88d448 100644 --- a/UI/Contacts/UIxContactEditor.m +++ b/UI/Contacts/UIxContactEditor.m @@ -34,8 +34,6 @@ #import #import -#import -#import #import #import @@ -50,20 +48,23 @@ #import "UIxContactEditor.h" +static Class SOGoContactGCSEntryK = Nil; + @implementation UIxContactEditor ++ (void) initialize +{ + SOGoContactGCSEntryK = [SOGoContactGCSEntry class]; +} + - (id) init { if ((self = [super init])) { - snapshot = [[NSMutableDictionary alloc] initWithCapacity: 16]; - preferredEmail = nil; - photosURL = nil; + ldifRecord = nil; addressBookItem = nil; item = nil; - card = nil; componentAddressBook = nil; - contactCategories = nil; } return self; @@ -71,18 +72,35 @@ - (void) dealloc { - [snapshot release]; - [preferredEmail release]; - [photosURL release]; + [ldifRecord release]; [addressBookItem release]; [item release]; [componentAddressBook release]; - [contactCategories release]; [super dealloc]; } /* accessors */ +- (NSMutableDictionary *) ldifRecord +{ + NSDictionary *clientLDIFRecord; + NSString *queryValue; + + if (!ldifRecord) + { + clientLDIFRecord = [[self clientObject] ldifRecord]; + ldifRecord = [clientLDIFRecord mutableCopy]; + queryValue = [self queryParameterForKey: @"contactEmail"]; + if ([queryValue length] > 0) + [ldifRecord setObject: queryValue forKey: @"mail"]; + queryValue = [self queryParameterForKey: @"contactFN"]; + if ([queryValue length] > 0) + [ldifRecord setObject: queryValue forKey: @"displayname"]; + } + + return ldifRecord; +} + - (void) setAddressBookItem: (id) _item { ASSIGN (addressBookItem, _item); @@ -130,27 +148,6 @@ /* load/store content format */ -// - (void) _fixupSnapshot -// { -// NSString *currentKey, *currentString; -// NSMutableString *newString; -// NSArray *keys; -// unsigned int count, max; - -// keys = [snapshot allKeys]; -// max = [keys count]; -// for (count = 0; count < max; count++) -// { -// currentKey = [keys objectAtIndex: count]; -// currentString = [snapshot objectForKey: currentKey]; -// newString = [currentString mutableCopy]; -// [newString autorelease]; -// [newString replaceString: @";" withString: @"\\;"]; -// if (![newString isEqualToString: currentString]) -// [snapshot setObject: newString forKey: currentKey]; -// } -// } - /* helper */ - (NSString *) _completeURIForMethod: (NSString *) _method @@ -179,12 +176,7 @@ - (BOOL) isNew { - id co; - - co = [self clientObject]; - - return ([co isKindOfClass: [SOGoContentObject class]] - && [co isNew]); + return ([[self clientObject] isNew]); } - (NSArray *) addressBooksList @@ -205,14 +197,13 @@ while (currentFolder) { if ([currentFolder isEqual: folder] || - ([currentFolder isKindOfClass: [SOGoContactGCSFolder class]] && - ![sm validatePermission: SoPerm_AddDocumentsImagesAndFiles - onObject: currentFolder - inContext: context])) + ![sm validatePermission: SoPerm_AddDocumentsImagesAndFiles + onObject: currentFolder + inContext: context]) [addressBooksList addObject: currentFolder]; currentFolder = [folders nextObject]; } - + return addressBooksList; } @@ -245,26 +236,30 @@ return fDisplayName; } -- (void) setContactCategories: (NSString *) jsonCategories +- (BOOL) supportCategories +{ + return [[self clientObject] isKindOfClass: SOGoContactGCSEntryK]; +} + +- (void) setJsonContactCategories: (NSString *) jsonCategories { NSArray *newCategories; newCategories = [jsonCategories objectFromJSONString]; if ([newCategories isKindOfClass: [NSArray class]]) - ASSIGN (contactCategories, newCategories); + [[self ldifRecord] setObject: newCategories + forKey: @"vcardcategories"]; + else + [[self ldifRecord] removeObjectForKey: @"vcardcategories"]; } -- (NSString *) contactCategories +- (NSString *) jsonContactCategories { - NSString *jsonCats; + NSArray *categories; - if (!contactCategories) - ASSIGN (contactCategories, [card categories]); - jsonCats = [contactCategories jsonRepresentation]; - if (!jsonCats) - jsonCats = @"[]"; + categories = [[self ldifRecord] objectForKey: @"vcardcategories"]; - return jsonCats; + return [categories jsonRepresentation]; } - (NSArray *) _languageContactsCategories @@ -279,11 +274,11 @@ return [categoryLabels trimmedComponents]; } -- (NSArray *) _fetchAndCombineCategoriesList: (NSArray *) contactCats +- (NSArray *) _fetchAndCombineCategoriesList { NSString *ownerLogin; SOGoUserDefaults *ud; - NSArray *cats, *newCats; + NSArray *cats, *newCats, *contactCategories; ownerLogin = [[self clientObject] ownerInContext: context]; ud = [[SOGoUser userWithLogin: ownerLogin] userDefaults]; @@ -291,9 +286,10 @@ if (!cats) cats = [self _languageContactsCategories]; - if (contactCats) + contactCategories = [[self ldifRecord] objectForKey: @"vcardcategories"]; + if (contactCategories) { - newCats = [cats mergedArrayWithArray: contactCats]; + newCats = [cats mergedArrayWithArray: contactCategories]; if ([newCats count] != [cats count]) { cats = [newCats sortedArrayUsingSelector: @@ -311,7 +307,7 @@ NSArray *cats; NSString *list; - cats = [self _fetchAndCombineCategoriesList: [card categories]]; + cats = [self _fetchAndCombineCategoriesList]; list = [cats jsonRepresentation]; if (!list) list = @"[]"; @@ -328,311 +324,7 @@ actionName = [[request requestHandlerPath] lastPathComponent]; - return ([[self clientObject] isKindOfClass: [SOGoContactGCSEntry class]] - && [actionName hasPrefix: @"save"]); -} - -- (void) _setSnapshotValue: (NSString *) key - to: (NSString *) aValue -{ - if (!aValue) - aValue = @""; - - [snapshot setObject: aValue forKey: key]; -} - -- (NSMutableDictionary *) snapshot -{ - return snapshot; -} - -- (NSString *) _simpleValueForType: (NSString *) aType - inArray: (NSArray *) anArray - excluding: (NSString *) aTypeToExclude -{ - NSArray *elements; - NSString *value; - - elements = [anArray cardElementsWithAttribute: @"type" - havingValue: aType]; - - value = nil; - - if ([elements count] > 0) - { - CardElement *ce; - int i; - - for (i = 0; i < [elements count]; i++) - { - ce = [elements objectAtIndex: i]; - value = [ce flattenedValuesForKey: @""]; - - if (!aTypeToExclude) - break; - - if (![ce hasAttribute: @"type" havingValue: aTypeToExclude]) - break; - - value = nil; - } - } - - return value; -} - -- (void) _setupEmailFields -{ - NSArray *elements; - NSString *workMail, *homeMail, *prefMail, *potential; - unsigned int max; - - elements = [card childrenWithTag: @"email"]; - max = [elements count]; - workMail = [self _simpleValueForType: @"work" - inArray: elements excluding: nil]; - homeMail = [self _simpleValueForType: @"home" - inArray: elements excluding: nil]; - prefMail = [self _simpleValueForType: @"pref" - inArray: elements excluding: nil]; - - if (max > 0) - { - potential = [[elements objectAtIndex: 0] flattenedValuesForKey: @""]; - if (!workMail) - { - if (homeMail && homeMail == potential) - { - if (max > 1) - workMail = [[elements objectAtIndex: 1] flattenedValuesForKey: @""]; - } - else - workMail = potential; - } - if (!homeMail && max > 1) - { - if (workMail && workMail == potential) - homeMail = [[elements objectAtIndex: 1] flattenedValuesForKey: @""]; - else - homeMail = potential; - } - - if (prefMail) - { - if (prefMail == workMail) - preferredEmail = @"work"; - else if (prefMail == homeMail) - preferredEmail = @"home"; - } - } - - [self _setSnapshotValue: @"workMail" to: workMail]; - [self _setSnapshotValue: @"homeMail" to: homeMail]; - - [self _setSnapshotValue: @"mozillaUseHtmlMail" - to: [[card uniqueChildWithTag: @"x-mozilla-html"] flattenedValuesForKey: @""]]; -} - -- (void) _setupOrgFields -{ - NSMutableArray *orgServices; - CardElement *org; - NSString *service; - NSUInteger count, max; - - org = [card org]; - [self _setSnapshotValue: @"workCompany" - to: [org flattenedValueAtIndex: 0 forKey: @""]]; - max = [[org valuesForKey: @""] count]; - if (max > 1) - { - orgServices = [NSMutableArray arrayWithCapacity: max]; - for (count = 1; count < max; count++) - { - service = [org flattenedValueAtIndex: count forKey: @""]; - if ([service length] > 0) - [orgServices addObject: service]; - } - - [self _setSnapshotValue: @"workService" - to: [orgServices componentsJoinedByString: @", "]]; - } -} - -- (NSString *) preferredEmail -{ - return preferredEmail; -} - -- (void) setPreferredEmail: (NSString *) aString -{ - preferredEmail = aString; -} - -- (void) _retrieveQueryParameter: (NSString *) queryKey - intoSnapshotValue: (NSString *) snapshotKey -{ - NSString *queryValue; - - queryValue = [self queryParameterForKey: queryKey]; - if (queryValue && [queryValue length] > 0) - [self _setSnapshotValue: snapshotKey to: queryValue]; -} - -- (void) initSnapshot -{ - NSArray *elements; - CardElement *element; - - element = [card n]; - [self _setSnapshotValue: @"sn" - to: [element flattenedValueAtIndex: 0 forKey: @""]]; - [self _setSnapshotValue: @"givenName" - to: [element flattenedValueAtIndex: 1 forKey: @""]]; - [self _setSnapshotValue: @"fn" to: [card fn]]; - [self _setSnapshotValue: @"nickname" to: [card nickname]]; - - elements = [card childrenWithTag: @"tel"]; - // We do this (exclude FAX) in order to avoid setting the WORK number as the FAX - // one if we do see the FAX field BEFORE the WORK number. - [self _setSnapshotValue: @"telephoneNumber" - to: [self _simpleValueForType: @"work" inArray: elements excluding: @"fax"]]; - [self _setSnapshotValue: @"homeTelephoneNumber" - to: [self _simpleValueForType: @"home" inArray: elements excluding: @"fax"]]; - [self _setSnapshotValue: @"mobile" - to: [self _simpleValueForType: @"cell" inArray: elements excluding: nil]]; - [self _setSnapshotValue: @"facsimileTelephoneNumber" - to: [self _simpleValueForType: @"fax" inArray: elements excluding: nil]]; - [self _setSnapshotValue: @"pager" - to: [self _simpleValueForType: @"pager" inArray: elements excluding: nil]]; - - // If we don't have a "home" and "work" phone number but - // we have a "voice" one defined, we set it to the "work" value - // This can happen when we have : - // VERSION:2.1 - // N:name;surname;;;; - // TEL;VOICE;HOME: - // TEL;VOICE;WORK: - // TEL;PAGER: - // TEL;FAX;WORK: - // TEL;CELL:514 123 1234 - // TEL;VOICE:450 456 6789 - // ADR;HOME:;;;;;; - // ADR;WORK:;;;;;; - // ADR:;;;;;; - if ([[snapshot objectForKey: @"telephoneNumber"] length] == 0 && - [[snapshot objectForKey: @"homeTelephoneNumber"] length] == 0 && - [elements count] > 0) - { - [self _setSnapshotValue: @"telephoneNumber" - to: [self _simpleValueForType: @"voice" inArray: elements excluding: nil]]; - } - - [self _setupEmailFields]; - - [self _setSnapshotValue: @"screenName" - to: [[card uniqueChildWithTag: @"x-aim"] flattenedValuesForKey: @""]]; - - elements = [card childrenWithTag: @"adr" - andAttribute: @"type" havingValue: @"work"]; - if (elements && [elements count] > 0) - { - element = [elements objectAtIndex: 0]; - [self _setSnapshotValue: @"workExtendedAddress" - to: [element flattenedValueAtIndex: 1 forKey: @""]]; - [self _setSnapshotValue: @"workStreetAddress" - to: [element flattenedValueAtIndex: 2 forKey: @""]]; - [self _setSnapshotValue: @"workCity" - to: [element flattenedValueAtIndex: 3 forKey: @""]]; - [self _setSnapshotValue: @"workState" - to: [element flattenedValueAtIndex: 4 forKey: @""]]; - [self _setSnapshotValue: @"workPostalCode" - to: [element flattenedValueAtIndex: 5 forKey: @""]]; - [self _setSnapshotValue: @"workCountry" - to: [element flattenedValueAtIndex: 6 forKey: @""]]; - } - - elements = [card childrenWithTag: @"adr" - andAttribute: @"type" havingValue: @"home"]; - if (elements && [elements count] > 0) - { - element = [elements objectAtIndex: 0]; - [self _setSnapshotValue: @"homeExtendedAddress" - to: [element flattenedValueAtIndex: 1 forKey: @""]]; - [self _setSnapshotValue: @"homeStreetAddress" - to: [element flattenedValueAtIndex: 2 forKey: @""]]; - [self _setSnapshotValue: @"homeCity" - to: [element flattenedValueAtIndex: 3 forKey: @""]]; - [self _setSnapshotValue: @"homeState" - to: [element flattenedValueAtIndex: 4 forKey: @""]]; - [self _setSnapshotValue: @"homePostalCode" - to: [element flattenedValueAtIndex: 5 forKey: @""]]; - [self _setSnapshotValue: @"homeCountry" - to: [element flattenedValueAtIndex: 6 forKey: @""]]; - } - - elements = [card childrenWithTag: @"url"]; - [self _setSnapshotValue: @"workURL" - to: [self _simpleValueForType: @"work" inArray: elements excluding: nil]]; - [self _setSnapshotValue: @"homeURL" - to: [self _simpleValueForType: @"home" inArray: elements excluding: nil]]; - - // If we don't have a "work" or "home" URL but we still have - // an URL field present, let's add it to the "home" value - if ([[snapshot objectForKey: @"workURL"] length] == 0 && - [[snapshot objectForKey: @"homeURL"] length] == 0 && - [elements count] > 0) - { - [self _setSnapshotValue: @"homeURL" - to: [[elements objectAtIndex: 0] flattenedValuesForKey: @""]]; - } - // If we do have a "work" URL but no "home" URL but two - // values URLs present, let's add the second one as the home URL - else if ([[snapshot objectForKey: @"workURL"] length] > 0 && - [[snapshot objectForKey: @"homeURL"] length] == 0 && - [elements count] > 1) - { - int i; - - for (i = 0; i < [elements count]; i++) - { - if ([[[elements objectAtIndex: i] flattenedValuesForKey: @""] - caseInsensitiveCompare: [snapshot objectForKey: @"workURL"]] != NSOrderedSame) - { - [self _setSnapshotValue: @"homeURL" - to: [[elements objectAtIndex: i] flattenedValuesForKey: @""]]; - break; - } - } - } - - - [self _setSnapshotValue: @"calFBURL" - to: [[card uniqueChildWithTag: @"FBURL"] flattenedValuesForKey: @""]]; - - [self _setSnapshotValue: @"title" to: [card title]]; - [self _setupOrgFields]; - - [self _setSnapshotValue: @"bday" to: [card bday]]; - [self _setSnapshotValue: @"tz" to: [card tz]]; - [self _setSnapshotValue: @"note" to: [card note]]; - - [self _retrieveQueryParameter: @"contactEmail" - intoSnapshotValue: @"workMail"]; - [self _retrieveQueryParameter: @"contactFN" - intoSnapshotValue: @"fn"]; -} - -- (id ) defaultAction -{ - card = [[self clientObject] vCard]; - if (card) - [self initSnapshot]; - else - return [NSException exceptionWithHTTPStatus:404 /* Not Found */ - reason: @"could not open contact"]; - - return self; + return ([actionName hasPrefix: @"save"]); } - (NSString *) viewActionName @@ -647,216 +339,60 @@ return @"editAsContact"; } -- (BOOL) canCreateOrModify +- (BOOL) supportPhotos { - SOGoObject *co; - - co = [self clientObject]; - - return ([co isKindOfClass: [SOGoContentObject class]] - && [super canCreateOrModify]); + return [[self clientObject] isKindOfClass: SOGoContactGCSEntryK]; } -- (NSArray *) photosURL +- (BOOL) hasPhoto +{ + return [[self clientObject] hasPhoto]; +} + +- (NSString *) photoURL { - NSArray *photoElements; NSURL *soURL; - NSString *baseInlineURL, *photoURL; - NGVCardPhoto *photo; - int count, max; - if (!photosURL) - { - soURL = [[self clientObject] soURL]; - baseInlineURL = [soURL absoluteString]; - photoElements = [card childrenWithTag: @"photo"]; - max = [photoElements count]; - photosURL = [[NSMutableArray alloc] initWithCapacity: max]; - for (count = 0; count < max; count++) - { - photo = [photoElements objectAtIndex: count]; - if ([photo isInline]) - photoURL = [NSString stringWithFormat: @"%@/photo%d", - baseInlineURL, count]; - else - photoURL = [photo flattenedValuesForKey: @""]; - [photosURL addObject: photoURL]; - } - } + soURL = [[self clientObject] soURL]; - return photosURL; -} - -- (CardElement *) _elementWithTag: (NSString *) tag - ofType: (NSString *) type -{ - NSArray *elements; - CardElement *element; - - elements = [card childrenWithTag: tag - andAttribute: @"type" havingValue: type]; - if ([elements count] > 0) - element = [elements objectAtIndex: 0]; - else - { - element = [CardElement new]; - [element autorelease]; - [element setTag: tag]; - [element addType: type]; - [card addChild: element]; - } - - return element; -} - -- (void) _savePhoneValues -{ - CardElement *phone; - - phone = [self _elementWithTag: @"tel" ofType: @"work"]; - [phone setSingleValue: [snapshot objectForKey: @"telephoneNumber"] forKey: @""]; - phone = [self _elementWithTag: @"tel" ofType: @"home"]; - [phone setSingleValue: [snapshot objectForKey: @"homeTelephoneNumber"] forKey: @""]; - phone = [self _elementWithTag: @"tel" ofType: @"cell"]; - [phone setSingleValue: [snapshot objectForKey: @"mobile"] forKey: @""]; - phone = [self _elementWithTag: @"tel" ofType: @"fax"]; - [phone setSingleValue: [snapshot objectForKey: @"facsimileTelephoneNumber"] - forKey: @""]; - phone = [self _elementWithTag: @"tel" ofType: @"pager"]; - [phone setSingleValue: [snapshot objectForKey: @"pager"] forKey: @""]; -} - -- (void) _saveEmails -{ - CardElement *workMail, *homeMail; - - workMail = [self _elementWithTag: @"email" ofType: @"work"]; - [workMail setSingleValue: [snapshot objectForKey: @"workMail"] forKey: @""]; - homeMail = [self _elementWithTag: @"email" ofType: @"home"]; - [homeMail setSingleValue: [snapshot objectForKey: @"homeMail"] forKey: @""]; - if (preferredEmail) - { - if ([preferredEmail isEqualToString: @"work"]) - [card setPreferred: workMail]; - else - [card setPreferred: homeMail]; - } - - [[card uniqueChildWithTag: @"x-mozilla-html"] - setSingleValue: [snapshot objectForKey: @"mozillaUseHtmlMail"] - forKey: @""]; -} - -- (void) _saveSnapshot -{ - CardElement *element; - NSArray *units; - - [card setNWithFamily: [snapshot objectForKey: @"sn"] - given: [snapshot objectForKey: @"givenName"] - additional: nil - prefixes: nil - suffixes: nil]; - [card setNickname: [snapshot objectForKey: @"nickname"]]; - [card setFn: [snapshot objectForKey: @"fn"]]; - [card setTitle: [snapshot objectForKey: @"title"]]; - [card setBday: [snapshot objectForKey: @"bday"]]; - [card setNote: [snapshot objectForKey: @"note"]]; - [card setTz: [snapshot objectForKey: @"tz"]]; - - element = [self _elementWithTag: @"adr" ofType: @"home"]; - [element setSingleValue: [snapshot objectForKey: @"homeExtendedAddress"] - atIndex: 1 forKey: @""]; - [element setSingleValue: [snapshot objectForKey: @"homeStreetAddress"] - atIndex: 2 forKey: @""]; - [element setSingleValue: [snapshot objectForKey: @"homeCity"] - atIndex: 3 forKey: @""]; - [element setSingleValue: [snapshot objectForKey: @"homeState"] - atIndex: 4 forKey: @""]; - [element setSingleValue: [snapshot objectForKey: @"homePostalCode"] - atIndex: 5 forKey: @""]; - [element setSingleValue: [snapshot objectForKey: @"homeCountry"] - atIndex: 6 forKey: @""]; - - element = [self _elementWithTag: @"adr" ofType: @"work"]; - [element setSingleValue: [snapshot objectForKey: @"workExtendedAddress"] - atIndex: 1 forKey: @""]; - [element setSingleValue: [snapshot objectForKey: @"workStreetAddress"] - atIndex: 2 forKey: @""]; - [element setSingleValue: [snapshot objectForKey: @"workCity"] - atIndex: 3 forKey: @""]; - [element setSingleValue: [snapshot objectForKey: @"workState"] - atIndex: 4 forKey: @""]; - [element setSingleValue: [snapshot objectForKey: @"workPostalCode"] - atIndex: 5 forKey: @""]; - [element setSingleValue: [snapshot objectForKey: @"workCountry"] - atIndex: 6 forKey: @""]; - - element = [CardElement simpleElementWithTag: @"fburl" - value: [snapshot objectForKey: @"calFBURL"]]; - [card setUniqueChild: element]; - - units = [NSArray arrayWithObject: [snapshot objectForKey: @"workService"]]; - [card setOrg: [snapshot objectForKey: @"workCompany"] - units: units]; - - [self _savePhoneValues]; - [self _saveEmails]; - [[self _elementWithTag: @"url" ofType: @"home"] - setSingleValue: [snapshot objectForKey: @"homeURL"] forKey: @""]; - [[self _elementWithTag: @"url" ofType: @"work"] - setSingleValue: [snapshot objectForKey: @"workURL"] forKey: @""]; - - [[card uniqueChildWithTag: @"x-aim"] - setSingleValue: [snapshot objectForKey: @"screenName"] - forKey: @""]; + return [NSString stringWithFormat: @"%@/photo", [soURL absoluteString]]; } - (id ) saveAction { - SOGoContactGCSEntry *contact; + SOGoObject *contact; id result; NSString *jsRefreshMethod; SoSecurityManager *sm; contact = [self clientObject]; - card = [contact vCard]; - if (card) - { -// [self _fixupSnapshot]; - [self _saveSnapshot]; - [card setCategories: contactCategories]; - [self _fetchAndCombineCategoriesList: contactCategories]; - [contact save]; + [contact setLDIFRecord: ldifRecord]; + [self _fetchAndCombineCategoriesList]; + [contact save]; - if (componentAddressBook && componentAddressBook != [self componentAddressBook]) - { - sm = [SoSecurityManager sharedSecurityManager]; - if (![sm validatePermission: SoPerm_DeleteObjects - onObject: componentAddressBook - inContext: context]) - { - if (![sm validatePermission: SoPerm_AddDocumentsImagesAndFiles - onObject: componentAddressBook - inContext: context]) - [contact moveToFolder: (SOGoGCSFolder *)componentAddressBook]; // TODO: handle exception - } - } - - if ([[[[self context] request] formValueForKey: @"nojs"] intValue]) - result = [self redirectToLocation: [self modulePath]]; - else + if (componentAddressBook && componentAddressBook != [self componentAddressBook]) + { + sm = [SoSecurityManager sharedSecurityManager]; + if (![sm validatePermission: SoPerm_DeleteObjects + onObject: componentAddressBook + inContext: context]) { - jsRefreshMethod - = [NSString stringWithFormat: @"refreshContacts(\"%@\")", - [contact nameInContainer]]; - result = [self jsCloseWithRefreshMethod: jsRefreshMethod]; + if (![sm validatePermission: SoPerm_AddDocumentsImagesAndFiles + onObject: componentAddressBook + inContext: context]) + [contact moveToFolder: (SOGoGCSFolder *)componentAddressBook]; // TODO: handle exception } } + + if ([[[[self context] request] formValueForKey: @"nojs"] intValue]) + result = [self redirectToLocation: [self modulePath]]; else - result = [NSException exceptionWithHTTPStatus: 400 /* Bad Request */ - reason: @"method cannot be invoked on " - @"the specified object"]; + { + jsRefreshMethod + = [NSString stringWithFormat: @"refreshContacts(\"%@\")", + [contact nameInContainer]]; + result = [self jsCloseWithRefreshMethod: jsRefreshMethod]; + } return result; } @@ -866,17 +402,15 @@ NSString *email, *cn, *url; NSMutableString *address; - card = [[self clientObject] vCard]; - [self initSnapshot]; - if ([preferredEmail isEqualToString: @"home"]) - email = [snapshot objectForKey: @"homeMail"]; - else - email = [snapshot objectForKey: @"workMail"]; + [self ldifRecord]; + email = [ldifRecord objectForKey: @"mail"]; + if ([email length] == 0) + email = [ldifRecord objectForKey: @"mozillasecondemail"]; if (email) { address = [NSMutableString string]; - cn = [card fn]; + cn = [ldifRecord objectForKey: @"cn"]; if ([cn length] > 0) [address appendFormat: @"%@ <%@>", cn, email]; else diff --git a/UI/Contacts/UIxContactFolderActions.m b/UI/Contacts/UIxContactFolderActions.m index 2dd503615..8adbcaa2b 100644 --- a/UI/Contacts/UIxContactFolderActions.m +++ b/UI/Contacts/UIxContactFolderActions.m @@ -35,6 +35,7 @@ #import #import #import +#import #import #import @@ -76,9 +77,10 @@ inContext: [self context] acquire: NO]; if ([currentChild respondsToSelector: @selector (vCard)]) - [content appendFormat: [[currentChild vCard] ldifString]]; + [content appendFormat: [[currentChild ldifRecord] ldifRecordAsString]]; else if ([currentChild respondsToSelector: @selector (vList)]) [content appendFormat: [[currentChild vList] ldifString]]; + [content appendString: @"\n"]; } response = [context response]; diff --git a/UI/Contacts/UIxContactView.m b/UI/Contacts/UIxContactView.m index 498bf2e91..3d3bf53c2 100644 --- a/UI/Contacts/UIxContactView.m +++ b/UI/Contacts/UIxContactView.m @@ -52,6 +52,7 @@ - (void) dealloc { + [card release]; [photosURL release]; [super dealloc]; } @@ -377,9 +378,7 @@ { NSString *data; - data = nil; - - if (url) + if ([url length] > 0) { if (![[url lowercaseString] rangeOfString: @"://"].length) url = [NSString stringWithFormat: @"http://%@", url]; @@ -388,6 +387,8 @@ @"%@", url, url]; } + else + data = nil; return [self _cardStringWithLabel: nil value: data]; } @@ -646,30 +647,12 @@ /* action */ -- (id ) vcardAction -{ -#warning this method is unused - WOResponse *response; - - card = [[self clientObject] vCard]; - if (card) - { - response = [context response]; - [response setHeader: @"text/vcard" forKey: @"Content-type"]; - [response appendContentString: [card versitString]]; - } - else - return [NSException exceptionWithHTTPStatus: 404 /* Not Found */ - reason:@"could not locate contact"]; - - return response; -} - - (id ) defaultAction { card = [[self clientObject] vCard]; if (card) { + [card retain]; phones = nil; homeAdr = nil; workAdr = nil; diff --git a/UI/Contacts/product.plist b/UI/Contacts/product.plist index e88d39ca5..ca66751fe 100644 --- a/UI/Contacts/product.plist +++ b/UI/Contacts/product.plist @@ -234,13 +234,27 @@ SOGoContactLDIFEntry = { methods = { view = { - protectedBy = ""; + protectedBy = "Access Contents Information"; pageName = "UIxContactView"; }; edit = { - protectedBy = ""; + protectedBy = "Access Contents Information"; pageName = "UIxContactEditor"; }; + editAsContact = { + protectedBy = "Access Contents Information"; + pageName = "UIxContactEditor"; + }; + save = { + protectedBy = "Change Images And Files"; + pageName = "UIxContactEditor"; + actionName = "save"; + }; + saveAsContact = { + protectedBy = "Change Images And Files"; + pageName = "UIxContactEditor"; + actionName = "save"; + }; write = { protectedBy = ""; pageName = "UIxContactEditor"; diff --git a/UI/Templates/ContactsUI/UIxContactEditor.wox b/UI/Templates/ContactsUI/UIxContactEditor.wox index ebb3eb41c..89b59cb8f 100644 --- a/UI/Templates/ContactsUI/UIxContactEditor.wox +++ b/UI/Templates/ContactsUI/UIxContactEditor.wox @@ -1,4 +1,4 @@ - + -
  • -
  • +
  • +
  • -
  • -
  • +
  • +
  • @@ -46,34 +50,34 @@ + + var:value="ldifRecord.sn" + /> + + @@ -82,26 +86,26 @@ + + + @@ -111,9 +115,8 @@ - + selection="ldifRecord.mozillausehtmlmail" + /> @@ -123,9 +126,9 @@ @@ -133,24 +136,19 @@ + name="homephone" id="homephone" + var:value="ldifRecord.homephone" + /> + name="facsimiletelephonenumber" + id="facsimiletelephonenumber" + var:value="ldifRecord.facsimiletelephonenumber" + /> @@ -159,38 +157,37 @@ - + var:value="ldifRecord.pager" + /> + var:value="ldifRecord.mobile" + /> -
    -
    -
    -
    +
    +
    + - - -
    + + +
    @@ -198,64 +195,64 @@ + + + + + + + @@ -267,128 +264,116 @@ - + var:value="ldifRecord.title" + /> + + + + + + + + +
    -
    - -
    -
    +
    + +
    - - - - - - diff --git a/UI/WebServerResources/ContactsUI.js b/UI/WebServerResources/ContactsUI.js index e38f37028..89f070d94 100644 --- a/UI/WebServerResources/ContactsUI.js +++ b/UI/WebServerResources/ContactsUI.js @@ -512,9 +512,6 @@ function onToolbarDeleteSelectedContactsConfirm(dialogId) { var contactsList = $('contactsList'); var rows = contactsList.getSelectedRowsId(); for (var i = 0; i < rows.length; i++) { - var row = $(rows[i]); - row.deselect(); - row.hide(); delete cachedContacts[Contact.currentAddressBook + "/" + rows[i]]; var urlstr = (URLForFolderID(Contact.currentAddressBook) + "/" + rows[i] + "/delete"); @@ -532,6 +529,7 @@ function onContactDeleteEventCallback(http) { $("contactView").update(); Contact.currentContact = null; } + Contact.deleteContactsRequestCount--; if (Contact.deleteContactsRequestCount == 0) { var nextRow = row.next("tr"); @@ -543,7 +541,10 @@ function onContactDeleteEventCallback(http) { loadContact(Contact.currentContact); } } - row.parentNode.removeChild(row); + if (row) { + row.deselect(); + row.parentNode.removeChild(row); + } } else if (parseInt(http.status) == 403) { var row = $(http.callbackData); diff --git a/UI/WebServerResources/UIxContactEditor.css b/UI/WebServerResources/UIxContactEditor.css index 58112a94d..26f3c8ba6 100644 --- a/UI/WebServerResources/UIxContactEditor.css +++ b/UI/WebServerResources/UIxContactEditor.css @@ -69,3 +69,9 @@ INPUT.comboBoxField, #emptyCategory #otherInfos TEXTAREA { width: 70%; } + +#birthday, #birthmonth +{ width: 18px; } + +#birthyear +{ width: 36px; } diff --git a/UI/WebServerResources/UIxContactEditor.js b/UI/WebServerResources/UIxContactEditor.js index f9f3a53bd..1db214b58 100644 --- a/UI/WebServerResources/UIxContactEditor.js +++ b/UI/WebServerResources/UIxContactEditor.js @@ -22,9 +22,9 @@ 02111-1307, USA. */ -var dateRegex = /^[0-9]{4}-[0-9]{2}-[0-9]{2}$/; +var dateRegex = /^(([0-9]{2})?[0-9])?[0-9]-[0-9]?[0-9]-[0-9]?[0-9]$/; -var displayNameChanged = false; +var displaynameChanged = false; var tabIndex = 0; @@ -37,43 +37,43 @@ function unescapeCallbackParameter(s) { } function copyContact(type, email, uid, sn, - cn, givenName, telephoneNumber, facsimileTelephoneNumber, - mobile, postalAddress, homePostalAddress, - departmentNumber, l) + displayname, givenname, telephonenumber, facsimiletelephonenumber, + mobile, postalAddress, homePostalAddress, + departmentnumber, l) { // var type = arguments[0]; // var email = arguments[1]; // var uid = arguments[2]; // var sn = arguments[3]; - // var givenName = arguments[4]; - // var telephoneNumber = arguments[5]; - // var facsimileTelephoneNumber = arguments[6]; + // var givenname = arguments[4]; + // var telephonenumber = arguments[5]; + // var facsimiletelephonenumber = arguments[6]; // var mobile = arguments[7]; - // var postalAddress = arguments[8]; - // var homePostalAddress = arguments[9]; - // var departmentNumber = arguments[10]; + // var postaladdress = arguments[8]; + // var homepostaladdress = arguments[9]; + // var departmentnumber = arguments[10]; // var l = arguments[11]; var e; - e = $('cn'); - e.setAttribute('value', unescapeCallbackParameter(cn)); + e = $('displayname'); + e.setAttribute('value', unescapeCallbackParameter(displayname)); e = $('email'); e.setAttribute('value', email); e = $('sn'); e.setAttribute('value', unescapeCallbackParameter(sn)); - e = $('givenName'); - e.setAttribute('value', unescapeCallbackParameter(givenName)); - e = $('telephoneNumber'); - e.setAttribute('value', telephoneNumber); - e = $('facsimileTelephoneNumber'); - e.setAttribute('value', facsimileTelephoneNumber); + e = $('givenname'); + e.setAttribute('value', unescapeCallbackParameter(givenname)); + e = $('telephonenumber'); + e.setAttribute('value', telephonenumber); + e = $('facsimiletelephonenumber'); + e.setAttribute('value', facsimileTelephonenumber); e = $('mobile'); e.setAttribute('value', mobile); - e = $('postalAddress'); + e = $('postaladdress'); e.setAttribute('value', unescapeCallbackParameter(postalAddress)); - e = $('homePostalAddress'); + e = $('homepostaladdress'); e.setAttribute('value', unescapeCallbackParameter(homePostalAddress)); - e = $('departmentNumber'); - e.setAttribute('value', unescapeCallbackParameter(departmentNumber)); + e = $('departmentnumber'); + e.setAttribute('value', unescapeCallbackParameter(departmentnumber)); e = $('l'); e.setAttribute('value', unescapeCallbackParameter(l)); }; @@ -81,23 +81,25 @@ function copyContact(type, email, uid, sn, function validateContactEditor() { var rc = true; - var e = $('workMail'); + var e = $('mail'); if (e.value.length > 0 && !emailRE.test(e.value)) { alert(_("invalidemailwarn")); rc = false; } - e = $('homeMail'); + e = $('mozillasecondemail'); if (e.value.length > 0 && !emailRE.test(e.value)) { alert(_("invalidemailwarn")); rc = false; } - e = $('birthday'); - if (e.value.length > 0 - && !dateRegex.test(e.value)) { + var byear = $('birthyear'); + var bmonth = $('birthmonth'); + var bday = $('birthday'); + var bdayValue = byear.value + "-" + bmonth.value + "-" + bday.value; + if (bdayValue != "--" && !dateRegex.test(bdayValue)) { alert(_("invaliddatewarn")); rc = false; } @@ -105,25 +107,25 @@ function validateContactEditor() { return rc; } -function onFnKeyDown() { - var fn = $("fn"); +function onDisplaynameKeyDown() { + var fn = $("displayname"); fn.onkeydown = null; - displayNameChanged = true; + displaynameChanged = true; return true; } -function onFnNewValue(event) { - if (!displayNameChanged) { +function onDisplaynameNewValue(event) { + if (!displaynameChanged) { var sn = $("sn").value.trim(); - var givenName = $("givenName").value.trim(); + var givenname = $("givenname").value.trim(); - var fullName = givenName; - if (fullName && sn) - fullName += ' '; - fullName += sn; + var fullname = givenname; + if (fullname && sn) + fullname += ' '; + fullname += sn; - $("fn").value = fullName; + $("displayname").value = fullname; } return true; @@ -144,7 +146,7 @@ function onEditorSubmitClick(event) { function saveCategories() { var container = $("categoryContainer"); - var catsInput = $("contactCategories"); + var catsInput = $("jsonContactCategories"); if (container && catsInput) { var newCategories = $([]); var inputs = container.select("INPUT"); @@ -164,8 +166,8 @@ function onDocumentKeydown(event) { var target = Event.element(event); if (target.tagName == "INPUT" || target.tagName == "SELECT") { if (event.keyCode == Event.KEY_RETURN) { - var fcn = onEditorSubmitClick.bind($("submitButton")); - fcn(); + var fdisplayname = onEditorSubmitClick.bind($("submitButton")); + fdisplayname(); Event.stop(event); } } @@ -267,10 +269,10 @@ function initEditorForm() { var controller = new SOGoTabsController(); controller.attachToTabsContainer(tabsContainer); - displayNameChanged = ($("fn").value.length > 0); - $("fn").onkeydown = onFnKeyDown; - $("sn").onkeyup = onFnNewValue; - $("givenName").onkeyup = onFnNewValue; + displaynameChanged = ($("displayname").value.length > 0); + $("displayname").onkeydown = onDisplaynameKeyDown; + $("sn").onkeyup = onDisplaynameNewValue; + $("givenname").onkeyup = onDisplaynameNewValue; $("cancelButton").observe("click", onEditorCancelClick); var submitButton = $("submitButton"); @@ -280,8 +282,10 @@ function initEditorForm() { Event.observe(document, "keydown", onDocumentKeydown); - regenerateCategoriesMenu(); - var catsInput = $("contactCategories"); + if (typeof(gCategories) != "undefined") { + regenerateCategoriesMenu(); + } + var catsInput = $("jsonContactCategories"); if (catsInput && catsInput.value.length > 0) { var contactCats = $(catsInput.value.evalJSON(false)); for (var i = 0; i < contactCats.length; i++) { @@ -294,6 +298,6 @@ function initEditorForm() { emptyCategory.tabIndex = 10000; emptyCategory.observe("click", onEmptyCategoryClick); } -} +} document.observe("dom:loaded", initEditorForm); From ad7259b751ad304c2487f5e3c322410bf33556d0 Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Fri, 30 Dec 2011 20:41:30 +0000 Subject: [PATCH 10/68] Monotone-Parent: d8a02a76c20d760d00a46a0e98b6ca134a05cbc7 Monotone-Revision: f2f2f66b3df579ca384e578aded6976b6634ffcb Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2011-12-30T20:41:30 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 3 + SoObjects/SOGo/LDAPSourceSchema.h | 45 +++++ SoObjects/SOGo/LDAPSourceSchema.m | 295 ++++++++++++++++++++++++++++++ 3 files changed, 343 insertions(+) create mode 100644 SoObjects/SOGo/LDAPSourceSchema.h create mode 100644 SoObjects/SOGo/LDAPSourceSchema.m diff --git a/ChangeLog b/ChangeLog index 371e3a5c7..3855edc51 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2011-12-30 Wolfgang Sourdeau + * SoObjects/SOGo/LDAPSourceSchema.[hm]: new class module enabling + schema auto-discovery. + * UI/WebServerResources/UIxContactEditor.js (validateContactEditor): the birth date is validated slightly differently, by enabling empty and 2-digit years as well as single diff --git a/SoObjects/SOGo/LDAPSourceSchema.h b/SoObjects/SOGo/LDAPSourceSchema.h new file mode 100644 index 000000000..13d415bae --- /dev/null +++ b/SoObjects/SOGo/LDAPSourceSchema.h @@ -0,0 +1,45 @@ +/* LDAPSourceSchema.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 LDAPSOURCESCHEMA_H +#define LDAPSOURCESCHEMA_H + +#import + +@class NSMutableDictionary; +@class NGLdapConnection; + +@interface LDAPSourceSchema : NSObject +{ + NSMutableDictionary *schema; +} + +- (void) readSchemaFromConnection: (NGLdapConnection *) conn; + +- (NSArray *) fieldsForClass: (NSString *) className; + +/* merged list of attributes with unique names */ +- (NSArray *) fieldsForClasses: (NSArray *) className; + +@end + +#endif /* LDAPSOURCESCHEMA_H */ diff --git a/SoObjects/SOGo/LDAPSourceSchema.m b/SoObjects/SOGo/LDAPSourceSchema.m new file mode 100644 index 000000000..a81417782 --- /dev/null +++ b/SoObjects/SOGo/LDAPSourceSchema.m @@ -0,0 +1,295 @@ +/* LDAPSourceSchema.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 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. + */ + +#import +#import +#import +#import + +#import + +#import +#import +#import + +#import "LDAPSourceSchema.h" +#import "NSDictionary+Utilities.h" + +static EOQualifier *allOCQualifier = nil; + +@implementation LDAPSourceSchema + ++ (void) initialize +{ + allOCQualifier = [[EOKeyValueQualifier alloc] + initWithKey: @"objectClass" + operatorSelector: EOQualifierOperatorEqual + value: @"*"]; +} + +- (id) init +{ + if ((self = [super init])) + { + schema = nil; + } + + return self; +} + +- (void) dealloc +{ + [schema release]; + [super dealloc]; +} + +static NSArray * +schemaTokens (NSString *schema) +{ + unichar *characters; + NSUInteger count, max, parenLevel = 0, firstChar = (NSUInteger) -1; + NSMutableArray *arrayString, *parentArray, *currentArray = nil; + NSArray *topArray = nil; + NSString *token; + + arrayString = [NSMutableArray array]; + + max = [schema length]; + characters = malloc ((max + 1) * sizeof (unichar)); + characters[max] = 0; + [schema getCharacters: characters]; + + for (count = 0; count < max; count++) + { + switch (characters[count]) + { + case '(': + // NSLog (@"increase"); + parenLevel++; + parentArray = currentArray; + currentArray = [NSMutableArray array]; + if (parentArray == nil) + topArray = currentArray; + [parentArray addObject: currentArray]; + [arrayString addObject: currentArray]; + break; + case ')': + // NSLog (@"decrease"); + parenLevel--; + [arrayString removeLastObject]; + currentArray = [arrayString lastObject]; + break; + case ' ': + if (firstChar != (NSUInteger) -1) + { + token = [NSString stringWithCharacters: characters + firstChar + length: (count - firstChar)]; + if (![token isEqualToString: @"$"]) + [currentArray addObject: token]; + // NSLog (@"added token: %@", token); + firstChar = (NSUInteger) -1; + } + break; + default: + if (currentArray && (firstChar == (NSUInteger) -1)) + firstChar = count; + } + } + + free (characters); + + return topArray; +} + +static inline id +schemaValue (NSArray *tokens, NSString *key) +{ + NSUInteger idx; + id value; + + idx = [tokens indexOfObject: key]; + if (idx != NSNotFound) + value = [tokens objectAtIndex: (idx + 1)]; + else + value = nil; + + return value; +} + +static NSMutableDictionary * +parseSchema (NSString *schema) +{ + NSArray *tokens; + NSMutableDictionary *schemaDict; + NSMutableArray *fields; + id value; + + schemaDict = [NSMutableDictionary dictionaryWithCapacity: 6]; + tokens = schemaTokens (schema); + // [schemaDict setObject: [tokens objectAtIndex: 0] + // forKey: @"oid"]; + value = schemaValue (tokens, @"NAME"); + if (value) + { + /* sometimes, objectClasses can have two names */ + if ([value isKindOfClass: [NSString class]]) + value = [NSArray arrayWithObject: value]; + [schemaDict setObject: value forKey: @"names"]; + } + + value = schemaValue (tokens, @"SUP"); + if (value) + [schemaDict setObject: value forKey: @"sup"]; + + fields = [NSMutableArray new]; + [schemaDict setObject: fields forKey: @"fields"]; + [fields release]; + value = schemaValue (tokens, @"MUST"); + if (value) + { + if ([value isKindOfClass: [NSArray class]]) + [fields addObjectsFromArray: value]; + else + [fields addObject: value]; + } + value = schemaValue (tokens, @"MAY"); + if (value) + { + if ([value isKindOfClass: [NSArray class]]) + [fields addObjectsFromArray: value]; + else + [fields addObject: value]; + } + + return schemaDict; +} + +static void +fillSchemaFromEntry (NSMutableDictionary *schema, NGLdapEntry *entry) +{ + NSEnumerator *strings; + NGLdapAttribute *attr; + NSMutableDictionary *schemaDict; + NSArray *names; + NSString *string, *name; + NSUInteger count, max; + + attr = [entry attributeWithName: @"objectclasses"]; + strings = [attr stringValueEnumerator]; + while ((string = [strings nextObject])) + { + schemaDict = parseSchema (string); + names = [schemaDict objectForKey: @"names"]; + max = [names count]; + for (count = 0; count < max; count++) + { + name = [[names objectAtIndex: count] lowercaseString]; + if ([name hasPrefix: @"'"] && [name hasSuffix: @"'"]) + name + = [name substringWithRange: NSMakeRange (1, [name length] - 2)]; + [schema setObject: schemaDict forKey: name]; + } + /* the list of names is no longer required from the schema itself */ + [schemaDict removeObjectForKey: @"names"]; + } +} + +- (void) readSchemaFromConnection: (NGLdapConnection *) conn +{ + NSEnumerator *entries; + NGLdapEntry *entry; + NSString *dn; + + ASSIGN (schema, [NSMutableDictionary new]); + [schema release]; + + entries = [conn baseSearchAtBaseDN: @"" + qualifier: allOCQualifier + attributes: [NSArray arrayWithObject: @"subschemaSubentry"]]; + entry = [entries nextObject]; + if (entry) + { + dn = [[entry attributeWithName: @"subschemaSubentry"] + stringValueAtIndex: 0]; + if (dn) + { + entries = [conn baseSearchAtBaseDN: dn + qualifier: allOCQualifier + attributes: [NSArray arrayWithObject: @"objectclasses"]]; + entry = [entries nextObject]; + if (entry) + fillSchemaFromEntry (schema, entry); + } + } +} + +static void +fillFieldsForClass (NSMutableDictionary *schema, NSString *schemaName, + NSMutableArray *fields) +{ + NSDictionary *schemaDict; + NSString *sup; + NSArray *schemaFields; + + schemaDict = [schema objectForKey: [schemaName lowercaseString]]; + if (schemaDict) + { + schemaFields = [schemaDict objectForKey: @"fields"]; + if ([schemaFields count] > 0) + [fields addObjectsFromArray: schemaFields]; + sup = [schemaDict objectForKey: @"sup"]; + if ([sup length] > 0) + fillFieldsForClass (schema, sup, fields); + } +} + +- (NSArray *) fieldsForClass: (NSString *) className +{ + NSMutableArray *fields; + + fields = [NSMutableArray arrayWithCapacity: 128]; + fillFieldsForClass (schema, className, fields); + + return fields; +} + +- (NSArray *) fieldsForClasses: (NSArray *) classNames +{ + NSMutableDictionary *fieldHash; + NSNumber *yesValue; + NSString *name; + NSUInteger count, max; + + yesValue = [NSNumber numberWithBool: YES]; + + fieldHash = [NSMutableDictionary dictionary]; + max = [classNames count]; + for (count = 0; count < max; count++) + { + name = [classNames objectAtIndex: count]; + [fieldHash setObject: yesValue forKeys: [self fieldsForClass: name]]; + } + + return [fieldHash allKeys]; +} + +@end From 831771c57264e4f6a54dfead219d0987754c35a7 Mon Sep 17 00:00:00 2001 From: Jean Raby Date: Tue, 3 Jan 2012 21:11:31 +0000 Subject: [PATCH 11/68] fix changelog merge... Monotone-Parent: 71d893c6f56295ecb4a106a46e10ccc403627cc4 Monotone-Revision: 61b0fdce16f6c0727e58cc97232171b41008ed41 Monotone-Author: jraby@inverse.ca Monotone-Date: 2012-01-03T21:11:31 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/ChangeLog b/ChangeLog index 2d94447e9..14f27a35a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +2012-01-02 Francis Lachapelle + + * UI/WebServerResources/UIxPreferences.js (getFilterFromEditor): + return a string (JSON) so the data is properly passed to the + opened window with IE7. + + * UI/WebServerResources/UIxFilterEditor.js (ensureFieldValidity): + a field value is always considered invalid when empty. + 2011-12-30 Wolfgang Sourdeau * SoObjects/SOGo/LDAPSourceSchema.[hm]: new class module enabling @@ -101,15 +110,6 @@ use of the lowercase instance of the string, which was erroneously ignored previously. -2012-01-02 Francis Lachapelle - - * UI/WebServerResources/UIxPreferences.js (getFilterFromEditor): - return a string (JSON) so the data is properly passed to the - opened window with IE7. - - * UI/WebServerResources/UIxFilterEditor.js (ensureFieldValidity): - a field value is always considered invalid when empty. - 2011-12-29 Ludovic Marcotte * SoObjects/SOGo/SOGoSQLUserProfile.m (_sqlJsonRepresentation:): From 53feece8924851dc38004f9dbc4563631c610bef Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Tue, 3 Jan 2012 22:52:27 +0000 Subject: [PATCH 12/68] Monotone-Parent: f2f2f66b3df579ca384e578aded6976b6634ffcb Monotone-Revision: c7009a2d8a1dce845aa1a365123f4b438e3168fe Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-01-03T22:52:27 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 11 +++++++++++ SoObjects/SOGo/SOGoUser.h | 3 +++ SoObjects/SOGo/SOGoUser.m | 11 +++++++++++ SoObjects/SOGo/SOGoUserManager.m | 6 ++++-- 4 files changed, 29 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index 3855edc51..96dc5e476 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,14 @@ +2012-01-03 Wolfgang Sourdeau + + * SoObjects/SOGo/SOGoUser.m (-authenticationSource): new method + that returned the SOGoSource instance that successfully recognized + the user represented by the current instance. + + * SoObjects/SOGo/SOGoUserManager.m + (_fillContactInfosForUser:withUIDorEmail:inDomain:): we now set + the identifier of the source that authenticated the specified user + as the "SOGoSource" entry of the returned dictionary. + 2011-12-30 Wolfgang Sourdeau * SoObjects/SOGo/LDAPSourceSchema.[hm]: new class module enabling diff --git a/SoObjects/SOGo/SOGoUser.h b/SoObjects/SOGo/SOGoUser.h index 7400021a9..7e7f0982e 100644 --- a/SoObjects/SOGo/SOGoUser.h +++ b/SoObjects/SOGo/SOGoUser.h @@ -52,6 +52,8 @@ @class SOGoUserProfile; @class SOGoUserSettings; +@protocol SOGoSource; + @interface SOGoUser : SoUser { SOGoUserDefaults *_defaults; @@ -88,6 +90,7 @@ /* properties */ - (NSString *) domain; +- (id ) authenticationSource; - (NSArray *) allEmails; - (BOOL) hasEmail: (NSString *) email; diff --git a/SoObjects/SOGo/SOGoUser.m b/SoObjects/SOGo/SOGoUser.m index 4d7aa6c67..ba4c0e471 100644 --- a/SoObjects/SOGo/SOGoUser.m +++ b/SoObjects/SOGo/SOGoUser.m @@ -287,6 +287,17 @@ return [self _fetchFieldForUser: @"c_domain"]; } +- (id ) authenticationSource +{ + NSString *sourceID; + SOGoUserManager *um; + + sourceID = [self _fetchFieldForUser: @"SOGoSource"]; + um = [SOGoUserManager sharedUserManager]; + + return [um sourceWithID: sourceID]; +} + - (NSArray *) allEmails { if (!allEmails) diff --git a/SoObjects/SOGo/SOGoUserManager.m b/SoObjects/SOGo/SOGoUserManager.m index 6d5e1415e..336585ba6 100644 --- a/SoObjects/SOGo/SOGoUserManager.m +++ b/SoObjects/SOGo/SOGoUserManager.m @@ -573,7 +573,7 @@ NSMutableArray *emails; NSDictionary *userEntry; NSEnumerator *sogoSources; - NSObject *currentSource; + NSObject *currentSource; NSString *sourceID, *cn, *c_domain, *c_uid, *c_imaphostname, *c_imaplogin; NSArray *c_emails; BOOL access; @@ -592,12 +592,14 @@ sogoSources = [[self authenticationSourceIDsInDomain: domain] objectEnumerator]; - while ((sourceID = [sogoSources nextObject])) + userEntry = nil; + while (!userEntry && (sourceID = [sogoSources nextObject])) { currentSource = [_sources objectForKey: sourceID]; userEntry = [currentSource lookupContactEntryWithUIDorEmail: uid]; if (userEntry) { + [currentUser setObject: sourceID forKey: @"SOGoSource"]; if (!cn) cn = [userEntry objectForKey: @"c_cn"]; if (!c_uid) From 1a8851fb8e997ac161297bee29dd2b18eb9e5a0c Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Tue, 3 Jan 2012 22:53:22 +0000 Subject: [PATCH 13/68] Monotone-Parent: c7009a2d8a1dce845aa1a365123f4b438e3168fe Monotone-Revision: 8fa93819d7083059128aa7103b83d47ee6a9cf2c Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-01-03T22:53:22 Monotone-Branch: ca.inverse.sogo --- SoObjects/SOGo/SOGoParentFolder.m | 3 --- 1 file changed, 3 deletions(-) diff --git a/SoObjects/SOGo/SOGoParentFolder.m b/SoObjects/SOGo/SOGoParentFolder.m index 64cf44218..08ecd35dd 100644 --- a/SoObjects/SOGo/SOGoParentFolder.m +++ b/SoObjects/SOGo/SOGoParentFolder.m @@ -183,7 +183,6 @@ static SoSecurityManager *sm = nil; SOGoGCSFolder *folder; NSString *key; NSException *error; - SOGoUser *currentUser; if (!subFolderClass) subFolderClass = [[self class] subFolderClass]; @@ -191,8 +190,6 @@ static SoSecurityManager *sm = nil; error = [fc evaluateExpressionX: sql]; if (!error) { - currentUser = [context activeUser]; - attrs = [fc describeResults: NO]; while ((row = [fc fetchAttributes: attrs withZone: NULL])) { From cba23f9993ec9356064cbb54a595b4da53cbd54c Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Tue, 3 Jan 2012 22:53:53 +0000 Subject: [PATCH 14/68] Monotone-Parent: 8fa93819d7083059128aa7103b83d47ee6a9cf2c Monotone-Revision: 16b846dd0fb3a63bdd80cb7ed360972da407527b Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-01-03T22:53:53 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ChangeLog b/ChangeLog index 96dc5e476..601b06bf5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2012-01-03 Wolfgang Sourdeau + * SoObjects/SOGo/SOGoParentFolder.m (-appendPersonalSources): made + method public so that it can be easily overriden in subclasses. + * SoObjects/SOGo/SOGoUser.m (-authenticationSource): new method that returned the SOGoSource instance that successfully recognized the user represented by the current instance. From 15e606341e7c8b086bde8bdf5831082ec3135b84 Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Wed, 4 Jan 2012 15:26:30 +0000 Subject: [PATCH 15/68] Monotone-Parent: 16b846dd0fb3a63bdd80cb7ed360972da407527b Monotone-Revision: bb276eea8dc76b5c71f49df1d23424ab19015233 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-01-04T15:26:30 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 54 +++ SoObjects/Contacts/SOGoContactFolders.h | 4 + SoObjects/Contacts/SOGoContactFolders.m | 141 +++++++ SoObjects/Contacts/SOGoContactSourceFolder.h | 4 + SoObjects/Contacts/SOGoContactSourceFolder.m | 108 +++++- SoObjects/SOGo/LDAPSource.h | 16 +- SoObjects/SOGo/LDAPSource.m | 360 ++++++++++++++++-- SoObjects/SOGo/SOGoFolder.h | 5 + SoObjects/SOGo/SOGoFolder.m | 29 ++ SoObjects/SOGo/SOGoGCSFolder.h | 3 - SoObjects/SOGo/SOGoGCSFolder.m | 23 -- SoObjects/SOGo/SOGoParentFolder.h | 2 + SoObjects/SOGo/SOGoSource.h | 26 ++ SoObjects/SOGo/SQLSource.m | 88 +++++ UI/Contacts/UIxContactFoldersView.m | 37 +- UI/Contacts/product.plist | 5 + .../ContactsUI/UIxContactFoldersView.wox | 2 + UI/WebServerResources/ContactsUI.js | 62 +-- 18 files changed, 859 insertions(+), 110 deletions(-) diff --git a/ChangeLog b/ChangeLog index 601b06bf5..092566ba1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,57 @@ +2012-01-04 Wolfgang Sourdeau + + * UI/WebServerResources/ContactsUI.js + (onAddressBooksMenuPrepareVisibility): the "new list", "sharing" and "import" + options are now greyed out properly, depending on the object type + and the new attributes below. + + * UI/Contacts/UIxContactFoldersView.m + (-currentContactFolderAclEditing) + (-currentContactFolderListEditing): new attribute accessors. + + * SoObjects/SOGo/SOGoFolder.m (-sendFolderAdvisoryTemplate:): + moved method from SOGoGCSFolder in order to make it available to + other folder classes. + + * SoObjects/SOGo/LDAPSource.m (-initFromUDSource:inDomain:): use + the new setters for certain ivars + take the new "abOU" key into + account. + (-setListRequiresDot:, -listRequiresDot:, -setSourceID:) + (-setDisplayName, -displayName, -setModifiers:): new accessors. + (-_convertRecordToLDAPAttributes): we now strip object classes that + are not supported by the server prior to remove the related fields. + (-hasUserAddressBooks): new method that returns whether user + addressbooks are supported, i.e. when "abOU" is set. + (-addressBookSourcesForUser:): when "abOU" is set, returns an + array of LDAPSource instances representing the personal + addressbooks of the specified user. + (-addAddressBookSource:withDisplayName:forUser:) + (-renameAddressBookSource:withDisplayName:forUser:) + (-removeAddressBookSource:forUser:): new methods with a + self-explicit name. + + * SoObjects/Contacts/SOGoContactSourceFolder.m + (-setIsPersonalSource, -isPersonalSource): new accessors for the + "isPersonalSource ivar". + (-lookupName:inContext:acquire:): setup the object classes of the + new entries to "inetorgperson" and "mozillaabpersonalpha". + (-lookupContactsWithFilter:onCriteria:sortBy:ordering:): check + whether "listRequiresDot" is set on the current source and return + the full listing if not required. + (-compare:): enhanced to treat personal sources as if they were + regular GCS folders, in order to sort them properly. + (-delete, -renameTo:): implemented method, required for the + corresponding web methods. + (-ownerInContext:) adapted method to personal sources. + + * SoObjects/Contacts/SOGoContactFolders.m (-appendPersonalSource): + overriden method for returning LDAP-based user addresbook sources. + (-newFolderWithName:andNameInContainer:): idem + (-renameLDAPAddressBook:withDisplayName:): new method that enables + the renaming of LDAP-based user addresbook sources. + (-removeLDAPAddressBook:): new method that enables + the removal of LDAP-based user addresbook sources. + 2012-01-03 Wolfgang Sourdeau * SoObjects/SOGo/SOGoParentFolder.m (-appendPersonalSources): made diff --git a/SoObjects/Contacts/SOGoContactFolders.h b/SoObjects/Contacts/SOGoContactFolders.h index 169046b67..820efb92d 100644 --- a/SoObjects/Contacts/SOGoContactFolders.h +++ b/SoObjects/Contacts/SOGoContactFolders.h @@ -27,6 +27,10 @@ @interface SOGoContactFolders : SOGoParentFolder +- (NSException *) renameLDAPAddressBook: (NSString *) sourceID + withDisplayName: (NSString *) newDisplayName; +- (NSException *) removeLDAPAddressBook: (NSString *) sourceID; + @end #endif /* SOGOCONTACTFOLDERS_H */ diff --git a/SoObjects/Contacts/SOGoContactFolders.m b/SoObjects/Contacts/SOGoContactFolders.m index f0f3574f3..cb00f7387 100644 --- a/SoObjects/Contacts/SOGoContactFolders.m +++ b/SoObjects/Contacts/SOGoContactFolders.m @@ -33,10 +33,12 @@ #import #import +#import #import #import #import +#import #import #import #import @@ -45,6 +47,7 @@ #import "SOGoContactGCSFolder.h" #import "SOGoContactSourceFolder.h" + #import "SOGoContactFolders.h" #define XMLNS_INVERSEDAV @"urn:inverse:params:xml:ns:inverse-dav" @@ -60,6 +63,62 @@ return [SOGoContactGCSFolder class]; } +- (void) _fetchLDAPAddressBooks: (id ) source +{ + id abSource; + SOGoContactSourceFolder *folder; + NSArray *abSources; + NSUInteger count, max; + NSString *name; + + abSources = [source addressBookSourcesForUser: owner]; + max = [abSources count]; + for (count = 0; count < max; count++) + { + abSource = [abSources objectAtIndex: count]; + name = [abSource sourceID]; + folder = [SOGoContactSourceFolder folderWithName: name + andDisplayName: [abSource displayName] + inContainer: self]; + [folder setSource: abSource]; + [folder setIsPersonalSource: YES]; + [subFolders setObject: folder forKey: name]; + } +} + +- (NSException *) appendPersonalSources +{ + SOGoUser *currentUser; + NSException *result; + id source; + + currentUser = [context activeUser]; + source = [currentUser authenticationSource]; + if ([source hasUserAddressBooks]) + { + result = nil; + /* We don't handle ACLs for user LDAP addressbooks yet, therefore only + the owner has access to his addressbooks. */ + if (activeUserIsOwner + || [[currentUser login] isEqualToString: owner]) + { + [self _fetchLDAPAddressBooks: source]; + if (![subFolders objectForKey: @"personal"]) + { + result = [source addAddressBookSource: @"personal" + withDisplayName: [self defaultFolderName] + forUser: owner]; + if (!result) + [self _fetchLDAPAddressBooks: source]; + } + } + } + else + result = [super appendPersonalSources]; + + return result; +} + - (NSException *) appendSystemSources { SOGoUserManager *um; @@ -101,6 +160,88 @@ return nil; } + +- (NSException *) newFolderWithName: (NSString *) name + andNameInContainer: (NSString *) newNameInContainer +{ + SOGoUser *currentUser; + NSException *result; + id source; + + currentUser = [context activeUser]; + source = [currentUser authenticationSource]; + if ([source hasUserAddressBooks]) + { + result = nil; + /* We don't handle ACLs for user LDAP addressbooks yet, therefore only + the owner has access to his addressbooks. */ + if (activeUserIsOwner + || [[currentUser login] isEqualToString: owner]) + { + result = [source addAddressBookSource: newNameInContainer + withDisplayName: name + forUser: owner]; + if (!result) + [self _fetchLDAPAddressBooks: source]; + } + } + else + result = [super newFolderWithName: name + andNameInContainer: newNameInContainer]; + + return result; +} + +- (NSException *) renameLDAPAddressBook: (NSString *) sourceID + withDisplayName: (NSString *) newDisplayName +{ + NSException *result; + SOGoUser *currentUser; + id source; + + currentUser = [context activeUser]; + source = [currentUser authenticationSource]; + /* We don't handle ACLs for user LDAP addressbooks yet, therefore only + the owner has access to his addressbooks. */ + if (activeUserIsOwner + || [[currentUser login] isEqualToString: owner]) + result = [source renameAddressBookSource: sourceID + withDisplayName: newDisplayName + forUser: owner]; + else + result = [NSException exceptionWithHTTPStatus: 403 + reason: @"operation denied"]; + + return result; +} + +- (NSException *) removeLDAPAddressBook: (NSString *) sourceID +{ + NSException *result; + SOGoUser *currentUser; + id source; + + if ([sourceID isEqualToString: @"personal"]) + result = [NSException exceptionWithHTTPStatus: 403 + reason: @"folder 'personal' cannot be deleted"]; + else + { + result = nil; + currentUser = [context activeUser]; + source = [currentUser authenticationSource]; + /* We don't handle ACLs for user LDAP addressbooks yet, therefore only + the owner has access to his addressbooks. */ + if (activeUserIsOwner + || [[currentUser login] isEqualToString: owner]) + result = [source removeAddressBookSource: sourceID + forUser: owner]; + else + result = [NSException exceptionWithHTTPStatus: 403 + reason: @"operation denied"]; + } + + return result; +} - (NSString *) defaultFolderName { diff --git a/SoObjects/Contacts/SOGoContactSourceFolder.h b/SoObjects/Contacts/SOGoContactSourceFolder.h index 09effd47d..d37b1c631 100644 --- a/SoObjects/Contacts/SOGoContactSourceFolder.h +++ b/SoObjects/Contacts/SOGoContactSourceFolder.h @@ -35,6 +35,7 @@ { id source; NSMutableDictionary *childRecords; + BOOL isPersonalSource; } + (id) folderWithName: (NSString *) aName @@ -48,6 +49,9 @@ - (NSException *) saveLDIFEntry: (SOGoContactLDIFEntry *) ldifEntry; - (NSException *) deleteLDIFEntry: (SOGoContactLDIFEntry *) ldifEntry; +- (void) setIsPersonalSource: (BOOL) isPersonal; +- (BOOL) isPersonalSource; + @end diff --git a/SoObjects/Contacts/SOGoContactSourceFolder.m b/SoObjects/Contacts/SOGoContactSourceFolder.m index ba576aa8f..2b7c942da 100644 --- a/SoObjects/Contacts/SOGoContactSourceFolder.m +++ b/SoObjects/Contacts/SOGoContactSourceFolder.m @@ -37,12 +37,16 @@ #import #import -#import -#import #import #import #import +#import +#import +#import +#import +#import "SOGoContactFolders.h" +#import "SOGoContactGCSFolder.h" #import "SOGoContactLDIFEntry.h" #import "SOGoContactSourceFolder.h" @@ -107,6 +111,16 @@ return source; } +- (void) setIsPersonalSource: (BOOL) isPersonal +{ + isPersonalSource = isPersonal; +} + +- (BOOL) isPersonalSource +{ + return isPersonalSource; +} + - (NSString *) groupDavResourceType { return @"vcard-collection"; @@ -135,6 +149,7 @@ SOGoContactLDIFEntry *obj; NSString *url; BOOL isNew = NO; + NSArray *baseClasses; /* first check attributes directly bound to the application */ obj = [super lookupName: objectName inContext: lookupContext acquire: NO]; @@ -152,7 +167,11 @@ url = [[[lookupContext request] uri] urlWithoutParameters]; if ([url hasSuffix: @"AsContact"]) { - ldifEntry = [NSMutableDictionary dictionary]; + baseClasses = [NSArray arrayWithObjects: @"inetorgperson", + @"mozillaabpersonalpha", nil]; + ldifEntry = [NSMutableDictionary + dictionaryWithObject: baseClasses + forKey: @"objectclass"]; isNew = YES; } } @@ -294,7 +313,8 @@ result = nil; - if ([filter length] > 0 && [criteria isEqualToString: @"name_or_address"]) + if (([filter length] > 0 && [criteria isEqualToString: @"name_or_address"]) + || ![source listRequiresDot]) { records = [source fetchContactsMatching: filter]; [childRecords setObjects: records @@ -335,22 +355,88 @@ - (NSComparisonResult) compare: (id) otherFolder { NSComparisonResult comparison; + BOOL otherIsPersonal; - if ([NSStringFromClass([otherFolder class]) - isEqualToString: @"SOGoContactGCSFolder"]) - comparison = NSOrderedDescending; + otherIsPersonal = ([otherFolder isKindOfClass: [SOGoContactGCSFolder class]] + || ([otherFolder isKindOfClass: isa] && [otherFolder isPersonalSource])); + + if (isPersonalSource) + { + if (otherIsPersonal && ![nameInContainer isEqualToString: @"personal"]) + { + if ([[otherFolder nameInContainer] isEqualToString: @"personal"]) + comparison = NSOrderedDescending; + else + comparison + = [[self displayName] + localizedCaseInsensitiveCompare: [otherFolder displayName]]; + } + else + comparison = NSOrderedAscending; + } else - comparison - = [[self displayName] - localizedCaseInsensitiveCompare: [otherFolder displayName]]; + { + if (otherIsPersonal) + comparison = NSOrderedDescending; + else + comparison + = [[self displayName] + localizedCaseInsensitiveCompare: [otherFolder displayName]]; + } return comparison; } +/* common methods */ + +- (NSException *) delete +{ + NSException *error; + + if (isPersonalSource) + { + error = [(SOGoContactFolders *) container + removeLDAPAddressBook: nameInContainer]; + if (!error && [[context request] handledByDefaultHandler]) + [self sendFolderAdvisoryTemplate: @"Removal"]; + } + else + error = [NSException exceptionWithHTTPStatus: 501 /* not implemented */ + reason: @"delete not available on system sources"]; + + return error; +} + +- (void) renameTo: (NSString *) newName +{ + NSException *error; + + if (isPersonalSource) + { + if (![[source displayName] isEqualToString: newName]) + { + error = [(SOGoContactFolders *) container + renameLDAPAddressBook: nameInContainer + withDisplayName: newName]; + if (!error) + [self setDisplayName: newName]; + } + } + /* If public source then method is ignored, maybe we should return an + NSException instead... */ +} + /* acls */ - (NSString *) ownerInContext: (WOContext *) noContext { - return @"nobody"; + NSString *sourceOwner; + + if (isPersonalSource) + sourceOwner = [[source modifiers] objectAtIndex: 0]; + else + sourceOwner = @"nobody"; + + return sourceOwner; } - (NSArray *) subscriptionRoles diff --git a/SoObjects/SOGo/LDAPSource.h b/SoObjects/SOGo/LDAPSource.h index 64fd9c02d..821dfec5d 100644 --- a/SoObjects/SOGo/LDAPSource.h +++ b/SoObjects/SOGo/LDAPSource.h @@ -31,10 +31,11 @@ #include "SOGoConstants.h" @class LDAPSourceSchema; -@class NSDictionary; -@class NSString; -@class NGLdapConnection; @class NGLdapEntry; +@class NSException; +@class NSMutableArray; +@class NSMutableDictionary; +@class NSString; @interface LDAPSource : NSObject { @@ -42,6 +43,8 @@ int queryTimeout; NSString *sourceID; + NSString *displayName; + NSString *bindDN; // The bindDN/password could be either the source's one NSString *password; // or the current user if _bindAsCurrentUser is set to YES NSString *sourceBindDN; // while sourceBindDN/sourceBindPassword always belong to the source @@ -50,6 +53,7 @@ unsigned int port; NSString *encryption; NSString *_filter; + BOOL _bindAsCurrentUser; NSString *_scope; NSString *baseDN; @@ -60,7 +64,8 @@ NSArray *mailFields, *searchFields; NSString *IMAPHostField, *IMAPLoginField; NSArray *bindFields; - BOOL _bindAsCurrentUser; + + BOOL listRequiresDot; NSString *domain; NSString *contactInfoAttribute; @@ -80,6 +85,9 @@ NSString *kindField; NSString *multipleBookingsField; + /* user addressbooks */ + NSString *abOU; + /* ACL */ NSArray *modifiers; } diff --git a/SoObjects/SOGo/LDAPSource.m b/SoObjects/SOGo/LDAPSource.m index 27ee22fe8..a36eeb48c 100644 --- a/SoObjects/SOGo/LDAPSource.m +++ b/SoObjects/SOGo/LDAPSource.m @@ -26,6 +26,7 @@ #import #import +#import #import #import @@ -42,7 +43,6 @@ #import "SOGoSystemDefaults.h" #import "LDAPSource.h" - #import "../../Main/SOGo.h" static Class NSStringK; @@ -139,6 +139,9 @@ static Class NSStringK; { if ((self = [super init])) { + sourceID = nil; + displayName = nil; + bindDN = nil; password = nil; sourceBindDN = nil; @@ -146,7 +149,6 @@ static Class NSStringK; hostname = nil; port = 389; encryption = nil; - sourceID = nil; domain = nil; baseDN = nil; @@ -164,6 +166,7 @@ static Class NSStringK; bindFields = nil; _scope = @"sub"; _filter = nil; + listRequiresDot = YES; searchAttributes = nil; passwordPolicy = NO; @@ -220,11 +223,12 @@ static Class NSStringK; inDomain: (NSString *) sourceDomain { SOGoDomainDefaults *dd; - NSNumber *udQueryLimit, *udQueryTimeout; + NSNumber *udQueryLimit, *udQueryTimeout, *dotValue; if ((self = [self init])) { - ASSIGN(sourceID, [udSource objectForKey: @"id"]); + [self setSourceID: [udSource objectForKey: @"id"]]; + [self setDisplayName: [udSource objectForKey: @"displayName"]]; [self setBindDN: [udSource objectForKey: @"bindDN"] password: [udSource objectForKey: @"bindPassword"] @@ -245,9 +249,15 @@ static Class NSStringK; kindField: [udSource objectForKey: @"KindFieldName"] andMultipleBookingsField: [udSource objectForKey: @"MultipleBookingsFieldName"]]; + dotValue = [udSource objectForKey: @"listRequiresDot"]; + if (dotValue) + [self setListRequiresDot: [dotValue boolValue]]; [self setContactMapping: [udSource objectForKey: @"mapping"] andObjectClasses: [udSource objectForKey: @"objectClasses"]]; + [self setModifiers: [udSource objectForKey: @"modifiers"]]; + ASSIGN (abOU, [udSource objectForKey: @"abOU"]); + if ([sourceDomain length]) { dd = [SOGoDomainDefaults defaultsForDomain: sourceDomain]; @@ -283,10 +293,8 @@ static Class NSStringK; if ([udSource objectForKey: @"passwordPolicy"]) passwordPolicy = [[udSource objectForKey: @"passwordPolicy"] boolValue]; - - ASSIGN (modifiers, [udSource objectForKey: @"modifiers"]); } - + return self; } @@ -386,6 +394,16 @@ andMultipleBookingsField: (NSString *) newMultipleBookingsField ASSIGN(multipleBookingsField, [newMultipleBookingsField lowercaseString]); } +- (void) setListRequiresDot: (BOOL) aBool +{ + listRequiresDot = aBool; +} + +- (BOOL) listRequiresDot +{ + return listRequiresDot; +} + - (void) setContactMapping: (NSDictionary *) newMapping andObjectClasses: (NSArray *) newObjectClasses { @@ -438,6 +456,11 @@ andMultipleBookingsField: (NSString *) newMultipleBookingsField [ldapConnection setQuerySizeLimit: queryLimit]; if (queryTimeout > 0) [ldapConnection setQueryTimeLimit: queryTimeout]; + if (!schema) + { + schema = [LDAPSourceSchema new]; + [schema readSchemaFromConnection: ldapConnection]; + } } else ldapConnection = nil; @@ -691,11 +714,18 @@ andMultipleBookingsField: (NSString *) newMultipleBookingsField [qs appendString: searchFormat]; } - if (_filter && [_filter length]) + if ([_filter length]) [qs appendFormat: @" AND %@", _filter]; qualifier = [EOQualifier qualifierWithQualifierFormat: qs]; } + else if (!listRequiresDot) + { + qs = [NSMutableString stringWithFormat: @"(%@='*')", CNField]; + if ([_filter length]) + [qs appendFormat: @" AND %@", _filter]; + qualifier = [EOQualifier qualifierWithQualifierFormat: qs]; + } else qualifier = nil; @@ -1058,7 +1088,7 @@ andMultipleBookingsField: (NSString *) newMultipleBookingsField contacts = [NSMutableArray array]; - if ([match length] > 0) + if ([match length] > 0 || !listRequiresDot) { ldapConnection = [self _ldapConnection]; qualifier = [self _qualifierForFilter: match]; @@ -1093,11 +1123,6 @@ andMultipleBookingsField: (NSString *) newMultipleBookingsField // attributes = [self _searchAttributes]; ldapConnection = [self _ldapConnection]; - if (!schema) - { - schema = [LDAPSourceSchema new]; - [schema readSchemaFromConnection: ldapConnection]; - } attributes = [NSArray arrayWithObject: @"*"]; if ([_scope caseInsensitiveCompare: @"BASE"] == NSOrderedSame) @@ -1221,16 +1246,36 @@ andMultipleBookingsField: (NSString *) newMultipleBookingsField return ldapEntry; } +- (void) setSourceID: (NSString *) newSourceID +{ + ASSIGN (sourceID, newSourceID); +} + - (NSString *) sourceID { return sourceID; } +- (void) setDisplayName: (NSString *) newDisplayName +{ + ASSIGN (displayName, newDisplayName); +} + +- (NSString *) displayName +{ + return displayName; +} + - (NSString *) baseDN { return baseDN; } +- (void) setModifiers: (NSArray *) newModifiers +{ + ASSIGN (modifiers, newModifiers); +} + - (NSArray *) modifiers { return modifiers; @@ -1240,33 +1285,50 @@ static NSArray * _convertRecordToLDAPAttributes (LDAPSourceSchema *schema, NSDictionary *ldifRecord) { /* convert resulting record to NGLdapEntry: + - strip non-existing object classes - ignore fields with empty values - ignore extra fields - use correct case for LDAP attribute matching classes */ - NSMutableArray *attributes; + NSMutableArray *validClasses, *validFields, *attributes; NGLdapAttribute *attribute; NSArray *classes, *fields, *values; - NSString *field, *lowerField, *value; + NSString *objectClass, *field, *lowerField, *value; NSUInteger count, max, valueCount, valueMax; - attributes = [NSMutableArray new]; - classes = [ldifRecord objectForKey: @"objectclass"]; if ([classes isKindOfClass: NSStringK]) classes = [NSArray arrayWithObject: classes]; - fields = [schema fieldsForClasses: classes]; + max = [classes count]; + validClasses = [NSMutableArray array]; + validFields = [NSMutableArray array]; + for (count = 0; count < max; count++) + { + objectClass = [classes objectAtIndex: count]; + fields = [schema fieldsForClass: objectClass]; + if ([fields count] > 0) + { + [validClasses addObject: objectClass]; + [validFields addObjectsFromArray: fields]; + } + } - max = [fields count]; + attributes = [NSMutableArray new]; + max = [validFields count]; for (count = 0; count < max; count++) { attribute = nil; - field = [fields objectAtIndex: count]; + field = [validFields objectAtIndex: count]; lowerField = [field lowercaseString]; if (![lowerField isEqualToString: @"dn"]) { - values = [ldifRecord objectForKey: lowerField]; - if ([values isKindOfClass: NSStringK]) - values = [NSArray arrayWithObject: values]; + if ([lowerField isEqualToString: @"objectclass"]) + values = validClasses; + else + { + values = [ldifRecord objectForKey: lowerField]; + if ([values isKindOfClass: NSStringK]) + values = [NSArray arrayWithObject: values]; + } valueMax = [values count]; for (valueCount = 0; valueCount < valueMax; valueCount++) { @@ -1292,7 +1354,7 @@ _convertRecordToLDAPAttributes (LDAPSourceSchema *schema, NSDictionary *ldifReco - (NSException *) addContactEntry: (NSDictionary *) roLdifRecord withID: (NSString *) aId { - NSException *result = nil; + NSException *result; NGLdapEntry *newEntry; NSMutableDictionary *ldifRecord; NSArray *attributes; @@ -1302,12 +1364,6 @@ _convertRecordToLDAPAttributes (LDAPSourceSchema *schema, NSDictionary *ldifReco if ([aId length] > 0) { ldapConnection = [self _ldapConnection]; - if (!schema) - { - schema = [LDAPSourceSchema new]; - [schema readSchemaFromConnection: ldapConnection]; - } - ldifRecord = [roLdifRecord mutableCopy]; [ldifRecord autorelease]; [ldifRecord setObject: aId forKey: UIDField]; @@ -1336,6 +1392,7 @@ _convertRecordToLDAPAttributes (LDAPSourceSchema *schema, NSDictionary *ldifReco NS_DURING { [ldapConnection addEntry: newEntry]; + result = nil; } NS_HANDLER { @@ -1413,7 +1470,7 @@ _makeLDAPChanges (NGLdapConnection *ldapConnection, - (NSException *) updateContactEntry: (NSDictionary *) roLdifRecord { - NSException *result = nil; + NSException *result; NSString *dn; NSMutableDictionary *ldifRecord; NSArray *attributes, *changes; @@ -1423,12 +1480,6 @@ _makeLDAPChanges (NGLdapConnection *ldapConnection, if ([dn length] > 0) { ldapConnection = [self _ldapConnection]; - if (!schema) - { - schema = [LDAPSourceSchema new]; - [schema readSchemaFromConnection: ldapConnection]; - } - ldifRecord = [roLdifRecord mutableCopy]; [ldifRecord autorelease]; [self _applyContactMappingToOutput: ldifRecord]; @@ -1440,6 +1491,7 @@ _makeLDAPChanges (NGLdapConnection *ldapConnection, { [ldapConnection modifyEntryWithDN: dn changes: changes]; + result = nil; } NS_HANDLER { @@ -1455,7 +1507,7 @@ _makeLDAPChanges (NGLdapConnection *ldapConnection, - (NSException *) removeContactEntryWithID: (NSString *) aId { - NSException *result = nil; + NSException *result; NGLdapConnection *ldapConnection; NSString *dn; @@ -1464,6 +1516,7 @@ _makeLDAPChanges (NGLdapConnection *ldapConnection, NS_DURING { [ldapConnection removeEntryWithDN: dn]; + result = nil; } NS_HANDLER { @@ -1474,4 +1527,233 @@ _makeLDAPChanges (NGLdapConnection *ldapConnection, return result; } +/* user addressbooks */ +- (BOOL) hasUserAddressBooks +{ + return ([abOU length] > 0); +} + +- (NSArray *) addressBookSourcesForUser: (NSString *) user +{ + NSMutableArray *sources; + NSString *abBaseDN; + NGLdapConnection *ldapConnection; + NSArray *attributes, *modifier; + NSEnumerator *entries; + NGLdapEntry *entry; + NSMutableDictionary *entryRecord; + NSDictionary *sourceRec; + LDAPSource *ab; + + if ([self hasUserAddressBooks]) + { + /* list subentries */ + sources = [NSMutableArray array]; + + ldapConnection = [self _ldapConnection]; + abBaseDN = [NSString stringWithFormat: @"ou=%@,%@=%@,%@", abOU, IDField, user, baseDN]; + + /* test ou=addressbooks entry */ + attributes = [NSArray arrayWithObject: @"*"]; + entries = [ldapConnection baseSearchAtBaseDN: abBaseDN + qualifier: nil + attributes: attributes]; + entry = [entries nextObject]; + if (entry) + { + attributes = [NSArray arrayWithObjects: @"ou", @"description", nil]; + entries = [ldapConnection flatSearchAtBaseDN: abBaseDN + qualifier: nil + attributes: attributes]; + modifier = [NSArray arrayWithObject: user]; + while ((entry = [entries nextObject])) + { + sourceRec = [entry _asDictionary]; + ab = [LDAPSource new]; + [ab setSourceID: [sourceRec objectForKey: @"ou"]]; + [ab setDisplayName: [sourceRec objectForKey: @"description"]]; + [ab setBindDN: bindDN + password: password + hostname: hostname + port: [NSString stringWithFormat: @"%d", port] + encryption: encryption + bindAsCurrentUser: NO]; + [ab setBaseDN: [entry dn] + IDField: @"cn" + CNField: @"displayName" + UIDField: @"cn" + mailFields: nil + searchFields: nil + IMAPHostField: nil + IMAPLoginField: nil + bindFields: nil + kindField: nil + andMultipleBookingsField: nil]; + [ab setListRequiresDot: NO]; + [ab setModifiers: modifier]; + [sources addObject: ab]; + [ab release]; + } + } + else + { + entryRecord = [NSMutableDictionary dictionary]; + [entryRecord setObject: @"organizationalUnit" forKey: @"objectclass"]; + [entryRecord setObject: @"addressbooks" forKey: @"ou"]; + attributes = _convertRecordToLDAPAttributes (schema, entryRecord); + entry = [[NGLdapEntry alloc] initWithDN: abBaseDN + attributes: attributes]; + [entry autorelease]; + [attributes release]; + NS_DURING + { + [ldapConnection addEntry: entry]; + } + NS_HANDLER + { + [self errorWithFormat: @"failed to create ou=addressbooks" + @" entry for user"]; + } + NS_ENDHANDLER; + } + } + else + sources = nil; + + return sources; +} + +- (NSException *) addAddressBookSource: (NSString *) newId + withDisplayName: (NSString *) newDisplayName + forUser: (NSString *) user +{ + NSException *result; + NSString *abDN; + NGLdapConnection *ldapConnection; + NSArray *attributes; + NGLdapEntry *entry; + NSMutableDictionary *entryRecord; + + if ([self hasUserAddressBooks]) + { + abDN = [NSString stringWithFormat: @"ou=%@,ou=%@,%@=%@,%@", + newId, abOU, IDField, user, baseDN]; + entryRecord = [NSMutableDictionary dictionary]; + [entryRecord setObject: @"organizationalUnit" forKey: @"objectclass"]; + [entryRecord setObject: newId forKey: @"ou"]; + if ([newDisplayName length] > 0) + [entryRecord setObject: newDisplayName forKey: @"description"]; + ldapConnection = [self _ldapConnection]; + attributes = _convertRecordToLDAPAttributes (schema, entryRecord); + entry = [[NGLdapEntry alloc] initWithDN: abDN + attributes: attributes]; + [entry autorelease]; + [attributes release]; + NS_DURING + { + [ldapConnection addEntry: entry]; + result = nil; + } + NS_HANDLER + { + [self errorWithFormat: @"failed to create addressbook entry"]; + result = localException; + } + NS_ENDHANDLER; + } + else + result = [NSException exceptionWithName: @"LDAPSourceIOException" + reason: @"user addressbooks" + @" are not supported" + userInfo: nil]; + + return result; +} + +- (NSException *) renameAddressBookSource: (NSString *) newId + withDisplayName: (NSString *) newDisplayName + forUser: (NSString *) user +{ + NSException *result; + NSString *abDN; + NGLdapConnection *ldapConnection; + NSArray *attributes, *changes; + NSMutableDictionary *entryRecord; + + if ([self hasUserAddressBooks]) + { + abDN = [NSString stringWithFormat: @"ou=%@,ou=%@,%@=%@,%@", + newId, abOU, IDField, user, baseDN]; + entryRecord = [NSMutableDictionary dictionary]; + [entryRecord setObject: @"organizationalUnit" forKey: @"objectclass"]; + [entryRecord setObject: newId forKey: @"ou"]; + if ([newDisplayName length] > 0) + [entryRecord setObject: newDisplayName forKey: @"description"]; + ldapConnection = [self _ldapConnection]; + attributes = _convertRecordToLDAPAttributes (schema, entryRecord); + changes = _makeLDAPChanges (ldapConnection, abDN, attributes); + [attributes release]; + NS_DURING + { + [ldapConnection modifyEntryWithDN: abDN + changes: changes]; + result = nil; + } + NS_HANDLER + { + [self errorWithFormat: @"failed to rename addressbook entry"]; + result = localException; + } + NS_ENDHANDLER; + } + else + result = [NSException exceptionWithName: @"LDAPSourceIOException" + reason: @"user addressbooks" + @" are not supported" + userInfo: nil]; + + return result; +} + +- (NSException *) removeAddressBookSource: (NSString *) newId + forUser: (NSString *) user +{ + NSException *result; + NSString *abDN; + NGLdapConnection *ldapConnection; + NSEnumerator *entries; + NGLdapEntry *entry; + + if ([self hasUserAddressBooks]) + { + abDN = [NSString stringWithFormat: @"ou=%@,ou=%@,%@=%@,%@", + newId, abOU, IDField, user, baseDN]; + ldapConnection = [self _ldapConnection]; + NS_DURING + { + /* we must remove the ab sub=entries prior to the ab entry */ + entries = [ldapConnection flatSearchAtBaseDN: abDN + qualifier: nil + attributes: nil]; + while ((entry = [entries nextObject])) + [ldapConnection removeEntryWithDN: [entry dn]]; + [ldapConnection removeEntryWithDN: abDN]; + result = nil; + } + NS_HANDLER + { + [self errorWithFormat: @"failed to remove addressbook entry"]; + result = localException; + } + NS_ENDHANDLER; + } + else + result = [NSException exceptionWithName: @"LDAPSourceIOException" + reason: @"user addressbooks" + @" are not supported" + userInfo: nil]; + + return result; +} + @end diff --git a/SoObjects/SOGo/SOGoFolder.h b/SoObjects/SOGo/SOGoFolder.h index cc97cf153..d51076805 100644 --- a/SoObjects/SOGo/SOGoFolder.h +++ b/SoObjects/SOGo/SOGoFolder.h @@ -25,6 +25,8 @@ #import "SOGoObject.h" +@class NSString; + @interface SOGoFolder : SOGoObject { NSMutableString *displayName; @@ -55,6 +57,9 @@ /* outlook */ - (NSString *) outlookFolderClass; +/* email advisories */ +- (void) sendFolderAdvisoryTemplate: (NSString *) template; + @end @interface SOGoFolder (GroupDAVExtensions) diff --git a/SoObjects/SOGo/SOGoFolder.m b/SoObjects/SOGo/SOGoFolder.m index 5587f18b8..adb8e4c4c 100644 --- a/SoObjects/SOGo/SOGoFolder.m +++ b/SoObjects/SOGo/SOGoFolder.m @@ -27,6 +27,7 @@ #import #import +#import #import #import #import @@ -37,12 +38,16 @@ #import +#import + #import "DOMNode+SOGo.h" #import "NSArray+Utilities.h" #import "NSObject+DAV.h" #import "NSString+DAV.h" #import "NSString+Utilities.h" #import "SOGoPermissions.h" +#import "SOGoUser.h" +#import "SOGoDomainDefaults.h" #import "SOGoWebDAVAclManager.h" #import "WORequest+SOGo.h" #import "WOResponse+SOGo.h" @@ -248,6 +253,30 @@ return comparison; } +/* email advisories */ + +- (void) sendFolderAdvisoryTemplate: (NSString *) template +{ + NSString *pageName; + SOGoUser *user; + SOGoFolderAdvisory *page; + NSString *language; + + user = [SOGoUser userWithLogin: [self ownerInContext: context]]; + if ([[user domainDefaults] foldersSendEMailNotifications]) + { + language = [[user userDefaults] language]; + pageName = [NSString stringWithFormat: @"SOGoFolder%@%@Advisory", + language, template]; + + page = [[WOApplication application] pageWithName: pageName + inContext: context]; + [page setFolderObject: self]; + [page setRecipientUID: [user login]]; + [page send]; + } +} + /* WebDAV */ - (NSString *) davEntityTag diff --git a/SoObjects/SOGo/SOGoGCSFolder.h b/SoObjects/SOGo/SOGoGCSFolder.h index 69c641020..b4575b815 100644 --- a/SoObjects/SOGo/SOGoGCSFolder.h +++ b/SoObjects/SOGo/SOGoGCSFolder.h @@ -118,9 +118,6 @@ - (void) removeAclsForUsers: (NSArray *) users forObjectAtPath: (NSArray *) objectPathArray; -/* advisories */ -- (void) sendFolderAdvisoryTemplate: (NSString *) template; - /* DAV */ - (NSURL *) publicDavURL; - (NSURL *) realDavURL; diff --git a/SoObjects/SOGo/SOGoGCSFolder.m b/SoObjects/SOGo/SOGoGCSFolder.m index 4b3c85070..7ba81b5df 100644 --- a/SoObjects/SOGo/SOGoGCSFolder.m +++ b/SoObjects/SOGo/SOGoGCSFolder.m @@ -52,7 +52,6 @@ #import #import #import -#import #import "NSDictionary+Utilities.h" #import "NSArray+Utilities.h" @@ -500,28 +499,6 @@ static NSArray *childRecordFields = nil; return folder; } -- (void) sendFolderAdvisoryTemplate: (NSString *) template -{ - NSString *pageName; - SOGoUser *user; - SOGoFolderAdvisory *page; - NSString *language; - - user = [SOGoUser userWithLogin: [self ownerInContext: context]]; - if ([[user domainDefaults] foldersSendEMailNotifications]) - { - language = [[user userDefaults] language]; - pageName = [NSString stringWithFormat: @"SOGoFolder%@%@Advisory", - language, template]; - - page = [[WOApplication application] pageWithName: pageName - inContext: context]; - [page setFolderObject: self]; - [page setRecipientUID: [user login]]; - [page send]; - } -} - - (BOOL) create { NSException *result; diff --git a/SoObjects/SOGo/SOGoParentFolder.h b/SoObjects/SOGo/SOGoParentFolder.h index cfd272121..278d94ac6 100644 --- a/SoObjects/SOGo/SOGoParentFolder.h +++ b/SoObjects/SOGo/SOGoParentFolder.h @@ -42,6 +42,8 @@ - (NSString *) defaultFolderName; +- (NSException *) appendPersonalSources; + - (void) setBaseOCSPath: (NSString *) newOCSPath; - (NSArray *) toManyRelationshipKeys; diff --git a/SoObjects/SOGo/SOGoSource.h b/SoObjects/SOGo/SOGoSource.h index 3c8f90380..2af5bd14d 100644 --- a/SoObjects/SOGo/SOGoSource.h +++ b/SoObjects/SOGo/SOGoSource.h @@ -41,6 +41,10 @@ - (NSString *) domain; +/* requires a "." to obtain the full list of contacts */ +- (void) setListRequiresDot: (BOOL) aBool; +- (BOOL) listRequiresDot; + - (BOOL) checkLogin: (NSString *) _login password: (NSString *) _pwd perr: (SOGoPasswordPolicyError *) _perr @@ -57,14 +61,36 @@ - (NSArray *) allEntryIDs; - (NSArray *) fetchContactsMatching: (NSString *) filter; + +- (void) setSourceID: (NSString *) newSourceID; - (NSString *) sourceID; + +- (void) setDisplayName: (NSString *) newDisplayName; +- (NSString *) displayName; + +- (void) setModifiers: (NSArray *) newModifiers; - (NSArray *) modifiers; +- (BOOL) hasUserAddressBooks; +- (NSArray *) addressBookSourcesForUser: (NSString *) user; + - (NSException *) addContactEntry: (NSDictionary *) roLdifRecord withID: (NSString *) aId; - (NSException *) updateContactEntry: (NSDictionary *) ldifRecord; - (NSException *) removeContactEntryWithID: (NSString *) aId; +/* user address books */ +- (NSArray *) addressBookSourcesForUser: (NSString *) user; + +- (NSException *) addAddressBookSource: (NSString *) newId + withDisplayName: (NSString *) newDisplayName + forUser: (NSString *) user; +- (NSException *) renameAddressBookSource: (NSString *) newId + withDisplayName: (NSString *) newDisplayName + forUser: (NSString *) user; +- (NSException *) removeAddressBookSource: (NSString *) newId + forUser: (NSString *) user; + @end @protocol SOGoDNSource diff --git a/SoObjects/SOGo/SQLSource.m b/SoObjects/SOGo/SQLSource.m index 6950e2d7e..441808915 100644 --- a/SoObjects/SOGo/SQLSource.m +++ b/SoObjects/SOGo/SQLSource.m @@ -691,13 +691,46 @@ return results; } +- (void) setSourceID: (NSString *) newSourceID +{ +} + - (NSString *) sourceID { return _sourceID; } +- (void) setDisplayName: (NSString *) newDisplayName +{ +} + +- (NSString *) displayName +{ + /* This method is only used when supporting user "source" addressbooks, + which is only supported by the LDAP backend for now. */ + return _sourceID; +} + +- (void) setListRequiresDot: (BOOL) newListRequiresDot +{ +} + +- (BOOL) listRequiresDot +{ + /* This method is not implemented for SQLSource. It must enable a mechanism + where using "." is not required to list the content of addressbooks. */ + return YES; +} + +/* card editing */ +- (void) setModifiers: (NSArray *) newModifiers +{ +} + - (NSArray *) modifiers { + /* This method is only used when supporting card editing, + which is only supported by the LDAP backend for now. */ return nil; } @@ -741,4 +774,59 @@ userInfo: nil]; } +/* user addressbooks */ +- (BOOL) hasUserAddressBooks +{ + return NO; +} + +- (NSArray *) addressBookSourcesForUser: (NSString *) user +{ + return nil; +} + +- (NSException *) addAddressBookSource: (NSString *) newId + withDisplayName: (NSString *) newDisplayName + forUser: (NSString *) user +{ + NSString *reason; + + reason = [NSString stringWithFormat: @"method '%@' is not available" + @" for class '%@'", NSStringFromSelector (_cmd), + NSStringFromClass (isa)]; + + return [NSException exceptionWithName: @"SQLSourceIOException" + reason: reason + userInfo: nil]; +} + +- (NSException *) renameAddressBookSource: (NSString *) newId + withDisplayName: (NSString *) newDisplayName + forUser: (NSString *) user +{ + NSString *reason; + + reason = [NSString stringWithFormat: @"method '%@' is not available" + @" for class '%@'", NSStringFromSelector (_cmd), + NSStringFromClass (isa)]; + + return [NSException exceptionWithName: @"SQLSourceIOException" + reason: reason + userInfo: nil]; +} + +- (NSException *) removeAddressBookSource: (NSString *) newId + forUser: (NSString *) user +{ + NSString *reason; + + reason = [NSString stringWithFormat: @"method '%@' is not available" + @" for class '%@'", NSStringFromSelector (_cmd), + NSStringFromClass (isa)]; + + return [NSException exceptionWithName: @"SQLSourceIOException" + reason: reason + userInfo: nil]; +} + @end diff --git a/UI/Contacts/UIxContactFoldersView.m b/UI/Contacts/UIxContactFoldersView.m index a9d56ac3e..e520318a7 100644 --- a/UI/Contacts/UIxContactFoldersView.m +++ b/UI/Contacts/UIxContactFoldersView.m @@ -52,8 +52,16 @@ #import "UIxContactFoldersView.h" +Class SOGoContactSourceFolderK, SOGoGCSFolderK; + @implementation UIxContactFoldersView ++ (void) initialize +{ + SOGoContactSourceFolderK = [SOGoContactSourceFolder class]; + SOGoGCSFolderK = [SOGoGCSFolder class]; +} + - (id) init { if ((self = [super init])) @@ -110,11 +118,13 @@ folders = [self clientObject]; folder = [folders lookupPersonalFolder: @"personal" ignoringRights: YES]; - - contactInfos = [folder lookupContactsWithFilter: nil - onCriteria: nil - sortBy: @"c_cn" - ordering: NSOrderedAscending]; + if (folder && [folder conformsToProtocol: @protocol (SOGoContactFolder)]) + contactInfos = [folder lookupContactsWithFilter: nil + onCriteria: nil + sortBy: @"c_cn" + ordering: NSOrderedAscending]; + else + contactInfos = nil; return contactInfos; } @@ -183,7 +193,7 @@ { folder = [folders objectAtIndex: i]; /* We first search in LDAP folders (in case of duplicated entries in GCS folders) */ - if ([folder isKindOfClass: [SOGoContactSourceFolder class]]) + if ([folder isKindOfClass: SOGoContactSourceFolderK]) [sortedFolders insertObject: folder atIndex: 0]; else [sortedFolders addObject: folder]; @@ -287,10 +297,23 @@ - (NSString *) currentContactFolderClass { - return ([currentFolder isKindOfClass: [SOGoContactSourceFolder class]] + return (([currentFolder isKindOfClass: SOGoContactSourceFolderK] + && ![currentFolder isPersonalSource]) ? @"remote" : @"local"); } +- (NSString *) currentContactFolderAclEditing +{ + return ([currentFolder isKindOfClass: SOGoGCSFolderK] + ? @"available": @"unavailable"); +} + +- (NSString *) currentContactFolderListEditing +{ + return ([currentFolder isKindOfClass: SOGoGCSFolderK] + ? @"available": @"unavailable"); +} + - (NSString *) verticalDragHandleStyle { NSString *vertical; diff --git a/UI/Contacts/product.plist b/UI/Contacts/product.plist index ca66751fe..8aadf26a2 100644 --- a/UI/Contacts/product.plist +++ b/UI/Contacts/product.plist @@ -139,6 +139,11 @@ pageName = "UIxContactEditor"; actionName = "new"; }; + renameFolder = { + protectedBy = "Change Permissions"; + actionClass = "UIxFolderActions"; + actionName = "renameFolder"; + }; mailer-contacts = { protectedBy = ""; pageName = "UIxContactFoldersView"; diff --git a/UI/Templates/ContactsUI/UIxContactFoldersView.wox b/UI/Templates/ContactsUI/UIxContactFoldersView.wox index e8f304862..a8a9a9653 100644 --- a/UI/Templates/ContactsUI/UIxContactFoldersView.wox +++ b/UI/Templates/ContactsUI/UIxContactFoldersView.wox @@ -121,6 +121,8 @@ >
  • diff --git a/UI/WebServerResources/ContactsUI.js b/UI/WebServerResources/ContactsUI.js index 89f070d94..8f1e2e656 100644 --- a/UI/WebServerResources/ContactsUI.js +++ b/UI/WebServerResources/ContactsUI.js @@ -258,17 +258,6 @@ function onContactContextMenuHide(event) { this.stopObserving("contextmenu:hide", onContactContextMenuHide); } -function onFolderMenuHide(event) { - var topNode = $('d'); - - if (topNode.menuSelectedEntry) { - topNode.menuSelectedEntry.deselect(); - topNode.menuSelectedEntry = null; - } - if (topNode.selectedEntry) - topNode.selectedEntry.selectElement(); -} - function _onContactMenuAction(folderItem, action, refresh) { var selectedFolders = $("contactFolders").getSelectedNodes(); var folderId = $(folderItem).readAttribute("folderId"); @@ -308,8 +297,9 @@ function onContactMenuMove(event) { function onMenuExportContact (event) { var selectedFolders = $("contactFolders").getSelectedNodes(); - var selectedFolderId = $(selectedFolders[0]).readAttribute("id"); - if (selectedFolderId != "/shared") { + var canExport = (selectedFolders[0].getAttribute("owner") != "nobody"); + if (canExport) { + var selectedFolderId = $(selectedFolders[0]).readAttribute("id"); var contactIds = $(document.menuTarget).collect(function(row) { return row.getAttribute("id"); }); @@ -599,10 +589,11 @@ function newContact(sender) { function newList(sender) { var li = $(Contact.currentAddressBook); - if (li.hasClassName("remote")) - showAlertDialog(_("You cannot create a list in a shared address book.")); - else + var listEditing = li.getAttribute("list-editing"); + if (listEditing && listEditing == "available") openContactWindow(URLForFolderID(Contact.currentAddressBook) + "/newlist"); + else + showAlertDialog(_("You cannot create a list in a shared address book.")); return false; } @@ -1069,15 +1060,15 @@ function onMenuSharing(event) { var folders = $("contactFolders"); var selected = folders.getSelectedNodes()[0]; - var owner = selected.getAttribute("owner"); - if (owner == "nobody") - showAlertDialog(_("The user rights cannot be edited for this object!")); - else { + var aclEditing = selected.getAttribute("acl-editing"); + if (aclEditing && aclEditing == "available") { var title = this.innerHTML; var url = URLForFolderID(selected.getAttribute("id")); openAclWindow(url + "/acls", title); } + else + showAlertDialog(_("The user rights cannot be edited for this object!")); } function onAddressBooksMenuPrepareVisibility() { @@ -1090,30 +1081,55 @@ function onAddressBooksMenuPrepareVisibility() { var menu = $("contactFoldersMenu").down("ul");; var listElements = menu.childNodesWithTag("li"); var modifyOption = listElements[0]; + var newListOption = listElements[3]; var removeOption = listElements[5]; var exportOption = listElements[7]; + var importOption = listElements[8]; var sharingOption = listElements[listElements.length - 1]; // Disable the "Sharing" and "Modify" options when address book // is not owned by user if (folderOwner == UserLogin || IsSuperUser) { - modifyOption.removeClassName("disabled"); // WARNING: will fail for super users anyway - sharingOption.removeClassName("disabled"); + modifyOption.removeClassName("disabled"); // WARNING: will fail + // for super users + // anyway + var aclEditing = selected[0].getAttribute("acl-editing"); + if (aclEditing && aclEditing == "available") { + sharingOption.removeClassName("disabled"); + } + else { + sharingOption.addClassName("disabled"); + } } else { modifyOption.addClassName("disabled"); sharingOption.addClassName("disabled"); } + var listEditing = selected[0].getAttribute("list-editing"); + if (listEditing && listEditing == "available") { + newListOption.removeClassName("disabled"); + } + else { + newListOption.addClassName("disabled"); + } + /* Disable the "remove" and "export ab" options when address book is public */ if (folderOwner == "nobody") { exportOption.addClassName("disabled"); + importOption.addClassName("disabled"); removeOption.addClassName("disabled"); } else { exportOption.removeClassName("disabled"); - removeOption.removeClassName("disabled"); + importOption.removeClassName("disabled"); + if (selected[0].getAttribute("id") == "/personal") { + removeOption.addClassName("disabled"); + } + else { + removeOption.removeClassName("disabled"); + } } return true; From 353757f88ea2a6f2bef7bfa493703503208aec01 Mon Sep 17 00:00:00 2001 From: Francis Lachapelle Date: Thu, 5 Jan 2012 19:45:25 +0000 Subject: [PATCH 16/68] remove stray backslash. Allows to build sogo-slapd-sockd again Monotone-Parent: f70b5b1e5f982c1feda21a5227e3bc26f8dc7f66 Monotone-Revision: c82bd90e75ed49d6ff6bdcbb89b1abd0e37397fe Monotone-Author: flachapelle@inverse.ca Monotone-Author: jraby@inverse.ca Monotone-Date: 2012-01-05T19:45:25 Monotone-Date: 2012-01-05T20:01:44 Monotone-Branch: ca.inverse.sogo Monotone-Branch: ca.inverse.sogo --- Tools/GNUmakefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tools/GNUmakefile b/Tools/GNUmakefile index c1bf7785b..33417f935 100644 --- a/Tools/GNUmakefile +++ b/Tools/GNUmakefile @@ -29,7 +29,7 @@ $(SOGO_SLAPD_SOCKD)_OBJC_FILES += \ \ SOGoSockD.m \ SOGoSockDScanner.m \ - SOGoSockDOperation.m \ + SOGoSockDOperation.m TOOL_NAME += $(SOGO_SLAPD_SOCKD) ### From a55fe2793c16285a01abd27116b8b70b7db9d022 Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Fri, 6 Jan 2012 14:35:55 +0000 Subject: [PATCH 17/68] Monotone-Parent: f70b5b1e5f982c1feda21a5227e3bc26f8dc7f66 Monotone-Revision: 0def0e9f316e25be6056fa81895ad9297459eaca Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-01-06T14:35:55 Monotone-Branch: ca.inverse.sogo --- SoObjects/Contacts/SOGoContactFolders.m | 1 - 1 file changed, 1 deletion(-) diff --git a/SoObjects/Contacts/SOGoContactFolders.m b/SoObjects/Contacts/SOGoContactFolders.m index cb00f7387..01b1f3abb 100644 --- a/SoObjects/Contacts/SOGoContactFolders.m +++ b/SoObjects/Contacts/SOGoContactFolders.m @@ -38,7 +38,6 @@ #import #import -#import #import #import #import From 61baa6118d6671672672d34b82357e3f9af6fb38 Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Mon, 9 Jan 2012 16:41:22 +0000 Subject: [PATCH 18/68] Monotone-Parent: 31373520d86c320c1658f22e3bde2eee178c2003 Monotone-Revision: d5752735c793358a8300960f867a0452965215ca Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-01-09T16:41:22 Monotone-Branch: ca.inverse.sogo --- OpenChange/MAPIStoreCalendarFolder.m | 5 +++++ OpenChange/MAPIStoreMailFolder.m | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/OpenChange/MAPIStoreCalendarFolder.m b/OpenChange/MAPIStoreCalendarFolder.m index 8ed3f19ed..747bfbef5 100644 --- a/OpenChange/MAPIStoreCalendarFolder.m +++ b/OpenChange/MAPIStoreCalendarFolder.m @@ -24,6 +24,7 @@ #import #import #import +#import #import #import #import @@ -132,6 +133,8 @@ [roles addObject: SOGoCalendarRole_ConfidentialViewer]; } + // [self logWithFormat: @"roles for rights %.8x = (%@)", rights, roles]; + return roles; } @@ -153,6 +156,8 @@ rights |= RightsReadItems; if (rights != 0) rights |= RoleNone; /* actually "folder visible" */ + + // [self logWithFormat: @"rights for roles (%@) = %.8x", roles, rights]; return rights; } diff --git a/OpenChange/MAPIStoreMailFolder.m b/OpenChange/MAPIStoreMailFolder.m index 3647b0766..393788914 100644 --- a/OpenChange/MAPIStoreMailFolder.m +++ b/OpenChange/MAPIStoreMailFolder.m @@ -1025,6 +1025,8 @@ _parseCOPYUID (NSString *line, NSArray **destUIDsP) if (rights & RightsCreateSubfolders) [roles addObject: SOGoRole_FolderCreator]; + // [self logWithFormat: @"roles for rights %.8x = (%@)", rights, roles]; + return roles; } @@ -1051,6 +1053,8 @@ _parseCOPYUID (NSString *line, NSArray **destUIDsP) if (rights != 0) rights |= RoleNone; /* actually "folder visible" */ + + // [self logWithFormat: @"rights for roles (%@) = %.8x", roles, rights]; return rights; } From 334ff94fb9fbc2fc21c4849c1455978fa0447a54 Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Mon, 9 Jan 2012 16:42:20 +0000 Subject: [PATCH 19/68] Monotone-Parent: d5752735c793358a8300960f867a0452965215ca Monotone-Revision: cec86bee0b5e2a1fc741887d9b9e535436381c6e Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-01-09T16:42:20 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 6 ++++++ OpenChange/MAPIStoreCalendarFolder.m | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 65907e7c1..9a921fde6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2012-01-09 Wolfgang Sourdeau + + * OpenChange/MAPIStoreCalendarFolder.m (-exchangeRightsForRoles): + add the freebusy read rights when the user has read permission on + calendar objects. + 2012-01-05 Francis Lachapelle * SoObjects/SOGo/SOGoUserManager.m (-_registerSource:inDomain::): diff --git a/OpenChange/MAPIStoreCalendarFolder.m b/OpenChange/MAPIStoreCalendarFolder.m index 747bfbef5..b7cdd5466 100644 --- a/OpenChange/MAPIStoreCalendarFolder.m +++ b/OpenChange/MAPIStoreCalendarFolder.m @@ -153,7 +153,7 @@ else if ([roles containsObject: SOGoCalendarRole_PublicViewer] && [roles containsObject: SOGoCalendarRole_PrivateViewer] && [roles containsObject: SOGoCalendarRole_ConfidentialViewer]) - rights |= RightsReadItems; + rights |= RightsReadItems | 0x1800; if (rights != 0) rights |= RoleNone; /* actually "folder visible" */ From 31962a0e281c1471d502e09ce992cfa4bc63e8b5 Mon Sep 17 00:00:00 2001 From: Jean Raby Date: Mon, 9 Jan 2012 17:18:11 +0000 Subject: [PATCH 20/68] Drop pytz as recent version doesn't work correctly with vobject. Use dateutil.tz instead. Monotone-Parent: cec86bee0b5e2a1fc741887d9b9e535436381c6e Monotone-Revision: b34c698acd51d633a7d276d6fbbcab8a47e7e460 Monotone-Author: jraby@inverse.ca Monotone-Date: 2012-01-09T17:18:11 Monotone-Branch: ca.inverse.sogo --- Tests/Integration/README | 2 +- Tests/Integration/test-caldav-scheduling.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Tests/Integration/README b/Tests/Integration/README index ca98c90c0..fdbf331c7 100644 --- a/Tests/Integration/README +++ b/Tests/Integration/README @@ -1,7 +1,7 @@ setup ----- -(you need "python-simplejson", "python-xml", "python-vobject" and "python-m2crypto" +(you need "python-simplejson", "python-xml", "python-vobject", "python-dateutil" and "python-m2crypto" in order to run the scripts on Debian) 1) copy config.py.in to config.py (make sure to never EVER add it to monotone) diff --git a/Tests/Integration/test-caldav-scheduling.py b/Tests/Integration/test-caldav-scheduling.py index a9fb92249..1fbd92b8e 100755 --- a/Tests/Integration/test-caldav-scheduling.py +++ b/Tests/Integration/test-caldav-scheduling.py @@ -8,10 +8,10 @@ from config import hostname, port, username, password, \ resource_no_overbook, resource_can_overbook import datetime +import dateutil.tz import sogotests import sys import time -import pytz import unittest import utilities import vobject @@ -143,7 +143,7 @@ class CalDAVITIPDelegationTest(unittest.TestCase): vevent.add('summary').value = summary vevent.add('transp').value = transparency[transp] - now = datetime.datetime.now(pytz.timezone("America/Montreal")) + now = datetime.datetime.now(dateutil.tz.gettz("America/Montreal")) startdate = vevent.add('dtstart') startdate.value = now enddate = vevent.add('dtend') From 0569c0ecbe24ff6d74bf90472f8dcefa0150f705 Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Mon, 9 Jan 2012 22:12:16 +0000 Subject: [PATCH 21/68] Monotone-Parent: cec86bee0b5e2a1fc741887d9b9e535436381c6e Monotone-Revision: ad2935dcd60331c29ad6e514b5a61fefaec7d69c Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-01-09T22:12:16 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 3 +++ OpenChange/MAPIStoreTypes.m | 2 -- OpenChange/NSObject+MAPIStore.m | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index 9a921fde6..bdb612633 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2012-01-09 Wolfgang Sourdeau + * OpenChange/NSObject+MAPIStore.m (-getValue:forTag:inMemCtx:): + handle PT_SVREID just as PT_BINARY. + * OpenChange/MAPIStoreCalendarFolder.m (-exchangeRightsForRoles): add the freebusy read rights when the user has read permission on calendar objects. diff --git a/OpenChange/MAPIStoreTypes.m b/OpenChange/MAPIStoreTypes.m index 06eadf4e9..980431d1b 100644 --- a/OpenChange/MAPIStoreTypes.m +++ b/OpenChange/MAPIStoreTypes.m @@ -148,7 +148,6 @@ NSObjectFromMAPISPropValue (const struct mapi_SPropValue *value) // #define PT_ERROR 0xa // #define PT_OBJECT 0xd // #define PT_I8 0x14 -// #define PT_SVREID 0xFB // #define PT_SRESTRICT 0xFD // #define PT_ACTIONS 0xFE result = [NSNull null]; @@ -244,7 +243,6 @@ NSObjectFromSPropValue (const struct SPropValue *value) // #define PT_ERROR 0xa // #define PT_OBJECT 0xd // #define PT_I8 0x14 -// #define PT_SVREID 0xFB // #define PT_SRESTRICT 0xFD // #define PT_ACTIONS 0xFE result = [NSNull null]; diff --git a/OpenChange/NSObject+MAPIStore.m b/OpenChange/NSObject+MAPIStore.m index 0774a5c14..198e1969a 100644 --- a/OpenChange/NSObject+MAPIStore.m +++ b/OpenChange/NSObject+MAPIStore.m @@ -106,6 +106,7 @@ static int MAPIStoreTallocWrapperDestroy (void *data) *data = [(NSCalendarDate * ) self asFileTimeInMemCtx: memCtx]; break; case PT_BINARY: + case PT_SVREID: *data = [(NSData *) self asBinaryInMemCtx: memCtx]; break; case PT_CLSID: From 85effb25129999bee967e69ecb41ba5bc8f8cbc7 Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Tue, 10 Jan 2012 17:35:01 +0000 Subject: [PATCH 22/68] Monotone-Parent: d9f13a2035f812bf584e6ed63e858a9373cd11c8 Monotone-Revision: 38edb6b526df653889b897cf6ff9643dfd3ffad2 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-01-10T17:35:01 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 5 +++++ OpenChange/MAPIStoreMailFolder.m | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index bdb612633..f0f686be9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2012-01-10 Wolfgang Sourdeau + + * OpenChange/MAPIStoreMailFolder.m (_parseCOPYUID): the uniString + buffer was allocated one byte too short. + 2012-01-09 Wolfgang Sourdeau * OpenChange/NSObject+MAPIStore.m (-getValue:forTag:inMemCtx:): diff --git a/OpenChange/MAPIStoreMailFolder.m b/OpenChange/MAPIStoreMailFolder.m index 393788914..5df5bf0ef 100644 --- a/OpenChange/MAPIStoreMailFolder.m +++ b/OpenChange/MAPIStoreMailFolder.m @@ -848,7 +848,7 @@ _parseCOPYUID (NSString *line, NSArray **destUIDsP) /* sample: 1 OK [COPYUID 1311899334 1:3 11:13] Completed */ max = [line length]; - uniString = NSZoneMalloc (NULL, max * sizeof (unichar) + 1); + uniString = NSZoneMalloc (NULL, sizeof (unichar) * (max + 1)); [line getCharacters: uniString]; uniString[max] = 0; From 74a4c00daad499e1251e6ad82f3ae4f0aaa52781 Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Thu, 12 Jan 2012 03:45:04 +0000 Subject: [PATCH 23/68] Monotone-Parent: 401881ecc26daa2ed137a36540462655a8e4504c Monotone-Revision: c19f9b62fb1cd88301e4921d8d23bb5b5169fb45 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-01-12T03:45:04 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 6 ++++++ OpenChange/MAPIStoreGCSMessageTable.m | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 49c01d4aa..ab0dc09c9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2012-01-11 Wolfgang Sourdeau + + * OpenChange/MAPIStoreGCSMessageTable.m + (_fixedDatePropertyRestriction:inMemCtx:): attached the result to + the memCtx passed as parameter to avoid a leak. + 2012-01-10 Wolfgang Sourdeau * SoObjects/Appointments/SOGoAppointmentObject.m diff --git a/OpenChange/MAPIStoreGCSMessageTable.m b/OpenChange/MAPIStoreGCSMessageTable.m index 6eb096ebe..45589fcc9 100644 --- a/OpenChange/MAPIStoreGCSMessageTable.m +++ b/OpenChange/MAPIStoreGCSMessageTable.m @@ -58,7 +58,7 @@ NSCalendarDate *dateValue; int32_t longDate; - translatedRes = talloc (NULL, struct mapi_SPropertyRestriction); + translatedRes = talloc (memCtx, struct mapi_SPropertyRestriction); translatedRes->ulPropTag = (res->ulPropTag & 0xffff0000) | PT_LONG; translatedRes->relop = res->relop; dateValue = NSObjectFromMAPISPropValue (&res->lpProp); From 4c2762e7b21dce863c5e2f6508270eea46219a56 Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Thu, 12 Jan 2012 13:39:18 +0000 Subject: [PATCH 24/68] Monotone-Parent: c19f9b62fb1cd88301e4921d8d23bb5b5169fb45 Monotone-Revision: 59576b63b875c6db3416e7be726b196610b856a3 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-01-12T13:39:18 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 6 ++++++ OpenChange/MAPIStoreTasksMessage.m | 33 +++++++++++++++++------------- 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/ChangeLog b/ChangeLog index ab0dc09c9..032295ad3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2012-01-12 Wolfgang Sourdeau + + * OpenChange/MAPIStoreTasksMessage.m (-save): the dates provided + by Outlook for start, due and completed are all-day dates, we thus + need to remove the timezone offset from those dates. + 2012-01-11 Wolfgang Sourdeau * OpenChange/MAPIStoreGCSMessageTable.m diff --git a/OpenChange/MAPIStoreTasksMessage.m b/OpenChange/MAPIStoreTasksMessage.m index 1418d76fc..81c2a9ba9 100644 --- a/OpenChange/MAPIStoreTasksMessage.m +++ b/OpenChange/MAPIStoreTasksMessage.m @@ -22,8 +22,10 @@ */ #import +#import #import #import +#import #import #import #import @@ -325,13 +327,11 @@ iCalCalendar *vCalendar; iCalToDo *vToDo; id value; - SOGoUserDefaults *ud; - iCalTimeZone *tz; iCalDateTime *date; - NSString *owner, *status, *priority; + NSString *status, *priority; NSCalendarDate *now; + NSInteger tzOffset; - owner = [sogoObject ownerInContext: nil]; vToDo = [sogoObject component: YES secure: NO]; vCalendar = [vToDo parent]; [vCalendar setProdID: @"-//Inverse inc.//OpenChange+SOGo//EN"]; @@ -366,17 +366,16 @@ [vToDo setTimeStampAsDate: value]; } - ud = [[SOGoUser userWithLogin: owner] userDefaults]; - tz = [iCalTimeZone timeZoneForName: [ud timeZoneName]]; - [vCalendar addTimeZone: tz]; - // start value = [properties objectForKey: MAPIPropertyKey (PidLidTaskStartDate)]; if (value) { date = (iCalDateTime *) [vToDo uniqueChildWithTag: @"dtstart"]; - [date setTimeZone: tz]; - [date setDateTime: value]; + tzOffset = [[value timeZone] secondsFromGMTForDate: value]; + value = [value dateByAddingYears: 0 months: 0 days: 0 + hours: 0 minutes: 0 + seconds: -tzOffset]; + [date setDate: value]; } else { @@ -388,8 +387,11 @@ if (value) { date = (iCalDateTime *) [vToDo uniqueChildWithTag: @"due"]; - [date setTimeZone: tz]; - [date setDateTime: value]; + tzOffset = [[value timeZone] secondsFromGMTForDate: value]; + value = [value dateByAddingYears: 0 months: 0 days: 0 + hours: 0 minutes: 0 + seconds: -tzOffset]; + [date setDate: value]; } else { @@ -401,8 +403,11 @@ if (value) { date = (iCalDateTime *) [vToDo uniqueChildWithTag: @"completed"]; - [date setTimeZone: tz]; - [date setDateTime: value]; + tzOffset = [[value timeZone] secondsFromGMTForDate: value]; + value = [value dateByAddingYears: 0 months: 0 days: 0 + hours: 0 minutes: 0 + seconds: -tzOffset]; + [date setDate: value]; } else { From 56ad0c3289f94c2d753f35227d1721a8cd667a2b Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Thu, 12 Jan 2012 16:06:57 +0000 Subject: [PATCH 25/68] Monotone-Parent: d0b2cce64733874b10da170e30bdcb9f9730f61e Monotone-Revision: d807738c8f35c5080fb2e1684404fb8d992c8032 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-01-12T16:06:57 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 7 ++++ OpenChange/MAPIStoreAppointmentWrapper.m | 20 ++++++++--- OpenChange/MAPIStoreCalendarMessage.m | 42 +++++++++++++++++------- 3 files changed, 52 insertions(+), 17 deletions(-) diff --git a/ChangeLog b/ChangeLog index 032295ad3..7467c990d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,12 @@ 2012-01-12 Wolfgang Sourdeau + * OpenChange/MAPIStoreCalendarMessage.m (-save): same as below + + ensure that nil fields are removed from non-new instances. + + * OpenChange/MAPIStoreAppointmentWrapper.m + (-getPrStartDate:inMemCtx:, -getPrEndDate:inMemCtx:): remove the + tz offset from dates in all-day events. + * OpenChange/MAPIStoreTasksMessage.m (-save): the dates provided by Outlook for start, due and completed are all-day dates, we thus need to remove the timezone offset from those dates. diff --git a/OpenChange/MAPIStoreAppointmentWrapper.m b/OpenChange/MAPIStoreAppointmentWrapper.m index c1f0e8b90..947e2119b 100644 --- a/OpenChange/MAPIStoreAppointmentWrapper.m +++ b/OpenChange/MAPIStoreAppointmentWrapper.m @@ -638,11 +638,19 @@ static NSCharacterSet *hexCharacterSet = nil; inMemCtx: (TALLOC_CTX *) memCtx { NSCalendarDate *dateValue; + NSInteger offset; if ([event isRecurrent]) dateValue = [event firstRecurrenceStartDate]; else dateValue = [event startDate]; + if ([event isAllDay]) + { + offset = -[timeZone secondsFromGMTForDate: dateValue]; + dateValue = [dateValue dateByAddingYears: 0 months: 0 days: 0 + hours: 0 minutes: 0 + seconds: offset]; + } [dateValue setTimeZone: utcTZ]; *data = [dateValue asFileTimeInMemCtx: memCtx]; @@ -882,16 +890,18 @@ static NSCharacterSet *hexCharacterSet = nil; inMemCtx: (TALLOC_CTX *) memCtx { NSCalendarDate *dateValue; + NSInteger offset; if ([event isRecurrent]) dateValue = [event firstRecurrenceStartDate]; else dateValue = [event startDate]; - dateValue - = [dateValue dateByAddingYears: 0 months: 0 days: 0 - hours: 0 minutes: 0 - seconds: (NSInteger) [event - durationAsTimeInterval]]; + offset = [event durationAsTimeInterval]; + if ([event isAllDay]) + offset -= [timeZone secondsFromGMTForDate: dateValue]; + dateValue = [dateValue dateByAddingYears: 0 months: 0 days: 0 + hours: 0 minutes: 0 + seconds: offset]; [dateValue setTimeZone: utcTZ]; *data = [dateValue asFileTimeInMemCtx: memCtx]; diff --git a/OpenChange/MAPIStoreCalendarMessage.m b/OpenChange/MAPIStoreCalendarMessage.m index 263e60d30..aa46a8962 100644 --- a/OpenChange/MAPIStoreCalendarMessage.m +++ b/OpenChange/MAPIStoreCalendarMessage.m @@ -207,7 +207,8 @@ - (int) getPidLidAppointmentSubType: (void **) data inMemCtx: (TALLOC_CTX *) memCtx { - return [[self appointmentWrapper] getPidLidAppointmentSubType: data inMemCtx: memCtx]; + return [[self appointmentWrapper] getPidLidAppointmentSubType: data + inMemCtx: memCtx]; } - (int) getPidLidBusyStatus: (void **) data // TODO @@ -653,6 +654,7 @@ iCalEvent *newEvent; iCalPerson *userPerson; NSUInteger responseStatus = 0; + NSInteger tzOffset; SOGoUser *activeUser, *ownerUser; id value; @@ -747,10 +749,11 @@ if (value) [newEvent setLocation: value]; - isAllDay = [[properties - objectForKey: MAPIPropertyKey (PidLidAppointmentSubType)] - boolValue]; - + isAllDay = [newEvent isAllDay]; + value = [properties + objectForKey: MAPIPropertyKey (PidLidAppointmentSubType)]; + if (value) + isAllDay = [value boolValue]; if (!isAllDay) { tzName = [[self ownerTimeZone] name]; @@ -767,7 +770,14 @@ { start = (iCalDateTime *) [newEvent uniqueChildWithTag: @"dtstart"]; if (isAllDay) - [start setDate: value]; + { + tzOffset = [[value timeZone] secondsFromGMTForDate: value]; + value = [value dateByAddingYears: 0 months: 0 days: 0 + hours: 0 minutes: 0 + seconds: -tzOffset]; + [start setTimeZone: nil]; + [start setDate: value]; + } else { [start setTimeZone: tz]; @@ -776,14 +786,21 @@ } /* end */ - value = [properties objectForKey: MAPIPropertyKey(PR_END_DATE)]; + value = [properties objectForKey: MAPIPropertyKey (PR_END_DATE)]; if (!value) - value = [properties objectForKey: MAPIPropertyKey(PidLidAppointmentEndWhole)]; + value = [properties objectForKey: MAPIPropertyKey (PidLidAppointmentEndWhole)]; if (value) { end = (iCalDateTime *) [newEvent uniqueChildWithTag: @"dtend"]; if (isAllDay) - [end setDate: value]; + { + tzOffset = [[value timeZone] secondsFromGMTForDate: value]; + value = [value dateByAddingYears: 0 months: 0 days: 0 + hours: 0 minutes: 0 + seconds: -tzOffset]; + [end setTimeZone: nil]; + [end setDate: value]; + } else { [end setTimeZone: tz]; @@ -840,13 +857,14 @@ if (value) { value = [[NSString alloc] initWithData: value - encoding: NSUTF8StringEncoding]; + encoding: NSUTF8StringEncoding]; [value autorelease]; value = [value htmlToText]; } } - if (value) - [newEvent setComment: value]; + if (value && [value length] == 0) + value = nil; + [newEvent setComment: value]; /* recurrence */ value = [properties From 537604d65a01f7f5165095e217681f207d259ba8 Mon Sep 17 00:00:00 2001 From: Francis Lachapelle Date: Thu, 12 Jan 2012 22:12:02 +0000 Subject: [PATCH 26/68] Fixed previous propagate. Monotone-Parent: 62ee066ad239ea56d6f2dc0b73f12d399813d68c Monotone-Revision: cd6f87a655862b556c2f1e1031a0cdd43a995041 Monotone-Author: flachapelle@inverse.ca Monotone-Date: 2012-01-12T22:12:02 Monotone-Branch: ca.inverse.sogo --- SoObjects/SOGo/LDAPSource.m | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/SoObjects/SOGo/LDAPSource.m b/SoObjects/SOGo/LDAPSource.m index 44f6f56a8..7736d3aba 100644 --- a/SoObjects/SOGo/LDAPSource.m +++ b/SoObjects/SOGo/LDAPSource.m @@ -214,12 +214,8 @@ static Class NSStringK; [_dnCache release]; [kindField release]; [multipleBookingsField release]; -<<<<<<< variant A [MSExchangeHostname release]; ->>>>>>> variant B [modifiers release]; -####### Ancestor -======= end [super dealloc]; } @@ -976,22 +972,12 @@ andMultipleBookingsField: (NSString *) newMultipleBookingsField NSMutableArray *classes; id o; -<<<<<<< variant A - contactEntry = [NSMutableDictionary dictionary]; - [contactEntry setObject: self forKey: @"source"]; - [contactEntry setObject: [ldapEntry dn] forKey: @"dn"]; - attributes = [[self _searchAttributes] objectEnumerator]; ->>>>>>> variant B if (!resourceKinds) resourceKinds = [[NSArray alloc] initWithObjects: @"location", @"thing", @"group", nil]; -####### Ancestor - contactEntry = [NSMutableDictionary dictionary]; - [contactEntry setObject: [ldapEntry dn] forKey: @"dn"]; - attributes = [[self _searchAttributes] objectEnumerator]; -======= end ldifRecord = [ldapEntry _asDictionary]; + [ldifRecord setObject: self forKey: @"source"]; [ldifRecord setObject: [ldapEntry dn] forKey: @"dn"]; // We get our objectClass attribute values. We lowercase @@ -1301,13 +1287,11 @@ andMultipleBookingsField: (NSString *) newMultipleBookingsField return baseDN; } -<<<<<<< variant A - (NSString *) MSExchangeHostname { return MSExchangeHostname; } ->>>>>>> variant B - (void) setModifiers: (NSArray *) newModifiers { ASSIGN (modifiers, newModifiers); @@ -1793,6 +1777,4 @@ _makeLDAPChanges (NGLdapConnection *ldapConnection, return result; } -####### Ancestor -======= end @end From 3fbe4213195bbe3f2cf7bcf108b1b72661302c4c Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Sun, 15 Jan 2012 23:58:26 +0000 Subject: [PATCH 27/68] Monotone-Parent: e3e0e252ab28dd87b80ca4f37b899dd9c416634b Monotone-Revision: 10bc75b22aa9828644f846e67ae3509d61793864 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-01-15T23:58:26 Monotone-Branch: ca.inverse.sogo --- OpenChange/MAPIStoreFolder.h | 10 ++++------ OpenChange/MAPIStoreFolder.m | 10 +++++----- OpenChange/MAPIStoreMailMessageTable.m | 2 +- OpenChange/MAPIStoreSOGo.m | 14 +++++--------- OpenChange/MAPIStoreTable.h | 13 ++++++++----- OpenChange/MAPIStoreTable.m | 8 ++++---- 6 files changed, 27 insertions(+), 30 deletions(-) diff --git a/OpenChange/MAPIStoreFolder.h b/OpenChange/MAPIStoreFolder.h index c124cdce7..9bd9491b0 100644 --- a/OpenChange/MAPIStoreFolder.h +++ b/OpenChange/MAPIStoreFolder.h @@ -25,8 +25,6 @@ #import -#import "MAPIStoreTable.h" - @class NSArray; @class NSMutableArray; @class NSNumber; @@ -105,7 +103,7 @@ andFID: (uint64_t) fid; - (int) deleteFolderWithFID: (uint64_t) fid; - (int) getChildCount: (uint32_t *) rowCount - ofTableType: (uint8_t) tableType; + ofTableType: (enum mapistore_table_type) tableType; - (int) createMessage: (MAPIStoreMessage **) messagePtr withMID: (uint64_t) mid @@ -128,12 +126,12 @@ - (int) getDeletedFMIDs: (struct I8Array_r **) fmidsPtr andCN: (uint64_t *) cnPtr fromChangeNumber: (uint64_t) changeNum - inTableType: (uint8_t) tableType + inTableType: (enum mapistore_table_type) tableType inMemCtx: (TALLOC_CTX *) mem_ctx; - (int) getTable: (MAPIStoreTable **) tablePtr andRowCount: (uint32_t *) count - tableType: (uint8_t) tableType + tableType: (enum mapistore_table_type) tableType andHandleId: (uint32_t) handleId; - (int) modifyPermissions: (struct PermissionData *) permissions @@ -150,7 +148,7 @@ andSortOrderings: (NSArray *) sortOrderings; - (NSArray *) getDeletedKeysFromChangeNumber: (uint64_t) changeNum andCN: (NSNumber **) cnNbr - inTableType: (uint8_t) tableType; + inTableType: (enum mapistore_table_type) tableType; - (NSString *) createFolder: (struct SRow *) aRow withFID: (uint64_t) newFID; diff --git a/OpenChange/MAPIStoreFolder.m b/OpenChange/MAPIStoreFolder.m index fa13b9da7..155991b42 100644 --- a/OpenChange/MAPIStoreFolder.m +++ b/OpenChange/MAPIStoreFolder.m @@ -383,7 +383,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe } - (int) getChildCount: (uint32_t *) rowCount - ofTableType: (uint8_t) tableType + ofTableType: (enum mapistore_table_type) tableType { NSArray *keys; int rc = MAPISTORE_SUCCESS; @@ -863,7 +863,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe - (int) getDeletedFMIDs: (struct I8Array_r **) fmidsPtr andCN: (uint64_t *) cnPtr fromChangeNumber: (uint64_t) changeNum - inTableType: (uint8_t) tableType + inTableType: (enum mapistore_table_type) tableType inMemCtx: (TALLOC_CTX *) memCtx { int rc; @@ -920,7 +920,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe - (int) getTable: (MAPIStoreTable **) tablePtr andRowCount: (uint32_t *) countPtr - tableType: (uint8_t) tableType + tableType: (enum mapistore_table_type) tableType andHandleId: (uint32_t) handleId { int rc = MAPISTORE_SUCCESS; @@ -1076,7 +1076,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe andType: MAPISTORE_FAI_TABLE]; } -- (void) _cleanupTableCaches: (uint8_t) tableType +- (void) _cleanupTableCaches: (enum mapistore_table_type) tableType { NSArray *tables; NSUInteger count, max; @@ -1552,7 +1552,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe - (NSArray *) getDeletedKeysFromChangeNumber: (uint64_t) changeNum andCN: (NSNumber **) cnNbrs - inTableType: (uint8_t) tableType + inTableType: (enum mapistore_table_type) tableType { return nil; } diff --git a/OpenChange/MAPIStoreMailMessageTable.m b/OpenChange/MAPIStoreMailMessageTable.m index f4d5c2003..72133a210 100644 --- a/OpenChange/MAPIStoreMailMessageTable.m +++ b/OpenChange/MAPIStoreMailMessageTable.m @@ -326,7 +326,7 @@ static Class MAPIStoreMailMessageK, NSDataK, NSStringK; - (int) getRow: (struct mapistore_property_data **) dataP withRowID: (uint32_t) rowId - andQueryType: (enum table_query_type) queryType + andQueryType: (enum mapistore_query_type) queryType inMemCtx: (TALLOC_CTX *) memCtx { if (!fetchedCoreInfos) diff --git a/OpenChange/MAPIStoreSOGo.m b/OpenChange/MAPIStoreSOGo.m index ffdbea6d0..527da32ec 100644 --- a/OpenChange/MAPIStoreSOGo.m +++ b/OpenChange/MAPIStoreSOGo.m @@ -43,10 +43,6 @@ #import "MAPIStoreTable.h" #import "NSObject+MAPIStore.h" -#undef DEBUG -#include -#include -#include #include #include @@ -331,7 +327,7 @@ sogo_folder_delete_folder(void *folder_object, uint64_t fid) } static enum mapistore_error -sogo_folder_get_child_count(void *folder_object, uint8_t table_type, uint32_t *child_count) +sogo_folder_get_child_count(void *folder_object, enum mapistore_table_type table_type, uint32_t *child_count) { struct MAPIStoreTallocWrapper *wrapper; NSAutoreleasePool *pool; @@ -494,7 +490,7 @@ sogo_folder_move_copy_messages(void *folder_object, static enum mapistore_error sogo_folder_get_deleted_fmids(void *folder_object, TALLOC_CTX *mem_ctx, - uint8_t table_type, uint64_t change_num, + enum mapistore_table_type table_type, uint64_t change_num, struct I8Array_r **fmidsp, uint64_t *cnp) { struct MAPIStoreTallocWrapper *wrapper; @@ -526,7 +522,7 @@ sogo_folder_get_deleted_fmids(void *folder_object, TALLOC_CTX *mem_ctx, static enum mapistore_error sogo_folder_open_table(void *folder_object, TALLOC_CTX *mem_ctx, - uint8_t table_type, uint32_t handle_id, + enum mapistore_table_type table_type, uint32_t handle_id, void **table_object, uint32_t *row_count) { struct MAPIStoreTallocWrapper *wrapper; @@ -974,7 +970,7 @@ sogo_table_set_sort_order (void *table_object, struct SSortOrderSet *sort_order, static enum mapistore_error sogo_table_get_row (void *table_object, TALLOC_CTX *mem_ctx, - enum table_query_type query_type, uint32_t row_id, + enum mapistore_query_type query_type, uint32_t row_id, struct mapistore_property_data **data) { struct MAPIStoreTallocWrapper *wrapper; @@ -1003,7 +999,7 @@ sogo_table_get_row (void *table_object, TALLOC_CTX *mem_ctx, static enum mapistore_error sogo_table_get_row_count (void *table_object, - enum table_query_type query_type, + enum mapistore_query_type query_type, uint32_t *row_countp) { struct MAPIStoreTallocWrapper *wrapper; diff --git a/OpenChange/MAPIStoreTable.h b/OpenChange/MAPIStoreTable.h index 43314d313..fbf13a680 100644 --- a/OpenChange/MAPIStoreTable.h +++ b/OpenChange/MAPIStoreTable.h @@ -27,6 +27,9 @@ #import +#undef DEBUG +#include + #define SENSITIVITY_NONE 0 #define SENSITIVITY_PERSONAL 1 #define SENSITIVITY_PRIVATE 2 @@ -62,7 +65,7 @@ typedef enum { uint32_t currentRow; MAPIStoreObject *currentChild; - uint8_t tableType; /* mapistore */ + enum mapistore_table_type tableType; /* mapistore */ /* proof of concept */ uint16_t columnsCount; @@ -75,13 +78,13 @@ typedef enum { - (id) initForContainer: (MAPIStoreObject *) newContainer; - (id) container; -- (uint8_t) tableType; +- (enum mapistore_table_type) tableType; - (void) setHandleId: (uint32_t) newHandleId; - (void) destroyHandle: (uint32_t) handleId; - (id) childAtRowID: (uint32_t) rowId - forQueryType: (enum table_query_type) queryType; + forQueryType: (enum mapistore_query_type) queryType; - (void) cleanupCaches; @@ -92,10 +95,10 @@ typedef enum { withCount: (uint16_t) newColumCount; - (int) getRow: (struct mapistore_property_data **) dataP withRowID: (uint32_t) rowId - andQueryType: (enum table_query_type) queryType + andQueryType: (enum mapistore_query_type) queryType inMemCtx: (TALLOC_CTX *) memCtx; - (int) getRowCount: (uint32_t *) countP - withQueryType: (enum table_query_type) queryType; + withQueryType: (enum mapistore_query_type) queryType; - (void) notifyChangesForChild: (MAPIStoreObject *) child; diff --git a/OpenChange/MAPIStoreTable.m b/OpenChange/MAPIStoreTable.m index 530949709..16b8febf6 100644 --- a/OpenChange/MAPIStoreTable.m +++ b/OpenChange/MAPIStoreTable.m @@ -337,7 +337,7 @@ static Class NSDataK, NSStringK; return container; } -- (uint8_t) tableType +- (enum mapistore_table_type) tableType { return tableType; } @@ -765,7 +765,7 @@ static Class NSDataK, NSStringK; } - (id) childAtRowID: (uint32_t) rowId - forQueryType: (enum table_query_type) queryType + forQueryType: (enum mapistore_query_type) queryType { id child; NSArray *children, *restrictedChildren; @@ -833,7 +833,7 @@ static Class NSDataK, NSStringK; - (int) getRow: (struct mapistore_property_data **) dataP withRowID: (uint32_t) rowId - andQueryType: (enum table_query_type) queryType + andQueryType: (enum mapistore_query_type) queryType inMemCtx: (TALLOC_CTX *) memCtx { NSUInteger count; @@ -860,7 +860,7 @@ static Class NSDataK, NSStringK; } - (int) getRowCount: (uint32_t *) countP - withQueryType: (enum table_query_type) queryType + withQueryType: (enum mapistore_query_type) queryType { NSArray *children; From 1746d307f339b8c82aed681d25958966871d6b55 Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Mon, 16 Jan 2012 20:18:39 +0000 Subject: [PATCH 28/68] Monotone-Parent: 10bc75b22aa9828644f846e67ae3509d61793864 Monotone-Revision: 181c204ddec5a60db59ce4a63428f75cb20a7a81 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-01-16T20:18:39 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 6 ++++++ SoObjects/SOGo/SQLSource.m | 9 ++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 7fb0e286e..2312c00a1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2012-01-16 Wolfgang Sourdeau + + * SoObjects/SOGo/SQLSource.m (_lookupContactEntry:considerEmail:) + (fetchContactsMatching:): assigned self to the "source" key of the + returned dictionaries. + 2012-01-13 Francis Lachapelle * SoObjects/Appointments/SOGoAppointmentFolder.m diff --git a/SoObjects/SOGo/SQLSource.m b/SoObjects/SOGo/SQLSource.m index 441808915..59bdf969e 100644 --- a/SoObjects/SOGo/SQLSource.m +++ b/SoObjects/SOGo/SQLSource.m @@ -572,6 +572,8 @@ forKey: @"numberOfSimultaneousBookings"]; } } + + [response setObject: self forKey: @"source"]; } else [self errorWithFormat: @"could not run SQL '%@': %@", sql, ex]; @@ -678,7 +680,12 @@ attrs = [channel describeResults: NO]; while ((row = [channel fetchAttributes: attrs withZone: NULL])) - [results addObject: row]; + { + row = [row mutableCopy]; + [(NSMutableDictionary *) row setObject: self forKey: @"source"]; + [results addObject: row]; + [row release]; + } } else [self errorWithFormat: @"could not run SQL '%@': %@", sql, ex]; From 4ec198dbd7a5940c20e80a9eaace66d27ff5c637 Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Fri, 20 Jan 2012 01:36:31 +0000 Subject: [PATCH 29/68] Monotone-Parent: 1cafe7a57bb95258d6e78d2bc0a96b00fcdb0325 Monotone-Revision: ffd7393157c88a834c33d2ca30dc76ef3026c253 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-01-20T01:36:31 Monotone-Branch: ca.inverse.sogo --- Tests/Integration/GNUmakefile.preamble | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Tests/Integration/GNUmakefile.preamble b/Tests/Integration/GNUmakefile.preamble index 4d20462de..90cc86a41 100644 --- a/Tests/Integration/GNUmakefile.preamble +++ b/Tests/Integration/GNUmakefile.preamble @@ -15,3 +15,5 @@ ADDITIONAL_LIB_DIRS += \ -L../../SOPE/GDLContentStore/$(GNUSTEP_OBJ_DIR)/ -lGDLContentStore \ -L../../SOPE/NGCards/$(GNUSTEP_OBJ_DIR)/ -lNGCards \ -L/usr/local/lib -L/usr/lib -lEOControl -lNGStreams -lNGMime -lNGExtensions + +ADDITIONAL_LDFLAGS += -Wl,--no-as-needed From 68b99f2f28fe8b1cc327b8b53f4452870426b1cd Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Thu, 26 Jan 2012 16:27:18 +0000 Subject: [PATCH 30/68] Fixed merge Monotone-Parent: 24936b7a90184df1eebd3fa1c0df81c4dcbb2751 Monotone-Revision: f2612a8cd144efc05fbd0847e973f12e72643c9b Monotone-Author: ludovic@Sophos.ca Monotone-Date: 2012-01-26T16:27:18 Monotone-Branch: ca.inverse.sogo --- SoObjects/SOGo/LDAPSource.m | 4 ---- 1 file changed, 4 deletions(-) diff --git a/SoObjects/SOGo/LDAPSource.m b/SoObjects/SOGo/LDAPSource.m index 3cedeef52..fa2db52bf 100644 --- a/SoObjects/SOGo/LDAPSource.m +++ b/SoObjects/SOGo/LDAPSource.m @@ -166,12 +166,8 @@ static Class NSStringK; bindFields = nil; _scope = @"sub"; _filter = nil; -<<<<<<< variant A _userPasswordAlgorithm = nil; ->>>>>>> variant B listRequiresDot = YES; -####### Ancestor -======= end searchAttributes = nil; passwordPolicy = NO; From 83709fd892d725b8099aa2e5a470af0f0e9612df Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Thu, 26 Jan 2012 19:54:36 +0000 Subject: [PATCH 31/68] Monotone-Parent: 3dc6d9dff558dfb935fde8eee2c978d9a9d1a774 Monotone-Revision: e89a46b719b80b6d34d9e7a110754e60344dd669 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-01-26T19:54:36 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 12 +++++++ OpenChange/MAPIStoreCalendarContext.m | 20 ++++++++++++ OpenChange/MAPIStoreContactsContext.m | 20 ++++++++++++ OpenChange/MAPIStoreContext.h | 5 +++ OpenChange/MAPIStoreContext.m | 34 +++++++++++++++++++ OpenChange/MAPIStoreMailContext.m | 47 +++++++++++++++++++++++++++ OpenChange/MAPIStoreNotesContext.m | 20 ++++++++++++ OpenChange/MAPIStoreSOGo.m | 36 ++++++++++++++++++-- OpenChange/MAPIStoreTasksContext.m | 20 ++++++++++++ 9 files changed, 211 insertions(+), 3 deletions(-) diff --git a/ChangeLog b/ChangeLog index 41234139a..cf40d5ec9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,15 @@ +2012-01-26 Wolfgang Sourdeau + + * OpenChange/MAPIStoreSOGo.m (sogo_backend_list_contexts): new + backend method. + + * OpenChange/MAPIStoreContext.m + (+listAllContextsForUser:inMemCtx:): centralized method for + returning all contexts available from all context classes for one + user. + (+listContextsForUser:inMemCtx:): new individual method invoked by + the above. Overridden by concrete subclasses. + 2012-01-26 Ludovic Marcotte * SoObjects/SOGo/LDAPSource.{h,m} - now honor diff --git a/OpenChange/MAPIStoreCalendarContext.m b/OpenChange/MAPIStoreCalendarContext.m index b3cbb2899..8f05ceb17 100644 --- a/OpenChange/MAPIStoreCalendarContext.m +++ b/OpenChange/MAPIStoreCalendarContext.m @@ -27,6 +27,9 @@ #import "MAPIStoreCalendarContext.h" +#undef DEBUG +#include + @implementation MAPIStoreCalendarContext + (NSString *) MAPIModuleName @@ -34,6 +37,23 @@ return @"calendar"; } ++ (struct mapistore_contexts_list *) listContextsForUser: (NSString *) userName + inMemCtx: (TALLOC_CTX *) memCtx +{ + struct mapistore_contexts_list *context; + + context = talloc_zero(memCtx, struct mapistore_contexts_list); + context->url = talloc_asprintf (context, "sogo://%s@calendar/", + [userName UTF8String]); + // context->name = "Agenda personnel"; + context->main_folder = true; + context->role = MAPISTORE_CALENDAR_ROLE; + context->tag = "tag"; + context->prev = context; + + return context; +} + - (void) setupBaseFolder: (NSURL *) newURL { baseFolder = [MAPIStoreCalendarFolder baseFolderWithURL: newURL diff --git a/OpenChange/MAPIStoreContactsContext.m b/OpenChange/MAPIStoreContactsContext.m index 4a266ac30..34b6257e8 100644 --- a/OpenChange/MAPIStoreContactsContext.m +++ b/OpenChange/MAPIStoreContactsContext.m @@ -27,6 +27,9 @@ #import "MAPIStoreContactsContext.h" +#undef DEBUG +#include + @implementation MAPIStoreContactsContext + (NSString *) MAPIModuleName @@ -34,6 +37,23 @@ return @"contacts"; } ++ (struct mapistore_contexts_list *) listContextsForUser: (NSString *) userName + inMemCtx: (TALLOC_CTX *) memCtx +{ + struct mapistore_contexts_list *context; + + context = talloc_zero(memCtx, struct mapistore_contexts_list); + context->url = talloc_asprintf (context, "sogo://%s@contacts/", + [userName UTF8String]); + // context->name = "Carnet d'adresses personnel"; + context->main_folder = true; + context->role = MAPISTORE_CONTACTS_ROLE; + context->tag = "tag"; + context->prev = context; + + return context; +} + - (void) setupBaseFolder: (NSURL *) newURL { baseFolder = [MAPIStoreContactsFolder baseFolderWithURL: newURL diff --git a/OpenChange/MAPIStoreContext.h b/OpenChange/MAPIStoreContext.h index 09774238d..88c902e42 100644 --- a/OpenChange/MAPIStoreContext.h +++ b/OpenChange/MAPIStoreContext.h @@ -69,6 +69,11 @@ MAPIStoreFolder *baseFolder; } ++ (struct mapistore_contexts_list *) listAllContextsForUser: (NSString *) userName + inMemCtx: (TALLOC_CTX *) memCtx; ++ (struct mapistore_contexts_list *) listContextsForUser: (NSString *) userName + inMemCtx: (TALLOC_CTX *) memCtx; + + (int) openContext: (MAPIStoreContext **) contextPtr withURI: (const char *) newUri connectionInfo: (struct mapistore_connection_info *) newConnInfo diff --git a/OpenChange/MAPIStoreContext.m b/OpenChange/MAPIStoreContext.m index 07ac7116a..9b6a40b46 100644 --- a/OpenChange/MAPIStoreContext.m +++ b/OpenChange/MAPIStoreContext.m @@ -101,6 +101,40 @@ static NSMutableDictionary *contextClassMapping; } } + ++ (struct mapistore_contexts_list *) listAllContextsForUser: (NSString *) userName + inMemCtx: (TALLOC_CTX *) memCtx +{ + struct mapistore_contexts_list *list, *current; + NSArray *classes; + Class currentClass; + NSUInteger count, max; + + list = NULL; + + classes = GSObjCAllSubclassesOfClass (self); + max = [classes count]; + for (count = 0; count < max; count++) + { + currentClass = [classes objectAtIndex: count]; + current = [currentClass listContextsForUser: userName + inMemCtx: memCtx]; + if (current) + { + [self logWithFormat: @"adding list: %p", current]; + DLIST_CONCATENATE(list, current, void); + } + } + + return list; +} + ++ (struct mapistore_contexts_list *) listContextsForUser: (NSString *) userName + inMemCtx: (TALLOC_CTX *) memCtx +{ + return NULL; +} + static inline enum mapistore_error _prepareContextClass (Class contextClass, struct mapistore_connection_info *connInfo, diff --git a/OpenChange/MAPIStoreMailContext.m b/OpenChange/MAPIStoreMailContext.m index 0ca511936..f4885f10b 100644 --- a/OpenChange/MAPIStoreMailContext.m +++ b/OpenChange/MAPIStoreMailContext.m @@ -24,9 +24,13 @@ #import "MAPIStoreMailFolder.h" #import "MAPIStoreMapping.h" +#import "NSString+MAPIStore.h" #import "MAPIStoreMailContext.h" +#undef DEBUG +#include + @implementation MAPIStoreMailContext + (NSString *) MAPIModuleName @@ -34,6 +38,11 @@ return nil; } ++ (enum mapistore_context_role) contextRole +{ + return MAPISTORE_MAIL_ROLE; +} + @end @implementation MAPIStoreInboxContext @@ -43,6 +52,24 @@ return @"inbox"; } ++ (struct mapistore_contexts_list *) listContextsForUser: (NSString *) userName + inMemCtx: (TALLOC_CTX *) memCtx +{ + struct mapistore_contexts_list *context; + NSString *url; + + context = talloc_zero(memCtx, struct mapistore_contexts_list); + url = [NSString stringWithFormat: @"sogo://%@:%@@%@/", userName, userName, [self MAPIModuleName]]; + context->url = [url asUnicodeInMemCtx: context]; + // context->name = "Inbox"; + context->main_folder = true; + context->role = [self contextRole]; + context->tag = "tag"; + context->prev = context; + + return context; +} + - (void) setupBaseFolder: (NSURL *) newURL { baseFolder = [MAPIStoreInboxFolder baseFolderWithURL: newURL @@ -59,6 +86,11 @@ return @"sent-items"; } ++ (enum mapistore_context_role) contextRole +{ + return MAPISTORE_SENTITEMS_ROLE; +} + - (void) setupBaseFolder: (NSURL *) newURL { baseFolder = [MAPIStoreSentItemsFolder baseFolderWithURL: newURL @@ -75,6 +107,11 @@ return @"drafts"; } ++ (enum mapistore_context_role) contextRole +{ + return MAPISTORE_DRAFTS_ROLE; +} + - (void) setupBaseFolder: (NSURL *) newURL { baseFolder = [MAPIStoreDraftsFolder baseFolderWithURL: newURL @@ -93,6 +130,11 @@ return @"deleted-items"; } ++ (enum mapistore_context_role) contextRole +{ + return MAPISTORE_DELETEDITEMS_ROLE; +} + - (void) setupBaseFolder: (NSURL *) newURL { baseFolder = [MAPIStoreFSFolder baseFolderWithURL: newURL inContext: self]; @@ -115,6 +157,11 @@ return @"outbox"; } ++ (enum mapistore_context_role) contextRole +{ + return MAPISTORE_OUTBOX_ROLE; +} + - (void) setupBaseFolder: (NSURL *) newURL { baseFolder = [MAPIStoreOutboxFolder baseFolderWithURL: newURL diff --git a/OpenChange/MAPIStoreNotesContext.m b/OpenChange/MAPIStoreNotesContext.m index 7171f7da0..b6ca62bc3 100644 --- a/OpenChange/MAPIStoreNotesContext.m +++ b/OpenChange/MAPIStoreNotesContext.m @@ -27,6 +27,9 @@ #import "MAPIStoreNotesContext.h" +#undef DEBUG +#include + @implementation MAPIStoreNotesContext + (NSString *) MAPIModuleName @@ -34,6 +37,23 @@ return @"notes"; } ++ (struct mapistore_contexts_list *) listContextsForUser: (NSString *) userName + inMemCtx: (TALLOC_CTX *) memCtx +{ + struct mapistore_contexts_list *context; + + context = talloc_zero(memCtx, struct mapistore_contexts_list); + context->url = talloc_asprintf (context, "sogo://%s@notes/", + [userName UTF8String]); + // context->name = "Notes personnelles"; + context->main_folder = true; + context->role = MAPISTORE_NOTES_ROLE; + context->tag = "tag"; + context->prev = context; + + return context; +} + - (void) setupBaseFolder: (NSURL *) newURL { baseFolder = [MAPIStoreNotesFolder baseFolderWithURL: newURL diff --git a/OpenChange/MAPIStoreSOGo.m b/OpenChange/MAPIStoreSOGo.m index 527da32ec..7f6878f96 100644 --- a/OpenChange/MAPIStoreSOGo.m +++ b/OpenChange/MAPIStoreSOGo.m @@ -46,6 +46,8 @@ #include #include +static Class MAPIStoreContextK = Nil; + static enum mapistore_error sogo_backend_unexpected_error() { @@ -81,7 +83,7 @@ sogo_backend_init (void) [SOGoSystemDefaults sharedSystemDefaults]; - // /* We force the plugin to base its configuration on the SOGo tree. */ + /* We force the plugin to base its configuration on the SOGo tree. */ ud = [NSUserDefaults standardUserDefaults]; [ud registerDefaults: [ud persistentDomainForName: @"sogod"]]; @@ -98,6 +100,8 @@ sogo_backend_init (void) [[SOGoCache sharedCache] disableRequestsCache]; [[SOGoCache sharedCache] disableLocalCache]; + MAPIStoreContextK = NSClassFromString (@"MAPIStoreContext"); + [pool release]; return MAPISTORE_SUCCESS; @@ -118,7 +122,6 @@ sogo_backend_create_context(TALLOC_CTX *mem_ctx, const char *uri, void **context_object) { NSAutoreleasePool *pool; - Class MAPIStoreContextK; MAPIStoreContext *context; int rc; @@ -126,7 +129,6 @@ sogo_backend_create_context(TALLOC_CTX *mem_ctx, pool = [NSAutoreleasePool new]; - MAPIStoreContextK = NSClassFromString (@"MAPIStoreContext"); if (MAPIStoreContextK) { rc = [MAPIStoreContextK openContext: &context @@ -144,6 +146,33 @@ sogo_backend_create_context(TALLOC_CTX *mem_ctx, return rc; } +static enum mapistore_error +sogo_backend_list_contexts(const char *username, TALLOC_CTX *mem_ctx, + struct mapistore_contexts_list **contexts_listp) +{ + NSAutoreleasePool *pool; + NSString *userName; + int rc; + + DEBUG(0, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__)); + + pool = [NSAutoreleasePool new]; + + if (MAPIStoreContextK) + { + userName = [NSString stringWithUTF8String: username]; + *contexts_listp = [MAPIStoreContextK listAllContextsForUser: userName + inMemCtx: mem_ctx]; + rc = MAPISTORE_SUCCESS; + } + else + rc = MAPISTORE_ERROR; + + [pool release]; + + return rc; +} + // andFID: fid // uint64_t fid, // void **private_data) @@ -1207,6 +1236,7 @@ int mapistore_init_backend(void) backend.backend.namespace = "sogo://"; backend.backend.init = sogo_backend_init; backend.backend.create_context = sogo_backend_create_context; + backend.backend.list_contexts = sogo_backend_list_contexts; backend.context.get_path = sogo_context_get_path; backend.context.get_root_folder = sogo_context_get_root_folder; backend.folder.open_folder = sogo_folder_open_folder; diff --git a/OpenChange/MAPIStoreTasksContext.m b/OpenChange/MAPIStoreTasksContext.m index 1d71c56da..644176aa3 100644 --- a/OpenChange/MAPIStoreTasksContext.m +++ b/OpenChange/MAPIStoreTasksContext.m @@ -27,6 +27,9 @@ #import "MAPIStoreTasksContext.h" +#undef DEBUG +#include + @implementation MAPIStoreTasksContext + (NSString *) MAPIModuleName @@ -34,6 +37,23 @@ return @"tasks"; } ++ (struct mapistore_contexts_list *) listContextsForUser: (NSString *) userName + inMemCtx: (TALLOC_CTX *) memCtx +{ + struct mapistore_contexts_list *context; + + context = talloc_zero(memCtx, struct mapistore_contexts_list); + context->url = talloc_asprintf (context, "sogo://%s@tasks/", + [userName UTF8String]); + // context->name = "Tâches personnelles"; + context->main_folder = true; + context->role = MAPISTORE_TASKS_ROLE; + context->tag = "tag"; + context->prev = context; + + return context; +} + - (void) setupBaseFolder: (NSURL *) newURL { baseFolder = [MAPIStoreTasksFolder baseFolderWithURL: newURL From ada80546fde81969415a003ebf783a23cc8e2fb2 Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Thu, 26 Jan 2012 19:56:24 +0000 Subject: [PATCH 32/68] Monotone-Parent: e89a46b719b80b6d34d9e7a110754e60344dd669 Monotone-Revision: bc440bc0c22c32437dc043e9566760ddd30fea67 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-01-26T19:56:24 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 5 +++++ OpenChange/MAPIStoreFSFolder.m | 5 +++++ OpenChange/MAPIStoreFolder.m | 8 +++++++- OpenChange/MAPIStoreMailFolder.m | 5 +++++ 4 files changed, 22 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index cf40d5ec9..01919d3e1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,10 @@ 2012-01-26 Wolfgang Sourdeau + * OpenChange/MAPIStoreFolder.m (supportsSubFolders): new + overridable method that returns whether the current folder can + contain subfolders, nowithstanding the right of the current user + to create or access them. + * OpenChange/MAPIStoreSOGo.m (sogo_backend_list_contexts): new backend method. diff --git a/OpenChange/MAPIStoreFSFolder.m b/OpenChange/MAPIStoreFSFolder.m index bb6cf3d7b..8e8ab1ef6 100644 --- a/OpenChange/MAPIStoreFSFolder.m +++ b/OpenChange/MAPIStoreFSFolder.m @@ -295,4 +295,9 @@ static NSString *MAPIStoreRightFolderContact = @"RightsFolderContact"; return [self _testRoleForActiveUser: MAPIStoreRightCreateSubfolders]; } +- (BOOL) supportsSubFolders +{ + return YES; +} + @end diff --git a/OpenChange/MAPIStoreFolder.m b/OpenChange/MAPIStoreFolder.m index 155991b42..a8384bb5b 100644 --- a/OpenChange/MAPIStoreFolder.m +++ b/OpenChange/MAPIStoreFolder.m @@ -1141,7 +1141,8 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe access |= 0x02; if (userIsOwner || [self subscriberCanDeleteMessages]) access |= 0x04; - if (userIsOwner || [self subscriberCanCreateSubFolders]) + if ((userIsOwner || [self subscriberCanCreateSubFolders]) + && [self supportsSubFolders]) access |= 0x08; if (userIsOwner || [self subscriberCanCreateMessages]) access |= 0x10; @@ -1595,4 +1596,9 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe return NO; } +- (BOOL) supportsSubFolders +{ + return NO; +} + @end diff --git a/OpenChange/MAPIStoreMailFolder.m b/OpenChange/MAPIStoreMailFolder.m index 5df5bf0ef..57fc1b348 100644 --- a/OpenChange/MAPIStoreMailFolder.m +++ b/OpenChange/MAPIStoreMailFolder.m @@ -1179,6 +1179,11 @@ _parseCOPYUID (NSString *line, NSArray **destUIDsP) return childFolder; } +- (BOOL) supportsSubFolders +{ + return !usesAltNameSpace; +} + @end @implementation MAPIStoreSentItemsFolder : MAPIStoreMailFolder From e942cb8b52422306ae3bf6cf2593a157166f1cf9 Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Thu, 26 Jan 2012 19:56:36 +0000 Subject: [PATCH 33/68] Monotone-Parent: bc440bc0c22c32437dc043e9566760ddd30fea67 Monotone-Revision: a42e52e628a2724b097b0a8aed05b39e75f6a528 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-01-26T19:56:36 Monotone-Branch: ca.inverse.sogo --- OpenChange/MAPIStoreFolder.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/OpenChange/MAPIStoreFolder.h b/OpenChange/MAPIStoreFolder.h index 9bd9491b0..285c2bba3 100644 --- a/OpenChange/MAPIStoreFolder.h +++ b/OpenChange/MAPIStoreFolder.h @@ -165,6 +165,8 @@ - (BOOL) subscriberCanDeleteMessages; - (BOOL) subscriberCanCreateSubFolders; +- (BOOL) supportsSubFolders; /* capability */ + /* subclass helpers */ - (void) postNotificationsForMoveCopyMessagesWithMIDs: (uint64_t *) srcMids andMessageURLs: (NSArray *) oldMessageURLs From 996b6ae8177dc4cc343aff533068f003892ce22e Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Thu, 26 Jan 2012 19:56:56 +0000 Subject: [PATCH 34/68] Monotone-Parent: a42e52e628a2724b097b0a8aed05b39e75f6a528 Monotone-Revision: 74ae59fc584e99eb514b2c1e615eb58cf0a162db Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-01-26T19:56:56 Monotone-Branch: ca.inverse.sogo --- OpenChange/MAPIStoreFallbackContext.m | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/OpenChange/MAPIStoreFallbackContext.m b/OpenChange/MAPIStoreFallbackContext.m index e48b63802..8368671e3 100644 --- a/OpenChange/MAPIStoreFallbackContext.m +++ b/OpenChange/MAPIStoreFallbackContext.m @@ -26,6 +26,9 @@ #import "MAPIStoreFallbackContext.h" +#undef DEBUG +#include + @implementation MAPIStoreFallbackContext + (NSString *) MAPIModuleName @@ -33,6 +36,23 @@ return @"fallback"; } ++ (struct mapistore_contexts_list *) listContextsForUser: (NSString *) userName + inMemCtx: (TALLOC_CTX *) memCtx +{ + struct mapistore_contexts_list *context; + + context = talloc_zero(memCtx, struct mapistore_contexts_list); + context->url = talloc_asprintf (context, "sogo://%s@fallback/", + [userName UTF8String]); + context->name = "Fallback"; + context->main_folder = true; + context->role = MAPISTORE_FALLBACK_ROLE; + context->tag = "tag"; + context->prev = context; + + return context; +} + - (void) setupBaseFolder: (NSURL *) newURL { baseFolder = [MAPIStoreFSFolder baseFolderWithURL: newURL inContext: self]; From 3f4197f2fab1d34e867226f0cc8263143f78febd Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Thu, 26 Jan 2012 19:57:17 +0000 Subject: [PATCH 35/68] Monotone-Parent: 74ae59fc584e99eb514b2c1e615eb58cf0a162db Monotone-Revision: 881d0e4a9a9117f8671dd4758b691044c07a1247 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-01-26T19:57:17 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 11 ++++++ OpenChange/GNUmakefile | 10 ----- OpenChange/MAPIStoreCommonViewsContext.h | 32 ---------------- OpenChange/MAPIStoreCommonViewsContext.m | 34 ----------------- OpenChange/MAPIStoreDeferredActionsContext.h | 32 ---------------- OpenChange/MAPIStoreDeferredActionsContext.m | 36 ------------------ OpenChange/MAPIStoreFreebusyContext.h | 32 ---------------- OpenChange/MAPIStoreFreebusyContext.m | 40 -------------------- OpenChange/MAPIStoreJournalContext.h | 32 ---------------- OpenChange/MAPIStoreJournalContext.m | 36 ------------------ OpenChange/MAPIStoreRemindersContext.h | 32 ---------------- OpenChange/MAPIStoreRemindersContext.m | 36 ------------------ OpenChange/MAPIStoreScheduleContext.h | 32 ---------------- OpenChange/MAPIStoreScheduleContext.m | 36 ------------------ OpenChange/MAPIStoreSearchContext.h | 32 ---------------- OpenChange/MAPIStoreSearchContext.m | 36 ------------------ OpenChange/MAPIStoreShortcutsContext.h | 32 ---------------- OpenChange/MAPIStoreShortcutsContext.m | 36 ------------------ OpenChange/MAPIStoreSpoolerContext.h | 32 ---------------- OpenChange/MAPIStoreSpoolerContext.m | 34 ----------------- OpenChange/MAPIStoreViewsContext.h | 32 ---------------- OpenChange/MAPIStoreViewsContext.m | 36 ------------------ 22 files changed, 11 insertions(+), 690 deletions(-) delete mode 100644 OpenChange/MAPIStoreCommonViewsContext.h delete mode 100644 OpenChange/MAPIStoreCommonViewsContext.m delete mode 100644 OpenChange/MAPIStoreDeferredActionsContext.h delete mode 100644 OpenChange/MAPIStoreDeferredActionsContext.m delete mode 100644 OpenChange/MAPIStoreFreebusyContext.h delete mode 100644 OpenChange/MAPIStoreFreebusyContext.m delete mode 100644 OpenChange/MAPIStoreJournalContext.h delete mode 100644 OpenChange/MAPIStoreJournalContext.m delete mode 100644 OpenChange/MAPIStoreRemindersContext.h delete mode 100644 OpenChange/MAPIStoreRemindersContext.m delete mode 100644 OpenChange/MAPIStoreScheduleContext.h delete mode 100644 OpenChange/MAPIStoreScheduleContext.m delete mode 100644 OpenChange/MAPIStoreSearchContext.h delete mode 100644 OpenChange/MAPIStoreSearchContext.m delete mode 100644 OpenChange/MAPIStoreShortcutsContext.h delete mode 100644 OpenChange/MAPIStoreShortcutsContext.m delete mode 100644 OpenChange/MAPIStoreSpoolerContext.h delete mode 100644 OpenChange/MAPIStoreSpoolerContext.m delete mode 100644 OpenChange/MAPIStoreViewsContext.h delete mode 100644 OpenChange/MAPIStoreViewsContext.m diff --git a/ChangeLog b/ChangeLog index 01919d3e1..17c99fd43 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,16 @@ 2012-01-26 Wolfgang Sourdeau + * OpenChange/MAPIStoreCommonViewsContext.[hm], + OpenChange/MAPIStoreDeferredActionsContext.[hm], + OpenChange/MAPIStoreFreebusyContext.[hm], + OpenChange/MAPIStoreJournalContext.[hm], + OpenChange/MAPIStoreRemindersContext.[hm], + OpenChange/MAPIStoreScheduleContext.[hm], + OpenChange/MAPIStoreSearchContext.[hm], + OpenChange/MAPIStoreShortcutsContext.[hm], + OpenChange/MAPIStoreSpoolerContext.[hm], + OpenChange/MAPIStoreViewsContext.[hm]: deleted obsolete classes. + * OpenChange/MAPIStoreFolder.m (supportsSubFolders): new overridable method that returns whether the current folder can contain subfolders, nowithstanding the right of the current user diff --git a/OpenChange/GNUmakefile b/OpenChange/GNUmakefile index a28abc6d3..822c137e9 100644 --- a/OpenChange/GNUmakefile +++ b/OpenChange/GNUmakefile @@ -102,17 +102,7 @@ $(SOGOBACKEND)_OBJC_FILES += \ MAPIStoreNotesFolder.m \ MAPIStoreNotesMessage.m \ \ - MAPIStoreCommonViewsContext.m \ - MAPIStoreDeferredActionsContext.m \ MAPIStoreFallbackContext.m \ - MAPIStoreFreebusyContext.m \ - MAPIStoreJournalContext.m \ - MAPIStoreRemindersContext.m \ - MAPIStoreScheduleContext.m \ - MAPIStoreSearchContext.m \ - MAPIStoreShortcutsContext.m \ - MAPIStoreSpoolerContext.m \ - MAPIStoreViewsContext.m \ \ NSArray+MAPIStore.m \ NSData+MAPIStore.m \ diff --git a/OpenChange/MAPIStoreCommonViewsContext.h b/OpenChange/MAPIStoreCommonViewsContext.h deleted file mode 100644 index 45dc82085..000000000 --- a/OpenChange/MAPIStoreCommonViewsContext.h +++ /dev/null @@ -1,32 +0,0 @@ -/* MAPIStoreCommonViewsContext.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 MAPISTORECOMMONVIEWSCONTEXT_H -#define MAPISTORECOMMONVIEWSCONTEXT_H - -#import "MAPIStoreFSBaseContext.h" - -@interface MAPIStoreCommonViewsContext : MAPIStoreFSBaseContext - -@end - -#endif /* MAPISTORECOMMONVIEWSCONTEXT_H */ diff --git a/OpenChange/MAPIStoreCommonViewsContext.m b/OpenChange/MAPIStoreCommonViewsContext.m deleted file mode 100644 index 210cbead7..000000000 --- a/OpenChange/MAPIStoreCommonViewsContext.m +++ /dev/null @@ -1,34 +0,0 @@ -/* MAPIStoreCommonViewsContext.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 "MAPIStoreCommonViewsContext.h" - -@implementation MAPIStoreCommonViewsContext - -+ (NSString *) MAPIModuleName -{ - return @"common-views"; -} - -@end diff --git a/OpenChange/MAPIStoreDeferredActionsContext.h b/OpenChange/MAPIStoreDeferredActionsContext.h deleted file mode 100644 index e4686b039..000000000 --- a/OpenChange/MAPIStoreDeferredActionsContext.h +++ /dev/null @@ -1,32 +0,0 @@ -/* MAPIStoreDeferredActionsContext.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 MAPISTOREDEFERREDACTIONSCONTEXT_H -#define MAPISTOREDEFERREDACTIONSCONTEXT_H - -#import "MAPIStoreFSBaseContext.h" - -@interface MAPIStoreDeferredActionsContext : MAPIStoreFSBaseContext - -@end - -#endif /* MAPISTOREDEFERREDACTIONSCONTEXT_H */ diff --git a/OpenChange/MAPIStoreDeferredActionsContext.m b/OpenChange/MAPIStoreDeferredActionsContext.m deleted file mode 100644 index 02bd8699b..000000000 --- a/OpenChange/MAPIStoreDeferredActionsContext.m +++ /dev/null @@ -1,36 +0,0 @@ -/* MAPIStoreDeferredActionsContext.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 "MAPIStoreMapping.h" - -#import "MAPIStoreDeferredActionsContext.h" - -@implementation MAPIStoreDeferredActionsContext - -+ (NSString *) MAPIModuleName -{ - return @"deferred-actions"; -} - -@end diff --git a/OpenChange/MAPIStoreFreebusyContext.h b/OpenChange/MAPIStoreFreebusyContext.h deleted file mode 100644 index 22ce8e9f3..000000000 --- a/OpenChange/MAPIStoreFreebusyContext.h +++ /dev/null @@ -1,32 +0,0 @@ -/* MAPIStoreFreebusyContext.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 MAPISTOREFREEBUSYCONTEXT_H -#define MAPISTOREFREEBUSYCONTEXT_H - -#import "MAPIStoreFSBaseContext.h" - -@interface MAPIStoreFreebusyContext : MAPIStoreFSBaseContext - -@end - -#endif /* MAPISTOREFREEBUSYCONTEXT_H */ diff --git a/OpenChange/MAPIStoreFreebusyContext.m b/OpenChange/MAPIStoreFreebusyContext.m deleted file mode 100644 index ea506e5fc..000000000 --- a/OpenChange/MAPIStoreFreebusyContext.m +++ /dev/null @@ -1,40 +0,0 @@ -/* MAPIStoreFreebusyContext.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 "MAPIApplication.h" -#import "MAPIStoreAuthenticator.h" -#import "MAPIStoreMapping.h" - -#import "MAPIStoreFreebusyContext.h" - -@implementation MAPIStoreFreebusyContext - -+ (NSString *) MAPIModuleName -{ - return @"freebusy"; -} - -@end diff --git a/OpenChange/MAPIStoreJournalContext.h b/OpenChange/MAPIStoreJournalContext.h deleted file mode 100644 index b18c1fcfc..000000000 --- a/OpenChange/MAPIStoreJournalContext.h +++ /dev/null @@ -1,32 +0,0 @@ -/* MAPIStoreJournalContext.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 MAPISTOREJOURNALCONTEXT_H -#define MAPISTOREJOURNALCONTEXT_H - -#import "MAPIStoreFSBaseContext.h" - -@interface MAPIStoreJournalContext : MAPIStoreFSBaseContext - -@end - -#endif /* MAPISTOREJOURNALCONTEXT_H */ diff --git a/OpenChange/MAPIStoreJournalContext.m b/OpenChange/MAPIStoreJournalContext.m deleted file mode 100644 index 309fd56d2..000000000 --- a/OpenChange/MAPIStoreJournalContext.m +++ /dev/null @@ -1,36 +0,0 @@ -/* MAPIStoreJournalContext.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 "MAPIStoreMapping.h" - -#import "MAPIStoreJournalContext.h" - -@implementation MAPIStoreJournalContext - -+ (NSString *) MAPIModuleName -{ - return @"journal"; -} - -@end diff --git a/OpenChange/MAPIStoreRemindersContext.h b/OpenChange/MAPIStoreRemindersContext.h deleted file mode 100644 index 337635110..000000000 --- a/OpenChange/MAPIStoreRemindersContext.h +++ /dev/null @@ -1,32 +0,0 @@ -/* MAPIStoreRemindersContext.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 MAPISTOREREMINDERSCONTEXT_H -#define MAPISTOREREMINDERSCONTEXT_H - -#import "MAPIStoreFSBaseContext.h" - -@interface MAPIStoreRemindersContext : MAPIStoreFSBaseContext - -@end - -#endif /* MAPISTOREREMINDERSCONTEXT_H */ diff --git a/OpenChange/MAPIStoreRemindersContext.m b/OpenChange/MAPIStoreRemindersContext.m deleted file mode 100644 index 262633ed5..000000000 --- a/OpenChange/MAPIStoreRemindersContext.m +++ /dev/null @@ -1,36 +0,0 @@ -/* MAPIStoreRemindersContext.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 "MAPIStoreMapping.h" - -#import "MAPIStoreRemindersContext.h" - -@implementation MAPIStoreRemindersContext - -+ (NSString *) MAPIModuleName -{ - return @"reminders"; -} - -@end diff --git a/OpenChange/MAPIStoreScheduleContext.h b/OpenChange/MAPIStoreScheduleContext.h deleted file mode 100644 index 473299891..000000000 --- a/OpenChange/MAPIStoreScheduleContext.h +++ /dev/null @@ -1,32 +0,0 @@ -/* MAPIStoreScheduleContext.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 MAPISTORESCHEDULECONTEXT_H -#define MAPISTORESCHEDULECONTEXT_H - -#import "MAPIStoreFSBaseContext.h" - -@interface MAPIStoreScheduleContext : MAPIStoreFSBaseContext - -@end - -#endif /* MAPISTORESCHEDULECONTEXT_H */ diff --git a/OpenChange/MAPIStoreScheduleContext.m b/OpenChange/MAPIStoreScheduleContext.m deleted file mode 100644 index 4b0af83d6..000000000 --- a/OpenChange/MAPIStoreScheduleContext.m +++ /dev/null @@ -1,36 +0,0 @@ -/* MAPIStoreScheduleContext.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 "MAPIStoreMapping.h" - -#import "MAPIStoreScheduleContext.h" - -@implementation MAPIStoreScheduleContext - -+ (NSString *) MAPIModuleName -{ - return @"schedule"; -} - -@end diff --git a/OpenChange/MAPIStoreSearchContext.h b/OpenChange/MAPIStoreSearchContext.h deleted file mode 100644 index c8aee3613..000000000 --- a/OpenChange/MAPIStoreSearchContext.h +++ /dev/null @@ -1,32 +0,0 @@ -/* MAPIStoreSearchContext.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 MAPISTORESEARCHCONTEXT_H -#define MAPISTORESEARCHCONTEXT_H - -#import "MAPIStoreFSBaseContext.h" - -@interface MAPIStoreSearchContext : MAPIStoreFSBaseContext - -@end - -#endif /* MAPISTORESEARCHCONTEXT_H */ diff --git a/OpenChange/MAPIStoreSearchContext.m b/OpenChange/MAPIStoreSearchContext.m deleted file mode 100644 index 3820eb22e..000000000 --- a/OpenChange/MAPIStoreSearchContext.m +++ /dev/null @@ -1,36 +0,0 @@ -/* MAPIStoreSearchContext.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 "MAPIStoreMapping.h" - -#import "MAPIStoreSearchContext.h" - -@implementation MAPIStoreSearchContext - -+ (NSString *) MAPIModuleName -{ - return @"search"; -} - -@end diff --git a/OpenChange/MAPIStoreShortcutsContext.h b/OpenChange/MAPIStoreShortcutsContext.h deleted file mode 100644 index 280f84367..000000000 --- a/OpenChange/MAPIStoreShortcutsContext.h +++ /dev/null @@ -1,32 +0,0 @@ -/* MAPIStoreShortcutsContext.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 MAPISTORESHORTCUTSCONTEXT_H -#define MAPISTORESHORTCUTSCONTEXT_H - -#import "MAPIStoreFSBaseContext.h" - -@interface MAPIStoreShortcutsContext : MAPIStoreFSBaseContext - -@end - -#endif /* MAPISTORESHORTCUTSCONTEXT_H */ diff --git a/OpenChange/MAPIStoreShortcutsContext.m b/OpenChange/MAPIStoreShortcutsContext.m deleted file mode 100644 index 66a7aa1ce..000000000 --- a/OpenChange/MAPIStoreShortcutsContext.m +++ /dev/null @@ -1,36 +0,0 @@ -/* MAPIStoreShortcutsContext.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 "MAPIStoreMapping.h" - -#import "MAPIStoreShortcutsContext.h" - -@implementation MAPIStoreShortcutsContext - -+ (NSString *) MAPIModuleName -{ - return @"shortcuts"; -} - -@end diff --git a/OpenChange/MAPIStoreSpoolerContext.h b/OpenChange/MAPIStoreSpoolerContext.h deleted file mode 100644 index 466f2c063..000000000 --- a/OpenChange/MAPIStoreSpoolerContext.h +++ /dev/null @@ -1,32 +0,0 @@ -/* MAPIStoreSpoolerContext.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 MAPISTORESPOOLERCONTEXT_H -#define MAPISTORESPOOLERCONTEXT_H - -#import "MAPIStoreFSBaseContext.h" - -@interface MAPIStoreSpoolerContext : MAPIStoreFSBaseContext - -@end - -#endif /* MAPISTORESPOOLERCONTEXT_H */ diff --git a/OpenChange/MAPIStoreSpoolerContext.m b/OpenChange/MAPIStoreSpoolerContext.m deleted file mode 100644 index 92d531ca8..000000000 --- a/OpenChange/MAPIStoreSpoolerContext.m +++ /dev/null @@ -1,34 +0,0 @@ -/* MAPIStoreSpoolerContext.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 "MAPIStoreSpoolerContext.h" - -@implementation MAPIStoreSpoolerContext - -+ (NSString *) MAPIModuleName -{ - return @"spooler-queue"; -} - -@end diff --git a/OpenChange/MAPIStoreViewsContext.h b/OpenChange/MAPIStoreViewsContext.h deleted file mode 100644 index 7f6d31783..000000000 --- a/OpenChange/MAPIStoreViewsContext.h +++ /dev/null @@ -1,32 +0,0 @@ -/* MAPIStoreViewsContext.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 MAPISTOREVIEWSCONTEXT_H -#define MAPISTOREVIEWSCONTEXT_H - -#import "MAPIStoreFSBaseContext.h" - -@interface MAPIStoreViewsContext : MAPIStoreFSBaseContext - -@end - -#endif /* MAPISTOREVIEWSCONTEXT_H */ diff --git a/OpenChange/MAPIStoreViewsContext.m b/OpenChange/MAPIStoreViewsContext.m deleted file mode 100644 index a5a9a9940..000000000 --- a/OpenChange/MAPIStoreViewsContext.m +++ /dev/null @@ -1,36 +0,0 @@ -/* MAPIStoreViewsContext.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 "MAPIStoreMapping.h" - -#import "MAPIStoreViewsContext.h" - -@implementation MAPIStoreViewsContext - -+ (NSString *) MAPIModuleName -{ - return @"views"; -} - -@end From e6806a7bc96a2b85494788bee6503b20ac708f8e Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Fri, 27 Jan 2012 15:24:00 +0000 Subject: [PATCH 36/68] Fixed merge Monotone-Parent: b1c3c4abdd41b8895de8a28a2a5fe6ce0af01caf Monotone-Revision: 8daffcffafece4be7e0553a181f9186e385e2b77 Monotone-Author: ludovic@Sophos.ca Monotone-Date: 2012-01-27T15:24:00 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 4 ---- 1 file changed, 4 deletions(-) diff --git a/ChangeLog b/ChangeLog index 2ea57bdc1..8eca5567f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,3 @@ -<<<<<<< variant A 2012-01-27 Ludovic Marcotte * SoObjects/Appointments/SOGoAppointmentFolder.m @@ -9,7 +8,6 @@ (_folderCalenars) - we now ask for the c_content and use a local autorelease pool to avoid consuming too much memory ->>>>>>> variant B 2012-01-26 Wolfgang Sourdeau * OpenChange/MAPIStoreCommonViewsContext.[hm], @@ -38,8 +36,6 @@ (+listContextsForUser:inMemCtx:): new individual method invoked by the above. Overridden by concrete subclasses. -####### Ancestor -======= end 2012-01-26 Ludovic Marcotte * SoObjects/SOGo/LDAPSource.{h,m} - now honor From 3ce82f744d247ab8c69c6381aa77526b8e01ba61 Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Sun, 29 Jan 2012 19:40:44 +0000 Subject: [PATCH 37/68] Monotone-Parent: 881d0e4a9a9117f8671dd4758b691044c07a1247 Monotone-Revision: 6e52ef029c1ba3330e77bd6dcc994c86557f4533 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-01-29T19:40:44 Monotone-Branch: ca.inverse.sogo --- OpenChange/GNUmakefile | 1 + OpenChange/MAPIStoreUserContext.h | 82 ++++++++++ OpenChange/MAPIStoreUserContext.m | 241 ++++++++++++++++++++++++++++++ 3 files changed, 324 insertions(+) create mode 100644 OpenChange/MAPIStoreUserContext.h create mode 100644 OpenChange/MAPIStoreUserContext.m diff --git a/OpenChange/GNUmakefile b/OpenChange/GNUmakefile index 822c137e9..0f3fb1a39 100644 --- a/OpenChange/GNUmakefile +++ b/OpenChange/GNUmakefile @@ -39,6 +39,7 @@ $(SOGOBACKEND)_OBJC_FILES += \ MAPIStoreTypes.m \ MAPIStorePropertySelectors.m \ MAPIStoreSamDBUtils.m \ + MAPIStoreUserContext.m \ \ SOGoMAPIVolatileMessage.m \ SOGoMAPIFSFolder.m \ diff --git a/OpenChange/MAPIStoreUserContext.h b/OpenChange/MAPIStoreUserContext.h new file mode 100644 index 000000000..3b986988f --- /dev/null +++ b/OpenChange/MAPIStoreUserContext.h @@ -0,0 +1,82 @@ +/* MAPIStoreUserContext.h - this file is part of $PROJECT_NAME_HERE$ + * + * 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 MAPISTOREUSERCONTEXT_H +#define MAPISTOREUSERCONTEXT_H + +#import + +@class NSString; + +@class WOContext; + +@class SOGoAppointmentFolders; +@class SOGoContactFolders; +@class SOGoMailAccount; +@class SOGoUser; +@class SOGoUserFolder; + +@class MAPIStoreAuthenticator; +@class MAPIStoreMapping; + +@interface MAPIStoreUserContext : NSObject +{ + NSString *username; + SOGoUser *sogoUser; + + SOGoUserFolder *userFolder; + SOGoAppointmentFolders *calendarRoot; + SOGoContactFolders *contactsRoot; + SOGoMailAccount *mailRoot; /* only one account supported */ + NSMutableArray *containersBag; + + MAPIStoreMapping *mapping; + + WOContext *woContext; + MAPIStoreAuthenticator *authenticator; +} + ++ (id) userContextWithUsername: (NSString *) username + andTDBIndexing: (struct tdb_wrap *) indexingTdb; + +- (id) initWithUsername: (NSString *) newUsername + andTDBIndexing: (struct tdb_wrap *) indexingTdb; + +- (NSString *) username; +- (SOGoUser *) sogoUser; + +- (SOGoUserFolder *) userFolder; + +- (SOGoAppointmentFolders *) calendarRoot; +- (SOGoContactFolders *) contactsRoot; +- (SOGoMailAccount *) mailRoot; + +- (MAPIStoreMapping *) mapping; + +/* SOGo hacky magic */ +- (void) activateWithUser: (SOGoUser *) activeUser; +- (MAPIStoreAuthenticator *) authenticator; +- (WOContext *) woContext; + +@end + +#endif /* MAPISTOREUSERCONTEXT_H */ diff --git a/OpenChange/MAPIStoreUserContext.m b/OpenChange/MAPIStoreUserContext.m new file mode 100644 index 000000000..c31777133 --- /dev/null +++ b/OpenChange/MAPIStoreUserContext.m @@ -0,0 +1,241 @@ +/* MAPIStoreUserContext.m - this file is part of $PROJECT_NAME_HERE$ + * + * 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. + */ + +#import +#import + +#import +#import + +#import + +#import +#import +#import +#import + +#import "MAPIApplication.h" +#import "MAPIStoreAuthenticator.h" +#import "MAPIStoreMapping.h" + +#import "MAPIStoreUserContext.h" + +static NSMapTable *contextsTable = nil; + +@implementation MAPIStoreUserContext + ++ (void) initialize +{ + contextsTable = [NSMapTable mapTableWithStrongToWeakObjects]; + [contextsTable retain]; +} + ++ (id) userContextWithUsername: (NSString *) username + andTDBIndexing: (struct tdb_wrap *) indexingTdb; +{ + id userContext; + + userContext = [contextsTable objectForKey: username]; + if (!userContext) + { + userContext = [[self alloc] initWithUsername: username + andTDBIndexing: indexingTdb]; + [userContext autorelease]; + } + + return userContext; +} + +- (id) init +{ + if ((self = [super init])) + { + username = nil; + sogoUser = nil; + + userFolder = nil; + calendarRoot = nil; + contactsRoot = nil; + mailRoot = nil; + + containersBag = [NSMutableArray new]; + + mapping = nil; + + authenticator = nil; + woContext = [WOContext contextWithRequest: nil]; + [woContext retain]; + } + + return self; +} + +- (id) initWithUsername: (NSString *) newUsername + andTDBIndexing: (struct tdb_wrap *) indexingTdb +{ + if ((self = [self init])) + { + /* "username" will be retained by table */ + username = newUsername; + if (indexingTdb) + ASSIGN (mapping, [MAPIStoreMapping mappingForUsername: username + withIndexing: indexingTdb]); + [contextsTable setObject: self forKey: username]; + + authenticator = [MAPIStoreAuthenticator new]; + [authenticator setUsername: username]; + + /* TODO: very hackish (IMAP access) */ + [authenticator setPassword: username]; + } + + return self; +} + +- (void) dealloc +{ + [contextsTable removeObjectForKey: username]; + + [userFolder release]; + [calendarRoot release]; + [contactsRoot release]; + [mailRoot release]; + [containersBag release]; + + [authenticator release]; + [mapping release]; + + [username release]; + [sogoUser release]; + + [super dealloc]; +} + +- (NSString *) username +{ + return username; +} + +- (SOGoUser *) sogoUser +{ + if (!sogoUser) + ASSIGN (sogoUser, [SOGoUser userWithLogin: username]); + + return sogoUser; +} + +- (SOGoUserFolder *) userFolder +{ + if (!userFolder) + { + userFolder = [SOGoUserFolder objectWithName: username + inContainer: MAPIApp]; + [userFolder retain]; + } + + return userFolder; +} + +- (SOGoAppointmentFolders *) calendarRoot +{ + if (!calendarRoot) + { + [self userFolder]; + [woContext setClientObject: userFolder]; + + calendarRoot = [userFolder lookupName: @"Calendar" + inContext: woContext + acquire: NO]; + [calendarRoot retain]; + } + + return calendarRoot; +} + +- (SOGoContactFolders *) contactsRoot +{ + if (!contactsRoot) + { + [self userFolder]; + [woContext setClientObject: userFolder]; + + contactsRoot = [userFolder lookupName: @"Contacts" + inContext: woContext + acquire: NO]; + [contactsRoot retain]; + } + + return contactsRoot; +} + +- (SOGoMailAccount *) mailRoot +{ + SOGoMailAccounts *accountsFolder; + + if (!mailRoot) + { + [self userFolder]; + [woContext setClientObject: userFolder]; + + accountsFolder = [userFolder lookupName: @"Mail" + inContext: woContext + acquire: NO]; + [containersBag addObject: accountsFolder]; + [woContext setClientObject: accountsFolder]; + + mailRoot = [accountsFolder lookupName: @"0" + inContext: woContext + acquire: NO]; + [[mailRoot imap4Connection] + enableExtension: @"QRESYNC"]; + [mailRoot retain]; + } + + return mailRoot; +} + +- (MAPIStoreMapping *) mapping +{ + return mapping; +} + +- (WOContext *) woContext +{ + return woContext; +} + +- (MAPIStoreAuthenticator *) authenticator +{ + return authenticator; +} + +- (void) activateWithUser: (SOGoUser *) activeUser; +{ + NSMutableDictionary *info; + + [MAPIApp setUserContext: self]; + [woContext setActiveUser: activeUser]; + info = [[NSThread currentThread] threadDictionary]; + [info setObject: woContext forKey: @"WOContext"]; +} + +@end From 7147aec5d5327a5ece0b8fd5ee01bb5c9bff90d7 Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Sun, 29 Jan 2012 19:41:50 +0000 Subject: [PATCH 38/68] Monotone-Parent: 91b83ade7a4eee6cb72c4f6f7e456706d0484cf0 Monotone-Revision: 15538f1cc9f8c3f555da20f31dcbe5ec191642e4 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-01-29T19:41:50 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/ChangeLog b/ChangeLog index 8eca5567f..26ede5240 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +2012-01-29 Wolfgang Sourdeau + + * OpenChange/MAPIStoreUserContext.[hm]: new class for accessing + user data common to all "mapistore contexts" as a singleton: the + same instance is used across all requests until all related + objects have been freed. + 2012-01-27 Ludovic Marcotte * SoObjects/Appointments/SOGoAppointmentFolder.m From a7da84c8048993c82e63738b2bf3d6d76761f83f Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Sun, 29 Jan 2012 19:43:09 +0000 Subject: [PATCH 39/68] Monotone-Parent: 15538f1cc9f8c3f555da20f31dcbe5ec191642e4 Monotone-Revision: 4e23e038f2b0b7eddf8b30700b8c9a8910768f98 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-01-29T19:43:09 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 3 +++ .../Appointments/SOGoAppointmentFolder.m | 5 ---- .../Appointments/SOGoAppointmentFolders.h | 1 + SoObjects/Contacts/SOGoContactGCSFolder.m | 5 ---- SoObjects/Mailer/SOGoMailFolder.m | 26 ------------------- SoObjects/Mailer/SOGoSentFolder.m | 6 ----- SoObjects/Mailer/SOGoTrashFolder.m | 6 ----- SoObjects/SOGo/SOGoFolder.h | 3 --- SoObjects/SOGo/SOGoFolder.m | 7 ----- 9 files changed, 4 insertions(+), 58 deletions(-) diff --git a/ChangeLog b/ChangeLog index 26ede5240..aa4ac5b72 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2012-01-29 Wolfgang Sourdeau + * SoObjects/SOGo/SOGoFolder.m (-outlookFolderClass): removed + useless method. + * OpenChange/MAPIStoreUserContext.[hm]: new class for accessing user data common to all "mapistore contexts" as a singleton: the same instance is used across all requests until all related diff --git a/SoObjects/Appointments/SOGoAppointmentFolder.m b/SoObjects/Appointments/SOGoAppointmentFolder.m index 882f5cac9..cedffbb96 100644 --- a/SoObjects/Appointments/SOGoAppointmentFolder.m +++ b/SoObjects/Appointments/SOGoAppointmentFolder.m @@ -2617,11 +2617,6 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir return @"Appointment"; } -- (NSString *) outlookFolderClass -{ - return @"IPF.Appointment"; -} - - (BOOL) isActive { SOGoUserSettings *settings; diff --git a/SoObjects/Appointments/SOGoAppointmentFolders.h b/SoObjects/Appointments/SOGoAppointmentFolders.h index 36dff6dc7..a4b84a6dc 100644 --- a/SoObjects/Appointments/SOGoAppointmentFolders.h +++ b/SoObjects/Appointments/SOGoAppointmentFolders.h @@ -26,6 +26,7 @@ #import @class NSArray; +@class NSMutableArray; @class SOGoWebAppointmentFolder; diff --git a/SoObjects/Contacts/SOGoContactGCSFolder.m b/SoObjects/Contacts/SOGoContactGCSFolder.m index 9fa758e75..c98e2ecd5 100644 --- a/SoObjects/Contacts/SOGoContactGCSFolder.m +++ b/SoObjects/Contacts/SOGoContactGCSFolder.m @@ -376,11 +376,6 @@ static NSArray *folderListingFields = nil; return @"Contact"; } -- (NSString *) outlookFolderClass -{ - return @"IPF.Contact"; -} - /* TODO: multiget reorg */ - (NSString *) _nodeTagForProperty: (NSString *) property { diff --git a/SoObjects/Mailer/SOGoMailFolder.m b/SoObjects/Mailer/SOGoMailFolder.m index ab8723508..fbcc73f7e 100644 --- a/SoObjects/Mailer/SOGoMailFolder.m +++ b/SoObjects/Mailer/SOGoMailFolder.m @@ -889,32 +889,6 @@ static NSString *defaultUserID = @"anyone"; return @"Mail"; } -- (NSString *) outlookFolderClass -{ - // TODO: detect Trash/Sent/Drafts folders - SOGoMailAccount *account; - NSString *name; - - if (!folderType) - { - account = [self mailAccountFolder]; - name = [self traversalFromMailAccount]; - - if ([name isEqualToString: [account trashFolderNameInContext: nil]]) - folderType = @"IPF.Trash"; - else if ([name - isEqualToString: [account inboxFolderNameInContext: nil]]) - folderType = @"IPF.Inbox"; - else if ([name - isEqualToString: [account sentFolderNameInContext: nil]]) - folderType = @"IPF.Sent"; - else - folderType = @"IPF.Folder"; - } - - return folderType; -} - /* acls */ - (NSArray *) _imapAclsToSOGoAcls: (NSString *) imapAcls diff --git a/SoObjects/Mailer/SOGoSentFolder.m b/SoObjects/Mailer/SOGoSentFolder.m index 92648a793..9dce47464 100644 --- a/SoObjects/Mailer/SOGoSentFolder.m +++ b/SoObjects/Mailer/SOGoSentFolder.m @@ -25,10 +25,4 @@ @implementation SOGoSentFolder -/* folder type */ - -- (NSString *)outlookFolderClass { - return @"IPF.Sent"; -} - @end /* SOGoSentFolder */ diff --git a/SoObjects/Mailer/SOGoTrashFolder.m b/SoObjects/Mailer/SOGoTrashFolder.m index b034bfb16..e0e224af0 100644 --- a/SoObjects/Mailer/SOGoTrashFolder.m +++ b/SoObjects/Mailer/SOGoTrashFolder.m @@ -25,10 +25,4 @@ @implementation SOGoTrashFolder -/* folder type */ - -- (NSString *)outlookFolderClass { - return @"IPF.Trash"; -} - @end /* SOGoTrashFolder */ diff --git a/SoObjects/SOGo/SOGoFolder.h b/SoObjects/SOGo/SOGoFolder.h index d51076805..1fa1c6ef8 100644 --- a/SoObjects/SOGo/SOGoFolder.h +++ b/SoObjects/SOGo/SOGoFolder.h @@ -54,9 +54,6 @@ /* dav */ - (NSArray *) davResourceType; -/* outlook */ -- (NSString *) outlookFolderClass; - /* email advisories */ - (void) sendFolderAdvisoryTemplate: (NSString *) template; diff --git a/SoObjects/SOGo/SOGoFolder.m b/SoObjects/SOGo/SOGoFolder.m index adb8e4c4c..a27466cdd 100644 --- a/SoObjects/SOGo/SOGoFolder.m +++ b/SoObjects/SOGo/SOGoFolder.m @@ -563,13 +563,6 @@ isEqualToString: [otherFolder nameInContainer]]); } -- (NSString *) outlookFolderClass -{ - [self subclassResponsibility: _cmd]; - - return nil; -} - /* acls */ - (NSString *) defaultUserID From 4b81cb167d98f5d2f2570186655396366de80954 Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Sun, 29 Jan 2012 19:55:21 +0000 Subject: [PATCH 40/68] Monotone-Parent: 4e23e038f2b0b7eddf8b30700b8c9a8910768f98 Monotone-Revision: 9235e5dc4d151a3cba8ad842ac39e1b2d18c8201 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-01-29T19:55:21 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 31 +++ OpenChange/MAPIApplication.h | 6 +- OpenChange/MAPIApplication.m | 10 +- OpenChange/MAPIStoreAttachment.m | 2 +- OpenChange/MAPIStoreCalendarContext.m | 23 +- OpenChange/MAPIStoreCalendarFolder.m | 31 --- OpenChange/MAPIStoreCalendarMessage.m | 11 +- OpenChange/MAPIStoreContactsContext.m | 25 +- OpenChange/MAPIStoreContactsFolder.m | 31 --- OpenChange/MAPIStoreContext.h | 31 +-- OpenChange/MAPIStoreContext.m | 280 ++++++++++------------ OpenChange/MAPIStoreFAIMessage.m | 5 +- OpenChange/MAPIStoreFSBaseContext.m | 30 ++- OpenChange/MAPIStoreFSFolder.m | 19 +- OpenChange/MAPIStoreFallbackContext.m | 9 +- OpenChange/MAPIStoreFolder.h | 8 +- OpenChange/MAPIStoreFolder.m | 196 ++++++++------- OpenChange/MAPIStoreGCSFolder.m | 34 ++- OpenChange/MAPIStoreGCSMessage.m | 14 +- OpenChange/MAPIStoreMailContext.h | 12 - OpenChange/MAPIStoreMailContext.m | 184 ++++++-------- OpenChange/MAPIStoreMailFolder.h | 21 +- OpenChange/MAPIStoreMailFolder.m | 243 +------------------ OpenChange/MAPIStoreMailMessage.m | 10 +- OpenChange/MAPIStoreMailVolatileMessage.m | 4 +- OpenChange/MAPIStoreMessage.m | 23 +- OpenChange/MAPIStoreNotesContext.m | 8 +- OpenChange/MAPIStoreObject.h | 7 +- OpenChange/MAPIStoreObject.m | 15 +- OpenChange/MAPIStoreSOGo.m | 5 +- OpenChange/MAPIStoreTasksContext.m | 24 +- OpenChange/MAPIStoreTasksFolder.m | 31 --- OpenChange/SOGoMAPIFSFolder.m | 6 +- 33 files changed, 537 insertions(+), 852 deletions(-) diff --git a/ChangeLog b/ChangeLog index aa4ac5b72..3be7dbf50 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,36 @@ 2012-01-29 Wolfgang Sourdeau + * OpenChange/MAPIStoreMailContext.[hm]: removed + "MAPIStoreInboxContext", "MAPIStoreSentItemsContext", + and child folders."MAPIStoreDraftsContext" and + "MAPIStoreOutboxContext" which have made obsolete by the new + provisioning and instantiation mechanisms. + + * OpenChange/MAPIStoreContext.m (-woContext:, -mapping) + (-authenticator): those methods are now part of + MAPIStoreUserContext. + (-setupBaseFolder:): removed method, obsoleted by those below. + (-getRootFolder:withFID:): the "root folder" is now instantiated + by lookups from the root folder provided by the user context and + the chain of folders listed in the context url path. + (-MAPIStoreFolderClass): new method returning the Class object + representing the context's class of objects. + (-rootSOGoFolder): new method that returns the proper root folder + depending on the context's class of objects. + + * OpenChange/MAPIStoreFolder.m (-initWithURL:inContext:): all + folders are now instantiated the same way since root objects are + now stored in the MAPIStoreUserContext instancesB. + (-setContext:): new setter to provide a reference to the folder's + mapistore context instance from the topmost parent. + (-setupVersionsMessage:): new helper method invoked during the + folder instantiations, from the moment its parent context has been + made available, which does not occur at the same moment for parent + and child folders... + + * OpenChange/MAPIApplication.m (-init): removed the "mapiContext" + but added the "userContext" ivars. + * SoObjects/SOGo/SOGoFolder.m (-outlookFolderClass): removed useless method. diff --git a/OpenChange/MAPIApplication.h b/OpenChange/MAPIApplication.h index 83e02fe6b..2b30e692c 100644 --- a/OpenChange/MAPIApplication.h +++ b/OpenChange/MAPIApplication.h @@ -25,16 +25,16 @@ #import -@class MAPIStoreContext; +@class MAPIStoreUserContext; @interface MAPIApplication : SoApplication { - MAPIStoreContext *mapiContext; + MAPIStoreUserContext *userContext; } - (id) authenticatorInContext: (id) context; -- (void) setMAPIStoreContext: (MAPIStoreContext *) newMAPIStoreContext; +- (void) setUserContext: (MAPIStoreUserContext *) newContext; @end diff --git a/OpenChange/MAPIApplication.m b/OpenChange/MAPIApplication.m index d3002940e..ccdb59614 100644 --- a/OpenChange/MAPIApplication.m +++ b/OpenChange/MAPIApplication.m @@ -27,7 +27,7 @@ #import -#import "MAPIStoreContext.h" +#import "MAPIStoreUserContext.h" #import "MAPIApplication.h" @@ -69,18 +69,18 @@ MAPIApplication *MAPIApp = nil; - (void) dealloc { - [mapiContext release]; + [userContext release]; [super dealloc]; } -- (void) setMAPIStoreContext: (MAPIStoreContext *) newMAPIStoreContext +- (void) setUserContext: (MAPIStoreUserContext *) newContext { - ASSIGN (mapiContext, newMAPIStoreContext); + ASSIGN (userContext, newContext); } - (id) authenticatorInContext: (id) context { - return [mapiContext authenticator]; + return [userContext authenticator]; } @end diff --git a/OpenChange/MAPIStoreAttachment.m b/OpenChange/MAPIStoreAttachment.m index 483944a1c..ca9076ad0 100644 --- a/OpenChange/MAPIStoreAttachment.m +++ b/OpenChange/MAPIStoreAttachment.m @@ -100,7 +100,7 @@ mapistoreMsg = talloc_zero (memCtx, struct mapistore_message); - mapping = [[self context] mapping]; + mapping = [self mapping]; attMessage = [self openEmbeddedMessage]; if (attMessage) diff --git a/OpenChange/MAPIStoreCalendarContext.m b/OpenChange/MAPIStoreCalendarContext.m index 8f05ceb17..7a6622b1d 100644 --- a/OpenChange/MAPIStoreCalendarContext.m +++ b/OpenChange/MAPIStoreCalendarContext.m @@ -21,29 +21,39 @@ */ #import +#import #import "MAPIStoreMapping.h" #import "MAPIStoreCalendarFolder.h" +#import "MAPIStoreUserContext.h" #import "MAPIStoreCalendarContext.h" #undef DEBUG #include +static Class MAPIStoreCalendarFolderK; + @implementation MAPIStoreCalendarContext ++ (void) initialize +{ + MAPIStoreCalendarFolderK = [MAPIStoreCalendarFolder class]; +} + + (NSString *) MAPIModuleName { return @"calendar"; } + (struct mapistore_contexts_list *) listContextsForUser: (NSString *) userName + withTDBIndexing: (struct tdb_wrap *) indexingTdb inMemCtx: (TALLOC_CTX *) memCtx { struct mapistore_contexts_list *context; context = talloc_zero(memCtx, struct mapistore_contexts_list); - context->url = talloc_asprintf (context, "sogo://%s@calendar/", + context->url = talloc_asprintf (context, "sogo://%s@calendar/personal", [userName UTF8String]); // context->name = "Agenda personnel"; context->main_folder = true; @@ -54,11 +64,14 @@ return context; } -- (void) setupBaseFolder: (NSURL *) newURL +- (Class) MAPIStoreFolderClass { - baseFolder = [MAPIStoreCalendarFolder baseFolderWithURL: newURL - inContext: self]; - [baseFolder retain]; + return MAPIStoreCalendarFolderK; +} + +- (id) rootSOGoFolder +{ + return [userContext calendarRoot]; } @end diff --git a/OpenChange/MAPIStoreCalendarFolder.m b/OpenChange/MAPIStoreCalendarFolder.m index b7cdd5466..f2ec0a4dd 100644 --- a/OpenChange/MAPIStoreCalendarFolder.m +++ b/OpenChange/MAPIStoreCalendarFolder.m @@ -43,37 +43,6 @@ @implementation MAPIStoreCalendarFolder -- (id) initWithURL: (NSURL *) newURL - inContext: (MAPIStoreContext *) newContext -{ - SOGoUserFolder *userFolder; - SOGoAppointmentFolders *parentFolder; - WOContext *woContext; - - if ((self = [super initWithURL: newURL - inContext: newContext])) - { - woContext = [newContext woContext]; - userFolder = [SOGoUserFolder objectWithName: [newURL user] - inContainer: MAPIApp]; - [parentContainersBag addObject: userFolder]; - [woContext setClientObject: userFolder]; - - parentFolder = [userFolder lookupName: @"Calendar" - inContext: woContext - acquire: NO]; - [parentContainersBag addObject: parentFolder]; - [woContext setClientObject: parentFolder]; - - sogoObject = [parentFolder lookupName: @"personal" - inContext: woContext - acquire: NO]; - [sogoObject retain]; - } - - return self; -} - - (MAPIStoreMessageTable *) messageTable { [self synchroniseCache]; diff --git a/OpenChange/MAPIStoreCalendarMessage.m b/OpenChange/MAPIStoreCalendarMessage.m index aa46a8962..a27ae244c 100644 --- a/OpenChange/MAPIStoreCalendarMessage.m +++ b/OpenChange/MAPIStoreCalendarMessage.m @@ -56,6 +56,7 @@ #import "MAPIStoreMapping.h" #import "MAPIStoreRecurrenceUtils.h" #import "MAPIStoreTypes.h" +#import "MAPIStoreUserContext.h" #import "NSDate+MAPIStore.h" #import "NSData+MAPIStore.h" #import "NSObject+MAPIStore.h" @@ -106,14 +107,16 @@ { iCalEvent *event; MAPIStoreContext *context; + MAPIStoreUserContext *userContext; if (!appointmentWrapper) { event = [sogoObject component: NO secure: YES]; context = [self context]; + userContext = [self userContext]; ASSIGN (appointmentWrapper, [MAPIStoreAppointmentWrapper wrapperWithICalEvent: event - andUser: [context ownerUser] + andUser: [userContext sogoUser] andSenderEmail: nil inTimeZone: [self ownerTimeZone] withConnectionInfo: [context connectionInfo]]); @@ -551,7 +554,7 @@ existingCName = [[container sogoObject] resourceNameForEventUID: uid]; if (existingCName) { - mapping = [[self context] mapping]; + mapping = [self mapping]; /* dissociate the object url from the old object's id */ existingURL = [NSString stringWithFormat: @"%@%@", @@ -568,7 +571,7 @@ [mapping registerURL: existingURL withID: objectId]; /* reinstantiate the old sogo object and attach it to self */ - woContext = [[self context] woContext]; + woContext = [[self userContext] woContext]; existingObject = [[container sogoObject] lookupName: existingCName inContext: woContext acquire: NO]; @@ -686,7 +689,7 @@ vCalendar = [iCalCalendar parseSingleFromSource: content]; newEvent = [[vCalendar events] objectAtIndex: 0]; - ownerUser = [[self context] ownerUser]; + ownerUser = [[self userContext] sogoUser]; userPerson = [newEvent userAsAttendee: ownerUser]; [newEvent setTimeStampAsDate: now]; diff --git a/OpenChange/MAPIStoreContactsContext.m b/OpenChange/MAPIStoreContactsContext.m index 34b6257e8..7c4052055 100644 --- a/OpenChange/MAPIStoreContactsContext.m +++ b/OpenChange/MAPIStoreContactsContext.m @@ -22,28 +22,38 @@ #import +#import + #import "MAPIStoreContactsFolder.h" -#import "MAPIStoreMapping.h" +#import "MAPIStoreUserContext.h" #import "MAPIStoreContactsContext.h" #undef DEBUG #include +static Class MAPIStoreContactsFolderK; + @implementation MAPIStoreContactsContext ++ (void) initialize +{ + MAPIStoreContactsFolderK = [MAPIStoreContactsFolder class]; +} + + (NSString *) MAPIModuleName { return @"contacts"; } + (struct mapistore_contexts_list *) listContextsForUser: (NSString *) userName + withTDBIndexing: (struct tdb_wrap *) indexingTdb inMemCtx: (TALLOC_CTX *) memCtx { struct mapistore_contexts_list *context; context = talloc_zero(memCtx, struct mapistore_contexts_list); - context->url = talloc_asprintf (context, "sogo://%s@contacts/", + context->url = talloc_asprintf (context, "sogo://%s@contacts/personal", [userName UTF8String]); // context->name = "Carnet d'adresses personnel"; context->main_folder = true; @@ -54,11 +64,14 @@ return context; } -- (void) setupBaseFolder: (NSURL *) newURL +- (Class) MAPIStoreFolderClass { - baseFolder = [MAPIStoreContactsFolder baseFolderWithURL: newURL - inContext: self]; - [baseFolder retain]; + return MAPIStoreContactsFolderK; +} + +- (id) rootSOGoFolder +{ + return [userContext contactsRoot]; } @end diff --git a/OpenChange/MAPIStoreContactsFolder.m b/OpenChange/MAPIStoreContactsFolder.m index ba3d36c6d..6485a3af8 100644 --- a/OpenChange/MAPIStoreContactsFolder.m +++ b/OpenChange/MAPIStoreContactsFolder.m @@ -39,37 +39,6 @@ @implementation MAPIStoreContactsFolder -- (id) initWithURL: (NSURL *) newURL - inContext: (MAPIStoreContext *) newContext -{ - SOGoUserFolder *userFolder; - SOGoContactFolders *parentFolder; - WOContext *woContext; - - if ((self = [super initWithURL: newURL - inContext: newContext])) - { - woContext = [newContext woContext]; - userFolder = [SOGoUserFolder objectWithName: [newURL user] - inContainer: MAPIApp]; - [parentContainersBag addObject: userFolder]; - [woContext setClientObject: userFolder]; - - parentFolder = [userFolder lookupName: @"Contacts" - inContext: woContext - acquire: NO]; - [parentContainersBag addObject: parentFolder]; - [woContext setClientObject: parentFolder]; - - sogoObject = [parentFolder lookupName: @"personal" - inContext: woContext - acquire: NO]; - [sogoObject retain]; - } - - return self; -} - - (MAPIStoreMessageTable *) messageTable { [self synchroniseCache]; diff --git a/OpenChange/MAPIStoreContext.h b/OpenChange/MAPIStoreContext.h index 88c902e42..1ff8dc609 100644 --- a/OpenChange/MAPIStoreContext.h +++ b/OpenChange/MAPIStoreContext.h @@ -47,31 +47,26 @@ @class MAPIStoreAttachment; @class MAPIStoreAttachmentTable; @class MAPIStoreFolder; -@class MAPIStoreMapping; @class MAPIStoreMessage; @class MAPIStoreTable; +@class MAPIStoreUserContext; @interface MAPIStoreContext : NSObject { - struct mapistore_context *mstoreCtx; struct mapistore_connection_info *connInfo; SOGoUser *activeUser; - SOGoUser *ownerUser; + + MAPIStoreUserContext *userContext; NSURL *contextUrl; - - MAPIStoreMapping *mapping; - - MAPIStoreAuthenticator *authenticator; - WOContext *woContext; - - MAPIStoreFolder *baseFolder; } + (struct mapistore_contexts_list *) listAllContextsForUser: (NSString *) userName + withTDBIndexing: (struct tdb_wrap *) indexingTdb inMemCtx: (TALLOC_CTX *) memCtx; + (struct mapistore_contexts_list *) listContextsForUser: (NSString *) userName + withTDBIndexing: (struct tdb_wrap *) indexingTdb inMemCtx: (TALLOC_CTX *) memCtx; + (int) openContext: (MAPIStoreContext **) contextPtr @@ -83,20 +78,12 @@ withConnectionInfo: (struct mapistore_connection_info *) newConnInfo andTDBIndexing: (struct tdb_wrap *) indexingTdb; -- (void) setAuthenticator: (MAPIStoreAuthenticator *) newAuthenticator; -- (MAPIStoreAuthenticator *) authenticator; - - (NSURL *) url; - (struct mapistore_connection_info *) connectionInfo; -- (WOContext *) woContext; -- (MAPIStoreMapping *) mapping; - -- (void) setupRequest; -- (void) tearDownRequest; +- (MAPIStoreUserContext *) userContext; - (SOGoUser *) activeUser; -- (SOGoUser *) ownerUser; // - (id) lookupObject: (NSString *) objectURLString; @@ -117,7 +104,11 @@ /* subclass methods */ + (NSString *) MAPIModuleName; -- (void) setupBaseFolder: (NSURL *) newURL; +- (Class) MAPIStoreFolderClass; + +/* the top-most parent of the context folder: SOGoMailAccount, + SOGoCalendarFolders, ... */ +- (id) rootSOGoFolder; @end diff --git a/OpenChange/MAPIStoreContext.m b/OpenChange/MAPIStoreContext.m index 9b6a40b46..bc5e99729 100644 --- a/OpenChange/MAPIStoreContext.m +++ b/OpenChange/MAPIStoreContext.m @@ -25,9 +25,7 @@ #import #import -#import #import - #import #import @@ -35,10 +33,8 @@ #import "SOGoMAPIFSFolder.h" #import "SOGoMAPIFSMessage.h" -#import "MAPIApplication.h" #import "MAPIStoreAttachment.h" // #import "MAPIStoreAttachmentTable.h" -#import "MAPIStoreAuthenticator.h" #import "MAPIStoreFolder.h" #import "MAPIStoreFolderTable.h" #import "MAPIStoreMapping.h" @@ -47,6 +43,7 @@ #import "MAPIStoreFAIMessage.h" #import "MAPIStoreFAIMessageTable.h" #import "MAPIStoreTypes.h" +#import "MAPIStoreUserContext.h" #import "NSArray+MAPIStore.h" #import "NSObject+MAPIStore.h" #import "NSString+MAPIStore.h" @@ -69,7 +66,7 @@ /* sogo://username:password@{contacts,calendar,tasks,journal,notes,mail}/dossier/id */ -static Class NSDataK, NSStringK, MAPIStoreFAIMessageK; +static Class NSExceptionK; static NSMutableDictionary *contextClassMapping; @@ -80,9 +77,7 @@ static NSMutableDictionary *contextClassMapping; NSUInteger count, max; NSString *moduleName; - NSDataK = [NSData class]; - NSStringK = [NSString class]; - MAPIStoreFAIMessageK = [MAPIStoreFAIMessage class]; + NSExceptionK = [NSException class]; contextClassMapping = [NSMutableDictionary new]; classes = GSObjCAllSubclassesOfClass (self); @@ -101,15 +96,19 @@ static NSMutableDictionary *contextClassMapping; } } - + (struct mapistore_contexts_list *) listAllContextsForUser: (NSString *) userName + withTDBIndexing: (struct tdb_wrap *) indexingTdb inMemCtx: (TALLOC_CTX *) memCtx { struct mapistore_contexts_list *list, *current; NSArray *classes; Class currentClass; NSUInteger count, max; + MAPIStoreUserContext *userContext; + userContext = [MAPIStoreUserContext userContextWithUsername: userName + andTDBIndexing: indexingTdb]; + [userContext activateWithUser: [userContext sogoUser]]; list = NULL; classes = GSObjCAllSubclassesOfClass (self); @@ -118,6 +117,7 @@ static NSMutableDictionary *contextClassMapping; { currentClass = [classes objectAtIndex: count]; current = [currentClass listContextsForUser: userName + withTDBIndexing: indexingTdb inMemCtx: memCtx]; if (current) { @@ -130,49 +130,24 @@ static NSMutableDictionary *contextClassMapping; } + (struct mapistore_contexts_list *) listContextsForUser: (NSString *) userName + withTDBIndexing: (struct tdb_wrap *) indexingTdb inMemCtx: (TALLOC_CTX *) memCtx { return NULL; } -static inline enum mapistore_error -_prepareContextClass (Class contextClass, - struct mapistore_connection_info *connInfo, - struct tdb_wrap *indexingTdb, NSURL *url, - MAPIStoreContext **contextP) +static inline NSURL *CompleteURLFromMapistoreURI (const char *uri) { - MAPIStoreContext *context; - MAPIStoreAuthenticator *authenticator; - enum mapistore_error rc; + NSString *urlString; + NSURL *completeURL; - context = [[contextClass alloc] initFromURL: url - withConnectionInfo: connInfo - andTDBIndexing: indexingTdb]; - if (context) - { - [context autorelease]; + urlString = [NSString stringWithFormat: @"sogo://%@", + [NSString stringWithUTF8String: uri]]; + if (![urlString hasSuffix: @"/"]) + urlString = [urlString stringByAppendingString: @"/"]; + completeURL = [NSURL URLWithString: [urlString stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding]]; - authenticator = [MAPIStoreAuthenticator new]; - [authenticator setUsername: [url user]]; - [authenticator setPassword: [url password]]; - [context setAuthenticator: authenticator]; - [authenticator release]; - - [context setupRequest]; - [context setupBaseFolder: url]; - [context tearDownRequest]; - if (context->baseFolder && [context->baseFolder sogoObject]) - { - *contextP = context; - rc = MAPISTORE_SUCCESS; - } - else - rc = MAPISTORE_ERR_DENIED; - } - else - rc = MAPISTORE_ERROR; - - return rc; + return completeURL; } + (int) openContext: (MAPIStoreContext **) contextPtr @@ -182,7 +157,7 @@ _prepareContextClass (Class contextClass, { MAPIStoreContext *context; Class contextClass; - NSString *module, *completeURLString, *urlString; + NSString *module; NSURL *baseURL; int rc = MAPISTORE_ERR_NOT_FOUND; @@ -190,41 +165,31 @@ _prepareContextClass (Class contextClass, context = nil; - urlString = [NSString stringWithUTF8String: newUri]; - if (urlString) + baseURL = CompleteURLFromMapistoreURI (newUri); + if (baseURL) { - completeURLString = [@"sogo://" stringByAppendingString: urlString]; - if (![completeURLString hasSuffix: @"/"]) - completeURLString = [completeURLString stringByAppendingString: @"/"]; - baseURL = [NSURL URLWithString: [completeURLString stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding]]; - if (baseURL) + module = [baseURL host]; + if (module) { - module = [baseURL host]; - if (module) + contextClass = [contextClassMapping objectForKey: module]; + if (contextClass) { - contextClass = [contextClassMapping objectForKey: module]; - if (contextClass) + context = [[contextClass alloc] initFromURL: baseURL + withConnectionInfo: newConnInfo + andTDBIndexing: indexingTdb]; + if (context) { - rc = _prepareContextClass (contextClass, - newConnInfo, indexingTdb, - baseURL, &context); - if (rc == MAPISTORE_SUCCESS) - { - *contextPtr = context; - mapistore_mgmt_backend_register_user (newConnInfo, - "SOGo", - [[[context authenticator] username] UTF8String]); - } + [context autorelease]; + rc = MAPISTORE_SUCCESS; + *contextPtr = context; } - else - NSLog (@"ERROR: unrecognized module name '%@'", module); } + else + NSLog (@"ERROR: unrecognized module name '%@'", module); } - else - NSLog (@"ERROR: url could not be parsed"); } else - NSLog (@"ERROR: url is an invalid UTF-8 string"); + NSLog (@"ERROR: url could not be parsed"); return rc; } @@ -233,9 +198,8 @@ _prepareContextClass (Class contextClass, { if ((self = [super init])) { - woContext = [WOContext contextWithRequest: nil]; - [woContext retain]; - baseFolder = nil; + activeUser = nil; + userContext = nil; contextUrl = nil; } @@ -250,6 +214,26 @@ _prepareContextClass (Class contextClass, if ((self = [self init])) { + ASSIGN (contextUrl, newUrl); + + username = [newUrl user]; + if ([username length] == 0) + { + [self errorWithFormat: + @"attempt to instantiate a context with an empty owner"]; + [self release]; + return nil; + } + + ASSIGN (userContext, + [MAPIStoreUserContext userContextWithUsername: username + andTDBIndexing: indexingTdb]); + + mapistore_mgmt_backend_register_user (newConnInfo, + "SOGo", + [username UTF8String]); + + connInfo = newConnInfo; username = [NSString stringWithUTF8String: newConnInfo->username]; ASSIGN (activeUser, [SOGoUser userWithLogin: username]); if (!activeUser) @@ -259,29 +243,6 @@ _prepareContextClass (Class contextClass, [self release]; return nil; } - [woContext setActiveUser: activeUser]; - username = [newUrl user]; - if ([username length] == 0) - { - [self errorWithFormat: - @"attempt to instantiate a context with an empty owner"]; - [self release]; - return nil; - } - ASSIGN (ownerUser, [SOGoUser userWithLogin: username]); - if (!ownerUser) - { - [self errorWithFormat: - @"attempt to instantiate a context without a valid owner"]; - [self release]; - return nil; - } - ASSIGN (mapping, [MAPIStoreMapping mappingForUsername: username - withIndexing: indexingTdb]); - [mapping increaseUseCount]; - ASSIGN (contextUrl, newUrl); - mstoreCtx = newConnInfo->mstore_ctx; - connInfo = newConnInfo; } return self; @@ -290,36 +251,17 @@ _prepareContextClass (Class contextClass, - (void) dealloc { mapistore_mgmt_backend_unregister_user ([self connectionInfo], "SOGo", - [[[self authenticator] username] + [[userContext username] UTF8String]); - [baseFolder release]; - [woContext release]; - [authenticator release]; - [mapping decreaseUseCount]; - [mapping release]; [contextUrl release]; + [userContext release]; [super dealloc]; } -- (WOContext *) woContext +- (MAPIStoreUserContext *) userContext { - return woContext; -} - -- (MAPIStoreMapping *) mapping -{ - return mapping; -} - -- (void) setAuthenticator: (MAPIStoreAuthenticator *) newAuthenticator -{ - ASSIGN (authenticator, newAuthenticator); -} - -- (MAPIStoreAuthenticator *) authenticator -{ - return authenticator; + return userContext; } - (NSURL *) url @@ -332,34 +274,11 @@ _prepareContextClass (Class contextClass, return connInfo; } -- (void) setupRequest -{ - NSMutableDictionary *info; - - [MAPIApp setMAPIStoreContext: self]; - info = [[NSThread currentThread] threadDictionary]; - [info setObject: woContext forKey: @"WOContext"]; -} - -- (void) tearDownRequest -{ - NSMutableDictionary *info; - - info = [[NSThread currentThread] threadDictionary]; - [info removeObjectForKey: @"WOContext"]; - [MAPIApp setMAPIStoreContext: nil]; -} - - (SOGoUser *) activeUser { return activeUser; } -- (SOGoUser *) ownerUser -{ - return ownerUser; -} - // - (void) logRestriction: (struct mapi_SRestriction *) res // withState: (MAPIRestrictionState) state // { @@ -379,7 +298,7 @@ _prepareContextClass (Class contextClass, // TDB_DATA key, dbuf; url = [contextUrl absoluteString]; - objectURL = [mapping urlFromID: fmid]; + objectURL = [[userContext mapping] urlFromID: fmid]; if (objectURL) { if ([objectURL hasPrefix: url]) @@ -417,15 +336,64 @@ _prepareContextClass (Class contextClass, return rc; } +- (void) ensureContextFolder +{ +} + - (int) getRootFolder: (MAPIStoreFolder **) folderPtr withFID: (uint64_t) newFid { + enum mapistore_error rc; + MAPIStoreMapping *mapping; + MAPIStoreFolder *baseFolder; + SOGoFolder *currentFolder; + WOContext *woContext; + NSString *path; + NSArray *pathComponents; + NSUInteger count, max; + + mapping = [userContext mapping]; if (![mapping urlFromID: newFid]) [mapping registerURL: [contextUrl absoluteString] withID: newFid]; - *folderPtr = baseFolder; - return (baseFolder) ? MAPISTORE_SUCCESS: MAPISTORE_ERROR; + [userContext activateWithUser: activeUser]; + woContext = [userContext woContext]; + + [self ensureContextFolder]; + currentFolder = [self rootSOGoFolder]; + 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++) + { + [woContext setClientObject: currentFolder]; + currentFolder + = [currentFolder lookupName: [pathComponents objectAtIndex: count] + inContext: woContext + acquire: NO]; + if ([currentFolder isKindOfClass: NSExceptionK]) + currentFolder = nil; + } + + if (currentFolder) + { + baseFolder = [[self MAPIStoreFolderClass] + mapiStoreObjectWithSOGoObject: currentFolder + inContainer: nil]; + [baseFolder setContext: self]; + + *folderPtr = baseFolder; + rc = MAPISTORE_SUCCESS; + } + else + rc = MAPISTORE_ERR_NOT_FOUND; + + return rc; } /* utils */ @@ -460,6 +428,7 @@ _prepareContextClass (Class contextClass, inFolderURL: (NSString *) folderURL { NSString *childURL, *owner; + MAPIStoreMapping *mapping; uint64_t mappingId; uint32_t contextId; void *rootObject; @@ -468,6 +437,7 @@ _prepareContextClass (Class contextClass, childURL = [NSString stringWithFormat: @"%@%@", folderURL, key]; else childURL = folderURL; + mapping = [userContext mapping]; mappingId = [mapping idFromURL: childURL]; if (mappingId == NSNotFound) { @@ -476,11 +446,10 @@ _prepareContextClass (Class contextClass, [mapping registerURL: childURL withID: mappingId]; contextId = 0; - // FIXME: + 7 to skip the BOM or what? - mapistore_search_context_by_uri (mstoreCtx, [folderURL UTF8String] + 7, + mapistore_search_context_by_uri (connInfo->mstore_ctx, [folderURL UTF8String] + 7, &contextId, &rootObject); - owner = [ownerUser login]; - mapistore_indexing_record_add_mid (mstoreCtx, contextId, + owner = [userContext username]; + mapistore_indexing_record_add_mid (connInfo->mstore_ctx, contextId, [owner UTF8String], mappingId); } @@ -507,9 +476,18 @@ _prepareContextClass (Class contextClass, return nil; } -- (void) setupBaseFolder: (NSURL *) newURL +- (Class) MAPIStoreFolderClass { [self subclassResponsibility: _cmd]; + + return nil; +} + +- (id) rootSOGoFolder +{ + [self subclassResponsibility: _cmd]; + + return nil; } @end diff --git a/OpenChange/MAPIStoreFAIMessage.m b/OpenChange/MAPIStoreFAIMessage.m index e0d5faeee..b8fe33988 100644 --- a/OpenChange/MAPIStoreFAIMessage.m +++ b/OpenChange/MAPIStoreFAIMessage.m @@ -22,6 +22,7 @@ #import "MAPIStoreActiveTables.h" #import "MAPIStoreContext.h" +#import "MAPIStoreUserContext.h" #import "NSObject+MAPIStore.h" #import "MAPIStoreFAIMessage.h" @@ -51,9 +52,11 @@ { enum mapistore_error rc; MAPIStoreContext *context; + SOGoUser *ownerUser; context = [self context]; - if ([[context activeUser] isEqual: [context ownerUser]]) + ownerUser = [[self userContext] sogoUser]; + if ([[context activeUser] isEqual: ownerUser]) rc = [super saveMessage]; else rc = MAPISTORE_ERR_DENIED; diff --git a/OpenChange/MAPIStoreFSBaseContext.m b/OpenChange/MAPIStoreFSBaseContext.m index bb34b4795..a3e448082 100644 --- a/OpenChange/MAPIStoreFSBaseContext.m +++ b/OpenChange/MAPIStoreFSBaseContext.m @@ -30,10 +30,14 @@ #import "MAPIStoreFSFolder.h" #import "MAPIStoreMapping.h" +#import "MAPIStoreUserContext.h" #import "SOGoMAPIFSFolder.h" #import "MAPIStoreFSBaseContext.h" +#undef DEBUG +#include + static Class MAPIStoreFSFolderK; @implementation MAPIStoreFSBaseContext @@ -48,12 +52,28 @@ static Class MAPIStoreFSFolderK; return nil; } -- (void) setupBaseFolder: (NSURL *) newURL +- (Class) MAPIStoreFolderClass { - [self logWithFormat: @"invoked %s", __PRETTY_FUNCTION__]; - baseFolder = [MAPIStoreFSFolderK baseFolderWithURL: newURL - inContext: self]; - [baseFolder retain]; + 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/MAPIStoreFSFolder.m b/OpenChange/MAPIStoreFSFolder.m index 8e8ab1ef6..684ab5a7b 100644 --- a/OpenChange/MAPIStoreFSFolder.m +++ b/OpenChange/MAPIStoreFSFolder.m @@ -34,6 +34,7 @@ #import "MAPIStoreFSMessage.h" #import "MAPIStoreFSMessageTable.h" #import "MAPIStoreTypes.h" +#import "MAPIStoreUserContext.h" #import "SOGoMAPIFSFolder.h" #import "SOGoMAPIFSMessage.h" @@ -64,20 +65,6 @@ static NSString *MAPIStoreRightFolderContact = @"RightsFolderContact"; EOKeyValueQualifierK = [EOKeyValueQualifier class]; } -- (id) initWithURL: (NSURL *) newURL - inContext: (MAPIStoreContext *) newContext -{ - if ((self = [super initWithURL: newURL - inContext: newContext])) - { - sogoObject = [SOGoMAPIFSFolder folderWithURL: newURL - andTableType: MAPISTORE_MESSAGE_TABLE]; - [sogoObject retain]; - } - - return self; -} - - (MAPIStoreMessageTable *) messageTable { return [MAPIStoreFSMessageTable tableForContainer: self]; @@ -126,8 +113,10 @@ static NSString *MAPIStoreRightFolderContact = @"RightsFolderContact"; andSortOrderings: (NSArray *) sortOrderings { NSArray *keys; + SOGoUser *ownerUser; - if ([[context activeUser] isEqual: [context ownerUser]] + ownerUser = [[self userContext] sogoUser]; + if ([[context activeUser] isEqual: ownerUser] || [self subscriberCanReadMessages]) keys = [(SOGoMAPIFSFolder *) sogoObject toOneRelationshipKeysMatchingQualifier: qualifier diff --git a/OpenChange/MAPIStoreFallbackContext.m b/OpenChange/MAPIStoreFallbackContext.m index 8368671e3..23c6285b2 100644 --- a/OpenChange/MAPIStoreFallbackContext.m +++ b/OpenChange/MAPIStoreFallbackContext.m @@ -22,8 +22,6 @@ #import -#import "MAPIStoreFSFolder.h" - #import "MAPIStoreFallbackContext.h" #undef DEBUG @@ -37,6 +35,7 @@ } + (struct mapistore_contexts_list *) listContextsForUser: (NSString *) userName + withTDBIndexing: (struct tdb_wrap *) indexingTdb inMemCtx: (TALLOC_CTX *) memCtx { struct mapistore_contexts_list *context; @@ -53,10 +52,4 @@ return context; } -- (void) setupBaseFolder: (NSURL *) newURL -{ - baseFolder = [MAPIStoreFSFolder baseFolderWithURL: newURL inContext: self]; - [baseFolder retain]; -} - @end diff --git a/OpenChange/MAPIStoreFolder.h b/OpenChange/MAPIStoreFolder.h index 285c2bba3..48e6f5f29 100644 --- a/OpenChange/MAPIStoreFolder.h +++ b/OpenChange/MAPIStoreFolder.h @@ -28,7 +28,6 @@ @class NSArray; @class NSMutableArray; @class NSNumber; -@class NSURL; @class EOQualifier; @@ -46,7 +45,6 @@ @interface MAPIStoreFolder : MAPIStoreObject { - NSURL *folderURL; MAPIStoreContext *context; NSArray *messageKeys; NSArray *faiMessageKeys; @@ -57,10 +55,7 @@ SOGoMAPIFSMessage *propsMessage; } -+ (id) baseFolderWithURL: (NSURL *) newURL - inContext: (MAPIStoreContext *) newContext; -- (id) initWithURL: (NSURL *) newURL - inContext: (MAPIStoreContext *) newContext; +- (void) setContext: (MAPIStoreContext *) newContext; - (NSArray *) activeMessageTables; - (NSArray *) activeFAIMessageTables; @@ -168,6 +163,7 @@ - (BOOL) supportsSubFolders; /* capability */ /* subclass helpers */ +- (void) setupVersionsMessage; - (void) postNotificationsForMoveCopyMessagesWithMIDs: (uint64_t *) srcMids andMessageURLs: (NSArray *) oldMessageURLs andCount: (uint32_t) midCount diff --git a/OpenChange/MAPIStoreFolder.m b/OpenChange/MAPIStoreFolder.m index a8384bb5b..2f5279ba0 100644 --- a/OpenChange/MAPIStoreFolder.m +++ b/OpenChange/MAPIStoreFolder.m @@ -43,6 +43,7 @@ #import "MAPIStorePermissionsTable.h" #import "MAPIStoreSamDBUtils.h" #import "MAPIStoreTypes.h" +#import "MAPIStoreUserContext.h" #import "NSDate+MAPIStore.h" #import "NSString+MAPIStore.h" #import "NSObject+MAPIStore.h" @@ -71,17 +72,6 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe MAPIStoreFolderTableK = [MAPIStoreFolderTable class]; } -+ (id) baseFolderWithURL: (NSURL *) newURL - inContext: (MAPIStoreContext *) newContext -{ - id newFolder; - - newFolder = [[self alloc] initWithURL: newURL inContext: newContext]; - [newFolder autorelease]; - - return newFolder; -} - - (id) init { if ((self = [super init])) @@ -90,7 +80,6 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe faiMessageKeys = nil; folderKeys = nil; faiFolder = nil; - folderURL = nil; context = nil; propsFolder = nil; @@ -100,54 +89,72 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe return self; } -/* from context */ -- (id) initWithURL: (NSURL *) newURL - inContext: (MAPIStoreContext *) newContext -{ - if ((self = [self init])) - { - context = newContext; - ASSIGN (folderURL, newURL); - ASSIGN (faiFolder, - [SOGoMAPIFSFolder folderWithURL: newURL - andTableType: MAPISTORE_FAI_TABLE]); - ASSIGN (propsFolder, - [SOGoMAPIFSFolder folderWithURL: newURL - andTableType: MAPISTORE_FOLDER_TABLE]); - ASSIGN (propsMessage, - [SOGoMAPIFSMessage objectWithName: @"properties.plist" - inContainer: propsFolder]); - } - - return self; -} - -/* from parent folder */ -- (id) initWithSOGoObject: (id) newSOGoObject - inContainer: (MAPIStoreObject *) newContainer +- (void) _setupAuxiliaryObjects { NSURL *propsURL; NSString *urlString; - if ((self = [super initWithSOGoObject: newSOGoObject inContainer: newContainer])) + 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]; +} + +- (id) initWithSOGoObject: (id) newSOGoObject + inContainer: (MAPIStoreObject *) newContainer +{ + /* The instantiation of auxiliary folders is postponed when newContainer is + nil since there is no way to deduce the parent url. When setContext: is + invoked, it becomes possible again. */ + if ((self = [super initWithSOGoObject: newSOGoObject + inContainer: newContainer]) + && newContainer) { - urlString = [[self url] stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding]; - propsURL = [NSURL URLWithString: urlString]; - 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 _setupAuxiliaryObjects]; } return self; } +- (void) setContext: (MAPIStoreContext *) newContext +{ + ASSIGN (context, newContext); + if (newContext) + [self _setupAuxiliaryObjects]; +} + +- (MAPIStoreContext *) context +{ + if (!context) + [self setContext: [container context]]; + + return context; +} + +- (void) dealloc +{ + [propsMessage release]; + [propsFolder release]; + [messageKeys release]; + [faiMessageKeys release]; + [folderKeys release]; + [faiFolder release]; + [context release]; + + [super dealloc]; +} + /* backend interface */ + - (SOGoMAPIFSMessage *) propertiesMessage { return propsMessage; @@ -185,7 +192,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe if ([[self folderKeys] containsObject: folderKey]) { - woContext = [[self context] woContext]; + woContext = [[self userContext] woContext]; sogoFolder = [sogoObject lookupName: folderKey inContext: woContext acquire: NO]; @@ -237,7 +244,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe acquire: NO]; if (msgObject && ![msgObject isKindOfClass: NSExceptionK]) { - [msgObject setContext: [[self context] woContext]]; + [msgObject setContext: [[self userContext] woContext]]; messageClass = [msgObject mapistoreMessageClass]; childMessage = [messageClass mapiStoreObjectWithSOGoObject: msgObject @@ -310,7 +317,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe [self logWithFormat: @"METHOD '%s' (%d)", __FUNCTION__, __LINE__]; - mapping = [[self context] mapping]; + mapping = [self mapping]; childURL = [mapping urlFromID: fid]; if (childURL) { @@ -333,13 +340,15 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe MAPIStoreMapping *mapping; NSString *baseURL, *childURL, *folderKey; MAPIStoreFolder *childFolder; + SOGoUser *ownerUser; [self logWithFormat: @"METHOD '%s' (%d)", __FUNCTION__, __LINE__]; - if ([[context activeUser] isEqual: [context ownerUser]] + ownerUser = [[self userContext] sogoUser]; + if ([[context activeUser] isEqual: ownerUser] || [self subscriberCanCreateSubFolders]) { - mapping = [[self context] mapping]; + mapping = [self mapping]; childURL = [mapping urlFromID: fid]; if (childURL) rc = MAPISTORE_ERR_EXIST; @@ -415,16 +424,18 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe NSString *messageURL; MAPIStoreMapping *mapping; MAPIStoreMessage *message; + SOGoUser *ownerUser; int rc = MAPISTORE_ERR_NOT_FOUND; - mapping = [[self context] mapping]; + mapping = [self mapping]; messageURL = [mapping urlFromID: mid]; if (messageURL) { message = [self lookupMessageByURL: messageURL]; if (message) { - if ([[context activeUser] isEqual: [context ownerUser]] + ownerUser = [[self userContext] sogoUser]; + if ([[context activeUser] isEqual: ownerUser] || (readWrite && [message subscriberCanModifyMessage]) || (!readWrite && [message subscriberCanReadMessage])) { @@ -447,15 +458,18 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe MAPIStoreMessage *message; NSString *baseURL, *childURL; MAPIStoreMapping *mapping; + SOGoUser *ownerUser; [self logWithFormat: @"METHOD '%s' -- mid: 0x%.16llx associated: %d", __FUNCTION__, mid, isAssociated]; context = [self context]; - if ([[context activeUser] isEqual: [context ownerUser]] + ownerUser = [[self userContext] sogoUser]; + + if ([[context activeUser] isEqual: ownerUser] || (!isAssociated && [self subscriberCanCreateMessages])) { - mapping = [[self context] mapping]; + mapping = [self mapping]; if ([mapping urlFromID: mid]) rc = MAPISTORE_ERR_EXIST; else @@ -491,20 +505,23 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe NSArray *activeTables; NSUInteger count, max; id msgObject; + SOGoUser *ownerUser; struct mapistore_connection_info *connInfo; struct mapistore_object_notification_parameters *notif_parameters; int rc; [self logWithFormat: @"-deleteMessageWithMID: mid: 0x%.16llx flags: %d", mid, flags]; - mapping = [[self context] mapping]; + mapping = [self mapping]; childURL = [mapping urlFromID: mid]; if (childURL) { message = [self lookupMessageByURL: childURL]; if (message) { - if ([[context activeUser] isEqual: [context ownerUser]] + ownerUser = [[self userContext] sogoUser]; + + if ([[context activeUser] isEqual: ownerUser] || (![message isKindOfClass: MAPIStoreFAIMessageK] && [self subscriberCanDeleteMessages])) { @@ -685,16 +702,19 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe NSMutableArray *oldMessageURLs; NSString *oldMessageURL; MAPIStoreMapping *mapping; + SOGoUser *ownerUser; struct Binary_r *targetChangeKey; - if (wantCopy || [[context activeUser] isEqual: [context ownerUser]]) + ownerUser = [[self userContext] sogoUser]; + + if (wantCopy || [[context activeUser] isEqual: ownerUser]) { if ([sourceFolder isKindOfClass: isa] || [self isKindOfClass: [sourceFolder class]]) [self logWithFormat: @"%s: this class could probably implement" @" a specialized/optimized version", __FUNCTION__]; oldMessageURLs = [NSMutableArray arrayWithCapacity: midCount]; - mapping = [[self context] mapping]; + mapping = [self mapping]; for (count = 0; rc == MAPISTORE_SUCCESS && count < midCount; count++) { oldMessageURL = [mapping urlFromID: srcMids[count]]; @@ -753,6 +773,10 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe [aclFolder setRoles: roles forUser: user]; } +- (void) setupVersionsMessage +{ +} + - (void) postNotificationsForMoveCopyMessagesWithMIDs: (uint64_t *) srcMids andMessageURLs: (NSArray *) oldMessageURLs andCount: (uint32_t) midCount @@ -849,7 +873,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe talloc_free(notif_parameters); // table notification - mapping = [[self context] mapping]; + mapping = [self mapping]; for (count = 0; count < midCount; count++) { messageURL = [mapping urlFromID: targetMids[count]]; @@ -879,7 +903,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe inTableType: tableType]; if (keys) { - mapping = [[self context] mapping]; + mapping = [self mapping]; max = [keys count]; @@ -976,27 +1000,6 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe [propsCopy release]; } -- (void) dealloc -{ - [propsMessage release]; - [propsFolder release]; - [folderURL release]; - [messageKeys release]; - [faiMessageKeys release]; - [folderKeys release]; - [faiFolder release]; - - [super dealloc]; -} - -- (MAPIStoreContext *) context -{ - if (!context) - context = [container context]; - - return context; -} - - (NSArray *) messageKeys { if (!messageKeys) @@ -1132,9 +1135,12 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe inMemCtx: (TALLOC_CTX *) memCtx { uint32_t access = 0; + SOGoUser *ownerUser; BOOL userIsOwner; - userIsOwner = [[context activeUser] isEqual: [context ownerUser]]; + ownerUser = [[self userContext] sogoUser]; + + userIsOwner = [[context activeUser] isEqual: ownerUser]; if (userIsOwner || [self subscriberCanModifyMessages]) access |= 0x01; if (userIsOwner || [self subscriberCanReadMessages]) @@ -1285,7 +1291,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe else newMessage = [self createMessage]; [newMessage setIsNew: YES]; - woContext = [[self context] woContext]; + woContext = [[self userContext] woContext]; [[newMessage sogoObject] setContext: woContext]; return newMessage; @@ -1305,10 +1311,14 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe { NSString *url; - if (folderURL) - url = [folderURL absoluteString]; - else + if (container) url = [NSString stringWithFormat: @"%@/", [super url]]; + else + { + url = [[context url] absoluteString]; + if (![url hasSuffix: @"/"]) + url = [NSString stringWithFormat: @"%@/", url]; + } return url; } @@ -1512,10 +1522,10 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe { uint64_t objectId; - if (folderURL) - objectId = [self idForObjectWithKey: nil]; - else + if (container) objectId = [super objectId]; + else + objectId = [self idForObjectWithKey: nil]; return objectId; } diff --git a/OpenChange/MAPIStoreGCSFolder.m b/OpenChange/MAPIStoreGCSFolder.m index 0a88a9a96..69f1d3d6c 100644 --- a/OpenChange/MAPIStoreGCSFolder.m +++ b/OpenChange/MAPIStoreGCSFolder.m @@ -34,6 +34,7 @@ #import "MAPIStoreContext.h" #import "MAPIStoreTypes.h" +#import "MAPIStoreUserContext.h" #import "NSData+MAPIStore.h" #import "NSDate+MAPIStore.h" #import "NSString+MAPIStore.h" @@ -46,33 +47,22 @@ @implementation MAPIStoreGCSFolder -- (id) initWithURL: (NSURL *) newURL - inContext: (MAPIStoreContext *) newContext +- (id) initWithSOGoObject: (id) newSOGoObject + inContainer: (MAPIStoreObject *) newContainer { - if ((self = [super initWithURL: newURL - inContext: newContext])) + if ((self = [super initWithSOGoObject: newSOGoObject inContainer: newContainer])) { - ASSIGN (versionsMessage, - [SOGoMAPIFSMessage objectWithName: @"versions.plist" - inContainer: propsFolder]); activeUserRoles = nil; } return self; } -- (id) initWithSOGoObject: (id) newSOGoObject - inContainer: (MAPIStoreObject *) newContainer +- (void) setupVersionsMessage { - if ((self = [super initWithSOGoObject: newSOGoObject inContainer: newContainer])) - { - ASSIGN (versionsMessage, - [SOGoMAPIFSMessage objectWithName: @"versions.plist" - inContainer: propsFolder]); - activeUserRoles = nil; - } - - return self; + ASSIGN (versionsMessage, + [SOGoMAPIFSMessage objectWithName: @"versions.plist" + inContainer: propsFolder]); } - (void) dealloc @@ -86,6 +76,7 @@ andSortOrderings: (NSArray *) sortOrderings { static NSArray *fields = nil; + SOGoUser *ownerUser; NSArray *records; NSMutableArray *qualifierArray; EOQualifier *fetchQualifier, *aclQualifier; @@ -98,7 +89,8 @@ initWithObjects: @"c_name", @"c_version", nil]; qualifierArray = [NSMutableArray new]; - if (![[context activeUser] isEqual: [context ownerUser]]) + ownerUser = [[self userContext] sogoUser]; + if (![[context activeUser] isEqual: ownerUser]) { aclQualifier = [self aclQualifier]; if (aclQualifier) @@ -528,12 +520,14 @@ - (NSArray *) activeUserRoles { SOGoUser *activeUser; + WOContext *woContext; if (!activeUserRoles) { activeUser = [[self context] activeUser]; + woContext = [[self userContext] woContext]; activeUserRoles = [activeUser rolesForObject: sogoObject - inContext: [context woContext]]; + inContext: woContext]; [activeUserRoles retain]; } diff --git a/OpenChange/MAPIStoreGCSMessage.m b/OpenChange/MAPIStoreGCSMessage.m index c5e945583..bb636f02d 100644 --- a/OpenChange/MAPIStoreGCSMessage.m +++ b/OpenChange/MAPIStoreGCSMessage.m @@ -30,6 +30,7 @@ #import "MAPIStoreContext.h" #import "MAPIStoreGCSFolder.h" #import "MAPIStoreTypes.h" +#import "MAPIStoreUserContext.h" #import "NSData+MAPIStore.h" #import "MAPIStoreGCSMessage.h" @@ -56,15 +57,17 @@ MAPIStoreContext *context; WOContext *woContext; SoSecurityManager *sm; + MAPIStoreUserContext *userContext; uint32_t access; context = [self context]; - if ([[context activeUser] isEqual: [context ownerUser]]) + userContext = [self userContext]; + if ([[context activeUser] isEqual: [userContext sogoUser]]) access = 0x03; else { sm = [SoSecurityManager sharedSecurityManager]; - woContext = [context woContext]; + woContext = [userContext woContext]; access = 0; if (![sm validatePermission: SoPerm_ChangeImagesAndFiles @@ -89,18 +92,19 @@ inMemCtx: (TALLOC_CTX *) memCtx { MAPIStoreContext *context; + MAPIStoreUserContext *userContext; WOContext *woContext; SoSecurityManager *sm; uint32_t accessLvl; context = [self context]; - if ([[context activeUser] isEqual: [context ownerUser]]) + userContext = [self userContext]; + if ([[context activeUser] isEqual: [userContext sogoUser]]) accessLvl = 1; else { sm = [SoSecurityManager sharedSecurityManager]; - woContext = [context woContext]; - + woContext = [userContext woContext]; if (![sm validatePermission: SoPerm_ChangeImagesAndFiles onObject: sogoObject inContext: woContext]) diff --git a/OpenChange/MAPIStoreMailContext.h b/OpenChange/MAPIStoreMailContext.h index 44778857c..ca67e1664 100644 --- a/OpenChange/MAPIStoreMailContext.h +++ b/OpenChange/MAPIStoreMailContext.h @@ -28,21 +28,9 @@ @interface MAPIStoreMailContext : MAPIStoreContext @end -@interface MAPIStoreInboxContext : MAPIStoreMailContext -@end - -@interface MAPIStoreSentItemsContext : MAPIStoreMailContext -@end - -@interface MAPIStoreDraftsContext : MAPIStoreMailContext -@end - #import "MAPIStoreFSBaseContext.h" @interface MAPIStoreDeletedItemsContext : MAPIStoreFSBaseContext @end -@interface MAPIStoreOutboxContext : MAPIStoreMailContext -@end - #endif /* MAPISTOREMAILCONTEXT_H */ diff --git a/OpenChange/MAPIStoreMailContext.m b/OpenChange/MAPIStoreMailContext.m index f4885f10b..1e740fdfd 100644 --- a/OpenChange/MAPIStoreMailContext.m +++ b/OpenChange/MAPIStoreMailContext.m @@ -22,20 +22,90 @@ #import +#import +#import + #import "MAPIStoreMailFolder.h" -#import "MAPIStoreMapping.h" +#import "MAPIStoreUserContext.h" #import "NSString+MAPIStore.h" #import "MAPIStoreMailContext.h" +#include #undef DEBUG #include +static Class MAPIStoreMailFolderK; + @implementation MAPIStoreMailContext ++ (void) initialize +{ + MAPIStoreMailFolderK = [MAPIStoreMailFolder class]; +} + + (NSString *) MAPIModuleName { - return nil; + return @"mail"; +} + ++ (struct mapistore_contexts_list *) listContextsForUser: (NSString *) userName + withTDBIndexing: (struct tdb_wrap *) indexingTdb + inMemCtx: (TALLOC_CTX *) memCtx +{ + struct mapistore_contexts_list *firstContext, *context; + NSString *urlBase, *stringData; + enum mapistore_context_role role[] = {MAPISTORE_MAIL_ROLE, + MAPISTORE_DRAFTS_ROLE, + MAPISTORE_SENTITEMS_ROLE, + MAPISTORE_OUTBOX_ROLE}; + NSString *folderName[4]; + NSUInteger count; + SOGoMailAccount *accountFolder; + MAPIStoreUserContext *userContext; + WOContext *woContext; + + firstContext = NULL; + + userContext = [MAPIStoreUserContext userContextWithUsername: userName + andTDBIndexing: indexingTdb]; + accountFolder = [userContext mailRoot]; + woContext = [userContext woContext]; + folderName[0] = @"folderINBOX"; + folderName[1] = [NSString stringWithFormat: @"folder%@", + [accountFolder draftsFolderNameInContext: woContext]]; + folderName[2] = [NSString stringWithFormat: @"folder%@", + [accountFolder sentFolderNameInContext: woContext]]; + folderName[3] = folderName[1]; + + urlBase = [NSString stringWithFormat: @"sogo://%@:%@@mail/", userName, userName]; + + for (count = 0; count < 4; count++) + { + context = talloc_zero (memCtx, struct mapistore_contexts_list); + stringData = [NSString stringWithFormat: @"%@%@", urlBase, + folderName[count]]; + context->url = [stringData asUnicodeInMemCtx: context]; + /* remove "folder" prefix */ + stringData = [folderName[count] substringFromIndex: 6]; + context->name = [stringData asUnicodeInMemCtx: context]; + context->main_folder = true; + context->role = role[count]; + context->tag = "tag"; + DLIST_ADD_END (firstContext, context, void); + } + + return firstContext; +} + +- (Class) MAPIStoreFolderClass +{ + return MAPIStoreMailFolderK; +} + +- (id) rootSOGoFolder +{ + return [userContext mailRoot]; } + (enum mapistore_context_role) contextRole @@ -45,82 +115,6 @@ @end -@implementation MAPIStoreInboxContext - -+ (NSString *) MAPIModuleName -{ - return @"inbox"; -} - -+ (struct mapistore_contexts_list *) listContextsForUser: (NSString *) userName - inMemCtx: (TALLOC_CTX *) memCtx -{ - struct mapistore_contexts_list *context; - NSString *url; - - context = talloc_zero(memCtx, struct mapistore_contexts_list); - url = [NSString stringWithFormat: @"sogo://%@:%@@%@/", userName, userName, [self MAPIModuleName]]; - context->url = [url asUnicodeInMemCtx: context]; - // context->name = "Inbox"; - context->main_folder = true; - context->role = [self contextRole]; - context->tag = "tag"; - context->prev = context; - - return context; -} - -- (void) setupBaseFolder: (NSURL *) newURL -{ - baseFolder = [MAPIStoreInboxFolder baseFolderWithURL: newURL - inContext: self]; - [baseFolder retain]; -} - -@end - -@implementation MAPIStoreSentItemsContext - -+ (NSString *) MAPIModuleName -{ - return @"sent-items"; -} - -+ (enum mapistore_context_role) contextRole -{ - return MAPISTORE_SENTITEMS_ROLE; -} - -- (void) setupBaseFolder: (NSURL *) newURL -{ - baseFolder = [MAPIStoreSentItemsFolder baseFolderWithURL: newURL - inContext: self]; - [baseFolder retain]; -} - -@end - -@implementation MAPIStoreDraftsContext - -+ (NSString *) MAPIModuleName -{ - return @"drafts"; -} - -+ (enum mapistore_context_role) contextRole -{ - return MAPISTORE_DRAFTS_ROLE; -} - -- (void) setupBaseFolder: (NSURL *) newURL -{ - baseFolder = [MAPIStoreDraftsFolder baseFolderWithURL: newURL - inContext: self]; - [baseFolder retain]; -} - -@end - #import "MAPIStoreFSFolder.h" @implementation MAPIStoreDeletedItemsContext @@ -135,38 +129,4 @@ return MAPISTORE_DELETEDITEMS_ROLE; } -- (void) setupBaseFolder: (NSURL *) newURL -{ - baseFolder = [MAPIStoreFSFolder baseFolderWithURL: newURL inContext: self]; - [baseFolder retain]; -} - -// - (void) setupBaseFolder: (NSURL *) newURL -// { -// baseFolder = [MAPIStoreDeletedItemsFolder baseFolderWithURL: newURL -// inContext: self]; -// [baseFolder retain]; -// } - -@end - -@implementation MAPIStoreOutboxContext - -+ (NSString *) MAPIModuleName -{ - return @"outbox"; -} - -+ (enum mapistore_context_role) contextRole -{ - return MAPISTORE_OUTBOX_ROLE; -} - -- (void) setupBaseFolder: (NSURL *) newURL -{ - baseFolder = [MAPIStoreOutboxFolder baseFolderWithURL: newURL - inContext: self]; - [baseFolder retain]; -} - @end diff --git a/OpenChange/MAPIStoreMailFolder.h b/OpenChange/MAPIStoreMailFolder.h index 3d8bad3f2..300a5dc3a 100644 --- a/OpenChange/MAPIStoreMailFolder.h +++ b/OpenChange/MAPIStoreMailFolder.h @@ -37,13 +37,13 @@ @interface MAPIStoreMailFolder : MAPIStoreFolder { SOGoMAPIFSMessage *versionsMessage; + BOOL usesAltNameSpace; } /* subclasses */ - (SOGoMailFolder *) specialFolderFromAccount: (SOGoMailAccount *) account inContext: (WOContext *) woContext; - /* synchronisation & versioning */ - (BOOL) synchroniseCache; - (NSNumber *) modseqFromMessageChangeNumber: (NSNumber *) changeNum; @@ -56,23 +56,4 @@ @end -@interface MAPIStoreInboxFolder : MAPIStoreMailFolder -{ - BOOL usesAltNameSpace; -} - -@end - -@interface MAPIStoreSentItemsFolder : MAPIStoreMailFolder -@end - -@interface MAPIStoreDraftsFolder : MAPIStoreMailFolder -@end - -// @interface MAPIStoreDeletedItemsFolder : MAPIStoreFFolder -// @end - -@interface MAPIStoreOutboxFolder : MAPIStoreMailFolder -@end - #endif /* MAPISTOREMAILFOLDER_H */ diff --git a/OpenChange/MAPIStoreMailFolder.m b/OpenChange/MAPIStoreMailFolder.m index 57fc1b348..c9391c3ff 100644 --- a/OpenChange/MAPIStoreMailFolder.m +++ b/OpenChange/MAPIStoreMailFolder.m @@ -77,57 +77,6 @@ static Class SOGoMailFolderK; [MAPIStoreAppointmentWrapper class]; } -- (id) initWithURL: (NSURL *) newURL - inContext: (MAPIStoreContext *) newContext -{ - SOGoUserFolder *userFolder; - SOGoMailAccounts *accountsFolder; - SOGoMailAccount *accountFolder; - SOGoFolder *currentContainer; - WOContext *woContext; - - if ((self = [super initWithURL: newURL - inContext: newContext])) - { - woContext = [newContext woContext]; - userFolder = [SOGoUserFolder objectWithName: [newURL user] - inContainer: MAPIApp]; - [parentContainersBag addObject: userFolder]; - [woContext setClientObject: userFolder]; - - accountsFolder = [userFolder lookupName: @"Mail" - inContext: woContext - acquire: NO]; - [parentContainersBag addObject: accountsFolder]; - [woContext setClientObject: accountsFolder]; - - accountFolder = [accountsFolder lookupName: @"0" - inContext: woContext - acquire: NO]; - [[accountFolder imap4Connection] - enableExtension: @"QRESYNC"]; - - [parentContainersBag addObject: accountFolder]; - [woContext setClientObject: accountFolder]; - - sogoObject = [self specialFolderFromAccount: accountFolder - inContext: woContext]; - [sogoObject retain]; - currentContainer = [sogoObject container]; - while (currentContainer != (SOGoFolder *) accountFolder) - { - [parentContainersBag addObject: currentContainer]; - currentContainer = [currentContainer container]; - } - - ASSIGN (versionsMessage, - [SOGoMAPIFSMessage objectWithName: @"versions.plist" - inContainer: propsFolder]); - } - - return self; -} - - (id) initWithSOGoObject: (id) newSOGoObject inContainer: (MAPIStoreObject *) newContainer { @@ -135,15 +84,20 @@ static Class SOGoMailFolderK; if ((self = [super initWithSOGoObject: newSOGoObject inContainer: newContainer])) { + usesAltNameSpace = NO; // urlString = [[self url] stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding]; - ASSIGN (versionsMessage, - [SOGoMAPIFSMessage objectWithName: @"versions.plist" - inContainer: propsFolder]); } return self; } +- (void) setupVersionsMessage +{ + ASSIGN (versionsMessage, + [SOGoMAPIFSMessage objectWithName: @"versions.plist" + inContainer: propsFolder]); +} + - (void) dealloc { [versionsMessage release]; @@ -166,7 +120,6 @@ static Class SOGoMailFolderK; - (NSString *) createFolder: (struct SRow *) aRow withFID: (uint64_t) newFID - inContainer: (id) subfolderParent { NSString *folderName, *nameInContainer; SOGoMailFolder *newFolder; @@ -188,7 +141,7 @@ static Class SOGoMailFolderK; nameInContainer = [NSString stringWithFormat: @"folder%@", [folderName asCSSIdentifier]]; newFolder = [SOGoMailFolderK objectWithName: nameInContainer - inContainer: subfolderParent]; + inContainer: sogoObject]; if (![newFolder create]) nameInContainer = nil; } @@ -196,13 +149,6 @@ static Class SOGoMailFolderK; return nameInContainer; } -- (NSString *) createFolder: (struct SRow *) aRow - withFID: (uint64_t) newFID -{ - return [self createFolder: aRow withFID: newFID - inContainer: sogoObject]; -} - - (int) getPrContentUnread: (void **) data inMemCtx: (TALLOC_CTX *) memCtx { @@ -896,7 +842,7 @@ _parseCOPYUID (NSString *line, NSArray **destUIDsP) wantCopy: wantCopy]; /* Conversion of mids to IMAP uids */ - mapping = [[self context] mapping]; + mapping = [self mapping]; uids = [NSMutableArray arrayWithCapacity: midCount]; oldMessageURLs = [NSMutableArray arrayWithCapacity: midCount]; for (count = 0; count < midCount; count++) @@ -1060,172 +1006,3 @@ _parseCOPYUID (NSString *line, NSArray **destUIDsP) } @end - -@implementation MAPIStoreInboxFolder : MAPIStoreMailFolder - -- (id) initWithURL: (NSURL *) newURL - inContext: (MAPIStoreContext *) newContext -{ - NSDictionary *list, *response; - NGImap4Client *client; - - if ((self = [super initWithURL: newURL - inContext: newContext])) - { - client = [[(SOGoMailFolder *) sogoObject imap4Connection] client]; - list = [client list: @"" pattern: @"INBOX"]; - response = [[list objectForKey: @"RawResponse"] objectForKey: @"list"]; - usesAltNameSpace = [[response objectForKey: @"flags"] containsObject: @"noinferiors"]; - } - - return self; -} - -- (SOGoMailFolder *) specialFolderFromAccount: (SOGoMailAccount *) accountFolder - inContext: (WOContext *) woContext -{ - return [accountFolder inboxFolderInContext: woContext]; -} - -- (NSString *) createFolder: (struct SRow *) aRow - withFID: (uint64_t) newFID -{ - id subfolderParent; - - if (usesAltNameSpace) - subfolderParent = [(SOGoMailFolder *) sogoObject mailAccountFolder]; - else - subfolderParent = sogoObject; - - return [self createFolder: aRow withFID: newFID - inContainer: subfolderParent]; -} - -- (NSMutableString *) _imapFolderNameRepresentation: (NSString *) subfolderName -{ - NSMutableString *representation; - - if (usesAltNameSpace) - { - /* with "altnamespace", the subfolders are NEVER subfolders of INBOX... */; - if (![subfolderName hasPrefix: @"folder"]) - abort (); - representation - = [NSMutableString stringWithString: - [subfolderName substringFromIndex: 6]]; - } - else - representation = [super _imapFolderNameRepresentation: subfolderName]; - - return representation; -} - -- (NSArray *) folderKeysMatchingQualifier: (EOQualifier *) qualifier - andSortOrderings: (NSArray *) sortOrderings -{ - NSMutableArray *subfolderKeys; - SOGoMailAccount *account; - - if (usesAltNameSpace) - { - if (qualifier) - [self errorWithFormat: @"qualifier is not used for folders"]; - if (sortOrderings) - [self errorWithFormat: @"sort orderings are not used for folders"]; - - account = [(SOGoMailFolder *) sogoObject mailAccountFolder]; - subfolderKeys - = [[account toManyRelationshipKeysWithNamespaces: NO] - mutableCopy]; - [subfolderKeys removeObject: @"folderINBOX"]; - - [self _cleanupSubfolderKeys: subfolderKeys]; - } - else - subfolderKeys = [[super folderKeysMatchingQualifier: qualifier - andSortOrderings: sortOrderings] - mutableCopy]; - - /* TODO: remove special folders */ - - [subfolderKeys autorelease]; - - return subfolderKeys; -} - -- (id) lookupFolder: (NSString *) childKey -{ - MAPIStoreMailFolder *childFolder = nil; - SOGoMailAccount *account; - SOGoMailFolder *sogoFolder; - WOContext *woContext; - - if (usesAltNameSpace) - { - if ([[self folderKeys] containsObject: childKey]) - { - woContext = [[self context] woContext]; - account = [(SOGoMailFolder *) sogoObject mailAccountFolder]; - sogoFolder = [account lookupName: childKey inContext: woContext - acquire: NO]; - [sogoFolder setContext: woContext]; - childFolder = [MAPIStoreMailFolder mapiStoreObjectWithSOGoObject: sogoFolder - inContainer: self]; - } - } - else - childFolder = [super lookupFolder: childKey]; - - return childFolder; -} - -- (BOOL) supportsSubFolders -{ - return !usesAltNameSpace; -} - -@end - -@implementation MAPIStoreSentItemsFolder : MAPIStoreMailFolder - -- (SOGoMailFolder *) specialFolderFromAccount: (SOGoMailAccount *) accountFolder - inContext: (WOContext *) woContext -{ - return [accountFolder sentFolderInContext: woContext]; -} - -@end - -@implementation MAPIStoreDraftsFolder : MAPIStoreMailFolder - -- (SOGoMailFolder *) specialFolderFromAccount: (SOGoMailAccount *) accountFolder - inContext: (WOContext *) woContext -{ - return [accountFolder draftsFolderInContext: woContext]; -} - -@end - -// @implementation MAPIStoreDeletedItemsFolder : MAPIStoreMailFolder - -// - (SOGoMailFolder *) specialFolderFromAccount: (SOGoMailAccount *) accountFolder -// inContext: (WOContext *) woContext -// { -// return [accountFolder trashFolderInContext: woContext]; -// } - -// @end - - -// -// -// -@implementation MAPIStoreOutboxFolder : MAPIStoreMailFolder - -- (SOGoMailFolder *) specialFolderFromAccount: (SOGoMailAccount *) accountFolder - inContext: (WOContext *) woContext -{ - return [accountFolder draftsFolderInContext: woContext]; -} - -@end diff --git a/OpenChange/MAPIStoreMailMessage.m b/OpenChange/MAPIStoreMailMessage.m index 8c165cc6e..00130d20c 100644 --- a/OpenChange/MAPIStoreMailMessage.m +++ b/OpenChange/MAPIStoreMailMessage.m @@ -62,7 +62,7 @@ @class iCalCalendar, iCalEvent; -static Class NSExceptionK, MAPIStoreSentItemsFolderK, MAPIStoreDraftsFolderK; +static Class NSExceptionK; @interface NSString (MAPIStoreMIME) @@ -105,8 +105,6 @@ static Class NSExceptionK, MAPIStoreSentItemsFolderK, MAPIStoreDraftsFolderK; + (void) initialize { NSExceptionK = [NSException class]; - MAPIStoreSentItemsFolderK = [MAPIStoreSentItemsFolder class]; - MAPIStoreDraftsFolderK = [MAPIStoreDraftsFolder class]; } - (id) init @@ -582,9 +580,9 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data) coreInfos = [sogoObject fetchCoreInfos]; flags = [coreInfos objectForKey: @"flags"]; - if ([container isKindOfClass: MAPIStoreSentItemsFolderK] - || [container isKindOfClass: MAPIStoreDraftsFolderK]) - v |= MSGFLAG_FROMME; + // if ([container isKindOfClass: MAPIStoreSentItemsFolderK] + // || [container isKindOfClass: MAPIStoreDraftsFolderK]) + // v |= MSGFLAG_FROMME; if ([flags containsObject: @"seen"]) v |= MSGFLAG_READ; if ([[self attachmentKeys] diff --git a/OpenChange/MAPIStoreMailVolatileMessage.m b/OpenChange/MAPIStoreMailVolatileMessage.m index 5952eb405..a8e06b92c 100644 --- a/OpenChange/MAPIStoreMailVolatileMessage.m +++ b/OpenChange/MAPIStoreMailVolatileMessage.m @@ -813,7 +813,7 @@ MakeMessageBody (NSDictionary *mailProperties, NSDictionary *attachmentParts, if (error) [self logWithFormat: @"an error occurred: '%@'", error]; - mapping = [[self context] mapping]; + mapping = [self mapping]; [mapping unregisterURLWithID: [self objectId]]; [self setIsNew: NO]; [properties removeAllObjects]; @@ -851,7 +851,7 @@ MakeMessageBody (NSDictionary *mailProperties, NSDictionary *attachmentParts, newIdString = [[flag componentsSeparatedByString: @" "] objectAtIndex: 2]; mid = [self objectId]; - mapping = [[self context] mapping]; + mapping = [self mapping]; [mapping unregisterURLWithID: mid]; [sogoObject setNameInContainer: [NSString stringWithFormat: @"%@.eml", newIdString]]; [mapping registerURL: [self url] withID: mid]; diff --git a/OpenChange/MAPIStoreMessage.m b/OpenChange/MAPIStoreMessage.m index be25d9a18..7e7a61b2e 100644 --- a/OpenChange/MAPIStoreMessage.m +++ b/OpenChange/MAPIStoreMessage.m @@ -39,6 +39,7 @@ #import "MAPIStorePropertySelectors.h" #import "MAPIStoreSamDBUtils.h" #import "MAPIStoreTypes.h" +#import "MAPIStoreUserContext.h" #import "NSData+MAPIStore.h" #import "NSObject+MAPIStore.h" #import "NSString+MAPIStore.h" @@ -286,10 +287,11 @@ rtf2html (NSData *compressedRTF) { enum mapistore_error rc; MAPIStoreContext *context; + SOGoUser *ownerUser; context = [self context]; - - if ([[context activeUser] isEqual: [context ownerUser]] + ownerUser = [[self userContext] sogoUser]; + if ([[context activeUser] isEqual: ownerUser] || [self subscriberCanModifyMessage]) rc = [super addPropertiesFromRow: aRow]; else @@ -432,9 +434,11 @@ rtf2html (NSData *compressedRTF) uint64_t folderId; struct mapistore_context *mstoreCtx; MAPIStoreContext *context; + SOGoUser *ownerUser; context = [self context]; - if ([[context activeUser] isEqual: [context ownerUser]] + ownerUser = [[self userContext] sogoUser]; + if ([[context activeUser] isEqual: ownerUser] || ((isNew && [(MAPIStoreFolder *) container subscriberCanCreateMessages]) || (!isNew && [self subscriberCanModifyMessage]))) @@ -560,9 +564,11 @@ rtf2html (NSData *compressedRTF) uint32_t access = 0; BOOL userIsOwner; MAPIStoreContext *context; + SOGoUser *ownerUser; context = [self context]; - userIsOwner = [[context activeUser] isEqual: [context ownerUser]]; + ownerUser = [[self userContext] sogoUser]; + userIsOwner = [[context activeUser] isEqual: ownerUser]; if (userIsOwner || [self subscriberCanModifyMessage]) access |= 0x01; if (userIsOwner || [self subscriberCanReadMessage]) @@ -587,9 +593,11 @@ rtf2html (NSData *compressedRTF) uint32_t access = 0; BOOL userIsOwner; MAPIStoreContext *context; + SOGoUser *ownerUser; context = [self context]; - userIsOwner = [[context activeUser] isEqual: [context ownerUser]]; + ownerUser = [[self userContext] sogoUser]; + userIsOwner = [[context activeUser] isEqual: ownerUser]; if (userIsOwner || [self subscriberCanModifyMessage]) access = 0x01; else @@ -862,14 +870,15 @@ rtf2html (NSData *compressedRTF) - (NSArray *) activeUserRoles { MAPIStoreContext *context; + MAPIStoreUserContext *userContext; if (!activeUserRoles) { context = [self context]; - + userContext = [self userContext]; activeUserRoles = [[context activeUser] rolesForObject: sogoObject - inContext: [context woContext]]; + inContext: [userContext woContext]]; [activeUserRoles retain]; } diff --git a/OpenChange/MAPIStoreNotesContext.m b/OpenChange/MAPIStoreNotesContext.m index b6ca62bc3..b73a4032a 100644 --- a/OpenChange/MAPIStoreNotesContext.m +++ b/OpenChange/MAPIStoreNotesContext.m @@ -38,6 +38,7 @@ } + (struct mapistore_contexts_list *) listContextsForUser: (NSString *) userName + withTDBIndexing: (struct tdb_wrap *) indexingTdb inMemCtx: (TALLOC_CTX *) memCtx { struct mapistore_contexts_list *context; @@ -54,11 +55,4 @@ return context; } -- (void) setupBaseFolder: (NSURL *) newURL -{ - baseFolder = [MAPIStoreNotesFolder baseFolderWithURL: newURL - inContext: self]; - [baseFolder retain]; -} - @end diff --git a/OpenChange/MAPIStoreObject.h b/OpenChange/MAPIStoreObject.h index d3b10cfbe..773db8bd4 100644 --- a/OpenChange/MAPIStoreObject.h +++ b/OpenChange/MAPIStoreObject.h @@ -35,8 +35,11 @@ @class EOQualifier; +@class MAPIStoreContext; @class MAPIStoreFolder; +@class MAPIStoreMapping; @class MAPIStoreTable; +@class MAPIStoreUserContext; @interface MAPIStoreObject : NSObject { @@ -71,7 +74,9 @@ - (id) sogoObject; - (MAPIStoreObject *) container; -- (id) context; +- (MAPIStoreContext *) context; +- (MAPIStoreUserContext *) userContext; +- (MAPIStoreMapping *) mapping; - (void) cleanupCaches; diff --git a/OpenChange/MAPIStoreObject.m b/OpenChange/MAPIStoreObject.m index 6652940f1..d064aca30 100644 --- a/OpenChange/MAPIStoreObject.m +++ b/OpenChange/MAPIStoreObject.m @@ -30,6 +30,7 @@ #import "MAPIStoreFolder.h" #import "MAPIStorePropertySelectors.h" #import "MAPIStoreTypes.h" +#import "MAPIStoreUserContext.h" #import "NSDate+MAPIStore.h" #import "NSData+MAPIStore.h" #import "NSObject+MAPIStore.h" @@ -170,11 +171,21 @@ static Class NSExceptionK, MAPIStoreFolderK; return [sogoObject nameInContainer]; } -- (id) context +- (MAPIStoreContext *) context { return [container context]; } +- (MAPIStoreUserContext *) userContext +{ + return [[self context] userContext]; +} + +- (MAPIStoreMapping *) mapping +{ + return [[self userContext] mapping]; +} + - (void) cleanupCaches { } @@ -217,7 +228,7 @@ static Class NSExceptionK, MAPIStoreFolderK; NSTimeZone *tz; WOContext *woContext; - woContext = [[self context] woContext]; + woContext = [[self userContext] woContext]; owner = [sogoObject ownerInContext: woContext]; ud = [[SOGoUser userWithLogin: owner] userDefaults]; tz = [ud timeZone]; diff --git a/OpenChange/MAPIStoreSOGo.m b/OpenChange/MAPIStoreSOGo.m index 7f6878f96..a5fbf7982 100644 --- a/OpenChange/MAPIStoreSOGo.m +++ b/OpenChange/MAPIStoreSOGo.m @@ -52,6 +52,7 @@ static enum mapistore_error sogo_backend_unexpected_error() { NSLog (@" UNEXPECTED WEIRDNESS: RECEIVED NO OBJECT"); + abort(); return MAPISTORE_SUCCESS; } @@ -147,7 +148,8 @@ sogo_backend_create_context(TALLOC_CTX *mem_ctx, } static enum mapistore_error -sogo_backend_list_contexts(const char *username, TALLOC_CTX *mem_ctx, +sogo_backend_list_contexts(const char *username, struct tdb_wrap *indexingTdb, + TALLOC_CTX *mem_ctx, struct mapistore_contexts_list **contexts_listp) { NSAutoreleasePool *pool; @@ -162,6 +164,7 @@ sogo_backend_list_contexts(const char *username, TALLOC_CTX *mem_ctx, { userName = [NSString stringWithUTF8String: username]; *contexts_listp = [MAPIStoreContextK listAllContextsForUser: userName + withTDBIndexing: indexingTdb inMemCtx: mem_ctx]; rc = MAPISTORE_SUCCESS; } diff --git a/OpenChange/MAPIStoreTasksContext.m b/OpenChange/MAPIStoreTasksContext.m index 644176aa3..c100cc3c2 100644 --- a/OpenChange/MAPIStoreTasksContext.m +++ b/OpenChange/MAPIStoreTasksContext.m @@ -21,29 +21,38 @@ */ #import +#import #import "MAPIStoreTasksFolder.h" -#import "MAPIStoreMapping.h" +#import "MAPIStoreUserContext.h" #import "MAPIStoreTasksContext.h" #undef DEBUG #include +static Class MAPIStoreTasksFolderK; + @implementation MAPIStoreTasksContext ++ (void) initialize +{ + MAPIStoreTasksFolderK = [MAPIStoreTasksFolder class]; +} + + (NSString *) MAPIModuleName { return @"tasks"; } + (struct mapistore_contexts_list *) listContextsForUser: (NSString *) userName + withTDBIndexing: (struct tdb_wrap *) indexingTdb inMemCtx: (TALLOC_CTX *) memCtx { struct mapistore_contexts_list *context; context = talloc_zero(memCtx, struct mapistore_contexts_list); - context->url = talloc_asprintf (context, "sogo://%s@tasks/", + context->url = talloc_asprintf (context, "sogo://%s@tasks/personal", [userName UTF8String]); // context->name = "Tâches personnelles"; context->main_folder = true; @@ -54,11 +63,14 @@ return context; } -- (void) setupBaseFolder: (NSURL *) newURL +- (Class) MAPIStoreFolderClass { - baseFolder = [MAPIStoreTasksFolder baseFolderWithURL: newURL - inContext: self]; - [baseFolder retain]; + return MAPIStoreTasksFolderK; +} + +- (id) rootSOGoFolder +{ + return [userContext calendarRoot]; } @end diff --git a/OpenChange/MAPIStoreTasksFolder.m b/OpenChange/MAPIStoreTasksFolder.m index 228fb8eb5..323bdf6e8 100644 --- a/OpenChange/MAPIStoreTasksFolder.m +++ b/OpenChange/MAPIStoreTasksFolder.m @@ -42,37 +42,6 @@ @implementation MAPIStoreTasksFolder -- (id) initWithURL: (NSURL *) newURL - inContext: (MAPIStoreContext *) newContext -{ - SOGoUserFolder *userFolder; - SOGoAppointmentFolders *parentFolder; - WOContext *woContext; - - if ((self = [super initWithURL: newURL - inContext: newContext])) - { - woContext = [newContext woContext]; - userFolder = [SOGoUserFolder objectWithName: [newURL user] - inContainer: MAPIApp]; - [parentContainersBag addObject: userFolder]; - [woContext setClientObject: userFolder]; - - parentFolder = [userFolder lookupName: @"Calendar" - inContext: woContext - acquire: NO]; - [parentContainersBag addObject: parentFolder]; - [woContext setClientObject: parentFolder]; - - sogoObject = [parentFolder lookupName: @"personal" - inContext: woContext - acquire: NO]; - [sogoObject retain]; - } - - return self; -} - - (MAPIStoreMessageTable *) messageTable { [self synchroniseCache]; diff --git a/OpenChange/SOGoMAPIFSFolder.m b/OpenChange/SOGoMAPIFSFolder.m index b2cf7cbe5..7969d9e65 100644 --- a/OpenChange/SOGoMAPIFSFolder.m +++ b/OpenChange/SOGoMAPIFSFolder.m @@ -96,7 +96,7 @@ static NSString *privateDir = nil; - (id) initWithURL: (NSURL *) url andTableType: (uint8_t) tableType { - NSString *path, *tableParticle; + NSString *path, *username, *tableParticle; if ((self = [self init])) { @@ -116,9 +116,11 @@ static NSString *privateDir = nil; path = [url path]; if (![path hasSuffix: @"/"]) path = [NSString stringWithFormat: @"%@/", path]; + username = [url user]; directory = [NSString stringWithFormat: @"%@/mapistore/SOGo/%@/%@/%@%@", - privateDir, [url user], tableParticle, + privateDir, username, tableParticle, [url host], path]; + [self setOwner: username]; [self logWithFormat: @"directory: %@", directory]; [directory retain]; ASSIGN (nameInContainer, [path stringByDeletingLastPathComponent]); From a4c6873091b7f514d1e3de97c06096160b791542 Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Mon, 30 Jan 2012 20:11:49 +0000 Subject: [PATCH 41/68] Monotone-Parent: 9235e5dc4d151a3cba8ad842ac39e1b2d18c8201 Monotone-Revision: aee2a5da27231d521f813fbb791f9e2c968f94b2 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-01-30T20:11:49 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 6 +++ OpenChange/MAPIStoreCalendarContext.m | 22 +-------- OpenChange/MAPIStoreContactsContext.m | 22 +-------- OpenChange/MAPIStoreGCSBaseContext.m | 67 +++++++++++++++++++++++++++ OpenChange/MAPIStoreTasksContext.m | 22 +-------- 5 files changed, 79 insertions(+), 60 deletions(-) diff --git a/ChangeLog b/ChangeLog index 3be7dbf50..44db2e954 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2012-01-30 Wolfgang Sourdeau + + * OpenChange/MAPIStoreGCSBaseContext.m + (+listContextsForUser:inMemCtx:): centralized code for all GCS + classes. + 2012-01-29 Wolfgang Sourdeau * OpenChange/MAPIStoreMailContext.[hm]: removed diff --git a/OpenChange/MAPIStoreCalendarContext.m b/OpenChange/MAPIStoreCalendarContext.m index 7a6622b1d..9e02b19a4 100644 --- a/OpenChange/MAPIStoreCalendarContext.m +++ b/OpenChange/MAPIStoreCalendarContext.m @@ -46,22 +46,9 @@ static Class MAPIStoreCalendarFolderK; return @"calendar"; } -+ (struct mapistore_contexts_list *) listContextsForUser: (NSString *) userName - withTDBIndexing: (struct tdb_wrap *) indexingTdb - inMemCtx: (TALLOC_CTX *) memCtx ++ (enum mapistore_context_role) MAPIModuleRole { - struct mapistore_contexts_list *context; - - context = talloc_zero(memCtx, struct mapistore_contexts_list); - context->url = talloc_asprintf (context, "sogo://%s@calendar/personal", - [userName UTF8String]); - // context->name = "Agenda personnel"; - context->main_folder = true; - context->role = MAPISTORE_CALENDAR_ROLE; - context->tag = "tag"; - context->prev = context; - - return context; + return MAPISTORE_CALENDAR_ROLE; } - (Class) MAPIStoreFolderClass @@ -69,9 +56,4 @@ static Class MAPIStoreCalendarFolderK; return MAPIStoreCalendarFolderK; } -- (id) rootSOGoFolder -{ - return [userContext calendarRoot]; -} - @end diff --git a/OpenChange/MAPIStoreContactsContext.m b/OpenChange/MAPIStoreContactsContext.m index 7c4052055..831ca0538 100644 --- a/OpenChange/MAPIStoreContactsContext.m +++ b/OpenChange/MAPIStoreContactsContext.m @@ -46,22 +46,9 @@ static Class MAPIStoreContactsFolderK; return @"contacts"; } -+ (struct mapistore_contexts_list *) listContextsForUser: (NSString *) userName - withTDBIndexing: (struct tdb_wrap *) indexingTdb - inMemCtx: (TALLOC_CTX *) memCtx ++ (enum mapistore_context_role) MAPIModuleRole { - struct mapistore_contexts_list *context; - - context = talloc_zero(memCtx, struct mapistore_contexts_list); - context->url = talloc_asprintf (context, "sogo://%s@contacts/personal", - [userName UTF8String]); - // context->name = "Carnet d'adresses personnel"; - context->main_folder = true; - context->role = MAPISTORE_CONTACTS_ROLE; - context->tag = "tag"; - context->prev = context; - - return context; + return MAPISTORE_CONTACTS_ROLE; } - (Class) MAPIStoreFolderClass @@ -69,9 +56,4 @@ static Class MAPIStoreContactsFolderK; return MAPIStoreContactsFolderK; } -- (id) rootSOGoFolder -{ - return [userContext contactsRoot]; -} - @end diff --git a/OpenChange/MAPIStoreGCSBaseContext.m b/OpenChange/MAPIStoreGCSBaseContext.m index d3dd27376..2ccfca080 100644 --- a/OpenChange/MAPIStoreGCSBaseContext.m +++ b/OpenChange/MAPIStoreGCSBaseContext.m @@ -20,10 +20,22 @@ * Boston, MA 02111-1307, USA. */ +#import +#import #import +#import +#import + +#import "MAPIStoreUserContext.h" +#import "NSString+MAPIStore.h" + #import "MAPIStoreGCSBaseContext.h" +#undef DEBUG +#include +#include + @implementation MAPIStoreGCSBaseContext + (NSString *) MAPIModuleName @@ -31,4 +43,59 @@ return nil; } ++ (enum mapistore_context_role) MAPIModuleRole +{ + return -1; +} + ++ (struct mapistore_contexts_list *) listContextsForUser: (NSString *) userName + withTDBIndexing: (struct tdb_wrap *) indexingTdb + inMemCtx: (TALLOC_CTX *) memCtx +{ + struct mapistore_contexts_list *firstContext = NULL, *context; + NSString *moduleName, *baseUrl, *url, *nameInContainer; + NSArray *subfolders; + MAPIStoreUserContext *userContext; + SOGoParentFolder *parentFolder; + NSUInteger count, max; + SOGoGCSFolder *currentFolder; + + moduleName = [self MAPIModuleName]; + if (moduleName) + { + userContext = [MAPIStoreUserContext userContextWithUsername: userName + andTDBIndexing: indexingTdb]; + parentFolder = [[userContext rootFolders] objectForKey: [self MAPIModuleName]]; + baseUrl = [NSString stringWithFormat: @"sogo://%@@%@/", + userName, moduleName]; + + subfolders = [parentFolder subFolders]; + max = [subfolders count]; + for (count = 0; count < max; count++) + { + currentFolder = [subfolders objectAtIndex: count]; + if ([[currentFolder ownerInContext: nil] isEqualToString: userName]) + { + context = talloc_zero (memCtx, struct mapistore_contexts_list); + nameInContainer = [currentFolder nameInContainer]; + url = [NSString stringWithFormat: @"%@%@", baseUrl, nameInContainer]; + context->url = [url asUnicodeInMemCtx: context]; + context->name = [[currentFolder displayName] + asUnicodeInMemCtx: context]; + context->main_folder = [nameInContainer isEqualToString: @"personal"]; + context->role = [self MAPIModuleRole]; + context->tag = "tag"; + DLIST_ADD_END (firstContext, context, void); + } + } + } + + return firstContext; +} + +- (id) rootSOGoFolder +{ + return [[userContext rootFolders] objectForKey: [isa MAPIModuleName]]; +} + @end diff --git a/OpenChange/MAPIStoreTasksContext.m b/OpenChange/MAPIStoreTasksContext.m index c100cc3c2..cf95fb736 100644 --- a/OpenChange/MAPIStoreTasksContext.m +++ b/OpenChange/MAPIStoreTasksContext.m @@ -45,22 +45,9 @@ static Class MAPIStoreTasksFolderK; return @"tasks"; } -+ (struct mapistore_contexts_list *) listContextsForUser: (NSString *) userName - withTDBIndexing: (struct tdb_wrap *) indexingTdb - inMemCtx: (TALLOC_CTX *) memCtx ++ (enum mapistore_context_role) MAPIModuleRole { - struct mapistore_contexts_list *context; - - context = talloc_zero(memCtx, struct mapistore_contexts_list); - context->url = talloc_asprintf (context, "sogo://%s@tasks/personal", - [userName UTF8String]); - // context->name = "Tâches personnelles"; - context->main_folder = true; - context->role = MAPISTORE_TASKS_ROLE; - context->tag = "tag"; - context->prev = context; - - return context; + return MAPISTORE_TASKS_ROLE; } - (Class) MAPIStoreFolderClass @@ -68,9 +55,4 @@ static Class MAPIStoreTasksFolderK; return MAPIStoreTasksFolderK; } -- (id) rootSOGoFolder -{ - return [userContext calendarRoot]; -} - @end From 054847d0ca83c08e3e29245614974bbacfe9eb7d Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Mon, 30 Jan 2012 20:12:18 +0000 Subject: [PATCH 42/68] Monotone-Parent: aee2a5da27231d521f813fbb791f9e2c968f94b2 Monotone-Revision: 6b3a3195262782a0783af8640a0fb24dd1103387 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-01-30T20:12:18 Monotone-Branch: ca.inverse.sogo --- OpenChange/MAPIStoreContext.m | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/OpenChange/MAPIStoreContext.m b/OpenChange/MAPIStoreContext.m index bc5e99729..921aaafef 100644 --- a/OpenChange/MAPIStoreContext.m +++ b/OpenChange/MAPIStoreContext.m @@ -106,10 +106,11 @@ static NSMutableDictionary *contextClassMapping; NSUInteger count, max; MAPIStoreUserContext *userContext; + list = NULL; + userContext = [MAPIStoreUserContext userContextWithUsername: userName andTDBIndexing: indexingTdb]; [userContext activateWithUser: [userContext sogoUser]]; - list = NULL; classes = GSObjCAllSubclassesOfClass (self); max = [classes count]; @@ -120,10 +121,7 @@ static NSMutableDictionary *contextClassMapping; withTDBIndexing: indexingTdb inMemCtx: memCtx]; if (current) - { - [self logWithFormat: @"adding list: %p", current]; - DLIST_CONCATENATE(list, current, void); - } + DLIST_CONCATENATE(list, current, void); } return list; From f3cbd4f172be8adead4554e6440afc1ab277ddfa Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Mon, 30 Jan 2012 20:17:21 +0000 Subject: [PATCH 43/68] Monotone-Parent: 0a8c7dabf7593bb4006f867c16170e5c59ac875a Monotone-Revision: 0e5cfd9eddf3cfeb9a5ff45ef7e78508b2d5a686 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-01-30T20:17:21 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 4 ++ OpenChange/MAPIStoreUserContext.h | 8 +-- OpenChange/MAPIStoreUserContext.m | 81 ++++++++++++------------------- 3 files changed, 38 insertions(+), 55 deletions(-) diff --git a/ChangeLog b/ChangeLog index 029b032ba..31872acd6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,9 @@ 2012-01-30 Wolfgang Sourdeau + * OpenChange/MAPIStoreUserContext.m (-rootFolders): new method + replacing the "...root" methods in way that can match the + MAPIModuleName. + * OpenChange/MAPIStoreGCSBaseContext.m (+listContextsForUser:inMemCtx:): centralized code for all GCS classes. diff --git a/OpenChange/MAPIStoreUserContext.h b/OpenChange/MAPIStoreUserContext.h index 3b986988f..01b6ef7e8 100644 --- a/OpenChange/MAPIStoreUserContext.h +++ b/OpenChange/MAPIStoreUserContext.h @@ -44,10 +44,8 @@ SOGoUser *sogoUser; SOGoUserFolder *userFolder; - SOGoAppointmentFolders *calendarRoot; - SOGoContactFolders *contactsRoot; - SOGoMailAccount *mailRoot; /* only one account supported */ NSMutableArray *containersBag; + NSMutableDictionary *rootFolders; MAPIStoreMapping *mapping; @@ -66,9 +64,7 @@ - (SOGoUserFolder *) userFolder; -- (SOGoAppointmentFolders *) calendarRoot; -- (SOGoContactFolders *) contactsRoot; -- (SOGoMailAccount *) mailRoot; +- (NSDictionary *) rootFolders; - (MAPIStoreMapping *) mapping; diff --git a/OpenChange/MAPIStoreUserContext.m b/OpenChange/MAPIStoreUserContext.m index c31777133..b2b97babc 100644 --- a/OpenChange/MAPIStoreUserContext.m +++ b/OpenChange/MAPIStoreUserContext.m @@ -1,4 +1,4 @@ -/* MAPIStoreUserContext.m - this file is part of $PROJECT_NAME_HERE$ +/* MAPIStoreUserContext.m - this file is part of SOGo * * Copyright (C) 2012 Inverse inc * @@ -20,6 +20,7 @@ * Boston, MA 02111-1307, USA. */ +#import #import #import @@ -73,11 +74,8 @@ static NSMapTable *contextsTable = nil; sogoUser = nil; userFolder = nil; - calendarRoot = nil; - contactsRoot = nil; - mailRoot = nil; - containersBag = [NSMutableArray new]; + rootFolders = nil; mapping = nil; @@ -113,13 +111,9 @@ static NSMapTable *contextsTable = nil; - (void) dealloc { - [contextsTable removeObjectForKey: username]; - [userFolder release]; - [calendarRoot release]; - [contactsRoot release]; - [mailRoot release]; [containersBag release]; + [rootFolders release]; [authenticator release]; [mapping release]; @@ -127,6 +121,8 @@ static NSMapTable *contextsTable = nil; [username release]; [sogoUser release]; + [contextsTable removeObjectForKey: username]; + [super dealloc]; } @@ -155,62 +151,49 @@ static NSMapTable *contextsTable = nil; return userFolder; } -- (SOGoAppointmentFolders *) calendarRoot -{ - if (!calendarRoot) - { - [self userFolder]; - [woContext setClientObject: userFolder]; - - calendarRoot = [userFolder lookupName: @"Calendar" - inContext: woContext - acquire: NO]; - [calendarRoot retain]; - } - - return calendarRoot; -} - -- (SOGoContactFolders *) contactsRoot -{ - if (!contactsRoot) - { - [self userFolder]; - [woContext setClientObject: userFolder]; - - contactsRoot = [userFolder lookupName: @"Contacts" - inContext: woContext - acquire: NO]; - [contactsRoot retain]; - } - - return contactsRoot; -} - -- (SOGoMailAccount *) mailRoot +- (NSDictionary *) rootFolders { SOGoMailAccounts *accountsFolder; + id currentFolder; - if (!mailRoot) + if (!rootFolders) { + rootFolders = [NSMutableDictionary new]; [self userFolder]; [woContext setClientObject: userFolder]; + /* Calendar */ + currentFolder = [userFolder lookupName: @"Calendar" + inContext: woContext + acquire: NO]; + [rootFolders setObject: currentFolder + forKey: @"calendar"]; + [rootFolders setObject: currentFolder + forKey: @"tasks"]; + + /* Contacts */ + currentFolder = [userFolder lookupName: @"Contacts" + inContext: woContext + acquire: NO]; + [rootFolders setObject: currentFolder + forKey: @"contacts"]; + + /* Mail */ accountsFolder = [userFolder lookupName: @"Mail" inContext: woContext acquire: NO]; [containersBag addObject: accountsFolder]; [woContext setClientObject: accountsFolder]; - - mailRoot = [accountsFolder lookupName: @"0" + currentFolder = [accountsFolder lookupName: @"0" inContext: woContext acquire: NO]; - [[mailRoot imap4Connection] + [rootFolders setObject: currentFolder + forKey: @"mail"]; + [[currentFolder imap4Connection] enableExtension: @"QRESYNC"]; - [mailRoot retain]; } - return mailRoot; + return rootFolders; } - (MAPIStoreMapping *) mapping From 98c9f995c81be057c73b355ee1011c2e2d96b9cc Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Mon, 30 Jan 2012 20:20:02 +0000 Subject: [PATCH 44/68] Monotone-Parent: 0e5cfd9eddf3cfeb9a5ff45ef7e78508b2d5a686 Monotone-Revision: 9bec05d8cd20a1e980c6381c10c71a9e57b984e6 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-01-30T20:20:02 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 4 +++ OpenChange/MAPIStoreFallbackContext.m | 42 +++++++++++++++++++++++---- 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/ChangeLog b/ChangeLog index 31872acd6..c3080acc6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,9 @@ 2012-01-30 Wolfgang Sourdeau + * OpenChange/MAPIStoreFallbackContext.m + (+listContextsForUser:withTDBIndexing:inMemCtx:): now returns the + fallback subfolders as secondary contexts (tmp hack). + * OpenChange/MAPIStoreUserContext.m (-rootFolders): new method replacing the "...root" methods in way that can match the MAPIModuleName. diff --git a/OpenChange/MAPIStoreFallbackContext.m b/OpenChange/MAPIStoreFallbackContext.m index 23c6285b2..c234e1f84 100644 --- a/OpenChange/MAPIStoreFallbackContext.m +++ b/OpenChange/MAPIStoreFallbackContext.m @@ -20,9 +20,13 @@ * Boston, MA 02111-1307, USA. */ +#import #import +#import #import "MAPIStoreFallbackContext.h" +#import "NSString+MAPIStore.h" +#import "SOGoMAPIFSFolder.h" #undef DEBUG #include @@ -38,18 +42,44 @@ withTDBIndexing: (struct tdb_wrap *) indexingTdb inMemCtx: (TALLOC_CTX *) memCtx { - struct mapistore_contexts_list *context; + struct mapistore_contexts_list *firstContext = NULL, *context; + SOGoMAPIFSFolder *root; + NSArray *names; + NSUInteger count, max; + NSString *baseURL, *url, *name; - context = talloc_zero(memCtx, struct mapistore_contexts_list); - context->url = talloc_asprintf (context, "sogo://%s@fallback/", - [userName UTF8String]); + baseURL = [NSString stringWithFormat: @"sogo://%@@fallback/", userName]; + + context = talloc_zero (memCtx, struct mapistore_contexts_list); + context->url = [baseURL asUnicodeInMemCtx: context]; context->name = "Fallback"; context->main_folder = true; context->role = MAPISTORE_FALLBACK_ROLE; context->tag = "tag"; - context->prev = context; - return context; + 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]; + names = [root toManyRelationshipKeys]; + max = [names count]; + for (count = 0; count < max; count++) + { + name = [names objectAtIndex: count]; + url = [NSString stringWithFormat: @"%@%@/", baseURL, name]; + context = talloc_zero (memCtx, struct mapistore_contexts_list); + context->url = [url asUnicodeInMemCtx: context]; + context->name = [name asUnicodeInMemCtx: context]; + context->main_folder = false; + context->role = MAPISTORE_FALLBACK_ROLE; + context->tag = "tag"; + DLIST_ADD_END (firstContext, context, void); + } + + return firstContext; } @end From 3bb6a1b975c8a1ca12ac26be0e5604198db21cb7 Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Mon, 30 Jan 2012 20:47:07 +0000 Subject: [PATCH 45/68] Monotone-Parent: 9bec05d8cd20a1e980c6381c10c71a9e57b984e6 Monotone-Revision: 4012894fc79b7641cebac42647c13996d86e9eca Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-01-30T20:47:07 Monotone-Branch: ca.inverse.sogo --- SoObjects/Mailer/SOGoMailFolder.h | 4 +-- SoObjects/Mailer/SOGoMailFolder.m | 47 ++++++++++++++++++++++++ UI/MailerUI/UIxMailFolderActions.m | 58 ++++-------------------------- 3 files changed, 55 insertions(+), 54 deletions(-) diff --git a/SoObjects/Mailer/SOGoMailFolder.h b/SoObjects/Mailer/SOGoMailFolder.h index bd23afbeb..f1a9e15cd 100644 --- a/SoObjects/Mailer/SOGoMailFolder.h +++ b/SoObjects/Mailer/SOGoMailFolder.h @@ -86,6 +86,8 @@ - (NSException *) expunge; +- (NSException *) renameTo: (NSString *) newName; + - (NSCalendarDate *) mostRecentMessageDate; /* flags */ @@ -94,8 +96,6 @@ /* folder type */ -- (NSString *) outlookFolderClass; - - (NSArray *) subfolders; - (BOOL) isSpecialFolder; diff --git a/SoObjects/Mailer/SOGoMailFolder.m b/SoObjects/Mailer/SOGoMailFolder.m index fbcc73f7e..03e7b9b15 100644 --- a/SoObjects/Mailer/SOGoMailFolder.m +++ b/SoObjects/Mailer/SOGoMailFolder.m @@ -267,6 +267,53 @@ static NSString *defaultUserID = @"anyone"; return filenames; } +- (NSException *) renameTo: (NSString *) newName +{ + NSException *error; + SOGoMailFolder *inbox; + NSURL *destURL; + NSString *path; + NGImap4Client *client; + + if ([newName length] > 0) + { + [self imap4URL]; + + [self imap4Connection]; + client = [imap4 client]; + + inbox = [[self mailAccountFolder] inboxFolderInContext: context]; + [client select: [inbox absoluteImap4Name]]; + + path = [[imap4URL path] stringByDeletingLastPathComponent]; + if (![path hasSuffix: @"/"]) + path = [path stringByAppendingString: @"/"]; + destURL = [[NSURL alloc] initWithScheme: [imap4URL scheme] + host: [imap4URL host] + path: [NSString stringWithFormat: @"%@%@", + path, newName]]; + [destURL autorelease]; + error = [imap4 moveMailboxAtURL: imap4URL + toURL: destURL]; + if (!error) + { + // We unsubscribe to the old one, and subscribe back to the new one + if ([[[context activeUser] userDefaults] + mailShowSubscribedFoldersOnly]) + { + [client subscribe: [destURL path]]; + [client unsubscribe: [imap4URL path]]; + } + } + } + else + error = [NSException exceptionWithName: @"SOGoMailException" + reason: @"given name is empty" + userInfo: nil]; + + return error; +} + /* messages */ - (void) prefetchCoreInfosForMessageKeys: (NSArray *) keys { diff --git a/UI/MailerUI/UIxMailFolderActions.m b/UI/MailerUI/UIxMailFolderActions.m index 57f94973e..2dc1371ba 100644 --- a/UI/MailerUI/UIxMailFolderActions.m +++ b/UI/MailerUI/UIxMailFolderActions.m @@ -84,70 +84,24 @@ return response; } -- (NSURL *) _urlOfFolder: (NSURL *) srcURL - renamedTo: (NSString *) folderName -{ - NSString *path; - NSURL *destURL; - - path = [[srcURL path] stringByDeletingLastPathComponent]; - if (![path hasSuffix: @"/"]) - path = [path stringByAppendingString: @"/"]; - - destURL = [[NSURL alloc] initWithScheme: [srcURL scheme] - host: [srcURL host] - path: [NSString stringWithFormat: @"%@%@", - path, folderName]]; - [destURL autorelease]; - - return destURL; -} - - (WOResponse *) renameFolderAction { - SOGoMailFolder *co, *inbox; + SOGoMailFolder *co; WOResponse *response; - NGImap4Connection *connection; NSException *error; NSString *folderName; - NSURL *srcURL, *destURL; co = [self clientObject]; folderName = [[context request] formValueForKey: @"name"]; - if ([folderName length] > 0) - { - srcURL = [co imap4URL]; - destURL = [self _urlOfFolder: srcURL renamedTo: folderName]; - connection = [co imap4Connection]; - inbox = [[co mailAccountFolder] inboxFolderInContext: context]; - [[connection client] select: [inbox absoluteImap4Name]]; - error = [connection moveMailboxAtURL: srcURL - toURL: destURL]; - - if (error) - { - response = [self responseWithStatus: 500]; - [response appendContentString: @"Unable to rename folder."]; - } - else - { - // We unsubscribe to the old one, and subscribe back to the new one - if ([[[context activeUser] userDefaults] - mailShowSubscribedFoldersOnly]) - { - [[connection client] subscribe: [destURL path]]; - [[connection client] unsubscribe: [srcURL path]]; - } - - response = [self responseWith204]; - } - } - else + error = [co renameTo: folderName]; + if (error) { response = [self responseWithStatus: 500]; - [response appendContentString: @"Missing 'name' parameter."]; + [response appendContentString: @"Unable to rename folder."]; } + else + response = [self responseWith204]; return response; } From 3a0c5fb72c3df74070e121e8e2302539970b31b9 Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Mon, 30 Jan 2012 21:36:48 +0000 Subject: [PATCH 46/68] Monotone-Parent: c8d70dff99b3d65489f2eb27876957a819f70431 Monotone-Revision: cba2145cf03250d3ed2c2ef72a393a81e47435c1 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-01-30T21:36:48 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ChangeLog b/ChangeLog index c3080acc6..2f365e188 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,9 @@ 2012-01-30 Wolfgang Sourdeau + * SoObjects/Mailer/SOGoMailFolder.m (-renameTo:): new method, + implementing most of the code from [UIxMailFolderActions + renameFolderAction]. + * OpenChange/MAPIStoreFallbackContext.m (+listContextsForUser:withTDBIndexing:inMemCtx:): now returns the fallback subfolders as secondary contexts (tmp hack). From 628a788085db9832ca1a7493cd3bbdfb721e9d1d Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Mon, 30 Jan 2012 21:42:03 +0000 Subject: [PATCH 47/68] Monotone-Parent: cba2145cf03250d3ed2c2ef72a393a81e47435c1 Monotone-Revision: 4aebdfab09fd72cefd327e453f27e340da9f8738 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-01-30T21:42:03 Monotone-Branch: ca.inverse.sogo --- OpenChange/MAPIStoreMailContext.m | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/OpenChange/MAPIStoreMailContext.m b/OpenChange/MAPIStoreMailContext.m index 1e740fdfd..3f0ff694e 100644 --- a/OpenChange/MAPIStoreMailContext.m +++ b/OpenChange/MAPIStoreMailContext.m @@ -20,6 +20,7 @@ * Boston, MA 02111-1307, USA. */ +#import #import #import @@ -69,7 +70,7 @@ static Class MAPIStoreMailFolderK; userContext = [MAPIStoreUserContext userContextWithUsername: userName andTDBIndexing: indexingTdb]; - accountFolder = [userContext mailRoot]; + accountFolder = [[userContext rootFolders] objectForKey: @"mail"]; woContext = [userContext woContext]; folderName[0] = @"folderINBOX"; folderName[1] = [NSString stringWithFormat: @"folder%@", @@ -105,7 +106,7 @@ static Class MAPIStoreMailFolderK; - (id) rootSOGoFolder { - return [userContext mailRoot]; + return [[userContext rootFolders] objectForKey: @"mail"]; } + (enum mapistore_context_role) contextRole From 8ffac8727d40aa7b8fa60263c38f629089c8b409 Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Mon, 30 Jan 2012 21:42:10 +0000 Subject: [PATCH 48/68] Monotone-Parent: 4aebdfab09fd72cefd327e453f27e340da9f8738 Monotone-Revision: 6d3ffdac670dcdb4b4ded0a4c992663c091081b4 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-01-30T21:42:10 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 7 +++++++ OpenChange/MAPIStoreGCSFolder.m | 20 +++++++++++++++++++ OpenChange/MAPIStoreMailFolder.h | 5 ----- OpenChange/MAPIStoreMailFolder.m | 33 ++++++++++++++++++++------------ 4 files changed, 48 insertions(+), 17 deletions(-) diff --git a/ChangeLog b/ChangeLog index 2f365e188..ca12f5eb8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,12 @@ 2012-01-30 Wolfgang Sourdeau + * OpenChange/MAPIStoreMailFolder.m (-addProperties): overriden + method in order to intercept rename operations. + (-initWithSOGoObject:inContainer:): removed obsolete method. + + * OpenChange/MAPIStoreGCSFolder.m (-addProperties): overriden + method in order to intercept rename operations. + * SoObjects/Mailer/SOGoMailFolder.m (-renameTo:): new method, implementing most of the code from [UIxMailFolderActions renameFolderAction]. diff --git a/OpenChange/MAPIStoreGCSFolder.m b/OpenChange/MAPIStoreGCSFolder.m index 69f1d3d6c..47e7ce24f 100644 --- a/OpenChange/MAPIStoreGCSFolder.m +++ b/OpenChange/MAPIStoreGCSFolder.m @@ -72,6 +72,26 @@ [super dealloc]; } +- (void) addProperties: (NSDictionary *) newProperties +{ + NSString *newDisplayName; + NSMutableDictionary *propsCopy; + NSNumber *key; + + key = MAPIPropertyKey (PR_DISPLAY_NAME_UNICODE); + newDisplayName = [newProperties objectForKey: key]; + if (newDisplayName) + { + [sogoObject renameTo: newDisplayName]; + propsCopy = [newProperties mutableCopy]; + [propsCopy removeObjectForKey: key]; + [propsCopy autorelease]; + newProperties = propsCopy; + } + + [super addProperties: newProperties]; +} + - (NSArray *) messageKeysMatchingQualifier: (EOQualifier *) qualifier andSortOrderings: (NSArray *) sortOrderings { diff --git a/OpenChange/MAPIStoreMailFolder.h b/OpenChange/MAPIStoreMailFolder.h index 300a5dc3a..8a5a51ec2 100644 --- a/OpenChange/MAPIStoreMailFolder.h +++ b/OpenChange/MAPIStoreMailFolder.h @@ -37,13 +37,8 @@ @interface MAPIStoreMailFolder : MAPIStoreFolder { SOGoMAPIFSMessage *versionsMessage; - BOOL usesAltNameSpace; } -/* subclasses */ -- (SOGoMailFolder *) specialFolderFromAccount: (SOGoMailAccount *) account - inContext: (WOContext *) woContext; - /* synchronisation & versioning */ - (BOOL) synchroniseCache; - (NSNumber *) modseqFromMessageChangeNumber: (NSNumber *) changeNum; diff --git a/OpenChange/MAPIStoreMailFolder.m b/OpenChange/MAPIStoreMailFolder.m index c9391c3ff..0541c6beb 100644 --- a/OpenChange/MAPIStoreMailFolder.m +++ b/OpenChange/MAPIStoreMailFolder.m @@ -77,15 +77,11 @@ static Class SOGoMailFolderK; [MAPIStoreAppointmentWrapper class]; } -- (id) initWithSOGoObject: (id) newSOGoObject - inContainer: (MAPIStoreObject *) newContainer +- (id) init { - // NSString *urlString; - - if ((self = [super initWithSOGoObject: newSOGoObject inContainer: newContainer])) + if ((self = [super init])) { - usesAltNameSpace = NO; - // urlString = [[self url] stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding]; + versionsMessage = nil; } return self; @@ -104,12 +100,25 @@ static Class SOGoMailFolderK; [super dealloc]; } -- (SOGoMailFolder *) specialFolderFromAccount: (SOGoMailAccount *) accountFolder - inContext: (WOContext *) woContext -{ - [self subclassResponsibility: _cmd]; - return nil; +- (void) addProperties: (NSDictionary *) newProperties +{ + NSString *newDisplayName; + NSMutableDictionary *propsCopy; + NSNumber *key; + + key = MAPIPropertyKey (PR_DISPLAY_NAME_UNICODE); + newDisplayName = [newProperties objectForKey: key]; + if (newDisplayName) + { + [(SOGoMailFolder *) sogoObject renameTo: newDisplayName]; + propsCopy = [newProperties mutableCopy]; + [propsCopy removeObjectForKey: key]; + [propsCopy autorelease]; + newProperties = propsCopy; + } + + [super addProperties: newProperties]; } - (MAPIStoreMessageTable *) messageTable From a0060fd7ceb9ed869faba80a0b64286d8c485f6b Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Tue, 31 Jan 2012 12:54:36 +0000 Subject: [PATCH 49/68] Monotone-Parent: 6d3ffdac670dcdb4b4ded0a4c992663c091081b4 Monotone-Revision: 38034fab3edb528144b8d3e433e6e9bb5ff1a41c Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-01-31T12:54:36 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 9 +++ OpenChange/MAPIStoreMailContext.h | 4 +- OpenChange/MAPIStoreMailContext.m | 99 +++++++++++++++++++++++-------- 3 files changed, 83 insertions(+), 29 deletions(-) diff --git a/ChangeLog b/ChangeLog index ca12f5eb8..99da1640d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +2012-01-31 Wolfgang Sourdeau + + * OpenChange/MAPIStoreMailContext.m + (+listContextsForUser:withTDBIndexing:inMemCtx:): now returns + secondary folders. + (+[MAPIStoreOutboxContext + listContextsForUser:withTDBIndexing:inMemCtx:]): overridden method + in outbox-specific class. + 2012-01-30 Wolfgang Sourdeau * OpenChange/MAPIStoreMailFolder.m (-addProperties): overriden diff --git a/OpenChange/MAPIStoreMailContext.h b/OpenChange/MAPIStoreMailContext.h index ca67e1664..ab176cd1a 100644 --- a/OpenChange/MAPIStoreMailContext.h +++ b/OpenChange/MAPIStoreMailContext.h @@ -28,9 +28,7 @@ @interface MAPIStoreMailContext : MAPIStoreContext @end -#import "MAPIStoreFSBaseContext.h" - -@interface MAPIStoreDeletedItemsContext : MAPIStoreFSBaseContext +@interface MAPIStoreOutboxContext : MAPIStoreMailContext @end #endif /* MAPISTOREMAILCONTEXT_H */ diff --git a/OpenChange/MAPIStoreMailContext.m b/OpenChange/MAPIStoreMailContext.m index 3f0ff694e..58825e166 100644 --- a/OpenChange/MAPIStoreMailContext.m +++ b/OpenChange/MAPIStoreMailContext.m @@ -20,6 +20,7 @@ * Boston, MA 02111-1307, USA. */ +#import #import #import @@ -54,34 +55,38 @@ static Class MAPIStoreMailFolderK; withTDBIndexing: (struct tdb_wrap *) indexingTdb inMemCtx: (TALLOC_CTX *) memCtx { - struct mapistore_contexts_list *firstContext, *context; - NSString *urlBase, *stringData; + struct mapistore_contexts_list *firstContext = NULL, *context; + NSString *urlBase, *stringData, *currentName, *inboxName, *draftsName, *sentName, *trashName; + NSMutableArray *secondaryFolders; enum mapistore_context_role role[] = {MAPISTORE_MAIL_ROLE, MAPISTORE_DRAFTS_ROLE, - MAPISTORE_SENTITEMS_ROLE, - MAPISTORE_OUTBOX_ROLE}; - NSString *folderName[4]; - NSUInteger count; + MAPISTORE_SENTITEMS_ROLE}; + NSString *folderName[3]; + NSUInteger count, max; SOGoMailAccount *accountFolder; MAPIStoreUserContext *userContext; WOContext *woContext; - firstContext = NULL; - userContext = [MAPIStoreUserContext userContextWithUsername: userName andTDBIndexing: indexingTdb]; accountFolder = [[userContext rootFolders] objectForKey: @"mail"]; woContext = [userContext woContext]; - folderName[0] = @"folderINBOX"; - folderName[1] = [NSString stringWithFormat: @"folder%@", - [accountFolder draftsFolderNameInContext: woContext]]; - folderName[2] = [NSString stringWithFormat: @"folder%@", - [accountFolder sentFolderNameInContext: woContext]]; - folderName[3] = folderName[1]; + + inboxName = @"folderINBOX"; + folderName[0] = inboxName; + + draftsName = [NSString stringWithFormat: @"folder%@", + [accountFolder draftsFolderNameInContext: woContext]]; + folderName[1] = draftsName; + sentName = [NSString stringWithFormat: @"folder%@", + [accountFolder sentFolderNameInContext: woContext]]; + folderName[2] = sentName; + trashName = [NSString stringWithFormat: @"folder%@", + [accountFolder trashFolderNameInContext: woContext]]; urlBase = [NSString stringWithFormat: @"sogo://%@:%@@mail/", userName, userName]; - for (count = 0; count < 4; count++) + for (count = 0; count < 3; count++) { context = talloc_zero (memCtx, struct mapistore_contexts_list); stringData = [NSString stringWithFormat: @"%@%@", urlBase, @@ -96,6 +101,29 @@ static Class MAPIStoreMailFolderK; DLIST_ADD_END (firstContext, context, void); } + secondaryFolders = [[accountFolder toManyRelationshipKeysWithNamespaces: NO] + mutableCopy]; + [secondaryFolders autorelease]; + [secondaryFolders removeObject: inboxName]; + [secondaryFolders removeObject: draftsName]; + [secondaryFolders removeObject: draftsName]; + [secondaryFolders removeObject: sentName]; + [secondaryFolders removeObject: trashName]; + max = [secondaryFolders count]; + for (count = 0; count < max; count++) + { + context = talloc_zero (memCtx, struct mapistore_contexts_list); + currentName = [secondaryFolders objectAtIndex: count]; + stringData = [NSString stringWithFormat: @"%@%@", urlBase, currentName]; + context->url = [stringData asUnicodeInMemCtx: context]; + stringData = [currentName substringFromIndex: 6]; + context->name = [stringData asUnicodeInMemCtx: context]; + context->main_folder = false; + context->role = MAPISTORE_MAIL_ROLE; + context->tag = "tag"; + DLIST_ADD_END (firstContext, context, void); + } + return firstContext; } @@ -109,25 +137,44 @@ static Class MAPIStoreMailFolderK; return [[userContext rootFolders] objectForKey: @"mail"]; } -+ (enum mapistore_context_role) contextRole -{ - return MAPISTORE_MAIL_ROLE; -} - @end -#import "MAPIStoreFSFolder.h" - -@implementation MAPIStoreDeletedItemsContext +@implementation MAPIStoreOutboxContext + (NSString *) MAPIModuleName { - return @"deleted-items"; + return @"outbox"; } -+ (enum mapistore_context_role) contextRole ++ (struct mapistore_contexts_list *) listContextsForUser: (NSString *) userName + withTDBIndexing: (struct tdb_wrap *) indexingTdb + inMemCtx: (TALLOC_CTX *) memCtx { - return MAPISTORE_DELETEDITEMS_ROLE; + struct mapistore_contexts_list *context; + NSString *url, *folderName; + SOGoMailAccount *accountFolder; + MAPIStoreUserContext *userContext; + WOContext *woContext; + + userContext = [MAPIStoreUserContext userContextWithUsername: userName + andTDBIndexing: indexingTdb]; + accountFolder = [[userContext rootFolders] objectForKey: @"mail"]; + woContext = [userContext woContext]; + folderName = [NSString stringWithFormat: @"folder%@", + [accountFolder draftsFolderNameInContext: woContext]]; + url = [NSString stringWithFormat: @"sogo://%@:%@@outbox/%@", userName, + userName, folderName]; + + context = talloc_zero (memCtx, struct mapistore_contexts_list); + context->url = [url asUnicodeInMemCtx: context]; + /* TODO: use a localized version of this display name */ + context->name = [@"Outbox" asUnicodeInMemCtx: context]; + context->main_folder = true; + context->role = MAPISTORE_OUTBOX_ROLE; + context->tag = "tag"; + context->prev = context; + + return context; } @end From 6bec1d842f670c8a0235319b126e7739fa1a24f8 Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Thu, 2 Feb 2012 01:38:50 +0000 Subject: [PATCH 50/68] Monotone-Parent: ba1ab7317c5e8c4b1e95d7e3f299bfd9ea38b10d Monotone-Revision: 392e0e0b68e0c3efba94a66ecf87596c3d6d967f Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-02-02T01:38:50 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 5 +++++ OpenChange/MAPIApplication.m | 10 +++------- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/ChangeLog b/ChangeLog index a81a5eb19..c35e36c60 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2012-02-01 Wolfgang Sourdeau + + * OpenChange/MAPIApplication.m (-setUserContext:): do not retain + the user context. + 2012-02-01 Francis Lachapelle * UI/WebServerResources/UIxCalUserRightsEditor.js (onUpdateACL): diff --git a/OpenChange/MAPIApplication.m b/OpenChange/MAPIApplication.m index ccdb59614..8cdce056d 100644 --- a/OpenChange/MAPIApplication.m +++ b/OpenChange/MAPIApplication.m @@ -67,15 +67,11 @@ MAPIApplication *MAPIApp = nil; return MAPIApp; } -- (void) dealloc -{ - [userContext release]; - [super dealloc]; -} - - (void) setUserContext: (MAPIStoreUserContext *) newContext { - ASSIGN (userContext, newContext); + /* user contexts must not be retained here ad their holder (mapistore) + contexts must be active when any operation occurs. */ + userContext = newContext; } - (id) authenticatorInContext: (id) context From 28f7997539cc4304a606612660ea754f957590f0 Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Thu, 2 Feb 2012 01:42:02 +0000 Subject: [PATCH 51/68] Monotone-Parent: 392e0e0b68e0c3efba94a66ecf87596c3d6d967f Monotone-Revision: be64ce923b1780a6ed810e7250748d84ad4425bb Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-02-02T01:42:02 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 3 +++ SoObjects/SOGo/SOGoParentFolder.h | 1 + SoObjects/SOGo/SOGoParentFolder.m | 5 +++++ 3 files changed, 9 insertions(+) diff --git a/ChangeLog b/ChangeLog index c35e36c60..ab02014d7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2012-02-01 Wolfgang Sourdeau + * SoObjects/SOGo/SOGoParentFolder.m (-removeSubFolder): new method + that removes a subfolder entry from the folder cache. + * OpenChange/MAPIApplication.m (-setUserContext:): do not retain the user context. diff --git a/SoObjects/SOGo/SOGoParentFolder.h b/SoObjects/SOGo/SOGoParentFolder.h index 278d94ac6..c0041879b 100644 --- a/SoObjects/SOGo/SOGoParentFolder.h +++ b/SoObjects/SOGo/SOGoParentFolder.h @@ -43,6 +43,7 @@ - (NSString *) defaultFolderName; - (NSException *) appendPersonalSources; +- (void) removeSubFolder: (NSString *) subfolderName; - (void) setBaseOCSPath: (NSString *) newOCSPath; diff --git a/SoObjects/SOGo/SOGoParentFolder.m b/SoObjects/SOGo/SOGoParentFolder.m index 08ecd35dd..58e19958b 100644 --- a/SoObjects/SOGo/SOGoParentFolder.m +++ b/SoObjects/SOGo/SOGoParentFolder.m @@ -361,6 +361,11 @@ static SoSecurityManager *sm = nil; return error; } +- (void) removeSubFolder: (NSString *) subfolderName +{ + [subFolders removeObjectForKey: subfolderName]; +} + - (NSException *) initSubscribedSubFolders { NSException *error; From 32ee60c025cd00b2cf3f404418f931e53b986ffa Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Thu, 2 Feb 2012 01:45:17 +0000 Subject: [PATCH 52/68] Monotone-Parent: be64ce923b1780a6ed810e7250748d84ad4425bb Monotone-Revision: 7f9dd19e7f17c342c380ab2a0c551594be6d8762 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-02-02T01:45:17 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 10 ++++++++++ OpenChange/MAPIStoreFolder.h | 2 +- OpenChange/MAPIStoreFolder.m | 8 +++++--- OpenChange/MAPIStoreGCSFolder.m | 30 ++++++++++++++++++++++++++++++ OpenChange/MAPIStoreSOGo.m | 6 +++--- OpenChange/SOGoMAPIFSFolder.m | 17 +++++++++++++++++ OpenChange/SOGoMAPIFSMessage.m | 10 +++++++--- 7 files changed, 73 insertions(+), 10 deletions(-) diff --git a/ChangeLog b/ChangeLog index ab02014d7..d15bec30a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,15 @@ 2012-02-01 Wolfgang Sourdeau + * OpenChange/SOGoMAPIFSMessage.m (-delete): returns the exception + rather than raising it. + + * OpenChange/SOGoMAPIFSFolder.m (-delete): new method that removes + the directory from the filesystem. + + * OpenChange/MAPIStoreGCSFolder.m (-deleteFolder): overriden method. + + * OpenChange/MAPIStoreFolder.m (-deleteFolder): implemented method. + * SoObjects/SOGo/SOGoParentFolder.m (-removeSubFolder): new method that removes a subfolder entry from the folder cache. diff --git a/OpenChange/MAPIStoreFolder.h b/OpenChange/MAPIStoreFolder.h index 48e6f5f29..178bb36fd 100644 --- a/OpenChange/MAPIStoreFolder.h +++ b/OpenChange/MAPIStoreFolder.h @@ -96,7 +96,7 @@ - (int) createFolder: (MAPIStoreFolder **) childFolderPtr withRow: (struct SRow *) aRow andFID: (uint64_t) fid; -- (int) deleteFolderWithFID: (uint64_t) fid; +- (int) deleteFolder; - (int) getChildCount: (uint32_t *) rowCount ofTableType: (enum mapistore_table_type) tableType; diff --git a/OpenChange/MAPIStoreFolder.m b/OpenChange/MAPIStoreFolder.m index 2f5279ba0..20670225d 100644 --- a/OpenChange/MAPIStoreFolder.m +++ b/OpenChange/MAPIStoreFolder.m @@ -384,11 +384,13 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe return rc; } -- (int) deleteFolderWithFID: (uint64_t) fid +- (int) deleteFolder { - [self logWithFormat: @"UNIMPLEMENTED METHOD '%s' (%d)", __FUNCTION__, __LINE__]; + [propsMessage delete]; + [propsFolder delete]; + [faiFolder delete]; - return MAPISTORE_ERROR; + return MAPISTORE_SUCCESS; } - (int) getChildCount: (uint32_t *) rowCount diff --git a/OpenChange/MAPIStoreGCSFolder.m b/OpenChange/MAPIStoreGCSFolder.m index 47e7ce24f..f41aa5eb3 100644 --- a/OpenChange/MAPIStoreGCSFolder.m +++ b/OpenChange/MAPIStoreGCSFolder.m @@ -22,6 +22,7 @@ #import #import +#import #import #import #import @@ -29,6 +30,7 @@ #import #import #import +#import #import #import @@ -44,6 +46,7 @@ #undef DEBUG #include +#include @implementation MAPIStoreGCSFolder @@ -72,6 +75,33 @@ [super dealloc]; } +- (int) deleteFolder +{ + int rc; + NSException *error; + NSString *name; + + name = [self nameInContainer]; + if ([name isEqualToString: @"personal"]) + rc = MAPISTORE_ERR_DENIED; + else + { + [[sogoObject container] removeSubFolder: name]; + error = [(SOGoGCSFolder *) sogoObject delete]; + if (error) + rc = MAPISTORE_ERROR; + else + { + if (![versionsMessage delete]) + rc = MAPISTORE_SUCCESS; + else + rc = MAPISTORE_ERROR; + } + } + + return (rc == MAPISTORE_SUCCESS) ? [super deleteFolder] : rc; +} + - (void) addProperties: (NSDictionary *) newProperties { NSString *newDisplayName; diff --git a/OpenChange/MAPIStoreSOGo.m b/OpenChange/MAPIStoreSOGo.m index a5fbf7982..1c8bc75a9 100644 --- a/OpenChange/MAPIStoreSOGo.m +++ b/OpenChange/MAPIStoreSOGo.m @@ -333,7 +333,7 @@ sogo_folder_create_folder(void *folder_object, TALLOC_CTX *mem_ctx, \return MAPISTORE_SUCCESS on success, otherwise MAPISTORE_ERROR */ static enum mapistore_error -sogo_folder_delete_folder(void *folder_object, uint64_t fid) +sogo_folder_delete(void *folder_object) { struct MAPIStoreTallocWrapper *wrapper; NSAutoreleasePool *pool; @@ -347,7 +347,7 @@ sogo_folder_delete_folder(void *folder_object, uint64_t fid) wrapper = folder_object; folder = wrapper->MAPIStoreSOGoObject; pool = [NSAutoreleasePool new]; - rc = [folder deleteFolderWithFID: fid]; + rc = [folder deleteFolder]; [pool release]; } else @@ -1244,7 +1244,7 @@ int mapistore_init_backend(void) backend.context.get_root_folder = sogo_context_get_root_folder; backend.folder.open_folder = sogo_folder_open_folder; backend.folder.create_folder = sogo_folder_create_folder; - backend.folder.delete_folder = sogo_folder_delete_folder; + backend.folder.delete = sogo_folder_delete; backend.folder.open_message = sogo_folder_open_message; backend.folder.create_message = sogo_folder_create_message; backend.folder.delete_message = sogo_folder_delete_message; diff --git a/OpenChange/SOGoMAPIFSFolder.m b/OpenChange/SOGoMAPIFSFolder.m index 7969d9e65..429e23f93 100644 --- a/OpenChange/SOGoMAPIFSFolder.m +++ b/OpenChange/SOGoMAPIFSFolder.m @@ -314,6 +314,23 @@ static NSString *privateDir = nil; 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 { diff --git a/OpenChange/SOGoMAPIFSMessage.m b/OpenChange/SOGoMAPIFSMessage.m index 971e6ba99..15fe4afa7 100644 --- a/OpenChange/SOGoMAPIFSMessage.m +++ b/OpenChange/SOGoMAPIFSMessage.m @@ -141,14 +141,18 @@ - (NSException *) delete { NSFileManager *fm; + NSException *error; fm = [NSFileManager defaultManager]; if (![fm removeFileAtPath: [self completeFilename] handler: NULL]) - [NSException raise: @"MAPIStoreIOException" - format: @"could not delete message"]; + error = [NSException exceptionWithName: @"MAPIStoreIOException" + reason: @"could not delete message" + userInfo: nil]; + else + error = nil; - return nil; + return error; } - (id) _fileAttributeForKey: (NSString *) key From cde6473b8b3c86a51c015586a8a448e81338153f Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Thu, 2 Feb 2012 01:48:17 +0000 Subject: [PATCH 53/68] Monotone-Parent: 7f9dd19e7f17c342c380ab2a0c551594be6d8762 Monotone-Revision: 66e23944b08cdd3ed0195dd37f8448063bdfa560 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-02-02T01:48:17 Monotone-Branch: ca.inverse.sogo --- OpenChange/MAPIStoreGCSBaseContext.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenChange/MAPIStoreGCSBaseContext.m b/OpenChange/MAPIStoreGCSBaseContext.m index 2ccfca080..d155200bd 100644 --- a/OpenChange/MAPIStoreGCSBaseContext.m +++ b/OpenChange/MAPIStoreGCSBaseContext.m @@ -65,7 +65,7 @@ { userContext = [MAPIStoreUserContext userContextWithUsername: userName andTDBIndexing: indexingTdb]; - parentFolder = [[userContext rootFolders] objectForKey: [self MAPIModuleName]]; + parentFolder = [[userContext rootFolders] objectForKey: moduleName]; baseUrl = [NSString stringWithFormat: @"sogo://%@@%@/", userName, moduleName]; From b1cf68c8374120eb1d1a5804a27e180de9825afb Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Thu, 2 Feb 2012 01:48:26 +0000 Subject: [PATCH 54/68] Monotone-Parent: 66e23944b08cdd3ed0195dd37f8448063bdfa560 Monotone-Revision: 5ac32e0bdcbade2a73e1d7ae56eef8485e68a6bb Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-02-02T01:48:26 Monotone-Branch: ca.inverse.sogo --- OpenChange/MAPIStoreUserContext.h | 1 + 1 file changed, 1 insertion(+) diff --git a/OpenChange/MAPIStoreUserContext.h b/OpenChange/MAPIStoreUserContext.h index 01b6ef7e8..43ae2e7ed 100644 --- a/OpenChange/MAPIStoreUserContext.h +++ b/OpenChange/MAPIStoreUserContext.h @@ -25,6 +25,7 @@ #import +@class NSMutableDictionary; @class NSString; @class WOContext; From e149946f3b6aa06e2eba97888062ac9f03dde34c Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Thu, 2 Feb 2012 01:49:39 +0000 Subject: [PATCH 55/68] Monotone-Parent: 5ac32e0bdcbade2a73e1d7ae56eef8485e68a6bb Monotone-Revision: 5acda2afec59d0f39574d366491c5ae9f40fa3de Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-02-02T01:49:39 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 3 +++ OpenChange/MAPIStoreTable.m | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index d15bec30a..c6309fcda 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2012-02-01 Wolfgang Sourdeau + * OpenChange/MAPIStoreTable.m (-destroyHandle:): skih operation if + the handle parameter is 0. + * OpenChange/SOGoMAPIFSMessage.m (-delete): returns the exception rather than raising it. diff --git a/OpenChange/MAPIStoreTable.m b/OpenChange/MAPIStoreTable.m index 16b8febf6..620b8f248 100644 --- a/OpenChange/MAPIStoreTable.m +++ b/OpenChange/MAPIStoreTable.m @@ -351,7 +351,7 @@ static Class NSDataK, NSStringK; - (void) destroyHandle: (uint32_t) tableHandleId { - if (handleId == tableHandleId) + if (tableHandleId && (handleId == tableHandleId)) [[MAPIStoreActiveTables activeTables] unregisterTable: self]; } From 82b2bedd7e54d7082de17ed45c5dbed43f191410 Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Thu, 2 Feb 2012 01:50:45 +0000 Subject: [PATCH 56/68] Monotone-Parent: 5acda2afec59d0f39574d366491c5ae9f40fa3de Monotone-Revision: 638f19a902b772b34bc553dda4b8925b5d0639df Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-02-02T01:50:45 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 5 +++++ OpenChange/MAPIStoreUserContext.m | 4 +--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/ChangeLog b/ChangeLog index c6309fcda..a2ba604f4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,10 @@ 2012-02-01 Wolfgang Sourdeau + * OpenChange/MAPIStoreUserContext.m (-destroy): dont't release + "username" as it is not initially retained. + (+userContextWithUsername:andTDBIndexing:): the resulting instance + is cached in the table from here instead. + * OpenChange/MAPIStoreTable.m (-destroyHandle:): skih operation if the handle parameter is 0. diff --git a/OpenChange/MAPIStoreUserContext.m b/OpenChange/MAPIStoreUserContext.m index b2b97babc..222f1d560 100644 --- a/OpenChange/MAPIStoreUserContext.m +++ b/OpenChange/MAPIStoreUserContext.m @@ -61,6 +61,7 @@ static NSMapTable *contextsTable = nil; userContext = [[self alloc] initWithUsername: username andTDBIndexing: indexingTdb]; [userContext autorelease]; + [contextsTable setObject: userContext forKey: username]; } return userContext; @@ -97,11 +98,9 @@ static NSMapTable *contextsTable = nil; if (indexingTdb) ASSIGN (mapping, [MAPIStoreMapping mappingForUsername: username withIndexing: indexingTdb]); - [contextsTable setObject: self forKey: username]; authenticator = [MAPIStoreAuthenticator new]; [authenticator setUsername: username]; - /* TODO: very hackish (IMAP access) */ [authenticator setPassword: username]; } @@ -118,7 +117,6 @@ static NSMapTable *contextsTable = nil; [authenticator release]; [mapping release]; - [username release]; [sogoUser release]; [contextsTable removeObjectForKey: username]; From 8cbe8f862ed7a49b11a0bf20eb3aba66d4d5a6f1 Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Fri, 3 Feb 2012 14:55:19 +0000 Subject: [PATCH 57/68] Monotone-Parent: 638f19a902b772b34bc553dda4b8925b5d0639df Monotone-Revision: 599ccb5bc0882492dab453f5e9af28f0342e6a87 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-02-03T14:55:19 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 5 +++++ SoObjects/Mailer/SOGoMailFolder.h | 2 ++ SoObjects/Mailer/SOGoMailFolder.m | 13 +++++++++---- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/ChangeLog b/ChangeLog index a2ba604f4..e40b7f3cb 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2012-02-03 Wolfgang Sourdeau + + * SoObjects/Mailer/SOGoMailFolder.m (-exists): new method that + returns whether an IMAP folder exists or not yet. + 2012-02-01 Wolfgang Sourdeau * OpenChange/MAPIStoreUserContext.m (-destroy): dont't release diff --git a/SoObjects/Mailer/SOGoMailFolder.h b/SoObjects/Mailer/SOGoMailFolder.h index f1a9e15cd..98bc94c85 100644 --- a/SoObjects/Mailer/SOGoMailFolder.h +++ b/SoObjects/Mailer/SOGoMailFolder.h @@ -82,6 +82,8 @@ - (void) markForExpunge; - (void) expungeLastMarkedFolder; +- (BOOL) exists; + - (BOOL) create; - (NSException *) expunge; diff --git a/SoObjects/Mailer/SOGoMailFolder.m b/SoObjects/Mailer/SOGoMailFolder.m index 03e7b9b15..87cece7d8 100644 --- a/SoObjects/Mailer/SOGoMailFolder.m +++ b/SoObjects/Mailer/SOGoMailFolder.m @@ -248,7 +248,7 @@ static NSString *defaultUserID = @"anyone"; if (!filenames) { filenames = [NSMutableArray new]; - if ([[self imap4Connection] doesMailboxExistAtURL: [self imap4URL]]) + if ([self exists]) { uids = [self fetchUIDsMatchingQualifier: nil sortOrdering: @"DATE"]; if (![uids isKindOfClass: [NSException class]]) @@ -555,7 +555,7 @@ static NSString *defaultUserID = @"anyone"; NSString *archiveName; EOQualifier *notDeleted; - if ([[self imap4Connection] doesMailboxExistAtURL: [self imap4URL]]) + if ([self exists]) { notDeleted = [EOQualifier qualifierWithQualifierFormat: @"(not (flags = %@))", @"deleted"]; @@ -722,7 +722,7 @@ static NSString *defaultUserID = @"anyone"; { // We check for the existence of the IMAP folder (likely to be the // Sent mailbox) prior to appending messages to it. - if ([[self imap4Connection] doesMailboxExistAtURL: [self imap4URL]] + if ([self exists] || ![[self imap4Connection] createMailbox: [[self imap4Connection] imap4FolderNameForURL: [self imap4URL]] atURL: [[self mailAccountFolder] imap4URL]]) return [[self imap4Connection] postData: _data flags: _flags @@ -835,7 +835,7 @@ static NSString *defaultUserID = @"anyone"; inContainer: self]; } else if (isdigit ([_key characterAtIndex: 0]) - && [[self imap4Connection] doesMailboxExistAtURL: [self imap4URL]]) + && [self exists]) { obj = [SOGoMailObject objectWithName: _key inContainer: self]; if ([_key hasSuffix: @".eml"]) @@ -863,6 +863,11 @@ static NSString *defaultUserID = @"anyone"; return [[self imap4Connection] createMailbox:_name atURL:[self imap4URL]]; } +- (BOOL) exists +{ + return [[self imap4Connection] doesMailboxExistAtURL: [self imap4URL]]; +} + - (BOOL) create { NSException *error; From dff90f588acbbba5b23aad46dd448452639d60d2 Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Fri, 3 Feb 2012 14:58:33 +0000 Subject: [PATCH 58/68] Monotone-Parent: 599ccb5bc0882492dab453f5e9af28f0342e6a87 Monotone-Revision: aff6a2cc9b4208ab638ccf2f17af6bb0874f5f5b Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-02-03T14:58:33 Monotone-Branch: ca.inverse.sogo --- OpenChange/MAPIStoreCalendarContext.m | 2 +- OpenChange/MAPIStoreContactsContext.m | 2 +- OpenChange/MAPIStoreGCSBaseContext.m | 7 +------ OpenChange/MAPIStoreTasksContext.m | 2 +- 4 files changed, 4 insertions(+), 9 deletions(-) diff --git a/OpenChange/MAPIStoreCalendarContext.m b/OpenChange/MAPIStoreCalendarContext.m index 9e02b19a4..16018ce85 100644 --- a/OpenChange/MAPIStoreCalendarContext.m +++ b/OpenChange/MAPIStoreCalendarContext.m @@ -46,7 +46,7 @@ static Class MAPIStoreCalendarFolderK; return @"calendar"; } -+ (enum mapistore_context_role) MAPIModuleRole ++ (enum mapistore_context_role) MAPIContextRole { return MAPISTORE_CALENDAR_ROLE; } diff --git a/OpenChange/MAPIStoreContactsContext.m b/OpenChange/MAPIStoreContactsContext.m index 831ca0538..967b1ccd3 100644 --- a/OpenChange/MAPIStoreContactsContext.m +++ b/OpenChange/MAPIStoreContactsContext.m @@ -46,7 +46,7 @@ static Class MAPIStoreContactsFolderK; return @"contacts"; } -+ (enum mapistore_context_role) MAPIModuleRole ++ (enum mapistore_context_role) MAPIContextRole { return MAPISTORE_CONTACTS_ROLE; } diff --git a/OpenChange/MAPIStoreGCSBaseContext.m b/OpenChange/MAPIStoreGCSBaseContext.m index d155200bd..5b8344f1c 100644 --- a/OpenChange/MAPIStoreGCSBaseContext.m +++ b/OpenChange/MAPIStoreGCSBaseContext.m @@ -43,11 +43,6 @@ return nil; } -+ (enum mapistore_context_role) MAPIModuleRole -{ - return -1; -} - + (struct mapistore_contexts_list *) listContextsForUser: (NSString *) userName withTDBIndexing: (struct tdb_wrap *) indexingTdb inMemCtx: (TALLOC_CTX *) memCtx @@ -83,7 +78,7 @@ context->name = [[currentFolder displayName] asUnicodeInMemCtx: context]; context->main_folder = [nameInContainer isEqualToString: @"personal"]; - context->role = [self MAPIModuleRole]; + context->role = [self MAPIContextRole]; context->tag = "tag"; DLIST_ADD_END (firstContext, context, void); } diff --git a/OpenChange/MAPIStoreTasksContext.m b/OpenChange/MAPIStoreTasksContext.m index cf95fb736..c8714c469 100644 --- a/OpenChange/MAPIStoreTasksContext.m +++ b/OpenChange/MAPIStoreTasksContext.m @@ -45,7 +45,7 @@ static Class MAPIStoreTasksFolderK; return @"tasks"; } -+ (enum mapistore_context_role) MAPIModuleRole ++ (enum mapistore_context_role) MAPIContextRole { return MAPISTORE_TASKS_ROLE; } From 3bf138283b88e8ae61a25ec8b4a58a8db8fb3982 Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Fri, 3 Feb 2012 15:01:23 +0000 Subject: [PATCH 59/68] Monotone-Parent: aff6a2cc9b4208ab638ccf2f17af6bb0874f5f5b Monotone-Revision: 425bfc2f774ffc6abc9a76908ac1b2fcf7d6ad14 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-02-03T15:01:23 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 2 ++ OpenChange/MAPIStoreMailFolder.m | 26 ++++++++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/ChangeLog b/ChangeLog index e40b7f3cb..3a3077043 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,7 @@ 2012-02-03 Wolfgang Sourdeau + * OpenChange/MAPIStoreMailFolder.m (-deleteFolder): overriden method. + * SoObjects/Mailer/SOGoMailFolder.m (-exists): new method that returns whether an IMAP folder exists or not yet. diff --git a/OpenChange/MAPIStoreMailFolder.m b/OpenChange/MAPIStoreMailFolder.m index 0541c6beb..a5e3483bd 100644 --- a/OpenChange/MAPIStoreMailFolder.m +++ b/OpenChange/MAPIStoreMailFolder.m @@ -158,6 +158,32 @@ static Class SOGoMailFolderK; return nameInContainer; } +- (int) deleteFolder +{ + int rc; + NSException *error; + NSString *name; + + name = [self nameInContainer]; + if ([name isEqualToString: @"folderINBOX"]) + rc = MAPISTORE_ERR_DENIED; + else + { + error = [(SOGoMailFolder *) sogoObject delete]; + if (error) + rc = MAPISTORE_ERROR; + else + { + if (![versionsMessage delete]) + rc = MAPISTORE_SUCCESS; + else + rc = MAPISTORE_ERROR; + } + } + + return (rc == MAPISTORE_SUCCESS) ? [super deleteFolder] : rc; +} + - (int) getPrContentUnread: (void **) data inMemCtx: (TALLOC_CTX *) memCtx { From d64c7164d72a03da3e3acbdd97740e2100418bc8 Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Fri, 3 Feb 2012 15:05:19 +0000 Subject: [PATCH 60/68] Monotone-Parent: 425bfc2f774ffc6abc9a76908ac1b2fcf7d6ad14 Monotone-Revision: 82a617e3ad7bb1a96e181c9d1c240bc2c89de7f7 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-02-03T15:05:19 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 4 ++++ OpenChange/MAPIStoreFSFolder.m | 12 ++++++------ OpenChange/MAPIStoreFolder.h | 5 +++-- OpenChange/MAPIStoreFolder.m | 13 ++++++------- OpenChange/MAPIStoreMailFolder.m | 16 +++++++++++----- 5 files changed, 30 insertions(+), 20 deletions(-) diff --git a/ChangeLog b/ChangeLog index 3a3077043..3e1e4651e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,9 @@ 2012-02-03 Wolfgang Sourdeau + * OpenChange/MAPIStoreFolder.m (-createFolder:withFID:andKey:): + modified method to return a enum mapistore_error, in order to + better determine of the failures that occur. + * OpenChange/MAPIStoreMailFolder.m (-deleteFolder): overriden method. * SoObjects/Mailer/SOGoMailFolder.m (-exists): new method that diff --git a/OpenChange/MAPIStoreFSFolder.m b/OpenChange/MAPIStoreFSFolder.m index 684ab5a7b..145781383 100644 --- a/OpenChange/MAPIStoreFSFolder.m +++ b/OpenChange/MAPIStoreFSFolder.m @@ -42,9 +42,7 @@ #undef DEBUG #include -// #include -// #include -// #include +#include static Class EOKeyValueQualifierK; @@ -75,8 +73,9 @@ static NSString *MAPIStoreRightFolderContact = @"RightsFolderContact"; return [MAPIStoreFSFolderTable tableForContainer: self]; } -- (NSString *) createFolder: (struct SRow *) aRow - withFID: (uint64_t) newFID +- (enum mapistore_error) createFolder: (struct SRow *) aRow + withFID: (uint64_t) newFID + andKey: (NSString **) newKeyP { NSString *newKey, *urlString; NSURL *childURL; @@ -89,8 +88,9 @@ static NSString *MAPIStoreRightFolderContact = @"RightsFolderContact"; childFolder = [SOGoMAPIFSFolder folderWithURL: childURL andTableType: MAPISTORE_MESSAGE_TABLE]; [childFolder ensureDirectory]; + *newKeyP = newKey; - return newKey; + return MAPISTORE_SUCCESS; } - (MAPIStoreMessage *) createMessage diff --git a/OpenChange/MAPIStoreFolder.h b/OpenChange/MAPIStoreFolder.h index 178bb36fd..b327e8d6d 100644 --- a/OpenChange/MAPIStoreFolder.h +++ b/OpenChange/MAPIStoreFolder.h @@ -145,8 +145,9 @@ andCN: (NSNumber **) cnNbr inTableType: (enum mapistore_table_type) tableType; -- (NSString *) createFolder: (struct SRow *) aRow - withFID: (uint64_t) newFID; +- (enum mapistore_error) createFolder: (struct SRow *) aRow + withFID: (uint64_t) newFID + andKey: (NSString **) newKeyP; - (NSCalendarDate *) lastMessageModificationTime; diff --git a/OpenChange/MAPIStoreFolder.m b/OpenChange/MAPIStoreFolder.m index 20670225d..912c766a6 100644 --- a/OpenChange/MAPIStoreFolder.m +++ b/OpenChange/MAPIStoreFolder.m @@ -354,8 +354,8 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe rc = MAPISTORE_ERR_EXIST; else { - folderKey = [self createFolder: aRow withFID: fid]; - if (folderKey) + rc = [self createFolder: aRow withFID: fid andKey: &folderKey]; + if (rc == MAPISTORE_SUCCESS) { [self cleanupCaches]; baseURL = [self url]; @@ -374,8 +374,6 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe [NSException raise: @"MAPIStoreIOException" format: @"unable to fetch created folder"]; } - else - rc = MAPISTORE_ERROR; } } else @@ -1299,12 +1297,13 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe return newMessage; } -- (NSString *) createFolder: (struct SRow *) aRow - withFID: (uint64_t) newFID +- (enum mapistore_error) createFolder: (struct SRow *) aRow + withFID: (uint64_t) newFID + andKey: (NSString **) newKeyP { [self errorWithFormat: @"new folders cannot be created in this context"]; - return nil; + return MAPISTORE_ERR_DENIED; } /* helpers */ diff --git a/OpenChange/MAPIStoreMailFolder.m b/OpenChange/MAPIStoreMailFolder.m index a5e3483bd..cd5c40371 100644 --- a/OpenChange/MAPIStoreMailFolder.m +++ b/OpenChange/MAPIStoreMailFolder.m @@ -127,9 +127,11 @@ static Class SOGoMailFolderK; return [MAPIStoreMailMessageTable tableForContainer: self]; } -- (NSString *) createFolder: (struct SRow *) aRow - withFID: (uint64_t) newFID +- (enum mapistore_error) createFolder: (struct SRow *) aRow + withFID: (uint64_t) newFID + andKey: (NSString **) newKeyP { + enum mapistore_error rc; NSString *folderName, *nameInContainer; SOGoMailFolder *newFolder; int i; @@ -151,11 +153,15 @@ static Class SOGoMailFolderK; [folderName asCSSIdentifier]]; newFolder = [SOGoMailFolderK objectWithName: nameInContainer inContainer: sogoObject]; - if (![newFolder create]) - nameInContainer = nil; + if ([newFolder create]) + *newKeyP = nameInContainer; + else if ([newFolder exists]) + rc = MAPISTORE_ERR_EXIST; + else + rc = MAPISTORE_ERR_DENIED; } - return nameInContainer; + return rc; } - (int) deleteFolder From f1a3bdd884004ef6262f02509de7650383dcd3b7 Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Fri, 3 Feb 2012 15:05:55 +0000 Subject: [PATCH 61/68] Monotone-Parent: 82a617e3ad7bb1a96e181c9d1c240bc2c89de7f7 Monotone-Revision: fdb9e758160f3e3509c45bb379d9169a3eb0118a Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-02-03T15:05:55 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 3 + OpenChange/MAPIStoreContext.h | 12 ++++ OpenChange/MAPIStoreContext.m | 85 ++++++++++++++++++++++++++- OpenChange/MAPIStoreFallbackContext.m | 19 +++++- OpenChange/MAPIStoreGCSBaseContext.m | 24 ++++++++ OpenChange/MAPIStoreMailContext.m | 37 +++++++++++- OpenChange/MAPIStoreSOGo.m | 39 ++++++++++++ 7 files changed, 213 insertions(+), 6 deletions(-) diff --git a/ChangeLog b/ChangeLog index 3e1e4651e..e578ce025 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2012-02-03 Wolfgang Sourdeau + * OpenChange/MAPIStoreSOGo.m (sogo_backend_create_root_folder): + new backend method. + * OpenChange/MAPIStoreFolder.m (-createFolder:withFID:andKey:): modified method to return a enum mapistore_error, in order to better determine of the failures that occur. diff --git a/OpenChange/MAPIStoreContext.h b/OpenChange/MAPIStoreContext.h index 1ff8dc609..3d41985ad 100644 --- a/OpenChange/MAPIStoreContext.h +++ b/OpenChange/MAPIStoreContext.h @@ -68,6 +68,12 @@ + (struct mapistore_contexts_list *) listContextsForUser: (NSString *) userName withTDBIndexing: (struct tdb_wrap *) indexingTdb inMemCtx: (TALLOC_CTX *) memCtx; ++ (enum mapistore_error) createRootFolder: (NSString **) mapistoreUriP + withFID: (uint64_t ) fid + andName: (NSString *) folderName + forUser: (NSString *) username + withRole: (enum mapistore_context_role) role + andTDBIndexing: (struct tdb_wrap *) indexingTdb; + (int) openContext: (MAPIStoreContext **) contextPtr withURI: (const char *) newUri @@ -104,6 +110,12 @@ /* subclass methods */ + (NSString *) MAPIModuleName; ++ (enum mapistore_context_role) MAPIContextRole; ++ (NSString *) + createRootSecondaryFolderWithFID: (uint64_t) fid + andName: (NSString *) folderName + forUser: (NSString *) userName + withTDBIndexing: (struct tdb_wrap *) indexingTdb; - (Class) MAPIStoreFolderClass; /* the top-most parent of the context folder: SOGoMailAccount, diff --git a/OpenChange/MAPIStoreContext.m b/OpenChange/MAPIStoreContext.m index 921aaafef..376e5e63b 100644 --- a/OpenChange/MAPIStoreContext.m +++ b/OpenChange/MAPIStoreContext.m @@ -35,6 +35,7 @@ #import "MAPIStoreAttachment.h" // #import "MAPIStoreAttachmentTable.h" +#import "MAPIStoreFallbackContext.h" #import "MAPIStoreFolder.h" #import "MAPIStoreFolderTable.h" #import "MAPIStoreMapping.h" @@ -66,7 +67,7 @@ /* sogo://username:password@{contacts,calendar,tasks,journal,notes,mail}/dossier/id */ -static Class NSExceptionK; +static Class NSExceptionK, MAPIStoreFallbackContextK; static NSMutableDictionary *contextClassMapping; @@ -89,11 +90,13 @@ static NSMutableDictionary *contextClassMapping; if (moduleName) { [contextClassMapping setObject: currentClass - forKey: moduleName]; + forKey: moduleName]; NSLog (@" registered class '%@' as handler of '%@' contexts", NSStringFromClass (currentClass), moduleName); } } + + MAPIStoreFallbackContextK = [MAPIStoreFallbackContext class]; } + (struct mapistore_contexts_list *) listAllContextsForUser: (NSString *) userName @@ -127,13 +130,73 @@ static NSMutableDictionary *contextClassMapping; return list; } -+ (struct mapistore_contexts_list *) listContextsForUser: (NSString *) userName ++ (struct mapistore_contexts_list *) listContextsForUser: (NSString *) userName withTDBIndexing: (struct tdb_wrap *) indexingTdb inMemCtx: (TALLOC_CTX *) memCtx { return NULL; } +static Class +MAPIStoreLookupContextClassByRole (Class self, enum mapistore_context_role role) +{ + static NSMutableDictionary *classMapping = nil; + Class currentClass; + enum mapistore_context_role classRole; + NSNumber *roleNbr; + NSArray *classes; + NSUInteger count, max; + + if (!classMapping) + { + classMapping = [NSMutableDictionary new]; + classes = GSObjCAllSubclassesOfClass (self); + max = [classes count]; + for (count = 0; count < max; count++) + { + currentClass = [classes objectAtIndex: count]; + classRole = [currentClass MAPIContextRole]; + if (classRole != -1) + { + roleNbr = [NSNumber numberWithUnsignedInt: classRole]; + [classMapping setObject: currentClass + forKey: roleNbr]; + } + } + } + + roleNbr = [NSNumber numberWithUnsignedInt: role]; + + return [classMapping objectForKey: roleNbr]; +} + ++ (enum mapistore_error) createRootFolder: (NSString **) mapistoreUriP + withFID: (uint64_t) fid + andName: (NSString *) folderName + forUser: (NSString *) userName + withRole: (enum mapistore_context_role) role + andTDBIndexing: (struct tdb_wrap *) indexingTdb +{ + Class contextClass; + NSString *mapistoreURI; + enum mapistore_error rc = MAPISTORE_SUCCESS; + + contextClass = MAPIStoreLookupContextClassByRole (self, role); + if (!contextClass) + contextClass = MAPIStoreFallbackContextK; + + mapistoreURI = [contextClass createRootSecondaryFolderWithFID: fid + andName: (NSString *) folderName + forUser: userName + withTDBIndexing: indexingTdb]; + if (mapistoreURI) + *mapistoreUriP = mapistoreURI; + else + rc = MAPISTORE_ERROR; + + return rc; +} + static inline NSURL *CompleteURLFromMapistoreURI (const char *uri) { NSString *urlString; @@ -474,6 +537,22 @@ static inline NSURL *CompleteURLFromMapistoreURI (const char *uri) return nil; } ++ (enum mapistore_context_role) MAPIContextRole +{ + return -1; +} + ++ (NSString *) + createRootSecondaryFolderWithFID: (uint64_t) fid + andName: (NSString *) folderName + forUser: (NSString *) userName + withTDBIndexing: (struct tdb_wrap *) indexingTdb +{ + [self subclassResponsibility: _cmd]; + + return nil; +} + - (Class) MAPIStoreFolderClass { [self subclassResponsibility: _cmd]; diff --git a/OpenChange/MAPIStoreFallbackContext.m b/OpenChange/MAPIStoreFallbackContext.m index c234e1f84..c3ff666bb 100644 --- a/OpenChange/MAPIStoreFallbackContext.m +++ b/OpenChange/MAPIStoreFallbackContext.m @@ -24,10 +24,12 @@ #import #import -#import "MAPIStoreFallbackContext.h" +#import "MAPIStoreUserContext.h" #import "NSString+MAPIStore.h" #import "SOGoMAPIFSFolder.h" +#import "MAPIStoreFallbackContext.h" + #undef DEBUG #include @@ -38,6 +40,11 @@ return @"fallback"; } ++ (enum mapistore_context_role) MAPIContextRole +{ + return MAPISTORE_MAIL_ROLE; +} + + (struct mapistore_contexts_list *) listContextsForUser: (NSString *) userName withTDBIndexing: (struct tdb_wrap *) indexingTdb inMemCtx: (TALLOC_CTX *) memCtx @@ -82,4 +89,14 @@ return firstContext; } ++ (NSString *) + createRootSecondaryFolderWithFID: (uint64_t) fid + andName: (NSString *) folderName + forUser: (NSString *) userName + withTDBIndexing: (struct tdb_wrap *) indexingTdb +{ + return [NSString stringWithFormat: @"sogo://%@@fallback/0x%.16"PRIx64"/", + userName, (unsigned long long) fid]; +} + @end diff --git a/OpenChange/MAPIStoreGCSBaseContext.m b/OpenChange/MAPIStoreGCSBaseContext.m index 5b8344f1c..a8e552e9c 100644 --- a/OpenChange/MAPIStoreGCSBaseContext.m +++ b/OpenChange/MAPIStoreGCSBaseContext.m @@ -88,6 +88,30 @@ return firstContext; } ++ (NSString *) + createRootSecondaryFolderWithFID: (uint64_t) fid + andName: (NSString *) folderName + forUser: (NSString *) userName + withTDBIndexing: (struct tdb_wrap *) indexingTdb +{ + NSString *mapistoreURI, *nameInContainer, *moduleName; + MAPIStoreUserContext *userContext; + SOGoParentFolder *parentFolder; + + userContext = [MAPIStoreUserContext userContextWithUsername: userName + andTDBIndexing: indexingTdb]; + moduleName = [self MAPIModuleName]; + parentFolder = [[userContext rootFolders] objectForKey: moduleName]; + if (![parentFolder newFolderWithName: folderName + nameInContainer: &nameInContainer]) + mapistoreURI = [NSString stringWithFormat: @"sogo://%@@%@/%@/", + userName, moduleName, nameInContainer]; + else + mapistoreURI = nil; + + return mapistoreURI; +} + - (id) rootSOGoFolder { return [[userContext rootFolders] objectForKey: [isa MAPIModuleName]]; diff --git a/OpenChange/MAPIStoreMailContext.m b/OpenChange/MAPIStoreMailContext.m index 58825e166..84d4c935a 100644 --- a/OpenChange/MAPIStoreMailContext.m +++ b/OpenChange/MAPIStoreMailContext.m @@ -31,6 +31,7 @@ #import "MAPIStoreUserContext.h" #import "NSString+MAPIStore.h" +#import #import "MAPIStoreMailContext.h" #include @@ -51,6 +52,11 @@ static Class MAPIStoreMailFolderK; return @"mail"; } ++ (enum mapistore_context_role) MAPIContextRole +{ + return MAPISTORE_MAIL_ROLE; +} + + (struct mapistore_contexts_list *) listContextsForUser: (NSString *) userName withTDBIndexing: (struct tdb_wrap *) indexingTdb inMemCtx: (TALLOC_CTX *) memCtx @@ -93,7 +99,7 @@ static Class MAPIStoreMailFolderK; folderName[count]]; context->url = [stringData asUnicodeInMemCtx: context]; /* remove "folder" prefix */ - stringData = [folderName[count] substringFromIndex: 6]; + stringData = [[folderName[count] substringFromIndex: 6] fromCSSIdentifier]; context->name = [stringData asUnicodeInMemCtx: context]; context->main_folder = true; context->role = role[count]; @@ -116,7 +122,7 @@ static Class MAPIStoreMailFolderK; currentName = [secondaryFolders objectAtIndex: count]; stringData = [NSString stringWithFormat: @"%@%@", urlBase, currentName]; context->url = [stringData asUnicodeInMemCtx: context]; - stringData = [currentName substringFromIndex: 6]; + stringData = [[currentName substringFromIndex: 6] fromCSSIdentifier]; context->name = [stringData asUnicodeInMemCtx: context]; context->main_folder = false; context->role = MAPISTORE_MAIL_ROLE; @@ -127,6 +133,33 @@ static Class MAPIStoreMailFolderK; return firstContext; } ++ (NSString *) + createRootSecondaryFolderWithFID: (uint64_t) fid + andName: (NSString *) newFolderName + forUser: (NSString *) userName + withTDBIndexing: (struct tdb_wrap *) indexingTdb +{ + NSString *mapistoreURI, *folderName; + MAPIStoreUserContext *userContext; + SOGoMailAccount *accountFolder; + SOGoMailFolder *newFolder; + + userContext = [MAPIStoreUserContext userContextWithUsername: userName + andTDBIndexing: indexingTdb]; + accountFolder = [[userContext rootFolders] objectForKey: @"mail"]; + folderName = [NSString stringWithFormat: @"folder%@", + [newFolderName asCSSIdentifier]]; + newFolder = [SOGoMailFolder objectWithName: folderName + inContainer: accountFolder]; + if ([newFolder create]) + mapistoreURI = [NSString stringWithFormat: @"sogo://%@:%@@mail/%@/", + userName, userName, folderName]; + else + mapistoreURI = nil; + + return mapistoreURI; +} + - (Class) MAPIStoreFolderClass { return MAPIStoreMailFolderK; diff --git a/OpenChange/MAPIStoreSOGo.m b/OpenChange/MAPIStoreSOGo.m index 1c8bc75a9..7f6767eb6 100644 --- a/OpenChange/MAPIStoreSOGo.m +++ b/OpenChange/MAPIStoreSOGo.m @@ -42,6 +42,7 @@ #import "MAPIStoreObject.h" #import "MAPIStoreTable.h" #import "NSObject+MAPIStore.h" +#import "NSString+MAPIStore.h" #include #include @@ -147,6 +148,43 @@ sogo_backend_create_context(TALLOC_CTX *mem_ctx, return rc; } +static enum mapistore_error +sogo_backend_create_root_folder (const char *username, + enum mapistore_context_role role, + uint64_t fid, const char *name, + struct tdb_wrap *indexingTdb, + TALLOC_CTX *mem_ctx, char **mapistore_urip) +{ + NSAutoreleasePool *pool; + NSString *userName, *folderName; + NSString *mapistoreUri; + int rc; + + DEBUG(0, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__)); + + pool = [NSAutoreleasePool new]; + + if (MAPIStoreContextK) + { + userName = [NSString stringWithUTF8String: username]; + folderName = [NSString stringWithUTF8String: name]; + rc = [MAPIStoreContextK createRootFolder: &mapistoreUri + withFID: fid + andName: folderName + forUser: userName + withRole: role + andTDBIndexing: indexingTdb]; + if (rc == MAPISTORE_SUCCESS) + *mapistore_urip = [mapistoreUri asUnicodeInMemCtx: mem_ctx]; + } + else + rc = MAPISTORE_ERROR; + + [pool release]; + + return rc; +} + static enum mapistore_error sogo_backend_list_contexts(const char *username, struct tdb_wrap *indexingTdb, TALLOC_CTX *mem_ctx, @@ -1239,6 +1277,7 @@ int mapistore_init_backend(void) backend.backend.namespace = "sogo://"; backend.backend.init = sogo_backend_init; backend.backend.create_context = sogo_backend_create_context; + backend.backend.create_root_folder = sogo_backend_create_root_folder; backend.backend.list_contexts = sogo_backend_list_contexts; backend.context.get_path = sogo_context_get_path; backend.context.get_root_folder = sogo_context_get_root_folder; From 3fbf55773ef978999e174781f91e7e902c01905c Mon Sep 17 00:00:00 2001 From: Francis Lachapelle Date: Fri, 3 Feb 2012 17:00:47 +0000 Subject: [PATCH 62/68] Monotone-Parent: ac81d77fda86dad3ce300e48e20f65bd639b24cb Monotone-Revision: 55d114b1af490a0c317169a03805f2c3a6873525 Monotone-Author: flachapelle@inverse.ca Monotone-Date: 2012-02-03T17:00:47 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 4 ---- 1 file changed, 4 deletions(-) diff --git a/ChangeLog b/ChangeLog index 6ba42cf1b..7568dc44a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,3 @@ -<<<<<<< variant A 2012-02-02 Francis Lachapelle * UI/MailerUI/UIxMailMainFrame.m (-composeAction): recipient @@ -8,7 +7,6 @@ received "mailto" parameter is now a JSON-formatted string; we parse it accordingly. ->>>>>>> variant B 2012-02-03 Wolfgang Sourdeau * OpenChange/MAPIStoreSOGo.m (sogo_backend_create_root_folder): @@ -49,8 +47,6 @@ * OpenChange/MAPIApplication.m (-setUserContext:): do not retain the user context. -####### Ancestor -======= end 2012-02-01 Francis Lachapelle * UI/WebServerResources/UIxCalUserRightsEditor.js (onUpdateACL): From f3c2b4cca515b35fb3ee2250df4a51ac438d22e1 Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Fri, 3 Feb 2012 17:22:45 +0000 Subject: [PATCH 63/68] Monotone-Parent: 15b9d9900d3605d1920661f52678d25d93c587ef Monotone-Revision: c0382b432c6712e7dc895590046f2754c8cd37f1 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-02-03T17:22:45 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 3 +++ OpenChange/MAPIStoreFolder.m | 2 ++ 2 files changed, 5 insertions(+) diff --git a/ChangeLog b/ChangeLog index e578ce025..df07e5215 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2012-02-03 Wolfgang Sourdeau + * OpenChange/MAPIStoreFolder.m (-deleteFolder): cleanup the caches + in order to let the folder disappear. + * OpenChange/MAPIStoreSOGo.m (sogo_backend_create_root_folder): new backend method. diff --git a/OpenChange/MAPIStoreFolder.m b/OpenChange/MAPIStoreFolder.m index 912c766a6..8c10feb07 100644 --- a/OpenChange/MAPIStoreFolder.m +++ b/OpenChange/MAPIStoreFolder.m @@ -388,6 +388,8 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe [propsFolder delete]; [faiFolder delete]; + [self cleanupCaches]; + return MAPISTORE_SUCCESS; } From 4d4629e816858414a3f3e118760f905ba829a322 Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Fri, 3 Feb 2012 17:23:56 +0000 Subject: [PATCH 64/68] Monotone-Parent: c0382b432c6712e7dc895590046f2754c8cd37f1 Monotone-Revision: 8c5a9e9cc065204a82679aa77cc03ea009be7cfd Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-02-03T17:23:56 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 5 +++++ OpenChange/MAPIStoreFolderTable.m | 20 ++++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/ChangeLog b/ChangeLog index df07e5215..dedb71535 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,10 @@ 2012-02-03 Wolfgang Sourdeau + * OpenChange/MAPIStoreFolderTable.m + (-evaluatePropertyRestriction:intoQualifier:): overriden method to + always return true for restrictions on PR_CHANGE_NUM, in order to + help the fxics mechanism. + * OpenChange/MAPIStoreFolder.m (-deleteFolder): cleanup the caches in order to let the folder disappear. diff --git a/OpenChange/MAPIStoreFolderTable.m b/OpenChange/MAPIStoreFolderTable.m index dfcb8af46..1deb68998 100644 --- a/OpenChange/MAPIStoreFolderTable.m +++ b/OpenChange/MAPIStoreFolderTable.m @@ -85,6 +85,26 @@ return restrictedChildKeys; } +- (MAPIRestrictionState) evaluatePropertyRestriction: (struct mapi_SPropertyRestriction *) res + intoQualifier: (EOQualifier **) qualifier +{ + MAPIRestrictionState rc; + + switch ((uint32_t) res->ulPropTag) + { + /* HACK: we cheat here as we current have no mechanism for searching + folders based on PR_CHANGE_NUM, which is used by the oxcfxics + mechanism... */ + case PR_CHANGE_NUM: + rc = MAPIRestrictionStateAlwaysTrue; + break; + default: + rc = [super evaluatePropertyRestriction: res intoQualifier: qualifier]; + } + + return rc; +} + - (id) lookupChild: (NSString *) childKey { return [(MAPIStoreFolder *) container lookupFolder: childKey]; From 07e9de28847a9da83265357c063bb02b5eb3c9f2 Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Fri, 3 Feb 2012 17:24:37 +0000 Subject: [PATCH 65/68] Monotone-Parent: 6e6ddde0b781ba8a6c4605edf730ec8e82cded23 Monotone-Revision: c8a56f550a65316cf040c7624d75ba835bbf97c8 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-02-03T17:24:37 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/ChangeLog b/ChangeLog index cadc6884e..8532ae8a7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,12 +1,3 @@ -2012-02-02 Francis Lachapelle - - * UI/MailerUI/UIxMailMainFrame.m (-composeAction): recipient - addresses are now passed as a JSON-formatted string. - - * UI/MailerUI/UIxMailAccountActions.m (-composeAction): the - received "mailto" parameter is now a JSON-formatted string; we - parse it accordingly. - 2012-02-03 Wolfgang Sourdeau * OpenChange/MAPIStoreFolderTable.m @@ -29,6 +20,15 @@ * SoObjects/Mailer/SOGoMailFolder.m (-exists): new method that returns whether an IMAP folder exists or not yet. +2012-02-02 Francis Lachapelle + + * UI/MailerUI/UIxMailMainFrame.m (-composeAction): recipient + addresses are now passed as a JSON-formatted string. + + * UI/MailerUI/UIxMailAccountActions.m (-composeAction): the + received "mailto" parameter is now a JSON-formatted string; we + parse it accordingly. + 2012-02-01 Wolfgang Sourdeau * OpenChange/MAPIStoreUserContext.m (-destroy): dont't release From d32f0d32b867974226a5fe2a15343fc773e6af05 Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Mon, 6 Feb 2012 13:55:46 +0000 Subject: [PATCH 66/68] Monotone-Parent: c8a56f550a65316cf040c7624d75ba835bbf97c8 Monotone-Revision: 6bc1556981bffd1a4a80e25cdf6ae2e257e57631 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-02-06T13:55:46 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 5 +++++ SoObjects/SOGo/LDAPSource.m | 24 ++++++++++++++++-------- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/ChangeLog b/ChangeLog index 8532ae8a7..0e9d73708 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2012-02-06 Wolfgang Sourdeau + + * SoObjects/SOGo/LDAPSource.m: escape built DNs using the new + -[NSString escapedForLDAPDN] method. + 2012-02-03 Wolfgang Sourdeau * OpenChange/MAPIStoreFolderTable.m diff --git a/SoObjects/SOGo/LDAPSource.m b/SoObjects/SOGo/LDAPSource.m index fa2db52bf..c25978eb2 100644 --- a/SoObjects/SOGo/LDAPSource.m +++ b/SoObjects/SOGo/LDAPSource.m @@ -35,6 +35,7 @@ #import #import #import +#import #import "LDAPSourceSchema.h" #import "NSArray+Utilities.h" @@ -601,7 +602,7 @@ andMultipleBookingsField: (NSString *) newMultipleBookingsField } else userDN = [NSString stringWithFormat: @"%@=%@,%@", - IDField, _login, baseDN]; + IDField, [_login escapedForLDAPDN], baseDN]; } if (userDN) @@ -690,7 +691,7 @@ andMultipleBookingsField: (NSString *) newMultipleBookingsField userDN = [self _fetchUserDNForLogin: login]; else userDN = [NSString stringWithFormat: @"%@=%@,%@", - IDField, login, baseDN]; + IDField, [login escapedForLDAPDN], baseDN]; if (userDN) { NS_DURING @@ -1439,7 +1440,8 @@ _convertRecordToLDAPAttributes (LDAPSourceSchema *schema, NSDictionary *ldifReco /* since the id might have changed due to the mapping above, we reload the record ID */ aId = [ldifRecord objectForKey: UIDField]; - dn = [NSString stringWithFormat: @"%@=%@,%@", IDField, aId, baseDN]; + dn = [NSString stringWithFormat: @"%@=%@,%@", IDField, + [aId escapedForLDAPDN], baseDN]; attributes = _convertRecordToLDAPAttributes (schema, ldifRecord); newEntry = [[NGLdapEntry alloc] initWithDN: dn @@ -1569,7 +1571,8 @@ _makeLDAPChanges (NGLdapConnection *ldapConnection, NSString *dn; ldapConnection = [self _ldapConnection]; - dn = [NSString stringWithFormat: @"%@=%@,%@", IDField, aId, baseDN]; + dn = [NSString stringWithFormat: @"%@=%@,%@", IDField, + [aId escapedForLDAPDN], baseDN]; NS_DURING { [ldapConnection removeEntryWithDN: dn]; @@ -1608,7 +1611,9 @@ _makeLDAPChanges (NGLdapConnection *ldapConnection, sources = [NSMutableArray array]; ldapConnection = [self _ldapConnection]; - abBaseDN = [NSString stringWithFormat: @"ou=%@,%@=%@,%@", abOU, IDField, user, baseDN]; + abBaseDN = [NSString stringWithFormat: @"ou=%@,%@=%@,%@", + [abOU escapedForLDAPDN], IDField, + [user escapedForLDAPDN], baseDN]; /* test ou=addressbooks entry */ attributes = [NSArray arrayWithObject: @"*"]; @@ -1694,7 +1699,8 @@ _makeLDAPChanges (NGLdapConnection *ldapConnection, if ([self hasUserAddressBooks]) { abDN = [NSString stringWithFormat: @"ou=%@,ou=%@,%@=%@,%@", - newId, abOU, IDField, user, baseDN]; + [newId escapedForLDAPDN], [abOU escapedForLDAPDN], + IDField, [user escapedForLDAPDN], baseDN]; entryRecord = [NSMutableDictionary dictionary]; [entryRecord setObject: @"organizationalUnit" forKey: @"objectclass"]; [entryRecord setObject: newId forKey: @"ou"]; @@ -1740,7 +1746,8 @@ _makeLDAPChanges (NGLdapConnection *ldapConnection, if ([self hasUserAddressBooks]) { abDN = [NSString stringWithFormat: @"ou=%@,ou=%@,%@=%@,%@", - newId, abOU, IDField, user, baseDN]; + [newId escapedForLDAPDN], [abOU escapedForLDAPDN], + IDField, [user escapedForLDAPDN], baseDN]; entryRecord = [NSMutableDictionary dictionary]; [entryRecord setObject: @"organizationalUnit" forKey: @"objectclass"]; [entryRecord setObject: newId forKey: @"ou"]; @@ -1784,7 +1791,8 @@ _makeLDAPChanges (NGLdapConnection *ldapConnection, if ([self hasUserAddressBooks]) { abDN = [NSString stringWithFormat: @"ou=%@,ou=%@,%@=%@,%@", - newId, abOU, IDField, user, baseDN]; + [newId escapedForLDAPDN], [abOU escapedForLDAPDN], + IDField, [user escapedForLDAPDN], baseDN]; ldapConnection = [self _ldapConnection]; NS_DURING { From b27f0de9a9acf2e26ea0491b32e73a214a6d44f4 Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Mon, 6 Feb 2012 21:32:39 +0000 Subject: [PATCH 67/68] Monotone-Parent: 6bc1556981bffd1a4a80e25cdf6ae2e257e57631 Monotone-Revision: 2a5d61f1ed60b9d00203fc41d7b5af818e00391f Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-02-06T21:32:39 Monotone-Branch: ca.inverse.sogo --- UI/Templates/ContactsUI/UIxContactEditor.wox | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/UI/Templates/ContactsUI/UIxContactEditor.wox b/UI/Templates/ContactsUI/UIxContactEditor.wox index 89b59cb8f..37849396b 100644 --- a/UI/Templates/ContactsUI/UIxContactEditor.wox +++ b/UI/Templates/ContactsUI/UIxContactEditor.wox @@ -322,8 +322,8 @@ From 499c4d1efb33caaf0c0ac8f164b910546aa84b33 Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Thu, 9 Feb 2012 20:34:52 +0000 Subject: [PATCH 68/68] Monotone-Parent: 96f11391475b0d4e71002f19f89309839d039b36 Monotone-Revision: b2bfdd5b59fed2950707131eca171b4758bcd635 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-02-09T20:34:52 Monotone-Branch: ca.inverse.sogo --- SoObjects/SOGo/NSString+Utilities.m | 4 ++-- UI/WebServerResources/JavascriptAPIExtensions.js | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/SoObjects/SOGo/NSString+Utilities.m b/SoObjects/SOGo/NSString+Utilities.m index c9079c4bd..0d01f9970 100644 --- a/SoObjects/SOGo/NSString+Utilities.m +++ b/SoObjects/SOGo/NSString+Utilities.m @@ -288,12 +288,12 @@ static int cssEscapingCount; int count; strings = [NSArray arrayWithObjects: @"_U_", @"_D_", @"_H_", @"_A_", @"_S_", - @"_C_", @"_CO_", @"_SP_", @"_SQ_", @"_AM_", nil]; + @"_C_", @"_CO_", @"_SP_", @"_SQ_", @"_AM_", @"_P_", nil]; [strings retain]; cssEscapingStrings = [strings asPointersOfObjects]; characters = [NSArray arrayWithObjects: @"_", @".", @"#", @"@", @"*", @":", - @",", @" ", @"'", @"&", nil]; + @",", @" ", @"'", @"&", @"+", nil]; cssEscapingCount = [strings count]; cssEscapingCharacters = NSZoneMalloc (NULL, (cssEscapingCount + 1) diff --git a/UI/WebServerResources/JavascriptAPIExtensions.js b/UI/WebServerResources/JavascriptAPIExtensions.js index 973082d14..32c7a7034 100644 --- a/UI/WebServerResources/JavascriptAPIExtensions.js +++ b/UI/WebServerResources/JavascriptAPIExtensions.js @@ -70,8 +70,10 @@ String.prototype.asDate = function () { }; String.prototype.asCSSIdentifier = function() { - var characters = [ '_' , '\\.', '#' , '@' , '\\*', ':' , ',' , ' ' , "'", '&' ]; - var escapeds = [ '_U_', '_D_', '_H_', '_A_', '_S_', '_C_', '_CO_', '_SP_', '_SQ_', '_AM_' ]; + var characters = [ '_' , '\\.', '#' , '@' , '\\*', ':' , ',' , ' ' + , "'", '&', '+' ]; + var escapeds = [ '_U_', '_D_', '_H_', '_A_', '_S_', '_C_', '_CO_', + '_SP_', '_SQ_', '_AM_', '_P_' ]; var newString = this; for (var i = 0; i < characters.length; i++) {
    - + + - + + - - - - + var:value="ldifRecord.birthday"/>
    - -
    +