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