source: trunk/macosx/PrefsController.m @ 6357

Last change on this file since 6357 was 6357, checked in by livings124, 14 years ago

clarify the port check failure message

  • Property svn:keywords set to Date Rev Author Id
File size: 40.9 KB
Line 
1/******************************************************************************
2 * $Id: PrefsController.m 6357 2008-07-18 11:48:52Z livings124 $
3 *
4 * Copyright (c) 2005-2008 Transmission authors and contributors
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 *****************************************************************************/
24
25#import "PrefsController.h"
26#import "BlocklistDownloader.h"
27#import "NSApplicationAdditions.h"
28#import "NSStringAdditions.h"
29#import "UKKQueue.h"
30
31#define DOWNLOAD_FOLDER     0
32#define DOWNLOAD_TORRENT    2
33
34#define PROXY_HTTP      0
35#define PROXY_SOCKS4    1
36#define PROXY_SOCKS5    2
37
38#define RPC_ACCESS_ALLOW    0
39#define RPC_ACCESS_DENY     1
40
41#define RPC_IP_ADD_TAG      0
42#define RPC_IP_REMOVE_TAG   1
43
44#define UPDATE_SECONDS 86400
45
46#define TOOLBAR_GENERAL     @"TOOLBAR_GENERAL"
47#define TOOLBAR_TRANSFERS   @"TOOLBAR_TRANSFERS"
48#define TOOLBAR_BANDWIDTH   @"TOOLBAR_BANDWIDTH"
49#define TOOLBAR_PEERS       @"TOOLBAR_PEERS"
50#define TOOLBAR_NETWORK     @"TOOLBAR_NETWORK"
51#define TOOLBAR_REMOTE      @"TOOLBAR_REMOTE"
52
53#define PROXY_KEYCHAIN_SERVICE  "Transmission:Proxy"
54#define PROXY_KEYCHAIN_NAME     "Proxy"
55
56#define RPC_KEYCHAIN_SERVICE    "Transmission:Remote"
57#define RPC_KEYCHAIN_NAME       "Remote"
58
59@interface PrefsController (Private)
60
61- (void) setPrefView: (id) sender;
62
63- (void) folderSheetClosed: (NSOpenPanel *) openPanel returnCode: (int) code contextInfo: (void *) info;
64- (void) incompleteFolderSheetClosed: (NSOpenPanel *) openPanel returnCode: (int) code contextInfo: (void *) info;
65- (void) importFolderSheetClosed: (NSOpenPanel *) openPanel returnCode: (int) code contextInfo: (void *) info;
66
67- (void) setKeychainPassword: (const char *) password forService: (const char *) service username: (const char *) username;
68
69@end
70
71@implementation PrefsController
72
73- (id) initWithHandle: (tr_handle *) handle
74{
75    if ((self = [super initWithWindowNibName: @"PrefsWindow"]))
76    {
77        fDefaults = [NSUserDefaults standardUserDefaults];
78        fHandle = handle;
79       
80        //checks for old version speeds of -1
81        if ([fDefaults integerForKey: @"UploadLimit"] < 0)
82        {
83            [fDefaults setInteger: 20 forKey: @"UploadLimit"];
84            [fDefaults setBool: NO forKey: @"CheckUpload"];
85        }
86        if ([fDefaults integerForKey: @"DownloadLimit"] < 0)
87        {
88            [fDefaults setInteger: 20 forKey: @"DownloadLimit"];
89            [fDefaults setBool: NO forKey: @"CheckDownload"];
90        }
91       
92        //check for old version download location (before 1.1)
93        NSString * choice;
94        if ((choice = [fDefaults stringForKey: @"DownloadChoice"]))
95        {
96            [fDefaults setBool: [choice isEqualToString: @"Constant"] forKey: @"DownloadLocationConstant"];
97            [fDefaults setBool: YES forKey: @"DownloadAsk"];
98           
99            [fDefaults removeObjectForKey: @"DownloadChoice"];
100        }
101       
102        //set check for update to right value
103        [self setCheckForUpdate: nil];
104       
105        //set auto import
106        NSString * autoPath;
107        if ([fDefaults boolForKey: @"AutoImport"] && (autoPath = [fDefaults stringForKey: @"AutoImportDirectory"]))
108            [[UKKQueue sharedFileWatcher] addPath: [autoPath stringByExpandingTildeInPath]];
109       
110        //set encryption
111        [self setEncryptionMode: nil];
112       
113        //actually set bandwidth limits
114        [self applySpeedSettings: nil];
115       
116        //set proxy type
117        [self updateProxyType];
118        [self updateProxyPassword];
119       
120        //update rpc access list
121        [self updateRPCPassword];
122       
123        fRPCAccessArray = [[fDefaults arrayForKey: @"RPCAccessList"] mutableCopy];
124        if (!fRPCAccessArray)
125            fRPCAccessArray = [[NSMutableArray arrayWithObject: [NSDictionary dictionaryWithObjectsAndKeys: @"127.0.0.1", @"IP",
126                                [NSNumber numberWithBool: YES], @"Allow", nil]] retain];
127        [self updateRPCAccessList];
128    }
129   
130    return self;
131}
132
133- (tr_handle *) handle
134{
135    return fHandle;
136}
137
138- (void) dealloc
139{
140    [fPortStatusTimer invalidate];
141    if (fPortChecker)
142    {
143        [fPortChecker cancelProbe];
144        [fPortChecker release];
145    }
146   
147    [fRPCAccessArray release];
148   
149    [super dealloc];
150}
151
152- (void) awakeFromNib
153{
154    fHasLoaded = YES;
155   
156    NSToolbar * toolbar = [[NSToolbar alloc] initWithIdentifier: @"Preferences Toolbar"];
157    [toolbar setDelegate: self];
158    [toolbar setAllowsUserCustomization: NO];
159    [toolbar setDisplayMode: NSToolbarDisplayModeIconAndLabel];
160    [toolbar setSizeMode: NSToolbarSizeModeRegular];
161    [toolbar setSelectedItemIdentifier: TOOLBAR_GENERAL];
162    [[self window] setToolbar: toolbar];
163    [toolbar release];
164   
165    [self setPrefView: nil];
166   
167    if (![NSApp isOnLeopardOrBetter])
168    {
169        [fRPCAddRemoveControl sizeToFit];
170        [fRPCAddRemoveControl setLabel: @"+" forSegment: RPC_IP_ADD_TAG];
171        [fRPCAddRemoveControl setLabel: @"-" forSegment: RPC_IP_REMOVE_TAG];
172    }
173   
174    //set download folder
175    [fFolderPopUp selectItemAtIndex: [fDefaults boolForKey: @"DownloadLocationConstant"] ? DOWNLOAD_FOLDER : DOWNLOAD_TORRENT];
176   
177    //set stop ratio
178    [self updateRatioStopField];
179   
180    //set limits
181    [self updateLimitFields];
182   
183    //set speed limit
184    [fSpeedLimitUploadField setIntValue: [fDefaults integerForKey: @"SpeedLimitUploadLimit"]];
185    [fSpeedLimitDownloadField setIntValue: [fDefaults integerForKey: @"SpeedLimitDownloadLimit"]];
186   
187    //set port
188    [fPortField setIntValue: [fDefaults integerForKey: @"BindPort"]];
189    fNatStatus = -1;
190   
191    [self updatePortStatus];
192    fPortStatusTimer = [NSTimer scheduledTimerWithTimeInterval: 5.0 target: self
193                        selector: @selector(updatePortStatus) userInfo: nil repeats: YES];
194   
195    //set peer connections
196    [fPeersGlobalField setIntValue: [fDefaults integerForKey: @"PeersTotal"]];
197    [fPeersTorrentField setIntValue: [fDefaults integerForKey: @"PeersTorrent"]];
198   
199    //set queue values
200    [fQueueDownloadField setIntValue: [fDefaults integerForKey: @"QueueDownloadNumber"]];
201    [fQueueSeedField setIntValue: [fDefaults integerForKey: @"QueueSeedNumber"]];
202    [fStalledField setIntValue: [fDefaults integerForKey: @"StalledMinutes"]];
203   
204    //set proxy type
205    [fProxyAddressField setStringValue: [fDefaults stringForKey: @"ProxyAddress"]];
206    int proxyType;
207    switch(tr_sessionGetProxyType(fHandle))
208    {
209        case TR_PROXY_SOCKS4:
210            proxyType = PROXY_SOCKS4;
211            break;
212        case TR_PROXY_SOCKS5:
213            proxyType = PROXY_SOCKS5;
214            break;
215        case TR_PROXY_HTTP:
216            proxyType = PROXY_HTTP;
217    }
218    [fProxyTypePopUp selectItemAtIndex: proxyType];
219   
220    //set proxy password - does NOT need to be released
221    [fProxyPasswordField setStringValue: [NSString stringWithUTF8String: tr_sessionGetProxyPassword(fHandle)]];
222   
223    //set proxy port
224    [fProxyPortField setIntValue: [fDefaults integerForKey: @"ProxyPort"]];
225   
226    //set blocklist
227    [self updateBlocklistFields];
228   
229    //set rpc port
230    [fRPCPortField setIntValue: [fDefaults integerForKey: @"RPCPort"]];
231   
232    //set rpc password - has to be released
233    const char * rpcPassword = tr_sessionGetRPCPassword(fHandle);
234    [fRPCPasswordField setStringValue: [NSString stringWithUTF8String: rpcPassword]];
235    tr_free(rpcPassword);
236}
237
238- (void) setUpdater: (SUUpdater *) updater
239{
240    fUpdater = updater;
241}
242
243- (NSToolbarItem *) toolbar: (NSToolbar *) toolbar itemForItemIdentifier: (NSString *) ident willBeInsertedIntoToolbar: (BOOL) flag
244{
245    NSToolbarItem * item = [[NSToolbarItem alloc] initWithItemIdentifier: ident];
246
247    if ([ident isEqualToString: TOOLBAR_GENERAL])
248    {
249        [item setLabel: NSLocalizedString(@"General", "Preferences -> toolbar item title")];
250        [item setImage: [NSImage imageNamed: [NSApp isOnLeopardOrBetter] ? NSImageNamePreferencesGeneral : @"Preferences.png"]];
251        [item setTarget: self];
252        [item setAction: @selector(setPrefView:)];
253        [item setAutovalidates: NO];
254    }
255    else if ([ident isEqualToString: TOOLBAR_TRANSFERS])
256    {
257        [item setLabel: NSLocalizedString(@"Transfers", "Preferences -> toolbar item title")];
258        [item setImage: [NSImage imageNamed: @"Transfers.png"]];
259        [item setTarget: self];
260        [item setAction: @selector(setPrefView:)];
261        [item setAutovalidates: NO];
262    }
263    else if ([ident isEqualToString: TOOLBAR_BANDWIDTH])
264    {
265        [item setLabel: NSLocalizedString(@"Bandwidth", "Preferences -> toolbar item title")];
266        [item setImage: [NSImage imageNamed: @"Bandwidth.png"]];
267        [item setTarget: self];
268        [item setAction: @selector(setPrefView:)];
269        [item setAutovalidates: NO];
270    }
271    else if ([ident isEqualToString: TOOLBAR_PEERS])
272    {
273        [item setLabel: NSLocalizedString(@"Peers", "Preferences -> toolbar item title")];
274        [item setImage: [NSImage imageNamed: [NSApp isOnLeopardOrBetter] ? NSImageNameUserGroup : @"Peers.png"]];
275        [item setTarget: self];
276        [item setAction: @selector(setPrefView:)];
277        [item setAutovalidates: NO];
278    }
279    else if ([ident isEqualToString: TOOLBAR_NETWORK])
280    {
281        [item setLabel: NSLocalizedString(@"Network", "Preferences -> toolbar item title")];
282        [item setImage: [NSImage imageNamed: [NSApp isOnLeopardOrBetter] ? NSImageNameNetwork : @"Network.png"]];
283        [item setTarget: self];
284        [item setAction: @selector(setPrefView:)];
285        [item setAutovalidates: NO];
286    }
287    else if ([ident isEqualToString: TOOLBAR_REMOTE])
288    {
289        [item setLabel: NSLocalizedString(@"Remote", "Preferences -> toolbar item title")];
290        [item setImage: [NSImage imageNamed: @"Remote.png"]];
291        [item setTarget: self];
292        [item setAction: @selector(setPrefView:)];
293        [item setAutovalidates: NO];
294    }
295    else
296    {
297        [item release];
298        return nil;
299    }
300
301    return [item autorelease];
302}
303
304- (NSArray *) toolbarSelectableItemIdentifiers: (NSToolbar *) toolbar
305{
306    return [self toolbarDefaultItemIdentifiers: toolbar];
307}
308
309- (NSArray *) toolbarDefaultItemIdentifiers: (NSToolbar *) toolbar
310{
311    return [self toolbarAllowedItemIdentifiers: toolbar];
312}
313
314- (NSArray *) toolbarAllowedItemIdentifiers: (NSToolbar *) toolbar
315{
316    return [NSArray arrayWithObjects: TOOLBAR_GENERAL, TOOLBAR_TRANSFERS, TOOLBAR_BANDWIDTH,
317                                        TOOLBAR_PEERS, TOOLBAR_NETWORK, TOOLBAR_REMOTE, nil];
318}
319
320//used by ipc
321- (void) updatePortField
322{
323    [fPortField setIntValue: [fDefaults integerForKey: @"BindPort"]];
324}
325
326- (void) setPort: (id) sender
327{
328    int port = [sender intValue];
329    [fDefaults setInteger: port forKey: @"BindPort"];
330    tr_sessionSetPeerPort(fHandle, port);
331   
332    fPeerPort = -1;
333    [self updatePortStatus];
334}
335
336- (void) setNat: (id) sender
337{
338    tr_sessionSetPortForwardingEnabled(fHandle, [fDefaults boolForKey: @"NatTraversal"]);
339   
340    fNatStatus = -1;
341    [self updatePortStatus];
342}
343
344- (void) updatePortStatus
345{
346    const tr_port_forwarding fwd = tr_sessionGetPortForwarding(fHandle);
347    const int port = tr_sessionGetPeerPort(fHandle);
348
349    if (fNatStatus != fwd || fPeerPort != port )
350    {
351        fNatStatus = fwd;
352        fPeerPort = port;
353       
354        [fPortStatusField setStringValue: @""];
355        [fPortStatusImage setImage: nil];
356        [fPortStatusProgress startAnimation: self];
357       
358        if (fPortChecker)
359        {
360            [fPortChecker cancelProbe];
361            [fPortChecker release];
362        }
363        fPortChecker = [[PortChecker alloc] initForPort: fPeerPort withDelegate: self];
364    }
365}
366
367- (void) portCheckerDidFinishProbing: (PortChecker *) portChecker
368{
369    [fPortStatusProgress stopAnimation: self];
370    switch ([fPortChecker status])
371    {
372        case PORT_STATUS_OPEN:
373            [fPortStatusField setStringValue: NSLocalizedString(@"Port is open", "Preferences -> Network -> port status")];
374            [fPortStatusImage setImage: [NSImage imageNamed: @"GreenDot.png"]];
375            break;
376        case PORT_STATUS_CLOSED:
377            [fPortStatusField setStringValue: NSLocalizedString(@"Port is closed", "Preferences -> Network -> port status")];
378            [fPortStatusImage setImage: [NSImage imageNamed: @"RedDot.png"]];
379            break;
380        case PORT_STATUS_STEALTH:
381            [fPortStatusField setStringValue: NSLocalizedString(@"Port is stealth", "Preferences -> Network -> port status")];
382            [fPortStatusImage setImage: [NSImage imageNamed: @"RedDot.png"]];
383            break;
384        case PORT_STATUS_ERROR:
385            [fPortStatusField setStringValue: NSLocalizedString(@"Port check website is down", "Preferences -> Network -> port status")];
386            [fPortStatusImage setImage: [NSImage imageNamed: @"YellowDot.png"]];
387            break;
388    }
389    [fPortChecker release];
390    fPortChecker = nil;
391}
392
393- (NSArray *) sounds
394{
395    NSMutableArray * sounds = [NSMutableArray array];
396   
397    NSMutableArray * directories = [NSMutableArray arrayWithObjects: @"/System/Library/Sounds", @"/Library/Sounds", nil];
398    if ([NSApp isOnLeopardOrBetter])
399        [directories addObject: [NSHomeDirectory() stringByAppendingPathComponent: @"Library/Sounds"]];
400   
401    BOOL isDirectory;
402    NSString * directory;
403    NSEnumerator * enumerator = [directories objectEnumerator];
404    while ((directory = [enumerator nextObject]))
405        if ([[NSFileManager defaultManager] fileExistsAtPath: directory isDirectory: &isDirectory] && isDirectory)
406        {
407            NSString * sound;
408            NSEnumerator * soundEnumerator = [[[NSFileManager defaultManager] directoryContentsAtPath: directory] objectEnumerator];
409            while ((sound = [soundEnumerator nextObject]))
410            {
411                sound = [sound stringByDeletingPathExtension];
412                if ([NSSound soundNamed: sound])
413                    [sounds addObject: sound];
414            }
415        }
416   
417    return sounds;
418}
419
420- (void) setSound: (id) sender
421{
422    //play sound when selecting
423    NSSound * sound;
424    if ((sound = [NSSound soundNamed: [sender titleOfSelectedItem]]))
425        [sound play];
426}
427
428- (void) setPeersGlobal: (id) sender
429{
430    int count = [sender intValue];
431    [fDefaults setInteger: count forKey: @"PeersTotal"];
432    tr_sessionSetPeerLimit(fHandle, count);
433}
434
435- (void) setPeersTorrent: (id) sender
436{
437    int count = [sender intValue];
438    [fDefaults setInteger: count forKey: @"PeersTorrent"];
439}
440
441- (void) setPEX: (id) sender
442{
443    tr_sessionSetPexEnabled(fHandle, [fDefaults boolForKey: @"PEXGlobal"]);
444}
445
446- (void) setEncryptionMode: (id) sender
447{
448    tr_sessionSetEncryption(fHandle, [fDefaults boolForKey: @"EncryptionPrefer"] ?
449        ([fDefaults boolForKey: @"EncryptionRequire"] ? TR_ENCRYPTION_REQUIRED : TR_ENCRYPTION_PREFERRED) : TR_PLAINTEXT_PREFERRED);
450}
451
452- (void) setBlocklistEnabled: (id) sender
453{
454    BOOL enable = [sender state] == NSOnState;
455    [fDefaults setBool: enable forKey: @"Blocklist"];
456    tr_blocklistSetEnabled(fHandle, enable);
457}
458
459- (void) updateBlocklist: (id) sender
460{
461    [BlocklistDownloader downloadWithPrefsController: self];
462}
463
464- (void) updateBlocklistFields
465{
466    BOOL exists = tr_blocklistExists(fHandle);
467   
468    if (exists)
469    {
470        NSNumberFormatter * numberFormatter = [[NSNumberFormatter alloc] init];
471        [numberFormatter setNumberStyle: NSNumberFormatterDecimalStyle];
472        [numberFormatter setMaximumFractionDigits: 0];
473        NSString * countString = [numberFormatter stringFromNumber: [NSNumber numberWithInt: tr_blocklistGetRuleCount(fHandle)]];
474        [numberFormatter release];
475       
476        [fBlocklistMessageField setStringValue: [NSString stringWithFormat: NSLocalizedString(@"%@ IP address rules in list",
477            "Prefs -> blocklist -> message"), countString]];
478    }
479    else
480        [fBlocklistMessageField setStringValue: NSLocalizedString(@"A blocklist must first be downloaded",
481            "Prefs -> blocklist -> message")];
482   
483    [fBlocklistEnableCheck setEnabled: exists];
484    [fBlocklistEnableCheck setState: exists && [fDefaults boolForKey: @"Blocklist"]];
485}
486
487- (void) applySpeedSettings: (id) sender
488{
489    if ([fDefaults boolForKey: @"SpeedLimit"])
490    {
491        tr_sessionSetSpeedLimitEnabled(fHandle, TR_UP, 1);
492        tr_sessionSetSpeedLimit(fHandle, TR_UP, [fDefaults integerForKey: @"SpeedLimitUploadLimit"]);
493       
494        tr_sessionSetSpeedLimitEnabled(fHandle, TR_DOWN, 1);
495        tr_sessionSetSpeedLimit(fHandle, TR_DOWN, [fDefaults integerForKey: @"SpeedLimitDownloadLimit"]);
496    }
497    else
498    {
499        tr_sessionSetSpeedLimitEnabled(fHandle, TR_UP, [fDefaults boolForKey: @"CheckUpload"]);
500        tr_sessionSetSpeedLimit(fHandle, TR_UP, [fDefaults integerForKey: @"UploadLimit"]);
501       
502        tr_sessionSetSpeedLimitEnabled(fHandle, TR_DOWN, [fDefaults boolForKey: @"CheckDownload"]);
503        tr_sessionSetSpeedLimit(fHandle, TR_DOWN, [fDefaults integerForKey: @"DownloadLimit"]);
504    }
505}
506
507- (void) applyRatioSetting: (id) sender
508{
509    [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateUI" object: nil];
510}
511
512- (void) updateRatioStopField
513{
514    if (!fHasLoaded)
515        return;
516   
517    [fRatioStopField setFloatValue: [fDefaults floatForKey: @"RatioLimit"]];
518   
519    [self applyRatioSetting: nil];
520}
521
522- (void) setRatioStop: (id) sender
523{
524    [fDefaults setFloat: [sender floatValue] forKey: @"RatioLimit"];
525    [self applyRatioSetting: nil];
526}
527
528- (void) updateLimitFields
529{
530    if (!fHasLoaded)
531        return;
532   
533    [fUploadField setIntValue: [fDefaults integerForKey: @"UploadLimit"]];
534    [fDownloadField setIntValue: [fDefaults integerForKey: @"DownloadLimit"]];
535}
536
537- (void) setGlobalLimit: (id) sender
538{
539    [fDefaults setInteger: [sender intValue] forKey: sender == fUploadField ? @"UploadLimit" : @"DownloadLimit"];
540    [self applySpeedSettings: self];
541}
542
543- (void) setSpeedLimit: (id) sender
544{
545    [fDefaults setInteger: [sender intValue] forKey: sender == fSpeedLimitUploadField
546                                                        ? @"SpeedLimitUploadLimit" : @"SpeedLimitDownloadLimit"];
547    [self applySpeedSettings: self];
548}
549
550- (void) setAutoSpeedLimit: (id) sender
551{
552    [[NSNotificationCenter defaultCenter] postNotificationName: @"AutoSpeedLimitChange" object: self];
553}
554
555- (BOOL) control: (NSControl *) control textShouldBeginEditing: (NSText *) fieldEditor
556{
557    [fInitialString release];
558    fInitialString = [[control stringValue] retain];
559   
560    return YES;
561}
562
563- (BOOL) control: (NSControl *) control didFailToFormatString: (NSString *) string errorDescription: (NSString *) error
564{
565    NSBeep();
566    if (fInitialString)
567    {
568        [control setStringValue: fInitialString];
569        [fInitialString release];
570        fInitialString = nil;
571    }
572    return NO;
573}
574
575- (void) setBadge: (id) sender
576{
577    [[NSNotificationCenter defaultCenter] postNotificationName: @"DockBadgeChange" object: self];
578}
579
580- (void) resetWarnings: (id) sender
581{
582    [fDefaults setBool: YES forKey: @"WarningDuplicate"];
583    [fDefaults setBool: YES forKey: @"WarningRemainingSpace"];
584    [fDefaults setBool: YES forKey: @"WarningFolderDataSameName"];
585    [fDefaults setBool: YES forKey: @"WarningResetStats"];
586    [fDefaults setBool: YES forKey: @"WarningCreatorBlankAddress"];
587    [fDefaults setBool: YES forKey: @"WarningRemoveBuiltInTracker"];
588    [fDefaults setBool: YES forKey: @"WarningInvalidOpen"];
589}
590
591- (void) setCheckForUpdate: (id) sender
592{
593    NSTimeInterval seconds = [fDefaults boolForKey: @"CheckForUpdates"] ? UPDATE_SECONDS : 0;
594    [fDefaults setInteger: seconds forKey: @"SUScheduledCheckInterval"];
595    if (fUpdater)
596        [fUpdater scheduleCheckWithInterval: seconds];
597}
598
599- (void) setQueue: (id) sender
600{
601    [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateQueue" object: self];
602}
603
604- (void) setQueueNumber: (id) sender
605{
606    [fDefaults setInteger: [sender intValue] forKey: sender == fQueueDownloadField ? @"QueueDownloadNumber" : @"QueueSeedNumber"];
607    [self setQueue: nil];
608}
609
610- (void) setStalled: (id) sender
611{
612    [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateQueue" object: self];
613}
614
615- (void) setStalledMinutes: (id) sender
616{
617    [fDefaults setInteger: [sender intValue] forKey: @"StalledMinutes"];
618    [self setStalled: nil];
619}
620
621- (void) setDownloadLocation: (id) sender
622{
623    [fDefaults setBool: [fFolderPopUp indexOfSelectedItem] == DOWNLOAD_FOLDER forKey: @"DownloadLocationConstant"];
624}
625
626- (void) folderSheetShow: (id) sender
627{
628    NSOpenPanel * panel = [NSOpenPanel openPanel];
629
630    [panel setPrompt: NSLocalizedString(@"Select", "Preferences -> Open panel prompt")];
631    [panel setAllowsMultipleSelection: NO];
632    [panel setCanChooseFiles: NO];
633    [panel setCanChooseDirectories: YES];
634    [panel setCanCreateDirectories: YES];
635
636    [panel beginSheetForDirectory: nil file: nil types: nil
637        modalForWindow: [self window] modalDelegate: self didEndSelector:
638        @selector(folderSheetClosed:returnCode:contextInfo:) contextInfo: nil];
639}
640
641- (void) incompleteFolderSheetShow: (id) sender
642{
643    NSOpenPanel * panel = [NSOpenPanel openPanel];
644
645    [panel setPrompt: NSLocalizedString(@"Select", "Preferences -> Open panel prompt")];
646    [panel setAllowsMultipleSelection: NO];
647    [panel setCanChooseFiles: NO];
648    [panel setCanChooseDirectories: YES];
649    [panel setCanCreateDirectories: YES];
650
651    [panel beginSheetForDirectory: nil file: nil types: nil
652        modalForWindow: [self window] modalDelegate: self didEndSelector:
653        @selector(incompleteFolderSheetClosed:returnCode:contextInfo:) contextInfo: nil];
654}
655
656- (void) setAutoImport: (id) sender
657{
658    NSString * path;
659    if ((path = [fDefaults stringForKey: @"AutoImportDirectory"]))
660    {
661        path = [path stringByExpandingTildeInPath];
662        if ([fDefaults boolForKey: @"AutoImport"])
663            [[UKKQueue sharedFileWatcher] addPath: path];
664        else
665            [[UKKQueue sharedFileWatcher] removePathFromQueue: path];
666       
667        [[NSNotificationCenter defaultCenter] postNotificationName: @"AutoImportSettingChange" object: self];
668    }
669    else
670        [self importFolderSheetShow: nil];
671}
672
673- (void) importFolderSheetShow: (id) sender
674{
675    NSOpenPanel * panel = [NSOpenPanel openPanel];
676
677    [panel setPrompt: NSLocalizedString(@"Select", "Preferences -> Open panel prompt")];
678    [panel setAllowsMultipleSelection: NO];
679    [panel setCanChooseFiles: NO];
680    [panel setCanChooseDirectories: YES];
681    [panel setCanCreateDirectories: YES];
682
683    [panel beginSheetForDirectory: nil file: nil types: nil
684        modalForWindow: [self window] modalDelegate: self didEndSelector:
685        @selector(importFolderSheetClosed:returnCode:contextInfo:) contextInfo: nil];
686}
687
688- (void) setAutoSize: (id) sender
689{
690    [[NSNotificationCenter defaultCenter] postNotificationName: @"AutoSizeSettingChange" object: self];
691}
692
693- (void) setProxyEnabled: (id) sender
694{
695    tr_sessionSetProxyEnabled(fHandle, [fDefaults boolForKey: @"Proxy"]);
696}
697
698- (void) setProxyAddress: (id) sender
699{
700    NSString * address = [sender stringValue];
701    BOOL blank = [address isEqualToString: @""];
702   
703    if (!blank && [address rangeOfString: @"://"].location == NSNotFound)
704        address = [@"http://" stringByAppendingString: address];
705   
706    if (blank || tr_httpIsValidURL([address UTF8String]))
707    {
708        tr_sessionSetProxy(fHandle, [address UTF8String]);
709        [sender setStringValue: address];
710        [fDefaults setObject: address forKey: @"ProxyAddress"];
711    }
712    else
713    {
714        NSBeep();
715        [sender setStringValue: [fDefaults stringForKey: @"ProxyAddress"]];
716    }
717}
718
719- (void) setProxyPort: (id) sender
720{
721    int port = [sender intValue];
722    [fDefaults setInteger: port forKey: @"ProxyPort"];
723    tr_sessionSetProxyPort(fHandle, port);
724}
725
726- (void) setProxyType: (id) sender
727{
728    NSString * type;
729    switch ([sender indexOfSelectedItem])
730    {
731        case PROXY_HTTP:
732            type = @"HTTP";
733            break;
734        case PROXY_SOCKS4:
735            type = @"SOCKS4";
736            break;
737        case PROXY_SOCKS5:
738            type = @"SOCKS5";
739    }
740   
741    [fDefaults setObject: type forKey: @"ProxyType"];
742    [self updateProxyType];
743}
744
745- (void) updateProxyType
746{
747    NSString * typeString = [fDefaults stringForKey: @"ProxyType"];
748    tr_proxy_type type;
749    if ([typeString isEqualToString: @"SOCKS4"])
750        type = TR_PROXY_SOCKS4;
751    else if ([typeString isEqualToString: @"SOCKS5"])
752        type = TR_PROXY_SOCKS4;
753    else
754    {
755        //safety
756        if (![typeString isEqualToString: @"HTTP"])
757        {
758            typeString = @"HTTP";
759            [fDefaults setObject: typeString forKey: @"ProxyType"];
760        }
761        type = TR_PROXY_HTTP;
762    }
763   
764    tr_sessionSetProxyType(fHandle, type);
765}
766
767- (void) setProxyAuthorize: (id) sender
768{
769    BOOL enable = [fDefaults boolForKey: @"ProxyAuthorize"];
770    tr_sessionSetProxyAuthEnabled(fHandle, enable);
771}
772
773- (void) setProxyUsername: (id) sender
774{
775    tr_sessionSetProxyUsername(fHandle, [[fDefaults stringForKey: @"ProxyUsername"] UTF8String]);
776}
777
778- (void) setProxyPassword: (id) sender
779{
780    const char * password = [[sender stringValue] UTF8String];
781    [self setKeychainPassword: password forService: PROXY_KEYCHAIN_SERVICE username: PROXY_KEYCHAIN_NAME];
782   
783    tr_sessionSetProxyPassword(fHandle, password);
784}
785
786- (void) updateProxyPassword
787{
788    UInt32 passwordLength;
789    const char * password = nil;
790    SecKeychainFindGenericPassword(NULL, strlen(PROXY_KEYCHAIN_SERVICE), PROXY_KEYCHAIN_SERVICE,
791        strlen(PROXY_KEYCHAIN_NAME), PROXY_KEYCHAIN_NAME, &passwordLength, (void **)&password, NULL);
792   
793    if (password != NULL)
794    {
795        char fullPassword[passwordLength+1];
796        strncpy(fullPassword, password, passwordLength);
797        fullPassword[passwordLength] = '\0';
798        SecKeychainItemFreeContent(NULL, (void *)password);
799       
800        tr_sessionSetProxyPassword(fHandle, fullPassword);
801        [fProxyPasswordField setStringValue: [NSString stringWithUTF8String: fullPassword]];
802    }
803}
804
805- (void) setRPCEnabled: (id) sender
806{
807    tr_sessionSetRPCEnabled(fHandle, [fDefaults boolForKey: @"RPC"]);
808}
809
810- (void) setRPCAuthorize: (id) sender
811{
812    tr_sessionSetRPCPasswordEnabled(fHandle, [fDefaults boolForKey: @"RPCAuthorize"]);
813}
814
815- (void) setRPCUsername: (id) sender
816{
817    tr_sessionSetRPCUsername(fHandle, [[fDefaults stringForKey: @"RPCUsername"] UTF8String]);
818}
819
820- (void) setRPCPassword: (id) sender
821{
822    const char * password = [[sender stringValue] UTF8String];
823    [self setKeychainPassword: password forService: RPC_KEYCHAIN_SERVICE username: RPC_KEYCHAIN_NAME];
824   
825    tr_sessionSetRPCPassword(fHandle, password);
826}
827
828- (void) updateRPCPassword
829{
830    UInt32 passwordLength;
831    const char * password = nil;
832    SecKeychainFindGenericPassword(NULL, strlen(RPC_KEYCHAIN_SERVICE), RPC_KEYCHAIN_SERVICE,
833        strlen(RPC_KEYCHAIN_NAME), RPC_KEYCHAIN_NAME, &passwordLength, (void **)&password, NULL);
834   
835    if (password != NULL)
836    {
837        char fullPassword[passwordLength+1];
838        strncpy(fullPassword, password, passwordLength);
839        fullPassword[passwordLength] = '\0';
840        SecKeychainItemFreeContent(NULL, (void *)password);
841       
842        tr_sessionSetRPCPassword(fHandle, fullPassword);
843        [fRPCPasswordField setStringValue: [NSString stringWithUTF8String: fullPassword]];
844    }
845}
846
847- (void) setRPCPort: (id) sender
848{
849    int port = [sender intValue];
850    [fDefaults setInteger: port forKey: @"RPCPort"];
851    tr_sessionSetRPCPort(fHandle, port);
852}
853
854- (void) updateRPCAccessList
855{
856    NSMutableArray * components = [NSMutableArray arrayWithCapacity: [fRPCAccessArray count]];
857   
858    NSEnumerator * enumerator = [fRPCAccessArray objectEnumerator];
859    NSDictionary * dict;
860    while ((dict = [enumerator nextObject]))
861        [components addObject: [NSString stringWithFormat: @"%c%@", [[dict objectForKey: @"Allow"] boolValue] ? '+' : '-',
862                                [dict objectForKey: @"IP"]]];
863   
864    NSString * string = [components componentsJoinedByString: @","];
865   
866    char * error = NULL;
867    if (tr_sessionSetRPCACL(fHandle, [string UTF8String], &error))
868    {
869        NSLog([NSString stringWithUTF8String: error]);
870        tr_free(error);
871    }
872}
873
874- (void) addRemoveRPCIP: (id) sender
875{
876    //don't allow add/remove when currently adding - it leads to weird results
877    if ([fRPCAccessTable editedRow] != -1)
878        return;
879   
880    if ([[sender cell] tagForSegment: [sender selectedSegment]] == RPC_IP_REMOVE_TAG)
881    {
882        [fRPCAccessArray removeObjectsAtIndexes: [fRPCAccessTable selectedRowIndexes]];
883        [fRPCAccessTable deselectAll: self];
884        [fRPCAccessTable reloadData];
885       
886        [fDefaults setObject: fRPCAccessArray forKey: @"RPCAccessList"];
887        [self updateRPCAccessList];
888    }
889    else
890    {
891        [fRPCAccessArray addObject: [NSDictionary dictionaryWithObjectsAndKeys: @"", @"IP",
892                                        [NSNumber numberWithBool: YES], @"Allow", nil]];
893        [fRPCAccessTable reloadData];
894       
895        int row = [fRPCAccessArray count] - 1;
896        [fRPCAccessTable selectRow: row byExtendingSelection: NO];
897        [fRPCAccessTable editColumn: 0 row: row withEvent: nil select: YES];
898    }
899}
900
901- (NSInteger) numberOfRowsInTableView: (NSTableView *) tableView
902{
903    return [fRPCAccessArray count];
904}
905
906- (id) tableView: (NSTableView *) tableView objectValueForTableColumn: (NSTableColumn *) tableColumn row: (NSInteger) row
907{
908    NSDictionary * dict = [fRPCAccessArray objectAtIndex: row];
909   
910    NSString * ident = [tableColumn identifier];
911    if ([ident isEqualToString: @"Permission"])
912    {
913        int allow = [[dict objectForKey: @"Allow"] boolValue] ? RPC_ACCESS_ALLOW : RPC_ACCESS_DENY;
914        return [NSNumber numberWithInt: allow];
915    }
916    else
917        return [dict objectForKey: @"IP"];
918}
919
920- (void) tableView: (NSTableView *) tableView setObjectValue: (id) object forTableColumn: (NSTableColumn *) tableColumn
921    row: (NSInteger) row
922{
923    NSDictionary * oldDict = [fRPCAccessArray objectAtIndex: row];
924   
925    NSString * ident = [tableColumn identifier];
926    if ([ident isEqualToString: @"Permission"])
927    {
928        NSNumber * allow = [NSNumber numberWithBool: [object intValue] == RPC_ACCESS_ALLOW];
929        NSDictionary * newDict = [NSDictionary dictionaryWithObjectsAndKeys: [oldDict objectForKey: @"IP"], @"IP", allow, @"Allow", nil];
930        [fRPCAccessArray replaceObjectAtIndex: row withObject: newDict];
931    }
932    else
933    {
934        NSArray * components = [object componentsSeparatedByString: @"."];
935        NSMutableArray * newComponents = [NSMutableArray arrayWithCapacity: 4];
936       
937        //create better-formatted ip string
938        if ([components count] == 4)
939        {
940            NSEnumerator * enumerator = [components objectEnumerator];
941            NSString * component;
942            while ((component = [enumerator nextObject]))
943            {
944                if ([component isEqualToString: @"*"])
945                    [newComponents addObject: component];
946                else
947                    [newComponents addObject: [[NSNumber numberWithInt: [component intValue]] stringValue]];
948            }
949        }
950       
951        NSString * newIP = [newComponents componentsJoinedByString: @"."];
952       
953        //verify ip string
954        if (!tr_sessionTestRPCACL(fHandle, [[@"+" stringByAppendingString: newIP] UTF8String], NULL))
955        {
956            NSDictionary * newDict = [NSDictionary dictionaryWithObjectsAndKeys: newIP, @"IP",
957                                        [oldDict objectForKey: @"Allow"], @"Allow", nil];
958            [fRPCAccessArray replaceObjectAtIndex: row withObject: newDict];
959           
960            NSSortDescriptor * descriptor = [[[NSSortDescriptor alloc] initWithKey: @"IP" ascending: YES
961                                                selector: @selector(compareNumeric:)] autorelease];
962            [fRPCAccessArray sortUsingDescriptors: [NSArray arrayWithObject: descriptor]];
963        }
964        else
965        {
966            NSBeep();
967           
968            if ([[oldDict objectForKey: @"IP"] isEqualToString: @""])
969                [fRPCAccessArray removeObjectAtIndex: row];
970        }
971       
972        [fRPCAccessTable deselectAll: self];
973        [fRPCAccessTable reloadData];
974    }
975   
976    [fDefaults setObject: fRPCAccessArray forKey: @"RPCAccessList"];
977    [self updateRPCAccessList];
978}
979
980- (void) tableViewSelectionDidChange: (NSNotification *) notification
981{
982    [fRPCAddRemoveControl setEnabled: [fRPCAccessTable numberOfSelectedRows] > 0 forSegment: RPC_IP_REMOVE_TAG];
983}
984
985- (void) helpForPeers: (id) sender
986{
987    [[NSHelpManager sharedHelpManager] openHelpAnchor: @"PeersPrefs"
988        inBook: [[NSBundle mainBundle] objectForInfoDictionaryKey: @"CFBundleHelpBookName"]];
989}
990
991- (void) helpForNetwork: (id) sender
992{
993    [[NSHelpManager sharedHelpManager] openHelpAnchor: @"NetworkPrefs"
994        inBook: [[NSBundle mainBundle] objectForInfoDictionaryKey: @"CFBundleHelpBookName"]];
995}
996
997- (void) helpForRemote: (id) sender
998{
999    [[NSHelpManager sharedHelpManager] openHelpAnchor: @"RemotePrefs"
1000        inBook: [[NSBundle mainBundle] objectForInfoDictionaryKey: @"CFBundleHelpBookName"]];
1001}
1002
1003- (void) rpcUpdatePrefs
1004{
1005    //encryption
1006    tr_encryption_mode encryptionMode = tr_sessionGetEncryption(fHandle);
1007    [fDefaults setBool: encryptionMode != TR_PLAINTEXT_PREFERRED forKey: @"EncryptionPrefer"];
1008    [fDefaults setBool: encryptionMode == TR_ENCRYPTION_REQUIRED forKey: @"EncryptionRequire"];
1009   
1010    //download directory
1011    NSString * downloadLocation = [[NSString stringWithUTF8String: tr_sessionGetDownloadDir(fHandle)] stringByStandardizingPath];
1012    [fDefaults setObject: downloadLocation forKey: @"DownloadFolder"];
1013   
1014    //peers
1015    uint16_t peersTotal = tr_sessionGetPeerLimit(fHandle);
1016    [fDefaults setInteger: peersTotal forKey: @"PeersTotal"];
1017   
1018    //pex
1019    BOOL pex = tr_sessionIsPexEnabled(fHandle);
1020    [fDefaults setBool: pex forKey: @"PEXGlobal"];
1021   
1022    //port
1023    int port = tr_sessionGetPeerPort(fHandle);
1024    [fDefaults setInteger: port forKey: @"BindPort"];
1025   
1026    BOOL nat = tr_sessionIsPortForwardingEnabled(fHandle);
1027    [fDefaults setBool: nat forKey: @"NatTraversal"];
1028   
1029    fPeerPort = -1;
1030    fNatStatus = -1;
1031    [self updatePortStatus];
1032   
1033    //speed limit - down
1034    BOOL downLimitEnabled = tr_sessionIsSpeedLimitEnabled(fHandle, TR_DOWN);
1035    [fDefaults setBool: downLimitEnabled forKey: @"CheckDownload"];
1036   
1037    int downLimit = tr_sessionGetSpeedLimit(fHandle, TR_DOWN);
1038    [fDefaults setInteger: downLimit forKey: @"DownloadLimit"];
1039   
1040    //speed limit - up
1041    BOOL upLimitEnabled = tr_sessionIsSpeedLimitEnabled(fHandle, TR_UP);
1042    [fDefaults setBool: upLimitEnabled forKey: @"CheckUpload"];
1043   
1044    int upLimit = tr_sessionGetSpeedLimit(fHandle, TR_UP);
1045    [fDefaults setInteger: upLimit forKey: @"UploadLimit"];
1046   
1047    //update gui if loaded
1048    if (fHasLoaded)
1049    {
1050        //encryption handled by bindings
1051       
1052        //download directory handled by bindings
1053       
1054        [fPeersGlobalField setIntValue: peersTotal];
1055       
1056        //pex handled by bindings
1057       
1058        [fPortField setIntValue: port];
1059        //port forwarding (nat) handled by bindings
1060       
1061        //limit check handled by bindings
1062        [fDownloadField setIntValue: downLimit];
1063       
1064        //limit check handled by bindings
1065        [fUploadField setIntValue: upLimit];
1066    }
1067}
1068
1069@end
1070
1071@implementation PrefsController (Private)
1072
1073- (void) setPrefView: (id) sender
1074{
1075    NSView * view = fGeneralView;
1076    if (sender)
1077    {
1078        NSString * identifier = [sender itemIdentifier];
1079        if ([identifier isEqualToString: TOOLBAR_TRANSFERS])
1080            view = fTransfersView;
1081        else if ([identifier isEqualToString: TOOLBAR_BANDWIDTH])
1082            view = fBandwidthView;
1083        else if ([identifier isEqualToString: TOOLBAR_PEERS])
1084            view = fPeersView;
1085        else if ([identifier isEqualToString: TOOLBAR_NETWORK])
1086            view = fNetworkView;
1087        else if ([identifier isEqualToString: TOOLBAR_REMOTE])
1088            view = fRemoteView;
1089        else; //general view already selected
1090    }
1091   
1092    NSWindow * window = [self window];
1093    if ([window contentView] == view)
1094        return;
1095   
1096    NSRect windowRect = [window frame];
1097    float difference = ([view frame].size.height - [[window contentView] frame].size.height) * [window userSpaceScaleFactor];
1098    windowRect.origin.y -= difference;
1099    windowRect.size.height += difference;
1100   
1101    [view setHidden: YES];
1102    [window setContentView: view];
1103    [window setFrame: windowRect display: YES animate: YES];
1104    [view setHidden: NO];
1105   
1106    //set title label
1107    if (sender)
1108        [window setTitle: [sender label]];
1109    else
1110    {
1111        NSToolbar * toolbar = [window toolbar];
1112        NSString * itemIdentifier = [toolbar selectedItemIdentifier];
1113        NSEnumerator * enumerator = [[toolbar items] objectEnumerator];
1114        NSToolbarItem * item;
1115        while ((item = [enumerator nextObject]))
1116            if ([[item itemIdentifier] isEqualToString: itemIdentifier])
1117            {
1118                [window setTitle: [item label]];
1119                break;
1120            }
1121    }
1122   
1123    //for network view make sure progress indicator hides itself (get around a Tiger bug)
1124    if (![NSApp isOnLeopardOrBetter] && view == fNetworkView && [fPortStatusImage image])
1125        [fPortStatusProgress setDisplayedWhenStopped: NO];
1126}
1127
1128- (void) folderSheetClosed: (NSOpenPanel *) openPanel returnCode: (int) code contextInfo: (void *) info
1129{
1130    if (code == NSOKButton)
1131    {
1132        [fFolderPopUp selectItemAtIndex: DOWNLOAD_FOLDER];
1133        [fDefaults setObject: [[openPanel filenames] objectAtIndex: 0] forKey: @"DownloadFolder"];
1134        [fDefaults setObject: @"Constant" forKey: @"DownloadChoice"];
1135    }
1136    else
1137    {
1138        //reset if cancelled
1139        [fFolderPopUp selectItemAtIndex: [fDefaults boolForKey: @"DownloadLocationConstant"] ? DOWNLOAD_FOLDER : DOWNLOAD_TORRENT];
1140    }
1141}
1142
1143- (void) incompleteFolderSheetClosed: (NSOpenPanel *) openPanel returnCode: (int) code contextInfo: (void *) info
1144{
1145    if (code == NSOKButton)
1146        [fDefaults setObject: [[openPanel filenames] objectAtIndex: 0] forKey: @"IncompleteDownloadFolder"];
1147    [fIncompleteFolderPopUp selectItemAtIndex: 0];
1148}
1149
1150- (void) importFolderSheetClosed: (NSOpenPanel *) openPanel returnCode: (int) code contextInfo: (void *) info
1151{
1152    NSString * path = [fDefaults stringForKey: @"AutoImportDirectory"];
1153    if (code == NSOKButton)
1154    {
1155        UKKQueue * sharedQueue = [UKKQueue sharedFileWatcher];
1156        if (path)
1157            [sharedQueue removePathFromQueue: [path stringByExpandingTildeInPath]];
1158       
1159        path = [[openPanel filenames] objectAtIndex: 0];
1160        [fDefaults setObject: path forKey: @"AutoImportDirectory"];
1161        [sharedQueue addPath: [path stringByExpandingTildeInPath]];
1162       
1163        [[NSNotificationCenter defaultCenter] postNotificationName: @"AutoImportSettingChange" object: self];
1164    }
1165    else if (!path)
1166        [fDefaults setBool: NO forKey: @"AutoImport"];
1167   
1168    [fImportFolderPopUp selectItemAtIndex: 0];
1169}
1170
1171- (void) setKeychainPassword: (const char *) password forService: (const char *) service username: (const char *) username
1172{
1173    SecKeychainItemRef item = NULL;
1174    NSUInteger passwordLength = strlen(password);
1175   
1176    OSStatus result = SecKeychainFindGenericPassword(NULL, strlen(service), service, strlen(username), username, NULL, NULL, &item);
1177    if (result == noErr && item)
1178    {
1179        if (passwordLength > 0) //found, so update
1180        {
1181            result = SecKeychainItemModifyAttributesAndData(item, NULL, passwordLength, (const void *)password);
1182            if (result != noErr)
1183                NSLog(@"Problem updating Keychain item: %s", GetMacOSStatusErrorString(result));
1184        }
1185        else //remove the item
1186        {
1187            result = SecKeychainItemDelete(item);
1188            if (result != noErr)
1189                NSLog(@"Problem removing Keychain item: %s", GetMacOSStatusErrorString(result));
1190        }
1191    }
1192    else if (result == errSecItemNotFound) //not found, so add
1193    {
1194        if (passwordLength > 0)
1195        {
1196            result = SecKeychainAddGenericPassword(NULL, strlen(service), service, strlen(username), username,
1197                        passwordLength, (const void *)password, NULL);
1198            if (result != noErr)
1199                NSLog(@"Problem adding Keychain item: %s", GetMacOSStatusErrorString(result));
1200        }
1201    }
1202    else
1203        NSLog(@"Problem accessing Keychain: %s", GetMacOSStatusErrorString(result));
1204}
1205
1206@end
Note: See TracBrowser for help on using the repository browser.