1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/tunblick/MenuController.m Wed Jul 29 11:23:17 2009 +0200 1.3 @@ -0,0 +1,1077 @@ 1.4 +/* 1.5 + * Copyright (c) 2004 Angelo Laub 1.6 + * Contributions by Dirk Theisen <dirk@objectpark.org>, 1.7 + * Jens Ohlig, 1.8 + * Waldemar Brodkorb 1.9 + * 1.10 + * This program is free software; you can redistribute it and/or modify 1.11 + * it under the terms of the GNU General Public License version 2 1.12 + * as published by the Free Software Foundation. 1.13 + * 1.14 + * This program is distributed in the hope that it will be useful, 1.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 1.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1.17 + * GNU General Public License for more details. 1.18 + * 1.19 + * You should have received a copy of the GNU General Public License 1.20 + * along with this program (see the file COPYING included with this 1.21 + * distribution); if not, write to the Free Software Foundation, Inc., 1.22 + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 1.23 + */ 1.24 + 1.25 + 1.26 +#import "MenuController.h" 1.27 +#import "NSApplication+NetworkNotifications.h" 1.28 +#import "helper.h" 1.29 + 1.30 + 1.31 +#define NSAppKitVersionNumber10_0 577 1.32 +#define NSAppKitVersionNumber10_1 620 1.33 +#define NSAppKitVersionNumber10_2 663 1.34 +#define NSAppKitVersionNumber10_3 743 1.35 + 1.36 + 1.37 + 1.38 +BOOL systemIsTigerOrNewer() 1.39 +{ 1.40 + return (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_3) ; 1.41 +} 1.42 + 1.43 +@interface NSStatusBar (NSStatusBar_Private) 1.44 +- (id)_statusItemWithLength:(float)l withPriority:(int)p; 1.45 +@end 1.46 + 1.47 + 1.48 +@implementation MenuController 1.49 + 1.50 +- (void) createStatusItem 1.51 +{ 1.52 + NSStatusBar *bar = [NSStatusBar systemStatusBar]; 1.53 + int priority = INT32_MAX; 1.54 + if (systemIsTigerOrNewer()) { 1.55 + priority = MIN(priority, 2147483646); // found by experimenting - dirk 1.56 + } 1.57 + 1.58 + if (!theItem) { 1.59 + theItem = [[bar _statusItemWithLength: NSVariableStatusItemLength withPriority: priority] retain]; 1.60 + //theItem = [[bar _statusItemWithLength: NSVariableStatusItemLength withPriority: 0] retain]; 1.61 + } 1.62 + // Dirk: For Tiger and up, re-insert item to place it correctly. 1.63 + if ([bar respondsToSelector: @selector(_insertStatusItem:withPriority:)]) { 1.64 + [bar removeStatusItem: theItem]; 1.65 + [bar _insertStatusItem: theItem withPriority: priority]; 1.66 + } 1.67 +} 1.68 + 1.69 +-(id) init 1.70 +{ 1.71 + if (self = [super init]) { 1.72 + [self dmgCheck]; 1.73 + 1.74 + errorImage = [[NSImage imageNamed: @"error.tif"] retain]; 1.75 + mainImage = [[NSImage imageNamed: @"00_closed.tif"] retain]; 1.76 + connectedImage = [[NSImage imageNamed: @"connected.png"] retain]; 1.77 + 1.78 + 1.79 + transitionalImage1 = [[NSImage imageNamed: @"01.tif"] retain]; 1.80 + transitionalImage2 = [[NSImage imageNamed: @"02.tif"] retain]; 1.81 + transitionalImage3 = [[NSImage imageNamed: @"03.tif"] retain]; 1.82 + [NSApp setDelegate:self]; 1.83 + 1.84 + myVPNConnectionDictionary = [[NSMutableDictionary alloc] init]; 1.85 + myVPNConnectionArray = [[[NSMutableArray alloc] init] retain]; 1.86 + userDefaults = [[NSMutableDictionary alloc] init]; 1.87 + 1.88 + connectionArray = [[[NSMutableArray alloc] init] retain]; 1.89 + appDefaults = [NSUserDefaults standardUserDefaults]; 1.90 + [appDefaults registerDefaults:userDefaults]; 1.91 + 1.92 + 1.93 + detailsItem = [[NSMenuItem alloc] init]; 1.94 + [detailsItem setTitle: @"Details..."]; 1.95 + [detailsItem setTarget: self]; 1.96 + [detailsItem setAction: @selector(openLogWindow:)]; 1.97 + 1.98 + quitItem = [[NSMenuItem alloc] init]; 1.99 + [quitItem setTitle: @"Quit"]; 1.100 + [quitItem setTarget: self]; 1.101 + [quitItem setAction: @selector(quit:)]; 1.102 + 1.103 + [self createStatusItem]; 1.104 + 1.105 + [self updateMenu]; 1.106 + [self setState: @"EXITING"]; // synonym for "Disconnected" 1.107 + 1.108 + [[NSNotificationCenter defaultCenter] addObserver: self 1.109 + selector: @selector(logNeedsScrolling:) 1.110 + name: @"LogDidChange" 1.111 + object: nil]; 1.112 + 1.113 + // In case the systemUIServer restarts, we observed this notification. 1.114 + // We use it to prevent to end up with a statusItem right of Spotlight: 1.115 + [[NSDistributedNotificationCenter defaultCenter] addObserver: self 1.116 + selector: @selector(menuExtrasWereAdded:) 1.117 + name: @"com.apple.menuextra.added" 1.118 + object: nil]; 1.119 + [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver: self 1.120 + selector: @selector(willGoToSleep) 1.121 + name: @"NSWorkspaceWillSleepNotification" 1.122 + object:nil]; 1.123 + 1.124 + [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver: self 1.125 + selector: @selector(wokeUpFromSleep) 1.126 + name: @"NSWorkspaceDidWakeNotification" 1.127 + object:nil]; 1.128 + 1.129 + NSString* vpnDirectory = [NSHomeDirectory() stringByAppendingPathComponent:@"Library/openvpn/"]; 1.130 + 1.131 + UKKQueue* myQueue = [UKKQueue sharedQueue]; 1.132 + [myQueue addPathToQueue: vpnDirectory]; 1.133 + [myQueue setDelegate: self]; 1.134 + [myQueue setAlwaysNotify: YES]; 1.135 + 1.136 + [NSThread detachNewThreadSelector:@selector(moveAllWindowsToForegroundThread) toTarget:self withObject:nil]; 1.137 + 1.138 + updater = [[SUUpdater alloc] init]; 1.139 + 1.140 + } 1.141 + return self; 1.142 +} 1.143 + 1.144 +-(void)moveAllWindowsToForegroundThread { 1.145 + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 1.146 + sleep(3); 1.147 + [self moveAllWindowsToForeground]; 1.148 +// [NSTimer scheduledTimerWithTimeInterval: 1.0 target: self selector: @selector(moveAllWindowsToForeground) userInfo: nil repeats: YES]; 1.149 + [pool release]; 1.150 + 1.151 +} 1.152 + 1.153 +- (void) menuExtrasWereAdded: (NSNotification*) n 1.154 +{ 1.155 + [self createStatusItem]; 1.156 +} 1.157 + 1.158 + 1.159 + 1.160 +- (IBAction) quit: (id) sender 1.161 +{ 1.162 + // Remove us from the login items if terminates manually... 1.163 + [NSApp setAutoLaunchOnLogin: NO]; 1.164 + [NSApp terminate: sender]; 1.165 +} 1.166 + 1.167 + 1.168 + 1.169 +- (void) awakeFromNib 1.170 +{ 1.171 + [self createDefaultConfig]; 1.172 + [self initialiseAnim]; 1.173 +} 1.174 + 1.175 +- (void) initialiseAnim 1.176 +{ 1.177 + NSAnimationProgress progMarks[] = { 1.178 + 1.0/8.0, 2.0/8.0, 3.0/8.0, 4.0/8.0, 5.0/8.0, 6.0/8.0, 7.0/8.0, 8.0/8.0 1.179 + }; 1.180 + 1.181 + int i, count = 8; 1.182 + // theAnim is an NSAnimation instance variable 1.183 + theAnim = [[NSAnimation alloc] initWithDuration:2.0 1.184 + animationCurve:NSAnimationLinear]; 1.185 + [theAnim setFrameRate:8.0]; 1.186 + [theAnim setDelegate:self]; 1.187 + 1.188 + for (i=0; i<count; i++) 1.189 + [theAnim addProgressMark:progMarks[i]]; 1.190 + 1.191 + [theAnim setAnimationBlockingMode: NSAnimationNonblocking]; 1.192 +} 1.193 + 1.194 +-(void) updateMenu 1.195 +{ 1.196 + [theItem setHighlightMode:YES]; 1.197 + [theItem setMenu:nil]; 1.198 + [myVPNMenu dealloc]; myVPNMenu = nil; 1.199 + [[myVPNConnectionDictionary allValues] makeObjectsPerformSelector:@selector(disconnect:) withObject:self]; 1.200 + [myVPNConnectionDictionary removeAllObjects]; 1.201 + 1.202 + myVPNMenu = [[NSMenu alloc] init]; 1.203 + [myVPNMenu setDelegate:self]; 1.204 + 1.205 + [theItem setMenu: myVPNMenu]; 1.206 + 1.207 + statusMenuItem = [[NSMenuItem alloc] init]; 1.208 + [myVPNMenu addItem:statusMenuItem]; 1.209 + [myVPNMenu addItem:[NSMenuItem separatorItem]]; 1.210 + 1.211 + [myConfigArray release]; 1.212 + myConfigArray = [[[self getConfigs] sortedArrayUsingSelector:@selector(compare:)] retain]; 1.213 + [myConfigModDatesArray release]; 1.214 + myConfigModDatesArray = [[self getModDates:myConfigArray] retain]; 1.215 + 1.216 + NSEnumerator *m = [myConfigArray objectEnumerator]; 1.217 + NSString *configString; 1.218 + int i = 2; // we start at MenuItem #2 1.219 + 1.220 + while (configString = [m nextObject]) 1.221 + { 1.222 + NSMenuItem *connectionItem = [[[NSMenuItem alloc] init] autorelease]; 1.223 + 1.224 + // configure connection object: 1.225 + VPNConnection* myConnection = [[VPNConnection alloc] initWithConfig: configString]; // initialize VPN Connection with config 1.226 + [myConnection setState:@"EXITING"]; 1.227 + [myConnection setDelegate:self]; 1.228 + 1.229 + // handle autoconnection: 1.230 + NSString *autoConnectKey = [[myConnection configName] stringByAppendingString: @"autoConnect"]; 1.231 + if([[NSUserDefaults standardUserDefaults] boolForKey:autoConnectKey]) 1.232 + { 1.233 + if(![myConnection isConnected]) [myConnection connect:self]; 1.234 + } 1.235 + 1.236 + [myVPNConnectionDictionary setObject: myConnection forKey:configString]; 1.237 + 1.238 + // Note: The item's title will be set on demand in -validateMenuItem 1.239 + [connectionItem setTarget:myConnection]; 1.240 + [connectionItem setAction:@selector(toggle:)]; 1.241 + 1.242 + [myVPNMenu insertItem:connectionItem atIndex:i]; 1.243 + i++; 1.244 + } 1.245 + 1.246 + [myVPNMenu addItem: [NSMenuItem separatorItem]]; 1.247 + [myVPNMenu addItem: detailsItem]; 1.248 + [myVPNMenu addItem: quitItem]; 1.249 + 1.250 + // Localize all menu items: 1.251 + NSMenuItem *item; 1.252 + NSEnumerator *e = [[myVPNMenu itemArray] objectEnumerator]; 1.253 + 1.254 + while (item = [e nextObject]) 1.255 + { 1.256 + [item setTitle:local([item title])]; 1.257 + } 1.258 +} 1.259 + 1.260 +- (void)activateStatusMenu 1.261 +{ 1.262 + //[theItem retain]; 1.263 + [self updateUI]; 1.264 + 1.265 + // If any config files were changed/added/deleted, update the menu 1.266 + // We don't do it UNLESS files were changed/added/deleted because all connections are reset when the menu is updated. 1.267 + // activateStatusMenu is called whenever anything changes in the config directory, even the file-accessed date, 1.268 + // so a backup of the directory, for example, would cause disconnects if we always updated the menu. 1.269 + NSArray * curConfigsArray = [[self getConfigs] sortedArrayUsingSelector:@selector(compare:)]; 1.270 + NSArray * curModDatesArray = [self getModDates:curConfigsArray]; 1.271 + 1.272 + if ( ! ( [myConfigArray isEqualToArray:curConfigsArray] 1.273 + && [myConfigModDatesArray isEqualToArray:curModDatesArray] ) ) { 1.274 + NSLog(@"One or more configuration files were changed, added, or deleted. All connections will be closed.\n"); 1.275 + [self updateMenu]; 1.276 + } 1.277 +} 1.278 + 1.279 +- (void)connectionStateDidChange:(id)connection 1.280 +{ 1.281 + [self updateTabLabels]; 1.282 + if (connection == [self selectedConnection]) 1.283 + { 1.284 + [self validateLogButtons]; 1.285 + } 1.286 +} 1.287 + 1.288 +-(NSArray *)getConfigs { 1.289 + int i = 0; 1.290 + NSMutableArray *array = [[[NSMutableArray alloc] init] autorelease]; 1.291 + NSString *file; 1.292 + NSString *confDir = [NSHomeDirectory() stringByAppendingPathComponent: @"/Library/openvpn"]; 1.293 + NSDirectoryEnumerator *dirEnum = [[NSFileManager defaultManager] enumeratorAtPath: confDir]; 1.294 + while (file = [dirEnum nextObject]) { 1.295 + if ([[file pathExtension] isEqualToString: @"conf"] || [[file pathExtension] isEqualToString: @"ovpn"]) { 1.296 + [array insertObject:file atIndex:i]; 1.297 + //if(NSDebugEnabled) NSLog(@"Object: %@ atIndex: %d\n",file,i); 1.298 + i++; 1.299 + } 1.300 + } 1.301 + return array; 1.302 +} 1.303 + 1.304 +// Returns an array of modification date strings 1.305 +// Each entry in the array is the modification date of the file in the corresponding entry in fileArray 1.306 +-(NSArray *)getModDates:(NSArray *)fileArray { 1.307 + int i; 1.308 + NSMutableArray *array = [[[NSMutableArray alloc] init] autorelease]; 1.309 + NSString *file; 1.310 + NSString *cfgDirSlash = [NSHomeDirectory() stringByAppendingString: @"/Library/openvpn/"]; 1.311 + NSString *filePath; 1.312 + NSDate *modDate; 1.313 + NSString *modDateS; 1.314 + NSFileManager *fileManager = [NSFileManager defaultManager]; 1.315 + for (i=0; i<[fileArray count]; i++) { 1.316 + file = [fileArray objectAtIndex:i]; 1.317 + filePath = [cfgDirSlash stringByAppendingString:file]; 1.318 + modDate = [[fileManager fileAttributesAtPath:filePath traverseLink:YES] fileModificationDate]; 1.319 + if (modDate == nil) { 1.320 + modDateS = @""; 1.321 + } else if ( (modDateS = [modDate description]) == nil ) { 1.322 + modDateS = @""; 1.323 + } 1.324 + [array insertObject:modDateS atIndex:i]; 1.325 + } 1.326 + return array; 1.327 +} 1.328 + 1.329 +- (IBAction)validateLogButtons 1.330 +{ 1.331 + //NSLog(@"validating log buttons"); 1.332 + VPNConnection* connection = [self selectedConnection]; 1.333 + [connectButton setEnabled:[connection isDisconnected]]; 1.334 + [disconnectButton setEnabled:(![connection isDisconnected])]; 1.335 + [[NSUserDefaults standardUserDefaults] synchronize]; 1.336 + NSString *autoConnectKey = [[connection configName] stringByAppendingString:@"autoConnect"]; 1.337 + if([[NSUserDefaults standardUserDefaults] boolForKey:autoConnectKey]) { 1.338 + [autoLaunchCheckbox setState:NSOnState]; 1.339 + } else { 1.340 + [autoLaunchCheckbox setState:NSOffState]; 1.341 + } 1.342 + 1.343 + BOOL lol = useDNSStatus(connection); 1.344 + if(lol) { 1.345 + [useNameserverCheckbox setState:NSOnState]; 1.346 + } else { 1.347 + [useNameserverCheckbox setState:NSOffState]; 1.348 + } 1.349 +} 1.350 + 1.351 +-(void)updateTabLabels 1.352 +{ 1.353 + NSArray *keyArray = [[myVPNConnectionDictionary allKeys] sortedArrayUsingSelector: @selector(compare:)]; 1.354 + NSArray *myConnectionArray = [myVPNConnectionDictionary objectsForKeys:keyArray notFoundMarker:[NSNull null]]; 1.355 + NSEnumerator *connectionEnumerator = [myConnectionArray objectEnumerator]; 1.356 + VPNConnection *myConnection; 1.357 + 1.358 + // Get preferences for showing duration times 1.359 + BOOL showAllDurations = FALSE; 1.360 + BOOL showConnectedDurations = TRUE; 1.361 + id tmp = [[NSUserDefaults standardUserDefaults] objectForKey:@"showAllDurations"]; 1.362 + if(tmp != nil) { 1.363 + showAllDurations = [[NSUserDefaults standardUserDefaults] boolForKey:@"showAllDurations"]; 1.364 + } 1.365 + tmp = [[NSUserDefaults standardUserDefaults] objectForKey:@"showConnectedDurations"]; 1.366 + if(tmp != nil) { 1.367 + showConnectedDurations = [[NSUserDefaults standardUserDefaults] boolForKey:@"showConnectedDurations"]; 1.368 + } 1.369 + 1.370 + int i = 0; 1.371 + while(myConnection = [connectionEnumerator nextObject]) { 1.372 + //NSLog(@"configName: %@\nconnectionState: %@\n",[myConnection configName],[myConnection state]); 1.373 + NSString * cState = [myConnection state]; 1.374 + NSString * cTimeS = @""; 1.375 + 1.376 + // Get connection duration if preferences say to 1.377 + if ( showAllDurations || ( showConnectedDurations && [cState isEqualToString: @"CONNECTED"] ) ) { 1.378 + NSDate * csd = [myConnection connectedSinceDate]; 1.379 + NSTimeInterval ti = [csd timeIntervalSinceNow]; 1.380 + long cTimeL = (long) round(-ti); 1.381 + if ( cTimeL >= 0 ) { 1.382 + if ( cTimeL < 3600 ) { 1.383 + cTimeS = [NSString stringWithFormat:@" %li:%02li", cTimeL/60, cTimeL%60]; 1.384 + } else { 1.385 + cTimeS = [NSString stringWithFormat:@" %li:%02li:%02li", cTimeL/3600, (cTimeL/60) % 60, cTimeL%60]; 1.386 + } 1.387 + } 1.388 + } 1.389 + NSString *label = [NSString stringWithFormat:@"%@ (%@%@)",[myConnection configName],local(cState), cTimeS]; 1.390 + [[tabView tabViewItemAtIndex:i] setLabel:label]; 1.391 + i++; 1.392 + } 1.393 +} 1.394 + 1.395 + 1.396 +- (void) updateUI 1.397 +{ 1.398 + unsigned connectionNumber = [connectionArray count]; 1.399 + NSString *myState; 1.400 + if(connectionNumber == 1) { 1.401 + myState = local(@"OpenVPN: 1 connection active."); 1.402 + } else { 1.403 + myState = [NSString stringWithFormat:local(@"OpenVPN: %d connections active."),connectionNumber]; 1.404 + } 1.405 + 1.406 + [statusMenuItem setTitle: myState]; 1.407 + [theItem setToolTip: myState]; 1.408 + 1.409 + if( (![lastState isEqualToString:@"EXITING"]) && (![lastState isEqualToString:@"CONNECTED"]) ) { 1.410 + // override while in transitional state 1.411 + // Any other state shows "transitional" image: 1.412 + //[theItem setImage: transitionalImage]; 1.413 + if (![theAnim isAnimating]) 1.414 + { 1.415 + //NSLog(@"Starting Animation"); 1.416 + [theAnim startAnimation]; 1.417 + } 1.418 + } else 1.419 + { 1.420 + if ([theAnim isAnimating]) 1.421 + { 1.422 + [theAnim stopAnimation]; 1.423 + } 1.424 + } 1.425 + if (connectionNumber > 0 ) { 1.426 + [theItem setImage: connectedImage]; 1.427 + } else { 1.428 + [theItem setImage: mainImage]; 1.429 + } 1.430 +} 1.431 + 1.432 +- (void)animationDidEnd:(NSAnimation*)animation 1.433 +{ 1.434 + if ((![lastState isEqualToString:@"EXITING"]) && (![lastState isEqualToString:@"CONNECTED"])) 1.435 + { 1.436 + // NSLog(@"Starting Animation (2)"); 1.437 + [theAnim startAnimation]; 1.438 + } 1.439 + if ([connectionArray count] > 0 ) { 1.440 + [theItem setImage: connectedImage]; 1.441 + } else { 1.442 + [theItem setImage: mainImage]; 1.443 + } 1.444 +} 1.445 + 1.446 +- (void)animation:(NSAnimation *)animation 1.447 + didReachProgressMark:(NSAnimationProgress)progress 1.448 +{ 1.449 + if (animation == theAnim) 1.450 + { 1.451 + // NSLog(@"progress is %f %i", progress, lround(progress * 8)); 1.452 + // Do an effect appropriate to progress mark. 1.453 + switch(lround(progress * 8)) 1.454 + { 1.455 + case 1: 1.456 + [theItem performSelectorOnMainThread:@selector(setImage:) withObject:mainImage waitUntilDone:YES]; 1.457 + break; 1.458 + 1.459 + case 2: 1.460 + [theItem performSelectorOnMainThread:@selector(setImage:) withObject:transitionalImage1 waitUntilDone:YES]; 1.461 + break; 1.462 + 1.463 + case 3: 1.464 + [theItem performSelectorOnMainThread:@selector(setImage:) withObject:transitionalImage2 waitUntilDone:YES]; 1.465 + break; 1.466 + 1.467 + case 4: 1.468 + [theItem performSelectorOnMainThread:@selector(setImage:) withObject:transitionalImage3 waitUntilDone:YES]; 1.469 + break; 1.470 + 1.471 + case 5: 1.472 + [theItem performSelectorOnMainThread:@selector(setImage:) withObject:connectedImage waitUntilDone:YES]; 1.473 + break; 1.474 + 1.475 + case 6: 1.476 + [theItem performSelectorOnMainThread:@selector(setImage:) withObject:transitionalImage3 waitUntilDone:YES]; 1.477 + break; 1.478 + 1.479 + case 7: 1.480 + [theItem performSelectorOnMainThread:@selector(setImage:) withObject:transitionalImage2 waitUntilDone:YES]; 1.481 + break; 1.482 + 1.483 + case 8: 1.484 + [theItem performSelectorOnMainThread:@selector(setImage:) withObject:transitionalImage1 waitUntilDone:YES]; 1.485 + break; 1.486 + 1.487 + default: 1.488 + NSLog(@"Unknown progress mark %f selected by Tunnelblick animation", progress); 1.489 + } 1.490 + } 1.491 +} 1.492 + 1.493 +- (void) tabView: (NSTabView*) inTabView willSelectTabViewItem: (NSTabViewItem*) tabViewItem 1.494 +{ 1.495 + NSView* view = [[inTabView selectedTabViewItem] view]; 1.496 + [tabViewItem setView: view]; 1.497 + [[[self selectedLogView] textStorage] setDelegate: nil]; 1.498 +} 1.499 + 1.500 +- (void) tabView: (NSTabView*) inTabView didSelectTabViewItem: (NSTabViewItem*) tabViewItem 1.501 +{ 1.502 + VPNConnection* newConnection = [self selectedConnection]; 1.503 + NSTextView* logView = [self selectedLogView]; 1.504 + [[logView layoutManager] replaceTextStorage: [newConnection logStorage]]; 1.505 + //[logView setSelectedRange: NSMakeRange([[logView textStorage] length],[[logView textStorage] length])]; 1.506 + [logView scrollRangeToVisible: NSMakeRange([[logView string] length]-1, 0)]; 1.507 + 1.508 + [[logView textStorage] setDelegate: self]; 1.509 + 1.510 + [self validateLogButtons]; 1.511 +} 1.512 +- (void) textStorageDidProcessEditing: (NSNotification*) aNotification 1.513 +{ 1.514 + NSNotification *notification = [NSNotification notificationWithName: @"LogDidChange" 1.515 + object: [self selectedLogView]]; 1.516 + [[NSNotificationQueue defaultQueue] enqueueNotification: notification 1.517 + postingStyle: NSPostWhenIdle 1.518 + coalesceMask: NSNotificationCoalescingOnName | NSNotificationCoalescingOnSender 1.519 + forModes: nil]; 1.520 +} 1.521 + 1.522 +- (void) logNeedsScrolling: (NSNotification*) aNotification 1.523 +{ 1.524 + NSTextView* textView = [aNotification object]; 1.525 + [textView scrollRangeToVisible: NSMakeRange([[textView string] length]-1, 0)]; 1.526 +} 1.527 + 1.528 +- (NSTextView*) selectedLogView 1.529 +{ 1.530 + NSTextView* result = [[[[[tabView selectedTabViewItem] view] subviews] lastObject] documentView]; 1.531 + return result; 1.532 +} 1.533 + 1.534 +- (IBAction) clearLog: (id) sender 1.535 +{ 1.536 + NSString * versionInfo = [NSString stringWithFormat:local(@"Tunnelblick version %@"),[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]]; 1.537 + NSCalendarDate* date = [NSCalendarDate date]; 1.538 + NSString *dateText = [NSString stringWithFormat:@"%@ %@\n",[date descriptionWithCalendarFormat:@"%Y-%m-%d %H:%M:%S"],versionInfo]; 1.539 + [[self selectedLogView] setString: [[[NSString alloc] initWithString: dateText] autorelease]]; 1.540 +} 1.541 + 1.542 +- (VPNConnection*) selectedConnection 1.543 + /*" Returns the connection associated with the currently selected log tab or nil on error. "*/ 1.544 +{ 1.545 + if (![tabView selectedTabViewItem]) { 1.546 + [tabView selectFirstTabViewItem: nil]; 1.547 + } 1.548 + 1.549 + NSString* configPath = [[tabView selectedTabViewItem] identifier]; 1.550 + VPNConnection* connection = [myVPNConnectionDictionary objectForKey:configPath]; 1.551 + NSArray *allConnections = [myVPNConnectionDictionary allValues]; 1.552 + if(connection) return connection; 1.553 + else if([allConnections count]) return [allConnections objectAtIndex:0] ; 1.554 + else return nil; 1.555 +} 1.556 + 1.557 + 1.558 + 1.559 + 1.560 +- (IBAction)connect:(id)sender 1.561 +{ 1.562 + [[self selectedConnection] connect: sender]; 1.563 +} 1.564 + 1.565 +- (IBAction)disconnect:(id)sender 1.566 +{ 1.567 + [[self selectedConnection] disconnect: sender]; 1.568 +} 1.569 + 1.570 + 1.571 +- (IBAction) openLogWindow: (id) sender 1.572 +{ 1.573 + if (logWindow != nil) { 1.574 + [logWindow makeKeyAndOrderFront: self]; 1.575 + [NSApp activateIgnoringOtherApps:YES]; 1.576 + return; 1.577 + } 1.578 + 1.579 + [NSBundle loadNibNamed: @"LogWindow" owner: self]; // also sets tabView etc. 1.580 + 1.581 + // Set the window's size and position from preferences (saved when window is closed) 1.582 + // But only if the preference's version matches the TB version (since window size could be different in different versions of TB) 1.583 + NSString * tbVersion = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]; 1.584 + id tmp = [[NSUserDefaults standardUserDefaults] objectForKey:@"detailsWindowFrameVersion"]; 1.585 + if (tmp != nil) { 1.586 + if ( [tbVersion isEqualToString: [[NSUserDefaults standardUserDefaults] stringForKey:@"detailsWindowFrameVersion"]] ) { 1.587 + tmp = [[NSUserDefaults standardUserDefaults] objectForKey:@"detailsWindowFrame"]; 1.588 + if(tmp != nil) { 1.589 + NSString * frame = [[NSUserDefaults standardUserDefaults] stringForKey:@"detailsWindowFrame"]; 1.590 + [logWindow setFrameFromString:frame]; 1.591 + } 1.592 + } 1.593 + } 1.594 + 1.595 + [logWindow setDelegate:self]; 1.596 + VPNConnection *myConnection = [self selectedConnection]; 1.597 + NSTextStorage* store = [myConnection logStorage]; 1.598 + [[[self selectedLogView] layoutManager] replaceTextStorage: store]; 1.599 + 1.600 + NSEnumerator* e = [[[myVPNConnectionDictionary allKeys] sortedArrayUsingSelector: @selector(compare:)] objectEnumerator]; 1.601 + //id test = [[myVPNConnectionDictionary allKeys] sortedArrayUsingSelector: @selector(compare:)]; 1.602 + NSTabViewItem* initialItem; 1.603 + VPNConnection* connection = [myVPNConnectionDictionary objectForKey: [e nextObject]]; 1.604 + if (connection) { 1.605 + initialItem = [tabView tabViewItemAtIndex: 0]; 1.606 + [initialItem setIdentifier: [connection configPath]]; 1.607 + [initialItem setLabel: [connection configName]]; 1.608 + 1.609 + int curTabIndex = 0; 1.610 + [tabView selectTabViewItemAtIndex:0]; 1.611 + BOOL haveOpenConnection = ! [connection isDisconnected]; 1.612 + while (connection = [myVPNConnectionDictionary objectForKey: [e nextObject]]) { 1.613 + NSTabViewItem* newItem = [[NSTabViewItem alloc] init]; 1.614 + [newItem setIdentifier: [connection configPath]]; 1.615 + [newItem setLabel: [connection configName]]; 1.616 + [tabView addTabViewItem: newItem]; 1.617 + ++curTabIndex; 1.618 + if ( ( ! haveOpenConnection ) && ( ! [connection isDisconnected] ) ) { 1.619 + [tabView selectTabViewItemAtIndex:curTabIndex]; 1.620 + haveOpenConnection = YES; 1.621 + } 1.622 + } 1.623 + } 1.624 + [self tabView:tabView didSelectTabViewItem:initialItem]; 1.625 + [self validateLogButtons]; 1.626 + [self updateTabLabels]; 1.627 + 1.628 + // Set up a timer to update the tab labels with connections' duration times 1.629 + BOOL showAllDurations = FALSE; 1.630 + BOOL showConnectedDurations = TRUE; 1.631 + tmp = [[NSUserDefaults standardUserDefaults] objectForKey:@"showAllDurations"]; 1.632 + if(tmp != nil) { 1.633 + showAllDurations = [[NSUserDefaults standardUserDefaults] boolForKey:@"showAllDurations"]; 1.634 + } 1.635 + tmp = [[NSUserDefaults standardUserDefaults] objectForKey:@"showConnectedDurations"]; 1.636 + if(tmp != nil) { 1.637 + showConnectedDurations = [[NSUserDefaults standardUserDefaults] boolForKey:@"showConnectedDurations"]; 1.638 + } 1.639 + 1.640 + if ( (showDurationsTimer == nil) && (showAllDurations || showConnectedDurations) ) { 1.641 + showDurationsTimer = [[NSTimer scheduledTimerWithTimeInterval:1.0 1.642 + target:self 1.643 + selector:@selector(updateTabLabels) 1.644 + userInfo:nil 1.645 + repeats:YES] retain]; 1.646 + } 1.647 + 1.648 + // Localize Buttons 1.649 + [clearButton setTitle:local([clearButton title])]; 1.650 + [editButton setTitle:local([editButton title])]; 1.651 + [connectButton setTitle:local([connectButton title])]; 1.652 + [disconnectButton setTitle:local([disconnectButton title])]; 1.653 + [useNameserverCheckbox setTitle:local([useNameserverCheckbox title])]; 1.654 + [autoLaunchCheckbox setTitle:local([autoLaunchCheckbox title])]; 1.655 + 1.656 + [logWindow makeKeyAndOrderFront: self]; 1.657 + [NSApp activateIgnoringOtherApps:YES]; 1.658 +} 1.659 + 1.660 +// Invoked when the Details... window (logWindow) will close 1.661 +- (void)windowWillClose:(NSNotification *)n 1.662 +{ 1.663 + if ( [n object] == logWindow ) { 1.664 + // Stop and release the timer used to update the duration displays 1.665 + if (showDurationsTimer != nil) { 1.666 + [showDurationsTimer invalidate]; 1.667 + [showDurationsTimer release]; 1.668 + showDurationsTimer = nil; 1.669 + } 1.670 + 1.671 + // Save the window's size and position in the preferences and save the TB version that saved them, BUT ONLY IF anything has changed 1.672 + NSString * frame = [logWindow stringWithSavedFrame]; 1.673 + NSString * tbVersion = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]; 1.674 + BOOL saveIt = TRUE; 1.675 + id tmp = [[NSUserDefaults standardUserDefaults] objectForKey:@"detailsWindowFrame"]; 1.676 + if(tmp != nil) { 1.677 + tmp = [[NSUserDefaults standardUserDefaults] objectForKey:@"detailsWindowFrameVersion"]; 1.678 + if (tmp != nil) { 1.679 + if ( [tbVersion isEqualToString: [[NSUserDefaults standardUserDefaults] stringForKey:@"detailsWindowFrameVersion"]] ) { 1.680 + if ( [frame isEqualToString: [[NSUserDefaults standardUserDefaults] stringForKey:@"detailsWindowFrame"]] ) { 1.681 + saveIt = FALSE; 1.682 + } 1.683 + } 1.684 + } 1.685 + } 1.686 + 1.687 + if (saveIt) { 1.688 + [[NSUserDefaults standardUserDefaults] setObject: frame forKey: @"detailsWindowFrame"]; 1.689 + [[NSUserDefaults standardUserDefaults] setObject: [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"] 1.690 + forKey: @"detailsWindowFrameVersion"]; 1.691 + [[NSUserDefaults standardUserDefaults] synchronize]; 1.692 + } 1.693 + } 1.694 +} 1.695 + 1.696 +- (void) dealloc 1.697 +{ 1.698 + [lastState release]; 1.699 + [theItem release]; 1.700 + [myConfigArray release]; 1.701 + 1.702 +#warning todo: release non-IB ivars here! 1.703 + [statusMenuItem release]; 1.704 + [myVPNMenu release]; 1.705 + [userDefaults release]; 1.706 + [appDefaults release]; 1.707 + [theItem release]; 1.708 + 1.709 + [mainImage release]; 1.710 + [connectedImage release]; 1.711 + [errorImage release]; 1.712 + [transitionalImage release]; 1.713 + [connectionArray release]; 1.714 + 1.715 + 1.716 + [super dealloc]; 1.717 +} 1.718 + 1.719 + 1.720 +-(void)killAllConnections 1.721 +{ 1.722 + id connection; 1.723 + NSEnumerator* e = [connectionArray objectEnumerator]; 1.724 + 1.725 + while (connection = [e nextObject]) { 1.726 + [connection disconnect:self]; 1.727 + if(NSDebugEnabled) NSLog(@"Killing connection.\n"); 1.728 + } 1.729 +} 1.730 + 1.731 +-(void)killAllOpenVPN 1.732 +{ 1.733 + NSString* path = [[NSBundle mainBundle] pathForResource: @"openvpnstart" 1.734 + ofType: nil]; 1.735 + NSTask* task = [[[NSTask alloc] init] autorelease]; 1.736 + [task setLaunchPath: path]; 1.737 + 1.738 + NSArray *arguments = [NSArray arrayWithObjects:@"killall", nil]; 1.739 + [task setArguments:arguments]; 1.740 + [task launch]; 1.741 + [task waitUntilExit]; 1.742 +} 1.743 + 1.744 +-(void)resetActiveConnections { 1.745 + VPNConnection *connection; 1.746 + NSEnumerator* e = [connectionArray objectEnumerator]; 1.747 + 1.748 + while (connection = [e nextObject]) { 1.749 + if (NSDebugEnabled) NSLog(@"Connection %@ is connected for %f seconds\n",[connection configName],[[connection connectedSinceDate] timeIntervalSinceNow]); 1.750 + if ([[connection connectedSinceDate] timeIntervalSinceNow] < -5) { 1.751 + if (NSDebugEnabled) NSLog(@"Resetting connection: %@\n",[connection configName]); 1.752 + [connection disconnect:self]; 1.753 + [connection connect:self]; 1.754 + } 1.755 + else { 1.756 + if (NSDebugEnabled) NSLog(@"Not Resetting connection: %@\n, waiting...",[connection configName]); 1.757 + } 1.758 + } 1.759 +} 1.760 + 1.761 +-(void)createDefaultConfig 1.762 +{ 1.763 + NSFileManager *fileManager = [NSFileManager defaultManager]; 1.764 + NSString *directoryPath = [NSHomeDirectory() stringByAppendingPathComponent:@"Library/openvpn"]; 1.765 + NSString *confResource = [[NSBundle mainBundle] pathForResource: @"openvpn" 1.766 + ofType: @"conf"]; 1.767 + 1.768 + if([[self getConfigs] count] == 0) { // if there are no config files, create a default one 1.769 + [NSApp activateIgnoringOtherApps:YES]; 1.770 + if(NSRunCriticalAlertPanel(local(@"Welcome to OpenVPN on Mac OS X: Please put your config file (e.g. openvpn.conf) to '~/Library/openvpn/'."), local(@"You can also continue and Tunnelblick will create an example config at the right place that you can customize or replace."),local(@"Quit"),local(@"Continue"),nil) == NSAlertDefaultReturn) { 1.771 + exit (1); 1.772 + } 1.773 + else { 1.774 + [fileManager createDirectoryAtPath:directoryPath attributes:nil]; 1.775 + [fileManager copyPath:confResource toPath:[directoryPath stringByAppendingPathComponent:@"/openvpn.conf"] handler:nil]; 1.776 + [self editConfig:self]; 1.777 + } 1.778 + 1.779 + 1.780 + } 1.781 +} 1.782 + 1.783 +-(IBAction)editConfig:(id)sender 1.784 +{ 1.785 + VPNConnection *connection = [self selectedConnection]; 1.786 + NSString *directoryPath = [NSHomeDirectory() stringByAppendingPathComponent:@"Library/openvpn"]; 1.787 + NSString *configPath = [connection configPath]; 1.788 + if(configPath == nil) configPath = @"/openvpn.conf"; 1.789 + 1.790 + // NSString *helper = @"/usr/sbin/chown"; 1.791 + // NSString *userString = [NSString stringWithFormat:@"%d",getuid()]; 1.792 + // NSArray *arguments = [NSArray arrayWithObjects:userString,configPath,nil]; 1.793 + // AuthorizationRef authRef = [NSApplication getAuthorizationRef]; 1.794 + // [NSApplication executeAuthorized:helper withArguments:arguments withAuthorizationRef:authRef]; 1.795 + // AuthorizationFree(authRef,kAuthorizationFlagDefaults); 1.796 + 1.797 + [[NSWorkspace sharedWorkspace] openFile:[directoryPath stringByAppendingPathComponent:configPath] withApplication:@"TextEdit"]; 1.798 +} 1.799 + 1.800 + 1.801 +- (void) networkConfigurationDidChange 1.802 +{ 1.803 + if (NSDebugEnabled) NSLog(@"Got networkConfigurationDidChange notification!!"); 1.804 + [self resetActiveConnections]; 1.805 +} 1.806 + 1.807 +- (void) applicationWillTerminate: (NSNotification*) notification 1.808 +{ 1.809 + if (NSDebugEnabled) NSLog(@"App will terminate...\n"); 1.810 + [self cleanup]; 1.811 +} 1.812 + 1.813 +-(void)cleanup 1.814 +{ 1.815 + [NSApp callDelegateOnNetworkChange: NO]; 1.816 + [self killAllConnections]; 1.817 + [self killAllOpenVPN]; 1.818 + [[NSStatusBar systemStatusBar] removeStatusItem:theItem]; 1.819 +} 1.820 + 1.821 +-(void)saveUseNameserverCheckboxState:(BOOL)inBool 1.822 +{ 1.823 + VPNConnection* connection = [self selectedConnection]; 1.824 + if(connection != nil) { 1.825 + NSString* key = [[connection configName] stringByAppendingString: @"useDNS"]; 1.826 + [[NSUserDefaults standardUserDefaults] setObject: [NSNumber numberWithBool: inBool] forKey: key]; 1.827 + [[NSUserDefaults standardUserDefaults] synchronize]; 1.828 + } 1.829 + 1.830 +} 1.831 +-(void)saveAutoLaunchCheckboxState:(BOOL)inBool 1.832 +{ 1.833 + VPNConnection* connection = [self selectedConnection]; 1.834 + if(connection != nil) { 1.835 + NSString* autoConnectKey = [[connection configName] stringByAppendingString: @"autoConnect"]; 1.836 + [[NSUserDefaults standardUserDefaults] setObject: [NSNumber numberWithBool: inBool] forKey: autoConnectKey]; 1.837 + [[NSUserDefaults standardUserDefaults] synchronize]; 1.838 + } 1.839 + 1.840 +} 1.841 + 1.842 +-(BOOL)getCurrentAutoLaunchSetting 1.843 +{ 1.844 + VPNConnection *connection = [self selectedConnection]; 1.845 + NSString *autoConnectKey = [[connection configName] stringByAppendingString:@"autoConnect"]; 1.846 + return [[NSUserDefaults standardUserDefaults] boolForKey:autoConnectKey]; 1.847 +} 1.848 + 1.849 +- (void) setState: (NSString*) newState 1.850 + // Be sure to call this in main thread only 1.851 +{ 1.852 + [newState retain]; 1.853 + [lastState release]; 1.854 + lastState = newState; 1.855 + //[self updateUI]; 1.856 + [self performSelectorOnMainThread:@selector(updateUI) withObject:nil waitUntilDone:NO]; 1.857 +} 1.858 + 1.859 +-(void)addConnection:(id)sender 1.860 +{ 1.861 + if(sender != nil) { 1.862 + [connectionArray removeObject:sender]; 1.863 + [connectionArray addObject:sender]; 1.864 + } 1.865 +} 1.866 + 1.867 +-(void)removeConnection:(id)sender 1.868 +{ 1.869 + if(sender != nil) [connectionArray removeObject:sender]; 1.870 +} 1.871 + 1.872 +static void signal_handler(int signalNumber) 1.873 +{ 1.874 + printf("signal %d caught!\n",signalNumber); 1.875 + 1.876 + if (signalNumber == SIGHUP) { 1.877 + printf("SIGHUP received. Restarting active connections...\n"); 1.878 + [[NSApp delegate] resetActiveConnections]; 1.879 + } else { 1.880 + printf("Received fatal signal. Cleaning up...\n"); 1.881 + [[NSApp delegate] cleanup]; 1.882 + exit(0); 1.883 + } 1.884 +} 1.885 + 1.886 + 1.887 +- (void) installSignalHandler 1.888 +{ 1.889 + struct sigaction action; 1.890 + 1.891 + action.sa_handler = signal_handler; 1.892 + sigemptyset(&action.sa_mask); 1.893 + action.sa_flags = 0; 1.894 + 1.895 + if (sigaction(SIGHUP, &action, NULL) || 1.896 + sigaction(SIGQUIT, &action, NULL) || 1.897 + sigaction(SIGTERM, &action, NULL) || 1.898 + sigaction(SIGBUS, &action, NULL) || 1.899 + sigaction(SIGSEGV, &action, NULL) || 1.900 + sigaction(SIGPIPE, &action, NULL)) { 1.901 + NSLog(@"Warning: setting signal handler failed: %s", strerror(errno)); 1.902 + } 1.903 +} 1.904 +- (void) applicationDidFinishLaunching: (NSNotification *)notification 1.905 +{ 1.906 + [NSApp callDelegateOnNetworkChange: NO]; 1.907 + [self installSignalHandler]; 1.908 + [NSApp setAutoLaunchOnLogin: YES]; 1.909 + [self activateStatusMenu]; 1.910 + if(needsRepair()){ 1.911 + if ([self repairPermissions] != TRUE) { 1.912 + [NSApp terminate:self]; 1.913 + } 1.914 + } 1.915 + 1.916 + [updater checkForUpdatesInBackground]; 1.917 +} 1.918 + 1.919 +-(void) dmgCheck 1.920 +{ 1.921 + NSString *path = [[NSBundle mainBundle] bundlePath]; 1.922 + if([path hasPrefix:@"/Volumes/Tunnelblick"]) { 1.923 + NSPanel *panel = NSGetAlertPanel(local(@"You're trying to launch Tunnelblick from the disk image"),local(@"Please copy Tunnelblick.app to your Harddisk before launching it."),local(@"Cancel"),nil,nil); 1.924 + [panel setLevel:NSStatusWindowLevel]; 1.925 + [panel makeKeyAndOrderFront:nil]; 1.926 + [NSApp runModalForWindow:panel]; 1.927 + exit(2); 1.928 + } 1.929 +} 1.930 + 1.931 +-(void)moveAllWindowsToForeground 1.932 +{ 1.933 + NSArray *windows = [NSApp windows]; 1.934 + NSEnumerator *e = [windows objectEnumerator]; 1.935 + NSWindow *window = nil; 1.936 + while(window = [e nextObject]) { 1.937 + [window setLevel:NSStatusWindowLevel]; 1.938 + } 1.939 +} 1.940 + 1.941 +-(void) fileSystemHasChanged: (NSNotification*) n 1.942 +{ 1.943 + if(NSDebugEnabled) NSLog(@"FileSystem has changed."); 1.944 + [self performSelectorOnMainThread: @selector(activateStatusMenu) withObject: nil waitUntilDone: YES]; 1.945 +} 1.946 +-(void) kqueue: (UKKQueue*) kq receivedNotification: (NSString*) nm forFile: (NSString*) fpath { 1.947 + 1.948 + [self fileSystemHasChanged: nil]; 1.949 +} 1.950 + 1.951 +-(BOOL)repairPermissions 1.952 +{ 1.953 + NSBundle *thisBundle = [NSBundle mainBundle]; 1.954 + NSString *installer = [thisBundle pathForResource:@"installer" ofType:nil]; 1.955 + 1.956 + AuthorizationRef authRef= [NSApplication getAuthorizationRef]; 1.957 + 1.958 + if(authRef == nil) 1.959 + return FALSE; 1.960 + 1.961 + while(needsRepair()) { 1.962 + NSLog(@"Repairing Application...\n"); 1.963 + [NSApplication executeAuthorized:installer withArguments:nil withAuthorizationRef:authRef]; 1.964 + sleep(1); 1.965 + } 1.966 + AuthorizationFree(authRef, kAuthorizationFlagDefaults); 1.967 + return TRUE; 1.968 +} 1.969 + 1.970 + 1.971 + 1.972 + 1.973 +BOOL needsRepair() 1.974 +{ 1.975 + NSBundle *thisBundle = [NSBundle mainBundle]; 1.976 + NSString *helperPath = [thisBundle pathForResource:@"openvpnstart" ofType:nil]; 1.977 + NSString *tunPath = [thisBundle pathForResource:@"tun.kext" ofType:nil]; 1.978 + NSString *tapPath = [thisBundle pathForResource:@"tap.kext" ofType:nil]; 1.979 + 1.980 + NSString *tunExecutable = [tunPath stringByAppendingPathComponent:@"/Contents/MacOS/tun"]; 1.981 + NSString *tapExecutable = [tapPath stringByAppendingPathComponent:@"/Contents/MacOS/tap"]; 1.982 + NSString *openvpnPath = [thisBundle pathForResource:@"openvpn" ofType:nil]; 1.983 + 1.984 + 1.985 + // check setuid helper 1.986 + const char *path = [helperPath UTF8String]; 1.987 + struct stat sb; 1.988 + if(stat(path,&sb)) runUnrecoverableErrorPanel(); 1.989 + 1.990 + if (!( (sb.st_mode & S_ISUID) // set uid bit is set 1.991 + && (sb.st_mode & S_IXUSR) // owner may execute it 1.992 + && (sb.st_uid == 0) // is owned by root 1.993 + )) { 1.994 + NSLog(@"openvpnstart helper has missing set uid bit"); 1.995 + return YES; 1.996 + } 1.997 + 1.998 + // check files which should be only accessible by root 1.999 + NSArray *inaccessibleObjects = [NSArray arrayWithObjects:tunExecutable,tapExecutable,openvpnPath,nil]; 1.1000 + NSEnumerator *e = [inaccessibleObjects objectEnumerator]; 1.1001 + NSString *currentPath; 1.1002 + NSFileManager *fileManager = [NSFileManager defaultManager]; 1.1003 + while(currentPath = [e nextObject]) { 1.1004 + NSDictionary *fileAttributes = [fileManager fileAttributesAtPath:currentPath traverseLink:YES]; 1.1005 + unsigned long perms = [fileAttributes filePosixPermissions]; 1.1006 + NSString *octalString = [NSString stringWithFormat:@"%o",perms]; 1.1007 + NSNumber *fileOwner = [fileAttributes fileOwnerAccountID]; 1.1008 + 1.1009 + if ( (![octalString isEqualToString:@"744"]) || (![fileOwner isEqualToNumber:[NSNumber numberWithInt:0]])) { 1.1010 + NSLog(@"File %@ has permissions: %@, is owned by %@ and needs repair...\n",currentPath,octalString,fileOwner); 1.1011 + return YES; 1.1012 + } 1.1013 + } 1.1014 + 1.1015 + // check tun and tap driver packages 1.1016 + NSArray *filesToCheck = [NSArray arrayWithObjects:tunPath,tapPath,nil]; 1.1017 + NSEnumerator *enumerator = [filesToCheck objectEnumerator]; 1.1018 + NSString *file; 1.1019 + while(file = [enumerator nextObject]) { 1.1020 + NSDictionary *fileAttributes = [fileManager fileAttributesAtPath:file traverseLink:YES]; 1.1021 + unsigned long perms = [fileAttributes filePosixPermissions]; 1.1022 + NSString *octalString = [NSString stringWithFormat:@"%o",perms]; 1.1023 + if ( (![octalString isEqualToString:@"755"]) ) { 1.1024 + NSLog(@"File %@ has permissions: %@ and needs repair...\n",currentPath,octalString); 1.1025 + return YES; 1.1026 + } 1.1027 + } 1.1028 + return NO; 1.1029 +} 1.1030 + 1.1031 +-(void)willGoToSleep 1.1032 +{ 1.1033 + if(NSDebugEnabled) NSLog(@"Computer will go to sleep...\n"); 1.1034 + connectionsToRestore = [connectionArray mutableCopy]; 1.1035 + [self killAllConnections]; 1.1036 +} 1.1037 +-(void)wokeUpFromSleep 1.1038 +{ 1.1039 + if(NSDebugEnabled) NSLog(@"Computer just woke up from sleep...\n"); 1.1040 + 1.1041 + NSEnumerator *e = [connectionsToRestore objectEnumerator]; 1.1042 + VPNConnection *connection; 1.1043 + while(connection = [e nextObject]) { 1.1044 + if(NSDebugEnabled) NSLog(@"Restoring Connection %@",[connection configName]); 1.1045 + [connection connect:self]; 1.1046 + } 1.1047 +} 1.1048 +int runUnrecoverableErrorPanel(void) 1.1049 +{ 1.1050 + NSPanel *panel = NSGetAlertPanel(local(@"Tunnelblick Error"),local(@"It seems like you need to reinstall Tunnelblick. Please move Tunnelblick to the Trash and download a fresh copy."),local(@"Download"),local(@"Quit"),nil); 1.1051 + [panel setLevel:NSStatusWindowLevel]; 1.1052 + [panel makeKeyAndOrderFront:nil]; 1.1053 + if( [NSApp runModalForWindow:panel] != NSAlertDefaultReturn ) { 1.1054 + exit(2); 1.1055 + } else { 1.1056 + [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"http://tunnelblick.net/"]]; 1.1057 + exit(2); 1.1058 + } 1.1059 +} 1.1060 + 1.1061 +-(IBAction) autoLaunchPrefButtonWasClicked: (id) sender 1.1062 +{ 1.1063 + if([sender state]) { 1.1064 + [self saveAutoLaunchCheckboxState:TRUE]; 1.1065 + } else { 1.1066 + [self saveAutoLaunchCheckboxState:FALSE]; 1.1067 + } 1.1068 +} 1.1069 + 1.1070 +-(IBAction) nameserverPrefButtonWasClicked: (id) sender 1.1071 +{ 1.1072 + if([sender state]) { 1.1073 + [self saveUseNameserverCheckboxState:TRUE]; 1.1074 + } else { 1.1075 + [self saveUseNameserverCheckboxState:FALSE]; 1.1076 + } 1.1077 +} 1.1078 + 1.1079 + 1.1080 +@end