snikket-ios/Tigase iOS Messenger/util/AccountManager.swift
2016-05-05 16:33:07 +02:00

190 lines
6.4 KiB
Swift

//
// AccountManager.swift
//
// Tigase iOS Messenger
// Copyright (C) 2016 "Tigase, Inc." <office@tigase.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License,
// or (at your option) any later version.
//
// This program 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 Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. Look for COPYING file in the top folder.
// If not, see http://www.gnu.org/licenses/.
//
import Foundation
import Security
public class AccountManager {
static func getAccounts() -> [String] {
var accounts = [String]();
let query = [ String(kSecClass) : kSecClassGenericPassword, String(kSecMatchLimit) : kSecMatchLimitAll, String(kSecReturnAttributes) : kCFBooleanTrue, String(kSecAttrService) : "xmpp" ];
var result:AnyObject?;
let lastResultCode: OSStatus = withUnsafeMutablePointer(&result) {
SecItemCopyMatching(query as CFDictionaryRef, UnsafeMutablePointer($0));
}
if lastResultCode == noErr {
if let results = result as? [[String:NSObject]] {
for var r in results {
let name = r[String(kSecAttrAccount)] as! String;
accounts.append(name);
}
}
}
return accounts;
}
static func getAccount(account:String) -> Account? {
let query = AccountManager.getAccountQuery(account);
var result:AnyObject?;
let lastResultCode: OSStatus = withUnsafeMutablePointer(&result) {
SecItemCopyMatching(query as CFDictionaryRef, UnsafeMutablePointer($0));
}
if lastResultCode == noErr {
if let r = result as? [String:NSObject] {
if let data = r[String(kSecAttrGeneric)] as? NSData {
let dict = NSKeyedUnarchiver.unarchiveObjectWithData(data) as? [String:AnyObject];
return Account(name: account, data: dict!);
} else {
return Account(name: account);
}
}
}
return nil;
}
static func getAccountPassword(account:String) -> String? {
let query = AccountManager.getAccountQuery(account, withData: kSecReturnData);
var result:AnyObject?;
let lastResultCode: OSStatus = withUnsafeMutablePointer(&result) {
SecItemCopyMatching(query as CFDictionaryRef, UnsafeMutablePointer($0));
}
if lastResultCode == noErr {
let data = result as! NSData;
return String(data: data, encoding: NSUTF8StringEncoding);
}
return nil;
}
static func updateAccountPassword(account:String, password:String) {
let update = [ String(kSecValueData) : password.dataUsingEncoding(NSUTF8StringEncoding)! ];
updateAccount(account, dataForUpdate: update);
}
static func deleteAccount(name:String) {
var query = AccountManager.getAccountQuery(name);
query.removeValueForKey(String(kSecMatchLimit));
query.removeValueForKey(String(kSecReturnAttributes));
let lastResultCode:OSStatus = SecItemDelete(query);
print("got result code", lastResultCode);
}
private static func updateAccount(account:String, dataForUpdate: [String:NSObject]) {
var query = AccountManager.getAccountQuery(account);
var result:AnyObject?;
var lastResultCode: OSStatus = withUnsafeMutablePointer(&result) {
SecItemCopyMatching(query as CFDictionaryRef, UnsafeMutablePointer($0));
}
var found:[String:NSObject]? = nil;
if lastResultCode == noErr {
found = result as? [String:NSObject];
}
// Removing from query attributtes forbidden in insert/update query
query.removeValueForKey(String(kSecMatchLimit));
query.removeValueForKey(String(kSecReturnAttributes));
if found == nil {
found = query;
for (k,v) in dataForUpdate {
found?[k] = v;
}
lastResultCode = SecItemAdd(found!, nil);
} else {
lastResultCode = SecItemUpdate(query, dataForUpdate);
}
}
private static func getAccountQuery(name:String, withData:CFString = kSecReturnAttributes) -> [String:NSObject] {
return [ String(kSecClass) : kSecClassGenericPassword, String(kSecMatchLimit) : kSecMatchLimitOne, String(withData) : kCFBooleanTrue, String(kSecAttrService) : "xmpp", String(kSecAttrAccount) : name ];
}
static func updateAccount(account:Account) {
let data = NSKeyedArchiver.archivedDataWithRootObject(account.data);
let update = [ String(kSecAttrGeneric) : data];
updateAccount(account.name, dataForUpdate: update);
}
public class Account {
private var data:[String:AnyObject];
public let name:String;
public var active:Bool {
get {
return (data["active"] as? Bool) ?? false;
}
set {
data["active"] = newValue;
}
}
public var password:String {
get {
return AccountManager.getAccountPassword(name)!;
}
set {
AccountManager.updateAccountPassword(name, password: newValue);
}
}
public var server:String? {
get {
return data["serverHost"] as? String;
}
set {
if newValue != nil {
data["serverHost"] = newValue;
} else {
data.removeValueForKey("serverHost");
}
}
}
public init(name:String) {
self.name = name;
self.data = [String:AnyObject]();
}
private init(name:String, data:[String:AnyObject]) {
self.name = name;
self.data = data;
}
}
}