Monotone-Parent: ec3d19f268090d56a33692cdc116485c488fd4d6

Monotone-Revision: 6b4e2f16b2a1ac3bbbb8f854763b7deb70bd8a46

Monotone-Author: wsourdeau@inverse.ca
Monotone-Date: 2011-07-28T00:55:50
Monotone-Branch: ca.inverse.sogo
maint-2.0.2
Wolfgang Sourdeau 2011-07-28 00:55:50 +00:00
parent dbed73c66f
commit 2f2605dfcc
8 changed files with 332 additions and 53 deletions

View File

@ -1,5 +1,26 @@
2011-07-27 Wolfgang Sourdeau <wsourdeau@inverse.ca>
* OpenChange/MAPIStoreGCSMessageTable.m
(-evaluatePropertyRestriction:intoQualifier:): now handles
PR_CHANGE_NUM using the new methods below.
* OpenChange/MAPIStoreGCSMessage.m (-objectVersion): overriden
method to make use of -[MAPIStoreGCSFolder
changeNumberForMessageWithKey:].
* OpenChange/MAPIStoreGCSFolder.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.
(-lastModifiedFromMessageChangeNumber:): new helper method that returns
the corresponding c_lastmodified from a MAPI change number.
(-changeNumberForMessageWithKey:): new helper method that returns the
version number associated with a message.
* OpenChange/MAPIStoreMailMessageTable.m
(-evaluatePropertyRestriction:intoQualifier:): now handles
PR_CHANGE_NUM using the new methods below.

View File

@ -83,6 +83,7 @@ static Class MAPIStoreCalendarMessageK;
- (MAPIStoreMessageTable *) messageTable
{
[self synchroniseCache];
return [MAPIStoreCalendarMessageTable tableForContainer: self];
}

View File

@ -85,6 +85,7 @@ static Class MAPIStoreContactsMessageK;
- (MAPIStoreMessageTable *) messageTable
{
[self synchroniseCache];
return [MAPIStoreContactsMessageTable tableForContainer: self];
}

View File

@ -26,8 +26,18 @@
#import "MAPIStoreFolder.h"
@class NSCalendarDate;
@class NSNumber;
@class NSString;
@interface MAPIStoreGCSFolder : MAPIStoreFolder
{
SOGoMAPIFSMessage *versionsMessage;
}
/* synchronisation */
- (BOOL) synchroniseCache;
- (NSNumber *) lastModifiedFromMessageChangeNumber: (NSNumber *) changeNum;
- (NSNumber *) changeNumberForMessageWithKey: (NSString *) messageKey;
/* subclasses */
- (EOQualifier *) componentQualifier;

View File

@ -20,24 +20,61 @@
* Boston, MA 02111-1307, USA.
*/
#import <Foundation/NSString.h>
#import <Foundation/NSCalendarDate.h>
#import <Foundation/NSDictionary.h>
#import <NGExtensions/NSObject+Logs.h>
#import <EOControl/EOQualifier.h>
#import <EOControl/EOFetchSpecification.h>
#import <EOControl/EOSortOrdering.h>
#import <GDLContentStore/GCSFolder.h>
#import <SOGo/NSArray+Utilities.h>
#import <SOGo/SOGoGCSFolder.h>
#import "MAPIStoreContext.h"
#import "MAPIStoreTypes.h"
#import "NSDate+MAPIStore.h"
#import "SOGoMAPIFSMessage.h"
#import "MAPIStoreGCSFolder.h"
#import "MAPIStoreTypes.h"
#undef DEBUG
#include <mapistore/mapistore.h>
@implementation MAPIStoreGCSFolder
- (id) initWithURL: (NSURL *) newURL
inContext: (MAPIStoreContext *) newContext
{
if ((self = [super initWithURL: newURL
inContext: newContext]))
{
ASSIGN (versionsMessage,
[SOGoMAPIFSMessage objectWithName: @"versions.plist"
inContainer: propsFolder]);
}
return self;
}
- (id) initWithSOGoObject: (id) newSOGoObject
inContainer: (MAPIStoreObject *) newContainer
{
if ((self = [super initWithSOGoObject: newSOGoObject inContainer: newContainer]))
{
ASSIGN (versionsMessage,
[SOGoMAPIFSMessage objectWithName: @"versions.plist"
inContainer: propsFolder]);
}
return self;
}
- (void) dealloc
{
[versionsMessage release];
[super dealloc];
}
- (NSArray *) messageKeysMatchingQualifier: (EOQualifier *) qualifier
andSortOrderings: (NSArray *) sortOrderings
{
@ -79,7 +116,187 @@
- (NSDate *) lastMessageModificationTime
{
return [[sogoObject ocsFolder] lastModificationDate];
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;
...
}
}
*/
- (BOOL) synchroniseCache
{
BOOL rc = YES, foundChange = NO;
uint64_t newChangeNum;
NSNumber *ti, *changeNumber, *lastModificationDate, *cName, *cVersion, *cLastModified;
EOFetchSpecification *fs;
EOQualifier *searchQualifier, *fetchQualifier;
NSUInteger count, max;
NSArray *fetchResults;
NSDictionary *result;
NSMutableDictionary *currentProperties, *messages, *mapping, *messageEntry;
NSCalendarDate *now;
GCSFolder *ocsFolder;
static NSArray *fields = nil;
static EOSortOrdering *sortOrdering = nil;
if (!fields)
fields = [[NSArray alloc]
initWithObjects: @"c_name", @"c_version", @"c_lastmodified",
nil];
if (!sortOrdering)
{
sortOrdering = [EOSortOrdering sortOrderingWithKey: @"c_lastmodified"
selector: EOCompareAscending];
[sortOrdering retain];
}
now = [NSCalendarDate date];
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];
}
lastModificationDate = [currentProperties objectForKey: @"SyncLastModificationDate"];
if (lastModificationDate)
{
searchQualifier = [[EOKeyValueQualifier alloc]
initWithKey: @"c_lastmodified"
operatorSelector: EOQualifierOperatorGreaterThanOrEqualTo
value: lastModificationDate];
fetchQualifier = [[EOAndQualifier alloc]
initWithQualifiers:
searchQualifier, [self componentQualifier], nil];
[fetchQualifier autorelease];
[searchQualifier release];
}
else
fetchQualifier = [self componentQualifier];
ocsFolder = [sogoObject ocsFolder];
fs = [EOFetchSpecification
fetchSpecificationWithEntityName: [ocsFolder folderName]
qualifier: fetchQualifier
sortOrderings: [NSArray arrayWithObject: sortOrdering]];
fetchResults = [ocsFolder fetchFields: fields fetchSpecification: fs];
max = [fetchResults count];
if (max > 0)
{
for (count = 0; count < max; count++)
{
result = [fetchResults objectAtIndex: count];
cName = [result objectForKey: @"c_name"];
cVersion = [result objectForKey: @"c_version"];
cLastModified = [result objectForKey: @"c_lastmodified"];
messageEntry = [messages objectForKey: cName];
if (!messageEntry)
{
messageEntry = [NSMutableDictionary new];
[messages setObject: messageEntry forKey: cName];
[messageEntry release];
}
if (![[messageEntry objectForKey: @"c_version"]
isEqual: cVersion])
{
foundChange = YES;
newChangeNum = [[self context] getNewChangeNumber];
changeNumber = [NSNumber numberWithUnsignedLongLong: newChangeNum];
[messageEntry setObject: cLastModified forKey: @"c_lastmodified"];
[messageEntry setObject: cVersion forKey: @"c_version"];
[messageEntry setObject: changeNumber forKey: @"version"];
[mapping setObject: cLastModified forKey: changeNumber];
if (!lastModificationDate
|| ([lastModificationDate compare: cLastModified]
== NSOrderedAscending))
lastModificationDate = cLastModified;
}
}
if (foundChange)
{
ti = [NSNumber numberWithDouble: [now timeIntervalSince1970]];
[currentProperties setObject: ti
forKey: @"SyncLastSynchronisationDate"];
[currentProperties setObject: lastModificationDate
forKey: @"SyncLastModificationDate"];
[versionsMessage appendProperties: currentProperties];
[versionsMessage save];
}
}
return rc;
}
- (NSNumber *) lastModifiedFromMessageChangeNumber: (NSNumber *) changeNum
{
NSDictionary *mapping;
NSNumber *modseq;
mapping = [[versionsMessage properties] objectForKey: @"VersionMapping"];
modseq = [mapping objectForKey: changeNum];
return modseq;
}
- (NSNumber *) changeNumberForMessageWithKey: (NSString *) messageKey
{
NSDictionary *messages;
NSNumber *changeNumber;
messages = [[versionsMessage properties] objectForKey: @"Messages"];
changeNumber = [[messages objectForKey: messageKey]
objectForKey: @"version"];
return changeNumber;
}
/* subclasses */

View File

@ -20,8 +20,10 @@
* Boston, MA 02111-1307, USA.
*/
#import <Foundation/NSValue.h>
#import <SOGo/SOGoContentObject.h>
#import "MAPIStoreGCSFolder.h"
#import "MAPIStoreTypes.h"
#import "MAPIStoreGCSMessage.h"
@ -41,4 +43,19 @@
return [sogoObject lastModified];
}
- (uint64_t) objectVersion
{
uint64_t version = 0xffffffffffffffffLL;
NSNumber *changeNumber;
changeNumber = [(MAPIStoreGCSFolder *) container
changeNumberForMessageWithKey: [self nameInContainer]];
if (changeNumber)
version = [changeNumber unsignedLongLongValue] >> 16;
else
abort ();
return version;
}
@end

View File

@ -36,8 +36,8 @@
#import <SOGo/SOGoGCSFolder.h>
#import "MAPIStoreTypes.h"
#import "MAPIStoreGCSFolder.h"
#import "NSAutoreleasePool+MAPIStore.h"
#import "MAPIStoreFolder.h"
#import "MAPIStoreGCSMessageTable.h"
@ -46,20 +46,10 @@
@implementation MAPIStoreGCSMessageTable
- (id) init
- (void) cleanupCaches
{
if ((self = [super init]))
{
sortOrderings = nil;
}
return self;
}
- (void) dealloc
{
[sortOrderings release];
[super dealloc];
[(MAPIStoreGCSFolder *) container synchroniseCache];
[super cleanupCaches];
}
- (struct mapi_SPropertyRestriction *) _fixedDatePropertyRestriction: (struct mapi_SPropertyRestriction *) res
@ -92,45 +82,67 @@
SEL operator;
id value;
NSString *property;
NSNumber *lastModified;
MAPIRestrictionState rc;
property = [self backendIdentifierForProperty: res->ulPropTag];
if (property)
if (res->ulPropTag == PR_CHANGE_NUM)
{
if (res->relop >= 0 && res->relop < 7)
operator = operators[res->relop];
else
{
operator = NULL;
[NSException raise: @"MAPIStoreRestrictionException"
format: @"unhandled operator type number %d", res->relop];
}
if ((res->ulPropTag & 0xffff) == PT_SYSTIME)
{
res = [self _fixedDatePropertyRestriction: res];
NSAutoreleaseTallocPointer (res);
}
value = NSObjectFromMAPISPropValue (&res->lpProp);
if ((res->ulPropTag & 0xffff) == PT_UNICODE)
lastModified = [(MAPIStoreGCSFolder *)
container lastModifiedFromMessageChangeNumber: value];
[self logWithFormat: @"change number from oxcfxics: %.16lx", [value unsignedLongLongValue]];
[self logWithFormat: @" c_lastmodified: %@", lastModified];
if (lastModified)
{
property = [NSString stringWithFormat: @"UPPER(%@)", property];
value = [value uppercaseString];
*qualifier = [[EOKeyValueQualifier alloc] initWithKey: @"c_lastmodified"
operatorSelector: EOQualifierOperatorGreaterThanOrEqualTo
value: lastModified];
[*qualifier autorelease];
rc = MAPIRestrictionStateNeedsEval;
}
*qualifier = [[EOKeyValueQualifier alloc] initWithKey: property
operatorSelector: operator
value: value];
[*qualifier autorelease];
rc = MAPIRestrictionStateNeedsEval;
else
rc = MAPIRestrictionStateAlwaysTrue;
}
else
{
[self warnUnhandledProperty: res->ulPropTag
inFunction: __FUNCTION__];
rc = MAPIRestrictionStateAlwaysFalse;
property = [self backendIdentifierForProperty: res->ulPropTag];
if (property)
{
if (res->relop >= 0 && res->relop < 7)
operator = operators[res->relop];
else
{
operator = NULL;
[NSException raise: @"MAPIStoreRestrictionException"
format: @"unhandled operator type number %d", res->relop];
}
if ((res->ulPropTag & 0xffff) == PT_SYSTIME)
{
res = [self _fixedDatePropertyRestriction: res];
NSAutoreleaseTallocPointer (res);
}
value = NSObjectFromMAPISPropValue (&res->lpProp);
if ((res->ulPropTag & 0xffff) == PT_UNICODE)
{
property = [NSString stringWithFormat: @"UPPER(%@)", property];
value = [value uppercaseString];
}
*qualifier = [[EOKeyValueQualifier alloc] initWithKey: property
operatorSelector: operator
value: value];
[*qualifier autorelease];
rc = MAPIRestrictionStateNeedsEval;
}
else
{
[self warnUnhandledProperty: res->ulPropTag
inFunction: __FUNCTION__];
rc = MAPIRestrictionStateAlwaysFalse;
}
}
return rc;
@ -147,9 +159,9 @@
- (EOSortOrdering *) _sortOrderingFromSortOrder: (struct SSortOrder *) sortOrder
{
EOSortOrdering *newSortOrdering;
EOSortOrdering *newSortOrdering = nil;
NSString *sortIdentifier;
SEL orderSelector;
SEL orderSelector = NULL;
const char *propName;
sortIdentifier = [self sortIdentifierForProperty: sortOrder->ulPropTag];
@ -182,12 +194,12 @@
@"TABLE_SORT_MAXIMUM_CATEGORY is not handled"];
}
}
newSortOrdering = [EOSortOrdering sortOrderingWithKey: sortIdentifier
selector: orderSelector];
if (orderSelector)
newSortOrdering = [EOSortOrdering sortOrderingWithKey: sortIdentifier
selector: orderSelector];
}
else
{
newSortOrdering = nil;
propName = get_proptag_name (sortOrder->ulPropTag);
if (!propName)
propName = "<unknown>";

View File

@ -83,6 +83,7 @@ static Class MAPIStoreTasksMessageK;
- (MAPIStoreMessageTable *) messageTable
{
[self synchroniseCache];
return [MAPIStoreTasksMessageTable tableForContainer: self];
}
@ -90,7 +91,6 @@ static Class MAPIStoreTasksMessageK;
{
static EOQualifier *componentQualifier = nil;
/* TODO: we need to support vlist as well */
if (!componentQualifier)
componentQualifier
= [[EOKeyValueQualifier alloc] initWithKey: @"c_component"