tunblick/MenuController.m

Wed, 10 Feb 2010 21:25:01 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 10 Feb 2010 21:25:01 +0100
changeset 18
8ec65b8f6e2c
parent 7
0c0e4024a98e
permissions
-rw-r--r--

Extend uac_auth() of the UAC module to workaround CSEQ problems.
This logic is meant to complement that of changeset 17, which
added rich authentication credentials to the gw table and its
associated logic in the LCR module.

michael@1 1 /*
michael@1 2 * Copyright (c) 2004 Angelo Laub
michael@1 3 * Contributions by Dirk Theisen <dirk@objectpark.org>,
michael@1 4 * Jens Ohlig,
michael@1 5 * Waldemar Brodkorb
michael@1 6 *
michael@1 7 * This program is free software; you can redistribute it and/or modify
michael@1 8 * it under the terms of the GNU General Public License version 2
michael@1 9 * as published by the Free Software Foundation.
michael@1 10 *
michael@1 11 * This program is distributed in the hope that it will be useful,
michael@1 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
michael@1 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
michael@1 14 * GNU General Public License for more details.
michael@1 15 *
michael@1 16 * You should have received a copy of the GNU General Public License
michael@1 17 * along with this program (see the file COPYING included with this
michael@1 18 * distribution); if not, write to the Free Software Foundation, Inc.,
michael@1 19 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
michael@1 20 */
michael@1 21
michael@1 22
michael@1 23 #import "MenuController.h"
michael@1 24 #import "NSApplication+NetworkNotifications.h"
michael@1 25 #import "helper.h"
michael@1 26
michael@1 27
michael@1 28 #define NSAppKitVersionNumber10_0 577
michael@1 29 #define NSAppKitVersionNumber10_1 620
michael@1 30 #define NSAppKitVersionNumber10_2 663
michael@1 31 #define NSAppKitVersionNumber10_3 743
michael@1 32
michael@1 33
michael@1 34
michael@1 35 BOOL systemIsTigerOrNewer()
michael@1 36 {
michael@1 37 return (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_3) ;
michael@1 38 }
michael@1 39
michael@1 40 @interface NSStatusBar (NSStatusBar_Private)
michael@1 41 - (id)_statusItemWithLength:(float)l withPriority:(int)p;
michael@1 42 @end
michael@1 43
michael@1 44
michael@1 45 @implementation MenuController
michael@1 46
michael@1 47 - (void) createStatusItem
michael@1 48 {
michael@1 49 NSStatusBar *bar = [NSStatusBar systemStatusBar];
michael@1 50 int priority = INT32_MAX;
michael@1 51 if (systemIsTigerOrNewer()) {
michael@1 52 priority = MIN(priority, 2147483646); // found by experimenting - dirk
michael@1 53 }
michael@1 54
michael@1 55 if (!theItem) {
michael@8 56 theItem = [[bar statusItemWithLength: NSVariableStatusItemLength] retain];
michael@1 57 //theItem = [[bar _statusItemWithLength: NSVariableStatusItemLength withPriority: 0] retain];
michael@1 58 }
michael@1 59 // Dirk: For Tiger and up, re-insert item to place it correctly.
michael@1 60 if ([bar respondsToSelector: @selector(_insertStatusItem:withPriority:)]) {
michael@1 61 [bar removeStatusItem: theItem];
michael@1 62 [bar _insertStatusItem: theItem withPriority: priority];
michael@1 63 }
michael@1 64 }
michael@1 65
michael@1 66 -(id) init
michael@1 67 {
michael@1 68 if (self = [super init]) {
michael@1 69 [self dmgCheck];
michael@1 70
michael@1 71 errorImage = [[NSImage imageNamed: @"error.tif"] retain];
michael@1 72 mainImage = [[NSImage imageNamed: @"00_closed.tif"] retain];
michael@1 73 connectedImage = [[NSImage imageNamed: @"connected.png"] retain];
michael@1 74
michael@1 75
michael@1 76 transitionalImage1 = [[NSImage imageNamed: @"01.tif"] retain];
michael@1 77 transitionalImage2 = [[NSImage imageNamed: @"02.tif"] retain];
michael@1 78 transitionalImage3 = [[NSImage imageNamed: @"03.tif"] retain];
michael@1 79 [NSApp setDelegate:self];
michael@1 80
michael@1 81 myVPNConnectionDictionary = [[NSMutableDictionary alloc] init];
michael@1 82 myVPNConnectionArray = [[[NSMutableArray alloc] init] retain];
michael@1 83 userDefaults = [[NSMutableDictionary alloc] init];
michael@1 84
michael@1 85 connectionArray = [[[NSMutableArray alloc] init] retain];
michael@1 86 appDefaults = [NSUserDefaults standardUserDefaults];
michael@1 87 [appDefaults registerDefaults:userDefaults];
michael@1 88
michael@1 89
michael@1 90 detailsItem = [[NSMenuItem alloc] init];
michael@1 91 [detailsItem setTitle: @"Details..."];
michael@1 92 [detailsItem setTarget: self];
michael@1 93 [detailsItem setAction: @selector(openLogWindow:)];
michael@1 94
michael@1 95 quitItem = [[NSMenuItem alloc] init];
michael@1 96 [quitItem setTitle: @"Quit"];
michael@1 97 [quitItem setTarget: self];
michael@1 98 [quitItem setAction: @selector(quit:)];
michael@1 99
michael@1 100 [self createStatusItem];
michael@1 101
michael@1 102 [self updateMenu];
michael@1 103 [self setState: @"EXITING"]; // synonym for "Disconnected"
michael@1 104
michael@1 105 [[NSNotificationCenter defaultCenter] addObserver: self
michael@1 106 selector: @selector(logNeedsScrolling:)
michael@1 107 name: @"LogDidChange"
michael@1 108 object: nil];
michael@1 109
michael@1 110 // In case the systemUIServer restarts, we observed this notification.
michael@1 111 // We use it to prevent to end up with a statusItem right of Spotlight:
michael@1 112 [[NSDistributedNotificationCenter defaultCenter] addObserver: self
michael@1 113 selector: @selector(menuExtrasWereAdded:)
michael@1 114 name: @"com.apple.menuextra.added"
michael@1 115 object: nil];
michael@1 116 [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver: self
michael@1 117 selector: @selector(willGoToSleep)
michael@1 118 name: @"NSWorkspaceWillSleepNotification"
michael@1 119 object:nil];
michael@1 120
michael@1 121 [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver: self
michael@1 122 selector: @selector(wokeUpFromSleep)
michael@1 123 name: @"NSWorkspaceDidWakeNotification"
michael@1 124 object:nil];
michael@1 125
michael@1 126 NSString* vpnDirectory = [NSHomeDirectory() stringByAppendingPathComponent:@"Library/openvpn/"];
michael@1 127
michael@1 128 UKKQueue* myQueue = [UKKQueue sharedQueue];
michael@1 129 [myQueue addPathToQueue: vpnDirectory];
michael@1 130 [myQueue setDelegate: self];
michael@1 131 [myQueue setAlwaysNotify: YES];
michael@1 132
michael@1 133 [NSThread detachNewThreadSelector:@selector(moveAllWindowsToForegroundThread) toTarget:self withObject:nil];
michael@1 134
michael@1 135 updater = [[SUUpdater alloc] init];
michael@1 136
michael@1 137 }
michael@1 138 return self;
michael@1 139 }
michael@1 140
michael@1 141 -(void)moveAllWindowsToForegroundThread {
michael@1 142 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
michael@1 143 sleep(3);
michael@1 144 [self moveAllWindowsToForeground];
michael@1 145 // [NSTimer scheduledTimerWithTimeInterval: 1.0 target: self selector: @selector(moveAllWindowsToForeground) userInfo: nil repeats: YES];
michael@1 146 [pool release];
michael@1 147
michael@1 148 }
michael@1 149
michael@1 150 - (void) menuExtrasWereAdded: (NSNotification*) n
michael@1 151 {
michael@1 152 [self createStatusItem];
michael@1 153 }
michael@1 154
michael@1 155
michael@1 156
michael@1 157 - (IBAction) quit: (id) sender
michael@1 158 {
michael@1 159 // Remove us from the login items if terminates manually...
michael@1 160 [NSApp setAutoLaunchOnLogin: NO];
michael@1 161 [NSApp terminate: sender];
michael@1 162 }
michael@1 163
michael@1 164
michael@1 165
michael@1 166 - (void) awakeFromNib
michael@1 167 {
michael@1 168 [self createDefaultConfig];
michael@1 169 [self initialiseAnim];
michael@1 170 }
michael@1 171
michael@1 172 - (void) initialiseAnim
michael@1 173 {
michael@1 174 NSAnimationProgress progMarks[] = {
michael@1 175 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
michael@1 176 };
michael@1 177
michael@1 178 int i, count = 8;
michael@1 179 // theAnim is an NSAnimation instance variable
michael@1 180 theAnim = [[NSAnimation alloc] initWithDuration:2.0
michael@1 181 animationCurve:NSAnimationLinear];
michael@1 182 [theAnim setFrameRate:8.0];
michael@1 183 [theAnim setDelegate:self];
michael@1 184
michael@1 185 for (i=0; i<count; i++)
michael@1 186 [theAnim addProgressMark:progMarks[i]];
michael@1 187
michael@1 188 [theAnim setAnimationBlockingMode: NSAnimationNonblocking];
michael@1 189 }
michael@1 190
michael@1 191 -(void) updateMenu
michael@1 192 {
michael@1 193 [theItem setHighlightMode:YES];
michael@1 194 [theItem setMenu:nil];
michael@1 195 [myVPNMenu dealloc]; myVPNMenu = nil;
michael@1 196 [[myVPNConnectionDictionary allValues] makeObjectsPerformSelector:@selector(disconnect:) withObject:self];
michael@1 197 [myVPNConnectionDictionary removeAllObjects];
michael@1 198
michael@1 199 myVPNMenu = [[NSMenu alloc] init];
michael@1 200 [myVPNMenu setDelegate:self];
michael@1 201
michael@1 202 [theItem setMenu: myVPNMenu];
michael@1 203
michael@1 204 statusMenuItem = [[NSMenuItem alloc] init];
michael@1 205 [myVPNMenu addItem:statusMenuItem];
michael@1 206 [myVPNMenu addItem:[NSMenuItem separatorItem]];
michael@1 207
michael@1 208 [myConfigArray release];
michael@1 209 myConfigArray = [[[self getConfigs] sortedArrayUsingSelector:@selector(compare:)] retain];
michael@1 210 [myConfigModDatesArray release];
michael@1 211 myConfigModDatesArray = [[self getModDates:myConfigArray] retain];
michael@1 212
michael@1 213 NSEnumerator *m = [myConfigArray objectEnumerator];
michael@1 214 NSString *configString;
michael@1 215 int i = 2; // we start at MenuItem #2
michael@1 216
michael@1 217 while (configString = [m nextObject])
michael@1 218 {
michael@1 219 NSMenuItem *connectionItem = [[[NSMenuItem alloc] init] autorelease];
michael@1 220
michael@1 221 // configure connection object:
michael@1 222 VPNConnection* myConnection = [[VPNConnection alloc] initWithConfig: configString]; // initialize VPN Connection with config
michael@1 223 [myConnection setState:@"EXITING"];
michael@1 224 [myConnection setDelegate:self];
michael@1 225
michael@1 226 // handle autoconnection:
michael@1 227 NSString *autoConnectKey = [[myConnection configName] stringByAppendingString: @"autoConnect"];
michael@1 228 if([[NSUserDefaults standardUserDefaults] boolForKey:autoConnectKey])
michael@1 229 {
michael@1 230 if(![myConnection isConnected]) [myConnection connect:self];
michael@1 231 }
michael@1 232
michael@1 233 [myVPNConnectionDictionary setObject: myConnection forKey:configString];
michael@1 234
michael@1 235 // Note: The item's title will be set on demand in -validateMenuItem
michael@1 236 [connectionItem setTarget:myConnection];
michael@1 237 [connectionItem setAction:@selector(toggle:)];
michael@1 238
michael@1 239 [myVPNMenu insertItem:connectionItem atIndex:i];
michael@1 240 i++;
michael@1 241 }
michael@1 242
michael@1 243 [myVPNMenu addItem: [NSMenuItem separatorItem]];
michael@1 244 [myVPNMenu addItem: detailsItem];
michael@1 245 [myVPNMenu addItem: quitItem];
michael@1 246
michael@1 247 // Localize all menu items:
michael@1 248 NSMenuItem *item;
michael@1 249 NSEnumerator *e = [[myVPNMenu itemArray] objectEnumerator];
michael@1 250
michael@1 251 while (item = [e nextObject])
michael@1 252 {
michael@7 253 [item setTitle:NSLocalizedString([item title], nil)];
michael@1 254 }
michael@1 255 }
michael@1 256
michael@1 257 - (void)activateStatusMenu
michael@1 258 {
michael@1 259 //[theItem retain];
michael@1 260 [self updateUI];
michael@1 261
michael@1 262 // If any config files were changed/added/deleted, update the menu
michael@1 263 // We don't do it UNLESS files were changed/added/deleted because all connections are reset when the menu is updated.
michael@1 264 // activateStatusMenu is called whenever anything changes in the config directory, even the file-accessed date,
michael@1 265 // so a backup of the directory, for example, would cause disconnects if we always updated the menu.
michael@1 266 NSArray * curConfigsArray = [[self getConfigs] sortedArrayUsingSelector:@selector(compare:)];
michael@1 267 NSArray * curModDatesArray = [self getModDates:curConfigsArray];
michael@1 268
michael@1 269 if ( ! ( [myConfigArray isEqualToArray:curConfigsArray]
michael@1 270 && [myConfigModDatesArray isEqualToArray:curModDatesArray] ) ) {
michael@1 271 NSLog(@"One or more configuration files were changed, added, or deleted. All connections will be closed.\n");
michael@1 272 [self updateMenu];
michael@1 273 }
michael@1 274 }
michael@1 275
michael@1 276 - (void)connectionStateDidChange:(id)connection
michael@1 277 {
michael@1 278 [self updateTabLabels];
michael@1 279 if (connection == [self selectedConnection])
michael@1 280 {
michael@1 281 [self validateLogButtons];
michael@1 282 }
michael@1 283 }
michael@1 284
michael@1 285 -(NSArray *)getConfigs {
michael@1 286 int i = 0;
michael@1 287 NSMutableArray *array = [[[NSMutableArray alloc] init] autorelease];
michael@1 288 NSString *file;
michael@1 289 NSString *confDir = [NSHomeDirectory() stringByAppendingPathComponent: @"/Library/openvpn"];
michael@1 290 NSDirectoryEnumerator *dirEnum = [[NSFileManager defaultManager] enumeratorAtPath: confDir];
michael@1 291 while (file = [dirEnum nextObject]) {
michael@1 292 if ([[file pathExtension] isEqualToString: @"conf"] || [[file pathExtension] isEqualToString: @"ovpn"]) {
michael@1 293 [array insertObject:file atIndex:i];
michael@1 294 //if(NSDebugEnabled) NSLog(@"Object: %@ atIndex: %d\n",file,i);
michael@1 295 i++;
michael@1 296 }
michael@1 297 }
michael@1 298 return array;
michael@1 299 }
michael@1 300
michael@1 301 // Returns an array of modification date strings
michael@1 302 // Each entry in the array is the modification date of the file in the corresponding entry in fileArray
michael@1 303 -(NSArray *)getModDates:(NSArray *)fileArray {
michael@1 304 int i;
michael@1 305 NSMutableArray *array = [[[NSMutableArray alloc] init] autorelease];
michael@1 306 NSString *file;
michael@1 307 NSString *cfgDirSlash = [NSHomeDirectory() stringByAppendingString: @"/Library/openvpn/"];
michael@1 308 NSString *filePath;
michael@1 309 NSDate *modDate;
michael@1 310 NSString *modDateS;
michael@1 311 NSFileManager *fileManager = [NSFileManager defaultManager];
michael@1 312 for (i=0; i<[fileArray count]; i++) {
michael@1 313 file = [fileArray objectAtIndex:i];
michael@1 314 filePath = [cfgDirSlash stringByAppendingString:file];
michael@1 315 modDate = [[fileManager fileAttributesAtPath:filePath traverseLink:YES] fileModificationDate];
michael@1 316 if (modDate == nil) {
michael@1 317 modDateS = @"";
michael@1 318 } else if ( (modDateS = [modDate description]) == nil ) {
michael@1 319 modDateS = @"";
michael@1 320 }
michael@1 321 [array insertObject:modDateS atIndex:i];
michael@1 322 }
michael@1 323 return array;
michael@1 324 }
michael@1 325
michael@1 326 - (IBAction)validateLogButtons
michael@1 327 {
michael@1 328 //NSLog(@"validating log buttons");
michael@1 329 VPNConnection* connection = [self selectedConnection];
michael@1 330 [connectButton setEnabled:[connection isDisconnected]];
michael@1 331 [disconnectButton setEnabled:(![connection isDisconnected])];
michael@1 332 [[NSUserDefaults standardUserDefaults] synchronize];
michael@1 333 NSString *autoConnectKey = [[connection configName] stringByAppendingString:@"autoConnect"];
michael@1 334 if([[NSUserDefaults standardUserDefaults] boolForKey:autoConnectKey]) {
michael@1 335 [autoLaunchCheckbox setState:NSOnState];
michael@1 336 } else {
michael@1 337 [autoLaunchCheckbox setState:NSOffState];
michael@1 338 }
michael@1 339
michael@1 340 BOOL lol = useDNSStatus(connection);
michael@1 341 if(lol) {
michael@1 342 [useNameserverCheckbox setState:NSOnState];
michael@1 343 } else {
michael@1 344 [useNameserverCheckbox setState:NSOffState];
michael@1 345 }
michael@1 346 }
michael@1 347
michael@1 348 -(void)updateTabLabels
michael@1 349 {
michael@1 350 NSArray *keyArray = [[myVPNConnectionDictionary allKeys] sortedArrayUsingSelector: @selector(compare:)];
michael@1 351 NSArray *myConnectionArray = [myVPNConnectionDictionary objectsForKeys:keyArray notFoundMarker:[NSNull null]];
michael@1 352 NSEnumerator *connectionEnumerator = [myConnectionArray objectEnumerator];
michael@1 353 VPNConnection *myConnection;
michael@1 354
michael@1 355 // Get preferences for showing duration times
michael@1 356 BOOL showAllDurations = FALSE;
michael@1 357 BOOL showConnectedDurations = TRUE;
michael@1 358 id tmp = [[NSUserDefaults standardUserDefaults] objectForKey:@"showAllDurations"];
michael@1 359 if(tmp != nil) {
michael@1 360 showAllDurations = [[NSUserDefaults standardUserDefaults] boolForKey:@"showAllDurations"];
michael@1 361 }
michael@1 362 tmp = [[NSUserDefaults standardUserDefaults] objectForKey:@"showConnectedDurations"];
michael@1 363 if(tmp != nil) {
michael@1 364 showConnectedDurations = [[NSUserDefaults standardUserDefaults] boolForKey:@"showConnectedDurations"];
michael@1 365 }
michael@1 366
michael@1 367 int i = 0;
michael@1 368 while(myConnection = [connectionEnumerator nextObject]) {
michael@1 369 //NSLog(@"configName: %@\nconnectionState: %@\n",[myConnection configName],[myConnection state]);
michael@1 370 NSString * cState = [myConnection state];
michael@1 371 NSString * cTimeS = @"";
michael@1 372
michael@1 373 // Get connection duration if preferences say to
michael@1 374 if ( showAllDurations || ( showConnectedDurations && [cState isEqualToString: @"CONNECTED"] ) ) {
michael@1 375 NSDate * csd = [myConnection connectedSinceDate];
michael@1 376 NSTimeInterval ti = [csd timeIntervalSinceNow];
michael@1 377 long cTimeL = (long) round(-ti);
michael@1 378 if ( cTimeL >= 0 ) {
michael@1 379 if ( cTimeL < 3600 ) {
michael@1 380 cTimeS = [NSString stringWithFormat:@" %li:%02li", cTimeL/60, cTimeL%60];
michael@1 381 } else {
michael@1 382 cTimeS = [NSString stringWithFormat:@" %li:%02li:%02li", cTimeL/3600, (cTimeL/60) % 60, cTimeL%60];
michael@1 383 }
michael@1 384 }
michael@1 385 }
michael@7 386 NSString *label = [NSString stringWithFormat:@"%@ (%@%@)",[myConnection configName],NSLocalizedString(cState, nil), cTimeS];
michael@1 387 [[tabView tabViewItemAtIndex:i] setLabel:label];
michael@1 388 i++;
michael@1 389 }
michael@1 390 }
michael@1 391
michael@1 392
michael@1 393 - (void) updateUI
michael@1 394 {
michael@1 395 unsigned connectionNumber = [connectionArray count];
michael@1 396 NSString *myState;
michael@1 397 if(connectionNumber == 1) {
michael@1 398 myState = local(@"OpenVPN: 1 connection active.");
michael@1 399 } else {
michael@7 400 myState = [NSString stringWithFormat:NSLocalizedString(@"OpenVPN: %d connections active.", nil),connectionNumber];
michael@1 401 }
michael@1 402
michael@1 403 [statusMenuItem setTitle: myState];
michael@1 404 [theItem setToolTip: myState];
michael@1 405
michael@1 406 if( (![lastState isEqualToString:@"EXITING"]) && (![lastState isEqualToString:@"CONNECTED"]) ) {
michael@1 407 // override while in transitional state
michael@1 408 // Any other state shows "transitional" image:
michael@1 409 //[theItem setImage: transitionalImage];
michael@1 410 if (![theAnim isAnimating])
michael@1 411 {
michael@1 412 //NSLog(@"Starting Animation");
michael@1 413 [theAnim startAnimation];
michael@1 414 }
michael@1 415 } else
michael@1 416 {
michael@1 417 if ([theAnim isAnimating])
michael@1 418 {
michael@1 419 [theAnim stopAnimation];
michael@1 420 }
michael@1 421 }
michael@1 422 if (connectionNumber > 0 ) {
michael@1 423 [theItem setImage: connectedImage];
michael@1 424 } else {
michael@1 425 [theItem setImage: mainImage];
michael@1 426 }
michael@1 427 }
michael@1 428
michael@1 429 - (void)animationDidEnd:(NSAnimation*)animation
michael@1 430 {
michael@1 431 if ((![lastState isEqualToString:@"EXITING"]) && (![lastState isEqualToString:@"CONNECTED"]))
michael@1 432 {
michael@1 433 // NSLog(@"Starting Animation (2)");
michael@1 434 [theAnim startAnimation];
michael@1 435 }
michael@1 436 if ([connectionArray count] > 0 ) {
michael@1 437 [theItem setImage: connectedImage];
michael@1 438 } else {
michael@1 439 [theItem setImage: mainImage];
michael@1 440 }
michael@1 441 }
michael@1 442
michael@1 443 - (void)animation:(NSAnimation *)animation
michael@1 444 didReachProgressMark:(NSAnimationProgress)progress
michael@1 445 {
michael@1 446 if (animation == theAnim)
michael@1 447 {
michael@1 448 // NSLog(@"progress is %f %i", progress, lround(progress * 8));
michael@1 449 // Do an effect appropriate to progress mark.
michael@1 450 switch(lround(progress * 8))
michael@1 451 {
michael@1 452 case 1:
michael@1 453 [theItem performSelectorOnMainThread:@selector(setImage:) withObject:mainImage waitUntilDone:YES];
michael@1 454 break;
michael@1 455
michael@1 456 case 2:
michael@1 457 [theItem performSelectorOnMainThread:@selector(setImage:) withObject:transitionalImage1 waitUntilDone:YES];
michael@1 458 break;
michael@1 459
michael@1 460 case 3:
michael@1 461 [theItem performSelectorOnMainThread:@selector(setImage:) withObject:transitionalImage2 waitUntilDone:YES];
michael@1 462 break;
michael@1 463
michael@1 464 case 4:
michael@1 465 [theItem performSelectorOnMainThread:@selector(setImage:) withObject:transitionalImage3 waitUntilDone:YES];
michael@1 466 break;
michael@1 467
michael@1 468 case 5:
michael@1 469 [theItem performSelectorOnMainThread:@selector(setImage:) withObject:connectedImage waitUntilDone:YES];
michael@1 470 break;
michael@1 471
michael@1 472 case 6:
michael@1 473 [theItem performSelectorOnMainThread:@selector(setImage:) withObject:transitionalImage3 waitUntilDone:YES];
michael@1 474 break;
michael@1 475
michael@1 476 case 7:
michael@1 477 [theItem performSelectorOnMainThread:@selector(setImage:) withObject:transitionalImage2 waitUntilDone:YES];
michael@1 478 break;
michael@1 479
michael@1 480 case 8:
michael@1 481 [theItem performSelectorOnMainThread:@selector(setImage:) withObject:transitionalImage1 waitUntilDone:YES];
michael@1 482 break;
michael@1 483
michael@1 484 default:
michael@1 485 NSLog(@"Unknown progress mark %f selected by Tunnelblick animation", progress);
michael@1 486 }
michael@1 487 }
michael@1 488 }
michael@1 489
michael@1 490 - (void) tabView: (NSTabView*) inTabView willSelectTabViewItem: (NSTabViewItem*) tabViewItem
michael@1 491 {
michael@1 492 NSView* view = [[inTabView selectedTabViewItem] view];
michael@1 493 [tabViewItem setView: view];
michael@1 494 [[[self selectedLogView] textStorage] setDelegate: nil];
michael@1 495 }
michael@1 496
michael@1 497 - (void) tabView: (NSTabView*) inTabView didSelectTabViewItem: (NSTabViewItem*) tabViewItem
michael@1 498 {
michael@1 499 VPNConnection* newConnection = [self selectedConnection];
michael@1 500 NSTextView* logView = [self selectedLogView];
michael@1 501 [[logView layoutManager] replaceTextStorage: [newConnection logStorage]];
michael@1 502 //[logView setSelectedRange: NSMakeRange([[logView textStorage] length],[[logView textStorage] length])];
michael@1 503 [logView scrollRangeToVisible: NSMakeRange([[logView string] length]-1, 0)];
michael@1 504
michael@1 505 [[logView textStorage] setDelegate: self];
michael@1 506
michael@1 507 [self validateLogButtons];
michael@1 508 }
michael@1 509 - (void) textStorageDidProcessEditing: (NSNotification*) aNotification
michael@1 510 {
michael@1 511 NSNotification *notification = [NSNotification notificationWithName: @"LogDidChange"
michael@1 512 object: [self selectedLogView]];
michael@1 513 [[NSNotificationQueue defaultQueue] enqueueNotification: notification
michael@1 514 postingStyle: NSPostWhenIdle
michael@1 515 coalesceMask: NSNotificationCoalescingOnName | NSNotificationCoalescingOnSender
michael@1 516 forModes: nil];
michael@1 517 }
michael@1 518
michael@1 519 - (void) logNeedsScrolling: (NSNotification*) aNotification
michael@1 520 {
michael@1 521 NSTextView* textView = [aNotification object];
michael@1 522 [textView scrollRangeToVisible: NSMakeRange([[textView string] length]-1, 0)];
michael@1 523 }
michael@1 524
michael@1 525 - (NSTextView*) selectedLogView
michael@1 526 {
michael@1 527 NSTextView* result = [[[[[tabView selectedTabViewItem] view] subviews] lastObject] documentView];
michael@1 528 return result;
michael@1 529 }
michael@1 530
michael@1 531 - (IBAction) clearLog: (id) sender
michael@1 532 {
michael@1 533 NSString * versionInfo = [NSString stringWithFormat:local(@"Tunnelblick version %@"),[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]];
michael@1 534 NSCalendarDate* date = [NSCalendarDate date];
michael@1 535 NSString *dateText = [NSString stringWithFormat:@"%@ %@\n",[date descriptionWithCalendarFormat:@"%Y-%m-%d %H:%M:%S"],versionInfo];
michael@1 536 [[self selectedLogView] setString: [[[NSString alloc] initWithString: dateText] autorelease]];
michael@1 537 }
michael@1 538
michael@1 539 - (VPNConnection*) selectedConnection
michael@1 540 /*" Returns the connection associated with the currently selected log tab or nil on error. "*/
michael@1 541 {
michael@1 542 if (![tabView selectedTabViewItem]) {
michael@1 543 [tabView selectFirstTabViewItem: nil];
michael@1 544 }
michael@1 545
michael@1 546 NSString* configPath = [[tabView selectedTabViewItem] identifier];
michael@1 547 VPNConnection* connection = [myVPNConnectionDictionary objectForKey:configPath];
michael@1 548 NSArray *allConnections = [myVPNConnectionDictionary allValues];
michael@1 549 if(connection) return connection;
michael@1 550 else if([allConnections count]) return [allConnections objectAtIndex:0] ;
michael@1 551 else return nil;
michael@1 552 }
michael@1 553
michael@1 554
michael@1 555
michael@1 556
michael@1 557 - (IBAction)connect:(id)sender
michael@1 558 {
michael@1 559 [[self selectedConnection] connect: sender];
michael@1 560 }
michael@1 561
michael@1 562 - (IBAction)disconnect:(id)sender
michael@1 563 {
michael@1 564 [[self selectedConnection] disconnect: sender];
michael@1 565 }
michael@1 566
michael@1 567
michael@1 568 - (IBAction) openLogWindow: (id) sender
michael@1 569 {
michael@1 570 if (logWindow != nil) {
michael@1 571 [logWindow makeKeyAndOrderFront: self];
michael@1 572 [NSApp activateIgnoringOtherApps:YES];
michael@1 573 return;
michael@1 574 }
michael@1 575
michael@1 576 [NSBundle loadNibNamed: @"LogWindow" owner: self]; // also sets tabView etc.
michael@1 577
michael@1 578 // Set the window's size and position from preferences (saved when window is closed)
michael@1 579 // But only if the preference's version matches the TB version (since window size could be different in different versions of TB)
michael@1 580 NSString * tbVersion = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"];
michael@1 581 id tmp = [[NSUserDefaults standardUserDefaults] objectForKey:@"detailsWindowFrameVersion"];
michael@1 582 if (tmp != nil) {
michael@1 583 if ( [tbVersion isEqualToString: [[NSUserDefaults standardUserDefaults] stringForKey:@"detailsWindowFrameVersion"]] ) {
michael@1 584 tmp = [[NSUserDefaults standardUserDefaults] objectForKey:@"detailsWindowFrame"];
michael@1 585 if(tmp != nil) {
michael@1 586 NSString * frame = [[NSUserDefaults standardUserDefaults] stringForKey:@"detailsWindowFrame"];
michael@1 587 [logWindow setFrameFromString:frame];
michael@1 588 }
michael@1 589 }
michael@1 590 }
michael@1 591
michael@1 592 [logWindow setDelegate:self];
michael@1 593 VPNConnection *myConnection = [self selectedConnection];
michael@1 594 NSTextStorage* store = [myConnection logStorage];
michael@1 595 [[[self selectedLogView] layoutManager] replaceTextStorage: store];
michael@1 596
michael@1 597 NSEnumerator* e = [[[myVPNConnectionDictionary allKeys] sortedArrayUsingSelector: @selector(compare:)] objectEnumerator];
michael@1 598 //id test = [[myVPNConnectionDictionary allKeys] sortedArrayUsingSelector: @selector(compare:)];
michael@1 599 NSTabViewItem* initialItem;
michael@1 600 VPNConnection* connection = [myVPNConnectionDictionary objectForKey: [e nextObject]];
michael@1 601 if (connection) {
michael@1 602 initialItem = [tabView tabViewItemAtIndex: 0];
michael@1 603 [initialItem setIdentifier: [connection configPath]];
michael@1 604 [initialItem setLabel: [connection configName]];
michael@1 605
michael@1 606 int curTabIndex = 0;
michael@1 607 [tabView selectTabViewItemAtIndex:0];
michael@1 608 BOOL haveOpenConnection = ! [connection isDisconnected];
michael@1 609 while (connection = [myVPNConnectionDictionary objectForKey: [e nextObject]]) {
michael@1 610 NSTabViewItem* newItem = [[NSTabViewItem alloc] init];
michael@1 611 [newItem setIdentifier: [connection configPath]];
michael@1 612 [newItem setLabel: [connection configName]];
michael@1 613 [tabView addTabViewItem: newItem];
michael@1 614 ++curTabIndex;
michael@1 615 if ( ( ! haveOpenConnection ) && ( ! [connection isDisconnected] ) ) {
michael@1 616 [tabView selectTabViewItemAtIndex:curTabIndex];
michael@1 617 haveOpenConnection = YES;
michael@1 618 }
michael@1 619 }
michael@1 620 }
michael@1 621 [self tabView:tabView didSelectTabViewItem:initialItem];
michael@1 622 [self validateLogButtons];
michael@1 623 [self updateTabLabels];
michael@1 624
michael@1 625 // Set up a timer to update the tab labels with connections' duration times
michael@1 626 BOOL showAllDurations = FALSE;
michael@1 627 BOOL showConnectedDurations = TRUE;
michael@1 628 tmp = [[NSUserDefaults standardUserDefaults] objectForKey:@"showAllDurations"];
michael@1 629 if(tmp != nil) {
michael@1 630 showAllDurations = [[NSUserDefaults standardUserDefaults] boolForKey:@"showAllDurations"];
michael@1 631 }
michael@1 632 tmp = [[NSUserDefaults standardUserDefaults] objectForKey:@"showConnectedDurations"];
michael@1 633 if(tmp != nil) {
michael@1 634 showConnectedDurations = [[NSUserDefaults standardUserDefaults] boolForKey:@"showConnectedDurations"];
michael@1 635 }
michael@1 636
michael@1 637 if ( (showDurationsTimer == nil) && (showAllDurations || showConnectedDurations) ) {
michael@1 638 showDurationsTimer = [[NSTimer scheduledTimerWithTimeInterval:1.0
michael@1 639 target:self
michael@1 640 selector:@selector(updateTabLabels)
michael@1 641 userInfo:nil
michael@1 642 repeats:YES] retain];
michael@1 643 }
michael@1 644
michael@1 645 // Localize Buttons
michael@1 646 [clearButton setTitle:local([clearButton title])];
michael@1 647 [editButton setTitle:local([editButton title])];
michael@1 648 [connectButton setTitle:local([connectButton title])];
michael@1 649 [disconnectButton setTitle:local([disconnectButton title])];
michael@1 650 [useNameserverCheckbox setTitle:local([useNameserverCheckbox title])];
michael@1 651 [autoLaunchCheckbox setTitle:local([autoLaunchCheckbox title])];
michael@1 652
michael@1 653 [logWindow makeKeyAndOrderFront: self];
michael@1 654 [NSApp activateIgnoringOtherApps:YES];
michael@1 655 }
michael@1 656
michael@1 657 // Invoked when the Details... window (logWindow) will close
michael@1 658 - (void)windowWillClose:(NSNotification *)n
michael@1 659 {
michael@1 660 if ( [n object] == logWindow ) {
michael@1 661 // Stop and release the timer used to update the duration displays
michael@1 662 if (showDurationsTimer != nil) {
michael@1 663 [showDurationsTimer invalidate];
michael@1 664 [showDurationsTimer release];
michael@1 665 showDurationsTimer = nil;
michael@1 666 }
michael@1 667
michael@1 668 // Save the window's size and position in the preferences and save the TB version that saved them, BUT ONLY IF anything has changed
michael@1 669 NSString * frame = [logWindow stringWithSavedFrame];
michael@1 670 NSString * tbVersion = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"];
michael@1 671 BOOL saveIt = TRUE;
michael@1 672 id tmp = [[NSUserDefaults standardUserDefaults] objectForKey:@"detailsWindowFrame"];
michael@1 673 if(tmp != nil) {
michael@1 674 tmp = [[NSUserDefaults standardUserDefaults] objectForKey:@"detailsWindowFrameVersion"];
michael@1 675 if (tmp != nil) {
michael@1 676 if ( [tbVersion isEqualToString: [[NSUserDefaults standardUserDefaults] stringForKey:@"detailsWindowFrameVersion"]] ) {
michael@1 677 if ( [frame isEqualToString: [[NSUserDefaults standardUserDefaults] stringForKey:@"detailsWindowFrame"]] ) {
michael@1 678 saveIt = FALSE;
michael@1 679 }
michael@1 680 }
michael@1 681 }
michael@1 682 }
michael@1 683
michael@1 684 if (saveIt) {
michael@1 685 [[NSUserDefaults standardUserDefaults] setObject: frame forKey: @"detailsWindowFrame"];
michael@1 686 [[NSUserDefaults standardUserDefaults] setObject: [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]
michael@1 687 forKey: @"detailsWindowFrameVersion"];
michael@1 688 [[NSUserDefaults standardUserDefaults] synchronize];
michael@1 689 }
michael@1 690 }
michael@1 691 }
michael@1 692
michael@1 693 - (void) dealloc
michael@1 694 {
michael@1 695 [lastState release];
michael@1 696 [theItem release];
michael@1 697 [myConfigArray release];
michael@1 698
michael@1 699 #warning todo: release non-IB ivars here!
michael@1 700 [statusMenuItem release];
michael@1 701 [myVPNMenu release];
michael@1 702 [userDefaults release];
michael@1 703 [appDefaults release];
michael@1 704 [theItem release];
michael@1 705
michael@1 706 [mainImage release];
michael@1 707 [connectedImage release];
michael@1 708 [errorImage release];
michael@1 709 [transitionalImage release];
michael@1 710 [connectionArray release];
michael@1 711
michael@1 712
michael@1 713 [super dealloc];
michael@1 714 }
michael@1 715
michael@1 716
michael@1 717 -(void)killAllConnections
michael@1 718 {
michael@1 719 id connection;
michael@1 720 NSEnumerator* e = [connectionArray objectEnumerator];
michael@1 721
michael@1 722 while (connection = [e nextObject]) {
michael@1 723 [connection disconnect:self];
michael@1 724 if(NSDebugEnabled) NSLog(@"Killing connection.\n");
michael@1 725 }
michael@1 726 }
michael@1 727
michael@1 728 -(void)killAllOpenVPN
michael@1 729 {
michael@1 730 NSString* path = [[NSBundle mainBundle] pathForResource: @"openvpnstart"
michael@1 731 ofType: nil];
michael@1 732 NSTask* task = [[[NSTask alloc] init] autorelease];
michael@1 733 [task setLaunchPath: path];
michael@1 734
michael@1 735 NSArray *arguments = [NSArray arrayWithObjects:@"killall", nil];
michael@1 736 [task setArguments:arguments];
michael@1 737 [task launch];
michael@1 738 [task waitUntilExit];
michael@1 739 }
michael@1 740
michael@1 741 -(void)resetActiveConnections {
michael@1 742 VPNConnection *connection;
michael@1 743 NSEnumerator* e = [connectionArray objectEnumerator];
michael@1 744
michael@1 745 while (connection = [e nextObject]) {
michael@1 746 if (NSDebugEnabled) NSLog(@"Connection %@ is connected for %f seconds\n",[connection configName],[[connection connectedSinceDate] timeIntervalSinceNow]);
michael@1 747 if ([[connection connectedSinceDate] timeIntervalSinceNow] < -5) {
michael@1 748 if (NSDebugEnabled) NSLog(@"Resetting connection: %@\n",[connection configName]);
michael@1 749 [connection disconnect:self];
michael@1 750 [connection connect:self];
michael@1 751 }
michael@1 752 else {
michael@1 753 if (NSDebugEnabled) NSLog(@"Not Resetting connection: %@\n, waiting...",[connection configName]);
michael@1 754 }
michael@1 755 }
michael@1 756 }
michael@1 757
michael@1 758 -(void)createDefaultConfig
michael@1 759 {
michael@1 760 NSFileManager *fileManager = [NSFileManager defaultManager];
michael@1 761 NSString *directoryPath = [NSHomeDirectory() stringByAppendingPathComponent:@"Library/openvpn"];
michael@1 762 NSString *confResource = [[NSBundle mainBundle] pathForResource: @"openvpn"
michael@1 763 ofType: @"conf"];
michael@1 764
michael@1 765 if([[self getConfigs] count] == 0) { // if there are no config files, create a default one
michael@1 766 [NSApp activateIgnoringOtherApps:YES];
michael@1 767 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) {
michael@1 768 exit (1);
michael@1 769 }
michael@1 770 else {
michael@1 771 [fileManager createDirectoryAtPath:directoryPath attributes:nil];
michael@1 772 [fileManager copyPath:confResource toPath:[directoryPath stringByAppendingPathComponent:@"/openvpn.conf"] handler:nil];
michael@1 773 [self editConfig:self];
michael@1 774 }
michael@1 775
michael@1 776
michael@1 777 }
michael@1 778 }
michael@1 779
michael@1 780 -(IBAction)editConfig:(id)sender
michael@1 781 {
michael@1 782 VPNConnection *connection = [self selectedConnection];
michael@1 783 NSString *directoryPath = [NSHomeDirectory() stringByAppendingPathComponent:@"Library/openvpn"];
michael@1 784 NSString *configPath = [connection configPath];
michael@1 785 if(configPath == nil) configPath = @"/openvpn.conf";
michael@1 786
michael@1 787 // NSString *helper = @"/usr/sbin/chown";
michael@1 788 // NSString *userString = [NSString stringWithFormat:@"%d",getuid()];
michael@1 789 // NSArray *arguments = [NSArray arrayWithObjects:userString,configPath,nil];
michael@1 790 // AuthorizationRef authRef = [NSApplication getAuthorizationRef];
michael@1 791 // [NSApplication executeAuthorized:helper withArguments:arguments withAuthorizationRef:authRef];
michael@1 792 // AuthorizationFree(authRef,kAuthorizationFlagDefaults);
michael@1 793
michael@1 794 [[NSWorkspace sharedWorkspace] openFile:[directoryPath stringByAppendingPathComponent:configPath] withApplication:@"TextEdit"];
michael@1 795 }
michael@1 796
michael@1 797
michael@1 798 - (void) networkConfigurationDidChange
michael@1 799 {
michael@1 800 if (NSDebugEnabled) NSLog(@"Got networkConfigurationDidChange notification!!");
michael@1 801 [self resetActiveConnections];
michael@1 802 }
michael@1 803
michael@1 804 - (void) applicationWillTerminate: (NSNotification*) notification
michael@1 805 {
michael@1 806 if (NSDebugEnabled) NSLog(@"App will terminate...\n");
michael@1 807 [self cleanup];
michael@1 808 }
michael@1 809
michael@1 810 -(void)cleanup
michael@1 811 {
michael@1 812 [NSApp callDelegateOnNetworkChange: NO];
michael@1 813 [self killAllConnections];
michael@1 814 [self killAllOpenVPN];
michael@1 815 [[NSStatusBar systemStatusBar] removeStatusItem:theItem];
michael@1 816 }
michael@1 817
michael@1 818 -(void)saveUseNameserverCheckboxState:(BOOL)inBool
michael@1 819 {
michael@1 820 VPNConnection* connection = [self selectedConnection];
michael@1 821 if(connection != nil) {
michael@1 822 NSString* key = [[connection configName] stringByAppendingString: @"useDNS"];
michael@1 823 [[NSUserDefaults standardUserDefaults] setObject: [NSNumber numberWithBool: inBool] forKey: key];
michael@1 824 [[NSUserDefaults standardUserDefaults] synchronize];
michael@1 825 }
michael@1 826
michael@1 827 }
michael@1 828 -(void)saveAutoLaunchCheckboxState:(BOOL)inBool
michael@1 829 {
michael@1 830 VPNConnection* connection = [self selectedConnection];
michael@1 831 if(connection != nil) {
michael@1 832 NSString* autoConnectKey = [[connection configName] stringByAppendingString: @"autoConnect"];
michael@1 833 [[NSUserDefaults standardUserDefaults] setObject: [NSNumber numberWithBool: inBool] forKey: autoConnectKey];
michael@1 834 [[NSUserDefaults standardUserDefaults] synchronize];
michael@1 835 }
michael@1 836
michael@1 837 }
michael@1 838
michael@1 839 -(BOOL)getCurrentAutoLaunchSetting
michael@1 840 {
michael@1 841 VPNConnection *connection = [self selectedConnection];
michael@1 842 NSString *autoConnectKey = [[connection configName] stringByAppendingString:@"autoConnect"];
michael@1 843 return [[NSUserDefaults standardUserDefaults] boolForKey:autoConnectKey];
michael@1 844 }
michael@1 845
michael@1 846 - (void) setState: (NSString*) newState
michael@1 847 // Be sure to call this in main thread only
michael@1 848 {
michael@1 849 [newState retain];
michael@1 850 [lastState release];
michael@1 851 lastState = newState;
michael@1 852 //[self updateUI];
michael@1 853 [self performSelectorOnMainThread:@selector(updateUI) withObject:nil waitUntilDone:NO];
michael@1 854 }
michael@1 855
michael@1 856 -(void)addConnection:(id)sender
michael@1 857 {
michael@1 858 if(sender != nil) {
michael@1 859 [connectionArray removeObject:sender];
michael@1 860 [connectionArray addObject:sender];
michael@1 861 }
michael@1 862 }
michael@1 863
michael@1 864 -(void)removeConnection:(id)sender
michael@1 865 {
michael@1 866 if(sender != nil) [connectionArray removeObject:sender];
michael@1 867 }
michael@1 868
michael@1 869 static void signal_handler(int signalNumber)
michael@1 870 {
michael@1 871 printf("signal %d caught!\n",signalNumber);
michael@1 872
michael@1 873 if (signalNumber == SIGHUP) {
michael@1 874 printf("SIGHUP received. Restarting active connections...\n");
michael@1 875 [[NSApp delegate] resetActiveConnections];
michael@1 876 } else {
michael@1 877 printf("Received fatal signal. Cleaning up...\n");
michael@1 878 [[NSApp delegate] cleanup];
michael@1 879 exit(0);
michael@1 880 }
michael@1 881 }
michael@1 882
michael@1 883
michael@1 884 - (void) installSignalHandler
michael@1 885 {
michael@1 886 struct sigaction action;
michael@1 887
michael@1 888 action.sa_handler = signal_handler;
michael@1 889 sigemptyset(&action.sa_mask);
michael@1 890 action.sa_flags = 0;
michael@1 891
michael@1 892 if (sigaction(SIGHUP, &action, NULL) ||
michael@1 893 sigaction(SIGQUIT, &action, NULL) ||
michael@1 894 sigaction(SIGTERM, &action, NULL) ||
michael@1 895 sigaction(SIGBUS, &action, NULL) ||
michael@1 896 sigaction(SIGSEGV, &action, NULL) ||
michael@1 897 sigaction(SIGPIPE, &action, NULL)) {
michael@1 898 NSLog(@"Warning: setting signal handler failed: %s", strerror(errno));
michael@1 899 }
michael@1 900 }
michael@1 901 - (void) applicationDidFinishLaunching: (NSNotification *)notification
michael@1 902 {
michael@1 903 [NSApp callDelegateOnNetworkChange: NO];
michael@1 904 [self installSignalHandler];
michael@1 905 [NSApp setAutoLaunchOnLogin: YES];
michael@1 906 [self activateStatusMenu];
michael@1 907 if(needsRepair()){
michael@1 908 if ([self repairPermissions] != TRUE) {
michael@1 909 [NSApp terminate:self];
michael@1 910 }
michael@1 911 }
michael@1 912
michael@1 913 [updater checkForUpdatesInBackground];
michael@1 914 }
michael@1 915
michael@1 916 -(void) dmgCheck
michael@1 917 {
michael@1 918 NSString *path = [[NSBundle mainBundle] bundlePath];
michael@1 919 if([path hasPrefix:@"/Volumes/Tunnelblick"]) {
michael@1 920 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);
michael@1 921 [panel setLevel:NSStatusWindowLevel];
michael@1 922 [panel makeKeyAndOrderFront:nil];
michael@1 923 [NSApp runModalForWindow:panel];
michael@1 924 exit(2);
michael@1 925 }
michael@1 926 }
michael@1 927
michael@1 928 -(void)moveAllWindowsToForeground
michael@1 929 {
michael@1 930 NSArray *windows = [NSApp windows];
michael@1 931 NSEnumerator *e = [windows objectEnumerator];
michael@1 932 NSWindow *window = nil;
michael@1 933 while(window = [e nextObject]) {
michael@1 934 [window setLevel:NSStatusWindowLevel];
michael@1 935 }
michael@1 936 }
michael@1 937
michael@1 938 -(void) fileSystemHasChanged: (NSNotification*) n
michael@1 939 {
michael@1 940 if(NSDebugEnabled) NSLog(@"FileSystem has changed.");
michael@1 941 [self performSelectorOnMainThread: @selector(activateStatusMenu) withObject: nil waitUntilDone: YES];
michael@1 942 }
michael@1 943 -(void) kqueue: (UKKQueue*) kq receivedNotification: (NSString*) nm forFile: (NSString*) fpath {
michael@1 944
michael@1 945 [self fileSystemHasChanged: nil];
michael@1 946 }
michael@1 947
michael@1 948 -(BOOL)repairPermissions
michael@1 949 {
michael@1 950 NSBundle *thisBundle = [NSBundle mainBundle];
michael@1 951 NSString *installer = [thisBundle pathForResource:@"installer" ofType:nil];
michael@1 952
michael@1 953 AuthorizationRef authRef= [NSApplication getAuthorizationRef];
michael@1 954
michael@1 955 if(authRef == nil)
michael@1 956 return FALSE;
michael@1 957
michael@1 958 while(needsRepair()) {
michael@1 959 NSLog(@"Repairing Application...\n");
michael@1 960 [NSApplication executeAuthorized:installer withArguments:nil withAuthorizationRef:authRef];
michael@1 961 sleep(1);
michael@1 962 }
michael@1 963 AuthorizationFree(authRef, kAuthorizationFlagDefaults);
michael@1 964 return TRUE;
michael@1 965 }
michael@1 966
michael@1 967
michael@1 968
michael@1 969
michael@1 970 BOOL needsRepair()
michael@1 971 {
michael@1 972 NSBundle *thisBundle = [NSBundle mainBundle];
michael@1 973 NSString *helperPath = [thisBundle pathForResource:@"openvpnstart" ofType:nil];
michael@1 974 NSString *tunPath = [thisBundle pathForResource:@"tun.kext" ofType:nil];
michael@1 975 NSString *tapPath = [thisBundle pathForResource:@"tap.kext" ofType:nil];
michael@1 976
michael@1 977 NSString *tunExecutable = [tunPath stringByAppendingPathComponent:@"/Contents/MacOS/tun"];
michael@1 978 NSString *tapExecutable = [tapPath stringByAppendingPathComponent:@"/Contents/MacOS/tap"];
michael@1 979 NSString *openvpnPath = [thisBundle pathForResource:@"openvpn" ofType:nil];
michael@1 980
michael@1 981
michael@1 982 // check setuid helper
michael@1 983 const char *path = [helperPath UTF8String];
michael@1 984 struct stat sb;
michael@1 985 if(stat(path,&sb)) runUnrecoverableErrorPanel();
michael@1 986
michael@1 987 if (!( (sb.st_mode & S_ISUID) // set uid bit is set
michael@1 988 && (sb.st_mode & S_IXUSR) // owner may execute it
michael@1 989 && (sb.st_uid == 0) // is owned by root
michael@1 990 )) {
michael@1 991 NSLog(@"openvpnstart helper has missing set uid bit");
michael@1 992 return YES;
michael@1 993 }
michael@1 994
michael@1 995 // check files which should be only accessible by root
michael@1 996 NSArray *inaccessibleObjects = [NSArray arrayWithObjects:tunExecutable,tapExecutable,openvpnPath,nil];
michael@1 997 NSEnumerator *e = [inaccessibleObjects objectEnumerator];
michael@1 998 NSString *currentPath;
michael@1 999 NSFileManager *fileManager = [NSFileManager defaultManager];
michael@1 1000 while(currentPath = [e nextObject]) {
michael@1 1001 NSDictionary *fileAttributes = [fileManager fileAttributesAtPath:currentPath traverseLink:YES];
michael@1 1002 unsigned long perms = [fileAttributes filePosixPermissions];
michael@1 1003 NSString *octalString = [NSString stringWithFormat:@"%o",perms];
michael@1 1004 NSNumber *fileOwner = [fileAttributes fileOwnerAccountID];
michael@1 1005
michael@6 1006 if ( (![octalString isEqualToString:@"755"]) || (![fileOwner isEqualToNumber:[NSNumber numberWithInt:0]])) {
michael@1 1007 NSLog(@"File %@ has permissions: %@, is owned by %@ and needs repair...\n",currentPath,octalString,fileOwner);
michael@1 1008 return YES;
michael@1 1009 }
michael@1 1010 }
michael@1 1011
michael@1 1012 // check tun and tap driver packages
michael@1 1013 NSArray *filesToCheck = [NSArray arrayWithObjects:tunPath,tapPath,nil];
michael@1 1014 NSEnumerator *enumerator = [filesToCheck objectEnumerator];
michael@1 1015 NSString *file;
michael@1 1016 while(file = [enumerator nextObject]) {
michael@1 1017 NSDictionary *fileAttributes = [fileManager fileAttributesAtPath:file traverseLink:YES];
michael@1 1018 unsigned long perms = [fileAttributes filePosixPermissions];
michael@1 1019 NSString *octalString = [NSString stringWithFormat:@"%o",perms];
michael@1 1020 if ( (![octalString isEqualToString:@"755"]) ) {
michael@1 1021 NSLog(@"File %@ has permissions: %@ and needs repair...\n",currentPath,octalString);
michael@1 1022 return YES;
michael@1 1023 }
michael@1 1024 }
michael@1 1025 return NO;
michael@1 1026 }
michael@1 1027
michael@1 1028 -(void)willGoToSleep
michael@1 1029 {
michael@1 1030 if(NSDebugEnabled) NSLog(@"Computer will go to sleep...\n");
michael@1 1031 connectionsToRestore = [connectionArray mutableCopy];
michael@1 1032 [self killAllConnections];
michael@1 1033 }
michael@1 1034 -(void)wokeUpFromSleep
michael@1 1035 {
michael@1 1036 if(NSDebugEnabled) NSLog(@"Computer just woke up from sleep...\n");
michael@1 1037
michael@1 1038 NSEnumerator *e = [connectionsToRestore objectEnumerator];
michael@1 1039 VPNConnection *connection;
michael@1 1040 while(connection = [e nextObject]) {
michael@1 1041 if(NSDebugEnabled) NSLog(@"Restoring Connection %@",[connection configName]);
michael@1 1042 [connection connect:self];
michael@1 1043 }
michael@1 1044 }
michael@1 1045 int runUnrecoverableErrorPanel(void)
michael@1 1046 {
michael@1 1047 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);
michael@1 1048 [panel setLevel:NSStatusWindowLevel];
michael@1 1049 [panel makeKeyAndOrderFront:nil];
michael@1 1050 if( [NSApp runModalForWindow:panel] != NSAlertDefaultReturn ) {
michael@1 1051 exit(2);
michael@1 1052 } else {
michael@1 1053 [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"http://tunnelblick.net/"]];
michael@1 1054 exit(2);
michael@1 1055 }
michael@1 1056 }
michael@1 1057
michael@1 1058 -(IBAction) autoLaunchPrefButtonWasClicked: (id) sender
michael@1 1059 {
michael@1 1060 if([sender state]) {
michael@1 1061 [self saveAutoLaunchCheckboxState:TRUE];
michael@1 1062 } else {
michael@1 1063 [self saveAutoLaunchCheckboxState:FALSE];
michael@1 1064 }
michael@1 1065 }
michael@1 1066
michael@1 1067 -(IBAction) nameserverPrefButtonWasClicked: (id) sender
michael@1 1068 {
michael@1 1069 if([sender state]) {
michael@1 1070 [self saveUseNameserverCheckboxState:TRUE];
michael@1 1071 } else {
michael@1 1072 [self saveUseNameserverCheckboxState:FALSE];
michael@1 1073 }
michael@1 1074 }
michael@1 1075
michael@1 1076
michael@1 1077 @end

mercurial