Monotone-Parent: cac2cd04f2f5027f49a937745510d92fa557a762

Monotone-Revision: ec3d19f268090d56a33692cdc116485c488fd4d6

Monotone-Author: wsourdeau@inverse.ca
Monotone-Date: 2011-07-28T00:52:38
Monotone-Branch: ca.inverse.sogo
maint-2.0.2
Wolfgang Sourdeau 2011-07-28 00:52:38 +00:00
parent 132495b7e4
commit dbed73c66f
5 changed files with 306 additions and 5 deletions

View File

@ -1,5 +1,27 @@
2011-07-27 Wolfgang Sourdeau <wsourdeau@inverse.ca>
* OpenChange/MAPIStoreMailMessageTable.m
(-evaluatePropertyRestriction:intoQualifier:): now handles
PR_CHANGE_NUM using the new methods below.
* OpenChange/MAPIStoreMailMessage.m (-objectVersion): overriden
method to make use of -[MAPIStoreMailFolder
changeNumberForMessageUID:].
* OpenChange/MAPIStoreMailFolder.m (-initWithURL:inContext:)
(initWithSOGoObject:inContainer:): initialise the new
"versionsMessage" ivar, pointing to a SOGoMAPIFSMessage object
that includes versioning information.
(-lastMessageModificationTime): return the date value stored in
"versionsMessage".
(-synchroniseCache): new method that updates "versionsMessage"
differentually with versioning data from the server.
(-modseqFromMessageChangeNumber:): new helper method that returns
the corresponding IMAP "modseq" from a MAPI change number.
(-messageUIDFromMessageKey:): new helper method.
(-changeNumberForMessageUID:): new helper method that returns the
version number associated with a message.
* OpenChange/MAPIStoreTable.m (-loggingPrefix): overriden method
in order to get more useful informations.

View File

@ -25,6 +25,8 @@
#import "MAPIStoreFolder.h"
@class NSNumber;
@class WOContext;
@class SOGoMailAccount;
@ -35,6 +37,7 @@
@interface MAPIStoreMailFolder : MAPIStoreFolder
{
MAPIStoreMailMessageTable *messageTable;
SOGoMAPIFSMessage *versionsMessage;
}
/* subclasses */
@ -42,6 +45,12 @@
inContext: (WOContext *) woContext;
/* synchronisation & versioning */
- (BOOL) synchroniseCache;
- (NSNumber *) modseqFromMessageChangeNumber: (NSNumber *) changeNum;
- (NSNumber *) messageUIDFromMessageKey: (NSString *) messageKey;
- (NSNumber *) changeNumberForMessageUID: (NSNumber *) messageUid;
@end
@interface MAPIStoreInboxFolder : MAPIStoreMailFolder

View File

@ -20,12 +20,16 @@
* Boston, MA 02111-1307, USA.
*/
#include <talloc.h>
#import <Foundation/NSArray.h>
#import <Foundation/NSCalendarDate.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSString.h>
#import <Foundation/NSURL.h>
#import <NGObjWeb/WOContext+SoObjects.h>
#import <EOControl/EOQualifier.h>
#import <EOControl/EOSortOrdering.h>
#import <NGExtensions/NSObject+Logs.h>
#import <NGExtensions/NSString+misc.h>
#import <Mailer/SOGoDraftsFolder.h>
@ -38,12 +42,14 @@
#import <SOGo/NSString+Utilities.h>
#import "MAPIApplication.h"
#import "MAPIStoreAppointmentWrapper.h"
#import "MAPIStoreContext.h"
#import "MAPIStoreDraftsMessage.h"
#import "MAPIStoreMailMessage.h"
#import "MAPIStoreMailMessageTable.h"
#import "MAPIStoreTypes.h"
#import "NSString+MAPIStore.h"
#import "SOGoMAPIFSMessage.h"
#import "MAPIStoreMailFolder.h"
@ -61,6 +67,7 @@ static Class SOGoMailFolderK;
{
MAPIStoreMailMessageK = [MAPIStoreMailMessage class];
SOGoMailFolderK = [SOGoMailFolder class];
[MAPIStoreAppointmentWrapper class];
}
- (id) initWithURL: (NSURL *) newURL
@ -103,7 +110,27 @@ static Class SOGoMailFolderK;
currentContainer = [currentContainer container];
}
[self logWithFormat: @"sogoObject: %@", sogoObject];
ASSIGN (versionsMessage,
[SOGoMAPIFSMessage objectWithName: @"versions.plist"
inContainer: propsFolder]);
}
return self;
}
- (id) initWithSOGoObject: (id) newSOGoObject
inContainer: (MAPIStoreObject *) newContainer
{
NSURL *propsURL;
NSString *urlString;
if ((self = [super initWithSOGoObject: newSOGoObject inContainer: newContainer]))
{
urlString = [[self url] stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding];
propsURL = [NSURL URLWithString: urlString];
ASSIGN (versionsMessage,
[SOGoMAPIFSMessage objectWithName: @"versions.plist"
inContainer: propsFolder]);
}
return self;
@ -111,6 +138,7 @@ static Class SOGoMailFolderK;
- (void) dealloc
{
[versionsMessage release];
[messageTable release];
[super dealloc];
}
@ -126,7 +154,10 @@ static Class SOGoMailFolderK;
- (MAPIStoreMessageTable *) messageTable
{
if (!messageTable)
ASSIGN (messageTable, [MAPIStoreMailMessageTable tableForContainer: self]);
{
[self synchroniseCache];
ASSIGN (messageTable, [MAPIStoreMailMessageTable tableForContainer: self]);
}
return messageTable;
}
@ -276,7 +307,198 @@ static Class SOGoMailFolderK;
- (NSDate *) lastMessageModificationTime
{
return [sogoObject mostRecentMessageDate];
NSNumber *ti;
NSDate *value = nil;
ti = [[versionsMessage properties]
objectForKey: @"SyncLastSynchronisationDate"];
if (ti)
value = [NSDate dateWithTimeIntervalSince1970: [ti doubleValue]];
else
value = [NSDate date];
[self logWithFormat: @"lastMessageModificationTime: %@", value];
return value;
}
/* synchronisation */
/* Tree:
{
SyncLastModseq = x;
SyncLastSynchronisationDate = x; ** not updated until something changed
Messages = {
MessageKey = {
Version = x;
Modseq = x;
Deleted = b;
};
...
};
VersionMapping = {
Version = MessageKey;
...
}
}
*/
static NSComparisonResult
_compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
{
static NSNumber *zeroNumber = nil;
NSNumber *modseq1, *modseq2;
if (!zeroNumber)
zeroNumber = [NSNumber numberWithUnsignedLongLong: 0];
modseq1 = [entry1 objectForKey: @"modseq"];
if (!modseq1)
modseq1 = zeroNumber;
modseq2 = [entry2 objectForKey: @"modseq"];
if (!modseq2)
modseq2 = zeroNumber;
return [modseq1 compare: modseq2];
}
- (BOOL) synchroniseCache
{
BOOL rc = YES;
uint64_t newChangeNum;
NSNumber *ti, *changeNumber, *modseq, *lastModseq, *nextModseq, *uid;
EOQualifier *searchQualifier;
NSArray *uids;
NSUInteger count, max;
NSArray *fetchResults;
NSDictionary *result;
NSMutableDictionary *currentProperties, *messages, *mapping, *messageEntry;
NSCalendarDate *now;
now = [NSCalendarDate date];
[now setTimeZone: utcTZ];
currentProperties = [[versionsMessage properties] mutableCopy];
if (!currentProperties)
currentProperties = [NSMutableDictionary new];
[currentProperties autorelease];
messages = [currentProperties objectForKey: @"Messages"];
if (!messages)
{
messages = [NSMutableDictionary new];
[currentProperties setObject: messages forKey: @"Messages"];
[messages release];
}
mapping = [currentProperties objectForKey: @"VersionMapping"];
if (!mapping)
{
mapping = [NSMutableDictionary new];
[currentProperties setObject: mapping forKey: @"VersionMapping"];
[mapping release];
}
lastModseq = [currentProperties objectForKey: @"SyncLastModseq"];
if (lastModseq)
{
nextModseq = [NSNumber numberWithUnsignedLongLong:
[lastModseq unsignedLongLongValue] + 1];
searchQualifier = [[EOKeyValueQualifier alloc]
initWithKey: @"modseq"
operatorSelector: EOQualifierOperatorGreaterThanOrEqualTo
value: nextModseq];
[searchQualifier autorelease];
}
else
searchQualifier = [self nonDeletedQualifier];
uids = [sogoObject fetchUIDsMatchingQualifier: searchQualifier
sortOrdering: nil];
max = [uids count];
if (max > 0)
{
fetchResults
= [(NSDictionary *) [sogoObject fetchUIDs: uids
parts: [NSArray arrayWithObject: @"modseq"]]
objectForKey: @"fetch"];
/* NOTE: we sort items manually because Cyrus does not properly sort
entries with a MODSEQ of 0 */
fetchResults
= [fetchResults sortedArrayUsingFunction: _compareFetchResultsByMODSEQ
context: NULL];
for (count = 0; count < max; count++)
{
result = [fetchResults objectAtIndex: count];
uid = [result objectForKey: @"uid"];
modseq = [result objectForKey: @"modseq"];
[self logWithFormat: @"uid '%@' has modseq '%@'", uid, modseq];
newChangeNum = [[self context] getNewChangeNumber];
changeNumber = [NSNumber numberWithUnsignedLongLong: newChangeNum];
messageEntry = [NSMutableDictionary new];
[messages setObject: messageEntry forKey: uid];
[messageEntry release];
[messageEntry setObject: modseq forKey: @"modseq"];
[messageEntry setObject: changeNumber forKey: @"version"];
[mapping setObject: modseq forKey: changeNumber];
if (!lastModseq
|| ([lastModseq compare: modseq] == NSOrderedAscending))
lastModseq = modseq;
}
ti = [NSNumber numberWithDouble: [now timeIntervalSince1970]];
[currentProperties setObject: ti
forKey: @"SyncLastSynchronisationDate"];
[currentProperties setObject: lastModseq forKey: @"SyncLastModseq"];
[versionsMessage appendProperties: currentProperties];
[versionsMessage save];
}
return rc;
}
- (NSNumber *) modseqFromMessageChangeNumber: (NSNumber *) changeNum
{
NSDictionary *mapping;
NSNumber *modseq;
mapping = [[versionsMessage properties] objectForKey: @"VersionMapping"];
modseq = [mapping objectForKey: changeNum];
return modseq;
}
- (NSNumber *) messageUIDFromMessageKey: (NSString *) messageKey
{
NSNumber *messageUid;
NSString *uidString;
NSRange dotRange;
dotRange = [messageKey rangeOfString: @".eml"];
if (dotRange.location != NSNotFound)
{
uidString = [messageKey substringToIndex: dotRange.location];
messageUid = [NSNumber numberWithInt: [uidString intValue]];
}
else
messageUid = nil;
return messageUid;
}
- (NSNumber *) changeNumberForMessageUID: (NSNumber *) messageUid
{
NSDictionary *messages;
NSNumber *changeNumber;
messages = [[versionsMessage properties] objectForKey: @"Messages"];
changeNumber = [[messages objectForKey: messageUid]
objectForKey: @"version"];
return changeNumber;
}
@end

View File

@ -266,6 +266,28 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
return appointmentWrapper;
}
- (uint64_t) objectVersion
{
uint64_t version = 0xffffffffffffffffLL;
NSNumber *uid, *changeNumber;
uid = [(MAPIStoreMailFolder *)
container messageUIDFromMessageKey: [self nameInContainer]];
if (uid)
{
changeNumber = [(MAPIStoreMailFolder *)
container changeNumberForMessageUID: uid];
if (changeNumber)
version = [changeNumber unsignedLongLongValue] >> 16;
else
abort ();
}
else
abort ();
return version;
}
- (int) getPrIconIndex: (void **) data
inMemCtx: (TALLOC_CTX *) memCtx
{
@ -1135,8 +1157,8 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
}
}
- (NSArray *) attachmentsKeysMatchingQualifier: (EOQualifier *) qualifier
andSortOrderings: (NSArray *) sortOrderings
- (NSArray *) attachmentKeysMatchingQualifier: (EOQualifier *) qualifier
andSortOrderings: (NSArray *) sortOrderings
{
[self _fetchAttachmentPartsInBodyInfo: [sogoObject bodyStructure]
withPrefix: @""];

View File

@ -72,6 +72,12 @@ static Class MAPIStoreMailMessageK, NSDataK, NSStringK;
return self;
}
- (void) cleanupCaches
{
[(MAPIStoreMailFolder *) container synchroniseCache];
[super cleanupCaches];
}
- (NSString *) backendIdentifierForProperty: (enum MAPITAGS) property
{
static NSMutableDictionary *knownProperties = nil;
@ -97,6 +103,7 @@ static Class MAPIStoreMailMessageK, NSDataK, NSStringK;
{
MAPIRestrictionState rc;
id value;
NSNumber *modseq;
value = NSObjectFromMAPISPropValue (&res->lpProp);
switch ((uint32_t) res->ulPropTag)
@ -143,6 +150,25 @@ static Class MAPIStoreMailMessageK, NSDataK, NSStringK;
case PR_CONVERSATION_KEY:
rc = MAPIRestrictionStateAlwaysFalse;
break;
case PR_CHANGE_NUM:
{
modseq = [(MAPIStoreMailFolder *)
container modseqFromMessageChangeNumber: value];
[self logWithFormat: @"change number from oxcfxics: %.16lx", [value unsignedLongLongValue]];
[self logWithFormat: @" modseq: %.16lx", [modseq unsignedLongLongValue]];
if (modseq)
modseq = [NSNumber numberWithUnsignedLongLong:
[modseq unsignedLongLongValue] + 1];
else
modseq = [NSNumber numberWithUnsignedLongLong: 0];
*qualifier = [[EOKeyValueQualifier alloc] initWithKey: @"MODSEQ"
operatorSelector: EOQualifierOperatorGreaterThanOrEqualTo
value: modseq];
[*qualifier autorelease];
rc = MAPIRestrictionStateNeedsEval;
}
break;
default:
rc = [super evaluatePropertyRestriction: res intoQualifier: qualifier];