sogo/OpenChange/MAPIStoreUserContext.m

415 lines
11 KiB
Objective-C

/* MAPIStoreUserContext.m - this file is part of SOGo
*
* Copyright (C) 2012-2016 Inverse inc
*
* 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/NSCharacterSet.h>
#import <Foundation/NSData.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSMapTable.h>
#import <Foundation/NSPropertyList.h>
#import <Foundation/NSThread.h>
#import <Foundation/NSTimeZone.h>
#import <Foundation/NSURL.h>
#import <NGObjWeb/WOContext.h>
#import <NGObjWeb/WOContext+SoObjects.h>
#import <NGImap4/NGImap4Connection.h>
#import <GDLContentStore/GCSChannelManager.h>
#import <GDLContentStore/GCSFolderManager.h>
#import <SOGo/SOGoDomainDefaults.h>
#import <SOGo/SOGoUser.h>
#import <SOGo/SOGoUserFolder.h>
#import <SOGo/NSString+Utilities.h>
#import <NGExtensions/NSObject+Logs.h>
#import <Mailer/SOGoMailAccount.h>
#import <Mailer/SOGoMailAccounts.h>
#import <SOGo/GCSSpecialQueries+SOGoCacheObject.h>
#import "MAPIApplication.h"
#import "MAPIStoreAuthenticator.h"
#import "MAPIStoreMapping.h"
#import "MAPIStoreUserContext.h"
static NSMapTable *contextsTable = nil;
@implementation MAPIStoreUserContext
+ (void) initialize
{
contextsTable = [NSMapTable mapTableWithStrongToWeakObjects];
[contextsTable retain];
}
+ (id) userContextWithUsername: (NSString *) username
andTDBIndexing: (struct indexing_context *) indexing;
{
id userContext;
userContext = [contextsTable objectForKey: username];
if (!userContext)
{
userContext = [[self alloc] initWithUsername: username
andTDBIndexing: indexing];
[userContext autorelease];
[contextsTable setObject: userContext forKey: username];
}
else
[userContext activate];
return userContext;
}
- (id) init
{
if ((self = [super init]))
{
username = nil;
sogoUser = nil;
userFolder = nil;
containersBag = [NSMutableArray new];
rootFolders = nil;
mapping = nil;
userDbTableExists = NO;
folderTableURL = nil;
authenticator = nil;
woContext = [WOContext contextWithRequest: nil];
[woContext retain];
}
return self;
}
- (NSString *) _readPasswordFile: (NSString *) path
{
NSString *password;
NSData *content;
password = nil;
content = [NSData dataWithContentsOfFile: path];
if (content)
{
password = [[NSString alloc] initWithData: content
encoding: NSUTF8StringEncoding];
[password autorelease];
password = [password stringByTrimmingCharactersInSet:
[NSCharacterSet characterSetWithCharactersInString: @"\r\n"]];
}
return password;
}
- (NSString *) _readUserPassword: (NSString *) newUsername
{
NSString *password, *path;
path = [NSString stringWithFormat: SAMBA_PRIVATE_DIR
@"/mapistore/%@/password", newUsername];
password = [self _readPasswordFile: path];
if (password == nil)
{
// Try to get master password
path = [NSString stringWithFormat: SAMBA_PRIVATE_DIR @"/mapistore/master.password"];
password = [self _readPasswordFile: path];
}
return password;
}
- (id) initWithUsername: (NSString *) newUsername
andTDBIndexing: (struct indexing_context *) indexing
{
NSString *userPassword;
if ((self = [self init]))
{
/* "username" will be retained by table */
username = newUsername;
if (indexing)
ASSIGN (mapping, [MAPIStoreMapping mappingForUsername: username
withIndexing: indexing]);
authenticator = [MAPIStoreAuthenticator new];
[authenticator setUsername: username];
/* TODO: very hackish (IMAP access) */
userPassword = [self _readUserPassword: newUsername];
if ([userPassword length] == 0)
userPassword = username;
[authenticator setPassword: userPassword];
// Activate the profile on initialization
[self activate];
}
return self;
}
- (void) dealloc
{
[userFolder release];
[containersBag release];
[rootFolders release];
[woContext release];
[authenticator release];
[mapping release];
[folderTableURL release];
[sogoUser release];
[contextsTable removeObjectForKey: username];
[super dealloc];
}
- (NSString *) username
{
return username;
}
- (SOGoUser *) sogoUser
{
if (!sogoUser)
ASSIGN (sogoUser, [SOGoUser userWithLogin: username]);
return sogoUser;
}
- (NSTimeZone *) timeZone
{
if (!timeZone)
{
SOGoUser *user;
user = [self sogoUser];
timeZone = [[user userDefaults] timeZone];
[timeZone retain];
}
return timeZone;
}
- (SOGoUserFolder *) userFolder
{
if (!userFolder)
{
userFolder = [SOGoUserFolder objectWithName: username
inContainer: nil];
[userFolder retain];
}
return userFolder;
}
- (NSDictionary *) rootFolders
{
SOGoMailAccounts *accountsFolder;
id currentFolder;
NGImap4Connection *connection;
NSDictionary *hierarchy;
NSArray *flags;
if (!rootFolders)
{
rootFolders = [NSMutableDictionary new];
[self activate];
[self userFolder]; // force lazy initialization
[woContext setClientObject: userFolder];
/* Calendar */
currentFolder = [userFolder lookupName: @"Calendar"
inContext: woContext
acquire: NO];
[rootFolders setObject: currentFolder
forKey: @"calendar"];
[rootFolders setObject: currentFolder
forKey: @"tasks"];
/* Contacts */
currentFolder = [userFolder lookupName: @"Contacts"
inContext: woContext
acquire: NO];
[rootFolders setObject: currentFolder
forKey: @"contacts"];
/* Mail */
accountsFolder = [userFolder lookupName: @"Mail"
inContext: woContext
acquire: NO];
[containersBag addObject: accountsFolder];
[woContext setClientObject: accountsFolder];
currentFolder = [accountsFolder lookupName: @"0"
inContext: woContext
acquire: NO];
[rootFolders setObject: currentFolder
forKey: @"mail"];
connection = [currentFolder imap4Connection];
[connection enableExtensions: [NSArray arrayWithObject: @"QRESYNC"]];
/* ensure the folder cache is filled */
[currentFolder toManyRelationshipKeysWithNamespaces: YES];
hierarchy = [connection cachedHierarchyResultsForURL: [currentFolder imap4URL]];
flags = [[hierarchy objectForKey: @"list"] objectForKey: @"/INBOX"];
inboxHasNoInferiors = [flags containsObject: @"noinferiors"];
}
return rootFolders;
}
- (BOOL) inboxHasNoInferiors
{
[self rootFolders];
return inboxHasNoInferiors;
}
- (MAPIStoreMapping *) mapping
{
return mapping;
}
/* OpenChange db table */
- (NSURL *) folderTableURL
{
NSString *urlString, *ocFSTableName;
NSMutableArray *parts;
SOGoUser *user;
if (!folderTableURL)
{
user = [self sogoUser];
urlString = [[user domainDefaults] folderInfoURL];
parts = [[urlString componentsSeparatedByString: @"/"]
mutableCopy];
[parts autorelease];
if ([parts count] == 5)
{
/* If "OCSFolderInfoURL" is properly configured, we must have 5
parts in this url. We strip the '-' character in case we have
this in the domain part - like foo@bar-zot.com */
ocFSTableName = [NSString stringWithFormat: @"sogo_cache_folder_%@",
[[[user login] asCSSIdentifier]
stringByReplacingOccurrencesOfString: @"-"
withString: @"_"]];
[parts replaceObjectAtIndex: 4 withObject: ocFSTableName];
folderTableURL
= [NSURL URLWithString: [parts componentsJoinedByString: @"/"]];
[folderTableURL retain];
}
else
[NSException raise: @"MAPIStoreIOException"
format: @"'OCSFolderInfoURL' is not set"];
}
return folderTableURL;
}
- (void) ensureFolderTableExists
{
GCSChannelManager *cm;
EOAdaptorChannel *channel;
NSString *tableName, *query;
GCSSpecialQueries *queries;
if ([GCSFolderManager singleStoreMode])
return;
[self folderTableURL];
cm = [GCSChannelManager defaultChannelManager];
channel = [cm acquireOpenChannelForURL: folderTableURL];
/* FIXME: make use of [EOChannelAdaptor describeTableNames] instead */
tableName = [[folderTableURL path] lastPathComponent];
if ([channel evaluateExpressionX:
[NSString stringWithFormat: @"SELECT count(*) FROM %@",
tableName]])
{
queries = [channel specialQueries];
query = [queries createSOGoCacheGCSFolderTableWithName: tableName];
if ([channel evaluateExpressionX: query])
[NSException raise: @"MAPIStoreIOException"
format: @"could not create special table '%@'", tableName];
}
else
[channel cancelFetch];
[cm releaseChannel: channel];
}
/* SOGo context objects */
- (WOContext *) woContext
{
return woContext;
}
- (MAPIStoreAuthenticator *) authenticator
{
return authenticator;
}
- (void) activate
{
[self activateWithUser: [self sogoUser]];
}
- (void) activateWithUser: (SOGoUser *) activeUser;
{
NSMutableDictionary *info;
[MAPIApp setUserContext: self];
[woContext setActiveUser: activeUser];
info = [[NSThread currentThread] threadDictionary];
[info setObject: woContext forKey: @"WOContext"];
}
- (void) deactivate
{
NSMutableDictionary *info;
if (self == [MAPIApp userContext])
[MAPIApp setUserContext: nil];
else
[self errorWithFormat: @"Error: Tried to deactivate an user context "
@"not enabled (%@ vs %@)",
[self username], [[MAPIApp userContext] username]];
info = [[NSThread currentThread] threadDictionary];
if (woContext == [info objectForKey: @"WOContext"])
[info removeObjectForKey: @"WOContext"];
else
[self errorWithFormat: @"Error: Tried to deactivate a WOContext "
@"not enabled (%@ vs %@)",
woContext, [info objectForKey: @"WOContext"]];
}
@end