source: trunk/PrefsPane/src/GlimmerBlockerPane.m @ 1046

Revision 1035, 32.2 KB checked in by speck, 7 months ago (diff)

Moved 2 debugs for better -hidden version with log at authorizationViewDidAuthorize/authorizationViewDidDeauthorize

  • Property svn:executable set to *
Line 
1/* Copyright (C) 2008-2009 Peter Speck
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
15 */
16
17//#define DEBUG 1
18
19#import <ExceptionHandling/NSExceptionHandler.h>
20#import <SecurityInterface/SFAuthorizationView.h>
21#import "GlimmerBlockerPane.h"
22#import "NetworkSettingsUpdaterUI.h"
23#import "GBDebug.h"
24#import "ServerCommunication.h"
25#import "FilterTabController.h"
26#import "HistoryController.h"
27#import "SuspectsController.h"
28#import "AlertHelper.h"
29#import "Prefs.h"
30#import "XmlDoc.h"
31#import "Filter.h"
32#import "NamedEnum.h"
33#import "InstallationHelper.h"
34
35// man malloc_history
36
37#ifdef DEBUG
38//#define DEBUG_FSEVENTS
39#endif
40
41@interface GlimmerBlockerPrefsPane ()
42
43- (void)startFileSystemEventListener;
44- (void)stopFileSystemEventListener;
45
46- (void)updateSelectedTabView;
47
48- (void)updateTabViewSetup;
49- (void)updateAutoUpdateViews;
50
51- (void)updateTabViewFilters;
52- (void)releaseFilterTabController;
53- (void)checkReloadSubscriptionFilterData;
54
55- (void)updateTabViewHistory;
56
57- (void)updateTabViewSuspects;
58
59- (void)updateTabViewNetwork;
60- (void)updateTabViewDeveloper;
61
62- (void)updateNetworkSettingsIfWanted;
63- (void)updateNetworkSettingsWithFeedbackIfUnchanged:(BOOL)feedbackIfUnchanged;
64
65- (void)workspaceSessionDidBecomeActiveNotification:(NSNotification*)notification;
66- (void)workspaceSessionDidResignActiveNotification:(NSNotification*)notification;
67- (void)checkEditFilterNotification:(NSNotification*)notification;
68
69- (void)setCheckbox:(NSButton*)button withXPath:(NSString*)xpath withEnabled:(BOOL)enabled;
70@end
71
72@implementation GlimmerBlockerPrefsPane
73
74-(id)initWithBundle:(NSBundle*)bundle
75{
76    DebugNSLog(@"NSAppKitVersionNumber = %f", NSAppKitVersionNumber);
77    if (floor(NSAppKitVersionNumber) <= NSAppKitVersionNumber10_5) {
78        NSLog(@"GlimmerBlocker refusing to run under Mac OS X 10.5 and earlier.");
79        // todo: show alert?
80        return NULL;
81    }
82    if (!(self = [super initWithBundle:bundle]))
83        return NULL;
84    if (getenv("NSZombieEnabled"))
85        NSLog(@"NSZombieEnabled enabled!");
86    if (getenv("NSAutoreleaseFreedObjectCheckEnabled"))
87        NSLog(@"NSAutoreleaseFreedObjectCheckEnabled enabled!");
88    srandom((int)time(NULL));
89    NSExceptionHandler* exHandler = [NSExceptionHandler defaultExceptionHandler];
90    [exHandler setExceptionHandlingMask:NSLogAndHandleEveryExceptionMask];
91    [exHandler setDelegate:self];
92    [NSDateFormatter setDefaultFormatterBehavior:NSDateFormatterBehavior10_4];
93    _glimmerBlockerVersionText = [[[bundle infoDictionary] objectForKey:@"CFBundleVersion"] retain];
94    return self;
95}
96
97- (void)dealloc
98{
99    [self stopFileSystemEventListener];
100    [_prefs saveIfDirty:self];
101    [_history notifyBecameHidden];
102    [_suspects notifyBecameHidden];
103    [_installationHelper release];
104    [_filterTabController release];
105    [_history release];
106    [_suspects release];
107    [_comm release];
108    [_prefs release];
109    [_alertHelper release];
110    [_glimmerBlockerVersionText release];
111    [super dealloc];
112}
113
114- (BOOL)checkInstallation
115{
116    if (_installationHelper)
117        return NO;
118    _installationHelper = [InstallationHelper alloc];
119    [_installationHelper initWithCopyrightMessage:[setupCopyrightTextView textStorage]
120                               withPreferencePane:self];
121    if (![_installationHelper validate:_glimmerBlockerVersionText]) {
122        [self stopFileSystemEventListener];
123        return NO;
124    }
125    [_installationHelper release];
126    _installationHelper = NULL;
127    return YES;
128}
129
130- (void)assignMainView
131{
132    NSAssert(mainPrefsPaneView, @"Missing mainPrefsPaneView");
133    if (![self checkInstallation])
134        return;
135    [self setMainView:mainPrefsPaneView];
136}
137
138- (void)mainViewDidLoad
139{
140    if (_installationHelper)
141        return;
142    NSAssert(mainPrefsPaneView, @"mainPrefsPaneView");
143    DebugNSLog(@"mainViewDidLoad, has-window = %d", !![mainPrefsPaneView window]);
144    _alertHelper = [[AlertHelper alloc] initWindowView:mainPrefsPaneView withGbBundle:[self bundle]];
145    [ProxyScriptEditor initProxyScriptEditor];
146    _prefs = [[Prefs prefsWithAlertHelper:_alertHelper
147                  withSFAuthorizationView:authView] retain];
148    _comm = [[ServerCommunication alloc] initWithPrefs:_prefs];
149    _filterTabController = [[FilterTabController alloc] initWithServerCommunication:_comm
150                                                                  withContainerView:filtersContainerView
151                                                                        withTabView:tabView
152                                                             withFiltersTabViewItem:filtersTabViewItem
153                                                                         withBundle:[self bundle]];
154    _history = [[HistoryController alloc] initWithFilterTabController:_filterTabController
155                                                    withContainerView:historyContainerView
156                                                           withBundle:[self bundle]];
157    _suspects = [[SuspectsController alloc] initWithFilterTabController:_filterTabController
158                                                      withContainerView:suspectsContainerView
159                                                             withBundle:[self bundle]];
160    // minimize window height to ensure users can click the SFAuthorizationView button on 800*600 monitors.
161    CGFloat shrink = 675 - [[NSScreen mainScreen] frame].size.height;
162    if (shrink > 0) {
163        NSSize sz = [mainPrefsPaneView frame].size;
164        sz.height -= shrink;
165        [mainPrefsPaneView setFrameSize:sz];
166    }
167    [tabView setDelegate:self];
168    NSAssert(setupEnableGlimmerBlockerCB, @"t1EnableCB");
169    [setupCopyrightTextView setEditable:NO]; // easier to have YES in IB, as it won't otherwise allow editing in IB.
170    [developerDumpHttpCacheMessageTextView setHidden:YES];
171    //
172    NSString* s = [_prefs stringForXPath:@"iu/@selected-tab"];
173    if (s) {
174        DebugNSLog(@"previous selected tab: %@", s);
175        NSInteger idx = [tabView indexOfTabViewItemWithIdentifier:s];
176        if (idx == NSNotFound)
177            s = NULL;
178        else
179            [tabView selectTabViewItemAtIndex:idx];
180    }
181    if (!s)
182        [tabView selectFirstTabViewItem:self];
183    //
184    NSAssert(authView, @"authView");
185    [authView setString:glimmerAuthString];
186    [authView setDelegate:self];
187    [authView updateStatus:self]; // force immediate update
188    [authView setAutoupdate:YES];
189    //DebugNSLog(@"auth state: %d", [authView authorizationState]);
190    NSNotificationCenter* nc = [[NSWorkspace sharedWorkspace] notificationCenter];
191    [nc addObserver:self
192           selector:@selector(workspaceSessionDidBecomeActiveNotification:)
193               name:NSWorkspaceSessionDidBecomeActiveNotification
194             object:nil];
195    [nc addObserver:self
196           selector:@selector(workspaceSessionDidResignActiveNotification:)
197               name:NSWorkspaceSessionDidResignActiveNotification
198             object:nil];
199    if ([_prefs zapRulePreviewElement] && [_prefs hasGlimmerAuth])
200        [_comm saveDirtyPrefsAndNotify];
201}
202
203#pragma mark - delegate methods
204
205- (void)willSelect
206{
207    if (_installationHelper)
208        return;
209    DebugNSLog(@"Our prefs panel is about to be selected.");
210}
211
212- (void)didSelect
213{
214    if (![self checkInstallation])
215        return;
216    DebugNSLog(@"Our prefs panel is selected.");
217    DebugNSLog(@"Our window: %@", [tabView window]);
218    [_comm performPostLoadPreferencePanelCheck];
219    if ([_prefs boolForXPath:@"proxy-server/@enabled"] && ![_comm isServerRunning]) {
220        NSLog(@"GlimmerBlockerProxy not running. Starts it.");
221        [_comm startServer];
222        for (int i = 0; i < 10; i++) {
223            @try {
224                NSError* error = NULL;
225                NSString* s = [_comm curl:@"/test-running" withErrorRef:&error];
226                if (!error && s) {
227                    NSLog(@"Server now running [%d]: %@", i, s);
228                    break;
229                }
230                NSLog(@"GB server not started yet [%@]: %@", error, s);
231            }
232            @catch (NSException* ex) {
233                NSLog(@"GB server not started yet [%d]: %@", i, ex);
234            }
235            sleep(1); // ugly, but shouldn't take (too) long.
236        }
237    }
238    [_filterTabController postInit];
239    if (!_hasAddedWindowNotifications) {
240        _hasAddedWindowNotifications = YES;
241        DebugNSLog(@"adds WindowNotifications: %@", NSApp);
242        NSNotificationCenter* nc = [[NSWorkspace sharedWorkspace] notificationCenter];
243        [nc addObserver:self
244               selector:@selector(checkEditFilterNotification:)
245                   name:NSWindowDidBecomeKeyNotification
246                 object:[tabView window]];
247        [nc addObserver:self
248               selector:@selector(checkEditFilterNotification:)
249                   name:NSApplicationDidUnhideNotification
250                 object:NSApp];
251        [nc addObserver:self
252               selector:@selector(checkEditFilterNotification:)
253                   name:NSApplicationDidBecomeActiveNotification
254                 object:NSApp];
255    }
256    [self checkEditFilterNotification:NULL];
257    [self updateSelectedTabView];
258    [self startFileSystemEventListener];
259    [self checkReloadSubscriptionFilterData];
260}
261
262- (void)willUnselect
263{
264    if (_installationHelper)
265        return;
266    DebugNSLog(@"Our prefs panel is about to be un-selected.");
267    DebugNSLog(@"Our window: %@", [tabView window]);
268}
269
270- (void)didUnselect
271{
272    if (_installationHelper)
273        return;
274    DebugNSLog(@"Our prefs panel is now un-selected.");
275    DebugNSLog(@"Our window: %@", [tabView window]);
276    [self stopFileSystemEventListener];
277}
278
279+ (void)openWebsiteInBrowser
280{
281    [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"http://glimmerblocker.org"]];
282}
283
284// -------------------------------------------------------------------------------
285- (void)workspaceSessionDidBecomeActiveNotification:(NSNotification*)notification
286{
287    DebugNSLog(@"GB.workspaceSessionDidBecomeActiveNotification");
288    if (![self checkInstallation])
289        return;
290    [_prefs forcedReload];
291    [self checkEditFilterNotification:notification];
292    [self updateSelectedTabView];
293    if ([self isSelected])
294        [self startFileSystemEventListener];
295}
296
297- (void)workspaceSessionDidResignActiveNotification:(NSNotification*)notification
298{
299    DebugNSLog(@"GB.workspaceSessionDidResignActiveNotification");
300    [self stopFileSystemEventListener];
301    [self releaseFilterTabController];
302    [_history notifyBecameHidden];
303    [_suspects notifyBecameHidden];
304    [_prefs saveIfDirty:self];
305}
306
307static void fileSystemEventCallback(ConstFSEventStreamRef streamRef, void *clientCallBackInfo, size_t numEvents,
308                                    void *eventPaths, const FSEventStreamEventFlags eventFlags[], const FSEventStreamEventId eventIds[])
309{
310#ifdef DEBUG_FSEVENTS
311    fprintf(stderr, "Got %d filesystem changes:  0x%lx\n", (int)numEvents, (long)clientCallBackInfo);
312    char **paths = eventPaths;
313    for (size_t i=0; i<numEvents; i++) {
314        fprintf(stderr, "%ld = %s\n", eventFlags[i], paths[i]);
315    }
316#endif
317    GlimmerBlockerPrefsPane* pp = (GlimmerBlockerPrefsPane*)clientCallBackInfo;
318    [pp checkReloadSubscriptionFilterData];
319}
320
321- (void)stopFileSystemEventListener
322{
323    if (!_fsEventStreamRef)
324        return;
325#ifdef DEBUG_FSEVENTS
326    DebugNSLog(@"Disposes FSEvent listener");
327#endif
328    FSEventStreamStop(_fsEventStreamRef);
329    FSEventStreamInvalidate(_fsEventStreamRef);
330    FSEventStreamRelease(_fsEventStreamRef);
331    _fsEventStreamRef = NULL;
332}
333
334- (void)startFileSystemEventListener
335{
336    if (_fsEventStreamRef)
337        return;
338#ifdef DEBUG_FSEVENTS
339    DebugNSLog(@"Creates FSEvent listener: 0x%lx", self);
340#endif
341    CFStringRef mypath = CFSTR("/Library/GlimmerBlocker/Filter subscriptions");
342    CFArrayRef pathsToWatch = CFArrayCreate(NULL, (const void **)&mypath, 1, NULL);
343    FSEventStreamContext context = {0, self, NULL, NULL, NULL};
344    CFAbsoluteTime latency = 0.5;
345    _fsEventStreamRef = FSEventStreamCreate(NULL, fileSystemEventCallback, &context, pathsToWatch, kFSEventStreamEventIdSinceNow,
346                                            latency, kFSEventStreamCreateFlagNone);
347    CFRelease(pathsToWatch);
348    if (!_fsEventStreamRef) {
349        NSLog(@"Failed creating FSEventStream");
350        return;
351    }
352    CFRunLoopRef mainLoop = [[NSRunLoop currentRunLoop] getCFRunLoop];
353    FSEventStreamScheduleWithRunLoop(_fsEventStreamRef, mainLoop, kCFRunLoopDefaultMode);
354    FSEventStreamStart(_fsEventStreamRef);
355}
356
357// -------------------------------------------------------------------------------
358- (void)checkEditFilterNotification:(NSNotification*)notification
359{
360    DebugNSLog(@"checkEditFilterNotification: %@", [notification name]);
361    NSString* path = [NSString stringWithFormat:@"/tmp/gb-edit-filter-%d", getuid()];
362    NSFileManager* fm = [NSFileManager defaultManager];
363    if (![fm isReadableFileAtPath:path])
364        return;
365    NSData* data = [NSData dataWithContentsOfURL:[NSURL fileURLWithPath:path]];
366    if (!data)
367        return;
368    NSInteger filterId = [[[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease] intValue];
369    DebugNSLog(@"filter-id from gb-edit-filter: %ld", (long)filterId);
370    if ([[tabView window] attachedSheet]) {
371        DebugNSLog(@"Can't show filter: has sheet open.");
372        return;
373    }
374    [tabView selectTabViewItem:filtersTabViewItem];
375    [self updateSelectedTabView];
376    DebugNSLog(@" updates.");
377    Filter* filter = [_filterTabController selectFilterById:filterId];
378    if (!filter)
379        DebugNSLog(@"Unknown filter.");
380    [fm removeItemAtPath:path error:NULL];
381    [filter startEditingSubscription:YES];
382}
383
384// -------------------------------------------------------------------------------
385- (void)updateSelectedTabView
386{
387    NSString *fane = [[tabView selectedTabViewItem] identifier];
388    if (![fane isEqual:@"suspects"])
389        [_suspects notifyBecameHidden];
390    if (![fane isEqual:@"history"])
391        [_history notifyBecameHidden];
392    //
393    if ([fane isEqual:@"setup"])
394        [self updateTabViewSetup];
395    else if ([fane isEqual:@"filters"])
396        [self updateTabViewFilters];
397    else if ([fane isEqual:@"history"])
398        [self updateTabViewHistory];
399    else if ([fane isEqual:@"suspects"])
400        [self updateTabViewSuspects];
401    else if ([fane isEqual:@"network"])
402        [self updateTabViewNetwork];
403    else if ([fane isEqual:@"developer"])
404        [self updateTabViewDeveloper];
405    else
406        NSLog(@"unknown tabViewItem identifier = '%@'", fane);
407}
408
409- (void)notifyUpdatedAuthorizationState
410{
411    [_prefs notifyUpdatedAuthorizationState]; // must be first as it updates cached state.
412    [self updateSelectedTabView];
413    [_filterTabController notifyUpdatedAuthorizationState];
414    [_history notifyUpdatedAuthorizationState];
415    [_suspects notifyUpdatedAuthorizationState];
416}
417
418#pragma mark ------------------- settings pane
419
420- (void)updateTabViewSetup
421{
422    BOOL hasAuth = [_prefs hasGlimmerAuth];
423    [setupEnableGlimmerBlockerCB setState:[_prefs boolForXPath:@"proxy-server/@enabled"]];
424    [setupEnableGlimmerBlockerCB setEnabled:hasAuth];
425    [self updateAutoUpdateViews];
426}
427
428- (IBAction)setupEnableGlimmerBlockerAction:(id)sender
429{
430    BOOL enable = [setupEnableGlimmerBlockerCB state];
431    if (enable && ![_prefs save]) {
432        NSLog(@"Can't save prefs, thus can't start proxy");
433        [setupEnableGlimmerBlockerCB setState:NO];
434        return;
435    }
436    [_prefs setBool:enable forXPath:@"proxy-server/@enabled"];
437    [_prefs markAsDirty];
438    if (enable)
439        [_comm startServer];
440    else
441        [_comm stopServer];
442    [self updateNetworkSettingsIfWanted];
443}
444
445- (IBAction)setupConfirmNetworkSettingsAction:(id)sender
446{
447    BOOL allow = [sender state];
448    [_prefs setBool:allow forXPath:@"network-settings/@confirm"];
449    [_prefs markAsDirty];
450}
451
452- (IBAction)setupSyncNetworkSettingsNowAction:(id)sender
453{
454    [self updateNetworkSettingsWithFeedbackIfUnchanged:YES];
455}
456
457//=================================================================================
458- (IBAction)setupUpdatesAutoAction:(id)sender
459{
460    [_prefs setBool:[sender state] forXPath:@"updater/@check-in-background"];
461    [_prefs markAsDirty];
462    [_comm updateUpdaterLaunchd];
463}
464
465- (void)postUpdateCheck
466{
467    [self performSelector:@selector(updateAutoUpdateViews) withObject:NULL afterDelay:5.0];
468    [self performSelector:@selector(updateAutoUpdateViews) withObject:NULL afterDelay:15.0];
469    [self performSelector:@selector(updateAutoUpdateViews) withObject:NULL afterDelay:25.0];
470    [self performSelector:@selector(updateAutoUpdateViews) withObject:NULL afterDelay:60.0];
471}
472
473- (IBAction)setupUpdatesManualAction:(id)sender
474{
475    [_comm launchUpdaterApp];
476    // keep the button visually pressed until the helper had a chance to launch,
477    // so it doesn't seem like nothing happends.
478    sleep(2);
479    [self postUpdateCheck];
480}
481
482- (IBAction)setupUpdatesBetaAction:(id)sender
483{
484    [_prefs setBool:[sender state] forXPath:@"updater/@use-beta-versions"];
485    [_prefs markAsDirty];
486    [_comm updateUpdaterLaunchd];
487}
488
489- (void)updateAutoUpdateViews
490{
491    //DebugNSLog(@"updateAutoUpdateViews...");
492    BOOL hasAuth = [_prefs hasGlimmerAuth];
493    [self setCheckbox:setupUpdatesAutoCB withXPath:@"updater/@check-in-background" withEnabled:hasAuth];
494    [self setCheckbox:developerUpdatesBetaCB withXPath:@"updater/@use-beta-versions" withEnabled:hasAuth];
495    [setupUpdatesNowBtn setEnabled:hasAuth];
496    [setupUpdatesNowBtn2 setEnabled:hasAuth];
497    //
498    NSFileManager* fm = [NSFileManager defaultManager];
499    NSDictionary* dict = [fm attributesOfItemAtPath:LAST_UPDATE_CHECK_MARKER_PATH error:NULL];
500    NSDate* lastCheckDate = [dict objectForKey:NSFileModificationDate];
501    NSString* s;
502    if (lastCheckDate) {
503        NSDateFormatter* formatter = [[[NSDateFormatter alloc] init] autorelease];
504        [formatter setDateStyle:NSDateFormatterShortStyle];
505        [formatter setTimeStyle:NSDateFormatterShortStyle];
506        s = [NSString stringWithFormat:@"Last check: %@", [formatter stringFromDate:lastCheckDate]];
507    } else {
508        s = @"Last check: none";
509        [developerUpdatesLastDateTextField setStringValue:s];
510    }
511    NSString* v = [NSString stringWithFormat:@"Installed version: %@", _glimmerBlockerVersionText];
512    s = [NSString stringWithFormat:@"%@\n%@", v, s];
513    [setupUpdatesLastDateTextField setStringValue:s];
514    [developerUpdatesLastDateTextField setStringValue:s];
515}
516
517#pragma mark ------------------- filters
518
519- (void)updateTabViewFilters
520{
521    BOOL running = [_comm isServerRunning];
522    [filtersMissingServerLabel setHidden:running];
523    [filtersContainerView setHidden:!running];
524    [_filterTabController postInit];
525    if (![mainPrefsPaneView window]) // not fully initialized yet - can't display alerts.
526        return;
527    if (!running)
528        return;
529    [_filterTabController updateTabViewWithForceUpdate:NO];
530}
531
532- (void)releaseFilterTabController
533{
534    [_filterTabController release];
535    _filterTabController = NULL;
536}
537
538- (void)checkReloadSubscriptionFilterData
539{
540    if (_installationHelper)
541        return;
542    if (![_comm isServerRunning])
543        return; // can't reload filters when the server isn't running.
544    [_filterTabController postInit];
545    for (Filter* filter in [_filterTabController filterArray])
546        [filter checkReloadSubscriptionFilterData];
547}
548
549#pragma mark ------------------- history
550
551- (void)updateTabViewHistory
552{
553    DebugNSLog(@"updateTabViewHistory");
554    BOOL running = [_comm isServerRunning];
555    [historyMissingServer setHidden:running];
556    [historyContainerView setHidden:!running];
557    if (![mainPrefsPaneView window]) // not fully initialized yet - can't display alerts.
558        return;
559    if (!running)
560        return;
561    [_history notifyBecameVisible];
562}
563
564#pragma mark ------------------- suspects
565
566- (void)updateTabViewSuspects
567{
568    DebugNSLog(@"updateTabViewSuspects");
569    BOOL running = [_comm isServerRunning];
570    [suspectsMissingServer setHidden:running];
571    [suspectsContainerView setHidden:!running];
572    if (![mainPrefsPaneView window]) // not fully initialized yet - can't display alerts.
573        return;
574    if (!running)
575        return;
576    [_suspects notifyBecameVisible];
577}
578
579#pragma mark ------------------- network
580
581- (void)updateTabViewNetwork
582{
583    [self updateAutoUpdateViews];
584    BOOL hasAuth = [_prefs hasGlimmerAuth];
585    [networkPortNumberTextField setStringValue:[NSString stringWithFormat:@"%d", [_comm getPortNumber]]];
586    [networkPortNumberTextField setEnabled:hasAuth];
587    [networkAllowOtherComputersCB setState:[_prefs boolForXPath:@"proxy-server/@listen-wildcard"] ? 1 : 0];
588    [networkAllowOtherComputersCB setEnabled:hasAuth];
589    [networkConfirmNetworkSettingsCB setState:[_prefs boolForXPath:@"network-settings/@confirm"]];
590    [networkConfirmNetworkSettingsCB setEnabled:hasAuth];
591    [networkSyncNetworkSettingsNowBtn setEnabled:hasAuth];
592    //
593    NSString* host = [_prefs stringForXPath:@"network-settings/@site-proxy-host"];
594    [networkSiteProxyHostTextField setStringValue:(host ? host : @"")];
595    NSString* port = [_prefs stringForXPath:@"network-settings/@site-proxy-port"];
596    [networkSiteProxyPortTextField setStringValue:(port ? port : @"")];
597    ProxyType type = (ProxyType)[proxyTypeEnum indexFromXPath:@"network-settings/@site-proxy" withPrefs:_prefs];
598    [networkSiteProxyMatrix selectCellAtRow:type column:0];
599    [networkSiteProxyMatrix setEnabled:hasAuth];
600    //DebugNSLog(@"proxy: %d   %@:%@", [_prefs boolForXPath:@"network-settings/@site-proxy"], host, port);
601    //
602    [self networkSiteProxyAction:self];
603}
604
605- (IBAction)networkPortNumberAction:(id)sender
606{
607    NSString *s = [networkPortNumberTextField stringValue];
608    DebugNSLog(@"networkPortNumberAction: %@", s);
609    NSUInteger i;
610    int port = 0;
611    for (i = 0; i < [s length]; i++) {
612        unichar ch = [s characterAtIndex:i];
613        if (ch < '0' || ch > '9') {
614            NSLog(@"Invalid port number: %@", s); // todo: error alert
615            return;
616        }
617        port = port * 10 + ch - '0';
618    }
619    int minPort = 1024;
620    int maxPort = 65535;
621    if (port < minPort || port > maxPort) {
622        NSString* title = [NSString stringWithFormat:@"Invalid port number: %d", port];
623        NSString* help = [NSString stringWithFormat:@"Must be between %d and %d", minPort, maxPort];
624        [_alertHelper prepareCriticalAlertWithTitle:title
625                                withInformativeText:help];
626        [_alertHelper showPreparedAlert];
627        [networkPortNumberTextField setStringValue:[NSString stringWithFormat:@"%d", [_comm getPortNumber]]];
628        return;
629    }
630    int oldPort = [_comm getPortNumber];
631    if (oldPort == port)
632        return;
633    DebugNSLog(@"Port number: %d -> %d", oldPort, port);
634    [_prefs setInt:port forXPath:@"proxy-server/@listen-port"];
635    [_prefs markAsDirty];
636    if (![_comm isServerRunning])
637        return;
638    [_comm saveDirtyPrefsAndNotify];
639    [self updateNetworkSettingsIfWanted];
640}
641
642- (IBAction)networkAllowOtherComputersAction:(id)sender
643{
644    BOOL allow = [sender state];
645    [_prefs setBool:allow forXPath:@"proxy-server/@listen-wildcard"];
646    [_prefs markAsDirty];
647    [_comm saveDirtyPrefsAndNotify];
648}
649
650- (void)notifyProxyScriptEditorCommitted:(ProxyScriptEditor*)editor
651{
652    [self networkSiteProxyAction:self];
653}
654
655- (IBAction)networkSiteProxyScriptEditAction:(id)sender
656{
657    [[[ProxyScriptEditor alloc] initWithServerCommunication:_comm
658                                                 withBundle:[self bundle]
659                                               withDelegate:self] autorelease];
660}
661
662- (BOOL)validateStaticProxyHost:(NSString*)host withPort:(NSString*)port
663{
664    NSString* msg = NULL;
665    if ([mainPrefsPaneView window] && [_comm isServerRunning]) {
666        // can only validate when _comm is fully initialized
667        NSString* pathArgs = [NSString stringWithFormat:@"/site-proxy/validate?host=%@&port=%@",
668                              [GBDebug percentEscapeUrlParameterValue:host],
669                              [GBDebug percentEscapeUrlParameterValue:port]];
670        XmlDoc* doc = [_comm curlToXml:pathArgs withActionDescription:NULL];
671        if (doc)
672            msg = [doc stringForXPath:@"@proxy-error"];
673        else
674            msg = @"Problem validating proxy hostname.";
675    }
676    [networkSiteProxyErrorTextField setStringValue:(msg ? msg : @"")];
677    return ![msg length];
678}
679
680- (BOOL)validateProxyJavascript
681{
682    NSString* msg = NULL;
683    if ([mainPrefsPaneView window] && [_comm isServerRunning]) {
684        // can only validate when _comm is fully initialized
685        NSString* js = [_prefs stringForXPath:@"network-settings/proxy-script"];
686        NSString* pathArgs = [NSString stringWithFormat:@"/site-proxy/validate?javascript=%@",
687                              [GBDebug percentEscapeUrlParameterValue:js]];
688        XmlDoc* doc = [_comm curlToXml:pathArgs withActionDescription:NULL];
689        if (doc)
690            msg = [doc stringForXPath:@"@proxy-error"];
691        else
692            msg = @"Problem validating proxy javascript.";
693    }
694    [networkSiteProxyErrorTextField setStringValue:(msg ? msg : @"")];
695    return ![msg length];
696}
697
698- (IBAction)networkSiteProxyAction:(id)sender;
699{
700    ProxyType oldType = (ProxyType)[proxyTypeEnum indexFromXPath:@"network-settings/@site-proxy" withPrefs:_prefs];
701    NSString* oldHost = [_prefs stringForXPath:@"network-settings/@site-proxy-host"];
702    NSString* oldPort = [_prefs stringForXPath:@"network-settings/@site-proxy-port"];
703    NSString* host = [[networkSiteProxyHostTextField stringValue] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
704    NSString* port = [[networkSiteProxyPortTextField stringValue] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
705    BOOL hasAuth = [_prefs hasGlimmerAuth];
706    ProxyType type = (ProxyType)[networkSiteProxyMatrix selectedRow];
707    //DebugNSLog(@"networkSiteProxyAction: type = %d,  static = %@:%@", type, host, port);
708    [networkSiteProxyScriptEditBtn setEnabled:(type == JavascriptProxy && hasAuth)];
709    NSColor* c = (type == StaticProxy) ? [NSColor blackColor] : [NSColor grayColor];
710    [networkSiteProxyHostLabel setTextColor:c];
711    [networkSiteProxyPortLabel setTextColor:c];
712    [networkSiteProxyHostTextField setEnabled:(type == StaticProxy && hasAuth)];
713    [networkSiteProxyPortTextField setEnabled:(type == StaticProxy && hasAuth)];
714    if (type == StaticProxy && sender == networkSiteProxyMatrix) {
715        [[mainPrefsPaneView window] performSelectorOnMainThread:@selector(makeFirstResponder:)
716                                                     withObject:networkSiteProxyHostTextField
717                                                  waitUntilDone:NO];
718    }
719    if (type == StaticProxy && ![self validateStaticProxyHost:host withPort:port])
720        return;
721    if (type == JavascriptProxy && ![self validateProxyJavascript])
722        return;
723    [networkSiteProxyErrorTextField setStringValue:@""];
724    BOOL changed = (type != oldType);
725    if (type == StaticProxy && (![host isEqual:oldHost] || ![port isEqual:oldPort])) {
726        [_prefs setString:host forXPath:@"network-settings/@site-proxy-host"];
727        [_prefs setString:port forXPath:@"network-settings/@site-proxy-port"];
728        changed = true;
729        DebugNSLog(@"Updated static site-wide proxy:  %@:%@  ->  %@:%@", oldHost, oldPort, host, port);
730    }
731    if (!changed)
732        return;
733    [_prefs setString:[proxyTypeEnum nameFromIndex:type] forXPath:@"network-settings/@site-proxy"];
734    [_prefs markAsDirty];
735    [_comm saveDirtyPrefsAndNotify];
736}
737
738
739#pragma mark ------------------- developer
740
741- (void)updateTabViewDeveloper
742{
743    [self updateAutoUpdateViews];
744    BOOL hasAuth = [_prefs hasGlimmerAuth];
745    [developerDumpHttpClientCacheBtn setEnabled:hasAuth];
746    [self setCheckbox:developerDebugFlagCB withXPath:@"debug/@enabled" withEnabled:hasAuth];
747    BOOL enabled = hasAuth && [_prefs boolForXPath:@"debug/@enabled"];
748    [self setCheckbox:developerDebugVerboseCB withXPath:@"debug/@verbose" withEnabled:enabled];
749    [self setCheckbox:developerDebugHttpClientCB withXPath:@"debug/@http-client" withEnabled:enabled];
750    [self setCheckbox:developerDebugHttpServerCB withXPath:@"debug/@http-server" withEnabled:enabled];
751    [self setCheckbox:developerDebugNetworkConfigCB withXPath:@"debug/@network-config" withEnabled:enabled];
752    [self setCheckbox:developerDebugFiltersCB withXPath:@"debug/@filters" withEnabled:enabled];
753    //
754    [self setCheckbox:developerTransformErrorHtmlCB withXPath:@"debug/@transform-html-box" withEnabled:hasAuth];
755    [self setCheckbox:developerCheckerboardPatternCB withXPath:@"debug/@checkerboard-pattern" withEnabled:hasAuth];
756}
757
758- (IBAction)developerDebugFlagsAction:(id)sender
759{
760    [_prefs setBool:[developerDebugFlagCB state] forXPath:@"debug/@enabled"];
761    [_prefs setBool:[developerDebugVerboseCB state] forXPath:@"debug/@verbose"];
762    [_prefs setBool:[developerDebugHttpClientCB state] forXPath:@"debug/@http-client"];
763    [_prefs setBool:[developerDebugHttpServerCB state] forXPath:@"debug/@http-server"];
764    [_prefs setBool:[developerDebugNetworkConfigCB state] forXPath:@"debug/@network-config"];
765    [_prefs setBool:[developerDebugFiltersCB state] forXPath:@"debug/@filters"];
766    [_prefs setBool:[developerTransformErrorHtmlCB state] forXPath:@"debug/@transform-html-box"];
767    [_prefs setBool:[developerCheckerboardPatternCB state] forXPath:@"debug/@checkerboard-pattern"];
768    [self updateTabViewDeveloper];
769    [_prefs markAsDirty];
770    [_comm saveDirtyPrefsAndNotify];
771}
772
773- (IBAction)developerShowLogAction:(id)sender
774{
775    [_comm openServerLogInConsole];
776}
777
778- (IBAction)developerDumpHttpClientCacheAction:(id)sender
779{
780    [developerDumpHttpCacheMessageTextView setHidden:NO];
781    [_comm curl:@"!/http-cache/dump"];
782}
783
784#pragma mark ------------------- utils
785
786- (NSString*)controlAlternatingRowBackgroundColorsAsUrlParam
787{
788    NSArray* colors = [NSColor controlAlternatingRowBackgroundColors];
789    NSMutableString* sb = [NSMutableString stringWithCapacity:40];
790    NSColorSpace* sRGB = [NSColorSpace sRGBColorSpace];
791    for (NSUInteger i = 0; i < [colors count]; i++) {
792        if ([sb length])
793            [sb appendString:@";"];
794        else
795            [sb appendString:@"arbc="];
796        CGFloat r, g, b, a;
797        NSColor* clr = [[colors objectAtIndex:i] colorUsingColorSpace:sRGB];
798        // why can't I get the rgb when the color is converted to sRGB?
799        // *** -getRed:green:blue:alpha: not defined for the
800        //              NSColor NSCustomColorSpace sRGB IEC61966-2.1 colorspace 1 1 1 1;
801        //              need to first convert colorspace.
802        [clr getRed:&r green:&g blue:&b alpha:&a];
803        [sb appendFormat:@"rgba(%f,%f,%f,%f)", r, g, b, a];
804    }
805    return sb;
806}
807
808#pragma mark ------------------- utils
809
810- (void)updateNetworkSettingsIfWanted
811{
812    [self updateNetworkSettingsWithFeedbackIfUnchanged:NO];
813}
814
815- (void)updateNetworkSettingsWithFeedbackIfUnchanged:(BOOL)feedbackIfUnchanged
816{
817    BOOL confirm = [_prefs boolForXPath:@"network-settings/@confirm"];
818    NetworkSettingsUpdaterUI* updater = [[[NetworkSettingsUpdaterUI alloc]
819                                          initWithServerCommunication:_comm
820                                          withWantEnable:[_comm isServerRunning]] autorelease];
821    [updater updateSettingsWithConfirm:confirm withFeedbackIfUnchanged:feedbackIfUnchanged];
822}
823
824- (void)updateAuthRightsDefinition
825{
826    if (_hasUpdatedAuthRightsDefinition)
827        return;
828    SFAuthorization* auth = [authView authorization];
829    if (!auth) {
830        DebugNSLog(@"updateAuthDefinition: Has no SFAuthorization");
831        return;
832    }
833    AuthorizationRef authRef = [auth authorizationRef];
834    if (!authRef) {
835        DebugNSLog(@"updateAuthDefinition: [auth authorizationRef] returned null");
836        return;
837    }
838    OSStatus err = AuthorizationRightGet(glimmerAuthString, NULL);
839    if (err != errAuthorizationDenied) {
840        _hasUpdatedAuthRightsDefinition = YES;
841        //DebugNSLog(@"Has authright definition.");
842        //err = AuthorizationRightRemove(authRef, glimmerAuthString);
843        //DebugNSLog(@"AuthorizationRightRemove returned %d", err);
844        return;
845    }
846    DebugNSLog(@"Missing authright definition.");
847    CFURLRef cfBundleUrl = (CFURLRef)[NSURL fileURLWithPath:[[self bundle] bundlePath]];
848    CFBundleRef cfBundle = CFBundleCreate(kCFAllocatorDefault, cfBundleUrl);
849    NSDictionary* rightDefinition = [NSDictionary dictionaryWithObjectsAndKeys:
850                                     @"user", @"class",
851                                     @"Just a comment", @"comment",
852                                     @"admin", @"group",
853                                     [NSNumber numberWithBool:YES], @"shared",
854                                     [NSNumber numberWithInt:365 * 24 * 60 * 60], @"timeout",
855                                     nil];
856    err = AuthorizationRightSet(authRef,
857                                glimmerAuthString,
858                                rightDefinition,
859                                CFSTR("GlimmerBlocker authentication"),
860                                cfBundle,
861                                NULL); // localeTableName
862    DebugNSLog(@"AuthorizationRightSet returned: %d", err);
863    CFRelease(cfBundle);
864    _hasUpdatedAuthRightsDefinition = !err;
865}
866
867- (void)setCheckbox:(NSButton*)button withXPath:(NSString*)xpath withEnabled:(BOOL)enabled
868{
869    BOOL state = [_prefs boolForXPath:xpath];
870    [button setState:state ? NSOnState : NSOffState];
871    [button setEnabled:enabled];
872}
873
874@end
875
876@implementation GlimmerBlockerPrefsPane(NSTabViewDelegate)
877
878- (void)tabView:(NSTabView *)tabView didSelectTabViewItem:(NSTabViewItem *)tabViewItem
879{
880    if (_installationHelper)
881        return;
882    [self updateSelectedTabView];
883    [_prefs setString:[tabViewItem identifier] forXPath:@"iu/@selected-tab"];
884    if ([_prefs hasGlimmerAuth])
885        [_prefs markAsDirty];
886}
887
888@end
889
890@implementation GlimmerBlockerPrefsPane(NSExceptionHandlerDelegate)
891
892- (BOOL)exceptionHandler:(NSExceptionHandler *)sender
893   shouldHandleException:(NSException *)exception
894                    mask:(NSUInteger)aMask;
895{
896    // http://www.mactech.com/articles/mactech/Vol.19/19.12/CocoaDebug/
897    NSLog(@"shouldHandleException '%@', reason: %@", [exception name], [exception reason]);
898    [GBDebug debugDumpExceptionStackframe:exception];
899    return YES;
900}
901
902- (BOOL)shouldDisplayException:(NSException *)exception;
903{
904    NSString* name = [exception name];
905    NSString* reason = [exception reason];
906    NSLog(@"SHOULD DISPLAY exception '%@', reason: %@", name, reason);
907    return ![name isEqualToString:@"NSImageCacheException"]
908    && ![name isEqualToString:@"GIFReadingException"]
909    && ![name isEqualToString:@"NSRTFException"]
910    && !([name isEqualToString:@"NSInternalInconsistencyException"] && [reason hasPrefix:@"lockFocus"]);
911}
912@end
913
914
915@implementation GlimmerBlockerPrefsPane(SFAuthorizationViewDelegate)
916
917- (void)authorizationViewDidAuthorize:(SFAuthorizationView *)view
918{
919    DebugNSLog(@"GlimmerBlocker got authorizationViewDidAuthorize.");
920    if (_installationHelper)
921        return;
922    [self updateAuthRightsDefinition];
923    [self notifyUpdatedAuthorizationState];
924    if (![_prefs hasGlimmerAuth]) {
925        DebugNSLog(@"authorizationViewDidAuthorize: auth disappeared");
926        return;
927    }
928}
929
930- (void)authorizationViewDidDeauthorize:(SFAuthorizationView *)view
931{
932    DebugNSLog(@"GlimmerBlocker got authorizationViewDidDeauthorize.");
933    if (_installationHelper)
934        return;
935    [self notifyUpdatedAuthorizationState];
936}
937
938@end
Note: See TracBrowser for help on using the repository browser.