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