From 058233801596c706efb1cb668068f1c8e0135cb6 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Sat, 14 May 2016 13:54:49 -0700 Subject: [PATCH 01/91] Initial Commit --- ZeroTier One.xcodeproj/project.pbxproj | 278 +++++++ .../contents.xcworkspacedata | 7 + .../xcschemes/ZeroTier One.xcscheme | 91 +++ .../xcschemes/xcschememanagement.plist | 22 + ZeroTier One/AppDelegate.swift | 27 + .../AppIcon.appiconset/Contents.json | 58 ++ ZeroTier One/Base.lproj/MainMenu.xib | 680 ++++++++++++++++++ ZeroTier One/Info.plist | 34 + 8 files changed, 1197 insertions(+) create mode 100644 ZeroTier One.xcodeproj/project.pbxproj create mode 100644 ZeroTier One.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 ZeroTier One.xcodeproj/xcuserdata/grant.xcuserdatad/xcschemes/ZeroTier One.xcscheme create mode 100644 ZeroTier One.xcodeproj/xcuserdata/grant.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100644 ZeroTier One/AppDelegate.swift create mode 100644 ZeroTier One/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 ZeroTier One/Base.lproj/MainMenu.xib create mode 100644 ZeroTier One/Info.plist diff --git a/ZeroTier One.xcodeproj/project.pbxproj b/ZeroTier One.xcodeproj/project.pbxproj new file mode 100644 index 000000000..107870671 --- /dev/null +++ b/ZeroTier One.xcodeproj/project.pbxproj @@ -0,0 +1,278 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 93326BDC1CE7C816005CA2AC /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93326BDB1CE7C816005CA2AC /* AppDelegate.swift */; }; + 93326BDE1CE7C816005CA2AC /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 93326BDD1CE7C816005CA2AC /* Assets.xcassets */; }; + 93326BE11CE7C816005CA2AC /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 93326BDF1CE7C816005CA2AC /* MainMenu.xib */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 93326BD81CE7C816005CA2AC /* ZeroTier One.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "ZeroTier One.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 93326BDB1CE7C816005CA2AC /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 93326BDD1CE7C816005CA2AC /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 93326BE01CE7C816005CA2AC /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; + 93326BE21CE7C816005CA2AC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 93326BD51CE7C816005CA2AC /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 93326BCF1CE7C816005CA2AC = { + isa = PBXGroup; + children = ( + 93326BDA1CE7C816005CA2AC /* ZeroTier One */, + 93326BD91CE7C816005CA2AC /* Products */, + ); + sourceTree = ""; + }; + 93326BD91CE7C816005CA2AC /* Products */ = { + isa = PBXGroup; + children = ( + 93326BD81CE7C816005CA2AC /* ZeroTier One.app */, + ); + name = Products; + sourceTree = ""; + }; + 93326BDA1CE7C816005CA2AC /* ZeroTier One */ = { + isa = PBXGroup; + children = ( + 93326BDB1CE7C816005CA2AC /* AppDelegate.swift */, + 93326BDD1CE7C816005CA2AC /* Assets.xcassets */, + 93326BDF1CE7C816005CA2AC /* MainMenu.xib */, + 93326BE21CE7C816005CA2AC /* Info.plist */, + ); + path = "ZeroTier One"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 93326BD71CE7C816005CA2AC /* ZeroTier One */ = { + isa = PBXNativeTarget; + buildConfigurationList = 93326BE51CE7C816005CA2AC /* Build configuration list for PBXNativeTarget "ZeroTier One" */; + buildPhases = ( + 93326BD41CE7C816005CA2AC /* Sources */, + 93326BD51CE7C816005CA2AC /* Frameworks */, + 93326BD61CE7C816005CA2AC /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "ZeroTier One"; + productName = "ZeroTier One"; + productReference = 93326BD81CE7C816005CA2AC /* ZeroTier One.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 93326BD01CE7C816005CA2AC /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0730; + LastUpgradeCheck = 0730; + ORGANIZATIONNAME = "ZeroTier, Inc"; + TargetAttributes = { + 93326BD71CE7C816005CA2AC = { + CreatedOnToolsVersion = 7.3; + }; + }; + }; + buildConfigurationList = 93326BD31CE7C816005CA2AC /* Build configuration list for PBXProject "ZeroTier One" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 93326BCF1CE7C816005CA2AC; + productRefGroup = 93326BD91CE7C816005CA2AC /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 93326BD71CE7C816005CA2AC /* ZeroTier One */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 93326BD61CE7C816005CA2AC /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 93326BDE1CE7C816005CA2AC /* Assets.xcassets in Resources */, + 93326BE11CE7C816005CA2AC /* MainMenu.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 93326BD41CE7C816005CA2AC /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 93326BDC1CE7C816005CA2AC /* AppDelegate.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 93326BDF1CE7C816005CA2AC /* MainMenu.xib */ = { + isa = PBXVariantGroup; + children = ( + 93326BE01CE7C816005CA2AC /* Base */, + ); + name = MainMenu.xib; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 93326BE31CE7C816005CA2AC /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 93326BE41CE7C816005CA2AC /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + }; + name = Release; + }; + 93326BE61CE7C816005CA2AC /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = "ZeroTier One/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "com.zerotier.ZeroTier-One"; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 93326BE71CE7C816005CA2AC /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = "ZeroTier One/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "com.zerotier.ZeroTier-One"; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 93326BD31CE7C816005CA2AC /* Build configuration list for PBXProject "ZeroTier One" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 93326BE31CE7C816005CA2AC /* Debug */, + 93326BE41CE7C816005CA2AC /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 93326BE51CE7C816005CA2AC /* Build configuration list for PBXNativeTarget "ZeroTier One" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 93326BE61CE7C816005CA2AC /* Debug */, + 93326BE71CE7C816005CA2AC /* Release */, + ); + defaultConfigurationIsVisible = 0; + }; +/* End XCConfigurationList section */ + }; + rootObject = 93326BD01CE7C816005CA2AC /* Project object */; +} diff --git a/ZeroTier One.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/ZeroTier One.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..fd60338ec --- /dev/null +++ b/ZeroTier One.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/ZeroTier One.xcodeproj/xcuserdata/grant.xcuserdatad/xcschemes/ZeroTier One.xcscheme b/ZeroTier One.xcodeproj/xcuserdata/grant.xcuserdatad/xcschemes/ZeroTier One.xcscheme new file mode 100644 index 000000000..ea3f64f87 --- /dev/null +++ b/ZeroTier One.xcodeproj/xcuserdata/grant.xcuserdatad/xcschemes/ZeroTier One.xcscheme @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ZeroTier One.xcodeproj/xcuserdata/grant.xcuserdatad/xcschemes/xcschememanagement.plist b/ZeroTier One.xcodeproj/xcuserdata/grant.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 000000000..70969d2de --- /dev/null +++ b/ZeroTier One.xcodeproj/xcuserdata/grant.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,22 @@ + + + + + SchemeUserState + + ZeroTier One.xcscheme + + orderHint + 0 + + + SuppressBuildableAutocreation + + 93326BD71CE7C816005CA2AC + + primary + + + + + diff --git a/ZeroTier One/AppDelegate.swift b/ZeroTier One/AppDelegate.swift new file mode 100644 index 000000000..6ac5a5c14 --- /dev/null +++ b/ZeroTier One/AppDelegate.swift @@ -0,0 +1,27 @@ +// +// AppDelegate.swift +// ZeroTier One +// +// Created by Grant Limberg on 5/14/16. +// Copyright © 2016 ZeroTier, Inc. All rights reserved. +// + +import Cocoa + +@NSApplicationMain +class AppDelegate: NSObject, NSApplicationDelegate { + + @IBOutlet weak var window: NSWindow! + + + func applicationDidFinishLaunching(aNotification: NSNotification) { + // Insert code here to initialize your application + } + + func applicationWillTerminate(aNotification: NSNotification) { + // Insert code here to tear down your application + } + + +} + diff --git a/ZeroTier One/Assets.xcassets/AppIcon.appiconset/Contents.json b/ZeroTier One/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 000000000..2db2b1c7c --- /dev/null +++ b/ZeroTier One/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,58 @@ +{ + "images" : [ + { + "idiom" : "mac", + "size" : "16x16", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "16x16", + "scale" : "2x" + }, + { + "idiom" : "mac", + "size" : "32x32", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "32x32", + "scale" : "2x" + }, + { + "idiom" : "mac", + "size" : "128x128", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "128x128", + "scale" : "2x" + }, + { + "idiom" : "mac", + "size" : "256x256", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "256x256", + "scale" : "2x" + }, + { + "idiom" : "mac", + "size" : "512x512", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "512x512", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ZeroTier One/Base.lproj/MainMenu.xib b/ZeroTier One/Base.lproj/MainMenu.xib new file mode 100644 index 000000000..3bcfb090a --- /dev/null +++ b/ZeroTier One/Base.lproj/MainMenu.xib @@ -0,0 +1,680 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Default + + + + + + + Left to Right + + + + + + + Right to Left + + + + + + + + + + + Default + + + + + + + Left to Right + + + + + + + Right to Left + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ZeroTier One/Info.plist b/ZeroTier One/Info.plist new file mode 100644 index 000000000..d5b744c6a --- /dev/null +++ b/ZeroTier One/Info.plist @@ -0,0 +1,34 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIconFile + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + NSHumanReadableCopyright + Copyright © 2016 ZeroTier, Inc. All rights reserved. + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + From 225c2b095b03e4b0383a99863349fc28b6a3f893 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Sat, 14 May 2016 15:42:45 -0700 Subject: [PATCH 02/91] Initial start of the menu bar app. Has menu items to join/leave networks which currently displays an empty popover from the icon (icon needs to be made still) --- ZeroTier One.xcodeproj/project.pbxproj | 16 +++++ .../xcdebugger/Breakpoints_v2.xcbkptlist | 5 ++ ZeroTier One/AppDelegate.swift | 64 +++++++++++++++++- ZeroTier One/Assets.xcassets/Contents.json | 6 ++ .../MenuBarIconMac.imageset/Contents.json | 21 ++++++ .../MenuBarIconMac.png | Bin 0 -> 3810 bytes .../MenuBarIconMac@2x.png | Bin 0 -> 5444 bytes ZeroTier One/Base.lproj/MainMenu.xib | 11 +-- ZeroTier One/Info.plist | 2 + ZeroTier One/JoinNetworkViewController.swift | 18 +++++ ZeroTier One/JoinNetworkViewController.xib | 20 ++++++ ZeroTier One/ShowNetworksViewController.swift | 18 +++++ ZeroTier One/ShowNetworksViewController.xib | 20 ++++++ 13 files changed, 195 insertions(+), 6 deletions(-) create mode 100644 ZeroTier One.xcodeproj/xcuserdata/grant.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist create mode 100644 ZeroTier One/Assets.xcassets/Contents.json create mode 100644 ZeroTier One/Assets.xcassets/MenuBarIconMac.imageset/Contents.json create mode 100644 ZeroTier One/Assets.xcassets/MenuBarIconMac.imageset/MenuBarIconMac.png create mode 100644 ZeroTier One/Assets.xcassets/MenuBarIconMac.imageset/MenuBarIconMac@2x.png create mode 100644 ZeroTier One/JoinNetworkViewController.swift create mode 100644 ZeroTier One/JoinNetworkViewController.xib create mode 100644 ZeroTier One/ShowNetworksViewController.swift create mode 100644 ZeroTier One/ShowNetworksViewController.xib diff --git a/ZeroTier One.xcodeproj/project.pbxproj b/ZeroTier One.xcodeproj/project.pbxproj index 107870671..2730b9873 100644 --- a/ZeroTier One.xcodeproj/project.pbxproj +++ b/ZeroTier One.xcodeproj/project.pbxproj @@ -10,6 +10,10 @@ 93326BDC1CE7C816005CA2AC /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93326BDB1CE7C816005CA2AC /* AppDelegate.swift */; }; 93326BDE1CE7C816005CA2AC /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 93326BDD1CE7C816005CA2AC /* Assets.xcassets */; }; 93326BE11CE7C816005CA2AC /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 93326BDF1CE7C816005CA2AC /* MainMenu.xib */; }; + 93326BEA1CE7D9B9005CA2AC /* JoinNetworkViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93326BE81CE7D9B9005CA2AC /* JoinNetworkViewController.swift */; }; + 93326BEB1CE7D9B9005CA2AC /* JoinNetworkViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 93326BE91CE7D9B9005CA2AC /* JoinNetworkViewController.xib */; }; + 93326BEE1CE7DA30005CA2AC /* ShowNetworksViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93326BEC1CE7DA30005CA2AC /* ShowNetworksViewController.swift */; }; + 93326BEF1CE7DA30005CA2AC /* ShowNetworksViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 93326BED1CE7DA30005CA2AC /* ShowNetworksViewController.xib */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -18,6 +22,10 @@ 93326BDD1CE7C816005CA2AC /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 93326BE01CE7C816005CA2AC /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; 93326BE21CE7C816005CA2AC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 93326BE81CE7D9B9005CA2AC /* JoinNetworkViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JoinNetworkViewController.swift; sourceTree = ""; }; + 93326BE91CE7D9B9005CA2AC /* JoinNetworkViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = JoinNetworkViewController.xib; sourceTree = ""; }; + 93326BEC1CE7DA30005CA2AC /* ShowNetworksViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShowNetworksViewController.swift; sourceTree = ""; }; + 93326BED1CE7DA30005CA2AC /* ShowNetworksViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ShowNetworksViewController.xib; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -51,9 +59,13 @@ isa = PBXGroup; children = ( 93326BDB1CE7C816005CA2AC /* AppDelegate.swift */, + 93326BE81CE7D9B9005CA2AC /* JoinNetworkViewController.swift */, + 93326BEC1CE7DA30005CA2AC /* ShowNetworksViewController.swift */, 93326BDD1CE7C816005CA2AC /* Assets.xcassets */, 93326BDF1CE7C816005CA2AC /* MainMenu.xib */, 93326BE21CE7C816005CA2AC /* Info.plist */, + 93326BE91CE7D9B9005CA2AC /* JoinNetworkViewController.xib */, + 93326BED1CE7DA30005CA2AC /* ShowNetworksViewController.xib */, ); path = "ZeroTier One"; sourceTree = ""; @@ -116,6 +128,8 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 93326BEF1CE7DA30005CA2AC /* ShowNetworksViewController.xib in Resources */, + 93326BEB1CE7D9B9005CA2AC /* JoinNetworkViewController.xib in Resources */, 93326BDE1CE7C816005CA2AC /* Assets.xcassets in Resources */, 93326BE11CE7C816005CA2AC /* MainMenu.xib in Resources */, ); @@ -129,6 +143,8 @@ buildActionMask = 2147483647; files = ( 93326BDC1CE7C816005CA2AC /* AppDelegate.swift in Sources */, + 93326BEA1CE7D9B9005CA2AC /* JoinNetworkViewController.swift in Sources */, + 93326BEE1CE7DA30005CA2AC /* ShowNetworksViewController.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/ZeroTier One.xcodeproj/xcuserdata/grant.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/ZeroTier One.xcodeproj/xcuserdata/grant.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist new file mode 100644 index 000000000..fe2b45415 --- /dev/null +++ b/ZeroTier One.xcodeproj/xcuserdata/grant.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -0,0 +1,5 @@ + + + diff --git a/ZeroTier One/AppDelegate.swift b/ZeroTier One/AppDelegate.swift index 6ac5a5c14..e5932f5f5 100644 --- a/ZeroTier One/AppDelegate.swift +++ b/ZeroTier One/AppDelegate.swift @@ -14,8 +14,34 @@ class AppDelegate: NSObject, NSApplicationDelegate { @IBOutlet weak var window: NSWindow! + let statusItem = NSStatusBar.systemStatusBar().statusItemWithLength(-2.0) + + let networkListPopover = NSPopover() + let joinNetworkPopover = NSPopover() + + var transientMonitor: AnyObject? = nil + func applicationDidFinishLaunching(aNotification: NSNotification) { - // Insert code here to initialize your application + + + statusItem.image = NSImage(named: "MenuBarIconMac") + + let menu = NSMenu() + + menu.addItem(NSMenuItem(title: "Show Networks", action: #selector(AppDelegate.showNetworks), keyEquivalent: "n")) + menu.addItem(NSMenuItem(title: "Join Network", action: #selector(AppDelegate.joinNetwork), keyEquivalent: "j")) + menu.addItem(NSMenuItem.separatorItem()) + menu.addItem(NSMenuItem(title: "Quit ZeroTier One", action: #selector(AppDelegate.quit), keyEquivalent: "q")) + + statusItem.menu = menu + + joinNetworkPopover.contentViewController = JoinNetworkViewController( + nibName: "JoinNetworkViewController", bundle: nil) + joinNetworkPopover.behavior = .Transient + + networkListPopover.contentViewController = ShowNetworksViewController( + nibName: "ShowNetworksViewController", bundle: nil) + networkListPopover.behavior = .Transient } func applicationWillTerminate(aNotification: NSNotification) { @@ -23,5 +49,41 @@ class AppDelegate: NSObject, NSApplicationDelegate { } + func showNetworks() { + if let button = statusItem.button { + networkListPopover.showRelativeToRect(button.bounds, ofView: button, preferredEdge: .MinY) + + if transientMonitor == nil { + transientMonitor = NSEvent.addGlobalMonitorForEventsMatchingMask( + [.LeftMouseDownMask, .RightMouseDownMask, .OtherMouseDownMask]) { (event: NSEvent) -> Void in + + NSEvent.removeMonitor(self.transientMonitor!) + self.transientMonitor = nil + self.networkListPopover.close() + } + } + } + } + + func joinNetwork() { + if let button = statusItem.button { + joinNetworkPopover.showRelativeToRect(button.bounds, ofView: button, preferredEdge: .MinY) + + if transientMonitor == nil { + transientMonitor = NSEvent.addGlobalMonitorForEventsMatchingMask( + [.LeftMouseDownMask, .RightMouseDownMask, .OtherMouseDownMask]) { (event: NSEvent) -> Void in + + NSEvent.removeMonitor(self.transientMonitor!) + self.transientMonitor = nil + self.joinNetworkPopover.close() + + } + } + } + } + + func quit() { + NSApp.performSelector(#selector(NSApp.terminate(_:)), withObject: nil, afterDelay: 0.0) + } } diff --git a/ZeroTier One/Assets.xcassets/Contents.json b/ZeroTier One/Assets.xcassets/Contents.json new file mode 100644 index 000000000..da4a164c9 --- /dev/null +++ b/ZeroTier One/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ZeroTier One/Assets.xcassets/MenuBarIconMac.imageset/Contents.json b/ZeroTier One/Assets.xcassets/MenuBarIconMac.imageset/Contents.json new file mode 100644 index 000000000..ab75ef9a7 --- /dev/null +++ b/ZeroTier One/Assets.xcassets/MenuBarIconMac.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "mac", + "filename" : "MenuBarIconMac.png", + "scale" : "1x" + }, + { + "idiom" : "mac", + "filename" : "MenuBarIconMac@2x.png", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + }, + "properties" : { + "template-rendering-intent" : "template" + } +} \ No newline at end of file diff --git a/ZeroTier One/Assets.xcassets/MenuBarIconMac.imageset/MenuBarIconMac.png b/ZeroTier One/Assets.xcassets/MenuBarIconMac.imageset/MenuBarIconMac.png new file mode 100644 index 0000000000000000000000000000000000000000..74e59fe79bd676b378d66de2d12d05dfa26e3746 GIT binary patch literal 3810 zcmdT{dsGu=79ZQHm6z4@NUANyhzhHd#}LTF5QQL+9)diAh*e-RnS|IR6Egz|DlGMZ zRiL!sT3AuEs8vw#fi4w`h=^~owmwie)<@C0nu3dOk)26+M1&r9&)GjV=Oi<8zx(^$ z`+dLf-ubdRB0R*^X{-|f0IssopeXV>hWfrgjQp*y)J-QZ?`Z<$0RXTk*ZCzplze6& zp;2-GSUed3GFAY{VI`JK_%oSQpx%!?PeQBii)6sN zr$tVXO^vX*EDn1hm@x?>Fgyt}_GbASws&|?0@R`%BrY{FC`^U)1~ev{bhbrNA#7BK z>QMuMlQ`~x`I6%F0$KZoh{1BSziSt;a#cMCXRKwOb(MA=a&jwPEA zbXE$PexHh@NMdUJMbx-1nG!O_pd}=b*d|MVbDyqu)NGTMq=PA#gxd{qv^n_qDkYLh zr4c4ft<|9tJSrrRA)6)j;|l%!K!`g&>v`{j5fjuO3l&+aWM7wwuOlsER4f|15kyC)R;*R6B4Z+)}U;o zK_h1O867;yy&{s6b*_R5I0z*^k4? z;fZieO_*U58lWM|;B8VL2-cT=Z-j1oa}sL!GkxuBHm|`+v<94j4G1a=l92m>rPZoL zLLaqK3GsYD9t0^tC5MNAN`$8deUy9%LY2Nk#Mf8MwzE6H2af?GwibZc_LHgy+HLI* z6SM6L{(mF{k^~zxs7k`_nOcvb|J?ev#{Wt?2=x&NxdH^_356;U7VsdDdImW#qVnUQ z0-gZk^vTW@@OhyiHc2;~sMeh5=x2pOjD9i~Pgu9{1ppp;5AALMdTUr?yzp`ZX! zp(>@C%U24Kp0#oy>kedJS%*WG{RC&LAMyw%aU!8T$3cnHV&li|QMaqUeoK8);;nEqYre91H;~dcrC_@=Er&Z{ z2#s&}rK;V1zw`Q}nY{Y&Z{}?~T8XuGRxP~~eto_6r)%eyJZUkXGuOP>fH%^NE;Utc zU!A<|zb8)0C|v2we6GH;`}tkgp{%NpDlTt-ku+%u4O`ypBu9%13N@l~__-o-#cC*VU6&D!uRp?=QV&m6fWJb0&Yf zf_5XYXkMXy-K{x3E*HEyqtB*&dE{vP{gxlP=#TQIcyA~g*?EB{_3miv{4tqlDV{F) zBE6{YNw#zOQ;(eDj_9?ye>l&hO~~FaU&fRc-8h3M7YUo2x7}O{l)7dH@5_ESOCL5z zd}e&{JATh+<_!rxij8b($XI(bd!h4EmQOzxrwtv;u;@Gwa4<{<5 z%V%8mFWR`FcJoOmUG9$WFt>^f7X4ymB407ZJ!5tlTX*dE@vNyy6(NGZmaYV?rI8Pf zTVjre%;?O?A3oO7S)U%d_CC<~Q)b}r`HwA68loFMEoh2vsK{MBvniwwtQIw=od2ve!29B8npt=w;&G+pEV}>HTySxp-nfYsPo!vKl z(WA2Jx$)OLtAPnk-#(4rmi2&TSeiB-J8YrHJpKrbK4_&Yv4kt0%tnpJS2Bilv**YX zI{%N#BR}UJuG0HoSdzN7?3WJTOHVzDADv#4=-ayd$Bc%;2VTb9+L+iI2)x?0#MO&) zH!^SiefP6_SK&Kb)=bXII9Sb%7~1&7_|Gd6@7zvyQGhP{^l_i0e7&i`>Njh_lc-E5 zbDqJ4u1Hz_7hNQtuAap8to_$Hm)dbLmm~1Yej{osJT?VC7GHKF+;b;v4|PdcHZAyg z$FX?n^1>&sOMD(h<}{?AIl$QSuuc~pUinmZyLs2|>l*G0Vw>LKw7;AFWd5&Tj{mv# z=7NVi%3MA!vfkvKdT^e3cwzqe8Eyxjh3n2-JXNO|FCLme+Z;dY$cY!!KS|lF@SyU* H#AW{m`BNzA literal 0 HcmV?d00001 diff --git a/ZeroTier One/Assets.xcassets/MenuBarIconMac.imageset/MenuBarIconMac@2x.png b/ZeroTier One/Assets.xcassets/MenuBarIconMac.imageset/MenuBarIconMac@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..1c99f851f4019b99d756e617bb59e1f973f16850 GIT binary patch literal 5444 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 - + - + + @@ -11,7 +12,7 @@ - + @@ -666,11 +667,11 @@ - + - + diff --git a/ZeroTier One/Info.plist b/ZeroTier One/Info.plist index d5b744c6a..f5d0dc948 100644 --- a/ZeroTier One/Info.plist +++ b/ZeroTier One/Info.plist @@ -2,6 +2,8 @@ + LSUIElement + CFBundleDevelopmentRegion en CFBundleExecutable diff --git a/ZeroTier One/JoinNetworkViewController.swift b/ZeroTier One/JoinNetworkViewController.swift new file mode 100644 index 000000000..d071ce912 --- /dev/null +++ b/ZeroTier One/JoinNetworkViewController.swift @@ -0,0 +1,18 @@ +// +// JoinNetworkViewController.swift +// ZeroTier One +// +// Created by Grant Limberg on 5/14/16. +// Copyright © 2016 ZeroTier, Inc. All rights reserved. +// + +import Cocoa + +class JoinNetworkViewController: NSViewController { + + override func viewDidLoad() { + super.viewDidLoad() + // Do view setup here. + } + +} diff --git a/ZeroTier One/JoinNetworkViewController.xib b/ZeroTier One/JoinNetworkViewController.xib new file mode 100644 index 000000000..7f3f13297 --- /dev/null +++ b/ZeroTier One/JoinNetworkViewController.xib @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/ZeroTier One/ShowNetworksViewController.swift b/ZeroTier One/ShowNetworksViewController.swift new file mode 100644 index 000000000..0ecb38f53 --- /dev/null +++ b/ZeroTier One/ShowNetworksViewController.swift @@ -0,0 +1,18 @@ +// +// ShowNetworksViewController.swift +// ZeroTier One +// +// Created by Grant Limberg on 5/14/16. +// Copyright © 2016 ZeroTier, Inc. All rights reserved. +// + +import Cocoa + +class ShowNetworksViewController: NSViewController { + + override func viewDidLoad() { + super.viewDidLoad() + // Do view setup here. + } + +} diff --git a/ZeroTier One/ShowNetworksViewController.xib b/ZeroTier One/ShowNetworksViewController.xib new file mode 100644 index 000000000..2fe5f22f3 --- /dev/null +++ b/ZeroTier One/ShowNetworksViewController.xib @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + From b4a80579948038989c382fd41101f1f489245124 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Mon, 16 May 2016 17:51:14 -0700 Subject: [PATCH 03/91] limit the join network combobox to hex characters. Join only clickable when a valid network is entered --- ZeroTier One.xcodeproj/project.pbxproj | 1 + ZeroTier One/JoinNetworkViewController.swift | 72 +++++++++++++++++++- ZeroTier One/JoinNetworkViewController.xib | 35 +++++++++- 3 files changed, 103 insertions(+), 5 deletions(-) diff --git a/ZeroTier One.xcodeproj/project.pbxproj b/ZeroTier One.xcodeproj/project.pbxproj index 2730b9873..4fb0a76e2 100644 --- a/ZeroTier One.xcodeproj/project.pbxproj +++ b/ZeroTier One.xcodeproj/project.pbxproj @@ -287,6 +287,7 @@ 93326BE71CE7C816005CA2AC /* Release */, ); defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; diff --git a/ZeroTier One/JoinNetworkViewController.swift b/ZeroTier One/JoinNetworkViewController.swift index d071ce912..0aaaf10f0 100644 --- a/ZeroTier One/JoinNetworkViewController.swift +++ b/ZeroTier One/JoinNetworkViewController.swift @@ -8,11 +8,77 @@ import Cocoa -class JoinNetworkViewController: NSViewController { +extension String { + func contains(find: String) -> Bool { + return self.rangeOfString(find) != nil + } + + func trunc(length: Int, trailing: String? = "...") -> String { + if self.characters.count > length { + return self.substringToIndex(self.startIndex.advancedBy(length)) + (trailing ?? "") + } else { + return self + } + } +} + +class JoinNetworkViewController: NSViewController, NSComboBoxDelegate { + + @IBOutlet var network: NSComboBox! + @IBOutlet var joinButton: NSButton! override func viewDidLoad() { super.viewDidLoad() - // Do view setup here. + network.setDelegate(self) } - + + @IBAction func onJoinClicked(sender: AnyObject?) { + let networkId = UInt64(network.stringValue, radix: 16) + + // TODO: Execute join network call + + network.stringValue = "" + } + + + // NSComboBoxDelegate Methods + + override func controlTextDidChange(obj: NSNotification) { + let cb = obj.object as! NSComboBox + let value = cb.stringValue + + + let allowedCharacters = "abcdefABCDEF0123456789" + + var outValue = "" + + for char in value.characters { + if !allowedCharacters.contains(String(char)) { + NSBeep() + } + else { + outValue += String(char) + } + } + + + + if outValue.lengthOfBytesUsingEncoding(NSUTF8StringEncoding) == 16 { + joinButton.enabled = true + } + else { + + if outValue.lengthOfBytesUsingEncoding(NSUTF8StringEncoding) > 16 { + outValue = outValue.trunc(16, trailing: "") + NSBeep() + joinButton.enabled = true + } + else { + joinButton.enabled = false + } + } + + cb.stringValue = outValue + } + // end NSComboBoxDelegate Methods } diff --git a/ZeroTier One/JoinNetworkViewController.xib b/ZeroTier One/JoinNetworkViewController.xib index 7f3f13297..2de44c9df 100644 --- a/ZeroTier One/JoinNetworkViewController.xib +++ b/ZeroTier One/JoinNetworkViewController.xib @@ -1,5 +1,5 @@ - + @@ -7,14 +7,45 @@ + + - + + + + + + + + + + + + + + + + + + + + + From ccbd6f97cda44999230222b8b54d077b7a754e0c Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Mon, 16 May 2016 18:02:26 -0700 Subject: [PATCH 04/91] remove some mistakenly committed files and add .gitignore for Xcode projects --- .gitignore | 105 ++++++++++++++++++ .../xcdebugger/Breakpoints_v2.xcbkptlist | 5 - .../xcschemes/ZeroTier One.xcscheme | 91 --------------- .../xcschemes/xcschememanagement.plist | 22 ---- 4 files changed, 105 insertions(+), 118 deletions(-) create mode 100644 .gitignore delete mode 100644 ZeroTier One.xcodeproj/xcuserdata/grant.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist delete mode 100644 ZeroTier One.xcodeproj/xcuserdata/grant.xcuserdatad/xcschemes/ZeroTier One.xcscheme delete mode 100644 ZeroTier One.xcodeproj/xcuserdata/grant.xcuserdatad/xcschemes/xcschememanagement.plist diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..c62c79396 --- /dev/null +++ b/.gitignore @@ -0,0 +1,105 @@ +##### +# OS X temporary files that should never be committed +# +# c.f. http://www.westwind.com/reference/os-x/invisibles.html + +.DS_Store + +# c.f. http://www.westwind.com/reference/os-x/invisibles.html + +.Trashes + +# c.f. http://www.westwind.com/reference/os-x/invisibles.html + +*.swp + +# +# *.lock - this is used and abused by many editors for many different things. +# For the main ones I use (e.g. Eclipse), it should be excluded +# from source-control, but YMMV. +# (lock files are usually local-only file-synchronization on the local FS that should NOT go in git) +# c.f. the "OPTIONAL" section at bottom though, for tool-specific variations! +# +# In particular, if you're using CocoaPods, you'll want to comment-out this line: +*.lock + + +# +# profile - REMOVED temporarily (on double-checking, I can't find it in OS X docs?) +#profile + + +#### +# Xcode temporary files that should never be committed +# +# NB: NIB/XIB files still exist even on Storyboard projects, so we want this... + +*~.nib + + +#### +# Xcode build files - +# +# NB: slash on the end, so we only remove the FOLDER, not any files that were badly named "DerivedData" + +DerivedData/ + +# NB: slash on the end, so we only remove the FOLDER, not any files that were badly named "build" + +build/ + + +##### +# Xcode private settings (window sizes, bookmarks, breakpoints, custom executables, smart groups) +# +# This is complicated: +# +# SOMETIMES you need to put this file in version control. +# Apple designed it poorly - if you use "custom executables", they are +# saved in this file. +# 99% of projects do NOT use those, so they do NOT want to version control this file. +# ..but if you're in the 1%, comment out the line "*.pbxuser" + +# .pbxuser: http://lists.apple.com/archives/xcode-users/2004/Jan/msg00193.html + +*.pbxuser + +# .mode1v3: http://lists.apple.com/archives/xcode-users/2007/Oct/msg00465.html + +*.mode1v3 + +# .mode2v3: http://lists.apple.com/archives/xcode-users/2007/Oct/msg00465.html + +*.mode2v3 + +# .perspectivev3: http://stackoverflow.com/questions/5223297/xcode-projects-what-is-a-perspectivev3-file + +*.perspectivev3 + +# NB: also, whitelist the default ones, some projects need to use these +!default.pbxuser +!default.mode1v3 +!default.mode2v3 +!default.perspectivev3 + + +#### +# Xcode 4 - semi-personal settings +# +# Apple Shared data that Apple put in the wrong folder +# c.f. http://stackoverflow.com/a/19260712/153422 +# FROM ANSWER: Apple says "don't ignore it" +# FROM COMMENTS: Apple is wrong; Apple code is too buggy to trust; there are no known negative side-effects to ignoring Apple's unofficial advice and instead doing the thing that actively fixes bugs in Xcode +# Up to you, but ... current advice: ignore it. +*.xccheckout + +# +# +# OPTION 1: --------------------------------- +# throw away ALL personal settings (including custom schemes! +# - unless they are "shared") +# As per build/ and DerivedData/, this ought to have a trailing slash +# +# NB: this is exclusive with OPTION 2 below +xcuserdata/ + diff --git a/ZeroTier One.xcodeproj/xcuserdata/grant.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/ZeroTier One.xcodeproj/xcuserdata/grant.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist deleted file mode 100644 index fe2b45415..000000000 --- a/ZeroTier One.xcodeproj/xcuserdata/grant.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ /dev/null @@ -1,5 +0,0 @@ - - - diff --git a/ZeroTier One.xcodeproj/xcuserdata/grant.xcuserdatad/xcschemes/ZeroTier One.xcscheme b/ZeroTier One.xcodeproj/xcuserdata/grant.xcuserdatad/xcschemes/ZeroTier One.xcscheme deleted file mode 100644 index ea3f64f87..000000000 --- a/ZeroTier One.xcodeproj/xcuserdata/grant.xcuserdatad/xcschemes/ZeroTier One.xcscheme +++ /dev/null @@ -1,91 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ZeroTier One.xcodeproj/xcuserdata/grant.xcuserdatad/xcschemes/xcschememanagement.plist b/ZeroTier One.xcodeproj/xcuserdata/grant.xcuserdatad/xcschemes/xcschememanagement.plist deleted file mode 100644 index 70969d2de..000000000 --- a/ZeroTier One.xcodeproj/xcuserdata/grant.xcuserdatad/xcschemes/xcschememanagement.plist +++ /dev/null @@ -1,22 +0,0 @@ - - - - - SchemeUserState - - ZeroTier One.xcscheme - - orderHint - 0 - - - SuppressBuildableAutocreation - - 93326BD71CE7C816005CA2AC - - primary - - - - - From acd6978a3094ae7cffdfb5cf79e4c2473ab3bb0f Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Mon, 16 May 2016 18:26:33 -0700 Subject: [PATCH 05/91] set the combo box to use a data source and remember the last 20 networks input --- ZeroTier One/JoinNetworkViewController.swift | 71 +++++++++++++++++++- ZeroTier One/JoinNetworkViewController.xib | 16 ++--- 2 files changed, 77 insertions(+), 10 deletions(-) diff --git a/ZeroTier One/JoinNetworkViewController.swift b/ZeroTier One/JoinNetworkViewController.swift index 0aaaf10f0..c9fdcebe1 100644 --- a/ZeroTier One/JoinNetworkViewController.swift +++ b/ZeroTier One/JoinNetworkViewController.swift @@ -22,22 +22,54 @@ extension String { } } -class JoinNetworkViewController: NSViewController, NSComboBoxDelegate { +let joinedNetworksKey = "com.zerotier.one.joined-networks" + + +class JoinNetworkViewController: NSViewController, NSComboBoxDelegate, NSComboBoxDataSource { @IBOutlet var network: NSComboBox! @IBOutlet var joinButton: NSButton! + var values: [String] = [String]() + override func viewDidLoad() { super.viewDidLoad() network.setDelegate(self) + network.dataSource = self + } + + override func viewWillAppear() { + let defaults = NSUserDefaults.standardUserDefaults() + + let vals = defaults.stringArrayForKey(joinedNetworksKey) + + if let v = vals { + values = v + } + } + + override func viewDidDisappear() { + let defaults = NSUserDefaults.standardUserDefaults() + + defaults.setObject(values, forKey: joinedNetworksKey) } @IBAction func onJoinClicked(sender: AnyObject?) { - let networkId = UInt64(network.stringValue, radix: 16) + let networkString = network.stringValue + let networkId = UInt64(networkString, radix: 16) // TODO: Execute join network call network.stringValue = "" + + + if !values.contains(networkString) { + values.insert(networkString, atIndex: 0) + + while values.count > 20 { + values.removeLast() + } + } } @@ -81,4 +113,39 @@ class JoinNetworkViewController: NSViewController, NSComboBoxDelegate { cb.stringValue = outValue } // end NSComboBoxDelegate Methods + + + // NSComboBoxDataSource methods + + func numberOfItemsInComboBox(aComboBox: NSComboBox) -> Int { + return values.count + } + + func comboBox(aComboBox: NSComboBox, objectValueForItemAtIndex index: Int) -> AnyObject { + return values[index] + } + + func comboBox(aComboBox: NSComboBox, indexOfItemWithStringValue string: String) -> Int { + + var counter = 0 + for val in values { + if val == string { + return counter + } + counter += 1 + } + return NSNotFound + } + + func comboBox(aComboBox: NSComboBox, completedString string: String) -> String? { + for val in values { + if val.hasPrefix(string) { + return val + } + } + + return nil + } + + // end NSComboBoxDataSorce methods } diff --git a/ZeroTier One/JoinNetworkViewController.xib b/ZeroTier One/JoinNetworkViewController.xib index 2de44c9df..7a2ffe708 100644 --- a/ZeroTier One/JoinNetworkViewController.xib +++ b/ZeroTier One/JoinNetworkViewController.xib @@ -26,14 +26,6 @@ - - - - - - - - + + + + + + + + From 4653ebc97ff15db5f22066a49d7fe4e5f8a9345d Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Mon, 16 May 2016 19:08:00 -0700 Subject: [PATCH 06/91] add table view to show networks view --- ZeroTier One/ShowNetworksViewController.swift | 2 + ZeroTier One/ShowNetworksViewController.xib | 100 +++++++++++++++++- 2 files changed, 101 insertions(+), 1 deletion(-) diff --git a/ZeroTier One/ShowNetworksViewController.swift b/ZeroTier One/ShowNetworksViewController.swift index 0ecb38f53..8bf84a1cb 100644 --- a/ZeroTier One/ShowNetworksViewController.swift +++ b/ZeroTier One/ShowNetworksViewController.swift @@ -10,6 +10,8 @@ import Cocoa class ShowNetworksViewController: NSViewController { + @IBOutlet var tableView: NSTableView! + override func viewDidLoad() { super.viewDidLoad() // Do view setup here. diff --git a/ZeroTier One/ShowNetworksViewController.xib b/ZeroTier One/ShowNetworksViewController.xib index 2fe5f22f3..e17bc5a97 100644 --- a/ZeroTier One/ShowNetworksViewController.xib +++ b/ZeroTier One/ShowNetworksViewController.xib @@ -1,5 +1,5 @@ - + @@ -7,6 +7,7 @@ + @@ -15,6 +16,103 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 40198d6c1398da33fb1c6340c03927da890d816e Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Mon, 16 May 2016 19:25:20 -0700 Subject: [PATCH 07/91] call super functions on a few overrides --- ZeroTier One/JoinNetworkViewController.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ZeroTier One/JoinNetworkViewController.swift b/ZeroTier One/JoinNetworkViewController.swift index c9fdcebe1..1ac3c7ea6 100644 --- a/ZeroTier One/JoinNetworkViewController.swift +++ b/ZeroTier One/JoinNetworkViewController.swift @@ -39,6 +39,8 @@ class JoinNetworkViewController: NSViewController, NSComboBoxDelegate, NSComboBo } override func viewWillAppear() { + super.viewWillAppear() + let defaults = NSUserDefaults.standardUserDefaults() let vals = defaults.stringArrayForKey(joinedNetworksKey) @@ -49,6 +51,8 @@ class JoinNetworkViewController: NSViewController, NSComboBoxDelegate, NSComboBo } override func viewDidDisappear() { + super.viewWillDisappear() + let defaults = NSUserDefaults.standardUserDefaults() defaults.setObject(values, forKey: joinedNetworksKey) From da30d2898e59d34dc47b251a07376db70fcd3e96 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Mon, 16 May 2016 19:58:24 -0700 Subject: [PATCH 08/91] beginning of communicating with the zero tier service --- ZeroTier One.xcodeproj/project.pbxproj | 4 + ZeroTier One/Info.plist | 5 + ZeroTier One/ServiceCom.swift | 97 +++++++++++++++++++ ZeroTier One/ShowNetworksViewController.swift | 6 ++ 4 files changed, 112 insertions(+) create mode 100644 ZeroTier One/ServiceCom.swift diff --git a/ZeroTier One.xcodeproj/project.pbxproj b/ZeroTier One.xcodeproj/project.pbxproj index 4fb0a76e2..d0441a5cb 100644 --- a/ZeroTier One.xcodeproj/project.pbxproj +++ b/ZeroTier One.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 9330F1351CEAB4C400687EC8 /* ServiceCom.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9330F1341CEAB4C400687EC8 /* ServiceCom.swift */; }; 93326BDC1CE7C816005CA2AC /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93326BDB1CE7C816005CA2AC /* AppDelegate.swift */; }; 93326BDE1CE7C816005CA2AC /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 93326BDD1CE7C816005CA2AC /* Assets.xcassets */; }; 93326BE11CE7C816005CA2AC /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 93326BDF1CE7C816005CA2AC /* MainMenu.xib */; }; @@ -17,6 +18,7 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 9330F1341CEAB4C400687EC8 /* ServiceCom.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServiceCom.swift; sourceTree = ""; }; 93326BD81CE7C816005CA2AC /* ZeroTier One.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "ZeroTier One.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 93326BDB1CE7C816005CA2AC /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 93326BDD1CE7C816005CA2AC /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; @@ -61,6 +63,7 @@ 93326BDB1CE7C816005CA2AC /* AppDelegate.swift */, 93326BE81CE7D9B9005CA2AC /* JoinNetworkViewController.swift */, 93326BEC1CE7DA30005CA2AC /* ShowNetworksViewController.swift */, + 9330F1341CEAB4C400687EC8 /* ServiceCom.swift */, 93326BDD1CE7C816005CA2AC /* Assets.xcassets */, 93326BDF1CE7C816005CA2AC /* MainMenu.xib */, 93326BE21CE7C816005CA2AC /* Info.plist */, @@ -145,6 +148,7 @@ 93326BDC1CE7C816005CA2AC /* AppDelegate.swift in Sources */, 93326BEA1CE7D9B9005CA2AC /* JoinNetworkViewController.swift in Sources */, 93326BEE1CE7DA30005CA2AC /* ShowNetworksViewController.swift in Sources */, + 9330F1351CEAB4C400687EC8 /* ServiceCom.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/ZeroTier One/Info.plist b/ZeroTier One/Info.plist index f5d0dc948..7a6ab7230 100644 --- a/ZeroTier One/Info.plist +++ b/ZeroTier One/Info.plist @@ -2,6 +2,11 @@ + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + LSUIElement CFBundleDevelopmentRegion diff --git a/ZeroTier One/ServiceCom.swift b/ZeroTier One/ServiceCom.swift new file mode 100644 index 000000000..a1bb0a3c2 --- /dev/null +++ b/ZeroTier One/ServiceCom.swift @@ -0,0 +1,97 @@ +// +// ServiceCom.swift +// ZeroTier One +// +// Created by Grant Limberg on 5/16/16. +// Copyright © 2016 ZeroTier, Inc. All rights reserved. +// + +import Cocoa + +class ServiceCom: NSObject { + static let baseURL = "http://localhost:9993" + static var key: NSString? = "ddeb3b1e6996b6b4f2d12d10" + + static func getNetworkList() { + + let urlString = baseURL + "/network?auth=\(ServiceCom.key!)" + + let url = NSURL(string: urlString) + + if let u = url { + let request = NSMutableURLRequest(URL: u) + let session = NSURLSession.sharedSession() + let task = session.dataTaskWithRequest(request) { (data, response, error) in + let httpResponse = response as! NSHTTPURLResponse + let status = httpResponse.statusCode + + if status == 200 { + do { + let json = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions()) + print("\(json)") + } + catch { + print("JSON Error: \(error)") + } + } + } + + task.resume() + } + else { + print("bad URL") + } + } + + + static func joinNetwork(network: String) { + let urlString = baseURL + "/network/\(network)?auth=\(ServiceCom.key!)" + let url = NSURL(string: urlString) + + if let u = url { + let request = NSMutableURLRequest(URL: u) + request.HTTPMethod = "POST" + + let session = NSURLSession.sharedSession() + let task = session.dataTaskWithRequest(request) { (data, response, error) in + let httpResponse = response as! NSHTTPURLResponse + let status = httpResponse.statusCode + + if status == 200 { + print("join ok") + } + else { + print("join error: \(status)") + } + } + + task.resume() + } + } + + static func leaveNetwork(network: String) { + let urlString = baseURL + "/network/\(network)?auth=\(ServiceCom.key!)" + let url = NSURL(string: urlString) + + if let u = url { + let request = NSMutableURLRequest(URL: u) + request.HTTPMethod = "DELETE" + + let session = NSURLSession.sharedSession() + let task = session.dataTaskWithRequest(request) { (data, response, error) in + let httpResponse = response as! NSHTTPURLResponse + let status = httpResponse.statusCode + + if status == 200 { + print("leave ok") + } + else { + print("leave error: \(status)") + } + } + + task.resume() + } + } + +} diff --git a/ZeroTier One/ShowNetworksViewController.swift b/ZeroTier One/ShowNetworksViewController.swift index 8bf84a1cb..0e8fd4b6b 100644 --- a/ZeroTier One/ShowNetworksViewController.swift +++ b/ZeroTier One/ShowNetworksViewController.swift @@ -16,5 +16,11 @@ class ShowNetworksViewController: NSViewController { super.viewDidLoad() // Do view setup here. } + + override func viewWillAppear() { + super.viewWillAppear() + + ServiceCom.getNetworkList() + } } From d5620288d56085e749c04ae5bb9908a61238aaa4 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Tue, 17 May 2016 19:41:54 -0700 Subject: [PATCH 09/91] Parse JSON network list into Network objects for the view --- ZeroTier One.xcodeproj/project.pbxproj | 4 + ZeroTier One/Network.swift | 112 ++++++++++++++++++ ZeroTier One/ServiceCom.swift | 13 +- ZeroTier One/ShowNetworksViewController.swift | 23 +++- ZeroTier One/ShowNetworksViewController.xib | 48 +------- 5 files changed, 151 insertions(+), 49 deletions(-) create mode 100644 ZeroTier One/Network.swift diff --git a/ZeroTier One.xcodeproj/project.pbxproj b/ZeroTier One.xcodeproj/project.pbxproj index d0441a5cb..0fcc78605 100644 --- a/ZeroTier One.xcodeproj/project.pbxproj +++ b/ZeroTier One.xcodeproj/project.pbxproj @@ -8,6 +8,7 @@ /* Begin PBXBuildFile section */ 9330F1351CEAB4C400687EC8 /* ServiceCom.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9330F1341CEAB4C400687EC8 /* ServiceCom.swift */; }; + 9330F1371CEBF87200687EC8 /* Network.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9330F1361CEBF87200687EC8 /* Network.swift */; }; 93326BDC1CE7C816005CA2AC /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93326BDB1CE7C816005CA2AC /* AppDelegate.swift */; }; 93326BDE1CE7C816005CA2AC /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 93326BDD1CE7C816005CA2AC /* Assets.xcassets */; }; 93326BE11CE7C816005CA2AC /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 93326BDF1CE7C816005CA2AC /* MainMenu.xib */; }; @@ -19,6 +20,7 @@ /* Begin PBXFileReference section */ 9330F1341CEAB4C400687EC8 /* ServiceCom.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServiceCom.swift; sourceTree = ""; }; + 9330F1361CEBF87200687EC8 /* Network.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Network.swift; sourceTree = ""; }; 93326BD81CE7C816005CA2AC /* ZeroTier One.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "ZeroTier One.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 93326BDB1CE7C816005CA2AC /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 93326BDD1CE7C816005CA2AC /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; @@ -62,6 +64,7 @@ children = ( 93326BDB1CE7C816005CA2AC /* AppDelegate.swift */, 93326BE81CE7D9B9005CA2AC /* JoinNetworkViewController.swift */, + 9330F1361CEBF87200687EC8 /* Network.swift */, 93326BEC1CE7DA30005CA2AC /* ShowNetworksViewController.swift */, 9330F1341CEAB4C400687EC8 /* ServiceCom.swift */, 93326BDD1CE7C816005CA2AC /* Assets.xcassets */, @@ -145,6 +148,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 9330F1371CEBF87200687EC8 /* Network.swift in Sources */, 93326BDC1CE7C816005CA2AC /* AppDelegate.swift in Sources */, 93326BEA1CE7D9B9005CA2AC /* JoinNetworkViewController.swift in Sources */, 93326BEE1CE7DA30005CA2AC /* ShowNetworksViewController.swift in Sources */, diff --git a/ZeroTier One/Network.swift b/ZeroTier One/Network.swift new file mode 100644 index 000000000..4ee2f7486 --- /dev/null +++ b/ZeroTier One/Network.swift @@ -0,0 +1,112 @@ +// +// Network.swift +// ZeroTier One +// +// Created by Grant Limberg on 5/17/16. +// Copyright © 2016 ZeroTier, Inc. All rights reserved. +// + +import Cocoa + +enum NetworkStatus : CustomStringConvertible { + case REQUESTING_CONFIGURATION + case OK + case ACCESS_DENIED + case NOT_FOUND + case PORT_ERROR + case CLIENT_TOO_OLD + + var description: String { + switch self { + case .REQUESTING_CONFIGURATION: return "REQUESTING_CONFIGURATION" + case .OK: return "OK" + case .ACCESS_DENIED: return "ACCESS_DENIED" + case .NOT_FOUND: return "NOT_FOUND" + case .PORT_ERROR: return "PORT_ERROR" + case .CLIENT_TOO_OLD: return "CLIENT_TOO_OLD" + } + } +} + +enum NetworkType: CustomStringConvertible { + case PUBLIC + case PRIVATE + + var description: String { + switch self { + case .PUBLIC: return "PUBLIC" + case .PRIVATE: return "PRIVATE" + } + } +} + +class Network: NSObject { + var assignedAddresses: [String] = [String]() + var bridge: Bool = false + var broadcastEnabled: Bool = false + var dhcp: Bool = false + var mac: String = "" + var mtu: Int = 0 + var multicastSubscriptions: [String] = [String]() + var name: String = "" + var netconfRevision: Int = 232 + var nwid: UInt64 = 0 + var portDeviceName: String = "" + var portError: Int = 0 + var status: NetworkStatus = .REQUESTING_CONFIGURATION + var type: NetworkType = .PRIVATE + + init(jsonData: [String: AnyObject]) { + super.init() + + let aa = jsonData["assignedAddresses"] as! [String] + for a in aa { + assignedAddresses.append(a) + } + + bridge = (jsonData["bridge"] as! NSNumber).boolValue + broadcastEnabled = (jsonData["broadcastEnabled"] as! NSNumber).boolValue + dhcp = (jsonData["dhcp"] as! NSNumber).boolValue + mac = jsonData["mac"] as! String + mtu = (jsonData["mtu"] as! NSNumber).integerValue + + let multSubs = jsonData["multicastSubscriptions"] as! [String] + for ms in multSubs { + multicastSubscriptions.append(ms) + } + + name = jsonData["name"] as! String + netconfRevision = (jsonData["netconfRevision"] as! NSNumber).integerValue + nwid = UInt64((jsonData["nwid"] as! String), radix: 16)! + portDeviceName = jsonData["portDeviceName"] as! String + portError = (jsonData["portError"] as! NSNumber).integerValue + + let statusStr = jsonData["status"] as! String + switch statusStr { + case "REQUESTING_CONFIGURATION": + status = .REQUESTING_CONFIGURATION + case "OK": + status = .OK + case "ACCESS_DENIED": + status = .ACCESS_DENIED + case "NOT_FOUND": + status = .NOT_FOUND + case "PORT_ERROR": + status = .PORT_ERROR + case "CLIENT_TOO_OLD": + status = .CLIENT_TOO_OLD + default: + break + } + + let typeStr = jsonData["type"] as! String + switch typeStr { + case "PRIVATE": + type = .PRIVATE + case "PUBLIC": + type = .PUBLIC + default: + break + } + } +} diff --git a/ZeroTier One/ServiceCom.swift b/ZeroTier One/ServiceCom.swift index a1bb0a3c2..bdb59792a 100644 --- a/ZeroTier One/ServiceCom.swift +++ b/ZeroTier One/ServiceCom.swift @@ -12,7 +12,7 @@ class ServiceCom: NSObject { static let baseURL = "http://localhost:9993" static var key: NSString? = "ddeb3b1e6996b6b4f2d12d10" - static func getNetworkList() { + static func getNetworkList(completionHandler: ([Network]) -> Void) { let urlString = baseURL + "/network?auth=\(ServiceCom.key!)" @@ -27,11 +27,16 @@ class ServiceCom: NSObject { if status == 200 { do { - let json = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions()) - print("\(json)") + let json = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions()) as! [[String: AnyObject]] + + var networks = [Network]() + for jobj in json { + networks.append(Network(jsonData: jobj)) + } + + completionHandler(networks) } catch { - print("JSON Error: \(error)") } } } diff --git a/ZeroTier One/ShowNetworksViewController.swift b/ZeroTier One/ShowNetworksViewController.swift index 0e8fd4b6b..d914c30e3 100644 --- a/ZeroTier One/ShowNetworksViewController.swift +++ b/ZeroTier One/ShowNetworksViewController.swift @@ -8,19 +8,36 @@ import Cocoa -class ShowNetworksViewController: NSViewController { +class ShowNetworksViewController: NSViewController, NSTableViewDelegate, NSTableViewDataSource { @IBOutlet var tableView: NSTableView! + var networkList: [Network] = [Network]() + override func viewDidLoad() { super.viewDidLoad() // Do view setup here. + + tableView.setDelegate(self) + tableView.setDataSource(self) } override func viewWillAppear() { super.viewWillAppear() - ServiceCom.getNetworkList() + ServiceCom.getNetworkList() { (networkList) -> Void in + NSOperationQueue.mainQueue().addOperationWithBlock() { () -> Void in + self.networkList = networkList + self.tableView.reloadData() + } + } } - + + // NSTableViewDataSource + + func numberOfRowsInTableView(tableView: NSTableView) -> Int { + return networkList.count + } + + // end NSTableViewDataSource } diff --git a/ZeroTier One/ShowNetworksViewController.xib b/ZeroTier One/ShowNetworksViewController.xib index e17bc5a97..fcc6cbfa7 100644 --- a/ZeroTier One/ShowNetworksViewController.xib +++ b/ZeroTier One/ShowNetworksViewController.xib @@ -20,17 +20,17 @@ - + - - + + - + @@ -44,7 +44,7 @@ - + @@ -62,44 +62,12 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From 940c0f2b93ab2cebd55f5f0199499b68fef07d90 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Fri, 27 May 2016 13:28:32 -0700 Subject: [PATCH 10/91] WIP. Mac UI --- ZeroTier One.xcodeproj/project.pbxproj | 4 + ZeroTier One/NetworkInfoCell.swift | 22 +++ ZeroTier One/ShowNetworksViewController.swift | 16 ++ ZeroTier One/ShowNetworksViewController.xib | 143 ++++++++++++++++-- 4 files changed, 170 insertions(+), 15 deletions(-) create mode 100644 ZeroTier One/NetworkInfoCell.swift diff --git a/ZeroTier One.xcodeproj/project.pbxproj b/ZeroTier One.xcodeproj/project.pbxproj index 0fcc78605..256d75e7a 100644 --- a/ZeroTier One.xcodeproj/project.pbxproj +++ b/ZeroTier One.xcodeproj/project.pbxproj @@ -9,6 +9,7 @@ /* Begin PBXBuildFile section */ 9330F1351CEAB4C400687EC8 /* ServiceCom.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9330F1341CEAB4C400687EC8 /* ServiceCom.swift */; }; 9330F1371CEBF87200687EC8 /* Network.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9330F1361CEBF87200687EC8 /* Network.swift */; }; + 9330F13B1CF534E500687EC8 /* NetworkInfoCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9330F13A1CF534E500687EC8 /* NetworkInfoCell.swift */; }; 93326BDC1CE7C816005CA2AC /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93326BDB1CE7C816005CA2AC /* AppDelegate.swift */; }; 93326BDE1CE7C816005CA2AC /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 93326BDD1CE7C816005CA2AC /* Assets.xcassets */; }; 93326BE11CE7C816005CA2AC /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 93326BDF1CE7C816005CA2AC /* MainMenu.xib */; }; @@ -21,6 +22,7 @@ /* Begin PBXFileReference section */ 9330F1341CEAB4C400687EC8 /* ServiceCom.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServiceCom.swift; sourceTree = ""; }; 9330F1361CEBF87200687EC8 /* Network.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Network.swift; sourceTree = ""; }; + 9330F13A1CF534E500687EC8 /* NetworkInfoCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkInfoCell.swift; sourceTree = ""; }; 93326BD81CE7C816005CA2AC /* ZeroTier One.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "ZeroTier One.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 93326BDB1CE7C816005CA2AC /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 93326BDD1CE7C816005CA2AC /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; @@ -65,6 +67,7 @@ 93326BDB1CE7C816005CA2AC /* AppDelegate.swift */, 93326BE81CE7D9B9005CA2AC /* JoinNetworkViewController.swift */, 9330F1361CEBF87200687EC8 /* Network.swift */, + 9330F13A1CF534E500687EC8 /* NetworkInfoCell.swift */, 93326BEC1CE7DA30005CA2AC /* ShowNetworksViewController.swift */, 9330F1341CEAB4C400687EC8 /* ServiceCom.swift */, 93326BDD1CE7C816005CA2AC /* Assets.xcassets */, @@ -150,6 +153,7 @@ files = ( 9330F1371CEBF87200687EC8 /* Network.swift in Sources */, 93326BDC1CE7C816005CA2AC /* AppDelegate.swift in Sources */, + 9330F13B1CF534E500687EC8 /* NetworkInfoCell.swift in Sources */, 93326BEA1CE7D9B9005CA2AC /* JoinNetworkViewController.swift in Sources */, 93326BEE1CE7DA30005CA2AC /* ShowNetworksViewController.swift in Sources */, 9330F1351CEAB4C400687EC8 /* ServiceCom.swift in Sources */, diff --git a/ZeroTier One/NetworkInfoCell.swift b/ZeroTier One/NetworkInfoCell.swift new file mode 100644 index 000000000..694eded50 --- /dev/null +++ b/ZeroTier One/NetworkInfoCell.swift @@ -0,0 +1,22 @@ +// +// NetworkInfoCell.swift +// ZeroTier One +// +// Created by Grant Limberg on 5/24/16. +// Copyright © 2016 ZeroTier, Inc. All rights reserved. +// + +import Cocoa + +class NetworkInfoCell: NSTableCellView { + + @IBOutlet var networkIdField: NSTextField! + @IBOutlet var networkNameField: NSTextField! + + override func drawRect(dirtyRect: NSRect) { + super.drawRect(dirtyRect) + + // Drawing code here. + } + +} diff --git a/ZeroTier One/ShowNetworksViewController.swift b/ZeroTier One/ShowNetworksViewController.swift index d914c30e3..53f2e0f4f 100644 --- a/ZeroTier One/ShowNetworksViewController.swift +++ b/ZeroTier One/ShowNetworksViewController.swift @@ -40,4 +40,20 @@ class ShowNetworksViewController: NSViewController, NSTableViewDelegate, NSTable } // end NSTableViewDataSource + + // NSTableViewDelegate + + func tableView(tableView: NSTableView, viewForTableColumn tableColumn: NSTableColumn?, row: Int) -> NSView? { + if let cell = tableView.makeViewWithIdentifier("NetworkInfoCell", owner: nil) as? NetworkInfoCell { + let network = networkList[row] + cell.networkIdField.stringValue = String(network.nwid, radix: 16) + cell.networkNameField.stringValue = network.name + + return cell + } + + return nil + } + + // end NSTableViewDelegate } diff --git a/ZeroTier One/ShowNetworksViewController.xib b/ZeroTier One/ShowNetworksViewController.xib index fcc6cbfa7..85dee589c 100644 --- a/ZeroTier One/ShowNetworksViewController.xib +++ b/ZeroTier One/ShowNetworksViewController.xib @@ -14,23 +14,23 @@ - + - - - - + + + + - - + + - + @@ -43,21 +43,127 @@ - - + + - - - + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + @@ -77,6 +183,13 @@ + + + + + + + From 1d27adb1021852709f144b6fef11cf83ef593e80 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Fri, 27 May 2016 13:48:22 -0700 Subject: [PATCH 11/91] Finished wiring up the view --- ZeroTier One/NetworkInfoCell.swift | 9 ++ ZeroTier One/ShowNetworksViewController.swift | 17 +++ ZeroTier One/ShowNetworksViewController.xib | 121 +++++++++++++++--- 3 files changed, 129 insertions(+), 18 deletions(-) diff --git a/ZeroTier One/NetworkInfoCell.swift b/ZeroTier One/NetworkInfoCell.swift index 694eded50..038507cac 100644 --- a/ZeroTier One/NetworkInfoCell.swift +++ b/ZeroTier One/NetworkInfoCell.swift @@ -13,6 +13,15 @@ class NetworkInfoCell: NSTableCellView { @IBOutlet var networkIdField: NSTextField! @IBOutlet var networkNameField: NSTextField! + @IBOutlet var statusField: NSTextField! + @IBOutlet var typeField: NSTextField! + @IBOutlet var macField: NSTextField! + @IBOutlet var mtuField: NSTextField! + @IBOutlet var broadcastField: NSTextField! + @IBOutlet var bridgingField: NSTextField! + @IBOutlet var deviceField: NSTextField! + @IBOutlet var addressesField: NSTextField! + override func drawRect(dirtyRect: NSRect) { super.drawRect(dirtyRect) diff --git a/ZeroTier One/ShowNetworksViewController.swift b/ZeroTier One/ShowNetworksViewController.swift index 53f2e0f4f..a12e07430 100644 --- a/ZeroTier One/ShowNetworksViewController.swift +++ b/ZeroTier One/ShowNetworksViewController.swift @@ -49,6 +49,23 @@ class ShowNetworksViewController: NSViewController, NSTableViewDelegate, NSTable cell.networkIdField.stringValue = String(network.nwid, radix: 16) cell.networkNameField.stringValue = network.name + cell.statusField.stringValue = network.status.description + cell.typeField.stringValue = network.type.description + cell.macField.stringValue = network.mac + cell.mtuField.stringValue = String(network.mtu) + cell.broadcastField.stringValue = network.broadcastEnabled ? "ENABLED" : "DISABLED" + cell.bridgingField.stringValue = network.bridge ? "ENABLED" : "DISABLED" + cell.deviceField.stringValue = network.portDeviceName + + + cell.addressesField.stringValue = "" + + for nw in network.assignedAddresses { + cell.addressesField.stringValue += nw + cell.addressesField.stringValue += "\n" + } + + return cell } diff --git a/ZeroTier One/ShowNetworksViewController.xib b/ZeroTier One/ShowNetworksViewController.xib index 85dee589c..93286f864 100644 --- a/ZeroTier One/ShowNetworksViewController.xib +++ b/ZeroTier One/ShowNetworksViewController.xib @@ -1,8 +1,8 @@ - + - + @@ -17,14 +17,14 @@ - + - - + + @@ -44,11 +44,11 @@ - + - + @@ -56,7 +56,7 @@ - + @@ -64,7 +64,7 @@ - + @@ -72,7 +72,7 @@ - + @@ -80,7 +80,7 @@ - + @@ -88,7 +88,7 @@ - + @@ -96,7 +96,7 @@ - + @@ -104,7 +104,7 @@ - + @@ -112,7 +112,7 @@ - + @@ -120,7 +120,7 @@ - + @@ -128,8 +128,64 @@ - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -137,33 +193,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 67d472424d92e1bd8dd2fff8d9ca911035569faf Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Fri, 27 May 2016 15:07:20 -0700 Subject: [PATCH 12/91] make the view show up light instead of dark --- ZeroTier One/AppDelegate.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ZeroTier One/AppDelegate.swift b/ZeroTier One/AppDelegate.swift index e5932f5f5..c7cbc09ac 100644 --- a/ZeroTier One/AppDelegate.swift +++ b/ZeroTier One/AppDelegate.swift @@ -39,9 +39,13 @@ class AppDelegate: NSObject, NSApplicationDelegate { nibName: "JoinNetworkViewController", bundle: nil) joinNetworkPopover.behavior = .Transient + joinNetworkPopover.appearance = NSAppearance(named: NSAppearanceNameAqua) + networkListPopover.contentViewController = ShowNetworksViewController( nibName: "ShowNetworksViewController", bundle: nil) networkListPopover.behavior = .Transient + + networkListPopover.appearance = NSAppearance(named: NSAppearanceNameAqua) } func applicationWillTerminate(aNotification: NSNotification) { From 59cf2ea34158ef6f6e827fbd9d062be6beef5691 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Fri, 27 May 2016 15:21:23 -0700 Subject: [PATCH 13/91] add proper border around the network list --- ZeroTier One/ShowNetworksViewController.swift | 1 + ZeroTier One/ShowNetworksViewController.xib | 40 +++++++++---------- 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/ZeroTier One/ShowNetworksViewController.swift b/ZeroTier One/ShowNetworksViewController.swift index a12e07430..857e2147b 100644 --- a/ZeroTier One/ShowNetworksViewController.swift +++ b/ZeroTier One/ShowNetworksViewController.swift @@ -20,6 +20,7 @@ class ShowNetworksViewController: NSViewController, NSTableViewDelegate, NSTable tableView.setDelegate(self) tableView.setDataSource(self) + tableView.backgroundColor = NSColor.clearColor() } override func viewWillAppear() { diff --git a/ZeroTier One/ShowNetworksViewController.xib b/ZeroTier One/ShowNetworksViewController.xib index 93286f864..6b12ea3fa 100644 --- a/ZeroTier One/ShowNetworksViewController.xib +++ b/ZeroTier One/ShowNetworksViewController.xib @@ -14,23 +14,23 @@ - + - - + + - + - + - + @@ -44,7 +44,7 @@ - + @@ -55,7 +55,7 @@ - + @@ -127,7 +127,7 @@ - + @@ -135,7 +135,7 @@ - + @@ -143,7 +143,7 @@ - + @@ -151,7 +151,7 @@ - + @@ -159,7 +159,7 @@ - + @@ -167,7 +167,7 @@ - + @@ -175,7 +175,7 @@ - + @@ -269,12 +269,12 @@ - - - - + + + + - + From 41328eef94e0f45a890c57b9d85862c7a54e0175 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Fri, 27 May 2016 15:25:32 -0700 Subject: [PATCH 14/91] . --- ZeroTier One/NetworkInfoCell.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ZeroTier One/NetworkInfoCell.swift b/ZeroTier One/NetworkInfoCell.swift index 038507cac..73305f838 100644 --- a/ZeroTier One/NetworkInfoCell.swift +++ b/ZeroTier One/NetworkInfoCell.swift @@ -22,6 +22,10 @@ class NetworkInfoCell: NSTableCellView { @IBOutlet var deviceField: NSTextField! @IBOutlet var addressesField: NSTextField! + override func awakeFromNib() { + super.awakeFromNib() + + } override func drawRect(dirtyRect: NSRect) { super.drawRect(dirtyRect) From b4f3dfc34727ec99853e22e7abb0c1dff9e5c205 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Tue, 31 May 2016 13:50:43 -0700 Subject: [PATCH 15/91] look up authtoken in user's app support directory --- ZeroTier One/ServiceCom.swift | 53 ++++++++++++++++++++++++++++++++--- 1 file changed, 49 insertions(+), 4 deletions(-) diff --git a/ZeroTier One/ServiceCom.swift b/ZeroTier One/ServiceCom.swift index bdb59792a..289572650 100644 --- a/ZeroTier One/ServiceCom.swift +++ b/ZeroTier One/ServiceCom.swift @@ -10,11 +10,56 @@ import Cocoa class ServiceCom: NSObject { static let baseURL = "http://localhost:9993" - static var key: NSString? = "ddeb3b1e6996b6b4f2d12d10" + + private static func getKey() -> String { + struct Holder { + static var key: String? = nil + } + + if Holder.key == nil { + do { + // Check the user's ZeroTier application support directory. If + // authtoken.secret exists, use it. + + var appSupportDir = try NSFileManager.defaultManager().URLForDirectory(.ApplicationSupportDirectory, inDomain: .UserDomainMask, appropriateForURL: nil, create: false) + NSLog("\(appSupportDir)") + + + appSupportDir = appSupportDir.URLByAppendingPathComponent("ZeroTier") + NSLog("\(appSupportDir)") + + appSupportDir = appSupportDir.URLByAppendingPathComponent("One") + NSLog("\(appSupportDir)") + + let authtokenURL = appSupportDir.URLByAppendingPathComponent("authtoken.secret") + + NSLog("\(authtokenURL)") + + if NSFileManager.defaultManager().fileExistsAtPath(authtokenURL.path!) { + Holder.key = try String(contentsOfURL: authtokenURL) + } + else { + // TODO: Elevate priviledge to copy /Library/Application Support/ZeroTier/One/authtoken.secret to the user's local AppSupport directory + } + } + catch { + NSLog("Error getting app support dir: \(error)") + Holder.key = nil + } + + } + + if let k = Holder.key { + return k + } + else { + return "" + } + } static func getNetworkList(completionHandler: ([Network]) -> Void) { - let urlString = baseURL + "/network?auth=\(ServiceCom.key!)" + let urlString = baseURL + "/network?auth=\(ServiceCom.getKey())" let url = NSURL(string: urlString) @@ -50,7 +95,7 @@ class ServiceCom: NSObject { static func joinNetwork(network: String) { - let urlString = baseURL + "/network/\(network)?auth=\(ServiceCom.key!)" + let urlString = baseURL + "/network/\(network)?auth=\(ServiceCom.getKey())" let url = NSURL(string: urlString) if let u = url { @@ -75,7 +120,7 @@ class ServiceCom: NSObject { } static func leaveNetwork(network: String) { - let urlString = baseURL + "/network/\(network)?auth=\(ServiceCom.key!)" + let urlString = baseURL + "/network/\(network)?auth=\(ServiceCom.getKey())" let url = NSURL(string: urlString) if let u = url { From 51e74f8d4f200a7e3be9a6ac4bef8486f83a5d5d Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Tue, 31 May 2016 15:01:42 -0700 Subject: [PATCH 16/91] remove some extra logging --- ZeroTier One/ServiceCom.swift | 9 --------- 1 file changed, 9 deletions(-) diff --git a/ZeroTier One/ServiceCom.swift b/ZeroTier One/ServiceCom.swift index 289572650..3aa9dc54e 100644 --- a/ZeroTier One/ServiceCom.swift +++ b/ZeroTier One/ServiceCom.swift @@ -22,19 +22,10 @@ class ServiceCom: NSObject { // authtoken.secret exists, use it. var appSupportDir = try NSFileManager.defaultManager().URLForDirectory(.ApplicationSupportDirectory, inDomain: .UserDomainMask, appropriateForURL: nil, create: false) - NSLog("\(appSupportDir)") - - appSupportDir = appSupportDir.URLByAppendingPathComponent("ZeroTier") - NSLog("\(appSupportDir)") - appSupportDir = appSupportDir.URLByAppendingPathComponent("One") - NSLog("\(appSupportDir)") - let authtokenURL = appSupportDir.URLByAppendingPathComponent("authtoken.secret") - NSLog("\(authtokenURL)") - if NSFileManager.defaultManager().fileExistsAtPath(authtokenURL.path!) { Holder.key = try String(contentsOfURL: authtokenURL) } From 975bcb8affa7fbd7ca534dac9ec8d642e6c977f7 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Tue, 31 May 2016 17:19:22 -0700 Subject: [PATCH 17/91] some OS X Authentication Services wizardry to get /Library/Application Support/ZeroTier/One/authtoken.secret and copy it to ~/Library/Application Support/ZeroTier/One/authtoken.secret --- ZeroTier One.xcodeproj/project.pbxproj | 13 +++ ZeroTier One/AuthtokenCopy.h | 16 ++++ ZeroTier One/AuthtokenCopy.m | 87 +++++++++++++++++++++ ZeroTier One/ServiceCom.swift | 31 +++++++- ZeroTier One/ZeroTier One-Bridging-Header.h | 5 ++ 5 files changed, 151 insertions(+), 1 deletion(-) create mode 100644 ZeroTier One/AuthtokenCopy.h create mode 100644 ZeroTier One/AuthtokenCopy.m create mode 100644 ZeroTier One/ZeroTier One-Bridging-Header.h diff --git a/ZeroTier One.xcodeproj/project.pbxproj b/ZeroTier One.xcodeproj/project.pbxproj index 256d75e7a..7c1e29bec 100644 --- a/ZeroTier One.xcodeproj/project.pbxproj +++ b/ZeroTier One.xcodeproj/project.pbxproj @@ -17,6 +17,7 @@ 93326BEB1CE7D9B9005CA2AC /* JoinNetworkViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 93326BE91CE7D9B9005CA2AC /* JoinNetworkViewController.xib */; }; 93326BEE1CE7DA30005CA2AC /* ShowNetworksViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93326BEC1CE7DA30005CA2AC /* ShowNetworksViewController.swift */; }; 93326BEF1CE7DA30005CA2AC /* ShowNetworksViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 93326BED1CE7DA30005CA2AC /* ShowNetworksViewController.xib */; }; + 93DAFE4B1CFE53CA00547CC4 /* AuthtokenCopy.m in Sources */ = {isa = PBXBuildFile; fileRef = 93DAFE4A1CFE53CA00547CC4 /* AuthtokenCopy.m */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -32,6 +33,9 @@ 93326BE91CE7D9B9005CA2AC /* JoinNetworkViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = JoinNetworkViewController.xib; sourceTree = ""; }; 93326BEC1CE7DA30005CA2AC /* ShowNetworksViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShowNetworksViewController.swift; sourceTree = ""; }; 93326BED1CE7DA30005CA2AC /* ShowNetworksViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ShowNetworksViewController.xib; sourceTree = ""; }; + 93DAFE491CFE53C900547CC4 /* ZeroTier One-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ZeroTier One-Bridging-Header.h"; sourceTree = ""; }; + 93DAFE4A1CFE53CA00547CC4 /* AuthtokenCopy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AuthtokenCopy.m; sourceTree = ""; }; + 93DAFE4C1CFE53DA00547CC4 /* AuthtokenCopy.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AuthtokenCopy.h; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -75,6 +79,9 @@ 93326BE21CE7C816005CA2AC /* Info.plist */, 93326BE91CE7D9B9005CA2AC /* JoinNetworkViewController.xib */, 93326BED1CE7DA30005CA2AC /* ShowNetworksViewController.xib */, + 93DAFE4A1CFE53CA00547CC4 /* AuthtokenCopy.m */, + 93DAFE4C1CFE53DA00547CC4 /* AuthtokenCopy.h */, + 93DAFE491CFE53C900547CC4 /* ZeroTier One-Bridging-Header.h */, ); path = "ZeroTier One"; sourceTree = ""; @@ -153,6 +160,7 @@ files = ( 9330F1371CEBF87200687EC8 /* Network.swift in Sources */, 93326BDC1CE7C816005CA2AC /* AppDelegate.swift in Sources */, + 93DAFE4B1CFE53CA00547CC4 /* AuthtokenCopy.m in Sources */, 9330F13B1CF534E500687EC8 /* NetworkInfoCell.swift in Sources */, 93326BEA1CE7D9B9005CA2AC /* JoinNetworkViewController.swift in Sources */, 93326BEE1CE7DA30005CA2AC /* ShowNetworksViewController.swift in Sources */, @@ -260,11 +268,14 @@ isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; COMBINE_HIDPI_IMAGES = YES; INFOPLIST_FILE = "ZeroTier One/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.zerotier.ZeroTier-One"; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "ZeroTier One/ZeroTier One-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; }; name = Debug; }; @@ -272,11 +283,13 @@ isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; COMBINE_HIDPI_IMAGES = YES; INFOPLIST_FILE = "ZeroTier One/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.zerotier.ZeroTier-One"; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "ZeroTier One/ZeroTier One-Bridging-Header.h"; }; name = Release; }; diff --git a/ZeroTier One/AuthtokenCopy.h b/ZeroTier One/AuthtokenCopy.h new file mode 100644 index 000000000..43c927e25 --- /dev/null +++ b/ZeroTier One/AuthtokenCopy.h @@ -0,0 +1,16 @@ +// +// AuthtokenCopy.h +// ZeroTier One +// +// Created by Grant Limberg on 5/31/16. +// Copyright © 2016 ZeroTier, Inc. All rights reserved. +// + +#ifndef AuthtokenCopy_h +#define AuthtokenCopy_h + +#import + +NSString* getAdminAuthToken(AuthorizationRef authRef); + +#endif /* AuthtokenCopy_h */ diff --git a/ZeroTier One/AuthtokenCopy.m b/ZeroTier One/AuthtokenCopy.m new file mode 100644 index 000000000..893d90cf9 --- /dev/null +++ b/ZeroTier One/AuthtokenCopy.m @@ -0,0 +1,87 @@ +// +// AuthtokenCopy.m +// ZeroTier One +// +// Created by Grant Limberg on 5/31/16. +// Copyright © 2016 ZeroTier, Inc. All rights reserved. +// + +#import + +#import "AuthtokenCopy.h" + + +NSString* getAdminAuthToken(AuthorizationRef authRef) { + char *tool = "/bin/cat"; + char *args[] = { "/Library/Application Support/ZeroTier/One/authtoken.secret", NULL}; + FILE *pipe = nil; + char token[25]; + memset(token, 0, sizeof(char)*25); + + + OSStatus status = AuthorizationExecuteWithPrivileges(authRef, tool, kAuthorizationFlagDefaults, args, &pipe); + + if (status != errAuthorizationSuccess) { + NSLog(@"Reading authtoken failed!"); + + + switch(status) { + case errAuthorizationDenied: + NSLog(@"Autorization Denied"); + break; + case errAuthorizationCanceled: + NSLog(@"Authorization Canceled"); + break; + case errAuthorizationInternal: + NSLog(@"Authorization Internal"); + break; + case errAuthorizationBadAddress: + NSLog(@"Bad Address"); + break; + case errAuthorizationInvalidRef: + NSLog(@"Invalid Ref"); + break; + case errAuthorizationInvalidSet: + NSLog(@"Invalid Set"); + break; + case errAuthorizationInvalidTag: + NSLog(@"Invalid Tag"); + break; + case errAuthorizationInvalidFlags: + NSLog(@"Invalid Flags"); + break; + case errAuthorizationInvalidPointer: + NSLog(@"Invalid Pointer"); + break; + case errAuthorizationToolExecuteFailure: + NSLog(@"Tool Execute Failure"); + break; + case errAuthorizationToolEnvironmentError: + NSLog(@"Tool Environment Failure"); + break; + case errAuthorizationExternalizeNotAllowed: + NSLog(@"Externalize Not Allowed"); + break; + case errAuthorizationInteractionNotAllowed: + NSLog(@"Interaction Not Allowed"); + break; + case errAuthorizationInternalizeNotAllowed: + NSLog(@"Internalize Not Allowed"); + break; + default: + NSLog(@"Unknown Error"); + break; + } + + return @""; + } + + if(pipe != nil) { + fread(&token, sizeof(char), 24, pipe); + fclose(pipe); + + return [NSString stringWithUTF8String:token]; + } + + return @""; +} \ No newline at end of file diff --git a/ZeroTier One/ServiceCom.swift b/ZeroTier One/ServiceCom.swift index 3aa9dc54e..e25266105 100644 --- a/ZeroTier One/ServiceCom.swift +++ b/ZeroTier One/ServiceCom.swift @@ -30,7 +30,36 @@ class ServiceCom: NSObject { Holder.key = try String(contentsOfURL: authtokenURL) } else { - // TODO: Elevate priviledge to copy /Library/Application Support/ZeroTier/One/authtoken.secret to the user's local AppSupport directory + try NSFileManager.defaultManager().createDirectoryAtURL(appSupportDir, withIntermediateDirectories: true, attributes: nil) + + var authRef: AuthorizationRef = nil + var status = AuthorizationCreate(nil, nil, .Defaults, &authRef) + + if status != errAuthorizationSuccess { + NSLog("Authorization Failed! \(status)") + return "" + } + + var authItem = AuthorizationItem(name: kAuthorizationRightExecute, valueLength: 0, value: nil, flags: 0) + var authRights = AuthorizationRights(count: 1, items: &authItem) + let authFlags: AuthorizationFlags = [.Defaults, .InteractionAllowed, .PreAuthorize, .ExtendRights] + + status = AuthorizationCopyRights(authRef, &authRights, nil, authFlags, nil) + + if status != errAuthorizationSuccess { + NSLog("Authorization Failed! \(status)") + return "" + } + + let localKey = getAdminAuthToken(authRef) + AuthorizationFree(authRef, .DestroyRights) + + if localKey != nil && localKey.lengthOfBytesUsingEncoding(NSUTF8StringEncoding) > 0 { + NSLog("\(localKey)") + Holder.key = localKey + + try localKey.writeToURL(authtokenURL, atomically: true, encoding: NSUTF8StringEncoding) + } } } catch { diff --git a/ZeroTier One/ZeroTier One-Bridging-Header.h b/ZeroTier One/ZeroTier One-Bridging-Header.h new file mode 100644 index 000000000..68795a0e2 --- /dev/null +++ b/ZeroTier One/ZeroTier One-Bridging-Header.h @@ -0,0 +1,5 @@ +// +// Use this file to import your target's public headers that you would like to expose to Swift. +// + +#import "AuthtokenCopy.h" From 188f8021f806ff922924949054b94e53dc0fed30 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Thu, 9 Jun 2016 19:59:05 -0700 Subject: [PATCH 18/91] Joining/Deleting networks now implemented. Still need to implement known networks cache so we can join/leave networks without deleting them from the UI --- ZeroTier One/JoinNetworkViewController.swift | 2 +- ZeroTier One/NetworkInfoCell.swift | 32 +++++++- ZeroTier One/ShowNetworksViewController.swift | 5 ++ ZeroTier One/ShowNetworksViewController.xib | 75 +++++++++++++------ 4 files changed, 88 insertions(+), 26 deletions(-) diff --git a/ZeroTier One/JoinNetworkViewController.swift b/ZeroTier One/JoinNetworkViewController.swift index 1ac3c7ea6..cc768254e 100644 --- a/ZeroTier One/JoinNetworkViewController.swift +++ b/ZeroTier One/JoinNetworkViewController.swift @@ -62,7 +62,7 @@ class JoinNetworkViewController: NSViewController, NSComboBoxDelegate, NSComboBo let networkString = network.stringValue let networkId = UInt64(networkString, radix: 16) - // TODO: Execute join network call + ServiceCom.joinNetwork(networkString) network.stringValue = "" diff --git a/ZeroTier One/NetworkInfoCell.swift b/ZeroTier One/NetworkInfoCell.swift index 73305f838..c31fcb912 100644 --- a/ZeroTier One/NetworkInfoCell.swift +++ b/ZeroTier One/NetworkInfoCell.swift @@ -10,6 +10,8 @@ import Cocoa class NetworkInfoCell: NSTableCellView { + weak var parent: ShowNetworksViewController! + @IBOutlet var networkIdField: NSTextField! @IBOutlet var networkNameField: NSTextField! @@ -22,6 +24,9 @@ class NetworkInfoCell: NSTableCellView { @IBOutlet var deviceField: NSTextField! @IBOutlet var addressesField: NSTextField! + @IBOutlet var connectedCheckbox: NSButton! + @IBOutlet var deleteButton: NSButton! + override func awakeFromNib() { super.awakeFromNib() @@ -31,5 +36,30 @@ class NetworkInfoCell: NSTableCellView { // Drawing code here. } - + + @IBAction func onConnectCheckStateChanged(sender: NSButton) { + NSLog("Checked State Changed") + + if(sender.state == NSOnState) { + NSLog("Checked") + joinNetwork(networkIdField.stringValue) + } + else { + NSLog("Unchecked") + leaveNetwork(networkIdField.stringValue) + } + } + + @IBAction func deleteNetwork(sender: NSButton) { + leaveNetwork(networkIdField.stringValue) + parent.deleteNetworkFromList(networkIdField.stringValue) + } + + func joinNetwork(nwid: String) { + ServiceCom.joinNetwork(nwid) + } + + func leaveNetwork(nwid: String) { + ServiceCom.leaveNetwork(nwid) + } } diff --git a/ZeroTier One/ShowNetworksViewController.swift b/ZeroTier One/ShowNetworksViewController.swift index 857e2147b..33148f839 100644 --- a/ZeroTier One/ShowNetworksViewController.swift +++ b/ZeroTier One/ShowNetworksViewController.swift @@ -34,6 +34,10 @@ class ShowNetworksViewController: NSViewController, NSTableViewDelegate, NSTable } } + func deleteNetworkFromList(nwid: String) { + + } + // NSTableViewDataSource func numberOfRowsInTableView(tableView: NSTableView) -> Int { @@ -47,6 +51,7 @@ class ShowNetworksViewController: NSViewController, NSTableViewDelegate, NSTable func tableView(tableView: NSTableView, viewForTableColumn tableColumn: NSTableColumn?, row: Int) -> NSView? { if let cell = tableView.makeViewWithIdentifier("NetworkInfoCell", owner: nil) as? NetworkInfoCell { let network = networkList[row] + cell.parent = self cell.networkIdField.stringValue = String(network.nwid, radix: 16) cell.networkNameField.stringValue = network.name diff --git a/ZeroTier One/ShowNetworksViewController.xib b/ZeroTier One/ShowNetworksViewController.xib index 6b12ea3fa..12f1ea3ce 100644 --- a/ZeroTier One/ShowNetworksViewController.xib +++ b/ZeroTier One/ShowNetworksViewController.xib @@ -1,8 +1,8 @@ - + - + @@ -17,14 +17,14 @@ - + - - + + @@ -44,11 +44,11 @@ - + - + @@ -56,7 +56,7 @@ - + @@ -64,7 +64,7 @@ - + @@ -72,7 +72,7 @@ - + @@ -80,7 +80,7 @@ - + @@ -88,7 +88,7 @@ - + @@ -96,7 +96,7 @@ - + @@ -104,7 +104,7 @@ - + @@ -112,7 +112,7 @@ - + @@ -120,7 +120,7 @@ - + @@ -128,7 +128,7 @@ - + @@ -136,7 +136,7 @@ - + @@ -144,7 +144,7 @@ - + @@ -152,7 +152,7 @@ - + @@ -160,7 +160,7 @@ - + @@ -168,7 +168,7 @@ - + @@ -176,7 +176,7 @@ - + @@ -184,13 +184,33 @@ - + + + @@ -210,6 +230,7 @@ + @@ -222,14 +243,18 @@ + + + + @@ -242,6 +267,8 @@ + + From b085329bca3e1eb1300045bf2cd2df52039e99d9 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Mon, 13 Jun 2016 19:46:54 -0700 Subject: [PATCH 19/91] Base functionality complete. Can join/leave/list networks. Can disconnect from networks without removing them from the network list. --- ZeroTier One/Network.swift | 62 ++++++++++++- ZeroTier One/ShowNetworksViewController.swift | 89 +++++++++++++++++-- 2 files changed, 143 insertions(+), 8 deletions(-) diff --git a/ZeroTier One/Network.swift b/ZeroTier One/Network.swift index 4ee2f7486..28f3eda68 100644 --- a/ZeroTier One/Network.swift +++ b/ZeroTier One/Network.swift @@ -8,7 +8,7 @@ import Cocoa -enum NetworkStatus : CustomStringConvertible { +enum NetworkStatus : Int, CustomStringConvertible { case REQUESTING_CONFIGURATION case OK case ACCESS_DENIED @@ -28,7 +28,7 @@ enum NetworkStatus : CustomStringConvertible { } } -enum NetworkType: CustomStringConvertible { +enum NetworkType: Int, CustomStringConvertible { case PUBLIC case PRIVATE @@ -40,7 +40,25 @@ enum NetworkType: CustomStringConvertible { } } -class Network: NSObject { + +struct PropertyKeys { + static let addressesKey = "addresses" + static let bridgeKey = "bridge" + static let broadcastKey = "broadcast" + static let dhcpKey = "dhcp" + static let macKey = "mac" + static let mtuKey = "mtu" + static let multicastKey = "multicast" + static let nameKey = "name" + static let netconfKey = "netconf" + static let nwidKey = "nwid" + static let portNameKey = "port" + static let portErrorKey = "portError" + static let statusKey = "status" + static let typeKey = "type" +} + +class Network: NSObject, NSCoding { var assignedAddresses: [String] = [String]() var bridge: Bool = false var broadcastEnabled: Bool = false @@ -55,6 +73,7 @@ class Network: NSObject { var portError: Int = 0 var status: NetworkStatus = .REQUESTING_CONFIGURATION var type: NetworkType = .PRIVATE + var connected: Bool = false // NOT PERSISTED. Set to true if loaded via JSON init(jsonData: [String: AnyObject]) { super.init() @@ -108,5 +127,42 @@ class Network: NSObject { default: break } + + // if it's being initialized via JSON, it's connected + connected = true + } + + required init?(coder aDecoder: NSCoder) { + self.assignedAddresses = aDecoder.decodeObjectForKey(PropertyKeys.addressesKey) as! [String] + self.bridge = aDecoder.decodeBoolForKey(PropertyKeys.bridgeKey) + self.broadcastEnabled = aDecoder.decodeBoolForKey(PropertyKeys.broadcastKey) + self.dhcp = aDecoder.decodeBoolForKey(PropertyKeys.dhcpKey) + self.mac = aDecoder.decodeObjectForKey(PropertyKeys.macKey) as! String + self.mtu = aDecoder.decodeIntegerForKey(PropertyKeys.mtuKey) + self.multicastSubscriptions = aDecoder.decodeObjectForKey(PropertyKeys.multicastKey) as! [String] + self.name = aDecoder.decodeObjectForKey(PropertyKeys.nameKey) as! String + self.netconfRevision = aDecoder.decodeIntegerForKey(PropertyKeys.netconfKey) + self.nwid = (aDecoder.decodeObjectForKey(PropertyKeys.nwidKey) as! NSNumber).unsignedLongLongValue + self.portDeviceName = aDecoder.decodeObjectForKey(PropertyKeys.portNameKey) as! String + self.portError = aDecoder.decodeIntegerForKey(PropertyKeys.portErrorKey) + self.status = NetworkStatus(rawValue: aDecoder.decodeIntegerForKey(PropertyKeys.statusKey))! + self.type = NetworkType(rawValue: aDecoder.decodeIntegerForKey(PropertyKeys.typeKey))! + } + + func encodeWithCoder(aCoder: NSCoder) { + aCoder.encodeObject(self.assignedAddresses, forKey: PropertyKeys.addressesKey) + aCoder.encodeBool(self.bridge, forKey: PropertyKeys.bridgeKey) + aCoder.encodeBool(self.broadcastEnabled, forKey: PropertyKeys.broadcastKey) + aCoder.encodeBool(self.dhcp, forKey: PropertyKeys.dhcpKey) + aCoder.encodeObject(self.mac, forKey: PropertyKeys.macKey) + aCoder.encodeInteger(self.mtu, forKey: PropertyKeys.mtuKey) + aCoder.encodeObject(self.multicastSubscriptions, forKey: PropertyKeys.multicastKey) + aCoder.encodeObject(self.name, forKey: PropertyKeys.nameKey) + aCoder.encodeInteger(self.netconfRevision, forKey: PropertyKeys.netconfKey) + aCoder.encodeObject(NSNumber(unsignedLongLong: self.nwid), forKey: PropertyKeys.nwidKey) + aCoder.encodeObject(self.portDeviceName, forKey: PropertyKeys.portNameKey) + aCoder.encodeInteger(self.portError, forKey: PropertyKeys.portErrorKey) + aCoder.encodeInteger(self.status.rawValue, forKey: PropertyKeys.statusKey) + aCoder.encodeInteger(self.type.rawValue, forKey: PropertyKeys.typeKey) } } diff --git a/ZeroTier One/ShowNetworksViewController.swift b/ZeroTier One/ShowNetworksViewController.swift index 33148f839..ed2479d16 100644 --- a/ZeroTier One/ShowNetworksViewController.swift +++ b/ZeroTier One/ShowNetworksViewController.swift @@ -23,19 +23,92 @@ class ShowNetworksViewController: NSViewController, NSTableViewDelegate, NSTable tableView.backgroundColor = NSColor.clearColor() } + private func dataFile() -> String { + var appSupport = NSFileManager.defaultManager().URLsForDirectory(.ApplicationSupportDirectory, inDomains: .UserDomainMask)[0] + appSupport = appSupport.URLByAppendingPathComponent("ZeroTier").URLByAppendingPathComponent("One").URLByAppendingPathComponent("networks.dat") + return appSupport.path! + } + + + private func findNetworkWithID(id: UInt64) -> Int { + + for (index, element) in networkList.enumerate() { + + if element.nwid == id { + return index + } + } + + return NSNotFound + } + override func viewWillAppear() { super.viewWillAppear() - ServiceCom.getNetworkList() { (networkList) -> Void in - NSOperationQueue.mainQueue().addOperationWithBlock() { () -> Void in - self.networkList = networkList - self.tableView.reloadData() + let filePath = dataFile() + + if NSFileManager.defaultManager().fileExistsAtPath(filePath) { + networkList = NSKeyedUnarchiver.unarchiveObjectWithFile(filePath) as! [Network] + + ServiceCom.getNetworkList() { (networkList) -> Void in + + for nw in networkList { + let index = self.findNetworkWithID(nw.nwid) + + if index != NSNotFound { + self.networkList[index] = nw + } + else { + self.networkList.append(nw) + } + } + + NSOperationQueue.mainQueue().addOperationWithBlock() { () -> Void in + self.networkList.sortInPlace({ (left, right) -> Bool in + if left.nwid < right.nwid { + return true + } + + return false + }) + self.tableView.reloadData() + } + } + } + else { + ServiceCom.getNetworkList() { (networkList) -> Void in + NSOperationQueue.mainQueue().addOperationWithBlock() { () -> Void in + self.networkList.sortInPlace({ (left, right) -> Bool in + if left.nwid < right.nwid { + return true + } + + return false + }) + + self.networkList = networkList + self.tableView.reloadData() + } } } } + override func viewWillDisappear() { + super.viewWillDisappear() + + let filePath = dataFile() + NSKeyedArchiver.archiveRootObject(self.networkList, toFile: filePath) + } + func deleteNetworkFromList(nwid: String) { - + if let nwid = UInt64(nwid, radix: 16) { + let index = findNetworkWithID(nwid) + + if index != NSNotFound { + networkList.removeAtIndex(index) + tableView.reloadData() + } + } } // NSTableViewDataSource @@ -71,6 +144,12 @@ class ShowNetworksViewController: NSViewController, NSTableViewDelegate, NSTable cell.addressesField.stringValue += "\n" } + if network.connected { + cell.connectedCheckbox.state = NSOnState + } + else { + cell.connectedCheckbox.state = NSOffState + } return cell } From 2c1a8524f0e79b8eb4a7e7160960bd4402d06a6e Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Mon, 13 Jun 2016 19:56:43 -0700 Subject: [PATCH 20/91] remove unused variables --- ZeroTier One/JoinNetworkViewController.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/ZeroTier One/JoinNetworkViewController.swift b/ZeroTier One/JoinNetworkViewController.swift index cc768254e..92e30cae1 100644 --- a/ZeroTier One/JoinNetworkViewController.swift +++ b/ZeroTier One/JoinNetworkViewController.swift @@ -60,7 +60,6 @@ class JoinNetworkViewController: NSViewController, NSComboBoxDelegate, NSComboBo @IBAction func onJoinClicked(sender: AnyObject?) { let networkString = network.stringValue - let networkId = UInt64(networkString, radix: 16) ServiceCom.joinNetwork(networkString) From 89cb0e260a65540ab6f3538ede1dc2493b031aba Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Mon, 13 Jun 2016 19:58:21 -0700 Subject: [PATCH 21/91] Set deployment target to 10.10 for the minimum OS X version for this app I'm currently using features only available in 10.10 or above. We'll have to discuss this and see what can be done for OS X 10.7 through 10.9. --- ZeroTier One.xcodeproj/project.pbxproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ZeroTier One.xcodeproj/project.pbxproj b/ZeroTier One.xcodeproj/project.pbxproj index 7c1e29bec..0a8188c99 100644 --- a/ZeroTier One.xcodeproj/project.pbxproj +++ b/ZeroTier One.xcodeproj/project.pbxproj @@ -272,6 +272,7 @@ COMBINE_HIDPI_IMAGES = YES; INFOPLIST_FILE = "ZeroTier One/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.10; PRODUCT_BUNDLE_IDENTIFIER = "com.zerotier.ZeroTier-One"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "ZeroTier One/ZeroTier One-Bridging-Header.h"; @@ -287,6 +288,7 @@ COMBINE_HIDPI_IMAGES = YES; INFOPLIST_FILE = "ZeroTier One/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.10; PRODUCT_BUNDLE_IDENTIFIER = "com.zerotier.ZeroTier-One"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "ZeroTier One/ZeroTier One-Bridging-Header.h"; From c8750e5812f6ad453cb482fe3ff629536e376960 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Tue, 14 Jun 2016 18:09:30 -0700 Subject: [PATCH 22/91] more error checking when parsing JSON for network list remove "multicastSubscriptions" field as it isn't used anyway --- ZeroTier One/Info.plist | 16 ++-- ZeroTier One/Network.swift | 177 +++++++++++++++++++++++++------------ 2 files changed, 127 insertions(+), 66 deletions(-) diff --git a/ZeroTier One/Info.plist b/ZeroTier One/Info.plist index 7a6ab7230..5777ef6c6 100644 --- a/ZeroTier One/Info.plist +++ b/ZeroTier One/Info.plist @@ -2,13 +2,6 @@ - NSAppTransportSecurity - - NSAllowsArbitraryLoads - - - LSUIElement - CFBundleDevelopmentRegion en CFBundleExecutable @@ -28,9 +21,16 @@ CFBundleSignature ???? CFBundleVersion - 1 + 2 LSMinimumSystemVersion $(MACOSX_DEPLOYMENT_TARGET) + LSUIElement + + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + NSHumanReadableCopyright Copyright © 2016 ZeroTier, Inc. All rights reserved. NSMainNibFile diff --git a/ZeroTier One/Network.swift b/ZeroTier One/Network.swift index 28f3eda68..95b3d93f6 100644 --- a/ZeroTier One/Network.swift +++ b/ZeroTier One/Network.swift @@ -65,7 +65,6 @@ class Network: NSObject, NSCoding { var dhcp: Bool = false var mac: String = "" var mtu: Int = 0 - var multicastSubscriptions: [String] = [String]() var name: String = "" var netconfRevision: Int = 232 var nwid: UInt64 = 0 @@ -78,54 +77,80 @@ class Network: NSObject, NSCoding { init(jsonData: [String: AnyObject]) { super.init() - let aa = jsonData["assignedAddresses"] as! [String] - for a in aa { - assignedAddresses.append(a) + if let aa = jsonData["assignedAddresses"] as? [String] { + for a in aa { + assignedAddresses.append(a) + } } - bridge = (jsonData["bridge"] as! NSNumber).boolValue - broadcastEnabled = (jsonData["broadcastEnabled"] as! NSNumber).boolValue - dhcp = (jsonData["dhcp"] as! NSNumber).boolValue - mac = jsonData["mac"] as! String - mtu = (jsonData["mtu"] as! NSNumber).integerValue - - let multSubs = jsonData["multicastSubscriptions"] as! [String] - for ms in multSubs { - multicastSubscriptions.append(ms) + if let b = jsonData["bridge"] as? NSNumber { + bridge = b.boolValue } - name = jsonData["name"] as! String - netconfRevision = (jsonData["netconfRevision"] as! NSNumber).integerValue - nwid = UInt64((jsonData["nwid"] as! String), radix: 16)! - portDeviceName = jsonData["portDeviceName"] as! String - portError = (jsonData["portError"] as! NSNumber).integerValue - - let statusStr = jsonData["status"] as! String - switch statusStr { - case "REQUESTING_CONFIGURATION": - status = .REQUESTING_CONFIGURATION - case "OK": - status = .OK - case "ACCESS_DENIED": - status = .ACCESS_DENIED - case "NOT_FOUND": - status = .NOT_FOUND - case "PORT_ERROR": - status = .PORT_ERROR - case "CLIENT_TOO_OLD": - status = .CLIENT_TOO_OLD - default: - break + if let b = jsonData["broadcastEnabled"] as? NSNumber { + broadcastEnabled = b.boolValue } - let typeStr = jsonData["type"] as! String - switch typeStr { - case "PRIVATE": - type = .PRIVATE - case "PUBLIC": - type = .PUBLIC - default: - break + if let d = jsonData["dhcp"] as? NSNumber { + dhcp = d.boolValue + } + + if let m = jsonData["mac"] as? String { + mac = m + } + + if let m = jsonData["mtu"] as? NSNumber { + mtu = m.integerValue + } + + if let n = jsonData["name"] as? String { + name = n + } + + if let n = jsonData["netconfRevision"] as? NSNumber { + netconfRevision = n.integerValue + } + + if let n = UInt64((jsonData["nwid"] as! String), radix: 16) { + nwid = n + } + + if let p = jsonData["portDeviceName"] as? String { + portDeviceName = p + } + + if let p = jsonData["portError"] as? NSNumber { + portError = p.integerValue + } + + if let statusStr = jsonData["status"] as? String { + switch statusStr { + case "REQUESTING_CONFIGURATION": + status = .REQUESTING_CONFIGURATION + case "OK": + status = .OK + case "ACCESS_DENIED": + status = .ACCESS_DENIED + case "NOT_FOUND": + status = .NOT_FOUND + case "PORT_ERROR": + status = .PORT_ERROR + case "CLIENT_TOO_OLD": + status = .CLIENT_TOO_OLD + default: + break + } + } + + if let typeStr = jsonData["type"] as? String { + switch typeStr { + case "PRIVATE": + type = .PRIVATE + case "PUBLIC": + type = .PUBLIC + default: + break + } } // if it's being initialized via JSON, it's connected @@ -133,20 +158,57 @@ class Network: NSObject, NSCoding { } required init?(coder aDecoder: NSCoder) { - self.assignedAddresses = aDecoder.decodeObjectForKey(PropertyKeys.addressesKey) as! [String] - self.bridge = aDecoder.decodeBoolForKey(PropertyKeys.bridgeKey) - self.broadcastEnabled = aDecoder.decodeBoolForKey(PropertyKeys.broadcastKey) - self.dhcp = aDecoder.decodeBoolForKey(PropertyKeys.dhcpKey) - self.mac = aDecoder.decodeObjectForKey(PropertyKeys.macKey) as! String - self.mtu = aDecoder.decodeIntegerForKey(PropertyKeys.mtuKey) - self.multicastSubscriptions = aDecoder.decodeObjectForKey(PropertyKeys.multicastKey) as! [String] - self.name = aDecoder.decodeObjectForKey(PropertyKeys.nameKey) as! String - self.netconfRevision = aDecoder.decodeIntegerForKey(PropertyKeys.netconfKey) - self.nwid = (aDecoder.decodeObjectForKey(PropertyKeys.nwidKey) as! NSNumber).unsignedLongLongValue - self.portDeviceName = aDecoder.decodeObjectForKey(PropertyKeys.portNameKey) as! String - self.portError = aDecoder.decodeIntegerForKey(PropertyKeys.portErrorKey) - self.status = NetworkStatus(rawValue: aDecoder.decodeIntegerForKey(PropertyKeys.statusKey))! - self.type = NetworkType(rawValue: aDecoder.decodeIntegerForKey(PropertyKeys.typeKey))! + if aDecoder.containsValueForKey(PropertyKeys.addressesKey) { + self.assignedAddresses = aDecoder.decodeObjectForKey(PropertyKeys.addressesKey) as! [String] + } + + if aDecoder.containsValueForKey(PropertyKeys.bridgeKey) { + self.bridge = aDecoder.decodeBoolForKey(PropertyKeys.bridgeKey) + } + + if aDecoder.containsValueForKey(PropertyKeys.broadcastKey) { + self.broadcastEnabled = aDecoder.decodeBoolForKey(PropertyKeys.broadcastKey) + } + + if aDecoder.containsValueForKey(PropertyKeys.dhcpKey) { + self.dhcp = aDecoder.decodeBoolForKey(PropertyKeys.dhcpKey) + } + + if aDecoder.containsValueForKey(PropertyKeys.macKey) { + self.mac = aDecoder.decodeObjectForKey(PropertyKeys.macKey) as! String + } + + if aDecoder.containsValueForKey(PropertyKeys.mtuKey) { + self.mtu = aDecoder.decodeIntegerForKey(PropertyKeys.mtuKey) + } + + if aDecoder.containsValueForKey(PropertyKeys.nameKey) { + self.name = aDecoder.decodeObjectForKey(PropertyKeys.nameKey) as! String + } + + if aDecoder.containsValueForKey(PropertyKeys.netconfKey) { + self.netconfRevision = aDecoder.decodeIntegerForKey(PropertyKeys.netconfKey) + } + + if aDecoder.containsValueForKey(PropertyKeys.nwidKey) { + self.nwid = (aDecoder.decodeObjectForKey(PropertyKeys.nwidKey) as! NSNumber).unsignedLongLongValue + } + + if aDecoder.containsValueForKey(PropertyKeys.portNameKey) { + self.portDeviceName = aDecoder.decodeObjectForKey(PropertyKeys.portNameKey) as! String + } + + if aDecoder.containsValueForKey(PropertyKeys.portErrorKey) { + self.portError = aDecoder.decodeIntegerForKey(PropertyKeys.portErrorKey) + } + + if aDecoder.containsValueForKey(PropertyKeys.statusKey) { + self.status = NetworkStatus(rawValue: aDecoder.decodeIntegerForKey(PropertyKeys.statusKey))! + } + + if aDecoder.containsValueForKey(PropertyKeys.typeKey) { + self.type = NetworkType(rawValue: aDecoder.decodeIntegerForKey(PropertyKeys.typeKey))! + } } func encodeWithCoder(aCoder: NSCoder) { @@ -156,7 +218,6 @@ class Network: NSObject, NSCoding { aCoder.encodeBool(self.dhcp, forKey: PropertyKeys.dhcpKey) aCoder.encodeObject(self.mac, forKey: PropertyKeys.macKey) aCoder.encodeInteger(self.mtu, forKey: PropertyKeys.mtuKey) - aCoder.encodeObject(self.multicastSubscriptions, forKey: PropertyKeys.multicastKey) aCoder.encodeObject(self.name, forKey: PropertyKeys.nameKey) aCoder.encodeInteger(self.netconfRevision, forKey: PropertyKeys.netconfKey) aCoder.encodeObject(NSNumber(unsignedLongLong: self.nwid), forKey: PropertyKeys.nwidKey) From bae0060552e652559937287f160a550622a5cab8 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Tue, 14 Jun 2016 19:59:45 -0700 Subject: [PATCH 23/91] add a timer to retrieve the network list every second. --- ZeroTier One/ShowNetworksViewController.swift | 51 +++++++++++-------- 1 file changed, 31 insertions(+), 20 deletions(-) diff --git a/ZeroTier One/ShowNetworksViewController.swift b/ZeroTier One/ShowNetworksViewController.swift index ed2479d16..d9a4291a0 100644 --- a/ZeroTier One/ShowNetworksViewController.swift +++ b/ZeroTier One/ShowNetworksViewController.swift @@ -14,6 +14,9 @@ class ShowNetworksViewController: NSViewController, NSTableViewDelegate, NSTable var networkList: [Network] = [Network]() + + var timer: NSTimer? = nil + override func viewDidLoad() { super.viewDidLoad() // Do view setup here. @@ -45,10 +48,36 @@ class ShowNetworksViewController: NSViewController, NSTableViewDelegate, NSTable override func viewWillAppear() { super.viewWillAppear() + updateNetworkInfo() + + self.timer = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: #selector(updateNetworkInfo), userInfo: nil, repeats: true) + } + + override func viewWillDisappear() { + super.viewWillDisappear() + + self.timer?.invalidate() + let filePath = dataFile() + NSKeyedArchiver.archiveRootObject(self.networkList, toFile: filePath) + } + + func deleteNetworkFromList(nwid: String) { + if let nwid = UInt64(nwid, radix: 16) { + let index = findNetworkWithID(nwid) + + if index != NSNotFound { + networkList.removeAtIndex(index) + tableView.reloadData() + } + } + } + + func updateNetworkInfo() { + let filePath = self.dataFile() if NSFileManager.defaultManager().fileExistsAtPath(filePath) { - networkList = NSKeyedUnarchiver.unarchiveObjectWithFile(filePath) as! [Network] + self.networkList = NSKeyedUnarchiver.unarchiveObjectWithFile(filePath) as! [Network] ServiceCom.getNetworkList() { (networkList) -> Void in @@ -85,7 +114,7 @@ class ShowNetworksViewController: NSViewController, NSTableViewDelegate, NSTable return false }) - + self.networkList = networkList self.tableView.reloadData() } @@ -93,24 +122,6 @@ class ShowNetworksViewController: NSViewController, NSTableViewDelegate, NSTable } } - override func viewWillDisappear() { - super.viewWillDisappear() - - let filePath = dataFile() - NSKeyedArchiver.archiveRootObject(self.networkList, toFile: filePath) - } - - func deleteNetworkFromList(nwid: String) { - if let nwid = UInt64(nwid, radix: 16) { - let index = findNetworkWithID(nwid) - - if index != NSNotFound { - networkList.removeAtIndex(index) - tableView.reloadData() - } - } - } - // NSTableViewDataSource func numberOfRowsInTableView(tableView: NSTableView) -> Int { From c8f85ffec97206174dbffbf6290ebcdcf84355ac Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Thu, 16 Jun 2016 20:53:55 -0700 Subject: [PATCH 24/91] Factored network updates into its own class. Delivered via notification --- ZeroTier One.xcodeproj/project.pbxproj | 4 + ZeroTier One/AppDelegate.swift | 12 +- ZeroTier One/Info.plist | 2 +- ZeroTier One/NetworkMonitor.swift | 116 ++++++++++++++++++ ZeroTier One/ServiceCom.swift | 5 + ZeroTier One/ShowNetworksViewController.swift | 87 ++----------- 6 files changed, 145 insertions(+), 81 deletions(-) create mode 100644 ZeroTier One/NetworkMonitor.swift diff --git a/ZeroTier One.xcodeproj/project.pbxproj b/ZeroTier One.xcodeproj/project.pbxproj index 0a8188c99..ed054cb4c 100644 --- a/ZeroTier One.xcodeproj/project.pbxproj +++ b/ZeroTier One.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 932D472D1D138B0C004BCFE2 /* NetworkMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 932D472C1D138B0C004BCFE2 /* NetworkMonitor.swift */; }; 9330F1351CEAB4C400687EC8 /* ServiceCom.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9330F1341CEAB4C400687EC8 /* ServiceCom.swift */; }; 9330F1371CEBF87200687EC8 /* Network.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9330F1361CEBF87200687EC8 /* Network.swift */; }; 9330F13B1CF534E500687EC8 /* NetworkInfoCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9330F13A1CF534E500687EC8 /* NetworkInfoCell.swift */; }; @@ -21,6 +22,7 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 932D472C1D138B0C004BCFE2 /* NetworkMonitor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkMonitor.swift; sourceTree = ""; }; 9330F1341CEAB4C400687EC8 /* ServiceCom.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServiceCom.swift; sourceTree = ""; }; 9330F1361CEBF87200687EC8 /* Network.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Network.swift; sourceTree = ""; }; 9330F13A1CF534E500687EC8 /* NetworkInfoCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkInfoCell.swift; sourceTree = ""; }; @@ -82,6 +84,7 @@ 93DAFE4A1CFE53CA00547CC4 /* AuthtokenCopy.m */, 93DAFE4C1CFE53DA00547CC4 /* AuthtokenCopy.h */, 93DAFE491CFE53C900547CC4 /* ZeroTier One-Bridging-Header.h */, + 932D472C1D138B0C004BCFE2 /* NetworkMonitor.swift */, ); path = "ZeroTier One"; sourceTree = ""; @@ -159,6 +162,7 @@ buildActionMask = 2147483647; files = ( 9330F1371CEBF87200687EC8 /* Network.swift in Sources */, + 932D472D1D138B0C004BCFE2 /* NetworkMonitor.swift in Sources */, 93326BDC1CE7C816005CA2AC /* AppDelegate.swift in Sources */, 93DAFE4B1CFE53CA00547CC4 /* AuthtokenCopy.m in Sources */, 9330F13B1CF534E500687EC8 /* NetworkInfoCell.swift in Sources */, diff --git a/ZeroTier One/AppDelegate.swift b/ZeroTier One/AppDelegate.swift index c7cbc09ac..f0bd04375 100644 --- a/ZeroTier One/AppDelegate.swift +++ b/ZeroTier One/AppDelegate.swift @@ -21,8 +21,11 @@ class AppDelegate: NSObject, NSApplicationDelegate { var transientMonitor: AnyObject? = nil - func applicationDidFinishLaunching(aNotification: NSNotification) { + let monitor = NetworkMonitor() + func applicationDidFinishLaunching(aNotification: NSNotification) { + let nc = NSNotificationCenter.defaultCenter() + nc.addObserver(self, selector: #selector(onNetworkListUpdated(_:)), name: networkUpdateKey, object: nil) statusItem.image = NSImage(named: "MenuBarIconMac") @@ -50,6 +53,8 @@ class AppDelegate: NSObject, NSApplicationDelegate { func applicationWillTerminate(aNotification: NSNotification) { // Insert code here to tear down your application + let nc = NSNotificationCenter.defaultCenter() + nc.removeObserver(self) } @@ -89,5 +94,10 @@ class AppDelegate: NSObject, NSApplicationDelegate { func quit() { NSApp.performSelector(#selector(NSApp.terminate(_:)), withObject: nil, afterDelay: 0.0) } + + func onNetworkListUpdated(note: NSNotification) { + let netList = note.userInfo!["networks"] as! [Network] + (networkListPopover.contentViewController as! ShowNetworksViewController).setNetworks(netList) + } } diff --git a/ZeroTier One/Info.plist b/ZeroTier One/Info.plist index 5777ef6c6..29e935d0d 100644 --- a/ZeroTier One/Info.plist +++ b/ZeroTier One/Info.plist @@ -21,7 +21,7 @@ CFBundleSignature ???? CFBundleVersion - 2 + 3 LSMinimumSystemVersion $(MACOSX_DEPLOYMENT_TARGET) LSUIElement diff --git a/ZeroTier One/NetworkMonitor.swift b/ZeroTier One/NetworkMonitor.swift new file mode 100644 index 000000000..124fce265 --- /dev/null +++ b/ZeroTier One/NetworkMonitor.swift @@ -0,0 +1,116 @@ +// +// NetworkMonitor.swift +// ZeroTier One +// +// Created by Grant Limberg on 6/16/16. +// Copyright © 2016 ZeroTier, Inc. All rights reserved. +// + +import Cocoa + +let networkUpdateKey = "com.zerotier.one.network-list" + +class NetworkMonitor: NSObject { + + var timer: NSTimer? = nil + + var savedNetworks: [Network] = [Network]() + var receivedNetworks: [Network] = [Network]() + var allNetworks: [Network] = [Network]() + + override init() { + super.init() + + timer = NSTimer.scheduledTimerWithTimeInterval(1.0, + target: self, + selector: #selector(updateNetworkInfo), + userInfo: nil, + repeats: true) + } + + deinit { + timer?.invalidate() + } + + private func dataFile() -> String { + var appSupport = NSFileManager.defaultManager().URLsForDirectory(.ApplicationSupportDirectory, inDomains: .UserDomainMask)[0] + appSupport = appSupport.URLByAppendingPathComponent("ZeroTier").URLByAppendingPathComponent("One").URLByAppendingPathComponent("networks.dat") + return appSupport.path! + } + + func updateNetworkInfo() { + let filePath = dataFile() + + if NSFileManager.defaultManager().fileExistsAtPath(filePath) { + self.savedNetworks = NSKeyedUnarchiver.unarchiveObjectWithFile(filePath) as! [Network] + + } + + ServiceCom.getNetworkList() { (networkList) -> Void in + self.receivedNetworks = networkList + + NSOperationQueue.mainQueue().addOperationWithBlock() { () -> Void in + self.internal_updateNetworkInfo() + } + } + } + + func deleteSavedNetwork(nwid: String) { + if let nwid = UInt64(nwid, radix: 16) { + let index = findNetworkWithID(nwid) + + if index != NSNotFound { + allNetworks.removeAtIndex(index) + } + } + } + + // Only to be called by updateNetworkInfo() + private func internal_updateNetworkInfo() { + var networks = self.savedNetworks + + for nw in receivedNetworks { + let index = findNetworkWithID(nw.nwid) + + if index != NSNotFound { + networks[index] = nw + } + networks.sortInPlace({ (left, right) -> Bool in + if left.nwid < right.nwid { + return true + } + + return false + }) + + objc_sync_enter(allNetworks) + allNetworks = networks + objc_sync_exit(allNetworks) + + saveNetworks() + + let nc = NSNotificationCenter.defaultCenter() + + nc.postNotificationName(networkUpdateKey, object: nil, userInfo: ["networks": networks]) + } + } + + private func findNetworkWithID(nwid: UInt64) -> Int { + for (index, element) in allNetworks.enumerate() { + + if element.nwid == nwid { + return index + } + } + + return NSNotFound + } + + private func saveNetworks() { + let file = dataFile() + + objc_sync_enter(allNetworks) + NSKeyedArchiver.archiveRootObject(self.allNetworks, toFile: file) + objc_sync_exit(allNetworks) + } +} diff --git a/ZeroTier One/ServiceCom.swift b/ZeroTier One/ServiceCom.swift index e25266105..21d5529c6 100644 --- a/ZeroTier One/ServiceCom.swift +++ b/ZeroTier One/ServiceCom.swift @@ -87,6 +87,11 @@ class ServiceCom: NSObject { let request = NSMutableURLRequest(URL: u) let session = NSURLSession.sharedSession() let task = session.dataTaskWithRequest(request) { (data, response, error) in + if error != nil{ + NSLog("\(error)") + return + } + let httpResponse = response as! NSHTTPURLResponse let status = httpResponse.statusCode diff --git a/ZeroTier One/ShowNetworksViewController.swift b/ZeroTier One/ShowNetworksViewController.swift index d9a4291a0..e8b9e332e 100644 --- a/ZeroTier One/ShowNetworksViewController.swift +++ b/ZeroTier One/ShowNetworksViewController.swift @@ -10,20 +10,18 @@ import Cocoa class ShowNetworksViewController: NSViewController, NSTableViewDelegate, NSTableViewDataSource { - @IBOutlet var tableView: NSTableView! + @IBOutlet var tableView: NSTableView? var networkList: [Network] = [Network]() - - - var timer: NSTimer? = nil + var netMonitor: NetworkMonitor! override func viewDidLoad() { super.viewDidLoad() // Do view setup here. - tableView.setDelegate(self) - tableView.setDataSource(self) - tableView.backgroundColor = NSColor.clearColor() + tableView?.setDelegate(self) + tableView?.setDataSource(self) + tableView?.backgroundColor = NSColor.clearColor() } private func dataFile() -> String { @@ -32,94 +30,25 @@ class ShowNetworksViewController: NSViewController, NSTableViewDelegate, NSTable return appSupport.path! } - - private func findNetworkWithID(id: UInt64) -> Int { - - for (index, element) in networkList.enumerate() { - - if element.nwid == id { - return index - } - } - - return NSNotFound - } - override func viewWillAppear() { super.viewWillAppear() - - updateNetworkInfo() - - self.timer = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: #selector(updateNetworkInfo), userInfo: nil, repeats: true) } override func viewWillDisappear() { super.viewWillDisappear() - self.timer?.invalidate() - let filePath = dataFile() NSKeyedArchiver.archiveRootObject(self.networkList, toFile: filePath) } func deleteNetworkFromList(nwid: String) { - if let nwid = UInt64(nwid, radix: 16) { - let index = findNetworkWithID(nwid) - if index != NSNotFound { - networkList.removeAtIndex(index) - tableView.reloadData() - } - } } - func updateNetworkInfo() { - let filePath = self.dataFile() - if NSFileManager.defaultManager().fileExistsAtPath(filePath) { - self.networkList = NSKeyedUnarchiver.unarchiveObjectWithFile(filePath) as! [Network] - - ServiceCom.getNetworkList() { (networkList) -> Void in - - for nw in networkList { - let index = self.findNetworkWithID(nw.nwid) - - if index != NSNotFound { - self.networkList[index] = nw - } - else { - self.networkList.append(nw) - } - } - - NSOperationQueue.mainQueue().addOperationWithBlock() { () -> Void in - self.networkList.sortInPlace({ (left, right) -> Bool in - if left.nwid < right.nwid { - return true - } - - return false - }) - self.tableView.reloadData() - } - } - } - else { - ServiceCom.getNetworkList() { (networkList) -> Void in - NSOperationQueue.mainQueue().addOperationWithBlock() { () -> Void in - self.networkList.sortInPlace({ (left, right) -> Bool in - if left.nwid < right.nwid { - return true - } - - return false - }) - - self.networkList = networkList - self.tableView.reloadData() - } - } - } + func setNetworks(list: [Network]) { + networkList = list + tableView?.reloadData() } // NSTableViewDataSource From 6d114b7480ed5a6356b8938a4ebf11a9d19adeec Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Tue, 21 Jun 2016 19:44:59 -0700 Subject: [PATCH 25/91] dynamically generate the menu so that networks are listed can join/leave networks directly from the menu --- ZeroTier One/AppDelegate.swift | 65 +++++++++++++++++++++++++++++----- 1 file changed, 57 insertions(+), 8 deletions(-) diff --git a/ZeroTier One/AppDelegate.swift b/ZeroTier One/AppDelegate.swift index f0bd04375..dc7a3b9ae 100644 --- a/ZeroTier One/AppDelegate.swift +++ b/ZeroTier One/AppDelegate.swift @@ -23,20 +23,15 @@ class AppDelegate: NSObject, NSApplicationDelegate { let monitor = NetworkMonitor() + var networks = [Network]() + func applicationDidFinishLaunching(aNotification: NSNotification) { let nc = NSNotificationCenter.defaultCenter() nc.addObserver(self, selector: #selector(onNetworkListUpdated(_:)), name: networkUpdateKey, object: nil) statusItem.image = NSImage(named: "MenuBarIconMac") - let menu = NSMenu() - - menu.addItem(NSMenuItem(title: "Show Networks", action: #selector(AppDelegate.showNetworks), keyEquivalent: "n")) - menu.addItem(NSMenuItem(title: "Join Network", action: #selector(AppDelegate.joinNetwork), keyEquivalent: "j")) - menu.addItem(NSMenuItem.separatorItem()) - menu.addItem(NSMenuItem(title: "Quit ZeroTier One", action: #selector(AppDelegate.quit), keyEquivalent: "q")) - - statusItem.menu = menu + buildMenu() joinNetworkPopover.contentViewController = JoinNetworkViewController( nibName: "JoinNetworkViewController", bundle: nil) @@ -98,6 +93,60 @@ class AppDelegate: NSObject, NSApplicationDelegate { func onNetworkListUpdated(note: NSNotification) { let netList = note.userInfo!["networks"] as! [Network] (networkListPopover.contentViewController as! ShowNetworksViewController).setNetworks(netList) + + self.networks = netList + + buildMenu() + } + + func buildMenu() { + let menu = NSMenu() + + menu.addItem(NSMenuItem(title: "Show Networks", action: #selector(AppDelegate.showNetworks), keyEquivalent: "n")) + menu.addItem(NSMenuItem(title: "Join Network", action: #selector(AppDelegate.joinNetwork), keyEquivalent: "j")) + menu.addItem(NSMenuItem.separatorItem()) + + if networks.count > 0 { + for net in networks { + let id = String(net.nwid, radix: 16) + let networkName = "\(id) (\(net.name))" + + let item = NSMenuItem(title: networkName, action: #selector(AppDelegate.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(title: "Quit ZeroTier One", action: #selector(AppDelegate.quit), keyEquivalent: "q")) + + statusItem.menu = menu + + } + + func toggleNetwork(sender: NSMenuItem) { + NSLog("\(sender.title)") + + let network = sender.representedObject as! Network + + let id = String(network.nwid, radix: 16) + + if network.connected { + ServiceCom.leaveNetwork(id) + } + else { + ServiceCom.joinNetwork(id) + } } } From b5a90a437b177e4cf486c571418f835e32ab8c1d Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Tue, 21 Jun 2016 19:45:08 -0700 Subject: [PATCH 26/91] update build number --- ZeroTier One/Info.plist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ZeroTier One/Info.plist b/ZeroTier One/Info.plist index 29e935d0d..87cfc3f6a 100644 --- a/ZeroTier One/Info.plist +++ b/ZeroTier One/Info.plist @@ -21,7 +21,7 @@ CFBundleSignature ???? CFBundleVersion - 3 + 4 LSMinimumSystemVersion $(MACOSX_DEPLOYMENT_TARGET) LSUIElement From b256111a17ba9ea223b7d706eebf66605e46c701 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Tue, 21 Jun 2016 20:02:18 -0700 Subject: [PATCH 27/91] misc cleanup --- ZeroTier One/AppDelegate.swift | 8 +++++++- ZeroTier One/ServiceCom.swift | 8 ++++---- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/ZeroTier One/AppDelegate.swift b/ZeroTier One/AppDelegate.swift index dc7a3b9ae..8e0cbf106 100644 --- a/ZeroTier One/AppDelegate.swift +++ b/ZeroTier One/AppDelegate.swift @@ -109,7 +109,13 @@ class AppDelegate: NSObject, NSApplicationDelegate { if networks.count > 0 { for net in networks { let id = String(net.nwid, radix: 16) - let networkName = "\(id) (\(net.name))" + var networkName = "" + if net.name.isEmpty { + networkName = "\(id)" + } + else { + networkName = "\(id) (\(net.name))" + } let item = NSMenuItem(title: networkName, action: #selector(AppDelegate.toggleNetwork(_:)), keyEquivalent: "") diff --git a/ZeroTier One/ServiceCom.swift b/ZeroTier One/ServiceCom.swift index 21d5529c6..23c194c27 100644 --- a/ZeroTier One/ServiceCom.swift +++ b/ZeroTier One/ServiceCom.swift @@ -133,10 +133,10 @@ class ServiceCom: NSObject { let status = httpResponse.statusCode if status == 200 { - print("join ok") + NSLog("join ok") } else { - print("join error: \(status)") + NSLog("join error: \(status)") } } @@ -158,10 +158,10 @@ class ServiceCom: NSObject { let status = httpResponse.statusCode if status == 200 { - print("leave ok") + NSLog("leave ok") } else { - print("leave error: \(status)") + NSLog("leave error: \(status)") } } From c3deedd6fd2198f2ee379c896aee55b7c6d894cb Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Thu, 23 Jun 2016 19:44:00 -0700 Subject: [PATCH 28/91] Fix menu bar icon. Add app icon. --- ZeroTier One.xcodeproj/project.pbxproj | 6 ++++-- .../AppIcon.appiconset/Contents.json | 3 ++- .../AppIcon.appiconset/ZeroTierIcon512x512.png | Bin 0 -> 51309 bytes .../MenuBarIconMac.imageset/Contents.json | 4 ++-- .../MenuBarIconMac.imageset/MenuBar.png | Bin 0 -> 392 bytes .../MenuBarIconMac.imageset/MenuBar@2x.png | Bin 0 -> 761 bytes .../MenuBarIconMac.imageset/MenuBarIconMac.png | Bin 3810 -> 0 bytes .../MenuBarIconMac@2x.png | Bin 5444 -> 0 bytes ZeroTier One/Info.plist | 2 +- ZeroTier One/ZeroTierIcon.icns | Bin 0 -> 125598 bytes 10 files changed, 9 insertions(+), 6 deletions(-) create mode 100644 ZeroTier One/Assets.xcassets/AppIcon.appiconset/ZeroTierIcon512x512.png create mode 100644 ZeroTier One/Assets.xcassets/MenuBarIconMac.imageset/MenuBar.png create mode 100644 ZeroTier One/Assets.xcassets/MenuBarIconMac.imageset/MenuBar@2x.png delete mode 100644 ZeroTier One/Assets.xcassets/MenuBarIconMac.imageset/MenuBarIconMac.png delete mode 100644 ZeroTier One/Assets.xcassets/MenuBarIconMac.imageset/MenuBarIconMac@2x.png create mode 100644 ZeroTier One/ZeroTierIcon.icns diff --git a/ZeroTier One.xcodeproj/project.pbxproj b/ZeroTier One.xcodeproj/project.pbxproj index ed054cb4c..5aa714565 100644 --- a/ZeroTier One.xcodeproj/project.pbxproj +++ b/ZeroTier One.xcodeproj/project.pbxproj @@ -8,6 +8,7 @@ /* Begin PBXBuildFile section */ 932D472D1D138B0C004BCFE2 /* NetworkMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 932D472C1D138B0C004BCFE2 /* NetworkMonitor.swift */; }; + 932D472F1D1CD499004BCFE2 /* ZeroTierIcon.icns in Resources */ = {isa = PBXBuildFile; fileRef = 932D472E1D1CD499004BCFE2 /* ZeroTierIcon.icns */; }; 9330F1351CEAB4C400687EC8 /* ServiceCom.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9330F1341CEAB4C400687EC8 /* ServiceCom.swift */; }; 9330F1371CEBF87200687EC8 /* Network.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9330F1361CEBF87200687EC8 /* Network.swift */; }; 9330F13B1CF534E500687EC8 /* NetworkInfoCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9330F13A1CF534E500687EC8 /* NetworkInfoCell.swift */; }; @@ -23,6 +24,7 @@ /* Begin PBXFileReference section */ 932D472C1D138B0C004BCFE2 /* NetworkMonitor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkMonitor.swift; sourceTree = ""; }; + 932D472E1D1CD499004BCFE2 /* ZeroTierIcon.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = ZeroTierIcon.icns; sourceTree = ""; }; 9330F1341CEAB4C400687EC8 /* ServiceCom.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServiceCom.swift; sourceTree = ""; }; 9330F1361CEBF87200687EC8 /* Network.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Network.swift; sourceTree = ""; }; 9330F13A1CF534E500687EC8 /* NetworkInfoCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkInfoCell.swift; sourceTree = ""; }; @@ -70,6 +72,7 @@ 93326BDA1CE7C816005CA2AC /* ZeroTier One */ = { isa = PBXGroup; children = ( + 932D472E1D1CD499004BCFE2 /* ZeroTierIcon.icns */, 93326BDB1CE7C816005CA2AC /* AppDelegate.swift */, 93326BE81CE7D9B9005CA2AC /* JoinNetworkViewController.swift */, 9330F1361CEBF87200687EC8 /* Network.swift */, @@ -151,6 +154,7 @@ 93326BEB1CE7D9B9005CA2AC /* JoinNetworkViewController.xib in Resources */, 93326BDE1CE7C816005CA2AC /* Assets.xcassets in Resources */, 93326BE11CE7C816005CA2AC /* MainMenu.xib in Resources */, + 932D472F1D1CD499004BCFE2 /* ZeroTierIcon.icns in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -271,7 +275,6 @@ 93326BE61CE7C816005CA2AC /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; COMBINE_HIDPI_IMAGES = YES; INFOPLIST_FILE = "ZeroTier One/Info.plist"; @@ -287,7 +290,6 @@ 93326BE71CE7C816005CA2AC /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; COMBINE_HIDPI_IMAGES = YES; INFOPLIST_FILE = "ZeroTier One/Info.plist"; diff --git a/ZeroTier One/Assets.xcassets/AppIcon.appiconset/Contents.json b/ZeroTier One/Assets.xcassets/AppIcon.appiconset/Contents.json index 2db2b1c7c..24c81d35f 100644 --- a/ZeroTier One/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/ZeroTier One/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -41,8 +41,9 @@ "scale" : "2x" }, { - "idiom" : "mac", "size" : "512x512", + "idiom" : "mac", + "filename" : "ZeroTierIcon512x512.png", "scale" : "1x" }, { diff --git a/ZeroTier One/Assets.xcassets/AppIcon.appiconset/ZeroTierIcon512x512.png b/ZeroTier One/Assets.xcassets/AppIcon.appiconset/ZeroTierIcon512x512.png new file mode 100644 index 0000000000000000000000000000000000000000..d225c2e339de78c4e4ba15c802cb7ae51f4222e4 GIT binary patch literal 51309 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 literal 0 HcmV?d00001 diff --git a/ZeroTier One/Assets.xcassets/MenuBarIconMac.imageset/Contents.json b/ZeroTier One/Assets.xcassets/MenuBarIconMac.imageset/Contents.json index ab75ef9a7..3213cbe1e 100644 --- a/ZeroTier One/Assets.xcassets/MenuBarIconMac.imageset/Contents.json +++ b/ZeroTier One/Assets.xcassets/MenuBarIconMac.imageset/Contents.json @@ -2,12 +2,12 @@ "images" : [ { "idiom" : "mac", - "filename" : "MenuBarIconMac.png", + "filename" : "MenuBar.png", "scale" : "1x" }, { "idiom" : "mac", - "filename" : "MenuBarIconMac@2x.png", + "filename" : "MenuBar@2x.png", "scale" : "2x" } ], diff --git a/ZeroTier One/Assets.xcassets/MenuBarIconMac.imageset/MenuBar.png b/ZeroTier One/Assets.xcassets/MenuBarIconMac.imageset/MenuBar.png new file mode 100644 index 0000000000000000000000000000000000000000..2734f6b65c4e9e68d047bf28443c9d2e849e55f9 GIT binary patch literal 392 zcmV;30eAk1P)x2xu{{I1W1VZdgxqhRd1Xz3-im2Y7}jnx}(SjG$Z)9Kb7l!LNes zr`YZ_TwpjR+!B#_joxrw#`D-oZfjP>4z{o^nRE@m@fAaj09&ydAX6y0dHlx#dV$GU z=uEg_B2`O6uTHWYYGXyfPMo8jsb`_^6ld7YLb4C4iZ;v@b;lnp;spltpl^bIi-4wc zZ}Xt-!A~QhC&3@)LHC09L_+(5@8>~32Y(+4eTO5g@k0000~48sId_XU!7=YDHg%bN={;9VIg8fIT1w= zNhtvZ!58=-M?ejnitqS5i|<=^&fL!IxwAPB@`q#QcK`RyZ-<%hn_FlS1=_9xHa8N) zqUXTP@XHRM@}GW2AezxU(Cy*r^T1{8p9C&=h*<_6dx%q|WkfS^$Y~2Z53SR6u66%YZwq z-;V%qjO#^Uan_LSz$IhQQDBX6KW^7A1crdmdar~$SSDHkj1kS^o*EukdRhy-(EFc& zo-9C%I>0YrSBxh~9_<2Nsi%x=IB(i(035aWk|z9($OfwbH;5`^D&>I>8gvYMN~@=|iWDSi92(3xILtsow%~9R7N1{fK(MAe%K-t{Gj8EPjxa z(6K?hs4YfNTZKvr+GqGqd1Cyd6Grf%7*7(=9^j*T9s=|HV#<0x zZ-5Lly$HBKw*v3<{xe{OZ^%#q)d${3NHz#h=y9Ok$c72;-2ytZf;82K98$u8?;xqB zm+Iv)fV?dGuGj7(2l3XKfDF|F>;$d??+w6OpXbXb1ZFwMSp roCWo*rUCVx&t4O~_!{>dyE*h5^ZnuuPfS-000000NkvXXu0mjf;_6!M literal 0 HcmV?d00001 diff --git a/ZeroTier One/Assets.xcassets/MenuBarIconMac.imageset/MenuBarIconMac.png b/ZeroTier One/Assets.xcassets/MenuBarIconMac.imageset/MenuBarIconMac.png deleted file mode 100644 index 74e59fe79bd676b378d66de2d12d05dfa26e3746..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3810 zcmdT{dsGu=79ZQHm6z4@NUANyhzhHd#}LTF5QQL+9)diAh*e-RnS|IR6Egz|DlGMZ zRiL!sT3AuEs8vw#fi4w`h=^~owmwie)<@C0nu3dOk)26+M1&r9&)GjV=Oi<8zx(^$ z`+dLf-ubdRB0R*^X{-|f0IssopeXV>hWfrgjQp*y)J-QZ?`Z<$0RXTk*ZCzplze6& zp;2-GSUed3GFAY{VI`JK_%oSQpx%!?PeQBii)6sN zr$tVXO^vX*EDn1hm@x?>Fgyt}_GbASws&|?0@R`%BrY{FC`^U)1~ev{bhbrNA#7BK z>QMuMlQ`~x`I6%F0$KZoh{1BSziSt;a#cMCXRKwOb(MA=a&jwPEA zbXE$PexHh@NMdUJMbx-1nG!O_pd}=b*d|MVbDyqu)NGTMq=PA#gxd{qv^n_qDkYLh zr4c4ft<|9tJSrrRA)6)j;|l%!K!`g&>v`{j5fjuO3l&+aWM7wwuOlsER4f|15kyC)R;*R6B4Z+)}U;o zK_h1O867;yy&{s6b*_R5I0z*^k4? z;fZieO_*U58lWM|;B8VL2-cT=Z-j1oa}sL!GkxuBHm|`+v<94j4G1a=l92m>rPZoL zLLaqK3GsYD9t0^tC5MNAN`$8deUy9%LY2Nk#Mf8MwzE6H2af?GwibZc_LHgy+HLI* z6SM6L{(mF{k^~zxs7k`_nOcvb|J?ev#{Wt?2=x&NxdH^_356;U7VsdDdImW#qVnUQ z0-gZk^vTW@@OhyiHc2;~sMeh5=x2pOjD9i~Pgu9{1ppp;5AALMdTUr?yzp`ZX! zp(>@C%U24Kp0#oy>kedJS%*WG{RC&LAMyw%aU!8T$3cnHV&li|QMaqUeoK8);;nEqYre91H;~dcrC_@=Er&Z{ z2#s&}rK;V1zw`Q}nY{Y&Z{}?~T8XuGRxP~~eto_6r)%eyJZUkXGuOP>fH%^NE;Utc zU!A<|zb8)0C|v2we6GH;`}tkgp{%NpDlTt-ku+%u4O`ypBu9%13N@l~__-o-#cC*VU6&D!uRp?=QV&m6fWJb0&Yf zf_5XYXkMXy-K{x3E*HEyqtB*&dE{vP{gxlP=#TQIcyA~g*?EB{_3miv{4tqlDV{F) zBE6{YNw#zOQ;(eDj_9?ye>l&hO~~FaU&fRc-8h3M7YUo2x7}O{l)7dH@5_ESOCL5z zd}e&{JATh+<_!rxij8b($XI(bd!h4EmQOzxrwtv;u;@Gwa4<{<5 z%V%8mFWR`FcJoOmUG9$WFt>^f7X4ymB407ZJ!5tlTX*dE@vNyy6(NGZmaYV?rI8Pf zTVjre%;?O?A3oO7S)U%d_CC<~Q)b}r`HwA68loFMEoh2vsK{MBvniwwtQIw=od2ve!29B8npt=w;&G+pEV}>HTySxp-nfYsPo!vKl z(WA2Jx$)OLtAPnk-#(4rmi2&TSeiB-J8YrHJpKrbK4_&Yv4kt0%tnpJS2Bilv**YX zI{%N#BR}UJuG0HoSdzN7?3WJTOHVzDADv#4=-ayd$Bc%;2VTb9+L+iI2)x?0#MO&) zH!^SiefP6_SK&Kb)=bXII9Sb%7~1&7_|Gd6@7zvyQGhP{^l_i0e7&i`>Njh_lc-E5 zbDqJ4u1Hz_7hNQtuAap8to_$Hm)dbLmm~1Yej{osJT?VC7GHKF+;b;v4|PdcHZAyg z$FX?n^1>&sOMD(h<}{?AIl$QSuuc~pUinmZyLs2|>l*G0Vw>LKw7;AFWd5&Tj{mv# z=7NVi%3MA!vfkvKdT^e3cwzqe8Eyxjh3n2-JXNO|FCLme+Z;dY$cY!!KS|lF@SyU* H#AW{m`BNzA diff --git a/ZeroTier One/Assets.xcassets/MenuBarIconMac.imageset/MenuBarIconMac@2x.png b/ZeroTier One/Assets.xcassets/MenuBarIconMac.imageset/MenuBarIconMac@2x.png deleted file mode 100644 index 1c99f851f4019b99d756e617bb59e1f973f16850..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5444 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 $(EXECUTABLE_NAME) CFBundleIconFile - + ZeroTierIcon.icns CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion diff --git a/ZeroTier One/ZeroTierIcon.icns b/ZeroTier One/ZeroTierIcon.icns new file mode 100644 index 0000000000000000000000000000000000000000..17e60d587da5167203d579a4468f4612fc403cbd GIT binary patch literal 125598 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 literal 0 HcmV?d00001 From 3c534abc9989719061dedd43a74221f73409357b Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Thu, 23 Jun 2016 19:48:48 -0700 Subject: [PATCH 29/91] "Show Networks" -> "Network Details" --- ZeroTier One/AppDelegate.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ZeroTier One/AppDelegate.swift b/ZeroTier One/AppDelegate.swift index 8e0cbf106..0179780f8 100644 --- a/ZeroTier One/AppDelegate.swift +++ b/ZeroTier One/AppDelegate.swift @@ -102,7 +102,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { func buildMenu() { let menu = NSMenu() - menu.addItem(NSMenuItem(title: "Show Networks", action: #selector(AppDelegate.showNetworks), keyEquivalent: "n")) + menu.addItem(NSMenuItem(title: "Network Details", action: #selector(AppDelegate.showNetworks), keyEquivalent: "n")) menu.addItem(NSMenuItem(title: "Join Network", action: #selector(AppDelegate.joinNetwork), keyEquivalent: "j")) menu.addItem(NSMenuItem.separatorItem()) From 35d1736c9726d3be8ab2cbc63b445d14b32c4d14 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Thu, 23 Jun 2016 20:04:57 -0700 Subject: [PATCH 30/91] add preferences view not populated yet --- ZeroTier One.xcodeproj/project.pbxproj | 8 ++++++ ZeroTier One/AppDelegate.swift | 27 ++++++++++++++++++++ ZeroTier One/JoinNetworkViewController.xib | 1 - ZeroTier One/PreferencesViewController.swift | 18 +++++++++++++ ZeroTier One/PreferencesViewController.xib | 19 ++++++++++++++ ZeroTier One/ShowNetworksViewController.xib | 1 - 6 files changed, 72 insertions(+), 2 deletions(-) create mode 100644 ZeroTier One/PreferencesViewController.swift create mode 100644 ZeroTier One/PreferencesViewController.xib diff --git a/ZeroTier One.xcodeproj/project.pbxproj b/ZeroTier One.xcodeproj/project.pbxproj index 5aa714565..d6b38dd8d 100644 --- a/ZeroTier One.xcodeproj/project.pbxproj +++ b/ZeroTier One.xcodeproj/project.pbxproj @@ -9,6 +9,8 @@ /* Begin PBXBuildFile section */ 932D472D1D138B0C004BCFE2 /* NetworkMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 932D472C1D138B0C004BCFE2 /* NetworkMonitor.swift */; }; 932D472F1D1CD499004BCFE2 /* ZeroTierIcon.icns in Resources */ = {isa = PBXBuildFile; fileRef = 932D472E1D1CD499004BCFE2 /* ZeroTierIcon.icns */; }; + 932D47321D1CD861004BCFE2 /* PreferencesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 932D47301D1CD861004BCFE2 /* PreferencesViewController.swift */; }; + 932D47331D1CD861004BCFE2 /* PreferencesViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 932D47311D1CD861004BCFE2 /* PreferencesViewController.xib */; }; 9330F1351CEAB4C400687EC8 /* ServiceCom.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9330F1341CEAB4C400687EC8 /* ServiceCom.swift */; }; 9330F1371CEBF87200687EC8 /* Network.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9330F1361CEBF87200687EC8 /* Network.swift */; }; 9330F13B1CF534E500687EC8 /* NetworkInfoCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9330F13A1CF534E500687EC8 /* NetworkInfoCell.swift */; }; @@ -25,6 +27,8 @@ /* Begin PBXFileReference section */ 932D472C1D138B0C004BCFE2 /* NetworkMonitor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkMonitor.swift; sourceTree = ""; }; 932D472E1D1CD499004BCFE2 /* ZeroTierIcon.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = ZeroTierIcon.icns; sourceTree = ""; }; + 932D47301D1CD861004BCFE2 /* PreferencesViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PreferencesViewController.swift; sourceTree = ""; }; + 932D47311D1CD861004BCFE2 /* PreferencesViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = PreferencesViewController.xib; sourceTree = ""; }; 9330F1341CEAB4C400687EC8 /* ServiceCom.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServiceCom.swift; sourceTree = ""; }; 9330F1361CEBF87200687EC8 /* Network.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Network.swift; sourceTree = ""; }; 9330F13A1CF534E500687EC8 /* NetworkInfoCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkInfoCell.swift; sourceTree = ""; }; @@ -88,6 +92,8 @@ 93DAFE4C1CFE53DA00547CC4 /* AuthtokenCopy.h */, 93DAFE491CFE53C900547CC4 /* ZeroTier One-Bridging-Header.h */, 932D472C1D138B0C004BCFE2 /* NetworkMonitor.swift */, + 932D47301D1CD861004BCFE2 /* PreferencesViewController.swift */, + 932D47311D1CD861004BCFE2 /* PreferencesViewController.xib */, ); path = "ZeroTier One"; sourceTree = ""; @@ -155,6 +161,7 @@ 93326BDE1CE7C816005CA2AC /* Assets.xcassets in Resources */, 93326BE11CE7C816005CA2AC /* MainMenu.xib in Resources */, 932D472F1D1CD499004BCFE2 /* ZeroTierIcon.icns in Resources */, + 932D47331D1CD861004BCFE2 /* PreferencesViewController.xib in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -167,6 +174,7 @@ files = ( 9330F1371CEBF87200687EC8 /* Network.swift in Sources */, 932D472D1D138B0C004BCFE2 /* NetworkMonitor.swift in Sources */, + 932D47321D1CD861004BCFE2 /* PreferencesViewController.swift in Sources */, 93326BDC1CE7C816005CA2AC /* AppDelegate.swift in Sources */, 93DAFE4B1CFE53CA00547CC4 /* AuthtokenCopy.m in Sources */, 9330F13B1CF534E500687EC8 /* NetworkInfoCell.swift in Sources */, diff --git a/ZeroTier One/AppDelegate.swift b/ZeroTier One/AppDelegate.swift index 0179780f8..f37eba731 100644 --- a/ZeroTier One/AppDelegate.swift +++ b/ZeroTier One/AppDelegate.swift @@ -18,6 +18,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { let networkListPopover = NSPopover() let joinNetworkPopover = NSPopover() + let preferencesPopover = NSPopover() var transientMonitor: AnyObject? = nil @@ -44,6 +45,12 @@ class AppDelegate: NSObject, NSApplicationDelegate { networkListPopover.behavior = .Transient networkListPopover.appearance = NSAppearance(named: NSAppearanceNameAqua) + + preferencesPopover.contentViewController = PreferencesViewController( + nibName: "PreferencesViewController", bundle: nil) + preferencesPopover.behavior = .Transient + + preferencesPopover.appearance = NSAppearance(named: NSAppearanceNameAqua) } func applicationWillTerminate(aNotification: NSNotification) { @@ -86,6 +93,22 @@ class AppDelegate: NSObject, NSApplicationDelegate { } } + func showPreferences() { + if let button = statusItem.button { + preferencesPopover.showRelativeToRect(button.bounds, ofView: button, preferredEdge: .MinY) + + if transientMonitor == nil { + transientMonitor = NSEvent.addGlobalMonitorForEventsMatchingMask( + [.LeftMouseDownMask, .RightMouseDownMask, .OtherMouseDownMask]) { (event: NSEvent) -> Void in + + NSEvent.removeMonitor(self.transientMonitor!) + self.transientMonitor = nil + self.preferencesPopover.close() + } + } + } + } + func quit() { NSApp.performSelector(#selector(NSApp.terminate(_:)), withObject: nil, afterDelay: 0.0) } @@ -134,6 +157,10 @@ class AppDelegate: NSObject, NSApplicationDelegate { menu.addItem(NSMenuItem.separatorItem()) } + menu.addItem(NSMenuItem(title: "Preferences...", action: #selector(AppDelegate.showPreferences), keyEquivalent: ",")) + + menu.addItem(NSMenuItem.separatorItem()) + menu.addItem(NSMenuItem(title: "Quit ZeroTier One", action: #selector(AppDelegate.quit), keyEquivalent: "q")) statusItem.menu = menu diff --git a/ZeroTier One/JoinNetworkViewController.xib b/ZeroTier One/JoinNetworkViewController.xib index 7a2ffe708..625f1a3e1 100644 --- a/ZeroTier One/JoinNetworkViewController.xib +++ b/ZeroTier One/JoinNetworkViewController.xib @@ -1,7 +1,6 @@ - diff --git a/ZeroTier One/PreferencesViewController.swift b/ZeroTier One/PreferencesViewController.swift new file mode 100644 index 000000000..95d2d8821 --- /dev/null +++ b/ZeroTier One/PreferencesViewController.swift @@ -0,0 +1,18 @@ +// +// PreferencesViewController.swift +// ZeroTier One +// +// Created by Grant Limberg on 6/23/16. +// Copyright © 2016 ZeroTier, Inc. All rights reserved. +// + +import Cocoa + +class PreferencesViewController: NSViewController { + + override func viewDidLoad() { + super.viewDidLoad() + // Do view setup here. + } + +} diff --git a/ZeroTier One/PreferencesViewController.xib b/ZeroTier One/PreferencesViewController.xib new file mode 100644 index 000000000..2a80a43a3 --- /dev/null +++ b/ZeroTier One/PreferencesViewController.xib @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/ZeroTier One/ShowNetworksViewController.xib b/ZeroTier One/ShowNetworksViewController.xib index 12f1ea3ce..69120fc96 100644 --- a/ZeroTier One/ShowNetworksViewController.xib +++ b/ZeroTier One/ShowNetworksViewController.xib @@ -1,7 +1,6 @@ - From f2e2632ae706a97c7172e6925d395d8752b044ed Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Thu, 23 Jun 2016 20:22:03 -0700 Subject: [PATCH 31/91] Added About view also not yet populated --- ZeroTier One.xcodeproj/project.pbxproj | 8 ++++++++ ZeroTier One/AboutViewController.swift | 18 ++++++++++++++++++ ZeroTier One/AboutViewController.xib | 19 +++++++++++++++++++ ZeroTier One/AppDelegate.swift | 25 +++++++++++++++++++++++++ 4 files changed, 70 insertions(+) create mode 100644 ZeroTier One/AboutViewController.swift create mode 100644 ZeroTier One/AboutViewController.xib diff --git a/ZeroTier One.xcodeproj/project.pbxproj b/ZeroTier One.xcodeproj/project.pbxproj index d6b38dd8d..657f5150c 100644 --- a/ZeroTier One.xcodeproj/project.pbxproj +++ b/ZeroTier One.xcodeproj/project.pbxproj @@ -11,6 +11,8 @@ 932D472F1D1CD499004BCFE2 /* ZeroTierIcon.icns in Resources */ = {isa = PBXBuildFile; fileRef = 932D472E1D1CD499004BCFE2 /* ZeroTierIcon.icns */; }; 932D47321D1CD861004BCFE2 /* PreferencesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 932D47301D1CD861004BCFE2 /* PreferencesViewController.swift */; }; 932D47331D1CD861004BCFE2 /* PreferencesViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 932D47311D1CD861004BCFE2 /* PreferencesViewController.xib */; }; + 932D47361D1CDC9B004BCFE2 /* AboutViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 932D47341D1CDC9B004BCFE2 /* AboutViewController.swift */; }; + 932D47371D1CDC9B004BCFE2 /* AboutViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 932D47351D1CDC9B004BCFE2 /* AboutViewController.xib */; }; 9330F1351CEAB4C400687EC8 /* ServiceCom.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9330F1341CEAB4C400687EC8 /* ServiceCom.swift */; }; 9330F1371CEBF87200687EC8 /* Network.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9330F1361CEBF87200687EC8 /* Network.swift */; }; 9330F13B1CF534E500687EC8 /* NetworkInfoCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9330F13A1CF534E500687EC8 /* NetworkInfoCell.swift */; }; @@ -29,6 +31,8 @@ 932D472E1D1CD499004BCFE2 /* ZeroTierIcon.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = ZeroTierIcon.icns; sourceTree = ""; }; 932D47301D1CD861004BCFE2 /* PreferencesViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PreferencesViewController.swift; sourceTree = ""; }; 932D47311D1CD861004BCFE2 /* PreferencesViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = PreferencesViewController.xib; sourceTree = ""; }; + 932D47341D1CDC9B004BCFE2 /* AboutViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AboutViewController.swift; sourceTree = ""; }; + 932D47351D1CDC9B004BCFE2 /* AboutViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = AboutViewController.xib; sourceTree = ""; }; 9330F1341CEAB4C400687EC8 /* ServiceCom.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServiceCom.swift; sourceTree = ""; }; 9330F1361CEBF87200687EC8 /* Network.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Network.swift; sourceTree = ""; }; 9330F13A1CF534E500687EC8 /* NetworkInfoCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkInfoCell.swift; sourceTree = ""; }; @@ -94,6 +98,8 @@ 932D472C1D138B0C004BCFE2 /* NetworkMonitor.swift */, 932D47301D1CD861004BCFE2 /* PreferencesViewController.swift */, 932D47311D1CD861004BCFE2 /* PreferencesViewController.xib */, + 932D47341D1CDC9B004BCFE2 /* AboutViewController.swift */, + 932D47351D1CDC9B004BCFE2 /* AboutViewController.xib */, ); path = "ZeroTier One"; sourceTree = ""; @@ -157,6 +163,7 @@ buildActionMask = 2147483647; files = ( 93326BEF1CE7DA30005CA2AC /* ShowNetworksViewController.xib in Resources */, + 932D47371D1CDC9B004BCFE2 /* AboutViewController.xib in Resources */, 93326BEB1CE7D9B9005CA2AC /* JoinNetworkViewController.xib in Resources */, 93326BDE1CE7C816005CA2AC /* Assets.xcassets in Resources */, 93326BE11CE7C816005CA2AC /* MainMenu.xib in Resources */, @@ -180,6 +187,7 @@ 9330F13B1CF534E500687EC8 /* NetworkInfoCell.swift in Sources */, 93326BEA1CE7D9B9005CA2AC /* JoinNetworkViewController.swift in Sources */, 93326BEE1CE7DA30005CA2AC /* ShowNetworksViewController.swift in Sources */, + 932D47361D1CDC9B004BCFE2 /* AboutViewController.swift in Sources */, 9330F1351CEAB4C400687EC8 /* ServiceCom.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/ZeroTier One/AboutViewController.swift b/ZeroTier One/AboutViewController.swift new file mode 100644 index 000000000..bbca975a9 --- /dev/null +++ b/ZeroTier One/AboutViewController.swift @@ -0,0 +1,18 @@ +// +// AboutViewController.swift +// ZeroTier One +// +// Created by Grant Limberg on 6/23/16. +// Copyright © 2016 ZeroTier, Inc. All rights reserved. +// + +import Cocoa + +class AboutViewController: NSViewController { + + override func viewDidLoad() { + super.viewDidLoad() + // Do view setup here. + } + +} diff --git a/ZeroTier One/AboutViewController.xib b/ZeroTier One/AboutViewController.xib new file mode 100644 index 000000000..8722929fe --- /dev/null +++ b/ZeroTier One/AboutViewController.xib @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/ZeroTier One/AppDelegate.swift b/ZeroTier One/AppDelegate.swift index f37eba731..2ca97f3c1 100644 --- a/ZeroTier One/AppDelegate.swift +++ b/ZeroTier One/AppDelegate.swift @@ -19,6 +19,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { let networkListPopover = NSPopover() let joinNetworkPopover = NSPopover() let preferencesPopover = NSPopover() + let aboutPopover = NSPopover() var transientMonitor: AnyObject? = nil @@ -51,6 +52,13 @@ class AppDelegate: NSObject, NSApplicationDelegate { preferencesPopover.behavior = .Transient preferencesPopover.appearance = NSAppearance(named: NSAppearanceNameAqua) + + aboutPopover.contentViewController = AboutViewController( + nibName: "AboutViewController", bundle: nil) + aboutPopover.behavior = .Transient + + aboutPopover.appearance = NSAppearance(named: NSAppearanceNameAqua) + } func applicationWillTerminate(aNotification: NSNotification) { @@ -109,6 +117,22 @@ class AppDelegate: NSObject, NSApplicationDelegate { } } + func showAbout() { + if let button = statusItem.button { + aboutPopover.showRelativeToRect(button.bounds, ofView: button, preferredEdge: .MinY) + + if transientMonitor == nil { + transientMonitor = NSEvent.addGlobalMonitorForEventsMatchingMask( + [.LeftMouseDownMask, .RightMouseDownMask, .OtherMouseDownMask]) { (event: NSEvent) -> Void in + + NSEvent.removeMonitor(self.transientMonitor!) + self.transientMonitor = nil + self.aboutPopover.close() + } + } + } + } + func quit() { NSApp.performSelector(#selector(NSApp.terminate(_:)), withObject: nil, afterDelay: 0.0) } @@ -157,6 +181,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { menu.addItem(NSMenuItem.separatorItem()) } + menu.addItem(NSMenuItem(title: "About ZeroTier One...", action: #selector(AppDelegate.showAbout), keyEquivalent: "")) menu.addItem(NSMenuItem(title: "Preferences...", action: #selector(AppDelegate.showPreferences), keyEquivalent: ",")) menu.addItem(NSMenuItem.separatorItem()) From e621058cfd6ad505de9dcb5ee38e0d0983091f66 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Fri, 24 Jun 2016 15:24:03 -0700 Subject: [PATCH 32/91] build number 5 --- ZeroTier One/Info.plist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ZeroTier One/Info.plist b/ZeroTier One/Info.plist index 80b0c4667..b4345ac4d 100644 --- a/ZeroTier One/Info.plist +++ b/ZeroTier One/Info.plist @@ -21,7 +21,7 @@ CFBundleSignature ???? CFBundleVersion - 4 + 5 LSMinimumSystemVersion $(MACOSX_DEPLOYMENT_TARGET) LSUIElement From 5da4fd2aef1f510d1bae4e90fa195f8e7962e36f Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Fri, 24 Jun 2016 15:24:29 -0700 Subject: [PATCH 33/91] Added "..." to menu items to signify it pops up in a window --- ZeroTier One/AppDelegate.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ZeroTier One/AppDelegate.swift b/ZeroTier One/AppDelegate.swift index 2ca97f3c1..e52b7be53 100644 --- a/ZeroTier One/AppDelegate.swift +++ b/ZeroTier One/AppDelegate.swift @@ -149,8 +149,8 @@ class AppDelegate: NSObject, NSApplicationDelegate { func buildMenu() { let menu = NSMenu() - menu.addItem(NSMenuItem(title: "Network Details", action: #selector(AppDelegate.showNetworks), keyEquivalent: "n")) - menu.addItem(NSMenuItem(title: "Join Network", action: #selector(AppDelegate.joinNetwork), keyEquivalent: "j")) + menu.addItem(NSMenuItem(title: "Network Details...", action: #selector(AppDelegate.showNetworks), keyEquivalent: "n")) + menu.addItem(NSMenuItem(title: "Join Network...", action: #selector(AppDelegate.joinNetwork), keyEquivalent: "j")) menu.addItem(NSMenuItem.separatorItem()) if networks.count > 0 { From 9c94d6527a1968468567232698818c479c04b44b Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Sun, 26 Jun 2016 16:11:30 -0700 Subject: [PATCH 34/91] add allowGlobal, allowDefault, allowManaged options to joining a network --- ZeroTier One/JoinNetworkViewController.swift | 10 ++++- ZeroTier One/JoinNetworkViewController.xib | 34 ++++++++++++--- ZeroTier One/ServiceCom.swift | 45 +++++++++++++------- 3 files changed, 67 insertions(+), 22 deletions(-) diff --git a/ZeroTier One/JoinNetworkViewController.swift b/ZeroTier One/JoinNetworkViewController.swift index 92e30cae1..cef30d4ee 100644 --- a/ZeroTier One/JoinNetworkViewController.swift +++ b/ZeroTier One/JoinNetworkViewController.swift @@ -30,6 +30,10 @@ class JoinNetworkViewController: NSViewController, NSComboBoxDelegate, NSComboBo @IBOutlet var network: NSComboBox! @IBOutlet var joinButton: NSButton! + @IBOutlet var allowManagedCheckBox: NSButton! + @IBOutlet var allowGlobalCheckBox: NSButton! + @IBOutlet var allowDefaultCheckBox:NSButton! + var values: [String] = [String]() override func viewDidLoad() { @@ -61,8 +65,10 @@ class JoinNetworkViewController: NSViewController, NSComboBoxDelegate, NSComboBo @IBAction func onJoinClicked(sender: AnyObject?) { let networkString = network.stringValue - ServiceCom.joinNetwork(networkString) - + ServiceCom.joinNetwork(networkString, + allowManaged: allowManagedCheckBox.state == NSOnState, + allowGlobal: allowGlobalCheckBox.state == NSOnState, + allowDefault: allowDefaultCheckBox.state == NSOnState) network.stringValue = "" diff --git a/ZeroTier One/JoinNetworkViewController.xib b/ZeroTier One/JoinNetworkViewController.xib index 625f1a3e1..187af0088 100644 --- a/ZeroTier One/JoinNetworkViewController.xib +++ b/ZeroTier One/JoinNetworkViewController.xib @@ -6,6 +6,9 @@ + + + @@ -14,11 +17,11 @@ - + - + @@ -26,7 +29,7 @@ - + + + + - + diff --git a/ZeroTier One/ServiceCom.swift b/ZeroTier One/ServiceCom.swift index 23c194c27..4c920ad22 100644 --- a/ZeroTier One/ServiceCom.swift +++ b/ZeroTier One/ServiceCom.swift @@ -119,29 +119,44 @@ class ServiceCom: NSObject { } - static func joinNetwork(network: String) { + static func joinNetwork(network: String, allowManaged: Bool = true, allowGlobal: Bool = false, allowDefault: Bool = false) { let urlString = baseURL + "/network/\(network)?auth=\(ServiceCom.getKey())" let url = NSURL(string: urlString) - if let u = url { - let request = NSMutableURLRequest(URL: u) - request.HTTPMethod = "POST" + var jsonDict = [String: AnyObject]() + jsonDict["allowManaged"] = NSNumber(bool: allowManaged) + jsonDict["allowGlobal"] = NSNumber(bool: allowGlobal) + jsonDict["allowDefault"] = NSNumber(bool: allowDefault) - let session = NSURLSession.sharedSession() - let task = session.dataTaskWithRequest(request) { (data, response, error) in - let httpResponse = response as! NSHTTPURLResponse - let status = httpResponse.statusCode + do { + let json = try NSJSONSerialization.dataWithJSONObject(jsonDict, options: NSJSONWritingOptions()) - if status == 200 { - NSLog("join ok") - } - else { - NSLog("join error: \(status)") + if let u = url { + let request = NSMutableURLRequest(URL: u) + request.HTTPMethod = "POST" + request.HTTPBody = json + request.setValue("application/json", forHTTPHeaderField: "Content-Type") + + let session = NSURLSession.sharedSession() + let task = session.dataTaskWithRequest(request) { (data, response, error) in + let httpResponse = response as! NSHTTPURLResponse + let status = httpResponse.statusCode + + if status == 200 { + NSLog("join ok") + } + else { + NSLog("join error: \(status)") + } } + + task.resume() } - - task.resume() } + catch { + NSLog("\(error)") + } + } static func leaveNetwork(network: String) { From 7e17a2072cbae143dde41a1a5bda646622cf3979 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Sun, 26 Jun 2016 16:41:52 -0700 Subject: [PATCH 35/91] add allowManaged, allowGlobal, allowDefault to Network object --- ZeroTier One/Network.swift | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/ZeroTier One/Network.swift b/ZeroTier One/Network.swift index 95b3d93f6..2e66ac8e1 100644 --- a/ZeroTier One/Network.swift +++ b/ZeroTier One/Network.swift @@ -56,6 +56,9 @@ struct PropertyKeys { static let portErrorKey = "portError" static let statusKey = "status" static let typeKey = "type" + static let allowManagedKey = "allowManaged" + static let allowGlobalKey = "allowGlobal" + static let allowDefaultKey = "allowDefault" } class Network: NSObject, NSCoding { @@ -72,6 +75,9 @@ class Network: NSObject, NSCoding { var portError: Int = 0 var status: NetworkStatus = .REQUESTING_CONFIGURATION var type: NetworkType = .PRIVATE + var allowManaged: Bool = true + var allowGlobal: Bool = false + var allowDefault: Bool = false var connected: Bool = false // NOT PERSISTED. Set to true if loaded via JSON init(jsonData: [String: AnyObject]) { @@ -123,6 +129,18 @@ class Network: NSObject, NSCoding { portError = p.integerValue } + if let a = jsonData["allowManaged"] as? NSNumber { + allowManaged = a.boolValue + } + + if let a = jsonData["allowGlobal"] as? NSNumber { + allowGlobal = a.boolValue + } + + if let a = jsonData["allowDefault"] as? NSNumber { + allowDefault = a.boolValue + } + if let statusStr = jsonData["status"] as? String { switch statusStr { case "REQUESTING_CONFIGURATION": @@ -209,6 +227,18 @@ class Network: NSObject, NSCoding { if aDecoder.containsValueForKey(PropertyKeys.typeKey) { self.type = NetworkType(rawValue: aDecoder.decodeIntegerForKey(PropertyKeys.typeKey))! } + + if aDecoder.containsValueForKey(PropertyKeys.allowManagedKey) { + self.allowManaged = aDecoder.decodeBoolForKey(PropertyKeys.allowManagedKey) + } + + if aDecoder.containsValueForKey(PropertyKeys.allowGlobalKey) { + self.allowGlobal = aDecoder.decodeBoolForKey(PropertyKeys.allowGlobalKey) + } + + if aDecoder.containsValueForKey(PropertyKeys.allowDefaultKey) { + self.allowDefault = aDecoder.decodeBoolForKey(PropertyKeys.allowDefaultKey) + } } func encodeWithCoder(aCoder: NSCoder) { @@ -225,5 +255,8 @@ class Network: NSObject, NSCoding { aCoder.encodeInteger(self.portError, forKey: PropertyKeys.portErrorKey) aCoder.encodeInteger(self.status.rawValue, forKey: PropertyKeys.statusKey) aCoder.encodeInteger(self.type.rawValue, forKey: PropertyKeys.typeKey) + aCoder.encodeBool(self.allowManaged, forKey: PropertyKeys.allowManagedKey) + aCoder.encodeBool(self.allowGlobal, forKey: PropertyKeys.allowGlobalKey) + aCoder.encodeBool(self.allowDefault, forKey: PropertyKeys.allowDefaultKey) } } From d57e343245558312847dc37ecdbebf6b65b6a66e Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Sun, 26 Jun 2016 17:21:45 -0700 Subject: [PATCH 36/91] add UI elements in network details for allowDefault, allowGlobal, allowManaged --- ZeroTier One/Network.swift | 10 + ZeroTier One/NetworkInfoCell.swift | 4 + ZeroTier One/ShowNetworksViewController.swift | 17 + ZeroTier One/ShowNetworksViewController.xib | 330 ++++++++++-------- 4 files changed, 222 insertions(+), 139 deletions(-) diff --git a/ZeroTier One/Network.swift b/ZeroTier One/Network.swift index 2e66ac8e1..b344b6c21 100644 --- a/ZeroTier One/Network.swift +++ b/ZeroTier One/Network.swift @@ -260,3 +260,13 @@ class Network: NSObject, NSCoding { aCoder.encodeBool(self.allowDefault, forKey: PropertyKeys.allowDefaultKey) } } + +func defaultRouteExists(netList: [Network]) -> Bool { + for net in netList { + if net.allowDefault { + return true + } + } + + return false +} diff --git a/ZeroTier One/NetworkInfoCell.swift b/ZeroTier One/NetworkInfoCell.swift index c31fcb912..427bdc84a 100644 --- a/ZeroTier One/NetworkInfoCell.swift +++ b/ZeroTier One/NetworkInfoCell.swift @@ -24,6 +24,10 @@ class NetworkInfoCell: NSTableCellView { @IBOutlet var deviceField: NSTextField! @IBOutlet var addressesField: NSTextField! + @IBOutlet var allowManaged: NSButton! + @IBOutlet var allowGlobal: NSButton! + @IBOutlet var allowDefault: NSButton! + @IBOutlet var connectedCheckbox: NSButton! @IBOutlet var deleteButton: NSButton! diff --git a/ZeroTier One/ShowNetworksViewController.swift b/ZeroTier One/ShowNetworksViewController.swift index e8b9e332e..250cc73a0 100644 --- a/ZeroTier One/ShowNetworksViewController.swift +++ b/ZeroTier One/ShowNetworksViewController.swift @@ -77,6 +77,23 @@ class ShowNetworksViewController: NSViewController, NSTableViewDelegate, NSTable cell.deviceField.stringValue = network.portDeviceName + if network.allowDefault { + cell.allowDefault.state = NSOnState + } + else { + cell.allowDefault.state = NSOffState + + if defaultRouteExists(networkList) { + cell.allowDefault.enabled = false + } + else { + cell.allowDefault.enabled = true + } + } + + cell.allowGlobal.state = (network.allowGlobal ? NSOnState : NSOffState) + cell.allowManaged.state = (network.allowManaged ? NSOnState : NSOffState) + cell.addressesField.stringValue = "" for nw in network.assignedAddresses { diff --git a/ZeroTier One/ShowNetworksViewController.xib b/ZeroTier One/ShowNetworksViewController.xib index 69120fc96..74582eb69 100644 --- a/ZeroTier One/ShowNetworksViewController.xib +++ b/ZeroTier One/ShowNetworksViewController.xib @@ -16,13 +16,13 @@ - + - + @@ -43,83 +43,75 @@ - + - - - - - - - - - - + + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + @@ -127,71 +119,15 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 1756e8b0f2bb5a322f38f5e0e079d27cfcfecd57 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Sun, 26 Jun 2016 17:33:46 -0700 Subject: [PATCH 37/91] Wire up checkboxes for allowGlobal, allowDefault, allowManaged to set the proper state in zerotier daemon checkboxes are disabled if the network is not currently connected. --- ZeroTier One/NetworkInfoCell.swift | 13 ++++++++----- ZeroTier One/ShowNetworksViewController.swift | 8 ++++++++ ZeroTier One/ShowNetworksViewController.xib | 9 +++++++++ 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/ZeroTier One/NetworkInfoCell.swift b/ZeroTier One/NetworkInfoCell.swift index 427bdc84a..12e719480 100644 --- a/ZeroTier One/NetworkInfoCell.swift +++ b/ZeroTier One/NetworkInfoCell.swift @@ -42,14 +42,10 @@ class NetworkInfoCell: NSTableCellView { } @IBAction func onConnectCheckStateChanged(sender: NSButton) { - NSLog("Checked State Changed") - if(sender.state == NSOnState) { - NSLog("Checked") joinNetwork(networkIdField.stringValue) } else { - NSLog("Unchecked") leaveNetwork(networkIdField.stringValue) } } @@ -60,10 +56,17 @@ class NetworkInfoCell: NSTableCellView { } func joinNetwork(nwid: String) { - ServiceCom.joinNetwork(nwid) + ServiceCom.joinNetwork(nwid, + allowManaged: allowManaged.state == NSOnState, + allowGlobal: allowGlobal.state == NSOnState, + allowDefault: !defaultRouteExists(parent.networkList) && (allowDefault.state == NSOnState)) } func leaveNetwork(nwid: String) { ServiceCom.leaveNetwork(nwid) } + + @IBAction func onAllowStatusChanged(sender: NSButton) { + joinNetwork(networkIdField.stringValue) + } } diff --git a/ZeroTier One/ShowNetworksViewController.swift b/ZeroTier One/ShowNetworksViewController.swift index 250cc73a0..e2bdd105c 100644 --- a/ZeroTier One/ShowNetworksViewController.swift +++ b/ZeroTier One/ShowNetworksViewController.swift @@ -103,9 +103,17 @@ class ShowNetworksViewController: NSViewController, NSTableViewDelegate, NSTable if network.connected { cell.connectedCheckbox.state = NSOnState + + cell.allowDefault.enabled = true + cell.allowGlobal.enabled = true + cell.allowManaged.enabled = true } else { cell.connectedCheckbox.state = NSOffState + + cell.allowDefault.enabled = false + cell.allowGlobal.enabled = false + cell.allowManaged.enabled = false } return cell diff --git a/ZeroTier One/ShowNetworksViewController.xib b/ZeroTier One/ShowNetworksViewController.xib index 74582eb69..1535c6685 100644 --- a/ZeroTier One/ShowNetworksViewController.xib +++ b/ZeroTier One/ShowNetworksViewController.xib @@ -168,6 +168,9 @@ + + + From 3fc11e2278a5e88ebc4ec3150fda050959e0c446 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Sun, 26 Jun 2016 18:18:59 -0700 Subject: [PATCH 38/91] misc bug fixes related to default routes --- ZeroTier One/AppDelegate.swift | 3 ++ ZeroTier One/JoinNetworkViewController.swift | 4 ++ ZeroTier One/JoinNetworkViewController.xib | 2 +- ZeroTier One/Network.swift | 2 +- ZeroTier One/NetworkMonitor.swift | 51 ++++++++++++------- ZeroTier One/ShowNetworksViewController.swift | 28 +++++----- 6 files changed, 57 insertions(+), 33 deletions(-) diff --git a/ZeroTier One/AppDelegate.swift b/ZeroTier One/AppDelegate.swift index e52b7be53..25c112298 100644 --- a/ZeroTier One/AppDelegate.swift +++ b/ZeroTier One/AppDelegate.swift @@ -164,6 +164,9 @@ class AppDelegate: NSObject, NSApplicationDelegate { networkName = "\(id) (\(net.name))" } + if net.allowDefault && net.connected { + networkName += " [default]" + } let item = NSMenuItem(title: networkName, action: #selector(AppDelegate.toggleNetwork(_:)), keyEquivalent: "") if net.connected { diff --git a/ZeroTier One/JoinNetworkViewController.swift b/ZeroTier One/JoinNetworkViewController.swift index cef30d4ee..ecab8f6c6 100644 --- a/ZeroTier One/JoinNetworkViewController.swift +++ b/ZeroTier One/JoinNetworkViewController.swift @@ -45,6 +45,10 @@ class JoinNetworkViewController: NSViewController, NSComboBoxDelegate, NSComboBo override func viewWillAppear() { super.viewWillAppear() + allowManagedCheckBox.state = NSOnState + allowGlobalCheckBox.state = NSOffState + allowDefaultCheckBox.state = NSOffState + let defaults = NSUserDefaults.standardUserDefaults() let vals = defaults.stringArrayForKey(joinedNetworksKey) diff --git a/ZeroTier One/JoinNetworkViewController.xib b/ZeroTier One/JoinNetworkViewController.xib index 187af0088..338d35eea 100644 --- a/ZeroTier One/JoinNetworkViewController.xib +++ b/ZeroTier One/JoinNetworkViewController.xib @@ -62,7 +62,7 @@ + + From ba0a45365c985a8a54b3d10a2942293ef5bb8148 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Thu, 30 Jun 2016 19:42:23 -0700 Subject: [PATCH 44/91] Work around for several strange corner cases in Automatic Reference Counting in Apple's runtime --- ZeroTier One/LaunchAtLoginController.m | 2 +- ZeroTier One/Network.swift | 13 ++++++++++--- ZeroTier One/NetworkMonitor.swift | 7 ++++++- ZeroTier One/ServiceCom.swift | 3 +-- ZeroTier One/ShowNetworksViewController.swift | 3 +-- 5 files changed, 19 insertions(+), 9 deletions(-) diff --git a/ZeroTier One/LaunchAtLoginController.m b/ZeroTier One/LaunchAtLoginController.m index f8aaafa00..53eb2b086 100644 --- a/ZeroTier One/LaunchAtLoginController.m +++ b/ZeroTier One/LaunchAtLoginController.m @@ -69,7 +69,7 @@ void sharedFileListDidChange(LSSharedFileListRef inList, void *context) if (wantedURL == NULL || fileList == NULL) return NULL; - NSArray *listSnapshot = (__bridge NSArray*)LSSharedFileListCopySnapshot(fileList, NULL); + NSArray *listSnapshot = (__bridge_transfer NSArray*)LSSharedFileListCopySnapshot(fileList, NULL); for (id itemObject in listSnapshot) { LSSharedFileListItemRef item = (__bridge LSSharedFileListItemRef) itemObject; UInt32 resolutionFlags = kLSSharedFileListNoUserInteraction | kLSSharedFileListDoNotMountVolumes; diff --git a/ZeroTier One/Network.swift b/ZeroTier One/Network.swift index b5f6b4235..451516864 100644 --- a/ZeroTier One/Network.swift +++ b/ZeroTier One/Network.swift @@ -177,7 +177,11 @@ class Network: NSObject, NSCoding { required init?(coder aDecoder: NSCoder) { if aDecoder.containsValueForKey(PropertyKeys.addressesKey) { - self.assignedAddresses = aDecoder.decodeObjectForKey(PropertyKeys.addressesKey) as! [String] + let addrs = aDecoder.decodeObjectForKey(PropertyKeys.addressesKey) as! [String] + + for a in addrs { + self.assignedAddresses.append(a) + } } if aDecoder.containsValueForKey(PropertyKeys.bridgeKey) { @@ -193,7 +197,8 @@ class Network: NSObject, NSCoding { } if aDecoder.containsValueForKey(PropertyKeys.macKey) { - self.mac = aDecoder.decodeObjectForKey(PropertyKeys.macKey) as! String + let mac = aDecoder.decodeObjectForKey(PropertyKeys.macKey) as! String + self.mac = mac } if aDecoder.containsValueForKey(PropertyKeys.mtuKey) { @@ -201,7 +206,9 @@ class Network: NSObject, NSCoding { } if aDecoder.containsValueForKey(PropertyKeys.nameKey) { - self.name = aDecoder.decodeObjectForKey(PropertyKeys.nameKey) as! String + let name = aDecoder.decodeObjectForKey(PropertyKeys.nameKey) as! String + + self.name = name } if aDecoder.containsValueForKey(PropertyKeys.netconfKey) { diff --git a/ZeroTier One/NetworkMonitor.swift b/ZeroTier One/NetworkMonitor.swift index d6ab55eed..c81ac47de 100644 --- a/ZeroTier One/NetworkMonitor.swift +++ b/ZeroTier One/NetworkMonitor.swift @@ -42,8 +42,13 @@ class NetworkMonitor: NSObject { let filePath = dataFile() if NSFileManager.defaultManager().fileExistsAtPath(filePath) { - self.savedNetworks = NSKeyedUnarchiver.unarchiveObjectWithFile(filePath) as! [Network] + let networks = NSKeyedUnarchiver.unarchiveObjectWithFile(filePath) as! [Network] + self.savedNetworks.removeAll() + + for n in networks { + self.savedNetworks.append(n) + } } ServiceCom.getNetworkList() { (networkList) -> Void in diff --git a/ZeroTier One/ServiceCom.swift b/ZeroTier One/ServiceCom.swift index 4c920ad22..2a98ec36d 100644 --- a/ZeroTier One/ServiceCom.swift +++ b/ZeroTier One/ServiceCom.swift @@ -161,9 +161,8 @@ class ServiceCom: NSObject { static func leaveNetwork(network: String) { let urlString = baseURL + "/network/\(network)?auth=\(ServiceCom.getKey())" - let url = NSURL(string: urlString) - if let u = url { + if let u = NSURL(string: urlString) { let request = NSMutableURLRequest(URL: u) request.HTTPMethod = "DELETE" diff --git a/ZeroTier One/ShowNetworksViewController.swift b/ZeroTier One/ShowNetworksViewController.swift index cb0170754..394a6bdc9 100644 --- a/ZeroTier One/ShowNetworksViewController.swift +++ b/ZeroTier One/ShowNetworksViewController.swift @@ -112,8 +112,7 @@ class ShowNetworksViewController: NSViewController, NSTableViewDelegate, NSTable cell.addressesField.stringValue = "" for nw in network.assignedAddresses { - cell.addressesField.stringValue += nw - cell.addressesField.stringValue += "\n" + cell.addressesField.stringValue += "\(nw)\n" } From d786c5265f5260528a7f461bcc0bbc3aa7c933c8 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Thu, 30 Jun 2016 19:46:59 -0700 Subject: [PATCH 45/91] bump version number --- ZeroTier One/Info.plist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ZeroTier One/Info.plist b/ZeroTier One/Info.plist index ce6eb7291..9c70406ff 100644 --- a/ZeroTier One/Info.plist +++ b/ZeroTier One/Info.plist @@ -21,7 +21,7 @@ CFBundleSignature ???? CFBundleVersion - 6 + 7 LSMinimumSystemVersion $(MACOSX_DEPLOYMENT_TARGET) LSUIElement From e7476927276cc957dce6710357326bb71ca3f17f Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Thu, 30 Jun 2016 20:07:22 -0700 Subject: [PATCH 46/91] remove background from network details view --- ZeroTier One/ShowNetworksViewController.xib | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/ZeroTier One/ShowNetworksViewController.xib b/ZeroTier One/ShowNetworksViewController.xib index 1535c6685..91ce57ccd 100644 --- a/ZeroTier One/ShowNetworksViewController.xib +++ b/ZeroTier One/ShowNetworksViewController.xib @@ -18,16 +18,16 @@ - - + + - + - - + + @@ -42,7 +42,7 @@ - + @@ -343,7 +343,7 @@ - + - + diff --git a/ZeroTier One/JoinNetworkViewController.xib b/ZeroTier One/JoinNetworkViewController.xib index 338d35eea..2ef43442c 100644 --- a/ZeroTier One/JoinNetworkViewController.xib +++ b/ZeroTier One/JoinNetworkViewController.xib @@ -1,5 +1,5 @@ - + diff --git a/ZeroTier One/ZeroTier One-Bridging-Header.h b/ZeroTier One/ZeroTier One-Bridging-Header.h deleted file mode 100644 index 60700eebb..000000000 --- a/ZeroTier One/ZeroTier One-Bridging-Header.h +++ /dev/null @@ -1,14 +0,0 @@ -// -// Use this file to import your target's public headers that you would like to expose to Swift. -// - -#import "AuthtokenCopy.h" -#import "NodeStatus.h" -#import "Network.h" -#import "ServiceCom.h" -#import "AboutViewController.h" -#import "PreferencesViewController.h" -#import "NetworkMonitor.h" -#import "NetworkInfoCell.h" -#import "ShowNetworksViewController.h" -#import "JoinNetworkViewController.h" diff --git a/ZeroTier One/main.m b/ZeroTier One/main.m new file mode 100644 index 000000000..399c483d4 --- /dev/null +++ b/ZeroTier One/main.m @@ -0,0 +1,13 @@ +// +// main.m +// testapp +// +// Created by Grant Limberg on 8/7/16. +// Copyright © 2016 ZeroTier, Inc. All rights reserved. +// + +#import + +int main(int argc, const char * argv[]) { + return NSApplicationMain(argc, argv); +} From c15c9775bd4e9699f76d1cd3a1074415890c6245 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Mon, 8 Aug 2016 17:45:52 -0700 Subject: [PATCH 70/91] explicitly link to WebKit framework --- ZeroTier One.xcodeproj/project.pbxproj | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ZeroTier One.xcodeproj/project.pbxproj b/ZeroTier One.xcodeproj/project.pbxproj index 49587b99f..f10aee87b 100644 --- a/ZeroTier One.xcodeproj/project.pbxproj +++ b/ZeroTier One.xcodeproj/project.pbxproj @@ -25,6 +25,7 @@ 93D167791D5815E600330C99 /* JoinNetworkViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D167781D5815E600330C99 /* JoinNetworkViewController.m */; }; 93D1677C1D58228A00330C99 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D1677B1D58228A00330C99 /* AppDelegate.m */; }; 93D1679B1D58300F00330C99 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D1679A1D58300F00330C99 /* main.m */; }; + 93D1679D1D595F0000330C99 /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 93D1679C1D595F0000330C99 /* WebKit.framework */; }; 93DAFB271D3F0BEE004D5417 /* about.html in Resources */ = {isa = PBXBuildFile; fileRef = 93DAFB261D3F0BEE004D5417 /* about.html */; }; 93DAFE4B1CFE53CA00547CC4 /* AuthtokenCopy.m in Sources */ = {isa = PBXBuildFile; fileRef = 93DAFE4A1CFE53CA00547CC4 /* AuthtokenCopy.m */; }; /* End PBXBuildFile section */ @@ -60,6 +61,7 @@ 93D1677A1D58228A00330C99 /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 93D1677B1D58228A00330C99 /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 93D1679A1D58300F00330C99 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 93D1679C1D595F0000330C99 /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = System/Library/Frameworks/WebKit.framework; sourceTree = SDKROOT; }; 93DAFB261D3F0BEE004D5417 /* about.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = about.html; sourceTree = ""; }; 93DAFE4A1CFE53CA00547CC4 /* AuthtokenCopy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AuthtokenCopy.m; sourceTree = ""; }; 93DAFE4C1CFE53DA00547CC4 /* AuthtokenCopy.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AuthtokenCopy.h; sourceTree = ""; }; @@ -70,6 +72,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 93D1679D1D595F0000330C99 /* WebKit.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -79,6 +82,7 @@ 93326BCF1CE7C816005CA2AC = { isa = PBXGroup; children = ( + 93D1679C1D595F0000330C99 /* WebKit.framework */, 93326BDA1CE7C816005CA2AC /* ZeroTier One */, 93326BD91CE7C816005CA2AC /* Products */, ); From b5cab8afaae9f5dc715827156f3a78e60aa9c3ec Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Mon, 8 Aug 2016 17:46:01 -0700 Subject: [PATCH 71/91] update build number --- ZeroTier One/Info.plist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ZeroTier One/Info.plist b/ZeroTier One/Info.plist index c741f8c2f..86e7f7fd5 100644 --- a/ZeroTier One/Info.plist +++ b/ZeroTier One/Info.plist @@ -21,7 +21,7 @@ CFBundleSignature ???? CFBundleVersion - 10 + 11 LSMinimumSystemVersion $(MACOSX_DEPLOYMENT_TARGET) LSUIElement From 13682bd91982108aff047f2a42d9133ec0c47ad0 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Mon, 8 Aug 2016 19:02:09 -0700 Subject: [PATCH 72/91] Change Deployment Target to 10.7. Apparently going below 10.10 was only an issue with Swift --- ZeroTier One.xcodeproj/project.pbxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ZeroTier One.xcodeproj/project.pbxproj b/ZeroTier One.xcodeproj/project.pbxproj index f10aee87b..8f09edda3 100644 --- a/ZeroTier One.xcodeproj/project.pbxproj +++ b/ZeroTier One.xcodeproj/project.pbxproj @@ -329,7 +329,7 @@ COMBINE_HIDPI_IMAGES = YES; INFOPLIST_FILE = "ZeroTier One/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 10.10; + MACOSX_DEPLOYMENT_TARGET = 10.7; PRODUCT_BUNDLE_IDENTIFIER = "com.zerotier.ZeroTier-One"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "ZeroTier One/ZeroTier One-Bridging-Header.h"; @@ -344,7 +344,7 @@ COMBINE_HIDPI_IMAGES = YES; INFOPLIST_FILE = "ZeroTier One/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 10.10; + MACOSX_DEPLOYMENT_TARGET = 10.7; PRODUCT_BUNDLE_IDENTIFIER = "com.zerotier.ZeroTier-One"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "ZeroTier One/ZeroTier One-Bridging-Header.h"; From 8ae07fb9c9b183ab93fff13692c974da7ac74241 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Mon, 8 Aug 2016 19:04:50 -0700 Subject: [PATCH 73/91] build number bump --- ZeroTier One/Info.plist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ZeroTier One/Info.plist b/ZeroTier One/Info.plist index 86e7f7fd5..f35a6dfcf 100644 --- a/ZeroTier One/Info.plist +++ b/ZeroTier One/Info.plist @@ -21,7 +21,7 @@ CFBundleSignature ???? CFBundleVersion - 11 + 12 LSMinimumSystemVersion $(MACOSX_DEPLOYMENT_TARGET) LSUIElement From 91b8d6f34f15372e1d7bbf7021c5b7958d80050f Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Tue, 9 Aug 2016 18:03:48 -0700 Subject: [PATCH 74/91] [pasteboard declareTypes] needs to be called before every time you set a string to the pasteboard. --- ZeroTier One/AppDelegate.h | 2 -- ZeroTier One/AppDelegate.m | 7 +++---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/ZeroTier One/AppDelegate.h b/ZeroTier One/AppDelegate.h index efd1b313b..b180261c4 100644 --- a/ZeroTier One/AppDelegate.h +++ b/ZeroTier One/AppDelegate.h @@ -31,8 +31,6 @@ @property (nonatomic) NodeStatus *status; -@property (nonatomic) NSPasteboard *pasteboard; - - (void)buildMenu; - (void)onNetworkListUpdated:(NSNotification*)note; diff --git a/ZeroTier One/AppDelegate.m b/ZeroTier One/AppDelegate.m index 13c702629..6acac165e 100644 --- a/ZeroTier One/AppDelegate.m +++ b/ZeroTier One/AppDelegate.m @@ -28,9 +28,6 @@ self.monitor = [[NetworkMonitor alloc] init]; self.networks = [NSMutableArray array]; self.status = nil; - self.pasteboard = [NSPasteboard generalPasteboard]; - - [self.pasteboard declareTypes:[NSArray arrayWithObject:NSPasteboardTypeString] owner:nil]; NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; NSDictionary *defaultsDict = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES] forKey:@"firstRun"]; @@ -266,7 +263,9 @@ - (void)copyNodeID { if(self.status != nil) { - [self.pasteboard setString:self.status.address forType:NSPasteboardTypeString]; + NSPasteboard *pasteboard = [NSPasteboard generalPasteboard]; + [pasteboard declareTypes:[NSArray arrayWithObject:NSPasteboardTypeString] owner:nil]; + [pasteboard setString:self.status.address forType:NSPasteboardTypeString]; } } From a7b635a980a0c74fbdd4ddd2b702dcf481670439 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Tue, 9 Aug 2016 19:03:09 -0700 Subject: [PATCH 75/91] add border around About web view. added padding to text div in the about page --- ZeroTier One/AboutViewController.m | 6 ++++++ ZeroTier One/AboutViewController.xib | 1 + ZeroTier One/about.html | 3 +++ 3 files changed, 10 insertions(+) diff --git a/ZeroTier One/AboutViewController.m b/ZeroTier One/AboutViewController.m index f6862d268..c0b6ec859 100644 --- a/ZeroTier One/AboutViewController.m +++ b/ZeroTier One/AboutViewController.m @@ -17,6 +17,12 @@ - (void)viewDidLoad { [super viewDidLoad]; + [self.webView setWantsLayer:YES]; + self.webView.layer.borderWidth = 1.0f; + [self.webView.layer setCornerRadius:1.0f]; + self.webView.layer.masksToBounds = YES; + [self.webView.layer setBorderColor:[[NSColor darkGrayColor] CGColor]]; + NSBundle *bundle = [NSBundle mainBundle]; NSURL *path = [bundle URLForResource:@"about" withExtension:@"html"]; if(path) { diff --git a/ZeroTier One/AboutViewController.xib b/ZeroTier One/AboutViewController.xib index ec0712c28..a0df0fcfc 100644 --- a/ZeroTier One/AboutViewController.xib +++ b/ZeroTier One/AboutViewController.xib @@ -1,6 +1,7 @@ + diff --git a/ZeroTier One/about.html b/ZeroTier One/about.html index 7754cf8de..4fa41d7b3 100644 --- a/ZeroTier One/about.html +++ b/ZeroTier One/about.html @@ -37,6 +37,9 @@ a:link { text-decoration: none; } + div.text { + padding: 5px; + } From 73fd934fecba8caca04215be86c6604068bbfd4c Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Tue, 9 Aug 2016 20:02:12 -0700 Subject: [PATCH 76/91] * Fixed menu icon size * Added icon for dark mode * Added detection for interface mode changing from light to dark and adjusting the icon accordingly --- ZeroTier One/AppDelegate.h | 2 ++ ZeroTier One/AppDelegate.m | 28 +++++++++++++++++- .../MenuBarIconMac.imageset/Contents.json | 1 - .../MenuBarIconMac.imageset/Menubar.png | Bin 14843 -> 17234 bytes .../Contents.json | 20 +++++++++++++ .../MenubarWhite.png} | Bin 15769 -> 17234 bytes 6 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 ZeroTier One/Assets.xcassets/MenuBarIconMacWhite.imageset/Contents.json rename ZeroTier One/Assets.xcassets/{MenuBarIconMac.imageset/MenuBar@2x.png => MenuBarIconMacWhite.imageset/MenubarWhite.png} (81%) diff --git a/ZeroTier One/AppDelegate.h b/ZeroTier One/AppDelegate.h index b180261c4..d77797279 100644 --- a/ZeroTier One/AppDelegate.h +++ b/ZeroTier One/AppDelegate.h @@ -46,4 +46,6 @@ - (void)menuWillOpen:(NSMenu*)menu; - (void)menuDidClose:(NSMenu*)menu; +- (void)darkModeChanged:(NSNotification*)note; + @end diff --git a/ZeroTier One/AppDelegate.m b/ZeroTier One/AppDelegate.m index 6acac165e..f60ccb6b6 100644 --- a/ZeroTier One/AppDelegate.m +++ b/ZeroTier One/AppDelegate.m @@ -44,7 +44,19 @@ name:StatusUpdateKey object:nil]; - self.statusItem.image = [NSImage imageNamed:@"MenuBarIconMac"]; + 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]; @@ -82,6 +94,9 @@ - (void)applicationWillTerminate:(NSNotification *)aNotification { [[NSNotificationCenter defaultCenter] removeObserver:self]; + [[NSDistributedNotificationCenter defaultCenter] removeObserver:self + name:@"AppleInterfaceThemeChangedNotification" + object:nil]; } - (void)showNetworks { @@ -277,4 +292,15 @@ } +- (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"]; + } +} + @end diff --git a/ZeroTier One/Assets.xcassets/MenuBarIconMac.imageset/Contents.json b/ZeroTier One/Assets.xcassets/MenuBarIconMac.imageset/Contents.json index a680b58b8..84b363863 100644 --- a/ZeroTier One/Assets.xcassets/MenuBarIconMac.imageset/Contents.json +++ b/ZeroTier One/Assets.xcassets/MenuBarIconMac.imageset/Contents.json @@ -7,7 +7,6 @@ }, { "idiom" : "mac", - "filename" : "MenuBar@2x.png", "scale" : "2x" } ], diff --git a/ZeroTier One/Assets.xcassets/MenuBarIconMac.imageset/Menubar.png b/ZeroTier One/Assets.xcassets/MenuBarIconMac.imageset/Menubar.png index 84ca66261e68244259d44ef4c8d87ccc3b525303..ee0d7e3fdf8da51765ea0e17fe32201b7252661c 100644 GIT binary patch literal 17234 zcmeHPUx*t;7~k_>?P(DyR#Cx_qO{P>ZvH3RB=ve}y+c!v>uK+NX*au*yQN7sX7`%h zJ&7XdgD*Z+sV@qG3ZhsPQ7HbY=$k%@FH)q{)_)HoR*>`iHoKe6-0m2#eNbk&UuI{% z@B7WnZ@!u2W$)R!Ll115ymL|zgl)4k)p@9QAa1+?K0iI(+zZv#&dib}2sdp?Zw(die|PFs&3XvqTlL(w;+`E^*gF|oY=8CSvK0`_%9!viN_4R96yq+ z$+gZjSutkTyX5ftp#^RIxK`BT`zn*Az5)a-Vym%!tJ$`cemU;&D)5ZmQat9k*vHH9 zDI74iSeuJYn_UviCUT;dOs8V`Vj`I>rV5!ov6P(5NpeO?7R6**fxnC#b4$E32~VZ2 z-caVN2i@VIRgSONc1MwQ4oEznDzUV# z(XkD)9YbEVZm!zpcpL}vi@UB?#~-L|x!Qq>QeW*z$%HIL1l4NnuvRNH(y|YnfSI@} z3mwo}SnCjJo>=B;S0e{bkhZ-uY!aOn(>ARYGh+U}IdT=c=1-)v+HE=l>zYKGqy=MI z5SI*HgKad1n=`Tmv_uX^`bI<;S_WZGDhzh01o@{-cZq77-38NZRt8x%=dC(6J&jqF zO=M%cju>s->{+`@5*;+$@gX0nsy3-$Mv8Jll#2_=q5^*@S(NjNEC)yio)kw7vR_wi zbsUxvUz*3%jmFwoEJM#xj#^EbZCke5*2rwN0&7nghOQ7Tt*ff8i`h)BE@rbCS*+`g zq?pR+B%4lYnS3MZEOIn_6LrC)(8FB=ZYH%E@WHj{Fjzyc*Bf$O7wegNT_lA}N>uCFl$ati zQS-V6<7wUyAvBwYnlUWMr?o(yLAE(}&tdZTXs>5z8qOWkQM(qwqgjr7XVbrQ{5u6> zoFlHN8s0t?4HKB?C26F0I4wWY4Kp@x=Xeqf_ygh5WAs)?o0a|pD#t6D#4;OpPwkSa zWyp~6&gOu3G>{&kB;kGCG_Jx{Sn7#?w-Mu=tN+PH3^Q3vvr7jbx(_St~!_I&wevK<|o5IOfom&hRv8j*tU&LFn zx$5}mQ!*4OiJxw0&`gIeu|tDqI&>KMNZYV0DR(rgI}nAxH55mm!|=11RC3vDI$M(H zZaQMl=#%DR)qw9q3-9$5jO4`7hS0#JsK9{14ICs!;0LUe--BNGc?`eICI6@SrquVp zO|Im_a79;=mEz)qP=c_VTrRdgl$Fax3Bqo2x!C$pRxTGM2)oJUV(UX$xm=VW>?W6s ztq*18a#4b?n_MooK9rTqMG3-ga=F<0P*yG%B?!C8(i3<#JJi zu$x>iwmy`V%S8#oZgRQU`cPIb7bOV0$>n0}Ls_|8lpyRTmy4|rW#w{Fg0P!hF19|D zmCHp5!ftZ8*!oabE*B*TyUFEZ>qA+&T$CW}CYOt?4`t?TvL$>Gbcqz!Ml_TW|4J*zK$0k6Tvw3+#uAgtdj z2v0mE2$wgY{y`8{WkI-jOc0clf^fU}{L!-q1!2p(v(>4E{)F)AmZd)?Ha5WZDwJ!k z0;fZH9ez$sOvIpqmxQlKIR`3Sa)^O$3V?4Shn36Fc^lvm;1b|{z+ZrC0b%78==?|+ zQ)AdW;YYxGVN7%%a*m@huBr^U8E`kC1c;g~nB2mcF>3w-;5^`C0A!i)HQ+12cK`wi z9l+s?Al?N*-vcfJb^}mjl;Si1%Q67-^c}#hfRPeKj3l1{eg+iWh%k)}up#+#051s~ z_bB2SH{fZ7Fb5lu?Cq50ov0|zWTCOqDvxn;9+Smn zT_(rLD;QfRFJn5xYo=>t9Aao`Wo&3=U^%&-S$*<5W&x0>d5EF8m8qGPk>O-+7S+j1 zm_^V;k20%Fj^)yttj=XNS(im}@&-<|$vG^#lUH!+O}@%v3+8_Wia%o!oSekE8O%Sz zZiZ%K9owwQS2*-Hw{xj5G8#-?!)?K6u=y!>zCO#dIJ;kyBdqo68GSun978H@t+`_D z9TOf;p`+cpXE)0~sYYo2lT zL%q+>Z25B=XC>*0-r1hWQ!n}@!9;ymOz6gj6f=S>hR=)O#I_I zzU@Dn-YG@!M*nDCI7en5|BH4H?`3hVg?0-yUO$vQq2SM2zgah>VC2J%!yeamL$jtX_442P(TRY7V_zX-P44$rjF6*2UngFDd B=A{4t diff --git a/ZeroTier One/Assets.xcassets/MenuBarIconMacWhite.imageset/Contents.json b/ZeroTier One/Assets.xcassets/MenuBarIconMacWhite.imageset/Contents.json new file mode 100644 index 000000000..cd55e6eb4 --- /dev/null +++ b/ZeroTier One/Assets.xcassets/MenuBarIconMacWhite.imageset/Contents.json @@ -0,0 +1,20 @@ +{ + "images" : [ + { + "idiom" : "mac", + "filename" : "MenubarWhite.png", + "scale" : "1x" + }, + { + "idiom" : "mac", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + }, + "properties" : { + "template-rendering-intent" : "template" + } +} \ No newline at end of file diff --git a/ZeroTier One/Assets.xcassets/MenuBarIconMac.imageset/MenuBar@2x.png b/ZeroTier One/Assets.xcassets/MenuBarIconMacWhite.imageset/MenubarWhite.png similarity index 81% rename from ZeroTier One/Assets.xcassets/MenuBarIconMac.imageset/MenuBar@2x.png rename to ZeroTier One/Assets.xcassets/MenuBarIconMacWhite.imageset/MenubarWhite.png index ce46c151fb1b6b0a325a6e4a618f97121177740c..7049ae55000a88568d1da6cb72f76c9577cd835e 100644 GIT binary patch literal 17234 zcmeHOONb*y81C`4?&u;SK2X7sWktbGcjw)0COS@bodL6sv!l*sOubq*F=CfV&5!ihSMuKdb7i zzbbv{hnM%?x@-2zSxJ(1EiG17aKDV={PS>q|Fy=Axb18&u6vSn!6g(k(vweJAxSr0 z=B%#yYt`Ef%WbAi+ub0kZnF(}Z4#5WV&W~1d9-MM6t*T6GXE6GIQ;vb$%&eIMP zYt`k%g4-d9Tq-YHS|*(+=qW9yr;FJe5@}V-D{5BJbXm(7_+`~Z*plU0Je4|j%~+}I z3%i3fm)!LIwxKB7+uNz_Ov>%l5w7csnpVh-Y=2w(q#D z1kG!1xLf{QGD$n?w{Tv~cE6*R7jg#{m9E)Vw3Mn$2&z{3Va?{)NYCGU1S1J&Hnu}= z^=O+YE5viRIu_Y`gtYwK;|9^*bbZ&`bSI3zZ=N^{+v*Rbz13+11lyKE8l;InJ;Z5a z=iocF!Qu=}fm$YZN4n00Flyjn0v}`Y{S+!YH5q4<#fi{kTo-#lZz&? zWUH97i#aQoE!eqWlEdL6sVlCvMJoZRdt2XjEu`-kmZ59;G^yoHIY&%Q)-;lrH}pJ_ zbvr|9c9xK0rWkb;OE%Kc?Hvavli7&*(7D*?%-YCi)LK@PYedf=F&0VBl8mhBmTl*2 zHBBq_yBL#Zq^m{8Lp>dh)fuWSIC~C|`-ZiVq2UB)NZagqgl^5bWVAQ?hfe=Yp)tW0 zH%yD3K4ps*FtJO@kajSveqs#=V!E~Kt?2OX ziVlU*-B=o&F41W)kJF8AT5q@}f=zpiSs&KYVTTno{>&;AwVGNWHqI=)Hu22j7h!PH z($mS-?X;b@bUABosB%%u61ivcJY7Y|E&*ou=uRou*kQO1o7rDFbE9lm3kxI8Cv$&EOQnBt~?s{~A3N8(V>Y zA+4e*8oh?0aWfmb&JT^7+0a4coh`>Nr^DVDbu5a$YZw7P2k=@<%K2O_gMUL-%|G%IlPUpj5 zMyE3?lS_2M5W*J)U3`8RtDuV^gf9xZ`1~+dK^H>^Ulerl`C+VrE`|`kDCpwz!&n7f z3?Y0`(8cG6u?o5vLinPfi_Z^Z6?8F#@I^rvpC865=wb-ri-ImbKa5q-#Sp?51zmi8 z7^|R*A%rgqy7>GsRzVj-2wxO*@%dq_f-Z&-z9{J8^TSvLT?`?7QP9Qbhp`H}7()1> zpo`BBV-<8Ugz!Z{7oQ)-D(GSe;fsPUK0l0A(8UnK7X@8>ei*Buiy?$B3cC3GFjheq zLkM3Kbn*FNtb#6v5WdKzYj*IlD{0{i*KK^tTL1W&d3*+&uohRUl5}j3B;Eg@B>jF0 z_irR=OO>RPha|~(T#_z#A3bXkDd$fBl(R#?&j9^?`!V1%fB=&_m@u{Lp#K^;30x0c z3Q#Iu1L#!i0Bx@V7Xu@7jws+nlY9XD02Bf0Gmc{idfFZV&H;vU4GE!f5qH`i0wVjk z#MJgSbws1HihP4R%Q0P`gTQydi@-m%0Ph2zApXyxJajdq7F$^p&$K-Q&{e($h;QkgkUs)w3raG| zLGRFomrzR;G2DgcUx4?3dw?&1SAZ4Z%Me-Idx>J1scuLaImPaDWCwwF0NQ>6jsyFE z_#k3w|9Qmmz)2gV?-S2VJB`lcKp$JcxF3D~lsePK@d@nF<-hlzhvLQ3uk3#A=?`bt Q=`@!1?ytOb%b^GU22B~BI{*Lx delta 1135 zcmccA#yGQDu`|HWotI0Bi-CcG*VDr#2uN#yFb5lu?D@ANccP*=lhu-qR(Xt*^O!6q z>oPe`UcuNpc^T6gUNc=I;}AnjD`P_|1Cz=1%<7ZhF$-`3Ma@GDEkHutEUJ^2FpD4y z9c5OT9LuFOS)I#lGBc-_PDO5kUP@+)m0?nfd9s1Afo_snvZbztxk(C;Fih1oG&M0# zO-fEOPfRkIypKa>@&``y$+|3(D&}b^=0=9b=DMktDQ3DBi6*JK78WKz{i&uY28kv> z^Ab}hXR+v$X2WF`do^O5VxvF#Ba7tZXDot~lUO&a5Tj+XC7T6mkZrDGQ(z>{BS5{# zp;*V(GC78gYw}c%yv^-g%8X1V29wutn**uMPr38;SuO{3#Z8W|)~jdA_jGX#skrrK zhIK}$qd?pKV&PtiqZ&Ky95^FRMEe@ zG-_m`TK}W9s5^Hx40Z!`hj7#Oi2z) zRfE)+BB%;Y`b-CNIOWy{FtS^rT# zA)Zg}4Z|_hbf>;4(R?#n{VfE(hFSVCjXsl^wqnHhBCE zwyKvay7%JMX=lyFn?C6p+_x0t&w3|r`LL(FU@o8ODW83o#?HQPnG(Xq8m6sgQ~L1A zz+hRODYt|7p?;-_^|5bf=`h^B!q&Q4;!}N8t|j+@^~WM-DKY9SeW>Ak*s9^_M22aX zgMQaD)@1#f%JCrTjAi`<)(5_>f(?fS8NO*)*QvxY8SHc~5#Q3n&lF zH7+?=nt1x~Hm4yK+X< zhj|){S3i{avZ~`t?0q>c#=LoJC!LIY!1lpzX-k=RpV{)KlYM!oujXE*vPE>|xzGFm zgn4*&#XT)A{;A^mjB)l?D=8<5?-zboGo1MOGDrGY)&>R!V6o!q>gTe~DWM4f Date: Tue, 9 Aug 2016 20:04:33 -0700 Subject: [PATCH 77/91] bump build number --- ZeroTier One/Info.plist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ZeroTier One/Info.plist b/ZeroTier One/Info.plist index f35a6dfcf..2688e45e6 100644 --- a/ZeroTier One/Info.plist +++ b/ZeroTier One/Info.plist @@ -21,7 +21,7 @@ CFBundleSignature ???? CFBundleVersion - 12 + 13 LSMinimumSystemVersion $(MACOSX_DEPLOYMENT_TARGET) LSUIElement From bc48a097f8083f4ec16c17bcd208c3dd3719ee85 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Thu, 11 Aug 2016 17:41:54 -0700 Subject: [PATCH 78/91] add @2x versions of menu bar icon --- .../MenuBarIconMac.imageset/Contents.json | 1 + .../MenuBarIconMac.imageset/MenuBar@2x.png | Bin 0 -> 22973 bytes .../MenuBarIconMacWhite.imageset/Contents.json | 1 + .../MenubarWhite@2x.png | Bin 0 -> 22973 bytes ZeroTier One/Info.plist | 2 +- 5 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 ZeroTier One/Assets.xcassets/MenuBarIconMac.imageset/MenuBar@2x.png create mode 100644 ZeroTier One/Assets.xcassets/MenuBarIconMacWhite.imageset/MenubarWhite@2x.png diff --git a/ZeroTier One/Assets.xcassets/MenuBarIconMac.imageset/Contents.json b/ZeroTier One/Assets.xcassets/MenuBarIconMac.imageset/Contents.json index 84b363863..a680b58b8 100644 --- a/ZeroTier One/Assets.xcassets/MenuBarIconMac.imageset/Contents.json +++ b/ZeroTier One/Assets.xcassets/MenuBarIconMac.imageset/Contents.json @@ -7,6 +7,7 @@ }, { "idiom" : "mac", + "filename" : "MenuBar@2x.png", "scale" : "2x" } ], diff --git a/ZeroTier One/Assets.xcassets/MenuBarIconMac.imageset/MenuBar@2x.png b/ZeroTier One/Assets.xcassets/MenuBarIconMac.imageset/MenuBar@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..9fd3d3deecc91db05729d77f7189a3ea04fcf700 GIT binary patch literal 22973 zcmeHPeTXDS74OxkapU1W==okxS4~gtbWPvx z1hP_(d)-y_>iz2VuU=L4+%ogY6UX0uWbv(wilQ7@Uu&L(`^~6VzY?xD-2MIqxV^f+ zcE(qfYu<>upxpO`>lMX#+}+p=He2sCY_GdyI-W(AhTT5&R+RcJ!@g;sB|*j_TW)VT z``qK-$!1(@AJO`p^J?E(y%cu-obR#&9_s z;u=uKW-Xfuy98&Kvnx1YX0vr7v+4~^K(GcePjO!Vb(JjZRHPlYn>If>C~8S6biFncyxZ#LlBmt5B| zY^Pc*o20Ck&5EU#?IKYvr(IDCmaZ2`&Cc6qDSXID_dU=}&)&gI0O--ucRU;5Sz;M_ zv0@c+d7_qc`4SKbLt1&Os#+zzY&oTBQFqutF|a)Y-7#=sXEHmnKKLvS3}#vFVo^6? z*T}bvYNcJY)tXf$YN1W4W!tJ0bCm*pmMJiMhFWud$fxtMJcDct_nuSa!;`Jl&~d{( zq;C#50;R1Qqs52JyXiuxk#;$`)iuy0tmNopj^}sB5@icib0YDtzOdUccU4dTwQdRt!7JAi9O< zmQ%5Fg`8@Y?V4Jx6dkzeMAb{h3b9}hF|FeCv>UZfj#1arMHqXKRj=a>TAo81uvLw{ z$IW9R>yB--y+PLu8gAFzB3i$Jyz3dUYuwDcjy+HJ+@O(cURIVmr4aVN%&^oW2;s_B}C0$~EIIEEZ|xS0!CyemJY7iz7r_m2`>u;jEG_ju3HG(k14Hvr4)+Lc~=`mzW>U zD(T_~5mzN$VtzQQq>Cd&T$OZ*`QfaRE{+g!RnjHqhqFq$I6}l#Ntc)(&MN8R2oYB$ zU1EMXtE7t~L|m10iTUBIk}i%AaaGbK=7+ONx;R3_RY{kaAI>W2;s_B}C0$~EIIEUqu;SSXjtHxk>9UvHLF}b-xwHggG4L@dh+tLcbLCVHzh}binYNAlRG2r#RRJ zalR5kT!Q;U2ZIZ}pM`*b8kT6}a{#3f@a20pOFi0n&^wLu&@H+{7cko;4mxGvuSW`p z6_Zm?x(4*SpihE64|*`6xv@`_mxJGF(5FD@?((06G6wr7XbJR+g~iDVR|bJUbHAWVR5p;OqZ|& zP9+ZXU(gSPEqgDs0!ELcpwECJ*0Z2*0Tl|1lNE&BL!$*(902vBVaxPOnt;)>4Ej7M z;(Y@Y*F~bRI9VaFYiPjZ_OGA_jwyk=Lz1Ko82vATQWD;S4M{@dOnY$H0Q8y&>=SUu z{GEq<9zZD82U7vk$OWBos(uBEOgsmQKjI^jD`6OM9tK71e}dwoo+>oXGz`BJ5ROJD z55fIkQxW&yi2FCn)u8y>MnXvwaD1&W%=EQmA##}9=c%>vw~9S;$d zw~ubg*E9jYE^@~_!{hbE5cq^2%C)n=(**oN1cHlCw4KcwOJt{U(0gLvnH|8BK0SiG zIT7h#2Ffg+dO}GP@Xulh^Z3jcgA0rJA3=1T{D3#|^)vy04V>_lLm{KvT(7qS8otS) ze1CLHzNQHn&-*V&5cmn=x@5S0S>h)gzFY|XV%Bv*1Y-vc1HU81fe0b)CglNCh0N48_=7pdsPOB?Wd4DWNJ zA(F2wPF6_n8@GHKDl};PxHC=C1Y85hzkwpwk3knfg~H-w1z~q)#GL;UC=L2vDD#KO ztbp$TpFc+!zkvJOKxYbzlNDyVunw5d7eQ&zKY=pt9@vhO6fo}LxUi_-x1sz7&{V?W zWQA026P+*z@nwhx@I5Hw7KMw4wqe@fE^rC--%tM%)yDwpJvckS#CY&&r^Pm2zoKZE6Y$=m~; jS71pvhcAaQ(+Z#Y&8Hsx=I&8^s`X>Xn~&Uj*T?<`D3(Og literal 0 HcmV?d00001 diff --git a/ZeroTier One/Assets.xcassets/MenuBarIconMacWhite.imageset/Contents.json b/ZeroTier One/Assets.xcassets/MenuBarIconMacWhite.imageset/Contents.json index cd55e6eb4..617377601 100644 --- a/ZeroTier One/Assets.xcassets/MenuBarIconMacWhite.imageset/Contents.json +++ b/ZeroTier One/Assets.xcassets/MenuBarIconMacWhite.imageset/Contents.json @@ -7,6 +7,7 @@ }, { "idiom" : "mac", + "filename" : "MenubarWhite@2x.png", "scale" : "2x" } ], diff --git a/ZeroTier One/Assets.xcassets/MenuBarIconMacWhite.imageset/MenubarWhite@2x.png b/ZeroTier One/Assets.xcassets/MenuBarIconMacWhite.imageset/MenubarWhite@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..8c20e36feffbc132f27e1113db1c74e44f4200cc GIT binary patch literal 22973 zcmeHPe~2Sj9iMHh);*6_ZEcm>3#rNpot@0&cgUt(H+Sw@b6sxt?4DBWWM;BEUXqDP z_BMO?$DxPNKm5m|Sc?=96p>P?h*B!Hf`wA?7g!bi0f$hnl=`Esv}!#+-^sjW@{+vF zUZQkmzT7wO``-6`KkxfK^WK}8mf3q(kAL9c{JZ8QNjkW)Ts(pDR?@5Ah~Jyfe`F5D zn;XlgT9S0&?WA+k17EyRl5&sP>l^Kj(uZ<}Q(MqYr)({BY7O+3r2K82hHjj;+L5xg zY1bE{e}3%oXv8)bqjx1su~K8n+On6=Hm$X@$JdRsr;V%`y{#~x@8kebv)X#3Q>)fn zxz1wL#m&K{W+fVNyR=U)Mvu^dk&V)7WXWk-k>o;3Hq^KlNoN<-WLC>0ZjER$HKoK7 zikg+xcn<#(v4|(p!aS^e)2!r96pwksp|%*^YPTCXMcLWeS=fm$IL%FjXR}I7Q#4IR z54p8lZ|fbo-nyj=!f=XK%V^q-w(Zm-gsYdG?e=0cN(1%8n^&#T8>rs$xC4tyM{g+V zLQEMGR4R$XYPFG(t@hC~$i$o3$N^jHyA4Y@VYQs?rePgDW7XTYj7p-h<+PpFmNO>* zo_p*pOrw`bW4l>(2{sMIs#-OS*+QH;at>{~(!V$ZQy|aS;jE55CX77YFn1|*uTUQA zFSpdR^tRJncbsaWyUJGCtRqWHw5pN|$;jcm?7HdfwBDar_@Mq3@8elTy=@g}Mar>^ ztg7p3Iv3C8R85YhbFr8Yq}!9jC}H)RdRq^IGSHXDaZJ0i8wjP}a}c9c%B|E}ZM|+- zE5!olzF^yCE}Kawb=9=wjHYXHHk*janM67vS5j)yOvEddOgigMaxi=dy670&v=RW_ z-TJ0u0KT`da$4F->zQmqPQ|n&h_q62Su1Dca#Br|&15E_nn^a04{T_lJDWC6CcWzG zLvt}PSSp^;jI>^nwRp;qvuZ+i7SkLvg@`Ej*(Y_ zW$2s8l2dh>CC9W1aes8bxld%?G;$TES<~AEyQXhiN~6A+SNe^kN3_LY1(C6?1y9>kjl<6+QZY!Q) z@%XIto}Y)-XYa_LrO}_!EKxbEa1f3VaW$k%%n#=c>EZ|xS3|nQ{BYioE{+g!HKa?- z59bZ(;s_B}L%PKLaNdwEju3G*q)W^X=MCxN2oYC9y2Si&-jFVi5OFo6OUw`F4e8l8aW$k%%n#=c>EZ|xS3|nQ{BYioE{+g!HKa?-59bZ(;s_B}L%PKL zaNdwEju3G*q)W^X=MCxN2oYC9y2Si&-jFVi5OFo6OUw`F4e8l8 zaW$k%%n#=c>EZ|xS3|nQ{BYioE{+g!HKa?-59bZ(;s_B}g>=pLzeu&}_zZOi-LrxVNz!&qlKylLK6AZal5TK5_pv9B;cM7Wt`v`~ zcjlz(%@;1tUAly>`$(?XN4dJG?z@Dq@-ZBRUG&voPv-BC%y%gvf5IB-U!caK@0?Mz?0P}fZ{EBe=3{&F|sBucc$M-$?4XjyU_5k#F64=)srvE3hLj4CZU%- z=H&*`=~9JWgpyEV_d!+YAt)0E#v$k#=nGILkHEMdy65rt9DqR%Kp%!Oc>;zEWmq>d z2gNMC}|pMaSx0cUm{`n?Dxp(R15!_e%#wjd0%*mH~TNV=U5i z4#q6K^#~+T0FoE>URL+8V~2klkkEh zOm7Jfc|i0Aal<6gJ^KB3xJM?%21%GOzU%?fE0yWlvnbvQ5Mr3*Q7FA-3ngg6-v5807Ji-ZEbDuzrDZehk(S{!qh&z$@VY8kEWXF!*QVNeSPHn7?`ezeRZy zG;kh)JNiKBjrkmu$uD87L8+ba#wC0hzDJ=P^jhdl#+v1STcdX~y0t#*JDz?D`-i^z)Mn6gc1+ecDc8?;O{oEWqpU+0H zUNeLQ%%^69D7ow3#bTRbhQRFEfClmZ?}Mp^V0rc!o(k=M7`*@1Fs+W~j{WPUCFBundleSignature ???? CFBundleVersion - 13 + 14 LSMinimumSystemVersion $(MACOSX_DEPLOYMENT_TARGET) LSUIElement From 628ef14d80263db11f5d836a49aa0e21c3e74c70 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Thu, 11 Aug 2016 17:50:15 -0700 Subject: [PATCH 79/91] added more spacing at the bottom of each network info cell --- ZeroTier One/ShowNetworksViewController.xib | 128 ++++++++++---------- 1 file changed, 65 insertions(+), 63 deletions(-) diff --git a/ZeroTier One/ShowNetworksViewController.xib b/ZeroTier One/ShowNetworksViewController.xib index 65692307e..f26cb446c 100644 --- a/ZeroTier One/ShowNetworksViewController.xib +++ b/ZeroTier One/ShowNetworksViewController.xib @@ -1,6 +1,7 @@ + @@ -16,13 +17,13 @@ - + - + @@ -44,101 +45,91 @@ - + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - + + - - + + - - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + @@ -258,13 +249,23 @@ - + + @@ -289,9 +290,9 @@ + - @@ -301,18 +302,19 @@ - - + + + From 35d5ed9c2bdbb920684a4088bd5bb6a3df332121 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Fri, 19 Aug 2016 18:13:00 -0700 Subject: [PATCH 80/91] version bump i forgot to commit --- ZeroTier One/Info.plist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ZeroTier One/Info.plist b/ZeroTier One/Info.plist index 0e6971b0f..e04b5f282 100644 --- a/ZeroTier One/Info.plist +++ b/ZeroTier One/Info.plist @@ -21,7 +21,7 @@ CFBundleSignature ???? CFBundleVersion - 14 + 15 LSMinimumSystemVersion $(MACOSX_DEPLOYMENT_TARGET) LSUIElement From c2a01f6db46273d7013d22c59e4cc6500866fbd3 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Tue, 30 Aug 2016 20:28:52 -0700 Subject: [PATCH 81/91] Added a path for error handling when fetching data from the ZeroTier daemon Still need to wire up error messages to the user. --- ZeroTier One/AppDelegate.m | 15 +++- ZeroTier One/JoinNetworkViewController.m | 9 ++- ZeroTier One/NetworkInfoCell.m | 15 +++- ZeroTier One/NetworkMonitor.m | 20 ++++- ZeroTier One/ServiceCom.h | 8 +- ZeroTier One/ServiceCom.m | 95 ++++++++++++++++++------ 6 files changed, 129 insertions(+), 33 deletions(-) diff --git a/ZeroTier One/AppDelegate.m b/ZeroTier One/AppDelegate.m index f60ccb6b6..048fa3852 100644 --- a/ZeroTier One/AppDelegate.m +++ b/ZeroTier One/AppDelegate.m @@ -266,13 +266,24 @@ NSString *nwid = [NSString stringWithFormat:@"%10llx", network.nwid]; if(network.connected) { - [[ServiceCom sharedInstance] leaveNetwork:nwid]; + NSError *error = nil; + + [[ServiceCom sharedInstance] leaveNetwork:nwid error:&error]; + + if (error) { + // TODO: Display error message + + } } else { + NSError *error = nil; [[ServiceCom sharedInstance] joinNetwork:nwid allowManaged:network.allowManaged allowGlobal:network.allowGlobal - allowDefault:(network.allowDefault && ![Network defaultRouteExists:self.networks])]; + allowDefault:(network.allowDefault && ![Network defaultRouteExists:self.networks]) + error:&error]; + + // TODO: Display error message } } diff --git a/ZeroTier One/JoinNetworkViewController.m b/ZeroTier One/JoinNetworkViewController.m index 60e8e1cce..f91589d92 100644 --- a/ZeroTier One/JoinNetworkViewController.m +++ b/ZeroTier One/JoinNetworkViewController.m @@ -72,10 +72,17 @@ NSString * const JoinedNetworksKey = @"com.zerotier.one.joined-networks"; - (IBAction)onJoinClicked:(id)sender { NSString *networkId = self.network.stringValue; + NSError *error = nil; [[ServiceCom sharedInstance] joinNetwork:networkId allowManaged:(self.allowManagedCheckBox.state == NSOnState) allowGlobal:(self.allowGlobalCheckBox.state == NSOnState) - allowDefault:(self.allowDefaultCheckBox.state == NSOnState)]; + allowDefault:(self.allowDefaultCheckBox.state == NSOnState) + error:&error]; + + if(error) { + // TODO: display error message + return; + } self.network.stringValue = @""; diff --git a/ZeroTier One/NetworkInfoCell.m b/ZeroTier One/NetworkInfoCell.m index 606bb849f..6ffb9e719 100644 --- a/ZeroTier One/NetworkInfoCell.m +++ b/ZeroTier One/NetworkInfoCell.m @@ -42,15 +42,26 @@ - (void)joinNetwork:(NSString*)nwid { + NSError *error = nil; [[ServiceCom sharedInstance] joinNetwork:nwid allowManaged:(self.allowManaged.state == NSOnState) allowGlobal:(self.allowGlobal.state == NSOnState) - allowDefault:![Network defaultRouteExists:_parent.networkList] && (self.allowDefault.state == NSOnState)]; + allowDefault:![Network defaultRouteExists:_parent.networkList] && (self.allowDefault.state == NSOnState) + error:&error]; + + if (error) { + // TODO: Display error message + } } - (void)leaveNetwork:(NSString*)nwid { - [[ServiceCom sharedInstance] leaveNetwork:nwid]; + NSError *error = nil; + [[ServiceCom sharedInstance] leaveNetwork:nwid error:&error]; + + if (error) { + // TODO: Display error message + } } @end diff --git a/ZeroTier One/NetworkMonitor.m b/ZeroTier One/NetworkMonitor.m index 466d7db1c..594513255 100644 --- a/ZeroTier One/NetworkMonitor.m +++ b/ZeroTier One/NetworkMonitor.m @@ -74,13 +74,21 @@ NSString * const StatusUpdateKey = @"com.zerotier.one.status"; } } + NSError *error = nil; + [[ServiceCom sharedInstance] getNetworklist:^(NSArray *networkList) { _receivedNetworks = networkList; [[NSOperationQueue mainQueue] addOperationWithBlock:^{ [self internal_updateNetworkInfo]; - }]; - }]; + } ]; + } error:&error]; + + if(error) { + // TODO: Display error message + + [self stop]; + } [[ServiceCom sharedInstance] getNodeStatus:^(NodeStatus *status) { NSDictionary *userInfo = [NSDictionary dictionaryWithObject:status forKey:@"status"]; @@ -90,7 +98,13 @@ NSString * const StatusUpdateKey = @"com.zerotier.one.status"; object:nil userInfo:userInfo]; }]; - }]; + } error:&error]; + + if (error) { + // TODO: Display error message + + [self stop]; + } } - (void)deleteSavedNetwork:(NSString*)networkId diff --git a/ZeroTier One/ServiceCom.h b/ZeroTier One/ServiceCom.h index a06650019..b24dd4a2c 100644 --- a/ZeroTier One/ServiceCom.h +++ b/ZeroTier One/ServiceCom.h @@ -20,9 +20,9 @@ - (id)init; -- (void)getNetworklist:(void (^)(NSArray*))completionHandler; -- (void)getNodeStatus:(void (^)(NodeStatus*))completionHandler; -- (void)joinNetwork:(NSString*)networkId allowManaged:(BOOL)allowManaged allowGlobal:(BOOL)allowGlobal allowDefault:(BOOL)allowDefault; -- (void)leaveNetwork:(NSString*)networkId; +- (void)getNetworklist:(void (^)(NSArray*))completionHandler error:(NSError* __autoreleasing *)error; +- (void)getNodeStatus:(void (^)(NodeStatus*))completionHandler error:(NSError*__autoreleasing*)error; +- (void)joinNetwork:(NSString*)networkId allowManaged:(BOOL)allowManaged allowGlobal:(BOOL)allowGlobal allowDefault:(BOOL)allowDefault error:(NSError*__autoreleasing*)error; +- (void)leaveNetwork:(NSString*)networkId error:(NSError*__autoreleasing*)error; @end diff --git a/ZeroTier One/ServiceCom.m b/ZeroTier One/ServiceCom.m index 4e23d496d..46468e25b 100644 --- a/ZeroTier One/ServiceCom.m +++ b/ZeroTier One/ServiceCom.m @@ -39,7 +39,7 @@ return self; } -- (NSString*)key +- (NSString*)key:(NSError* __autoreleasing *)err { static NSString *k = nil; @@ -63,10 +63,20 @@ if (error) { NSLog(@"Error: %@", error); k = nil; + *err = error; return @""; } } else { + NSURL *sysAppSupportDir = [[NSFileManager defaultManager] URLForDirectory:NSApplicationSupportDirectory inDomain:NSSystemDomainMask appropriateForURL:nil create:false error:nil]; + + sysAppSupportDir = [[sysAppSupportDir URLByAppendingPathComponent:@"ZeroTier"] URLByAppendingPathComponent:@"One"]; + NSURL *sysAuthtokenURL = [sysAppSupportDir URLByAppendingPathComponent:@"authtoken.secret"]; + + if(![[NSFileManager defaultManager] fileExistsAtPath:[sysAuthtokenURL path]]) { + + } + [[NSFileManager defaultManager] createDirectoryAtURL:appSupportDir withIntermediateDirectories:YES attributes:nil @@ -74,6 +84,7 @@ if (error) { NSLog(@"Error: %@", error); + *err = error; k = nil; return @""; } @@ -83,6 +94,12 @@ if (status != errAuthorizationSuccess) { NSLog(@"Authorization Failed! %d", status); + + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: NSLocalizedString(@"Couldn't create AuthorizationRef", nil), + }; + *err = [NSError errorWithDomain:@"com.zerotier.one" code:-1 userInfo:userInfo]; + return @""; } @@ -104,6 +121,10 @@ if (status != errAuthorizationSuccess) { NSLog(@"Authorization Failed! %d", status); + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: NSLocalizedString(@"Couldn't copy authorization rights", nil), + }; + *err = [NSError errorWithDomain:@"com.zerotier.one" code:-1 userInfo:userInfo]; return @""; } @@ -120,21 +141,32 @@ if (error) { NSLog(@"Error writing token to disk: %@", error); + *err = error; } } } } if (k == nil) { + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: NSLocalizedString(@"Unknown error finding authorization key", nil), + }; + *err = [NSError errorWithDomain:@"com.zerotier.one" code:-1 userInfo:userInfo]; + return @""; } return k; } -- (void)getNetworklist:(void (^)(NSArray*))completionHandler +- (void)getNetworklist:(void (^)(NSArray *))completionHandler error:(NSError *__autoreleasing*)error { - NSString *urlString = [[baseURL stringByAppendingString:@"/network?auth="] stringByAppendingString:[self key]]; + NSString* key = [self key:error]; + if(*error) { + return; + } + + NSString *urlString = [[baseURL stringByAppendingString:@"/network?auth="] stringByAppendingString:key]; NSURL *url = [NSURL URLWithString:urlString]; NSURLSessionDataTask *task = @@ -171,31 +203,36 @@ [task resume]; } -- (void)getNodeStatus:(void (^)(NodeStatus*))completionHandler +- (void)getNodeStatus:(void (^)(NodeStatus*))completionHandler error:(NSError*__autoreleasing*)error { - NSString *urlString = [[baseURL stringByAppendingString:@"/status?auth="] stringByAppendingString:[self key]]; + NSString *key = [self key:error]; + if(*error) { + return; + } + + NSString *urlString = [[baseURL stringByAppendingString:@"/status?auth="] stringByAppendingString:key]; NSURL *url = [NSURL URLWithString:urlString]; NSURLSessionDataTask *task = [session dataTaskWithURL:url - completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { + completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable err) { - if(error) { - NSLog(@"Error: %@", error); + if(err) { + NSLog(@"Error: %@", err); return; } NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response; NSInteger status = [httpResponse statusCode]; - NSError *err; + NSError *err2; if(status == 200) { NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 - error:&err]; + error:&err2]; - if(err) { - NSLog(@"Error fetching node status: %@", err); + if(err2) { + NSLog(@"Error fetching node status: %@", err2); return; } @@ -207,10 +244,17 @@ [task resume]; } -- (void)joinNetwork:(NSString*)networkId allowManaged:(BOOL)allowManaged allowGlobal:(BOOL)allowGlobal allowDefault:(BOOL)allowDefault +- (void)joinNetwork:(NSString*)networkId allowManaged:(BOOL)allowManaged allowGlobal:(BOOL)allowGlobal allowDefault:(BOOL)allowDefault error:(NSError *__autoreleasing*)error { + NSString *key = [self key:error]; + if(*error) { + return; + } - NSString *urlString = [[[[baseURL stringByAppendingString:@"/network/"] stringByAppendingString:networkId] stringByAppendingString:@"?auth="] stringByAppendingString:[self key]]; + NSString *urlString = [[[[baseURL stringByAppendingString:@"/network/"] + stringByAppendingString:networkId] + stringByAppendingString:@"?auth="] + stringByAppendingString:key]; NSURL *url = [NSURL URLWithString:urlString]; @@ -227,6 +271,7 @@ if(err) { NSLog(@"Error creating json data: %@", err); + *error = err; return; } @@ -236,9 +281,9 @@ [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"]; NSURLSessionDataTask *task = - [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { + [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable err) { if(error) { - NSLog(@"Error posting join request: %@", error); + NSLog(@"Error posting join request: %@", err); } NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response; @@ -254,9 +299,17 @@ [task resume]; } -- (void)leaveNetwork:(NSString*)networkId +- (void)leaveNetwork:(NSString*)networkId error:(NSError*__autoreleasing*)error { - NSString *urlString = [[[[baseURL stringByAppendingString:@"/network/"] stringByAppendingString:networkId] stringByAppendingString:@"?auth="] stringByAppendingString:[self key]]; + NSString *key = [self key:error]; + if(*error) { + return; + } + + NSString *urlString = [[[[baseURL stringByAppendingString:@"/network/"] + stringByAppendingString:networkId] + stringByAppendingString:@"?auth="] + stringByAppendingString:key]; NSURL *url = [NSURL URLWithString:urlString]; @@ -264,9 +317,9 @@ request.HTTPMethod = @"DELETE"; NSURLSessionDataTask *task = - [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { - if(error) { - NSLog(@"Error posting delete request: %@", error); + [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable err) { + if(err) { + NSLog(@"Error posting delete request: %@", err); return; } From 94263ffcc1213b9e46097dc6c6431b27a2f1d1b3 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Thu, 1 Sep 2016 19:02:27 -0700 Subject: [PATCH 82/91] Added error handling messages --- ZeroTier One/AppDelegate.m | 13 ++- ZeroTier One/JoinNetworkViewController.m | 6 +- ZeroTier One/NetworkInfoCell.m | 12 ++- ZeroTier One/NetworkMonitor.m | 37 ++++++- ZeroTier One/ServiceCom.h | 1 + ZeroTier One/ServiceCom.m | 131 +++++++++++++++++++++-- 6 files changed, 183 insertions(+), 17 deletions(-) diff --git a/ZeroTier One/AppDelegate.m b/ZeroTier One/AppDelegate.m index 048fa3852..a00128385 100644 --- a/ZeroTier One/AppDelegate.m +++ b/ZeroTier One/AppDelegate.m @@ -271,8 +271,11 @@ [[ServiceCom sharedInstance] leaveNetwork:nwid error:&error]; if (error) { - // TODO: Display error message + NSAlert *alert = [NSAlert alertWithError:error]; + alert.alertStyle = NSCriticalAlertStyle; + [alert addButtonWithTitle:@"Ok"]; + [alert runModal]; } } else { @@ -283,7 +286,13 @@ allowDefault:(network.allowDefault && ![Network defaultRouteExists:self.networks]) error:&error]; - // TODO: Display error message + if (error) { + NSAlert *alert = [NSAlert alertWithError:error]; + alert.alertStyle = NSCriticalAlertStyle; + [alert addButtonWithTitle:@"Ok"]; + + [alert runModal]; + } } } diff --git a/ZeroTier One/JoinNetworkViewController.m b/ZeroTier One/JoinNetworkViewController.m index f91589d92..e75cc841c 100644 --- a/ZeroTier One/JoinNetworkViewController.m +++ b/ZeroTier One/JoinNetworkViewController.m @@ -80,7 +80,11 @@ NSString * const JoinedNetworksKey = @"com.zerotier.one.joined-networks"; error:&error]; if(error) { - // TODO: display error message + NSAlert *alert = [NSAlert alertWithError:error]; + alert.alertStyle = NSCriticalAlertStyle; + [alert addButtonWithTitle:@"Ok"]; + + [alert runModal]; return; } diff --git a/ZeroTier One/NetworkInfoCell.m b/ZeroTier One/NetworkInfoCell.m index 6ffb9e719..b37fa8a8f 100644 --- a/ZeroTier One/NetworkInfoCell.m +++ b/ZeroTier One/NetworkInfoCell.m @@ -50,7 +50,11 @@ error:&error]; if (error) { - // TODO: Display error message + NSAlert *alert = [NSAlert alertWithError:error]; + alert.alertStyle = NSCriticalAlertStyle; + [alert addButtonWithTitle:@"Ok"]; + + [alert runModal]; } } @@ -60,7 +64,11 @@ [[ServiceCom sharedInstance] leaveNetwork:nwid error:&error]; if (error) { - // TODO: Display error message + NSAlert *alert = [NSAlert alertWithError:error]; + alert.alertStyle = NSCriticalAlertStyle; + [alert addButtonWithTitle:@"Ok"]; + + [alert runModal]; } } diff --git a/ZeroTier One/NetworkMonitor.m b/ZeroTier One/NetworkMonitor.m index 594513255..e219e7629 100644 --- a/ZeroTier One/NetworkMonitor.m +++ b/ZeroTier One/NetworkMonitor.m @@ -11,6 +11,9 @@ #import "ServiceCom.h" #import "NodeStatus.h" +@import AppKit; + + NSString * const NetworkUpdateKey = @"com.zerotier.one.network-list"; NSString * const StatusUpdateKey = @"com.zerotier.one.status"; @@ -85,9 +88,22 @@ NSString * const StatusUpdateKey = @"com.zerotier.one.status"; } error:&error]; if(error) { - // TODO: Display error message - [self stop]; + + NSAlert *alert = [NSAlert alertWithError:error]; + alert.alertStyle = NSCriticalAlertStyle; + [alert addButtonWithTitle:@"Quit"]; + [alert addButtonWithTitle:@"Retry"]; + + NSModalResponse res = [alert runModal]; + + if(res == NSAlertFirstButtonReturn) { + [NSApp performSelector:@selector(terminate:) withObject:nil afterDelay:0.0]; + } + else if(res == NSAlertSecondButtonReturn) { + [self start]; + return; + } } [[ServiceCom sharedInstance] getNodeStatus:^(NodeStatus *status) { @@ -101,9 +117,22 @@ NSString * const StatusUpdateKey = @"com.zerotier.one.status"; } error:&error]; if (error) { - // TODO: Display error message - [self stop]; + + NSAlert *alert = [NSAlert alertWithError:error]; + alert.alertStyle = NSCriticalAlertStyle; + [alert addButtonWithTitle:@"Quit"]; + [alert addButtonWithTitle:@"Retry"]; + + NSModalResponse res = [alert runModal]; + + if(res == NSAlertFirstButtonReturn) { + [NSApp performSelector:@selector(terminate:) withObject:nil afterDelay:0.0]; + } + else if(res == NSAlertSecondButtonReturn) { + [self start]; + return; + } } } diff --git a/ZeroTier One/ServiceCom.h b/ZeroTier One/ServiceCom.h index b24dd4a2c..b7ef8ddac 100644 --- a/ZeroTier One/ServiceCom.h +++ b/ZeroTier One/ServiceCom.h @@ -15,6 +15,7 @@ { NSString *baseURL; NSURLSession *session; + BOOL _isQuitting; } + (ServiceCom*)sharedInstance; diff --git a/ZeroTier One/ServiceCom.m b/ZeroTier One/ServiceCom.m index 46468e25b..7c1b38be6 100644 --- a/ZeroTier One/ServiceCom.m +++ b/ZeroTier One/ServiceCom.m @@ -10,6 +10,7 @@ #import "AuthtokenCopy.h" #import "Network.h" #import "NodeStatus.h" +@import AppKit; @interface ServiceCom (Private) @@ -34,6 +35,7 @@ if(self) { baseURL = @"http://localhost:9993"; session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration ephemeralSessionConfiguration]]; + _isQuitting = NO; } return self; @@ -171,24 +173,62 @@ NSURL *url = [NSURL URLWithString:urlString]; NSURLSessionDataTask *task = [session dataTaskWithURL:url - completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { + completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable err) { - if (error) { - NSLog(@"Error: %@", error); + if (err) { + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + NSAlert *alert = [NSAlert alertWithError:err]; + alert.alertStyle = NSCriticalAlertStyle; + [alert addButtonWithTitle:@"Quit"]; + [alert addButtonWithTitle:@"Retry"]; + + NSModalResponse res; + if (!_isQuitting) { + res = [alert runModal]; + } + else { + return; + } + + if(res == NSAlertFirstButtonReturn) { + [NSApp performSelector:@selector(terminate:) withObject:nil afterDelay:0.0]; + _isQuitting = YES; + } + }]; return; } NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response; NSInteger status = [httpResponse statusCode]; - NSError *err; + NSError *err2; if (status == 200) { NSArray *json = [NSJSONSerialization JSONObjectWithData:data options:0 - error:&err]; + error:&err2]; if (err) { - NSLog(@"Error fetching network list: %@", err); + NSLog(@"Error fetching network list: %@", err2); + + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + NSAlert *alert = [NSAlert alertWithError:err2]; + alert.alertStyle = NSCriticalAlertStyle; + [alert addButtonWithTitle:@"Quit"]; + [alert addButtonWithTitle:@"Retry"]; + + NSModalResponse res; + if (!_isQuitting) { + res = [alert runModal]; + } + else { + return; + } + + if(res == NSAlertFirstButtonReturn) { + _isQuitting = YES; + [NSApp performSelector:@selector(terminate:) withObject:nil afterDelay:0.0]; + } + }]; return; } @@ -218,7 +258,25 @@ completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable err) { if(err) { - NSLog(@"Error: %@", err); + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + NSAlert *alert = [NSAlert alertWithError:err]; + alert.alertStyle = NSCriticalAlertStyle; + [alert addButtonWithTitle:@"Quit"]; + [alert addButtonWithTitle:@"Retry"]; + + NSModalResponse res; + if (!_isQuitting) { + res = [alert runModal]; + } + else { + return; + } + + if(res == NSAlertFirstButtonReturn) { + [NSApp performSelector:@selector(terminate:) withObject:nil afterDelay:0.0]; + _isQuitting = YES; + } + }]; return; } @@ -233,6 +291,25 @@ if(err2) { NSLog(@"Error fetching node status: %@", err2); + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + NSAlert *alert = [NSAlert alertWithError:err2]; + alert.alertStyle = NSCriticalAlertStyle; + [alert addButtonWithTitle:@"Quit"]; + [alert addButtonWithTitle:@"Retry"]; + + NSModalResponse res; + if (!_isQuitting) { + res = [alert runModal]; + } + else { + return; + } + + if(res == NSAlertFirstButtonReturn) { + [NSApp performSelector:@selector(terminate:) withObject:nil afterDelay:0.0]; + _isQuitting = YES; + } + }]; return; } @@ -282,8 +359,27 @@ NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable err) { - if(error) { + if(err) { NSLog(@"Error posting join request: %@", err); + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + NSAlert *alert = [NSAlert alertWithError:err]; + alert.alertStyle = NSCriticalAlertStyle; + [alert addButtonWithTitle:@"Quit"]; + [alert addButtonWithTitle:@"Retry"]; + + NSModalResponse res; + if (!_isQuitting) { + res = [alert runModal]; + } + else { + return; + } + + if(res == NSAlertFirstButtonReturn) { + [NSApp performSelector:@selector(terminate:) withObject:nil afterDelay:0.0]; + _isQuitting = YES; + } + }]; } NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response; @@ -320,6 +416,25 @@ [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable err) { if(err) { NSLog(@"Error posting delete request: %@", err); + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + NSAlert *alert = [NSAlert alertWithError:err]; + alert.alertStyle = NSCriticalAlertStyle; + [alert addButtonWithTitle:@"Quit"]; + [alert addButtonWithTitle:@"Retry"]; + + NSModalResponse res; + if (!_isQuitting) { + res = [alert runModal]; + } + else { + return; + } + + if(res == NSAlertFirstButtonReturn) { + [NSApp performSelector:@selector(terminate:) withObject:nil afterDelay:0.0]; + _isQuitting = YES; + } + }]; return; } From a813e29491faaa00c807c6784e04ba3e4e4e9eac Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Mon, 17 Oct 2016 12:26:48 -0700 Subject: [PATCH 83/91] Don't need AppDelegate to be a NSMenuDelegate --- ZeroTier One/AppDelegate.h | 2 +- ZeroTier One/AppDelegate.m | 8 -------- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/ZeroTier One/AppDelegate.h b/ZeroTier One/AppDelegate.h index d77797279..ef287bbb8 100644 --- a/ZeroTier One/AppDelegate.h +++ b/ZeroTier One/AppDelegate.h @@ -12,7 +12,7 @@ @class Network; @class NodeStatus; -@interface AppDelegate : NSObject +@interface AppDelegate : NSObject @property (weak, nonatomic) IBOutlet NSWindow *window; diff --git a/ZeroTier One/AppDelegate.m b/ZeroTier One/AppDelegate.m index a00128385..de28dcdbe 100644 --- a/ZeroTier One/AppDelegate.m +++ b/ZeroTier One/AppDelegate.m @@ -304,14 +304,6 @@ } } -- (void)menuWillOpen:(NSMenu*)menu { - -} - -- (void)menuDidClose:(NSMenu*)menu { - -} - - (void)darkModeChanged:(NSNotification*)note { NSString *osxMode = [[NSUserDefaults standardUserDefaults] stringForKey:@"AppleInterfaceStyle"]; From 4730111492f5381f968b5b3def23ad5d7d9531c3 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Mon, 17 Oct 2016 12:27:07 -0700 Subject: [PATCH 84/91] updated project settings for Xcode 8.0 --- ZeroTier One.xcodeproj/project.pbxproj | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ZeroTier One.xcodeproj/project.pbxproj b/ZeroTier One.xcodeproj/project.pbxproj index 8f09edda3..900daacbc 100644 --- a/ZeroTier One.xcodeproj/project.pbxproj +++ b/ZeroTier One.xcodeproj/project.pbxproj @@ -162,7 +162,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0730; - LastUpgradeCheck = 0730; + LastUpgradeCheck = 0800; ORGANIZATIONNAME = "ZeroTier, Inc"; TargetAttributes = { 93326BD71CE7C816005CA2AC = { @@ -254,8 +254,10 @@ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = "-"; @@ -299,8 +301,10 @@ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = "-"; From 46e9c1195cc232d99f53107c01a01f5ada3e8f8a Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Mon, 17 Oct 2016 12:29:28 -0700 Subject: [PATCH 85/91] more removal of NSMenuDelegate --- ZeroTier One/AppDelegate.h | 4 ++-- ZeroTier One/AppDelegate.m | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/ZeroTier One/AppDelegate.h b/ZeroTier One/AppDelegate.h index ef287bbb8..df66c17d8 100644 --- a/ZeroTier One/AppDelegate.h +++ b/ZeroTier One/AppDelegate.h @@ -43,8 +43,8 @@ - (void)quit; - (void)toggleNetwork:(NSMenuItem*)sender; - (void)copyNodeID; -- (void)menuWillOpen:(NSMenu*)menu; -- (void)menuDidClose:(NSMenu*)menu; + +- (void)closeJoinNetworkPopover; - (void)darkModeChanged:(NSNotification*)note; diff --git a/ZeroTier One/AppDelegate.m b/ZeroTier One/AppDelegate.m index de28dcdbe..9adbaa981 100644 --- a/ZeroTier One/AppDelegate.m +++ b/ZeroTier One/AppDelegate.m @@ -192,7 +192,6 @@ - (void)buildMenu { NSMenu *menu = [[NSMenu alloc] init]; - menu.delegate = self; if(self.status != nil) { NSString *nodeId = @"Node ID: "; @@ -315,4 +314,8 @@ } } +- (void)closeJoinNetworkPopover { + +} + @end From c44c3b053030d57afaf70790e82e69d14c736a83 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Mon, 17 Oct 2016 12:31:59 -0700 Subject: [PATCH 86/91] cleanup --- ZeroTier One/JoinNetworkViewController.m | 5 ----- 1 file changed, 5 deletions(-) diff --git a/ZeroTier One/JoinNetworkViewController.m b/ZeroTier One/JoinNetworkViewController.m index e75cc841c..d51f7de07 100644 --- a/ZeroTier One/JoinNetworkViewController.m +++ b/ZeroTier One/JoinNetworkViewController.m @@ -16,7 +16,6 @@ NSString * const JoinedNetworksKey = @"com.zerotier.one.joined-networks"; @interface NSString (extra) - (BOOL)contains:(NSString*)find; -//- (NSString*)trunc:(NSInteger)length trailing:(NSString*)trailing; @end @@ -26,10 +25,6 @@ NSString * const JoinedNetworksKey = @"com.zerotier.one.joined-networks"; NSRange range = [self rangeOfString:find]; return range.location != NSNotFound; } -// -//- (NSString*)trunc:(NSInteger)length trailing:(NSString*)trailing { -// -//} @end From ed58467d12914a9be53e47060cf148ab218b499a Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Mon, 17 Oct 2016 13:43:27 -0700 Subject: [PATCH 87/91] Clicking "Join" to join a new network now properly closes the popover --- ZeroTier One/AppDelegate.m | 5 +++-- ZeroTier One/JoinNetworkViewController.h | 3 +++ ZeroTier One/JoinNetworkViewController.m | 4 +++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/ZeroTier One/AppDelegate.m b/ZeroTier One/AppDelegate.m index 9adbaa981..86c83367c 100644 --- a/ZeroTier One/AppDelegate.m +++ b/ZeroTier One/AppDelegate.m @@ -59,8 +59,9 @@ object:nil]; [self buildMenu]; - - self.joinNetworkPopover.contentViewController = [[JoinNetworkViewController alloc] initWithNibName:@"JoinNetworkViewController" bundle:nil]; + 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]; diff --git a/ZeroTier One/JoinNetworkViewController.h b/ZeroTier One/JoinNetworkViewController.h index 894b4ec25..a928d046c 100644 --- a/ZeroTier One/JoinNetworkViewController.h +++ b/ZeroTier One/JoinNetworkViewController.h @@ -11,6 +11,8 @@ extern NSString * const JoinedNetworksKey; +@class AppDelegate; + @interface JoinNetworkViewController : NSViewController @property (nonatomic, weak) IBOutlet NSComboBox *network; @@ -18,6 +20,7 @@ extern NSString * const JoinedNetworksKey; @property (nonatomic, weak) IBOutlet NSButton *allowManagedCheckBox; @property (nonatomic, weak) IBOutlet NSButton *allowGlobalCheckBox; @property (nonatomic, weak) IBOutlet NSButton *allowDefaultCheckBox; +@property (nonatomic, weak) IBOutlet AppDelegate *appDelegate; @property (nonatomic) NSMutableArray *values; diff --git a/ZeroTier One/JoinNetworkViewController.m b/ZeroTier One/JoinNetworkViewController.m index d51f7de07..abe8f9597 100644 --- a/ZeroTier One/JoinNetworkViewController.m +++ b/ZeroTier One/JoinNetworkViewController.m @@ -8,7 +8,7 @@ #import "JoinNetworkViewController.h" #import "ServiceCom.h" - +#import "AppDelegate.h" NSString * const JoinedNetworksKey = @"com.zerotier.one.joined-networks"; @@ -92,6 +92,8 @@ NSString * const JoinedNetworksKey = @"com.zerotier.one.joined-networks"; [self.values removeLastObject]; } } + + [self.appDelegate closeJoinNetworkPopover]; } // NSComboBoxDelegate methods From f444da80bcafc7ded1395a0c5349e991fef2fcfd Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Mon, 17 Oct 2016 13:43:44 -0700 Subject: [PATCH 88/91] Add a 2 second delay before the About window shows up on the first run --- ZeroTier One/AppDelegate.m | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/ZeroTier One/AppDelegate.m b/ZeroTier One/AppDelegate.m index 86c83367c..b1d8cce25 100644 --- a/ZeroTier One/AppDelegate.m +++ b/ZeroTier One/AppDelegate.m @@ -84,9 +84,12 @@ [prefsView setLaunchAtLoginEnabled:YES]; - [[NSOperationQueue mainQueue] addOperationWithBlock:^{ - [self showAbout]; - }]; + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + sleep(2); + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + [self showAbout]; + }]; + }); } [self.monitor updateNetworkInfo]; @@ -316,7 +319,11 @@ } - (void)closeJoinNetworkPopover { - + if (self.transientMonitor) { + [NSEvent removeMonitor:self.transientMonitor]; + self.transientMonitor = nil; + } + [self.joinNetworkPopover close]; } @end From 7c12991f5788d112535b880c282ca6f3e17596b8 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Mon, 17 Oct 2016 13:48:25 -0700 Subject: [PATCH 89/91] wasn't setting the Connected checkbox to off if the network wasn't connected --- ZeroTier One/ShowNetworksViewController.m | 1 + 1 file changed, 1 insertion(+) diff --git a/ZeroTier One/ShowNetworksViewController.m b/ZeroTier One/ShowNetworksViewController.m index 2031af346..64e061972 100644 --- a/ZeroTier One/ShowNetworksViewController.m +++ b/ZeroTier One/ShowNetworksViewController.m @@ -88,6 +88,7 @@ cell.allowManaged.enabled = YES; } else { + cell.connectedCheckbox.state = NSOffState; cell.allowDefault.enabled = NO; cell.allowGlobal.enabled = NO; cell.allowManaged.enabled = NO; From 7025c7090b62d3404c2ea5eda0eb35fad1d2aafa Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Mon, 17 Oct 2016 14:43:24 -0700 Subject: [PATCH 90/91] set proper copyright/license in code files --- ZeroTier One/AboutViewController.h | 24 +++++++++++++++------ ZeroTier One/AboutViewController.m | 24 +++++++++++++++------ ZeroTier One/AppDelegate.h | 24 +++++++++++++++------ ZeroTier One/AppDelegate.m | 24 +++++++++++++++------ ZeroTier One/AuthtokenCopy.h | 24 +++++++++++++++------ ZeroTier One/AuthtokenCopy.m | 26 ++++++++++++++++------- ZeroTier One/JoinNetworkViewController.h | 24 +++++++++++++++------ ZeroTier One/JoinNetworkViewController.m | 24 +++++++++++++++------ ZeroTier One/Network.h | 24 +++++++++++++++------ ZeroTier One/Network.m | 24 +++++++++++++++------ ZeroTier One/NetworkInfoCell.h | 24 +++++++++++++++------ ZeroTier One/NetworkInfoCell.m | 24 +++++++++++++++------ ZeroTier One/NetworkMonitor.h | 24 +++++++++++++++------ ZeroTier One/NetworkMonitor.m | 26 ++++++++++++++++------- ZeroTier One/NodeStatus.h | 24 +++++++++++++++------ ZeroTier One/NodeStatus.m | 25 +++++++++++++++------- ZeroTier One/PreferencesViewController.h | 24 +++++++++++++++------ ZeroTier One/PreferencesViewController.m | 24 +++++++++++++++------ ZeroTier One/ServiceCom.h | 24 +++++++++++++++------ ZeroTier One/ServiceCom.m | 24 +++++++++++++++------ ZeroTier One/ShowNetworksViewController.h | 24 +++++++++++++++------ ZeroTier One/ShowNetworksViewController.m | 24 +++++++++++++++------ ZeroTier One/main.m | 24 +++++++++++++++------ 23 files changed, 393 insertions(+), 164 deletions(-) diff --git a/ZeroTier One/AboutViewController.h b/ZeroTier One/AboutViewController.h index 4260419e8..d3d5bc14e 100644 --- a/ZeroTier One/AboutViewController.h +++ b/ZeroTier One/AboutViewController.h @@ -1,10 +1,20 @@ -// -// AboutViewController.h -// ZeroTier One -// -// Created by Grant Limberg on 8/7/16. -// Copyright © 2016 ZeroTier, Inc. All rights reserved. -// +/* + * 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 . + */ #import #import diff --git a/ZeroTier One/AboutViewController.m b/ZeroTier One/AboutViewController.m index c0b6ec859..21dceae16 100644 --- a/ZeroTier One/AboutViewController.m +++ b/ZeroTier One/AboutViewController.m @@ -1,10 +1,20 @@ -// -// AboutViewController.m -// ZeroTier One -// -// Created by Grant Limberg on 8/7/16. -// Copyright © 2016 ZeroTier, Inc. All rights reserved. -// +/* + * 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 . + */ #import "AboutViewController.h" diff --git a/ZeroTier One/AppDelegate.h b/ZeroTier One/AppDelegate.h index df66c17d8..a00cfba9d 100644 --- a/ZeroTier One/AppDelegate.h +++ b/ZeroTier One/AppDelegate.h @@ -1,10 +1,20 @@ -// -// AppDelegate.h -// ZeroTier One -// -// Created by Grant Limberg on 8/7/16. -// Copyright © 2016 ZeroTier, Inc. All rights reserved. -// +/* + * 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 . + */ #import diff --git a/ZeroTier One/AppDelegate.m b/ZeroTier One/AppDelegate.m index b1d8cce25..5da6b3549 100644 --- a/ZeroTier One/AppDelegate.m +++ b/ZeroTier One/AppDelegate.m @@ -1,10 +1,20 @@ -// -// AppDelegate.m -// ZeroTier One -// -// Created by Grant Limberg on 8/7/16. -// Copyright © 2016 ZeroTier, Inc. All rights reserved. -// +/* + * 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 . + */ #import "AppDelegate.h" #import "NetworkMonitor.h" diff --git a/ZeroTier One/AuthtokenCopy.h b/ZeroTier One/AuthtokenCopy.h index 43c927e25..f0497cc6d 100644 --- a/ZeroTier One/AuthtokenCopy.h +++ b/ZeroTier One/AuthtokenCopy.h @@ -1,10 +1,20 @@ -// -// AuthtokenCopy.h -// ZeroTier One -// -// Created by Grant Limberg on 5/31/16. -// Copyright © 2016 ZeroTier, Inc. All rights reserved. -// +/* + * 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 . + */ #ifndef AuthtokenCopy_h #define AuthtokenCopy_h diff --git a/ZeroTier One/AuthtokenCopy.m b/ZeroTier One/AuthtokenCopy.m index 893d90cf9..a10350f7b 100644 --- a/ZeroTier One/AuthtokenCopy.m +++ b/ZeroTier One/AuthtokenCopy.m @@ -1,10 +1,20 @@ -// -// AuthtokenCopy.m -// ZeroTier One -// -// Created by Grant Limberg on 5/31/16. -// Copyright © 2016 ZeroTier, Inc. All rights reserved. -// +/* + * 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 . + */ #import @@ -84,4 +94,4 @@ NSString* getAdminAuthToken(AuthorizationRef authRef) { } return @""; -} \ No newline at end of file +} diff --git a/ZeroTier One/JoinNetworkViewController.h b/ZeroTier One/JoinNetworkViewController.h index a928d046c..428959fba 100644 --- a/ZeroTier One/JoinNetworkViewController.h +++ b/ZeroTier One/JoinNetworkViewController.h @@ -1,10 +1,20 @@ -// -// JoinNetworkViewController.h -// ZeroTier One -// -// Created by Grant Limberg on 8/7/16. -// Copyright © 2016 ZeroTier, Inc. All rights reserved. -// +/* + * 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 . + */ #import diff --git a/ZeroTier One/JoinNetworkViewController.m b/ZeroTier One/JoinNetworkViewController.m index abe8f9597..cae265416 100644 --- a/ZeroTier One/JoinNetworkViewController.m +++ b/ZeroTier One/JoinNetworkViewController.m @@ -1,10 +1,20 @@ -// -// JoinNetworkViewController.m -// ZeroTier One -// -// Created by Grant Limberg on 8/7/16. -// Copyright © 2016 ZeroTier, Inc. All rights reserved. -// +/* + * 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 . + */ #import "JoinNetworkViewController.h" #import "ServiceCom.h" diff --git a/ZeroTier One/Network.h b/ZeroTier One/Network.h index f8d0deb4b..0f3c49643 100644 --- a/ZeroTier One/Network.h +++ b/ZeroTier One/Network.h @@ -1,10 +1,20 @@ -// -// Network.h -// ZeroTier One -// -// Created by Grant Limberg on 8/4/16. -// Copyright © 2016 ZeroTier, Inc. All rights reserved. -// +/* + * 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 . + */ #import diff --git a/ZeroTier One/Network.m b/ZeroTier One/Network.m index 30b3f884d..16efc6e32 100644 --- a/ZeroTier One/Network.m +++ b/ZeroTier One/Network.m @@ -1,10 +1,20 @@ -// -// Network.m -// ZeroTier One -// -// Created by Grant Limberg on 8/4/16. -// Copyright © 2016 ZeroTier, Inc. All rights reserved. -// +/* + * 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 . + */ #import "Network.h" diff --git a/ZeroTier One/NetworkInfoCell.h b/ZeroTier One/NetworkInfoCell.h index b687bc4fc..be9345d70 100644 --- a/ZeroTier One/NetworkInfoCell.h +++ b/ZeroTier One/NetworkInfoCell.h @@ -1,10 +1,20 @@ -// -// NetworkInfoCell.h -// ZeroTier One -// -// Created by Grant Limberg on 8/7/16. -// Copyright © 2016 ZeroTier, Inc. All rights reserved. -// +/* + * 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 . + */ #import diff --git a/ZeroTier One/NetworkInfoCell.m b/ZeroTier One/NetworkInfoCell.m index b37fa8a8f..dc75cab39 100644 --- a/ZeroTier One/NetworkInfoCell.m +++ b/ZeroTier One/NetworkInfoCell.m @@ -1,10 +1,20 @@ -// -// NetworkInfoCell.m -// ZeroTier One -// -// Created by Grant Limberg on 8/7/16. -// Copyright © 2016 ZeroTier, Inc. All rights reserved. -// +/* + * 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 . + */ #import "NetworkInfoCell.h" #import "ServiceCom.h" diff --git a/ZeroTier One/NetworkMonitor.h b/ZeroTier One/NetworkMonitor.h index 0bd94814f..8cdec4ed6 100644 --- a/ZeroTier One/NetworkMonitor.h +++ b/ZeroTier One/NetworkMonitor.h @@ -1,10 +1,20 @@ -// -// NetworkMonitor.h -// ZeroTier One -// -// Created by Grant Limberg on 8/7/16. -// Copyright © 2016 ZeroTier, Inc. All rights reserved. -// +/* + * 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 . + */ #import diff --git a/ZeroTier One/NetworkMonitor.m b/ZeroTier One/NetworkMonitor.m index e219e7629..7ed22c4a9 100644 --- a/ZeroTier One/NetworkMonitor.m +++ b/ZeroTier One/NetworkMonitor.m @@ -1,10 +1,20 @@ -// -// NetworkMonitor.m -// ZeroTier One -// -// Created by Grant Limberg on 8/7/16. -// Copyright © 2016 ZeroTier, Inc. All rights reserved. -// +/* + * 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 . + */ #import "NetworkMonitor.h" #import "Network.h" @@ -240,4 +250,4 @@ NSString * const StatusUpdateKey = @"com.zerotier.one.status"; } } -@end \ No newline at end of file +@end diff --git a/ZeroTier One/NodeStatus.h b/ZeroTier One/NodeStatus.h index 074928b41..eab5bfe44 100644 --- a/ZeroTier One/NodeStatus.h +++ b/ZeroTier One/NodeStatus.h @@ -1,10 +1,20 @@ -// -// NodeStatus.h -// ZeroTier One -// -// Created by Grant Limberg on 8/4/16. -// Copyright © 2016 ZeroTier, Inc. All rights reserved. -// +/* + * 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 . + */ #import diff --git a/ZeroTier One/NodeStatus.m b/ZeroTier One/NodeStatus.m index 5a3684919..3bae3c7da 100644 --- a/ZeroTier One/NodeStatus.m +++ b/ZeroTier One/NodeStatus.m @@ -1,11 +1,20 @@ -// -// NodeStatus.m -// ZeroTier One -// -// Created by Grant Limberg on 8/4/16. -// Copyright © 2016 ZeroTier, Inc. All rights reserved. -// - +/* + * 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 . + */ #import "NodeStatus.h" @implementation NodeStatus diff --git a/ZeroTier One/PreferencesViewController.h b/ZeroTier One/PreferencesViewController.h index e9e4e4b13..56d0fdb82 100644 --- a/ZeroTier One/PreferencesViewController.h +++ b/ZeroTier One/PreferencesViewController.h @@ -1,10 +1,20 @@ -// -// PreferencesViewController.h -// ZeroTier One -// -// Created by Grant Limberg on 8/7/16. -// Copyright © 2016 ZeroTier, Inc. All rights reserved. -// +/* + * 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 . + */ #import diff --git a/ZeroTier One/PreferencesViewController.m b/ZeroTier One/PreferencesViewController.m index 2292367d4..13927fbaf 100644 --- a/ZeroTier One/PreferencesViewController.m +++ b/ZeroTier One/PreferencesViewController.m @@ -1,10 +1,20 @@ -// -// PreferencesViewController.m -// ZeroTier One -// -// Created by Grant Limberg on 8/7/16. -// Copyright © 2016 ZeroTier, Inc. All rights reserved. -// +/* + * 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 . + */ #import "PreferencesViewController.h" diff --git a/ZeroTier One/ServiceCom.h b/ZeroTier One/ServiceCom.h index b7ef8ddac..74ab2b350 100644 --- a/ZeroTier One/ServiceCom.h +++ b/ZeroTier One/ServiceCom.h @@ -1,10 +1,20 @@ -// -// ServiceCom.h -// ZeroTier One -// -// Created by Grant Limberg on 8/4/16. -// Copyright © 2016 ZeroTier, Inc. All rights reserved. -// +/* + * 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 . + */ #import diff --git a/ZeroTier One/ServiceCom.m b/ZeroTier One/ServiceCom.m index 7c1b38be6..4982d40e7 100644 --- a/ZeroTier One/ServiceCom.m +++ b/ZeroTier One/ServiceCom.m @@ -1,10 +1,20 @@ -// -// ServiceCom.m -// ZeroTier One -// -// Created by Grant Limberg on 8/4/16. -// Copyright © 2016 ZeroTier, Inc. All rights reserved. -// +/* + * 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 . + */ #import "ServiceCom.h" #import "AuthtokenCopy.h" diff --git a/ZeroTier One/ShowNetworksViewController.h b/ZeroTier One/ShowNetworksViewController.h index 5722d3f46..5c251674d 100644 --- a/ZeroTier One/ShowNetworksViewController.h +++ b/ZeroTier One/ShowNetworksViewController.h @@ -1,10 +1,20 @@ -// -// ShowNetworksViewController.h -// ZeroTier One -// -// Created by Grant Limberg on 8/7/16. -// Copyright © 2016 ZeroTier, Inc. All rights reserved. -// +/* + * 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 . + */ #import diff --git a/ZeroTier One/ShowNetworksViewController.m b/ZeroTier One/ShowNetworksViewController.m index 64e061972..e3a1e52c1 100644 --- a/ZeroTier One/ShowNetworksViewController.m +++ b/ZeroTier One/ShowNetworksViewController.m @@ -1,10 +1,20 @@ -// -// ShowNetworksViewController.m -// ZeroTier One -// -// Created by Grant Limberg on 8/7/16. -// Copyright © 2016 ZeroTier, Inc. All rights reserved. -// +/* + * 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 . + */ #import "ShowNetworksViewController.h" #import "NetworkMonitor.h" diff --git a/ZeroTier One/main.m b/ZeroTier One/main.m index 399c483d4..108a6bd18 100644 --- a/ZeroTier One/main.m +++ b/ZeroTier One/main.m @@ -1,10 +1,20 @@ -// -// main.m -// testapp -// -// Created by Grant Limberg on 8/7/16. -// Copyright © 2016 ZeroTier, Inc. All rights reserved. -// +/* + * 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 . + */ #import From 7b7ec133496db4fef867d9100285a02cd3f1aed6 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Mon, 17 Oct 2016 14:44:32 -0700 Subject: [PATCH 91/91] Prepare for merging into main ZeroTier repo --- .../ZeroTier One.xcodeproj}/project.pbxproj | 0 .../project.xcworkspace/contents.xcworkspacedata | 0 .../ZeroTier One}/AboutViewController.h | 0 .../ZeroTier One}/AboutViewController.m | 0 .../ZeroTier One}/AboutViewController.xib | 0 {ZeroTier One => macui/ZeroTier One}/AppDelegate.h | 0 {ZeroTier One => macui/ZeroTier One}/AppDelegate.m | 0 .../AppIcon.appiconset/Contents.json | 0 .../AppIcon.appiconset/ZeroTierIcon512x512.png | Bin .../ZeroTier One}/Assets.xcassets/Contents.json | 0 .../MenuBarIconMac.imageset/Contents.json | 0 .../MenuBarIconMac.imageset/MenuBar@2x.png | Bin .../MenuBarIconMac.imageset/Menubar.png | Bin .../MenuBarIconMacWhite.imageset/Contents.json | 0 .../MenuBarIconMacWhite.imageset/MenubarWhite.png | Bin .../MenubarWhite@2x.png | Bin .../ZeroTier One}/AuthtokenCopy.h | 0 .../ZeroTier One}/AuthtokenCopy.m | 0 .../ZeroTier One}/Base.lproj/MainMenu.xib | 0 {ZeroTier One => macui/ZeroTier One}/Info.plist | 0 .../ZeroTier One}/JoinNetworkViewController.h | 0 .../ZeroTier One}/JoinNetworkViewController.m | 0 .../ZeroTier One}/JoinNetworkViewController.xib | 0 {ZeroTier One => macui/ZeroTier One}/Network.h | 0 {ZeroTier One => macui/ZeroTier One}/Network.m | 0 .../ZeroTier One}/NetworkInfoCell.h | 0 .../ZeroTier One}/NetworkInfoCell.m | 0 .../ZeroTier One}/NetworkMonitor.h | 0 .../ZeroTier One}/NetworkMonitor.m | 0 {ZeroTier One => macui/ZeroTier One}/NodeStatus.h | 0 {ZeroTier One => macui/ZeroTier One}/NodeStatus.m | 0 .../ZeroTier One}/PreferencesViewController.h | 0 .../ZeroTier One}/PreferencesViewController.m | 0 .../ZeroTier One}/PreferencesViewController.xib | 0 {ZeroTier One => macui/ZeroTier One}/ServiceCom.h | 0 {ZeroTier One => macui/ZeroTier One}/ServiceCom.m | 0 .../ZeroTier One}/ShowNetworksViewController.h | 0 .../ZeroTier One}/ShowNetworksViewController.m | 0 .../ZeroTier One}/ShowNetworksViewController.xib | 0 .../ZeroTier One}/ZeroTierIcon.icns | Bin {ZeroTier One => macui/ZeroTier One}/about.html | 0 {ZeroTier One => macui/ZeroTier One}/main.m | 0 42 files changed, 0 insertions(+), 0 deletions(-) rename {ZeroTier One.xcodeproj => macui/ZeroTier One.xcodeproj}/project.pbxproj (100%) rename {ZeroTier One.xcodeproj => macui/ZeroTier One.xcodeproj}/project.xcworkspace/contents.xcworkspacedata (100%) rename {ZeroTier One => macui/ZeroTier One}/AboutViewController.h (100%) rename {ZeroTier One => macui/ZeroTier One}/AboutViewController.m (100%) rename {ZeroTier One => macui/ZeroTier One}/AboutViewController.xib (100%) rename {ZeroTier One => macui/ZeroTier One}/AppDelegate.h (100%) rename {ZeroTier One => macui/ZeroTier One}/AppDelegate.m (100%) rename {ZeroTier One => macui/ZeroTier One}/Assets.xcassets/AppIcon.appiconset/Contents.json (100%) rename {ZeroTier One => macui/ZeroTier One}/Assets.xcassets/AppIcon.appiconset/ZeroTierIcon512x512.png (100%) rename {ZeroTier One => macui/ZeroTier One}/Assets.xcassets/Contents.json (100%) rename {ZeroTier One => macui/ZeroTier One}/Assets.xcassets/MenuBarIconMac.imageset/Contents.json (100%) rename {ZeroTier One => macui/ZeroTier One}/Assets.xcassets/MenuBarIconMac.imageset/MenuBar@2x.png (100%) rename {ZeroTier One => macui/ZeroTier One}/Assets.xcassets/MenuBarIconMac.imageset/Menubar.png (100%) rename {ZeroTier One => macui/ZeroTier One}/Assets.xcassets/MenuBarIconMacWhite.imageset/Contents.json (100%) rename {ZeroTier One => macui/ZeroTier One}/Assets.xcassets/MenuBarIconMacWhite.imageset/MenubarWhite.png (100%) rename {ZeroTier One => macui/ZeroTier One}/Assets.xcassets/MenuBarIconMacWhite.imageset/MenubarWhite@2x.png (100%) rename {ZeroTier One => macui/ZeroTier One}/AuthtokenCopy.h (100%) rename {ZeroTier One => macui/ZeroTier One}/AuthtokenCopy.m (100%) rename {ZeroTier One => macui/ZeroTier One}/Base.lproj/MainMenu.xib (100%) rename {ZeroTier One => macui/ZeroTier One}/Info.plist (100%) rename {ZeroTier One => macui/ZeroTier One}/JoinNetworkViewController.h (100%) rename {ZeroTier One => macui/ZeroTier One}/JoinNetworkViewController.m (100%) rename {ZeroTier One => macui/ZeroTier One}/JoinNetworkViewController.xib (100%) rename {ZeroTier One => macui/ZeroTier One}/Network.h (100%) rename {ZeroTier One => macui/ZeroTier One}/Network.m (100%) rename {ZeroTier One => macui/ZeroTier One}/NetworkInfoCell.h (100%) rename {ZeroTier One => macui/ZeroTier One}/NetworkInfoCell.m (100%) rename {ZeroTier One => macui/ZeroTier One}/NetworkMonitor.h (100%) rename {ZeroTier One => macui/ZeroTier One}/NetworkMonitor.m (100%) rename {ZeroTier One => macui/ZeroTier One}/NodeStatus.h (100%) rename {ZeroTier One => macui/ZeroTier One}/NodeStatus.m (100%) rename {ZeroTier One => macui/ZeroTier One}/PreferencesViewController.h (100%) rename {ZeroTier One => macui/ZeroTier One}/PreferencesViewController.m (100%) rename {ZeroTier One => macui/ZeroTier One}/PreferencesViewController.xib (100%) rename {ZeroTier One => macui/ZeroTier One}/ServiceCom.h (100%) rename {ZeroTier One => macui/ZeroTier One}/ServiceCom.m (100%) rename {ZeroTier One => macui/ZeroTier One}/ShowNetworksViewController.h (100%) rename {ZeroTier One => macui/ZeroTier One}/ShowNetworksViewController.m (100%) rename {ZeroTier One => macui/ZeroTier One}/ShowNetworksViewController.xib (100%) rename {ZeroTier One => macui/ZeroTier One}/ZeroTierIcon.icns (100%) rename {ZeroTier One => macui/ZeroTier One}/about.html (100%) rename {ZeroTier One => macui/ZeroTier One}/main.m (100%) diff --git a/ZeroTier One.xcodeproj/project.pbxproj b/macui/ZeroTier One.xcodeproj/project.pbxproj similarity index 100% rename from ZeroTier One.xcodeproj/project.pbxproj rename to macui/ZeroTier One.xcodeproj/project.pbxproj diff --git a/ZeroTier One.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/macui/ZeroTier One.xcodeproj/project.xcworkspace/contents.xcworkspacedata similarity index 100% rename from ZeroTier One.xcodeproj/project.xcworkspace/contents.xcworkspacedata rename to macui/ZeroTier One.xcodeproj/project.xcworkspace/contents.xcworkspacedata diff --git a/ZeroTier One/AboutViewController.h b/macui/ZeroTier One/AboutViewController.h similarity index 100% rename from ZeroTier One/AboutViewController.h rename to macui/ZeroTier One/AboutViewController.h diff --git a/ZeroTier One/AboutViewController.m b/macui/ZeroTier One/AboutViewController.m similarity index 100% rename from ZeroTier One/AboutViewController.m rename to macui/ZeroTier One/AboutViewController.m diff --git a/ZeroTier One/AboutViewController.xib b/macui/ZeroTier One/AboutViewController.xib similarity index 100% rename from ZeroTier One/AboutViewController.xib rename to macui/ZeroTier One/AboutViewController.xib diff --git a/ZeroTier One/AppDelegate.h b/macui/ZeroTier One/AppDelegate.h similarity index 100% rename from ZeroTier One/AppDelegate.h rename to macui/ZeroTier One/AppDelegate.h diff --git a/ZeroTier One/AppDelegate.m b/macui/ZeroTier One/AppDelegate.m similarity index 100% rename from ZeroTier One/AppDelegate.m rename to macui/ZeroTier One/AppDelegate.m diff --git a/ZeroTier One/Assets.xcassets/AppIcon.appiconset/Contents.json b/macui/ZeroTier One/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from ZeroTier One/Assets.xcassets/AppIcon.appiconset/Contents.json rename to macui/ZeroTier One/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/ZeroTier One/Assets.xcassets/AppIcon.appiconset/ZeroTierIcon512x512.png b/macui/ZeroTier One/Assets.xcassets/AppIcon.appiconset/ZeroTierIcon512x512.png similarity index 100% rename from ZeroTier One/Assets.xcassets/AppIcon.appiconset/ZeroTierIcon512x512.png rename to macui/ZeroTier One/Assets.xcassets/AppIcon.appiconset/ZeroTierIcon512x512.png diff --git a/ZeroTier One/Assets.xcassets/Contents.json b/macui/ZeroTier One/Assets.xcassets/Contents.json similarity index 100% rename from ZeroTier One/Assets.xcassets/Contents.json rename to macui/ZeroTier One/Assets.xcassets/Contents.json diff --git a/ZeroTier One/Assets.xcassets/MenuBarIconMac.imageset/Contents.json b/macui/ZeroTier One/Assets.xcassets/MenuBarIconMac.imageset/Contents.json similarity index 100% rename from ZeroTier One/Assets.xcassets/MenuBarIconMac.imageset/Contents.json rename to macui/ZeroTier One/Assets.xcassets/MenuBarIconMac.imageset/Contents.json diff --git a/ZeroTier One/Assets.xcassets/MenuBarIconMac.imageset/MenuBar@2x.png b/macui/ZeroTier One/Assets.xcassets/MenuBarIconMac.imageset/MenuBar@2x.png similarity index 100% rename from ZeroTier One/Assets.xcassets/MenuBarIconMac.imageset/MenuBar@2x.png rename to macui/ZeroTier One/Assets.xcassets/MenuBarIconMac.imageset/MenuBar@2x.png diff --git a/ZeroTier One/Assets.xcassets/MenuBarIconMac.imageset/Menubar.png b/macui/ZeroTier One/Assets.xcassets/MenuBarIconMac.imageset/Menubar.png similarity index 100% rename from ZeroTier One/Assets.xcassets/MenuBarIconMac.imageset/Menubar.png rename to macui/ZeroTier One/Assets.xcassets/MenuBarIconMac.imageset/Menubar.png diff --git a/ZeroTier One/Assets.xcassets/MenuBarIconMacWhite.imageset/Contents.json b/macui/ZeroTier One/Assets.xcassets/MenuBarIconMacWhite.imageset/Contents.json similarity index 100% rename from ZeroTier One/Assets.xcassets/MenuBarIconMacWhite.imageset/Contents.json rename to macui/ZeroTier One/Assets.xcassets/MenuBarIconMacWhite.imageset/Contents.json diff --git a/ZeroTier One/Assets.xcassets/MenuBarIconMacWhite.imageset/MenubarWhite.png b/macui/ZeroTier One/Assets.xcassets/MenuBarIconMacWhite.imageset/MenubarWhite.png similarity index 100% rename from ZeroTier One/Assets.xcassets/MenuBarIconMacWhite.imageset/MenubarWhite.png rename to macui/ZeroTier One/Assets.xcassets/MenuBarIconMacWhite.imageset/MenubarWhite.png diff --git a/ZeroTier One/Assets.xcassets/MenuBarIconMacWhite.imageset/MenubarWhite@2x.png b/macui/ZeroTier One/Assets.xcassets/MenuBarIconMacWhite.imageset/MenubarWhite@2x.png similarity index 100% rename from ZeroTier One/Assets.xcassets/MenuBarIconMacWhite.imageset/MenubarWhite@2x.png rename to macui/ZeroTier One/Assets.xcassets/MenuBarIconMacWhite.imageset/MenubarWhite@2x.png diff --git a/ZeroTier One/AuthtokenCopy.h b/macui/ZeroTier One/AuthtokenCopy.h similarity index 100% rename from ZeroTier One/AuthtokenCopy.h rename to macui/ZeroTier One/AuthtokenCopy.h diff --git a/ZeroTier One/AuthtokenCopy.m b/macui/ZeroTier One/AuthtokenCopy.m similarity index 100% rename from ZeroTier One/AuthtokenCopy.m rename to macui/ZeroTier One/AuthtokenCopy.m diff --git a/ZeroTier One/Base.lproj/MainMenu.xib b/macui/ZeroTier One/Base.lproj/MainMenu.xib similarity index 100% rename from ZeroTier One/Base.lproj/MainMenu.xib rename to macui/ZeroTier One/Base.lproj/MainMenu.xib diff --git a/ZeroTier One/Info.plist b/macui/ZeroTier One/Info.plist similarity index 100% rename from ZeroTier One/Info.plist rename to macui/ZeroTier One/Info.plist diff --git a/ZeroTier One/JoinNetworkViewController.h b/macui/ZeroTier One/JoinNetworkViewController.h similarity index 100% rename from ZeroTier One/JoinNetworkViewController.h rename to macui/ZeroTier One/JoinNetworkViewController.h diff --git a/ZeroTier One/JoinNetworkViewController.m b/macui/ZeroTier One/JoinNetworkViewController.m similarity index 100% rename from ZeroTier One/JoinNetworkViewController.m rename to macui/ZeroTier One/JoinNetworkViewController.m diff --git a/ZeroTier One/JoinNetworkViewController.xib b/macui/ZeroTier One/JoinNetworkViewController.xib similarity index 100% rename from ZeroTier One/JoinNetworkViewController.xib rename to macui/ZeroTier One/JoinNetworkViewController.xib diff --git a/ZeroTier One/Network.h b/macui/ZeroTier One/Network.h similarity index 100% rename from ZeroTier One/Network.h rename to macui/ZeroTier One/Network.h diff --git a/ZeroTier One/Network.m b/macui/ZeroTier One/Network.m similarity index 100% rename from ZeroTier One/Network.m rename to macui/ZeroTier One/Network.m diff --git a/ZeroTier One/NetworkInfoCell.h b/macui/ZeroTier One/NetworkInfoCell.h similarity index 100% rename from ZeroTier One/NetworkInfoCell.h rename to macui/ZeroTier One/NetworkInfoCell.h diff --git a/ZeroTier One/NetworkInfoCell.m b/macui/ZeroTier One/NetworkInfoCell.m similarity index 100% rename from ZeroTier One/NetworkInfoCell.m rename to macui/ZeroTier One/NetworkInfoCell.m diff --git a/ZeroTier One/NetworkMonitor.h b/macui/ZeroTier One/NetworkMonitor.h similarity index 100% rename from ZeroTier One/NetworkMonitor.h rename to macui/ZeroTier One/NetworkMonitor.h diff --git a/ZeroTier One/NetworkMonitor.m b/macui/ZeroTier One/NetworkMonitor.m similarity index 100% rename from ZeroTier One/NetworkMonitor.m rename to macui/ZeroTier One/NetworkMonitor.m diff --git a/ZeroTier One/NodeStatus.h b/macui/ZeroTier One/NodeStatus.h similarity index 100% rename from ZeroTier One/NodeStatus.h rename to macui/ZeroTier One/NodeStatus.h diff --git a/ZeroTier One/NodeStatus.m b/macui/ZeroTier One/NodeStatus.m similarity index 100% rename from ZeroTier One/NodeStatus.m rename to macui/ZeroTier One/NodeStatus.m diff --git a/ZeroTier One/PreferencesViewController.h b/macui/ZeroTier One/PreferencesViewController.h similarity index 100% rename from ZeroTier One/PreferencesViewController.h rename to macui/ZeroTier One/PreferencesViewController.h diff --git a/ZeroTier One/PreferencesViewController.m b/macui/ZeroTier One/PreferencesViewController.m similarity index 100% rename from ZeroTier One/PreferencesViewController.m rename to macui/ZeroTier One/PreferencesViewController.m diff --git a/ZeroTier One/PreferencesViewController.xib b/macui/ZeroTier One/PreferencesViewController.xib similarity index 100% rename from ZeroTier One/PreferencesViewController.xib rename to macui/ZeroTier One/PreferencesViewController.xib diff --git a/ZeroTier One/ServiceCom.h b/macui/ZeroTier One/ServiceCom.h similarity index 100% rename from ZeroTier One/ServiceCom.h rename to macui/ZeroTier One/ServiceCom.h diff --git a/ZeroTier One/ServiceCom.m b/macui/ZeroTier One/ServiceCom.m similarity index 100% rename from ZeroTier One/ServiceCom.m rename to macui/ZeroTier One/ServiceCom.m diff --git a/ZeroTier One/ShowNetworksViewController.h b/macui/ZeroTier One/ShowNetworksViewController.h similarity index 100% rename from ZeroTier One/ShowNetworksViewController.h rename to macui/ZeroTier One/ShowNetworksViewController.h diff --git a/ZeroTier One/ShowNetworksViewController.m b/macui/ZeroTier One/ShowNetworksViewController.m similarity index 100% rename from ZeroTier One/ShowNetworksViewController.m rename to macui/ZeroTier One/ShowNetworksViewController.m diff --git a/ZeroTier One/ShowNetworksViewController.xib b/macui/ZeroTier One/ShowNetworksViewController.xib similarity index 100% rename from ZeroTier One/ShowNetworksViewController.xib rename to macui/ZeroTier One/ShowNetworksViewController.xib diff --git a/ZeroTier One/ZeroTierIcon.icns b/macui/ZeroTier One/ZeroTierIcon.icns similarity index 100% rename from ZeroTier One/ZeroTierIcon.icns rename to macui/ZeroTier One/ZeroTierIcon.icns diff --git a/ZeroTier One/about.html b/macui/ZeroTier One/about.html similarity index 100% rename from ZeroTier One/about.html rename to macui/ZeroTier One/about.html diff --git a/ZeroTier One/main.m b/macui/ZeroTier One/main.m similarity index 100% rename from ZeroTier One/main.m rename to macui/ZeroTier One/main.m