2006-06-15 21:34:10 +02:00
|
|
|
/*
|
2016-01-22 16:30:27 +01:00
|
|
|
Copyright (C) 2007-2016 Inverse inc.
|
2006-06-15 21:34:10 +02:00
|
|
|
|
2009-03-13 20:48:15 +01:00
|
|
|
This file is part of SOGo.
|
2006-06-15 21:34:10 +02:00
|
|
|
|
2009-03-13 20:48:15 +01:00
|
|
|
SOGo is free software; you can redistribute it and/or modify it under
|
2006-06-15 21:34:10 +02:00
|
|
|
the terms of the GNU Lesser General Public License as published by the
|
|
|
|
Free Software Foundation; either version 2, or (at your option) any
|
|
|
|
later version.
|
|
|
|
|
2009-03-13 20:48:15 +01:00
|
|
|
SOGo is distributed in the hope that it will be useful, but WITHOUT ANY
|
2006-06-15 21:34:10 +02:00
|
|
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
|
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
|
|
|
License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
2009-10-09 23:01:54 +02:00
|
|
|
License along with SOGo; see the file COPYING. If not, write to the
|
2006-06-15 21:34:10 +02:00
|
|
|
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
|
|
|
|
02111-1307, USA.
|
|
|
|
*/
|
|
|
|
|
2014-01-10 20:06:53 +01:00
|
|
|
#import <Foundation/NSAutoreleasePool.h>
|
2009-11-29 05:19:32 +01:00
|
|
|
#import <Foundation/NSDictionary.h>
|
2007-08-01 20:32:00 +02:00
|
|
|
#import <Foundation/NSURL.h>
|
2011-04-06 17:15:16 +02:00
|
|
|
#import <Foundation/NSValue.h>
|
2007-08-01 20:32:00 +02:00
|
|
|
|
|
|
|
#import <NGObjWeb/NSException+HTTP.h>
|
|
|
|
#import <NGObjWeb/WOContext+SoObjects.h>
|
|
|
|
#import <NGExtensions/NSNull+misc.h>
|
2008-02-08 00:09:12 +01:00
|
|
|
#import <NGExtensions/NSObject+Logs.h>
|
|
|
|
#import <NGExtensions/NSString+misc.h>
|
2007-08-01 20:32:00 +02:00
|
|
|
#import <NGImap4/NGImap4Connection.h>
|
2008-02-01 17:28:53 +01:00
|
|
|
#import <NGImap4/NGImap4Client.h>
|
2014-12-29 18:43:20 +01:00
|
|
|
#import <NGImap4/NSString+Imap4.h>
|
2007-08-01 20:32:00 +02:00
|
|
|
|
2009-11-29 05:19:32 +01:00
|
|
|
#import <SOGo/NSArray+Utilities.h>
|
2010-01-05 23:34:35 +01:00
|
|
|
#import <SOGo/NSString+Utilities.h>
|
2010-08-06 16:25:59 +02:00
|
|
|
#import <SOGo/SOGoAuthenticator.h>
|
2009-11-29 05:19:32 +01:00
|
|
|
#import <SOGo/SOGoDomainDefaults.h>
|
2016-12-02 23:38:33 +01:00
|
|
|
#import <SOGo/SOGoSystemDefaults.h>
|
2012-10-22 16:09:13 +02:00
|
|
|
#import <SOGo/SOGoUserSettings.h>
|
2014-01-10 20:06:53 +01:00
|
|
|
#import <SOGo/SOGoUserManager.h>
|
2011-06-29 19:43:10 +02:00
|
|
|
#import <SOGo/SOGoSieveManager.h>
|
2007-10-17 23:03:39 +02:00
|
|
|
|
2007-11-01 13:48:19 +01:00
|
|
|
#import "SOGoDraftsFolder.h"
|
2010-01-05 23:34:35 +01:00
|
|
|
#import "SOGoMailNamespace.h"
|
2007-11-01 13:48:19 +01:00
|
|
|
#import "SOGoSentFolder.h"
|
|
|
|
#import "SOGoTrashFolder.h"
|
2016-01-22 16:30:27 +01:00
|
|
|
#import "SOGoJunkFolder.h"
|
2010-09-30 20:05:14 +02:00
|
|
|
#import "SOGoUser+Mailer.h"
|
2010-03-02 20:57:30 +01:00
|
|
|
|
2007-08-01 20:32:00 +02:00
|
|
|
#import "SOGoMailAccount.h"
|
2014-05-27 20:44:57 +02:00
|
|
|
#import <Foundation/NSProcessInfo.h>
|
|
|
|
|
2006-06-15 21:34:10 +02:00
|
|
|
|
2013-11-13 21:44:57 +01:00
|
|
|
#define XMLNS_INVERSEDAV @"urn:inverse:params:xml:ns:inverse-dav"
|
|
|
|
|
2006-06-15 21:34:10 +02:00
|
|
|
@implementation SOGoMailAccount
|
|
|
|
|
2007-08-15 22:22:24 +02:00
|
|
|
static NSString *inboxFolderName = @"INBOX";
|
2009-10-09 23:01:54 +02:00
|
|
|
|
2007-08-15 22:22:24 +02:00
|
|
|
- (id) init
|
|
|
|
{
|
|
|
|
if ((self = [super init]))
|
|
|
|
{
|
2016-12-02 23:38:33 +01:00
|
|
|
NSString *sieveFolderEncoding = [[SOGoSystemDefaults sharedSystemDefaults] sieveFolderEncoding];
|
2007-08-15 22:22:24 +02:00
|
|
|
inboxFolder = nil;
|
|
|
|
draftsFolder = nil;
|
|
|
|
sentFolder = nil;
|
|
|
|
trashFolder = nil;
|
2016-01-22 16:30:27 +01:00
|
|
|
junkFolder = nil;
|
2009-11-29 05:19:32 +01:00
|
|
|
imapAclStyle = undefined;
|
2010-09-30 20:05:14 +02:00
|
|
|
identities = nil;
|
2011-04-21 18:02:31 +02:00
|
|
|
otherUsersFolderName = nil;
|
|
|
|
sharedFoldersName = nil;
|
2016-09-14 21:57:49 +02:00
|
|
|
subscribedFolders = nil;
|
2016-12-02 23:38:33 +01:00
|
|
|
sieveFolderUTF8Encoding = [sieveFolderEncoding isEqualToString: @"UTF-8"];
|
2007-08-15 22:22:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) dealloc
|
|
|
|
{
|
|
|
|
[inboxFolder release];
|
|
|
|
[draftsFolder release];
|
|
|
|
[sentFolder release];
|
|
|
|
[trashFolder release];
|
2016-01-22 16:30:27 +01:00
|
|
|
[junkFolder release];
|
2010-09-30 20:05:14 +02:00
|
|
|
[identities release];
|
2011-04-21 18:02:31 +02:00
|
|
|
[otherUsersFolderName release];
|
|
|
|
[sharedFoldersName release];
|
2016-09-14 21:57:49 +02:00
|
|
|
[subscribedFolders release];
|
2007-08-15 22:22:24 +02:00
|
|
|
[super dealloc];
|
2006-06-15 21:34:10 +02:00
|
|
|
}
|
|
|
|
|
2007-08-15 22:22:24 +02:00
|
|
|
- (BOOL) isInDraftsFolder
|
|
|
|
{
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
2010-01-05 23:34:35 +01:00
|
|
|
- (void) _appendNamespace: (NSArray *) namespace
|
|
|
|
toFolders: (NSMutableArray *) folders
|
|
|
|
{
|
|
|
|
NSString *newFolder;
|
|
|
|
NSDictionary *currentPart;
|
|
|
|
int count, max;
|
|
|
|
|
|
|
|
max = [namespace count];
|
|
|
|
for (count = 0; count < max; count++)
|
|
|
|
{
|
|
|
|
currentPart = [namespace objectAtIndex: count];
|
|
|
|
newFolder
|
|
|
|
= [[currentPart objectForKey: @"prefix"] substringFromIndex: 1];
|
|
|
|
if ([newFolder length])
|
2010-04-07 16:04:48 +02:00
|
|
|
[folders addObjectUniquely: newFolder];
|
2010-01-05 23:34:35 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) _appendNamespaces: (NSMutableArray *) folders
|
|
|
|
{
|
|
|
|
NSDictionary *namespaceDict;
|
|
|
|
NSArray *namespace;
|
|
|
|
NGImap4Client *client;
|
|
|
|
|
|
|
|
client = [[self imap4Connection] client];
|
|
|
|
namespaceDict = [client namespace];
|
2011-04-21 18:02:31 +02:00
|
|
|
|
2010-01-05 23:34:35 +01:00
|
|
|
namespace = [namespaceDict objectForKey: @"personal"];
|
|
|
|
if (namespace)
|
|
|
|
[self _appendNamespace: namespace toFolders: folders];
|
2011-04-21 18:02:31 +02:00
|
|
|
|
2010-01-05 23:34:35 +01:00
|
|
|
namespace = [namespaceDict objectForKey: @"other users"];
|
|
|
|
if (namespace)
|
2011-04-21 18:02:31 +02:00
|
|
|
{
|
|
|
|
[self _appendNamespace: namespace toFolders: folders];
|
|
|
|
ASSIGN(otherUsersFolderName, [folders lastObject]);
|
|
|
|
}
|
|
|
|
|
2010-01-05 23:34:35 +01:00
|
|
|
namespace = [namespaceDict objectForKey: @"shared"];
|
|
|
|
if (namespace)
|
2011-04-21 18:02:31 +02:00
|
|
|
{
|
|
|
|
[self _appendNamespace: namespace toFolders: folders];
|
|
|
|
ASSIGN(sharedFoldersName, [folders lastObject]);
|
|
|
|
}
|
2010-01-05 23:34:35 +01:00
|
|
|
}
|
|
|
|
|
2010-01-06 14:45:55 +01:00
|
|
|
- (NSArray *) _namespacesWithKey: (NSString *) nsKey
|
|
|
|
{
|
|
|
|
NSDictionary *namespaceDict;
|
|
|
|
NSArray *namespace;
|
|
|
|
NGImap4Client *client;
|
|
|
|
NSMutableArray *folders;
|
|
|
|
|
|
|
|
client = [[self imap4Connection] client];
|
|
|
|
namespaceDict = [client namespace];
|
|
|
|
namespace = [namespaceDict objectForKey: nsKey];
|
|
|
|
if (namespace)
|
|
|
|
{
|
|
|
|
folders = [NSMutableArray array];
|
|
|
|
[self _appendNamespace: namespace toFolders: folders];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
folders = nil;
|
|
|
|
|
|
|
|
return folders;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSArray *) otherUsersFolderNamespaces
|
|
|
|
{
|
|
|
|
return [self _namespacesWithKey: @"other users"];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSArray *) sharedFolderNamespaces
|
|
|
|
{
|
|
|
|
return [self _namespacesWithKey: @"shared"];
|
|
|
|
}
|
|
|
|
|
2011-08-15 21:10:56 +02:00
|
|
|
- (NSArray *) toManyRelationshipKeysWithNamespaces: (BOOL) withNSs
|
2007-03-30 17:25:20 +02:00
|
|
|
{
|
|
|
|
NSMutableArray *folders;
|
2011-08-15 21:10:56 +02:00
|
|
|
NSArray *imapFolders, *nss;
|
2007-03-30 17:25:20 +02:00
|
|
|
|
|
|
|
imapFolders = [[self imap4Connection] subfoldersForURL: [self imap4URL]];
|
2010-01-05 23:34:35 +01:00
|
|
|
folders = [imapFolders mutableCopy];
|
|
|
|
[folders autorelease];
|
2011-08-15 21:10:56 +02:00
|
|
|
if (withNSs)
|
|
|
|
[self _appendNamespaces: folders];
|
|
|
|
else
|
|
|
|
{ /* some implementation insist on returning NSs in the list of
|
|
|
|
folders... */
|
|
|
|
nss = [self otherUsersFolderNamespaces];
|
|
|
|
if (nss)
|
|
|
|
[folders removeObjectsInArray: nss];
|
|
|
|
nss = [self sharedFolderNamespaces];
|
|
|
|
if (nss)
|
|
|
|
[folders removeObjectsInArray: nss];
|
|
|
|
}
|
2010-01-05 23:34:35 +01:00
|
|
|
|
2015-11-05 15:59:31 +01:00
|
|
|
return [[folders resultsOfSelector: @selector (asCSSIdentifier)]
|
|
|
|
stringsWithFormat: @"folder%@"];
|
2006-06-15 21:34:10 +02:00
|
|
|
}
|
|
|
|
|
2011-08-15 21:10:56 +02:00
|
|
|
- (NSArray *) toManyRelationshipKeys
|
|
|
|
{
|
|
|
|
return [self toManyRelationshipKeysWithNamespaces: YES];
|
|
|
|
}
|
|
|
|
|
2009-11-29 05:19:32 +01:00
|
|
|
- (SOGoIMAPAclStyle) imapAclStyle
|
|
|
|
{
|
|
|
|
SOGoDomainDefaults *dd;
|
|
|
|
|
|
|
|
if (imapAclStyle == undefined)
|
|
|
|
{
|
|
|
|
dd = [[context activeUser] domainDefaults];
|
|
|
|
if ([[dd imapAclStyle] isEqualToString: @"rfc2086"])
|
|
|
|
imapAclStyle = rfc2086;
|
|
|
|
else
|
|
|
|
imapAclStyle = rfc4314;
|
|
|
|
}
|
|
|
|
|
|
|
|
return imapAclStyle;
|
|
|
|
}
|
|
|
|
|
2010-02-19 17:25:55 +01:00
|
|
|
/* see http://tools.ietf.org/id/draft-ietf-imapext-acl */
|
2009-11-29 05:19:32 +01:00
|
|
|
- (BOOL) imapAclConformsToIMAPExt
|
|
|
|
{
|
2010-02-19 17:25:55 +01:00
|
|
|
NGImap4Client *imapClient;
|
|
|
|
NSArray *capability;
|
|
|
|
int count, max;
|
|
|
|
BOOL conforms;
|
|
|
|
|
|
|
|
conforms = NO;
|
2009-11-29 05:19:32 +01:00
|
|
|
|
2010-02-19 17:25:55 +01:00
|
|
|
imapClient = [[self imap4Connection] client];
|
|
|
|
capability = [[imapClient capability] objectForKey: @"capability"];
|
|
|
|
max = [capability count];
|
|
|
|
for (count = 0; !conforms && count < max; count++)
|
|
|
|
{
|
|
|
|
if ([[capability objectAtIndex: count] hasPrefix: @"acl2"])
|
|
|
|
conforms = YES;
|
|
|
|
}
|
2009-11-29 05:19:32 +01:00
|
|
|
|
2010-02-19 17:25:55 +01:00
|
|
|
return conforms;
|
2009-11-29 05:19:32 +01:00
|
|
|
}
|
|
|
|
|
2011-07-25 14:52:56 +02:00
|
|
|
/* capabilities */
|
|
|
|
- (BOOL) hasCapability: (NSString *) capability
|
2008-02-01 17:28:53 +01:00
|
|
|
{
|
|
|
|
NGImap4Client *imapClient;
|
2011-07-25 14:52:56 +02:00
|
|
|
NSArray *capabilities;
|
2008-02-01 17:28:53 +01:00
|
|
|
|
|
|
|
imapClient = [[self imap4Connection] client];
|
2011-07-25 14:52:56 +02:00
|
|
|
capabilities = [[imapClient capability] objectForKey: @"capability"];
|
2008-02-01 17:28:53 +01:00
|
|
|
|
2011-07-25 14:52:56 +02:00
|
|
|
return [capabilities containsObject: capability];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL) supportsQuotas
|
|
|
|
{
|
|
|
|
return [self hasCapability: @"quota"];
|
2008-02-01 17:28:53 +01:00
|
|
|
}
|
|
|
|
|
2011-07-25 14:55:44 +02:00
|
|
|
- (BOOL) supportsQResync
|
|
|
|
{
|
|
|
|
return [self hasCapability: @"qresync"];
|
|
|
|
}
|
|
|
|
|
2011-03-17 22:26:51 +01:00
|
|
|
- (id) getInboxQuota
|
|
|
|
{
|
|
|
|
SOGoMailFolder *inbox;
|
|
|
|
NGImap4Client *client;
|
|
|
|
NSString *inboxName;
|
|
|
|
SOGoDomainDefaults *dd;
|
|
|
|
id inboxQuota, infos;
|
|
|
|
float quota;
|
|
|
|
|
|
|
|
inboxQuota = nil;
|
|
|
|
if ([self supportsQuotas])
|
|
|
|
{
|
|
|
|
dd = [[context activeUser] domainDefaults];
|
|
|
|
quota = [dd softQuotaRatio];
|
|
|
|
inbox = [self inboxFolderInContext: context];
|
|
|
|
inboxName = [NSString stringWithFormat: @"/%@", [inbox relativeImap4Name]];
|
|
|
|
client = [[inbox imap4Connection] client];
|
|
|
|
infos = [[client getQuotaRoot: [inbox relativeImap4Name]] objectForKey: @"quotas"];
|
|
|
|
inboxQuota = [infos objectForKey: inboxName];
|
|
|
|
if (quota != 0 && inboxQuota != nil)
|
|
|
|
{
|
|
|
|
// A soft quota ratio is imposed for all users
|
|
|
|
quota = quota * [(NSNumber*)[inboxQuota objectForKey: @"maxQuota"] intValue];
|
|
|
|
inboxQuota = [NSDictionary dictionaryWithObjectsAndKeys:
|
|
|
|
[NSNumber numberWithLong: (long)(quota+0.5)], @"maxQuota",
|
2015-03-18 20:00:46 +01:00
|
|
|
[NSNumber numberWithLong: [[inboxQuota objectForKey: @"usedSpace"] longLongValue]], @"usedSpace",
|
2011-03-17 22:26:51 +01:00
|
|
|
nil];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return inboxQuota;
|
|
|
|
}
|
|
|
|
|
2009-10-09 23:01:54 +02:00
|
|
|
- (BOOL) updateFilters
|
2014-03-04 04:07:24 +01:00
|
|
|
{
|
|
|
|
return [self updateFiltersWithUsername: nil andPassword: nil];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL) updateFiltersWithUsername: (NSString *) theUsername
|
|
|
|
andPassword: (NSString *) thePassword
|
2009-10-09 23:01:54 +02:00
|
|
|
{
|
2011-06-29 19:43:10 +02:00
|
|
|
SOGoSieveManager *manager;
|
2010-03-02 20:57:30 +01:00
|
|
|
|
2011-06-29 19:43:10 +02:00
|
|
|
manager = [SOGoSieveManager sieveManagerForUser: [context activeUser]];
|
2010-11-08 18:04:29 +01:00
|
|
|
|
2014-03-04 04:07:24 +01:00
|
|
|
return [manager updateFiltersForAccount: self withUsername: theUsername andPassword: thePassword];
|
2009-10-09 23:01:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-06-15 21:34:10 +02:00
|
|
|
/* hierarchy */
|
|
|
|
|
2007-08-15 22:22:24 +02:00
|
|
|
- (SOGoMailAccount *) mailAccountFolder
|
|
|
|
{
|
2006-06-15 21:34:10 +02:00
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
2010-01-05 23:34:35 +01:00
|
|
|
- (NSArray *) _allFoldersFromNS: (NSString *) namespace
|
|
|
|
subscribedOnly: (BOOL) subscribedOnly
|
|
|
|
{
|
|
|
|
NSArray *folders;
|
|
|
|
NSURL *nsURL;
|
|
|
|
NSString *baseURLString, *urlString;
|
|
|
|
|
2017-10-03 15:13:56 +02:00
|
|
|
baseURLString = [self imap4URLString];
|
2010-01-05 23:34:35 +01:00
|
|
|
urlString = [NSString stringWithFormat: @"%@%@/", baseURLString, [namespace stringByEscapingURL]];
|
|
|
|
nsURL = [NSURL URLWithString: urlString];
|
|
|
|
folders = [[self imap4Connection] allFoldersForURL: nsURL
|
|
|
|
onlySubscribedFolders: subscribedOnly];
|
|
|
|
|
|
|
|
return folders;
|
|
|
|
}
|
|
|
|
|
2014-01-10 20:06:53 +01:00
|
|
|
//
|
|
|
|
//
|
|
|
|
//
|
2016-09-14 21:57:49 +02:00
|
|
|
- (NSArray *) allFolderPaths: (SOGoMailListingMode) theListingMode
|
2007-05-28 21:06:13 +02:00
|
|
|
{
|
2010-01-05 23:34:35 +01:00
|
|
|
NSMutableArray *folderPaths, *namespaces;
|
|
|
|
NSArray *folders, *mainFolders;
|
2016-02-05 16:01:18 +01:00
|
|
|
NSString *namespace;
|
|
|
|
|
2010-01-05 23:34:35 +01:00
|
|
|
BOOL subscribedOnly;
|
|
|
|
int count, max;
|
2007-05-28 21:06:13 +02:00
|
|
|
|
2016-09-14 21:57:49 +02:00
|
|
|
if (theListingMode == SOGoMailStandardListing)
|
|
|
|
subscribedOnly = [[[context activeUser] userDefaults] mailShowSubscribedFoldersOnly];
|
|
|
|
else
|
|
|
|
{
|
|
|
|
subscribedOnly = NO;
|
|
|
|
DESTROY(subscribedFolders);
|
|
|
|
subscribedFolders = [[NSMutableDictionary alloc] init];
|
|
|
|
folders = [[self imap4Connection] allFoldersForURL: [self imap4URL]
|
|
|
|
onlySubscribedFolders: YES];
|
|
|
|
max = [folders count];
|
|
|
|
for (count = 0; count < max; count++)
|
|
|
|
{
|
|
|
|
[subscribedFolders setObject: [NSNull null]
|
|
|
|
forKey: [folders objectAtIndex: count]];
|
|
|
|
}
|
|
|
|
[[self imap4Connection] flushFolderHierarchyCache];
|
|
|
|
}
|
2007-05-28 21:06:13 +02:00
|
|
|
|
2007-11-01 13:48:19 +01:00
|
|
|
mainFolders = [[NSArray arrayWithObjects:
|
|
|
|
[self inboxFolderNameInContext: context],
|
|
|
|
[self draftsFolderNameInContext: context],
|
|
|
|
[self sentFolderNameInContext: context],
|
|
|
|
[self trashFolderNameInContext: context],
|
2016-09-14 21:57:49 +02:00
|
|
|
[self junkFolderNameInContext: context],
|
|
|
|
nil] stringsWithFormat: @"/%@"];
|
2010-01-05 23:34:35 +01:00
|
|
|
folders = [[self imap4Connection] allFoldersForURL: [self imap4URL]
|
2016-09-14 21:57:49 +02:00
|
|
|
onlySubscribedFolders: subscribedOnly];
|
2010-01-05 23:34:35 +01:00
|
|
|
folderPaths = [folders mutableCopy];
|
|
|
|
[folderPaths autorelease];
|
2016-09-14 21:57:49 +02:00
|
|
|
|
2007-11-01 13:48:19 +01:00
|
|
|
[folderPaths removeObjectsInArray: mainFolders];
|
2010-01-05 23:34:35 +01:00
|
|
|
namespaces = [NSMutableArray arrayWithCapacity: 10];
|
|
|
|
[self _appendNamespaces: namespaces];
|
|
|
|
max = [namespaces count];
|
|
|
|
for (count = 0; count < max; count++)
|
|
|
|
{
|
2016-02-05 16:01:18 +01:00
|
|
|
namespace = [namespaces objectAtIndex: count];
|
|
|
|
folders = [self _allFoldersFromNS: namespace
|
2010-01-05 23:34:35 +01:00
|
|
|
subscribedOnly: subscribedOnly];
|
|
|
|
if ([folders count])
|
|
|
|
{
|
|
|
|
[folderPaths removeObjectsInArray: folders];
|
|
|
|
[folderPaths addObjectsFromArray: folders];
|
2016-02-05 16:01:18 +01:00
|
|
|
|
|
|
|
// We make sure our "shared" / "public" namespace is always defined. Cyrus does NOT
|
|
|
|
// return them in LIST while Dovecot does.
|
|
|
|
namespace = [NSString stringWithFormat: @"/%@", namespace];
|
|
|
|
[folderPaths removeObject: namespace];
|
|
|
|
[folderPaths addObject: namespace];
|
2010-01-05 23:34:35 +01:00
|
|
|
}
|
|
|
|
}
|
2007-11-01 13:48:19 +01:00
|
|
|
[folderPaths
|
|
|
|
sortUsingSelector: @selector (localizedCaseInsensitiveCompare:)];
|
|
|
|
[folderPaths replaceObjectsInRange: NSMakeRange (0, 0)
|
|
|
|
withObjectsFromArray: mainFolders];
|
|
|
|
|
|
|
|
return folderPaths;
|
2006-06-15 21:34:10 +02:00
|
|
|
}
|
|
|
|
|
2014-01-10 20:06:53 +01:00
|
|
|
//
|
|
|
|
//
|
|
|
|
//
|
|
|
|
- (NSString *) _folderType: (NSString *) folderName
|
2016-02-10 16:00:59 +01:00
|
|
|
flags: (NSMutableArray *) flags
|
2014-01-10 20:06:53 +01:00
|
|
|
{
|
2016-02-10 16:00:59 +01:00
|
|
|
NSString *folderType, *key;
|
|
|
|
NSDictionary *metadata;
|
|
|
|
SOGoUserDefaults *ud;
|
|
|
|
|
|
|
|
ud = [[context activeUser] userDefaults];
|
|
|
|
|
|
|
|
metadata = [[[self imap4Connection] allFoldersMetadataForURL: [self imap4URL]
|
|
|
|
onlySubscribedFolders: [ud mailShowSubscribedFoldersOnly]]
|
|
|
|
objectForKey: @"list"];
|
|
|
|
|
|
|
|
key = [NSString stringWithFormat: @"/%@", folderName];
|
|
|
|
[flags addObjectsFromArray: [metadata objectForKey: key]];
|
2014-01-10 20:06:53 +01:00
|
|
|
|
2016-02-10 16:00:59 +01:00
|
|
|
// RFC6154 (https://tools.ietf.org/html/rfc6154) describes special uses for IMAP mailboxes.
|
|
|
|
// We do honor them, as long as your SOGo{Drafts,Trash,Sent,Junk}FolderName are properly configured
|
|
|
|
// See http://wiki.dovecot.org/MailboxSettings for a Dovecot example.
|
2014-12-18 21:22:29 +01:00
|
|
|
if ([folderName isEqualToString: inboxFolderName])
|
2014-01-10 20:06:53 +01:00
|
|
|
folderType = @"inbox";
|
2016-02-10 16:00:59 +01:00
|
|
|
else if ([flags containsObject: [self draftsFolderNameInContext: context]] ||
|
|
|
|
[folderName isEqualToString: [self draftsFolderNameInContext: context]])
|
2014-01-10 20:06:53 +01:00
|
|
|
folderType = @"draft";
|
2016-02-10 16:00:59 +01:00
|
|
|
else if ([flags containsObject: [self sentFolderNameInContext: context]] ||
|
|
|
|
[folderName isEqualToString: [self sentFolderNameInContext: context]])
|
2014-01-10 20:06:53 +01:00
|
|
|
folderType = @"sent";
|
2016-02-10 16:00:59 +01:00
|
|
|
else if ([flags containsObject: [self trashFolderNameInContext: context]] ||
|
|
|
|
[folderName isEqualToString: [self trashFolderNameInContext: context]])
|
2014-01-10 20:06:53 +01:00
|
|
|
folderType = @"trash";
|
2016-02-10 16:00:59 +01:00
|
|
|
else if ([flags containsObject: [self junkFolderNameInContext: context]] ||
|
|
|
|
[folderName isEqualToString: [self junkFolderNameInContext: context]])
|
|
|
|
folderType = @"junk";
|
2014-12-18 21:22:29 +01:00
|
|
|
else if ([folderName isEqualToString: otherUsersFolderName])
|
|
|
|
folderType = @"otherUsers";
|
|
|
|
else if ([folderName isEqualToString: sharedFoldersName])
|
|
|
|
folderType = @"shared";
|
2014-01-10 20:06:53 +01:00
|
|
|
else
|
|
|
|
folderType = @"folder";
|
|
|
|
|
|
|
|
return folderType;
|
|
|
|
}
|
|
|
|
|
2016-09-14 21:57:49 +02:00
|
|
|
//
|
|
|
|
//
|
|
|
|
//
|
2014-11-14 21:21:59 +01:00
|
|
|
- (NSMutableDictionary *) _insertFolder: (NSString *) folderPath
|
|
|
|
foldersList: (NSMutableArray *) theFolders
|
2014-01-10 20:06:53 +01:00
|
|
|
{
|
|
|
|
NSArray *pathComponents;
|
2016-02-10 16:00:59 +01:00
|
|
|
NSMutableArray *folders, *flags;
|
2014-11-14 21:21:59 +01:00
|
|
|
NSMutableDictionary *currentFolder, *parentFolder, *folder;
|
2016-12-02 23:38:33 +01:00
|
|
|
NSString *currentFolderName, *currentPath, *sievePath, *fullName, *folderType;
|
2014-11-14 21:21:59 +01:00
|
|
|
SOGoUserManager *userManager;
|
2016-09-14 21:57:49 +02:00
|
|
|
|
|
|
|
BOOL last, isOtherUsersFolder, parentIsOtherUsersFolder, isSubscribed;
|
2014-11-14 21:21:59 +01:00
|
|
|
int i, j, count;
|
|
|
|
|
|
|
|
parentFolder = nil;
|
|
|
|
parentIsOtherUsersFolder = NO;
|
|
|
|
pathComponents = [folderPath pathComponents];
|
|
|
|
count = [pathComponents count];
|
|
|
|
|
|
|
|
// Make sure all ancestors exist.
|
|
|
|
// The variable folderPath is something like '/INBOX/Junk' so pathComponents becomes ('/', 'INBOX', 'Junk').
|
|
|
|
// That's why we always ignore the first element
|
|
|
|
for (i = 1; i < count; i++)
|
|
|
|
{
|
|
|
|
last = ((count - i) == 1);
|
|
|
|
folder = nil;
|
|
|
|
currentPath = [[[pathComponents subarrayWithRange: NSMakeRange(0,i+1)] componentsJoinedByString: @"/"] substringFromIndex: 2];
|
|
|
|
|
|
|
|
// Search for the current path in the children of the parent folder.
|
|
|
|
// For the first iteration, take the parent folder passed as argument.
|
|
|
|
if (parentFolder)
|
2016-09-14 21:57:49 +02:00
|
|
|
folders = [parentFolder objectForKey: @"children"];
|
2014-11-14 21:21:59 +01:00
|
|
|
else
|
2016-09-14 21:57:49 +02:00
|
|
|
folders = theFolders;
|
2014-01-10 20:06:53 +01:00
|
|
|
|
2014-11-14 21:21:59 +01:00
|
|
|
for (j = 0; j < [folders count]; j++)
|
|
|
|
{
|
|
|
|
currentFolder = [folders objectAtIndex: j];
|
|
|
|
if ([currentPath isEqualToString: [currentFolder objectForKey: @"path"]])
|
|
|
|
{
|
|
|
|
folder = currentFolder;
|
|
|
|
// Make sure all branches are ready to receive children
|
|
|
|
if (!last && ![folder objectForKey: @"children"])
|
|
|
|
[folder setObject: [NSMutableArray array] forKey: @"children"];
|
2016-09-14 21:57:49 +02:00
|
|
|
|
|
|
|
break;
|
2014-11-14 21:21:59 +01:00
|
|
|
}
|
|
|
|
}
|
2014-01-10 20:06:53 +01:00
|
|
|
|
2014-11-14 21:21:59 +01:00
|
|
|
// Check if the current folder is the "Other users" folder (shared mailboxes)
|
|
|
|
currentFolderName = [[pathComponents objectAtIndex: i] stringByDecodingImap4FolderName];
|
|
|
|
if (otherUsersFolderName
|
|
|
|
&& [currentFolderName caseInsensitiveCompare: otherUsersFolderName] == NSOrderedSame)
|
2016-09-14 21:57:49 +02:00
|
|
|
isOtherUsersFolder = YES;
|
2014-11-14 21:21:59 +01:00
|
|
|
else
|
2016-09-14 21:57:49 +02:00
|
|
|
isOtherUsersFolder = NO;
|
2014-11-14 21:21:59 +01:00
|
|
|
|
|
|
|
if (folder == nil)
|
|
|
|
{
|
|
|
|
// Folder was not found; create it and add it to the folders list
|
|
|
|
if (parentIsOtherUsersFolder)
|
|
|
|
{
|
|
|
|
// Parent folder is the "Other users" folder; translate the user's mailbox name
|
|
|
|
// to the full name of the person
|
|
|
|
userManager = [SOGoUserManager sharedUserManager];
|
2016-06-17 20:28:03 +02:00
|
|
|
if ((fullName = [userManager getCNForUID: currentFolderName]) && [fullName length])
|
2014-11-14 21:21:59 +01:00
|
|
|
currentFolderName = fullName;
|
2016-06-17 20:28:03 +02:00
|
|
|
else if ((fullName = [userManager getEmailForUID: currentFolderName]) && [fullName length])
|
|
|
|
currentFolderName = fullName;
|
2014-11-14 21:21:59 +01:00
|
|
|
}
|
|
|
|
else if (isOtherUsersFolder)
|
2016-09-14 21:57:49 +02:00
|
|
|
currentFolderName = [self labelForKey: @"OtherUsersFolderName"];
|
2014-11-14 21:21:59 +01:00
|
|
|
else if (sharedFoldersName
|
|
|
|
&& [currentFolderName caseInsensitiveCompare: sharedFoldersName] == NSOrderedSame)
|
2016-09-14 21:57:49 +02:00
|
|
|
currentFolderName = [self labelForKey: @"SharedFoldersName"];
|
2014-11-14 21:21:59 +01:00
|
|
|
|
2016-02-10 16:00:59 +01:00
|
|
|
flags = [NSMutableArray array];;
|
|
|
|
|
2014-11-14 21:21:59 +01:00
|
|
|
if (last)
|
2016-02-10 16:00:59 +01:00
|
|
|
folderType = [self _folderType: currentPath
|
|
|
|
flags: flags];
|
2014-11-14 21:21:59 +01:00
|
|
|
else
|
|
|
|
folderType = @"additional";
|
|
|
|
|
2016-09-14 21:57:49 +02:00
|
|
|
if ([subscribedFolders objectForKey: folderPath])
|
|
|
|
isSubscribed = YES;
|
|
|
|
else
|
|
|
|
isSubscribed = NO;
|
|
|
|
|
2014-11-14 21:21:59 +01:00
|
|
|
folder = [NSMutableDictionary dictionaryWithObjectsAndKeys:
|
|
|
|
currentPath, @"path",
|
2016-09-14 21:57:49 +02:00
|
|
|
folderType, @"type",
|
|
|
|
currentFolderName, @"name",
|
|
|
|
[NSMutableArray array], @"children",
|
|
|
|
flags, @"flags",
|
|
|
|
[NSNumber numberWithBool: isSubscribed], @"subscribed",
|
|
|
|
nil];
|
2016-12-02 23:38:33 +01:00
|
|
|
|
|
|
|
if (sieveFolderUTF8Encoding)
|
|
|
|
sievePath = [folderPath stringByDecodingImap4FolderName];
|
|
|
|
else
|
|
|
|
sievePath = folderPath;
|
|
|
|
[folder setObject: [sievePath substringFromIndex: 1] forKey: @"sievePath"];
|
|
|
|
|
2014-11-14 21:21:59 +01:00
|
|
|
// Either add this new folder to its parent or the list of root folders
|
|
|
|
[folders addObject: folder];
|
|
|
|
}
|
|
|
|
|
|
|
|
parentFolder = folder;
|
|
|
|
parentIsOtherUsersFolder = isOtherUsersFolder;
|
2014-01-10 20:06:53 +01:00
|
|
|
}
|
2014-11-14 21:21:59 +01:00
|
|
|
|
|
|
|
return parentFolder;
|
2014-01-10 20:06:53 +01:00
|
|
|
}
|
2006-06-15 21:34:10 +02:00
|
|
|
|
2014-01-10 20:06:53 +01:00
|
|
|
//
|
2014-11-14 21:21:59 +01:00
|
|
|
// Return a tree representation of the mailboxes
|
2014-01-10 20:06:53 +01:00
|
|
|
//
|
2016-09-14 21:57:49 +02:00
|
|
|
- (NSArray *) allFoldersMetadata: (SOGoMailListingMode) theListingMode
|
2014-01-10 20:06:53 +01:00
|
|
|
{
|
2014-11-14 21:21:59 +01:00
|
|
|
NSString *currentFolder;
|
|
|
|
NSMutableArray *folders;
|
2014-01-10 20:06:53 +01:00
|
|
|
NSEnumerator *rawFolders;
|
|
|
|
NSAutoreleasePool *pool;
|
|
|
|
NSArray *allFolderPaths;
|
|
|
|
|
2016-09-14 21:57:49 +02:00
|
|
|
allFolderPaths = [self allFolderPaths: theListingMode];
|
2014-01-10 20:06:53 +01:00
|
|
|
rawFolders = [allFolderPaths objectEnumerator];
|
|
|
|
folders = [NSMutableArray array];
|
2014-11-14 21:21:59 +01:00
|
|
|
|
2014-01-10 20:06:53 +01:00
|
|
|
while ((currentFolder = [rawFolders nextObject]))
|
|
|
|
{
|
|
|
|
// Using a local pool to avoid using too many file descriptors. This could
|
|
|
|
// happen with tons of mailboxes under "Other Users" as LDAP connections
|
|
|
|
// are never reused and "autoreleased" at the end. This loop would consume
|
|
|
|
// lots of LDAP connections during its execution.
|
|
|
|
pool = [[NSAutoreleasePool alloc] init];
|
|
|
|
|
2014-11-14 21:21:59 +01:00
|
|
|
// Insert folder into folders tree
|
|
|
|
[self _insertFolder: currentFolder
|
|
|
|
foldersList: folders];
|
2014-01-10 20:06:53 +01:00
|
|
|
|
|
|
|
[pool release];
|
|
|
|
}
|
|
|
|
|
|
|
|
return folders;
|
|
|
|
}
|
|
|
|
|
2010-08-03 20:52:49 +02:00
|
|
|
- (NSDictionary *) _mailAccount
|
2008-02-08 00:09:12 +01:00
|
|
|
{
|
|
|
|
NSDictionary *mailAccount;
|
2010-08-03 20:52:49 +02:00
|
|
|
NSArray *accounts;
|
|
|
|
SOGoUser *user;
|
2008-02-08 00:09:12 +01:00
|
|
|
|
2010-08-03 20:52:49 +02:00
|
|
|
user = [SOGoUser userWithLogin: [self ownerInContext: nil]];
|
|
|
|
accounts = [user mailAccounts];
|
|
|
|
mailAccount = [accounts objectAtIndex: [nameInContainer intValue]];
|
2008-02-08 00:09:12 +01:00
|
|
|
|
2010-08-03 20:52:49 +02:00
|
|
|
return mailAccount;
|
2008-02-08 00:09:12 +01:00
|
|
|
}
|
|
|
|
|
2010-09-30 20:05:14 +02:00
|
|
|
- (void) _appendDelegatorIdentities
|
|
|
|
{
|
|
|
|
NSArray *delegators;
|
|
|
|
SOGoUser *delegatorUser;
|
|
|
|
NSDictionary *delegatorAccount;
|
|
|
|
NSInteger count, max;
|
|
|
|
|
|
|
|
delegators = [[SOGoUser userWithLogin: owner] mailDelegators];
|
|
|
|
max = [delegators count];
|
|
|
|
for (count = 0; count < max; count++)
|
|
|
|
{
|
|
|
|
delegatorUser = [SOGoUser
|
|
|
|
userWithLogin: [delegators objectAtIndex: count]];
|
|
|
|
if (delegatorUser)
|
|
|
|
{
|
|
|
|
delegatorAccount = [[delegatorUser mailAccounts]
|
|
|
|
objectAtIndex: 0];
|
|
|
|
[identities addObjectsFromArray:
|
|
|
|
[delegatorAccount objectForKey: @"identities"]];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-08-06 16:25:59 +02:00
|
|
|
- (NSArray *) identities
|
|
|
|
{
|
2010-09-30 20:05:14 +02:00
|
|
|
if (!identities)
|
|
|
|
{
|
|
|
|
identities = [[[self _mailAccount] objectForKey: @"identities"]
|
|
|
|
mutableCopy];
|
|
|
|
if ([nameInContainer isEqualToString: @"0"])
|
|
|
|
[self _appendDelegatorIdentities];
|
|
|
|
}
|
|
|
|
|
|
|
|
return identities;
|
2010-08-06 16:25:59 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString *) signature
|
|
|
|
{
|
|
|
|
NSString *signature;
|
|
|
|
|
2010-09-30 20:05:14 +02:00
|
|
|
[self identities];
|
2010-08-06 16:25:59 +02:00
|
|
|
if ([identities count] > 0)
|
|
|
|
signature = [[identities objectAtIndex: 0] objectForKey: @"signature"];
|
|
|
|
else
|
|
|
|
signature = nil;
|
|
|
|
|
|
|
|
return signature;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString *) encryption
|
|
|
|
{
|
|
|
|
NSString *encryption;
|
|
|
|
|
|
|
|
encryption = [[self _mailAccount] objectForKey: @"encryption"];
|
|
|
|
if (![encryption length])
|
|
|
|
encryption = @"none";
|
|
|
|
|
|
|
|
return encryption;
|
|
|
|
}
|
|
|
|
|
2007-07-10 21:23:39 +02:00
|
|
|
- (NSMutableString *) imap4URLString
|
|
|
|
{
|
2010-08-03 20:52:49 +02:00
|
|
|
NSMutableString *imap4URLString;
|
|
|
|
NSDictionary *mailAccount;
|
|
|
|
NSString *encryption, *protocol, *username, *escUsername;
|
|
|
|
int defaultPort, port;
|
2007-07-10 21:23:39 +02:00
|
|
|
|
2010-08-03 20:52:49 +02:00
|
|
|
mailAccount = [self _mailAccount];
|
|
|
|
encryption = [mailAccount objectForKey: @"encryption"];
|
2010-09-02 18:47:06 +02:00
|
|
|
defaultPort = 143;
|
2010-09-03 14:21:48 +02:00
|
|
|
protocol = @"imap";
|
2010-08-24 15:27:02 +02:00
|
|
|
|
2010-09-02 18:47:06 +02:00
|
|
|
if ([encryption isEqualToString: @"ssl"])
|
2010-09-03 14:21:48 +02:00
|
|
|
{
|
|
|
|
protocol = @"imaps";
|
|
|
|
defaultPort = 993;
|
|
|
|
}
|
|
|
|
else if ([encryption isEqualToString: @"tls"])
|
|
|
|
{
|
|
|
|
protocol = @"imaps";
|
|
|
|
}
|
2010-08-25 15:09:25 +02:00
|
|
|
|
2010-08-03 20:52:49 +02:00
|
|
|
username = [mailAccount objectForKey: @"userName"];
|
|
|
|
escUsername
|
|
|
|
= [[username stringByEscapingURL] stringByReplacingString: @"@"
|
|
|
|
withString: @"%40"];
|
|
|
|
imap4URLString = [NSMutableString stringWithFormat: @"%@://%@@%@",
|
|
|
|
protocol, escUsername,
|
|
|
|
[mailAccount objectForKey: @"serverName"]];
|
|
|
|
port = [[mailAccount objectForKey: @"port"] intValue];
|
|
|
|
if (port && port != defaultPort)
|
|
|
|
[imap4URLString appendFormat: @":%d", port];
|
2010-09-02 18:47:06 +02:00
|
|
|
|
2010-08-03 20:52:49 +02:00
|
|
|
[imap4URLString appendString: @"/"];
|
2010-09-02 18:47:06 +02:00
|
|
|
|
2010-08-03 20:52:49 +02:00
|
|
|
return imap4URLString;
|
2006-06-15 21:34:10 +02:00
|
|
|
}
|
|
|
|
|
2007-10-31 16:55:47 +01:00
|
|
|
- (NSMutableString *) traversalFromMailAccount
|
|
|
|
{
|
|
|
|
return [NSMutableString string];
|
|
|
|
}
|
|
|
|
|
2010-12-28 18:42:50 +01:00
|
|
|
//
|
|
|
|
// Extract password from basic authentication.
|
|
|
|
//
|
2010-08-06 16:25:59 +02:00
|
|
|
- (NSString *) imap4PasswordRenewed: (BOOL) renewed
|
2007-07-10 21:23:39 +02:00
|
|
|
{
|
2010-08-06 16:25:59 +02:00
|
|
|
NSString *password;
|
2010-12-28 18:42:50 +01:00
|
|
|
NSURL *imapURL;
|
2010-08-06 16:25:59 +02:00
|
|
|
|
2010-12-28 18:42:50 +01:00
|
|
|
// Default account - ie., the account that is provided with a default
|
|
|
|
// SOGo installation. User-added IMAP accounts will have name >= 1.
|
2010-08-06 16:25:59 +02:00
|
|
|
if ([nameInContainer isEqualToString: @"0"])
|
|
|
|
{
|
|
|
|
imapURL = [self imap4URL];
|
|
|
|
|
|
|
|
password = [[self authenticatorInContext: context]
|
|
|
|
imapPasswordInContext: context
|
2011-02-17 21:55:19 +01:00
|
|
|
forURL: imapURL
|
2010-08-06 16:25:59 +02:00
|
|
|
forceRenew: renewed];
|
|
|
|
if (!password)
|
|
|
|
[self errorWithFormat: @"no IMAP4 password available"];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
password = [[self _mailAccount] objectForKey: @"password"];
|
|
|
|
if (!password)
|
|
|
|
password = @"";
|
|
|
|
}
|
|
|
|
|
|
|
|
return password;
|
2006-06-15 21:34:10 +02:00
|
|
|
}
|
|
|
|
|
2014-05-27 20:44:57 +02:00
|
|
|
|
|
|
|
- (NSDictionary *) imapFolderGUIDs
|
|
|
|
{
|
2016-03-28 17:13:41 +02:00
|
|
|
NSDictionary *result, *nresult;
|
2014-05-27 20:44:57 +02:00
|
|
|
NSMutableDictionary *folders;
|
|
|
|
NGImap4Client *client;
|
2016-01-07 15:17:35 +01:00
|
|
|
SOGoUserDefaults *ud;
|
2014-05-27 20:44:57 +02:00
|
|
|
NSArray *folderList;
|
|
|
|
NSEnumerator *e;
|
|
|
|
NSString *guid;
|
2016-03-28 17:13:41 +02:00
|
|
|
id currentFolder;
|
2014-05-27 20:44:57 +02:00
|
|
|
|
2014-12-29 18:43:20 +01:00
|
|
|
BOOL hasAnnotatemore;
|
2014-05-27 20:44:57 +02:00
|
|
|
|
2016-01-07 15:17:35 +01:00
|
|
|
ud = [[context activeUser] userDefaults];
|
|
|
|
|
2016-01-22 16:30:27 +01:00
|
|
|
// We skip the Junk folder here, as EAS doesn't know about this
|
2016-01-07 15:17:35 +01:00
|
|
|
if ([ud synchronizeOnlyDefaultMailFolders])
|
|
|
|
folderList = [[NSArray arrayWithObjects:
|
|
|
|
[self inboxFolderNameInContext: context],
|
|
|
|
[self draftsFolderNameInContext: context],
|
|
|
|
[self sentFolderNameInContext: context],
|
|
|
|
[self trashFolderNameInContext: context],
|
|
|
|
nil] stringsWithFormat: @"/%@"];
|
|
|
|
else
|
2016-09-14 21:57:49 +02:00
|
|
|
folderList = [self allFolderPaths: SOGoMailStandardListing];
|
2014-05-27 20:44:57 +02:00
|
|
|
|
|
|
|
folders = [NSMutableDictionary dictionary];
|
|
|
|
|
|
|
|
client = [[self imap4Connection] client];
|
2014-12-16 01:34:51 +01:00
|
|
|
hasAnnotatemore = [self hasCapability: @"annotatemore"];
|
2014-12-08 16:29:23 +01:00
|
|
|
|
2014-12-16 01:34:51 +01:00
|
|
|
if (hasAnnotatemore)
|
2014-12-29 18:43:20 +01:00
|
|
|
result = [client annotation: @"*" entryName: @"/comment" attributeName: @"value.priv"];
|
2014-12-16 01:34:51 +01:00
|
|
|
else
|
2014-12-29 18:43:20 +01:00
|
|
|
result = [client lstatus: @"*" flags: [NSArray arrayWithObjects: @"x-guid", nil]];
|
|
|
|
|
2014-05-27 20:44:57 +02:00
|
|
|
e = [folderList objectEnumerator];
|
2014-11-14 21:21:59 +01:00
|
|
|
|
2016-03-28 17:13:41 +02:00
|
|
|
while ((currentFolder = [[e nextObject] substringFromIndex: 1]))
|
2014-05-27 20:44:57 +02:00
|
|
|
{
|
2014-12-16 01:34:51 +01:00
|
|
|
if (hasAnnotatemore)
|
2016-03-28 17:13:41 +02:00
|
|
|
guid = [[[[result objectForKey: @"FolderList"] objectForKey: currentFolder] objectForKey: @"/comment"] objectForKey: @"value.priv"];
|
2014-12-16 01:34:51 +01:00
|
|
|
else
|
2016-03-28 17:13:41 +02:00
|
|
|
guid = [[[result objectForKey: @"status"] objectForKey: currentFolder] objectForKey: @"x-guid"];
|
2014-12-29 18:43:20 +01:00
|
|
|
|
2016-10-25 17:18:49 +02:00
|
|
|
if (!guid || [guid isNull])
|
2014-05-27 20:44:57 +02:00
|
|
|
{
|
2014-12-08 16:29:23 +01:00
|
|
|
// Don't generate a GUID for "Other users" and "Shared" namespace folders - user foldername instead
|
2016-03-28 17:13:41 +02:00
|
|
|
if ((otherUsersFolderName && [currentFolder isEqualToString: otherUsersFolderName]) ||
|
|
|
|
(sharedFoldersName && [currentFolder isEqualToString: sharedFoldersName]))
|
|
|
|
guid = [NSString stringWithFormat: @"%@", currentFolder];
|
|
|
|
// If Dovecot is used with mail_shared_explicit_inbox = yes we have to generate a guid for "shared/user".
|
|
|
|
// * LIST (\NonExistent \HasChildren) "/" shared
|
|
|
|
// * LIST (\NonExistent \HasChildren) "/" shared/jdoe@example.com
|
|
|
|
// * LIST (\HasNoChildren) "/" shared/jdoe@example.com/INBOX
|
2016-11-24 17:39:05 +01:00
|
|
|
else if (!hasAnnotatemore &&
|
|
|
|
([[[result objectForKey: @"list"] objectForKey: currentFolder] indexOfObject: @"nonexistent"] != NSNotFound &&
|
|
|
|
[[[result objectForKey: @"list"] objectForKey: currentFolder] indexOfObject: @"haschildren"] != NSNotFound))
|
2016-03-28 17:13:41 +02:00
|
|
|
guid = [NSString stringWithFormat: @"%@", currentFolder];
|
|
|
|
else
|
2014-12-29 18:43:20 +01:00
|
|
|
{
|
2016-03-28 17:13:41 +02:00
|
|
|
// If folder doesn't exists - ignore it.
|
|
|
|
nresult = [client status: currentFolder
|
|
|
|
flags: [NSArray arrayWithObject: @"UIDVALIDITY"]];
|
|
|
|
if (![[nresult valueForKey: @"result"] boolValue])
|
|
|
|
continue;
|
|
|
|
else if (hasAnnotatemore)
|
|
|
|
{
|
|
|
|
guid = [[NSProcessInfo processInfo] globallyUniqueString];
|
|
|
|
nresult = [client annotation: currentFolder entryName: @"/comment" attributeName: @"value.priv" attributeValue: guid];
|
|
|
|
}
|
|
|
|
|
|
|
|
// setannotation failed or annotatemore is not available
|
|
|
|
if ((hasAnnotatemore && ![[nresult objectForKey: @"result"] boolValue]) || !hasAnnotatemore)
|
|
|
|
guid = [NSString stringWithFormat: @"%@", currentFolder];
|
2014-12-29 18:43:20 +01:00
|
|
|
}
|
2014-05-27 20:44:57 +02:00
|
|
|
}
|
2014-12-29 18:43:20 +01:00
|
|
|
|
2016-03-28 17:13:41 +02:00
|
|
|
[folders setObject: [NSString stringWithFormat: @"folder%@", guid] forKey: [NSString stringWithFormat: @"folder%@", currentFolder]];
|
2014-05-27 20:44:57 +02:00
|
|
|
}
|
2014-12-29 18:43:20 +01:00
|
|
|
|
2014-05-27 20:44:57 +02:00
|
|
|
return folders;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-06-15 21:34:10 +02:00
|
|
|
/* name lookup */
|
|
|
|
|
2016-02-26 20:59:54 +01:00
|
|
|
- (id) lookupNameByPaths: (NSArray *) _paths
|
|
|
|
inContext: (id)_ctx
|
|
|
|
acquire: (BOOL) _flag
|
|
|
|
{
|
|
|
|
NSString *folderName;
|
|
|
|
NSUInteger count, max;
|
|
|
|
SOGoMailBaseObject *folder;
|
|
|
|
|
|
|
|
max = [_paths count];
|
|
|
|
folder = self;
|
|
|
|
for (count = 0; folder && count < max; count++)
|
|
|
|
{
|
|
|
|
folderName = [_paths objectAtIndex: count];
|
|
|
|
folder = [folder lookupName: folderName inContext: _ctx acquire: _flag];
|
|
|
|
}
|
|
|
|
|
|
|
|
return folder;
|
|
|
|
}
|
|
|
|
|
2007-07-30 17:55:28 +02:00
|
|
|
- (id) lookupName: (NSString *) _key
|
|
|
|
inContext: (id)_ctx
|
|
|
|
acquire: (BOOL) _flag
|
|
|
|
{
|
2007-11-01 13:48:19 +01:00
|
|
|
NSString *folderName;
|
2010-01-05 23:34:35 +01:00
|
|
|
NSMutableArray *namespaces;
|
2007-11-01 13:48:19 +01:00
|
|
|
Class klazz;
|
2006-06-15 21:34:10 +02:00
|
|
|
id obj;
|
2007-07-30 17:55:28 +02:00
|
|
|
|
2010-01-05 23:34:35 +01:00
|
|
|
[[[self imap4Connection] client] namespace];
|
|
|
|
|
2007-07-30 17:55:28 +02:00
|
|
|
if ([_key hasPrefix: @"folder"])
|
|
|
|
{
|
2010-01-05 23:34:35 +01:00
|
|
|
folderName = [[_key substringFromIndex: 6] fromCSSIdentifier];
|
|
|
|
|
|
|
|
namespaces = [NSMutableArray array];
|
|
|
|
[self _appendNamespaces: namespaces];
|
|
|
|
if ([namespaces containsObject: folderName])
|
|
|
|
klazz = [SOGoMailNamespace class];
|
2007-11-01 13:48:19 +01:00
|
|
|
else if ([folderName
|
|
|
|
isEqualToString: [self draftsFolderNameInContext: _ctx]])
|
|
|
|
klazz = [SOGoDraftsFolder class];
|
2012-04-30 19:19:00 +02:00
|
|
|
else if ([folderName
|
|
|
|
isEqualToString: [self sentFolderNameInContext: _ctx]])
|
|
|
|
klazz = [SOGoSentFolder class];
|
2007-11-01 13:48:19 +01:00
|
|
|
else if ([folderName
|
|
|
|
isEqualToString: [self trashFolderNameInContext: _ctx]])
|
|
|
|
klazz = [SOGoTrashFolder class];
|
2016-01-22 16:30:27 +01:00
|
|
|
else if ([folderName
|
|
|
|
isEqualToString: [self junkFolderNameInContext: _ctx]])
|
|
|
|
klazz = [SOGoJunkFolder class];
|
2007-07-30 17:55:28 +02:00
|
|
|
else
|
2007-11-01 13:48:19 +01:00
|
|
|
klazz = [SOGoMailFolder class];
|
|
|
|
|
|
|
|
obj = [klazz objectWithName: _key inContainer: self];
|
2007-07-30 17:55:28 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
obj = [super lookupName: _key inContext: _ctx acquire: NO];
|
2006-06-15 21:34:10 +02:00
|
|
|
|
|
|
|
/* return 404 to stop acquisition */
|
2007-07-30 17:55:28 +02:00
|
|
|
if (!obj)
|
|
|
|
obj = [NSException exceptionWithHTTPStatus: 404 /* Not Found */];
|
|
|
|
|
|
|
|
return obj;
|
2006-06-15 21:34:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* special folders */
|
|
|
|
|
2007-07-30 20:01:25 +02:00
|
|
|
- (NSString *) inboxFolderNameInContext: (id)_ctx
|
|
|
|
{
|
2007-08-01 20:32:00 +02:00
|
|
|
/* cannot be changed in Cyrus ? */
|
2007-11-01 13:48:19 +01:00
|
|
|
return inboxFolderName;
|
2006-06-15 21:34:10 +02:00
|
|
|
}
|
2007-07-30 20:01:25 +02:00
|
|
|
|
2007-10-17 23:03:39 +02:00
|
|
|
- (NSString *) _userFolderNameWithPurpose: (NSString *) purpose
|
|
|
|
{
|
2009-11-29 05:19:32 +01:00
|
|
|
SOGoUser *user;
|
2010-08-06 16:25:59 +02:00
|
|
|
NSArray *accounts;
|
|
|
|
int accountIdx;
|
|
|
|
NSDictionary *account;
|
|
|
|
NSString *folderName;
|
2007-10-17 23:03:39 +02:00
|
|
|
|
|
|
|
folderName = nil;
|
2009-11-29 05:19:32 +01:00
|
|
|
|
2010-08-06 16:25:59 +02:00
|
|
|
user = [SOGoUser userWithLogin: [self ownerInContext: nil]];
|
|
|
|
accounts = [user mailAccounts];
|
|
|
|
accountIdx = [nameInContainer intValue];
|
|
|
|
account = [accounts objectAtIndex: accountIdx];
|
2014-11-28 17:20:49 +01:00
|
|
|
folderName = [[account objectForKey: @"specialMailboxes"]
|
2010-08-06 16:25:59 +02:00
|
|
|
objectForKey: purpose];
|
|
|
|
if (!folderName && accountIdx > 0)
|
2009-11-29 05:19:32 +01:00
|
|
|
{
|
2010-08-06 16:25:59 +02:00
|
|
|
account = [accounts objectAtIndex: 0];
|
2014-11-28 17:20:49 +01:00
|
|
|
folderName = [[account objectForKey: @"specialMailboxes"]
|
2010-08-06 16:25:59 +02:00
|
|
|
objectForKey: purpose];
|
2009-11-29 05:19:32 +01:00
|
|
|
}
|
2007-10-17 23:03:39 +02:00
|
|
|
|
|
|
|
return folderName;
|
|
|
|
}
|
|
|
|
|
2007-07-30 20:01:25 +02:00
|
|
|
- (NSString *) draftsFolderNameInContext: (id) _ctx
|
|
|
|
{
|
2009-11-29 05:19:32 +01:00
|
|
|
return [self _userFolderNameWithPurpose: @"Drafts"];
|
2006-06-15 21:34:10 +02:00
|
|
|
}
|
2007-07-30 20:01:25 +02:00
|
|
|
|
2007-08-01 20:32:00 +02:00
|
|
|
- (NSString *) sentFolderNameInContext: (id)_ctx
|
2007-07-30 20:01:25 +02:00
|
|
|
{
|
2009-11-29 05:19:32 +01:00
|
|
|
return [self _userFolderNameWithPurpose: @"Sent"];
|
2006-06-15 21:34:10 +02:00
|
|
|
}
|
2007-07-30 20:01:25 +02:00
|
|
|
|
2007-08-01 20:32:00 +02:00
|
|
|
- (NSString *) trashFolderNameInContext: (id)_ctx
|
2007-07-30 20:01:25 +02:00
|
|
|
{
|
2009-11-29 05:19:32 +01:00
|
|
|
return [self _userFolderNameWithPurpose: @"Trash"];
|
2007-11-01 13:48:19 +01:00
|
|
|
}
|
|
|
|
|
2016-01-22 16:30:27 +01:00
|
|
|
- (NSString *) junkFolderNameInContext: (id)_ctx
|
|
|
|
{
|
|
|
|
return [self _userFolderNameWithPurpose: @"Junk"];
|
|
|
|
}
|
|
|
|
|
2011-04-21 18:02:31 +02:00
|
|
|
- (NSString *) otherUsersFolderNameInContext: (id)_ctx
|
|
|
|
{
|
|
|
|
return otherUsersFolderName;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString *) sharedFoldersNameInContext: (id)_ctx
|
|
|
|
{
|
|
|
|
return sharedFoldersName;
|
|
|
|
}
|
|
|
|
|
2007-11-01 13:48:19 +01:00
|
|
|
- (id) folderWithTraversal: (NSString *) traversal
|
|
|
|
andClassName: (NSString *) className
|
|
|
|
{
|
|
|
|
NSArray *paths;
|
|
|
|
NSString *currentName;
|
|
|
|
id currentContainer;
|
|
|
|
unsigned int count, max;
|
|
|
|
Class clazz;
|
|
|
|
|
|
|
|
currentContainer = self;
|
|
|
|
paths = [traversal componentsSeparatedByString: @"/"];
|
|
|
|
|
|
|
|
if (!className)
|
|
|
|
clazz = [SOGoMailFolder class];
|
|
|
|
else
|
|
|
|
clazz = NSClassFromString (className);
|
|
|
|
|
|
|
|
max = [paths count];
|
|
|
|
for (count = 0; count < max - 1; count++)
|
|
|
|
{
|
|
|
|
currentName = [NSString stringWithFormat: @"folder%@",
|
|
|
|
[paths objectAtIndex: count]];
|
|
|
|
currentContainer = [SOGoMailFolder objectWithName: currentName
|
|
|
|
inContainer: currentContainer];
|
|
|
|
}
|
|
|
|
currentName = [NSString stringWithFormat: @"folder%@",
|
|
|
|
[paths objectAtIndex: max - 1]];
|
|
|
|
|
|
|
|
return [clazz objectWithName: currentName inContainer: currentContainer];
|
2006-06-15 21:34:10 +02:00
|
|
|
}
|
|
|
|
|
2007-07-30 20:01:25 +02:00
|
|
|
- (SOGoMailFolder *) inboxFolderInContext: (id) _ctx
|
|
|
|
{
|
2006-06-15 21:34:10 +02:00
|
|
|
// TODO: use some profile to determine real location, use a -traverse lookup
|
2007-07-30 20:01:25 +02:00
|
|
|
if (!inboxFolder)
|
|
|
|
{
|
2007-11-01 13:48:19 +01:00
|
|
|
inboxFolder
|
|
|
|
= [self folderWithTraversal: [self inboxFolderNameInContext: _ctx]
|
|
|
|
andClassName: nil];
|
2007-07-30 20:01:25 +02:00
|
|
|
[inboxFolder retain];
|
|
|
|
}
|
|
|
|
|
|
|
|
return inboxFolder;
|
2006-06-15 21:34:10 +02:00
|
|
|
}
|
|
|
|
|
2007-08-15 22:22:24 +02:00
|
|
|
- (SOGoDraftsFolder *) draftsFolderInContext: (id) _ctx
|
|
|
|
{
|
|
|
|
// TODO: use some profile to determine real location, use a -traverse lookup
|
|
|
|
|
|
|
|
if (!draftsFolder)
|
|
|
|
{
|
2007-10-23 19:46:08 +02:00
|
|
|
draftsFolder
|
2007-11-01 13:48:19 +01:00
|
|
|
= [self folderWithTraversal: [self draftsFolderNameInContext: _ctx]
|
|
|
|
andClassName: @"SOGoDraftsFolder"];
|
2007-08-15 22:22:24 +02:00
|
|
|
[draftsFolder retain];
|
|
|
|
}
|
|
|
|
|
|
|
|
return draftsFolder;
|
|
|
|
}
|
|
|
|
|
2007-11-01 13:48:19 +01:00
|
|
|
- (SOGoSentFolder *) sentFolderInContext: (id) _ctx
|
2007-07-30 20:01:25 +02:00
|
|
|
{
|
2006-06-15 21:34:10 +02:00
|
|
|
// TODO: use some profile to determine real location, use a -traverse lookup
|
2007-07-30 20:01:25 +02:00
|
|
|
|
|
|
|
if (!sentFolder)
|
|
|
|
{
|
2007-11-01 13:48:19 +01:00
|
|
|
sentFolder
|
|
|
|
= [self folderWithTraversal: [self sentFolderNameInContext: _ctx]
|
|
|
|
andClassName: @"SOGoSentFolder"];
|
2007-07-30 20:01:25 +02:00
|
|
|
[sentFolder retain];
|
|
|
|
}
|
|
|
|
|
|
|
|
return sentFolder;
|
2006-06-15 21:34:10 +02:00
|
|
|
}
|
|
|
|
|
2007-11-01 13:48:19 +01:00
|
|
|
- (SOGoTrashFolder *) trashFolderInContext: (id) _ctx
|
2007-07-30 20:01:25 +02:00
|
|
|
{
|
|
|
|
if (!trashFolder)
|
|
|
|
{
|
2007-11-01 13:48:19 +01:00
|
|
|
trashFolder
|
|
|
|
= [self folderWithTraversal: [self trashFolderNameInContext: _ctx]
|
|
|
|
andClassName: @"SOGoTrashFolder"];
|
2007-07-30 20:01:25 +02:00
|
|
|
[trashFolder retain];
|
|
|
|
}
|
|
|
|
|
|
|
|
return trashFolder;
|
2006-06-15 21:34:10 +02:00
|
|
|
}
|
|
|
|
|
2016-01-22 16:30:27 +01:00
|
|
|
- (SOGoJunkFolder *) junkFolderInContext: (id) _ctx
|
|
|
|
{
|
|
|
|
if (!junkFolder)
|
|
|
|
{
|
|
|
|
junkFolder
|
|
|
|
= [self folderWithTraversal: [self junkFolderNameInContext: _ctx]
|
|
|
|
andClassName: @"SOGoJunkFolder"];
|
|
|
|
[trashFolder retain];
|
|
|
|
}
|
|
|
|
|
|
|
|
return junkFolder;
|
|
|
|
}
|
|
|
|
|
2010-09-30 20:05:14 +02:00
|
|
|
/* account delegation */
|
|
|
|
- (NSArray *) delegates
|
|
|
|
{
|
|
|
|
NSDictionary *mailSettings;
|
|
|
|
SOGoUser *ownerUser;
|
|
|
|
NSArray *delegates;
|
|
|
|
|
|
|
|
if ([nameInContainer isEqualToString: @"0"])
|
|
|
|
{
|
|
|
|
ownerUser = [SOGoUser userWithLogin: [self ownerInContext: context]];
|
|
|
|
mailSettings = [[ownerUser userSettings] objectForKey: @"Mail"];
|
|
|
|
delegates = [mailSettings objectForKey: @"DelegateTo"];
|
|
|
|
if (!delegates)
|
|
|
|
delegates = [NSArray array];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
delegates = nil;
|
|
|
|
|
|
|
|
return delegates;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) _setDelegates: (NSArray *) newDelegates
|
|
|
|
{
|
2017-05-08 17:23:32 +02:00
|
|
|
NSMutableDictionary *mailSettings;
|
2010-09-30 20:05:14 +02:00
|
|
|
SOGoUser *ownerUser;
|
|
|
|
SOGoUserSettings *settings;
|
|
|
|
|
|
|
|
ownerUser = [SOGoUser userWithLogin: [self ownerInContext: context]];
|
|
|
|
settings = [ownerUser userSettings];
|
2017-05-08 17:23:32 +02:00
|
|
|
mailSettings = [settings objectForKey: @"Mail"];
|
|
|
|
if (!mailSettings)
|
|
|
|
{
|
|
|
|
mailSettings = [NSMutableDictionary dictionaryWithCapacity: 1];
|
|
|
|
[settings setObject: mailSettings forKey: @"Mail"];
|
|
|
|
}
|
|
|
|
[mailSettings setObject: newDelegates
|
|
|
|
forKey: @"DelegateTo"];
|
2010-09-30 20:05:14 +02:00
|
|
|
[settings synchronize];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) addDelegates: (NSArray *) newDelegates
|
|
|
|
{
|
|
|
|
NSMutableArray *delegates;
|
|
|
|
NSInteger count, max;
|
|
|
|
NSString *currentDelegate;
|
|
|
|
SOGoUser *delegateUser;
|
|
|
|
|
|
|
|
if ([nameInContainer isEqualToString: @"0"])
|
|
|
|
{
|
|
|
|
delegates = [[self delegates] mutableCopy];
|
|
|
|
[delegates autorelease];
|
|
|
|
max = [newDelegates count];
|
|
|
|
for (count = 0; count < max; count++)
|
|
|
|
{
|
2016-07-06 21:35:09 +02:00
|
|
|
currentDelegate = [newDelegates objectAtIndex: count];
|
2010-09-30 20:05:14 +02:00
|
|
|
delegateUser = [SOGoUser userWithLogin: currentDelegate];
|
|
|
|
if (delegateUser)
|
|
|
|
{
|
|
|
|
[delegates addObjectUniquely: currentDelegate];
|
|
|
|
[delegateUser addMailDelegator: owner];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
[self _setDelegates: delegates];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) removeDelegates: (NSArray *) oldDelegates
|
|
|
|
{
|
|
|
|
NSMutableArray *delegates;
|
|
|
|
NSInteger count, max;
|
|
|
|
NSString *currentDelegate;
|
|
|
|
SOGoUser *delegateUser;
|
|
|
|
|
|
|
|
if ([nameInContainer isEqualToString: @"0"])
|
|
|
|
{
|
|
|
|
delegates = [[self delegates] mutableCopy];
|
|
|
|
[delegates autorelease];
|
|
|
|
max = [oldDelegates count];
|
|
|
|
for (count = 0; count < max; count++)
|
|
|
|
{
|
2016-07-06 21:35:09 +02:00
|
|
|
currentDelegate = [oldDelegates objectAtIndex: count];
|
2010-09-30 20:05:14 +02:00
|
|
|
delegateUser = [SOGoUser userWithLogin: currentDelegate];
|
2016-10-26 21:42:50 +02:00
|
|
|
[delegates removeObject: currentDelegate];
|
2010-09-30 20:05:14 +02:00
|
|
|
if (delegateUser)
|
2016-10-26 21:42:50 +02:00
|
|
|
[delegateUser removeMailDelegator: owner];
|
2010-09-30 20:05:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
[self _setDelegates: delegates];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-06-15 21:34:10 +02:00
|
|
|
/* WebDAV */
|
|
|
|
|
2007-06-01 07:45:12 +02:00
|
|
|
- (NSString *) davContentType
|
|
|
|
{
|
|
|
|
return @"httpd/unix-directory";
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL) davIsCollection
|
|
|
|
{
|
2006-06-15 21:34:10 +02:00
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
2007-06-01 07:45:12 +02:00
|
|
|
- (NSException *) davCreateCollection: (NSString *) _name
|
|
|
|
inContext: (id) _ctx
|
|
|
|
{
|
2006-06-15 21:34:10 +02:00
|
|
|
return [[self imap4Connection] createMailbox:_name atURL:[self imap4URL]];
|
|
|
|
}
|
|
|
|
|
2007-06-01 07:45:12 +02:00
|
|
|
- (NSString *) davDisplayName
|
|
|
|
{
|
2010-08-03 20:52:49 +02:00
|
|
|
return [[self _mailAccount] objectForKey: @"name"];
|
2006-06-15 21:34:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@end /* SOGoMailAccount */
|