/* 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 #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #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