source: trunk/macosx/PrefsController.m @ 13325

Last change on this file since 13325 was 13325, checked in by livings124, 9 years ago

silence 2 warnings

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