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