source: trunk/macosx/PrefsController.m @ 11046

Last change on this file since 11046 was 11046, checked in by livings124, 12 years ago

update Mac build for the change from inactive to idle

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