diff --git a/ChangeLog b/ChangeLog index 3dc8c33ee..f03475c1c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,18 @@ +2011-09-21 Wolfgang Sourdeau + + * OpenChange/MAPIStoreMailFolder.m + (-moveCopyMessagesWithMIDs:andCount:toFolder:withMIDs:wantCopy:): + improved by acting directly on source and dest MIDs, by handling + multiple messages at once and by accepting proper UID ranges in + the COPYUID response. + + * OpenChange/MAPIStoreSOGo.m (sogo_folder_move_copy_messages): + adapted to new backend prototype. + + * OpenChange/MAPIStoreFolder.m (-createMessage): reverse the + specification of the "mid" parameter as the copy/move hack in + MAPIStoreMailFolder is no longer needed. + 2011-09-20 Wolfgang Sourdeau * OpenChange/MAPIStoreObject.m (-getReplicaKeyFromGlobCnt:): new diff --git a/OpenChange/MAPIStoreCalendarFolder.m b/OpenChange/MAPIStoreCalendarFolder.m index 8059daa3f..1609ae63a 100644 --- a/OpenChange/MAPIStoreCalendarFolder.m +++ b/OpenChange/MAPIStoreCalendarFolder.m @@ -100,7 +100,7 @@ static Class MAPIStoreCalendarMessageK; return componentQualifier; } -- (MAPIStoreMessage *) createMessageWithMID: (uint64_t) mid +- (MAPIStoreMessage *) createMessage { MAPIStoreMessage *newMessage; SOGoAppointmentObject *newEntry; diff --git a/OpenChange/MAPIStoreContactsFolder.m b/OpenChange/MAPIStoreContactsFolder.m index 3545a2570..67641d427 100644 --- a/OpenChange/MAPIStoreContactsFolder.m +++ b/OpenChange/MAPIStoreContactsFolder.m @@ -103,7 +103,7 @@ static Class MAPIStoreContactsMessageK; return componentQualifier; } -- (MAPIStoreMessage *) createMessageWithMID: (uint64_t) mid +- (MAPIStoreMessage *) createMessage { MAPIStoreMessage *newMessage; SOGoContactGCSEntry *newEntry; diff --git a/OpenChange/MAPIStoreFSFolder.m b/OpenChange/MAPIStoreFSFolder.m index 5525826fd..eaa622710 100644 --- a/OpenChange/MAPIStoreFSFolder.m +++ b/OpenChange/MAPIStoreFSFolder.m @@ -100,7 +100,7 @@ static Class EOKeyValueQualifierK, MAPIStoreFSMessageK; return newKey; } -- (MAPIStoreMessage *) createMessageWithMID: (uint64_t) mid +- (MAPIStoreMessage *) createMessage { MAPIStoreMessage *newMessage; SOGoMAPIFSMessage *fsObject; diff --git a/OpenChange/MAPIStoreFolder.h b/OpenChange/MAPIStoreFolder.h index 52cb1dd80..3c154615d 100644 --- a/OpenChange/MAPIStoreFolder.h +++ b/OpenChange/MAPIStoreFolder.h @@ -95,8 +95,7 @@ - (NSArray *) folderKeysMatchingQualifier: (EOQualifier *) qualifier andSortOrderings: (NSArray *) sortOrderings; -- (MAPIStoreMessage *) createMessageWithMID: (uint64_t) mid - isAssociated: (BOOL) isAssociated; +- (MAPIStoreMessage *) createMessage: (BOOL) isAssociated; /* backend interface */ @@ -120,10 +119,13 @@ 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) moveCopyMessagesWithMIDs: (uint64_t *) srcMids + andCount: (uint32_t) count + toFolder: (MAPIStoreFolder *) targetFolder + withMIDs: (uint64_t *) targetMids + wantCopy: (uint8_t) want_copy; + - (int) getDeletedFMIDs: (struct I8Array_r **) fmidsPtr andCN: (uint64_t *) cnPtr fromChangeNumber: (uint64_t) changeNum @@ -140,7 +142,7 @@ /* subclasses */ - (Class) messageClass; -- (MAPIStoreMessage *) createMessageWithMID: (uint64_t) mid; +- (MAPIStoreMessage *) createMessage; - (MAPIStoreMessageTable *) messageTable; - (NSArray *) messageKeysMatchingQualifier: (EOQualifier *) qualifier andSortOrderings: (NSArray *) sortOrderings; diff --git a/OpenChange/MAPIStoreFolder.m b/OpenChange/MAPIStoreFolder.m index 6c026c1b7..4b28db02c 100644 --- a/OpenChange/MAPIStoreFolder.m +++ b/OpenChange/MAPIStoreFolder.m @@ -437,8 +437,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe rc = MAPISTORE_ERR_EXIST; else { - message = [self createMessageWithMID: mid - isAssociated: isAssociated]; + message = [self createMessage: isAssociated]; if (message) { baseURL = [self url]; @@ -552,10 +551,11 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe return rc; } -- (int) moveCopyMessageWithMID: (uint64_t) mid - toFolder: (MAPIStoreFolder *) targetFolder - inMessage: (MAPIStoreMessage *) targetMessage - wantCopy: (uint8_t) want_copy +- (int) moveCopyMessagesWithMIDs: (uint64_t *) srcMids + andCount: (uint32_t) count + toFolder: (MAPIStoreFolder *) targetFolder + withMIDs: (uint64_t *) targetMids + wantCopy: (uint8_t) want_copy { int rc; @@ -962,13 +962,11 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe fsObject = [SOGoMAPIFSMessage objectWithName: newKey inContainer: faiFolder]; newMessage = [MAPIStoreFAIMessageK mapiStoreObjectWithSOGoObject: fsObject inContainer: self]; - return newMessage; } -- (MAPIStoreMessage *) createMessageWithMID: (uint64_t) mid - isAssociated: (BOOL) isAssociated +- (MAPIStoreMessage *) createMessage: (BOOL) isAssociated { MAPIStoreMessage *newMessage; WOContext *woContext; @@ -976,7 +974,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe if (isAssociated) newMessage = [self _createAssociatedMessage]; else - newMessage = [self createMessageWithMID: mid]; + newMessage = [self createMessage]; [newMessage setIsNew: YES]; woContext = [[self context] woContext]; [[newMessage sogoObject] setContext: woContext]; @@ -1068,7 +1066,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe return Nil; } -- (MAPIStoreMessage *) createMessageWithMID: (uint64_t) mid +- (MAPIStoreMessage *) createMessage { [self logWithFormat: @"ignored method: %s", __PRETTY_FUNCTION__]; return nil; diff --git a/OpenChange/MAPIStoreMailFolder.m b/OpenChange/MAPIStoreMailFolder.m index 7476c5a2c..dc9af26fc 100644 --- a/OpenChange/MAPIStoreMailFolder.m +++ b/OpenChange/MAPIStoreMailFolder.m @@ -761,24 +761,98 @@ _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 +static void +_appendIMAPRange (NSMutableArray *UIDs, uint32_t low, uint32_t high) { - MAPIStoreMessage *message; - SOGoMailObject *mail; - - mail = [SOGoMailObject objectWithName: [NSString stringWithFormat: @"%llu", mid] - inContainer: sogoObject]; - - message = [MAPIStoreMailMessage mapiStoreObjectWithSOGoObject: mail - inContainer: self]; + uint32_t count; - return message; + for (count = low; count < high + 1; count++) + [UIDs addObject: [NSNumber numberWithUnsignedLong: count]]; +} + +static NSUInteger +_parseUID (const unichar *uniString, uint32_t *newUidP) +{ + NSUInteger count = 0; + uint32_t newUid = 0; + + while (uniString[count] >= '0' && uniString[count] <= '9') + { + newUid = newUid * 10 + (uniString[count] - 48); + count++; + } + *newUidP = newUid; + + return count; +} + +static NSUInteger +_parseIMAPRange (const unichar *uniString, NSArray **UIDsP) +{ + NSMutableArray *UIDs; + NSUInteger count = 0; + uint32_t currentUid, rangeMin; + BOOL done = NO, inRange = NO; + + UIDs = [NSMutableArray array]; + while (!done) + { + count += _parseUID (uniString + count, ¤tUid); + switch (uniString[count]) + { + case ':': + inRange = YES; + rangeMin = currentUid; + break; + case ' ': + case 0: + done = YES; + case ',': + if (inRange) + { + _appendIMAPRange (UIDs, rangeMin, currentUid); + inRange = NO; + } + else + [UIDs addObject: [NSNumber numberWithUnsignedLong: currentUid]]; + break; + default: + abort (); + } + count++; + } + *UIDsP = UIDs; + + return count; +} + +static void +_parseCOPYUID (NSString *line, NSArray **destUIDsP) +{ + unichar *uniString; + NSUInteger count = 0, max; + // char state = 'i'; /* i = init, v = validity, s = source range, d = dest range */ + + /* sample: 1 OK [COPYUID 1311899334 1:3 11:13] Completed */ + + max = [line length]; + uniString = NSZoneMalloc (NULL, max * sizeof (unichar) + 1); + [line getCharacters: uniString]; + uniString[max] = 0; + + while (count < max && uniString[count] != ' ') + count++; + count++; + while (count < max && uniString[count] != ' ') + count++; + count++; + while (count < max && uniString[count] != ' ') + count++; + count++; + if (count < max) + count += _parseIMAPRange (uniString + count, destUIDsP); + + NSZoneFree (NULL, uniString); } // @@ -786,195 +860,195 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data) // "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 +- (int) moveCopyMessagesWithMIDs: (uint64_t *) srcMids + andCount: (uint32_t) midCount + toFolder: (MAPIStoreFolder *) targetFolder + withMIDs: (uint64_t *) targetMids + wantCopy: (uint8_t) want_copy { - NSString *folderName, *messageURL, *uid, *url, *v; + NGImap4Connection *connection; + NGImap4Client *client; + NSString *sourceFolderName, *targetFolderName, *messageURL, *v; + NSMutableArray *uids, *oldMessageURLs; + NSNumber *uid; + NSArray *destUIDs; MAPIStoreMapping *mapping; NSDictionary *result; - - - unsigned int new_uid; - uint64_t target_mid; - int rc; + NSUInteger count, tableCount, max; + // uint64_t target_mid; + MAPIStoreMessage *message; + NSArray *a, *activeTables; + struct mapistore_object_notification_parameters *notif_parameters; + struct mapistore_connection_info *connInfo; // FIXME // We only support IMAP-to-IMAP copy operations for now. // Otherwise we silently fail (for now, at least!) if (![targetFolder isKindOfClass: [MAPIStoreMailFolder class]]) return MAPISTORE_SUCCESS; - + + /* Conversion of mids to IMAP uids */ 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) + uids = [NSMutableArray arrayWithCapacity: midCount]; + oldMessageURLs = [NSMutableArray arrayWithCapacity: midCount]; + for (count = 0; count < midCount; count++) + { + messageURL = [mapping urlFromID: srcMids[count]]; + if (messageURL) { - MAPIStoreMessage *message; - NSArray *a, *activeTables; - NGImap4Client *client; + uid = [self + messageUIDFromMessageKey: [messageURL lastPathComponent]]; + [uids addObject: uid]; - struct mapistore_object_notification_parameters *notif_parameters; - struct mapistore_connection_info *connInfo; - NSUInteger count, max; - - // 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]; - - // We check if the COPY operation succeeded - if (![[result objectForKey: @"result"] boolValue]) - return MAPISTORE_ERR_NOT_FOUND; - - 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]; - - - // For the "source folder, we ensure the table caches are loaded so - // that old and new state can be compared - message = [self lookupMessageByURL: messageURL]; - activeTables = [self activeMessageTables]; - max = [activeTables count]; - for (count = 0; count < max; count++) - [[activeTables objectAtIndex: count] restrictedChildKeys]; - - // We notify the client. We start with the source folder. - 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] - 1; - connInfo = [[self context] connectionInfo]; - mapistore_push_notification (connInfo->mstore_ctx, - MAPISTORE_FOLDER, - MAPISTORE_OBJECT_MODIFIED, - notif_parameters); - talloc_free(notif_parameters); - - // move/copy notification of the copied/moved message - 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]; - - // For the "destination folder, we ensure the table caches are loaded so - // that old and new state can be compared - message = targetMessage; - activeTables = [targetFolder activeMessageTables]; - max = [activeTables count]; - for (count = 0; count < max; count++) - [[activeTables objectAtIndex: count] restrictedChildKeys]; - - notif_parameters = talloc_zero(NULL, struct mapistore_object_notification_parameters); - notif_parameters->object_id = [targetFolder 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 = [[targetFolder messageKeys] count] + 1; - connInfo = [[self context] connectionInfo]; - mapistore_push_notification (connInfo->mstore_ctx, - MAPISTORE_FOLDER, - MAPISTORE_OBJECT_MODIFIED, - 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 destination folders - [self cleanupCaches]; - [targetFolder cleanupCaches]; - rc = MAPISTORE_SUCCESS; - } + [oldMessageURLs addObject: messageURL]; + } else - rc = MAPISTORE_ERR_NOT_FOUND; + return MAPISTORE_ERROR; } - else - rc = MAPISTORE_ERR_NOT_FOUND; - return rc; + /* IMAP COPY */ + connection = [sogoObject imap4Connection]; + sourceFolderName = [connection + imap4FolderNameForURL: [sogoObject imap4URL]]; + targetFolderName = [connection + imap4FolderNameForURL: [[targetFolder sogoObject] imap4URL]]; + + client = [connection client]; + [client select: sourceFolderName]; + result = [client copyUids: uids toFolder: targetFolderName]; + if (![[result objectForKey: @"result"] boolValue]) + return MAPISTORE_ERROR; + + /* "Move" treatment: Store \Deleted and unregister urls */ + if (!want_copy) + { + [client storeFlags: [NSArray arrayWithObject: @"Deleted"] forUIDs: uids + addOrRemove: YES]; + for (count = 0; count < midCount; count++) + [mapping unregisterURLWithID: srcMids[count]]; + } + + /* Registration of target messages */ + // + // 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) + _parseCOPYUID (v, &destUIDs); + else + { + /* FIXME: this may fail if new messages are appended to the folder + between the COPY and SORT operations */ + [client select: targetFolderName]; + a = [[client sort: @"ARRIVAL" qualifier: nil encoding: @"UTF-8"] + objectForKey: @"sort"]; + destUIDs = [[a sortedArrayUsingSelector: @selector (compare:)] + subarrayWithRange: NSMakeRange ([a count] - midCount, midCount)]; + } + for (count = 0; count < midCount; count++) + { + messageURL = [NSString stringWithFormat: @"%@%@.eml", + [targetFolder url], + [destUIDs objectAtIndex: count]]; + [mapping registerURL: messageURL withID: targetMids[count]]; + } + + // For the "source folder, we ensure the table caches are loaded so + // that old and new state can be compared + activeTables = [self activeMessageTables]; + max = [activeTables count]; + for (count = 0; count < max; count++) + [[activeTables objectAtIndex: count] restrictedChildKeys]; + + // We notify the client. We start with the source folder. + 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] - midCount; + connInfo = [[self context] connectionInfo]; + mapistore_push_notification (connInfo->mstore_ctx, + MAPISTORE_FOLDER, + MAPISTORE_OBJECT_MODIFIED, + notif_parameters); + talloc_free(notif_parameters); + + // move/copy notification of the copied/moved message + for (count = 0; count < midCount; count++) + { + 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 = targetMids[count]; + notif_parameters->folder_id = [targetFolder objectId]; + notif_parameters->old_object_id = srcMids[count]; + 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); + + message = [self lookupMessageByURL: [oldMessageURLs objectAtIndex: count]]; + // table notification + for (tableCount = 0; tableCount < max; tableCount++) + [[activeTables objectAtIndex: tableCount] + notifyChangesForChild: message]; + } + + // For the "destination folder, we ensure the table caches are loaded so + // that old and new state can be compared + // message = targetMessage; + activeTables = [targetFolder activeMessageTables]; + max = [activeTables count]; + for (count = 0; count < max; count++) + [[activeTables objectAtIndex: count] restrictedChildKeys]; + + notif_parameters = talloc_zero(NULL, struct mapistore_object_notification_parameters); + notif_parameters->object_id = [targetFolder 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 = [[targetFolder messageKeys] count] + midCount; + connInfo = [[self context] connectionInfo]; + mapistore_push_notification (connInfo->mstore_ctx, + MAPISTORE_FOLDER, + MAPISTORE_OBJECT_MODIFIED, + notif_parameters); + talloc_free(notif_parameters); + + // table notification + for (count = 0; count < midCount; count++) + { + messageURL = [mapping urlFromID: targetMids[count]]; + message = [self lookupMessageByURL: messageURL]; + for (tableCount = 0; tableCount < max; tableCount++) + [[activeTables objectAtIndex: tableCount] + notifyChangesForChild: message]; + } + + // We cleanup cache of our source and destination folders + [self cleanupCaches]; + [targetFolder cleanupCaches]; + + return MAPISTORE_SUCCESS; } @@ -1150,7 +1224,7 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data) return MAPIStoreDraftsMessageK; } -- (MAPIStoreMessage *) createMessageWithMID: (uint64_t) mid +- (MAPIStoreMessage *) createMessage { MAPIStoreDraftsMessage *newMessage; SOGoDraftObject *newDraft; diff --git a/OpenChange/MAPIStoreSOGo.m b/OpenChange/MAPIStoreSOGo.m index dc0e2031e..252d68b03 100644 --- a/OpenChange/MAPIStoreSOGo.m +++ b/OpenChange/MAPIStoreSOGo.m @@ -443,12 +443,13 @@ sogo_folder_delete_message(void *folder_object, uint64_t mid, uint8_t flags) } static int -sogo_folder_move_copy_message(void *folder_object, uint64_t mid, void *target_folder, void *target_message, uint8_t want_copy) +sogo_folder_move_copy_messages(void *folder_object, + uint32_t mid_count, uint64_t *src_mids, + void *target_folder_object, uint64_t *t_mids, + uint8_t want_copy) { MAPIStoreFolder *sourceFolder, *targetFolder; - MAPIStoreMessage *targetMessage; NSAutoreleasePool *pool; - struct MAPIStoreTallocWrapper *wrapper; int rc; @@ -458,18 +459,16 @@ sogo_folder_move_copy_message(void *folder_object, uint64_t mid, void *target_fo { wrapper = folder_object; sourceFolder = wrapper->MAPIStoreSOGoObject; - - wrapper = target_folder; + + wrapper = target_folder_object; targetFolder = wrapper->MAPIStoreSOGoObject; - wrapper = target_message; - targetMessage = wrapper->MAPIStoreSOGoObject; - pool = [NSAutoreleasePool new]; - rc = [sourceFolder moveCopyMessageWithMID: mid - toFolder: targetFolder - inMessage: targetMessage - wantCopy: want_copy]; + rc = [sourceFolder moveCopyMessagesWithMIDs: src_mids + andCount: mid_count + toFolder: targetFolder + withMIDs: t_mids + wantCopy: want_copy]; [pool release]; } else @@ -1070,7 +1069,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.move_copy_messages = sogo_folder_move_copy_messages; 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 7e0393ee8..be96b9ab9 100644 --- a/OpenChange/MAPIStoreTasksFolder.m +++ b/OpenChange/MAPIStoreTasksFolder.m @@ -100,7 +100,7 @@ static Class MAPIStoreTasksMessageK; return componentQualifier; } -- (MAPIStoreMessage *) createMessageWithMID: (uint64_t) mid +- (MAPIStoreMessage *) createMessage { MAPIStoreMessage *newMessage; SOGoTaskObject *newEntry;