2 * Adium is the legal property of its developers, whose names are listed in the copyright file included
3 * with this source distribution.
5 * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
6 * General Public License as published by the Free Software Foundation; either version 2 of the License,
7 * or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
10 * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
11 * Public License for more details.
13 * You should have received a copy of the GNU General Public License along with this program; if not,
14 * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 #import <Adium/AIAccountControllerProtocol.h>
18 #import <Adium/AILoginControllerProtocol.h>
19 #import "AdiumPasswords.h"
20 #import "ESAccountPasswordPromptController.h"
21 #import "ESProxyPasswordPromptController.h"
22 #import <AIUtilities/AIKeychain.h>
23 #import <AIUtilities/AIObjectAdditions.h>
24 #import <Adium/AIAccount.h>
25 #import <Adium/AIService.h>
27 @interface AdiumPasswords (PRIVATE)
28 - (NSString *)_accountNameForAccount:(AIAccount *)inAccount;
29 - (NSString *)_passKeyForAccount:(AIAccount *)inAccount;
30 - (NSString *)_accountNameForProxyServer:(NSString *)proxyServer userName:(NSString *)userName;
31 - (NSString *)_passKeyForProxyServer:(NSString *)proxyServer;
34 @implementation AdiumPasswords
36 //Accounts -------------------------------------------------------------------------------------------------------------
40 * @brief Set the password of an account
42 * @param inPassword password to store
43 * @param inAccount account the password belongs to
45 - (void)setPassword:(NSString *)inPassword forAccount:(AIAccount *)inAccount
48 [[AIKeychain defaultKeychain_error:&error] setInternetPassword:inPassword
49 forServer:[self _passKeyForAccount:inAccount]
50 account:[self _accountNameForAccount:inAccount]
51 protocol:FOUR_CHAR_CODE('AdIM')
54 OSStatus err = [error code];
55 /*errSecItemNotFound: no entry in the keychain. a harmless error.
56 *we don't ignore it if we're trying to set the password, though (because that would be strange).
57 *we don't get here at all for noErr (error will be nil).
59 if (inPassword || (err != errSecItemNotFound)) {
60 NSDictionary *userInfo = [error userInfo];
61 NSLog(@"could not %@ password for account %@: %@ returned %i (%@)", inPassword ? @"set" : @"remove", [self _accountNameForAccount:inAccount], [userInfo objectForKey:AIKEYCHAIN_ERROR_USERINFO_SECURITYFUNCTIONNAME], err, [userInfo objectForKey:AIKEYCHAIN_ERROR_USERINFO_ERRORDESCRIPTION]);
67 * @brief Forget the password of an account
69 * @param inAccount account whose password should be forgotten
71 - (void)forgetPasswordForAccount:(AIAccount *)inAccount
74 AIKeychain *keychain = [AIKeychain defaultKeychain_error:&error];
75 [keychain deleteInternetPasswordForServer:[self _passKeyForAccount:inAccount]
76 account:[self _accountNameForAccount:inAccount]
77 protocol:FOUR_CHAR_CODE('AdIM')
80 OSStatus err = [error code];
81 /*errSecItemNotFound: no entry in the keychain. a harmless error.
82 *we don't get here at all for noErr (error will be nil).
84 if (err != errSecItemNotFound) {
85 NSDictionary *userInfo = [error userInfo];
86 NSLog(@"could not delete password for account %@: %@ returned %i (%@)", [self _accountNameForAccount:inAccount], [userInfo objectForKey:AIKEYCHAIN_ERROR_USERINFO_SECURITYFUNCTIONNAME], err, [userInfo objectForKey:AIKEYCHAIN_ERROR_USERINFO_ERRORDESCRIPTION]);
92 * @brief Retrieve the password of an account
94 * @param inAccount account whose password is desired
95 * @return account password, or nil if the password is not available
97 - (NSString *)passwordForAccount:(AIAccount *)inAccount
100 AIKeychain *keychain = [AIKeychain defaultKeychain_error:&error];
101 NSString *password = [keychain internetPasswordForServer:[self _passKeyForAccount:inAccount]
102 account:[self _accountNameForAccount:inAccount]
103 protocol:FOUR_CHAR_CODE('AdIM')
106 OSStatus err = [error code];
107 /*errSecItemNotFound: no entry in the keychain. a harmless error.
108 *we don't get here at all for noErr (error will be nil).
110 if (err != errSecItemNotFound) {
111 NSDictionary *userInfo = [error userInfo];
112 NSLog(@"could not retrieve password for account %@: %@ returned %i (%@)", [self _accountNameForAccount:inAccount], [userInfo objectForKey:AIKEYCHAIN_ERROR_USERINFO_SECURITYFUNCTIONNAME], err, [userInfo objectForKey:AIKEYCHAIN_ERROR_USERINFO_ERRORDESCRIPTION]);
119 * @brief Retrieve the password of an account, prompting the user if necessary
121 * @param inAccount account whose password is desired
122 * @param inTarget target to notify when password is available
123 * @param inSelector selector to notify when password is available
124 * @param inContext context passed to target
126 - (void)passwordForAccount:(AIAccount *)inAccount notifyingTarget:(id)inTarget selector:(SEL)inSelector context:(id)inContext
128 NSError *error = nil;
129 AIKeychain *keychain = [AIKeychain defaultKeychain_error:&error];
130 NSString *password = [keychain internetPasswordForServer:[self _passKeyForAccount:inAccount]
131 account:[self _accountNameForAccount:inAccount]
132 protocol:FOUR_CHAR_CODE('AdIM')
135 OSStatus err = [error code];
136 /*errSecItemNotFound: no entry in the keychain. a harmless error.
137 *we don't get here at all for noErr (error will be nil).
139 if (err != errSecItemNotFound) {
140 NSDictionary *userInfo = [error userInfo];
141 NSLog(@"could not retrieve password for account %@: %@ returned %i (%@)", [self _accountNameForAccount:inAccount], [userInfo objectForKey:AIKEYCHAIN_ERROR_USERINFO_SECURITYFUNCTIONNAME], err, [userInfo objectForKey:AIKEYCHAIN_ERROR_USERINFO_ERRORDESCRIPTION]);
145 if (password && [password length] != 0) {
146 //Invoke the target right away
147 [inTarget performSelector:inSelector withObject:password withObject:inContext afterDelay:0.0001];
149 //Prompt the user for their password
150 [ESAccountPasswordPromptController showPasswordPromptForAccount:inAccount
151 notifyingTarget:inTarget
157 //Proxy Servers --------------------------------------------------------------------------------------------------------
158 #pragma mark Proxy Servers
161 * @brief Set the password for a proxy server
163 * @param inPassword password to store
164 * @param server proxy server name
165 * @param userName proxy server user name
167 * XXX - This is inconsistent. Above we have a separate forget method, here we forget when nil is passed...
169 - (void)setPassword:(NSString *)inPassword forProxyServer:(NSString *)server userName:(NSString *)userName
171 NSError *error = nil;
172 [[AIKeychain defaultKeychain_error:&error] setInternetPassword:inPassword
173 forServer:[self _passKeyForProxyServer:server]
174 account:[self _accountNameForProxyServer:server
176 protocol:FOUR_CHAR_CODE('AdIM')
179 OSStatus err = [error code];
180 /*errSecItemNotFound: no entry in the keychain. a harmless error.
181 *we don't ignore it if we're trying to set the password, though (because that would be strange).
182 *we don't get here at all for noErr (error will be nil).
184 if (inPassword || (err != errSecItemNotFound)) {
185 NSDictionary *userInfo = [error userInfo];
186 NSLog(@"could not %@ password for proxy server %@: %@ returned %i (%@)",
187 inPassword ? @"set" : @"remove",
188 [self _accountNameForProxyServer:server
190 [userInfo objectForKey:AIKEYCHAIN_ERROR_USERINFO_SECURITYFUNCTIONNAME],
192 [userInfo objectForKey:AIKEYCHAIN_ERROR_USERINFO_ERRORDESCRIPTION]);
198 * @brief Retrieve the password for a proxy server
200 * @param server proxy server name
201 * @param userName proxy server user name
202 * @return proxy server password, or nil if the password is not available
204 - (NSString *)passwordForProxyServer:(NSString *)server userName:(NSString *)userName
206 NSError *error = nil;
207 AIKeychain *keychain = [AIKeychain defaultKeychain_error:&error];
208 NSString *password = [keychain internetPasswordForServer:[self _passKeyForProxyServer:server]
209 account:[self _accountNameForProxyServer:server
211 protocol:FOUR_CHAR_CODE('AdIM')
214 OSStatus err = [error code];
215 /*errSecItemNotFound: no entry in the keychain. a harmless error.
216 *we don't get here at all for noErr (error will be nil).
218 if (err != errSecItemNotFound) {
219 NSDictionary *userInfo = [error userInfo];
220 NSLog(@"could not retrieve password for proxy server %@: %@ returned %i (%@)",
221 [self _accountNameForProxyServer:server
223 [userInfo objectForKey:AIKEYCHAIN_ERROR_USERINFO_SECURITYFUNCTIONNAME],
225 [userInfo objectForKey:AIKEYCHAIN_ERROR_USERINFO_ERRORDESCRIPTION]);
232 * @brief Retrieve the password for a proxy server, prompting the user if necessary
234 * @param server proxy server name
235 * @param userName proxy server user name
236 * @param inTarget target to notify when password is available
237 * @param inSelector selector to notify when password is available
238 * @param inContext context passed to target
240 - (void)passwordForProxyServer:(NSString *)server userName:(NSString *)userName notifyingTarget:(id)inTarget selector:(SEL)inSelector context:(id)inContext
242 NSError *error = nil;
243 AIKeychain *keychain = [AIKeychain defaultKeychain_error:&error];
244 NSString *password = [keychain internetPasswordForServer:[self _passKeyForProxyServer:server]
245 account:[self _accountNameForProxyServer:server
247 protocol:FOUR_CHAR_CODE('AdIM')
250 OSStatus err = [error code];
251 /*errSecItemNotFound: no entry in the keychain. a harmless error.
252 *we don't get here at all for noErr (error will be nil).
254 if (err != errSecItemNotFound) {
255 NSDictionary *userInfo = [error userInfo];
256 NSLog(@"could not retrieve password for proxy server %@: %@ returned %i (%@)",
257 [self _accountNameForProxyServer:server
259 [userInfo objectForKey:AIKEYCHAIN_ERROR_USERINFO_SECURITYFUNCTIONNAME],
261 [userInfo objectForKey:AIKEYCHAIN_ERROR_USERINFO_ERRORDESCRIPTION]);
265 if (password && [password length] != 0) {
266 //Invoke the target right away
267 [inTarget performSelector:inSelector withObject:password withObject:inContext afterDelay:0.0001];
269 //Prompt the user for their password
270 [ESProxyPasswordPromptController showPasswordPromptForProxyServer:server
272 notifyingTarget:inTarget
279 //Password Keys --------------------------------------------------------------------------------------------------------
280 #pragma mark Password Keys
283 * @brief Keychain identifier for an account
285 - (NSString *)_accountNameForAccount:(AIAccount *)inAccount{
286 return [NSString stringWithFormat:@"%@.%@",[[inAccount service] serviceID],[inAccount internalObjectID]];
288 - (NSString *)_passKeyForAccount:(AIAccount *)inAccount{
289 if ([[[adium loginController] userArray] count] > 1) {
290 return [NSString stringWithFormat:@"Adium.%@.%@",[[adium loginController] currentUser],[self _accountNameForAccount:inAccount]];
292 return [NSString stringWithFormat:@"Adium.%@",[self _accountNameForAccount:inAccount]];
297 * @brief Keychain identifier for a proxy server
299 - (NSString *)_accountNameForProxyServer:(NSString *)proxyServer userName:(NSString *)userName{
300 return [NSString stringWithFormat:@"%@.%@",proxyServer,userName];
302 - (NSString *)_passKeyForProxyServer:(NSString *)proxyServer{
303 if ([[[adium loginController] userArray] count] > 1) {
304 return [NSString stringWithFormat:@"Adium.%@.%@",[[adium loginController] currentUser],proxyServer];
306 return [NSString stringWithFormat:@"Adium.%@",proxyServer];