ba68bd8935
The folder names are encoded through the `asCSSIdentifier` and `stringByEncodingImap4FolderName` functions when we store them as folder keys. In addition, the prefix "folder" is added to the key. The order in which these operations were done when storing the folder keys (and reverted when retrieving them) wasn't consistent trough the code. This led to problems such as creating twice a folder with a digit at the beginning of its name. The folder name goes now through the following operations when being stored as a key (the retrieval reverts these in the reverse order): * `stringByEncodingImap4FolderName` * `asCSSIdentifier` * Add "folder" prefix
313 lines
12 KiB
Objective-C
313 lines
12 KiB
Objective-C
/* MAPIStoreMailContext.m - this file is part of SOGo
|
|
*
|
|
* Copyright (C) 2010-2012 Inverse inc.
|
|
*
|
|
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
|
*
|
|
* 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
|
|
* the Free Software Foundation; either version 3, or (at your option)
|
|
* 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.
|
|
*/
|
|
|
|
#import <Foundation/NSArray.h>
|
|
#import <Foundation/NSDictionary.h>
|
|
#import <Foundation/NSString.h>
|
|
#import <Foundation/NSURL.h>
|
|
#import <NGImap4/NSString+Imap4.h>
|
|
#import <NGExtensions/NSString+misc.h>
|
|
#import <Mailer/SOGoMailAccount.h>
|
|
#import <Mailer/SOGoMailFolder.h>
|
|
|
|
#import "MAPIStoreMailFolder.h"
|
|
#import "MAPIStoreUserContext.h"
|
|
#import "NSString+MAPIStore.h"
|
|
|
|
#import <SOGo/NSString+Utilities.h>
|
|
#import "MAPIApplication.h"
|
|
#import "MAPIStoreMailContext.h"
|
|
|
|
#include <dlinklist.h>
|
|
#undef DEBUG
|
|
#include <mapistore/mapistore.h>
|
|
|
|
static Class MAPIStoreMailFolderK, MAPIStoreOutboxFolderK;
|
|
|
|
@implementation MAPIStoreMailContext
|
|
|
|
+ (void) initialize
|
|
{
|
|
MAPIStoreMailFolderK = [MAPIStoreMailFolder class];
|
|
MAPIStoreOutboxFolderK = [MAPIStoreOutboxFolder class];
|
|
}
|
|
|
|
+ (NSString *) MAPIModuleName
|
|
{
|
|
return @"mail";
|
|
}
|
|
|
|
+ (enum mapistore_context_role) MAPIContextRole
|
|
{
|
|
return MAPISTORE_MAIL_ROLE;
|
|
}
|
|
|
|
static inline NSString *
|
|
MakeDisplayFolderName (NSString *folderName)
|
|
{
|
|
NSArray *parts;
|
|
NSString *lastFolder;
|
|
NSUInteger max;
|
|
|
|
parts = [folderName componentsSeparatedByString: @"/"];
|
|
max = [parts count];
|
|
if (max > 1)
|
|
{
|
|
lastFolder = [parts objectAtIndex: max - 1];
|
|
if ([lastFolder length] == 0)
|
|
lastFolder = [parts objectAtIndex: max - 2];
|
|
}
|
|
else
|
|
lastFolder = folderName;
|
|
|
|
return [[lastFolder substringFromIndex: 6] fromCSSIdentifier];
|
|
}
|
|
|
|
+ (struct mapistore_contexts_list *) listContextsForUser: (NSString *) userName
|
|
withIndexing: (struct indexing_context *) indexing
|
|
inMemCtx: (TALLOC_CTX *) memCtx
|
|
{
|
|
struct mapistore_contexts_list *firstContext = NULL, *context;
|
|
NSString *urlBase, *stringData, *currentName, *realName, *inboxName, *draftsName, *sentName, *trashName;
|
|
NSArray *unprefixedFolders;
|
|
NSMutableArray *secondaryFolders;
|
|
enum mapistore_context_role role[] = {MAPISTORE_MAIL_ROLE,
|
|
MAPISTORE_DRAFTS_ROLE,
|
|
MAPISTORE_SENTITEMS_ROLE};
|
|
NSString *folderName[3];
|
|
NSUInteger count, max;
|
|
SOGoMailAccount *accountFolder;
|
|
MAPIStoreUserContext *userContext;
|
|
WOContext *woContext;
|
|
|
|
userContext = [MAPIStoreUserContext userContextWithUsername: userName
|
|
andTDBIndexing: indexing];
|
|
accountFolder = [[userContext rootFolders] objectForKey: @"mail"];
|
|
woContext = [userContext woContext];
|
|
|
|
inboxName = @"folderINBOX";
|
|
folderName[0] = inboxName;
|
|
|
|
unprefixedFolders = [[accountFolder draftsFolderNameInContext: woContext]
|
|
componentsSeparatedByString: @"/"];
|
|
draftsName = [NSString stringWithFormat: @"folder%@",
|
|
[unprefixedFolders componentsJoinedByString: @"/folder"]];
|
|
folderName[1] = draftsName;
|
|
|
|
unprefixedFolders = [[accountFolder sentFolderNameInContext: woContext]
|
|
componentsSeparatedByString: @"/"];
|
|
sentName = [NSString stringWithFormat: @"folder%@",
|
|
[unprefixedFolders componentsJoinedByString: @"/folder"]];
|
|
folderName[2] = sentName;
|
|
|
|
/* Note: trash is not used as a mail folder, since "Deleted Items" makes use of
|
|
the fallback context */
|
|
unprefixedFolders = [[accountFolder trashFolderNameInContext: woContext]
|
|
componentsSeparatedByString: @"/"];
|
|
trashName = [NSString stringWithFormat: @"folder%@",
|
|
[unprefixedFolders componentsJoinedByString: @"/folder"]];
|
|
|
|
urlBase = [NSString stringWithFormat: @"sogo://%@:%@@mail/",
|
|
[userName stringByReplacingOccurrencesOfString: @"@"
|
|
withString: @"%40"],
|
|
[userName stringByReplacingOccurrencesOfString: @"@"
|
|
withString: @"%40"]];
|
|
for (count = 0; count < 3; count++)
|
|
{
|
|
context = talloc_zero (memCtx, struct mapistore_contexts_list);
|
|
stringData = [NSString stringWithFormat: @"%@%@", urlBase,
|
|
[folderName[count] stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding]];
|
|
context->url = [stringData asUnicodeInMemCtx: context];
|
|
/* remove "folder" prefix */
|
|
stringData = MakeDisplayFolderName (folderName[count]);
|
|
context->name = [stringData asUnicodeInMemCtx: context];
|
|
context->main_folder = true;
|
|
context->role = role[count];
|
|
context->tag = "tag";
|
|
DLIST_ADD_END (firstContext, context, void);
|
|
}
|
|
|
|
/* FIXME: Flush any cache before retrieving the hierarchy */
|
|
[accountFolder flushMailCaches];
|
|
|
|
secondaryFolders = [[accountFolder toManyRelationshipKeysWithNamespaces: NO]
|
|
mutableCopy];
|
|
[secondaryFolders autorelease];
|
|
[secondaryFolders removeObject: inboxName];
|
|
[secondaryFolders removeObject: draftsName];
|
|
[secondaryFolders removeObject: sentName];
|
|
[secondaryFolders removeObject: trashName];
|
|
max = [secondaryFolders count];
|
|
for (count = 0; count < max; count++)
|
|
{
|
|
context = talloc_zero (memCtx, struct mapistore_contexts_list);
|
|
// 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)
|
|
// e.g.: Problèmes de synchronisation
|
|
realName = [[[currentName substringFromIndex: 6]
|
|
fromCSSIdentifier]
|
|
stringByDecodingImap4FolderName];
|
|
// And finally to represent that as URI we have to (1) asCSSIdentifier,
|
|
// (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 asUnicodeInMemCtx: context];
|
|
context->main_folder = false;
|
|
context->role = MAPISTORE_MAIL_ROLE;
|
|
context->tag = "tag";
|
|
DLIST_ADD_END (firstContext, context, void);
|
|
}
|
|
|
|
return firstContext;
|
|
}
|
|
|
|
+ (NSString *)
|
|
createRootSecondaryFolderWithFID: (uint64_t) fid
|
|
andName: (NSString *) newFolderName
|
|
forUser: (NSString *) userName
|
|
{
|
|
NSString *mapistoreURI, *folderName;
|
|
MAPIStoreUserContext *userContext;
|
|
SOGoMailAccount *accountFolder;
|
|
SOGoMailFolder *newFolder;
|
|
|
|
userContext = [MAPIStoreUserContext userContextWithUsername: userName
|
|
andTDBIndexing: NULL];
|
|
accountFolder = [[userContext rootFolders] objectForKey: @"mail"];
|
|
folderName = [NSString stringWithFormat: @"folder%@",
|
|
[[newFolderName stringByEncodingImap4FolderName] asCSSIdentifier]];
|
|
newFolder = [SOGoMailFolder objectWithName: folderName
|
|
inContainer: accountFolder];
|
|
if ([newFolder create])
|
|
mapistoreURI = [NSString stringWithFormat: @"sogo://%@:%@@mail/%@/",
|
|
[userName stringByReplacingOccurrencesOfString: @"@"
|
|
withString: @"%40"],
|
|
[userName stringByReplacingOccurrencesOfString: @"@"
|
|
withString: @"%40"],
|
|
[folderName stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding]];
|
|
else
|
|
mapistoreURI = nil;
|
|
|
|
return mapistoreURI;
|
|
}
|
|
|
|
- (Class) MAPIStoreFolderClass
|
|
{
|
|
return MAPIStoreMailFolderK;
|
|
}
|
|
|
|
- (id) rootSOGoFolder
|
|
{
|
|
return [[userContext rootFolders] objectForKey: @"mail"];
|
|
}
|
|
|
|
- (void) updateURLWithFolderName: (NSString *) newFolderName
|
|
{
|
|
NSString *urlString, *escapedName;
|
|
NSMutableArray *pathComponents;
|
|
BOOL hasSlash;
|
|
NSUInteger max, folderNameIdx;
|
|
NSURL *newURL;
|
|
|
|
/* we do not need to unescape the url here as it will be reassembled later
|
|
in the method */
|
|
urlString = [contextUrl absoluteString];
|
|
hasSlash = [urlString hasSuffix: @"/"];
|
|
pathComponents = [[urlString componentsSeparatedByString: @"/"]
|
|
mutableCopy];
|
|
[pathComponents autorelease];
|
|
max = [pathComponents count];
|
|
if (hasSlash)
|
|
folderNameIdx = max - 2;
|
|
else
|
|
folderNameIdx = max - 1;
|
|
escapedName = [newFolderName stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding];
|
|
[pathComponents replaceObjectAtIndex: folderNameIdx
|
|
withObject: escapedName];
|
|
urlString = [pathComponents componentsJoinedByString: @"/"];
|
|
newURL = [NSURL URLWithString: urlString];
|
|
ASSIGN (contextUrl, newURL);
|
|
}
|
|
|
|
@end
|
|
|
|
@implementation MAPIStoreOutboxContext
|
|
|
|
+ (NSString *) MAPIModuleName
|
|
{
|
|
return @"outbox";
|
|
}
|
|
|
|
+ (struct mapistore_contexts_list *) listContextsForUser: (NSString *) userName
|
|
withIndexing: (struct indexing_context *) indexing
|
|
inMemCtx: (TALLOC_CTX *) memCtx
|
|
{
|
|
struct mapistore_contexts_list *context;
|
|
NSString *url, *folderName;
|
|
NSArray *unprefixedFolders;
|
|
SOGoMailAccount *accountFolder;
|
|
MAPIStoreUserContext *userContext;
|
|
WOContext *woContext;
|
|
|
|
userContext = [MAPIStoreUserContext userContextWithUsername: userName
|
|
andTDBIndexing: indexing];
|
|
accountFolder = [[userContext rootFolders] objectForKey: @"mail"];
|
|
woContext = [userContext woContext];
|
|
|
|
unprefixedFolders = [[accountFolder draftsFolderNameInContext: woContext]
|
|
componentsSeparatedByString: @"/"];
|
|
folderName = [NSString stringWithFormat: @"folder%@",
|
|
[unprefixedFolders componentsJoinedByString: @"/folder"]];
|
|
url = [NSString stringWithFormat: @"sogo://%@:%@@outbox/%@",
|
|
[userName stringByReplacingOccurrencesOfString: @"@"
|
|
withString: @"%40"],
|
|
[userName stringByReplacingOccurrencesOfString: @"@"
|
|
withString: @"%40"],
|
|
folderName];
|
|
|
|
context = talloc_zero (memCtx, struct mapistore_contexts_list);
|
|
context->url = [url asUnicodeInMemCtx: context];
|
|
/* TODO: use a localized version of this display name */
|
|
context->name = [@"Outbox" asUnicodeInMemCtx: context];
|
|
context->main_folder = true;
|
|
context->role = MAPISTORE_OUTBOX_ROLE;
|
|
context->tag = "tag";
|
|
context->prev = context;
|
|
|
|
return context;
|
|
}
|
|
|
|
- (Class) MAPIStoreFolderClass
|
|
{
|
|
return MAPIStoreOutboxFolderK;
|
|
}
|
|
|
|
@end
|