Wed, 10 Feb 2010 21:25:01 +0100
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 |