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