Allow localization of the Alert Text label in the Display an Alert action
[adiumx.git] / Source / AdiumPasswords.m
blob8b0cb027bd0aa26db7da0a6705150cce8d11efda
1 /* 
2  * Adium is the legal property of its developers, whose names are listed in the copyright file included
3  * with this source distribution.
4  * 
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.
8  * 
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.
12  * 
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.
15  */
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;
32 @end
34 @implementation AdiumPasswords
36 //Accounts -------------------------------------------------------------------------------------------------------------
37 #pragma mark Accounts
39 /*!
40  * @brief Set the password of an account
41  *
42  * @param inPassword password to store
43  * @param inAccount account the password belongs to
44  */
45 - (void)setPassword:(NSString *)inPassword forAccount:(AIAccount *)inAccount
47         NSError *error = nil;
48         [[AIKeychain defaultKeychain_error:&error] setInternetPassword:inPassword
49                                                                                                                  forServer:[self _passKeyForAccount:inAccount]
50                                                                                                                    account:[self _accountNameForAccount:inAccount]
51                                                                                                                   protocol:FOUR_CHAR_CODE('AdIM')
52                                                                                                                          error:&error];
53         if (error) {
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).
58                  */
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]);
62                 }
63         }
66 /*!
67  * @brief Forget the password of an account
68  *
69  * @param inAccount account whose password should be forgotten
70  */
71 - (void)forgetPasswordForAccount:(AIAccount *)inAccount
73         NSError         *error    = nil;
74         AIKeychain      *keychain = [AIKeychain defaultKeychain_error:&error];
75         [keychain deleteInternetPasswordForServer:[self _passKeyForAccount:inAccount]
76                 account:[self _accountNameForAccount:inAccount]
77                 protocol:FOUR_CHAR_CODE('AdIM')
78                 error:&error];
79         if (error) {
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).
83                  */
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]);
87                 }
88         }
91 /*!
92  * @brief Retrieve the password of an account
93  * 
94  * @param inAccount account whose password is desired
95  * @return account password, or nil if the password is not available
96  */
97 - (NSString *)passwordForAccount:(AIAccount *)inAccount
99         NSError         *error    = nil;
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')
104                                                                                                                   error:&error];
105         if (error) {
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).
109                  */
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]);
113                 }
114         }
115         return password;
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
126  */
127 - (void)passwordForAccount:(AIAccount *)inAccount forcePromptDisplay:(BOOL)forceDisplay notifyingTarget:(id)inTarget selector:(SEL)inSelector context:(id)inContext
129         NSString        *password = [self passwordForAccount:inAccount];
130         
131         if (password && [password length] && !forceDisplay) {
132                 //Invoke the target right away
133                 [inTarget performSelector:inSelector withObject:password withObject:inContext afterDelay:0.0001];
134         } else {
135                 //Prompt the user for their password
136                 [ESAccountPasswordPromptController showPasswordPromptForAccount:inAccount
137                                                                                                                            password:password
138                                                                                                                 notifyingTarget:inTarget
139                                                                                                                            selector:inSelector
140                                                                                                                                 context:inContext];
141         }
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...
155  */
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 
162                                                                                                                                                                                    userName:userName]
163                                                                                                                   protocol:FOUR_CHAR_CODE('AdIM')
164                                                                                                                          error:&error];
165         if (error) {
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).
170                  */
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
176                                                           userName:userName],
177                                   [userInfo objectForKey:AIKEYCHAIN_ERROR_USERINFO_SECURITYFUNCTIONNAME],
178                                   err,
179                                   [userInfo objectForKey:AIKEYCHAIN_ERROR_USERINFO_ERRORDESCRIPTION]);
180                 }
181         }
185  * @brief Retrieve the password for a proxy server
186  * 
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
190  */
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 
197                                                                                                                                                                                 userName:userName]
198                                                                                                            protocol:FOUR_CHAR_CODE('AdIM')
199                                                                                                                   error:&error];
200         if (error) {
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).
204                  */
205                 if (err != errSecItemNotFound) {
206                         NSDictionary *userInfo = [error userInfo];
207                         NSLog(@"could not retrieve password for proxy server %@: %@ returned %i (%@)",
208                                   [self _accountNameForProxyServer:server
209                                                           userName:userName],
210                                   [userInfo objectForKey:AIKEYCHAIN_ERROR_USERINFO_SECURITYFUNCTIONNAME],
211                                   err,
212                                   [userInfo objectForKey:AIKEYCHAIN_ERROR_USERINFO_ERRORDESCRIPTION]);
213                 }
214         }
215         return password;
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
226  */
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];
230         
231         if (password && [password length] != 0) {
232                 //Invoke the target right away
233                 [inTarget performSelector:inSelector withObject:password withObject:inContext afterDelay:0.0001];    
234         } else {
235                 //Prompt the user for their password
236                 [ESProxyPasswordPromptController showPasswordPromptForProxyServer:server
237                                                                                                                                  userName:userName
238                                                                                                                   notifyingTarget:inTarget
239                                                                                                                                  selector:inSelector
240                                                                                                                                   context:inContext];
241         }
245 //Password Keys --------------------------------------------------------------------------------------------------------
246 #pragma mark Password Keys
249  * @brief Keychain identifier for an account
250  */
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]];
257         } else {
258                 return [NSString stringWithFormat:@"Adium.%@",[self _accountNameForAccount:inAccount]];
259         }
263  * @brief Keychain identifier for a proxy server
264  */
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];
271         } else {
272                 return [NSString stringWithFormat:@"Adium.%@",proxyServer];     
273         }
276 @end