Browse Source

installer for nitoTV for for tvOS initial commit

Kevin Bradley 6 years ago
parent
commit
dfdaa66d07

+ 431 - 0
nitoTV4Installer.xcodeproj/project.pbxproj

@@ -0,0 +1,431 @@
+// !$*UTF8*$!
+{
+	archiveVersion = 1;
+	classes = {
+	};
+	objectVersion = 48;
+	objects = {
+
+/* Begin PBXBuildFile section */
+		32D633E42019876B00D091A0 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 32D633E32019876B00D091A0 /* AppDelegate.m */; };
+		32D633E62019876B00D091A0 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 32D633E52019876B00D091A0 /* Assets.xcassets */; };
+		32D633E92019876B00D091A0 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 32D633E72019876B00D091A0 /* MainMenu.xib */; };
+		32D633EC2019876B00D091A0 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 32D633EB2019876B00D091A0 /* main.m */; };
+		32D634022019889E00D091A0 /* EMKeychainItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 32D634012019889E00D091A0 /* EMKeychainItem.m */; };
+		32D63405201988AE00D091A0 /* libcrypto.0.9.8.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 32D63404201988AE00D091A0 /* libcrypto.0.9.8.tbd */; };
+		32D63407201988B500D091A0 /* libssl.0.9.8.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 32D63406201988B500D091A0 /* libssl.0.9.8.tbd */; };
+		32D63409201988CE00D091A0 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 32D63408201988CE00D091A0 /* Security.framework */; };
+		32D6340B201988D200D091A0 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 32D6340A201988D200D091A0 /* Cocoa.framework */; };
+		32D6340C201988DC00D091A0 /* libssh2.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 32D633FA2019889E00D091A0 /* libssh2.a */; };
+		32D6340F20198B1100D091A0 /* ObjSSH.m in Sources */ = {isa = PBXBuildFile; fileRef = 32D633FD2019889E00D091A0 /* ObjSSH.m */; };
+		32D6341020198B1700D091A0 /* ATVDeviceController.m in Sources */ = {isa = PBXBuildFile; fileRef = 32D633F52019889D00D091A0 /* ATVDeviceController.m */; };
+		32D6341220198C1800D091A0 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 32D6341120198C1700D091A0 /* libz.tbd */; };
+		32D634152019AE1C00D091A0 /* KBDownloadFile.m in Sources */ = {isa = PBXBuildFile; fileRef = 32D634142019AE1C00D091A0 /* KBDownloadFile.m */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXFileReference section */
+		32D633DF2019876B00D091A0 /* nitoTV4Installer.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = nitoTV4Installer.app; sourceTree = BUILT_PRODUCTS_DIR; };
+		32D633E22019876B00D091A0 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
+		32D633E32019876B00D091A0 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
+		32D633E52019876B00D091A0 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
+		32D633E82019876B00D091A0 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
+		32D633EA2019876B00D091A0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+		32D633EB2019876B00D091A0 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
+		32D633ED2019876B00D091A0 /* nitoTV4Installer.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = nitoTV4Installer.entitlements; sourceTree = "<group>"; };
+		32D633F42019889D00D091A0 /* ATVDeviceController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ATVDeviceController.h; sourceTree = "<group>"; };
+		32D633F52019889D00D091A0 /* ATVDeviceController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ATVDeviceController.m; sourceTree = "<group>"; };
+		32D633F82019889E00D091A0 /* libssh2-i386.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libssh2-i386.a"; sourceTree = "<group>"; };
+		32D633F92019889E00D091A0 /* libssh2-x86_64.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libssh2-x86_64.a"; sourceTree = "<group>"; };
+		32D633FA2019889E00D091A0 /* libssh2.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libssh2.a; sourceTree = "<group>"; };
+		32D633FB2019889E00D091A0 /* libssh2.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = libssh2.h; sourceTree = "<group>"; };
+		32D633FC2019889E00D091A0 /* ObjSSH.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ObjSSH.h; sourceTree = "<group>"; };
+		32D633FD2019889E00D091A0 /* ObjSSH.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ObjSSH.m; sourceTree = "<group>"; };
+		32D633FE2019889E00D091A0 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
+		32D633FF2019889E00D091A0 /* todo.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = todo.txt; sourceTree = "<group>"; };
+		32D634002019889E00D091A0 /* EMKeychainItem.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = EMKeychainItem.h; sourceTree = "<group>"; };
+		32D634012019889E00D091A0 /* EMKeychainItem.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = EMKeychainItem.m; sourceTree = "<group>"; };
+		32D63404201988AE00D091A0 /* libcrypto.0.9.8.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libcrypto.0.9.8.tbd; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/usr/lib/libcrypto.0.9.8.tbd; sourceTree = DEVELOPER_DIR; };
+		32D63406201988B500D091A0 /* libssl.0.9.8.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libssl.0.9.8.tbd; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/usr/lib/libssl.0.9.8.tbd; sourceTree = DEVELOPER_DIR; };
+		32D63408201988CE00D091A0 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/System/Library/Frameworks/Security.framework; sourceTree = DEVELOPER_DIR; };
+		32D6340A201988D200D091A0 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/System/Library/Frameworks/Cocoa.framework; sourceTree = DEVELOPER_DIR; };
+		32D6340D201989CA00D091A0 /* nitoTV4Installer.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = nitoTV4Installer.pch; sourceTree = "<group>"; };
+		32D6340E201989CA00D091A0 /* Defines.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Defines.h; sourceTree = "<group>"; };
+		32D6341120198C1700D091A0 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/usr/lib/libz.tbd; sourceTree = DEVELOPER_DIR; };
+		32D634132019AE1C00D091A0 /* KBDownloadFile.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBDownloadFile.h; sourceTree = "<group>"; };
+		32D634142019AE1C00D091A0 /* KBDownloadFile.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBDownloadFile.m; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+		32D633DC2019876B00D091A0 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				32D6341220198C1800D091A0 /* libz.tbd in Frameworks */,
+				32D6340B201988D200D091A0 /* Cocoa.framework in Frameworks */,
+				32D63409201988CE00D091A0 /* Security.framework in Frameworks */,
+				32D63407201988B500D091A0 /* libssl.0.9.8.tbd in Frameworks */,
+				32D63405201988AE00D091A0 /* libcrypto.0.9.8.tbd in Frameworks */,
+				32D6340C201988DC00D091A0 /* libssh2.a in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+		32D633D62019876B00D091A0 = {
+			isa = PBXGroup;
+			children = (
+				32D633E12019876B00D091A0 /* nitoTV4Installer */,
+				32D633E02019876B00D091A0 /* Products */,
+				32D63403201988AE00D091A0 /* Frameworks */,
+			);
+			sourceTree = "<group>";
+		};
+		32D633E02019876B00D091A0 /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				32D633DF2019876B00D091A0 /* nitoTV4Installer.app */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
+		32D633E12019876B00D091A0 /* nitoTV4Installer */ = {
+			isa = PBXGroup;
+			children = (
+				32D633F32019889D00D091A0 /* ATVDeviceController */,
+				32D634002019889E00D091A0 /* EMKeychainItem.h */,
+				32D634012019889E00D091A0 /* EMKeychainItem.m */,
+				32D634132019AE1C00D091A0 /* KBDownloadFile.h */,
+				32D634142019AE1C00D091A0 /* KBDownloadFile.m */,
+				32D633F62019889E00D091A0 /* ObjSSH */,
+				32D633E22019876B00D091A0 /* AppDelegate.h */,
+				32D633E32019876B00D091A0 /* AppDelegate.m */,
+				32D6340E201989CA00D091A0 /* Defines.h */,
+				32D6340D201989CA00D091A0 /* nitoTV4Installer.pch */,
+				32D633E52019876B00D091A0 /* Assets.xcassets */,
+				32D633E72019876B00D091A0 /* MainMenu.xib */,
+				32D633EA2019876B00D091A0 /* Info.plist */,
+				32D633EB2019876B00D091A0 /* main.m */,
+				32D633ED2019876B00D091A0 /* nitoTV4Installer.entitlements */,
+			);
+			path = nitoTV4Installer;
+			sourceTree = "<group>";
+		};
+		32D633F32019889D00D091A0 /* ATVDeviceController */ = {
+			isa = PBXGroup;
+			children = (
+				32D633F42019889D00D091A0 /* ATVDeviceController.h */,
+				32D633F52019889D00D091A0 /* ATVDeviceController.m */,
+			);
+			path = ATVDeviceController;
+			sourceTree = "<group>";
+		};
+		32D633F62019889E00D091A0 /* ObjSSH */ = {
+			isa = PBXGroup;
+			children = (
+				32D633F72019889E00D091A0 /* lib */,
+				32D633FC2019889E00D091A0 /* ObjSSH.h */,
+				32D633FD2019889E00D091A0 /* ObjSSH.m */,
+				32D633FE2019889E00D091A0 /* README.md */,
+				32D633FF2019889E00D091A0 /* todo.txt */,
+			);
+			path = ObjSSH;
+			sourceTree = "<group>";
+		};
+		32D633F72019889E00D091A0 /* lib */ = {
+			isa = PBXGroup;
+			children = (
+				32D633F82019889E00D091A0 /* libssh2-i386.a */,
+				32D633F92019889E00D091A0 /* libssh2-x86_64.a */,
+				32D633FA2019889E00D091A0 /* libssh2.a */,
+				32D633FB2019889E00D091A0 /* libssh2.h */,
+			);
+			path = lib;
+			sourceTree = "<group>";
+		};
+		32D63403201988AE00D091A0 /* Frameworks */ = {
+			isa = PBXGroup;
+			children = (
+				32D6341120198C1700D091A0 /* libz.tbd */,
+				32D6340A201988D200D091A0 /* Cocoa.framework */,
+				32D63408201988CE00D091A0 /* Security.framework */,
+				32D63406201988B500D091A0 /* libssl.0.9.8.tbd */,
+				32D63404201988AE00D091A0 /* libcrypto.0.9.8.tbd */,
+			);
+			name = Frameworks;
+			sourceTree = "<group>";
+		};
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+		32D633DE2019876B00D091A0 /* nitoTV4Installer */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 32D633F02019876B00D091A0 /* Build configuration list for PBXNativeTarget "nitoTV4Installer" */;
+			buildPhases = (
+				32D633DB2019876B00D091A0 /* Sources */,
+				32D633DC2019876B00D091A0 /* Frameworks */,
+				32D633DD2019876B00D091A0 /* Resources */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+			);
+			name = nitoTV4Installer;
+			productName = nitoTV4Installer;
+			productReference = 32D633DF2019876B00D091A0 /* nitoTV4Installer.app */;
+			productType = "com.apple.product-type.application";
+		};
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+		32D633D72019876B00D091A0 /* Project object */ = {
+			isa = PBXProject;
+			attributes = {
+				LastUpgradeCheck = 0900;
+				ORGANIZATIONNAME = "Kevin Bradley";
+				TargetAttributes = {
+					32D633DE2019876B00D091A0 = {
+						CreatedOnToolsVersion = 9.0;
+						ProvisioningStyle = Automatic;
+						SystemCapabilities = {
+							com.apple.Sandbox = {
+								enabled = 0;
+							};
+						};
+					};
+				};
+			};
+			buildConfigurationList = 32D633DA2019876B00D091A0 /* Build configuration list for PBXProject "nitoTV4Installer" */;
+			compatibilityVersion = "Xcode 8.0";
+			developmentRegion = en;
+			hasScannedForEncodings = 0;
+			knownRegions = (
+				en,
+				Base,
+			);
+			mainGroup = 32D633D62019876B00D091A0;
+			productRefGroup = 32D633E02019876B00D091A0 /* Products */;
+			projectDirPath = "";
+			projectRoot = "";
+			targets = (
+				32D633DE2019876B00D091A0 /* nitoTV4Installer */,
+			);
+		};
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+		32D633DD2019876B00D091A0 /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				32D633E62019876B00D091A0 /* Assets.xcassets in Resources */,
+				32D633E92019876B00D091A0 /* MainMenu.xib in Resources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+		32D633DB2019876B00D091A0 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				32D6340F20198B1100D091A0 /* ObjSSH.m in Sources */,
+				32D633EC2019876B00D091A0 /* main.m in Sources */,
+				32D633E42019876B00D091A0 /* AppDelegate.m in Sources */,
+				32D6341020198B1700D091A0 /* ATVDeviceController.m in Sources */,
+				32D634152019AE1C00D091A0 /* KBDownloadFile.m in Sources */,
+				32D634022019889E00D091A0 /* EMKeychainItem.m in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXVariantGroup section */
+		32D633E72019876B00D091A0 /* MainMenu.xib */ = {
+			isa = PBXVariantGroup;
+			children = (
+				32D633E82019876B00D091A0 /* Base */,
+			);
+			name = MainMenu.xib;
+			sourceTree = "<group>";
+		};
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+		32D633EE2019876B00D091A0 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_ANALYZER_NONNULL = YES;
+				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_COMMA = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INFINITE_RECURSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+				CLANG_WARN_STRICT_PROTOTYPES = YES;
+				CLANG_WARN_SUSPICIOUS_MOVE = YES;
+				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				CODE_SIGN_IDENTITY = "Mac Developer";
+				COPY_PHASE_STRIP = NO;
+				DEBUG_INFORMATION_FORMAT = dwarf;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				ENABLE_TESTABILITY = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu11;
+				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.12;
+				MTL_ENABLE_DEBUG_INFO = YES;
+				ONLY_ACTIVE_ARCH = YES;
+				SDKROOT = macosx;
+			};
+			name = Debug;
+		};
+		32D633EF2019876B00D091A0 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_ANALYZER_NONNULL = YES;
+				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_COMMA = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INFINITE_RECURSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+				CLANG_WARN_STRICT_PROTOTYPES = YES;
+				CLANG_WARN_SUSPICIOUS_MOVE = YES;
+				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				CODE_SIGN_IDENTITY = "Mac Developer";
+				COPY_PHASE_STRIP = NO;
+				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+				ENABLE_NS_ASSERTIONS = NO;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu11;
+				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.12;
+				MTL_ENABLE_DEBUG_INFO = NO;
+				SDKROOT = macosx;
+			};
+			name = Release;
+		};
+		32D633F12019876B00D091A0 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				CODE_SIGN_IDENTITY = "";
+				CODE_SIGN_STYLE = Automatic;
+				COMBINE_HIDPI_IMAGES = YES;
+				DEVELOPMENT_TEAM = "";
+				GCC_PRECOMPILE_PREFIX_HEADER = YES;
+				GCC_PREFIX_HEADER = nitoTV4Installer/nitoTV4Installer.pch;
+				INFOPLIST_FILE = nitoTV4Installer/Info.plist;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
+				LIBRARY_SEARCH_PATHS = (
+					"$(inherited)",
+					"$(PROJECT_DIR)/nitoTV4Installer/ObjSSH/lib",
+				);
+				MACOSX_DEPLOYMENT_TARGET = 10.11;
+				PRODUCT_BUNDLE_IDENTIFIER = com.nito.com.nitoTV4Installer;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				PROVISIONING_PROFILE_SPECIFIER = "";
+				SDKROOT = macosx10.11;
+			};
+			name = Debug;
+		};
+		32D633F22019876B00D091A0 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				CODE_SIGN_IDENTITY = "";
+				CODE_SIGN_STYLE = Automatic;
+				COMBINE_HIDPI_IMAGES = YES;
+				DEVELOPMENT_TEAM = "";
+				GCC_PRECOMPILE_PREFIX_HEADER = YES;
+				GCC_PREFIX_HEADER = nitoTV4Installer/nitoTV4Installer.pch;
+				INFOPLIST_FILE = nitoTV4Installer/Info.plist;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
+				LIBRARY_SEARCH_PATHS = (
+					"$(inherited)",
+					"$(PROJECT_DIR)/nitoTV4Installer/ObjSSH/lib",
+				);
+				MACOSX_DEPLOYMENT_TARGET = 10.11;
+				PRODUCT_BUNDLE_IDENTIFIER = com.nito.com.nitoTV4Installer;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				PROVISIONING_PROFILE_SPECIFIER = "";
+				SDKROOT = macosx10.11;
+			};
+			name = Release;
+		};
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+		32D633DA2019876B00D091A0 /* Build configuration list for PBXProject "nitoTV4Installer" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				32D633EE2019876B00D091A0 /* Debug */,
+				32D633EF2019876B00D091A0 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		32D633F02019876B00D091A0 /* Build configuration list for PBXNativeTarget "nitoTV4Installer" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				32D633F12019876B00D091A0 /* Debug */,
+				32D633F22019876B00D091A0 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+/* End XCConfigurationList section */
+	};
+	rootObject = 32D633D72019876B00D091A0 /* Project object */;
+}

+ 7 - 0
nitoTV4Installer.xcodeproj/project.xcworkspace/contents.xcworkspacedata

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+   version = "1.0">
+   <FileRef
+      location = "self:nitoTV4Installer.xcodeproj">
+   </FileRef>
+</Workspace>

+ 50 - 0
nitoTV4Installer/ATVDeviceController/ATVDeviceController.h

@@ -0,0 +1,50 @@
+/* ATVDeviceController */
+
+#import <Cocoa/Cocoa.h>
+#import <Foundation/Foundation.h>
+
+@protocol ATVDeviceControllerDelegate <NSObject>
+
+- (void)servicesFound:(NSArray *)services;
+
+@end
+
+@interface ATVDeviceController : NSObject <NSNetServiceBrowserDelegate>
+{
+	
+	IBOutlet id hostNameField;
+	IBOutlet id deviceList;
+	IBOutlet id apiVersionLabel;
+	IBOutlet id osVersionLabel;
+
+    IBOutlet NSWindow *myWindow;
+	IBOutlet NSPopUpButton *theComboBox;
+	IBOutlet NSArrayController *deviceController;
+
+    BOOL searching;
+    NSNetServiceBrowser * browser;
+    NSMutableArray * services;
+    NSMutableData * currentDownload;
+	NSArray *receivedFiles;
+    NSDictionary *deviceDictionary;
+	
+	
+}
+
+
+@property (nonatomic, strong) IBOutlet NSPopUpButton *theComboBox;
+@property (nonatomic, strong) NSArrayController *deviceController;
+
+@property (readwrite, weak) id <ATVDeviceControllerDelegate> delegate;
+
+
+- (NSDictionary *)deviceDictionary;
+
+- (NSDictionary *)stringDictionaryFromService:(NSNetService *)theService;
+
+
+- (IBAction)serviceClicked:(id)sender;
+- (IBAction)menuItemSelected:(id)sender;
+- (NSDictionary *)currentServiceDictionary;
+
+@end

+ 453 - 0
nitoTV4Installer/ATVDeviceController/ATVDeviceController.m

@@ -0,0 +1,453 @@
+
+
+#import "ATVDeviceController.h"
+#import <sys/types.h>
+#import <sys/socket.h>
+#import <netinet/in.h>
+#import <arpa/inet.h>
+#import "AppDelegate.h"
+
+#define APP_DELEGATE (AppDelegate *)[[NSApplication sharedApplication] delegate]
+
+@implementation ATVDeviceController
+
+
+@synthesize deviceController, theComboBox, delegate;
+
+- (NSString *)input: (NSString *)prompt defaultValue: (NSString *)defaultValue {
+    NSAlert *alert = [NSAlert alertWithMessageText: prompt
+                                     defaultButton:@"OK"
+                                   alternateButton:@"Cancel"
+                                       otherButton:nil
+                         informativeTextWithFormat:@""];
+    
+    NSTextField *input = [[NSTextField alloc] initWithFrame:NSMakeRect(0, 0, 200, 24)];
+    [input setStringValue:defaultValue];
+    [alert setAccessoryView:input];
+    NSInteger button = [alert runModal];
+    if (button == NSAlertDefaultReturn) {
+        [input validateEditing];
+        NSString *inputString = [input stringValue];
+
+        return inputString;
+    } else if (button == NSAlertAlternateReturn) {
+
+        return nil;
+    } else {
+        
+        NSAssert1(NO, @"Invalid input dialog button %d", button);
+        //[input autorelease];
+        return nil;
+    }
+}
+
+- (IBAction)menuItemSelected:(id)sender
+{
+    NSLog(@"sender: %@", sender);
+    if (sender == nil)
+    {
+        return;
+    }
+    [APP_DELEGATE setAtvAvailable:NO];
+    [[APP_DELEGATE progressInd] startAnimation:nil];
+    NSInteger index = [sender indexOfSelectedItem];
+    
+    NSInteger itemCount = [sender numberOfItems];
+    
+    if (index == 0)
+    {
+        [APP_DELEGATE setAtvAvailable:FALSE];
+        [APP_DELEGATE resetServerSettings];
+        
+        return;
+    }
+    
+    if (index == itemCount-1)
+    {
+        //[sender setEditable:TRUE];
+        
+        NSString *output = [self input:@"Enter an Apple TV name or IP address" defaultValue:@""];
+        [APP_DELEGATE setAtvAvailable:TRUE];
+        [DEFAULTS setObject:output forKey:@"appleTVHost"];
+        return;
+        
+    } else {
+        
+        //	[sender setEditable:FALSE];
+    }
+    
+    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND
+                                             , 0), ^{
+        NSNetService * clickedService = [services objectAtIndex:index];
+        NSDictionary *finalDict = [self stringDictionaryFromService:clickedService];
+        NSLog(@"finalDict: %@", finalDict);
+        if ([[finalDict allKeys] count] > 0)
+        {
+            NSString *model = [finalDict objectForKey:@"model"];
+            if ([model isEqualToString:@"AppleTV3,1"])
+            {
+                dispatch_async(dispatch_get_main_queue(), ^{
+                    [sender selectItemAtIndex:0];
+                    [APP_DELEGATE setAtvAvailable:FALSE];
+                    [APP_DELEGATE showATVWarning];
+                    [[APP_DELEGATE progressInd] stopAnimation:nil];
+                });
+                
+            } else {
+                NSString *ip;
+                //int port;
+                struct sockaddr_in *addr;
+                
+                addr = (struct sockaddr_in *) [[[clickedService addresses] objectAtIndex:0]
+                                               bytes];
+                ip = [NSString stringWithUTF8String:(char *) inet_ntoa(addr->sin_addr)];
+                
+                NSString *fullIP = [NSString stringWithFormat:@"%@:%i", ip, 22];
+                
+                //[APP_DELEGATE setAtvAvailable:TRUE];
+                [DEFAULTS setObject:fullIP forKey:ATV_HOST];
+                
+                [APP_DELEGATE setStatusText:@"Checking device for jailbreak, please wait..."];
+                NSLog(@"ATVADDRESS: %@", APPLE_TV_ADDRESS);
+                BOOL jailbroken = [APP_DELEGATE isJailbroken];
+                
+                //hacky check here to see if its jailbroken
+                dispatch_async(dispatch_get_main_queue(), ^{
+                    NSLog(@"is jailbroken: %lu", jailbroken);
+                    if (jailbroken == FALSE){
+                    [APP_DELEGATE showNotJailbrokenWarning];
+                    [DEFAULTS removeObjectForKey:ATV_HOST];
+                    [DEFAULTS removeObjectForKey:@"selectedValue"];
+                        [APP_DELEGATE setStatusText:@"Device is not jailbroken! :("];
+                        [[APP_DELEGATE progressInd] stopAnimation:nil];
+                    } else {
+                        [APP_DELEGATE setStatusText:@"Device is jailbroken!"];
+                        [APP_DELEGATE setAtvAvailable:YES];
+                        [[APP_DELEGATE progressInd] stopAnimation:nil];
+                        [APP_DELEGATE setDeviceDict:finalDict];
+                    }
+                    
+                });
+                
+            }
+        }
+    });
+    
+    
+}
+
+- (NSString *)convertedName:(NSString *)inputName
+{
+	
+	
+    NSMutableString	*fixedNetLabel = [NSMutableString stringWithString:[inputName stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@".#,<>/?\'\\\[]{}+=-~`\";:"]]];
+	//NSLog(@"fixedNetLabel: %@", fixedNetLabel);
+    [fixedNetLabel replaceOccurrencesOfString:@" " withString:@"-" options:0 range:NSMakeRange(0, [fixedNetLabel length])];
+	//int nameLength = [fixedNetLabel length];
+	//fixedNetLabel = [fixedNetLabel substringToIndex:(nameLength-1)];
+    return [NSString stringWithString:fixedNetLabel];
+}
+
+- (NSString *)fixedName:(NSString *)inputName
+{
+	NSInteger nameLength = [inputName length];
+	NSString *newName = [inputName substringToIndex:(nameLength-1)];
+	return newName;
+}
+
+
+
+- (NSDictionary *)stringDictionaryFromService:(NSNetService *)theService
+{
+	NSData *txtRecordDict = [theService TXTRecordData];
+	
+	NSDictionary *theDict = [NSNetService dictionaryFromTXTRecordData:txtRecordDict];
+	NSMutableDictionary *finalDict = [[NSMutableDictionary alloc] init];
+	NSArray *keys = [theDict allKeys];
+	for (NSString *theKey in keys)
+	{
+		NSString *currentString = [[NSString alloc] initWithData:[theDict valueForKey:theKey] encoding:NSUTF8StringEncoding];
+		[finalDict setObject:currentString forKey:theKey];
+	}
+    NSString *ip;
+    int port;
+    struct sockaddr_in *addr;
+    if ([[theService addresses] count] == 0)
+    {
+        NSLog(@"no addresses resolved!?!?");
+        return nil;
+    }
+    addr = (struct sockaddr_in *) [[[theService addresses] objectAtIndex:0]
+                                   bytes];
+    ip = [NSString stringWithUTF8String:(char *) inet_ntoa(addr->sin_addr)];
+    port = ntohs(((struct sockaddr_in *)addr)->sin_port);
+    //NSLog(@"ipaddress: %@", ip);
+    //NSLog(@"port: %i", port);
+    
+    NSString *fullIP = [NSString stringWithFormat:@"%@:%i", ip, port];
+    finalDict[@"fullIP"] = fullIP;
+    finalDict[@"hostname"] = theService.hostName;
+	return finalDict;
+}
+
+
+
+
+
+
+- (id)init {
+	
+    browser = [[NSNetServiceBrowser alloc] init];
+    services = [NSMutableArray array];
+    [browser setDelegate:self];
+    //NSLog(@"awake from nib");
+    // Passing in "" for the domain causes us to browse in the default browse domain
+
+    
+   [browser searchForServicesOfType:@"_airplay._tcp." inDomain:@""];
+	// [hostNameField setStringValue:@""];
+	self = [super init];
+    
+
+	NSDictionary *catv = [NSDictionary dictionaryWithObject:@"Choose Apple TV" forKey:@"name"];
+	NSDictionary *theDict = [NSDictionary dictionaryWithObject:@"Other..." forKey:@"name"];
+	[services addObject:catv];
+	[services addObject:theDict];
+
+	return self;
+}
+
+- (void)netServiceBrowserDidStopSearch:(NSNetServiceBrowser *)browser
+
+{
+	//NSLog(@"%@ %s", self, _cmd);
+    searching = NO;
+	//[_parentObject endServices:services];
+    [self updateUI];
+	
+}
+
+- (void)updateUI
+
+{
+	//NSLog(@"%@ %s", self, _cmd);
+    if(searching)
+		
+    {
+		
+        // Update the user interface to indicate searching
+		
+        // Also update any UI that lists available services
+		
+    }
+	
+    else
+		
+    {
+		
+		NSLog(@"services: %@", services);
+        // Update the user interface to indicate not searching
+		
+    }
+	
+}
+
+- (void)netServiceBrowserWillSearch:(NSNetServiceBrowser *)browser
+
+{
+
+	//NSLog(@"%@ %s", self, _cmd);
+    searching = YES;
+	
+    [self updateUI];
+	
+}
+
+// Error handling code
+
+- (void)handleError:(NSNumber *)error
+
+{
+	
+    NSLog(@"An error occurred. Error code = %d", [error intValue]);
+	
+    // Handle error here
+	
+}
+
+
+- (BOOL)searching {
+    return searching;
+}
+
+
+// This object is the delegate of its NSNetServiceBrowser object. We're only interested in services-related methods, so that's what we'll call.
+- (void)netServiceBrowser:(NSNetServiceBrowser *)aNetServiceBrowser didFindService:(NSNetService *)aNetService moreComing:(BOOL)moreComing {
+	
+    NSInteger servicesCount = [services count]-1;
+    
+    [services insertObject:aNetService atIndex:servicesCount];
+
+    [aNetService setDelegate:self];
+    [aNetService resolveWithTimeout:5.0];
+    
+    if(!moreComing) {
+ 		
+
+		[deviceController setContent:services];
+
+		 
+	}
+}
+
+#if TARGET_OS_OSX
+/*
+- (NSString *)input: (NSString *)prompt defaultValue: (NSString *)defaultValue {
+	NSAlert *alert = [NSAlert alertWithMessageText: prompt
+									 defaultButton:@"OK"
+								   alternateButton:@"Cancel"
+									   otherButton:nil
+						 informativeTextWithFormat:@""];
+	
+	NSTextField *input = [[NSTextField alloc] initWithFrame:NSMakeRect(0, 0, 200, 24)];
+	[input setStringValue:defaultValue];
+
+	[alert setAccessoryView:input];
+	NSInteger button = [alert runModal];
+	if (button == NSAlertDefaultReturn) {
+		[input validateEditing];
+		NSString *inputString = [input stringValue];
+
+		return inputString;
+	} else if (button == NSAlertAlternateReturn) {
+		return nil;
+	} else {
+		
+		NSAssert1(NO, @"Invalid input dialog button %d", button);
+		return nil;
+	}
+}
+ */
+#endif
+
+- (void)netServiceBrowser:(NSNetServiceBrowser *)aNetServiceBrowser didRemoveService:(NSNetService *)aNetService moreComing:(BOOL)moreComing {
+    [services removeObject:aNetService];
+    
+    if(!moreComing) {
+	
+	}
+}
+
+- (NSDictionary *)deviceDictionary {
+    return deviceDictionary;
+}
+
+- (void)setDeviceDictionary:(NSDictionary *)value {
+    if (deviceDictionary != value) {
+    
+        deviceDictionary = [value copy];
+    }
+}
+
+//resolution stuff
+
+
+- (BOOL)addressesComplete:(NSArray *)addresses
+
+		   forServiceType:(NSString *)serviceType
+
+{
+    // Perform appropriate logic to ensure that [netService addresses]
+	
+    if ([self.delegate respondsToSelector:@selector(servicesFound:)])
+    {
+        NSMutableArray *fullServices = [NSMutableArray new];
+        for (NSNetService *service in services)
+        {
+            NSDictionary *fullDict = [self stringDictionaryFromService:service];
+            if (fullDict != nil)
+                [fullServices addObject:fullDict];
+        }
+        
+          [self.delegate servicesFound:fullServices];
+    }
+    
+	
+    return YES;
+	
+}
+
+// Sent when addresses are resolved
+
+- (void)netServiceDidResolveAddress:(NSNetService *)aNetService
+
+{
+    NSDictionary *finalDict = [self stringDictionaryFromService:aNetService];
+    if ([[finalDict allKeys] count] > 0)
+    {
+        
+        NSString *model = [finalDict objectForKey:@"model"];
+        if ([model isEqualToString:@"AppleTV5,3"])
+        {
+            //NSLog(@"should add service: %@", aNetService);
+           // NSInteger servicesCount = [services count]-1;
+            
+            //[services insertObject:aNetService atIndex:servicesCount];
+            
+        } else {
+            [services removeObject:aNetService];
+            
+            [deviceController setContent:services];
+        }
+    }
+	/*
+    if ([self addressesComplete:[netService addresses]
+		 
+				 forServiceType:[netService type]]) {
+		
+		
+    }
+	*/
+}
+
+
+
+// Sent if resolution fails
+
+- (void)netService:(NSNetService *)netService
+
+	 didNotResolve:(NSDictionary *)errorDict
+
+{
+	//NSLog(@"%@ %s", self, _cmd);
+    [self handleError:[errorDict objectForKey:NSNetServicesErrorCode]];
+	
+    [services removeObject:netService];
+	
+}
+
+#if TARGET_OS_OSX
+
+// This object is the data source of its NSTableView. servicesList is the NSArray containing all those services that have been discovered.
+- (int)numberOfRowsInTableView:(NSTableView *)theTableView {
+    return [services count];
+}
+
+- (NSDictionary *)currentServiceDictionary {
+    NSNetService * clickedService = [services objectAtIndex:[[self theComboBox] indexOfSelectedItem]];
+    
+    //NSLog(@"clickedService: %@" ,[self theComboBox]);
+    
+    return [self stringDictionaryFromService:clickedService];
+}
+
+
+- (id)tableView:(NSTableView *)theTableView objectValueForTableColumn:(NSTableColumn *)theColumn row:(int)rowIndex {
+   NSString *fixedName = [[[[services objectAtIndex:rowIndex] name] componentsSeparatedByString:@"@"] lastObject];
+	return fixedName;
+	// return [[services objectAtIndex:rowIndex] name];
+}
+
+#endif
+
+@end

+ 54 - 0
nitoTV4Installer/AppDelegate.h

@@ -0,0 +1,54 @@
+//
+//  AppDelegate.h
+//  nitoTV4Installer
+//
+//  Created by Kevin Bradley on 1/24/18.
+//  Copyright © 2018 Kevin Bradley. All rights reserved.
+//
+
+#import <Cocoa/Cocoa.h>
+#include "ObjSSH.h"
+#import "EMKeychainItem.h"
+#import "KBDownloadFile.h"
+
+typedef enum
+{
+    KBInstallVersionStateNine,
+    KBInstallVersionStateTenOne,
+    KBInstallVersionStateTenTwo,
+    KBInstallVersionStateEleven
+}
+KBInstallVersionState;
+
+@interface AppDelegate : NSObject <NSApplicationDelegate>
+{
+    ObjSSH *sshSession;
+    BOOL isSending;
+    BOOL _atvAvailable;
+    NSString *finalDestination;
+    IBOutlet NSTextField *statusLabel;
+    
+    NSString *fileName;
+}
+@property (readwrite, assign) KBInstallVersionState versionState;
+@property (nonatomic, strong) ObjSSH *sshSession;
+@property (nonatomic, strong) NSString *workingPath;
+@property (readwrite, assign) BOOL isSending;
+@property (nonatomic, weak) IBOutlet NSProgressIndicator *progressInd;
+@property (nonatomic, weak) IBOutlet NSButton *mainButton;
+@property (nonatomic, strong) KBDownloadFile *downloadFile;
+@property (readwrite, assign) BOOL downloading;
+@property (nonatomic, strong) NSDictionary *deviceDict;
+
+//@property (readwrite, assign) BOOL atvAvailable;
+@property (nonatomic, strong) NSString *bundleID;
+- (void)setAtvAvailable:(BOOL)atvAvailable;
+- (BOOL)atvAvailable;
+- (void)checkJailbreakWithCompletion:(void(^)(BOOL jailbroken))completion;
+- (IBAction)doIt:(id)sender;
+- (void)resetServerSettings;
+- (void)setStatusText:(NSString *)statusText;
+- (void)showATVWarning;
+- (BOOL)isJailbroken;- (void)showNotJailbrokenWarning;
+@end
+

+ 826 - 0
nitoTV4Installer/AppDelegate.m

@@ -0,0 +1,826 @@
+//
+//  AppDelegate.m
+//  nitoTV4Installer
+//
+//  Created by Kevin Bradley on 1/24/18.
+//  Copyright © 2018 Kevin Bradley. All rights reserved.
+//
+
+#import "AppDelegate.h"
+#import "ATVDeviceController.h"
+
+@interface AppDelegate ()
+
+@property (weak) IBOutlet NSWindow *window;
+//@property (readwrite, assign) BOOL atvAvailable;
+//@property (nonatomic, strong) NSString *bundleID;
+@property (nonatomic, strong) IBOutlet ATVDeviceController *deviceController;
+@end
+
+@implementation AppDelegate
+
+@synthesize window,workingPath, sshSession, isSending, downloading, downloadFile;
+@synthesize deviceController;
+
+static NSString *appleTVAddress = nil;
+
+- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
+    // Insert code here to initialize your application
+    
+    //deviceController = [[ATVDeviceController alloc] init];
+    //variables that was a carry over from code i migrated this from, can probably be pruned, dont think i use it.
+    appleTVAddress = APPLE_TV_ADDRESS;
+    NSLog(@"SELECTED_VALUE: %@", SELECTED_VALUE);
+    if (SELECTED_VALUE != nil)
+    {
+        [deviceController.theComboBox selectItemWithTitle:SELECTED_VALUE];
+        
+        [deviceController menuItemSelected:deviceController.theComboBox];
+    }
+    
+    NSLog(@"appleTVAddress: %@", appleTVAddress);
+    
+    if ([[appleTVAddress componentsSeparatedByString:@":"] count] < 2)
+    {
+        [self resetServerSettings];
+        
+    }
+    
+}
+
+- (NSString *)appSupportFolder
+{
+    NSString *supportFolder = [NSHomeDirectory() stringByAppendingPathComponent:@"Library/Application Support/nitoDownload"];
+    NSLog(@"dir: %@", supportFolder);
+    if (![FM fileExistsAtPath:supportFolder])
+    {
+        [FM createDirectoryAtPath:supportFolder withIntermediateDirectories:YES attributes:nil error:nil];
+    }
+    return supportFolder;
+}
+
++ (NSArray *)returnForProcess:(NSString *)call
+{
+    if (call==nil)
+        return 0;
+    char line[200];
+    
+    FILE* fp = popen([call UTF8String], "r");
+    NSMutableArray *lines = [[NSMutableArray alloc]init];
+    if (fp)
+    {
+        while (fgets(line, sizeof line, fp))
+        {
+            NSString *s = [NSString stringWithCString:line encoding:NSUTF8StringEncoding];
+            s = [s stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
+            [lines addObject:s];
+        }
+    }
+    pclose(fp);
+    return lines;
+}
+//59293ffbdee698488aba799a96fcfe95e1165fa10f760362a2ce910576134e39
+- (BOOL)verifyChecksum:(NSString *)checksum onFile:(NSString *)file
+{
+    NSString *processReturn = [[AppDelegate returnForProcess:[NSString stringWithFormat:@"/usr/bin/shasum -a 256 \"%@\" | cut -c 1-64", file]] componentsJoinedByString:@"\n"];
+    NSLog(@"return: -%@-", processReturn);
+    return [processReturn isEqualToString:checksum];
+    
+}
+
+- (void)downloadFile:(NSString *)thePayload
+{
+    //we're already downloading, cancel
+    //TODO: make downloading NSOperation/NSOperationQueue based
+    if (self.downloading == true)
+    {
+        [downloadFile cancel];
+        self.downloading = false;
+        statusLabel.stringValue = @"";
+        [self.progressInd setDoubleValue:0];
+        [self.progressInd setHidden:TRUE];
+        self.mainButton.enabled = TRUE;
+        return;
+    }
+    //create instance of downloader class
+    downloadFile = [KBDownloadFile new];;
+    //self.downloadButton.title = @"Cancel";
+    self.downloading = true;
+    
+    NSString *downloadLocation = [[self appSupportFolder] stringByAppendingPathComponent:[thePayload lastPathComponent]];
+    
+    if ([FM fileExistsAtPath:downloadLocation])
+    {
+        NSString *hash = @"59293ffbdee698488aba799a96fcfe95e1165fa10f760362a2ce910576134e39";
+        switch (self.versionState) {
+            case KBInstallVersionStateEleven:
+            case KBInstallVersionStateTenTwo:
+                
+                hash = @"59293ffbdee698488aba799a96fcfe95e1165fa10f760362a2ce910576134e39";
+                break;
+                
+            case KBInstallVersionStateNine:
+            case KBInstallVersionStateTenOne:
+                
+                hash = @"327c7cea49282109e23c9415d318121f91b79ca7bb813c8724a1c0752e8311af";
+                break;
+                
+            default:
+                break;
+        }
+       if( [self verifyChecksum:hash onFile:downloadLocation])
+       {
+           NSLog(@"file validated!");
+           
+           [self handleDownloadFile:downloadLocation];
+           return;
+           //completion(true);
+           
+       }
+    }
+    
+    statusLabel.stringValue = [NSString stringWithFormat:@"Downloading: %@...", thePayload.lastPathComponent];
+    
+    //get the stream we want to download
+    
+    [downloadFile downloadFileWithURL:[NSURL URLWithString:thePayload] toLocation:downloadLocation progress:^(double percentComplete) {
+        
+        [self setDownloadProgress:percentComplete];
+        
+    } completed:^(NSString *downloadedFile) {
+        //[self hideProgress];
+        self.downloading = false;
+        
+        [self setDownloadProgress:0];
+        statusLabel.stringValue = [NSString stringWithFormat:@"Uploading file %@....", downloadedFile.lastPathComponent];
+        
+        
+        [self handleDownloadFile:downloadedFile];
+        
+        
+        
+        //[self uploadFile:downloadedFile];
+        
+    }];
+    
+}
+
+- (void)handleDownloadFile:(NSString *)downloadedFile
+{
+    [self setDownloadProgress:0];
+    statusLabel.stringValue = [NSString stringWithFormat:@"Uploading file %@....", downloadedFile.lastPathComponent];
+    
+    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
+        
+        
+        [self uploadFile:downloadedFile withCompletion:^(BOOL success) {
+            
+            dispatch_async(dispatch_get_main_queue(), ^{
+                
+                if (success)
+                {
+                    NSLog(@"file uploaded successfully!");
+                    statusLabel.stringValue = @"File uploaded successfully!";
+                    statusLabel.stringValue = @"Installing payload...";
+                    NSString *tarString = [self tarPathForCurrentMode];
+                    NSString *bootstrap = [NSString stringWithFormat:@"/var/mobile/Documents/%@", downloadedFile.lastPathComponent];
+                    
+                    NSString *installCommand = [NSString stringWithFormat:@"%@ fxpv %@ -C / ; /usr/libexec/substrate ; /usr/bin/uicache  ; /bin/bash /usr/libexec/nito/firmware.sh ; rm /var/mobile/Library/Preferences/Featured.plist", tarString, bootstrap];
+                    
+                   
+                    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
+                        
+                        NSString *returnString = [self sendCommandString:installCommand];
+                        NSLog(@"return string: %@", returnString);
+                        NSString *logFile = [[self appSupportFolder] stringByAppendingPathComponent:@"install.log"];
+                        
+                        [returnString writeToFile:logFile atomically:YES encoding:NSUTF8StringEncoding error:nil];
+                        
+                        if (self.versionState == KBInstallVersionStateNine)
+                        {
+                            [self sendCommandString:@"/usr/bin/killall -9 PineBoard HeadBoard lsd nitoTV"];
+                        }
+                        
+                        dispatch_async(dispatch_get_main_queue(), ^{
+                            [[NSWorkspace sharedWorkspace] openFile:logFile];
+                            
+                            statusLabel.stringValue = @"Payload installed?!?!";
+                            
+                            
+                            [self.progressInd stopAnimation:nil];
+                            self.mainButton.enabled = TRUE;
+                        });
+                    });
+                    
+                } else {
+                    statusLabel.stringValue = @"File failed to upload!";
+                    [self.progressInd stopAnimation:nil];
+                    self.mainButton.enabled = TRUE;
+                }
+                
+            });
+            
+        }];
+        
+    });
+    
+   
+   
+}
+
+- (void)hideProgress
+{
+    dispatch_async(dispatch_get_main_queue(), ^{
+        statusLabel.stringValue = @"";
+        [[self progressInd] stopAnimation:nil];
+        [[self progressInd] setDoubleValue:0];
+        [[self progressInd] setHidden:true];
+    });
+    
+}
+
+- (void)setDownloadProgress:(double)theProgress
+{
+    dispatch_async(dispatch_get_main_queue(), ^{
+        
+        if (theProgress == 0)
+        {
+            [self.progressInd setIndeterminate:TRUE];
+            [self.progressInd setHidden:FALSE];
+            [self.progressInd setNeedsDisplay:YES];
+            [self.progressInd setUsesThreadedAnimation:YES];
+            [self.progressInd startAnimation:self];
+            return;
+        }
+        [self.progressInd setIndeterminate:FALSE];
+        [self.progressInd startAnimation:self];
+        [self.progressInd setHidden:FALSE];
+        [self.progressInd setNeedsDisplay:YES];
+        [self.progressInd setDoubleValue:theProgress];
+    });
+    
+}
+
+- (void)setStatusText:(NSString *)statusText
+{
+    dispatch_async(dispatch_get_main_queue(), ^{
+       
+        statusLabel.stringValue = statusText;
+    });
+}
+
+- (BOOL)atvAvailable
+{
+    return _atvAvailable;
+}
+
+- (void)setAtvAvailable:(BOOL)atvAvailable
+{
+    _atvAvailable = atvAvailable;
+}
+- (void)applicationWillTerminate:(NSNotification *)aNotification {
+    // Insert code here to tear down your application
+}
+
+- (NSString *)tarPathForCurrentMode
+{
+    NSString *path = nil;
+    switch (self.versionState) {
+        case KBInstallVersionStateNine:
+            
+            path= @"/usr/bin/tar";
+            break;
+            
+        case KBInstallVersionStateTenOne:
+            
+            path= @"/tmp/usr/bin/tar"; //need to verify
+            break;
+            
+        case KBInstallVersionStateTenTwo:
+            
+            path= @"/usr/bin/tar";
+            break;
+            
+        case KBInstallVersionStateEleven:
+            
+            path= @"/jb/usr/bin/tar";
+            break;
+        default:
+            break;
+    }
+    return path;
+}
+
+- (NSString *)lsPathForCurrentMode
+{
+    NSString *path = nil;
+    switch (self.versionState) {
+        case KBInstallVersionStateNine:
+            
+            path= @"/bin/ls";
+            break;
+            
+        case KBInstallVersionStateTenOne:
+            
+            path= @"/tmp/bin/ls";
+            break;
+            
+        case KBInstallVersionStateTenTwo:
+            
+            path= @"/usr/bin/ls";
+            break;
+            
+        case KBInstallVersionStateEleven:
+            
+             path= @"/jb/bin/ls";
+            break;
+        default:
+            break;
+    }
+    return path;
+}
+
+- (BOOL)checkForFile:(NSString *)file
+{
+    //@"ls /usr/bin/ | grep appinst"
+    NSString *lsCommand = [self lsPathForCurrentMode];
+    NSString *path = [file stringByDeletingLastPathComponent];
+    NSString *bareFile = [file lastPathComponent];
+    NSString *theReturn = [self sendCommandString:[NSString stringWithFormat:@"%@ -a %@ | grep %@", lsCommand, path, bareFile]];
+    if (theReturn != nil)
+    { return (TRUE);
+    } else { return (FALSE);}
+    
+    return (FALSE);
+}
+
+
+- (void)checkJailbreakWithCompletion:(void(^)(BOOL jailbroken))completion
+{
+    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
+        
+        BOOL jb = [self isJailbroken];
+        if (jb)
+        {
+            NSLog(@"we outchea");
+        } else {
+            NSLog(@"nope!");
+        }
+        completion(jb);
+    });
+}
+
+- (BOOL)isJailbroken
+{
+    NSError *error = nil;
+    if (APPLE_TV_ADDRESS != nil)
+    {
+        ObjSSH *ssh = [ObjSSH connectToHost:APPLE_TV_ADDRESS withUsername:@"root" password:@"alpine" error:&error];
+        if (error)
+        {
+            NSLog(@"error: %@", [error localizedDescription]);
+            if ([[error localizedDescription] isEqualToString:@"Failed to connect"])
+            {
+                [ssh disconnect];
+                
+                return (FALSE);
+            }
+            
+            [ssh disconnect];
+            
+        }
+    } else {
+        return (FALSE);
+    }
+    
+    
+    return (TRUE);
+}
+
+//if they changed password from alpine this is used to get the proper password to connect
+
+- (void)resetServerSettings
+{
+    [DEFAULTS removeObjectForKey:@"appleTVHost"];
+    [DEFAULTS removeObjectForKey:ATV_OS];
+    [DEFAULTS removeObjectForKey:ATV_API];
+    [DEFAULTS setObject:@"Choose Apple TV" forKey:@"selectedValue"];
+    appleTVAddress = nil;
+    [[self progressInd] stopAnimation:nil];
+}
+
+
+- (NSString *)secureInput: (NSString *)prompt defaultValue: (NSString *)defaultValue {
+    NSAlert *alert = [NSAlert alertWithMessageText: prompt
+                                     defaultButton:@"OK"
+                                   alternateButton:@"Cancel"
+                                       otherButton:nil
+                         informativeTextWithFormat:@""];
+    
+    NSSecureTextField *input = [[NSSecureTextField alloc] initWithFrame:NSMakeRect(0, 0, 200, 24)];
+    [input setStringValue:defaultValue];
+    
+    [alert setAccessoryView:input];
+    NSInteger button = [alert runModal];
+    if (button == NSAlertDefaultReturn) {
+        [input validateEditing];
+        NSString *inputString = [input stringValue];
+        
+        return inputString;
+    } else if (button == NSAlertAlternateReturn) {
+        
+        return nil;
+    } else {
+        
+        return nil;
+    }
+}
+
+
+
+//add a password to the keychain for SSH
+
+- (void)addkeychainPassword:(NSString *)password
+{
+    //kSecProtocolTypeSSH
+    [EMInternetKeychainItem addInternetKeychainItemForServer:APPLE_TV_ADDRESS    withUsername:@"root" password:password path:@"/usr/bin/ssh" port:22 protocol:kSecProtocolTypeSSH];
+}
+
+- (void)removeKeychainForHost:(NSString *)ipAddress
+{
+    //kSecProtocolTypeSSH
+    EMInternetKeychainItem *keychainItem = [EMInternetKeychainItem internetKeychainItemForServer:ipAddress withUsername:@"root" path:@"/usr/bin/ssh" port:22 protocol:kSecProtocolTypeSSH];
+    [keychainItem removeFromKeychain];
+}
+
+//fetch the password from the keychain for the specified ip address
+
+- (NSString *)passwordForHost:(NSString *)ipAddress
+{
+    EMInternetKeychainItem *keychainItem = [EMInternetKeychainItem internetKeychainItemForServer:ipAddress withUsername:@"root" path:@"/usr/bin/ssh" port:22 protocol:kSecProtocolTypeSSH];
+    //Grab the password.
+    if (keychainItem != nil)
+    {
+        //Grab the password.
+        NSString *password = keychainItem.password;
+        
+        return password;
+    }
+    
+    NSLog(@"nothing!");
+    return nil;
+    
+}
+
+- (BOOL)uploadFile:(NSString *)theFile toPath:(NSString *)newPath
+{
+    NSLog(@"uploading file: %@", theFile);
+    NSError *error = nil;
+    BOOL getSession = [self connectToSSH];
+    if (getSession == FALSE)
+    {
+        NSLog(@"failed to get session!");
+        return (FALSE);
+    }
+    
+    NSString *finalPath = [newPath stringByAppendingPathComponent:[theFile lastPathComponent]];
+    
+    BOOL uploadFile = [sshSession uploadFile:theFile to:finalPath error:&error];
+    if (error)
+    {
+        NSLog(@"ERROR!: %@", error);
+    }
+    return (uploadFile);
+    
+}
+
+- (void)uploadFile:(NSString *)theFile withCompletion:(void(^)(BOOL success))completion
+{
+    NSLog(@"uploading file: %@", theFile);
+    NSError *error = nil;
+    BOOL getSession = [self connectToSSH];
+    if (getSession == FALSE)
+    {
+        NSLog(@"failed to get session!");
+        completion(false);
+    }
+    BOOL uploadFile = [self uploadFile:theFile toPath:@"/var/mobile/Documents"];
+    completion(uploadFile);
+    
+}
+
+//upload a file over SSH to the selected AppleTV
+
+- (BOOL)uploadFile:(NSString *)theFile
+{
+    NSLog(@"uploading file: %@", theFile);
+    NSError *error = nil;
+    BOOL getSession = [self connectToSSH];
+    if (getSession == FALSE)
+    {
+        NSLog(@"failed to get session!");
+        return (FALSE);
+    }
+    statusLabel.stringValue = [NSString stringWithFormat:@"Uploading file %@....", theFile.lastPathComponent];
+    BOOL uploadFile = [sshSession uploadFile:theFile to:[theFile lastPathComponent] error:&error];
+    if (error)
+    {
+        NSLog(@"ERROR!: %@", error);
+    }
+    return (uploadFile);
+    
+}
+
+//used to send basic commands to the jailbroken AppleTV over SSH
+
+- (NSString *)sendCommandString:(NSString *)theCommand
+{
+    NSLog(@"%@", theCommand);
+    NSError *error = nil;
+    BOOL getSession = [self connectToSSH];
+    if (getSession == FALSE)
+    {
+        NSLog(@"failed to get session!");
+        return nil;
+    }
+    
+    
+    NSString *response = [sshSession execute:theCommand error:&error];
+    
+    return response;
+    
+}
+
+//open the SSH session
+
+- (BOOL)connectToSSH
+{
+    NSError *error = nil;
+    
+    if (sshSession == nil)
+    {
+        //NSLog(@"APPLE_TV_ADDRESS: %@", APPLE_TV_ADDRESS);
+        sshSession = [ObjSSH connectToHost:APPLE_TV_ADDRESS withUsername:@"root" password:@"alpine" error:&error];
+        if (sshSession == nil)
+        {
+            NSLog(@"error: %@ get password!", error);
+            NSString *passwordForHost = [self passwordForHost:APPLE_TV_ADDRESS];
+            NSString *output = nil;
+            if (passwordForHost != nil)
+            {
+                output = passwordForHost;
+                
+            } else {
+                output = [self secureInput:@"Enter Password" defaultValue:@""];
+            }
+            
+            if ([output length] == 0)
+            {
+                NSLog(@"no password to send!! return!");
+                
+                return (FALSE);
+                
+            } else {
+                
+                
+                
+                error = nil;
+                sshSession = [ObjSSH connectToHost:APPLE_TV_ADDRESS withUsername:@"root" password:output error:&error];
+                
+                if (error != nil)
+                {
+                    NSLog(@"error: %@ password failed!", error);
+                    
+                    [self removeKeychainForHost:APPLE_TV_ADDRESS];
+                    return (FALSE);
+                } else {
+                    [self addkeychainPassword:output];
+                }
+                
+            }
+            
+            
+        }
+    }
+    if (sshSession != nil)
+        return (TRUE);
+    
+    
+    return (FALSE);
+}
+
+- (void)showNotJailbrokenWarning
+{
+    NSAlert *alert = [NSAlert alertWithMessageText:@"This Apple TV isn't jailbroken, please jailbreak it first! Would you like to visit our web site for further assistance?"
+                                     defaultButton:@"Yes"
+                                   alternateButton:@"Cancel"
+                                       otherButton:nil
+                         informativeTextWithFormat:@""];
+    
+    NSInteger button = [alert runModal];
+    if (button == NSAlertDefaultReturn) {
+        
+        [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"http://wiki.awkwardtv.org"]];
+    }
+}
+
+- (void)showATVWarning
+{
+    NSAlert *alert = [NSAlert alertWithMessageText:@"Only the AppleTV 4 is supported"
+                                     defaultButton:@"OK"
+                                   alternateButton:nil
+                                       otherButton:nil
+                         informativeTextWithFormat:@""];
+    
+    [alert runModal];
+}
+
+- (NSString *)logLocation
+{
+    NSString *location = [NSHomeDirectory() stringByAppendingPathComponent:@"Library/Logs/iReSign"];
+    if (![FM fileExistsAtPath:location])
+    {
+        [FM createDirectoryAtPath:location withIntermediateDirectories:true attributes:nil error:nil];
+    }
+    return location;
+}
+
+//versions
+//265.5 = 9.0
+//301.44.3 = 10.0
+//310.17 = 10.1 (either .0 or .1)
+//320.20.1 = 10.2.2
+//353.50 = 11.1
+
+- (IBAction)doIt:(id)sender
+{
+    [self.mainButton setEnabled:NO];
+    NSString *bootstrapPath = @"http://nitosoft.com/ATV4/bootstraps/";
+    NSString *swVers = self.deviceDict[@"osvers"];
+    if (swVers.length > 0)
+    {
+        NSLog(@"our sw vers: %@", swVers);
+        if ([swVers floatValue] >= 11)
+        {
+            self.versionState = KBInstallVersionStateEleven;
+            NSLog(@"11 payload!");
+            bootstrapPath = [bootstrapPath stringByAppendingString:@"bootstrap11.tar"];
+        }
+            
+    } else {
+        
+        swVers = self.deviceDict[@"srcvers"];
+        {
+            NSLog(@"our src vers: %@", swVers);
+            if([swVers compare:@"320.20.1" options:NSNumericSearch] != NSOrderedAscending)
+            {
+                self.versionState = KBInstallVersionStateTenTwo;
+                NSLog(@"10.2.2 or greater!");
+                bootstrapPath = [bootstrapPath stringByAppendingString:@"bootstrap11.tar"];
+            } else if([swVers compare:@"301.44.3" options:NSNumericSearch] != NSOrderedAscending)
+            {
+                NSLog(@"10.0 or greater");
+                self.versionState = KBInstallVersionStateTenOne;
+                bootstrapPath = [bootstrapPath stringByAppendingString:@"bootstrap9.tar"];
+            } else {
+                NSLog(@"9.0 or greater?");
+                self.versionState = KBInstallVersionStateNine;
+                bootstrapPath = [bootstrapPath stringByAppendingString:@"bootstrap9.tar"];
+                
+                
+            }
+            
+        }
+    }
+    if ([self connectToSSH])
+    {
+       // if ([self checkForFile:@"/var/root/.bootstrapped"]){
+            
+          //  NSLog(@"were already bootstrapped!");
+        //} else {
+            [self downloadFile:bootstrapPath];
+       // }
+       //
+        //[self downloadSyslogAndShow:YES];
+    } else {
+        self.mainButton.enabled = TRUE;
+    }
+}
+
+- (BOOL)downloadSyslogAndShow:(BOOL)show
+{
+    BOOL getSession = [self connectToSSH];
+    if (getSession == FALSE)
+    {
+        NSLog(@"failed to get session!");
+        return false;
+    }
+    NSError *error = nil;
+    NSString *newSyslog = [[self logLocation] stringByAppendingPathComponent:@"syslog.log"];
+    if ([[NSFileManager defaultManager] fileExistsAtPath:newSyslog])
+    {
+        [[NSFileManager defaultManager] removeItemAtPath:newSyslog error:nil];
+    }
+    //echo latest syslog output to a new file for downloadin..
+    [self sendCommandString:@"/jb/usr/bin/syslog > syslog.log"];
+    BOOL downloadFile = [sshSession downloadFile:@"/var/root/syslog.log" to:newSyslog error:&error];
+    if (downloadFile)
+    {
+        NSLog(@"File downloaded Successfully!");
+        if (show == true)
+        {
+            [[NSWorkspace sharedWorkspace] openFile:newSyslog];
+        }
+        return true;
+    }
+    return false;
+}
+
+
+- (void)installFile:(NSString *)theFile withCompletionBlock:(void(^)(BOOL success))completionBlock
+{
+    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
+        
+        @autoreleasepool {
+            
+            BOOL success = FALSE;
+            
+            if ([self uploadFile:finalDestination toPath:@"/var/mobile/Documents"] == true)
+            {
+                dispatch_async(dispatch_get_main_queue(), ^{
+                    
+                    [statusLabel setStringValue:[NSString stringWithFormat:@"Installing file %@...", fileName]];
+                    
+                });
+                
+                /*
+                 
+                 Massively kludgy but it works, for some reason when appinst runs it doesnt go to stdout (or something) so i /need/ to redirect it to a text file, then cat that text file to check for whether or not "installed <bundle_id>" exists
+                 
+                 */
+                
+                NSString *checkResponse = [NSString stringWithFormat:@"installed %@", self.bundleID];
+                
+                NSString *runLine = [NSString stringWithFormat:@"/usr/bin/appinst /var/mobile/Documents/%@ 2> install.txt ; cat install.txt", fileName];
+                //NSString *runLine = [NSString stringWithFormat:@"/usr/bin/appinst /var/mobile/Documents/%@", fileName];
+                
+                NSString *response =  [self sendCommandString:runLine];
+                
+                //using rangeOfString because containsString is too new for backwards compat.
+                
+                if ([response rangeOfString:checkResponse].location == NSNotFound)
+                {
+                    
+                    NSString *errorLog = [[self logLocation] stringByAppendingFormat:@"/%@.log", self.bundleID];
+                    
+                    //remove old copies
+                    if ([FM fileExistsAtPath:errorLog])
+                    {
+                        [FM removeItemAtPath:errorLog error:nil];
+                    }
+                    
+                    //the response above has a bunch of garbled text in it, download the install file "proper" to get the cleaner version
+                    
+                    BOOL downloadFile = [sshSession downloadFile:@"/var/root/install.txt" to:errorLog error:nil];
+                    if (downloadFile == false)
+                    {
+                        
+                        //if that fails for some reason write the version with the garbage at the end
+                        
+                        [response writeToFile:errorLog atomically:true encoding:NSUTF8StringEncoding error:nil];
+                        
+                    }
+                    
+                    response = [NSString stringWithContentsOfFile:errorLog encoding:NSUTF8StringEncoding error:nil];
+                    NSLog(@"INSTALLATION FAILED WITH LOG: %@", response);
+                    
+                    //grab latest relevant syslog chunk
+                    [self downloadSyslogAndShow:false];
+                    
+                } else {
+                    runLine = [NSString stringWithFormat:@"/bin/rm /var/mobile/Documents/%@", fileName];
+                    [self sendCommandString:runLine];
+                    success = true;
+                }
+                
+                
+            }
+            
+            
+            dispatch_async(dispatch_get_main_queue(), ^{
+                
+                
+                completionBlock(success);
+                
+                
+                
+            });
+            
+        }
+        
+    });
+}
+
+
+
+@end

+ 58 - 0
nitoTV4Installer/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"
+  }
+}

+ 755 - 0
nitoTV4Installer/Base.lproj/MainMenu.xib

@@ -0,0 +1,755 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="13196" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
+    <dependencies>
+        <deployment identifier="macosx"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="13196"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
+            <connections>
+                <outlet property="delegate" destination="Voe-Tx-rLC" id="GzC-gU-4Uq"/>
+            </connections>
+        </customObject>
+        <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
+        <customObject id="-3" userLabel="Application" customClass="NSObject"/>
+        <customObject id="Voe-Tx-rLC" customClass="AppDelegate">
+            <connections>
+                <outlet property="deviceController" destination="fuO-f1-kJ3" id="7aR-ru-fcI"/>
+                <outlet property="mainButton" destination="OHh-hg-rQh" id="3dp-l8-XIf"/>
+                <outlet property="progressInd" destination="foy-RA-rvh" id="Xxp-jy-0LM"/>
+                <outlet property="statusLabel" destination="rmB-Fy-pOd" id="acf-50-FWP"/>
+                <outlet property="window" destination="QvC-M9-y7g" id="gIp-Ho-8D9"/>
+            </connections>
+        </customObject>
+        <customObject id="YLy-65-1bz" customClass="NSFontManager"/>
+        <customObject id="fuO-f1-kJ3" customClass="ATVDeviceController">
+            <connections>
+                <outlet property="deviceController" destination="Tvm-Nd-BiA" id="3kJ-oo-JjE"/>
+                <outlet property="theComboBox" destination="fTc-TT-leC" id="kYY-zP-BuH"/>
+            </connections>
+        </customObject>
+        <arrayController id="Tvm-Nd-BiA"/>
+        <menu title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
+            <items>
+                <menuItem title="nitoTV4Installer" id="1Xt-HY-uBw">
+                    <modifierMask key="keyEquivalentModifierMask"/>
+                    <menu key="submenu" title="nitoTV4Installer" systemMenu="apple" id="uQy-DD-JDr">
+                        <items>
+                            <menuItem title="About nitoTV4Installer" id="5kV-Vb-QxS">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <connections>
+                                    <action selector="orderFrontStandardAboutPanel:" target="-1" id="Exp-CZ-Vem"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem isSeparatorItem="YES" id="VOq-y0-SEH"/>
+                            <menuItem title="Preferences…" keyEquivalent="," id="BOF-NM-1cW"/>
+                            <menuItem isSeparatorItem="YES" id="wFC-TO-SCJ"/>
+                            <menuItem title="Services" id="NMo-om-nkz">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <menu key="submenu" title="Services" systemMenu="services" id="hz9-B4-Xy5"/>
+                            </menuItem>
+                            <menuItem isSeparatorItem="YES" id="4je-JR-u6R"/>
+                            <menuItem title="Hide nitoTV4Installer" keyEquivalent="h" id="Olw-nP-bQN">
+                                <connections>
+                                    <action selector="hide:" target="-1" id="PnN-Uc-m68"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Hide Others" keyEquivalent="h" id="Vdr-fp-XzO">
+                                <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+                                <connections>
+                                    <action selector="hideOtherApplications:" target="-1" id="VT4-aY-XCT"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Show All" id="Kd2-mp-pUS">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <connections>
+                                    <action selector="unhideAllApplications:" target="-1" id="Dhg-Le-xox"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem isSeparatorItem="YES" id="kCx-OE-vgT"/>
+                            <menuItem title="Quit nitoTV4Installer" keyEquivalent="q" id="4sb-4s-VLi">
+                                <connections>
+                                    <action selector="terminate:" target="-1" id="Te7-pn-YzF"/>
+                                </connections>
+                            </menuItem>
+                        </items>
+                    </menu>
+                </menuItem>
+                <menuItem title="File" id="dMs-cI-mzQ">
+                    <modifierMask key="keyEquivalentModifierMask"/>
+                    <menu key="submenu" title="File" id="bib-Uj-vzu">
+                        <items>
+                            <menuItem title="New" keyEquivalent="n" id="Was-JA-tGl">
+                                <connections>
+                                    <action selector="newDocument:" target="-1" id="4Si-XN-c54"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Open…" keyEquivalent="o" id="IAo-SY-fd9">
+                                <connections>
+                                    <action selector="openDocument:" target="-1" id="bVn-NM-KNZ"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Open Recent" id="tXI-mr-wws">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <menu key="submenu" title="Open Recent" systemMenu="recentDocuments" id="oas-Oc-fiZ">
+                                    <items>
+                                        <menuItem title="Clear Menu" id="vNY-rz-j42">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="clearRecentDocuments:" target="-1" id="Daa-9d-B3U"/>
+                                            </connections>
+                                        </menuItem>
+                                    </items>
+                                </menu>
+                            </menuItem>
+                            <menuItem isSeparatorItem="YES" id="m54-Is-iLE"/>
+                            <menuItem title="Close" keyEquivalent="w" id="DVo-aG-piG">
+                                <connections>
+                                    <action selector="performClose:" target="-1" id="HmO-Ls-i7Q"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Save…" keyEquivalent="s" id="pxx-59-PXV">
+                                <connections>
+                                    <action selector="saveDocument:" target="-1" id="teZ-XB-qJY"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Save As…" keyEquivalent="S" id="Bw7-FT-i3A">
+                                <connections>
+                                    <action selector="saveDocumentAs:" target="-1" id="mDf-zr-I0C"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Revert to Saved" keyEquivalent="r" id="KaW-ft-85H">
+                                <connections>
+                                    <action selector="revertDocumentToSaved:" target="-1" id="iJ3-Pv-kwq"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem isSeparatorItem="YES" id="aJh-i4-bef"/>
+                            <menuItem title="Page Setup…" keyEquivalent="P" id="qIS-W8-SiK">
+                                <modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
+                                <connections>
+                                    <action selector="runPageLayout:" target="-1" id="Din-rz-gC5"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Print…" keyEquivalent="p" id="aTl-1u-JFS">
+                                <connections>
+                                    <action selector="print:" target="-1" id="qaZ-4w-aoO"/>
+                                </connections>
+                            </menuItem>
+                        </items>
+                    </menu>
+                </menuItem>
+                <menuItem title="Edit" id="5QF-Oa-p0T">
+                    <modifierMask key="keyEquivalentModifierMask"/>
+                    <menu key="submenu" title="Edit" id="W48-6f-4Dl">
+                        <items>
+                            <menuItem title="Undo" keyEquivalent="z" id="dRJ-4n-Yzg">
+                                <connections>
+                                    <action selector="undo:" target="-1" id="M6e-cu-g7V"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Redo" keyEquivalent="Z" id="6dh-zS-Vam">
+                                <connections>
+                                    <action selector="redo:" target="-1" id="oIA-Rs-6OD"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem isSeparatorItem="YES" id="WRV-NI-Exz"/>
+                            <menuItem title="Cut" keyEquivalent="x" id="uRl-iY-unG">
+                                <connections>
+                                    <action selector="cut:" target="-1" id="YJe-68-I9s"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Copy" keyEquivalent="c" id="x3v-GG-iWU">
+                                <connections>
+                                    <action selector="copy:" target="-1" id="G1f-GL-Joy"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Paste" keyEquivalent="v" id="gVA-U4-sdL">
+                                <connections>
+                                    <action selector="paste:" target="-1" id="UvS-8e-Qdg"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Paste and Match Style" keyEquivalent="V" id="WeT-3V-zwk">
+                                <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+                                <connections>
+                                    <action selector="pasteAsPlainText:" target="-1" id="cEh-KX-wJQ"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Delete" id="pa3-QI-u2k">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <connections>
+                                    <action selector="delete:" target="-1" id="0Mk-Ml-PaM"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Select All" keyEquivalent="a" id="Ruw-6m-B2m">
+                                <connections>
+                                    <action selector="selectAll:" target="-1" id="VNm-Mi-diN"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem isSeparatorItem="YES" id="uyl-h8-XO2"/>
+                            <menuItem title="Find" id="4EN-yA-p0u">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <menu key="submenu" title="Find" id="1b7-l0-nxx">
+                                    <items>
+                                        <menuItem title="Find…" tag="1" keyEquivalent="f" id="Xz5-n4-O0W">
+                                            <connections>
+                                                <action selector="performFindPanelAction:" target="-1" id="cD7-Qs-BN4"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Find and Replace…" tag="12" keyEquivalent="f" id="YEy-JH-Tfz">
+                                            <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+                                            <connections>
+                                                <action selector="performFindPanelAction:" target="-1" id="WD3-Gg-5AJ"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Find Next" tag="2" keyEquivalent="g" id="q09-fT-Sye">
+                                            <connections>
+                                                <action selector="performFindPanelAction:" target="-1" id="NDo-RZ-v9R"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Find Previous" tag="3" keyEquivalent="G" id="OwM-mh-QMV">
+                                            <connections>
+                                                <action selector="performFindPanelAction:" target="-1" id="HOh-sY-3ay"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Use Selection for Find" tag="7" keyEquivalent="e" id="buJ-ug-pKt">
+                                            <connections>
+                                                <action selector="performFindPanelAction:" target="-1" id="U76-nv-p5D"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Jump to Selection" keyEquivalent="j" id="S0p-oC-mLd">
+                                            <connections>
+                                                <action selector="centerSelectionInVisibleArea:" target="-1" id="IOG-6D-g5B"/>
+                                            </connections>
+                                        </menuItem>
+                                    </items>
+                                </menu>
+                            </menuItem>
+                            <menuItem title="Spelling and Grammar" id="Dv1-io-Yv7">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <menu key="submenu" title="Spelling" id="3IN-sU-3Bg">
+                                    <items>
+                                        <menuItem title="Show Spelling and Grammar" keyEquivalent=":" id="HFo-cy-zxI">
+                                            <connections>
+                                                <action selector="showGuessPanel:" target="-1" id="vFj-Ks-hy3"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Check Document Now" keyEquivalent=";" id="hz2-CU-CR7">
+                                            <connections>
+                                                <action selector="checkSpelling:" target="-1" id="fz7-VC-reM"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem isSeparatorItem="YES" id="bNw-od-mp5"/>
+                                        <menuItem title="Check Spelling While Typing" id="rbD-Rh-wIN">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="toggleContinuousSpellChecking:" target="-1" id="7w6-Qz-0kB"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Check Grammar With Spelling" id="mK6-2p-4JG">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="toggleGrammarChecking:" target="-1" id="muD-Qn-j4w"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Correct Spelling Automatically" id="78Y-hA-62v">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="toggleAutomaticSpellingCorrection:" target="-1" id="2lM-Qi-WAP"/>
+                                            </connections>
+                                        </menuItem>
+                                    </items>
+                                </menu>
+                            </menuItem>
+                            <menuItem title="Substitutions" id="9ic-FL-obx">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <menu key="submenu" title="Substitutions" id="FeM-D8-WVr">
+                                    <items>
+                                        <menuItem title="Show Substitutions" id="z6F-FW-3nz">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="orderFrontSubstitutionsPanel:" target="-1" id="oku-mr-iSq"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem isSeparatorItem="YES" id="gPx-C9-uUO"/>
+                                        <menuItem title="Smart Copy/Paste" id="9yt-4B-nSM">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="toggleSmartInsertDelete:" target="-1" id="3IJ-Se-DZD"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Smart Quotes" id="hQb-2v-fYv">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="toggleAutomaticQuoteSubstitution:" target="-1" id="ptq-xd-QOA"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Smart Dashes" id="rgM-f4-ycn">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="toggleAutomaticDashSubstitution:" target="-1" id="oCt-pO-9gS"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Smart Links" id="cwL-P1-jid">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="toggleAutomaticLinkDetection:" target="-1" id="Gip-E3-Fov"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Data Detectors" id="tRr-pd-1PS">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="toggleAutomaticDataDetection:" target="-1" id="R1I-Nq-Kbl"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Text Replacement" id="HFQ-gK-NFA">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="toggleAutomaticTextReplacement:" target="-1" id="DvP-Fe-Py6"/>
+                                            </connections>
+                                        </menuItem>
+                                    </items>
+                                </menu>
+                            </menuItem>
+                            <menuItem title="Transformations" id="2oI-Rn-ZJC">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <menu key="submenu" title="Transformations" id="c8a-y6-VQd">
+                                    <items>
+                                        <menuItem title="Make Upper Case" id="vmV-6d-7jI">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="uppercaseWord:" target="-1" id="sPh-Tk-edu"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Make Lower Case" id="d9M-CD-aMd">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="lowercaseWord:" target="-1" id="iUZ-b5-hil"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Capitalize" id="UEZ-Bs-lqG">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="capitalizeWord:" target="-1" id="26H-TL-nsh"/>
+                                            </connections>
+                                        </menuItem>
+                                    </items>
+                                </menu>
+                            </menuItem>
+                            <menuItem title="Speech" id="xrE-MZ-jX0">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <menu key="submenu" title="Speech" id="3rS-ZA-NoH">
+                                    <items>
+                                        <menuItem title="Start Speaking" id="Ynk-f8-cLZ">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="startSpeaking:" target="-1" id="654-Ng-kyl"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Stop Speaking" id="Oyz-dy-DGm">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="stopSpeaking:" target="-1" id="dX8-6p-jy9"/>
+                                            </connections>
+                                        </menuItem>
+                                    </items>
+                                </menu>
+                            </menuItem>
+                        </items>
+                    </menu>
+                </menuItem>
+                <menuItem title="Format" id="jxT-CU-nIS">
+                    <modifierMask key="keyEquivalentModifierMask"/>
+                    <menu key="submenu" title="Format" id="GEO-Iw-cKr">
+                        <items>
+                            <menuItem title="Font" id="Gi5-1S-RQB">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <menu key="submenu" title="Font" systemMenu="font" id="aXa-aM-Jaq">
+                                    <items>
+                                        <menuItem title="Show Fonts" keyEquivalent="t" id="Q5e-8K-NDq">
+                                            <connections>
+                                                <action selector="orderFrontFontPanel:" target="YLy-65-1bz" id="WHr-nq-2xA"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Bold" tag="2" keyEquivalent="b" id="GB9-OM-e27">
+                                            <connections>
+                                                <action selector="addFontTrait:" target="YLy-65-1bz" id="hqk-hr-sYV"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Italic" tag="1" keyEquivalent="i" id="Vjx-xi-njq">
+                                            <connections>
+                                                <action selector="addFontTrait:" target="YLy-65-1bz" id="IHV-OB-c03"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Underline" keyEquivalent="u" id="WRG-CD-K1S">
+                                            <connections>
+                                                <action selector="underline:" target="-1" id="FYS-2b-JAY"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem isSeparatorItem="YES" id="5gT-KC-WSO"/>
+                                        <menuItem title="Bigger" tag="3" keyEquivalent="+" id="Ptp-SP-VEL">
+                                            <connections>
+                                                <action selector="modifyFont:" target="YLy-65-1bz" id="Uc7-di-UnL"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Smaller" tag="4" keyEquivalent="-" id="i1d-Er-qST">
+                                            <connections>
+                                                <action selector="modifyFont:" target="YLy-65-1bz" id="HcX-Lf-eNd"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem isSeparatorItem="YES" id="kx3-Dk-x3B"/>
+                                        <menuItem title="Kern" id="jBQ-r6-VK2">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <menu key="submenu" title="Kern" id="tlD-Oa-oAM">
+                                                <items>
+                                                    <menuItem title="Use Default" id="GUa-eO-cwY">
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <connections>
+                                                            <action selector="useStandardKerning:" target="-1" id="6dk-9l-Ckg"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                    <menuItem title="Use None" id="cDB-IK-hbR">
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <connections>
+                                                            <action selector="turnOffKerning:" target="-1" id="U8a-gz-Maa"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                    <menuItem title="Tighten" id="46P-cB-AYj">
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <connections>
+                                                            <action selector="tightenKerning:" target="-1" id="hr7-Nz-8ro"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                    <menuItem title="Loosen" id="ogc-rX-tC1">
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <connections>
+                                                            <action selector="loosenKerning:" target="-1" id="8i4-f9-FKE"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                </items>
+                                            </menu>
+                                        </menuItem>
+                                        <menuItem title="Ligatures" id="o6e-r0-MWq">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <menu key="submenu" title="Ligatures" id="w0m-vy-SC9">
+                                                <items>
+                                                    <menuItem title="Use Default" id="agt-UL-0e3">
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <connections>
+                                                            <action selector="useStandardLigatures:" target="-1" id="7uR-wd-Dx6"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                    <menuItem title="Use None" id="J7y-lM-qPV">
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <connections>
+                                                            <action selector="turnOffLigatures:" target="-1" id="iX2-gA-Ilz"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                    <menuItem title="Use All" id="xQD-1f-W4t">
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <connections>
+                                                            <action selector="useAllLigatures:" target="-1" id="KcB-kA-TuK"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                </items>
+                                            </menu>
+                                        </menuItem>
+                                        <menuItem title="Baseline" id="OaQ-X3-Vso">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <menu key="submenu" title="Baseline" id="ijk-EB-dga">
+                                                <items>
+                                                    <menuItem title="Use Default" id="3Om-Ey-2VK">
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <connections>
+                                                            <action selector="unscript:" target="-1" id="0vZ-95-Ywn"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                    <menuItem title="Superscript" id="Rqc-34-cIF">
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <connections>
+                                                            <action selector="superscript:" target="-1" id="3qV-fo-wpU"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                    <menuItem title="Subscript" id="I0S-gh-46l">
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <connections>
+                                                            <action selector="subscript:" target="-1" id="Q6W-4W-IGz"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                    <menuItem title="Raise" id="2h7-ER-AoG">
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <connections>
+                                                            <action selector="raiseBaseline:" target="-1" id="4sk-31-7Q9"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                    <menuItem title="Lower" id="1tx-W0-xDw">
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <connections>
+                                                            <action selector="lowerBaseline:" target="-1" id="OF1-bc-KW4"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                </items>
+                                            </menu>
+                                        </menuItem>
+                                        <menuItem isSeparatorItem="YES" id="Ndw-q3-faq"/>
+                                        <menuItem title="Show Colors" keyEquivalent="C" id="bgn-CT-cEk">
+                                            <connections>
+                                                <action selector="orderFrontColorPanel:" target="-1" id="mSX-Xz-DV3"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem isSeparatorItem="YES" id="iMs-zA-UFJ"/>
+                                        <menuItem title="Copy Style" keyEquivalent="c" id="5Vv-lz-BsD">
+                                            <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+                                            <connections>
+                                                <action selector="copyFont:" target="-1" id="GJO-xA-L4q"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Paste Style" keyEquivalent="v" id="vKC-jM-MkH">
+                                            <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+                                            <connections>
+                                                <action selector="pasteFont:" target="-1" id="JfD-CL-leO"/>
+                                            </connections>
+                                        </menuItem>
+                                    </items>
+                                </menu>
+                            </menuItem>
+                            <menuItem title="Text" id="Fal-I4-PZk">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <menu key="submenu" title="Text" id="d9c-me-L2H">
+                                    <items>
+                                        <menuItem title="Align Left" keyEquivalent="{" id="ZM1-6Q-yy1">
+                                            <connections>
+                                                <action selector="alignLeft:" target="-1" id="zUv-R1-uAa"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Center" keyEquivalent="|" id="VIY-Ag-zcb">
+                                            <connections>
+                                                <action selector="alignCenter:" target="-1" id="spX-mk-kcS"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Justify" id="J5U-5w-g23">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="alignJustified:" target="-1" id="ljL-7U-jND"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Align Right" keyEquivalent="}" id="wb2-vD-lq4">
+                                            <connections>
+                                                <action selector="alignRight:" target="-1" id="r48-bG-YeY"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem isSeparatorItem="YES" id="4s2-GY-VfK"/>
+                                        <menuItem title="Writing Direction" id="H1b-Si-o9J">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <menu key="submenu" title="Writing Direction" id="8mr-sm-Yjd">
+                                                <items>
+                                                    <menuItem title="Paragraph" enabled="NO" id="ZvO-Gk-QUH">
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                    </menuItem>
+                                                    <menuItem id="YGs-j5-SAR">
+                                                        <string key="title">	Default</string>
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <connections>
+                                                            <action selector="makeBaseWritingDirectionNatural:" target="-1" id="qtV-5e-UBP"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                    <menuItem id="Lbh-J2-qVU">
+                                                        <string key="title">	Left to Right</string>
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <connections>
+                                                            <action selector="makeBaseWritingDirectionLeftToRight:" target="-1" id="S0X-9S-QSf"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                    <menuItem id="jFq-tB-4Kx">
+                                                        <string key="title">	Right to Left</string>
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <connections>
+                                                            <action selector="makeBaseWritingDirectionRightToLeft:" target="-1" id="5fk-qB-AqJ"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                    <menuItem isSeparatorItem="YES" id="swp-gr-a21"/>
+                                                    <menuItem title="Selection" enabled="NO" id="cqv-fj-IhA">
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                    </menuItem>
+                                                    <menuItem id="Nop-cj-93Q">
+                                                        <string key="title">	Default</string>
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <connections>
+                                                            <action selector="makeTextWritingDirectionNatural:" target="-1" id="lPI-Se-ZHp"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                    <menuItem id="BgM-ve-c93">
+                                                        <string key="title">	Left to Right</string>
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <connections>
+                                                            <action selector="makeTextWritingDirectionLeftToRight:" target="-1" id="caW-Bv-w94"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                    <menuItem id="RB4-Sm-HuC">
+                                                        <string key="title">	Right to Left</string>
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <connections>
+                                                            <action selector="makeTextWritingDirectionRightToLeft:" target="-1" id="EXD-6r-ZUu"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                </items>
+                                            </menu>
+                                        </menuItem>
+                                        <menuItem isSeparatorItem="YES" id="fKy-g9-1gm"/>
+                                        <menuItem title="Show Ruler" id="vLm-3I-IUL">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="toggleRuler:" target="-1" id="FOx-HJ-KwY"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Copy Ruler" keyEquivalent="c" id="MkV-Pr-PK5">
+                                            <modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
+                                            <connections>
+                                                <action selector="copyRuler:" target="-1" id="71i-fW-3W2"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Paste Ruler" keyEquivalent="v" id="LVM-kO-fVI">
+                                            <modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
+                                            <connections>
+                                                <action selector="pasteRuler:" target="-1" id="cSh-wd-qM2"/>
+                                            </connections>
+                                        </menuItem>
+                                    </items>
+                                </menu>
+                            </menuItem>
+                        </items>
+                    </menu>
+                </menuItem>
+                <menuItem title="View" id="H8h-7b-M4v">
+                    <modifierMask key="keyEquivalentModifierMask"/>
+                    <menu key="submenu" title="View" id="HyV-fh-RgO">
+                        <items>
+                            <menuItem title="Show Toolbar" keyEquivalent="t" id="snW-S8-Cw5">
+                                <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+                                <connections>
+                                    <action selector="toggleToolbarShown:" target="-1" id="BXY-wc-z0C"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Customize Toolbar…" id="1UK-8n-QPP">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <connections>
+                                    <action selector="runToolbarCustomizationPalette:" target="-1" id="pQI-g3-MTW"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem isSeparatorItem="YES" id="hB3-LF-h0Y"/>
+                            <menuItem title="Show Sidebar" keyEquivalent="s" id="kIP-vf-haE">
+                                <modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
+                                <connections>
+                                    <action selector="toggleSourceList:" target="-1" id="iwa-gc-5KM"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Enter Full Screen" keyEquivalent="f" id="4J7-dP-txa">
+                                <modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
+                                <connections>
+                                    <action selector="toggleFullScreen:" target="-1" id="dU3-MA-1Rq"/>
+                                </connections>
+                            </menuItem>
+                        </items>
+                    </menu>
+                </menuItem>
+                <menuItem title="Window" id="aUF-d1-5bR">
+                    <modifierMask key="keyEquivalentModifierMask"/>
+                    <menu key="submenu" title="Window" systemMenu="window" id="Td7-aD-5lo">
+                        <items>
+                            <menuItem title="Minimize" keyEquivalent="m" id="OY7-WF-poV">
+                                <connections>
+                                    <action selector="performMiniaturize:" target="-1" id="VwT-WD-YPe"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Zoom" id="R4o-n2-Eq4">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <connections>
+                                    <action selector="performZoom:" target="-1" id="DIl-cC-cCs"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem isSeparatorItem="YES" id="eu3-7i-yIM"/>
+                            <menuItem title="Bring All to Front" id="LE2-aR-0XJ">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <connections>
+                                    <action selector="arrangeInFront:" target="-1" id="DRN-fu-gQh"/>
+                                </connections>
+                            </menuItem>
+                        </items>
+                    </menu>
+                </menuItem>
+                <menuItem title="Help" id="wpr-3q-Mcd">
+                    <modifierMask key="keyEquivalentModifierMask"/>
+                    <menu key="submenu" title="Help" systemMenu="help" id="F2S-fz-NVQ">
+                        <items>
+                            <menuItem title="nitoTV4Installer Help" keyEquivalent="?" id="FKE-Sm-Kum">
+                                <connections>
+                                    <action selector="showHelp:" target="-1" id="y7X-2Q-9no"/>
+                                </connections>
+                            </menuItem>
+                        </items>
+                    </menu>
+                </menuItem>
+            </items>
+            <point key="canvasLocation" x="127" y="415"/>
+        </menu>
+        <window title="nitoTV4Installer" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" animationBehavior="default" id="QvC-M9-y7g">
+            <windowStyleMask key="styleMask" titled="YES" miniaturizable="YES"/>
+            <windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
+            <rect key="contentRect" x="335" y="390" width="442" height="235"/>
+            <rect key="screenRect" x="0.0" y="0.0" width="1920" height="1177"/>
+            <view key="contentView" wantsLayer="YES" id="EiT-Mj-1SZ">
+                <rect key="frame" x="0.0" y="0.0" width="442" height="235"/>
+                <autoresizingMask key="autoresizingMask"/>
+                <subviews>
+                    <popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="fTc-TT-leC">
+                        <rect key="frame" x="89" y="167" width="234" height="26"/>
+                        <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
+                        <popUpButtonCell key="cell" type="push" title="Item 1" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="qTn-1Y-yTu" id="jYa-Xg-NVI">
+                            <behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
+                            <font key="font" metaFont="menu"/>
+                            <menu key="menu" id="can-0J-0Wk">
+                                <items>
+                                    <menuItem title="Item 1" state="on" id="qTn-1Y-yTu"/>
+                                    <menuItem title="Item 2" id="ezJ-JA-vNy"/>
+                                    <menuItem title="Item 3" id="XSn-1B-tj1"/>
+                                </items>
+                            </menu>
+                        </popUpButtonCell>
+                        <connections>
+                            <action selector="menuItemSelected:" target="fuO-f1-kJ3" id="y5x-9h-NYV"/>
+                            <binding destination="Tvm-Nd-BiA" name="content" keyPath="arrangedObjects.name" id="MiJ-e7-7M8"/>
+                            <binding destination="cE9-50-YeO" name="selectedValue" keyPath="values.selectedValue" previousBinding="MiJ-e7-7M8" id="Bhw-VB-hG4"/>
+                        </connections>
+                    </popUpButton>
+                    <button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="OHh-hg-rQh">
+                        <rect key="frame" x="142" y="44" width="127" height="32"/>
+                        <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
+                        <buttonCell key="cell" type="push" title="Install Payload" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="BEr-UM-Nwz">
+                            <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
+                            <font key="font" metaFont="system"/>
+                        </buttonCell>
+                        <connections>
+                            <action selector="doIt:" target="Voe-Tx-rLC" id="itD-oF-AYl"/>
+                            <binding destination="Voe-Tx-rLC" name="enabled" keyPath="self.atvAvailable" id="qJM-9a-Iab"/>
+                        </connections>
+                    </button>
+                    <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="rmB-Fy-pOd">
+                        <rect key="frame" x="73" y="135" width="264" height="17"/>
+                        <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
+                        <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="center" id="hH5-bI-NQ4">
+                            <font key="font" metaFont="system"/>
+                            <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
+                            <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
+                        </textFieldCell>
+                    </textField>
+                    <progressIndicator wantsLayer="YES" horizontalHuggingPriority="750" verticalHuggingPriority="750" fixedFrame="YES" maxValue="100" displayedWhenStopped="NO" bezeled="NO" indeterminate="YES" controlSize="small" style="spinning" translatesAutoresizingMaskIntoConstraints="NO" id="foy-RA-rvh">
+                        <rect key="frame" x="195" y="101" width="16" height="16"/>
+                        <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
+                    </progressIndicator>
+                </subviews>
+            </view>
+            <point key="canvasLocation" x="120" y="215.5"/>
+        </window>
+        <userDefaultsController representsSharedInstance="YES" id="cE9-50-YeO"/>
+    </objects>
+</document>

+ 28 - 0
nitoTV4Installer/Defines.h

@@ -0,0 +1,28 @@
+//
+//  Defines.h
+//  iReSign
+//
+//  Created by Kevin Bradley on 5/19/16.
+//  Copyright © 2016 nil. All rights reserved.
+//
+
+#ifndef Defines_h
+#define Defines_h
+
+#define DLog(format, ...) CFShow((__bridge CFStringRef)[NSString stringWithFormat:format, ## __VA_ARGS__]);
+#define LOG_SELF        NSLog(@"%@ %@", self, NSStringFromSelector(_cmd))
+#define DLOG_SELF DLog(@"%@ %@", self, NSStringFromSelector(_cmd))
+
+#define DEFAULTS [NSUserDefaults standardUserDefaults]
+#define FM [NSFileManager defaultManager]
+#define ATV_HOST @"appleTVHost"
+#define ATV_API  @"atvAPIVersion"
+#define ATV_OS	 @"atvOSVersion"
+#define ATV4Install @"atv4Install"
+#define APPLE_TV_ADDRESS [DEFAULTS stringForKey:ATV_HOST]
+#define INSTALL_ON_ATV [DEFAULTS boolForKey:ATV4Install]
+#define APPLE_TV_API [DEFAULTS stringForKey:ATV_API]
+#define APPLE_TV_OS [DEFAULTS stringForKey:ATV_OS]
+#define SELECTED_VALUE [DEFAULTS stringForKey:@"selectedValue"]
+
+#endif /* Defines_h */

+ 175 - 0
nitoTV4Installer/EMKeychainItem.h

@@ -0,0 +1,175 @@
+/*Copyright (c) 2009 Extendmac, LLC. <support@extendmac.com>
+ 
+ Permission is hereby granted, free of charge, to any person
+ obtaining a copy of this software and associated documentation
+ files (the "Software"), to deal in the Software without
+ restriction, including without limitation the rights to use,
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following
+ conditions:
+ 
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+ 
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+//Last Updated February 8th, 2011.
+
+
+#import <Cocoa/Cocoa.h>
+#import <Carbon/Carbon.h>
+#import <Security/Security.h>
+
+/*!
+	@abstract EMKeychainItem is a self-contained wrapper class for two-way communication with the keychain. You can add, retrieve, and remove both generic and internet keychain items.
+	@dicussion All keychain items have a username, password, and optionally a label.
+ */
+@interface EMKeychainItem : NSObject 
+{
+	@private
+	NSString *mUsername;
+	NSString *mPassword;
+	NSString *mLabel;
+	
+	@protected
+	SecKeychainItemRef mCoreKeychainItem;
+}
+
+/*!
+ @abstract Returns whether or not errors are logged.
+ @discussion Errors occur whenever a keychain item fails to appropriately update a property, or when a given keychain item cannot be found.
+ */
++ (BOOL)logsErrors;
+
+//! @abstracts Sets whether or not errors are logged.
++ (void)setLogsErrors:(BOOL)logsErrors;
+
+//! @abstracts Locks the keychain.
++ (void)lockKeychain;
+
+//! @abstract Unlocks the keychain.
++ (void)unlockKeychain;
+
+//! @abstract The keychain item's username.
+@property (readwrite, copy) NSString *username;
+
+//! @abstract The keychain item's password.
+@property (readwrite, copy) NSString *password;
+
+//! @abstract The keychain item's label.
+@property (readwrite, copy) NSString *label;
+
+/*!
+	@abstract Removes the receiver from the keychain.
+	@discussion After calling this method, you should generally discard of the receiver. The receiver cannot be "re-added" to the keychain; invoke either addGenericKeychainItemForService:... or addInternetKeychainItemForServer:... instead.
+ */
+- (void)removeFromKeychain;
+
+@end 
+
+#pragma mark -
+
+/*!
+	@abstract An EMGenericKeychainItem wraps the functionality and data-members associated with a generic keychain item.
+	@discussion Generic keychain items have a service name in addition to the standard keychain item properties.
+ */
+@interface EMGenericKeychainItem : EMKeychainItem
+{
+	@private
+	NSString *mServiceName;
+}
+
+//! @abstract The keychain item's service name.
+@property (readwrite, copy) NSString *serviceName;
+
+/*!
+	@abstract Returns, if possible, a generic keychain item that corresponds to the given service.
+	@param serviceName The service name. Cannot be nil.
+	@param username The username. Cannot be nil.
+	@result An EMGenericKeychainItem if the keychain item can be discovered. Otherwise, nil.
+ */
++ (EMGenericKeychainItem *)genericKeychainItemForService:(NSString *)serviceName
+											withUsername:(NSString *)username;
+
+/*!
+	@abstract Adds a keychain item for the given service.
+	@param serviceName The service name. Cannot be nil.
+	@param username The username. Cannot be nil.
+	@param password The password to associate with the username and service. Cannot be nil.
+	@result An EMGenericKeychainItem if the service can be added to the keychain. Otherwise, nil.
+ */
++ (EMGenericKeychainItem *)addGenericKeychainItemForService:(NSString *)serviceName
+											   withUsername:(NSString *)username
+												   password:(NSString *)password;
+@end
+
+#pragma mark -
+
+/*!
+	@abstract An EMInternetKeychainItem wraps the functionality and data-members associated with an internet keychain item.
+	@discussion Internet keychain items can optionally have a server, path, port, and protocol in addition to the standard keychain item properties.
+ */
+@interface EMInternetKeychainItem : EMKeychainItem
+{
+	@private
+	NSString *mServer;
+	NSString *mPath;
+	NSInteger mPort;
+	SecProtocolType mProtocol;
+}
+
+
+/*!
+	@abstract Returns, if possible, an internet keychain item that corresponds to the given server.
+	@param server The server. Cannot be nil.
+	@param username The username. Cannot be nil.
+	@param path The path.
+	@param port The port.
+	@param protocol The protocol.
+	@result An EMInternetKeychainItem if the keychain item can be discovered. Otherwise, nil.
+ */
++ (EMInternetKeychainItem *)internetKeychainItemForServer:(NSString *)server
+											 withUsername:(NSString *)username
+													 path:(NSString *)path
+													 port:(NSInteger)port
+												 protocol:(SecProtocolType)protocol;
+
+/*!
+	@abstract Adds a keychain item for the given server.
+	@param server The server. Cannot be nil.
+	@param username The username. Cannot be nil.
+	@param password The password to associate with the server, username, path, port, and protocol. Cannot be nil.
+	@param path The path.
+	@param port The port.
+	@param protocol The protocol.
+	@result An EMInternetKeychainItem if the item can be added to the keychain. Otherwise, nil.
+ */
++ (EMInternetKeychainItem *)addInternetKeychainItemForServer:(NSString *)server
+												withUsername:(NSString *)username
+													password:(NSString *)password
+														path:(NSString *)path
+														port:(NSInteger)port
+													protocol:(SecProtocolType)protocol;
+
+//! @abstract The keychain item's server.
+@property (readwrite, copy) NSString *server;
+
+//! @abstract The keychain item's path.
+@property (readwrite, copy) NSString *path;
+
+//! @abstract The keychain item's port.
+@property (readwrite, assign) NSInteger port;
+
+//! @abstract The keychain item's protocol.
+@property (readwrite, assign) SecProtocolType protocol;
+
+@end

+ 539 - 0
nitoTV4Installer/EMKeychainItem.m

@@ -0,0 +1,539 @@
+/*Copyright (c) 2009 Extendmac, LLC. <support@extendmac.com>
+ 
+ Permission is hereby granted, free of charge, to any person
+ obtaining a copy of this software and associated documentation
+ files (the "Software"), to deal in the Software without
+ restriction, including without limitation the rights to use,
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following
+ conditions:
+ 
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+ 
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#import "EMKeychainItem.h"
+
+@interface EMKeychainItem (Private)
+
+/*!
+	@abstract Modifies the given attribute to be newValue.
+	@param attributeTag The attribute's tag.
+	@param newValue A pointer to the new value.
+	@param newLength The length of the new value.
+*/	
+- (void)_modifyAttributeWithTag:(SecItemAttr)attributeTag toBeValue:(void *)newValue ofLength:(UInt32)newLength;
+
+@end
+
+@implementation EMKeychainItem
+
+static BOOL _logsErrors;
+
++ (void)lockKeychain
+{
+	SecKeychainLock(NULL);
+}
+
++ (void)unlockKeychain
+{
+	SecKeychainUnlock(NULL, 0, NULL, NO);
+}
+
++ (BOOL)logsErrors
+{
+	@synchronized (self)
+	{
+		return _logsErrors;
+	}
+	return NO;
+}
+
++ (void)setLogsErrors:(BOOL)logsErrors
+{
+	@synchronized (self)
+	{
+		if (_logsErrors == logsErrors)
+			return;
+		
+		_logsErrors = logsErrors;
+	}
+}
+
+#pragma mark -
+
+- (id)_initWithCoreKeychainItem:(SecKeychainItemRef)item
+					   username:(NSString *)username
+					   password:(NSString *)password
+{
+	if ((self = [super init]))
+	{
+		mCoreKeychainItem = item;
+		mUsername = [username copy];
+		mPassword = [password copy];
+		
+		return self;
+	}
+	return nil;
+}
+
+- (void)_modifyAttributeWithTag:(SecItemAttr)attributeTag toBeValue:(void *)newValue ofLength:(UInt32)newLength
+{
+	NSAssert(mCoreKeychainItem, @"Core keychain item is nil. You cannot modify a keychain item that is not in the keychain.");
+	
+	SecKeychainAttribute attributes[1];
+	attributes[0].tag = attributeTag;
+	attributes[0].length = newLength;
+	attributes[0].data = newValue;
+	
+	SecKeychainAttributeList attributeList;
+	attributeList.count = 1;
+	attributeList.attr = attributes;
+	
+	SecKeychainItemModifyAttributesAndData(mCoreKeychainItem, &attributeList, 0, NULL);
+}
+
+- (void)dealloc
+{
+    /*
+	[mUsername release];
+	[mPassword release];
+	[mLabel release];
+     */
+	
+	if (mCoreKeychainItem)
+		CFRelease(mCoreKeychainItem);
+	
+	//[super dealloc];
+}
+
+#pragma mark -
+#pragma mark General Properties
+@dynamic password;
+- (NSString *)password
+{
+	@synchronized (self)
+	{
+		return mPassword;
+	}
+}
+
+- (void)setPassword:(NSString *)newPassword
+{
+	@synchronized (self)
+	{
+		if (mPassword == newPassword)
+			return;
+		
+		mPassword = newPassword;
+		
+		const char *newPasswordCString = [newPassword UTF8String];
+		SecKeychainItemModifyAttributesAndData(mCoreKeychainItem, NULL, strlen(newPasswordCString), (void *)newPasswordCString);
+	}
+}
+
+#pragma mark -
+@dynamic username;
+- (NSString *)username
+{
+	@synchronized (self)
+	{
+		return mUsername;
+	}
+}
+
+- (void)setUsername:(NSString *)newUsername
+{
+	@synchronized (self)
+	{
+		if (mUsername == newUsername)
+			return;
+		
+	//	[mUsername release];
+		mUsername = newUsername;
+		
+		const char *newUsernameCString = [newUsername UTF8String];
+		[self _modifyAttributeWithTag:kSecAccountItemAttr toBeValue:(void *)newUsernameCString ofLength:strlen(newUsernameCString)];
+	}
+}
+
+#pragma mark -
+@dynamic label;
+- (NSString *)label
+{
+	@synchronized (self)
+	{
+		return mLabel;
+	}
+}
+
+- (void)setLabel:(NSString *)newLabel
+{
+	@synchronized (self)
+	{
+		if (mLabel == newLabel)
+			return;
+		
+		//[mLabel release];
+		mLabel = newLabel;
+		
+		const char *newLabelCString = [newLabel UTF8String];
+		[self _modifyAttributeWithTag:kSecLabelItemAttr toBeValue:(void *)newLabelCString ofLength:strlen(newLabelCString)];
+	}
+}
+
+#pragma mark -
+#pragma mark Actions
+- (void)removeFromKeychain
+{
+	NSAssert(mCoreKeychainItem, @"Core keychain item is nil. You cannot remove a keychain item that is not in the keychain already.");
+	
+	if (mCoreKeychainItem)
+	{
+		OSStatus resultStatus = SecKeychainItemDelete(mCoreKeychainItem);
+		if (resultStatus == noErr)
+		{
+			CFRelease(mCoreKeychainItem);
+			mCoreKeychainItem = nil;
+		}
+	}
+}
+
+@end
+
+#pragma mark -
+@implementation EMGenericKeychainItem
+
+- (id)_initWithCoreKeychainItem:(SecKeychainItemRef)item
+					serviceName:(NSString *)serviceName
+					   username:(NSString *)username
+					   password:(NSString *)password
+{
+	if ((self = [super _initWithCoreKeychainItem:item username:username password:password]))
+	{
+		mServiceName = [serviceName copy];
+		return self;
+	}
+	return nil;
+}
+
++ (id)_genericKeychainItemWithCoreKeychainItem:(SecKeychainItemRef)coreKeychainItem 
+								forServiceName:(NSString *)serviceName
+									  username:(NSString *)username
+									  password:(NSString *)password
+{
+    return [[EMGenericKeychainItem alloc] _initWithCoreKeychainItem:coreKeychainItem
+                                                        serviceName:serviceName
+                                                           username:username
+                                                           password:password];
+}
+
+- (void)dealloc
+{
+	//[mServiceName release];
+
+	//[super dealloc];
+}
+
+#pragma mark -
++ (EMGenericKeychainItem *)genericKeychainItemForService:(NSString *)serviceName 
+											withUsername:(NSString *)username
+{
+	if (!serviceName || !username)
+		return nil;
+	
+	const char *serviceNameCString = [serviceName UTF8String];
+	const char *usernameCString = [username UTF8String];
+	
+	UInt32 passwordLength = 0;
+	char *password = nil;
+	
+	SecKeychainItemRef item = nil;
+	OSStatus returnStatus = SecKeychainFindGenericPassword(NULL, strlen(serviceNameCString), serviceNameCString, strlen(usernameCString), usernameCString, &passwordLength, (void **)&password, &item);
+	if (returnStatus != noErr || !item)
+	{
+		if (_logsErrors)
+			NSLog(@"Error (%@) - %s", NSStringFromSelector(_cmd), GetMacOSStatusErrorString(returnStatus));
+		return nil;
+	}
+	NSString *passwordString = [[NSString alloc] initWithData:[NSData dataWithBytes:password length:passwordLength] encoding:NSUTF8StringEncoding];
+	SecKeychainItemFreeContent(NULL, password);
+	
+	return [EMGenericKeychainItem _genericKeychainItemWithCoreKeychainItem:item forServiceName:serviceName username:username password:passwordString];
+}
+
++ (EMGenericKeychainItem *)addGenericKeychainItemForService:(NSString *)serviceName
+											   withUsername:(NSString *)username
+												   password:(NSString *)password
+{
+	if (!serviceName || !username || !password)
+		return nil;
+	
+	const char *serviceNameCString = [serviceName UTF8String];
+	const char *usernameCString = [username UTF8String];
+	const char *passwordCString = [password UTF8String];
+	
+	SecKeychainItemRef item = nil;
+	OSStatus returnStatus = SecKeychainAddGenericPassword(NULL, strlen(serviceNameCString), serviceNameCString, strlen(usernameCString), usernameCString, strlen(passwordCString), (void *)passwordCString, &item);
+	
+	if (returnStatus != noErr || !item)
+	{
+		if (_logsErrors)
+			NSLog(@"Error (%@) - %s", NSStringFromSelector(_cmd), GetMacOSStatusErrorString(returnStatus));
+		return nil;
+	}
+	return [EMGenericKeychainItem _genericKeychainItemWithCoreKeychainItem:item forServiceName:serviceName username:username password:password];
+}
+
+#pragma mark -
+#pragma mark Generic Properties
+@dynamic serviceName;
+- (NSString *)serviceName
+{
+	@synchronized (self)
+	{
+		return mServiceName;
+	}
+}
+
+- (void)setServiceName:(NSString *)newServiceName
+{
+	@synchronized (self)
+	{
+		if (mServiceName == newServiceName)
+			return;
+		
+		mServiceName = newServiceName;
+		
+		const char *newServiceNameCString = [newServiceName UTF8String];
+		[self _modifyAttributeWithTag:kSecServiceItemAttr toBeValue:(void *)newServiceNameCString ofLength:strlen(newServiceNameCString)];
+	}
+}
+
+@end
+
+#pragma mark -
+@implementation EMInternetKeychainItem
+
+- (id)_initWithCoreKeychainItem:(SecKeychainItemRef)item
+						 server:(NSString *)server
+					   username:(NSString *)username
+					   password:(NSString *)password
+						   path:(NSString *)path
+						   port:(NSInteger)port
+					   protocol:(SecProtocolType)protocol
+{
+	if ((self = [super _initWithCoreKeychainItem:item username:username password:password]))
+	{
+		mServer = [server copy];
+		mPath = [path copy];
+		mPort = port;
+		mProtocol = protocol;
+		
+		return self;
+	}
+	return nil;
+}
+
+- (void)dealloc
+{
+	//[mServer release];
+	//[mPath release];
+	
+	//[super dealloc];
+}
+
++ (id)_internetKeychainItemWithCoreKeychainItem:(SecKeychainItemRef)coreKeychainItem
+									  forServer:(NSString *)server
+									   username:(NSString *)username
+									   password:(NSString *)password
+										   path:(NSString *)path
+										   port:(NSInteger)port
+									   protocol:(SecProtocolType)protocol
+{
+    return [[EMInternetKeychainItem alloc] _initWithCoreKeychainItem:coreKeychainItem
+                                                              server:server
+                                                            username:username
+                                                            password:password
+                                                                path:path
+                                                                port:port
+                                                            protocol:protocol];
+}
+
+#pragma mark -
++ (EMInternetKeychainItem *)internetKeychainItemForServer:(NSString *)server
+											 withUsername:(NSString *)username
+													 path:(NSString *)path
+													 port:(NSInteger)port
+												 protocol:(SecProtocolType)protocol
+{
+	if (!server || !username)
+		return nil;
+	
+	const char *serverCString = [server UTF8String];
+	const char *usernameCString = [username UTF8String];
+	const char *pathCString = [path UTF8String];
+	
+	if (!path || [path length] == 0)
+		pathCString = "";
+	
+	UInt32 passwordLength = 0;
+	char *password = nil;
+	
+	SecKeychainItemRef item = nil;
+	//0 is kSecAuthenticationTypeAny
+	OSStatus returnStatus = SecKeychainFindInternetPassword(NULL, strlen(serverCString), serverCString, 0, NULL, strlen(usernameCString), usernameCString, strlen(pathCString), pathCString, port, protocol, 0, &passwordLength, (void **)&password, &item);
+	
+	if (returnStatus != noErr && protocol == kSecProtocolTypeFTP)
+	{
+		//Some clients (like Transmit) still save passwords with kSecProtocolTypeFTPAccount, which was deprecated.  Let's check for that.
+		protocol = kSecProtocolTypeFTPAccount;		
+		returnStatus = SecKeychainFindInternetPassword(NULL, strlen(serverCString), serverCString, 0, NULL, strlen(usernameCString), usernameCString, strlen(pathCString), pathCString, port, protocol, 0, &passwordLength, (void **)&password, &item);
+	}
+	
+	if (returnStatus != noErr || !item)
+	{
+		if (_logsErrors)
+			NSLog(@"Error (%@) - %s", NSStringFromSelector(_cmd), GetMacOSStatusErrorString(returnStatus));
+		return nil;
+	}
+	NSString *passwordString = [[NSString alloc] initWithData:[NSData dataWithBytes:password length:passwordLength] encoding:NSUTF8StringEncoding];
+	SecKeychainItemFreeContent(NULL, password);
+	
+	return [EMInternetKeychainItem _internetKeychainItemWithCoreKeychainItem:item forServer:server username:username password:passwordString path:path port:port protocol:protocol];
+}
+
++ (EMInternetKeychainItem *)addInternetKeychainItemForServer:(NSString *)server
+												withUsername:(NSString *)username
+													password:(NSString *)password
+														path:(NSString *)path
+														port:(NSInteger)port
+													protocol:(SecProtocolType)protocol
+{
+	if (!username || !server || !password)
+		return nil;
+	
+	const char *serverCString = [server UTF8String];
+	const char *usernameCString = [username UTF8String];
+	const char *passwordCString = [password UTF8String];
+	const char *pathCString = [path UTF8String];
+	
+	if (!path || [path length] == 0)
+		pathCString = "";
+	
+	SecKeychainItemRef item = nil;
+	OSStatus returnStatus = SecKeychainAddInternetPassword(NULL, strlen(serverCString), serverCString, 0, NULL, strlen(usernameCString), usernameCString, strlen(pathCString), pathCString, port, protocol, kSecAuthenticationTypeDefault, strlen(passwordCString), (void *)passwordCString, &item);
+	
+	if (returnStatus != noErr || !item)
+	{
+		if (_logsErrors)
+			NSLog(@"Error (%@) - %s", NSStringFromSelector(_cmd), GetMacOSStatusErrorString(returnStatus));
+		return nil;
+	}
+	return [EMInternetKeychainItem _internetKeychainItemWithCoreKeychainItem:item forServer:server username:username password:password path:path port:port protocol:protocol];
+}
+
+#pragma mark -
+#pragma mark Internet Properties
+@dynamic server;
+- (NSString *)server
+{
+	@synchronized (self)
+	{
+		return mServer;
+	}
+}
+
+- (void)setServer:(NSString *)newServer
+{
+	@synchronized (self)
+	{
+		if (mServer == newServer)
+			return;
+		
+		mServer = newServer;
+		
+		const char *newServerCString = [newServer UTF8String];
+		[self _modifyAttributeWithTag:kSecServerItemAttr toBeValue:(void *)newServerCString ofLength:strlen(newServerCString)];
+	}
+}
+
+#pragma mark -
+@dynamic path;
+- (NSString *)path
+{
+	@synchronized (self)
+	{
+		return mPath;
+	}
+}
+
+- (void)setPath:(NSString *)newPath
+{
+	if (mPath == newPath)
+		return;
+	
+	//[mPath release];
+	mPath = newPath;
+	
+	const char *newPathCString = [newPath UTF8String];
+	[self _modifyAttributeWithTag:kSecPathItemAttr toBeValue:(void *)newPathCString ofLength:strlen(newPathCString)];
+}
+
+#pragma mark -
+@dynamic port;
+- (NSInteger)port
+{
+	@synchronized (self)
+	{
+		return mPort;
+	}
+}
+
+- (void)setPort:(NSInteger)newPort
+{
+	@synchronized (self)
+	{
+		if (mPort == newPort)
+			return;
+		
+		mPort = newPort;
+		
+		UInt32 newPortValue = newPort;
+		[self _modifyAttributeWithTag:kSecPortItemAttr toBeValue:&newPortValue ofLength:sizeof(newPortValue)];
+	}
+}
+
+#pragma mark -
+@dynamic protocol;
+- (SecProtocolType)protocol
+{
+	@synchronized (self)
+	{
+		return mProtocol;
+	}
+}
+
+- (void)setProtocol:(SecProtocolType)newProtocol
+{
+	@synchronized (self)
+	{
+		if (mProtocol == newProtocol)
+			return;
+		
+		mProtocol = newProtocol;
+		
+		[self _modifyAttributeWithTag:kSecProtocolItemAttr toBeValue:&newProtocol ofLength:sizeof(newProtocol)];
+	}
+}
+@end

+ 37 - 0
nitoTV4Installer/Info.plist

@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>$(DEVELOPMENT_LANGUAGE)</string>
+	<key>CFBundleExecutable</key>
+	<string>$(EXECUTABLE_NAME)</string>
+	<key>CFBundleIconFile</key>
+	<string></string>
+	<key>CFBundleIdentifier</key>
+	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>$(PRODUCT_NAME)</string>
+	<key>CFBundlePackageType</key>
+	<string>APPL</string>
+	<key>CFBundleShortVersionString</key>
+	<string>1.0</string>
+	<key>CFBundleVersion</key>
+	<string>1</string>
+	<key>LSMinimumSystemVersion</key>
+	<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
+	<key>NSAppTransportSecurity</key>
+	<dict>
+		<key>NSAllowsArbitraryLoads</key>
+		<true/>
+	</dict>
+	<key>NSHumanReadableCopyright</key>
+	<string>Copyright © 2018 Kevin Bradley. All rights reserved.</string>
+	<key>NSMainNibFile</key>
+	<string>MainMenu</string>
+	<key>NSPrincipalClass</key>
+	<string>NSApplication</string>
+</dict>
+</plist>

+ 64 - 0
nitoTV4Installer/KBDownloadFile.h

@@ -0,0 +1,64 @@
+//
+//  KBYTDownloadStream.h
+//  Seas0nPass
+//
+//  Created by Kevin Bradley on 3/9/07.
+//  Copyright 2007 nito, LLC. All rights reserved.
+//
+
+#import <Cocoa/Cocoa.h>
+
+/**
+ 
+ This class is used for downloading files from youtube, it takes care of fixing and multiplexing streams when necessary
+ 
+ feed it a stream into downloadStream method and it will take care of the rest.
+ 
+ */
+
+@protocol KBDownloadFileDelegate
+
+- (void)downloadFinished:(NSString *)downloadFile;
+- (void)downloadFailed:(NSString *)downloadFile;
+- (void)setDownloadProgress:(double)theProgress;
+
+@end
+
+
+@interface KBDownloadFile : NSObject <NSURLDownloadDelegate>  {
+	
+	NSURLDownload				*urlDownload;
+    NSURLResponse				*myResponse;
+	float						bytesReceived;
+	NSString					*downloadLocation;
+	long long					updateFrequency;
+	long long					freq;
+    NSString                    *videoDownloadLocation;
+
+}
+
+@property (strong, atomic) void (^ProgressBlock)(double percentComplete);
+@property (strong, atomic) void (^FancyProgressBlock)(double percentComplete, NSString *status);
+@property (strong, atomic) void (^CompletedBlock)(NSString *downloadedFile);
+
+typedef void(^DownloadProgressBlock)(double percentComplete);
+typedef void(^FancyDownloadProgressBlock)(double percentComplete, NSString *downloadedFile);
+typedef void(^DownloadCompletedBlock)(NSString *downloadedFile);
+
+@property (nonatomic, retain) NSString *downloadLocation;
+@property (readwrite, assign) NSInteger downloadMode; //0 = muxed file, 1 = demuxed tracks
+
+
+
+- (void)downloadFileWithURL:(NSURL *)url
+                 toLocation:(NSString *)dlLocation
+                   progress:(DownloadProgressBlock)progressBlock
+                  completed:(DownloadCompletedBlock)completedBlock;
+
+
+- (long long)updateFrequency;
+- (void)setUpdateFrequency:(long long)newUpdateFrequency;
+- (void)setDownloadResponse:(NSURLResponse *)response;
+- (void)cancel;
+
+@end

+ 153 - 0
nitoTV4Installer/KBDownloadFile.m

@@ -0,0 +1,153 @@
+//
+//  KBYTDownloadStream.m
+//  Seas0nPass
+//
+//  Created by Kevin Bradley on 3/9/07.
+//  Copyright 2007 nito, LLC. All rights reserved.
+//
+
+/*
+ 
+ class adapted from hawkeye's KBYTDownloadStream class for downloading youtube files, largely pruned to remove irrelevant sections + updated to cancel the xfer + remodified/updated to use blocks instead of antiquated delegate methods.
+ 
+ */
+
+#import "KBDownloadFile.h"
+
+
+@implementation KBDownloadFile
+
+@synthesize downloadLocation;
+
+#pragma mark -
+#pragma mark •• URL code
+
+- (void)dealloc
+{
+	downloadLocation = nil;
+}
+
+- (void)cancel
+{
+    NSError *theError = nil;
+	[self download:urlDownload didFailWithError:theError];
+	[urlDownload cancel];
+}
+
+
+- (long long)updateFrequency
+{
+	return updateFrequency;
+}
+
+- (void)setUpdateFrequency:(long long)newUpdateFrequency
+{
+	updateFrequency = newUpdateFrequency;
+}
+
+- (id)init
+{
+	if(self = [super init]) {
+        [self setUpdateFrequency:1];
+        
+	}
+	
+	return self;
+}
+
+
+//deprecated / obsolete, SHOULD still work but should never be used.
+- (void)downloadFileWithURL:(NSURL *)url
+                 toLocation:(NSString *)dlLocation
+                   progress:(DownloadProgressBlock)progressBlock
+                  completed:(DownloadCompletedBlock)completedBlock
+{
+    self.CompletedBlock = completedBlock;
+    self.ProgressBlock = progressBlock;
+    self.downloadLocation = dlLocation;
+    NSURLRequest *theRequest = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];
+    urlDownload = [[NSURLDownload alloc] initWithRequest:theRequest delegate:self];
+    [urlDownload setDestination:downloadLocation allowOverwrite:YES];
+ 
+}
+
+
+- (void)download:(NSURLDownload *)download didFailWithError:(NSError *)error
+
+{
+	NSLog(@"error: %@", error);
+	//[handler downloadFailed:downloadLocation];
+}
+
+- (void)downloadDidFinish:(NSURLDownload *)download
+{
+   if(download == urlDownload) {
+       
+  
+       if (self.CompletedBlock != nil)
+       {
+           self.CompletedBlock(downloadLocation);
+       }
+	}
+
+}
+
+- (void)download:(NSURLDownload *)download didReceiveResponse:(NSURLResponse *)response
+
+{
+    bytesReceived=0;
+    [self setDownloadResponse:response];
+}
+
+- (void)setDownloadResponse:(NSURLResponse *)response
+{
+	myResponse = response;
+}
+
+- (NSURLResponse *)downloadResponse
+{
+    return myResponse;
+}
+
+- (void)download:(NSURLDownload *)download didReceiveDataOfLength:(NSUInteger)length
+
+{
+    long long expectedLength = [[self downloadResponse] expectedContentLength];
+    bytesReceived=bytesReceived+length;
+    
+    if (expectedLength != NSURLResponseUnknownLength) {
+        
+        double percentComplete=(bytesReceived/(float)expectedLength)*100.0;
+       // NSLog(@"Percent complete - %f",percentComplete);
+		
+		if((freq%updateFrequency) == 0){
+	        
+            if (self.ProgressBlock != nil)
+            {
+                self.ProgressBlock(percentComplete);
+            }
+            
+            if (self.FancyProgressBlock != nil)
+            {
+                NSString *mediaType = @"media";
+                NSString *pathExt = downloadLocation.pathExtension;
+                if ([pathExt isEqualToString:@"m4v"])
+                {
+                    mediaType = @"video";
+                } else if ([pathExt isEqualToString:@"aac"])
+                {
+                    mediaType = @"audio";
+                }
+                self.FancyProgressBlock(percentComplete, [NSString stringWithFormat:@"Downloading %@ file...", mediaType]);
+            }
+		}
+		freq++;
+	    
+    } else {
+        
+        NSLog(@"Bytes received - %f",bytesReceived);
+        
+    }
+	
+}
+@end

+ 103 - 0
nitoTV4Installer/ObjSSH/ObjSSH.h

@@ -0,0 +1,103 @@
+#import <Foundation/Foundation.h>
+
+/**
+ * ObjSSH aims to be a full Objective-C wrapper for libssh2, with an API
+ * that is easy to understand and fun to work with.
+ *
+ * To achieve that goal, the library will assume conventions but still
+ * make it easy to override them without writing ugly code.
+ */
+@interface ObjSSH : NSObject {
+    NSString *_host;
+    NSNumber *_port;
+    NSString *_username;
+    NSString *_password;
+    NSString *_privateKey;
+    NSString *_publicKey;
+}
+
+/**
+ * Connect to a remote host with username and password
+ *
+ * Unless otherwise specified in the host parameter, the port is assumed to be
+ * 22. To change port, append ":{portnr}" to the hostname.
+ *
+ * Examples:
+ *
+ *     ObjSSH *ssh = [ObjSSH connectToHost:@"127.0.0.1" withUsername:@"user" password:@"pass" error:&error];
+ *     ObjSSH *ssh2 = [ObjSSH connectToHost:@"127.0.0.1:4567" withUsername:@"user" password:@"pass" error:&error];
+ */
++ (id)connectToHost:(NSString *)host withUsername:(NSString *)username password:(NSString *)password error:(NSError **)error;
+
+/**
+ * Connect to a remote host with username and public/private key pair
+ *
+ * Unless otherwise specified in the host parameter, the port is assumed to be
+ * 22. To change port, append ":{portnr}" to the hostname.
+ *
+ * Examples:
+ *
+ *     ObjSSH *ssh = [ObjSSH connectToHost:@"127.0.0.1" withUsername:@"user" publicKey:@"/home/user/.ssh/id_rsa.pub" privateKey:@"/home/user/.ssh/id_rsa" error:&error];
+ */
++ (id)connectToHost:(NSString *)host withUsername:(NSString *)username publicKey:(NSString *)publicKey privateKey:(NSString *)privateKey error:(NSError **)error;
+
+/**
+ * Initialize ObjSSH and set its instance variables.
+ *
+ * Examples:
+ *
+ *     ObjSSH *ssh = [[ObjSSH alloc] initWithHost:@"127.0.0.1" username:@"user" password:@"pass" publicKey:nil privateKey:nil];
+ */
+- (id)initWithHost:(NSString *)host username:(NSString *)username password:(NSString *)password publicKey:(NSString *)publicKey privateKey:(NSString *)priateKey;
+
+/**
+ * Connect to a remote host. The return value is a boolean indicating whether or
+ * not the connection succeded.
+ *
+ * Examples:
+ *
+ *     NSError *error;
+ *     [ssh connect:&error];
+ */
+- (BOOL)connect:(NSError **)error;
+
+/**
+ * Disconnect from a remote host
+ *
+ * Examples:
+ *
+ *     [ssh disconnect];
+ *     [ssh release];
+ */
+- (void)disconnect;
+
+/**
+ * Execute command in remote shell
+ *
+ * Examples:
+ *
+ *     NSString *response = [ssh execute:@"ls -la" error:&error];
+ */
+- (NSString *)execute:(NSString *)command error:(NSError **)error;
+
+/**
+ * Upload a file to the remote server via SCP.
+ *
+ * Examples:
+ *
+ *     NSError *error;
+ *     BOOL success = [ssh uploadFile:@"/path/to/local.txt" to:@"/path/to/remote.txt" error:&error];
+ */
+- (BOOL)uploadFile:(NSString *)localPath to:(NSString *)remotePath error:(NSError **)error;
+
+/**
+ * Request a file from the remote server via SCP.
+ *
+ * Examples:
+ *
+ *     NSError *error;
+ *     BOOL success = [ssh downloadFile:@"/path/to/remote.txt" to:@"/path/to/local.txt" error:&error];
+ */
+- (BOOL)downloadFile:(NSString *)remotePath to:(NSString *)localPath error:(NSError **)error;
+
+@end

+ 429 - 0
nitoTV4Installer/ObjSSH/ObjSSH.m

@@ -0,0 +1,429 @@
+/*
+ Copyright (c) 2011 Christoffer Lejdborg
+
+ Permission is hereby granted, free of charge, to any person
+ obtaining a copy of this software and associated documentation
+ files (the "Software"), to deal in the Software without
+ restriction, including without limitation the rights to use,
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following
+ conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ OTHER DEALINGS IN THE SOFTWARE.
+
+ -------------------------------------------------------------------------------
+
+ The project contains modified code from the examples at
+ http://libssh2.org/examples/ssh2_exec.html
+ */
+
+#import "ObjSSH.h"
+
+#import "libssh2.h"
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+@implementation ObjSSH
+
+unsigned long hostaddr;
+int sock;
+struct sockaddr_in soin;
+int rc;
+
+LIBSSH2_SESSION *session;
+LIBSSH2_CHANNEL *channel;
+
+static int waitsocket(int socket_fd, LIBSSH2_SESSION *session) {
+    struct timeval timeout;
+    int rc;
+    fd_set fd;
+    fd_set *writefd = NULL;
+    fd_set *readfd = NULL;
+    int dir;
+
+    timeout.tv_sec = 10;
+    timeout.tv_usec = 0;
+
+    FD_ZERO(&fd);
+
+    FD_SET(socket_fd, &fd);
+
+    /* now make sure we wait in the correct direction */
+    dir = libssh2_session_block_directions(session);
+
+    if(dir & LIBSSH2_SESSION_BLOCK_INBOUND)
+        readfd = &fd;
+
+    if(dir & LIBSSH2_SESSION_BLOCK_OUTBOUND)
+        writefd = &fd;
+
+    rc = select(socket_fd + 1, readfd, writefd, NULL, &timeout);
+
+    return rc;
+}
+
+// -----------------------------------------------------------------------------
+// INITIALIZATION
+// -----------------------------------------------------------------------------
+
++ (id)connectToHost:(NSString *)host withUsername:(NSString *)username password:(NSString *)password error:(NSError **)error {
+	if (host == nil)
+		return nil;
+    ObjSSH *ssh = [[ObjSSH alloc] initWithHost:host username:username password:password publicKey:nil privateKey:nil];
+    return [ssh connect:error] ? ssh : nil;
+}
+
++ (id)connectToHost:(NSString *)host withUsername:(NSString *)username publicKey:(NSString *)publicKey privateKey:(NSString *)privateKey error:(NSError **)error {
+    if (host == nil)
+		return nil;
+	ObjSSH *ssh = [[ObjSSH alloc] initWithHost:host username:username password:nil publicKey:publicKey privateKey:privateKey];
+    return [ssh connect:error] ? ssh : nil;
+}
+
+- (id)initWithHost:(NSString *)host username:(NSString *)username password:(NSString *)password publicKey:(NSString *)publicKey privateKey:(NSString *)priateKey {
+    self = [super init];
+
+    if (self) {
+        // Set defaults from parameters
+        _host = host;
+        _port = [NSNumber numberWithInt:22];
+        _username = username;
+        _password = password;
+        _publicKey = publicKey;
+        _privateKey = priateKey;
+
+        // Find out the ip address and port number from host
+        NSMutableArray *hostParts = (NSMutableArray *)[host componentsSeparatedByString:@":"];
+        if ([hostParts count] > 1) {
+            NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
+            [formatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"en_US"]];
+
+            NSNumber *port = [formatter numberFromString:[hostParts objectAtIndex:1]];
+            if (port) {
+               
+                _port = port;
+                _host = [hostParts objectAtIndex:0];
+            }
+        }
+    }
+
+    return self;
+}
+
+// -----------------------------------------------------------------------------
+// HANDLE CONNECTIONS
+// -----------------------------------------------------------------------------
+
+- (BOOL)connect:(NSError **)error {
+    hostaddr = inet_addr([_host cStringUsingEncoding:NSUTF8StringEncoding]);
+    sock = socket(AF_INET, SOCK_STREAM, 0);
+    soin.sin_family = AF_INET;
+    soin.sin_port = htons([_port intValue]);
+    soin.sin_addr.s_addr = hostaddr;
+
+    // Connect to socket
+    if (connect(sock, (struct sockaddr*)(&soin),sizeof(struct sockaddr_in)) != 0) {
+        NSMutableDictionary *errorDetail = [NSMutableDictionary dictionary];
+        [errorDetail setValue:@"Failed to connect" forKey:NSLocalizedDescriptionKey];
+        *error = [NSError errorWithDomain:@"ObjSSH" code:100 userInfo:errorDetail];
+
+        return NO;
+    }
+
+    // Create a session instance
+    session = libssh2_session_init();
+    if (!session) {
+        NSMutableDictionary *errorDetail = [NSMutableDictionary dictionary];
+        [errorDetail setValue:@"Failed to create a session instance" forKey:NSLocalizedDescriptionKey];
+        *error = [NSError errorWithDomain:@"ObjSSH" code:101 userInfo:errorDetail];
+
+        return NO;
+    }
+
+    // Tell libssh2 we want it all done non-blocking
+    libssh2_session_set_blocking(session, 0);
+
+    // Start it up. This will trade welcome banners, exchange keys,
+    // and setup crypto, compression, and MAC layers
+    while ((rc = libssh2_session_startup(session, sock)) == LIBSSH2_ERROR_EAGAIN);
+    if (rc) {
+        NSMutableDictionary *errorDetail = [NSMutableDictionary dictionary];
+        [errorDetail setValue:[NSString stringWithFormat:@"Failed establishing SSH session: %d", rc] forKey:NSLocalizedDescriptionKey];
+        *error = [NSError errorWithDomain:@"ObjSSH" code:102 userInfo:errorDetail];
+
+        return NO;
+    }
+
+    const char *username = [_username cStringUsingEncoding:NSUTF8StringEncoding];
+    const char *password = [_password cStringUsingEncoding:NSUTF8StringEncoding];
+    if ([_privateKey length] == 0) {
+        // We could authenticate via password
+        while ((rc = libssh2_userauth_password(session, username, password)) == LIBSSH2_ERROR_EAGAIN);
+        if (rc) {
+            NSMutableDictionary *errorDetail = [NSMutableDictionary dictionary];
+            [errorDetail setValue:@"Authentication by password failed" forKey:NSLocalizedDescriptionKey];
+            *error = [NSError errorWithDomain:@"ObjSSH" code:103 userInfo:errorDetail];
+
+            return NO;
+        }
+    }
+    else {
+        // Or by public key
+        while ((rc = libssh2_userauth_publickey_fromfile(session, username, [_publicKey cStringUsingEncoding:NSUTF8StringEncoding], [_privateKey cStringUsingEncoding:NSUTF8StringEncoding], password)) == LIBSSH2_ERROR_EAGAIN);
+        if (rc) {
+            NSMutableDictionary *errorDetail = [NSMutableDictionary dictionary];
+            [errorDetail setValue:@"Authentication by public key failed" forKey:NSLocalizedDescriptionKey];
+            *error = [NSError errorWithDomain:@"ObjSSH" code:100 userInfo:errorDetail];
+
+            return NO;
+        }
+    }
+
+    return YES;
+}
+
+- (void)disconnect {
+    libssh2_session_disconnect(session, "Disconnect");
+    libssh2_session_free(session);
+    close(sock);
+}
+
+// -----------------------------------------------------------------------------
+// EXECUTION
+// -----------------------------------------------------------------------------
+
+- (NSString *)execute:(NSString *)command error:(NSError **)error {
+    NSString *result = nil;
+
+    // Exececute command non-blocking on the remote host
+    while ( (channel = libssh2_channel_open_session(session)) == NULL && libssh2_session_last_error(session, NULL, NULL, 0) == LIBSSH2_ERROR_EAGAIN ) {
+        waitsocket(sock, session);
+    }
+
+    if ( channel == NULL ) {
+        NSMutableDictionary *errorDetail = [NSMutableDictionary dictionary];
+        [errorDetail setValue:@"An error occured while opening a channel on the remote host" forKey:NSLocalizedDescriptionKey];
+        *error = [NSError errorWithDomain:@"ObjSSH" code:201 userInfo:errorDetail];
+        return nil;
+    }
+
+    while ( (rc = libssh2_channel_exec(channel, [command cStringUsingEncoding:NSUTF8StringEncoding])) == LIBSSH2_ERROR_EAGAIN ) {
+        waitsocket(sock, session);
+    }
+
+    if ( rc != 0 ) {
+        NSMutableDictionary *errorDetail = [NSMutableDictionary dictionary];
+        [errorDetail setValue:@"An error occured while executing command on remote server" forKey:NSLocalizedDescriptionKey];
+        *error = [NSError errorWithDomain:@"ObjSSH" code:100 userInfo:errorDetail];
+
+        return nil;
+    }
+
+    for ( ;; ) {
+        // Loop until we block
+        int rc1;
+        do {
+            char buffer[0x9000];
+            rc1 = libssh2_channel_read(channel, buffer, sizeof(buffer));
+            if ( rc1 > 0 ) {
+                result = [NSString stringWithCString:buffer encoding:NSASCIIStringEncoding];
+                NSLog(@"result: %@", result);
+            }
+        }
+        while ( rc1 > 0 );
+
+        // This is due to blocking that would occur otherwise so we loop on
+        // this condition
+        if ( rc1 == LIBSSH2_ERROR_EAGAIN ) {
+            waitsocket(sock, session);
+        }
+        else {
+            break;
+        }
+    }
+
+    while ( (rc = libssh2_channel_close(channel)) == LIBSSH2_ERROR_EAGAIN ) waitsocket(sock, session);
+
+    libssh2_channel_free(channel);
+    channel = NULL;
+
+    return result;
+}
+
+// -----------------------------------------------------------------------------
+// SCP
+// -----------------------------------------------------------------------------
+
+- (BOOL)uploadFile:(NSString *)localPath to:(NSString *)remotePath error:(NSError **)error {
+    struct stat fileinfo;
+    size_t nread;
+    char mem[1024*100];
+    char *ptr;
+    size_t prev;
+
+    // Read local file
+    FILE *local = fopen([localPath cStringUsingEncoding:NSUTF8StringEncoding], "rb");
+    if (!local) {
+        NSMutableDictionary *errorDetail = [NSMutableDictionary dictionary];
+        [errorDetail setValue:[NSString stringWithFormat:@"Can't read local file: %@", localPath] forKey:NSLocalizedDescriptionKey];
+        *error = [NSError errorWithDomain:@"ObjSSH" code:401 userInfo:errorDetail];
+
+        return NO;
+    }
+
+    stat([localPath cStringUsingEncoding:NSUTF8StringEncoding], &fileinfo);
+
+    // Send the file via SCP
+    do {
+        channel = libssh2_scp_send(session, [remotePath cStringUsingEncoding:NSUTF8StringEncoding], fileinfo.st_mode & 0777, (unsigned long)fileinfo.st_size);
+
+        if ((!channel) && (libssh2_session_last_errno(session) != LIBSSH2_ERROR_EAGAIN)) {
+            char *err_msg;
+            libssh2_session_last_error(session, &err_msg, NULL, 0);
+
+            NSMutableDictionary *errorDetail = [NSMutableDictionary dictionary];
+            [errorDetail setValue:[NSString stringWithCString:err_msg encoding:NSUTF8StringEncoding] forKey:NSLocalizedDescriptionKey];
+            *error = [NSError errorWithDomain:@"ObjSSH" code:402 userInfo:errorDetail];
+
+            return NO;
+        }
+    } while (!channel);
+
+    do {
+        nread = fread(mem, 1, sizeof(mem), local);
+        if (nread <= 0) {
+            // end of file
+            break;
+        }
+        ptr = mem;
+
+        prev = 0;
+        do {
+            while ((rc = libssh2_channel_write(channel, ptr, nread)) == LIBSSH2_ERROR_EAGAIN) {
+                waitsocket(sock, session);
+                prev = 0;
+            }
+
+            if (rc < 0) {
+                break;
+            }
+            else {
+                prev = nread;
+
+                // rc indicates how many bytes were written this time
+                nread -= rc;
+                ptr += rc;
+            }
+        } while (nread);
+    } while (!nread); // only continue if nread was drained
+
+    // Sending EOF
+    while (libssh2_channel_send_eof(channel) == LIBSSH2_ERROR_EAGAIN);
+
+    // Waiting for EOF
+    while (libssh2_channel_wait_eof(channel) == LIBSSH2_ERROR_EAGAIN);
+
+    // Waiting for channel to close
+    while (libssh2_channel_wait_closed(channel) == LIBSSH2_ERROR_EAGAIN);
+
+    libssh2_channel_free(channel);
+    channel = NULL;
+
+    return YES;
+}
+
+- (BOOL)downloadFile:(NSString *)remotePath to:(NSString *)localPath error:(NSError **)error {
+    int spin = 0;
+    struct stat fileinfo;
+    off_t got = 0;
+    int localFile = open([localPath cStringUsingEncoding:NSUTF8StringEncoding], O_WRONLY|O_CREAT, 0755);
+
+    do {
+        channel = libssh2_scp_recv(session, [remotePath cStringUsingEncoding:NSUTF8StringEncoding], &fileinfo);
+
+        if (!channel) {
+            if (libssh2_session_last_errno(session) == LIBSSH2_ERROR_EAGAIN) {
+                waitsocket(sock, session);
+            }
+            else {
+                char *err_msg;
+                libssh2_session_last_error(session, &err_msg, NULL, 0);
+
+                NSMutableDictionary *errorDetail = [NSMutableDictionary dictionary];
+                [errorDetail setValue:[NSString stringWithCString:err_msg encoding:NSUTF8StringEncoding] forKey:NSLocalizedDescriptionKey];
+                *error = [NSError errorWithDomain:@"ObjSSH" code:301 userInfo:errorDetail];
+
+                return NO;
+            }
+        }
+    } while (!channel);
+
+    // libssh2_scp_recv() is done, now receive data
+    while (got < fileinfo.st_size) {
+        char mem[1024*24];
+        int rc;
+
+        do {
+            int amount = sizeof(mem);
+
+            if ((fileinfo.st_size -got) < amount) {
+                amount = fileinfo.st_size - got;
+            }
+
+            // Loop until we block
+            rc = libssh2_channel_read(channel, mem, amount);
+
+            if (rc > 0) {
+                write(localFile, mem, rc);
+                got += rc;
+            }
+        } while (rc > 0);
+
+        if ((rc == LIBSSH2_ERROR_EAGAIN) && (got < fileinfo.st_size)) {
+            // This is due to blocking that would occur otherwise
+            // so we loop on this condition
+
+            spin++;
+            waitsocket(sock, session);
+            continue;
+        }
+
+        break;
+    }
+
+    libssh2_channel_free(channel);
+    channel = NULL;
+    close(localFile);
+
+    return YES;
+}
+
+// -----------------------------------------------------------------------------
+// MEMORY STUFF
+// -----------------------------------------------------------------------------
+
+- (void)dealloc {
+ /*
+    [_host release];
+    [_port release];
+    [_username release];
+    [_password release];
+    [_privateKey release];
+    [_publicKey release];
+
+    [super dealloc];
+  */
+}
+
+@end

+ 79 - 0
nitoTV4Installer/ObjSSH/README.md

@@ -0,0 +1,79 @@
+# The Objective-C wrapper for libssh2
+
+ObjSSH aims to be a full Objective-C wrapper for libssh2, with an API that is easy to understand and fun to work with.
+
+To achieve that goal, the library will assume conventions but still make it easy to override them without writing ugly code.
+
+Let's begin with some samples...
+
+## Install the library
+
+Drag and drop the project directory and all it's files in to your Xcode project. Then add the following header where you want to use the library.
+
+    #include "ObjSSH.h"
+
+## Connect to a server
+
+    NSError *error;
+    ObjSSH *ssh = [ObjSSH connectToHost:@"127.0.0.1" withUsername:@"user" password:@"ssh, secret!" error:&error];
+
+It's that simple. Need to use another port? Set `connectToHost:@"127.0.0.1:456"`. No password? `password:nil`. Now, wasn't that nice and tidy?
+
+ObjSSH also supports public/private key pairs. Connect using the flowing method:
+
+    NSError *error;
+    ObjSSH *ssh = [ObjSSH connectToHost:@"127.0.0.1" withUsername:@"user" publicKey:@"/home/user/.ssh/id_rsa.pub" privateKey:@"/home/user/.ssh/id_rsa" error:&error];
+
+To disconnect just run:
+
+    [ssh disconnect];
+    [ssh release];
+
+## Executing command
+
+Executing a command is as simple as:
+
+    NSError *error;
+    NSString *response = [ssh execute:@"ls -la" error:&error];
+
+The response from the command is conveniently returned as a `NSString`.
+
+## Using SCP
+
+Sending and fetching files is just as simple:
+
+__Sending files__
+
+    NSError *error;
+    [ssh uploadFile:@"/local/file.txt" to:@"/remote/file.txt" error:&error];
+
+__Fetching files__
+
+    NSError *error;
+    [ssh downloadFile:@"/remote/file.txt" to:@"/local/file.txt" error:&error];
+
+
+## Licence
+
+Copyright (c) 2011 Christoffer Lejdborg
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.

BIN
nitoTV4Installer/ObjSSH/lib/libssh2-i386.a


BIN
nitoTV4Installer/ObjSSH/lib/libssh2-x86_64.a


BIN
nitoTV4Installer/ObjSSH/lib/libssh2.a


File diff suppressed because it is too large
+ 1117 - 0
nitoTV4Installer/ObjSSH/lib/libssh2.h


+ 4 - 0
nitoTV4Installer/ObjSSH/todo.txt

@@ -0,0 +1,4 @@
+- Add support for string hostnames, such as "mydomain.com"
+- SFTP support
+- When using SCP methods a to:-parameter that ends with a trailing slash
+  should inherit the file name from the from:-parameter.

+ 13 - 0
nitoTV4Installer/main.m

@@ -0,0 +1,13 @@
+//
+//  main.m
+//  nitoTV4Installer
+//
+//  Created by Kevin Bradley on 1/24/18.
+//  Copyright © 2018 Kevin Bradley. All rights reserved.
+//
+
+#import <Cocoa/Cocoa.h>
+
+int main(int argc, const char * argv[]) {
+    return NSApplicationMain(argc, argv);
+}

+ 5 - 0
nitoTV4Installer/nitoTV4Installer.entitlements

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict/>
+</plist>

+ 8 - 0
nitoTV4Installer/nitoTV4Installer.pch

@@ -0,0 +1,8 @@
+//
+// Prefix header for all source files of the 'iReSign' target in the 'iReSign' project
+//
+
+#ifdef __OBJC__
+    #import <Cocoa/Cocoa.h>
+#import "Defines.h"
+#endif