source: trunk/macosx/PrefsController.m @ 8147

Last change on this file since 8147 was 8147, checked in by livings124, 13 years ago

first pass through for making the Mac client respond to the new rpc messages - still missing a few things

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