|
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 |