From 057a38355bcac192de3eb4250bafd67107f1a3ab Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Wed, 14 Sep 2011 18:33:44 +0000 Subject: [PATCH] See ChangeLog Monotone-Parent: 8027e57cbf5cbb4ab2ee6bf9509fe2f571f150c1 Monotone-Revision: 0ffde732abb09a8e3d0382ed47a3ec787f25796e Monotone-Author: ludovic@Sophos.ca Monotone-Date: 2011-09-14T18:33:44 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 23 ++ OpenChange/MAPIStoreCalendarFolder.m | 2 +- OpenChange/MAPIStoreContactsFolder.m | 2 +- OpenChange/MAPIStoreContactsMessage.m | 281 ++++++++++++++++++++-- OpenChange/MAPIStoreFSFolder.m | 2 +- OpenChange/MAPIStoreFolder.h | 12 +- OpenChange/MAPIStoreFolder.m | 40 ++- OpenChange/MAPIStoreGCSFolder.h | 5 + OpenChange/MAPIStoreGCSFolder.m | 23 +- OpenChange/MAPIStoreGCSMessage.h | 5 + OpenChange/MAPIStoreGCSMessage.m | 29 ++- OpenChange/MAPIStoreMailFolder.m | 192 ++++++++++++++- OpenChange/MAPIStoreMailMessage.h | 1 + OpenChange/MAPIStoreMailMessage.m | 8 +- OpenChange/MAPIStoreMapping.m | 12 +- OpenChange/MAPIStoreSOGo.m | 42 +++- OpenChange/MAPIStoreTasksFolder.m | 2 +- OpenChange/MAPIStoreTasksMessage.m | 59 ++++- SOPE/NGCards/iCalRepeatableEntityObject.h | 5 - SoObjects/Mailer/SOGoMailBaseObject.m | 8 +- SoObjects/SOGo/SOGoObject.h | 2 + SoObjects/SOGo/SOGoObject.m | 5 + 22 files changed, 713 insertions(+), 47 deletions(-) diff --git a/ChangeLog b/ChangeLog index 20e8b8481..247b3f406 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,26 @@ +2011-09-14 Ludovic Marcotte + + * SoObjects/Mailer/SOGoMailBaseObject.m: (imap4Connection): + Cache "corruption" bug fix that could lead us to using the + wrong IMAP connection for OpenChange users + + * OpenChange/MAPIStoreMapping.m: (unregisterURLWithID:) + We also remove the keys/values from the indexing database + + * OpenChange/MAPIStoreSOGo.m + RopMoveCopyMessages, RopCopyTo and messages move/copy + notifications support for messages + + * OpenChange/MAPIStoreContactsMessage.m: massive improvements + to support many new properties + + * OpenChange/MAPIStoreTasksMessage.m: massive improvements + to support many new properties + + * OpenChange/MAPIStoreFolder.m: (getPrAccess:inMemCtx:) fixed + permissions issue - we were returning a wrong value leading to + IMAP subfolders not being "writable" + 2011-09-04 Ludovic Marcotte * OpenChange/MAPIStoreMapping.m: (MAPIStoreMappingTDBTraverse()) diff --git a/OpenChange/MAPIStoreCalendarFolder.m b/OpenChange/MAPIStoreCalendarFolder.m index 1609ae63a..8059daa3f 100644 --- a/OpenChange/MAPIStoreCalendarFolder.m +++ b/OpenChange/MAPIStoreCalendarFolder.m @@ -100,7 +100,7 @@ static Class MAPIStoreCalendarMessageK; return componentQualifier; } -- (MAPIStoreMessage *) createMessage +- (MAPIStoreMessage *) createMessageWithMID: (uint64_t) mid { MAPIStoreMessage *newMessage; SOGoAppointmentObject *newEntry; diff --git a/OpenChange/MAPIStoreContactsFolder.m b/OpenChange/MAPIStoreContactsFolder.m index 67641d427..3545a2570 100644 --- a/OpenChange/MAPIStoreContactsFolder.m +++ b/OpenChange/MAPIStoreContactsFolder.m @@ -103,7 +103,7 @@ static Class MAPIStoreContactsMessageK; return componentQualifier; } -- (MAPIStoreMessage *) createMessage +- (MAPIStoreMessage *) createMessageWithMID: (uint64_t) mid { MAPIStoreMessage *newMessage; SOGoContactGCSEntry *newEntry; diff --git a/OpenChange/MAPIStoreContactsMessage.m b/OpenChange/MAPIStoreContactsMessage.m index 5ce3d3463..2d569cbae 100644 --- a/OpenChange/MAPIStoreContactsMessage.m +++ b/OpenChange/MAPIStoreContactsMessage.m @@ -3,6 +3,7 @@ * Copyright (C) 2011 Inverse inc * * Author: Wolfgang Sourdeau + * Ludovic Marcotte * * 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 @@ -50,6 +51,31 @@ @implementation MAPIStoreContactsMessage +// TODO: this should be combined with the version found +// in UIxContactEditor.m +- (CardElement *) _elementWithTag: (NSString *) tag + ofType: (NSString *) type + forCard: (NGVCard *) card +{ + 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; +} + - (int) getPrIconIndex: (void **) data // TODO inMemCtx: (TALLOC_CTX *) memCtx { @@ -352,6 +378,13 @@ atPos: 0 inData: data inMemCtx: memCtx]; } +- (int) getPrPagerTelephoneNumber: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getElement: @"tel" ofType: @"pager" excluding: nil + atPos: 0 inData: data inMemCtx: memCtx]; +} + - (int) getPrPrimaryTelephoneNumber: (void **) data inMemCtx: (TALLOC_CTX *) memCtx { @@ -359,6 +392,13 @@ atPos: 0 inData: data inMemCtx: memCtx]; } +- (int) getPrBusinessFaxNumber: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getElement: @"tel" ofType: @"fax" excluding: nil + atPos: 0 inData: data inMemCtx: memCtx]; +} + - (int) getPrBusinessHomePage: (void **) data inMemCtx: (TALLOC_CTX *) memCtx { @@ -432,6 +472,10 @@ return MAPISTORE_SUCCESS; } + +// +// getters when no address is selected as the Mailing Address +// - (int) getPrPostalAddress: (void **) data inMemCtx: (TALLOC_CTX *) memCtx { @@ -481,6 +525,60 @@ atPos: 6 inData: data inMemCtx: memCtx]; } +// +// home address getters +// +- (int) getPidLidHomeAddress: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getElement: @"label" ofType: @"home" excluding: nil + atPos: 0 inData: data inMemCtx: memCtx]; +} + +- (int) getPrHomeAddressPostOfficeBox: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getElement: @"adr" ofType: @"home" excluding: nil + atPos: 0 inData: data inMemCtx: memCtx]; +} + +- (int) getPrHomeAddressStreet: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getElement: @"adr" ofType: @"home" excluding: nil + atPos: 2 inData: data inMemCtx: memCtx]; +} + +- (int) getPrHomeAddressCity: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getElement: @"adr" ofType: @"home" excluding: nil + atPos: 3 inData: data inMemCtx: memCtx]; +} + +- (int) getPrHomeAddressStateOrProvince: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getElement: @"adr" ofType: @"home" excluding: nil + atPos: 4 inData: data inMemCtx: memCtx]; +} + +- (int) getPrHomeAddressPostalCode: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getElement: @"adr" ofType: @"home" excluding: nil + atPos: 5 inData: data inMemCtx: memCtx]; +} +- (int) getPrHomeAddressCountry: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getElement: @"adr" ofType: @"home" excluding: nil + atPos: 6 inData: data inMemCtx: memCtx]; +} + +// +// Work addresss +// - (int) getPidLidWorkAddress: (void **) data inMemCtx: (TALLOC_CTX *) memCtx { @@ -530,6 +628,9 @@ atPos: 6 inData: data inMemCtx: memCtx]; } +// +// +// - (int) getPrNickname: (void **) data inMemCtx: (TALLOC_CTX *) memCtx { @@ -565,9 +666,10 @@ - (void) save { - NGVCard *newCard; - NSArray *elements; + NSArray *elements, *units; CardElement *element; + NGVCard *newCard; + int postalAddressId; id value; @@ -578,14 +680,26 @@ [newCard setVersion: @"3.0"]; [newCard setProdID: @"-//Inverse inc.//OpenChange+SOGo//EN"]; [newCard setProfile: @"vCard"]; + + // Decomposed fullname + [newCard setNWithFamily: [newProperties objectForKey: MAPIPropertyKey(PR_SURNAME_UNICODE)] + given: [newProperties objectForKey: MAPIPropertyKey(PR_GIVEN_NAME_UNICODE)] + additional: [newProperties objectForKey: MAPIPropertyKey(PR_MIDDLE_NAME_UNICODE)] + prefixes: [newProperties objectForKey: MAPIPropertyKey(PR_DISPLAY_NAME_PREFIX_UNICODE)] + suffixes: [newProperties objectForKey: MAPIPropertyKey(PR_GENERATION_UNICODE)]]; - value = [newProperties - objectForKey: MAPIPropertyKey (PR_DISPLAY_NAME_UNICODE)]; + // + // display name + // + value = [newProperties objectForKey: MAPIPropertyKey(PR_DISPLAY_NAME_UNICODE)]; if (value) [newCard setFn: value]; + // + // email addresses handling + // elements = [newCard childrenWithTag: @"email"]; - value = [newProperties objectForKey: MAPIPropertyKey (PidLidEmail1EmailAddress)]; + value = [newProperties objectForKey: MAPIPropertyKey(PidLidEmail1EmailAddress)]; if (value) { if ([elements count] > 0) @@ -611,11 +725,21 @@ [newCard addEmail: value types: nil]; } + // + // work postal addresses handling + // + // Possible values for PidLidPostalAddressId are: + // + // 0x00000000 - No address is selected as the Mailing Address. + // 0x00000001 - The Home Address is the Mailing Address. + // 0x00000002 - The Work Address is the Mailing Address + // 0x00000003 - The Other Address is the Mailing Address. + // + // postalAddressId = [[newProperties objectForKey: MAPIPropertyKey (PidLidPostalAddressId)] intValue]; - - /* Work address */ - value = [newProperties objectForKey: MAPIPropertyKey (PidLidWorkAddress)]; + + value = [newProperties objectForKey: MAPIPropertyKey(PidLidWorkAddress)]; if ([value length]) { elements = [newCard childrenWithTag: @"label" @@ -652,25 +776,154 @@ } if (postalAddressId == 2) [element addAttribute: @"type" value: @"pref"]; - value = [newProperties objectForKey: MAPIPropertyKey (PidLidWorkAddressPostOfficeBox)]; + value = [newProperties objectForKey: MAPIPropertyKey(PidLidWorkAddressPostOfficeBox)]; if (value) [element setValue: 0 to: value]; - value = [newProperties objectForKey: MAPIPropertyKey (PidLidWorkAddressStreet)]; + value = [newProperties objectForKey: MAPIPropertyKey(PidLidWorkAddressStreet)]; if (value) [element setValue: 2 to: value]; - value = [newProperties objectForKey: MAPIPropertyKey (PidLidWorkAddressCity)]; + value = [newProperties objectForKey: MAPIPropertyKey(PidLidWorkAddressCity)]; if (value) [element setValue: 3 to: value]; - value = [newProperties objectForKey: MAPIPropertyKey (PidLidWorkAddressState)]; + value = [newProperties objectForKey: MAPIPropertyKey(PidLidWorkAddressState)]; if (value) [element setValue: 4 to: value]; - value = [newProperties objectForKey: MAPIPropertyKey (PidLidWorkAddressPostalCode)]; + value = [newProperties objectForKey: MAPIPropertyKey(PidLidWorkAddressPostalCode)]; if (value) [element setValue: 5 to: value]; - value = [newProperties objectForKey: MAPIPropertyKey (PidLidWorkAddressCountry)]; + value = [newProperties objectForKey: MAPIPropertyKey(PidLidWorkAddressCountry)]; + if (value) + [element setValue: 6 to: value]; + + // + // home postal addresses handling + // + value = [newProperties objectForKey: MAPIPropertyKey(PidLidHomeAddress)]; + if ([value length]) + { + elements = [newCard childrenWithTag: @"label" + andAttribute: @"type" + havingValue: @"home"]; + if ([elements count] > 0) + element = [elements objectAtIndex: 0]; + else + { + element = [CardElement elementWithTag: @"label"]; + [element addAttribute: @"type" value: @"home"]; + [newCard addChild: element]; + } + if (postalAddressId == 1) + { + [element removeValue: @"pref" + fromAttribute: @"type"]; + [element addAttribute: @"type" + value: @"pref"]; + } + [element setValue: 0 to: value]; + } + + elements = [newCard childrenWithTag: @"adr" + andAttribute: @"type" + havingValue: @"home"]; + if ([elements count] > 0) + element = [elements objectAtIndex: 0]; + else + { + element = [CardElement elementWithTag: @"adr"]; + [element addAttribute: @"type" value: @"home"]; + [newCard addChild: element]; + } + if (postalAddressId == 1) + [element addAttribute: @"type" value: @"pref"]; + + value = [newProperties objectForKey: MAPIPropertyKey(PR_HOME_ADDRESS_POST_OFFICE_BOX_UNICODE)]; + if (value) + [element setValue: 0 to: value]; + value = [newProperties objectForKey: MAPIPropertyKey( PR_HOME_ADDRESS_STREET_UNICODE)]; + if (value) + [element setValue: 2 to: value]; + value = [newProperties objectForKey: MAPIPropertyKey(PR_HOME_ADDRESS_CITY_UNICODE)]; + if (value) + [element setValue: 3 to: value]; + value = [newProperties objectForKey: MAPIPropertyKey(PR_HOME_ADDRESS_STATE_OR_PROVINCE_UNICODE)]; + if (value) + [element setValue: 4 to: value]; + value = [newProperties objectForKey: MAPIPropertyKey(PR_HOME_ADDRESS_POSTAL_CODE_UNICODE)]; + if (value) + [element setValue: 5 to: value]; + value = [newProperties objectForKey: MAPIPropertyKey(PR_HOME_ADDRESS_COUNTRY_UNICODE)]; if (value) [element setValue: 6 to: value]; + + // + // telephone numbers: work, home, fax, pager and mobile + // + element = [self _elementWithTag: @"tel" ofType: @"work" forCard: newCard]; + value = [newProperties objectForKey: MAPIPropertyKey(PR_OFFICE_TELEPHONE_NUMBER_UNICODE)]; + if (value) + [element setValue: 0 to: value]; + + element = [self _elementWithTag: @"tel" ofType: @"home" forCard: newCard]; + value = [newProperties objectForKey: MAPIPropertyKey(PR_HOME_TELEPHONE_NUMBER_UNICODE)]; + if (value) + [element setValue: 0 to: value]; + + element = [self _elementWithTag: @"tel" ofType: @"fax" forCard: newCard]; + value = [newProperties objectForKey: MAPIPropertyKey(PR_BUSINESS_FAX_NUMBER_UNICODE)]; + if (value) + [element setValue: 0 to: value]; + + element = [self _elementWithTag: @"tel" ofType: @"pager" forCard: newCard]; + value = [newProperties objectForKey: MAPIPropertyKey(PR_PAGER_TELEPHONE_NUMBER_UNICODE)]; + if (value) + [element setValue: 0 to: value]; + + element = [self _elementWithTag: @"tel" ofType: @"cell" forCard: newCard]; + value = [newProperties objectForKey: MAPIPropertyKey(PR_MOBILE_TELEPHONE_NUMBER_UNICODE)]; + if (value) + [element setValue: 0 to: value]; + + + // + // job title, nickname, company name, deparment, work url, im address/screen name + // + value = [newProperties objectForKey: MAPIPropertyKey(PR_TITLE_UNICODE)]; + if (value) + [newCard setTitle: value]; + + value = [newProperties objectForKey: MAPIPropertyKey(PR_NICKNAME_UNICODE)]; + if (value) + [newCard setNickname: value]; + + value = [newProperties objectForKey: MAPIPropertyKey(PR_DEPARTMENT_NAME_UNICODE)]; + if (value) + units = [NSArray arrayWithObject: value]; + else + units = nil; + + value = [newProperties objectForKey: MAPIPropertyKey(PR_COMPANY_NAME_UNICODE)]; + if (value) + [newCard setOrg: value units: units]; + + value = [newProperties objectForKey: MAPIPropertyKey(PR_BUSINESS_HOME_PAGE_UNICODE)]; + if (value) + { + [[self _elementWithTag: @"url" ofType: @"work" forCard: newCard] + setValue: 0 to: value]; + } + + value = [newProperties objectForKey: MAPIPropertyKey(PidLidInstantMessagingAddress)]; + if (value) + { + [[newCard uniqueChildWithTag: @"x-aim"] + setValue: 0 + to: value]; + } + + // + // we save the new/modified card + // [sogoObject saveContentString: [newCard versitString]]; [(MAPIStoreContactsFolder *) container synchroniseCache]; } diff --git a/OpenChange/MAPIStoreFSFolder.m b/OpenChange/MAPIStoreFSFolder.m index eaa622710..5525826fd 100644 --- a/OpenChange/MAPIStoreFSFolder.m +++ b/OpenChange/MAPIStoreFSFolder.m @@ -100,7 +100,7 @@ static Class EOKeyValueQualifierK, MAPIStoreFSMessageK; return newKey; } -- (MAPIStoreMessage *) createMessage +- (MAPIStoreMessage *) createMessageWithMID: (uint64_t) mid { MAPIStoreMessage *newMessage; SOGoMAPIFSMessage *fsObject; diff --git a/OpenChange/MAPIStoreFolder.h b/OpenChange/MAPIStoreFolder.h index 04d93bc7a..52cb1dd80 100644 --- a/OpenChange/MAPIStoreFolder.h +++ b/OpenChange/MAPIStoreFolder.h @@ -43,6 +43,7 @@ @class SOGoMAPIFSFolder; @class SOGoMAPIFSMessage; + #import "MAPIStoreObject.h" @interface MAPIStoreFolder : MAPIStoreObject @@ -94,7 +95,8 @@ - (NSArray *) folderKeysMatchingQualifier: (EOQualifier *) qualifier andSortOrderings: (NSArray *) sortOrderings; -- (MAPIStoreMessage *) createMessage: (BOOL) isAssociated; +- (MAPIStoreMessage *) createMessageWithMID: (uint64_t) mid + isAssociated: (BOOL) isAssociated; /* backend interface */ @@ -110,12 +112,18 @@ - (int) createMessage: (MAPIStoreMessage **) messagePtr withMID: (uint64_t) mid isAssociated: (BOOL) isAssociated; + + - (int) openMessage: (MAPIStoreMessage **) messagePtr andMessageData: (struct mapistore_message **) dataPtr withMID: (uint64_t) mid inMemCtx: (TALLOC_CTX *) memCtx; - (int) deleteMessageWithMID: (uint64_t) mid andFlags: (uint8_t) flags; +- (int) moveCopyMessageWithMID: (uint64_t) mid + toFolder: (MAPIStoreFolder *) targetFolder + inMessage: (MAPIStoreMessage *) targetMessage + wantCopy: (uint8_t) want_copy; - (int) getDeletedFMIDs: (struct I8Array_r **) fmidsPtr andCN: (uint64_t *) cnPtr fromChangeNumber: (uint64_t) changeNum @@ -132,7 +140,7 @@ /* subclasses */ - (Class) messageClass; -- (MAPIStoreMessage *) createMessage; +- (MAPIStoreMessage *) createMessageWithMID: (uint64_t) mid; - (MAPIStoreMessageTable *) messageTable; - (NSArray *) messageKeysMatchingQualifier: (EOQualifier *) qualifier andSortOrderings: (NSArray *) sortOrderings; diff --git a/OpenChange/MAPIStoreFolder.m b/OpenChange/MAPIStoreFolder.m index 120ce242a..6c026c1b7 100644 --- a/OpenChange/MAPIStoreFolder.m +++ b/OpenChange/MAPIStoreFolder.m @@ -437,7 +437,8 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe rc = MAPISTORE_ERR_EXIST; else { - message = [self createMessage: isAssociated]; + message = [self createMessageWithMID: mid + isAssociated: isAssociated]; if (message) { baseURL = [self url]; @@ -551,6 +552,18 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe return rc; } +- (int) moveCopyMessageWithMID: (uint64_t) mid + toFolder: (MAPIStoreFolder *) targetFolder + inMessage: (MAPIStoreMessage *) targetMessage + wantCopy: (uint8_t) want_copy +{ + int rc; + + rc = MAPISTORE_SUCCESS; + + return rc; +} + - (int) getDeletedFMIDs: (struct I8Array_r **) fmidsPtr andCN: (uint64_t *) cnPtr fromChangeNumber: (uint64_t) changeNum @@ -814,14 +827,30 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe return MAPISTORE_SUCCESS; } +/* + Possible values are: + + 0x00000001 Modify + 0x00000002 Read + 0x00000004 Delete + 0x00000008 Create Hierarchy Table + 0x00000010 Create Contents Table + 0x00000020 Create Associated Contents Table +*/ - (int) getPrAccess: (void **) data inMemCtx: (TALLOC_CTX *) memCtx { - *data = MAPILongValue (memCtx, 0x63); + *data = MAPILongValue (memCtx, 0x1|0x2|0x4|0x8|0x10|0x20); return MAPISTORE_SUCCESS; } +/* + Possible values are: + + 0x00000000 Read-Only + 0x00000001 Modify +*/ - (int) getPrAccessLevel: (void **) data inMemCtx: (TALLOC_CTX *) memCtx { @@ -938,7 +967,8 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe return newMessage; } -- (MAPIStoreMessage *) createMessage: (BOOL) isAssociated +- (MAPIStoreMessage *) createMessageWithMID: (uint64_t) mid + isAssociated: (BOOL) isAssociated { MAPIStoreMessage *newMessage; WOContext *woContext; @@ -946,7 +976,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe if (isAssociated) newMessage = [self _createAssociatedMessage]; else - newMessage = [self createMessage]; + newMessage = [self createMessageWithMID: mid]; [newMessage setIsNew: YES]; woContext = [[self context] woContext]; [[newMessage sogoObject] setContext: woContext]; @@ -1038,7 +1068,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe return Nil; } -- (MAPIStoreMessage *) createMessage +- (MAPIStoreMessage *) createMessageWithMID: (uint64_t) mid { [self logWithFormat: @"ignored method: %s", __PRETTY_FUNCTION__]; return nil; diff --git a/OpenChange/MAPIStoreGCSFolder.h b/OpenChange/MAPIStoreGCSFolder.h index ed26fd29f..d61c7cdd4 100644 --- a/OpenChange/MAPIStoreGCSFolder.h +++ b/OpenChange/MAPIStoreGCSFolder.h @@ -26,12 +26,14 @@ #import "MAPIStoreFolder.h" @class NSCalendarDate; +@class NSMutableDictionary; @class NSNumber; @class NSString; @interface MAPIStoreGCSFolder : MAPIStoreFolder { SOGoMAPIFSMessage *versionsMessage; + NSMutableDictionary *initialVersions; } /* synchronisation */ @@ -42,6 +44,9 @@ /* subclasses */ - (EOQualifier *) componentQualifier; +- (void) setInitialVersion: (uint64_t) version + forMessage: (NSString *) theMessage; + @end #endif /* MAPISTOREGCSFOLDER_H */ diff --git a/OpenChange/MAPIStoreGCSFolder.m b/OpenChange/MAPIStoreGCSFolder.m index 513b50106..bcde67941 100644 --- a/OpenChange/MAPIStoreGCSFolder.m +++ b/OpenChange/MAPIStoreGCSFolder.m @@ -50,7 +50,9 @@ { ASSIGN (versionsMessage, [SOGoMAPIFSMessage objectWithName: @"versions.plist" - inContainer: propsFolder]); + inContainer: propsFolder]); + + initialVersions = [[NSMutableDictionary alloc] init]; } return self; @@ -64,6 +66,8 @@ ASSIGN (versionsMessage, [SOGoMAPIFSMessage objectWithName: @"versions.plist" inContainer: propsFolder]); + + initialVersions = [[NSMutableDictionary alloc] init]; } return self; @@ -72,6 +76,7 @@ - (void) dealloc { [versionsMessage release]; + [initialVersions release]; [super dealloc]; } @@ -247,7 +252,14 @@ { foundChange = YES; - newChangeNum = [[self context] getNewChangeNumber]; + //if ([[messageEntry objectForKey: @"c_version"] intValue] == 0) + // newChangeNum = [[initialVersions objectForKey: cName] unsignedLongLongValue]; + //else + { + newChangeNum = [[self context] getNewChangeNumber]; + [initialVersions removeObjectForKey: cName]; + } + changeNumber = [NSNumber numberWithUnsignedLongLong: newChangeNum]; [messageEntry setObject: cLastModified forKey: @"c_lastmodified"]; @@ -398,4 +410,11 @@ return nil; } +- (void) setInitialVersion: (uint64_t) version + forMessage: (NSString *) theMessage +{ + [initialVersions setObject: [NSNumber numberWithUnsignedLongLong: version] + forKey: theMessage]; +} + @end diff --git a/OpenChange/MAPIStoreGCSMessage.h b/OpenChange/MAPIStoreGCSMessage.h index 6c3886db3..3edc4791a 100644 --- a/OpenChange/MAPIStoreGCSMessage.h +++ b/OpenChange/MAPIStoreGCSMessage.h @@ -26,6 +26,11 @@ #import "MAPIStoreMessage.h" @interface MAPIStoreGCSMessage : MAPIStoreMessage +{ + @private + uint64_t _version; +} + @end #endif /* MAPISTOREGCSMESSAGE_H */ diff --git a/OpenChange/MAPIStoreGCSMessage.m b/OpenChange/MAPIStoreGCSMessage.m index d6142a477..584a9455e 100644 --- a/OpenChange/MAPIStoreGCSMessage.m +++ b/OpenChange/MAPIStoreGCSMessage.m @@ -24,6 +24,7 @@ #import #import +#import "MAPIStoreContext.h" #import "MAPIStoreGCSFolder.h" #import "MAPIStoreTypes.h" @@ -34,6 +35,16 @@ @implementation MAPIStoreGCSMessage +- (id) init +{ + if ((self = [super init])) + { + _version = 0xffffffffffffffffLL; + } + + return self; +} + - (NSCalendarDate *) creationTime { return [sogoObject creationDate]; @@ -48,8 +59,20 @@ { uint64_t version = 0xffffffffffffffffLL; NSNumber *changeNumber; - - if (![sogoObject isNew]) + + if ([sogoObject isNew]) + { +#if 0 + if (_version == 0xffffffffffffffffLL) + { + _version = [[self context] getNewChangeNumber]; + [(MAPIStoreGCSFolder *)[self container] setInitialVersion: _version + forMessage: [self nameInContainer]]; + } + version = _version; +#endif + } + else { changeNumber = [(MAPIStoreGCSFolder *) container changeNumberForMessageWithKey: [self nameInContainer]]; @@ -66,7 +89,7 @@ else { [self errorWithFormat: @"still nothing. We crash!"]; - abort (); + abort(); } } version = [changeNumber unsignedLongLongValue] >> 16; diff --git a/OpenChange/MAPIStoreMailFolder.m b/OpenChange/MAPIStoreMailFolder.m index 2c1662e83..6621abb2c 100644 --- a/OpenChange/MAPIStoreMailFolder.m +++ b/OpenChange/MAPIStoreMailFolder.m @@ -38,6 +38,7 @@ #import #import #import +#import #import #import #import @@ -47,8 +48,10 @@ #import "MAPIStoreAppointmentWrapper.h" #import "MAPIStoreContext.h" #import "MAPIStoreDraftsMessage.h" +#import "MAPIStoreFAIMessage.h" #import "MAPIStoreMailMessage.h" #import "MAPIStoreMailMessageTable.h" +#import "MAPIStoreMapping.h" #import "MAPIStoreTypes.h" #import "NSString+MAPIStore.h" #import "SOGoMAPIFSMessage.h" @@ -62,6 +65,7 @@ static Class SOGoMailFolderK; #undef DEBUG #include #include +#include @implementation MAPIStoreMailFolder @@ -631,6 +635,187 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data) return deletedKeys; } +// +// We need to support creating "fake" emails for RopCopyTo and other +// other ROPs to function properly. We don't know what will be the +// object's name (as it's the IMAP's UID) until the message really +// is created in the mail store. +// +- (MAPIStoreMessage *) createMessageWithMID: (uint64_t) mid +{ + MAPIStoreMessage *message; + SOGoMailObject *mail; + + mail = [SOGoMailObject objectWithName: [NSString stringWithFormat: @"%llu", mid] + inContainer: sogoObject]; + + message = [MAPIStoreMailMessage mapiStoreObjectWithSOGoObject: mail + inContainer: self]; + + return message; +} + +// +// Move (or eventually copy) the mail identified by +// "mid" within this current folder. The message is +// of course coming from an other folder +// +- (int) moveCopyMessageWithMID: (uint64_t) mid + toFolder: (MAPIStoreFolder *) targetFolder + inMessage: (MAPIStoreMessage *) targetMessage + wantCopy: (uint8_t) want_copy +{ + NSString *folderName, *messageURL, *uid, *url, *v; + MAPIStoreMapping *mapping; + NSDictionary *result; + + + unsigned int new_uid; + uint64_t target_mid; + int rc; + + mapping = [[self context] mapping]; + + messageURL = [mapping urlFromID: mid]; + + if (messageURL) + { + // We get the message UID from that folder by stripping the .eml + // This is the message we'll copy in the folder specified by targetURL + uid = [messageURL lastPathComponent]; + uid = [uid substringToIndex: [uid length] - 4]; + + folderName = [[targetFolder sogoObject] relativeImap4Name]; + + if (uid && [folderName length] > 0) + { + struct mapistore_object_notification_parameters *notif_parameters; + struct mapistore_connection_info *connInfo; + NGImap4Client *client; + NSArray *a; + + // We copy the message, get the new UID and set the old one as deleted + client = [[[self sogoObject] imap4Connection] client]; + [client select: [[self sogoObject] relativeImap4Name]]; + result = [client copyUid: [uid intValue] toFolder: folderName]; + + if (!want_copy) + [client storeUid: [uid intValue] add: [NSNumber numberWithBool: YES] flags: [NSArray arrayWithObject: @"Deleted"]]; + + // + // We use the UIDPLUS IMAP extension here in order to speedup UID retreival + // If supported by the server, we'll get something like: COPYUID 1315425789 1 8 + // + // Sometimes COPYUID isn't returned at all by Cyrus or in case the server doesn't + // support the UIDPLUS IMAP extension, we fallback to a simple UID search. + // + v = [[[result objectForKey: @"RawResponse"] objectForKey: @"ResponseResult"] objectForKey: @"flag"]; + + if (v) + { + unsigned int current_uid, uid_validity; + const char *s; + char tag[7]; + + s = [v cStringUsingEncoding: NSASCIIStringEncoding]; + sscanf(s, "%s %u %u %u", tag, &uid_validity, ¤t_uid, &new_uid); + } + + else + { + [client select: folderName]; + a = [[client sort: @"ARRIVAL" qualifier: nil encoding: @"UTF-8"] objectForKey: @"sort"]; + new_uid = [[[a sortedArrayUsingSelector: @selector(compare:)] lastObject] intValue]; + } + + // We compute the URL of the move message and update our mapping + url = [NSString stringWithFormat: @"%@%d.eml", [targetFolder url], new_uid]; + + if (!want_copy) + [mapping unregisterURLWithID: mid]; + + // We adjust its name within the container with the newly obtained UID. This was previously the + // MID of the message and that value was temporary since we created a "fake" message + target_mid = strtoull([[[targetMessage sogoObject] nameInContainer] UTF8String], NULL, 10); + + [[targetMessage sogoObject] setNameInContainer: [NSString stringWithFormat: @"%u.eml", new_uid]]; + + // We unregister the previously (and temporary) registered mid and register + // it again with its new and valid URL + [mapping unregisterURLWithID: target_mid]; + [mapping registerURL: url withID: target_mid]; + + NSArray *activeTables; + NSUInteger count, max; + /* we ensure the table caches are loaded so that old and new state + can be compared */ + MAPIStoreMessage *message; + + message = [self lookupMessageByURL: messageURL]; + activeTables = ([message isKindOfClass: [MAPIStoreFAIMessage class]] + ? [self activeFAIMessageTables] + : [self activeMessageTables]); + max = [activeTables count]; + for (count = 0; count < max; count++) + [[activeTables objectAtIndex: count] restrictedChildKeys]; + + + + // We notify the client + notif_parameters = talloc_zero(NULL, struct mapistore_object_notification_parameters); + notif_parameters->object_id = [self objectId]; + notif_parameters->tag_count = 5; + notif_parameters->tags = talloc_array (notif_parameters, enum MAPITAGS, 5); + notif_parameters->tags[0] = PR_CONTENT_COUNT; + notif_parameters->tags[1] = PR_DELETED_COUNT_TOTAL; + notif_parameters->tags[2] = PR_MESSAGE_SIZE; + notif_parameters->tags[3] = PR_NORMAL_MESSAGE_SIZE; + notif_parameters->tags[4] = PR_RECIPIENT_ON_NORMAL_MSG_COUNT; + notif_parameters->new_message_count = true; + notif_parameters->message_count = [[self messageKeys] count]; + connInfo = [[self context] connectionInfo]; + mapistore_push_notification (connInfo->mstore_ctx, + MAPISTORE_FOLDER, + MAPISTORE_OBJECT_MODIFIED, + notif_parameters); + talloc_free(notif_parameters); + + /* move/copy notification */ + notif_parameters = talloc_zero(NULL, struct mapistore_object_notification_parameters); + notif_parameters->tag_count = 0; + notif_parameters->new_message_count = true; + notif_parameters->message_count = 0; + notif_parameters->object_id = target_mid; + notif_parameters->folder_id = [targetFolder objectId]; + notif_parameters->old_object_id = mid; + notif_parameters->old_folder_id = [self objectId]; + + mapistore_push_notification (connInfo->mstore_ctx, + MAPISTORE_MESSAGE, + (want_copy ? MAPISTORE_OBJECT_COPIED : MAPISTORE_OBJECT_MOVED), + notif_parameters); + talloc_free(notif_parameters); + + /* table notification */ + for (count = 0; count < max; count++) + [[activeTables objectAtIndex: count] + notifyChangesForChild: message]; + + // We cleanup cache of our source and destionation folders + [self cleanupCaches]; + [targetFolder cleanupCaches]; + rc = MAPISTORE_SUCCESS; + } + else + rc = MAPISTORE_ERR_NOT_FOUND; + } + else + rc = MAPISTORE_ERR_NOT_FOUND; + + return rc; +} + + @end @implementation MAPIStoreInboxFolder : MAPIStoreMailFolder @@ -781,6 +966,10 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data) // @end + +// +// +// @implementation MAPIStoreOutboxFolder : MAPIStoreMailFolder + (void) initialize @@ -799,7 +988,7 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data) return MAPIStoreDraftsMessageK; } -- (MAPIStoreMessage *) createMessage +- (MAPIStoreMessage *) createMessageWithMID: (uint64_t) mid { MAPIStoreDraftsMessage *newMessage; SOGoDraftObject *newDraft; @@ -808,7 +997,6 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data) newMessage = [MAPIStoreDraftsMessage mapiStoreObjectWithSOGoObject: newDraft inContainer: self]; - return newMessage; } diff --git a/OpenChange/MAPIStoreMailMessage.h b/OpenChange/MAPIStoreMailMessage.h index 08693c720..a39ebc0b0 100644 --- a/OpenChange/MAPIStoreMailMessage.h +++ b/OpenChange/MAPIStoreMailMessage.h @@ -29,6 +29,7 @@ @class NSString; @class MAPIStoreAppointmentWrapper; +@class MAPIStoreMailFolder; @interface MAPIStoreMailMessage : MAPIStoreMessage { diff --git a/OpenChange/MAPIStoreMailMessage.m b/OpenChange/MAPIStoreMailMessage.m index 8fab6ba51..c4a344467 100644 --- a/OpenChange/MAPIStoreMailMessage.m +++ b/OpenChange/MAPIStoreMailMessage.m @@ -3,6 +3,7 @@ * Copyright (C) 2011 Inverse inc * * Author: Wolfgang Sourdeau + * Ludovic Marcotte * * 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 @@ -24,6 +25,8 @@ #import #import #import +#import +#import #import #import #import @@ -40,6 +43,7 @@ #import "MAPIStoreFolder.h" #import "MAPIStoreMailAttachment.h" #import "MAPIStoreMailFolder.h" +#import "MAPIStoreMapping.h" #import "MAPIStoreTypes.h" #import "MAPIStoreMailMessage.h" @@ -289,13 +293,13 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data) else { [self errorWithFormat: @"still nothing. We crash!"]; - abort (); + abort(); } } version = [changeNumber unsignedLongLongValue] >> 16; } else - abort (); + abort(); return version; } diff --git a/OpenChange/MAPIStoreMapping.m b/OpenChange/MAPIStoreMapping.m index fa1415d6b..9253c2b25 100644 --- a/OpenChange/MAPIStoreMapping.m +++ b/OpenChange/MAPIStoreMapping.m @@ -55,7 +55,7 @@ MAPIStoreMappingTDBTraverse (TDB_CONTEXT *ctx, TDB_DATA data1, TDB_DATA data2, NSNumber *idNbr; NSString *uri; char *idStr, *uriStr; - long long unsigned int idVal; + uint64_t idVal; // get the key // key examples : key(18) = "0x6900000000000001" @@ -239,13 +239,21 @@ MAPIStoreMappingTDBTraverse (TDB_CONTEXT *ctx, TDB_DATA data1, TDB_DATA data2, - (void) unregisterURLWithID: (uint64_t) idNbr { - NSNumber *idKey; NSString *urlString; + NSNumber *idKey; + TDB_DATA key; idKey = [NSNumber numberWithUnsignedLongLong: idNbr]; urlString = [mapping objectForKey: idKey]; [reverseMapping removeObjectForKey: urlString]; [mapping removeObjectForKey: idKey]; + + /* We hard-delete the entry from the indexing database */ + key.dptr = (unsigned char *) talloc_asprintf(NULL, "0x%.16"PRIx64, idNbr); + key.dsize = strlen((const char *) key.dptr); + + tdb_delete(indexing->tdb, key); + talloc_free(key.dptr); } @end diff --git a/OpenChange/MAPIStoreSOGo.m b/OpenChange/MAPIStoreSOGo.m index 9b5758f21..dc0e2031e 100644 --- a/OpenChange/MAPIStoreSOGo.m +++ b/OpenChange/MAPIStoreSOGo.m @@ -402,7 +402,8 @@ sogo_folder_create_message(void *folder_object, folder = wrapper->MAPIStoreSOGoObject; pool = [NSAutoreleasePool new]; rc = [folder createMessage: &message - withMID: mid isAssociated: associated]; + withMID: mid + isAssociated: associated]; if (rc == MAPISTORE_SUCCESS) *message_object = [message tallocWrapper: mem_ctx]; [pool release]; @@ -441,6 +442,44 @@ sogo_folder_delete_message(void *folder_object, uint64_t mid, uint8_t flags) return rc; } +static int +sogo_folder_move_copy_message(void *folder_object, uint64_t mid, void *target_folder, void *target_message, uint8_t want_copy) +{ + MAPIStoreFolder *sourceFolder, *targetFolder; + MAPIStoreMessage *targetMessage; + NSAutoreleasePool *pool; + + struct MAPIStoreTallocWrapper *wrapper; + int rc; + + DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__)); + + if (folder_object) + { + wrapper = folder_object; + sourceFolder = wrapper->MAPIStoreSOGoObject; + + wrapper = target_folder; + targetFolder = wrapper->MAPIStoreSOGoObject; + + wrapper = target_message; + targetMessage = wrapper->MAPIStoreSOGoObject; + + pool = [NSAutoreleasePool new]; + rc = [sourceFolder moveCopyMessageWithMID: mid + toFolder: targetFolder + inMessage: targetMessage + wantCopy: want_copy]; + [pool release]; + } + else + { + rc = sogo_backend_unexpected_error(); + } + + return rc; +} + static int sogo_folder_get_deleted_fmids(void *folder_object, TALLOC_CTX *mem_ctx, uint8_t table_type, uint64_t change_num, @@ -1031,6 +1070,7 @@ int mapistore_init_backend(void) backend.folder.open_message = sogo_folder_open_message; backend.folder.create_message = sogo_folder_create_message; backend.folder.delete_message = sogo_folder_delete_message; + backend.folder.move_copy_message = sogo_folder_move_copy_message; backend.folder.get_deleted_fmids = sogo_folder_get_deleted_fmids; backend.folder.get_child_count = sogo_folder_get_child_count; backend.folder.open_table = sogo_folder_open_table; diff --git a/OpenChange/MAPIStoreTasksFolder.m b/OpenChange/MAPIStoreTasksFolder.m index be96b9ab9..7e0393ee8 100644 --- a/OpenChange/MAPIStoreTasksFolder.m +++ b/OpenChange/MAPIStoreTasksFolder.m @@ -100,7 +100,7 @@ static Class MAPIStoreTasksMessageK; return componentQualifier; } -- (MAPIStoreMessage *) createMessage +- (MAPIStoreMessage *) createMessageWithMID: (uint64_t) mid { MAPIStoreMessage *newMessage; SOGoTaskObject *newEntry; diff --git a/OpenChange/MAPIStoreTasksMessage.m b/OpenChange/MAPIStoreTasksMessage.m index 93a465881..863ba04ef 100644 --- a/OpenChange/MAPIStoreTasksMessage.m +++ b/OpenChange/MAPIStoreTasksMessage.m @@ -3,6 +3,7 @@ * Copyright (C) 2011 Inverse inc * * Author: Wolfgang Sourdeau + * Ludovic Marcotte * * 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 @@ -209,6 +210,24 @@ return rc; } +- (int) getPidLidTaskStartDate: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + int rc = MAPISTORE_SUCCESS; + NSCalendarDate *dateValue; + iCalToDo *task; + + task = [sogoObject component: NO secure: NO]; + dateValue = [task startDate]; + if (dateValue) + *data = [dateValue asFileTimeInMemCtx: memCtx]; + else + rc = MAPISTORE_ERR_NOT_FOUND; + + return rc; +} + + - (int) getPrMessageDeliveryTime: (void **) data inMemCtx: (TALLOC_CTX *) memCtx { @@ -253,7 +272,11 @@ - (int) getPidLidTaskOwner: (void **) data inMemCtx: (TALLOC_CTX *) memCtx { - *data = [@"openchange@example.com" asUnicodeInMemCtx: memCtx]; + NSString *owner; + + owner = [sogoObject ownerInContext: nil]; + + *data = [owner asUnicodeInMemCtx: memCtx]; return MAPISTORE_SUCCESS; } @@ -292,17 +315,17 @@ if (value) [vToDo setComment: value]; - // Location + // location value = [newProperties objectForKey: MAPIPropertyKey (PidLidLocation)]; if (value) [vToDo setLocation: value]; - /* created */ + // created value = [newProperties objectForKey: MAPIPropertyKey (PR_CREATION_TIME)]; if (value) [vToDo setCreated: value]; - /* last-modified + dtstamp */ + // last-modified + dtstamp value = [newProperties objectForKey: MAPIPropertyKey (PR_LAST_MODIFICATION_TIME)]; if (value) { @@ -322,6 +345,10 @@ [date setTimeZone: tz]; [date setDateTime: value]; } + else + { + [vToDo setStartDate: nil]; + } // due value = [newProperties objectForKey: MAPIPropertyKey (PidLidTaskDueDate)]; @@ -331,6 +358,23 @@ [date setTimeZone: tz]; [date setDateTime: value]; } + else + { + [vToDo setDue: nil]; + } + + // completed + value = [newProperties objectForKey: MAPIPropertyKey (PidLidTaskDateCompleted)]; + if (value) + { + date = (iCalDateTime *) [vToDo uniqueChildWithTag: @"completed"]; + [date setTimeZone: tz]; + [date setDateTime: value]; + } + else + { + [vToDo setCompleted: nil]; + } // status value = [newProperties objectForKey: MAPIPropertyKey (PidLidTaskStatus)]; @@ -365,6 +409,13 @@ priority = @"0"; // None [vToDo setPriority: priority]; + // percent complete + // NOTE: this does not seem to work on Outlook 2003. PidLidPercentComplete's value + // is always set to 0, no matter what value is set in Outlook + value = [newProperties objectForKey: MAPIPropertyKey(PidLidPercentComplete)]; + if (value) + [vToDo setPercentComplete: [value stringValue]]; + now = [NSCalendarDate date]; if ([sogoObject isNew]) { diff --git a/SOPE/NGCards/iCalRepeatableEntityObject.h b/SOPE/NGCards/iCalRepeatableEntityObject.h index ea70b3486..9f70e2033 100644 --- a/SOPE/NGCards/iCalRepeatableEntityObject.h +++ b/SOPE/NGCards/iCalRepeatableEntityObject.h @@ -36,11 +36,6 @@ @class iCalTimeZone; @interface iCalRepeatableEntityObject : iCalEntityObject -// { -// NSMutableArray *rRules; -// NSMutableArray *exRules; -// NSMutableArray *exDates; -// } - (void)removeAllRecurrenceRules; - (void)addToRecurrenceRules:(id)_rrule; diff --git a/SoObjects/Mailer/SOGoMailBaseObject.m b/SoObjects/Mailer/SOGoMailBaseObject.m index d0b7bfbb2..9d41bfe9c 100644 --- a/SoObjects/Mailer/SOGoMailBaseObject.m +++ b/SoObjects/Mailer/SOGoMailBaseObject.m @@ -160,7 +160,13 @@ static BOOL debugOn = YES; if (!imap4) { sogoCache = [SOGoCache sharedCache]; - cacheKey = [[self mailAccountFolder] nameInContainer]; + // The cacheKey *MUST* be prefixed by the username here as + // the cache is shared across OpenChange users and not necessarily + // flushed between requests. This could lead us to using the wrong + // IMAP connection. + cacheKey = [NSString stringWithFormat: @"%@+%@", + [[[self context] activeUser] login], + [[self mailAccountFolder] nameInContainer]]; imap4 = [sogoCache imap4ConnectionForKey: cacheKey]; if (!imap4) { diff --git a/SoObjects/SOGo/SOGoObject.h b/SoObjects/SOGo/SOGoObject.h index 85474b7c9..dc26fd78e 100644 --- a/SoObjects/SOGo/SOGoObject.h +++ b/SoObjects/SOGo/SOGoObject.h @@ -87,7 +87,9 @@ - (void) setContext: (WOContext *) newContext; - (WOContext *) context; +- (void) setNameInContainer: (NSString *) theName; - (NSString *) nameInContainer; + - (NSString *) displayName; - (id) container; diff --git a/SoObjects/SOGo/SOGoObject.m b/SoObjects/SOGo/SOGoObject.m index ab86d73bb..18c2212a1 100644 --- a/SoObjects/SOGo/SOGoObject.m +++ b/SoObjects/SOGo/SOGoObject.m @@ -209,6 +209,11 @@ return context; } +- (void) setNameInContainer: (NSString *) theName +{ + ASSIGN(nameInContainer, theName); +} + - (NSString *) nameInContainer { return nameInContainer;