(fix) manually added fixes from PR#120

pull/101/head^2
Ludovic Marcotte 2015-11-05 09:59:31 -05:00
parent edebdf1171
commit 5f82d3fb37
16 changed files with 269 additions and 100 deletions

View File

@ -45,16 +45,28 @@
@implementation MAPIStoreDBMessage
+ (int) getAvailableProperties: (struct SPropTagArray **) propertiesP
inMemCtx: (TALLOC_CTX *) memCtx
+ (enum mapistore_error) getAvailableProperties: (struct SPropTagArray **) propertiesP
inMemCtx: (TALLOC_CTX *) memCtx
{
struct SPropTagArray *properties;
NSUInteger count;
enum MAPITAGS faiProperties[] = { 0x68350102, 0x683c0102, 0x683e0102,
0x683f0102, 0x68410003, 0x68420102,
0x68450102, 0x68460003,
// PR_VD_NAME_W, PR_VD_FLAGS, PR_VD_VERSION, PR_VIEW_CLSID
0x7006001F, 0x70030003, 0x70070003, 0x68330048 };
enum MAPITAGS faiProperties[] = {
0x68330048, /* PR_VIEW_CLSID */
0x68350102, /* PR_VIEW_STATE */
0x683c0102,
0x683d0040,
0x683e0102,
0x683f0102, /* PR_VIEW_VIEWTYPE_KEY */
0x68410003,
0x68420102,
0x68450102,
0x68460003,
0x7006001F, /* PR_VD_NAME_W */
0x70030003, /* PR_VD_FLAGS */
0x70070003 /* PR_VD_VERSION */
};
size_t faiSize = sizeof(faiProperties) / sizeof(enum MAPITAGS);
properties = talloc_zero (memCtx, struct SPropTagArray);
@ -76,6 +88,13 @@
return MAPISTORE_SUCCESS;
}
- (enum mapistore_error) getAvailableProperties: (struct SPropTagArray **) propertiesP
inMemCtx: (TALLOC_CTX *) memCtx
{
return [[self class] getAvailableProperties: propertiesP
inMemCtx: memCtx];
}
- (id) initWithSOGoObject: (id) newSOGoObject
inContainer: (MAPIStoreObject *) newContainer
{

View File

@ -41,6 +41,7 @@
/* synchronisation */
- (BOOL) synchroniseCache;
- (BOOL) synchroniseCacheFor: (NSString *) nameInContainer;
- (void) updateVersionsForMessageWithKey: (NSString *) messageKey
withChangeKey: (NSData *) oldChangeKey
andPredecessorChangeList: (NSData *) pcl;

View File

@ -548,6 +548,115 @@ static Class NSNumberK;
return rc;
}
- (BOOL) synchroniseCacheFor: (NSString *) nameInContainer
{
/* Try to synchronise old messages in versions.plist cache using an
specific c_name. It returns a boolean indicating if the
synchronisation was carried out succesfully.
It should be used as last resort, keeping synchroniseCache as the
main sync entry point. */
uint64_t changeNumber;
NSString *changeNumberStr;
NSData *changeKey;
NSNumber *cLastModified, *cDeleted, *cVersion;
EOFetchSpecification *fs;
EOQualifier *searchQualifier, *fetchQualifier;
NSArray *fetchResults;
NSDictionary *result;
NSMutableDictionary *currentProperties, *messages, *mapping, *messageEntry;
GCSFolder *ocsFolder;
static NSArray *fields;
[versionsMessage reloadIfNeeded];
currentProperties = [versionsMessage properties];
messages = [currentProperties objectForKey: @"Messages"];
if (!messages)
{
messages = [NSMutableDictionary new];
[currentProperties setObject: messages forKey: @"Messages"];
[messages release];
}
messageEntry = [messages objectForKey: nameInContainer];
if (!messageEntry)
{
/* Fetch the message by its name */
if (!fields)
fields = [[NSArray alloc]
initWithObjects: @"c_name", @"c_version", @"c_lastmodified",
@"c_deleted", nil];
searchQualifier = [[EOKeyValueQualifier alloc] initWithKey: @"c_name"
operatorSelector: EOQualifierOperatorEqual
value: nameInContainer];
fetchQualifier = [[EOAndQualifier alloc]
initWithQualifiers: searchQualifier,
[self contentComponentQualifier],
nil];
[fetchQualifier autorelease];
[searchQualifier release];
ocsFolder = [sogoObject ocsFolder];
fs = [EOFetchSpecification
fetchSpecificationWithEntityName: [ocsFolder folderName]
qualifier: fetchQualifier
sortOrderings: nil];
fetchResults = [ocsFolder fetchFields: fields
fetchSpecification: fs
ignoreDeleted: NO];
if ([fetchResults count] == 1)
{
result = [fetchResults objectAtIndex: 0];
cLastModified = [result objectForKey: @"c_lastmodified"];
cDeleted = [result objectForKey: @"c_deleted"];
if ([cDeleted isKindOfClass: NSNumberK] && [cDeleted intValue])
cVersion = [NSNumber numberWithInt: -1];
else
cVersion = [result objectForKey: @"c_version"];
changeNumber = [[self context] getNewChangeNumber];
changeNumberStr = [NSString stringWithUnsignedLongLong: changeNumber];
/* Create new message entry in Messages dict */
messageEntry = [NSMutableDictionary new];
[messages setObject: messageEntry forKey: nameInContainer];
[messageEntry release];
/* Store cLastModified, cVersion and the change number */
[messageEntry setObject: cLastModified forKey: @"c_lastmodified"];
[messageEntry setObject: cVersion forKey: @"c_version"];
[messageEntry setObject: changeNumberStr forKey: @"version"];
/* Store the change key */
changeKey = [self getReplicaKeyFromGlobCnt: changeNumber >> 16];
[self _setChangeKey: changeKey forMessageEntry: messageEntry];
/* Store the changeNumber -> cLastModified mapping */
mapping = [currentProperties objectForKey: @"VersionMapping"];
if (!mapping)
{
mapping = [NSMutableDictionary new];
[currentProperties setObject: mapping forKey: @"VersionMapping"];
[mapping release];
}
[mapping setObject: cLastModified forKey: changeNumberStr];
/* Save the message */
[versionsMessage save];
return YES;
}
else
return NO;
}
/* If message entry exists, then synchroniseCache did its job */
return YES;
}
- (void) updateVersionsForMessageWithKey: (NSString *) messageKey
withChangeKey: (NSData *) oldChangeKey
andPredecessorChangeList: (NSData *) pcl

View File

@ -180,7 +180,8 @@
{
uint64_t version = ULLONG_MAX;
NSString *changeNumber;
BOOL synced;
if (!isNew)
{
changeNumber = [(MAPIStoreGCSFolder *) container
@ -189,16 +190,28 @@
{
[self warnWithFormat: @"attempting to get change number"
@" by synchronising folder..."];
[(MAPIStoreGCSFolder *) container synchroniseCache];
changeNumber = [(MAPIStoreGCSFolder *) container
changeNumberForMessageWithKey: [self nameInContainer]];
if (changeNumber)
[self logWithFormat: @"got one"];
else
synced = [(MAPIStoreGCSFolder *) container synchroniseCache];
if (synced)
{
[self errorWithFormat: @"still nothing. We crash!"];
abort();
changeNumber = [(MAPIStoreGCSFolder *) container
changeNumberForMessageWithKey: [self nameInContainer]];
}
if (!changeNumber)
{
[self warnWithFormat: @"attempting to get change number"
@" by synchronising this specific message..."];
synced = [(MAPIStoreGCSFolder *) container
synchroniseCacheFor: [self nameInContainer]];
if (synced)
{
changeNumber = [(MAPIStoreGCSFolder *) container
changeNumberForMessageWithKey: [self nameInContainer]];
}
if (!changeNumber)
{
[self errorWithFormat: @"still nothing. We crash!"];
abort();
}
}
}
version = [changeNumber unsignedLongLongValue] >> 16;

View File

@ -160,23 +160,25 @@ MakeDisplayFolderName (NSString *folderName)
for (count = 0; count < max; count++)
{
context = talloc_zero (memCtx, struct mapistore_contexts_list);
// secondaryFolders has the names (1) Imap4Encoded and (2) asCSSIdentifier
// e.g.: Probl&AOg-mes_SP_de_SP_synchronisation
// secondaryFolders has the names (1) Imap4Encoded ,(2) asCSSIdentifier and (3) "folder"-prefixed
// e.g.: folderProbl&AOg-mes_SP_de_SP_synchronisation
currentName = [secondaryFolders objectAtIndex: count];
// To get the real name we have to revert that (applying the decode functions)
// in reverse order
// To get the real name we have to revert that (applying the decode functions
// in reverse order)
// e.g.: Problèmes de synchronisation
realName = [[currentName fromCSSIdentifier]
stringByDecodingImap4FolderName];
realName = [[[currentName substringFromIndex: 6]
fromCSSIdentifier]
stringByDecodingImap4FolderName];
// And finally to represent that as URI we have to (1) asCSSIdentifier,
// (2) Imap4Encode and (3) AddPercentEscapes
// e.g.: Probl&AOg-mes_SP_de_SP_synchronisation
// (2) Imap4Encode (3) AddPercentEscapes and (4) add the "folder" prefix
// e.g.: folderProbl&AOg-mes_SP_de_SP_synchronisation
// In the example there are no percent escapes added because is already ok
stringData = [[[realName asCSSIdentifier]
stringByEncodingImap4FolderName]
stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding];
stringData = [NSString stringWithFormat: @"folder%@", stringData];
context->url = [[NSString stringWithFormat: @"%@%@", urlBase, stringData] asUnicodeInMemCtx: context];
context->name = [[realName substringFromIndex: 6] asUnicodeInMemCtx: context];
context->name = [realName asUnicodeInMemCtx: context];
context->main_folder = false;
context->role = MAPISTORE_MAIL_ROLE;
context->tag = "tag";
@ -200,7 +202,7 @@ MakeDisplayFolderName (NSString *folderName)
andTDBIndexing: NULL];
accountFolder = [[userContext rootFolders] objectForKey: @"mail"];
folderName = [NSString stringWithFormat: @"folder%@",
[newFolderName asCSSIdentifier]];
[[newFolderName stringByEncodingImap4FolderName] asCSSIdentifier]];
newFolder = [SOGoMailFolder objectWithName: folderName
inContainer: accountFolder];
if ([newFolder create])
@ -209,7 +211,7 @@ MakeDisplayFolderName (NSString *folderName)
withString: @"%40"],
[userName stringByReplacingOccurrencesOfString: @"@"
withString: @"%40"],
[[folderName stringByEncodingImap4FolderName] stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding]];
[folderName stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding]];
else
mapistoreURI = nil;

View File

@ -594,8 +594,7 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
{
BOOL rc = YES;
uint64_t newChangeNum;
NSNumber *ti, *modseq, *initialLastModseq, *lastModseq,
*nextModseq;
NSNumber *ti, *modseq, *lastModseq, *nextModseq;
NSString *changeNumber, *uid, *messageKey;
uint64_t lastModseqNbr;
EOQualifier *searchQualifier;
@ -634,7 +633,6 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
}
lastModseq = [currentProperties objectForKey: @"SyncLastModseq"];
initialLastModseq = lastModseq;
if (lastModseq)
{
lastModseqNbr = [lastModseq unsignedLongLongValue];
@ -718,40 +716,37 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
}
/* 2. we synchronise expunged UIDs */
if (initialLastModseq)
fetchResults = [(SOGoMailFolder *) sogoObject
fetchUIDsOfVanishedItems: lastModseqNbr];
max = [fetchResults count];
changeNumber = nil;
for (count = 0; count < max; count++)
{
fetchResults = [(SOGoMailFolder *) sogoObject
fetchUIDsOfVanishedItems: lastModseqNbr];
max = [fetchResults count];
changeNumber = nil;
for (count = 0; count < max; count++)
uid = [[fetchResults objectAtIndex: count] stringValue];
if ([messages objectForKey: uid])
{
uid = [[fetchResults objectAtIndex: count] stringValue];
if ([messages objectForKey: uid])
if (!changeNumber)
{
if (!changeNumber)
{
newChangeNum = [[self context] getNewChangeNumber];
changeNumber = [NSString stringWithUnsignedLongLong: newChangeNum];
}
[messages removeObjectForKey: uid];
[self logWithFormat: @"Removed message entry for UID %@", uid];
}
else
{
[self logWithFormat:@"Message entry not found for UID %@", uid];
newChangeNum = [[self context] getNewChangeNumber];
changeNumber = [NSString stringWithUnsignedLongLong: newChangeNum];
}
[messages removeObjectForKey: uid];
[self logWithFormat: @"Removed message entry for UID %@", uid];
}
if (changeNumber)
else
{
[currentProperties setObject: changeNumber
forKey: @"SyncLastDeleteChangeNumber"];
[mapping setObject: lastModseq forKey: changeNumber];
foundChange = YES;
[self logWithFormat:@"Message entry not found for UID %@", uid];
}
}
if (changeNumber)
{
[currentProperties setObject: changeNumber
forKey: @"SyncLastDeleteChangeNumber"];
[mapping setObject: lastModseq forKey: changeNumber];
foundChange = YES;
}
if (foundChange)
{

View File

@ -1645,27 +1645,11 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
- (enum mapistore_error) setReadFlag: (uint8_t) flag
{
BOOL modified = NO;
BOOL alreadyRead = NO;
NSString *imapFlag = @"\\Seen";
alreadyRead = [[[sogoObject fetchCoreInfos] objectForKey: @"flags"]
containsObject: @"seen"];
/* TODO: notifications should probably be emitted from here */
if (flag & CLEAR_READ_FLAG)
{
[sogoObject removeFlags: imapFlag];
modified = alreadyRead;
}
[properties setObject: [NSNumber numberWithBool: NO] forKey: @"read_flag_set"];
else
{
[sogoObject addFlags: imapFlag];
modified = !alreadyRead;
}
if (modified)
[(MAPIStoreMailFolder *)[self container] synchroniseCache];
[properties setObject: [NSNumber numberWithBool: YES] forKey: @"read_flag_set"];
return MAPISTORE_SUCCESS;
}
@ -1708,7 +1692,10 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
- (void) save: (TALLOC_CTX *) memCtx
{
BOOL modified = NO;
BOOL seen, storedSeenFlag;
NSNumber *value;
NSString *imapFlag = @"\\Seen";
value = [properties objectForKey: MAPIPropertyKey (PR_FLAG_STATUS)];
if (value)
@ -1718,8 +1705,35 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
[sogoObject addFlags: @"\\Flagged"];
else /* 0: unflagged, 1: follow up complete */
[sogoObject removeFlags: @"\\Flagged"];
modified = YES;
}
/* Manage seen flag on save */
value = [properties objectForKey: @"read_flag_set"];
if (value)
{
seen = [value boolValue];
storedSeenFlag = [[[sogoObject fetchCoreInfos] objectForKey: @"flags"] containsObject: @"seen"];
/* We modify the flags anyway to generate a new change number */
if (seen)
{
if (storedSeenFlag)
[sogoObject removeFlags: imapFlag];
[sogoObject addFlags: imapFlag];
}
else
{
if (!storedSeenFlag)
[sogoObject addFlags: imapFlag];
[sogoObject removeFlags: imapFlag];
}
modified = YES;
}
if (modified)
[(MAPIStoreMailFolder *)[self container] synchroniseCache];
if (mailIsSharingObject)
[[self _sharingObject] saveWithMessage: self
andSOGoObject: sogoObject];

View File

@ -167,9 +167,9 @@ static NSString *recTypes[] = { @"orig", @"to", @"cc", @"bcc" };
- (BOOL) hasContentId
{
return ([properties
objectForKey: MAPIPropertyKey (PR_ATTACH_CONTENT_ID_UNICODE)]
!= nil);
NSString *contentId = [properties
objectForKey: MAPIPropertyKey (PR_ATTACH_CONTENT_ID_UNICODE)];
return contentId && [contentId length] > 0;
}
- (NGMimeBodyPart *) asMIMEBodyPart
@ -233,7 +233,7 @@ static NSString *recTypes[] = { @"orig", @"to", @"cc", @"bcc" };
[map addObject: contentDisposition forKey: @"content-disposition"];
contentId = [properties
objectForKey: MAPIPropertyKey (PR_ATTACH_CONTENT_ID_UNICODE)];
if (contentId)
if (contentId && [contentId length] > 0)
[map setObject: [NSString stringWithFormat: @"<%@>", contentId]
forKey: @"content-id"];
bodyPart = [NGMimeBodyPart bodyPartWithHeader: map];

View File

@ -268,6 +268,9 @@ def asCSSIdentifier(inputString):
newChars = []
if str.isdigit(inputString[0]):
newChars.append("_")
for c in inputString:
if c in cssEscapingCharMap:
newChars.append(cssEscapingCharMap[c])

View File

@ -1013,7 +1013,7 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
{
if ([dateRange containsDate: [component startDate]])
{
// We must pass nill to :container here in order to avoid re-entrancy issues.
// We must pass nil to :container here in order to avoid re-entrancy issues.
newRecord = [self _fixupRecord: [component quickRecordFromContent: nil container: nil]];
[ma replaceObjectAtIndex: recordIndex withObject: newRecord];
}
@ -1030,15 +1030,20 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
{
// The recurrence id of the exception is outside the date range;
// simply add the exception to the records array.
// We must pass nill to :container here in order to avoid re-entrancy issues.
// We must pass nil to :container here in order to avoid re-entrancy issues.
newRecord = [self _fixupRecord: [component quickRecordFromContent: nil container: nil]];
newRecordRange = [NGCalendarDateRange
calendarDateRangeWithStartDate: [newRecord objectForKey: @"startDate"]
endDate: [newRecord objectForKey: @"endDate"]];
if ([dateRange doesIntersectWithDateRange: newRecordRange])
if ([newRecord objectForKey: @"startDate"] && [newRecord objectForKey: @"endDate"]) {
newRecordRange = [NGCalendarDateRange
calendarDateRangeWithStartDate: [newRecord objectForKey: @"startDate"]
endDate: [newRecord objectForKey: @"endDate"]];
if ([dateRange doesIntersectWithDateRange: newRecordRange])
[ma addObject: newRecord];
else
else
newRecord = nil;
} else {
[self warnWithFormat: @"Recurrence %@ without dtstart or dtend. Ignoring", recurrenceId];
newRecord = nil;
}
}
if (newRecord)

View File

@ -48,6 +48,7 @@
#import <SOGo/SOGoPermissions.h>
#import <SOGo/SOGoSource.h>
#import <SOGo/SOGoUserSettings.h>
#import <SOGo/SOGoSystemDefaults.h>
#import <SOGo/WORequest+SOGo.h>
#import <SOGo/WOResponse+SOGo.h>
@ -248,6 +249,13 @@
data = @"";
[newRecord setObject: data forKey: @"c_cn"];
if ([[SOGoSystemDefaults sharedSystemDefaults] enableDomainBasedUID])
{
data = [oldRecord objectForKey: @"c_domain"];
if (data)
[newRecord setObject: data forKey: @"c_domain"];
}
// mail => emails[]
data = [oldRecord objectForKey: @"c_emails"];
if (data)

View File

@ -206,8 +206,8 @@ static NSString *inboxFolderName = @"INBOX";
[folders removeObjectsInArray: nss];
}
return [[folders stringsWithFormat: @"folder%@"]
resultsOfSelector: @selector (asCSSIdentifier)];
return [[folders resultsOfSelector: @selector (asCSSIdentifier)]
stringsWithFormat: @"folder%@"];
}
- (NSArray *) toManyRelationshipKeys

View File

@ -199,9 +199,9 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
{
NSArray *subfolders;
subfolders = [[self subfolders] stringsWithFormat: @"folder%@"];
subfolders = [[self subfolders] resultsOfSelector: @selector (asCSSIdentifier)];
return [subfolders resultsOfSelector: @selector (asCSSIdentifier)];
return [subfolders stringsWithFormat: @"folder%@"];
}
- (NSArray *) subfolders
@ -634,7 +634,7 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
inContext: (id) localContext
{
NSArray *folders;
NSString *currentFolderName, *currentAccountName;
NSString *currentFolderName, *currentAccountName, *destinationAccountName;
NSMutableString *imapDestinationFolder;
NGImap4Client *client;
id result;
@ -642,24 +642,24 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
#warning this code will fail on implementation using something else than '/' as delimiter
imapDestinationFolder = [NSMutableString string];
folders = [[destinationFolder componentsSeparatedByString: @"/"]
resultsOfSelector: @selector (fromCSSIdentifier)];
folders = [destinationFolder componentsSeparatedByString: @"/"];
max = [folders count];
if (max > 1)
{
currentAccountName = [[self mailAccountFolder] nameInContainer];
client = [[self imap4Connection] client];
[imap4 selectFolder: [self imap4URL]];
destinationAccountName = [[folders objectAtIndex: 1] fromCSSIdentifier];
for (count = 2; count < max; count++)
{
currentFolderName = [[folders objectAtIndex: count] substringFromIndex: 6];
currentFolderName = [[[folders objectAtIndex: count] substringFromIndex: 6] fromCSSIdentifier];
[imapDestinationFolder appendFormat: @"/%@", currentFolderName];
}
if (client)
{
if ([[folders objectAtIndex: 1] isEqualToString: currentAccountName])
if ([destinationAccountName isEqualToString: currentAccountName])
{
// We make sure the destination IMAP folder exist, if not, we create it.
result = [[client status: imapDestinationFolder
@ -688,7 +688,7 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
userFolder = [[context activeUser] homeFolderInContext: context];
accounts = [userFolder lookupName: @"Mail" inContext: context acquire: NO];
account = [accounts lookupName: [folders objectAtIndex: 1] inContext: localContext acquire: NO];
account = [accounts lookupName: destinationAccountName inContext: localContext acquire: NO];
if ([account isKindOfClass: [NSException class]])
{

View File

@ -156,7 +156,7 @@
}
else
{
newKeyForMsgUIDs = [[NSString stringWithFormat:@"/%@/folder%@", currentAccount, newFolderName] asCSSIdentifier];
newKeyForMsgUIDs = [NSString stringWithFormat:@"/%@/folder%@", [currentAccount asCSSIdentifier], [newFolderName asCSSIdentifier]];
error = [co renameTo: newFolderName];
if (error)
{

View File

@ -726,8 +726,8 @@
for (k = 0; k < [pathComponents count]; k++)
{
component = [NSString stringWithFormat: @"folder%@", [pathComponents objectAtIndex: k]];
[path appendString: [component asCSSIdentifier]];
component = [[pathComponents objectAtIndex: k] asCSSIdentifier];
[path appendString: [NSString stringWithFormat: @"folder%@", component]];
if (k < [pathComponents count] - 1)
[path appendString: @"/"];
}

View File

@ -231,7 +231,7 @@
/* Searching */
"view_all" = "Todos los eventos";
"view_all" = "Todos";
"view_today" = "Hoy";
"view_next7" = "Próximos 7 días";
"view_next14" = "Próximos 14 días";