source: trunk/macosx/PrefsController.m @ 13103

Last change on this file since 13103 was 13103, checked in by livings124, 10 years ago

#4654 Make Mist notifications an optional setting configured through Preferences

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