r81649 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r81648‎ | r81649 | r81650 >
Date:21:45, 7 February 2011
Author:hartman
Status:deferred
Tags:
Comment:
Store the password into Keychain

Uses SFHFKeychainUtils by Buzz Andersen, Jonathan Wight, Jon Crosby and Mike Malone.
SFHFKeychainUtils is licensed MIT
Modified paths:
  • /trunk/tools/WikiSnaps/Classes/CommonsUpload.m (modified) (history)
  • /trunk/tools/WikiSnaps/Classes/Configuration.h (modified) (history)
  • /trunk/tools/WikiSnaps/Classes/SettingsViewController.m (modified) (history)
  • /trunk/tools/WikiSnaps/Classes/security (added) (history)
  • /trunk/tools/WikiSnaps/Classes/security/SFHFKeychainUtils.h (added) (history)
  • /trunk/tools/WikiSnaps/Classes/security/SFHFKeychainUtils.m (added) (history)
  • /trunk/tools/WikiSnaps/WikiSnaps.xcodeproj/project.pbxproj (modified) (history)

Diff [purge]

Index: trunk/tools/WikiSnaps/Classes/SettingsViewController.m
@@ -9,6 +9,7 @@
1010
1111 #import "SettingsViewController.h"
1212 #import "ASIFormDataRequest.h"
 13+#import "SFHFKeychainUtils.h"
1314
1415
1516 @implementation SettingsViewController
@@ -38,9 +39,13 @@
3940 usernameLabel.text = NSLocalizedString( @"License", @"Label of the license textfield" );
4041
4142 username.text = [[NSUserDefaults standardUserDefaults] valueForKey: COMMONS_USERNAME_KEY];
42 - password.text = [[NSUserDefaults standardUserDefaults] valueForKey: COMMONS_PASSWORD_KEY];
4343 license.text = [[NSUserDefaults standardUserDefaults] valueForKey: COMMONS_LICENSE_KEY];
4444
 45+ NSError *error = nil;
 46+ password.text = [SFHFKeychainUtils getPasswordForUsername:username.text andServiceName:COMMONS_KEYCHAIN_KEY error: &error];
 47+ if( error ) {
 48+ NSLog( @"pasword storage problem: %@", [error localizedDescription] );
 49+ }
4550 }
4651
4752 /*
@@ -56,10 +61,29 @@
5762
5863 - (void)viewWillDisappear:(BOOL)animated {
5964 [super viewWillDisappear:animated];
 65+
 66+ NSError *error = nil;
 67+ NSString *oldUsername = [[NSUserDefaults standardUserDefaults] valueForKey: COMMONS_USERNAME_KEY];
 68+ if( [username.text compare:oldUsername] != NSOrderedSame ) {
 69+ /* Delete password for previous username */
 70+ [SFHFKeychainUtils deleteItemForUsername:oldUsername andServiceName:COMMONS_KEYCHAIN_KEY error:&error];
 71+ if( error ) {
 72+ NSLog( @"pasword deletion problem: %@", [error localizedDescription] );
 73+ error = nil;
 74+ }
 75+ }
 76+
6077 /* Save the data */
6178 [[NSUserDefaults standardUserDefaults] setObject:username.text forKey:COMMONS_USERNAME_KEY];
62 - // FIXME insecure
63 - [[NSUserDefaults standardUserDefaults] setObject:password.text forKey:COMMONS_PASSWORD_KEY];
 79+ [[NSUserDefaults standardUserDefaults] setObject:license.text forKey:COMMONS_LICENSE_KEY];
 80+
 81+ /* Store the password in the keychain */
 82+ [SFHFKeychainUtils storeUsername: username.text andPassword: password.text forServiceName:COMMONS_KEYCHAIN_KEY updateExisting: YES error: &error];
 83+
 84+ if( error ) {
 85+ NSLog( @"pasword storage problem: %@", [error localizedDescription] );
 86+ }
 87+ [[NSUserDefaults standardUserDefaults] synchronize];
6488 }
6589
6690 /*
Index: trunk/tools/WikiSnaps/Classes/Configuration.h
@@ -12,8 +12,7 @@
1313 // Keys for preferences
1414 #define COMMONS_USERNAME_KEY @"CommonsUsernameKey"
1515 #define COMMONS_LICENSE_KEY @"CommonsLicenseKey"
16 -// FIXME insecure
17 -#define COMMONS_PASSWORD_KEY @"CommonsPasswordKey"
 16+#define COMMONS_KEYCHAIN_KEY @"CommonsKey"
1817
1918 // Constants
2019 #define COMMONS_DESTINATION_URL @"http://commons.wikimedia.org/wiki/File:%@"
Index: trunk/tools/WikiSnaps/Classes/security/SFHFKeychainUtils.h
@@ -0,0 +1,41 @@
 2+//
 3+// SFHFKeychainUtils.h
 4+//
 5+// Created by Buzz Andersen on 10/20/08.
 6+// Based partly on code by Jonathan Wight, Jon Crosby, and Mike Malone.
 7+// Copyright 2008 Sci-Fi Hi-Fi. All rights reserved.
 8+//
 9+// Permission is hereby granted, free of charge, to any person
 10+// obtaining a copy of this software and associated documentation
 11+// files (the "Software"), to deal in the Software without
 12+// restriction, including without limitation the rights to use,
 13+// copy, modify, merge, publish, distribute, sublicense, and/or sell
 14+// copies of the Software, and to permit persons to whom the
 15+// Software is furnished to do so, subject to the following
 16+// conditions:
 17+//
 18+// The above copyright notice and this permission notice shall be
 19+// included in all copies or substantial portions of the Software.
 20+//
 21+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 22+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 23+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 24+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 25+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 26+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 27+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 28+// OTHER DEALINGS IN THE SOFTWARE.
 29+//
 30+
 31+#import <UIKit/UIKit.h>
 32+
 33+
 34+@interface SFHFKeychainUtils : NSObject {
 35+
 36+}
 37+
 38++ (NSString *) getPasswordForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error;
 39++ (BOOL) storeUsername: (NSString *) username andPassword: (NSString *) password forServiceName: (NSString *) serviceName updateExisting: (BOOL) updateExisting error: (NSError **) error;
 40++ (BOOL) deleteItemForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error;
 41+
 42+@end
\ No newline at end of file
Property changes on: trunk/tools/WikiSnaps/Classes/security/SFHFKeychainUtils.h
___________________________________________________________________
Added: svn:eol-style
143 + native
Index: trunk/tools/WikiSnaps/Classes/security/SFHFKeychainUtils.m
@@ -0,0 +1,434 @@
 2+//
 3+// SFHFKeychainUtils.m
 4+//
 5+// Created by Buzz Andersen on 10/20/08.
 6+// Based partly on code by Jonathan Wight, Jon Crosby, and Mike Malone.
 7+// Copyright 2008 Sci-Fi Hi-Fi. All rights reserved.
 8+//
 9+// Permission is hereby granted, free of charge, to any person
 10+// obtaining a copy of this software and associated documentation
 11+// files (the "Software"), to deal in the Software without
 12+// restriction, including without limitation the rights to use,
 13+// copy, modify, merge, publish, distribute, sublicense, and/or sell
 14+// copies of the Software, and to permit persons to whom the
 15+// Software is furnished to do so, subject to the following
 16+// conditions:
 17+//
 18+// The above copyright notice and this permission notice shall be
 19+// included in all copies or substantial portions of the Software.
 20+//
 21+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 22+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 23+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 24+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 25+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 26+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 27+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 28+// OTHER DEALINGS IN THE SOFTWARE.
 29+//
 30+
 31+#import "SFHFKeychainUtils.h"
 32+#import <Security/Security.h>
 33+
 34+static NSString *SFHFKeychainUtilsErrorDomain = @"SFHFKeychainUtilsErrorDomain";
 35+
 36+#if __IPHONE_OS_VERSION_MIN_REQUIRED < 30000 && TARGET_IPHONE_SIMULATOR
 37+@interface SFHFKeychainUtils (PrivateMethods)
 38++ (SecKeychainItemRef) getKeychainItemReferenceForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error;
 39+@end
 40+#endif
 41+
 42+@implementation SFHFKeychainUtils
 43+
 44+#if __IPHONE_OS_VERSION_MIN_REQUIRED < 30000 && TARGET_IPHONE_SIMULATOR
 45+
 46++ (NSString *) getPasswordForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error {
 47+ if (!username || !serviceName) {
 48+ *error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -2000 userInfo: nil];
 49+ return nil;
 50+ }
 51+
 52+ SecKeychainItemRef item = [SFHFKeychainUtils getKeychainItemReferenceForUsername: username andServiceName: serviceName error: error];
 53+
 54+ if (*error || !item) {
 55+ return nil;
 56+ }
 57+
 58+ // from Advanced Mac OS X Programming, ch. 16
 59+ UInt32 length;
 60+ char *password;
 61+ SecKeychainAttribute attributes[8];
 62+ SecKeychainAttributeList list;
 63+
 64+ attributes[0].tag = kSecAccountItemAttr;
 65+ attributes[1].tag = kSecDescriptionItemAttr;
 66+ attributes[2].tag = kSecLabelItemAttr;
 67+ attributes[3].tag = kSecModDateItemAttr;
 68+
 69+ list.count = 4;
 70+ list.attr = attributes;
 71+
 72+ OSStatus status = SecKeychainItemCopyContent(item, NULL, &list, &length, (void **)&password);
 73+
 74+ if (status != noErr) {
 75+ *error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil];
 76+ return nil;
 77+ }
 78+
 79+ NSString *passwordString = nil;
 80+
 81+ if (password != NULL) {
 82+ char passwordBuffer[1024];
 83+
 84+ if (length > 1023) {
 85+ length = 1023;
 86+ }
 87+ strncpy(passwordBuffer, password, length);
 88+
 89+ passwordBuffer[length] = '\0';
 90+ passwordString = [NSString stringWithCString:passwordBuffer];
 91+ }
 92+
 93+ SecKeychainItemFreeContent(&list, password);
 94+
 95+ CFRelease(item);
 96+
 97+ return passwordString;
 98+}
 99+
 100++ (void) storeUsername: (NSString *) username andPassword: (NSString *) password forServiceName: (NSString *) serviceName updateExisting: (BOOL) updateExisting error: (NSError **) error {
 101+ if (!username || !password || !serviceName) {
 102+ *error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -2000 userInfo: nil];
 103+ return;
 104+ }
 105+
 106+ OSStatus status = noErr;
 107+
 108+ SecKeychainItemRef item = [SFHFKeychainUtils getKeychainItemReferenceForUsername: username andServiceName: serviceName error: error];
 109+
 110+ if (*error && [*error code] != noErr) {
 111+ return;
 112+ }
 113+
 114+ *error = nil;
 115+
 116+ if (item) {
 117+ status = SecKeychainItemModifyAttributesAndData(item,
 118+ NULL,
 119+ strlen([password UTF8String]),
 120+ [password UTF8String]);
 121+
 122+ CFRelease(item);
 123+ }
 124+ else {
 125+ status = SecKeychainAddGenericPassword(NULL,
 126+ strlen([serviceName UTF8String]),
 127+ [serviceName UTF8String],
 128+ strlen([username UTF8String]),
 129+ [username UTF8String],
 130+ strlen([password UTF8String]),
 131+ [password UTF8String],
 132+ NULL);
 133+ }
 134+
 135+ if (status != noErr) {
 136+ *error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil];
 137+ }
 138+}
 139+
 140++ (void) deleteItemForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error {
 141+ if (!username || !serviceName) {
 142+ *error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: 2000 userInfo: nil];
 143+ return;
 144+ }
 145+
 146+ *error = nil;
 147+
 148+ SecKeychainItemRef item = [SFHFKeychainUtils getKeychainItemReferenceForUsername: username andServiceName: serviceName error: error];
 149+
 150+ if (*error && [*error code] != noErr) {
 151+ return;
 152+ }
 153+
 154+ OSStatus status;
 155+
 156+ if (item) {
 157+ status = SecKeychainItemDelete(item);
 158+
 159+ CFRelease(item);
 160+ }
 161+
 162+ if (status != noErr) {
 163+ *error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil];
 164+ }
 165+}
 166+
 167++ (SecKeychainItemRef) getKeychainItemReferenceForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error {
 168+ if (!username || !serviceName) {
 169+ *error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -2000 userInfo: nil];
 170+ return nil;
 171+ }
 172+
 173+ *error = nil;
 174+
 175+ SecKeychainItemRef item;
 176+
 177+ OSStatus status = SecKeychainFindGenericPassword(NULL,
 178+ strlen([serviceName UTF8String]),
 179+ [serviceName UTF8String],
 180+ strlen([username UTF8String]),
 181+ [username UTF8String],
 182+ NULL,
 183+ NULL,
 184+ &item);
 185+
 186+ if (status != noErr) {
 187+ if (status != errSecItemNotFound) {
 188+ *error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil];
 189+ }
 190+
 191+ return nil;
 192+ }
 193+
 194+ return item;
 195+}
 196+
 197+#else
 198+
 199++ (NSString *) getPasswordForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error {
 200+ if (!username || !serviceName) {
 201+ if (error != nil) {
 202+ *error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -2000 userInfo: nil];
 203+ }
 204+ return nil;
 205+ }
 206+
 207+ if (error != nil) {
 208+ *error = nil;
 209+ }
 210+
 211+ // Set up a query dictionary with the base query attributes: item type (generic), username, and service
 212+
 213+ NSArray *keys = [[[NSArray alloc] initWithObjects: (NSString *) kSecClass, kSecAttrAccount, kSecAttrService, nil] autorelease];
 214+ NSArray *objects = [[[NSArray alloc] initWithObjects: (NSString *) kSecClassGenericPassword, username, serviceName, nil] autorelease];
 215+
 216+ NSMutableDictionary *query = [[[NSMutableDictionary alloc] initWithObjects: objects forKeys: keys] autorelease];
 217+
 218+ // First do a query for attributes, in case we already have a Keychain item with no password data set.
 219+ // One likely way such an incorrect item could have come about is due to the previous (incorrect)
 220+ // version of this code (which set the password as a generic attribute instead of password data).
 221+
 222+ NSDictionary *attributeResult = NULL;
 223+ NSMutableDictionary *attributeQuery = [query mutableCopy];
 224+ [attributeQuery setObject: (id) kCFBooleanTrue forKey:(id) kSecReturnAttributes];
 225+ OSStatus status = SecItemCopyMatching((CFDictionaryRef) attributeQuery, (CFTypeRef *) &attributeResult);
 226+
 227+ [attributeResult release];
 228+ [attributeQuery release];
 229+
 230+ if (status != noErr) {
 231+ // No existing item found--simply return nil for the password
 232+ if (error != nil && status != errSecItemNotFound) {
 233+ //Only return an error if a real exception happened--not simply for "not found."
 234+ *error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil];
 235+ }
 236+
 237+ return nil;
 238+ }
 239+
 240+ // We have an existing item, now query for the password data associated with it.
 241+
 242+ NSData *resultData = nil;
 243+ NSMutableDictionary *passwordQuery = [query mutableCopy];
 244+ [passwordQuery setObject: (id) kCFBooleanTrue forKey: (id) kSecReturnData];
 245+
 246+ status = SecItemCopyMatching((CFDictionaryRef) passwordQuery, (CFTypeRef *) &resultData);
 247+
 248+ [resultData autorelease];
 249+ [passwordQuery release];
 250+
 251+ if (status != noErr) {
 252+ if (status == errSecItemNotFound) {
 253+ // We found attributes for the item previously, but no password now, so return a special error.
 254+ // Users of this API will probably want to detect this error and prompt the user to
 255+ // re-enter their credentials. When you attempt to store the re-entered credentials
 256+ // using storeUsername:andPassword:forServiceName:updateExisting:error
 257+ // the old, incorrect entry will be deleted and a new one with a properly encrypted
 258+ // password will be added.
 259+ if (error != nil) {
 260+ *error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -1999 userInfo: nil];
 261+ }
 262+ }
 263+ else {
 264+ // Something else went wrong. Simply return the normal Keychain API error code.
 265+ if (error != nil) {
 266+ *error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil];
 267+ }
 268+ }
 269+
 270+ return nil;
 271+ }
 272+
 273+ NSString *password = nil;
 274+
 275+ if (resultData) {
 276+ password = [[NSString alloc] initWithData: resultData encoding: NSUTF8StringEncoding];
 277+ }
 278+ else {
 279+ // There is an existing item, but we weren't able to get password data for it for some reason,
 280+ // Possibly as a result of an item being incorrectly entered by the previous code.
 281+ // Set the -1999 error so the code above us can prompt the user again.
 282+ if (error != nil) {
 283+ *error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -1999 userInfo: nil];
 284+ }
 285+ }
 286+
 287+ return [password autorelease];
 288+}
 289+
 290++ (BOOL) storeUsername: (NSString *) username andPassword: (NSString *) password forServiceName: (NSString *) serviceName updateExisting: (BOOL) updateExisting error: (NSError **) error
 291+{
 292+ if (!username || !password || !serviceName)
 293+ {
 294+ if (error != nil)
 295+ {
 296+ *error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -2000 userInfo: nil];
 297+ }
 298+ return NO;
 299+ }
 300+
 301+ // See if we already have a password entered for these credentials.
 302+ NSError *getError = nil;
 303+ NSString *existingPassword = [SFHFKeychainUtils getPasswordForUsername: username andServiceName: serviceName error:&getError];
 304+
 305+ if ([getError code] == -1999)
 306+ {
 307+ // There is an existing entry without a password properly stored (possibly as a result of the previous incorrect version of this code.
 308+ // Delete the existing item before moving on entering a correct one.
 309+
 310+ getError = nil;
 311+
 312+ [self deleteItemForUsername: username andServiceName: serviceName error: &getError];
 313+
 314+ if ([getError code] != noErr)
 315+ {
 316+ if (error != nil)
 317+ {
 318+ *error = getError;
 319+ }
 320+ return NO;
 321+ }
 322+ }
 323+ else if ([getError code] != noErr)
 324+ {
 325+ if (error != nil)
 326+ {
 327+ *error = getError;
 328+ }
 329+ return NO;
 330+ }
 331+
 332+ if (error != nil)
 333+ {
 334+ *error = nil;
 335+ }
 336+
 337+ OSStatus status = noErr;
 338+
 339+ if (existingPassword)
 340+ {
 341+ // We have an existing, properly entered item with a password.
 342+ // Update the existing item.
 343+
 344+ if (![existingPassword isEqualToString:password] && updateExisting)
 345+ {
 346+ //Only update if we're allowed to update existing. If not, simply do nothing.
 347+
 348+ NSArray *keys = [[[NSArray alloc] initWithObjects: (NSString *) kSecClass,
 349+ kSecAttrService,
 350+ kSecAttrLabel,
 351+ kSecAttrAccount,
 352+ nil] autorelease];
 353+
 354+ NSArray *objects = [[[NSArray alloc] initWithObjects: (NSString *) kSecClassGenericPassword,
 355+ serviceName,
 356+ serviceName,
 357+ username,
 358+ nil] autorelease];
 359+
 360+ NSDictionary *query = [[[NSDictionary alloc] initWithObjects: objects forKeys: keys] autorelease];
 361+
 362+ status = SecItemUpdate((CFDictionaryRef) query, (CFDictionaryRef) [NSDictionary dictionaryWithObject: [password dataUsingEncoding: NSUTF8StringEncoding] forKey: (NSString *) kSecValueData]);
 363+ }
 364+ }
 365+ else
 366+ {
 367+ // No existing entry (or an existing, improperly entered, and therefore now
 368+ // deleted, entry). Create a new entry.
 369+
 370+ NSArray *keys = [[[NSArray alloc] initWithObjects: (NSString *) kSecClass,
 371+ kSecAttrService,
 372+ kSecAttrLabel,
 373+ kSecAttrAccount,
 374+ kSecValueData,
 375+ nil] autorelease];
 376+
 377+ NSArray *objects = [[[NSArray alloc] initWithObjects: (NSString *) kSecClassGenericPassword,
 378+ serviceName,
 379+ serviceName,
 380+ username,
 381+ [password dataUsingEncoding: NSUTF8StringEncoding],
 382+ nil] autorelease];
 383+
 384+ NSDictionary *query = [[[NSDictionary alloc] initWithObjects: objects forKeys: keys] autorelease];
 385+
 386+ status = SecItemAdd((CFDictionaryRef) query, NULL);
 387+ }
 388+
 389+ if (error != nil && status != noErr)
 390+ {
 391+ // Something went wrong with adding the new item. Return the Keychain error code.
 392+ *error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil];
 393+
 394+ return NO;
 395+ }
 396+
 397+ return YES;
 398+}
 399+
 400++ (BOOL) deleteItemForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error
 401+{
 402+ if (!username || !serviceName)
 403+ {
 404+ if (error != nil)
 405+ {
 406+ *error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -2000 userInfo: nil];
 407+ }
 408+ return NO;
 409+ }
 410+
 411+ if (error != nil)
 412+ {
 413+ *error = nil;
 414+ }
 415+
 416+ NSArray *keys = [[[NSArray alloc] initWithObjects: (NSString *) kSecClass, kSecAttrAccount, kSecAttrService, kSecReturnAttributes, nil] autorelease];
 417+ NSArray *objects = [[[NSArray alloc] initWithObjects: (NSString *) kSecClassGenericPassword, username, serviceName, kCFBooleanTrue, nil] autorelease];
 418+
 419+ NSDictionary *query = [[[NSDictionary alloc] initWithObjects: objects forKeys: keys] autorelease];
 420+
 421+ OSStatus status = SecItemDelete((CFDictionaryRef) query);
 422+
 423+ if (error != nil && status != noErr)
 424+ {
 425+ *error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil];
 426+
 427+ return NO;
 428+ }
 429+
 430+ return YES;
 431+}
 432+
 433+#endif
 434+
 435+@end
\ No newline at end of file
Index: trunk/tools/WikiSnaps/Classes/CommonsUpload.m
@@ -11,6 +11,7 @@
1212 #import "Configuration.h"
1313 #import "ASIFormDataRequest.h"
1414 #import "XMLReader.h"
 15+#import "SFHFKeychainUtils.h"
1516
1617 /* Private */
1718 @interface CommonsUpload (Internal)
@@ -51,9 +52,19 @@
5253
5354 [request addPostValue:@"login" forKey:@"action"];
5455 [request addPostValue:@"xml" forKey: @"format"];
55 - [request addPostValue:[[NSUserDefaults standardUserDefaults] valueForKey:COMMONS_USERNAME_KEY] forKey: @"lgname"];
56 - [request addPostValue:[[NSUserDefaults standardUserDefaults] valueForKey:COMMONS_PASSWORD_KEY] forKey: @"lgpassword"];
57 -
 56+ NSString *username = [[NSUserDefaults standardUserDefaults] valueForKey:COMMONS_USERNAME_KEY];
 57+ [request addPostValue:username forKey: @"lgname"];
 58+
 59+ NSError *error = nil;
 60+ NSString *password = [SFHFKeychainUtils getPasswordForUsername:username andServiceName:COMMONS_KEYCHAIN_KEY error:&error];
 61+ [request addPostValue:password forKey: @"lgpassword"];
 62+
 63+ if( error ) {
 64+ /* password retrieval error */
 65+ [delegate uploadFailed: [error localizedDescription]];
 66+ return;
 67+ }
 68+
5869 [request setDelegate:self];
5970 [request setDidFinishSelector:@selector(requestTokenFinished:)];
6071 [request setDidFailSelector:@selector(requestTokenFailed:)];
@@ -98,9 +109,18 @@
99110 [newRequest addPostValue:@"login" forKey:@"action"];
100111 [newRequest addPostValue:@"xml" forKey: @"format"];
101112 [newRequest addPostValue:token forKey:@"lgtoken"];
102 - [newRequest addPostValue:[[NSUserDefaults standardUserDefaults] valueForKey:COMMONS_USERNAME_KEY] forKey: @"lgname"];
103 - [newRequest addPostValue:[[NSUserDefaults standardUserDefaults] valueForKey:COMMONS_PASSWORD_KEY] forKey: @"lgpassword"];
 113+ NSString *username = [[NSUserDefaults standardUserDefaults] valueForKey:COMMONS_USERNAME_KEY];
 114+ [newRequest addPostValue:username forKey: @"lgname"];
 115+
 116+ NSString *password = [SFHFKeychainUtils getPasswordForUsername:username andServiceName:COMMONS_KEYCHAIN_KEY error:&error];
 117+ [newRequest addPostValue:password forKey: @"lgpassword"];
104118
 119+ if( error ) {
 120+ /* password retrieval error */
 121+ [delegate uploadFailed: [error localizedDescription]];
 122+ return;
 123+ }
 124+
105125 [newRequest setDelegate:self];
106126 [newRequest setDidFinishSelector:@selector(requestLoginFinished:)];
107127 [newRequest setDidFailSelector:@selector(requestLoginFailed:)];
Index: trunk/tools/WikiSnaps/WikiSnaps.xcodeproj/project.pbxproj
@@ -14,6 +14,8 @@
1515 288765A50DF7441C002DB57D /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 288765A40DF7441C002DB57D /* CoreGraphics.framework */; };
1616 28AD733F0D9D9553002E5188 /* MainWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 28AD733E0D9D9553002E5188 /* MainWindow.xib */; };
1717 3A113EC910CF3C5500C9DDCF /* Entitlements.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3A113EC810CF3C5500C9DDCF /* Entitlements.plist */; };
 18+ 4935493F13008DA900A7A44E /* SFHFKeychainUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 4935493E13008DA900A7A44E /* SFHFKeychainUtils.m */; };
 19+ 49354963130096A000A7A44E /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 49354962130096A000A7A44E /* Security.framework */; };
1820 494C81E212E8DBAD000931F2 /* Default.png in Resources */ = {isa = PBXBuildFile; fileRef = 494C81E112E8DBAD000931F2 /* Default.png */; };
1921 494C821212E8E385000931F2 /* SourcePickerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 494C821012E8E385000931F2 /* SourcePickerViewController.m */; };
2022 494C821312E8E385000931F2 /* SourcePickerViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 494C821112E8E385000931F2 /* SourcePickerViewController.xib */; };
@@ -59,6 +61,9 @@
6062 29B97316FDCFA39411CA2CEA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
6163 32CA4F630368D1EE00C91783 /* WikiSnaps_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WikiSnaps_Prefix.pch; sourceTree = "<group>"; };
6264 3A113EC810CF3C5500C9DDCF /* Entitlements.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Entitlements.plist; sourceTree = "<group>"; };
 65+ 4935493D13008DA900A7A44E /* SFHFKeychainUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SFHFKeychainUtils.h; sourceTree = "<group>"; };
 66+ 4935493E13008DA900A7A44E /* SFHFKeychainUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SFHFKeychainUtils.m; sourceTree = "<group>"; };
 67+ 49354962130096A000A7A44E /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; };
6368 494C81E112E8DBAD000931F2 /* Default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Default.png; sourceTree = "<group>"; };
6469 494C820F12E8E385000931F2 /* SourcePickerViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SourcePickerViewController.h; sourceTree = "<group>"; };
6570 494C821012E8E385000931F2 /* SourcePickerViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SourcePickerViewController.m; sourceTree = "<group>"; };
@@ -129,6 +134,7 @@
130135 A159144112E0F6770003BBE2 /* CFNetwork.framework in Frameworks */,
131136 A159144712E0F69C0003BBE2 /* MobileCoreServices.framework in Frameworks */,
132137 A159144B12E0F6BE0003BBE2 /* SystemConfiguration.framework in Frameworks */,
 138+ 49354963130096A000A7A44E /* Security.framework in Frameworks */,
133139 );
134140 runOnlyForDeploymentPostprocessing = 0;
135141 };
@@ -138,6 +144,7 @@
139145 080E96DDFE201D6D7F000001 /* Classes */ = {
140146 isa = PBXGroup;
141147 children = (
 148+ 4935493C13008DA900A7A44E /* security */,
142149 A1338B7112E0EFFF00662B28 /* ASI */,
143150 494C833E12ECE900000931F2 /* XMLtoDict */,
144151 F130561D10CEDBAC00271CD6 /* Configuration.h */,
@@ -213,10 +220,20 @@
214221 A159144012E0F6770003BBE2 /* CFNetwork.framework */,
215222 A159144612E0F69C0003BBE2 /* MobileCoreServices.framework */,
216223 A159144A12E0F6BE0003BBE2 /* SystemConfiguration.framework */,
 224+ 49354962130096A000A7A44E /* Security.framework */,
217225 );
218226 name = Frameworks;
219227 sourceTree = "<group>";
220228 };
 229+ 4935493C13008DA900A7A44E /* security */ = {
 230+ isa = PBXGroup;
 231+ children = (
 232+ 4935493D13008DA900A7A44E /* SFHFKeychainUtils.h */,
 233+ 4935493E13008DA900A7A44E /* SFHFKeychainUtils.m */,
 234+ );
 235+ path = security;
 236+ sourceTree = "<group>";
 237+ };
221238 494C82FA12ECDE94000931F2 /* Images */ = {
222239 isa = PBXGroup;
223240 children = (
@@ -374,6 +391,7 @@
375392 494C821212E8E385000931F2 /* SourcePickerViewController.m in Sources */,
376393 494C82C812ECBEB6000931F2 /* ImageUploadViewController.m in Sources */,
377394 494C834E12ECEAB3000931F2 /* XMLReader.m in Sources */,
 395+ 4935493F13008DA900A7A44E /* SFHFKeychainUtils.m in Sources */,
378396 );
379397 runOnlyForDeploymentPostprocessing = 0;
380398 };

Status & tagging log