summaryrefslogtreecommitdiff
path: root/macui/ZeroTier One/AppDelegate.m
diff options
context:
space:
mode:
Diffstat (limited to 'macui/ZeroTier One/AppDelegate.m')
-rw-r--r--macui/ZeroTier One/AppDelegate.m339
1 files changed, 339 insertions, 0 deletions
diff --git a/macui/ZeroTier One/AppDelegate.m b/macui/ZeroTier One/AppDelegate.m
new file mode 100644
index 00000000..5da6b354
--- /dev/null
+++ b/macui/ZeroTier One/AppDelegate.m
@@ -0,0 +1,339 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#import "AppDelegate.h"
+#import "NetworkMonitor.h"
+#import "Network.h"
+#import "NodeStatus.h"
+#import "JoinNetworkViewController.h"
+#import "ShowNetworksViewController.h"
+#import "PreferencesViewController.h"
+#import "AboutViewController.h"
+#import "ServiceCom.h"
+
+@implementation AppDelegate
+
+- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
+ self.statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:-2.0f];
+ self.networkListPopover = [[NSPopover alloc] init];
+ self.joinNetworkPopover = [[NSPopover alloc] init];
+ self.preferencesPopover = [[NSPopover alloc] init];
+ self.aboutPopover = [[NSPopover alloc] init];
+ self.transientMonitor = nil;
+ self.monitor = [[NetworkMonitor alloc] init];
+ self.networks = [NSMutableArray<Network*> array];
+ self.status = nil;
+
+ NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
+ NSDictionary *defaultsDict = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES] forKey:@"firstRun"];
+ [defaults registerDefaults:defaultsDict];
+
+ NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
+
+ [nc addObserver:self
+ selector:@selector(onNetworkListUpdated:)
+ name:NetworkUpdateKey
+ object:nil];
+ [nc addObserver:self
+ selector:@selector(onNodeStatusUpdated:)
+ name:StatusUpdateKey
+ object:nil];
+
+ NSString *osxMode = [defaults stringForKey:@"AppleInterfaceStyle"];
+
+ if(osxMode != nil && [osxMode isEqualToString:@"Dark"]) {
+ self.statusItem.image = [NSImage imageNamed:@"MenuBarIconMacWhite"];
+ }
+ else {
+ self.statusItem.image = [NSImage imageNamed:@"MenuBarIconMac"];
+ }
+
+ [[NSDistributedNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(darkModeChanged:)
+ name:@"AppleInterfaceThemeChangedNotification"
+ object:nil];
+
+ [self buildMenu];
+ JoinNetworkViewController *jnvc = [[JoinNetworkViewController alloc] initWithNibName:@"JoinNetworkViewController" bundle:nil];
+ jnvc.appDelegate = self;
+ self.joinNetworkPopover.contentViewController = jnvc;
+ self.joinNetworkPopover.behavior = NSPopoverBehaviorTransient;
+
+ ShowNetworksViewController *showNetworksView = [[ShowNetworksViewController alloc] initWithNibName:@"ShowNetworksViewController" bundle:nil];
+ showNetworksView.netMonitor = self.monitor;
+ self.networkListPopover.contentViewController = showNetworksView;
+ self.networkListPopover.behavior = NSPopoverBehaviorTransient;
+
+ PreferencesViewController *prefsView = [[PreferencesViewController alloc] initWithNibName:@"PreferencesViewController" bundle:nil];
+ self.preferencesPopover.contentViewController = prefsView;
+ self.preferencesPopover.behavior = NSPopoverBehaviorTransient;
+
+ self.aboutPopover.contentViewController = [[AboutViewController alloc] initWithNibName:@"AboutViewController" bundle:nil];
+ self.aboutPopover.behavior = NSPopoverBehaviorTransient;
+
+ BOOL firstRun = [defaults boolForKey:@"firstRun"];
+
+ if(firstRun) {
+ [defaults setBool:NO forKey:@"firstRun"];
+ [defaults synchronize];
+
+ [prefsView setLaunchAtLoginEnabled:YES];
+
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+ sleep(2);
+ [[NSOperationQueue mainQueue] addOperationWithBlock:^{
+ [self showAbout];
+ }];
+ });
+ }
+
+ [self.monitor updateNetworkInfo];
+ [self.monitor start];
+}
+
+- (void)applicationWillTerminate:(NSNotification *)aNotification {
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+ [[NSDistributedNotificationCenter defaultCenter] removeObserver:self
+ name:@"AppleInterfaceThemeChangedNotification"
+ object:nil];
+}
+
+- (void)showNetworks {
+ if(self.statusItem.button != nil) {
+ NSStatusBarButton *button = self.statusItem.button;
+ [self.networkListPopover showRelativeToRect:button.bounds
+ ofView:button
+ preferredEdge:NSMinYEdge];
+
+ if(self.transientMonitor == nil) {
+ self.transientMonitor =
+ [NSEvent addGlobalMonitorForEventsMatchingMask:(NSLeftMouseDown|NSRightMouseDown|NSOtherMouseDown)
+ handler:^(NSEvent * _Nonnull e) {
+ [NSEvent removeMonitor:self.transientMonitor];
+ self.transientMonitor = nil;
+ [self.networkListPopover close];
+ }];
+ }
+ }
+}
+
+- (void)joinNetwork {
+ if(self.statusItem.button != nil) {
+ NSStatusBarButton *button = self.statusItem.button;
+ [self.joinNetworkPopover showRelativeToRect:button.bounds
+ ofView:button
+ preferredEdge:NSMinYEdge];
+ if(self.transientMonitor == nil) {
+ self.transientMonitor =
+ [NSEvent addGlobalMonitorForEventsMatchingMask:(NSLeftMouseDown|NSRightMouseDown|NSOtherMouseDown)
+ handler:^(NSEvent * _Nonnull e) {
+ [NSEvent removeMonitor:self.transientMonitor];
+ self.transientMonitor = nil;
+ [self.joinNetworkPopover close];
+ }];
+ }
+ }
+}
+
+- (void)showPreferences {
+ if(self.statusItem.button != nil) {
+ NSStatusBarButton *button = self.statusItem.button;
+ [self.preferencesPopover showRelativeToRect:button.bounds
+ ofView:button
+ preferredEdge:NSMinYEdge];
+ if(self.transientMonitor == nil) {
+ [NSEvent addGlobalMonitorForEventsMatchingMask:(NSLeftMouseDown|NSRightMouseDown|NSOtherMouseDown)
+ handler:^(NSEvent * _Nonnull e) {
+ [NSEvent removeMonitor:self.transientMonitor];
+ self.transientMonitor = nil;
+ [self.preferencesPopover close];
+ }];
+ }
+ }
+}
+
+- (void)showAbout {
+ if(self.statusItem.button != nil) {
+ NSStatusBarButton *button = self.statusItem.button;
+ [self.aboutPopover showRelativeToRect:button.bounds
+ ofView:button
+ preferredEdge:NSMinYEdge];
+ if(self.transientMonitor == nil) {
+ [NSEvent addGlobalMonitorForEventsMatchingMask:(NSLeftMouseDown|NSRightMouseDown|NSOtherMouseDown)
+ handler:^(NSEvent * _Nonnull e) {
+ [NSEvent removeMonitor:self.transientMonitor];
+ self.transientMonitor = nil;
+ [self.aboutPopover close];
+ }];
+ }
+ }
+
+}
+
+- (void)quit {
+ [NSApp performSelector:@selector(terminate:) withObject:nil afterDelay:0.0];
+}
+
+- (void)onNetworkListUpdated:(NSNotification*)note {
+ NSArray<Network*> *netList = [note.userInfo objectForKey:@"networks"];
+ [(ShowNetworksViewController*)self.networkListPopover.contentViewController setNetworks:netList];
+ self.networks = [netList mutableCopy];
+
+ [self buildMenu];
+}
+
+- (void)onNodeStatusUpdated:(NSNotification*)note {
+ NodeStatus *status = [note.userInfo objectForKey:@"status"];
+ self.status = status;
+
+ [self buildMenu];
+}
+
+- (void)buildMenu {
+ NSMenu *menu = [[NSMenu alloc] init];
+
+ if(self.status != nil) {
+ NSString *nodeId = @"Node ID: ";
+ nodeId = [nodeId stringByAppendingString:self.status.address];
+ [menu addItem:[[NSMenuItem alloc] initWithTitle:nodeId
+ action:@selector(copyNodeID)
+ keyEquivalent:@""]];
+ [menu addItem:[NSMenuItem separatorItem]];
+ }
+
+ [menu addItem:[[NSMenuItem alloc] initWithTitle:@"Network Details..."
+ action:@selector(showNetworks)
+ keyEquivalent:@"n"]];
+ [menu addItem:[[NSMenuItem alloc] initWithTitle:@"Join Network..."
+ action:@selector(joinNetwork)
+ keyEquivalent:@"j"]];
+
+ [menu addItem:[NSMenuItem separatorItem]];
+
+ if([self.networks count] > 0) {
+ for(Network *net in self.networks) {
+ NSString *nwid = [NSString stringWithFormat:@"%10llx", net.nwid];
+ NSString *networkName = @"";
+ if([net.name lengthOfBytesUsingEncoding:NSUTF8StringEncoding] == 0) {
+ networkName = nwid;
+ }
+ else {
+ networkName = [NSString stringWithFormat:@"%@ (%@)", nwid, net.name];
+ }
+
+ if(net.allowDefault && net.connected) {
+ networkName = [networkName stringByAppendingString:@" [default]"];
+ }
+
+ NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:networkName
+ action:@selector(toggleNetwork:)
+ keyEquivalent:@""];
+ if(net.connected) {
+ item.state = NSOnState;
+ }
+ else {
+ item.state = NSOffState;
+ }
+
+ item.representedObject = net;
+
+ [menu addItem:item];
+ }
+
+ [menu addItem:[NSMenuItem separatorItem]];
+ }
+
+ [menu addItem:[[NSMenuItem alloc] initWithTitle:@"About ZeroTier One..."
+ action:@selector(showAbout)
+ keyEquivalent:@""]];
+ [menu addItem:[[NSMenuItem alloc] initWithTitle:@"Preferences..."
+ action:@selector(showPreferences)
+ keyEquivalent:@""]];
+
+ [menu addItem:[NSMenuItem separatorItem]];
+
+ [menu addItem:[[NSMenuItem alloc] initWithTitle:@"Quit"
+ action:@selector(quit)
+ keyEquivalent:@"q"]];
+
+ self.statusItem.menu = menu;
+}
+
+- (void)toggleNetwork:(NSMenuItem*)sender {
+ Network *network = sender.representedObject;
+ NSString *nwid = [NSString stringWithFormat:@"%10llx", network.nwid];
+
+ if(network.connected) {
+ NSError *error = nil;
+
+ [[ServiceCom sharedInstance] leaveNetwork:nwid error:&error];
+
+ if (error) {
+ NSAlert *alert = [NSAlert alertWithError:error];
+ alert.alertStyle = NSCriticalAlertStyle;
+ [alert addButtonWithTitle:@"Ok"];
+
+ [alert runModal];
+ }
+ }
+ else {
+ NSError *error = nil;
+ [[ServiceCom sharedInstance] joinNetwork:nwid
+ allowManaged:network.allowManaged
+ allowGlobal:network.allowGlobal
+ allowDefault:(network.allowDefault && ![Network defaultRouteExists:self.networks])
+ error:&error];
+
+ if (error) {
+ NSAlert *alert = [NSAlert alertWithError:error];
+ alert.alertStyle = NSCriticalAlertStyle;
+ [alert addButtonWithTitle:@"Ok"];
+
+ [alert runModal];
+ }
+ }
+}
+
+- (void)copyNodeID {
+ if(self.status != nil) {
+ NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
+ [pasteboard declareTypes:[NSArray arrayWithObject:NSPasteboardTypeString] owner:nil];
+ [pasteboard setString:self.status.address forType:NSPasteboardTypeString];
+ }
+}
+
+- (void)darkModeChanged:(NSNotification*)note {
+ NSString *osxMode = [[NSUserDefaults standardUserDefaults] stringForKey:@"AppleInterfaceStyle"];
+
+ if(osxMode != nil && [osxMode isEqualToString:@"Dark"]) {
+ self.statusItem.image = [NSImage imageNamed:@"MenuBarIconMacWhite"];
+ }
+ else {
+ self.statusItem.image = [NSImage imageNamed:@"MenuBarIconMac"];
+ }
+}
+
+- (void)closeJoinNetworkPopover {
+ if (self.transientMonitor) {
+ [NSEvent removeMonitor:self.transientMonitor];
+ self.transientMonitor = nil;
+ }
+ [self.joinNetworkPopover close];
+}
+
+@end