source: trunk/macosx/PrefsController.m @ 13326

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

session_handle does not need to be a global variable in the prefs controller

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