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 forceDisplay If YES, a password prompt will be shown even if a stored password is available. If NO, it will only be displayed if no password is stored.
123 * @param inTarget target to notify when password is available
124 * @param inSelector selector to notify when password is available
125 * @param inContext context passed to target
127 - (void)passwordForAccount:(AIAccount *)inAccount forcePromptDisplay:(BOOL)forceDisplay notifyingTarget:(id)inTarget selector:(SEL)inSelector context:(id)inContext
129 NSString *password = [self passwordForAccount:inAccount];
131 if (password && [password length] && !forceDisplay) {
132 //Invoke the target right away
133 [inTarget performSelector:inSelector withObject:password withObject:inContext afterDelay:0.0001];
135 //Prompt the user for their password
136 [ESAccountPasswordPromptController showPasswordPromptForAccount:inAccount
138 notifyingTarget:inTarget
144 //Proxy Servers --------------------------------------------------------------------------------------------------------
145 #pragma mark Proxy Servers
148 * @brief Set the password for a proxy server
150 * @param inPassword password to store
151 * @param server proxy server name
152 * @param userName proxy server user name
154 * XXX - This is inconsistent. Above we have a separate forget method, here we forget when nil is passed...
156 - (void)setPassword:(NSString *)inPassword forProxyServer:(NSString *)server userName:(NSString *)userName
158 NSError *error = nil;
159 [[AIKeychain defaultKeychain_error:&error] setInternetPassword:inPassword
160 forServer:[self _passKeyForProxyServer:server]
161 account:[self _accountNameForProxyServer:server
163 protocol:FOUR_CHAR_CODE('AdIM')
166 OSStatus err = [error code];
167 /*errSecItemNotFound: no entry in the keychain. a harmless error.
168 *we don't ignore it if we're trying to set the password, though (because that would be strange).
169 *we don't get here at all for noErr (error will be nil).
171 if (inPassword || (err != errSecItemNotFound)) {
172 NSDictionary *userInfo = [error userInfo];
173 NSLog(@"could not %@ password for proxy server %@: %@ returned %i (%@)",
174 inPassword ? @"set" : @"remove",
175 [self _accountNameForProxyServer:server
177 [userInfo objectForKey:AIKEYCHAIN_ERROR_USERINFO_SECURITYFUNCTIONNAME],
179 [userInfo objectForKey:AIKEYCHAIN_ERROR_USERINFO_ERRORDESCRIPTION]);
185 * @brief Retrieve the password for a proxy server
187 * @param server proxy server name
188 * @param userName proxy server user name
189 * @return proxy server password, or nil if the password is not available
191 - (NSString *)passwordForProxyServer:(NSString *)server userName:(NSString *)userName
193 NSError *error = nil;
194 AIKeychain *keychain = [AIKeychain defaultKeychain_error:&error];
195 NSString *password = [keychain internetPasswordForServer:[self _passKeyForProxyServer:server]
196 account:[self _accountNameForProxyServer:server
198 protocol:FOUR_CHAR_CODE('AdIM')
201 OSStatus err = [error code];
202 /*errSecItemNotFound: no entry in the keychain. a harmless error.
203 *we don't get here at all for noErr (error will be nil).
205 if (err != errSecItemNotFound) {
206 NSDictionary *userInfo = [error userInfo];
207 NSLog(@"could not retrieve password for proxy server %@: %@ returned %i (%@)",
208 [self _accountNameForProxyServer:server
210 [userInfo objectForKey:AIKEYCHAIN_ERROR_USERINFO_SECURITYFUNCTIONNAME],
212 [userInfo objectForKey:AIKEYCHAIN_ERROR_USERINFO_ERRORDESCRIPTION]);
219 * @brief Retrieve the password for a proxy server, prompting the user if necessary
221 * @param server proxy server name
222 * @param userName proxy server user name
223 * @param inTarget target to notify when password is available
224 * @param inSelector selector to notify when password is available
225 * @param inContext context passed to target
227 - (void)passwordForProxyServer:(NSString *)server userName:(NSString *)userName notifyingTarget:(id)inTarget selector:(SEL)inSelector context:(id)inContext
229 NSString *password = [self passwordForProxyServer:server userName:userName];
231 if (password && [password length] != 0) {
232 //Invoke the target right away
233 [inTarget performSelector:inSelector withObject:password withObject:inContext afterDelay:0.0001];
235 //Prompt the user for their password
236 [ESProxyPasswordPromptController showPasswordPromptForProxyServer:server
238 notifyingTarget:inTarget
245 //Password Keys --------------------------------------------------------------------------------------------------------
246 #pragma mark Password Keys
249 * @brief Keychain identifier for an account
251 - (NSString *)_accountNameForAccount:(AIAccount *)inAccount{
252 return [NSString stringWithFormat:@"%@.%@",[[inAccount service] serviceID],[inAccount internalObjectID]];
254 - (NSString *)_passKeyForAccount:(AIAccount *)inAccount{
255 if ([[[adium loginController] userArray] count] > 1) {
256 return [NSString stringWithFormat:@"Adium.%@.%@",[[adium loginController] currentUser],[self _accountNameForAccount:inAccount]];
258 return [NSString stringWithFormat:@"Adium.%@",[self _accountNameForAccount:inAccount]];
263 * @brief Keychain identifier for a proxy server
265 - (NSString *)_accountNameForProxyServer:(NSString *)proxyServer userName:(NSString *)userName{
266 return [NSString stringWithFormat:@"%@.%@",proxyServer,userName];
268 - (NSString *)_passKeyForProxyServer:(NSString *)proxyServer{
269 if ([[[adium loginController] userArray] count] > 1) {
270 return [NSString stringWithFormat:@"Adium.%@.%@",[[adium loginController] currentUser],proxyServer];
272 return [NSString stringWithFormat:@"Adium.%@",proxyServer];