Monotone-Parent: ec3d19f268090d56a33692cdc116485c488fd4d6
Monotone-Revision: 6b4e2f16b2a1ac3bbbb8f854763b7deb70bd8a46 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2011-07-28T00:55:50 Monotone-Branch: ca.inverse.sogomaint-2.0.2
parent
dbed73c66f
commit
2f2605dfcc
21
ChangeLog
21
ChangeLog
|
@ -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.
|
||||
|
|
|
@ -83,6 +83,7 @@ static Class MAPIStoreCalendarMessageK;
|
|||
|
||||
- (MAPIStoreMessageTable *) messageTable
|
||||
{
|
||||
[self synchroniseCache];
|
||||
return [MAPIStoreCalendarMessageTable tableForContainer: self];
|
||||
}
|
||||
|
||||
|
|
|
@ -85,6 +85,7 @@ static Class MAPIStoreContactsMessageK;
|
|||
|
||||
- (MAPIStoreMessageTable *) messageTable
|
||||
{
|
||||
[self synchroniseCache];
|
||||
return [MAPIStoreContactsMessageTable tableForContainer: self];
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>";
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Reference in New Issue