source: trunk/macosx/PrefsController.m @ 11875

Last change on this file since 11875 was 11875, checked in by livings124, 11 years ago

inspector: update the displayed global seeding settings only when necessary; update only the displayed global seeding settings instead of all the options

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