1 /******************************************************************************
2 * $Id: PrefsController.m 13251 2012-03-13 02:52:11Z livings124 $
4 * Copyright (c) 2005-2012 Transmission authors and contributors
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:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
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"
33 #import "transmission.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;
66 @implementation PrefsController
69 + (void) setHandle: (tr_session *) handle
74 + (tr_session *) handle
81 if ((self = [super initWithWindowNibName: @"PrefsWindow"]))
83 fDefaults = [NSUserDefaults standardUserDefaults];
85 //check for old version download location (before 1.1)
87 if ((choice = [fDefaults stringForKey: @"DownloadChoice"]))
89 [fDefaults setBool: [choice isEqualToString: @"Constant"] forKey: @"DownloadLocationConstant"];
90 [fDefaults setBool: YES forKey: @"DownloadAsk"];
92 [fDefaults removeObjectForKey: @"DownloadChoice"];
95 //check for old version blocklist (before 2.12)
96 NSDate * blocklistDate;
97 if ((blocklistDate = [fDefaults objectForKey: @"BlocklistLastUpdate"]))
99 [fDefaults setObject: blocklistDate forKey: @"BlocklistNewLastUpdateSuccess"];
100 [fDefaults setObject: blocklistDate forKey: @"BlocklistNewLastUpdate"];
101 [fDefaults removeObjectForKey: @"BlocklistLastUpdate"];
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]]
110 //save a new random port
111 if ([fDefaults boolForKey: @"RandomPort"])
112 [fDefaults setInteger: tr_sessionGetPeerPort(fHandle) forKey: @"BindPort"];
116 if ([fDefaults boolForKey: @"AutoImport"] && (autoPath = [fDefaults stringForKey: @"AutoImportDirectory"]))
117 [[UKKQueue sharedFileWatcher] addPath: [autoPath stringByExpandingTildeInPath]];
119 //set blocklist scheduler
120 [[BlocklistScheduler scheduler] updateSchedule];
123 [self setEncryptionMode: nil];
125 //update rpc whitelist
126 [self updateRPCPassword];
128 fRPCWhitelistArray = [[fDefaults arrayForKey: @"RPCWhitelist"] mutableCopy];
129 if (!fRPCWhitelistArray)
130 fRPCWhitelistArray = [[NSMutableArray arrayWithObject: @"127.0.0.1"] retain];
131 [self updateRPCWhitelist];
133 //reset old Sparkle settings from previous versions
134 [fDefaults removeObjectForKey: @"SUScheduledCheckInterval"];
135 if ([fDefaults objectForKey: @"CheckForUpdates"])
137 [[SUUpdater sharedUpdater] setAutomaticallyChecksForUpdates: [fDefaults boolForKey: @"CheckForUpdates"]];
138 [fDefaults removeObjectForKey: @"CheckForUpdates"];
142 [GrowlApplicationBridge setShouldUseBuiltInNotifications: [fDefaults boolForKey: @"DisplayNotifications"]];
144 [self setAutoUpdateToBeta: nil];
152 [[NSNotificationCenter defaultCenter] removeObserver: self];
154 [fPortStatusTimer invalidate];
157 [fPortChecker cancelProbe];
158 [fPortChecker release];
161 [fRPCWhitelistArray release];
163 [fRPCPassword release];
168 - (void) awakeFromNib
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];
181 [self setPrefView: nil];
183 [fBuiltInGrowlButton setState: [fDefaults boolForKey: @"DisplayNotifications"]];
184 const BOOL growlRunning = [GrowlApplicationBridge isGrowlRunning];
185 [fBuiltInGrowlButton setHidden: growlRunning];
186 [fGrowlInstalledField setHidden: !growlRunning];
188 //set download folder
189 [fFolderPopUp selectItemAtIndex: [fDefaults boolForKey: @"DownloadLocationConstant"] ? DOWNLOAD_FOLDER : DOWNLOAD_TORRENT];
192 [fRatioStopField setFloatValue: [fDefaults floatForKey: @"RatioLimit"]];
194 //set idle seeding minutes
195 [fIdleStopField setIntegerValue: [fDefaults integerForKey: @"IdleLimitMinutes"]];
198 [self updateLimitFields];
201 [fSpeedLimitUploadField setIntValue: [fDefaults integerForKey: @"SpeedLimitUploadLimit"]];
202 [fSpeedLimitDownloadField setIntValue: [fDefaults integerForKey: @"SpeedLimitDownloadLimit"]];
205 [fPortField setIntValue: [fDefaults integerForKey: @"BindPort"]];
208 [self updatePortStatus];
209 fPortStatusTimer = [NSTimer scheduledTimerWithTimeInterval: 5.0 target: self
210 selector: @selector(updatePortStatus) userInfo: nil repeats: YES];
212 //set peer connections
213 [fPeersGlobalField setIntValue: [fDefaults integerForKey: @"PeersTotal"]];
214 [fPeersTorrentField setIntValue: [fDefaults integerForKey: @"PeersTorrent"]];
217 [fQueueDownloadField setIntValue: [fDefaults integerForKey: @"QueueDownloadNumber"]];
218 [fQueueSeedField setIntValue: [fDefaults integerForKey: @"QueueSeedNumber"]];
219 [fStalledField setIntValue: [fDefaults integerForKey: @"StalledMinutes"]];
222 NSString * blocklistURL = [fDefaults stringForKey: @"BlocklistURL"];
224 [fBlocklistURLField setStringValue: blocklistURL];
226 [self updateBlocklistButton];
227 [self updateBlocklistFields];
229 [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(updateLimitFields)
230 name: @"UpdateSpeedLimitValuesOutsidePrefs" object: nil];
232 [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(updateRatioStopField)
233 name: @"UpdateRatioStopValueOutsidePrefs" object: nil];
235 [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(updateLimitStopField)
236 name: @"UpdateIdleStopValueOutsidePrefs" object: nil];
238 [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(updateBlocklistFields)
239 name: @"BlocklistUpdated" object: nil];
241 [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(updateBlocklistURLField)
242 name: NSControlTextDidChangeNotification object: fBlocklistURLField];
245 [fRPCPortField setIntValue: [fDefaults integerForKey: @"RPCPort"]];
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])
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];
264 else if ([ident isEqualToString: TOOLBAR_TRANSFERS])
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];
272 else if ([ident isEqualToString: TOOLBAR_GROUPS])
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];
280 else if ([ident isEqualToString: TOOLBAR_BANDWIDTH])
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];
288 else if ([ident isEqualToString: TOOLBAR_PEERS])
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];
296 else if ([ident isEqualToString: TOOLBAR_NETWORK])
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];
304 else if ([ident isEqualToString: TOOLBAR_REMOTE])
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];
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
341 #define SPARKLE_TAG [fDefaults boolForKey: @"AutoUpdateBeta"]
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);
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];
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"]);
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)
393 [fPortStatusField setStringValue: @""];
394 [fPortStatusImage setImage: nil];
395 [fPortStatusProgress startAnimation: self];
399 [fPortChecker cancelProbe];
400 [fPortChecker release];
402 BOOL delay = natStatusChanged || tr_sessionIsPortForwardingEnabled(fHandle);
403 fPortChecker = [[PortChecker alloc] initForPort: fPeerPort delay: delay withDelegate: self];
407 - (void) portCheckerDidFinishProbing: (PortChecker *) portChecker
409 [fPortStatusProgress stopAnimation: self];
410 switch ([fPortChecker status])
412 case PORT_STATUS_OPEN:
413 [fPortStatusField setStringValue: NSLocalizedString(@"Port is open", "Preferences -> Network -> port status")];
414 [fPortStatusImage setImage: [NSImage imageNamed: @"GreenDot.png"]];
416 case PORT_STATUS_CLOSED:
417 [fPortStatusField setStringValue: NSLocalizedString(@"Port is closed", "Preferences -> Network -> port status")];
418 [fPortStatusImage setImage: [NSImage imageNamed: @"RedDot.png"]];
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"]];
425 NSAssert1(NO, @"Port checker returned invalid status: %d", [fPortChecker status]);
428 [fPortChecker release];
434 NSMutableArray * sounds = [NSMutableArray array];
436 NSArray * directories = NSSearchPathForDirectoriesInDomains(NSAllLibrariesDirectory,
437 NSUserDomainMask | NSLocalDomainMask | NSSystemDomainMask, YES);
439 for (NSString * directory in directories)
441 directory = [directory stringByAppendingPathComponent: @"Sounds"];
444 if ([[NSFileManager defaultManager] fileExistsAtPath: directory isDirectory: &isDirectory] && isDirectory)
446 NSArray * directoryContents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath: directory error: NULL];
447 for (NSString * sound in directoryContents)
449 sound = [sound stringByDeletingPathExtension];
450 if ([NSSound soundNamed: sound])
451 [sounds addObject: sound];
459 - (void) setSound: (id) sender
461 //play sound when selecting
463 if ((sound = [NSSound soundNamed: [sender titleOfSelectedItem]]))
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"]);
512 [[BlocklistScheduler scheduler] updateSchedule];
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);
533 NSString * countString = [NSString formattedUInteger: tr_blocklistGetRuleCount(fHandle)];
534 [fBlocklistMessageField setStringValue: [NSString stringWithFormat: NSLocalizedString(@"%@ IP address rules in list",
535 "Prefs -> blocklist -> message"), countString]];
538 [fBlocklistMessageField setStringValue: NSLocalizedString(@"A blocklist must first be downloaded",
539 "Prefs -> blocklist -> message")];
541 NSString * updatedDateString;
544 NSDate * updatedDate = [fDefaults objectForKey: @"BlocklistNewLastUpdateSuccess"];
547 updatedDateString = [NSDateFormatter localizedStringFromDate: updatedDate dateStyle: NSDateFormatterFullStyle timeStyle: NSDateFormatterShortStyle];
549 updatedDateString = NSLocalizedString(@"N/A", "Prefs -> blocklist -> message");
552 updatedDateString = NSLocalizedString(@"Never", "Prefs -> blocklist -> message");
554 [fBlocklistDateField setStringValue: [NSString stringWithFormat: @"%@: %@",
555 NSLocalizedString(@"Last updated", "Prefs -> blocklist -> message"), updatedDateString]];
558 - (void) updateBlocklistURLField
560 NSString * blocklistString = [fBlocklistURLField stringValue];
562 [fDefaults setObject: blocklistString forKey: @"BlocklistURL"];
563 tr_blocklistSetURL(fHandle, [blocklistString UTF8String]);
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"]);
586 tr_sessionLimitSpeed(fHandle, TR_DOWN, [fDefaults boolForKey: @"CheckDownload"]);
587 tr_sessionSetSpeedLimit_KBps(fHandle, TR_DOWN, [fDefaults integerForKey: @"DownloadLimit"]);
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"]);
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"]);
605 //reload main table for seeding progress
606 [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateUI" object: nil];
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"];
616 [self applyRatioSetting: nil];
619 - (void) updateRatioStopField
622 [fRatioStopField setFloatValue: [fDefaults floatForKey: @"RatioLimit"]];
625 - (void) updateRatioStopFieldOld
627 [self updateRatioStopField];
629 [self applyRatioSetting: nil];
632 - (void) applyIdleStopSetting: (id) sender
634 tr_sessionSetIdleLimited(fHandle, [fDefaults boolForKey: @"IdleLimitCheck"]);
635 tr_sessionSetIdleLimit(fHandle, [fDefaults integerForKey: @"IdleLimitMinutes"]);
637 //reload main table for remaining seeding time
638 [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateUI" object: nil];
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"];
648 [self applyIdleStopSetting: nil];
651 - (void) updateLimitStopField
654 [fIdleStopField setIntegerValue: [fDefaults integerForKey: @"IdleLimitMinutes"]];
657 - (void) updateLimitFields
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];
708 return [[NSCalendar currentCalendar] dateFromComponents: comps];
711 - (BOOL) control: (NSControl *) control textShouldBeginEditing: (NSText *) fieldEditor
713 [fInitialString release];
714 fInitialString = [[control stringValue] retain];
719 - (BOOL) control: (NSControl *) control didFailToFormatString: (NSString *) string errorDescription: (NSString *) error
724 [control setStringValue: fInitialString];
725 [fInitialString release];
726 fInitialString = nil;
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);
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"]);
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;
781 [fDefaults setInteger: number forKey: seed ? @"QueueSeedNumber" : @"QueueDownloadNumber"];
783 tr_sessionSetQueueSize(fHandle, seed ? TR_UP : TR_DOWN, number);
786 - (void) setStalled: (id) sender
788 tr_sessionSetQueueStalledEnabled(fHandle, [fDefaults boolForKey: @"CheckStalled"]);
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);
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];
819 [panel beginSheetModalForWindow: [self window] completionHandler: ^(NSInteger result) {
820 if (result == NSFileHandlingPanelOKButton)
822 [fFolderPopUp selectItemAtIndex: DOWNLOAD_FOLDER];
824 NSString * folder = [[[panel URLs] objectAtIndex: 0] path];
825 [fDefaults setObject: folder forKey: @"DownloadFolder"];
826 [fDefaults setObject: @"Constant" forKey: @"DownloadChoice"];
828 tr_sessionSetDownloadDir(fHandle, [folder UTF8String]);
833 [fFolderPopUp selectItemAtIndex: [fDefaults boolForKey: @"DownloadLocationConstant"] ? DOWNLOAD_FOLDER : DOWNLOAD_TORRENT];
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];
848 [panel beginSheetModalForWindow: [self window] completionHandler: ^(NSInteger result) {
849 if (result == NSFileHandlingPanelOKButton)
851 NSString * folder = [[[panel URLs] objectAtIndex: 0] path];
852 [fDefaults setObject: folder forKey: @"IncompleteDownloadFolder"];
854 tr_sessionSetIncompleteDir(fHandle, [folder UTF8String]);
856 [fIncompleteFolderPopUp selectItemAtIndex: 0];
860 - (void) doneScriptSheetShow:(id)sender
862 NSOpenPanel * panel = [NSOpenPanel openPanel];
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];
870 [panel beginSheetModalForWindow: [self window] completionHandler: ^(NSInteger result) {
871 if (result == NSFileHandlingPanelOKButton)
873 NSString * filePath = [[[panel URLs] objectAtIndex: 0] path];
875 [fDefaults setObject: filePath forKey: @"DoneScriptPath"];
876 tr_sessionSetTorrentDoneScript(fHandle, [filePath UTF8String]);
878 [fDefaults setBool: YES forKey: @"DoneScriptEnabled"];
879 tr_sessionSetTorrentDoneScriptEnabled(fHandle, YES);
881 [fDoneScriptPopUp selectItemAtIndex: 0];
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"]])
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];
903 tr_sessionSetTorrentDoneScriptEnabled(fHandle, [fDefaults boolForKey: @"DoneScriptEnabled"]);
906 - (void) setAutoImport: (id) sender
909 if ((path = [fDefaults stringForKey: @"AutoImportDirectory"]))
911 path = [path stringByExpandingTildeInPath];
912 if ([fDefaults boolForKey: @"AutoImport"])
913 [[UKKQueue sharedFileWatcher] addPath: path];
915 [[UKKQueue sharedFileWatcher] removePathFromQueue: path];
917 [[NSNotificationCenter defaultCenter] postNotificationName: @"AutoImportSettingChange" object: self];
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)
937 UKKQueue * sharedQueue = [UKKQueue sharedFileWatcher];
939 [sharedQueue removePathFromQueue: [path stringByExpandingTildeInPath]];
941 path = [[[panel URLs] objectAtIndex: 0] path];
942 [fDefaults setObject: path forKey: @"AutoImportDirectory"];
943 [sharedQueue addPath: [path stringByExpandingTildeInPath]];
945 [[NSNotificationCenter defaultCenter] postNotificationName: @"AutoImportSettingChange" object: self];
948 [fDefaults setBool: NO forKey: @"AutoImport"];
950 [fImportFolderPopUp selectItemAtIndex: 0];
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);
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];
988 const char * password = [[sender stringValue] UTF8String];
989 [self setKeychainPassword: password forService: RPC_KEYCHAIN_SERVICE username: RPC_KEYCHAIN_NAME];
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);
1001 [fRPCPassword release];
1002 if (password != NULL)
1004 char fullPassword[passwordLength+1];
1005 strncpy(fullPassword, password, passwordLength);
1006 fullPassword[passwordLength] = '\0';
1007 SecKeychainItemFreeContent(NULL, (void *)password);
1009 tr_sessionSetRPCPassword(fHandle, fullPassword);
1011 fRPCPassword = [[NSString alloc] initWithUTF8String: fullPassword];
1012 [fRPCPasswordField setStringValue: fRPCPassword];
1018 - (void) setRPCPort: (id) sender
1020 int port = [sender intValue];
1021 [fDefaults setInteger: port forKey: @"RPCPort"];
1022 tr_sessionSetRPCPort(fHandle, port);
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"]];
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)
1052 if ([[sender cell] tagForSegment: [sender selectedSegment]] == RPC_IP_REMOVE_TAG)
1054 [fRPCWhitelistArray removeObjectsAtIndexes: [fRPCWhitelistTable selectedRowIndexes]];
1055 [fRPCWhitelistTable deselectAll: self];
1056 [fRPCWhitelistTable reloadData];
1058 [fDefaults setObject: fRPCWhitelistArray forKey: @"RPCWhitelist"];
1059 [self updateRPCWhitelist];
1063 [fRPCWhitelistArray addObject: @""];
1064 [fRPCWhitelistTable reloadData];
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];
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];
1088 //create better-formatted ip string
1090 if ([components count] == 4)
1093 for (NSString * component in components)
1095 if ([component isEqualToString: @"*"])
1096 [newComponents addObject: component];
1099 int num = [component intValue];
1100 if (num >= 0 && num < 256)
1101 [newComponents addObject: [[NSNumber numberWithInt: num] stringValue]];
1114 newIP = [newComponents componentsJoinedByString: @"."];
1116 //don't allow the same ip address
1117 if ([fRPCWhitelistArray containsObject: newIP] && ![[fRPCWhitelistArray objectAtIndex: row] isEqualToString: newIP])
1123 [fRPCWhitelistArray replaceObjectAtIndex: row withObject: newIP];
1124 [fRPCWhitelistArray sortUsingSelector: @selector(compareNumeric:)];
1129 if ([[fRPCWhitelistArray objectAtIndex: row] isEqualToString: @""])
1130 [fRPCWhitelistArray removeObjectAtIndex: row];
1133 [fRPCWhitelistTable deselectAll: self];
1134 [fRPCWhitelistTable reloadData];
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
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"];
1176 //download directory
1177 NSString * downloadLocation = [[NSString stringWithUTF8String: tr_sessionGetDownloadDir(fHandle)] stringByStandardizingPath];
1178 [fDefaults setObject: downloadLocation forKey: @"DownloadFolder"];
1180 NSString * incompleteLocation = [[NSString stringWithUTF8String: tr_sessionGetIncompleteDir(fHandle)] stringByStandardizingPath];
1181 [fDefaults setObject: incompleteLocation forKey: @"IncompleteDownloadFolder"];
1183 const BOOL useIncomplete = tr_sessionIsIncompleteDirEnabled(fHandle);
1184 [fDefaults setBool: useIncomplete forKey: @"UseIncompleteDownloadFolder"];
1186 const BOOL usePartialFileRanaming = tr_sessionIsIncompleteFileNamingEnabled(fHandle);
1187 [fDefaults setBool: usePartialFileRanaming forKey: @"RenamePartialFiles"];
1190 const BOOL utp = tr_sessionIsUTPEnabled(fHandle);
1191 [fDefaults setBool: utp forKey: @"UTPGlobal"];
1194 const uint16_t peersTotal = tr_sessionGetPeerLimit(fHandle);
1195 [fDefaults setInteger: peersTotal forKey: @"PeersTotal"];
1197 const uint16_t peersTorrent = tr_sessionGetPeerLimitPerTorrent(fHandle);
1198 [fDefaults setInteger: peersTorrent forKey: @"PeersTorrent"];
1201 const BOOL pex = tr_sessionIsPexEnabled(fHandle);
1202 [fDefaults setBool: pex forKey: @"PEXGlobal"];
1205 const BOOL dht = tr_sessionIsDHTEnabled(fHandle);
1206 [fDefaults setBool: dht forKey: @"DHTGlobal"];
1209 const BOOL lpd = tr_sessionIsLPDEnabled(fHandle);
1210 [fDefaults setBool: lpd forKey: @"LocalPeerDiscoveryGlobal"];
1213 const BOOL autoStart = !tr_sessionGetPaused(fHandle);
1214 [fDefaults setBool: autoStart forKey: @"AutoStartDownload"];
1217 const tr_port port = tr_sessionGetPeerPort(fHandle);
1218 [fDefaults setInteger: port forKey: @"BindPort"];
1220 const BOOL nat = tr_sessionIsPortForwardingEnabled(fHandle);
1221 [fDefaults setBool: nat forKey: @"NatTraversal"];
1225 [self updatePortStatus];
1227 const BOOL randomPort = tr_sessionGetPeerPortRandomOnStart(fHandle);
1228 [fDefaults setBool: randomPort forKey: @"RandomPort"];
1230 //speed limit - down
1231 const BOOL downLimitEnabled = tr_sessionIsSpeedLimited(fHandle, TR_DOWN);
1232 [fDefaults setBool: downLimitEnabled forKey: @"CheckDownload"];
1234 const int downLimit = tr_sessionGetSpeedLimit_KBps(fHandle, TR_DOWN);
1235 [fDefaults setInteger: downLimit forKey: @"DownloadLimit"];
1238 const BOOL upLimitEnabled = tr_sessionIsSpeedLimited(fHandle, TR_UP);
1239 [fDefaults setBool: upLimitEnabled forKey: @"CheckUpload"];
1241 const int upLimit = tr_sessionGetSpeedLimit_KBps(fHandle, TR_UP);
1242 [fDefaults setInteger: upLimit forKey: @"UploadLimit"];
1244 //alt speed limit enabled
1245 const BOOL useAltSpeed = tr_sessionUsesAltSpeed(fHandle);
1246 [fDefaults setBool: useAltSpeed forKey: @"SpeedLimit"];
1248 //alt speed limit - down
1249 const int downLimitAlt = tr_sessionGetAltSpeed_KBps(fHandle, TR_DOWN);
1250 [fDefaults setInteger: downLimitAlt forKey: @"SpeedLimitDownloadLimit"];
1252 //alt speed limit - up
1253 const int upLimitAlt = tr_sessionGetAltSpeed_KBps(fHandle, TR_UP);
1254 [fDefaults setInteger: upLimitAlt forKey: @"SpeedLimitUploadLimit"];
1256 //alt speed limit schedule
1257 const BOOL useAltSpeedSched = tr_sessionUsesAltSpeedTime(fHandle);
1258 [fDefaults setBool: useAltSpeedSched forKey: @"SpeedLimitAuto"];
1260 NSDate * limitStartDate = [PrefsController timeSumToDate: tr_sessionGetAltSpeedBegin(fHandle)];
1261 [fDefaults setObject: limitStartDate forKey: @"SpeedLimitAutoOnDate"];
1263 NSDate * limitEndDate = [PrefsController timeSumToDate: tr_sessionGetAltSpeedEnd(fHandle)];
1264 [fDefaults setObject: limitEndDate forKey: @"SpeedLimitAutoOffDate"];
1266 const int limitDay = tr_sessionGetAltSpeedDay(fHandle);
1267 [fDefaults setInteger: limitDay forKey: @"SpeedLimitAutoDay"];
1270 const BOOL blocklist = tr_blocklistIsEnabled(fHandle);
1271 [fDefaults setBool: blocklist forKey: @"BlocklistNew"];
1273 NSString * blocklistURL = [NSString stringWithUTF8String: tr_blocklistGetURL(fHandle)];
1274 [fDefaults setObject: blocklistURL forKey: @"BlocklistURL"];
1277 const BOOL ratioLimited = tr_sessionIsRatioLimited(fHandle);
1278 [fDefaults setBool: ratioLimited forKey: @"RatioCheck"];
1280 const float ratioLimit = tr_sessionGetRatioLimit(fHandle);
1281 [fDefaults setFloat: ratioLimit forKey: @"RatioLimit"];
1284 const BOOL idleLimited = tr_sessionIsIdleLimited(fHandle);
1285 [fDefaults setBool: idleLimited forKey: @"IdleLimitCheck"];
1287 const NSUInteger idleLimitMin = tr_sessionGetIdleLimit(fHandle);
1288 [fDefaults setInteger: idleLimitMin forKey: @"IdleLimitMinutes"];
1291 const BOOL downloadQueue = tr_sessionGetQueueEnabled(fHandle, TR_DOWN);
1292 [fDefaults setBool: downloadQueue forKey: @"Queue"];
1294 const int downloadQueueNum = tr_sessionGetQueueSize(fHandle, TR_DOWN);
1295 [fDefaults setInteger: downloadQueueNum forKey: @"QueueDownloadNumber"];
1297 const BOOL seedQueue = tr_sessionGetQueueEnabled(fHandle, TR_UP);
1298 [fDefaults setBool: seedQueue forKey: @"QueueSeed"];
1300 const int seedQueueNum = tr_sessionGetQueueSize(fHandle, TR_UP);
1301 [fDefaults setInteger: seedQueueNum forKey: @"QueueSeedNumber"];
1303 const BOOL checkStalled = tr_sessionGetQueueStalledEnabled(fHandle);
1304 [fDefaults setBool: checkStalled forKey: @"CheckStalled"];
1306 const int stalledMinutes = tr_sessionGetQueueStalledMinutes(fHandle);
1307 [fDefaults setInteger: stalledMinutes forKey: @"StalledMinutes"];
1310 const BOOL doneScriptEnabled = tr_sessionIsTorrentDoneScriptEnabled(fHandle);
1311 [fDefaults setBool: doneScriptEnabled forKey: @"DoneScriptEnabled"];
1313 NSString * doneScriptPath = [NSString stringWithUTF8String: tr_sessionGetTorrentDoneScript(fHandle)];
1314 [fDefaults setObject: doneScriptPath forKey: @"DoneScriptPath"];
1316 //update gui if loaded
1319 //encryption handled by bindings
1321 //download directory handled by bindings
1323 //utp handled by bindings
1325 [fPeersGlobalField setIntValue: peersTotal];
1326 [fPeersTorrentField setIntValue: peersTorrent];
1328 //pex handled by bindings
1330 //dht handled by bindings
1332 //lpd handled by bindings
1334 [fPortField setIntValue: port];
1335 //port forwarding (nat) handled by bindings
1336 //random port handled by bindings
1338 //limit check handled by bindings
1339 [fDownloadField setIntValue: downLimit];
1341 //limit check handled by bindings
1342 [fUploadField setIntValue: upLimit];
1344 [fSpeedLimitDownloadField setIntValue: downLimitAlt];
1346 [fSpeedLimitUploadField setIntValue: upLimitAlt];
1348 //speed limit schedule handled by bindings
1350 //speed limit schedule times and day handled by bindings
1352 [fBlocklistURLField setStringValue: blocklistURL];
1353 [self updateBlocklistButton];
1354 [self updateBlocklistFields];
1356 //ratio limit enabled handled by bindings
1357 [fRatioStopField setFloatValue: ratioLimit];
1359 //idle limit enabled handled by bindings
1360 [fIdleStopField setIntegerValue: idleLimitMin];
1362 //queues enabled handled by bindings
1363 [fQueueDownloadField setIntValue: downloadQueueNum];
1364 [fQueueSeedField setIntValue: seedQueueNum];
1366 //check stalled handled by bindings
1367 [fStalledField setIntValue: stalledMinutes];
1370 [[NSNotificationCenter defaultCenter] postNotificationName: @"SpeedLimitUpdate" object: nil];
1372 //reload global settings in inspector
1373 [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateGlobalOptions" object: nil];
1378 @implementation PrefsController (Private)
1380 - (void) setPrefView: (id) sender
1382 NSString * identifier;
1385 identifier = [sender itemIdentifier];
1386 [[NSUserDefaults standardUserDefaults] setObject: identifier forKey: @"SelectedPrefView"];
1389 identifier = [[NSUserDefaults standardUserDefaults] stringForKey: @"SelectedPrefView"];
1392 if ([identifier isEqualToString: TOOLBAR_TRANSFERS])
1393 view = fTransfersView;
1394 else if ([identifier isEqualToString: TOOLBAR_GROUPS])
1396 else if ([identifier isEqualToString: TOOLBAR_BANDWIDTH])
1397 view = fBandwidthView;
1398 else if ([identifier isEqualToString: TOOLBAR_PEERS])
1400 else if ([identifier isEqualToString: TOOLBAR_NETWORK])
1401 view = fNetworkView;
1402 else if ([identifier isEqualToString: TOOLBAR_REMOTE])
1406 identifier = TOOLBAR_GENERAL; //general view is the default selected
1407 view = fGeneralView;
1410 [[[self window] toolbar] setSelectedItemIdentifier: identifier];
1412 NSWindow * window = [self window];
1413 if ([window contentView] == view)
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;
1421 [view setHidden: YES];
1422 [window setContentView: view];
1423 [window setFrame: windowRect display: YES animate: YES];
1424 [view setHidden: NO];
1428 [window setTitle: [sender label]];
1431 NSToolbar * toolbar = [window toolbar];
1432 NSString * itemIdentifier = [toolbar selectedItemIdentifier];
1433 for (NSToolbarItem * item in [toolbar items])
1434 if ([[item itemIdentifier] isEqualToString: itemIdentifier])
1436 [window setTitle: [item label]];
1442 - (void) setKeychainPassword: (const char *) password forService: (const char *) service username: (const char *) username
1444 SecKeychainItemRef item = NULL;
1445 NSUInteger passwordLength = strlen(password);
1447 OSStatus result = SecKeychainFindGenericPassword(NULL, strlen(service), service, strlen(username), username, NULL, NULL, &item);
1448 if (result == noErr && item)
1450 if (passwordLength > 0) //found, so update
1452 result = SecKeychainItemModifyAttributesAndData(item, NULL, passwordLength, (const void *)password);
1453 if (result != noErr)
1454 NSLog(@"Problem updating Keychain item: %s", GetMacOSStatusErrorString(result));
1456 else //remove the item
1458 result = SecKeychainItemDelete(item);
1459 if (result != noErr)
1460 NSLog(@"Problem removing Keychain item: %s", GetMacOSStatusErrorString(result));
1463 else if (result == errSecItemNotFound) //not found, so add
1465 if (passwordLength > 0)
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));
1474 NSLog(@"Problem accessing Keychain: %s", GetMacOSStatusErrorString(result));