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