source: trunk/macosx/PrefsController.m @ 10666

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

nothing exciting

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