diff --git a/OpenChange/MAPIStoreMailFolder.h b/OpenChange/MAPIStoreMailFolder.h index c56e74862..717868efb 100644 --- a/OpenChange/MAPIStoreMailFolder.h +++ b/OpenChange/MAPIStoreMailFolder.h @@ -52,6 +52,8 @@ - (NSString *) changeNumberForMessageUID: (NSString *) messageUid; - (void) setChangeKey: (NSData *) changeKey forMessageWithKey: (NSString *) messageKey; +- (BOOL) updatePredecessorChangeListWith: (NSData *) changeKey + forMessageWithKey: (NSString *) messageKey; - (NSData *) changeKeyForMessageWithKey: (NSString *) messageKey; - (NSData *) predecessorChangeListForMessageWithKey: (NSString *) messageKey; diff --git a/OpenChange/MAPIStoreMailFolder.m b/OpenChange/MAPIStoreMailFolder.m index 963fb7463..06a7d039d 100644 --- a/OpenChange/MAPIStoreMailFolder.m +++ b/OpenChange/MAPIStoreMailFolder.m @@ -999,6 +999,44 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data) [versionsMessage save]; } +- (BOOL) updatePredecessorChangeListWith: (NSData *) changeKey + forMessageWithKey: (NSString *) messageKey +{ + /* Update predecessor change list property given the change key. It + returns if the change key has been added to the list or not */ + BOOL added = NO; + NSData *globCnt, *oldGlobCnt; + NSDictionary *messageEntry; + NSMutableDictionary *changeList; + NSString *guid; + struct XID *xid; + + xid = [changeKey asXIDInMemCtx: NULL]; + guid = [NSString stringWithGUID: &xid->NameSpaceGuid]; + globCnt = [NSData dataWithBytes: xid->LocalId.data length: xid->LocalId.length]; + talloc_free (xid); + + messageEntry = [self _messageEntryFromMessageKey: messageKey]; + if (messageEntry) + { + changeList = [messageEntry objectForKey: @"PredecessorChangeList"]; + if (changeList) + { + oldGlobCnt = [changeList objectForKey: guid]; + if (!oldGlobCnt || ([globCnt compare: oldGlobCnt] == NSOrderedDescending)) + { + [changeList setObject: globCnt forKey: guid]; + [versionsMessage save]; + added = YES; + } + } + else + [self errorWithFormat: @"Missing predecessor change list to update"]; + } + + return added; +} + - (NSData *) changeKeyForMessageWithKey: (NSString *) messageKey { NSDictionary *messages, *changeKeyDict; diff --git a/OpenChange/MAPIStoreMailVolatileMessage.m b/OpenChange/MAPIStoreMailVolatileMessage.m index f2581a54a..8a7ba2c58 100644 --- a/OpenChange/MAPIStoreMailVolatileMessage.m +++ b/OpenChange/MAPIStoreMailVolatileMessage.m @@ -34,6 +34,7 @@ #import #import #import +#import #import #import #import @@ -285,7 +286,7 @@ static NSString *recTypes[] = { @"orig", @"to", @"cc", @"bcc" }; version = [properties objectForKey: @"version"]; return (version - ? exchange_globcnt ([version unsignedLongLongValue]) + ? [version unsignedLongLongValue] : ULLONG_MAX); } @@ -1110,7 +1111,8 @@ MakeMessageBody (NSDictionary *mailProperties, NSDictionary *attachmentParts, NS - (void) save: (TALLOC_CTX *) memCtx { - NSString *folderName, *flag, *newIdString, *messageKey; + BOOL updatedMetadata; + NSString *folderName, *flag, *newIdString, *messageKey, *changeNumber; NSData *changeKey, *messageData; NGImap4Connection *connection; NGImap4Client *client; @@ -1146,21 +1148,33 @@ MakeMessageBody (NSDictionary *mailProperties, NSDictionary *attachmentParts, NS [sogoObject setNameInContainer: messageKey]; [mapping registerURL: [self url] withID: mid]; - /* synchronise the cache and update the change key with the one provided - by the client. Before doing this, lets issue a unselect/select combo - because of timing issues with Dovecot in obtaining the latest modseq. - Sometimes, Dovecot doesn't return the newly appended UID if we do - a "UID SORT (DATE) UTF-8 (MODSEQ XYZ) (NOT DELETED)" command (where - XYZ is the HIGHESTMODSEQ+1) immediately after IMAP APPEND */ + /* synchronise the cache and update the predecessor change list + with the change key provided by the client. Before doing + this, lets issue a unselect/select combo because of timing + issues with Dovecot in obtaining the latest modseq. + Sometimes, Dovecot doesn't return the newly appended UID if + we do a "UID SORT (DATE) UTF-8 (MODSEQ XYZ) (NOT DELETED)" + command (where XYZ is the HIGHESTMODSEQ+1) immediately after + IMAP APPEND */ [client unselect]; [client select: folderName]; [(MAPIStoreMailFolder *) container synchroniseCache]; changeKey = [properties objectForKey: MAPIPropertyKey (PR_CHANGE_KEY)]; if (changeKey) - [(MAPIStoreMailFolder *) container - setChangeKey: changeKey - forMessageWithKey: messageKey]; + { + updatedMetadata = [(MAPIStoreMailFolder *) container updatePredecessorChangeListWith: changeKey + forMessageWithKey: messageKey]; + if (!updatedMetadata) + [self warnWithFormat: @"Predecessor change list not updated with client data"]; + } + + /* Update version property (PR_CHANGE_KEY indeed) as it is + requested once it is saved */ + changeNumber = [(MAPIStoreMailFolder *) container changeNumberForMessageUID: newIdString]; + if (changeNumber) + [properties setObject: [NSNumber numberWithUnsignedLongLong: [changeNumber unsignedLongLongValue] >> 16] + forKey: @"version"]; } } diff --git a/OpenChange/MAPIStoreMessage.m b/OpenChange/MAPIStoreMessage.m index b842b2d6c..a5febc41c 100644 --- a/OpenChange/MAPIStoreMessage.m +++ b/OpenChange/MAPIStoreMessage.m @@ -549,11 +549,12 @@ rtf2html (NSData *compressedRTF) } [self save: memCtx]; - /* We make sure that any change-related properties are removes from the + /* We make sure that any change-related properties are removed from the properties dictionary, to make sure that related methods will be invoked the next time they are requested. */ [properties removeObjectForKey: MAPIPropertyKey (PidTagChangeKey)]; [properties removeObjectForKey: MAPIPropertyKey (PidTagChangeNumber)]; + [properties removeObjectForKey: MAPIPropertyKey (PidTagPredecessorChangeList)]; if ([container isKindOfClass: MAPIStoreFolderK]) {