source: trunk/macosx/PrefsController.m @ 11556

Last change on this file since 11556 was 11556, checked in by livings124, 11 years ago

silence a warning

  • Property svn:keywords set to Date Rev Author Id
File size: 49.2 KB
Line 
1/******************************************************************************
2 * $Id: PrefsController.m 11556 2010-12-20 14:25:20Z livings124 $
3 *
4 * Copyright (c) 2005-2010 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 "BlocklistDownloaderViewController.h"
27#import "BlocklistScheduler.h"
28#import "PortChecker.h"
29#import "BonjourController.h"
30#import "NSApplicationAdditions.h"
31#import "NSStringAdditions.h"
32#import "UKKQueue.h"
33
34#import "transmission.h"
35#import "utils.h"
36
37#import <Sparkle/Sparkle.h>
38
39#define DOWNLOAD_FOLDER     0
40#define DOWNLOAD_TORRENT    2
41
42#define RPC_IP_ADD_TAG      0
43#define RPC_IP_REMOVE_TAG   1
44
45#define TOOLBAR_GENERAL     @"TOOLBAR_GENERAL"
46#define TOOLBAR_TRANSFERS   @"TOOLBAR_TRANSFERS"
47#define TOOLBAR_GROUPS      @"TOOLBAR_GROUPS"
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 RPC_KEYCHAIN_SERVICE    "Transmission:Remote"
54#define RPC_KEYCHAIN_NAME       "Remote"
55
56#define WEBUI_URL   @"http://localhost:%d/transmission/web/"
57
58@interface PrefsController (Private)
59
60- (void) setPrefView: (id) sender;
61
62- (void) folderSheetClosed: (NSOpenPanel *) openPanel returnCode: (int) code contextInfo: (void *) info;
63- (void) incompleteFolderSheetClosed: (NSOpenPanel *) openPanel returnCode: (int) code contextInfo: (void *) info;
64- (void) importFolderSheetClosed: (NSOpenPanel *) openPanel returnCode: (int) code contextInfo: (void *) info;
65- (void) doneScriptSheetClosed: (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
73tr_session * fHandle;
74+ (void) setHandle: (tr_session *) handle
75{
76    fHandle = handle;
77}
78
79+ (tr_session *) handle
80{
81    return fHandle;
82}
83
84- (id) init
85{
86    if ((self = [super initWithWindowNibName: @"PrefsWindow"]))
87    {
88        fDefaults = [NSUserDefaults standardUserDefaults];
89       
90        //check for old version download location (before 1.1)
91        NSString * choice;
92        if ((choice = [fDefaults stringForKey: @"DownloadChoice"]))
93        {
94            [fDefaults setBool: [choice isEqualToString: @"Constant"] forKey: @"DownloadLocationConstant"];
95            [fDefaults setBool: YES forKey: @"DownloadAsk"];
96           
97            [fDefaults removeObjectForKey: @"DownloadChoice"];
98        }
99       
100        //check for old version blocklist (before 2.12)
101        NSDate * blocklistDate;
102        if ((blocklistDate = [fDefaults objectForKey: @"BlocklistLastUpdate"]))
103        {
104            [fDefaults setObject: blocklistDate forKey: @"BlocklistNewLastUpdateSuccess"];
105            [fDefaults setObject: blocklistDate forKey: @"BlocklistNewLastUpdate"];
106            [fDefaults removeObjectForKey: @"BlocklistLastUpdate"];
107           
108            NSString * blocklistDir = [NSHomeDirectory() stringByAppendingPathComponent:
109                                        @"/Library/Application Support/Transmission/blocklists/"];
110            [[NSFileManager defaultManager] moveItemAtPath: [blocklistDir stringByAppendingPathComponent: @"level1.bin"]
111                toPath: [blocklistDir stringByAppendingPathComponent: [NSString stringWithUTF8String: DEFAULT_BLOCKLIST_FILENAME]]
112                error: nil];
113        }
114       
115        //save a new random port
116        if ([fDefaults boolForKey: @"RandomPort"])
117            [fDefaults setInteger: tr_sessionGetPeerPort(fHandle) forKey: @"BindPort"];
118       
119        //set auto import
120        NSString * autoPath;
121        if ([fDefaults boolForKey: @"AutoImport"] && (autoPath = [fDefaults stringForKey: @"AutoImportDirectory"]))
122            [[UKKQueue sharedFileWatcher] addPath: [autoPath stringByExpandingTildeInPath]];
123       
124        //set blocklist scheduler
125        [[BlocklistScheduler scheduler] updateSchedule];
126       
127        //set encryption
128        [self setEncryptionMode: nil];
129       
130        //update rpc whitelist
131        [self updateRPCPassword];
132       
133        fRPCWhitelistArray = [[fDefaults arrayForKey: @"RPCWhitelist"] mutableCopy];
134        if (!fRPCWhitelistArray)
135            fRPCWhitelistArray = [[NSMutableArray arrayWithObject: @"127.0.0.1"] retain];
136        [self updateRPCWhitelist];
137       
138        //reset old Sparkle settings from previous versions
139        [fDefaults removeObjectForKey: @"SUScheduledCheckInterval"];
140        if ([fDefaults objectForKey: @"CheckForUpdates"])
141        {
142            [[SUUpdater sharedUpdater] setAutomaticallyChecksForUpdates: [fDefaults boolForKey: @"CheckForUpdates"]];
143            [fDefaults removeObjectForKey: @"CheckForUpdates"];
144        }
145       
146        [self setAutoUpdateToBeta: nil];
147    }
148   
149    return self;
150}
151
152- (void) dealloc
153{
154    [[NSNotificationCenter defaultCenter] removeObserver: self];
155   
156    [fPortStatusTimer invalidate];
157    if (fPortChecker)
158    {
159        [fPortChecker cancelProbe];
160        [fPortChecker release];
161    }
162   
163    [fRPCWhitelistArray release];
164   
165    [fRPCPassword release];
166   
167    [super dealloc];
168}
169
170- (void) awakeFromNib
171{
172    fHasLoaded = YES;
173   
174    NSToolbar * toolbar = [[NSToolbar alloc] initWithIdentifier: @"Preferences Toolbar"];
175    [toolbar setDelegate: self];
176    [toolbar setAllowsUserCustomization: NO];
177    [toolbar setDisplayMode: NSToolbarDisplayModeIconAndLabel];
178    [toolbar setSizeMode: NSToolbarSizeModeRegular];
179    [toolbar setSelectedItemIdentifier: TOOLBAR_GENERAL];
180    [[self window] setToolbar: toolbar];
181    [toolbar release];
182   
183    [self setPrefView: nil];
184   
185    //set download folder
186    [fFolderPopUp selectItemAtIndex: [fDefaults boolForKey: @"DownloadLocationConstant"] ? DOWNLOAD_FOLDER : DOWNLOAD_TORRENT];
187   
188    //set stop ratio
189    [fRatioStopField setFloatValue: [fDefaults floatForKey: @"RatioLimit"]];
190   
191    //set idle seeding minutes
192    [fIdleStopField setIntegerValue: [fDefaults integerForKey: @"IdleLimitMinutes"]];
193   
194    //set limits
195    [self updateLimitFields];
196   
197    //set speed limit
198    [fSpeedLimitUploadField setIntValue: [fDefaults integerForKey: @"SpeedLimitUploadLimit"]];
199    [fSpeedLimitDownloadField setIntValue: [fDefaults integerForKey: @"SpeedLimitDownloadLimit"]];
200   
201    //set port
202    [fPortField setIntValue: [fDefaults integerForKey: @"BindPort"]];
203    fNatStatus = -1;
204   
205    [self updatePortStatus];
206    fPortStatusTimer = [NSTimer scheduledTimerWithTimeInterval: 5.0 target: self
207                        selector: @selector(updatePortStatus) userInfo: nil repeats: YES];
208   
209    //set peer connections
210    [fPeersGlobalField setIntValue: [fDefaults integerForKey: @"PeersTotal"]];
211    [fPeersTorrentField setIntValue: [fDefaults integerForKey: @"PeersTorrent"]];
212   
213    //set queue values
214    [fQueueDownloadField setIntValue: [fDefaults integerForKey: @"QueueDownloadNumber"]];
215    [fQueueSeedField setIntValue: [fDefaults integerForKey: @"QueueSeedNumber"]];
216    [fStalledField setIntValue: [fDefaults integerForKey: @"StalledMinutes"]];
217   
218    //set blocklist
219    NSString * blocklistURL = [fDefaults stringForKey: @"BlocklistURL"];
220    if (blocklistURL)
221        [fBlocklistURLField setStringValue: blocklistURL];
222   
223    [self updateBlocklistButton];
224    [self updateBlocklistFields];
225   
226    [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(updateBlocklistFields)
227        name: @"BlocklistUpdated" object: nil];
228   
229    [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(updateBlocklistURLField)
230        name: NSControlTextDidChangeNotification object: fBlocklistURLField];
231   
232    //set rpc port
233    [fRPCPortField setIntValue: [fDefaults integerForKey: @"RPCPort"]];
234   
235    //set rpc password
236    if (fRPCPassword)
237        [fRPCPasswordField setStringValue: fRPCPassword];
238}
239
240- (NSToolbarItem *) toolbar: (NSToolbar *) toolbar itemForItemIdentifier: (NSString *) ident willBeInsertedIntoToolbar: (BOOL) flag
241{
242    NSToolbarItem * item = [[NSToolbarItem alloc] initWithItemIdentifier: ident];
243
244    if ([ident isEqualToString: TOOLBAR_GENERAL])
245    {
246        [item setLabel: NSLocalizedString(@"General", "Preferences -> toolbar item title")];
247        [item setImage: [NSImage imageNamed: NSImageNamePreferencesGeneral]];
248        [item setTarget: self];
249        [item setAction: @selector(setPrefView:)];
250        [item setAutovalidates: NO];
251    }
252    else if ([ident isEqualToString: TOOLBAR_TRANSFERS])
253    {
254        [item setLabel: NSLocalizedString(@"Transfers", "Preferences -> toolbar item title")];
255        [item setImage: [NSImage imageNamed: @"Transfers.png"]];
256        [item setTarget: self];
257        [item setAction: @selector(setPrefView:)];
258        [item setAutovalidates: NO];
259    }
260    else if ([ident isEqualToString: TOOLBAR_GROUPS])
261    {
262        [item setLabel: NSLocalizedString(@"Groups", "Preferences -> toolbar item title")];
263        [item setImage: [NSImage imageNamed: @"Groups.png"]];
264        [item setTarget: self];
265        [item setAction: @selector(setPrefView:)];
266        [item setAutovalidates: NO];
267    }
268    else if ([ident isEqualToString: TOOLBAR_BANDWIDTH])
269    {
270        [item setLabel: NSLocalizedString(@"Bandwidth", "Preferences -> toolbar item title")];
271        [item setImage: [NSImage imageNamed: @"Bandwidth.png"]];
272        [item setTarget: self];
273        [item setAction: @selector(setPrefView:)];
274        [item setAutovalidates: NO];
275    }
276    else if ([ident isEqualToString: TOOLBAR_PEERS])
277    {
278        [item setLabel: NSLocalizedString(@"Peers", "Preferences -> toolbar item title")];
279        [item setImage: [NSImage imageNamed: NSImageNameUserGroup]];
280        [item setTarget: self];
281        [item setAction: @selector(setPrefView:)];
282        [item setAutovalidates: NO];
283    }
284    else if ([ident isEqualToString: TOOLBAR_NETWORK])
285    {
286        [item setLabel: NSLocalizedString(@"Network", "Preferences -> toolbar item title")];
287        [item setImage: [NSImage imageNamed: NSImageNameNetwork]];
288        [item setTarget: self];
289        [item setAction: @selector(setPrefView:)];
290        [item setAutovalidates: NO];
291    }
292    else if ([ident isEqualToString: TOOLBAR_REMOTE])
293    {
294        [item setLabel: NSLocalizedString(@"Remote", "Preferences -> toolbar item title")];
295        [item setImage: [NSImage imageNamed: @"Remote.png"]];
296        [item setTarget: self];
297        [item setAction: @selector(setPrefView:)];
298        [item setAutovalidates: NO];
299    }
300    else
301    {
302        [item release];
303        return nil;
304    }
305
306    return [item autorelease];
307}
308
309- (NSArray *) toolbarAllowedItemIdentifiers: (NSToolbar *) toolbar
310{
311    return [NSArray arrayWithObjects: TOOLBAR_GENERAL, TOOLBAR_TRANSFERS, TOOLBAR_GROUPS, TOOLBAR_BANDWIDTH,
312                                        TOOLBAR_PEERS, TOOLBAR_NETWORK, TOOLBAR_REMOTE, nil];
313}
314
315- (NSArray *) toolbarSelectableItemIdentifiers: (NSToolbar *) toolbar
316{
317    return [self toolbarAllowedItemIdentifiers: toolbar];
318}
319
320- (NSArray *) toolbarDefaultItemIdentifiers: (NSToolbar *) toolbar
321{
322    return [self toolbarAllowedItemIdentifiers: toolbar];
323}
324
325//for a beta release, always use the beta appcast
326#if defined(TR_BETA_RELEASE)
327#define SPARKLE_TAG YES
328#else
329#define SPARKLE_TAG [fDefaults boolForKey: @"AutoUpdateBeta"]
330#endif
331- (void) setAutoUpdateToBeta: (id) sender
332{
333    [[SUUpdater sharedUpdater] setAllowedTags: SPARKLE_TAG ? [NSSet setWithObject: @"beta"] : nil];
334}
335
336- (void) setPort: (id) sender
337{
338    const tr_port port = [sender intValue];
339    [fDefaults setInteger: port forKey: @"BindPort"];
340    tr_sessionSetPeerPort(fHandle, port);
341   
342    fPeerPort = -1;
343    [self updatePortStatus];
344}
345
346- (void) randomPort: (id) sender
347{
348    const tr_port port = tr_sessionSetPeerPortRandom(fHandle);
349    [fDefaults setInteger: port forKey: @"BindPort"];
350    [fPortField setIntValue: port];
351   
352    fPeerPort = -1;
353    [self updatePortStatus];
354}
355
356- (void) setRandomPortOnStart: (id) sender
357{
358    tr_sessionSetPeerPortRandomOnStart(fHandle, [sender state] == NSOnState);
359}
360
361- (void) setNat: (id) sender
362{
363    tr_sessionSetPortForwardingEnabled(fHandle, [fDefaults boolForKey: @"NatTraversal"]);
364   
365    fNatStatus = -1;
366    [self updatePortStatus];
367}
368
369- (void) updatePortStatus
370{
371    const tr_port_forwarding fwd = tr_sessionGetPortForwarding(fHandle);
372    const int port = tr_sessionGetPeerPort(fHandle);
373    BOOL natStatusChanged = (fNatStatus != fwd);
374    BOOL peerPortChanged = (fPeerPort != port);
375
376    if (natStatusChanged || peerPortChanged)
377    {
378        fNatStatus = fwd;
379        fPeerPort = port;
380       
381        [fPortStatusField setStringValue: @""];
382        [fPortStatusImage setImage: nil];
383        [fPortStatusProgress startAnimation: self];
384       
385        if (fPortChecker)
386        {
387            [fPortChecker cancelProbe];
388            [fPortChecker release];
389        }
390        BOOL delay = natStatusChanged || tr_sessionIsPortForwardingEnabled(fHandle);
391        fPortChecker = [[PortChecker alloc] initForPort: fPeerPort delay: delay withDelegate: self];
392    }
393}
394
395- (void) portCheckerDidFinishProbing: (PortChecker *) portChecker
396{
397    [fPortStatusProgress stopAnimation: self];
398    switch ([fPortChecker status])
399    {
400        case PORT_STATUS_OPEN:
401            [fPortStatusField setStringValue: NSLocalizedString(@"Port is open", "Preferences -> Network -> port status")];
402            [fPortStatusImage setImage: [NSImage imageNamed: @"GreenDot.png"]];
403            break;
404        case PORT_STATUS_CLOSED:
405            [fPortStatusField setStringValue: NSLocalizedString(@"Port is closed", "Preferences -> Network -> port status")];
406            [fPortStatusImage setImage: [NSImage imageNamed: @"RedDot.png"]];
407            break;
408        case PORT_STATUS_ERROR:
409            [fPortStatusField setStringValue: NSLocalizedString(@"Port check site is down", "Preferences -> Network -> port status")];
410            [fPortStatusImage setImage: [NSImage imageNamed: @"YellowDot.png"]];
411            break;
412        default:
413            NSAssert1(NO, @"Port checker returned invalid status: %d", [fPortChecker status]);
414            return;
415    }
416    [fPortChecker release];
417    fPortChecker = nil;
418}
419
420- (NSArray *) sounds
421{
422    NSMutableArray * sounds = [NSMutableArray array];
423   
424    NSArray * directories = NSSearchPathForDirectoriesInDomains(NSAllLibrariesDirectory,
425                                NSUserDomainMask | NSLocalDomainMask | NSSystemDomainMask, YES);
426   
427    for (NSString * directory in directories)
428    {
429        directory = [directory stringByAppendingPathComponent: @"Sounds"];
430       
431        BOOL isDirectory;
432        if ([[NSFileManager defaultManager] fileExistsAtPath: directory isDirectory: &isDirectory] && isDirectory)
433        {
434            NSArray * directoryContents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath: directory error: NULL];
435            for (NSString * sound in directoryContents)
436            {
437                sound = [sound stringByDeletingPathExtension];
438                if ([NSSound soundNamed: sound])
439                    [sounds addObject: sound];
440            }
441        }
442    }
443   
444    return sounds;
445}
446
447- (void) setSound: (id) sender
448{
449    //play sound when selecting
450    NSSound * sound;
451    if ((sound = [NSSound soundNamed: [sender titleOfSelectedItem]]))
452        [sound play];
453}
454
455- (void) setPeersGlobal: (id) sender
456{
457    const int count = [sender intValue];
458    [fDefaults setInteger: count forKey: @"PeersTotal"];
459    tr_sessionSetPeerLimit(fHandle, count);
460}
461
462- (void) setPeersTorrent: (id) sender
463{
464    const int count = [sender intValue];
465    [fDefaults setInteger: count forKey: @"PeersTorrent"];
466    tr_sessionSetPeerLimitPerTorrent(fHandle, count);
467}
468
469- (void) setPEX: (id) sender
470{
471    tr_sessionSetPexEnabled(fHandle, [fDefaults boolForKey: @"PEXGlobal"]);
472}
473
474- (void) setDHT: (id) sender
475{
476    tr_sessionSetDHTEnabled(fHandle, [fDefaults boolForKey: @"DHTGlobal"]);
477}
478
479- (void) setLPD: (id) sender
480{
481    tr_sessionSetLPDEnabled(fHandle, [fDefaults boolForKey: @"LocalPeerDiscoveryGlobal"]);
482}
483
484- (void) setEncryptionMode: (id) sender
485{
486    const tr_encryption_mode mode = [fDefaults boolForKey: @"EncryptionPrefer"] ?
487        ([fDefaults boolForKey: @"EncryptionRequire"] ? TR_ENCRYPTION_REQUIRED : TR_ENCRYPTION_PREFERRED) : TR_CLEAR_PREFERRED;
488    tr_sessionSetEncryption(fHandle, mode);
489}
490
491- (void) setBlocklistEnabled: (id) sender
492{
493    tr_blocklistSetEnabled(fHandle, [fDefaults boolForKey: @"BlocklistNew"]);
494   
495    [[BlocklistScheduler scheduler] updateSchedule];
496   
497    [self updateBlocklistButton];
498}
499
500- (void) updateBlocklist: (id) sender
501{
502    [BlocklistDownloaderViewController downloadWithPrefsController: self];
503}
504
505- (void) setBlocklistAutoUpdate: (id) sender
506{
507    [[BlocklistScheduler scheduler] updateSchedule];
508}
509
510- (void) updateBlocklistFields
511{
512    const BOOL exists = tr_blocklistExists(fHandle);
513   
514    if (exists)
515    {
516        NSString * countString = [NSString formattedUInteger: tr_blocklistGetRuleCount(fHandle)];
517        [fBlocklistMessageField setStringValue: [NSString stringWithFormat: NSLocalizedString(@"%@ IP address rules in list",
518            "Prefs -> blocklist -> message"), countString]];
519    }
520    else
521        [fBlocklistMessageField setStringValue: NSLocalizedString(@"A blocklist must first be downloaded",
522            "Prefs -> blocklist -> message")];
523   
524    NSString * updatedDateString;
525    if (exists)
526    {
527        NSDate * updatedDate = [fDefaults objectForKey: @"BlocklistNewLastUpdateSuccess"];
528       
529        if (updatedDate)
530        {
531            if ([NSApp isOnSnowLeopardOrBetter])
532                updatedDateString = [NSDateFormatter localizedStringFromDate: updatedDate dateStyle: NSDateFormatterFullStyle
533                                        timeStyle: NSDateFormatterShortStyle];
534            else
535            {
536                NSDateFormatter * dateFormatter = [[NSDateFormatter alloc] init];
537                [dateFormatter setDateStyle: NSDateFormatterFullStyle];
538                [dateFormatter setTimeStyle: NSDateFormatterShortStyle];
539               
540                updatedDateString = [dateFormatter stringFromDate: updatedDate];
541                [dateFormatter release];
542            }
543        }
544        else
545            updatedDateString = NSLocalizedString(@"N/A", "Prefs -> blocklist -> message");
546    }
547    else
548        updatedDateString = NSLocalizedString(@"Never", "Prefs -> blocklist -> message");
549   
550    [fBlocklistDateField setStringValue: [NSString stringWithFormat: @"%@: %@",
551        NSLocalizedString(@"Last updated", "Prefs -> blocklist -> message"), updatedDateString]];
552}
553
554- (void) updateBlocklistURLField
555{
556    NSString * blocklistString = [fBlocklistURLField stringValue];
557   
558    [fDefaults setObject: blocklistString forKey: @"BlocklistURL"];
559    tr_blocklistSetURL(fHandle, [blocklistString UTF8String]);
560   
561    [self updateBlocklistButton];
562}
563
564- (void) updateBlocklistButton
565{
566    NSString * blocklistString = [fDefaults objectForKey: @"BlocklistURL"];
567    const BOOL enable = (blocklistString && ![blocklistString isEqualToString: @""])
568                            && [fDefaults boolForKey: @"BlocklistNew"];
569    [fBlocklistButton setEnabled: enable];
570}
571
572- (void) setAutoStartDownloads: (id) sender
573{
574    tr_sessionSetPaused(fHandle, ![fDefaults boolForKey: @"AutoStartDownload"]);
575}
576
577- (void) applySpeedSettings: (id) sender
578{
579    tr_sessionLimitSpeed(fHandle, TR_UP, [fDefaults boolForKey: @"CheckUpload"]);
580    tr_sessionSetSpeedLimit_KBps(fHandle, TR_UP, [fDefaults integerForKey: @"UploadLimit"]);
581   
582    tr_sessionLimitSpeed(fHandle, TR_DOWN, [fDefaults boolForKey: @"CheckDownload"]);
583    tr_sessionSetSpeedLimit_KBps(fHandle, TR_DOWN, [fDefaults integerForKey: @"DownloadLimit"]);
584   
585    [[NSNotificationCenter defaultCenter] postNotificationName: @"SpeedLimitUpdate" object: nil];
586}
587
588- (void) applyAltSpeedSettings
589{
590    tr_sessionSetAltSpeed_KBps(fHandle, TR_UP, [fDefaults integerForKey: @"SpeedLimitUploadLimit"]);
591    tr_sessionSetAltSpeed_KBps(fHandle, TR_DOWN, [fDefaults integerForKey: @"SpeedLimitDownloadLimit"]);
592       
593    [[NSNotificationCenter defaultCenter] postNotificationName: @"SpeedLimitUpdate" object: nil];
594}
595
596- (void) applyRatioSetting: (id) sender
597{
598    tr_sessionSetRatioLimited(fHandle, [fDefaults boolForKey: @"RatioCheck"]);
599    tr_sessionSetRatioLimit(fHandle, [fDefaults floatForKey: @"RatioLimit"]);
600}
601
602- (void) setRatioStop: (id) sender
603{
604    [fDefaults setFloat: [sender floatValue] forKey: @"RatioLimit"];
605   
606    [self applyRatioSetting: nil];
607}
608
609- (void) updateRatioStopField
610{
611    if (fHasLoaded)
612        [fRatioStopField setFloatValue: [fDefaults floatForKey: @"RatioLimit"]];
613   
614    [self applyRatioSetting: nil];
615}
616
617- (void) applyIdleStopSetting: (id) sender
618{
619    tr_sessionSetIdleLimited(fHandle, [fDefaults boolForKey: @"IdleLimitCheck"]);
620    tr_sessionSetIdleLimit(fHandle, [fDefaults integerForKey: @"IdleLimitMinutes"]);
621}
622
623- (void) setIdleStop: (id) sender
624{
625    [fDefaults setInteger: [sender integerValue] forKey: @"IdleLimitMinutes"];
626   
627    [self applyIdleStopSetting: nil];
628}
629
630- (void) updateLimitFields
631{
632    if (!fHasLoaded)
633        return;
634   
635    [fUploadField setIntValue: [fDefaults integerForKey: @"UploadLimit"]];
636    [fDownloadField setIntValue: [fDefaults integerForKey: @"DownloadLimit"]];
637}
638
639- (void) setGlobalLimit: (id) sender
640{
641    [fDefaults setInteger: [sender intValue] forKey: sender == fUploadField ? @"UploadLimit" : @"DownloadLimit"];
642    [self applySpeedSettings: self];
643}
644
645- (void) setSpeedLimit: (id) sender
646{
647    [fDefaults setInteger: [sender intValue] forKey: sender == fSpeedLimitUploadField
648                                                        ? @"SpeedLimitUploadLimit" : @"SpeedLimitDownloadLimit"];
649    [self applyAltSpeedSettings];
650}
651
652- (void) setAutoSpeedLimit: (id) sender
653{
654    tr_sessionUseAltSpeedTime(fHandle, [fDefaults boolForKey: @"SpeedLimitAuto"]);
655}
656
657- (void) setAutoSpeedLimitTime: (id) sender
658{
659    tr_sessionSetAltSpeedBegin(fHandle, [PrefsController dateToTimeSum: [fDefaults objectForKey: @"SpeedLimitAutoOnDate"]]);
660    tr_sessionSetAltSpeedEnd(fHandle, [PrefsController dateToTimeSum: [fDefaults objectForKey: @"SpeedLimitAutoOffDate"]]);
661}
662
663- (void) setAutoSpeedLimitDay: (id) sender
664{
665    tr_sessionSetAltSpeedDay(fHandle, [[sender selectedItem] tag]);
666}
667
668+ (NSInteger) dateToTimeSum: (NSDate *) date
669{
670    NSCalendar * calendar = [NSCalendar currentCalendar];
671    NSDateComponents * components = [calendar components: NSHourCalendarUnit | NSMinuteCalendarUnit fromDate: date];
672    return [components hour] * 60 + [components minute];
673}
674
675+ (NSDate *) timeSumToDate: (NSInteger) sum
676{
677    NSDateComponents * comps = [[[NSDateComponents alloc] init] autorelease];
678    [comps setHour: sum / 60];
679    [comps setMinute: sum % 60];
680   
681    return [[NSCalendar currentCalendar] dateFromComponents: comps];
682}
683
684- (BOOL) control: (NSControl *) control textShouldBeginEditing: (NSText *) fieldEditor
685{
686    [fInitialString release];
687    fInitialString = [[control stringValue] retain];
688   
689    return YES;
690}
691
692- (BOOL) control: (NSControl *) control didFailToFormatString: (NSString *) string errorDescription: (NSString *) error
693{
694    NSBeep();
695    if (fInitialString)
696    {
697        [control setStringValue: fInitialString];
698        [fInitialString release];
699        fInitialString = nil;
700    }
701    return NO;
702}
703
704- (void) setBadge: (id) sender
705{
706    [[NSNotificationCenter defaultCenter] postNotificationName: @"DockBadgeChange" object: self];
707}
708
709- (void) resetWarnings: (id) sender
710{
711    [fDefaults removeObjectForKey: @"WarningDuplicate"];
712    [fDefaults removeObjectForKey: @"WarningRemainingSpace"];
713    [fDefaults removeObjectForKey: @"WarningFolderDataSameName"];
714    [fDefaults removeObjectForKey: @"WarningResetStats"];
715    [fDefaults removeObjectForKey: @"WarningCreatorBlankAddress"];
716    [fDefaults removeObjectForKey: @"WarningCreatorPrivateBlankAddress"];
717    [fDefaults removeObjectForKey: @"WarningRemoveTrackers"];
718    [fDefaults removeObjectForKey: @"WarningInvalidOpen"];
719    [fDefaults removeObjectForKey: @"WarningDonate"];
720    //[fDefaults removeObjectForKey: @"WarningLegal"];
721}
722
723- (void) setDefaultForMagnets: (id) sender
724{
725    NSString * bundleID = [[NSBundle mainBundle] bundleIdentifier];
726    const OSStatus result = LSSetDefaultHandlerForURLScheme((CFStringRef)@"magnet", (CFStringRef)bundleID);
727    if (result != noErr)
728        NSLog(@"Failed setting default magnet link handler");
729}
730
731- (void) setQueue: (id) sender
732{
733    [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateQueue" object: self];
734}
735
736- (void) setQueueNumber: (id) sender
737{
738    [fDefaults setInteger: [sender intValue] forKey: sender == fQueueDownloadField ? @"QueueDownloadNumber" : @"QueueSeedNumber"];
739    [self setQueue: nil];
740}
741
742- (void) setStalled: (id) sender
743{
744    [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateQueue" object: self];
745}
746
747- (void) setStalledMinutes: (id) sender
748{
749    [fDefaults setInteger: [sender intValue] forKey: @"StalledMinutes"];
750    [self setStalled: nil];
751}
752
753- (void) setDownloadLocation: (id) sender
754{
755    [fDefaults setBool: [fFolderPopUp indexOfSelectedItem] == DOWNLOAD_FOLDER forKey: @"DownloadLocationConstant"];
756}
757
758- (void) folderSheetShow: (id) sender
759{
760    NSOpenPanel * panel = [NSOpenPanel openPanel];
761
762    [panel setPrompt: NSLocalizedString(@"Select", "Preferences -> Open panel prompt")];
763    [panel setAllowsMultipleSelection: NO];
764    [panel setCanChooseFiles: NO];
765    [panel setCanChooseDirectories: YES];
766    [panel setCanCreateDirectories: YES];
767
768    [panel beginSheetForDirectory: nil file: nil types: nil
769        modalForWindow: [self window] modalDelegate: self didEndSelector:
770        @selector(folderSheetClosed:returnCode:contextInfo:) contextInfo: nil];
771}
772
773- (void) incompleteFolderSheetShow: (id) sender
774{
775    NSOpenPanel * panel = [NSOpenPanel openPanel];
776
777    [panel setPrompt: NSLocalizedString(@"Select", "Preferences -> Open panel prompt")];
778    [panel setAllowsMultipleSelection: NO];
779    [panel setCanChooseFiles: NO];
780    [panel setCanChooseDirectories: YES];
781    [panel setCanCreateDirectories: YES];
782
783    [panel beginSheetForDirectory: nil file: nil types: nil
784        modalForWindow: [self window] modalDelegate: self didEndSelector:
785        @selector(incompleteFolderSheetClosed:returnCode:contextInfo:) contextInfo: nil];
786}
787
788- (void) doneScriptSheetShow:(id)sender
789{
790    NSOpenPanel * panel = [NSOpenPanel openPanel];
791   
792    [panel setPrompt: NSLocalizedString(@"Select", "Preferences -> Open panel prompt")];
793    [panel setAllowsMultipleSelection: NO];
794    [panel setCanChooseFiles: YES];
795    [panel setCanChooseDirectories: NO];
796    [panel setCanCreateDirectories: NO];
797   
798    [panel beginSheetForDirectory: nil file: nil types: nil
799                   modalForWindow: [self window] modalDelegate: self didEndSelector:
800     @selector(doneScriptSheetClosed:returnCode:contextInfo:) contextInfo: nil];
801}
802
803- (void) setUseIncompleteFolder: (id) sender
804{
805    tr_sessionSetIncompleteDirEnabled(fHandle, [fDefaults boolForKey: @"UseIncompleteDownloadFolder"]);
806}
807
808- (void) setRenamePartialFiles: (id) sender
809{
810    tr_sessionSetIncompleteFileNamingEnabled(fHandle, [fDefaults boolForKey: @"RenamePartialFiles"]);
811}
812
813- (void) setDoneScriptEnabled: (id) sender
814{
815    if ([fDefaults boolForKey: @"DoneScriptEnabled"] && ![[NSFileManager defaultManager] fileExistsAtPath: [fDefaults stringForKey:@"DoneScriptPath"]])
816    {
817        // enabled is set but script file doesn't exist, so prompt for one and disable until they pick one
818        [fDefaults setBool: NO forKey: @"DoneScriptEnabled"];
819        [self doneScriptSheetShow: sender];
820    }
821    tr_sessionSetTorrentDoneScriptEnabled(fHandle, [fDefaults boolForKey: @"DoneScriptEnabled"]);
822}
823
824- (void) setAutoImport: (id) sender
825{
826    NSString * path;
827    if ((path = [fDefaults stringForKey: @"AutoImportDirectory"]))
828    {
829        path = [path stringByExpandingTildeInPath];
830        if ([fDefaults boolForKey: @"AutoImport"])
831            [[UKKQueue sharedFileWatcher] addPath: path];
832        else
833            [[UKKQueue sharedFileWatcher] removePathFromQueue: path];
834       
835        [[NSNotificationCenter defaultCenter] postNotificationName: @"AutoImportSettingChange" object: self];
836    }
837    else
838        [self importFolderSheetShow: nil];
839}
840
841- (void) importFolderSheetShow: (id) sender
842{
843    NSOpenPanel * panel = [NSOpenPanel openPanel];
844
845    [panel setPrompt: NSLocalizedString(@"Select", "Preferences -> Open panel prompt")];
846    [panel setAllowsMultipleSelection: NO];
847    [panel setCanChooseFiles: NO];
848    [panel setCanChooseDirectories: YES];
849    [panel setCanCreateDirectories: YES];
850
851    [panel beginSheetForDirectory: nil file: nil types: nil
852        modalForWindow: [self window] modalDelegate: self didEndSelector:
853        @selector(importFolderSheetClosed:returnCode:contextInfo:) contextInfo: nil];
854}
855
856- (void) setAutoSize: (id) sender
857{
858    [[NSNotificationCenter defaultCenter] postNotificationName: @"AutoSizeSettingChange" object: self];
859}
860
861- (void) setRPCEnabled: (id) sender
862{
863    BOOL enable = [fDefaults boolForKey: @"RPC"];
864    tr_sessionSetRPCEnabled(fHandle, enable);
865   
866    [self setRPCWebUIDiscovery: nil];
867}
868
869- (void) linkWebUI: (id) sender
870{
871    NSString * urlString = [NSString stringWithFormat: WEBUI_URL, [fDefaults integerForKey: @"RPCPort"]];
872    [[NSWorkspace sharedWorkspace] openURL: [NSURL URLWithString: urlString]];
873}
874
875- (void) setRPCAuthorize: (id) sender
876{
877    tr_sessionSetRPCPasswordEnabled(fHandle, [fDefaults boolForKey: @"RPCAuthorize"]);
878}
879
880- (void) setRPCUsername: (id) sender
881{
882    tr_sessionSetRPCUsername(fHandle, [[fDefaults stringForKey: @"RPCUsername"] UTF8String]);
883}
884
885- (void) setRPCPassword: (id) sender
886{
887    [fRPCPassword release];
888    fRPCPassword = [[sender stringValue] copy];
889   
890    const char * password = [[sender stringValue] UTF8String];
891    [self setKeychainPassword: password forService: RPC_KEYCHAIN_SERVICE username: RPC_KEYCHAIN_NAME];
892   
893    tr_sessionSetRPCPassword(fHandle, password);
894}
895
896- (void) updateRPCPassword
897{
898    UInt32 passwordLength;
899    const char * password = nil;
900    SecKeychainFindGenericPassword(NULL, strlen(RPC_KEYCHAIN_SERVICE), RPC_KEYCHAIN_SERVICE,
901        strlen(RPC_KEYCHAIN_NAME), RPC_KEYCHAIN_NAME, &passwordLength, (void **)&password, NULL);
902   
903    [fRPCPassword release];
904    if (password != NULL)
905    {
906        char fullPassword[passwordLength+1];
907        strncpy(fullPassword, password, passwordLength);
908        fullPassword[passwordLength] = '\0';
909        SecKeychainItemFreeContent(NULL, (void *)password);
910       
911        tr_sessionSetRPCPassword(fHandle, fullPassword);
912       
913        fRPCPassword = [[NSString alloc] initWithUTF8String: fullPassword];
914        [fRPCPasswordField setStringValue: fRPCPassword];
915    }
916    else
917        fRPCPassword = nil;
918}
919
920- (void) setRPCPort: (id) sender
921{
922    int port = [sender intValue];
923    [fDefaults setInteger: port forKey: @"RPCPort"];
924    tr_sessionSetRPCPort(fHandle, port);
925   
926    [self setRPCWebUIDiscovery: nil];
927}
928
929- (void) setRPCUseWhitelist: (id) sender
930{
931    tr_sessionSetRPCWhitelistEnabled(fHandle, [fDefaults boolForKey: @"RPCUseWhitelist"]);
932}
933
934- (void) setRPCWebUIDiscovery: (id) sender
935{
936    if ([fDefaults boolForKey:@"RPC"] && [fDefaults boolForKey: @"RPCWebDiscovery"])
937        [[BonjourController defaultController] startWithPort: [fDefaults integerForKey: @"RPCPort"]];
938    else
939        [[BonjourController defaultController] stop];
940}
941
942- (void) updateRPCWhitelist
943{
944    NSString * string = [fRPCWhitelistArray componentsJoinedByString: @","];
945    tr_sessionSetRPCWhitelist(fHandle, [string UTF8String]);
946}
947
948- (void) addRemoveRPCIP: (id) sender
949{
950    //don't allow add/remove when currently adding - it leads to weird results
951    if ([fRPCWhitelistTable editedRow] != -1)
952        return;
953   
954    if ([[sender cell] tagForSegment: [sender selectedSegment]] == RPC_IP_REMOVE_TAG)
955    {
956        [fRPCWhitelistArray removeObjectsAtIndexes: [fRPCWhitelistTable selectedRowIndexes]];
957        [fRPCWhitelistTable deselectAll: self];
958        [fRPCWhitelistTable reloadData];
959       
960        [fDefaults setObject: fRPCWhitelistArray forKey: @"RPCWhitelist"];
961        [self updateRPCWhitelist];
962    }
963    else
964    {
965        [fRPCWhitelistArray addObject: @""];
966        [fRPCWhitelistTable reloadData];
967       
968        const int row = [fRPCWhitelistArray count] - 1;
969        [fRPCWhitelistTable selectRowIndexes: [NSIndexSet indexSetWithIndex: row] byExtendingSelection: NO];
970        [fRPCWhitelistTable editColumn: 0 row: row withEvent: nil select: YES];
971    }
972}
973
974- (NSInteger) numberOfRowsInTableView: (NSTableView *) tableView
975{
976    return [fRPCWhitelistArray count];
977}
978
979- (id) tableView: (NSTableView *) tableView objectValueForTableColumn: (NSTableColumn *) tableColumn row: (NSInteger) row
980{
981    return [fRPCWhitelistArray objectAtIndex: row];
982}
983
984- (void) tableView: (NSTableView *) tableView setObjectValue: (id) object forTableColumn: (NSTableColumn *) tableColumn
985    row: (NSInteger) row
986{
987    NSArray * components = [object componentsSeparatedByString: @"."];
988    NSMutableArray * newComponents = [NSMutableArray arrayWithCapacity: 4];
989       
990    //create better-formatted ip string
991    BOOL valid = false;
992    if ([components count] == 4)
993    {
994        valid = true;
995        for (NSString * component in components)
996        {
997            if ([component isEqualToString: @"*"])
998                [newComponents addObject: component];
999            else
1000            {
1001                int num = [component intValue];
1002                if (num >= 0 && num < 256)
1003                    [newComponents addObject: [[NSNumber numberWithInt: num] stringValue]];
1004                else
1005                {
1006                    valid = false;
1007                    break;
1008                }
1009            }
1010        }
1011    }
1012   
1013    NSString * newIP;
1014    if (valid)
1015    {
1016        newIP = [newComponents componentsJoinedByString: @"."];
1017       
1018        //don't allow the same ip address
1019        if ([fRPCWhitelistArray containsObject: newIP] && ![[fRPCWhitelistArray objectAtIndex: row] isEqualToString: newIP])
1020            valid = false;
1021    }
1022   
1023    if (valid)
1024    {
1025        [fRPCWhitelistArray replaceObjectAtIndex: row withObject: newIP];
1026        [fRPCWhitelistArray sortUsingSelector: @selector(compareNumeric:)];
1027    }
1028    else
1029    {
1030        NSBeep();
1031        if ([[fRPCWhitelistArray objectAtIndex: row] isEqualToString: @""])
1032            [fRPCWhitelistArray removeObjectAtIndex: row];
1033    }
1034       
1035    [fRPCWhitelistTable deselectAll: self];
1036    [fRPCWhitelistTable reloadData];
1037   
1038    [fDefaults setObject: fRPCWhitelistArray forKey: @"RPCWhitelist"];
1039    [self updateRPCWhitelist];
1040}
1041
1042- (void) tableViewSelectionDidChange: (NSNotification *) notification
1043{
1044    [fRPCAddRemoveControl setEnabled: [fRPCWhitelistTable numberOfSelectedRows] > 0 forSegment: RPC_IP_REMOVE_TAG];
1045}
1046
1047- (void) helpForScript: (id) sender
1048{
1049    [[NSHelpManager sharedHelpManager] openHelpAnchor: @"script"
1050        inBook: [[NSBundle mainBundle] objectForInfoDictionaryKey: @"CFBundleHelpBookName"]];
1051}
1052
1053- (void) helpForPeers: (id) sender
1054{
1055    [[NSHelpManager sharedHelpManager] openHelpAnchor: @"peers"
1056        inBook: [[NSBundle mainBundle] objectForInfoDictionaryKey: @"CFBundleHelpBookName"]];
1057}
1058
1059- (void) helpForNetwork: (id) sender
1060{
1061    [[NSHelpManager sharedHelpManager] openHelpAnchor: @"network"
1062        inBook: [[NSBundle mainBundle] objectForInfoDictionaryKey: @"CFBundleHelpBookName"]];
1063}
1064
1065- (void) helpForRemote: (id) sender
1066{
1067    [[NSHelpManager sharedHelpManager] openHelpAnchor: @"remote"
1068        inBook: [[NSBundle mainBundle] objectForInfoDictionaryKey: @"CFBundleHelpBookName"]];
1069}
1070
1071- (void) rpcUpdatePrefs
1072{
1073    //encryption
1074    const tr_encryption_mode encryptionMode = tr_sessionGetEncryption(fHandle);
1075    [fDefaults setBool: encryptionMode != TR_CLEAR_PREFERRED forKey: @"EncryptionPrefer"];
1076    [fDefaults setBool: encryptionMode == TR_ENCRYPTION_REQUIRED forKey: @"EncryptionRequire"];
1077   
1078    //download directory
1079    NSString * downloadLocation = [[NSString stringWithUTF8String: tr_sessionGetDownloadDir(fHandle)] stringByStandardizingPath];
1080    [fDefaults setObject: downloadLocation forKey: @"DownloadFolder"];
1081   
1082    NSString * incompleteLocation = [[NSString stringWithUTF8String: tr_sessionGetIncompleteDir(fHandle)] stringByStandardizingPath];
1083    [fDefaults setObject: incompleteLocation forKey: @"IncompleteDownloadFolder"];
1084   
1085    const BOOL useIncomplete = tr_sessionIsIncompleteDirEnabled(fHandle);
1086    [fDefaults setBool: useIncomplete forKey: @"UseIncompleteDownloadFolder"];
1087   
1088    const BOOL usePartialFileRanaming = tr_sessionIsIncompleteFileNamingEnabled(fHandle);
1089    [fDefaults setBool: usePartialFileRanaming forKey: @"RenamePartialFiles"];
1090   
1091    //peers
1092    const uint16_t peersTotal = tr_sessionGetPeerLimit(fHandle);
1093    [fDefaults setInteger: peersTotal forKey: @"PeersTotal"];
1094   
1095    const uint16_t peersTorrent = tr_sessionGetPeerLimitPerTorrent(fHandle);
1096    [fDefaults setInteger: peersTorrent forKey: @"PeersTorrent"];
1097   
1098    //pex
1099    const BOOL pex = tr_sessionIsPexEnabled(fHandle);
1100    [fDefaults setBool: pex forKey: @"PEXGlobal"];
1101   
1102    //dht
1103    const BOOL dht = tr_sessionIsDHTEnabled(fHandle);
1104    [fDefaults setBool: dht forKey: @"DHTGlobal"];
1105   
1106    //dht
1107    const BOOL lpd = tr_sessionIsLPDEnabled(fHandle);
1108    [fDefaults setBool: lpd forKey: @"LocalPeerDiscoveryGlobal"];
1109   
1110    //auto start
1111    const BOOL autoStart = !tr_sessionGetPaused(fHandle);
1112    [fDefaults setBool: autoStart forKey: @"AutoStartDownload"];
1113   
1114    //port
1115    const tr_port port = tr_sessionGetPeerPort(fHandle);
1116    [fDefaults setInteger: port forKey: @"BindPort"];
1117   
1118    const BOOL nat = tr_sessionIsPortForwardingEnabled(fHandle);
1119    [fDefaults setBool: nat forKey: @"NatTraversal"];
1120   
1121    fPeerPort = -1;
1122    fNatStatus = -1;
1123    [self updatePortStatus];
1124   
1125    const BOOL randomPort = tr_sessionGetPeerPortRandomOnStart(fHandle);
1126    [fDefaults setBool: randomPort forKey: @"RandomPort"];
1127   
1128    //speed limit - down
1129    const BOOL downLimitEnabled = tr_sessionIsSpeedLimited(fHandle, TR_DOWN);
1130    [fDefaults setBool: downLimitEnabled forKey: @"CheckDownload"];
1131   
1132    const int downLimit = tr_sessionGetSpeedLimit_KBps(fHandle, TR_DOWN);
1133    [fDefaults setInteger: downLimit forKey: @"DownloadLimit"];
1134   
1135    //speed limit - up
1136    const BOOL upLimitEnabled = tr_sessionIsSpeedLimited(fHandle, TR_UP);
1137    [fDefaults setBool: upLimitEnabled forKey: @"CheckUpload"];
1138   
1139    const int upLimit = tr_sessionGetSpeedLimit_KBps(fHandle, TR_UP);
1140    [fDefaults setInteger: upLimit forKey: @"UploadLimit"];
1141   
1142    //alt speed limit enabled
1143    const BOOL useAltSpeed = tr_sessionUsesAltSpeed(fHandle);
1144    [fDefaults setBool: useAltSpeed forKey: @"SpeedLimit"];
1145   
1146    //alt speed limit - down
1147    const int downLimitAlt = tr_sessionGetAltSpeed_KBps(fHandle, TR_DOWN);
1148    [fDefaults setInteger: downLimitAlt forKey: @"SpeedLimitDownloadLimit"];
1149   
1150    //alt speed limit - up
1151    const int upLimitAlt = tr_sessionGetAltSpeed_KBps(fHandle, TR_UP);
1152    [fDefaults setInteger: upLimitAlt forKey: @"SpeedLimitUploadLimit"];
1153   
1154    //alt speed limit schedule
1155    const BOOL useAltSpeedSched = tr_sessionUsesAltSpeedTime(fHandle);
1156    [fDefaults setBool: useAltSpeedSched forKey: @"SpeedLimitAuto"];
1157   
1158    NSDate * limitStartDate = [PrefsController timeSumToDate: tr_sessionGetAltSpeedBegin(fHandle)];
1159    [fDefaults setObject: limitStartDate forKey: @"SpeedLimitAutoOnDate"];
1160   
1161    NSDate * limitEndDate = [PrefsController timeSumToDate: tr_sessionGetAltSpeedEnd(fHandle)];
1162    [fDefaults setObject: limitEndDate forKey: @"SpeedLimitAutoOffDate"];
1163   
1164    const int limitDay = tr_sessionGetAltSpeedDay(fHandle);
1165    [fDefaults setInteger: limitDay forKey: @"SpeedLimitAutoDay"];
1166   
1167    //blocklist
1168    const BOOL blocklist = tr_blocklistIsEnabled(fHandle);
1169    [fDefaults setBool: blocklist forKey: @"BlocklistNew"];
1170   
1171    NSString * blocklistURL = [NSString stringWithUTF8String: tr_blocklistGetURL(fHandle)];
1172    [fDefaults setObject: blocklistURL forKey: @"BlocklistURL"];
1173   
1174    //seed ratio
1175    const BOOL ratioLimited = tr_sessionIsRatioLimited(fHandle);
1176    [fDefaults setBool: ratioLimited forKey: @"RatioCheck"];
1177   
1178    const float ratioLimit = tr_sessionGetRatioLimit(fHandle);
1179    [fDefaults setFloat: ratioLimit forKey: @"RatioLimit"];
1180   
1181    //Idle seed limit
1182    const BOOL idleLimited = tr_sessionIsIdleLimited(fHandle);
1183    [fDefaults setBool: idleLimited forKey: @"IdleLimitCheck"];
1184   
1185    const NSUInteger idleLimitMin = tr_sessionGetIdleLimit(fHandle);
1186    [fDefaults setInteger: idleLimitMin forKey: @"IdleLimitMinutes"];
1187   
1188    //done script
1189    const BOOL doneScriptEnabled = tr_sessionIsTorrentDoneScriptEnabled(fHandle);
1190    [fDefaults setBool: doneScriptEnabled forKey: @"DoneScriptEnabled"];
1191   
1192    NSString * doneScriptPath = [NSString stringWithUTF8String: tr_sessionGetTorrentDoneScript(fHandle)];
1193    [fDefaults setObject: doneScriptPath forKey: @"DoneScriptPath"];
1194   
1195    //update gui if loaded
1196    if (fHasLoaded)
1197    {
1198        //encryption handled by bindings
1199       
1200        //download directory handled by bindings
1201       
1202        [fPeersGlobalField setIntValue: peersTotal];
1203        [fPeersTorrentField setIntValue: peersTorrent];
1204       
1205        //pex handled by bindings
1206       
1207        //dht handled by bindings
1208       
1209        //lpd handled by bindings
1210       
1211        [fPortField setIntValue: port];
1212        //port forwarding (nat) handled by bindings
1213        //random port handled by bindings
1214       
1215        //limit check handled by bindings
1216        [fDownloadField setIntValue: downLimit];
1217       
1218        //limit check handled by bindings
1219        [fUploadField setIntValue: upLimit];
1220       
1221        [fSpeedLimitDownloadField setIntValue: downLimitAlt];
1222       
1223        [fSpeedLimitUploadField setIntValue: upLimitAlt];
1224       
1225        //speed limit schedule handled by bindings
1226       
1227        //speed limit schedule times and day handled by bindings
1228       
1229        [fBlocklistURLField setStringValue: blocklistURL];
1230        [self updateBlocklistButton];
1231        [self updateBlocklistFields];
1232       
1233        //ratio limit enabled handled by bindings
1234        [fRatioStopField setFloatValue: ratioLimit];
1235       
1236        //idle limit enabled handled by bindings
1237        [fIdleStopField setIntegerValue: idleLimitMin];
1238    }
1239   
1240    [[NSNotificationCenter defaultCenter] postNotificationName: @"SpeedLimitUpdate" object: nil];
1241}
1242
1243@end
1244
1245@implementation PrefsController (Private)
1246
1247- (void) setPrefView: (id) sender
1248{
1249    NSString * identifier;
1250    if (sender)
1251    {
1252        identifier = [sender itemIdentifier];
1253        [[NSUserDefaults standardUserDefaults] setObject: identifier forKey: @"SelectedPrefView"];
1254    }
1255    else
1256        identifier = [[NSUserDefaults standardUserDefaults] stringForKey: @"SelectedPrefView"];
1257   
1258    NSView * view;
1259    if ([identifier isEqualToString: TOOLBAR_TRANSFERS])
1260        view = fTransfersView;
1261    else if ([identifier isEqualToString: TOOLBAR_GROUPS])
1262        view = fGroupsView;
1263    else if ([identifier isEqualToString: TOOLBAR_BANDWIDTH])
1264        view = fBandwidthView;
1265    else if ([identifier isEqualToString: TOOLBAR_PEERS])
1266        view = fPeersView;
1267    else if ([identifier isEqualToString: TOOLBAR_NETWORK])
1268        view = fNetworkView;
1269    else if ([identifier isEqualToString: TOOLBAR_REMOTE])
1270        view = fRemoteView;
1271    else
1272    {
1273        identifier = TOOLBAR_GENERAL; //general view is the default selected
1274        view = fGeneralView;
1275    }
1276   
1277    [[[self window] toolbar] setSelectedItemIdentifier: identifier];
1278   
1279    NSWindow * window = [self window];
1280    if ([window contentView] == view)
1281        return;
1282   
1283    NSRect windowRect = [window frame];
1284    float difference = ([view frame].size.height - [[window contentView] frame].size.height) * [window userSpaceScaleFactor];
1285    windowRect.origin.y -= difference;
1286    windowRect.size.height += difference;
1287   
1288    [view setHidden: YES];
1289    [window setContentView: view];
1290    [window setFrame: windowRect display: YES animate: YES];
1291    [view setHidden: NO];
1292   
1293    //set title label
1294    if (sender)
1295        [window setTitle: [sender label]];
1296    else
1297    {
1298        NSToolbar * toolbar = [window toolbar];
1299        NSString * itemIdentifier = [toolbar selectedItemIdentifier];
1300        for (NSToolbarItem * item in [toolbar items])
1301            if ([[item itemIdentifier] isEqualToString: itemIdentifier])
1302            {
1303                [window setTitle: [item label]];
1304                break;
1305            }
1306    }
1307}
1308
1309- (void) folderSheetClosed: (NSOpenPanel *) openPanel returnCode: (int) code contextInfo: (void *) info
1310{
1311    if (code == NSOKButton)
1312    {
1313        [fFolderPopUp selectItemAtIndex: DOWNLOAD_FOLDER];
1314       
1315        NSString * folder = [[openPanel filenames] objectAtIndex: 0];
1316        [fDefaults setObject: folder forKey: @"DownloadFolder"];
1317        [fDefaults setObject: @"Constant" forKey: @"DownloadChoice"];
1318       
1319        tr_sessionSetDownloadDir(fHandle, [folder UTF8String]);
1320    }
1321    else
1322    {
1323        //reset if cancelled
1324        [fFolderPopUp selectItemAtIndex: [fDefaults boolForKey: @"DownloadLocationConstant"] ? DOWNLOAD_FOLDER : DOWNLOAD_TORRENT];
1325    }
1326}
1327
1328- (void) incompleteFolderSheetClosed: (NSOpenPanel *) openPanel returnCode: (int) code contextInfo: (void *) info
1329{
1330    if (code == NSOKButton)
1331    {
1332        NSString * folder = [[openPanel filenames] objectAtIndex: 0];
1333        [fDefaults setObject: folder forKey: @"IncompleteDownloadFolder"];
1334       
1335        tr_sessionSetIncompleteDir(fHandle, [folder UTF8String]);
1336    }
1337    [fIncompleteFolderPopUp selectItemAtIndex: 0];
1338}
1339
1340- (void) importFolderSheetClosed: (NSOpenPanel *) openPanel returnCode: (int) code contextInfo: (void *) info
1341{
1342    NSString * path = [fDefaults stringForKey: @"AutoImportDirectory"];
1343    if (code == NSOKButton)
1344    {
1345        UKKQueue * sharedQueue = [UKKQueue sharedFileWatcher];
1346        if (path)
1347            [sharedQueue removePathFromQueue: [path stringByExpandingTildeInPath]];
1348       
1349        path = [[openPanel filenames] objectAtIndex: 0];
1350        [fDefaults setObject: path forKey: @"AutoImportDirectory"];
1351        [sharedQueue addPath: [path stringByExpandingTildeInPath]];
1352       
1353        [[NSNotificationCenter defaultCenter] postNotificationName: @"AutoImportSettingChange" object: self];
1354    }
1355    else if (!path)
1356        [fDefaults setBool: NO forKey: @"AutoImport"];
1357   
1358    [fImportFolderPopUp selectItemAtIndex: 0];
1359}
1360
1361- (void) doneScriptSheetClosed: (NSOpenPanel *) openPanel returnCode: (int) code contextInfo: (void *) info
1362{
1363    if (code == NSOKButton)
1364    {
1365        NSString * filePath = [[openPanel filenames] objectAtIndex: 0];
1366       
1367        if ([[NSFileManager defaultManager] fileExistsAtPath: filePath])  // script file exists
1368        {
1369            [fDefaults setObject: filePath forKey: @"DoneScriptPath"];
1370            [fDefaults setBool: YES forKey: @"DoneScriptEnabled"];
1371        }
1372        else // script file doesn't exist so don't enable
1373        {
1374            [fDefaults setObject: nil forKey:@"DoneScriptPath"];
1375            [fDefaults setBool: NO forKey: @"DoneScriptEnabled"];
1376        }
1377        tr_sessionSetTorrentDoneScript(fHandle, [[fDefaults stringForKey:@"DoneScriptPath"] UTF8String]);
1378        tr_sessionSetTorrentDoneScriptEnabled(fHandle, [fDefaults boolForKey:@"DoneScriptEnabled"]);
1379    }
1380    [fDoneScriptPopUp selectItemAtIndex: 0];
1381}
1382
1383- (void) setKeychainPassword: (const char *) password forService: (const char *) service username: (const char *) username
1384{
1385    SecKeychainItemRef item = NULL;
1386    NSUInteger passwordLength = strlen(password);
1387   
1388    OSStatus result = SecKeychainFindGenericPassword(NULL, strlen(service), service, strlen(username), username, NULL, NULL, &item);
1389    if (result == noErr && item)
1390    {
1391        if (passwordLength > 0) //found, so update
1392        {
1393            result = SecKeychainItemModifyAttributesAndData(item, NULL, passwordLength, (const void *)password);
1394            if (result != noErr)
1395                NSLog(@"Problem updating Keychain item: %s", GetMacOSStatusErrorString(result));
1396        }
1397        else //remove the item
1398        {
1399            result = SecKeychainItemDelete(item);
1400            if (result != noErr)
1401                NSLog(@"Problem removing Keychain item: %s", GetMacOSStatusErrorString(result));
1402        }
1403    }
1404    else if (result == errSecItemNotFound) //not found, so add
1405    {
1406        if (passwordLength > 0)
1407        {
1408            result = SecKeychainAddGenericPassword(NULL, strlen(service), service, strlen(username), username,
1409                        passwordLength, (const void *)password, NULL);
1410            if (result != noErr)
1411                NSLog(@"Problem adding Keychain item: %s", GetMacOSStatusErrorString(result));
1412        }
1413    }
1414    else
1415        NSLog(@"Problem accessing Keychain: %s", GetMacOSStatusErrorString(result));
1416}
1417
1418@end
Note: See TracBrowser for help on using the repository browser.