2011-02-24 21:32:27 +01:00
|
|
|
/* MAPIStoreMailFolder.m - this file is part of SOGo
|
|
|
|
*
|
2014-01-10 03:06:31 +01:00
|
|
|
* Copyright (C) 2011-2013 Inverse inc
|
2011-02-24 21:32:27 +01:00
|
|
|
*
|
|
|
|
* This file is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
2011-08-12 17:02:01 +02:00
|
|
|
* the Free Software Foundation; either version 3, or (at your option)
|
2011-02-24 21:32:27 +01:00
|
|
|
* any later version.
|
|
|
|
*
|
|
|
|
* This file is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; see the file COPYING. If not, write to
|
|
|
|
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
|
|
* Boston, MA 02111-1307, USA.
|
|
|
|
*/
|
|
|
|
|
2011-07-28 02:52:38 +02:00
|
|
|
#include <talloc.h>
|
|
|
|
|
2011-02-24 21:32:27 +01:00
|
|
|
#import <Foundation/NSArray.h>
|
2011-07-08 22:07:21 +02:00
|
|
|
#import <Foundation/NSCalendarDate.h>
|
2011-07-28 02:52:38 +02:00
|
|
|
#import <Foundation/NSDictionary.h>
|
2012-10-06 19:02:39 +02:00
|
|
|
#import <Foundation/NSSet.h>
|
2011-02-24 21:32:27 +01:00
|
|
|
#import <Foundation/NSString.h>
|
|
|
|
#import <Foundation/NSURL.h>
|
|
|
|
#import <NGObjWeb/WOContext+SoObjects.h>
|
|
|
|
#import <EOControl/EOQualifier.h>
|
2011-07-28 02:52:38 +02:00
|
|
|
#import <EOControl/EOSortOrdering.h>
|
2011-02-24 21:32:27 +01:00
|
|
|
#import <NGExtensions/NSObject+Logs.h>
|
2012-10-09 22:39:24 +02:00
|
|
|
#import <NGExtensions/NSObject+Values.h>
|
2011-04-05 17:30:54 +02:00
|
|
|
#import <NGExtensions/NSString+misc.h>
|
2011-07-29 04:13:39 +02:00
|
|
|
#import <NGImap4/NGImap4Connection.h>
|
2011-08-15 21:25:13 +02:00
|
|
|
#import <NGImap4/NGImap4Client.h>
|
2013-09-25 16:41:07 +02:00
|
|
|
#import <NGImap4/NSString+Imap4.h>
|
2011-02-24 21:32:27 +01:00
|
|
|
#import <Mailer/SOGoDraftsFolder.h>
|
|
|
|
#import <Mailer/SOGoMailAccount.h>
|
|
|
|
#import <Mailer/SOGoMailAccounts.h>
|
|
|
|
#import <Mailer/SOGoMailFolder.h>
|
2011-09-14 20:33:44 +02:00
|
|
|
#import <Mailer/SOGoMailObject.h>
|
2011-02-24 21:32:27 +01:00
|
|
|
#import <Mailer/SOGoSentFolder.h>
|
|
|
|
#import <Mailer/SOGoTrashFolder.h>
|
|
|
|
#import <SOGo/NSArray+Utilities.h>
|
2011-04-05 17:30:54 +02:00
|
|
|
#import <SOGo/NSString+Utilities.h>
|
2011-11-18 16:26:03 +01:00
|
|
|
#import <SOGo/SOGoPermissions.h>
|
2011-02-24 21:32:27 +01:00
|
|
|
|
|
|
|
#import "MAPIApplication.h"
|
2011-07-28 02:52:38 +02:00
|
|
|
#import "MAPIStoreAppointmentWrapper.h"
|
2011-02-24 21:32:27 +01:00
|
|
|
#import "MAPIStoreContext.h"
|
2011-09-14 20:33:44 +02:00
|
|
|
#import "MAPIStoreFAIMessage.h"
|
2012-08-15 21:01:24 +02:00
|
|
|
#import "MAPIStoreMailContext.h"
|
2012-10-06 19:02:39 +02:00
|
|
|
#import "MAPIStoreMailMessage.h"
|
2015-04-30 00:16:24 +02:00
|
|
|
#import "MAPIStoreMailFolderTable.h"
|
2011-02-24 21:32:27 +01:00
|
|
|
#import "MAPIStoreMailMessageTable.h"
|
2011-09-14 20:33:44 +02:00
|
|
|
#import "MAPIStoreMapping.h"
|
2011-02-24 21:32:27 +01:00
|
|
|
#import "MAPIStoreTypes.h"
|
2012-09-26 17:50:28 +02:00
|
|
|
#import "MAPIStoreUserContext.h"
|
2011-09-20 21:37:03 +02:00
|
|
|
#import "NSData+MAPIStore.h"
|
2011-02-24 21:32:27 +01:00
|
|
|
#import "NSString+MAPIStore.h"
|
2012-06-29 19:59:38 +02:00
|
|
|
#import "SOGoMAPIDBMessage.h"
|
2014-05-14 03:06:59 +02:00
|
|
|
#import <SOGo/SOGoCacheGCSFolder.h>
|
2011-02-24 21:32:27 +01:00
|
|
|
|
2011-10-26 23:33:51 +02:00
|
|
|
#import "MAPIStoreMailVolatileMessage.h"
|
2011-09-23 01:45:33 +02:00
|
|
|
|
2011-02-24 21:32:27 +01:00
|
|
|
#import "MAPIStoreMailFolder.h"
|
|
|
|
|
2012-08-09 23:35:09 +02:00
|
|
|
static Class SOGoMailFolderK, MAPIStoreMailFolderK, MAPIStoreOutboxFolderK;
|
2011-04-05 17:30:54 +02:00
|
|
|
|
|
|
|
#undef DEBUG
|
2011-11-09 16:02:29 +01:00
|
|
|
#include <util/attr.h>
|
2011-04-05 17:30:54 +02:00
|
|
|
#include <libmapi/libmapi.h>
|
2014-11-06 17:42:45 +01:00
|
|
|
#include <libmapiproxy.h>
|
2011-06-07 02:17:46 +02:00
|
|
|
#include <mapistore/mapistore.h>
|
2011-09-14 20:33:44 +02:00
|
|
|
#include <mapistore/mapistore_errors.h>
|
2011-02-24 21:32:27 +01:00
|
|
|
|
|
|
|
@implementation MAPIStoreMailFolder
|
|
|
|
|
|
|
|
+ (void) initialize
|
|
|
|
{
|
2011-04-05 17:30:54 +02:00
|
|
|
SOGoMailFolderK = [SOGoMailFolder class];
|
2012-08-09 23:35:09 +02:00
|
|
|
MAPIStoreMailFolderK = [MAPIStoreMailFolder class];
|
2012-02-20 20:47:58 +01:00
|
|
|
MAPIStoreOutboxFolderK = [MAPIStoreOutboxFolder class];
|
2011-07-28 02:52:38 +02:00
|
|
|
[MAPIStoreAppointmentWrapper class];
|
2011-02-24 21:32:27 +01:00
|
|
|
}
|
|
|
|
|
2012-01-30 22:42:10 +01:00
|
|
|
- (id) init
|
2011-07-28 02:52:38 +02:00
|
|
|
{
|
2012-01-30 22:42:10 +01:00
|
|
|
if ((self = [super init]))
|
2011-07-28 02:52:38 +02:00
|
|
|
{
|
2012-01-30 22:42:10 +01:00
|
|
|
versionsMessage = nil;
|
2012-10-06 19:02:39 +02:00
|
|
|
bodyData = [NSMutableDictionary new];
|
2011-02-24 21:32:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
2012-02-14 15:58:34 +01:00
|
|
|
- (void) dealloc
|
|
|
|
{
|
|
|
|
[versionsMessage release];
|
2012-10-06 19:02:39 +02:00
|
|
|
[bodyData release];
|
2012-02-14 15:58:34 +01:00
|
|
|
[super dealloc];
|
|
|
|
}
|
|
|
|
|
2012-01-29 20:55:21 +01:00
|
|
|
- (void) setupVersionsMessage
|
|
|
|
{
|
|
|
|
ASSIGN (versionsMessage,
|
2012-06-29 19:59:38 +02:00
|
|
|
[SOGoMAPIDBMessage objectWithName: @"versions.plist"
|
|
|
|
inContainer: dbFolder]);
|
2014-05-13 23:54:02 +02:00
|
|
|
[versionsMessage setObjectType: MAPIInternalCacheObject];
|
2012-01-29 20:55:21 +01:00
|
|
|
}
|
|
|
|
|
2012-02-14 15:58:34 +01:00
|
|
|
- (BOOL) ensureFolderExists
|
2011-06-07 02:17:46 +02:00
|
|
|
{
|
2012-02-14 15:58:34 +01:00
|
|
|
return [(SOGoMailFolder *) sogoObject exists] || [sogoObject create];
|
2011-06-07 02:17:46 +02:00
|
|
|
}
|
|
|
|
|
2012-01-30 22:42:10 +01:00
|
|
|
- (void) addProperties: (NSDictionary *) newProperties
|
2011-02-24 21:32:27 +01:00
|
|
|
{
|
2012-08-15 21:01:24 +02:00
|
|
|
NSString *newDisplayName, *newNameInContainer;
|
2012-01-30 22:42:10 +01:00
|
|
|
NSMutableDictionary *propsCopy;
|
|
|
|
NSNumber *key;
|
2012-08-15 17:24:42 +02:00
|
|
|
uint64_t fid;
|
2012-01-30 22:42:10 +01:00
|
|
|
|
|
|
|
key = MAPIPropertyKey (PR_DISPLAY_NAME_UNICODE);
|
|
|
|
newDisplayName = [newProperties objectForKey: key];
|
2012-02-20 20:47:58 +01:00
|
|
|
if (newDisplayName
|
|
|
|
&& ![self isKindOfClass: MAPIStoreOutboxFolderK]
|
|
|
|
&& ![[(SOGoMailFolder *) sogoObject displayName]
|
|
|
|
isEqualToString: newDisplayName])
|
2012-01-30 22:42:10 +01:00
|
|
|
{
|
2012-08-15 17:24:42 +02:00
|
|
|
fid = [self objectId];
|
2012-01-30 22:42:10 +01:00
|
|
|
[(SOGoMailFolder *) sogoObject renameTo: newDisplayName];
|
2012-08-15 21:01:24 +02:00
|
|
|
newNameInContainer = [sogoObject nameInContainer];
|
|
|
|
if (!container)
|
|
|
|
[(MAPIStoreMailContext *) context
|
|
|
|
updateURLWithFolderName: newNameInContainer];
|
2012-08-15 17:24:42 +02:00
|
|
|
[[self mapping] updateID: fid withURL: [self url]];
|
2012-08-15 21:01:24 +02:00
|
|
|
[dbFolder setNameInContainer: newNameInContainer];
|
2012-08-15 17:24:42 +02:00
|
|
|
[self cleanupCaches];
|
|
|
|
|
2012-01-30 22:42:10 +01:00
|
|
|
propsCopy = [newProperties mutableCopy];
|
|
|
|
[propsCopy removeObjectForKey: key];
|
|
|
|
[propsCopy autorelease];
|
|
|
|
newProperties = propsCopy;
|
|
|
|
}
|
2011-02-24 21:32:27 +01:00
|
|
|
|
2012-01-30 22:42:10 +01:00
|
|
|
[super addProperties: newProperties];
|
2011-02-24 21:32:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
- (MAPIStoreMessageTable *) messageTable
|
|
|
|
{
|
2011-08-15 17:57:23 +02:00
|
|
|
[self synchroniseCache];
|
|
|
|
return [MAPIStoreMailMessageTable tableForContainer: self];
|
2011-02-24 21:32:27 +01:00
|
|
|
}
|
|
|
|
|
2015-04-30 00:16:24 +02:00
|
|
|
- (MAPIStoreFolderTable *) folderTable
|
|
|
|
{
|
|
|
|
return [MAPIStoreMailFolderTable tableForContainer: self];
|
|
|
|
}
|
|
|
|
|
2012-02-03 16:05:19 +01:00
|
|
|
- (enum mapistore_error) createFolder: (struct SRow *) aRow
|
|
|
|
withFID: (uint64_t) newFID
|
|
|
|
andKey: (NSString **) newKeyP
|
2011-02-24 21:32:27 +01:00
|
|
|
{
|
2012-02-03 16:05:19 +01:00
|
|
|
enum mapistore_error rc;
|
2011-04-05 17:30:54 +02:00
|
|
|
NSString *folderName, *nameInContainer;
|
|
|
|
SOGoMailFolder *newFolder;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
nameInContainer = nil;
|
2015-01-19 15:35:44 +01:00
|
|
|
rc = MAPISTORE_ERROR;
|
2011-04-05 17:30:54 +02:00
|
|
|
|
|
|
|
folderName = nil;
|
|
|
|
for (i = 0; !folderName && i < aRow->cValues; i++)
|
|
|
|
{
|
|
|
|
if (aRow->lpProps[i].ulPropTag == PR_DISPLAY_NAME_UNICODE)
|
|
|
|
folderName = [NSString stringWithUTF8String: aRow->lpProps[i].value.lpszW];
|
|
|
|
else if (aRow->lpProps[i].ulPropTag == PR_DISPLAY_NAME)
|
|
|
|
folderName = [NSString stringWithUTF8String: aRow->lpProps[i].value.lpszA];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (folderName)
|
|
|
|
{
|
|
|
|
nameInContainer = [NSString stringWithFormat: @"folder%@",
|
2013-09-25 16:41:07 +02:00
|
|
|
[[folderName stringByEncodingImap4FolderName] asCSSIdentifier]];
|
2015-03-25 16:53:42 +01:00
|
|
|
|
|
|
|
/* it may be the operation is interleaved with operations
|
|
|
|
from other users having cached information in the thread
|
|
|
|
with the other user, so it'd better activate the user again here... */
|
|
|
|
[[self userContext] activateWithUser: [[[self userContext] woContext] activeUser]];
|
|
|
|
|
2011-04-05 17:30:54 +02:00
|
|
|
newFolder = [SOGoMailFolderK objectWithName: nameInContainer
|
2012-01-29 20:55:21 +01:00
|
|
|
inContainer: sogoObject];
|
2012-02-03 16:05:19 +01:00
|
|
|
if ([newFolder create])
|
2012-02-14 15:58:34 +01:00
|
|
|
{
|
|
|
|
*newKeyP = nameInContainer;
|
|
|
|
rc = MAPISTORE_SUCCESS;
|
|
|
|
}
|
2012-02-03 16:05:19 +01:00
|
|
|
else if ([newFolder exists])
|
|
|
|
rc = MAPISTORE_ERR_EXIST;
|
|
|
|
else
|
|
|
|
rc = MAPISTORE_ERR_DENIED;
|
2011-04-05 17:30:54 +02:00
|
|
|
}
|
|
|
|
|
2012-02-03 16:05:19 +01:00
|
|
|
return rc;
|
2011-02-24 21:32:27 +01:00
|
|
|
}
|
|
|
|
|
2012-02-03 16:01:23 +01:00
|
|
|
- (int) deleteFolder
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
NSException *error;
|
|
|
|
NSString *name;
|
|
|
|
|
|
|
|
name = [self nameInContainer];
|
|
|
|
if ([name isEqualToString: @"folderINBOX"])
|
|
|
|
rc = MAPISTORE_ERR_DENIED;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
error = [(SOGoMailFolder *) sogoObject delete];
|
|
|
|
if (error)
|
|
|
|
rc = MAPISTORE_ERROR;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (![versionsMessage delete])
|
|
|
|
rc = MAPISTORE_SUCCESS;
|
|
|
|
else
|
|
|
|
rc = MAPISTORE_ERROR;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return (rc == MAPISTORE_SUCCESS) ? [super deleteFolder] : rc;
|
|
|
|
}
|
|
|
|
|
2012-03-02 21:44:24 +01:00
|
|
|
- (int) getPidTagContentUnreadCount: (void **) data
|
2012-08-15 03:02:08 +02:00
|
|
|
inMemCtx: (TALLOC_CTX *) memCtx
|
2011-02-24 21:32:27 +01:00
|
|
|
{
|
|
|
|
EOQualifier *searchQualifier;
|
2011-06-07 02:17:46 +02:00
|
|
|
uint32_t longValue;
|
|
|
|
|
|
|
|
searchQualifier
|
|
|
|
= [EOQualifier qualifierWithQualifierFormat: @"flags = %@", @"unseen"];
|
|
|
|
longValue = [[sogoObject fetchUIDsMatchingQualifier: searchQualifier
|
|
|
|
sortOrdering: nil]
|
|
|
|
count];
|
|
|
|
*data = MAPILongValue (memCtx, longValue);
|
2011-02-24 21:32:27 +01:00
|
|
|
|
2011-06-07 02:17:46 +02:00
|
|
|
return MAPISTORE_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2012-03-02 21:44:24 +01:00
|
|
|
- (int) getPidTagContainerClass: (void **) data
|
2012-03-16 22:10:33 +01:00
|
|
|
inMemCtx: (TALLOC_CTX *) memCtx
|
2011-06-07 02:17:46 +02:00
|
|
|
{
|
|
|
|
*data = [@"IPF.Note" asUnicodeInMemCtx: memCtx];
|
2011-02-24 21:32:27 +01:00
|
|
|
|
2011-06-07 02:17:46 +02:00
|
|
|
return MAPISTORE_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2011-07-26 22:13:10 +02:00
|
|
|
- (EOQualifier *) nonDeletedQualifier
|
2011-02-24 21:32:27 +01:00
|
|
|
{
|
|
|
|
static EOQualifier *nonDeletedQualifier = nil;
|
2011-07-26 22:13:10 +02:00
|
|
|
EOQualifier *deletedQualifier;
|
2011-02-24 21:32:27 +01:00
|
|
|
|
|
|
|
if (!nonDeletedQualifier)
|
|
|
|
{
|
|
|
|
deletedQualifier
|
|
|
|
= [[EOKeyValueQualifier alloc]
|
|
|
|
initWithKey: @"FLAGS"
|
|
|
|
operatorSelector: EOQualifierOperatorContains
|
|
|
|
value: [NSArray arrayWithObject: @"Deleted"]];
|
|
|
|
nonDeletedQualifier = [[EONotQualifier alloc]
|
|
|
|
initWithQualifier: deletedQualifier];
|
|
|
|
[deletedQualifier release];
|
|
|
|
}
|
|
|
|
|
2011-07-26 22:13:10 +02:00
|
|
|
return nonDeletedQualifier;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSArray *) messageKeysMatchingQualifier: (EOQualifier *) qualifier
|
|
|
|
andSortOrderings: (NSArray *) sortOrderings
|
|
|
|
{
|
|
|
|
NSArray *uidKeys;
|
|
|
|
EOQualifier *fetchQualifier;
|
|
|
|
|
2012-02-14 15:58:34 +01:00
|
|
|
if ([self ensureFolderExists])
|
2011-02-24 21:32:27 +01:00
|
|
|
{
|
2012-02-14 15:58:34 +01:00
|
|
|
if (!sortOrderings)
|
|
|
|
sortOrderings = [NSArray arrayWithObject: @"ARRIVAL"];
|
|
|
|
|
|
|
|
if (qualifier)
|
|
|
|
{
|
|
|
|
fetchQualifier
|
|
|
|
= [[EOAndQualifier alloc] initWithQualifiers:
|
|
|
|
[self nonDeletedQualifier], qualifier,
|
|
|
|
nil];
|
|
|
|
[fetchQualifier autorelease];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
fetchQualifier = [self nonDeletedQualifier];
|
|
|
|
|
|
|
|
uidKeys = [[sogoObject fetchUIDsMatchingQualifier: fetchQualifier
|
|
|
|
sortOrdering: sortOrderings]
|
|
|
|
stringsWithFormat: @"%@.eml"];
|
2011-02-24 21:32:27 +01:00
|
|
|
}
|
|
|
|
else
|
2012-02-14 15:58:34 +01:00
|
|
|
uidKeys = nil;
|
2011-02-24 21:32:27 +01:00
|
|
|
|
2012-02-14 15:58:34 +01:00
|
|
|
return uidKeys;
|
2011-02-24 21:32:27 +01:00
|
|
|
}
|
|
|
|
|
2011-08-15 22:31:57 +02:00
|
|
|
- (NSMutableString *) _imapFolderNameRepresentation: (NSString *) subfolderName
|
|
|
|
{
|
|
|
|
NSMutableString *representation;
|
|
|
|
NSString *nameInContainer, *strippedName;
|
|
|
|
|
|
|
|
nameInContainer = [self nameInContainer];
|
|
|
|
if (container)
|
|
|
|
representation = [(MAPIStoreMailFolder *) container
|
|
|
|
_imapFolderNameRepresentation: nameInContainer];
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (![nameInContainer hasPrefix: @"folder"])
|
|
|
|
abort ();
|
|
|
|
strippedName = [nameInContainer substringFromIndex: 6];
|
|
|
|
representation = [NSMutableString stringWithString: strippedName];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (![subfolderName hasPrefix: @"folder"])
|
|
|
|
abort ();
|
2013-09-25 16:41:07 +02:00
|
|
|
strippedName = [[subfolderName substringFromIndex: 6] stringByDecodingImap4FolderName];
|
2011-08-15 22:31:57 +02:00
|
|
|
[representation appendFormat: @"/%@", strippedName];
|
|
|
|
|
|
|
|
return representation;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) _cleanupSubfolderKeys: (NSMutableArray *) subfolderKeys
|
|
|
|
{
|
|
|
|
SOGoMailAccount *account;
|
|
|
|
NSString *draftsFolderName, *sentFolderName, *trashFolderName;
|
|
|
|
NSString *subfolderKey, *cmpString;
|
|
|
|
NSUInteger count, max;
|
|
|
|
NSMutableArray *keysToRemove;
|
|
|
|
|
|
|
|
account = [(SOGoMailFolder *) sogoObject mailAccountFolder];
|
|
|
|
draftsFolderName = [account draftsFolderNameInContext: nil];
|
|
|
|
sentFolderName = [account sentFolderNameInContext: nil];
|
|
|
|
trashFolderName = [account trashFolderNameInContext: nil];
|
|
|
|
|
|
|
|
max = [subfolderKeys count];
|
|
|
|
keysToRemove = [NSMutableArray arrayWithCapacity: max];
|
|
|
|
for (count = 0; count < max; count++)
|
|
|
|
{
|
|
|
|
subfolderKey = [subfolderKeys objectAtIndex: count];
|
2011-08-15 22:39:27 +02:00
|
|
|
cmpString = [self _imapFolderNameRepresentation: subfolderKey];
|
2011-08-15 22:31:57 +02:00
|
|
|
if ([cmpString isEqualToString: draftsFolderName]
|
|
|
|
|| [cmpString isEqualToString: sentFolderName]
|
|
|
|
|| [cmpString isEqualToString: trashFolderName])
|
|
|
|
[keysToRemove addObject: subfolderKey];
|
|
|
|
}
|
|
|
|
|
|
|
|
[subfolderKeys removeObjectsInArray: keysToRemove];
|
|
|
|
}
|
|
|
|
|
2011-08-15 21:25:13 +02:00
|
|
|
- (NSArray *) folderKeysMatchingQualifier: (EOQualifier *) qualifier
|
|
|
|
andSortOrderings: (NSArray *) sortOrderings
|
2011-04-05 17:30:54 +02:00
|
|
|
{
|
2015-04-30 00:16:24 +02:00
|
|
|
NSArray *filteredSubfolderKeys;
|
2011-08-15 22:31:57 +02:00
|
|
|
NSMutableArray *subfolderKeys;
|
2015-04-30 00:16:24 +02:00
|
|
|
NSMutableArray *subfolderKeysQualifying;
|
|
|
|
NSString *subfolderKey;
|
|
|
|
NSUInteger count, max;
|
2011-08-15 22:31:57 +02:00
|
|
|
|
2012-02-14 15:58:34 +01:00
|
|
|
if ([self ensureFolderExists])
|
|
|
|
{
|
2015-04-30 00:16:24 +02:00
|
|
|
/* Only folder name can be used as qualifier key */
|
2012-02-14 15:58:34 +01:00
|
|
|
if (qualifier)
|
2015-04-30 00:16:24 +02:00
|
|
|
[self warnWithFormat: @"qualifier is only used for folders with name"];
|
2012-02-14 15:58:34 +01:00
|
|
|
if (sortOrderings)
|
|
|
|
[self errorWithFormat: @"sort orderings are not used for folders"];
|
|
|
|
|
2015-05-05 10:46:53 +02:00
|
|
|
/* FIXME: Flush any cache before retrieving the hierarchy, this
|
|
|
|
slows things down but it is safer */
|
|
|
|
if (!qualifier)
|
|
|
|
[sogoObject flushMailCaches];
|
|
|
|
|
2012-02-14 15:58:34 +01:00
|
|
|
subfolderKeys = [[sogoObject toManyRelationshipKeys] mutableCopy];
|
|
|
|
[subfolderKeys autorelease];
|
|
|
|
|
|
|
|
[self _cleanupSubfolderKeys: subfolderKeys];
|
2015-04-30 00:16:24 +02:00
|
|
|
|
|
|
|
if (qualifier)
|
|
|
|
{
|
|
|
|
subfolderKeysQualifying = [NSMutableArray array];
|
|
|
|
max = [subfolderKeys count];
|
|
|
|
for (count = 0; count < max; count++) {
|
|
|
|
subfolderKey = [subfolderKeys objectAtIndex: count];
|
|
|
|
/* Remove "folder" prefix */
|
|
|
|
subfolderKey = [subfolderKey substringFromIndex: 6];
|
|
|
|
subfolderKey = [[subfolderKey fromCSSIdentifier] stringByDecodingImap4FolderName];
|
|
|
|
[subfolderKeysQualifying addObject: [NSDictionary dictionaryWithObject: subfolderKey
|
|
|
|
forKey: @"name"]];
|
|
|
|
}
|
|
|
|
filteredSubfolderKeys = [subfolderKeysQualifying filteredArrayUsingQualifier: qualifier];
|
|
|
|
|
|
|
|
max = [filteredSubfolderKeys count];
|
|
|
|
subfolderKeys = [NSMutableArray arrayWithCapacity: max];
|
|
|
|
for (count = 0; count < max; count++)
|
|
|
|
{
|
|
|
|
subfolderKey = [[filteredSubfolderKeys objectAtIndex: count] valueForKey: @"name"];
|
|
|
|
subfolderKey = [NSString stringWithFormat: @"folder%@", [[subfolderKey stringByEncodingImap4FolderName] asCSSIdentifier]];
|
|
|
|
[subfolderKeys addObject: subfolderKey];
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2012-02-14 15:58:34 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
subfolderKeys = nil;
|
2011-08-15 22:31:57 +02:00
|
|
|
|
|
|
|
return subfolderKeys;
|
2011-04-05 17:30:54 +02:00
|
|
|
}
|
|
|
|
|
2011-10-14 21:42:16 +02:00
|
|
|
- (NSDate *) creationTime
|
2011-06-07 02:17:46 +02:00
|
|
|
{
|
|
|
|
return [NSCalendarDate dateWithTimeIntervalSince1970: 0x4dbb2dbe]; /* oc_version_time */
|
|
|
|
}
|
|
|
|
|
2011-07-08 22:07:21 +02:00
|
|
|
- (NSDate *) lastMessageModificationTime
|
2011-06-07 02:17:46 +02:00
|
|
|
{
|
2011-07-28 02:52:38 +02:00
|
|
|
NSNumber *ti;
|
|
|
|
NSDate *value = nil;
|
|
|
|
|
2011-09-23 20:02:29 +02:00
|
|
|
[self synchroniseCache];
|
2011-07-28 02:52:38 +02:00
|
|
|
ti = [[versionsMessage properties]
|
|
|
|
objectForKey: @"SyncLastSynchronisationDate"];
|
|
|
|
if (ti)
|
|
|
|
value = [NSDate dateWithTimeIntervalSince1970: [ti doubleValue]];
|
|
|
|
else
|
|
|
|
value = [NSDate date];
|
|
|
|
|
2013-03-13 20:04:11 +01:00
|
|
|
//[self logWithFormat: @"lastMessageModificationTime: %@", value];
|
2011-07-28 02:52:38 +02:00
|
|
|
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
2011-11-18 16:26:03 +01:00
|
|
|
- (SOGoFolder *) aclFolder
|
|
|
|
{
|
|
|
|
return (SOGoFolder *) sogoObject;
|
|
|
|
}
|
|
|
|
|
2012-02-14 15:58:34 +01:00
|
|
|
- (NSArray *) permissionEntries
|
|
|
|
{
|
|
|
|
NSArray *permissionEntries;
|
|
|
|
|
|
|
|
if ([self ensureFolderExists])
|
|
|
|
permissionEntries = [super permissionEntries];
|
|
|
|
else
|
|
|
|
permissionEntries = nil;
|
|
|
|
|
|
|
|
return permissionEntries;
|
|
|
|
}
|
|
|
|
|
2012-08-13 16:08:49 +02:00
|
|
|
- (BOOL) supportsSubFolders
|
|
|
|
{
|
2012-09-26 17:50:28 +02:00
|
|
|
BOOL supportsSubFolders;
|
|
|
|
MAPIStoreUserContext *userContext;
|
|
|
|
|
|
|
|
if ([[self nameInContainer] isEqualToString: @"folderINBOX"])
|
|
|
|
{
|
|
|
|
userContext = [self userContext];
|
|
|
|
supportsSubFolders = ![userContext inboxHasNoInferiors];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
supportsSubFolders = YES;
|
|
|
|
|
|
|
|
return supportsSubFolders;
|
2012-08-13 16:08:49 +02:00
|
|
|
}
|
|
|
|
|
2011-07-28 02:52:38 +02:00
|
|
|
/* 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)
|
2011-10-28 18:09:51 +02:00
|
|
|
{
|
|
|
|
zeroNumber = [NSNumber numberWithUnsignedLongLong: 0];
|
|
|
|
[zeroNumber retain];
|
|
|
|
}
|
2011-07-28 02:52:38 +02:00
|
|
|
|
|
|
|
modseq1 = [entry1 objectForKey: @"modseq"];
|
|
|
|
if (!modseq1)
|
|
|
|
modseq1 = zeroNumber;
|
|
|
|
modseq2 = [entry2 objectForKey: @"modseq"];
|
|
|
|
if (!modseq2)
|
|
|
|
modseq2 = zeroNumber;
|
|
|
|
|
|
|
|
return [modseq1 compare: modseq2];
|
|
|
|
}
|
|
|
|
|
2011-09-20 21:37:03 +02:00
|
|
|
- (void) _setChangeKey: (NSData *) changeKey
|
|
|
|
forMessageEntry: (NSMutableDictionary *) messageEntry
|
|
|
|
{
|
|
|
|
struct XID *xid;
|
|
|
|
NSString *guid;
|
|
|
|
NSData *globCnt;
|
|
|
|
NSDictionary *changeKeyDict;
|
|
|
|
NSMutableDictionary *changeList;
|
|
|
|
|
|
|
|
xid = [changeKey asXIDInMemCtx: NULL];
|
|
|
|
guid = [NSString stringWithGUID: &xid->GUID];
|
|
|
|
globCnt = [NSData dataWithBytes: xid->Data length: xid->Size];
|
|
|
|
talloc_free (xid);
|
|
|
|
|
2011-09-22 15:39:41 +02:00
|
|
|
/* 1. set change key association */
|
2011-09-20 21:37:03 +02:00
|
|
|
changeKeyDict = [NSDictionary dictionaryWithObjectsAndKeys:
|
|
|
|
guid, @"GUID",
|
|
|
|
globCnt, @"LocalId",
|
|
|
|
nil];
|
|
|
|
[messageEntry setObject: changeKeyDict forKey: @"ChangeKey"];
|
|
|
|
|
|
|
|
/* 2. append/update predecessor change list */
|
|
|
|
changeList = [messageEntry objectForKey: @"PredecessorChangeList"];
|
|
|
|
if (!changeList)
|
|
|
|
{
|
|
|
|
changeList = [NSMutableDictionary new];
|
|
|
|
[messageEntry setObject: changeList
|
|
|
|
forKey: @"PredecessorChangeList"];
|
|
|
|
[changeList release];
|
|
|
|
}
|
|
|
|
[changeList setObject: globCnt forKey: guid];
|
|
|
|
}
|
|
|
|
|
2011-07-28 02:52:38 +02:00
|
|
|
- (BOOL) synchroniseCache
|
|
|
|
{
|
|
|
|
BOOL rc = YES;
|
|
|
|
uint64_t newChangeNum;
|
2012-10-09 22:39:24 +02:00
|
|
|
NSNumber *ti, *modseq, *initialLastModseq, *lastModseq,
|
|
|
|
*nextModseq;
|
2012-10-13 05:29:38 +02:00
|
|
|
NSString *changeNumber, *uid, *messageKey;
|
2011-09-23 20:02:29 +02:00
|
|
|
uint64_t lastModseqNbr;
|
2015-03-26 22:45:13 +01:00
|
|
|
EOQualifier *searchQualifier;
|
2012-10-13 05:29:38 +02:00
|
|
|
NSArray *uids, *changeNumbers;
|
2011-07-28 02:52:38 +02:00
|
|
|
NSUInteger count, max;
|
|
|
|
NSArray *fetchResults;
|
|
|
|
NSDictionary *result;
|
2011-09-20 21:37:03 +02:00
|
|
|
NSData *changeKey;
|
2012-10-13 05:29:38 +02:00
|
|
|
NSMutableArray *messageKeys;
|
2011-07-28 02:52:38 +02:00
|
|
|
NSMutableDictionary *currentProperties, *messages, *mapping, *messageEntry;
|
|
|
|
NSCalendarDate *now;
|
2011-09-24 02:10:26 +02:00
|
|
|
BOOL foundChange = NO;
|
2011-07-28 02:52:38 +02:00
|
|
|
|
2012-10-09 22:39:24 +02:00
|
|
|
/* NOTE: we are using NSString instance for "uid" and "changeNumber" because
|
|
|
|
NSNumber proved to give very bad performances when used as NSDictionary
|
|
|
|
keys with GNUstep 1.22.1. The bug seems to be solved with 1.24 but many
|
|
|
|
distros still ship an older version. */
|
2011-07-28 02:52:38 +02:00
|
|
|
now = [NSCalendarDate date];
|
|
|
|
[now setTimeZone: utcTZ];
|
|
|
|
|
2012-06-29 19:59:38 +02:00
|
|
|
[versionsMessage reloadIfNeeded];
|
|
|
|
currentProperties = [versionsMessage properties];
|
2011-07-28 02:52:38 +02:00
|
|
|
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"];
|
2011-10-10 06:01:35 +02:00
|
|
|
initialLastModseq = lastModseq;
|
2011-07-28 02:52:38 +02:00
|
|
|
if (lastModseq)
|
|
|
|
{
|
2011-09-23 20:02:29 +02:00
|
|
|
lastModseqNbr = [lastModseq unsignedLongLongValue];
|
|
|
|
nextModseq = [NSNumber numberWithUnsignedLongLong: lastModseqNbr + 1];
|
2015-03-26 22:45:13 +01:00
|
|
|
searchQualifier = [[EOKeyValueQualifier alloc]
|
2011-07-28 02:52:38 +02:00
|
|
|
initWithKey: @"modseq"
|
|
|
|
operatorSelector: EOQualifierOperatorGreaterThanOrEqualTo
|
|
|
|
value: nextModseq];
|
2015-03-26 22:45:13 +01:00
|
|
|
|
2011-07-28 02:52:38 +02:00
|
|
|
[searchQualifier autorelease];
|
|
|
|
}
|
|
|
|
else
|
2011-09-29 17:52:27 +02:00
|
|
|
{
|
|
|
|
lastModseqNbr = 0;
|
|
|
|
searchQualifier = [self nonDeletedQualifier];
|
|
|
|
}
|
2011-07-28 02:52:38 +02:00
|
|
|
|
2011-10-10 06:01:35 +02:00
|
|
|
/* 1. we fetch modified or added uids */
|
2011-07-28 02:52:38 +02:00
|
|
|
uids = [sogoObject fetchUIDsMatchingQualifier: searchQualifier
|
|
|
|
sortOrdering: nil];
|
|
|
|
max = [uids count];
|
|
|
|
if (max > 0)
|
|
|
|
{
|
2012-10-13 05:29:38 +02:00
|
|
|
messageKeys = [NSMutableArray arrayWithCapacity: max];
|
|
|
|
for (count = 0; count < max; count++)
|
|
|
|
{
|
|
|
|
messageKey = [NSString stringWithFormat: @"%@.eml",
|
|
|
|
[uids objectAtIndex: count]];
|
|
|
|
[messageKeys addObject: messageKey];
|
|
|
|
}
|
|
|
|
[self ensureIDsForChildKeys: messageKeys];
|
|
|
|
|
|
|
|
changeNumbers = [[self context] getNewChangeNumbers: max];
|
|
|
|
|
2011-07-28 02:52:38 +02:00
|
|
|
fetchResults
|
|
|
|
= [(NSDictionary *) [sogoObject fetchUIDs: uids
|
2015-03-26 22:45:13 +01:00
|
|
|
parts: [NSArray arrayWithObjects: @"modseq", @"flags", nil]]
|
2011-07-28 02:52:38 +02:00
|
|
|
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];
|
2011-08-31 20:11:09 +02:00
|
|
|
|
2011-07-28 02:52:38 +02:00
|
|
|
for (count = 0; count < max; count++)
|
|
|
|
{
|
|
|
|
result = [fetchResults objectAtIndex: count];
|
2012-10-09 22:39:24 +02:00
|
|
|
uid = [[result objectForKey: @"uid"] stringValue];
|
2011-07-28 02:52:38 +02:00
|
|
|
modseq = [result objectForKey: @"modseq"];
|
2012-10-13 05:29:38 +02:00
|
|
|
newChangeNum = [[changeNumbers objectAtIndex: count]
|
|
|
|
unsignedLongLongValue];
|
2012-10-09 22:39:24 +02:00
|
|
|
changeNumber = [NSString stringWithUnsignedLongLong: newChangeNum];
|
2011-07-28 02:52:38 +02:00
|
|
|
|
|
|
|
messageEntry = [NSMutableDictionary new];
|
|
|
|
[messages setObject: messageEntry forKey: uid];
|
|
|
|
[messageEntry release];
|
|
|
|
|
|
|
|
[messageEntry setObject: modseq forKey: @"modseq"];
|
|
|
|
[messageEntry setObject: changeNumber forKey: @"version"];
|
|
|
|
|
2013-03-13 20:04:11 +01:00
|
|
|
//[self logWithFormat: @"added message entry for uid %@, modseq %@,"
|
|
|
|
// @" version %@", uid, modseq, changeNumber];
|
2011-10-10 06:01:35 +02:00
|
|
|
|
2011-09-20 21:37:03 +02:00
|
|
|
changeKey = [self getReplicaKeyFromGlobCnt: newChangeNum >> 16];
|
|
|
|
[self _setChangeKey: changeKey forMessageEntry: messageEntry];
|
|
|
|
|
2011-07-28 02:52:38 +02:00
|
|
|
[mapping setObject: modseq forKey: changeNumber];
|
|
|
|
|
|
|
|
if (!lastModseq
|
|
|
|
|| ([lastModseq compare: modseq] == NSOrderedAscending))
|
|
|
|
lastModseq = modseq;
|
2015-03-26 22:45:13 +01:00
|
|
|
|
|
|
|
if ([[result objectForKey: @"flags"] containsObject: @"deleted"])
|
|
|
|
[currentProperties setObject: changeNumber
|
|
|
|
forKey: @"SyncLastDeleteChangeNumber"];
|
2011-07-28 02:52:38 +02:00
|
|
|
}
|
|
|
|
|
2011-10-10 06:01:35 +02:00
|
|
|
[currentProperties setObject: lastModseq forKey: @"SyncLastModseq"];
|
2011-09-24 02:10:26 +02:00
|
|
|
foundChange = YES;
|
2011-09-23 20:02:29 +02:00
|
|
|
}
|
|
|
|
|
2015-03-26 22:45:13 +01:00
|
|
|
/* 2. we synchronise expunged UIDs */
|
2011-10-10 06:01:35 +02:00
|
|
|
if (initialLastModseq)
|
2011-09-23 20:02:29 +02:00
|
|
|
{
|
2011-10-10 06:01:35 +02:00
|
|
|
fetchResults = [(SOGoMailFolder *) sogoObject
|
|
|
|
fetchUIDsOfVanishedItems: lastModseqNbr];
|
2015-03-26 22:45:13 +01:00
|
|
|
|
2011-10-10 06:01:35 +02:00
|
|
|
max = [fetchResults count];
|
2015-01-23 18:15:44 +01:00
|
|
|
|
2011-10-10 06:01:35 +02:00
|
|
|
changeNumber = nil;
|
|
|
|
for (count = 0; count < max; count++)
|
|
|
|
{
|
2014-08-04 13:56:22 +02:00
|
|
|
uid = [[fetchResults objectAtIndex: count] stringValue];
|
2011-10-10 06:01:35 +02:00
|
|
|
if ([messages objectForKey: uid])
|
|
|
|
{
|
2015-01-23 18:15:44 +01:00
|
|
|
if (!changeNumber)
|
|
|
|
{
|
|
|
|
newChangeNum = [[self context] getNewChangeNumber];
|
|
|
|
changeNumber = [NSString stringWithUnsignedLongLong: newChangeNum];
|
|
|
|
}
|
2011-10-10 06:01:35 +02:00
|
|
|
[messages removeObjectForKey: uid];
|
2014-08-04 13:56:30 +02:00
|
|
|
[self logWithFormat: @"Removed message entry for UID %@", uid];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
[self logWithFormat:@"Message entry not found for UID %@", uid];
|
2011-10-10 06:01:35 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (changeNumber)
|
|
|
|
{
|
|
|
|
[currentProperties setObject: changeNumber
|
|
|
|
forKey: @"SyncLastDeleteChangeNumber"];
|
2015-01-23 18:17:45 +01:00
|
|
|
[mapping setObject: lastModseq forKey: changeNumber];
|
2011-10-10 06:01:35 +02:00
|
|
|
foundChange = YES;
|
|
|
|
}
|
2011-09-23 20:02:29 +02:00
|
|
|
}
|
|
|
|
|
2011-09-24 02:10:26 +02:00
|
|
|
if (foundChange)
|
2011-09-23 20:02:29 +02:00
|
|
|
{
|
2014-11-06 17:42:45 +01:00
|
|
|
[self synchronizeUpdatedFolder: lastModseq
|
|
|
|
withMapping: mapping];
|
|
|
|
|
2011-07-28 02:52:38 +02:00
|
|
|
ti = [NSNumber numberWithDouble: [now timeIntervalSince1970]];
|
|
|
|
[currentProperties setObject: ti
|
|
|
|
forKey: @"SyncLastSynchronisationDate"];
|
|
|
|
[versionsMessage save];
|
|
|
|
}
|
|
|
|
|
2014-11-06 17:42:45 +01:00
|
|
|
|
2011-07-28 02:52:38 +02:00
|
|
|
return rc;
|
|
|
|
}
|
2014-11-06 17:42:45 +01:00
|
|
|
|
|
|
|
- (void) synchronizeUpdatedFolder: (NSNumber *) lastModseq
|
|
|
|
withMapping: (NSMutableDictionary *) mapping
|
|
|
|
{
|
|
|
|
/* This method should be called whenever something has changed on the folder.
|
|
|
|
Then we will perform two actions:
|
2014-12-01 23:19:38 +01:00
|
|
|
1 - Update the PidTagChangeNumber property of the root container.
|
2014-11-06 17:42:45 +01:00
|
|
|
2 - Store relationship PidTagChangenumber with lastModseq value on the
|
2014-12-01 23:19:38 +01:00
|
|
|
mapping given as parameter for this folder */
|
2014-11-06 17:42:45 +01:00
|
|
|
uint64_t *current_cn;
|
|
|
|
struct SRow row;
|
|
|
|
struct SPropValue prop;
|
|
|
|
uint64_t fid;
|
|
|
|
const char *username;
|
|
|
|
struct openchangedb_context *oc_ctx;
|
|
|
|
enum MAPISTATUS retval;
|
|
|
|
TALLOC_CTX *local_mem_ctx = NULL;
|
|
|
|
|
|
|
|
row.cValues = 1;
|
|
|
|
prop.ulPropTag = PidTagChangeNumber;
|
|
|
|
prop.value.d = 0; // It doesn't matter, it will be autogenerated
|
|
|
|
row.lpProps = ∝
|
|
|
|
|
2014-12-01 23:19:38 +01:00
|
|
|
/* We are doing a "touch" operation to update change number of the root container.
|
|
|
|
We get the root container as it has the properties in the OpenChange DB */
|
2014-11-06 17:42:45 +01:00
|
|
|
username = [[self context] connectionInfo]->username;
|
|
|
|
oc_ctx = [[self context] connectionInfo]->oc_ctx;
|
2014-12-01 23:19:38 +01:00
|
|
|
fid = [[self rootContainer] objectId];
|
2014-11-06 17:42:45 +01:00
|
|
|
retval = openchangedb_set_folder_properties(oc_ctx, username, fid, &row);
|
|
|
|
if (retval != MAPI_E_SUCCESS)
|
|
|
|
{
|
|
|
|
[self errorWithFormat:@"%s: Error setting change number on %"PRIu64,
|
|
|
|
__PRETTY_FUNCTION__, fid];
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
local_mem_ctx = talloc_named(NULL, 0, __PRETTY_FUNCTION__);
|
|
|
|
if (local_mem_ctx == NULL)
|
|
|
|
{
|
|
|
|
[self errorWithFormat:@"%s: Error with talloc_named, out of memory?",
|
|
|
|
__PRETTY_FUNCTION__];
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
retval = openchangedb_get_folder_property(local_mem_ctx, oc_ctx, username,
|
|
|
|
PidTagChangeNumber, fid,
|
|
|
|
(void **) ¤t_cn);
|
|
|
|
if (retval != MAPI_E_SUCCESS)
|
|
|
|
{
|
|
|
|
[self errorWithFormat:@"%s: Error getting change number on %"PRIu64,
|
|
|
|
__PRETTY_FUNCTION__, fid];
|
|
|
|
talloc_free(local_mem_ctx);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
[mapping setObject: lastModseq
|
|
|
|
forKey: [NSString stringWithUnsignedLongLong: *current_cn]];
|
|
|
|
talloc_free(local_mem_ctx);
|
|
|
|
}
|
|
|
|
|
2014-11-19 00:23:16 +01:00
|
|
|
- (BOOL) synchroniseCacheForUID: (NSString *) messageUID
|
|
|
|
{
|
|
|
|
/* Try to synchronise old UIDs in versions.plist cache using an
|
|
|
|
specific UID. It returns a boolean indicating if the
|
|
|
|
synchronisation were done.
|
|
|
|
|
|
|
|
It should be used as last resort, keeping synchroniseCache to main
|
|
|
|
sync entry point.
|
|
|
|
*/
|
|
|
|
NSMutableDictionary *currentProperties, *messages, *messageEntry, *mapping;
|
|
|
|
NSArray *fetchResults;
|
|
|
|
uint64_t changeNumber;
|
|
|
|
NSDictionary *result;
|
|
|
|
NSNumber *modseq;
|
|
|
|
NSString *changeNumberStr;
|
|
|
|
NSData *changeKey;
|
|
|
|
|
|
|
|
[versionsMessage reloadIfNeeded];
|
|
|
|
currentProperties = [versionsMessage properties];
|
|
|
|
messages = [currentProperties objectForKey: @"Messages"];
|
|
|
|
messageEntry = [messages objectForKey: messageUID];
|
|
|
|
if (!messageEntry)
|
|
|
|
{
|
|
|
|
fetchResults = [(NSDictionary *) [sogoObject fetchUIDs: [NSArray arrayWithObject: messageUID]
|
2015-03-26 22:45:13 +01:00
|
|
|
parts: [NSArray arrayWithObjects: @"modseq", @"flags", nil]]
|
2014-11-19 00:23:16 +01:00
|
|
|
objectForKey: @"fetch"];
|
|
|
|
if ([fetchResults count] == 1)
|
|
|
|
{
|
|
|
|
result = [fetchResults objectAtIndex: 0];
|
|
|
|
modseq = [result objectForKey: @"modseq"];
|
2015-01-23 18:18:25 +01:00
|
|
|
changeNumber = [[self context] getNewChangeNumber];
|
2014-11-19 00:23:16 +01:00
|
|
|
changeNumberStr = [NSString stringWithUnsignedLongLong: changeNumber];
|
|
|
|
|
|
|
|
/* Create new message entry in Messages dict */
|
|
|
|
messageEntry = [NSMutableDictionary new];
|
|
|
|
[messages setObject: messageEntry forKey: messageUID];
|
|
|
|
[messageEntry release];
|
|
|
|
|
|
|
|
/* Store the modseq and change number */
|
|
|
|
[messageEntry setObject: modseq forKey: @"modseq"];
|
|
|
|
[messageEntry setObject: changeNumberStr forKey: @"version"];
|
|
|
|
|
|
|
|
/* Store the change key */
|
|
|
|
changeKey = [self getReplicaKeyFromGlobCnt: changeNumber >> 16];
|
|
|
|
[self _setChangeKey: changeKey forMessageEntry: messageEntry];
|
|
|
|
|
|
|
|
/* Store the changeNumber -> modseq mapping */
|
|
|
|
mapping = [currentProperties objectForKey: @"VersionMapping"];
|
|
|
|
[mapping setObject: modseq forKey: changeNumberStr];
|
|
|
|
|
2015-03-26 22:45:13 +01:00
|
|
|
/* Store the last deleted change number if it is soft-deleted */
|
|
|
|
if ([[result objectForKey: @"flags"] containsObject: @"deleted"])
|
|
|
|
[currentProperties setObject: changeNumberStr
|
|
|
|
forKey: @"SyncLastDeleteChangeNumber"];
|
|
|
|
|
2014-11-19 00:23:16 +01:00
|
|
|
/* Save the message */
|
|
|
|
[versionsMessage save];
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* If message entry exists, then synchroniseCache did its job */
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
2015-01-23 18:19:24 +01:00
|
|
|
|
2012-10-09 22:39:24 +02:00
|
|
|
- (NSNumber *) modseqFromMessageChangeNumber: (NSString *) changeNum
|
2011-07-28 02:52:38 +02:00
|
|
|
{
|
|
|
|
NSDictionary *mapping;
|
|
|
|
NSNumber *modseq;
|
2015-01-23 18:19:24 +01:00
|
|
|
NSEnumerator *enumerator;
|
|
|
|
id key;
|
|
|
|
uint64_t found, target, current, replica_id, current_cn;
|
|
|
|
NSString *closestChangeNum;
|
2011-07-28 02:52:38 +02:00
|
|
|
|
|
|
|
mapping = [[versionsMessage properties] objectForKey: @"VersionMapping"];
|
|
|
|
modseq = [mapping objectForKey: changeNum];
|
2015-01-23 18:19:24 +01:00
|
|
|
if (modseq) return modseq;
|
|
|
|
|
|
|
|
// Not found from stored change numbers for this folder.
|
|
|
|
// Get the closest modseq for the change number given.
|
|
|
|
// O(n) cost but will be unusual behaviour.
|
|
|
|
target = exchange_globcnt([changeNum unsignedLongLongValue] >> 16);
|
|
|
|
replica_id = [changeNum unsignedLongLongValue] & 0xFFFF;
|
|
|
|
found = 0;
|
|
|
|
enumerator = [mapping keyEnumerator];
|
|
|
|
while ((key = [enumerator nextObject]))
|
|
|
|
{
|
|
|
|
current_cn = [(NSString *)key unsignedLongLongValue];
|
|
|
|
if ((current_cn & 0xFFFF) != replica_id)
|
|
|
|
continue;
|
|
|
|
current = exchange_globcnt(current_cn >> 16);
|
|
|
|
if (current < target && current > found)
|
|
|
|
found = current;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (found)
|
|
|
|
{
|
|
|
|
closestChangeNum = [NSString stringWithUnsignedLongLong:
|
|
|
|
(exchange_globcnt(found) << 16 | replica_id)];
|
|
|
|
modseq = [mapping objectForKey: closestChangeNum];
|
|
|
|
}
|
2011-07-28 02:52:38 +02:00
|
|
|
|
|
|
|
return modseq;
|
|
|
|
}
|
|
|
|
|
2012-10-09 22:39:24 +02:00
|
|
|
- (NSString *) messageUIDFromMessageKey: (NSString *) messageKey
|
2011-07-28 02:52:38 +02:00
|
|
|
{
|
2012-10-09 22:39:24 +02:00
|
|
|
NSString *messageUid;
|
2011-07-28 02:52:38 +02:00
|
|
|
NSRange dotRange;
|
|
|
|
|
|
|
|
dotRange = [messageKey rangeOfString: @".eml"];
|
|
|
|
if (dotRange.location != NSNotFound)
|
2012-10-09 22:39:24 +02:00
|
|
|
messageUid = [messageKey substringToIndex: dotRange.location];
|
2011-07-28 02:52:38 +02:00
|
|
|
else
|
2014-08-04 13:56:30 +02:00
|
|
|
{
|
|
|
|
messageUid = nil;
|
|
|
|
[self errorWithFormat:@"%s: Unexpected messageKey value [%@]",
|
|
|
|
__PRETTY_FUNCTION__, messageKey];
|
|
|
|
}
|
2011-07-28 02:52:38 +02:00
|
|
|
|
|
|
|
return messageUid;
|
|
|
|
}
|
|
|
|
|
2012-10-09 22:39:24 +02:00
|
|
|
- (NSString *) changeNumberForMessageUID: (NSString *) messageUid
|
2011-07-28 02:52:38 +02:00
|
|
|
{
|
|
|
|
NSDictionary *messages;
|
2012-10-09 22:39:24 +02:00
|
|
|
NSString *changeNumber;
|
2011-07-28 02:52:38 +02:00
|
|
|
|
|
|
|
messages = [[versionsMessage properties] objectForKey: @"Messages"];
|
|
|
|
changeNumber = [[messages objectForKey: messageUid]
|
|
|
|
objectForKey: @"version"];
|
|
|
|
|
|
|
|
return changeNumber;
|
2011-06-07 02:17:46 +02:00
|
|
|
}
|
|
|
|
|
2011-09-20 21:37:03 +02:00
|
|
|
- (void) setChangeKey: (NSData *) changeKey
|
|
|
|
forMessageWithKey: (NSString *) messageKey
|
|
|
|
{
|
2012-10-09 22:39:24 +02:00
|
|
|
NSMutableDictionary *messages, *messageEntry;
|
|
|
|
NSString *messageUid;
|
2014-11-29 08:07:41 +01:00
|
|
|
BOOL synced;
|
2011-09-20 21:37:03 +02:00
|
|
|
|
|
|
|
messageUid = [self messageUIDFromMessageKey: messageKey];
|
|
|
|
messages = [[versionsMessage properties] objectForKey: @"Messages"];
|
|
|
|
messageEntry = [messages objectForKey: messageUid];
|
|
|
|
if (!messageEntry)
|
2014-11-29 08:07:41 +01:00
|
|
|
{
|
|
|
|
[self warnWithFormat: @"attempting to synchronise to set the change key for "
|
|
|
|
@"this message %@", messageKey];
|
|
|
|
synced = [self synchroniseCacheForUID: messageUid];
|
|
|
|
if (synced)
|
|
|
|
messageEntry = [[[versionsMessage properties] objectForKey: @"Messages"] objectForKey: messageUid];
|
2014-12-01 10:47:16 +01:00
|
|
|
if (!messageEntry)
|
|
|
|
{
|
|
|
|
[self errorWithFormat: @"still nothing. We crash!"];
|
|
|
|
abort ();
|
|
|
|
}
|
2014-11-29 08:07:41 +01:00
|
|
|
}
|
2011-09-20 21:37:03 +02:00
|
|
|
[self _setChangeKey: changeKey forMessageEntry: messageEntry];
|
|
|
|
|
|
|
|
[versionsMessage save];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSData *) changeKeyForMessageWithKey: (NSString *) messageKey
|
|
|
|
{
|
|
|
|
NSDictionary *messages, *changeKeyDict;
|
2012-10-09 22:39:24 +02:00
|
|
|
NSString *guid, *messageUid;
|
2011-09-20 21:37:03 +02:00
|
|
|
NSData *globCnt, *changeKey = nil;
|
|
|
|
|
|
|
|
messageUid = [self messageUIDFromMessageKey: messageKey];
|
|
|
|
messages = [[versionsMessage properties] objectForKey: @"Messages"];
|
|
|
|
changeKeyDict = [[messages objectForKey: messageUid]
|
|
|
|
objectForKey: @"ChangeKey"];
|
|
|
|
if (changeKeyDict)
|
|
|
|
{
|
|
|
|
guid = [changeKeyDict objectForKey: @"GUID"];
|
|
|
|
globCnt = [changeKeyDict objectForKey: @"LocalId"];
|
2012-03-16 21:51:51 +01:00
|
|
|
changeKey = [NSData dataWithChangeKeyGUID: guid andCnt: globCnt];
|
2011-09-20 21:37:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return changeKey;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSData *) predecessorChangeListForMessageWithKey: (NSString *) messageKey
|
|
|
|
{
|
2012-03-16 22:09:10 +01:00
|
|
|
NSMutableData *list = nil;
|
2011-09-20 21:37:03 +02:00
|
|
|
NSDictionary *messages, *changeListDict;
|
|
|
|
NSArray *keys;
|
2012-03-16 22:09:10 +01:00
|
|
|
NSMutableArray *changeKeys;
|
2011-09-20 21:37:03 +02:00
|
|
|
NSUInteger count, max;
|
|
|
|
NSData *changeKey;
|
2012-10-09 22:39:24 +02:00
|
|
|
NSString *guid, *messageUid;
|
2011-09-20 21:37:03 +02:00
|
|
|
NSData *globCnt;
|
|
|
|
|
|
|
|
messageUid = [self messageUIDFromMessageKey: messageKey];
|
|
|
|
messages = [[versionsMessage properties] objectForKey: @"Messages"];
|
|
|
|
changeListDict = [[messages objectForKey: messageUid]
|
|
|
|
objectForKey: @"PredecessorChangeList"];
|
|
|
|
if (changeListDict)
|
|
|
|
{
|
|
|
|
keys = [changeListDict allKeys];
|
|
|
|
max = [keys count];
|
|
|
|
|
2012-03-16 22:09:10 +01:00
|
|
|
changeKeys = [NSMutableArray arrayWithCapacity: max];
|
2011-09-20 21:37:03 +02:00
|
|
|
for (count = 0; count < max; count++)
|
|
|
|
{
|
|
|
|
guid = [keys objectAtIndex: count];
|
|
|
|
globCnt = [changeListDict objectForKey: guid];
|
2012-03-16 21:51:51 +01:00
|
|
|
changeKey = [NSData dataWithChangeKeyGUID: guid andCnt: globCnt];
|
2012-03-16 22:09:10 +01:00
|
|
|
[changeKeys addObject: changeKey];
|
|
|
|
}
|
|
|
|
[changeKeys sortUsingFunction: MAPIChangeKeyGUIDCompare
|
|
|
|
context: nil];
|
|
|
|
|
|
|
|
list = [NSMutableData data];
|
|
|
|
for (count = 0; count < max; count++)
|
|
|
|
{
|
|
|
|
changeKey = [changeKeys objectAtIndex: count];
|
|
|
|
[list appendUInt8: [changeKey length]];
|
|
|
|
[list appendData: changeKey];
|
2011-09-20 21:37:03 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-03-16 22:09:10 +01:00
|
|
|
return list;
|
2011-09-20 21:37:03 +02:00
|
|
|
}
|
|
|
|
|
2015-03-18 23:54:10 +01:00
|
|
|
/* Management for extra properties once they already hit the IMAP server */
|
|
|
|
- (void) setExtraProperties: (NSDictionary *) props
|
|
|
|
forMessage: (NSString *) messageKey
|
|
|
|
{
|
|
|
|
NSMutableDictionary *extraProps, *currentProperties;
|
|
|
|
NSString *messageUid;
|
|
|
|
|
|
|
|
messageUid = [self messageUIDFromMessageKey: messageKey];
|
|
|
|
currentProperties = [versionsMessage properties];
|
|
|
|
extraProps = [currentProperties objectForKey: @"ExtraMessagesProperties"];
|
|
|
|
if (!extraProps)
|
|
|
|
{
|
|
|
|
extraProps = [NSMutableDictionary new];
|
|
|
|
[currentProperties setObject: extraProps forKey: @"ExtraMessagesProperties"];
|
|
|
|
[extraProps release];
|
|
|
|
}
|
|
|
|
|
|
|
|
[extraProps setObject: props
|
|
|
|
forKey: messageUid];
|
|
|
|
[versionsMessage save];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSDictionary *) extraPropertiesForMessage: (NSString *) messageKey
|
|
|
|
{
|
|
|
|
NSString *messageUid;
|
|
|
|
|
|
|
|
messageUid = [self messageUIDFromMessageKey: messageKey];
|
|
|
|
return [[[versionsMessage properties] objectForKey: @"ExtraMessagesProperties"]
|
|
|
|
objectForKey: messageUid];
|
|
|
|
}
|
|
|
|
|
2011-07-29 04:13:39 +02:00
|
|
|
- (NSArray *) getDeletedKeysFromChangeNumber: (uint64_t) changeNum
|
|
|
|
andCN: (NSNumber **) cnNbr
|
|
|
|
inTableType: (uint8_t) tableType
|
|
|
|
{
|
|
|
|
NSArray *deletedKeys, *deletedUIDs;
|
2012-10-09 22:39:24 +02:00
|
|
|
NSString *changeNumber;
|
2011-07-29 04:13:39 +02:00
|
|
|
uint64_t modseq;
|
2011-10-10 06:01:35 +02:00
|
|
|
NSDictionary *versionProperties;
|
2015-03-26 22:05:43 +01:00
|
|
|
EOQualifier *deletedQualifier, *kvQualifier, *searchQualifier;
|
2011-07-29 04:13:39 +02:00
|
|
|
|
|
|
|
if (tableType == MAPISTORE_MESSAGE_TABLE)
|
|
|
|
{
|
2013-02-14 21:59:19 +01:00
|
|
|
changeNumber = [NSString stringWithFormat: @"0x%.16llx", changeNum];
|
2011-10-10 06:01:35 +02:00
|
|
|
modseq = [[self modseqFromMessageChangeNumber: changeNumber]
|
2011-07-29 04:13:39 +02:00
|
|
|
unsignedLongLongValue];
|
|
|
|
if (modseq > 0)
|
|
|
|
{
|
2015-03-26 22:05:43 +01:00
|
|
|
/* Hard deleted items */
|
2011-10-09 19:50:47 +02:00
|
|
|
deletedUIDs = [(SOGoMailFolder *) sogoObject
|
2012-10-09 22:39:24 +02:00
|
|
|
fetchUIDsOfVanishedItems: modseq];
|
2015-03-26 22:05:43 +01:00
|
|
|
|
|
|
|
/* Soft deleted items */
|
|
|
|
kvQualifier = [[EOKeyValueQualifier alloc]
|
|
|
|
initWithKey: @"modseq"
|
|
|
|
operatorSelector: EOQualifierOperatorGreaterThanOrEqualTo
|
|
|
|
value: [NSNumber numberWithUnsignedLongLong: modseq]];
|
|
|
|
deletedQualifier
|
|
|
|
= [[EOKeyValueQualifier alloc]
|
|
|
|
initWithKey: @"FLAGS"
|
|
|
|
operatorSelector: EOQualifierOperatorContains
|
|
|
|
value: [NSArray arrayWithObject: @"Deleted"]];
|
|
|
|
|
|
|
|
searchQualifier = [[EOAndQualifier alloc]
|
|
|
|
initWithQualifiers:
|
|
|
|
kvQualifier, deletedQualifier, nil];
|
|
|
|
|
|
|
|
deletedUIDs = [deletedUIDs arrayByAddingObjectsFromArray:
|
|
|
|
[sogoObject fetchUIDsMatchingQualifier: searchQualifier
|
|
|
|
sortOrdering: nil]];
|
|
|
|
|
|
|
|
[deletedQualifier release];
|
|
|
|
[kvQualifier release];
|
|
|
|
[searchQualifier release];
|
|
|
|
|
2011-10-09 19:50:47 +02:00
|
|
|
deletedKeys = [deletedUIDs stringsWithFormat: @"%@.eml"];
|
2011-10-10 06:01:35 +02:00
|
|
|
if ([deletedUIDs count] > 0)
|
2011-07-29 04:13:39 +02:00
|
|
|
{
|
2011-10-10 06:01:35 +02:00
|
|
|
versionProperties = [versionsMessage properties];
|
|
|
|
changeNumber = [versionProperties
|
|
|
|
objectForKey: @"SyncLastDeleteChangeNumber"];
|
2012-10-09 22:39:24 +02:00
|
|
|
*cnNbr = [NSNumber numberWithUnsignedLongLong:
|
|
|
|
[changeNumber unsignedLongLongValue]];
|
2011-10-09 19:50:47 +02:00
|
|
|
[versionsMessage save];
|
2011-07-29 04:13:39 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
deletedKeys = [NSArray array];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
deletedKeys = [super getDeletedKeysFromChangeNumber: changeNum
|
|
|
|
andCN: cnNbr
|
|
|
|
inTableType: tableType];
|
|
|
|
|
|
|
|
return deletedKeys;
|
|
|
|
}
|
|
|
|
|
2011-09-21 21:33:35 +02:00
|
|
|
static void
|
|
|
|
_appendIMAPRange (NSMutableArray *UIDs, uint32_t low, uint32_t high)
|
2011-09-14 20:33:44 +02:00
|
|
|
{
|
2011-09-21 21:33:35 +02:00
|
|
|
uint32_t count;
|
|
|
|
|
|
|
|
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;
|
2011-09-14 20:33:44 +02:00
|
|
|
|
2015-01-19 15:35:44 +01:00
|
|
|
rangeMin = 0;
|
|
|
|
currentUid = 0;
|
2011-09-21 21:33:35 +02:00
|
|
|
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];
|
2012-01-10 18:35:01 +01:00
|
|
|
uniString = NSZoneMalloc (NULL, sizeof (unichar) * (max + 1));
|
2011-09-21 21:33:35 +02:00
|
|
|
[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);
|
2011-09-14 20:33:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//
|
2011-09-22 15:39:41 +02:00
|
|
|
// Move (or eventually copy) the mails identified by
|
|
|
|
// "srcMids" from the source folder into this folder.
|
2011-09-14 20:33:44 +02:00
|
|
|
//
|
2011-09-21 21:33:35 +02:00
|
|
|
- (int) moveCopyMessagesWithMIDs: (uint64_t *) srcMids
|
|
|
|
andCount: (uint32_t) midCount
|
2011-09-22 15:39:41 +02:00
|
|
|
fromFolder: (MAPIStoreFolder *) sourceFolder
|
2011-09-21 21:33:35 +02:00
|
|
|
withMIDs: (uint64_t *) targetMids
|
2011-09-27 20:26:40 +02:00
|
|
|
andChangeKeys: (struct Binary_r **) targetChangeKeys
|
2011-09-22 15:39:41 +02:00
|
|
|
wantCopy: (uint8_t) wantCopy
|
2012-12-03 15:18:17 +01:00
|
|
|
inMemCtx: (TALLOC_CTX *) memCtx
|
|
|
|
|
2011-09-14 20:33:44 +02:00
|
|
|
{
|
2011-09-21 21:33:35 +02:00
|
|
|
NGImap4Connection *connection;
|
|
|
|
NGImap4Client *client;
|
2012-10-09 22:39:24 +02:00
|
|
|
NSString *sourceFolderName, *targetFolderName, *messageURL, *messageKey,
|
|
|
|
*uid, *v;
|
2011-09-21 21:33:35 +02:00
|
|
|
NSMutableArray *uids, *oldMessageURLs;
|
|
|
|
NSArray *destUIDs;
|
2011-09-14 20:33:44 +02:00
|
|
|
MAPIStoreMapping *mapping;
|
|
|
|
NSDictionary *result;
|
2011-09-22 17:54:47 +02:00
|
|
|
NSUInteger count;
|
|
|
|
NSArray *a;
|
2012-02-29 05:15:42 +01:00
|
|
|
NSData *changeKey;
|
2011-09-14 20:33:44 +02:00
|
|
|
|
2011-09-22 15:39:41 +02:00
|
|
|
if (![sourceFolder isKindOfClass: [MAPIStoreMailFolder class]])
|
2011-09-22 17:39:53 +02:00
|
|
|
return [super moveCopyMessagesWithMIDs: srcMids andCount: midCount
|
|
|
|
fromFolder: sourceFolder withMIDs: targetMids
|
2011-09-27 20:26:40 +02:00
|
|
|
andChangeKeys: targetChangeKeys
|
2012-12-03 15:18:17 +01:00
|
|
|
wantCopy: wantCopy
|
|
|
|
inMemCtx: memCtx];
|
2011-09-21 21:33:35 +02:00
|
|
|
|
|
|
|
/* Conversion of mids to IMAP uids */
|
2012-01-29 20:55:21 +01:00
|
|
|
mapping = [self mapping];
|
2011-09-21 21:33:35 +02:00
|
|
|
uids = [NSMutableArray arrayWithCapacity: midCount];
|
|
|
|
oldMessageURLs = [NSMutableArray arrayWithCapacity: midCount];
|
|
|
|
for (count = 0; count < midCount; count++)
|
|
|
|
{
|
|
|
|
messageURL = [mapping urlFromID: srcMids[count]];
|
|
|
|
if (messageURL)
|
|
|
|
{
|
2011-09-22 15:39:41 +02:00
|
|
|
uid = [self messageUIDFromMessageKey: [messageURL lastPathComponent]];
|
2011-09-21 21:33:35 +02:00
|
|
|
[uids addObject: uid];
|
2011-09-15 02:59:36 +02:00
|
|
|
|
2011-09-21 21:33:35 +02:00
|
|
|
[oldMessageURLs addObject: messageURL];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return MAPISTORE_ERROR;
|
|
|
|
}
|
2011-09-14 20:33:44 +02:00
|
|
|
|
2011-09-21 21:33:35 +02:00
|
|
|
/* IMAP COPY */
|
|
|
|
connection = [sogoObject imap4Connection];
|
|
|
|
sourceFolderName = [connection
|
2011-09-22 15:39:41 +02:00
|
|
|
imap4FolderNameForURL: [[sourceFolder sogoObject] imap4URL]];
|
2011-09-21 21:33:35 +02:00
|
|
|
targetFolderName = [connection
|
2011-09-22 15:39:41 +02:00
|
|
|
imap4FolderNameForURL: [sogoObject imap4URL]];
|
2011-09-21 21:33:35 +02:00
|
|
|
|
|
|
|
client = [connection client];
|
|
|
|
[client select: sourceFolderName];
|
|
|
|
result = [client copyUids: uids toFolder: targetFolderName];
|
|
|
|
if (![[result objectForKey: @"result"] boolValue])
|
|
|
|
return MAPISTORE_ERROR;
|
|
|
|
|
2015-03-12 20:24:06 +01:00
|
|
|
/* "Move" treatment: Store \Deleted and unregister urls as soft-deleted */
|
2011-09-22 15:39:41 +02:00
|
|
|
if (!wantCopy)
|
2011-09-21 21:33:35 +02:00
|
|
|
{
|
|
|
|
[client storeFlags: [NSArray arrayWithObject: @"Deleted"] forUIDs: uids
|
|
|
|
addOrRemove: YES];
|
|
|
|
for (count = 0; count < midCount; count++)
|
2015-03-12 20:24:06 +01:00
|
|
|
{
|
|
|
|
/* Using soft-deleted to make deleted fmids to return the
|
|
|
|
srcMids.
|
|
|
|
See [MAPIStoreFolder getDeletedFMIDs:andCN:fromChangeNumber:inTableType:inMemCtx]
|
|
|
|
for details */
|
|
|
|
[mapping unregisterURLWithID: srcMids[count]
|
|
|
|
andFlags: MAPISTORE_SOFT_DELETE];
|
|
|
|
}
|
2011-09-21 21:33:35 +02:00
|
|
|
}
|
2011-09-14 20:33:44 +02:00
|
|
|
|
2011-09-21 21:33:35 +02:00
|
|
|
/* Registration of target messages */
|
|
|
|
//
|
2011-10-07 17:47:13 +02:00
|
|
|
// We use the UIDPLUS IMAP extension here in order to speedup UID retrieval
|
2011-09-21 21:33:35 +02:00
|
|
|
// 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)
|
2011-09-29 17:52:27 +02:00
|
|
|
{
|
|
|
|
destUIDs = nil;
|
|
|
|
_parseCOPYUID (v, &destUIDs);
|
|
|
|
}
|
2011-09-21 21:33:35 +02:00
|
|
|
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",
|
2011-09-22 15:39:41 +02:00
|
|
|
[self url],
|
2011-09-21 21:33:35 +02:00
|
|
|
[destUIDs objectAtIndex: count]];
|
|
|
|
[mapping registerURL: messageURL withID: targetMids[count]];
|
|
|
|
}
|
|
|
|
|
2012-02-29 05:15:42 +01:00
|
|
|
/* Update the change keys */
|
2012-08-14 22:15:15 +02:00
|
|
|
if (targetChangeKeys)
|
2012-02-29 05:15:42 +01:00
|
|
|
{
|
2012-08-14 22:15:15 +02:00
|
|
|
[self synchroniseCache];
|
|
|
|
for (count = 0; count < midCount; count++)
|
|
|
|
{
|
|
|
|
changeKey = [NSData dataWithBinary: targetChangeKeys[count]];
|
|
|
|
messageKey = [NSString stringWithFormat: @"%@.eml",
|
|
|
|
[destUIDs objectAtIndex: count]];
|
|
|
|
[self setChangeKey: changeKey
|
|
|
|
forMessageWithKey: messageKey];
|
|
|
|
}
|
2012-02-29 05:15:42 +01:00
|
|
|
}
|
|
|
|
|
2011-09-21 21:33:35 +02:00
|
|
|
// We cleanup cache of our source and destination folders
|
|
|
|
[self cleanupCaches];
|
2011-09-22 15:39:41 +02:00
|
|
|
[sourceFolder cleanupCaches];
|
2011-09-21 21:33:35 +02:00
|
|
|
|
|
|
|
return MAPISTORE_SUCCESS;
|
2011-09-14 20:33:44 +02:00
|
|
|
}
|
|
|
|
|
2012-08-15 03:02:08 +02:00
|
|
|
- (enum mapistore_error) moveCopyToFolder: (MAPIStoreFolder *) targetFolder
|
|
|
|
withNewName: (NSString *) newFolderName
|
|
|
|
isMove: (BOOL) isMove
|
|
|
|
isRecursive: (BOOL) isRecursive
|
2012-12-07 19:30:50 +01:00
|
|
|
inMemCtx: (TALLOC_CTX *) memCtx
|
2012-08-09 23:35:09 +02:00
|
|
|
{
|
|
|
|
enum mapistore_error rc;
|
|
|
|
NSURL *folderURL, *newFolderURL;
|
2012-08-15 15:13:15 +02:00
|
|
|
struct SRow folderRow;
|
|
|
|
struct SPropValue nameProperty;
|
|
|
|
MAPIStoreMailFolder *newFolder;
|
2015-02-05 23:48:46 +01:00
|
|
|
SOGoMailAccount *accountFolder;
|
2012-08-10 16:08:40 +02:00
|
|
|
SOGoMailFolder *targetSOGoFolder;
|
2012-08-15 15:13:15 +02:00
|
|
|
NSMutableArray *uids;
|
|
|
|
NSArray *childKeys;
|
|
|
|
NSUInteger count, max;
|
|
|
|
NGImap4Connection *connection;
|
|
|
|
NGImap4Client *client;
|
2012-09-07 18:46:18 +02:00
|
|
|
NSString *newURL, *parentDBFolderPath, *childKey, *folderIMAPName,
|
2015-01-26 15:55:22 +01:00
|
|
|
*urlNamePart, *newFolderIMAPName, *newFolderDBName;
|
2012-08-09 23:35:09 +02:00
|
|
|
NSException *error;
|
2012-08-13 05:58:14 +02:00
|
|
|
MAPIStoreMapping *mapping;
|
2012-08-15 15:13:15 +02:00
|
|
|
NSDictionary *result;
|
2012-08-09 23:35:09 +02:00
|
|
|
|
2015-02-05 23:48:46 +01:00
|
|
|
if ([targetFolder isKindOfClass: MAPIStoreMailFolderK] || (!targetFolder && isMove))
|
2012-08-09 23:35:09 +02:00
|
|
|
{
|
2012-08-10 16:08:40 +02:00
|
|
|
folderURL = [sogoObject imap4URL];
|
|
|
|
if (!newFolderName)
|
2012-08-15 03:02:08 +02:00
|
|
|
newFolderName = [sogoObject displayName];
|
2012-08-10 16:08:40 +02:00
|
|
|
targetSOGoFolder = [targetFolder sogoObject];
|
2012-08-15 15:13:15 +02:00
|
|
|
if (isMove)
|
|
|
|
{
|
2015-02-05 23:48:46 +01:00
|
|
|
newFolderDBName = [[newFolderName stringByEncodingImap4FolderName] asCSSIdentifier];
|
|
|
|
if (targetSOGoFolder)
|
|
|
|
{
|
|
|
|
/* Mimetise [SOGoMailFolderK imap4URLString] */
|
|
|
|
urlNamePart = [[newFolderName stringByEncodingImap4FolderName] stringByEscapingURL];
|
|
|
|
newFolderURL = [NSURL URLWithString: urlNamePart
|
|
|
|
relativeToURL: [targetSOGoFolder imap4URL]];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Mimetise what createRootSecondaryFolderWithFID does */
|
|
|
|
accountFolder = [[[self userContext] rootFolders] objectForKey: @"mail"];
|
|
|
|
targetSOGoFolder = [SOGoMailFolder objectWithName: [NSString stringWithFormat: @"folder%@",
|
|
|
|
newFolderDBName]
|
|
|
|
inContainer: accountFolder];
|
|
|
|
newFolderURL = [targetSOGoFolder imap4URL];
|
|
|
|
}
|
2012-08-15 15:13:15 +02:00
|
|
|
error = [[sogoObject imap4Connection]
|
2012-08-17 19:56:25 +02:00
|
|
|
moveMailboxAtURL: folderURL
|
|
|
|
toURL: newFolderURL];
|
2012-08-15 15:13:15 +02:00
|
|
|
if (error)
|
|
|
|
rc = MAPISTORE_ERR_DENIED;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
rc = MAPISTORE_SUCCESS;
|
|
|
|
mapping = [self mapping];
|
2015-02-05 23:48:46 +01:00
|
|
|
if (targetFolder)
|
|
|
|
newURL = [NSString stringWithFormat: @"%@folder%@/",
|
|
|
|
[targetFolder url], newFolderDBName];
|
|
|
|
else
|
|
|
|
newURL = [NSString stringWithFormat: @"sogo://%@:%@@mail/folder%@/",
|
|
|
|
[[self userContext] username], [[self userContext] username],
|
|
|
|
newFolderDBName];
|
2012-08-17 19:56:25 +02:00
|
|
|
[mapping updateID: [self objectId] withURL: newURL];
|
2015-02-05 23:48:46 +01:00
|
|
|
if (targetFolder)
|
|
|
|
{
|
|
|
|
parentDBFolderPath = [[targetFolder dbFolder] path];
|
|
|
|
if (!parentDBFolderPath)
|
|
|
|
parentDBFolderPath = @"";
|
|
|
|
[dbFolder changePathTo: [NSString stringWithFormat:
|
|
|
|
@"%@/folder%@",
|
|
|
|
parentDBFolderPath,
|
|
|
|
newFolderDBName]
|
|
|
|
intoNewContainer: [targetFolder dbFolder]];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
[dbFolder changePathTo: [NSString stringWithFormat:
|
|
|
|
@"/mail/folder%@", newFolderDBName]
|
|
|
|
intoNewContainer: nil];
|
2012-08-15 15:13:15 +02:00
|
|
|
}
|
|
|
|
}
|
2012-08-09 23:35:09 +02:00
|
|
|
else
|
2012-08-13 05:58:14 +02:00
|
|
|
{
|
2012-08-15 15:13:15 +02:00
|
|
|
nameProperty.ulPropTag = PidTagDisplayName;
|
|
|
|
nameProperty.value.lpszW = [newFolderName UTF8String];
|
|
|
|
folderRow.lpProps = &nameProperty;
|
|
|
|
folderRow.cValues = 1;
|
|
|
|
rc = [targetFolder createFolder: &folderRow
|
|
|
|
withFID: -1
|
|
|
|
andKey: &childKey];
|
|
|
|
if (rc == MAPISTORE_SUCCESS)
|
|
|
|
{
|
|
|
|
newFolder = [targetFolder lookupFolder: childKey];
|
|
|
|
|
|
|
|
connection = [sogoObject imap4Connection];
|
|
|
|
folderIMAPName = [connection
|
|
|
|
imap4FolderNameForURL: [sogoObject imap4URL]];
|
|
|
|
newFolderIMAPName = [connection
|
|
|
|
imap4FolderNameForURL: [[newFolder sogoObject] imap4URL]];
|
|
|
|
client = [connection client];
|
|
|
|
[client select: folderIMAPName];
|
|
|
|
|
|
|
|
childKeys = [self messageKeys];
|
|
|
|
max = [childKeys count];
|
|
|
|
uids = [NSMutableArray arrayWithCapacity: max];
|
|
|
|
for (count = 0; count < max; count++)
|
|
|
|
{
|
|
|
|
childKey = [childKeys objectAtIndex: count];
|
|
|
|
[uids addObject: [self messageUIDFromMessageKey: childKey]];
|
|
|
|
}
|
|
|
|
|
|
|
|
result = [client copyUids: uids
|
|
|
|
toFolder: newFolderIMAPName];
|
|
|
|
if ([[result objectForKey: @"result"] boolValue])
|
|
|
|
{
|
|
|
|
if (isRecursive)
|
|
|
|
{
|
|
|
|
childKeys = [self folderKeys];
|
|
|
|
max = [childKeys count];
|
|
|
|
for (count = 0; count < max; count++)
|
|
|
|
{
|
|
|
|
childKey = [childKeys objectAtIndex: count];
|
|
|
|
[[self lookupFolder: childKey]
|
|
|
|
moveCopyToFolder: newFolder
|
|
|
|
withNewName: nil
|
|
|
|
isMove: NO
|
2012-12-07 19:30:50 +01:00
|
|
|
isRecursive: YES
|
|
|
|
inMemCtx: memCtx];
|
2012-08-15 15:13:15 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
rc = MAPISTORE_ERROR;
|
|
|
|
}
|
2012-08-13 05:58:14 +02:00
|
|
|
}
|
2012-08-15 03:02:08 +02:00
|
|
|
[targetFolder cleanupCaches];
|
2012-08-09 23:35:09 +02:00
|
|
|
}
|
|
|
|
else
|
2012-08-15 03:02:08 +02:00
|
|
|
rc = [super moveCopyToFolder: targetFolder withNewName: newFolderName
|
|
|
|
isMove: isMove
|
2012-12-07 19:30:50 +01:00
|
|
|
isRecursive: isRecursive
|
|
|
|
inMemCtx: memCtx];
|
2012-08-09 23:35:09 +02:00
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2011-09-23 01:45:33 +02:00
|
|
|
- (MAPIStoreMessage *) createMessage
|
|
|
|
{
|
2014-05-14 03:06:59 +02:00
|
|
|
SOGoCacheObject *childObject;
|
2011-09-14 20:33:44 +02:00
|
|
|
|
2014-05-14 03:06:59 +02:00
|
|
|
childObject = [SOGoCacheObject objectWithName: [SOGoCacheObject
|
2012-06-29 19:59:38 +02:00
|
|
|
globallyUniqueObjectId]
|
|
|
|
inContainer: sogoObject];
|
|
|
|
return [MAPIStoreMailVolatileMessage
|
|
|
|
mapiStoreObjectWithSOGoObject: childObject
|
|
|
|
inContainer: self];
|
|
|
|
}
|
2011-11-18 16:26:03 +01:00
|
|
|
|
2012-10-06 19:02:39 +02:00
|
|
|
- (id) lookupMessage: (NSString *) messageKey
|
|
|
|
{
|
|
|
|
MAPIStoreMailMessage *message;
|
|
|
|
NSData *rawBodyData;
|
|
|
|
|
|
|
|
message = [super lookupMessage: messageKey];
|
|
|
|
if (message)
|
|
|
|
{
|
2012-10-09 22:39:24 +02:00
|
|
|
rawBodyData = [bodyData objectForKey: messageKey];
|
2012-10-06 19:02:39 +02:00
|
|
|
if (rawBodyData)
|
|
|
|
[message setBodyContentFromRawData: rawBodyData];
|
|
|
|
}
|
|
|
|
|
|
|
|
return message;
|
|
|
|
}
|
|
|
|
|
2011-11-18 16:26:03 +01:00
|
|
|
- (NSArray *) rolesForExchangeRights: (uint32_t) rights
|
|
|
|
{
|
|
|
|
NSMutableArray *roles;
|
|
|
|
|
|
|
|
roles = [NSMutableArray arrayWithCapacity: 6];
|
|
|
|
if (rights & RoleOwner)
|
|
|
|
[roles addObject: SOGoMailRole_Administrator];
|
|
|
|
if (rights & RightsCreateItems)
|
|
|
|
{
|
|
|
|
[roles addObject: SOGoRole_ObjectCreator];
|
|
|
|
[roles addObject: SOGoMailRole_Writer];
|
|
|
|
[roles addObject: SOGoMailRole_Poster];
|
|
|
|
}
|
|
|
|
if (rights & RightsDeleteAll)
|
|
|
|
{
|
|
|
|
[roles addObject: SOGoRole_ObjectEraser];
|
|
|
|
[roles addObject: SOGoRole_FolderEraser];
|
|
|
|
[roles addObject: SOGoMailRole_Expunger];
|
|
|
|
}
|
|
|
|
if (rights & RightsEditAll)
|
|
|
|
[roles addObject: SOGoRole_ObjectEditor];
|
|
|
|
if (rights & RightsReadItems)
|
|
|
|
[roles addObject: SOGoRole_ObjectViewer];
|
|
|
|
if (rights & RightsCreateSubfolders)
|
|
|
|
[roles addObject: SOGoRole_FolderCreator];
|
|
|
|
|
2012-01-09 17:41:22 +01:00
|
|
|
// [self logWithFormat: @"roles for rights %.8x = (%@)", rights, roles];
|
|
|
|
|
2011-11-18 16:26:03 +01:00
|
|
|
return roles;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (uint32_t) exchangeRightsForRoles: (NSArray *) roles
|
|
|
|
{
|
|
|
|
uint32_t rights = 0;
|
|
|
|
|
|
|
|
if ([roles containsObject: SOGoMailRole_Administrator])
|
|
|
|
rights |= (RoleOwner ^ RightsAll);
|
|
|
|
if ([roles containsObject: SOGoRole_ObjectCreator])
|
|
|
|
rights |= RightsCreateItems;
|
|
|
|
if ([roles containsObject: SOGoRole_ObjectEraser]
|
|
|
|
&& [roles containsObject: SOGoRole_FolderEraser])
|
|
|
|
rights |= RightsDeleteAll;
|
|
|
|
|
|
|
|
if ([roles containsObject: SOGoRole_ObjectEditor])
|
|
|
|
rights |= RightsEditAll;
|
|
|
|
if ([roles containsObject: SOGoRole_ObjectViewer])
|
|
|
|
rights |= RightsReadItems;
|
|
|
|
if ([roles containsObject: SOGoRole_FolderCreator])
|
|
|
|
rights |= RightsCreateSubfolders;
|
|
|
|
|
|
|
|
if (rights != 0)
|
|
|
|
rights |= RoleNone; /* actually "folder visible" */
|
2012-01-09 17:41:22 +01:00
|
|
|
|
|
|
|
// [self logWithFormat: @"rights for roles (%@) = %.8x", roles, rights];
|
2011-11-18 16:26:03 +01:00
|
|
|
|
|
|
|
return rights;
|
|
|
|
}
|
|
|
|
|
2012-10-06 19:02:39 +02:00
|
|
|
- (enum mapistore_error) preloadMessageBodiesWithKeys: (NSArray *) keys
|
2012-10-10 16:38:50 +02:00
|
|
|
ofTableType: (enum mapistore_table_type) tableType
|
2012-10-06 19:02:39 +02:00
|
|
|
{
|
|
|
|
MAPIStoreMailMessage *message;
|
|
|
|
NSMutableSet *bodyPartKeys;
|
|
|
|
NSMutableDictionary *keyAssoc;
|
|
|
|
NSDictionary *response;
|
|
|
|
NSUInteger count, max;
|
2012-10-09 22:39:24 +02:00
|
|
|
NSString *messageKey, *messageUid, *bodyPartKey;
|
2012-10-06 19:02:39 +02:00
|
|
|
NGImap4Client *client;
|
2014-10-07 12:37:23 +02:00
|
|
|
NSArray *fetch;
|
2012-10-06 19:02:39 +02:00
|
|
|
NSData *bodyContent;
|
|
|
|
|
2012-10-10 16:38:50 +02:00
|
|
|
if (tableType == MAPISTORE_MESSAGE_TABLE)
|
2012-10-06 19:02:39 +02:00
|
|
|
{
|
2012-10-10 16:38:50 +02:00
|
|
|
[bodyData removeAllObjects];
|
|
|
|
max = [keys count];
|
2012-10-06 19:02:39 +02:00
|
|
|
|
2012-10-10 16:38:50 +02:00
|
|
|
if (max > 0)
|
2012-10-06 19:02:39 +02:00
|
|
|
{
|
2012-10-10 16:38:50 +02:00
|
|
|
bodyPartKeys = [NSMutableSet setWithCapacity: max];
|
|
|
|
|
|
|
|
keyAssoc = [NSMutableDictionary dictionaryWithCapacity: max];
|
|
|
|
for (count = 0; count < max; count++)
|
2012-10-06 19:02:39 +02:00
|
|
|
{
|
2012-10-10 16:38:50 +02:00
|
|
|
messageKey = [keys objectAtIndex: count];
|
|
|
|
message = [self lookupMessage: messageKey];
|
|
|
|
if (message)
|
|
|
|
{
|
|
|
|
bodyPartKey = [message bodyContentPartKey];
|
2012-10-15 14:04:26 +02:00
|
|
|
if (bodyPartKey)
|
|
|
|
{
|
|
|
|
[bodyPartKeys addObject: bodyPartKey];
|
|
|
|
messageUid = [self messageUIDFromMessageKey: messageKey];
|
2015-03-13 09:34:21 +01:00
|
|
|
/* If the bodyPartKey include peek, remove it as it is not returned
|
|
|
|
as key in the IMAP server response.
|
|
|
|
|
|
|
|
IMAP conversation example:
|
|
|
|
a4 UID FETCH 1 (UID BODY.PEEK[text])
|
|
|
|
* 1 FETCH (UID 1 BODY[TEXT] {1677}
|
2014-10-07 12:37:23 +02:00
|
|
|
*/
|
2015-03-13 09:34:21 +01:00
|
|
|
bodyPartKey = [bodyPartKey stringByReplacingOccurrencesOfString: @"body.peek"
|
|
|
|
withString: @"body"];
|
2012-10-15 14:04:26 +02:00
|
|
|
[keyAssoc setObject: bodyPartKey forKey: messageUid];
|
|
|
|
}
|
2012-10-10 16:38:50 +02:00
|
|
|
}
|
2012-10-06 19:02:39 +02:00
|
|
|
}
|
|
|
|
|
2012-10-10 16:38:50 +02:00
|
|
|
client = [[(SOGoMailFolder *) sogoObject imap4Connection] client];
|
|
|
|
[client select: [sogoObject absoluteImap4Name]];
|
|
|
|
response = [client fetchUids: [keyAssoc allKeys]
|
2012-10-06 19:02:39 +02:00
|
|
|
parts: [bodyPartKeys allObjects]];
|
2012-10-10 16:38:50 +02:00
|
|
|
fetch = [response objectForKey: @"fetch"];
|
|
|
|
max = [fetch count];
|
|
|
|
for (count = 0; count < max; count++)
|
2012-10-06 19:02:39 +02:00
|
|
|
{
|
2012-10-10 16:38:50 +02:00
|
|
|
response = [fetch objectAtIndex: count];
|
|
|
|
messageUid = [[response objectForKey: @"uid"] stringValue];
|
|
|
|
bodyPartKey = [keyAssoc objectForKey: messageUid];
|
|
|
|
if (bodyPartKey)
|
2012-10-06 19:02:39 +02:00
|
|
|
{
|
2012-10-10 16:38:50 +02:00
|
|
|
bodyContent = [[response objectForKey: bodyPartKey]
|
|
|
|
objectForKey: @"data"];
|
|
|
|
if (bodyContent)
|
|
|
|
{
|
|
|
|
messageKey = [NSString stringWithFormat: @"%@.eml",
|
|
|
|
messageUid];
|
|
|
|
[bodyData setObject: bodyContent forKey: messageKey];
|
|
|
|
}
|
2012-10-06 19:02:39 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return MAPISTORE_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2011-02-24 21:32:27 +01:00
|
|
|
@end
|
2012-02-20 20:47:58 +01:00
|
|
|
|
|
|
|
@implementation MAPIStoreOutboxFolder
|
|
|
|
|
2012-03-02 21:44:24 +01:00
|
|
|
- (int) getPidTagDisplayName: (void **) data
|
2012-10-13 05:29:38 +02:00
|
|
|
inMemCtx: (TALLOC_CTX *) memCtx
|
2012-02-20 20:47:58 +01:00
|
|
|
{
|
|
|
|
*data = [@"Outbox" asUnicodeInMemCtx: memCtx];
|
|
|
|
|
|
|
|
return MAPISTORE_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
@end
|