fix dependencies building, looks like i cant use the sim bc libs are compiled for ios

This commit is contained in:
neon443
2025-06-05 11:17:37 +01:00
parent 2a2ed76f58
commit 7d206478ea
92 changed files with 14342 additions and 0 deletions

1
Cartfile Normal file
View File

@@ -0,0 +1 @@
github "NMSSH/NMSSH"

1
Cartfile.resolved Normal file
View File

@@ -0,0 +1 @@
github "NMSSH/NMSSH" "2.3.1"

49
Carthage/Checkouts/NMSSH/.gitignore vendored Normal file
View File

@@ -0,0 +1,49 @@
# Xcode
#
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
## Build generated
build/
DerivedData
## Various settings
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata
## Other
*.xccheckout
*.moved-aside
*.xcuserstate
*.xcscmblueprint
## Obj-C/Swift specific
*.hmap
*.ipa
# CocoaPods
#
# We recommend against adding the Pods directory to your .gitignore. However
# you should judge for yourself, the pros and cons are mentioned at:
# http://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
#
#Pods/
# Carthage
#
# Add this line if you want to avoid checking in source code from Carthage dependencies.
Cartfile*
Carthage/
Carthage/Checkouts
Carthage/Build
Documentation/
Products/
NMSSHTests/Settings/config.yml
.DS_Store

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:PTYExample/PTYExample.xcodeproj">
</FileRef>
<FileRef
location = "group:../NMSSH-iOS.xcodeproj">
</FileRef>
</Workspace>

View File

@@ -0,0 +1,375 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
18221C5A1EA9080300C489F4 /* NMSSH.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 18221C591EA9080300C489F4 /* NMSSH.framework */; };
18221C5B1EA9080300C489F4 /* NMSSH.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 18221C591EA9080300C489F4 /* NMSSH.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
183E6A341B38510500727F97 /* Launch Screen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 183E6A331B38510500727F97 /* Launch Screen.xib */; };
18AE8FA918BBEDD000AE0FC3 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 18AE8FA818BBEDD000AE0FC3 /* Foundation.framework */; };
18AE8FAB18BBEDD000AE0FC3 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 18AE8FAA18BBEDD000AE0FC3 /* CoreGraphics.framework */; };
18AE8FAD18BBEDD000AE0FC3 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 18AE8FAC18BBEDD000AE0FC3 /* UIKit.framework */; };
18AE8FB318BBEDD000AE0FC3 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 18AE8FB118BBEDD000AE0FC3 /* InfoPlist.strings */; };
18AE8FB518BBEDD000AE0FC3 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 18AE8FB418BBEDD000AE0FC3 /* main.m */; };
18AE8FB918BBEDD100AE0FC3 /* NMAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 18AE8FB818BBEDD100AE0FC3 /* NMAppDelegate.m */; };
18AE8FBC18BBEDD100AE0FC3 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 18AE8FBA18BBEDD100AE0FC3 /* Main.storyboard */; };
18AE8FBF18BBEDD100AE0FC3 /* NMViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 18AE8FBE18BBEDD100AE0FC3 /* NMViewController.m */; };
18AE8FC118BBEDD100AE0FC3 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 18AE8FC018BBEDD100AE0FC3 /* Images.xcassets */; };
18AE8FE018BBF0C100AE0FC3 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 18AE8FDF18BBF0C100AE0FC3 /* libz.dylib */; };
18AE8FE218BBF0C700AE0FC3 /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 18AE8FE118BBF0C700AE0FC3 /* CFNetwork.framework */; };
18AE8FE518BBF11B00AE0FC3 /* NMTerminalViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 18AE8FE418BBF11B00AE0FC3 /* NMTerminalViewController.m */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
18221C5C1EA9080300C489F4 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 10;
files = (
18221C5B1EA9080300C489F4 /* NMSSH.framework in Embed Frameworks */,
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
18221C591EA9080300C489F4 /* NMSSH.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = NMSSH.framework; sourceTree = BUILT_PRODUCTS_DIR; };
183E6A331B38510500727F97 /* Launch Screen.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = "Launch Screen.xib"; sourceTree = "<group>"; };
18AE8FA518BBEDD000AE0FC3 /* PTYExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PTYExample.app; sourceTree = BUILT_PRODUCTS_DIR; };
18AE8FA818BBEDD000AE0FC3 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
18AE8FAA18BBEDD000AE0FC3 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
18AE8FAC18BBEDD000AE0FC3 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
18AE8FB018BBEDD000AE0FC3 /* PTYExample-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "PTYExample-Info.plist"; sourceTree = "<group>"; };
18AE8FB218BBEDD000AE0FC3 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
18AE8FB418BBEDD000AE0FC3 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
18AE8FB618BBEDD100AE0FC3 /* PTYExample-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "PTYExample-Prefix.pch"; sourceTree = "<group>"; };
18AE8FB718BBEDD100AE0FC3 /* NMAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NMAppDelegate.h; sourceTree = "<group>"; };
18AE8FB818BBEDD100AE0FC3 /* NMAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NMAppDelegate.m; sourceTree = "<group>"; };
18AE8FBB18BBEDD100AE0FC3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
18AE8FBD18BBEDD100AE0FC3 /* NMViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NMViewController.h; sourceTree = "<group>"; };
18AE8FBE18BBEDD100AE0FC3 /* NMViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NMViewController.m; sourceTree = "<group>"; };
18AE8FC018BBEDD100AE0FC3 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
18AE8FC718BBEDD100AE0FC3 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; };
18AE8FDF18BBF0C100AE0FC3 /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; };
18AE8FE118BBF0C700AE0FC3 /* CFNetwork.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CFNetwork.framework; path = System/Library/Frameworks/CFNetwork.framework; sourceTree = SDKROOT; };
18AE8FE318BBF11B00AE0FC3 /* NMTerminalViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NMTerminalViewController.h; sourceTree = "<group>"; };
18AE8FE418BBF11B00AE0FC3 /* NMTerminalViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NMTerminalViewController.m; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
18AE8FA218BBEDD000AE0FC3 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
18AE8FE218BBF0C700AE0FC3 /* CFNetwork.framework in Frameworks */,
18AE8FE018BBF0C100AE0FC3 /* libz.dylib in Frameworks */,
18221C5A1EA9080300C489F4 /* NMSSH.framework in Frameworks */,
18AE8FAB18BBEDD000AE0FC3 /* CoreGraphics.framework in Frameworks */,
18AE8FAD18BBEDD000AE0FC3 /* UIKit.framework in Frameworks */,
18AE8FA918BBEDD000AE0FC3 /* Foundation.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
18AE8F9C18BBEDD000AE0FC3 = {
isa = PBXGroup;
children = (
18AE8FAE18BBEDD000AE0FC3 /* PTYExample */,
18AE8FA718BBEDD000AE0FC3 /* Frameworks */,
18AE8FA618BBEDD000AE0FC3 /* Products */,
);
sourceTree = "<group>";
};
18AE8FA618BBEDD000AE0FC3 /* Products */ = {
isa = PBXGroup;
children = (
18AE8FA518BBEDD000AE0FC3 /* PTYExample.app */,
);
name = Products;
sourceTree = "<group>";
};
18AE8FA718BBEDD000AE0FC3 /* Frameworks */ = {
isa = PBXGroup;
children = (
18221C591EA9080300C489F4 /* NMSSH.framework */,
18AE8FDF18BBF0C100AE0FC3 /* libz.dylib */,
18AE8FE118BBF0C700AE0FC3 /* CFNetwork.framework */,
18AE8FA818BBEDD000AE0FC3 /* Foundation.framework */,
18AE8FAA18BBEDD000AE0FC3 /* CoreGraphics.framework */,
18AE8FAC18BBEDD000AE0FC3 /* UIKit.framework */,
18AE8FC718BBEDD100AE0FC3 /* XCTest.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
18AE8FAE18BBEDD000AE0FC3 /* PTYExample */ = {
isa = PBXGroup;
children = (
18AE8FB718BBEDD100AE0FC3 /* NMAppDelegate.h */,
18AE8FB818BBEDD100AE0FC3 /* NMAppDelegate.m */,
183E6A331B38510500727F97 /* Launch Screen.xib */,
18AE8FBA18BBEDD100AE0FC3 /* Main.storyboard */,
18AE8FBD18BBEDD100AE0FC3 /* NMViewController.h */,
18AE8FBE18BBEDD100AE0FC3 /* NMViewController.m */,
18AE8FE318BBF11B00AE0FC3 /* NMTerminalViewController.h */,
18AE8FE418BBF11B00AE0FC3 /* NMTerminalViewController.m */,
18AE8FC018BBEDD100AE0FC3 /* Images.xcassets */,
18AE8FAF18BBEDD000AE0FC3 /* Supporting Files */,
);
path = PTYExample;
sourceTree = "<group>";
};
18AE8FAF18BBEDD000AE0FC3 /* Supporting Files */ = {
isa = PBXGroup;
children = (
18AE8FB018BBEDD000AE0FC3 /* PTYExample-Info.plist */,
18AE8FB118BBEDD000AE0FC3 /* InfoPlist.strings */,
18AE8FB418BBEDD000AE0FC3 /* main.m */,
18AE8FB618BBEDD100AE0FC3 /* PTYExample-Prefix.pch */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
18AE8FA418BBEDD000AE0FC3 /* PTYExample */ = {
isa = PBXNativeTarget;
buildConfigurationList = 18AE8FD718BBEDD100AE0FC3 /* Build configuration list for PBXNativeTarget "PTYExample" */;
buildPhases = (
18AE8FA118BBEDD000AE0FC3 /* Sources */,
18AE8FA218BBEDD000AE0FC3 /* Frameworks */,
18AE8FA318BBEDD000AE0FC3 /* Resources */,
18221C5C1EA9080300C489F4 /* Embed Frameworks */,
);
buildRules = (
);
dependencies = (
);
name = PTYExample;
productName = PTYExample;
productReference = 18AE8FA518BBEDD000AE0FC3 /* PTYExample.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
18AE8F9D18BBEDD000AE0FC3 /* Project object */ = {
isa = PBXProject;
attributes = {
CLASSPREFIX = NM;
LastUpgradeCheck = 0830;
ORGANIZATIONNAME = "Nine Muses";
};
buildConfigurationList = 18AE8FA018BBEDD000AE0FC3 /* Build configuration list for PBXProject "PTYExample" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 18AE8F9C18BBEDD000AE0FC3;
productRefGroup = 18AE8FA618BBEDD000AE0FC3 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
18AE8FA418BBEDD000AE0FC3 /* PTYExample */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
18AE8FA318BBEDD000AE0FC3 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
18AE8FC118BBEDD100AE0FC3 /* Images.xcassets in Resources */,
183E6A341B38510500727F97 /* Launch Screen.xib in Resources */,
18AE8FB318BBEDD000AE0FC3 /* InfoPlist.strings in Resources */,
18AE8FBC18BBEDD100AE0FC3 /* Main.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
18AE8FA118BBEDD000AE0FC3 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
18AE8FBF18BBEDD100AE0FC3 /* NMViewController.m in Sources */,
18AE8FB518BBEDD000AE0FC3 /* main.m in Sources */,
18AE8FB918BBEDD100AE0FC3 /* NMAppDelegate.m in Sources */,
18AE8FE518BBF11B00AE0FC3 /* NMTerminalViewController.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
18AE8FB118BBEDD000AE0FC3 /* InfoPlist.strings */ = {
isa = PBXVariantGroup;
children = (
18AE8FB218BBEDD000AE0FC3 /* en */,
);
name = InfoPlist.strings;
sourceTree = "<group>";
};
18AE8FBA18BBEDD100AE0FC3 /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
18AE8FBB18BBEDD100AE0FC3 /* Base */,
);
name = Main.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
18AE8FD518BBEDD100AE0FC3 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
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;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
};
name = Debug;
};
18AE8FD618BBEDD100AE0FC3 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = YES;
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
SDKROOT = iphoneos;
VALIDATE_PRODUCT = YES;
};
name = Release;
};
18AE8FD818BBEDD100AE0FC3 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
DEVELOPMENT_TEAM = "";
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "PTYExample/PTYExample-Prefix.pch";
INFOPLIST_FILE = "PTYExample/PTYExample-Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "se.ninemuses.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = app;
};
name = Debug;
};
18AE8FD918BBEDD100AE0FC3 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
DEVELOPMENT_TEAM = "";
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "PTYExample/PTYExample-Prefix.pch";
INFOPLIST_FILE = "PTYExample/PTYExample-Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "se.ninemuses.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = app;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
18AE8FA018BBEDD000AE0FC3 /* Build configuration list for PBXProject "PTYExample" */ = {
isa = XCConfigurationList;
buildConfigurations = (
18AE8FD518BBEDD100AE0FC3 /* Debug */,
18AE8FD618BBEDD100AE0FC3 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
18AE8FD718BBEDD100AE0FC3 /* Build configuration list for PBXNativeTarget "PTYExample" */ = {
isa = XCConfigurationList;
buildConfigurations = (
18AE8FD818BBEDD100AE0FC3 /* Debug */,
18AE8FD918BBEDD100AE0FC3 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 18AE8F9D18BBEDD000AE0FC3 /* Project object */;
}

View File

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

View File

@@ -0,0 +1,202 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="7706" systemVersion="14A389" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" initialViewController="FFG-Nw-qqH">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="7703"/>
</dependencies>
<scenes>
<!--Navigation Controller-->
<scene sceneID="IRZ-Nc-OdO">
<objects>
<navigationController definesPresentationContext="YES" id="FFG-Nw-qqH" sceneMemberID="viewController">
<navigationBar key="navigationBar" contentMode="scaleToFill" id="7HS-aM-dfR">
<autoresizingMask key="autoresizingMask"/>
</navigationBar>
<connections>
<segue destination="vXZ-lx-hvc" kind="relationship" relationship="rootViewController" id="nxf-jZ-FIf"/>
</connections>
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="Imc-TS-SCT" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-689" y="-54"/>
</scene>
<!--NMSSH Example-->
<scene sceneID="ufC-wZ-h7g">
<objects>
<viewController id="vXZ-lx-hvc" customClass="NMViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="K8b-9K-nFW"/>
<viewControllerLayoutGuide type="bottom" id="Dac-hY-bze"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="kh9-bI-dsS">
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Host:" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="gHf-nl-Wxn">
<rect key="frame" x="20" y="77" width="280" height="21"/>
<constraints>
<constraint firstAttribute="height" constant="21" id="7xk-ui-yFT"/>
</constraints>
<fontDescription key="fontDescription" type="boldSystem" pointSize="17"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="utH-V5-mhW">
<rect key="frame" x="20" y="106" width="280" height="30"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits"/>
<connections>
<action selector="hostend:" destination="vXZ-lx-hvc" eventType="editingDidEndOnExit" id="QfS-Gy-CNd"/>
<action selector="hostend:" destination="vXZ-lx-hvc" eventType="editingDidEnd" id="Xk0-Xs-aGB"/>
</connections>
</textField>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Username:" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="4QY-Xj-CSz">
<rect key="frame" x="20" y="144" width="280" height="21"/>
<constraints>
<constraint firstAttribute="height" constant="21" id="k3O-Ty-hS8"/>
</constraints>
<fontDescription key="fontDescription" type="boldSystem" pointSize="17"/>
<nil key="highlightedColor"/>
</label>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="ctE-X4-ljr">
<rect key="frame" x="20" y="173" width="280" height="30"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits"/>
<connections>
<action selector="usernameend:" destination="vXZ-lx-hvc" eventType="editingDidEndOnExit" id="MoF-zp-IEA"/>
</connections>
</textField>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Password:" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="kv4-jy-zIz">
<rect key="frame" x="20" y="211" width="280" height="21"/>
<constraints>
<constraint firstAttribute="height" constant="21" id="eyf-eF-N9d"/>
</constraints>
<fontDescription key="fontDescription" type="boldSystem" pointSize="17"/>
<nil key="highlightedColor"/>
</label>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="efG-PA-3AY">
<rect key="frame" x="20" y="240" width="280" height="30"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits"/>
<connections>
<action selector="passwordend:" destination="vXZ-lx-hvc" eventType="editingDidEndOnExit" id="dea-6b-Chs"/>
</connections>
</textField>
<segmentedControl opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="0" translatesAutoresizingMaskIntoConstraints="NO" id="dwy-YR-1OQ">
<rect key="frame" x="20" y="307" width="280" height="29"/>
<segments>
<segment title="Password"/>
<segment title="Keyboard-Interactive"/>
</segments>
<color key="tintColor" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
<connections>
<action selector="authentication:" destination="vXZ-lx-hvc" eventType="valueChanged" id="Q4U-S5-FDG"/>
</connections>
</segmentedControl>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Authentication:" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ZkA-Hc-las">
<rect key="frame" x="20" y="278" width="280" height="21"/>
<constraints>
<constraint firstAttribute="height" constant="21" id="Y6t-uj-yAL"/>
</constraints>
<fontDescription key="fontDescription" type="boldSystem" pointSize="17"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
<constraints>
<constraint firstItem="efG-PA-3AY" firstAttribute="trailing" secondItem="kv4-jy-zIz" secondAttribute="trailing" id="0EY-kj-kxh"/>
<constraint firstItem="4QY-Xj-CSz" firstAttribute="leading" secondItem="ctE-X4-ljr" secondAttribute="leading" id="1JK-7W-ft0"/>
<constraint firstItem="ZkA-Hc-las" firstAttribute="leading" secondItem="dwy-YR-1OQ" secondAttribute="leading" id="28g-fj-6Pc"/>
<constraint firstItem="ZkA-Hc-las" firstAttribute="trailing" secondItem="dwy-YR-1OQ" secondAttribute="trailing" id="6kb-Gx-WpQ"/>
<constraint firstItem="gHf-nl-Wxn" firstAttribute="leading" secondItem="kh9-bI-dsS" secondAttribute="leading" constant="20" id="ADk-OC-EJq"/>
<constraint firstItem="4QY-Xj-CSz" firstAttribute="leading" secondItem="kh9-bI-dsS" secondAttribute="leading" constant="20" id="BIf-qd-SmU"/>
<constraint firstItem="gHf-nl-Wxn" firstAttribute="leading" secondItem="utH-V5-mhW" secondAttribute="leading" id="Dkf-qW-vgb"/>
<constraint firstItem="efG-PA-3AY" firstAttribute="top" secondItem="kv4-jy-zIz" secondAttribute="bottom" constant="8" id="F8o-HB-LJA"/>
<constraint firstItem="ctE-X4-ljr" firstAttribute="top" secondItem="4QY-Xj-CSz" secondAttribute="bottom" constant="8" id="G1b-wh-Oey"/>
<constraint firstItem="kv4-jy-zIz" firstAttribute="leading" secondItem="kh9-bI-dsS" secondAttribute="leading" constant="20" id="GBG-x3-DL8"/>
<constraint firstItem="4QY-Xj-CSz" firstAttribute="top" secondItem="utH-V5-mhW" secondAttribute="bottom" constant="8" id="Kpu-6c-hsO"/>
<constraint firstAttribute="trailing" secondItem="ZkA-Hc-las" secondAttribute="trailing" constant="20" id="L7x-8G-CZt"/>
<constraint firstItem="ZkA-Hc-las" firstAttribute="leading" secondItem="kh9-bI-dsS" secondAttribute="leading" constant="20" id="Nga-ut-Vqs"/>
<constraint firstItem="utH-V5-mhW" firstAttribute="top" secondItem="gHf-nl-Wxn" secondAttribute="bottom" constant="8" id="Rb1-H8-n9X"/>
<constraint firstItem="efG-PA-3AY" firstAttribute="leading" secondItem="kv4-jy-zIz" secondAttribute="leading" id="SLu-DP-MVW"/>
<constraint firstItem="kv4-jy-zIz" firstAttribute="top" secondItem="ctE-X4-ljr" secondAttribute="bottom" constant="8" id="Wwh-jx-wYd"/>
<constraint firstItem="gHf-nl-Wxn" firstAttribute="trailing" secondItem="utH-V5-mhW" secondAttribute="trailing" id="YHf-2q-HLq"/>
<constraint firstItem="4QY-Xj-CSz" firstAttribute="trailing" secondItem="ctE-X4-ljr" secondAttribute="trailing" id="bSS-bu-9AC"/>
<constraint firstItem="ZkA-Hc-las" firstAttribute="top" secondItem="efG-PA-3AY" secondAttribute="bottom" constant="8" id="cef-pk-nHF"/>
<constraint firstAttribute="trailing" secondItem="4QY-Xj-CSz" secondAttribute="trailing" constant="20" id="gNO-Z1-fzq"/>
<constraint firstItem="gHf-nl-Wxn" firstAttribute="top" secondItem="K8b-9K-nFW" secondAttribute="bottom" constant="13" id="h0k-N3-2gZ"/>
<constraint firstItem="dwy-YR-1OQ" firstAttribute="top" secondItem="ZkA-Hc-las" secondAttribute="bottom" constant="8" id="qrX-Nh-H0r"/>
<constraint firstAttribute="trailing" secondItem="gHf-nl-Wxn" secondAttribute="trailing" constant="20" id="suc-4e-sZy"/>
<constraint firstAttribute="trailing" secondItem="kv4-jy-zIz" secondAttribute="trailing" constant="20" id="vZ3-E1-q9v"/>
</constraints>
</view>
<navigationItem key="navigationItem" title="NMSSH Example" id="PC6-EQ-vMq">
<barButtonItem key="rightBarButtonItem" title="Login" id="tfx-Qe-Jrm">
<connections>
<action selector="login:" destination="vXZ-lx-hvc" id="t6x-he-Gg8"/>
</connections>
</barButtonItem>
</navigationItem>
<connections>
<outlet property="authenticationControl" destination="dwy-YR-1OQ" id="50B-sU-ewp"/>
<outlet property="hostField" destination="utH-V5-mhW" id="LYc-cV-lxM"/>
<outlet property="passwordField" destination="efG-PA-3AY" id="WB1-cE-8cB"/>
<outlet property="usernameField" destination="ctE-X4-ljr" id="A3s-fz-bZM"/>
<segue destination="PW5-B9-Jky" kind="push" identifier="loginSegue" id="Wz8-8w-Se2"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="x5A-6p-PRh" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-162" y="-54"/>
</scene>
<!--Terminal View Controller-->
<scene sceneID="1Gj-GQ-1ed">
<objects>
<viewController id="PW5-B9-Jky" customClass="NMTerminalViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="nx7-Ow-OqW"/>
<viewControllerLayoutGuide type="bottom" id="r8D-UH-Rm6"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="p0y-g1-cTe">
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Mx3-3h-dTj">
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
<fontDescription key="fontDescription" name="Menlo-Regular" family="Menlo" pointSize="10"/>
<textInputTraits key="textInputTraits" autocorrectionType="no"/>
<connections>
<outlet property="delegate" destination="PW5-B9-Jky" id="NVw-Ar-T2s"/>
</connections>
</textView>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
</view>
<navigationItem key="navigationItem" id="G5k-Sr-spF">
<barButtonItem key="leftBarButtonItem" title="Disconnect" id="UaD-oB-vbm">
<connections>
<action selector="disconnect:" destination="PW5-B9-Jky" id="wUc-Zq-UwC"/>
</connections>
</barButtonItem>
<barButtonItem key="rightBarButtonItem" title="Connect" id="SwZ-yZ-J1g">
<connections>
<action selector="connect:" destination="PW5-B9-Jky" id="m1q-Cf-DXP"/>
</connections>
</barButtonItem>
</navigationItem>
<connections>
<outlet property="textView" destination="Mx3-3h-dTj" id="gYK-dG-IxF"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="b8S-y0-PxV" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="300" y="-54"/>
</scene>
</scenes>
<simulatedMetricsContainer key="defaultSimulatedMetrics">
<simulatedStatusBarMetrics key="statusBar"/>
<simulatedOrientationMetrics key="orientation"/>
<simulatedScreenMetrics key="destination" type="retina4"/>
</simulatedMetricsContainer>
</document>

View File

@@ -0,0 +1,23 @@
{
"images" : [
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "2x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@@ -0,0 +1,23 @@
{
"images" : [
{
"orientation" : "portrait",
"idiom" : "iphone",
"extent" : "full-screen",
"minimum-system-version" : "7.0",
"scale" : "2x"
},
{
"orientation" : "portrait",
"idiom" : "iphone",
"subtype" : "retina4",
"extent" : "full-screen",
"minimum-system-version" : "7.0",
"scale" : "2x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="6214" systemVersion="14A314h" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6207"/>
<capability name="Constraints with non-1.0 multipliers" minToolsVersion="5.1"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" id="iN0-l3-epB">
<rect key="frame" x="0.0" y="0.0" width="480" height="480"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text=" Copyright (c) 2015 Nine Muses. All rights reserved." textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="9" translatesAutoresizingMaskIntoConstraints="NO" id="8ie-xW-0ye">
<rect key="frame" x="20" y="439" width="441" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
<variation key="widthClass=compact">
<fontDescription key="fontDescription" type="system" pointSize="11"/>
</variation>
</label>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="PTYExample" textAlignment="center" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" minimumFontSize="18" translatesAutoresizingMaskIntoConstraints="NO" id="kId-c2-rCX">
<rect key="frame" x="20" y="140" width="441" height="43"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="36"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
<constraints>
<constraint firstItem="kId-c2-rCX" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="bottom" multiplier="1/3" constant="1" id="Kid-kn-2rF"/>
<constraint firstAttribute="centerX" secondItem="kId-c2-rCX" secondAttribute="centerX" id="Koa-jz-hwk"/>
<constraint firstAttribute="bottom" secondItem="8ie-xW-0ye" secondAttribute="bottom" constant="20" id="Kzo-t9-V3l"/>
<constraint firstItem="8ie-xW-0ye" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" symbolic="YES" id="MfP-vx-nX0"/>
<constraint firstAttribute="centerX" secondItem="8ie-xW-0ye" secondAttribute="centerX" id="ZEH-qu-HZ9"/>
<constraint firstItem="kId-c2-rCX" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" symbolic="YES" id="fvb-Df-36g"/>
</constraints>
<nil key="simulatedStatusBarMetrics"/>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<point key="canvasLocation" x="404" y="445"/>
</view>
</objects>
</document>

View File

@@ -0,0 +1,7 @@
#import <UIKit/UIKit.h>
@interface NMAppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
@end

View File

@@ -0,0 +1,43 @@
#import "NMAppDelegate.h"
@implementation NMAppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[[NSUserDefaults standardUserDefaults] registerDefaults:@{ @"host": @"",
@"username": @"",
@"auth": @(0) }];
// Override point for customization after application launch.
return YES;
}
- (void)applicationWillResignActive:(UIApplication *)application
{
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
// Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
- (void)applicationWillTerminate:(UIApplication *)application
{
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
@end

View File

@@ -0,0 +1,13 @@
#import <UIKit/UIKit.h>
@interface NMTerminalViewController : UIViewController <UITextViewDelegate>
@property (nonatomic, strong) NSString *host;
@property (nonatomic, strong) NSString *username;
@property (nonatomic, strong) NSString *password;
@property (nonatomic) IBOutlet UITextView *textView;
- (IBAction)connect:(id)sender;
- (IBAction)disconnect:(id)sender;
@end

View File

@@ -0,0 +1,218 @@
#import "NMTerminalViewController.h"
#import <NMSSH/NMSSH.h>
@interface NMTerminalViewController () <NMSSHSessionDelegate, NMSSHChannelDelegate>
@property (nonatomic, strong) dispatch_queue_t sshQueue;
@property (nonatomic, strong) NMSSHSession *session;
@property (nonatomic, assign) dispatch_once_t onceToken;
@property (nonatomic, strong) dispatch_semaphore_t semaphore;
@property (nonatomic, strong) NSMutableString *lastCommand;
@property (nonatomic, assign) BOOL keyboardInteractive;
@end
@implementation NMTerminalViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.keyboardInteractive = self.password == nil;
self.textView.editable = NO;
self.textView.selectable = NO;
self.lastCommand = [[NSMutableString alloc] init];
self.sshQueue = dispatch_queue_create("NMSSH.queue", DISPATCH_QUEUE_SERIAL);
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWillHide:)
name:UIKeyboardWillHideNotification
object:nil];
dispatch_once(&_onceToken, ^{
[self connect:self];
});
}
- (IBAction)connect:(id)sender {
dispatch_async(self.sshQueue, ^{
self.session = [NMSSHSession connectToHost:self.host withUsername:self.username];
self.session.delegate = self;
if (!self.session.connected) {
dispatch_async(dispatch_get_main_queue(), ^{
[self appendToTextView:@"Connection error"];
});
return;
}
dispatch_async(dispatch_get_main_queue(), ^{
[self appendToTextView:[NSString stringWithFormat:@"ssh %@@%@\n", self.session.username, self.host]];
});
if (self.keyboardInteractive) {
[self.session authenticateByKeyboardInteractive];
}
else {
[self.session authenticateByPassword:self.password];
}
if (!self.session.authorized) {
dispatch_async(dispatch_get_main_queue(), ^{
[self appendToTextView:@"Authentication error\n"];
self.textView.editable = NO;
});
}
else {
dispatch_async(dispatch_get_main_queue(), ^{
self.textView.editable = YES;
});
self.session.channel.delegate = self;
self.session.channel.requestPty = YES;
NSError *error;
[self.session.channel startShell:&error];
if (error) {
dispatch_async(dispatch_get_main_queue(), ^{
[self appendToTextView:error.localizedDescription];
self.textView.editable = NO;
});
}
}
});
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[self.textView resignFirstResponder];
}
- (IBAction)disconnect:(id)sender {
dispatch_async(self.sshQueue, ^{
[self.session disconnect];
});
}
- (void)appendToTextView:(NSString *)text {
self.textView.text = [NSString stringWithFormat:@"%@%@", self.textView.text, text];
[self.textView scrollRangeToVisible:NSMakeRange([self.textView.text length] - 1, 1)];
}
- (void)performCommand {
if (self.semaphore) {
self.password = [self.lastCommand substringToIndex:MAX(0, self.lastCommand.length - 1)];
dispatch_semaphore_signal(self.semaphore);
}
else {
NSString *command = [self.lastCommand copy];
dispatch_async(self.sshQueue, ^{
[[self.session channel] write:command error:nil timeout:@10];
});
}
[self.lastCommand setString:@""];
}
- (void)channel:(NMSSHChannel *)channel didReadData:(NSString *)message {
NSString *msg = [message copy];
dispatch_async(dispatch_get_main_queue(), ^{
[self appendToTextView:msg];
});
}
- (void)channel:(NMSSHChannel *)channel didReadError:(NSString *)error {
dispatch_async(dispatch_get_main_queue(), ^{
[self appendToTextView:[NSString stringWithFormat:@"[ERROR] %@", error]];
});
}
- (void)channelShellDidClose:(NMSSHChannel *)channel {
dispatch_async(dispatch_get_main_queue(), ^{
[self appendToTextView:@"\nShell closed\n"];
self.textView.editable = NO;
});
}
- (NSString *)session:(NMSSHSession *)session keyboardInteractiveRequest:(NSString *)request {
dispatch_async(dispatch_get_main_queue(), ^{
[self appendToTextView:request];
self.textView.editable = YES;
});
self.semaphore = dispatch_semaphore_create(0);
dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);
self.semaphore = nil;
return self.password;
}
- (void)session:(NMSSHSession *)session didDisconnectWithError:(NSError *)error {
dispatch_async(dispatch_get_main_queue(), ^{
[self appendToTextView:[NSString stringWithFormat:@"\nDisconnected with error: %@", error.localizedDescription]];
self.textView.editable = NO;
});
}
- (void)textViewDidChange:(UITextView *)textView {
[textView scrollRangeToVisible:NSMakeRange([textView.text length] - 1, 1)];
}
- (void)textViewDidChangeSelection:(UITextView *)textView {
if (textView.selectedRange.location < textView.text.length - self.lastCommand.length - 1) {
textView.selectedRange = NSMakeRange([textView.text length], 0);
}
}
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
if (text.length == 0) {
if ([self.lastCommand length] > 0) {
[self.lastCommand replaceCharactersInRange:NSMakeRange(self.lastCommand.length-1, 1) withString:@""];
return YES;
}
else {
return NO;
}
}
[self.lastCommand appendString:text];
if ([text isEqualToString:@"\n"]) {
[self performCommand];
}
return YES;
}
- (void)keyboardWillShow:(NSNotification *)notification {
NSDictionary *userInfo = [notification userInfo];
CGRect keyboardFrame = [userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
CGRect ownFrame = [[[[UIApplication sharedApplication] delegate] window] convertRect:self.textView.frame fromView:self.textView.superview];
CGRect coveredFrame = CGRectIntersection(ownFrame, keyboardFrame);
coveredFrame = [[[[UIApplication sharedApplication] delegate] window] convertRect:coveredFrame toView:self.textView.superview];
self.textView.contentInset = UIEdgeInsetsMake(self.textView.contentInset.top, 0, coveredFrame.size.height, 0);
self.textView.scrollIndicatorInsets = self.textView.contentInset;
}
- (void)keyboardWillHide:(NSNotification *)notification {
self.textView.contentInset = UIEdgeInsetsMake(self.textView.contentInset.top, 0, 0, 0);
self.textView.scrollIndicatorInsets = self.textView.contentInset;
}
@end

View File

@@ -0,0 +1,13 @@
#import <UIKit/UIKit.h>
@interface NMViewController : UIViewController
@property (nonatomic) IBOutlet UITextField *hostField;
@property (nonatomic) IBOutlet UITextField *usernameField;
@property (nonatomic) IBOutlet UITextField *passwordField;
@property (nonatomic) IBOutlet UISegmentedControl *authenticationControl;
- (IBAction)login:(id)sender;
- (IBAction)authentication:(id)sender;
@end

View File

@@ -0,0 +1,74 @@
#import "NMViewController.h"
#import "NMTerminalViewController.h"
@interface NMViewController ()
@end
@implementation NMViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.hostField.text = [[NSUserDefaults standardUserDefaults] stringForKey:@"host"];
self.usernameField.text = [[NSUserDefaults standardUserDefaults] stringForKey:@"username"];
self.authenticationControl.selectedSegmentIndex = [[NSUserDefaults standardUserDefaults] integerForKey:@"auth"];
}
- (IBAction)hostend:(id)sender {
[self.hostField resignFirstResponder];
}
- (IBAction)usernameend:(id)sender {
[self.usernameField resignFirstResponder];
}
- (IBAction)passwordend:(id)sender {
[self.passwordField resignFirstResponder];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[self.hostField resignFirstResponder];
[self.usernameField resignFirstResponder];
[self.passwordField resignFirstResponder];
}
- (IBAction)authentication:(id)sender {
self.passwordField.enabled = self.authenticationControl.selectedSegmentIndex == 0;
}
- (IBAction)login:(id)sender {
if (self.hostField.text.length == 0 || self.usernameField.text.length == 0 || (self.authenticationControl.selectedSegmentIndex == 0 && self.passwordField.text.length == 0)) {
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Error"
message:@"All fields are required!"
delegate:Nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil];
[alertView show];
}
else {
[self performSegueWithIdentifier:@"loginSegue" sender:self];
}
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier] isEqualToString:@"loginSegue"]) {
[[NSUserDefaults standardUserDefaults] setObject:self.hostField.text forKey:@"host"];
[[NSUserDefaults standardUserDefaults] setObject:self.usernameField.text forKey:@"username"];
[[NSUserDefaults standardUserDefaults] setObject:@(self.authenticationControl.selectedSegmentIndex) forKey:@"auth"];
NMTerminalViewController *terminalController = [segue destinationViewController];
terminalController.host = self.hostField.text;
terminalController.username = self.usernameField.text;
if (self.authenticationControl.selectedSegmentIndex == 0) {
terminalController.password = self.passwordField.text;
}
else {
terminalController.password = nil;
}
}
}
@end

View File

@@ -0,0 +1,42 @@
<?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>en</string>
<key>CFBundleDisplayName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</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>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>Launch Screen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>

View File

@@ -0,0 +1,16 @@
//
// Prefix header
//
// The contents of this file are implicitly included at the beginning of every source file.
//
#import <Availability.h>
#ifndef __IPHONE_5_0
#warning "This project uses features only available in iOS SDK 5.0 and later."
#endif
#ifdef __OBJC__
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#endif

View File

@@ -0,0 +1,2 @@
/* Localized versions of Info.plist keys */

View File

@@ -0,0 +1,10 @@
#import <UIKit/UIKit.h>
#import "NMAppDelegate.h"
int main(int argc, char * argv[])
{
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([NMAppDelegate class]));
}
}

7
Carthage/Checkouts/NMSSH/LICENSE vendored Normal file
View File

@@ -0,0 +1,7 @@
Copyright (c) 2013 Nine Muses AB
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.

View File

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,118 @@
/* Copyright (c) 2004-2006, Sara Golemon <sarag@libssh2.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms,
* with or without modification, are permitted provided
* that the following conditions are met:
*
* Redistributions of source code must retain the above
* copyright notice, this list of conditions and the
* following disclaimer.
*
* Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* Neither the name of the copyright holder nor the names
* of any other contributors may be used to endorse or
* promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*/
/* Note: This include file is only needed for using the
* publickey SUBSYSTEM which is not the same as publickey
* authentication. For authentication you only need libssh2.h
*
* For more information on the publickey subsystem,
* refer to IETF draft: secsh-publickey
*/
#ifndef LIBSSH2_PUBLICKEY_H
#define LIBSSH2_PUBLICKEY_H 1
#include "libssh2.h"
typedef struct _LIBSSH2_PUBLICKEY LIBSSH2_PUBLICKEY;
typedef struct _libssh2_publickey_attribute {
const char *name;
unsigned long name_len;
const char *value;
unsigned long value_len;
char mandatory;
} libssh2_publickey_attribute;
typedef struct _libssh2_publickey_list {
unsigned char *packet; /* For freeing */
const unsigned char *name;
unsigned long name_len;
const unsigned char *blob;
unsigned long blob_len;
unsigned long num_attrs;
libssh2_publickey_attribute *attrs; /* free me */
} libssh2_publickey_list;
/* Generally use the first macro here, but if both name and value are string literals, you can use _fast() to take advantage of preprocessing */
#define libssh2_publickey_attribute(name, value, mandatory) \
{ (name), strlen(name), (value), strlen(value), (mandatory) },
#define libssh2_publickey_attribute_fast(name, value, mandatory) \
{ (name), sizeof(name) - 1, (value), sizeof(value) - 1, (mandatory) },
#ifdef __cplusplus
extern "C" {
#endif
/* Publickey Subsystem */
LIBSSH2_API LIBSSH2_PUBLICKEY *libssh2_publickey_init(LIBSSH2_SESSION *session);
LIBSSH2_API int libssh2_publickey_add_ex(LIBSSH2_PUBLICKEY *pkey,
const unsigned char *name,
unsigned long name_len,
const unsigned char *blob,
unsigned long blob_len, char overwrite,
unsigned long num_attrs,
const libssh2_publickey_attribute attrs[]);
#define libssh2_publickey_add(pkey, name, blob, blob_len, overwrite, \
num_attrs, attrs) \
libssh2_publickey_add_ex((pkey), (name), strlen(name), (blob), (blob_len), \
(overwrite), (num_attrs), (attrs))
LIBSSH2_API int libssh2_publickey_remove_ex(LIBSSH2_PUBLICKEY *pkey,
const unsigned char *name,
unsigned long name_len,
const unsigned char *blob,
unsigned long blob_len);
#define libssh2_publickey_remove(pkey, name, blob, blob_len) \
libssh2_publickey_remove_ex((pkey), (name), strlen(name), (blob), (blob_len))
LIBSSH2_API int
libssh2_publickey_list_fetch(LIBSSH2_PUBLICKEY *pkey,
unsigned long *num_keys,
libssh2_publickey_list **pkey_list);
LIBSSH2_API void libssh2_publickey_list_free(LIBSSH2_PUBLICKEY *pkey,
libssh2_publickey_list *pkey_list);
LIBSSH2_API int libssh2_publickey_shutdown(LIBSSH2_PUBLICKEY *pkey);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* ifndef: LIBSSH2_PUBLICKEY_H */

View File

@@ -0,0 +1,346 @@
/* Copyright (c) 2004-2008, Sara Golemon <sarag@libssh2.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms,
* with or without modification, are permitted provided
* that the following conditions are met:
*
* Redistributions of source code must retain the above
* copyright notice, this list of conditions and the
* following disclaimer.
*
* Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* Neither the name of the copyright holder nor the names
* of any other contributors may be used to endorse or
* promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*/
#ifndef LIBSSH2_SFTP_H
#define LIBSSH2_SFTP_H 1
#include "libssh2.h"
#ifndef WIN32
#include <unistd.h>
#endif
#ifdef __cplusplus
extern "C" {
#endif
/* Note: Version 6 was documented at the time of writing
* However it was marked as "DO NOT IMPLEMENT" due to pending changes
*
* Let's start with Version 3 (The version found in OpenSSH) and go from there
*/
#define LIBSSH2_SFTP_VERSION 3
typedef struct _LIBSSH2_SFTP LIBSSH2_SFTP;
typedef struct _LIBSSH2_SFTP_HANDLE LIBSSH2_SFTP_HANDLE;
typedef struct _LIBSSH2_SFTP_ATTRIBUTES LIBSSH2_SFTP_ATTRIBUTES;
typedef struct _LIBSSH2_SFTP_STATVFS LIBSSH2_SFTP_STATVFS;
/* Flags for open_ex() */
#define LIBSSH2_SFTP_OPENFILE 0
#define LIBSSH2_SFTP_OPENDIR 1
/* Flags for rename_ex() */
#define LIBSSH2_SFTP_RENAME_OVERWRITE 0x00000001
#define LIBSSH2_SFTP_RENAME_ATOMIC 0x00000002
#define LIBSSH2_SFTP_RENAME_NATIVE 0x00000004
/* Flags for stat_ex() */
#define LIBSSH2_SFTP_STAT 0
#define LIBSSH2_SFTP_LSTAT 1
#define LIBSSH2_SFTP_SETSTAT 2
/* Flags for symlink_ex() */
#define LIBSSH2_SFTP_SYMLINK 0
#define LIBSSH2_SFTP_READLINK 1
#define LIBSSH2_SFTP_REALPATH 2
/* SFTP attribute flag bits */
#define LIBSSH2_SFTP_ATTR_SIZE 0x00000001
#define LIBSSH2_SFTP_ATTR_UIDGID 0x00000002
#define LIBSSH2_SFTP_ATTR_PERMISSIONS 0x00000004
#define LIBSSH2_SFTP_ATTR_ACMODTIME 0x00000008
#define LIBSSH2_SFTP_ATTR_EXTENDED 0x80000000
/* SFTP statvfs flag bits */
#define LIBSSH2_SFTP_ST_RDONLY 0x00000001
#define LIBSSH2_SFTP_ST_NOSUID 0x00000002
struct _LIBSSH2_SFTP_ATTRIBUTES {
/* If flags & ATTR_* bit is set, then the value in this struct will be
* meaningful Otherwise it should be ignored
*/
unsigned long flags;
libssh2_uint64_t filesize;
unsigned long uid, gid;
unsigned long permissions;
unsigned long atime, mtime;
};
struct _LIBSSH2_SFTP_STATVFS {
libssh2_uint64_t f_bsize; /* file system block size */
libssh2_uint64_t f_frsize; /* fragment size */
libssh2_uint64_t f_blocks; /* size of fs in f_frsize units */
libssh2_uint64_t f_bfree; /* # free blocks */
libssh2_uint64_t f_bavail; /* # free blocks for non-root */
libssh2_uint64_t f_files; /* # inodes */
libssh2_uint64_t f_ffree; /* # free inodes */
libssh2_uint64_t f_favail; /* # free inodes for non-root */
libssh2_uint64_t f_fsid; /* file system ID */
libssh2_uint64_t f_flag; /* mount flags */
libssh2_uint64_t f_namemax; /* maximum filename length */
};
/* SFTP filetypes */
#define LIBSSH2_SFTP_TYPE_REGULAR 1
#define LIBSSH2_SFTP_TYPE_DIRECTORY 2
#define LIBSSH2_SFTP_TYPE_SYMLINK 3
#define LIBSSH2_SFTP_TYPE_SPECIAL 4
#define LIBSSH2_SFTP_TYPE_UNKNOWN 5
#define LIBSSH2_SFTP_TYPE_SOCKET 6
#define LIBSSH2_SFTP_TYPE_CHAR_DEVICE 7
#define LIBSSH2_SFTP_TYPE_BLOCK_DEVICE 8
#define LIBSSH2_SFTP_TYPE_FIFO 9
/*
* Reproduce the POSIX file modes here for systems that are not POSIX
* compliant.
*
* These is used in "permissions" of "struct _LIBSSH2_SFTP_ATTRIBUTES"
*/
/* File type */
#define LIBSSH2_SFTP_S_IFMT 0170000 /* type of file mask */
#define LIBSSH2_SFTP_S_IFIFO 0010000 /* named pipe (fifo) */
#define LIBSSH2_SFTP_S_IFCHR 0020000 /* character special */
#define LIBSSH2_SFTP_S_IFDIR 0040000 /* directory */
#define LIBSSH2_SFTP_S_IFBLK 0060000 /* block special */
#define LIBSSH2_SFTP_S_IFREG 0100000 /* regular */
#define LIBSSH2_SFTP_S_IFLNK 0120000 /* symbolic link */
#define LIBSSH2_SFTP_S_IFSOCK 0140000 /* socket */
/* File mode */
/* Read, write, execute/search by owner */
#define LIBSSH2_SFTP_S_IRWXU 0000700 /* RWX mask for owner */
#define LIBSSH2_SFTP_S_IRUSR 0000400 /* R for owner */
#define LIBSSH2_SFTP_S_IWUSR 0000200 /* W for owner */
#define LIBSSH2_SFTP_S_IXUSR 0000100 /* X for owner */
/* Read, write, execute/search by group */
#define LIBSSH2_SFTP_S_IRWXG 0000070 /* RWX mask for group */
#define LIBSSH2_SFTP_S_IRGRP 0000040 /* R for group */
#define LIBSSH2_SFTP_S_IWGRP 0000020 /* W for group */
#define LIBSSH2_SFTP_S_IXGRP 0000010 /* X for group */
/* Read, write, execute/search by others */
#define LIBSSH2_SFTP_S_IRWXO 0000007 /* RWX mask for other */
#define LIBSSH2_SFTP_S_IROTH 0000004 /* R for other */
#define LIBSSH2_SFTP_S_IWOTH 0000002 /* W for other */
#define LIBSSH2_SFTP_S_IXOTH 0000001 /* X for other */
/* macros to check for specific file types, added in 1.2.5 */
#define LIBSSH2_SFTP_S_ISLNK(m) \
(((m) & LIBSSH2_SFTP_S_IFMT) == LIBSSH2_SFTP_S_IFLNK)
#define LIBSSH2_SFTP_S_ISREG(m) \
(((m) & LIBSSH2_SFTP_S_IFMT) == LIBSSH2_SFTP_S_IFREG)
#define LIBSSH2_SFTP_S_ISDIR(m) \
(((m) & LIBSSH2_SFTP_S_IFMT) == LIBSSH2_SFTP_S_IFDIR)
#define LIBSSH2_SFTP_S_ISCHR(m) \
(((m) & LIBSSH2_SFTP_S_IFMT) == LIBSSH2_SFTP_S_IFCHR)
#define LIBSSH2_SFTP_S_ISBLK(m) \
(((m) & LIBSSH2_SFTP_S_IFMT) == LIBSSH2_SFTP_S_IFBLK)
#define LIBSSH2_SFTP_S_ISFIFO(m) \
(((m) & LIBSSH2_SFTP_S_IFMT) == LIBSSH2_SFTP_S_IFIFO)
#define LIBSSH2_SFTP_S_ISSOCK(m) \
(((m) & LIBSSH2_SFTP_S_IFMT) == LIBSSH2_SFTP_S_IFSOCK)
/* SFTP File Transfer Flags -- (e.g. flags parameter to sftp_open())
* Danger will robinson... APPEND doesn't have any effect on OpenSSH servers */
#define LIBSSH2_FXF_READ 0x00000001
#define LIBSSH2_FXF_WRITE 0x00000002
#define LIBSSH2_FXF_APPEND 0x00000004
#define LIBSSH2_FXF_CREAT 0x00000008
#define LIBSSH2_FXF_TRUNC 0x00000010
#define LIBSSH2_FXF_EXCL 0x00000020
/* SFTP Status Codes (returned by libssh2_sftp_last_error() ) */
#define LIBSSH2_FX_OK 0
#define LIBSSH2_FX_EOF 1
#define LIBSSH2_FX_NO_SUCH_FILE 2
#define LIBSSH2_FX_PERMISSION_DENIED 3
#define LIBSSH2_FX_FAILURE 4
#define LIBSSH2_FX_BAD_MESSAGE 5
#define LIBSSH2_FX_NO_CONNECTION 6
#define LIBSSH2_FX_CONNECTION_LOST 7
#define LIBSSH2_FX_OP_UNSUPPORTED 8
#define LIBSSH2_FX_INVALID_HANDLE 9
#define LIBSSH2_FX_NO_SUCH_PATH 10
#define LIBSSH2_FX_FILE_ALREADY_EXISTS 11
#define LIBSSH2_FX_WRITE_PROTECT 12
#define LIBSSH2_FX_NO_MEDIA 13
#define LIBSSH2_FX_NO_SPACE_ON_FILESYSTEM 14
#define LIBSSH2_FX_QUOTA_EXCEEDED 15
#define LIBSSH2_FX_UNKNOWN_PRINCIPLE 16 /* Initial mis-spelling */
#define LIBSSH2_FX_UNKNOWN_PRINCIPAL 16
#define LIBSSH2_FX_LOCK_CONFlICT 17 /* Initial mis-spelling */
#define LIBSSH2_FX_LOCK_CONFLICT 17
#define LIBSSH2_FX_DIR_NOT_EMPTY 18
#define LIBSSH2_FX_NOT_A_DIRECTORY 19
#define LIBSSH2_FX_INVALID_FILENAME 20
#define LIBSSH2_FX_LINK_LOOP 21
/* Returned by any function that would block during a read/write opperation */
#define LIBSSH2SFTP_EAGAIN LIBSSH2_ERROR_EAGAIN
/* SFTP API */
LIBSSH2_API LIBSSH2_SFTP *libssh2_sftp_init(LIBSSH2_SESSION *session);
LIBSSH2_API int libssh2_sftp_shutdown(LIBSSH2_SFTP *sftp);
LIBSSH2_API unsigned long libssh2_sftp_last_error(LIBSSH2_SFTP *sftp);
LIBSSH2_API LIBSSH2_CHANNEL *libssh2_sftp_get_channel(LIBSSH2_SFTP *sftp);
/* File / Directory Ops */
LIBSSH2_API LIBSSH2_SFTP_HANDLE *libssh2_sftp_open_ex(LIBSSH2_SFTP *sftp,
const char *filename,
unsigned int filename_len,
unsigned long flags,
long mode, int open_type);
#define libssh2_sftp_open(sftp, filename, flags, mode) \
libssh2_sftp_open_ex((sftp), (filename), strlen(filename), (flags), \
(mode), LIBSSH2_SFTP_OPENFILE)
#define libssh2_sftp_opendir(sftp, path) \
libssh2_sftp_open_ex((sftp), (path), strlen(path), 0, 0, \
LIBSSH2_SFTP_OPENDIR)
LIBSSH2_API ssize_t libssh2_sftp_read(LIBSSH2_SFTP_HANDLE *handle,
char *buffer, size_t buffer_maxlen);
LIBSSH2_API int libssh2_sftp_readdir_ex(LIBSSH2_SFTP_HANDLE *handle, \
char *buffer, size_t buffer_maxlen,
char *longentry,
size_t longentry_maxlen,
LIBSSH2_SFTP_ATTRIBUTES *attrs);
#define libssh2_sftp_readdir(handle, buffer, buffer_maxlen, attrs) \
libssh2_sftp_readdir_ex((handle), (buffer), (buffer_maxlen), NULL, 0, \
(attrs))
LIBSSH2_API ssize_t libssh2_sftp_write(LIBSSH2_SFTP_HANDLE *handle,
const char *buffer, size_t count);
LIBSSH2_API int libssh2_sftp_fsync(LIBSSH2_SFTP_HANDLE *handle);
LIBSSH2_API int libssh2_sftp_close_handle(LIBSSH2_SFTP_HANDLE *handle);
#define libssh2_sftp_close(handle) libssh2_sftp_close_handle(handle)
#define libssh2_sftp_closedir(handle) libssh2_sftp_close_handle(handle)
LIBSSH2_API void libssh2_sftp_seek(LIBSSH2_SFTP_HANDLE *handle, size_t offset);
LIBSSH2_API void libssh2_sftp_seek64(LIBSSH2_SFTP_HANDLE *handle,
libssh2_uint64_t offset);
#define libssh2_sftp_rewind(handle) libssh2_sftp_seek64((handle), 0)
LIBSSH2_API size_t libssh2_sftp_tell(LIBSSH2_SFTP_HANDLE *handle);
LIBSSH2_API libssh2_uint64_t libssh2_sftp_tell64(LIBSSH2_SFTP_HANDLE *handle);
LIBSSH2_API int libssh2_sftp_fstat_ex(LIBSSH2_SFTP_HANDLE *handle,
LIBSSH2_SFTP_ATTRIBUTES *attrs,
int setstat);
#define libssh2_sftp_fstat(handle, attrs) \
libssh2_sftp_fstat_ex((handle), (attrs), 0)
#define libssh2_sftp_fsetstat(handle, attrs) \
libssh2_sftp_fstat_ex((handle), (attrs), 1)
/* Miscellaneous Ops */
LIBSSH2_API int libssh2_sftp_rename_ex(LIBSSH2_SFTP *sftp,
const char *source_filename,
unsigned int srouce_filename_len,
const char *dest_filename,
unsigned int dest_filename_len,
long flags);
#define libssh2_sftp_rename(sftp, sourcefile, destfile) \
libssh2_sftp_rename_ex((sftp), (sourcefile), strlen(sourcefile), \
(destfile), strlen(destfile), \
LIBSSH2_SFTP_RENAME_OVERWRITE | \
LIBSSH2_SFTP_RENAME_ATOMIC | \
LIBSSH2_SFTP_RENAME_NATIVE)
LIBSSH2_API int libssh2_sftp_unlink_ex(LIBSSH2_SFTP *sftp,
const char *filename,
unsigned int filename_len);
#define libssh2_sftp_unlink(sftp, filename) \
libssh2_sftp_unlink_ex((sftp), (filename), strlen(filename))
LIBSSH2_API int libssh2_sftp_fstatvfs(LIBSSH2_SFTP_HANDLE *handle,
LIBSSH2_SFTP_STATVFS *st);
LIBSSH2_API int libssh2_sftp_statvfs(LIBSSH2_SFTP *sftp,
const char *path,
size_t path_len,
LIBSSH2_SFTP_STATVFS *st);
LIBSSH2_API int libssh2_sftp_mkdir_ex(LIBSSH2_SFTP *sftp,
const char *path,
unsigned int path_len, long mode);
#define libssh2_sftp_mkdir(sftp, path, mode) \
libssh2_sftp_mkdir_ex((sftp), (path), strlen(path), (mode))
LIBSSH2_API int libssh2_sftp_rmdir_ex(LIBSSH2_SFTP *sftp,
const char *path,
unsigned int path_len);
#define libssh2_sftp_rmdir(sftp, path) \
libssh2_sftp_rmdir_ex((sftp), (path), strlen(path))
LIBSSH2_API int libssh2_sftp_stat_ex(LIBSSH2_SFTP *sftp,
const char *path,
unsigned int path_len,
int stat_type,
LIBSSH2_SFTP_ATTRIBUTES *attrs);
#define libssh2_sftp_stat(sftp, path, attrs) \
libssh2_sftp_stat_ex((sftp), (path), strlen(path), LIBSSH2_SFTP_STAT, \
(attrs))
#define libssh2_sftp_lstat(sftp, path, attrs) \
libssh2_sftp_stat_ex((sftp), (path), strlen(path), LIBSSH2_SFTP_LSTAT, \
(attrs))
#define libssh2_sftp_setstat(sftp, path, attrs) \
libssh2_sftp_stat_ex((sftp), (path), strlen(path), LIBSSH2_SFTP_SETSTAT, \
(attrs))
LIBSSH2_API int libssh2_sftp_symlink_ex(LIBSSH2_SFTP *sftp,
const char *path,
unsigned int path_len,
char *target,
unsigned int target_len, int link_type);
#define libssh2_sftp_symlink(sftp, orig, linkpath) \
libssh2_sftp_symlink_ex((sftp), (orig), strlen(orig), (linkpath), \
strlen(linkpath), LIBSSH2_SFTP_SYMLINK)
#define libssh2_sftp_readlink(sftp, path, target, maxlen) \
libssh2_sftp_symlink_ex((sftp), (path), strlen(path), (target), (maxlen), \
LIBSSH2_SFTP_READLINK)
#define libssh2_sftp_realpath(sftp, path, target, maxlen) \
libssh2_sftp_symlink_ex((sftp), (path), strlen(path), (target), (maxlen), \
LIBSSH2_SFTP_REALPATH)
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* LIBSSH2_SFTP_H */

View File

Binary file not shown.

View File

Binary file not shown.

View File

Binary file not shown.

View File

@@ -0,0 +1,778 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXAggregateTarget section */
18A0965D17D6A97C008B76FB /* NMSSH Framework */ = {
isa = PBXAggregateTarget;
buildConfigurationList = 18A0965E17D6A97C008B76FB /* Build configuration list for PBXAggregateTarget "NMSSH Framework" */;
buildPhases = (
18A0966117D6A992008B76FB /* Build Frameworks */,
18A0966217D6A994008B76FB /* Lipo Frameworks */,
);
dependencies = (
);
name = "NMSSH Framework";
productName = "NMSSH Framework";
};
/* End PBXAggregateTarget section */
/* Begin PBXBuildFile section */
186CC9721B69117D00F674C4 /* NMSSH.h in Headers */ = {isa = PBXBuildFile; fileRef = 186CC9711B69117D00F674C4 /* NMSSH.h */; settings = {ATTRIBUTES = (Public, ); }; };
186CC9731B69123900F674C4 /* libssh2_publickey.h in Headers */ = {isa = PBXBuildFile; fileRef = 18A0968117D6AA7B008B76FB /* libssh2_publickey.h */; };
186CC9741B69123900F674C4 /* NMSSH+Protected.h in Headers */ = {isa = PBXBuildFile; fileRef = 18B4FE82188C8195004E05FF /* NMSSH+Protected.h */; };
186CC9751B69125400F674C4 /* NMSSHChannel.h in Headers */ = {isa = PBXBuildFile; fileRef = 18A0966D17D6AA51008B76FB /* NMSSHChannel.h */; settings = {ATTRIBUTES = (Public, ); }; };
186CC9761B69125400F674C4 /* NMSFTP.h in Headers */ = {isa = PBXBuildFile; fileRef = 18A0966A17D6AA51008B76FB /* NMSFTP.h */; settings = {ATTRIBUTES = (Public, ); }; };
186CC9771B69125400F674C4 /* NMSFTPFile.h in Headers */ = {isa = PBXBuildFile; fileRef = E46F9E1F188AC7010056E5DB /* NMSFTPFile.h */; settings = {ATTRIBUTES = (Public, ); }; };
186CC9781B69125400F674C4 /* NMSSHSession.h in Headers */ = {isa = PBXBuildFile; fileRef = 18A0966F17D6AA51008B76FB /* NMSSHSession.h */; settings = {ATTRIBUTES = (Public, ); }; };
186CC9791B69125400F674C4 /* NMSSHConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = 18A197C0191FA77A0004D88E /* NMSSHConfig.h */; settings = {ATTRIBUTES = (Public, ); }; };
186CC97A1B69125400F674C4 /* NMSSHHostConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = 18A197C2191FA77A0004D88E /* NMSSHHostConfig.h */; settings = {ATTRIBUTES = (Public, ); }; };
186CC97B1B69125500F674C4 /* libssh2.h in Headers */ = {isa = PBXBuildFile; fileRef = 18A0968017D6AA7B008B76FB /* libssh2.h */; settings = {ATTRIBUTES = (Public, ); }; };
186CC97C1B69125500F674C4 /* libssh2_sftp.h in Headers */ = {isa = PBXBuildFile; fileRef = 18A0968217D6AA7B008B76FB /* libssh2_sftp.h */; settings = {ATTRIBUTES = (Public, ); }; };
186CC97D1B69125500F674C4 /* NMSSHChannelDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 18A0967917D6AA64008B76FB /* NMSSHChannelDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; };
186CC97E1B69125500F674C4 /* NMSSHSessionDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 18A0967A17D6AA64008B76FB /* NMSSHSessionDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; };
186CC97F1B69125500F674C4 /* socket_helper.h in Headers */ = {isa = PBXBuildFile; fileRef = 18A0966517D6AA3D008B76FB /* socket_helper.h */; };
186CC9801B69125500F674C4 /* NMSSHLogger.h in Headers */ = {isa = PBXBuildFile; fileRef = 18F1A2D018158D78000635AB /* NMSSHLogger.h */; settings = {ATTRIBUTES = (Public, ); }; };
186CC9811B69127600F674C4 /* libcrypto.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 18A096D017D6AA7B008B76FB /* libcrypto.a */; };
186CC9821B69127600F674C4 /* libssh2.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 18A096D117D6AA7B008B76FB /* libssh2.a */; };
186CC9831B69127600F674C4 /* libssl.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 18A096D217D6AA7B008B76FB /* libssl.a */; };
186CC9841B69142600F674C4 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 18A0972417D6AAA8008B76FB /* libz.dylib */; };
186CC9851B69144800F674C4 /* NMSSHChannel.m in Sources */ = {isa = PBXBuildFile; fileRef = 18A0966E17D6AA51008B76FB /* NMSSHChannel.m */; };
186CC9861B69144800F674C4 /* NMSFTP.m in Sources */ = {isa = PBXBuildFile; fileRef = 18A0966B17D6AA51008B76FB /* NMSFTP.m */; };
186CC9871B69144800F674C4 /* NMSFTPFile.m in Sources */ = {isa = PBXBuildFile; fileRef = E46F9E20188AC7010056E5DB /* NMSFTPFile.m */; };
186CC9881B69144800F674C4 /* NMSSHSession.m in Sources */ = {isa = PBXBuildFile; fileRef = 18A0967017D6AA51008B76FB /* NMSSHSession.m */; };
186CC9891B69144800F674C4 /* NMSSHConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = 18A197C1191FA77A0004D88E /* NMSSHConfig.m */; };
186CC98A1B69144800F674C4 /* NMSSHHostConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = 18A197C3191FA77A0004D88E /* NMSSHHostConfig.m */; };
186CC98B1B69144800F674C4 /* socket_helper.m in Sources */ = {isa = PBXBuildFile; fileRef = 18A0966617D6AA3D008B76FB /* socket_helper.m */; };
186CC98C1B69144800F674C4 /* NMSSHLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = 18F1A2D118158D78000635AB /* NMSSHLogger.m */; };
18A0966817D6AA3D008B76FB /* socket_helper.h in Headers */ = {isa = PBXBuildFile; fileRef = 18A0966517D6AA3D008B76FB /* socket_helper.h */; };
18A0966917D6AA3D008B76FB /* socket_helper.m in Sources */ = {isa = PBXBuildFile; fileRef = 18A0966617D6AA3D008B76FB /* socket_helper.m */; };
18A0967117D6AA51008B76FB /* NMSFTP.h in Headers */ = {isa = PBXBuildFile; fileRef = 18A0966A17D6AA51008B76FB /* NMSFTP.h */; settings = {ATTRIBUTES = (Public, ); }; };
18A0967217D6AA51008B76FB /* NMSFTP.m in Sources */ = {isa = PBXBuildFile; fileRef = 18A0966B17D6AA51008B76FB /* NMSFTP.m */; };
18A0967317D6AA51008B76FB /* NMSSH.h in Headers */ = {isa = PBXBuildFile; fileRef = 18A0966C17D6AA51008B76FB /* NMSSH.h */; settings = {ATTRIBUTES = (Public, ); }; };
18A0967417D6AA51008B76FB /* NMSSHChannel.h in Headers */ = {isa = PBXBuildFile; fileRef = 18A0966D17D6AA51008B76FB /* NMSSHChannel.h */; settings = {ATTRIBUTES = (Public, ); }; };
18A0967517D6AA51008B76FB /* NMSSHChannel.m in Sources */ = {isa = PBXBuildFile; fileRef = 18A0966E17D6AA51008B76FB /* NMSSHChannel.m */; };
18A0967617D6AA51008B76FB /* NMSSHSession.h in Headers */ = {isa = PBXBuildFile; fileRef = 18A0966F17D6AA51008B76FB /* NMSSHSession.h */; settings = {ATTRIBUTES = (Public, ); }; };
18A0967717D6AA51008B76FB /* NMSSHSession.m in Sources */ = {isa = PBXBuildFile; fileRef = 18A0967017D6AA51008B76FB /* NMSSHSession.m */; };
18A0967B17D6AA64008B76FB /* NMSSHChannelDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 18A0967917D6AA64008B76FB /* NMSSHChannelDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; };
18A0967C17D6AA64008B76FB /* NMSSHSessionDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 18A0967A17D6AA64008B76FB /* NMSSHSessionDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; };
18A096D317D6AA7B008B76FB /* libssh2.h in Headers */ = {isa = PBXBuildFile; fileRef = 18A0968017D6AA7B008B76FB /* libssh2.h */; settings = {ATTRIBUTES = (Public, ); }; };
18A096D417D6AA7B008B76FB /* libssh2_publickey.h in Headers */ = {isa = PBXBuildFile; fileRef = 18A0968117D6AA7B008B76FB /* libssh2_publickey.h */; };
18A096D517D6AA7B008B76FB /* libssh2_sftp.h in Headers */ = {isa = PBXBuildFile; fileRef = 18A0968217D6AA7B008B76FB /* libssh2_sftp.h */; settings = {ATTRIBUTES = (Public, ); }; };
18A0972117D6AA7B008B76FB /* libcrypto.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 18A096D017D6AA7B008B76FB /* libcrypto.a */; };
18A0972217D6AA7C008B76FB /* libssh2.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 18A096D117D6AA7B008B76FB /* libssh2.a */; };
18A0972317D6AA7C008B76FB /* libssl.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 18A096D217D6AA7B008B76FB /* libssl.a */; };
18A197C4191FA77A0004D88E /* NMSSHConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = 18A197C0191FA77A0004D88E /* NMSSHConfig.h */; settings = {ATTRIBUTES = (Public, ); }; };
18A197C5191FA77A0004D88E /* NMSSHConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = 18A197C1191FA77A0004D88E /* NMSSHConfig.m */; };
18A197C6191FA77A0004D88E /* NMSSHHostConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = 18A197C2191FA77A0004D88E /* NMSSHHostConfig.h */; settings = {ATTRIBUTES = (Public, ); }; };
18A197C7191FA77A0004D88E /* NMSSHHostConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = 18A197C3191FA77A0004D88E /* NMSSHHostConfig.m */; };
18B4FE83188C8774004E05FF /* NMSSH+Protected.h in Headers */ = {isa = PBXBuildFile; fileRef = 18B4FE82188C8195004E05FF /* NMSSH+Protected.h */; };
18F1A2D218158D78000635AB /* NMSSHLogger.h in Headers */ = {isa = PBXBuildFile; fileRef = 18F1A2D018158D78000635AB /* NMSSHLogger.h */; settings = {ATTRIBUTES = (Public, ); }; };
18F1A2D318158D78000635AB /* NMSSHLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = 18F1A2D118158D78000635AB /* NMSSHLogger.m */; };
E46F9E21188AC7010056E5DB /* NMSFTPFile.h in Headers */ = {isa = PBXBuildFile; fileRef = E46F9E1F188AC7010056E5DB /* NMSFTPFile.h */; settings = {ATTRIBUTES = (Public, ); }; };
E46F9E22188AC7010056E5DB /* NMSFTPFile.m in Sources */ = {isa = PBXBuildFile; fileRef = E46F9E20188AC7010056E5DB /* NMSFTPFile.m */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
186CC9581B69110500F674C4 /* NMSSH.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = NMSSH.framework; sourceTree = BUILT_PRODUCTS_DIR; };
186CC95B1B69110500F674C4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
186CC9711B69117D00F674C4 /* NMSSH.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NMSSH.h; path = "NMSSH-iOS/NMSSH.h"; sourceTree = SOURCE_ROOT; };
18A0963017D6A85F008B76FB /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
18A0964F17D6A8C4008B76FB /* NMSSH Static.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "NMSSH Static.framework"; sourceTree = BUILT_PRODUCTS_DIR; };
18A0965817D6A8C4008B76FB /* NMSSH-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NMSSH-Prefix.pch"; sourceTree = "<group>"; };
18A0966517D6AA3D008B76FB /* socket_helper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = socket_helper.h; sourceTree = "<group>"; };
18A0966617D6AA3D008B76FB /* socket_helper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = socket_helper.m; sourceTree = "<group>"; };
18A0966A17D6AA51008B76FB /* NMSFTP.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NMSFTP.h; sourceTree = "<group>"; };
18A0966B17D6AA51008B76FB /* NMSFTP.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NMSFTP.m; sourceTree = "<group>"; };
18A0966C17D6AA51008B76FB /* NMSSH.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NMSSH.h; sourceTree = "<group>"; };
18A0966D17D6AA51008B76FB /* NMSSHChannel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NMSSHChannel.h; sourceTree = "<group>"; };
18A0966E17D6AA51008B76FB /* NMSSHChannel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NMSSHChannel.m; sourceTree = "<group>"; };
18A0966F17D6AA51008B76FB /* NMSSHSession.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NMSSHSession.h; sourceTree = "<group>"; };
18A0967017D6AA51008B76FB /* NMSSHSession.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NMSSHSession.m; sourceTree = "<group>"; };
18A0967917D6AA64008B76FB /* NMSSHChannelDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NMSSHChannelDelegate.h; sourceTree = "<group>"; };
18A0967A17D6AA64008B76FB /* NMSSHSessionDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NMSSHSessionDelegate.h; sourceTree = "<group>"; };
18A0968017D6AA7B008B76FB /* libssh2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = libssh2.h; sourceTree = "<group>"; };
18A0968117D6AA7B008B76FB /* libssh2_publickey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = libssh2_publickey.h; sourceTree = "<group>"; };
18A0968217D6AA7B008B76FB /* libssh2_sftp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = libssh2_sftp.h; sourceTree = "<group>"; };
18A096D017D6AA7B008B76FB /* libcrypto.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libcrypto.a; sourceTree = "<group>"; };
18A096D117D6AA7B008B76FB /* libssh2.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libssh2.a; sourceTree = "<group>"; };
18A096D217D6AA7B008B76FB /* libssl.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libssl.a; sourceTree = "<group>"; };
18A0972417D6AAA8008B76FB /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; };
18A197C0191FA77A0004D88E /* NMSSHConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NMSSHConfig.h; sourceTree = "<group>"; };
18A197C1191FA77A0004D88E /* NMSSHConfig.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NMSSHConfig.m; sourceTree = "<group>"; };
18A197C2191FA77A0004D88E /* NMSSHHostConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NMSSHHostConfig.h; sourceTree = "<group>"; };
18A197C3191FA77A0004D88E /* NMSSHHostConfig.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NMSSHHostConfig.m; sourceTree = "<group>"; };
18B4FE82188C8195004E05FF /* NMSSH+Protected.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NMSSH+Protected.h"; sourceTree = "<group>"; };
18F1A2D018158D78000635AB /* NMSSHLogger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NMSSHLogger.h; sourceTree = "<group>"; };
18F1A2D118158D78000635AB /* NMSSHLogger.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NMSSHLogger.m; sourceTree = "<group>"; };
E46F9E1F188AC7010056E5DB /* NMSFTPFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NMSFTPFile.h; sourceTree = "<group>"; };
E46F9E20188AC7010056E5DB /* NMSFTPFile.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NMSFTPFile.m; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
186CC9541B69110500F674C4 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
186CC9841B69142600F674C4 /* libz.dylib in Frameworks */,
186CC9811B69127600F674C4 /* libcrypto.a in Frameworks */,
186CC9821B69127600F674C4 /* libssh2.a in Frameworks */,
186CC9831B69127600F674C4 /* libssl.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
18A0964C17D6A8C4008B76FB /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
18A0972117D6AA7B008B76FB /* libcrypto.a in Frameworks */,
18A0972217D6AA7C008B76FB /* libssh2.a in Frameworks */,
18A0972317D6AA7C008B76FB /* libssl.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
186CC9591B69110500F674C4 /* NMSSH */ = {
isa = PBXGroup;
children = (
186CC9711B69117D00F674C4 /* NMSSH.h */,
186CC95A1B69110500F674C4 /* Supporting Files */,
);
path = NMSSH;
sourceTree = "<group>";
};
186CC95A1B69110500F674C4 /* Supporting Files */ = {
isa = PBXGroup;
children = (
186CC95B1B69110500F674C4 /* Info.plist */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
18A0962217D6A85F008B76FB = {
isa = PBXGroup;
children = (
18A0965217D6A8C4008B76FB /* NMSSH */,
186CC9591B69110500F674C4 /* NMSSH */,
18A0962D17D6A85F008B76FB /* Frameworks */,
18A0962C17D6A85F008B76FB /* Products */,
);
sourceTree = "<group>";
};
18A0962C17D6A85F008B76FB /* Products */ = {
isa = PBXGroup;
children = (
18A0964F17D6A8C4008B76FB /* NMSSH Static.framework */,
186CC9581B69110500F674C4 /* NMSSH.framework */,
);
name = Products;
sourceTree = "<group>";
};
18A0962D17D6A85F008B76FB /* Frameworks */ = {
isa = PBXGroup;
children = (
18A0972417D6AAA8008B76FB /* libz.dylib */,
18A0963017D6A85F008B76FB /* Foundation.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
18A0965217D6A8C4008B76FB /* NMSSH */ = {
isa = PBXGroup;
children = (
18A0966C17D6AA51008B76FB /* NMSSH.h */,
18A0966D17D6AA51008B76FB /* NMSSHChannel.h */,
18A0966E17D6AA51008B76FB /* NMSSHChannel.m */,
18A0966A17D6AA51008B76FB /* NMSFTP.h */,
18A0966B17D6AA51008B76FB /* NMSFTP.m */,
E46F9E1F188AC7010056E5DB /* NMSFTPFile.h */,
E46F9E20188AC7010056E5DB /* NMSFTPFile.m */,
18A0966F17D6AA51008B76FB /* NMSSHSession.h */,
18A0967017D6AA51008B76FB /* NMSSHSession.m */,
18A197C0191FA77A0004D88E /* NMSSHConfig.h */,
18A197C1191FA77A0004D88E /* NMSSHConfig.m */,
18A197C2191FA77A0004D88E /* NMSSHHostConfig.h */,
18A197C3191FA77A0004D88E /* NMSSHHostConfig.m */,
18A0967D17D6AA7B008B76FB /* Libraries */,
18A0967817D6AA64008B76FB /* Protocols */,
18A0966317D6AA3D008B76FB /* Config */,
18A0965317D6A8C4008B76FB /* Supporting Files */,
);
path = NMSSH;
sourceTree = "<group>";
};
18A0965317D6A8C4008B76FB /* Supporting Files */ = {
isa = PBXGroup;
children = (
18A0965817D6A8C4008B76FB /* NMSSH-Prefix.pch */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
18A0966317D6AA3D008B76FB /* Config */ = {
isa = PBXGroup;
children = (
18A0966517D6AA3D008B76FB /* socket_helper.h */,
18A0966617D6AA3D008B76FB /* socket_helper.m */,
18F1A2D018158D78000635AB /* NMSSHLogger.h */,
18F1A2D118158D78000635AB /* NMSSHLogger.m */,
18B4FE82188C8195004E05FF /* NMSSH+Protected.h */,
);
path = Config;
sourceTree = "<group>";
};
18A0967817D6AA64008B76FB /* Protocols */ = {
isa = PBXGroup;
children = (
18A0967917D6AA64008B76FB /* NMSSHChannelDelegate.h */,
18A0967A17D6AA64008B76FB /* NMSSHSessionDelegate.h */,
);
path = Protocols;
sourceTree = "<group>";
};
18A0967D17D6AA7B008B76FB /* Libraries */ = {
isa = PBXGroup;
children = (
18A0967E17D6AA7B008B76FB /* include */,
18A096CF17D6AA7B008B76FB /* lib */,
);
name = Libraries;
path = "NMSSH-iOS/Libraries";
sourceTree = SOURCE_ROOT;
};
18A0967E17D6AA7B008B76FB /* include */ = {
isa = PBXGroup;
children = (
18A0967F17D6AA7B008B76FB /* libssh2 */,
);
path = include;
sourceTree = "<group>";
};
18A0967F17D6AA7B008B76FB /* libssh2 */ = {
isa = PBXGroup;
children = (
18A0968017D6AA7B008B76FB /* libssh2.h */,
18A0968117D6AA7B008B76FB /* libssh2_publickey.h */,
18A0968217D6AA7B008B76FB /* libssh2_sftp.h */,
);
path = libssh2;
sourceTree = "<group>";
};
18A096CF17D6AA7B008B76FB /* lib */ = {
isa = PBXGroup;
children = (
18A096D017D6AA7B008B76FB /* libcrypto.a */,
18A096D117D6AA7B008B76FB /* libssh2.a */,
18A096D217D6AA7B008B76FB /* libssl.a */,
);
path = lib;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
186CC9551B69110500F674C4 /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
186CC9721B69117D00F674C4 /* NMSSH.h in Headers */,
186CC9751B69125400F674C4 /* NMSSHChannel.h in Headers */,
186CC9761B69125400F674C4 /* NMSFTP.h in Headers */,
186CC9771B69125400F674C4 /* NMSFTPFile.h in Headers */,
186CC9781B69125400F674C4 /* NMSSHSession.h in Headers */,
186CC9791B69125400F674C4 /* NMSSHConfig.h in Headers */,
186CC97A1B69125400F674C4 /* NMSSHHostConfig.h in Headers */,
186CC97B1B69125500F674C4 /* libssh2.h in Headers */,
186CC97C1B69125500F674C4 /* libssh2_sftp.h in Headers */,
186CC97D1B69125500F674C4 /* NMSSHChannelDelegate.h in Headers */,
186CC97E1B69125500F674C4 /* NMSSHSessionDelegate.h in Headers */,
186CC9801B69125500F674C4 /* NMSSHLogger.h in Headers */,
186CC97F1B69125500F674C4 /* socket_helper.h in Headers */,
186CC9731B69123900F674C4 /* libssh2_publickey.h in Headers */,
186CC9741B69123900F674C4 /* NMSSH+Protected.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
18A0965C17D6A956008B76FB /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
18A0967117D6AA51008B76FB /* NMSFTP.h in Headers */,
E46F9E21188AC7010056E5DB /* NMSFTPFile.h in Headers */,
18A0967317D6AA51008B76FB /* NMSSH.h in Headers */,
18A0967417D6AA51008B76FB /* NMSSHChannel.h in Headers */,
18A0967617D6AA51008B76FB /* NMSSHSession.h in Headers */,
18A0967B17D6AA64008B76FB /* NMSSHChannelDelegate.h in Headers */,
18A0967C17D6AA64008B76FB /* NMSSHSessionDelegate.h in Headers */,
18A197C4191FA77A0004D88E /* NMSSHConfig.h in Headers */,
18F1A2D218158D78000635AB /* NMSSHLogger.h in Headers */,
18A197C6191FA77A0004D88E /* NMSSHHostConfig.h in Headers */,
18A096D317D6AA7B008B76FB /* libssh2.h in Headers */,
18A096D517D6AA7B008B76FB /* libssh2_sftp.h in Headers */,
18B4FE83188C8774004E05FF /* NMSSH+Protected.h in Headers */,
18A0966817D6AA3D008B76FB /* socket_helper.h in Headers */,
18A096D417D6AA7B008B76FB /* libssh2_publickey.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXHeadersBuildPhase section */
/* Begin PBXNativeTarget section */
186CC9571B69110500F674C4 /* NMSSH */ = {
isa = PBXNativeTarget;
buildConfigurationList = 186CC96B1B69110500F674C4 /* Build configuration list for PBXNativeTarget "NMSSH" */;
buildPhases = (
186CC9531B69110500F674C4 /* Sources */,
186CC9541B69110500F674C4 /* Frameworks */,
186CC9551B69110500F674C4 /* Headers */,
186CC9561B69110500F674C4 /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = NMSSH;
productName = NMSSH;
productReference = 186CC9581B69110500F674C4 /* NMSSH.framework */;
productType = "com.apple.product-type.framework";
};
18A0964E17D6A8C4008B76FB /* NMSSH Static */ = {
isa = PBXNativeTarget;
buildConfigurationList = 18A0965B17D6A8C4008B76FB /* Build configuration list for PBXNativeTarget "NMSSH Static" */;
buildPhases = (
18A0964B17D6A8C4008B76FB /* Sources */,
18A0964C17D6A8C4008B76FB /* Frameworks */,
18A0964D17D6A8C4008B76FB /* Resources */,
18A0965C17D6A956008B76FB /* Headers */,
);
buildRules = (
);
dependencies = (
);
name = "NMSSH Static";
productName = NMSSH;
productReference = 18A0964F17D6A8C4008B76FB /* NMSSH Static.framework */;
productType = "com.apple.product-type.bundle";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
18A0962317D6A85F008B76FB /* Project object */ = {
isa = PBXProject;
attributes = {
CLASSPREFIX = NMSSH;
LastUpgradeCheck = 1000;
ORGANIZATIONNAME = "Nine Muses AB";
TargetAttributes = {
186CC9571B69110500F674C4 = {
CreatedOnToolsVersion = 6.4;
DevelopmentTeam = 8JGND254B7;
};
18A0964E17D6A8C4008B76FB = {
DevelopmentTeam = 8JGND254B7;
};
};
};
buildConfigurationList = 18A0962617D6A85F008B76FB /* Build configuration list for PBXProject "NMSSH-iOS" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 18A0962217D6A85F008B76FB;
productRefGroup = 18A0962C17D6A85F008B76FB /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
186CC9571B69110500F674C4 /* NMSSH */,
18A0964E17D6A8C4008B76FB /* NMSSH Static */,
18A0965D17D6A97C008B76FB /* NMSSH Framework */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
186CC9561B69110500F674C4 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
18A0964D17D6A8C4008B76FB /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
18A0966117D6A992008B76FB /* Build Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Build Frameworks";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "# Sets the target folders and the final framework product.\nTARGET=\"NMSSH Static\"\nPROJECT=\"NMSSH-iOS.xcodeproj\"\n\nset -e\n\n# Building iphoneos.\nxcodebuild -configuration \"Release\" -target \"${TARGET}\" -sdk iphoneos -project \"${PROJECT}\"\n\n# Building iphonesimulator.\nxcodebuild -configuration \"Release\" -target \"${TARGET}\" -sdk iphonesimulator -project \"${PROJECT}\"\n\nset +e\n";
};
18A0966217D6A994008B76FB /* Lipo Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Lipo Frameworks";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "# Sets the target folders and the final framework product.\nFMK_NAME=\"NMSSH\"\nFMK_VERSION=\"A\"\nTARGET=\"NMSSH Static\"\n\n# Install dir will be the final output to the framework.\n# The following line create it in the root folder of the current project.\nINSTALL_DIR=\"${SRCROOT}/Products/${FMK_NAME}.framework\"\n\n# Working dir will be deleted after the framework creation.\nWRK_DIR=\"build\"\nDEVICE_DIR=\"${WRK_DIR}/Release-iphoneos/${TARGET}.framework\"\nSIMULATOR_DIR=\"${WRK_DIR}/Release-iphonesimulator/${TARGET}.framework\"\n\nset -e\n\n# Cleaning the oldest.\nif [ -d \"${INSTALL_DIR}\" ]\nthen\nrm -rf \"${INSTALL_DIR}\"\nfi\n\n# Creates and renews the final product folder.\nmkdir -p \"${INSTALL_DIR}\"\nmkdir -p \"${INSTALL_DIR}/Versions\"\nmkdir -p \"${INSTALL_DIR}/Versions/${FMK_VERSION}\"\nmkdir -p \"${INSTALL_DIR}/Versions/${FMK_VERSION}/Resources\"\nmkdir -p \"${INSTALL_DIR}/Versions/${FMK_VERSION}/Headers\"\n\n# Creates the internal links.\n# It MUST uses relative path, otherwise will not work when the folder is copied/moved.\nln -s \"${FMK_VERSION}\" \"${INSTALL_DIR}/Versions/Current\"\nln -s \"Versions/Current/Headers\" \"${INSTALL_DIR}/Headers\"\nln -s \"Versions/Current/Resources\" \"${INSTALL_DIR}/Resources\"\nln -s \"Versions/Current/${FMK_NAME}\" \"${INSTALL_DIR}/${FMK_NAME}\"\n\n# Copies the headers and resources files to the final product folder.\ncp -R \"${DEVICE_DIR}/Headers/\" \"${INSTALL_DIR}/Versions/${FMK_VERSION}/Headers/\"\ncp -R \"${DEVICE_DIR}/\" \"${INSTALL_DIR}/Versions/${FMK_VERSION}/Resources/\"\n\n# Removes the binary and header from the resources folder.\nrm -r \"${INSTALL_DIR}/Versions/${FMK_VERSION}/Resources/Headers\" \"${INSTALL_DIR}/Versions/${FMK_VERSION}/Resources/${TARGET}\"\n\n# Uses the Lipo Tool to merge both binary files into one Universal final product.\nlipo -create \"${DEVICE_DIR}/${TARGET}\" \"${SIMULATOR_DIR}/${TARGET}\" -output \"${INSTALL_DIR}/Versions/${FMK_VERSION}/${FMK_NAME}\"\nrm -r \"${WRK_DIR}\"\n\nset +e\n";
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
186CC9531B69110500F674C4 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
186CC9851B69144800F674C4 /* NMSSHChannel.m in Sources */,
186CC9861B69144800F674C4 /* NMSFTP.m in Sources */,
186CC9871B69144800F674C4 /* NMSFTPFile.m in Sources */,
186CC9881B69144800F674C4 /* NMSSHSession.m in Sources */,
186CC9891B69144800F674C4 /* NMSSHConfig.m in Sources */,
186CC98A1B69144800F674C4 /* NMSSHHostConfig.m in Sources */,
186CC98B1B69144800F674C4 /* socket_helper.m in Sources */,
186CC98C1B69144800F674C4 /* NMSSHLogger.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
18A0964B17D6A8C4008B76FB /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
18A0966917D6AA3D008B76FB /* socket_helper.m in Sources */,
18A197C7191FA77A0004D88E /* NMSSHHostConfig.m in Sources */,
E46F9E22188AC7010056E5DB /* NMSFTPFile.m in Sources */,
18A0967217D6AA51008B76FB /* NMSFTP.m in Sources */,
18A197C5191FA77A0004D88E /* NMSSHConfig.m in Sources */,
18A0967517D6AA51008B76FB /* NMSSHChannel.m in Sources */,
18F1A2D318158D78000635AB /* NMSSHLogger.m in Sources */,
18A0967717D6AA51008B76FB /* NMSSHSession.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
186CC96C1B69110500F674C4 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
CODE_SIGN_IDENTITY = "Apple Development";
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEFINES_MODULE = YES;
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 8JGND254B7;
"DEVELOPMENT_TEAM[sdk=macosx*]" = 8JGND254B7;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_NO_COMMON_BLOCKS = YES;
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;
INFOPLIST_FILE = NMSSH/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 9;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/NMSSH-iOS/Libraries/lib",
);
MTL_ENABLE_DEBUG_INFO = YES;
PRODUCT_BUNDLE_IDENTIFIER = "se.ninemuses.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
name = Debug;
};
186CC96D1B69110500F674C4 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
CODE_SIGN_IDENTITY = "Apple Development";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEFINES_MODULE = YES;
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 8JGND254B7;
"DEVELOPMENT_TEAM[sdk=macosx*]" = 8JGND254B7;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
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;
INFOPLIST_FILE = NMSSH/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 9;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/NMSSH-iOS/Libraries/lib",
);
MTL_ENABLE_DEBUG_INFO = NO;
PRODUCT_BUNDLE_IDENTIFIER = "se.ninemuses.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
name = Release;
};
18A0964617D6A85F008B76FB /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
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_DEPRECATED_OBJC_IMPLEMENTATIONS = 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_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
18A0964717D6A85F008B76FB /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
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_DEPRECATED_OBJC_IMPLEMENTATIONS = 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_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1";
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
18A0965917D6A8C4008B76FB /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
COMBINE_HIDPI_IMAGES = YES;
DEAD_CODE_STRIPPING = NO;
DEVELOPMENT_TEAM = 8JGND254B7;
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "NMSSH/NMSSH-Prefix.pch";
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
INFOPLIST_FILE = "";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles";
IPHONEOS_DEPLOYMENT_TARGET = 12;
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"\"$(SRCROOT)/NMSSH-iOS/Libraries/lib\"",
);
LINK_WITH_STANDARD_LIBRARIES = NO;
MACH_O_TYPE = mh_object;
MACOSX_DEPLOYMENT_TARGET = "";
ONLY_ACTIVE_ARCH = NO;
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = framework;
};
name = Debug;
};
18A0965A17D6A8C4008B76FB /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
COMBINE_HIDPI_IMAGES = YES;
DEAD_CODE_STRIPPING = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = 8JGND254B7;
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "NMSSH/NMSSH-Prefix.pch";
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
INFOPLIST_FILE = "";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles";
IPHONEOS_DEPLOYMENT_TARGET = 12;
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"\"$(SRCROOT)/NMSSH-iOS/Libraries/lib\"",
);
LINK_WITH_STANDARD_LIBRARIES = NO;
MACH_O_TYPE = mh_object;
MACOSX_DEPLOYMENT_TARGET = "";
ONLY_ACTIVE_ARCH = NO;
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = framework;
};
name = Release;
};
18A0965F17D6A97C008B76FB /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
PRODUCT_NAME = "$(TARGET_NAME)";
TVOS_DEPLOYMENT_TARGET = 14.0;
};
name = Debug;
};
18A0966017D6A97C008B76FB /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
PRODUCT_NAME = "$(TARGET_NAME)";
TVOS_DEPLOYMENT_TARGET = 14.0;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
186CC96B1B69110500F674C4 /* Build configuration list for PBXNativeTarget "NMSSH" */ = {
isa = XCConfigurationList;
buildConfigurations = (
186CC96C1B69110500F674C4 /* Debug */,
186CC96D1B69110500F674C4 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
18A0962617D6A85F008B76FB /* Build configuration list for PBXProject "NMSSH-iOS" */ = {
isa = XCConfigurationList;
buildConfigurations = (
18A0964617D6A85F008B76FB /* Debug */,
18A0964717D6A85F008B76FB /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
18A0965B17D6A8C4008B76FB /* Build configuration list for PBXNativeTarget "NMSSH Static" */ = {
isa = XCConfigurationList;
buildConfigurations = (
18A0965917D6A8C4008B76FB /* Debug */,
18A0965A17D6A8C4008B76FB /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
18A0965E17D6A97C008B76FB /* Build configuration list for PBXAggregateTarget "NMSSH Framework" */ = {
isa = XCConfigurationList;
buildConfigurations = (
18A0965F17D6A97C008B76FB /* Debug */,
18A0966017D6A97C008B76FB /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 18A0962317D6A85F008B76FB /* Project object */;
}

View File

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

View File

@@ -0,0 +1,8 @@
<?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>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View File

@@ -0,0 +1,113 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1000"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "186CC9571B69110500F674C4"
BuildableName = "NMSSH.framework"
BlueprintName = "NMSSH"
ReferencedContainer = "container:NMSSH-iOS.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "NO"
buildForArchiving = "NO"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "186CC9611B69110500F674C4"
BuildableName = "NMSSHTests.xctest"
BlueprintName = "NMSSHTests"
ReferencedContainer = "container:NMSSH-iOS.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "186CC9611B69110500F674C4"
BuildableName = "NMSSHTests.xctest"
BlueprintName = "NMSSHTests"
ReferencedContainer = "container:NMSSH-iOS.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "186CC9571B69110500F674C4"
BuildableName = "NMSSH.framework"
BlueprintName = "NMSSH"
ReferencedContainer = "container:NMSSH-iOS.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "186CC9571B69110500F674C4"
BuildableName = "NMSSH.framework"
BlueprintName = "NMSSH"
ReferencedContainer = "container:NMSSH-iOS.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "186CC9571B69110500F674C4"
BuildableName = "NMSSH.framework"
BlueprintName = "NMSSH"
ReferencedContainer = "container:NMSSH-iOS.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,119 @@
/* Copyright (c) 2004-2006, Sara Golemon <sarag@libssh2.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms,
* with or without modification, are permitted provided
* that the following conditions are met:
*
* Redistributions of source code must retain the above
* copyright notice, this list of conditions and the
* following disclaimer.
*
* Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* Neither the name of the copyright holder nor the names
* of any other contributors may be used to endorse or
* promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*/
/* Note: This include file is only needed for using the
* publickey SUBSYSTEM which is not the same as publickey
* authentication. For authentication you only need libssh2.h
*
* For more information on the publickey subsystem,
* refer to IETF draft: secsh-publickey
*/
#ifndef LIBSSH2_PUBLICKEY_H
#define LIBSSH2_PUBLICKEY_H 1
#include "libssh2.h"
typedef struct _LIBSSH2_PUBLICKEY LIBSSH2_PUBLICKEY;
typedef struct _libssh2_publickey_attribute {
const char *name;
unsigned long name_len;
const char *value;
unsigned long value_len;
char mandatory;
} libssh2_publickey_attribute;
typedef struct _libssh2_publickey_list {
unsigned char *packet; /* For freeing */
const unsigned char *name;
unsigned long name_len;
const unsigned char *blob;
unsigned long blob_len;
unsigned long num_attrs;
libssh2_publickey_attribute *attrs; /* free me */
} libssh2_publickey_list;
/* Generally use the first macro here, but if both name and value are string
literals, you can use _fast() to take advantage of preprocessing */
#define libssh2_publickey_attribute(name, value, mandatory) \
{ (name), strlen(name), (value), strlen(value), (mandatory) },
#define libssh2_publickey_attribute_fast(name, value, mandatory) \
{ (name), sizeof(name) - 1, (value), sizeof(value) - 1, (mandatory) },
#ifdef __cplusplus
extern "C" {
#endif
/* Publickey Subsystem */
LIBSSH2_API LIBSSH2_PUBLICKEY *libssh2_publickey_init(LIBSSH2_SESSION *session);
LIBSSH2_API int libssh2_publickey_add_ex(LIBSSH2_PUBLICKEY *pkey,
const unsigned char *name,
unsigned long name_len,
const unsigned char *blob,
unsigned long blob_len, char overwrite,
unsigned long num_attrs,
const libssh2_publickey_attribute attrs[]);
#define libssh2_publickey_add(pkey, name, blob, blob_len, overwrite, \
num_attrs, attrs) \
libssh2_publickey_add_ex((pkey), (name), strlen(name), (blob), (blob_len), \
(overwrite), (num_attrs), (attrs))
LIBSSH2_API int libssh2_publickey_remove_ex(LIBSSH2_PUBLICKEY *pkey,
const unsigned char *name,
unsigned long name_len,
const unsigned char *blob,
unsigned long blob_len);
#define libssh2_publickey_remove(pkey, name, blob, blob_len) \
libssh2_publickey_remove_ex((pkey), (name), strlen(name), (blob), (blob_len))
LIBSSH2_API int
libssh2_publickey_list_fetch(LIBSSH2_PUBLICKEY *pkey,
unsigned long *num_keys,
libssh2_publickey_list **pkey_list);
LIBSSH2_API void libssh2_publickey_list_free(LIBSSH2_PUBLICKEY *pkey,
libssh2_publickey_list *pkey_list);
LIBSSH2_API int libssh2_publickey_shutdown(LIBSSH2_PUBLICKEY *pkey);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* ifndef: LIBSSH2_PUBLICKEY_H */

View File

@@ -0,0 +1,346 @@
/* Copyright (c) 2004-2008, Sara Golemon <sarag@libssh2.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms,
* with or without modification, are permitted provided
* that the following conditions are met:
*
* Redistributions of source code must retain the above
* copyright notice, this list of conditions and the
* following disclaimer.
*
* Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* Neither the name of the copyright holder nor the names
* of any other contributors may be used to endorse or
* promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*/
#ifndef LIBSSH2_SFTP_H
#define LIBSSH2_SFTP_H 1
#include "libssh2.h"
#ifndef WIN32
#include <unistd.h>
#endif
#ifdef __cplusplus
extern "C" {
#endif
/* Note: Version 6 was documented at the time of writing
* However it was marked as "DO NOT IMPLEMENT" due to pending changes
*
* Let's start with Version 3 (The version found in OpenSSH) and go from there
*/
#define LIBSSH2_SFTP_VERSION 3
typedef struct _LIBSSH2_SFTP LIBSSH2_SFTP;
typedef struct _LIBSSH2_SFTP_HANDLE LIBSSH2_SFTP_HANDLE;
typedef struct _LIBSSH2_SFTP_ATTRIBUTES LIBSSH2_SFTP_ATTRIBUTES;
typedef struct _LIBSSH2_SFTP_STATVFS LIBSSH2_SFTP_STATVFS;
/* Flags for open_ex() */
#define LIBSSH2_SFTP_OPENFILE 0
#define LIBSSH2_SFTP_OPENDIR 1
/* Flags for rename_ex() */
#define LIBSSH2_SFTP_RENAME_OVERWRITE 0x00000001
#define LIBSSH2_SFTP_RENAME_ATOMIC 0x00000002
#define LIBSSH2_SFTP_RENAME_NATIVE 0x00000004
/* Flags for stat_ex() */
#define LIBSSH2_SFTP_STAT 0
#define LIBSSH2_SFTP_LSTAT 1
#define LIBSSH2_SFTP_SETSTAT 2
/* Flags for symlink_ex() */
#define LIBSSH2_SFTP_SYMLINK 0
#define LIBSSH2_SFTP_READLINK 1
#define LIBSSH2_SFTP_REALPATH 2
/* SFTP attribute flag bits */
#define LIBSSH2_SFTP_ATTR_SIZE 0x00000001
#define LIBSSH2_SFTP_ATTR_UIDGID 0x00000002
#define LIBSSH2_SFTP_ATTR_PERMISSIONS 0x00000004
#define LIBSSH2_SFTP_ATTR_ACMODTIME 0x00000008
#define LIBSSH2_SFTP_ATTR_EXTENDED 0x80000000
/* SFTP statvfs flag bits */
#define LIBSSH2_SFTP_ST_RDONLY 0x00000001
#define LIBSSH2_SFTP_ST_NOSUID 0x00000002
struct _LIBSSH2_SFTP_ATTRIBUTES {
/* If flags & ATTR_* bit is set, then the value in this struct will be
* meaningful Otherwise it should be ignored
*/
unsigned long flags;
libssh2_uint64_t filesize;
unsigned long uid, gid;
unsigned long permissions;
unsigned long atime, mtime;
};
struct _LIBSSH2_SFTP_STATVFS {
libssh2_uint64_t f_bsize; /* file system block size */
libssh2_uint64_t f_frsize; /* fragment size */
libssh2_uint64_t f_blocks; /* size of fs in f_frsize units */
libssh2_uint64_t f_bfree; /* # free blocks */
libssh2_uint64_t f_bavail; /* # free blocks for non-root */
libssh2_uint64_t f_files; /* # inodes */
libssh2_uint64_t f_ffree; /* # free inodes */
libssh2_uint64_t f_favail; /* # free inodes for non-root */
libssh2_uint64_t f_fsid; /* file system ID */
libssh2_uint64_t f_flag; /* mount flags */
libssh2_uint64_t f_namemax; /* maximum filename length */
};
/* SFTP filetypes */
#define LIBSSH2_SFTP_TYPE_REGULAR 1
#define LIBSSH2_SFTP_TYPE_DIRECTORY 2
#define LIBSSH2_SFTP_TYPE_SYMLINK 3
#define LIBSSH2_SFTP_TYPE_SPECIAL 4
#define LIBSSH2_SFTP_TYPE_UNKNOWN 5
#define LIBSSH2_SFTP_TYPE_SOCKET 6
#define LIBSSH2_SFTP_TYPE_CHAR_DEVICE 7
#define LIBSSH2_SFTP_TYPE_BLOCK_DEVICE 8
#define LIBSSH2_SFTP_TYPE_FIFO 9
/*
* Reproduce the POSIX file modes here for systems that are not POSIX
* compliant.
*
* These is used in "permissions" of "struct _LIBSSH2_SFTP_ATTRIBUTES"
*/
/* File type */
#define LIBSSH2_SFTP_S_IFMT 0170000 /* type of file mask */
#define LIBSSH2_SFTP_S_IFIFO 0010000 /* named pipe (fifo) */
#define LIBSSH2_SFTP_S_IFCHR 0020000 /* character special */
#define LIBSSH2_SFTP_S_IFDIR 0040000 /* directory */
#define LIBSSH2_SFTP_S_IFBLK 0060000 /* block special */
#define LIBSSH2_SFTP_S_IFREG 0100000 /* regular */
#define LIBSSH2_SFTP_S_IFLNK 0120000 /* symbolic link */
#define LIBSSH2_SFTP_S_IFSOCK 0140000 /* socket */
/* File mode */
/* Read, write, execute/search by owner */
#define LIBSSH2_SFTP_S_IRWXU 0000700 /* RWX mask for owner */
#define LIBSSH2_SFTP_S_IRUSR 0000400 /* R for owner */
#define LIBSSH2_SFTP_S_IWUSR 0000200 /* W for owner */
#define LIBSSH2_SFTP_S_IXUSR 0000100 /* X for owner */
/* Read, write, execute/search by group */
#define LIBSSH2_SFTP_S_IRWXG 0000070 /* RWX mask for group */
#define LIBSSH2_SFTP_S_IRGRP 0000040 /* R for group */
#define LIBSSH2_SFTP_S_IWGRP 0000020 /* W for group */
#define LIBSSH2_SFTP_S_IXGRP 0000010 /* X for group */
/* Read, write, execute/search by others */
#define LIBSSH2_SFTP_S_IRWXO 0000007 /* RWX mask for other */
#define LIBSSH2_SFTP_S_IROTH 0000004 /* R for other */
#define LIBSSH2_SFTP_S_IWOTH 0000002 /* W for other */
#define LIBSSH2_SFTP_S_IXOTH 0000001 /* X for other */
/* macros to check for specific file types, added in 1.2.5 */
#define LIBSSH2_SFTP_S_ISLNK(m) \
(((m) & LIBSSH2_SFTP_S_IFMT) == LIBSSH2_SFTP_S_IFLNK)
#define LIBSSH2_SFTP_S_ISREG(m) \
(((m) & LIBSSH2_SFTP_S_IFMT) == LIBSSH2_SFTP_S_IFREG)
#define LIBSSH2_SFTP_S_ISDIR(m) \
(((m) & LIBSSH2_SFTP_S_IFMT) == LIBSSH2_SFTP_S_IFDIR)
#define LIBSSH2_SFTP_S_ISCHR(m) \
(((m) & LIBSSH2_SFTP_S_IFMT) == LIBSSH2_SFTP_S_IFCHR)
#define LIBSSH2_SFTP_S_ISBLK(m) \
(((m) & LIBSSH2_SFTP_S_IFMT) == LIBSSH2_SFTP_S_IFBLK)
#define LIBSSH2_SFTP_S_ISFIFO(m) \
(((m) & LIBSSH2_SFTP_S_IFMT) == LIBSSH2_SFTP_S_IFIFO)
#define LIBSSH2_SFTP_S_ISSOCK(m) \
(((m) & LIBSSH2_SFTP_S_IFMT) == LIBSSH2_SFTP_S_IFSOCK)
/* SFTP File Transfer Flags -- (e.g. flags parameter to sftp_open())
* Danger will robinson... APPEND doesn't have any effect on OpenSSH servers */
#define LIBSSH2_FXF_READ 0x00000001
#define LIBSSH2_FXF_WRITE 0x00000002
#define LIBSSH2_FXF_APPEND 0x00000004
#define LIBSSH2_FXF_CREAT 0x00000008
#define LIBSSH2_FXF_TRUNC 0x00000010
#define LIBSSH2_FXF_EXCL 0x00000020
/* SFTP Status Codes (returned by libssh2_sftp_last_error() ) */
#define LIBSSH2_FX_OK 0
#define LIBSSH2_FX_EOF 1
#define LIBSSH2_FX_NO_SUCH_FILE 2
#define LIBSSH2_FX_PERMISSION_DENIED 3
#define LIBSSH2_FX_FAILURE 4
#define LIBSSH2_FX_BAD_MESSAGE 5
#define LIBSSH2_FX_NO_CONNECTION 6
#define LIBSSH2_FX_CONNECTION_LOST 7
#define LIBSSH2_FX_OP_UNSUPPORTED 8
#define LIBSSH2_FX_INVALID_HANDLE 9
#define LIBSSH2_FX_NO_SUCH_PATH 10
#define LIBSSH2_FX_FILE_ALREADY_EXISTS 11
#define LIBSSH2_FX_WRITE_PROTECT 12
#define LIBSSH2_FX_NO_MEDIA 13
#define LIBSSH2_FX_NO_SPACE_ON_FILESYSTEM 14
#define LIBSSH2_FX_QUOTA_EXCEEDED 15
#define LIBSSH2_FX_UNKNOWN_PRINCIPLE 16 /* Initial mis-spelling */
#define LIBSSH2_FX_UNKNOWN_PRINCIPAL 16
#define LIBSSH2_FX_LOCK_CONFlICT 17 /* Initial mis-spelling */
#define LIBSSH2_FX_LOCK_CONFLICT 17
#define LIBSSH2_FX_DIR_NOT_EMPTY 18
#define LIBSSH2_FX_NOT_A_DIRECTORY 19
#define LIBSSH2_FX_INVALID_FILENAME 20
#define LIBSSH2_FX_LINK_LOOP 21
/* Returned by any function that would block during a read/write opperation */
#define LIBSSH2SFTP_EAGAIN LIBSSH2_ERROR_EAGAIN
/* SFTP API */
LIBSSH2_API LIBSSH2_SFTP *libssh2_sftp_init(LIBSSH2_SESSION *session);
LIBSSH2_API int libssh2_sftp_shutdown(LIBSSH2_SFTP *sftp);
LIBSSH2_API unsigned long libssh2_sftp_last_error(LIBSSH2_SFTP *sftp);
LIBSSH2_API LIBSSH2_CHANNEL *libssh2_sftp_get_channel(LIBSSH2_SFTP *sftp);
/* File / Directory Ops */
LIBSSH2_API LIBSSH2_SFTP_HANDLE *libssh2_sftp_open_ex(LIBSSH2_SFTP *sftp,
const char *filename,
unsigned int filename_len,
unsigned long flags,
long mode, int open_type);
#define libssh2_sftp_open(sftp, filename, flags, mode) \
libssh2_sftp_open_ex((sftp), (filename), strlen(filename), (flags), \
(mode), LIBSSH2_SFTP_OPENFILE)
#define libssh2_sftp_opendir(sftp, path) \
libssh2_sftp_open_ex((sftp), (path), strlen(path), 0, 0, \
LIBSSH2_SFTP_OPENDIR)
LIBSSH2_API ssize_t libssh2_sftp_read(LIBSSH2_SFTP_HANDLE *handle,
char *buffer, size_t buffer_maxlen);
LIBSSH2_API int libssh2_sftp_readdir_ex(LIBSSH2_SFTP_HANDLE *handle, \
char *buffer, size_t buffer_maxlen,
char *longentry,
size_t longentry_maxlen,
LIBSSH2_SFTP_ATTRIBUTES *attrs);
#define libssh2_sftp_readdir(handle, buffer, buffer_maxlen, attrs) \
libssh2_sftp_readdir_ex((handle), (buffer), (buffer_maxlen), NULL, 0, \
(attrs))
LIBSSH2_API ssize_t libssh2_sftp_write(LIBSSH2_SFTP_HANDLE *handle,
const char *buffer, size_t count);
LIBSSH2_API int libssh2_sftp_fsync(LIBSSH2_SFTP_HANDLE *handle);
LIBSSH2_API int libssh2_sftp_close_handle(LIBSSH2_SFTP_HANDLE *handle);
#define libssh2_sftp_close(handle) libssh2_sftp_close_handle(handle)
#define libssh2_sftp_closedir(handle) libssh2_sftp_close_handle(handle)
LIBSSH2_API void libssh2_sftp_seek(LIBSSH2_SFTP_HANDLE *handle, size_t offset);
LIBSSH2_API void libssh2_sftp_seek64(LIBSSH2_SFTP_HANDLE *handle,
libssh2_uint64_t offset);
#define libssh2_sftp_rewind(handle) libssh2_sftp_seek64((handle), 0)
LIBSSH2_API size_t libssh2_sftp_tell(LIBSSH2_SFTP_HANDLE *handle);
LIBSSH2_API libssh2_uint64_t libssh2_sftp_tell64(LIBSSH2_SFTP_HANDLE *handle);
LIBSSH2_API int libssh2_sftp_fstat_ex(LIBSSH2_SFTP_HANDLE *handle,
LIBSSH2_SFTP_ATTRIBUTES *attrs,
int setstat);
#define libssh2_sftp_fstat(handle, attrs) \
libssh2_sftp_fstat_ex((handle), (attrs), 0)
#define libssh2_sftp_fsetstat(handle, attrs) \
libssh2_sftp_fstat_ex((handle), (attrs), 1)
/* Miscellaneous Ops */
LIBSSH2_API int libssh2_sftp_rename_ex(LIBSSH2_SFTP *sftp,
const char *source_filename,
unsigned int srouce_filename_len,
const char *dest_filename,
unsigned int dest_filename_len,
long flags);
#define libssh2_sftp_rename(sftp, sourcefile, destfile) \
libssh2_sftp_rename_ex((sftp), (sourcefile), strlen(sourcefile), \
(destfile), strlen(destfile), \
LIBSSH2_SFTP_RENAME_OVERWRITE | \
LIBSSH2_SFTP_RENAME_ATOMIC | \
LIBSSH2_SFTP_RENAME_NATIVE)
LIBSSH2_API int libssh2_sftp_unlink_ex(LIBSSH2_SFTP *sftp,
const char *filename,
unsigned int filename_len);
#define libssh2_sftp_unlink(sftp, filename) \
libssh2_sftp_unlink_ex((sftp), (filename), strlen(filename))
LIBSSH2_API int libssh2_sftp_fstatvfs(LIBSSH2_SFTP_HANDLE *handle,
LIBSSH2_SFTP_STATVFS *st);
LIBSSH2_API int libssh2_sftp_statvfs(LIBSSH2_SFTP *sftp,
const char *path,
size_t path_len,
LIBSSH2_SFTP_STATVFS *st);
LIBSSH2_API int libssh2_sftp_mkdir_ex(LIBSSH2_SFTP *sftp,
const char *path,
unsigned int path_len, long mode);
#define libssh2_sftp_mkdir(sftp, path, mode) \
libssh2_sftp_mkdir_ex((sftp), (path), strlen(path), (mode))
LIBSSH2_API int libssh2_sftp_rmdir_ex(LIBSSH2_SFTP *sftp,
const char *path,
unsigned int path_len);
#define libssh2_sftp_rmdir(sftp, path) \
libssh2_sftp_rmdir_ex((sftp), (path), strlen(path))
LIBSSH2_API int libssh2_sftp_stat_ex(LIBSSH2_SFTP *sftp,
const char *path,
unsigned int path_len,
int stat_type,
LIBSSH2_SFTP_ATTRIBUTES *attrs);
#define libssh2_sftp_stat(sftp, path, attrs) \
libssh2_sftp_stat_ex((sftp), (path), strlen(path), LIBSSH2_SFTP_STAT, \
(attrs))
#define libssh2_sftp_lstat(sftp, path, attrs) \
libssh2_sftp_stat_ex((sftp), (path), strlen(path), LIBSSH2_SFTP_LSTAT, \
(attrs))
#define libssh2_sftp_setstat(sftp, path, attrs) \
libssh2_sftp_stat_ex((sftp), (path), strlen(path), LIBSSH2_SFTP_SETSTAT, \
(attrs))
LIBSSH2_API int libssh2_sftp_symlink_ex(LIBSSH2_SFTP *sftp,
const char *path,
unsigned int path_len,
char *target,
unsigned int target_len, int link_type);
#define libssh2_sftp_symlink(sftp, orig, linkpath) \
libssh2_sftp_symlink_ex((sftp), (orig), strlen(orig), (linkpath), \
strlen(linkpath), LIBSSH2_SFTP_SYMLINK)
#define libssh2_sftp_readlink(sftp, path, target, maxlen) \
libssh2_sftp_symlink_ex((sftp), (path), strlen(path), (target), (maxlen), \
LIBSSH2_SFTP_READLINK)
#define libssh2_sftp_realpath(sftp, path, target, maxlen) \
libssh2_sftp_symlink_ex((sftp), (path), strlen(path), (target), (maxlen), \
LIBSSH2_SFTP_REALPATH)
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* LIBSSH2_SFTP_H */

View File

Binary file not shown.

View File

Binary file not shown.

View File

Binary file not shown.

View File

@@ -0,0 +1,24 @@
#import <UIKit/UIKit.h>
//! Project version number for NMSSH.
FOUNDATION_EXPORT double NMSSHVersionNumber;
//! Project version string for NMSSH.
FOUNDATION_EXPORT const unsigned char NMSSHVersionString[];
// In this header, you should import all the public headers of your framework using statements like #import <NMSSH/PublicHeader.h>
#import "libssh2.h"
#import "libssh2_sftp.h"
#import "NMSSHSessionDelegate.h"
#import "NMSSHChannelDelegate.h"
#import "NMSSHSession.h"
#import "NMSSHChannel.h"
#import "NMSFTP.h"
#import "NMSFTPFile.h"
#import "NMSSHConfig.h"
#import "NMSSHHostConfig.h"
#import "NMSSHLogger.h"

35
Carthage/Checkouts/NMSSH/NMSSH.podspec vendored Normal file
View File

@@ -0,0 +1,35 @@
Pod::Spec.new do |spec|
spec.name = "NMSSH"
spec.version = "2.3.1"
spec.summary = "NMSSH is a clean, easy-to-use, unit tested framework for iOS and OSX that wraps libssh2."
spec.homepage = "https://github.com/NMSSH/NMSSH"
spec.license = 'MIT'
spec.authors = { "Christoffer Lejdborg" => "hello@9muses.se", "Tommaso Madonia" => "tommaso@madonia.me" }
spec.source = { :git => "https://github.com/NMSSH/NMSSH.git", :tag => spec.version.to_s }
spec.requires_arc = true
spec.platform = :ios
spec.platform = :osx
spec.source_files = 'NMSSH', 'NMSSH/**/*.{h,m}'
spec.public_header_files = 'NMSSH/*.h', 'NMSSH/Protocols/*.h', 'NMSSH/Config/NMSSHLogger.h'
spec.private_header_files = 'NMSSH/Config/NMSSH+Protected.h', 'NMSSH/Config/socket_helper.h'
spec.libraries = 'z'
spec.framework = 'CFNetwork'
spec.ios.deployment_target = '7.0'
spec.ios.vendored_libraries = 'NMSSH-iOS/Libraries/lib/libssh2.a', 'NMSSH-iOS/Libraries/lib/libssl.a', 'NMSSH-iOS/Libraries/lib/libcrypto.a'
spec.ios.source_files = 'NMSSH-iOS', 'NMSSH-iOS/Libraries/**/*.h'
spec.ios.public_header_files = 'NMSSH-iOS/Libraries/**/*.h'
spec.osx.deployment_target = '10.8'
spec.osx.vendored_libraries = 'NMSSH-OSX/Libraries/lib/libssh2.a', 'NMSSH-OSX/Libraries/lib/libssl.a', 'NMSSH-OSX/Libraries/lib/libcrypto.a'
spec.osx.source_files = 'NMSSH-OSX', 'NMSSH-OSX/Libraries/**/*.h'
spec.osx.public_header_files = 'NMSSH-OSX/Libraries/**/*.h'
spec.xcconfig = {
"OTHER_LDFLAGS" => "-ObjC",
}
end

View File

@@ -0,0 +1,703 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
188B5E3B1C7CB7D0003A6AFD /* libcrypto.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 188B5E391C7CB7D0003A6AFD /* libcrypto.a */; };
188B5E3C1C7CB7D0003A6AFD /* libssl.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 188B5E3A1C7CB7D0003A6AFD /* libssl.a */; };
18B4FE85188C87F3004E05FF /* NMSSH+Protected.h in Headers */ = {isa = PBXBuildFile; fileRef = 18B4FE84188C87F3004E05FF /* NMSSH+Protected.h */; };
18B4FE8E188CB2BB004E05FF /* libssh2.h in Headers */ = {isa = PBXBuildFile; fileRef = 18B4FE89188CB2BB004E05FF /* libssh2.h */; settings = {ATTRIBUTES = (Public, ); }; };
18B4FE8F188CB2BB004E05FF /* libssh2_publickey.h in Headers */ = {isa = PBXBuildFile; fileRef = 18B4FE8A188CB2BB004E05FF /* libssh2_publickey.h */; };
18B4FE90188CB2BB004E05FF /* libssh2_sftp.h in Headers */ = {isa = PBXBuildFile; fileRef = 18B4FE8B188CB2BB004E05FF /* libssh2_sftp.h */; settings = {ATTRIBUTES = (Public, ); }; };
18B4FE91188CB2BB004E05FF /* libssh2.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 18B4FE8D188CB2BB004E05FF /* libssh2.a */; };
18E4D2391815F6F600432102 /* NMSSHLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = 18E4D2381815F6F600432102 /* NMSSHLogger.m */; };
18E4D23A1815F70D00432102 /* NMSSHLogger.h in Headers */ = {isa = PBXBuildFile; fileRef = E4F1CBB217206D730025EBFC /* NMSSHLogger.h */; settings = {ATTRIBUTES = (Public, ); }; };
4A04ECAE174F51E8006DD8E7 /* NMSSHChannelDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 18F9DABF17302F7F004CECAA /* NMSSHChannelDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; };
6EB9E8051887F52C003A9BE4 /* NMSFTPFile.h in Headers */ = {isa = PBXBuildFile; fileRef = 6EB9E8031887F52C003A9BE4 /* NMSFTPFile.h */; settings = {ATTRIBUTES = (Public, ); }; };
6EB9E8061887F52C003A9BE4 /* NMSFTPFile.m in Sources */ = {isa = PBXBuildFile; fileRef = 6EB9E8041887F52C003A9BE4 /* NMSFTPFile.m */; };
6EB9E8071887F533003A9BE4 /* NMSFTPFile.m in Sources */ = {isa = PBXBuildFile; fileRef = 6EB9E8041887F52C003A9BE4 /* NMSFTPFile.m */; };
6EE908A5188D597300997E11 /* NMSFTPFileTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6EE908A4188D597300997E11 /* NMSFTPFileTests.m */; };
A6AE1EBB191C7B5800780C19 /* NMSSHConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = A6AE1EB9191C7B5800780C19 /* NMSSHConfig.h */; settings = {ATTRIBUTES = (Public, ); }; };
A6AE1EBC191C7B5800780C19 /* NMSSHConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = A6AE1EBA191C7B5800780C19 /* NMSSHConfig.m */; };
A6AE1EBE191C835900780C19 /* NMSSHConfigTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A6AE1EBD191C835900780C19 /* NMSSHConfigTests.m */; };
A6AE1ECA191EDBD700780C19 /* NMSSHHostConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = A6AE1EC8191EDBD700780C19 /* NMSSHHostConfig.h */; settings = {ATTRIBUTES = (Public, ); }; };
A6AE1ECB191EDBD700780C19 /* NMSSHHostConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = A6AE1EC9191EDBD700780C19 /* NMSSHHostConfig.m */; };
E42815BC1593D13800CF680C /* YAML.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = E4E96DD9158FD65D002E6E0A /* YAML.framework */; };
E42815BF1593D6E900CF680C /* NMSSHSessionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E42815BE1593D6E900CF680C /* NMSSHSessionTests.m */; };
E42815C21593D95200CF680C /* NMSSHSession.h in Headers */ = {isa = PBXBuildFile; fileRef = E42815C01593D95200CF680C /* NMSSHSession.h */; settings = {ATTRIBUTES = (Public, ); }; };
E42815C31593D95200CF680C /* NMSSHSession.m in Sources */ = {isa = PBXBuildFile; fileRef = E42815C11593D95200CF680C /* NMSSHSession.m */; };
E42815FE15962B7600CF680C /* NMSSH.h in Headers */ = {isa = PBXBuildFile; fileRef = E4E96D94158E10FD002E6E0A /* NMSSH.h */; settings = {ATTRIBUTES = (Public, ); }; };
E46A02E115919BE3007049AB /* ConfigHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = E46A02E015919BE3007049AB /* ConfigHelper.m */; };
E4814268172BC4F700283132 /* NMSSHSessionDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = E49AA6DB17228C33007101A4 /* NMSSHSessionDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; };
E48DA7B915D0DCC100721060 /* NMSFTPTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E48DA7B815D0DCC100721060 /* NMSFTPTests.m */; };
E48DA7BD15D0EB2800721060 /* NMSFTP.h in Headers */ = {isa = PBXBuildFile; fileRef = E48DA7BB15D0EB2800721060 /* NMSFTP.h */; settings = {ATTRIBUTES = (Public, ); }; };
E48DA7BE15D0EB2800721060 /* NMSFTP.m in Sources */ = {isa = PBXBuildFile; fileRef = E48DA7BC15D0EB2800721060 /* NMSFTP.m */; };
E48DA7BF15D0EB2800721060 /* NMSFTP.m in Sources */ = {isa = PBXBuildFile; fileRef = E48DA7BC15D0EB2800721060 /* NMSFTP.m */; };
E4D99AA415DE15B300EB5615 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = E4D99AA315DE15B300EB5615 /* libz.dylib */; };
E4E96D88158E10FD002E6E0A /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E4E96D87158E10FD002E6E0A /* Cocoa.framework */; };
E4E96D9F158E10FD002E6E0A /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E4E96D87158E10FD002E6E0A /* Cocoa.framework */; };
E4E96DA2158E10FD002E6E0A /* NMSSH.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E4E96D84158E10FD002E6E0A /* NMSSH.framework */; };
E4E96DDA158FD65D002E6E0A /* YAML.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E4E96DD9158FD65D002E6E0A /* YAML.framework */; };
E4E96DDC158FD6B6002E6E0A /* config.yml in Resources */ = {isa = PBXBuildFile; fileRef = E4E96DDB158FD6B6002E6E0A /* config.yml */; };
E4F1CBB4172073A00025EBFC /* socket_helper.m in Sources */ = {isa = PBXBuildFile; fileRef = E4F1CBB3172073A00025EBFC /* socket_helper.m */; };
E4F1E67C159F5923007B0B2F /* NMSSHChannelTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E4F1E67B159F5923007B0B2F /* NMSSHChannelTests.m */; };
E4F1E680159F5B13007B0B2F /* NMSSHChannel.h in Headers */ = {isa = PBXBuildFile; fileRef = E4F1E67E159F5B13007B0B2F /* NMSSHChannel.h */; settings = {ATTRIBUTES = (Public, ); }; };
E4F1E681159F5B13007B0B2F /* NMSSHChannel.m in Sources */ = {isa = PBXBuildFile; fileRef = E4F1E67F159F5B13007B0B2F /* NMSSHChannel.m */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
E4E96DA0158E10FD002E6E0A /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = E4E96D7A158E10FD002E6E0A /* Project object */;
proxyType = 1;
remoteGlobalIDString = E4E96D83158E10FD002E6E0A;
remoteInfo = NMSSH;
};
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
E42815BA1593D12F00CF680C /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 16;
files = (
E42815BC1593D13800CF680C /* YAML.framework in CopyFiles */,
);
runOnlyForDeploymentPostprocessing = 0;
};
E4D99A9915DE141B00EB5615 /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 10;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
188B5E391C7CB7D0003A6AFD /* libcrypto.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libcrypto.a; sourceTree = "<group>"; };
188B5E3A1C7CB7D0003A6AFD /* libssl.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libssl.a; sourceTree = "<group>"; };
18B4FE84188C87F3004E05FF /* NMSSH+Protected.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NMSSH+Protected.h"; sourceTree = "<group>"; };
18B4FE89188CB2BB004E05FF /* libssh2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = libssh2.h; sourceTree = "<group>"; };
18B4FE8A188CB2BB004E05FF /* libssh2_publickey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = libssh2_publickey.h; sourceTree = "<group>"; };
18B4FE8B188CB2BB004E05FF /* libssh2_sftp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = libssh2_sftp.h; sourceTree = "<group>"; };
18B4FE8D188CB2BB004E05FF /* libssh2.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libssh2.a; sourceTree = "<group>"; };
18E4D2381815F6F600432102 /* NMSSHLogger.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NMSSHLogger.m; sourceTree = "<group>"; };
18F9DABF17302F7F004CECAA /* NMSSHChannelDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NMSSHChannelDelegate.h; sourceTree = "<group>"; };
6EB9E8031887F52C003A9BE4 /* NMSFTPFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NMSFTPFile.h; sourceTree = "<group>"; };
6EB9E8041887F52C003A9BE4 /* NMSFTPFile.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NMSFTPFile.m; sourceTree = "<group>"; };
6EE908A4188D597300997E11 /* NMSFTPFileTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NMSFTPFileTests.m; sourceTree = "<group>"; };
A6AE1EB9191C7B5800780C19 /* NMSSHConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NMSSHConfig.h; sourceTree = "<group>"; };
A6AE1EBA191C7B5800780C19 /* NMSSHConfig.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NMSSHConfig.m; sourceTree = "<group>"; };
A6AE1EBD191C835900780C19 /* NMSSHConfigTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NMSSHConfigTests.m; sourceTree = "<group>"; };
A6AE1EC8191EDBD700780C19 /* NMSSHHostConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NMSSHHostConfig.h; sourceTree = "<group>"; };
A6AE1EC9191EDBD700780C19 /* NMSSHHostConfig.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NMSSHHostConfig.m; sourceTree = "<group>"; };
E42815BD1593D6E900CF680C /* NMSSHSessionTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NMSSHSessionTests.h; sourceTree = "<group>"; };
E42815BE1593D6E900CF680C /* NMSSHSessionTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NMSSHSessionTests.m; sourceTree = "<group>"; };
E42815C01593D95200CF680C /* NMSSHSession.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NMSSHSession.h; sourceTree = "<group>"; };
E42815C11593D95200CF680C /* NMSSHSession.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NMSSHSession.m; sourceTree = "<group>"; };
E46A02DF15919BE3007049AB /* ConfigHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ConfigHelper.h; sourceTree = "<group>"; };
E46A02E015919BE3007049AB /* ConfigHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ConfigHelper.m; sourceTree = "<group>"; };
E48DA7B715D0DCC100721060 /* NMSFTPTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NMSFTPTests.h; sourceTree = "<group>"; };
E48DA7B815D0DCC100721060 /* NMSFTPTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NMSFTPTests.m; sourceTree = "<group>"; };
E48DA7BB15D0EB2800721060 /* NMSFTP.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NMSFTP.h; sourceTree = "<group>"; };
E48DA7BC15D0EB2800721060 /* NMSFTP.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NMSFTP.m; sourceTree = "<group>"; };
E49AA6DB17228C33007101A4 /* NMSSHSessionDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NMSSHSessionDelegate.h; sourceTree = "<group>"; };
E4D99A9C15DE149D00EB5615 /* libssl.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libssl.dylib; path = usr/lib/libssl.dylib; sourceTree = SDKROOT; };
E4D99AA115DE15A600EB5615 /* libcrypto.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libcrypto.dylib; path = usr/lib/libcrypto.dylib; sourceTree = SDKROOT; };
E4D99AA315DE15B300EB5615 /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; };
E4E96D84158E10FD002E6E0A /* NMSSH.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = NMSSH.framework; sourceTree = BUILT_PRODUCTS_DIR; };
E4E96D87158E10FD002E6E0A /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
E4E96D8A158E10FD002E6E0A /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; };
E4E96D8B158E10FD002E6E0A /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; };
E4E96D8C158E10FD002E6E0A /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
E4E96D8F158E10FD002E6E0A /* NMSSH-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "NMSSH-Info.plist"; sourceTree = "<group>"; };
E4E96D93158E10FD002E6E0A /* NMSSH-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NMSSH-Prefix.pch"; sourceTree = "<group>"; };
E4E96D94158E10FD002E6E0A /* NMSSH.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NMSSH.h; sourceTree = "<group>"; };
E4E96D9C158E10FD002E6E0A /* NMSSHTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = NMSSHTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
E4E96DA5158E10FD002E6E0A /* NMSSHTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "NMSSHTests-Info.plist"; sourceTree = "<group>"; };
E4E96DD9158FD65D002E6E0A /* YAML.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = YAML.framework; sourceTree = "<group>"; };
E4E96DDB158FD6B6002E6E0A /* config.yml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = config.yml; sourceTree = "<group>"; };
E4F1CBB217206D730025EBFC /* NMSSHLogger.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NMSSHLogger.h; sourceTree = "<group>"; };
E4F1CBB3172073A00025EBFC /* socket_helper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = socket_helper.m; sourceTree = "<group>"; };
E4F1CBB5172073AC0025EBFC /* socket_helper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = socket_helper.h; sourceTree = "<group>"; };
E4F1E67A159F5923007B0B2F /* NMSSHChannelTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NMSSHChannelTests.h; sourceTree = "<group>"; };
E4F1E67B159F5923007B0B2F /* NMSSHChannelTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NMSSHChannelTests.m; sourceTree = "<group>"; };
E4F1E67E159F5B13007B0B2F /* NMSSHChannel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NMSSHChannel.h; sourceTree = "<group>"; };
E4F1E67F159F5B13007B0B2F /* NMSSHChannel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NMSSHChannel.m; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
E4E96D80158E10FD002E6E0A /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
188B5E3C1C7CB7D0003A6AFD /* libssl.a in Frameworks */,
E4E96D88158E10FD002E6E0A /* Cocoa.framework in Frameworks */,
E4D99AA415DE15B300EB5615 /* libz.dylib in Frameworks */,
188B5E3B1C7CB7D0003A6AFD /* libcrypto.a in Frameworks */,
18B4FE91188CB2BB004E05FF /* libssh2.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
E4E96D98158E10FD002E6E0A /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
E4E96D9F158E10FD002E6E0A /* Cocoa.framework in Frameworks */,
E4E96DA2158E10FD002E6E0A /* NMSSH.framework in Frameworks */,
E4E96DDA158FD65D002E6E0A /* YAML.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
18B4FE86188CB2BB004E05FF /* Libraries */ = {
isa = PBXGroup;
children = (
18B4FE87188CB2BB004E05FF /* include */,
18B4FE8C188CB2BB004E05FF /* lib */,
);
name = Libraries;
path = "NMSSH-OSX/Libraries";
sourceTree = SOURCE_ROOT;
};
18B4FE87188CB2BB004E05FF /* include */ = {
isa = PBXGroup;
children = (
18B4FE88188CB2BB004E05FF /* libssh2 */,
);
path = include;
sourceTree = "<group>";
};
18B4FE88188CB2BB004E05FF /* libssh2 */ = {
isa = PBXGroup;
children = (
18B4FE89188CB2BB004E05FF /* libssh2.h */,
18B4FE8A188CB2BB004E05FF /* libssh2_publickey.h */,
18B4FE8B188CB2BB004E05FF /* libssh2_sftp.h */,
);
path = libssh2;
sourceTree = "<group>";
};
18B4FE8C188CB2BB004E05FF /* lib */ = {
isa = PBXGroup;
children = (
188B5E391C7CB7D0003A6AFD /* libcrypto.a */,
188B5E3A1C7CB7D0003A6AFD /* libssl.a */,
18B4FE8D188CB2BB004E05FF /* libssh2.a */,
);
path = lib;
sourceTree = "<group>";
};
E49AA6DA17228C25007101A4 /* Protocols */ = {
isa = PBXGroup;
children = (
18F9DABF17302F7F004CECAA /* NMSSHChannelDelegate.h */,
E49AA6DB17228C33007101A4 /* NMSSHSessionDelegate.h */,
);
path = Protocols;
sourceTree = "<group>";
};
E4E96D78158E10FD002E6E0A = {
isa = PBXGroup;
children = (
E4E96D8D158E10FD002E6E0A /* NMSSH */,
E4E96DA3158E10FD002E6E0A /* NMSSHTests */,
E4E96D86158E10FD002E6E0A /* Frameworks */,
E4E96D85158E10FD002E6E0A /* Products */,
);
sourceTree = "<group>";
};
E4E96D85158E10FD002E6E0A /* Products */ = {
isa = PBXGroup;
children = (
E4E96D84158E10FD002E6E0A /* NMSSH.framework */,
E4E96D9C158E10FD002E6E0A /* NMSSHTests.xctest */,
);
name = Products;
sourceTree = "<group>";
};
E4E96D86158E10FD002E6E0A /* Frameworks */ = {
isa = PBXGroup;
children = (
E4E96D87158E10FD002E6E0A /* Cocoa.framework */,
E4D99AA315DE15B300EB5615 /* libz.dylib */,
E4D99AA115DE15A600EB5615 /* libcrypto.dylib */,
E4D99A9C15DE149D00EB5615 /* libssl.dylib */,
E4E96D89158E10FD002E6E0A /* Other Frameworks */,
);
name = Frameworks;
sourceTree = "<group>";
};
E4E96D89158E10FD002E6E0A /* Other Frameworks */ = {
isa = PBXGroup;
children = (
E4E96D8A158E10FD002E6E0A /* AppKit.framework */,
E4E96D8B158E10FD002E6E0A /* CoreData.framework */,
E4E96D8C158E10FD002E6E0A /* Foundation.framework */,
);
name = "Other Frameworks";
sourceTree = "<group>";
};
E4E96D8D158E10FD002E6E0A /* NMSSH */ = {
isa = PBXGroup;
children = (
E4F1CBB117206D640025EBFC /* Config */,
18B4FE86188CB2BB004E05FF /* Libraries */,
E48DA7BB15D0EB2800721060 /* NMSFTP.h */,
E48DA7BC15D0EB2800721060 /* NMSFTP.m */,
6EB9E8031887F52C003A9BE4 /* NMSFTPFile.h */,
6EB9E8041887F52C003A9BE4 /* NMSFTPFile.m */,
E4E96D94158E10FD002E6E0A /* NMSSH.h */,
E4F1E67E159F5B13007B0B2F /* NMSSHChannel.h */,
E4F1E67F159F5B13007B0B2F /* NMSSHChannel.m */,
A6AE1EB9191C7B5800780C19 /* NMSSHConfig.h */,
A6AE1EBA191C7B5800780C19 /* NMSSHConfig.m */,
A6AE1EC8191EDBD700780C19 /* NMSSHHostConfig.h */,
A6AE1EC9191EDBD700780C19 /* NMSSHHostConfig.m */,
E42815C01593D95200CF680C /* NMSSHSession.h */,
E42815C11593D95200CF680C /* NMSSHSession.m */,
E49AA6DA17228C25007101A4 /* Protocols */,
E4E96D8E158E10FD002E6E0A /* Supporting Files */,
);
path = NMSSH;
sourceTree = "<group>";
};
E4E96D8E158E10FD002E6E0A /* Supporting Files */ = {
isa = PBXGroup;
children = (
E4E96D8F158E10FD002E6E0A /* NMSSH-Info.plist */,
E4E96D93158E10FD002E6E0A /* NMSSH-Prefix.pch */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
E4E96DA3158E10FD002E6E0A /* NMSSHTests */ = {
isa = PBXGroup;
children = (
E4E96DD0158FD309002E6E0A /* Settings */,
E4E96DA4158E10FD002E6E0A /* Supporting Files */,
E4F1E67A159F5923007B0B2F /* NMSSHChannelTests.h */,
E4F1E67B159F5923007B0B2F /* NMSSHChannelTests.m */,
A6AE1EBD191C835900780C19 /* NMSSHConfigTests.m */,
E42815BD1593D6E900CF680C /* NMSSHSessionTests.h */,
E42815BE1593D6E900CF680C /* NMSSHSessionTests.m */,
E48DA7B715D0DCC100721060 /* NMSFTPTests.h */,
E48DA7B815D0DCC100721060 /* NMSFTPTests.m */,
6EE908A4188D597300997E11 /* NMSFTPFileTests.m */,
);
path = NMSSHTests;
sourceTree = "<group>";
};
E4E96DA4158E10FD002E6E0A /* Supporting Files */ = {
isa = PBXGroup;
children = (
E4E96DA5158E10FD002E6E0A /* NMSSHTests-Info.plist */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
E4E96DD0158FD309002E6E0A /* Settings */ = {
isa = PBXGroup;
children = (
E4E96DD8158FD65D002E6E0A /* lib */,
E4E96DDB158FD6B6002E6E0A /* config.yml */,
E46A02DF15919BE3007049AB /* ConfigHelper.h */,
E46A02E015919BE3007049AB /* ConfigHelper.m */,
);
path = Settings;
sourceTree = "<group>";
};
E4E96DD8158FD65D002E6E0A /* lib */ = {
isa = PBXGroup;
children = (
E4E96DD9158FD65D002E6E0A /* YAML.framework */,
);
path = lib;
sourceTree = "<group>";
};
E4F1CBB117206D640025EBFC /* Config */ = {
isa = PBXGroup;
children = (
18B4FE84188C87F3004E05FF /* NMSSH+Protected.h */,
E4F1CBB217206D730025EBFC /* NMSSHLogger.h */,
18E4D2381815F6F600432102 /* NMSSHLogger.m */,
E4F1CBB5172073AC0025EBFC /* socket_helper.h */,
E4F1CBB3172073A00025EBFC /* socket_helper.m */,
);
path = Config;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
E4E96D81158E10FD002E6E0A /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
18B4FE8F188CB2BB004E05FF /* libssh2_publickey.h in Headers */,
E42815FE15962B7600CF680C /* NMSSH.h in Headers */,
18B4FE85188C87F3004E05FF /* NMSSH+Protected.h in Headers */,
E42815C21593D95200CF680C /* NMSSHSession.h in Headers */,
E4814268172BC4F700283132 /* NMSSHSessionDelegate.h in Headers */,
4A04ECAE174F51E8006DD8E7 /* NMSSHChannelDelegate.h in Headers */,
18B4FE90188CB2BB004E05FF /* libssh2_sftp.h in Headers */,
18B4FE8E188CB2BB004E05FF /* libssh2.h in Headers */,
E4F1E680159F5B13007B0B2F /* NMSSHChannel.h in Headers */,
A6AE1ECA191EDBD700780C19 /* NMSSHHostConfig.h in Headers */,
A6AE1EBB191C7B5800780C19 /* NMSSHConfig.h in Headers */,
6EB9E8051887F52C003A9BE4 /* NMSFTPFile.h in Headers */,
E48DA7BD15D0EB2800721060 /* NMSFTP.h in Headers */,
18E4D23A1815F70D00432102 /* NMSSHLogger.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXHeadersBuildPhase section */
/* Begin PBXNativeTarget section */
E4E96D83158E10FD002E6E0A /* NMSSH */ = {
isa = PBXNativeTarget;
buildConfigurationList = E4E96DAE158E10FD002E6E0A /* Build configuration list for PBXNativeTarget "NMSSH" */;
buildPhases = (
E4E96D7F158E10FD002E6E0A /* Sources */,
E4E96D80158E10FD002E6E0A /* Frameworks */,
E4E96D81158E10FD002E6E0A /* Headers */,
E4E96D82158E10FD002E6E0A /* Resources */,
E4D99A9915DE141B00EB5615 /* CopyFiles */,
);
buildRules = (
);
dependencies = (
);
name = NMSSH;
productName = NMSSH;
productReference = E4E96D84158E10FD002E6E0A /* NMSSH.framework */;
productType = "com.apple.product-type.framework";
};
E4E96D9B158E10FD002E6E0A /* NMSSHTests */ = {
isa = PBXNativeTarget;
buildConfigurationList = E4E96DB1158E10FD002E6E0A /* Build configuration list for PBXNativeTarget "NMSSHTests" */;
buildPhases = (
E4E96D97158E10FD002E6E0A /* Sources */,
E4E96D98158E10FD002E6E0A /* Frameworks */,
E4E96D99158E10FD002E6E0A /* Resources */,
E42815BA1593D12F00CF680C /* CopyFiles */,
);
buildRules = (
);
dependencies = (
E4E96DA1158E10FD002E6E0A /* PBXTargetDependency */,
);
name = NMSSHTests;
productName = NMSSHTests;
productReference = E4E96D9C158E10FD002E6E0A /* NMSSHTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
E4E96D7A158E10FD002E6E0A /* Project object */ = {
isa = PBXProject;
attributes = {
LastTestingUpgradeCheck = 0510;
LastUpgradeCheck = 1000;
};
buildConfigurationList = E4E96D7D158E10FD002E6E0A /* Build configuration list for PBXProject "NMSSH" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
);
mainGroup = E4E96D78158E10FD002E6E0A;
productRefGroup = E4E96D85158E10FD002E6E0A /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
E4E96D83158E10FD002E6E0A /* NMSSH */,
E4E96D9B158E10FD002E6E0A /* NMSSHTests */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
E4E96D82158E10FD002E6E0A /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
E4E96D99158E10FD002E6E0A /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
E4E96DDC158FD6B6002E6E0A /* config.yml in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
E4E96D7F158E10FD002E6E0A /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
E42815C31593D95200CF680C /* NMSSHSession.m in Sources */,
A6AE1ECB191EDBD700780C19 /* NMSSHHostConfig.m in Sources */,
6EB9E8061887F52C003A9BE4 /* NMSFTPFile.m in Sources */,
E4F1E681159F5B13007B0B2F /* NMSSHChannel.m in Sources */,
A6AE1EBC191C7B5800780C19 /* NMSSHConfig.m in Sources */,
E48DA7BE15D0EB2800721060 /* NMSFTP.m in Sources */,
18E4D2391815F6F600432102 /* NMSSHLogger.m in Sources */,
E4F1CBB4172073A00025EBFC /* socket_helper.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
E4E96D97158E10FD002E6E0A /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
6EE908A5188D597300997E11 /* NMSFTPFileTests.m in Sources */,
E46A02E115919BE3007049AB /* ConfigHelper.m in Sources */,
6EB9E8071887F533003A9BE4 /* NMSFTPFile.m in Sources */,
E42815BF1593D6E900CF680C /* NMSSHSessionTests.m in Sources */,
E4F1E67C159F5923007B0B2F /* NMSSHChannelTests.m in Sources */,
E48DA7B915D0DCC100721060 /* NMSFTPTests.m in Sources */,
E48DA7BF15D0EB2800721060 /* NMSFTP.m in Sources */,
A6AE1EBE191C835900780C19 /* NMSSHConfigTests.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
E4E96DA1158E10FD002E6E0A /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = E4E96D83158E10FD002E6E0A /* NMSSH */;
targetProxy = E4E96DA0158E10FD002E6E0A /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin XCBuildConfiguration section */
E4E96DAC158E10FD002E6E0A /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
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_DEPRECATED_OBJC_IMPLEMENTATIONS = 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_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.7;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx;
};
name = Debug;
};
E4E96DAD158E10FD002E6E0A /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
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_DEPRECATED_OBJC_IMPLEMENTATIONS = 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_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
GCC_NO_COMMON_BLOCKS = YES;
GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.7;
SDKROOT = macosx;
};
name = Release;
};
E4E96DAF158E10FD002E6E0A /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
COMBINE_HIDPI_IMAGES = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
FRAMEWORK_VERSION = A;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "NMSSH/NMSSH-Prefix.pch";
HEADER_SEARCH_PATHS = "";
INFOPLIST_FILE = "NMSSH/NMSSH-Info.plist";
INSTALL_PATH = "@executable_path/../Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(SDKROOT)/usr/lib/system",
"$(SRCROOT)/NMSSH-OSX/Libraries/lib",
"$(PROJECT_DIR)/NMSSH-OSX/Libraries/lib",
);
PRODUCT_BUNDLE_IDENTIFIER = "se.ninemuses.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = framework;
};
name = Debug;
};
E4E96DB0158E10FD002E6E0A /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
COMBINE_HIDPI_IMAGES = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
FRAMEWORK_VERSION = A;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "NMSSH/NMSSH-Prefix.pch";
HEADER_SEARCH_PATHS = "";
INFOPLIST_FILE = "NMSSH/NMSSH-Info.plist";
INSTALL_PATH = "@executable_path/../Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(SDKROOT)/usr/lib/system",
"$(SRCROOT)/NMSSH-OSX/Libraries/lib",
"$(PROJECT_DIR)/NMSSH-OSX/Libraries/lib",
);
PRODUCT_BUNDLE_IDENTIFIER = "se.ninemuses.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = framework;
};
name = Release;
};
E4E96DB2158E10FD002E6E0A /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
COMBINE_HIDPI_IMAGES = YES;
FRAMEWORK_SEARCH_PATHS = (
"\"$(SRCROOT)/NMSSHTests/Settings/lib\"",
"$(inherited)",
);
GCC_ENABLE_OBJC_GC = unsupported;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "NMSSH/NMSSH-Prefix.pch";
INFOPLIST_FILE = "NMSSHTests/NMSSHTests-Info.plist";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"\"$(SRCROOT)/NMSSH-OSX/Libraries/lib\"",
);
PRODUCT_BUNDLE_IDENTIFIER = "se.ninemuses.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Debug;
};
E4E96DB3158E10FD002E6E0A /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
COMBINE_HIDPI_IMAGES = YES;
FRAMEWORK_SEARCH_PATHS = (
"\"$(SRCROOT)/NMSSHTests/Settings/lib\"",
"$(inherited)",
);
GCC_ENABLE_OBJC_GC = unsupported;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "NMSSH/NMSSH-Prefix.pch";
INFOPLIST_FILE = "NMSSHTests/NMSSHTests-Info.plist";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"\"$(SRCROOT)/NMSSH-OSX/Libraries/lib\"",
);
PRODUCT_BUNDLE_IDENTIFIER = "se.ninemuses.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
E4E96D7D158E10FD002E6E0A /* Build configuration list for PBXProject "NMSSH" */ = {
isa = XCConfigurationList;
buildConfigurations = (
E4E96DAC158E10FD002E6E0A /* Debug */,
E4E96DAD158E10FD002E6E0A /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
E4E96DAE158E10FD002E6E0A /* Build configuration list for PBXNativeTarget "NMSSH" */ = {
isa = XCConfigurationList;
buildConfigurations = (
E4E96DAF158E10FD002E6E0A /* Debug */,
E4E96DB0158E10FD002E6E0A /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
E4E96DB1158E10FD002E6E0A /* Build configuration list for PBXNativeTarget "NMSSHTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
E4E96DB2158E10FD002E6E0A /* Debug */,
E4E96DB3158E10FD002E6E0A /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = E4E96D7A158E10FD002E6E0A /* Project object */;
}

View File

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

View File

@@ -0,0 +1,8 @@
<?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>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View File

@@ -0,0 +1,81 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1000"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "E4E96D83158E10FD002E6E0A"
BuildableName = "NMSSH.framework"
BlueprintName = "NMSSH"
ReferencedContainer = "container:NMSSH.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "E4E96D9B158E10FD002E6E0A"
BuildableName = "NMSSHTests.xctest"
BlueprintName = "NMSSHTests"
ReferencedContainer = "container:NMSSH.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "E4E96D83158E10FD002E6E0A"
BuildableName = "NMSSH.framework"
BlueprintName = "NMSSH"
ReferencedContainer = "container:NMSSH.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -0,0 +1,19 @@
#ifndef NMSSH_Protected_h
#define NMSSH_Protected_h
#import <CoreFoundation/CoreFoundation.h>
#import <netinet/in.h>
#import <sys/socket.h>
#import <arpa/inet.h>
#import "socket_helper.h"
#define kNMSSHBufferSize (0x4000)
#define NMSSHLogVerbose(frmt, ...) [[NMSSHLogger logger] logVerbose:[NSString stringWithFormat:frmt, ##__VA_ARGS__]]
#define NMSSHLogInfo(frmt, ...) [[NMSSHLogger logger] logInfo:[NSString stringWithFormat:frmt, ##__VA_ARGS__]]
#define NMSSHLogWarn(frmt, ...) [[NMSSHLogger logger] logWarn:[NSString stringWithFormat:frmt, ##__VA_ARGS__]]
#define NMSSHLogError(frmt, ...) [[NMSSHLogger logger] logError:[NSString stringWithFormat:frmt, ##__VA_ARGS__]]
#define strlen (unsigned int)strlen
#endif

View File

@@ -0,0 +1,81 @@
#import "NMSSH.h"
typedef NS_OPTIONS(NSUInteger, NMSSHLogLevel) {
NMSSHLogLevelVerbose = (1 << 0 | 1 << 1 | 1 << 2 | 1 << 3),
NMSSHLogLevelInfo = (1 << 1 | 1 << 2 | 1 << 3),
NMSSHLogLevelWarn = (1 << 2 | 1 << 3),
NMSSHLogLevelError = (1 << 3)
};
/**
NMSSHLogger provides the functionality to customize the framework logging.
*/
@interface NMSSHLogger : NSObject
/// ----------------------------------------------------------------------------
/// @name Retrieve the shared logger
/// ----------------------------------------------------------------------------
/**
Retrieve the shared logger instance
@returns Shared logger
*/
+ (nonnull instancetype)logger NS_SWIFT_NAME(shared());
/// ----------------------------------------------------------------------------
/// @name Logger settings
/// ----------------------------------------------------------------------------
/**
The block called to print the log message.
The default implementation print the log
message using NSLog.
The block takes two argument:
_level_ - Log level<br>
_format_ - Log message
*/
@property (nonatomic, nonnull, copy) void (^logBlock)(NMSSHLogLevel level, NSString * _Nonnull format);
/** The maximum log level */
@property (nonatomic, assign) NMSSHLogLevel logLevel;
/** Enable or disable the logging feature */
@property (nonatomic, assign, getter = isEnabled) BOOL enabled;
/// ----------------------------------------------------------------------------
/// @name Logging
/// ----------------------------------------------------------------------------
/**
Log with verbose level
@param format Log message
*/
- (void)logVerbose:(nonnull NSString *)format;
/**
Log with info level
@param format Log message
*/
- (void)logInfo:(nonnull NSString *)format;
/**
Log with warn level
@param format Log message
*/
- (void)logWarn:(nonnull NSString *)format;
/**
Log with error level
@param format Log message
*/
- (void)logError:(nonnull NSString *)format;
@end

View File

@@ -0,0 +1,77 @@
#import "NMSSHLogger.h"
#import "NMSSH+Protected.h"
typedef NS_OPTIONS(NSUInteger, NMSSHLogFlag) {
NMSSHLogFlagVerbose = (1 << 0),
NMSSHLogFlagInfo = (1 << 1),
NMSSHLogFlagWarn = (1 << 2),
NMSSHLogFlagError = (1 << 3)
};
@interface NMSSHLogger ()
#if OS_OBJECT_USE_OBJC
@property (nonatomic, strong) dispatch_queue_t loggerQueue;
#else
@property (nonatomic, assign) dispatch_queue_t loggerQueue;
#endif
@end
@implementation NMSSHLogger
// -----------------------------------------------------------------------------
#pragma mark - INITIALIZE THE LOGGER INSTANCE
// -----------------------------------------------------------------------------
+ (NMSSHLogger *)logger {
static NMSSHLogger *logger = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
logger = [[NMSSHLogger alloc] init];
[logger setEnabled:YES];
[logger setLogLevel:NMSSHLogLevelVerbose];
[logger setLogBlock:^(NMSSHLogLevel level, NSString *format) {
NSLog(@"%@", format);
}];
[logger setLoggerQueue:dispatch_queue_create("NMSSH.loggerQueue", DISPATCH_QUEUE_SERIAL)];
});
return logger;
}
#if !(OS_OBJECT_USE_OBJC)
- (void)dealloc {
dispatch_release(self.loggerQueue);
}
#endif
// -----------------------------------------------------------------------------
#pragma mark - LOGGING
// -----------------------------------------------------------------------------
- (void)log:(NSString *)format level:(NMSSHLogLevel)level flag:(NMSSHLogFlag)flag {
if (flag & self.logLevel && self.enabled && self.logBlock) {
dispatch_async(self.loggerQueue, ^{
self.logBlock(level, [NSString stringWithFormat:@"NMSSH: %@", format]);
});
}
}
- (void)logVerbose:(NSString *)format {
[self log:format level:NMSSHLogLevelVerbose flag:NMSSHLogFlagVerbose];
}
- (void)logInfo:(NSString *)format{
[self log:format level:NMSSHLogLevelInfo flag:NMSSHLogFlagInfo];
}
- (void)logWarn:(NSString *)format{
[self log:format level:NMSSHLogLevelWarn flag:NMSSHLogFlagWarn];
}
- (void)logError:(NSString *)format{
[self log:format level:NMSSHLogLevelError flag:NMSSHLogFlagError];
}
@end

View File

@@ -0,0 +1,3 @@
#import "libssh2.h"
int waitsocket(int socket_fd, LIBSSH2_SESSION *session);

View File

@@ -0,0 +1,34 @@
#import "socket_helper.h"
#import <Foundation/Foundation.h>
#import <sys/time.h>
int waitsocket(int socket_fd, LIBSSH2_SESSION *session) {
struct timeval timeout;
fd_set fd;
fd_set *writefd = NULL;
fd_set *readfd = NULL;
int rc;
int dir;
timeout.tv_sec = 0;
timeout.tv_usec = 500000;
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;
}

View File

@@ -0,0 +1,26 @@
<?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>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</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>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSPrincipalClass</key>
<string></string>
</dict>
</plist>

310
Carthage/Checkouts/NMSSH/NMSSH/NMSFTP.h vendored Normal file
View File

@@ -0,0 +1,310 @@
#import "NMSSH.h"
@class NMSSHSession, NMSFTPFile;
/**
NMSFTP provides functionality for working with SFTP servers.
*/
@interface NMSFTP : NSObject
/** A valid NMSSHSession instance */
@property (nonatomic, nonnull, readonly) NMSSHSession *session;
/** Property that keeps track of connection status to the server */
@property (nonatomic, readonly, getter = isConnected) BOOL connected;
/** Property that set/get read buffer size */
@property (nonatomic) NSUInteger bufferSize;
///-----------------------------------------------------------------------------
/// @name Initializer
/// ----------------------------------------------------------------------------
/**
Create a new NMSFTP instance and connect it.
@param session A valid, connected, NMSSHSession instance
@returns Connected NMSFTP instance
*/
+ (nonnull instancetype)connectWithSession:(nonnull NMSSHSession *)session;
/**
Create a new NMSFTP instance.
@param session A valid, connected, NMSSHSession instance
@returns New NMSFTP instance
*/
- (nonnull instancetype)initWithSession:(nonnull NMSSHSession *)session;
/// ----------------------------------------------------------------------------
/// @name Connection
/// ----------------------------------------------------------------------------
/**
Create and connect to a SFTP session
@returns Connection status
*/
- (BOOL)connect;
/**
Disconnect SFTP session
*/
- (void)disconnect;
/// ----------------------------------------------------------------------------
/// @name Manipulate file system entries
/// ----------------------------------------------------------------------------
/**
Move or rename an item
@param sourcePath Item to move
@param destPath Destination to move to
@returns Move success
*/
- (BOOL)moveItemAtPath:(nonnull NSString *)sourcePath toPath:(nonnull NSString *)destPath;
/// ----------------------------------------------------------------------------
/// @name Manipulate directories
/// ----------------------------------------------------------------------------
/**
Test if a directory exists at the specified path.
Note: Will return NO if a file exists at the path, but not a directory.
@param path Path to check
@returns YES if file exists
*/
- (BOOL)directoryExistsAtPath:(nonnull NSString *)path;
/**
Create a directory at path
@param path Path to directory
@returns Creation success
*/
- (BOOL)createDirectoryAtPath:(nonnull NSString *)path;
/**
Remove directory at path
@param path Existing directory
@returns Remove success
*/
- (BOOL)removeDirectoryAtPath:(nonnull NSString *)path;
/**
Get a list of files for a directory path
@param path Existing directory to list items from
@returns List of relative paths
*/
- (nullable NSArray<NMSFTPFile *> *)contentsOfDirectoryAtPath:(nonnull NSString *)path;
/// ----------------------------------------------------------------------------
/// @name Manipulate symlinks and files
/// ----------------------------------------------------------------------------
/**
Reads the attributes from a file.
@param path An existing file path
@return A NMSFTPFile that contains the fetched file attributes.
*/
- (nullable NMSFTPFile *)infoForFileAtPath:(nonnull NSString *)path;
/**
Test if a file exists at the specified path.
Note: Will return NO if a directory exists at the path, but not a file.
@param path Path to check
@returns YES if file exists
*/
- (BOOL)fileExistsAtPath:(nonnull NSString *)path;
/**
Create a symbolic link
@param linkPath Path that will be linked to
@param destPath Path the link will be created at
@returns Creation success
*/
- (BOOL)createSymbolicLinkAtPath:(nonnull NSString *)linkPath
withDestinationPath:(nonnull NSString *)destPath;
/**
Remove file at path
@param path Path to existing file
@returns Remove success
*/
- (BOOL)removeFileAtPath:(nonnull NSString *)path;
/**
Read the contents of a file
@param path An existing file path
@returns File contents
*/
- (nullable NSData *)contentsAtPath:(nonnull NSString *)path;
/**
Refer to contentsAtPath:
This adds the ability to get periodic updates to bytes received.
@param path An existing file path
@param progress Method called periodically with number of bytes downloaded and total file size.
Returns NO to abort.
@returns File contents
*/
- (nullable NSData *)contentsAtPath:(nonnull NSString *)path progress:(BOOL (^_Nullable)(NSUInteger got, NSUInteger totalBytes))progress;
/**
Refer to contentsAtPath:
This adds the ability to get periodic updates to bytes received.
@param path An existing file path
@param stream Stream to write bytes to
@param progress Method called periodically with number of bytes downloaded and total file size. Returns NO to abort.
@return File read success
*/
- (BOOL)contentsAtPath:(nonnull NSString *)path toStream:(nonnull NSOutputStream *)stream progress:(BOOL (^_Nullable)(NSUInteger, NSUInteger))progress;
/**
Overwrite the contents of a file
If no file exists, one is created.
@param contents Bytes to write
@param path File path to write bytes at
@returns Write success
*/
- (BOOL)writeContents:(nonnull NSData *)contents toFileAtPath:(nonnull NSString *)path;
/**
Refer to writeContents:toFileAtPath:
This adds the ability to get periodic updates to bytes sent.
@param contents Bytes to write
@param path File path to write bytes at
@param progress Method called periodically with number of bytes sent.
Returns NO to abort.
@returns Write success
*/
- (BOOL)writeContents:(nonnull NSData *)contents toFileAtPath:(nonnull NSString *)path progress:(BOOL (^_Nullable)(NSUInteger sent))progress;
/**
Overwrite the contents of a file
If no file exists, one is created.
@param localPath File path to read bytes at
@param path File path to write bytes at
@returns Write success
*/
- (BOOL)writeFileAtPath:(nonnull NSString *)localPath toFileAtPath:(nonnull NSString *)path;
/**
Refer to writeFileAtPath:toFileAtPath:
This adds the ability to get periodic updates to bytes sent.
@param localPath File path to read bytes at
@param path File path to write bytes at
@param progress Method called periodically with number of bytes sent.
Returns NO to abort.
@returns Write success
*/
- (BOOL)writeFileAtPath:(nonnull NSString *)localPath toFileAtPath:(nonnull NSString *)path progress:(BOOL (^_Nullable)(NSUInteger sent))progress;
/**
Overwrite the contents of a file
If no file exists, one is created.
@param inputStream Stream to read bytes from
@param path File path to write bytes at
@returns Write success
*/
- (BOOL)writeStream:(nonnull NSInputStream *)inputStream toFileAtPath:(nonnull NSString *)path;
/**
Refer to writeStream:toFileAtPath:
This adds the ability to get periodic updates to bytes sent.
@param inputStream Stream to read bytes from
@param path File path to write bytes at
@param progress Method called periodically with number of bytes sent.
Returns NO to abort.
@returns Write success
*/
- (BOOL)writeStream:(nonnull NSInputStream *)inputStream toFileAtPath:(nonnull NSString *)path progress:(BOOL (^_Nullable)(NSUInteger sent))progress;
/**
Start or resume writing the contents of a file
If no file exists, one is created.
If the file already exists the size of the output file will be used as offset
and the input file will be appended to the output file, starting at that offset.
@param localPath File path to read bytes at
@param path File path to write bytes at
@param progress Method called periodically with number of bytes appended and total bytes.
Returns NO to abort.
@returns Write success
*/
- (BOOL)resumeFileAtPath:(nonnull NSString *)localPath toFileAtPath:(nonnull NSString *)path progress:(BOOL (^_Nullable)(NSUInteger delta, NSUInteger totalBytes))progress;
/**
Start or resume writing the contents of a file
If no file exists, one is created.
If the file already exists the size of the output file will be used as offset
and the inputstream will be appended to the output file, starting at that offset.
@param inputStream Stream to read bytes from
@param path File path to write bytes at
@param progress Method called periodically with number of bytes appended and total bytes.
Returns NO to abort.
@returns Write success
*/
- (BOOL)resumeStream:(nonnull NSInputStream *)inputStream toFileAtPath:(nonnull NSString *)path progress:(BOOL (^_Nullable)(NSUInteger delta, NSUInteger totalBytes))progress;
/**
Append contents to the end of a file
If no file exists, one is created.
@param contents Bytes to write
@param path File path to write bytes at
@returns Append success
*/
- (BOOL)appendContents:(nonnull NSData *)contents toFileAtPath:(nonnull NSString *)path;
/**
Append contents to the end of a file
If no file exists, one is created.
@param inputStream Stream to write bytes from
@param path File path to write bytes at
@returns Append success
*/
- (BOOL)appendStream:(nonnull NSInputStream *)inputStream toFileAtPath:(nonnull NSString *)path;
/**
Copy a file remotely.
@param fromPath Path to copy from
@param toPath Path to copy to
*/
- (BOOL)copyContentsOfPath:(nonnull NSString *)fromPath toFileAtPath:(nonnull NSString *)toPath progress:(BOOL (^_Nullable)(NSUInteger copied, NSUInteger totalBytes))progress;
@end

558
Carthage/Checkouts/NMSSH/NMSSH/NMSFTP.m vendored Normal file
View File

@@ -0,0 +1,558 @@
#import "NMSFTP.h"
#import "NMSSH+Protected.h"
@interface NMSFTP ()
@property (nonatomic, strong) NMSSHSession *session;
@property (nonatomic, assign) LIBSSH2_SFTP *sftpSession;
@property (nonatomic, readwrite, getter = isConnected) BOOL connected;
- (BOOL)writeStream:(NSInputStream *)inputStream toSFTPHandle:(LIBSSH2_SFTP_HANDLE *)handle;
- (BOOL)writeStream:(NSInputStream *)inputStream toSFTPHandle:(LIBSSH2_SFTP_HANDLE *)handle progress:(BOOL (^)(NSUInteger))progress;
- (BOOL)readContentsAtPath:(NSString *)path toStream:(NSOutputStream *)stream progress:(BOOL (^)(NSUInteger, NSUInteger))progress;
@end
@implementation NMSFTP
// -----------------------------------------------------------------------------
#pragma mark - INITIALIZER
// -----------------------------------------------------------------------------
+ (instancetype)connectWithSession:(NMSSHSession *)session {
NMSFTP *sftp = [[NMSFTP alloc] initWithSession:session];
[sftp connect];
return sftp;
}
- (instancetype)initWithSession:(NMSSHSession *)session {
if ((self = [super init])) {
[self setSession:session];
// Make sure we were provided a valid session
if (![session isKindOfClass:[NMSSHSession class]]) {
@throw @"You have to provide a valid NMSSHSession!";
}
}
return self;
}
// -----------------------------------------------------------------------------
#pragma mark - CONNECTION
// -----------------------------------------------------------------------------
- (BOOL)connect {
// Set blocking mode
libssh2_session_set_blocking(self.session.rawSession, 1);
[self setSftpSession:libssh2_sftp_init(self.session.rawSession)];
if (!self.sftpSession) {
NMSSHLogError(@"Unable to init SFTP session");
return NO;
}
[self setConnected:YES];
[self setBufferSize:kNMSSHBufferSize];
return self.isConnected;
}
- (void)disconnect {
libssh2_sftp_shutdown(self.sftpSession);
[self setConnected:NO];
}
// -----------------------------------------------------------------------------
#pragma mark - MANIPULATE FILE SYSTEM ENTRIES
// -----------------------------------------------------------------------------
- (BOOL)moveItemAtPath:(NSString *)sourcePath toPath:(NSString *)destPath {
return libssh2_sftp_rename(self.sftpSession, [sourcePath UTF8String], [destPath UTF8String]) == 0;
}
// -----------------------------------------------------------------------------
#pragma mark - MANIPULATE DIRECTORIES
// -----------------------------------------------------------------------------
- (LIBSSH2_SFTP_HANDLE *)openDirectoryAtPath:(NSString *)path {
LIBSSH2_SFTP_HANDLE *handle = libssh2_sftp_opendir(self.sftpSession, [path UTF8String]);
if (!handle) {
NSError *error = [self.session lastError];
NMSSHLogError(@"Could not open directory at path %@ (Error %li: %@)", path, (long)error.code, error.localizedDescription);
if ([error code] == LIBSSH2_ERROR_SFTP_PROTOCOL) {
NMSSHLogError(@"SFTP error %lu", libssh2_sftp_last_error(self.sftpSession));
}
}
return handle;
}
- (BOOL)directoryExistsAtPath:(NSString *)path {
LIBSSH2_SFTP_HANDLE *handle = [self openFileAtPath:path flags:LIBSSH2_FXF_READ mode:0];
if (!handle) {
return NO;
}
LIBSSH2_SFTP_ATTRIBUTES fileAttributes;
int rc = libssh2_sftp_fstat(handle, &fileAttributes);
libssh2_sftp_close(handle);
return rc == 0 && LIBSSH2_SFTP_S_ISDIR(fileAttributes.permissions);
}
- (BOOL)createDirectoryAtPath:(NSString *)path {
int rc = libssh2_sftp_mkdir(self.sftpSession, [path UTF8String],
LIBSSH2_SFTP_S_IRWXU|
LIBSSH2_SFTP_S_IRGRP|LIBSSH2_SFTP_S_IXGRP|
LIBSSH2_SFTP_S_IROTH|LIBSSH2_SFTP_S_IXOTH);
return rc == 0;
}
- (BOOL)removeDirectoryAtPath:(NSString *)path {
return libssh2_sftp_rmdir(self.sftpSession, [path UTF8String]) == 0;
}
- (NSArray *)contentsOfDirectoryAtPath:(NSString *)path {
LIBSSH2_SFTP_HANDLE *handle = [self openDirectoryAtPath:path];
if (!handle) {
return nil;
}
NSArray *ignoredFiles = @[@".", @".."];
NSMutableArray *contents = [NSMutableArray array];
int rc;
do {
char buffer[512];
LIBSSH2_SFTP_ATTRIBUTES fileAttributes;
rc = libssh2_sftp_readdir(handle, buffer, sizeof(buffer), &fileAttributes);
if (rc > 0) {
NSString *fileName = [[NSString alloc] initWithBytes:buffer length:rc encoding:NSUTF8StringEncoding];
if (![ignoredFiles containsObject:fileName]) {
// Append a "/" at the end of all directories
if (LIBSSH2_SFTP_S_ISDIR(fileAttributes.permissions)) {
fileName = [fileName stringByAppendingString:@"/"];
}
NMSFTPFile *file = [[NMSFTPFile alloc] initWithFilename:fileName];
[file populateValuesFromSFTPAttributes:fileAttributes];
[contents addObject:file];
}
}
} while (rc > 0);
if (rc < 0) {
NMSSHLogError(@"Unable to read directory");
}
rc = libssh2_sftp_closedir(handle);
if (rc < 0) {
NMSSHLogError(@"Failed to close directory");
}
return [contents sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
return [obj1 compare:obj2];
}];
}
// -----------------------------------------------------------------------------
#pragma mark - MANIPULATE SYMLINKS AND FILES
// -----------------------------------------------------------------------------
- (NMSFTPFile *)infoForFileAtPath:(NSString *)path {
LIBSSH2_SFTP_HANDLE *handle = [self openFileAtPath:path flags:LIBSSH2_FXF_READ mode:0];
if (!handle) {
return nil;
}
LIBSSH2_SFTP_ATTRIBUTES fileAttributes;
ssize_t rc = libssh2_sftp_fstat(handle, &fileAttributes);
libssh2_sftp_close(handle);
if (rc < 0) {
return nil;
}
NMSFTPFile *file = [[NMSFTPFile alloc] initWithFilename:path.lastPathComponent];
[file populateValuesFromSFTPAttributes:fileAttributes];
return file;
}
- (LIBSSH2_SFTP_HANDLE *)openFileAtPath:(NSString *)path flags:(unsigned long)flags mode:(long)mode {
LIBSSH2_SFTP_HANDLE *handle = libssh2_sftp_open(self.sftpSession, [path UTF8String], flags, mode);
if (!handle) {
NSError *error = [self.session lastError];
NMSSHLogError(@"Could not open file at path %@ (Error %li: %@)", path, (long)error.code, error.localizedDescription);
if ([error code] == LIBSSH2_ERROR_SFTP_PROTOCOL) {
NMSSHLogError(@"SFTP error %lu", libssh2_sftp_last_error(self.sftpSession));
}
}
return handle;
}
- (BOOL)fileExistsAtPath:(NSString *)path {
LIBSSH2_SFTP_HANDLE *handle = [self openFileAtPath:path flags:LIBSSH2_FXF_READ mode:0];
if (!handle) {
return NO;
}
LIBSSH2_SFTP_ATTRIBUTES fileAttributes;
int rc = libssh2_sftp_fstat(handle, &fileAttributes);
libssh2_sftp_close(handle);
return rc == 0 && !LIBSSH2_SFTP_S_ISDIR(fileAttributes.permissions);
}
- (BOOL)createSymbolicLinkAtPath:(NSString *)linkPath
withDestinationPath:(NSString *)destPath {
int rc = libssh2_sftp_symlink(self.sftpSession, [destPath UTF8String], (char *)[linkPath UTF8String]);
return rc == 0;
}
- (BOOL)removeFileAtPath:(NSString *)path {
return libssh2_sftp_unlink(self.sftpSession, [path UTF8String]) == 0;
}
- (NSData *)contentsAtPath:(NSString *)path {
return [self contentsAtPath:path progress:nil];
}
- (NSData *)contentsAtPath:(NSString *)path progress:(BOOL (^)(NSUInteger, NSUInteger))progress {
NSOutputStream *outputStream = [NSOutputStream outputStreamToMemory];
BOOL success = [self readContentsAtPath:path toStream:outputStream progress:progress];
if (success) {
return [outputStream propertyForKey:NSStreamDataWrittenToMemoryStreamKey];
} else {
return nil;
}
}
- (BOOL)contentsAtPath:(NSString *)path toStream:(NSOutputStream *)outputStream progress:(BOOL (^)(NSUInteger, NSUInteger))progress {
return [self readContentsAtPath:path toStream:outputStream progress:progress];
}
- (BOOL)readContentsAtPath:(NSString *)path toStream:(NSOutputStream *)outputStream progress:(BOOL (^)(NSUInteger, NSUInteger))progress {
LIBSSH2_SFTP_HANDLE *handle = [self openFileAtPath:path flags:LIBSSH2_FXF_READ mode:0];
if (!handle) {
return NO;
}
NMSFTPFile *file = [self infoForFileAtPath:path];
if (!file) {
NMSSHLogWarn(@"contentsAtPath:progress: failed to get file attributes");
return NO;
}
if ([outputStream streamStatus] == NSStreamStatusNotOpen) {
[outputStream open];
}
char buffer[self.bufferSize];
ssize_t rc;
NSUInteger got = 0;
while ((rc = libssh2_sftp_read(handle, buffer, (ssize_t)sizeof(buffer))) > 0) {
NSUInteger remainingBytes = rc;
NSInteger writeResult;
do {
writeResult = [outputStream write:(const uint8_t *)&buffer maxLength:remainingBytes];
remainingBytes -= MAX(0, writeResult);
} while (remainingBytes > 0 && writeResult > 0);
if (writeResult < 0 || (writeResult == 0 && remainingBytes > 0)) {
libssh2_sftp_close(handle);
[outputStream close];
return NO;
}
got += rc;
if (progress && !progress(got, (NSUInteger)[file.fileSize integerValue])) {
libssh2_sftp_close(handle);
[outputStream close];
return NO;
}
}
libssh2_sftp_close(handle);
[outputStream close];
if (rc < 0) {
return NO;
}
return YES;
}
- (BOOL)writeContents:(NSData *)contents toFileAtPath:(NSString *)path {
return [self writeContents:contents toFileAtPath:path progress:nil];
}
- (BOOL)writeContents:(NSData *)contents toFileAtPath:(NSString *)path progress:(BOOL (^)(NSUInteger))progress {
return [self writeStream:[NSInputStream inputStreamWithData:contents] toFileAtPath:path progress:progress];
}
- (BOOL)writeFileAtPath:(NSString *)localPath toFileAtPath:(NSString *)path {
return [self writeFileAtPath:localPath toFileAtPath:path progress:nil];
}
- (BOOL)writeFileAtPath:(NSString *)localPath toFileAtPath:(NSString *)path progress:(BOOL (^)(NSUInteger))progress {
return [self writeStream:[NSInputStream inputStreamWithFileAtPath:localPath] toFileAtPath:path progress:progress];
}
- (BOOL)writeStream:(NSInputStream *)inputStream toFileAtPath:(NSString *)path {
return [self writeStream:inputStream toFileAtPath:path progress:nil];
}
- (BOOL)writeStream:(NSInputStream *)inputStream toFileAtPath:(NSString *)path progress:(BOOL (^)(NSUInteger))progress {
if ([inputStream streamStatus] == NSStreamStatusNotOpen) {
[inputStream open];
}
if (![inputStream hasBytesAvailable]) {
NMSSHLogWarn(@"No bytes available in the stream");
return NO;
}
LIBSSH2_SFTP_HANDLE *handle = [self openFileAtPath:path
flags:LIBSSH2_FXF_WRITE|LIBSSH2_FXF_CREAT|LIBSSH2_FXF_TRUNC
mode:LIBSSH2_SFTP_S_IRUSR|LIBSSH2_SFTP_S_IWUSR|LIBSSH2_SFTP_S_IRGRP|LIBSSH2_SFTP_S_IROTH];
if (!handle) {
[inputStream close];
return NO;
}
BOOL success = [self writeStream:inputStream toSFTPHandle:handle progress:progress];
libssh2_sftp_close(handle);
[inputStream close];
return success;
}
- (BOOL)resumeFileAtPath:(NSString *)localPath toFileAtPath:(NSString *)path progress:(BOOL (^)( NSUInteger, NSUInteger ))progress {
return [self resumeStream:[NSInputStream inputStreamWithFileAtPath:localPath] toFileAtPath:path progress:progress];
}
- (BOOL)resumeStream:(NSInputStream *)inputStream toFileAtPath:(NSString *)path progress:(BOOL (^)( NSUInteger, NSUInteger ))progress {
if ([inputStream streamStatus] == NSStreamStatusNotOpen) {
[inputStream open];
}
if (![inputStream hasBytesAvailable]) {
NMSSHLogWarn(@"No bytes available in the stream");
return NO;
}
LIBSSH2_SFTP_HANDLE *handle = [self openFileAtPath:path
flags:LIBSSH2_FXF_WRITE|LIBSSH2_FXF_CREAT|LIBSSH2_FXF_READ
mode:LIBSSH2_SFTP_S_IRUSR|LIBSSH2_SFTP_S_IWUSR|LIBSSH2_SFTP_S_IRGRP|LIBSSH2_SFTP_S_IROTH];
if (!handle) {
[inputStream close];
return NO;
}
NMSSHLogVerbose(@"Resume destFile %@", path);
BOOL success = [self resumeStream:inputStream toSFTPHandle:handle progress:progress];
libssh2_sftp_close(handle);
[inputStream close];
return success;
}
- (BOOL)resumeStream:(NSInputStream *)inputStream toSFTPHandle:(LIBSSH2_SFTP_HANDLE *)handle progress:(BOOL (^)( NSUInteger, NSUInteger ))progress {
uint8_t buffer[self.bufferSize];
NSInteger bytesRead = -1;
long rc = 0;
NSUInteger delta = 0;
LIBSSH2_SFTP_ATTRIBUTES attributes;
if (libssh2_sftp_fstat(handle, &attributes) < 0) {
[inputStream close];
NMSSHLogError(@"Unable to get attributes of handle");
return NO;
}
libssh2_sftp_seek64(handle, attributes.filesize);
NMSSHLogVerbose(@"Seek to position %llu of destFile", attributes.filesize);
[inputStream setProperty:[NSNumber numberWithUnsignedLongLong:attributes.filesize] forKey:NSStreamFileCurrentOffsetKey];
while (rc >= 0 && [inputStream hasBytesAvailable]) {
bytesRead = [inputStream read:buffer maxLength:self.bufferSize];
if (bytesRead > 0) {
uint8_t *ptr = buffer;
do {
rc = libssh2_sftp_write(handle, (const char *)ptr, bytesRead);
if(rc < 0){
NMSSHLogWarn(@"libssh2_sftp_write failed (Error %li)", rc);
break;
}
delta += rc;
ptr += rc;
bytesRead -= rc;
if (progress && !progress(delta, delta + (NSUInteger)attributes.filesize))
{
return NO;
}
}while(bytesRead);
}
}
if (bytesRead < 0 || rc < 0) {
return NO;
}
return YES;
}
- (BOOL)appendContents:(NSData *)contents toFileAtPath:(NSString *)path {
return [self appendStream:[NSInputStream inputStreamWithData:contents] toFileAtPath:path];
}
- (BOOL)appendStream:(NSInputStream *)inputStream toFileAtPath:(NSString *)path {
if ([inputStream streamStatus] == NSStreamStatusNotOpen) {
[inputStream open];
}
if (![inputStream hasBytesAvailable]) {
NMSSHLogWarn(@"No bytes available in the stream");
return NO;
}
LIBSSH2_SFTP_HANDLE *handle = [self openFileAtPath:path
flags:LIBSSH2_FXF_WRITE|LIBSSH2_FXF_CREAT|LIBSSH2_FXF_READ
mode:LIBSSH2_SFTP_S_IRUSR|LIBSSH2_SFTP_S_IWUSR|LIBSSH2_SFTP_S_IRGRP|LIBSSH2_SFTP_S_IROTH];
if (!handle) {
[inputStream close];
return NO;
}
LIBSSH2_SFTP_ATTRIBUTES attributes;
if (libssh2_sftp_fstat(handle, &attributes) < 0) {
[inputStream close];
NMSSHLogError(@"Unable to get attributes of file %@", path);
return NO;
}
libssh2_sftp_seek64(handle, attributes.filesize);
NMSSHLogVerbose(@"Seek to position %ld", (long)attributes.filesize);
BOOL success = [self writeStream:inputStream toSFTPHandle:handle];
libssh2_sftp_close(handle);
[inputStream close];
return success;
}
- (BOOL)writeStream:(NSInputStream *)inputStream toSFTPHandle:(LIBSSH2_SFTP_HANDLE *)handle {
return [self writeStream:inputStream toSFTPHandle:handle progress:nil];
}
- (BOOL)writeStream:(NSInputStream *)inputStream toSFTPHandle:(LIBSSH2_SFTP_HANDLE *)handle progress:(BOOL (^)(NSUInteger))progress {
uint8_t buffer[self.bufferSize];
NSInteger bytesRead = -1;
long rc = 0;
NSUInteger total = 0;
while (rc >= 0 && [inputStream hasBytesAvailable]) {
bytesRead = [inputStream read:buffer maxLength:self.bufferSize];
if (bytesRead > 0) {
uint8_t *ptr = buffer;
do {
rc = libssh2_sftp_write(handle, (const char *)ptr, bytesRead);
if(rc < 0){
NMSSHLogWarn(@"libssh2_sftp_write failed (Error %li)", rc);
break;
}
total += rc;
ptr += rc;
bytesRead -= rc;
if (progress && !progress(total))
{
return NO;
}
}while(bytesRead);
}
}
if (bytesRead < 0 || rc < 0) {
return NO;
}
return YES;
}
- (BOOL)copyContentsOfPath:(NSString *)fromPath toFileAtPath:(NSString *)toPath progress:(BOOL (^)(NSUInteger, NSUInteger))progress
{
// Open handle for reading.
LIBSSH2_SFTP_HANDLE *fromHandle = [self openFileAtPath:fromPath flags:LIBSSH2_FXF_READ mode:0];
// Open handle for writing.
LIBSSH2_SFTP_HANDLE *toHandle = [self openFileAtPath:toPath
flags:LIBSSH2_FXF_WRITE|LIBSSH2_FXF_CREAT|LIBSSH2_FXF_READ
mode:LIBSSH2_SFTP_S_IRUSR|LIBSSH2_SFTP_S_IWUSR|LIBSSH2_SFTP_S_IRGRP|LIBSSH2_SFTP_S_IROTH];
// Get information about the file to copy.
NMSFTPFile *file = [self infoForFileAtPath:fromPath];
if (!file) {
NMSSHLogWarn(@"contentsAtPath:progress: failed to get file attributes");
return NO;
}
char buffer[self.bufferSize];
ssize_t bytesRead;
off_t copied = 0;
long rc = 0;
while ((bytesRead = libssh2_sftp_read(fromHandle, buffer, (ssize_t)sizeof(buffer))) > 0) {
if (bytesRead > 0) {
char *ptr = buffer;
do {
rc = libssh2_sftp_write(toHandle, (const char *)ptr, (NSInteger)bytesRead);
if(rc < 0){
NMSSHLogWarn(@"libssh2_sftp_write failed (Error %li)", rc);
break;
}
copied += rc;
ptr += rc;
bytesRead -= rc;
if (progress && !progress((NSUInteger)copied, (NSUInteger)[file.fileSize integerValue])) {
libssh2_sftp_close(fromHandle);
libssh2_sftp_close(toHandle);
return NO;
}
}while(bytesRead);
}
}
libssh2_sftp_close(fromHandle);
libssh2_sftp_close(toHandle);
return YES;
}
@end

View File

@@ -0,0 +1,64 @@
#import "NMSSH.h"
/**
The NMSFTPFile class provides an interface to store file attributes retrieved
from a SFTP host.
*/
@interface NMSFTPFile : NSObject <NSCopying>
/**
Property that stores the name of the underlaying file.
Note that the file may also be a directory.
*/
@property (nonatomic, nonnull, readonly) NSString *filename;
/** Property that declares whether the file is a directory or a regular file */
@property (nonatomic, readonly) BOOL isDirectory;
/** Returns the last modification date of the file */
@property (nonatomic, nullable, readonly) NSDate *modificationDate;
/** Returns the date of the last access to the file */
@property (nonatomic, nullable, readonly) NSDate *lastAccess;
/** Property that returns the file size in bytes */
@property (nonatomic, nullable, readonly) NSNumber *fileSize;
/** Returns the numeric identifier of the user that is the owner of the file */
@property (nonatomic, readonly) unsigned long ownerUserID;
/** Returns the numeric identifier of the group that is the owner of the file */
@property (nonatomic, readonly) unsigned long ownerGroupID;
/** Returns the file permissions in symbolic notation. E.g. drwxr-xr-x */
@property (nonatomic, nullable, readonly) NSString *permissions;
/** Returns the user defined flags for the file */
@property (nonatomic, readonly) u_long flags;
- (nonnull instancetype)init NS_UNAVAILABLE;
/**
Initializes an NMSFTPFile instance and sets the filename.
@param filename The name of the underlaying file.
@return A new NMSFTPFile instance initialized with the corresponding filename.
*/
- (nonnull instancetype)initWithFilename:(nonnull NSString *)filename;
/**
Convenience initializer for creating an NMSFTPFile instance with a defined filename.
@param filename The name of the underlaying file.
@return A new NMSFTPFile instance initialized with the corresponding filename.
*/
+ (nonnull instancetype)fileWithName:(nonnull NSString *)filename;
/**
Populates the file properties with the attributes taken from the LIBSSH2_SFTP_ATTRIBUTES object.
@param fileAttributes The LIBSSH2_SFTP_ATTRIBUTES object that contains the attributes that are being extracted.
*/
- (void)populateValuesFromSFTPAttributes:(LIBSSH2_SFTP_ATTRIBUTES)fileAttributes;
@end

View File

@@ -0,0 +1,175 @@
#import "NMSFTPFile.h"
#import "NMSSH+Protected.h"
@interface NMSFTPFile ()
@property (nonatomic, strong) NSString *filename;
@property (nonatomic, readwrite) BOOL isDirectory;
@property (nonatomic, strong) NSDate *modificationDate;
@property (nonatomic, strong) NSDate *lastAccess;
@property (nonatomic, strong) NSNumber *fileSize;
@property (nonatomic, readwrite) unsigned long ownerUserID;
@property (nonatomic, readwrite) unsigned long ownerGroupID;
@property (nonatomic, strong) NSString *permissions;
@property (nonatomic, readwrite) u_long flags;
@end
@implementation NMSFTPFile
- (instancetype)initWithFilename:(NSString *)filename {
if ((self = [super init])) {
[self setFilename:filename];
}
return self;
}
+ (instancetype)fileWithName:(NSString *)filename {
return [[self alloc] initWithFilename:filename];
}
- (void)populateValuesFromSFTPAttributes:(LIBSSH2_SFTP_ATTRIBUTES)fileAttributes {
[self setModificationDate:[NSDate dateWithTimeIntervalSince1970:fileAttributes.mtime]];
[self setLastAccess:[NSDate dateWithTimeIntervalSinceNow:fileAttributes.atime]];
[self setFileSize:@(fileAttributes.filesize)];
[self setOwnerUserID:fileAttributes.uid];
[self setOwnerGroupID:fileAttributes.gid];
[self setPermissions:[self convertPermissionToSymbolicNotation:fileAttributes.permissions]];
[self setIsDirectory:LIBSSH2_SFTP_S_ISDIR(fileAttributes.permissions)];
[self setFlags:fileAttributes.flags];
}
#pragma mark - Comparison and Equality
/**
Ensures that the sorting of the files is according to their filenames.
@param file The other file that it should be compared to.
@return The comparison result that determins the order of the two files.
*/
- (NSComparisonResult)compare:(NMSFTPFile *)file {
return [self.filename localizedCaseInsensitiveCompare:file.filename];
}
/**
Defines that two NMSFTPFile objects are equal, if their filenames are equal.
@param object The other file that it should be compared with
@return YES in case the two objects are considered equal, NO otherwise.
*/
- (BOOL)isEqual:(id)object {
if (![object isKindOfClass:[NMSFTPFile class]]) {
return NO;
}
return [self.filename isEqualToString:((NMSFTPFile *)object).filename];
}
#pragma mark - Permissions conversion methods
/**
Convert a mode field into "ls -l" type perms field. By courtesy of Jonathan Leffler
http://stackoverflow.com/questions/10323060/printing-file-permissions-like-ls-l-using-stat2-in-c
@param mode The numeric mode that is returned by the 'stat' function
@return A string containing the symbolic representation of the file permissions.
*/
- (NSString *)convertPermissionToSymbolicNotation:(unsigned long)mode {
static char *rwx[] = {"---", "--x", "-w-", "-wx", "r--", "r-x", "rw-", "rwx"};
char bits[11];
bits[0] = [self filetypeletter:mode];
strcpy(&bits[1], rwx[(mode >> 6)& 7]);
strcpy(&bits[4], rwx[(mode >> 3)& 7]);
strcpy(&bits[7], rwx[(mode & 7)]);
if (mode & S_ISUID) {
bits[3] = (mode & 0100) ? 's' : 'S';
}
if (mode & S_ISGID) {
bits[6] = (mode & 0010) ? 's' : 'l';
}
if (mode & S_ISVTX) {
bits[9] = (mode & 0100) ? 't' : 'T';
}
bits[10] = '\0';
return [NSString stringWithCString:bits encoding:NSUTF8StringEncoding];
}
/**
Extracts the unix letter for the file type of the given permission value.
@param mode The numeric mode that is returned by the 'stat' function
@return A character that represents the given file type.
*/
- (char)filetypeletter:(unsigned long)mode {
char c;
if (S_ISREG(mode)) {
c = '-';
}
else if (S_ISDIR(mode)) {
c = 'd';
}
else if (S_ISBLK(mode)) {
c = 'b';
}
else if (S_ISCHR(mode)) {
c = 'c';
}
#ifdef S_ISFIFO
else if (S_ISFIFO(mode)) {
c = 'p';
}
#endif
#ifdef S_ISLNK
else if (S_ISLNK(mode)) {
c = 'l';
}
#endif
#ifdef S_ISSOCK
else if (S_ISSOCK(mode)) {
c = 's';
}
#endif
#ifdef S_ISDOOR
// Solaris 2.6, etc.
else if (S_ISDOOR(mode)) {
c = 'D';
}
#endif
else {
// Unknown type -- possibly a regular file?
c = '?';
}
return c;
}
- (NSString *)description {
return [NSString stringWithFormat:@"<%@: %p> Filename: %@", NSStringFromClass([self class]), self, self.filename];
}
- (id)copyWithZone:(NSZone *)zone {
NMSFTPFile *object = [[[self class] allocWithZone:zone] init];
if (object) {
object.filename = [self.filename copyWithZone:zone];
object.modificationDate = [self.modificationDate copyWithZone:zone];
object.lastAccess = [self.lastAccess copyWithZone:zone];
object.fileSize = [self.fileSize copyWithZone:zone];
object.permissions = [self.permissions copyWithZone:zone];
object.isDirectory = self.isDirectory;
object.ownerUserID = self.ownerUserID;
object.ownerGroupID = self.ownerGroupID;
object.flags = self.flags;
}
return object;
}
@end

View File

@@ -0,0 +1,30 @@
<?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>English</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>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2012 Nine Muses AB. All rights reserved.</string>
<key>NSPrincipalClass</key>
<string></string>
</dict>
</plist>

View File

@@ -0,0 +1,5 @@
#ifdef __OBJC__
#import <Foundation/Foundation.h>
#endif

21
Carthage/Checkouts/NMSSH/NMSSH/NMSSH.h vendored Normal file
View File

@@ -0,0 +1,21 @@
#import <Foundation/Foundation.h>
#ifndef _NMSSH_
#define _NMSSH_
#import "libssh2.h"
#import "libssh2_sftp.h"
#import "NMSSHSessionDelegate.h"
#import "NMSSHChannelDelegate.h"
#import "NMSSHSession.h"
#import "NMSSHChannel.h"
#import "NMSFTP.h"
#import "NMSFTPFile.h"
#import "NMSSHConfig.h"
#import "NMSSHHostConfig.h"
#import "NMSSHLogger.h"
#endif

View File

@@ -0,0 +1,254 @@
#import "NMSSH.h"
@class NMSSHSession;
@protocol NMSSHChannelDelegate;
typedef NS_ENUM(NSInteger, NMSSHChannelError) {
NMSSHChannelExecutionError,
NMSSHChannelExecutionResponseError,
NMSSHChannelRequestPtyError,
NMSSHChannelExecutionTimeout,
NMSSHChannelAllocationError,
NMSSHChannelRequestShellError,
NMSSHChannelWriteError,
NMSSHChannelReadError
};
typedef NS_ENUM(NSInteger, NMSSHChannelPtyTerminal) {
NMSSHChannelPtyTerminalVanilla,
NMSSHChannelPtyTerminalVT100,
NMSSHChannelPtyTerminalVT102,
NMSSHChannelPtyTerminalVT220,
NMSSHChannelPtyTerminalAnsi,
NMSSHChannelPtyTerminalXterm
};
typedef NS_ENUM(NSInteger, NMSSHChannelType) {
NMSSHChannelTypeClosed, // Channel = NULL
NMSSHChannelTypeExec,
NMSSHChannelTypeShell,
NMSSHChannelTypeSCP,
NMSSHChannelTypeSubsystem // Not supported by NMSSH framework
};
/**
NMSSHChannel provides functionality to work with SSH shells and SCP.
*/
@interface NMSSHChannel : NSObject
/** A valid NMSSHSession instance */
@property (nonatomic, nonnull, readonly) NMSSHSession *session;
/** Size of the buffers used by the channel, defaults to 0x4000 */
@property (nonatomic, assign) NSUInteger bufferSize;
/// ----------------------------------------------------------------------------
/// @name Setting the Delegate
/// ----------------------------------------------------------------------------
/**
The receivers `delegate`.
You can use the `delegate` to receive asynchronous read from a shell.
*/
@property (nonatomic, nullable, weak) id<NMSSHChannelDelegate> delegate;
/// ----------------------------------------------------------------------------
/// @name Initializer
/// ----------------------------------------------------------------------------
/** Current channel type or `NMSSHChannelTypeClosed` if the channel is closed */
@property (nonatomic, readonly) NMSSHChannelType type;
- (nonnull instancetype)init NS_UNAVAILABLE;
/**
Create a new NMSSHChannel instance.
@param session A valid, connected, NMSSHSession instance
@returns New NMSSHChannel instance
*/
- (nonnull instancetype)initWithSession:(nonnull NMSSHSession *)session;
/// ----------------------------------------------------------------------------
/// @name Shell command execution
/// ----------------------------------------------------------------------------
/** The last response from a shell command execution */
@property (nonatomic, nullable, readonly) NSString *lastResponse;
/** Request a pseudo terminal before executing a command */
@property (nonatomic, assign) BOOL requestPty;
/** Terminal emulation mode if a PTY is requested, defaults to vanilla */
@property (nonatomic, assign) NMSSHChannelPtyTerminal ptyTerminalType;
/**
Execute a shell command on the server.
If an error occurs, it will return `nil` and populate the error object.
If requestPty is enabled request a pseudo terminal before running the
command.
@param command Any shell script that is available on the server
@param error Error handler
@returns Shell command response
*/
- (nonnull NSString *)execute:(nonnull NSString *)command error:(NSError * _Nullable * _Nullable)error;
/**
Execute a shell command on the server with a given timeout.
If an error occurs or the connection timed out, it will return `nil` and populate the error object.
If requestPty is enabled request a pseudo terminal before running the
command.
@param command Any shell script that is available on the server
@param error Error handler
@param timeout The time to wait (in seconds) before giving up on the request
@returns Shell command response
*/
- (nullable NSString *)execute:(nonnull NSString *)command error:(NSError * _Nullable * _Nullable)error timeout:(nonnull NSNumber *)timeout;
/// ----------------------------------------------------------------------------
/// @name Remote shell session
/// ----------------------------------------------------------------------------
/** User-defined environment variables for the session, defaults to `nil` */
@property (nonatomic, nullable, strong) NSDictionary *environmentVariables;
/**
Request a remote shell on the channel.
If an error occurs, it will return NO and populate the error object.
If requestPty is enabled request a pseudo terminal before running the
command.
@param error Error handler
@returns Shell initialization success
*/
- (BOOL)startShell:(NSError * _Nullable * _Nullable)error;
/**
Close a remote shell on an active channel.
*/
- (void)closeShell;
/**
Write a command on the remote shell.
If an error occurs or the connection timed out, it will return NO and populate the error object.
@param command Any command that is available on the server
@param error Error handler
@returns Shell write success
*/
- (BOOL)write:(nonnull NSString *)command error:(NSError * _Nullable * _Nullable)error;
/**
Write a command on the remote shell with a given timeout.
If an error occurs or the connection timed out, it will return NO and populate the error object.
@param command Any command that is available on the server
@param error Error handler
@param timeout The time to wait (in seconds) before giving up on the request
@returns Shell write success
*/
- (BOOL)write:(nonnull NSString *)command error:(NSError * _Nullable * _Nullable)error timeout:(nonnull NSNumber *)timeout;
/**
Write data on the remote shell.
If an error occurs or the connection timed out, it will return NO and populate the error object.
@param data Any data
@param error Error handler
@returns Shell write success
*/
- (BOOL)writeData:(nonnull NSData *)data error:(NSError * _Nullable * _Nullable)error;
/**
Write data on the remote shell with a given timeout.
If an error occurs or the connection timed out, it will return NO and populate the error object.
@param data Any data
@param error Error handler
@param timeout The time to wait (in seconds) before giving up on the request
@returns Shell write success
*/
- (BOOL)writeData:(nonnull NSData *)data error:(NSError * _Nullable * _Nullable)error timeout:(nonnull NSNumber *)timeout;
/**
Request size for the remote pseudo terminal.
This method should be called only after startShell:
@param width Width in characters for terminal
@param height Height in characters for terminal
@returns Size change success
*/
- (BOOL)requestSizeWidth:(NSUInteger)width height:(NSUInteger)height;
/// ----------------------------------------------------------------------------
/// @name SCP file transfer
/// ----------------------------------------------------------------------------
/**
Upload a local file to a remote server.
If to: specifies a directory, the file name from the original file will be
used.
@param localPath Path to a file on the local computer
@param remotePath Path to save the file to
@returns SCP upload success
*/
- (BOOL)uploadFile:(nonnull NSString *)localPath to:(nonnull NSString *)remotePath;
/**
Download a remote file to local the filesystem.
If to: specifies a directory, the file name from the original file will be
used.
@param remotePath Path to a file on the remote server
@param localPath Path to save the file to
@returns SCP download success
*/
- (BOOL)downloadFile:(nonnull NSString *)remotePath to:(nonnull NSString *)localPath;
/**
Download a remote file to local the filesystem.
If to: specifies a directory, the file name from the original file will be
used.
@param remotePath Path to a file on the remote server
@param localPath Path to save the file to
@param progress Method called periodically with number of bytes downloaded and total file size.
Returns NO to abort.
@returns SCP download success
*/
- (BOOL)downloadFile:(nonnull NSString *)remotePath
to:(nonnull NSString *)localPath
progress:(BOOL (^_Nullable)(NSUInteger, NSUInteger))progress;
/**
Upload a local file to a remote server.
If to: specifies a directory, the file name from the original file will be
used.
@param localPath Path to a file on the local computer
@param remotePath Path to save the file to
@param progress Method called periodically with number of bytes uploaded. Returns NO to abort.
@returns SCP upload success
*/
- (BOOL)uploadFile:(nonnull NSString *)localPath
to:(nonnull NSString *)remotePath
progress:(BOOL (^_Nullable)(NSUInteger))progress;
@end

View File

@@ -0,0 +1,685 @@
#import "NMSSHChannel.h"
#import "NMSSH+Protected.h"
@interface NMSSHChannel ()
@property (nonatomic, strong) NMSSHSession *session;
@property (nonatomic, assign) LIBSSH2_CHANNEL *channel;
@property (nonatomic, readwrite) NMSSHChannelType type;
@property (nonatomic, assign) const char *ptyTerminalName;
@property (nonatomic, strong) NSString *lastResponse;
#if OS_OBJECT_USE_OBJC
@property (nonatomic, strong) dispatch_source_t source;
#else
@property (nonatomic, assign) dispatch_source_t source;
#endif
@end
@implementation NMSSHChannel
// -----------------------------------------------------------------------------
#pragma mark - INITIALIZER
// -----------------------------------------------------------------------------
- (instancetype)initWithSession:(NMSSHSession *)session {
if ((self = [super init])) {
[self setSession:session];
[self setBufferSize:kNMSSHBufferSize];
[self setRequestPty:NO];
[self setPtyTerminalType:NMSSHChannelPtyTerminalVanilla];
[self setType:NMSSHChannelTypeClosed];
// Make sure we were provided a valid session
if (![self.session isKindOfClass:[NMSSHSession class]]) {
@throw @"You have to provide a valid NMSSHSession!";
}
}
return self;
}
- (BOOL)openChannel:(NSError *__autoreleasing *)error {
if (self.channel != NULL) {
NMSSHLogWarn(@"The channel will be closed before continue");
if (self.type == NMSSHChannelTypeShell) {
[self closeShell];
}
else {
[self closeChannel];
}
}
// Set blocking mode
libssh2_session_set_blocking(self.session.rawSession, 1);
// Open up the channel
LIBSSH2_CHANNEL *channel = libssh2_channel_open_session(self.session.rawSession);
if (channel == NULL){
NMSSHLogError(@"Unable to open a session");
if (error) {
*error = [NSError errorWithDomain:@"NMSSH"
code:NMSSHChannelAllocationError
userInfo:@{ NSLocalizedDescriptionKey : @"Channel allocation error" }];
}
return NO;
}
[self setChannel:channel];
// Try to set environment variables
if (self.environmentVariables) {
for (NSString *key in self.environmentVariables) {
if ([key isKindOfClass:[NSString class]] && [[self.environmentVariables objectForKey:key] isKindOfClass:[NSString class]]) {
libssh2_channel_setenv(self.channel, [key UTF8String], [[self.environmentVariables objectForKey:key] UTF8String]);
}
}
}
int rc = 0;
// If requested, try to allocate a pty
if (self.requestPty) {
rc = libssh2_channel_request_pty(self.channel, self.ptyTerminalName);
if (rc != 0) {
if (error) {
NSDictionary *userInfo = @{ NSLocalizedDescriptionKey : [NSString stringWithFormat:@"Error requesting %s pty: %@", self.ptyTerminalName, [[self.session lastError] localizedDescription]] };
*error = [NSError errorWithDomain:@"NMSSH"
code:NMSSHChannelRequestPtyError
userInfo:userInfo];
}
NMSSHLogError(@"Error requesting pseudo terminal");
[self closeChannel];
return NO;
}
}
return YES;
}
- (void)closeChannel {
// Set blocking mode
if (self.session.rawSession) {
libssh2_session_set_blocking(self.session.rawSession, 1);
}
if (self.channel) {
int rc;
rc = libssh2_channel_close(self.channel);
if (rc == 0) {
libssh2_channel_wait_closed(self.channel);
}
libssh2_channel_free(self.channel);
[self setType:NMSSHChannelTypeClosed];
[self setChannel:NULL];
}
}
- (BOOL)sendEOF {
int rc;
// Send EOF to host
rc = libssh2_channel_send_eof(self.channel);
NMSSHLogVerbose(@"Sent EOF to host (return code = %i)", rc);
return rc == 0;
}
- (void)waitEOF {
if (libssh2_channel_eof(self.channel) == 0) {
// Wait for host acknowledge
int rc = libssh2_channel_wait_eof(self.channel);
NMSSHLogVerbose(@"Received host acknowledge for EOF (return code = %i)", rc);
}
}
// -----------------------------------------------------------------------------
#pragma mark - SHELL COMMAND EXECUTION
// -----------------------------------------------------------------------------
- (const char *)ptyTerminalName {
switch (self.ptyTerminalType) {
case NMSSHChannelPtyTerminalVanilla:
return "vanilla";
case NMSSHChannelPtyTerminalVT100:
return "vt100";
case NMSSHChannelPtyTerminalVT102:
return "vt102";
case NMSSHChannelPtyTerminalVT220:
return "vt220";
case NMSSHChannelPtyTerminalAnsi:
return "ansi";
case NMSSHChannelPtyTerminalXterm:
return "xterm";
}
// catch invalid values
return "vanilla";
}
- (NSString *)execute:(NSString *)command error:(NSError *__autoreleasing *)error {
return [self execute:command error:error timeout:@0];
}
- (NSString *)execute:(NSString *)command error:(NSError *__autoreleasing *)error timeout:(NSNumber *)timeout {
NMSSHLogInfo(@"Exec command %@", command);
// In case of error...
NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithObject:command forKey:@"command"];
if (![self openChannel:error]) {
return nil;
}
[self setLastResponse:nil];
int rc = 0;
[self setType:NMSSHChannelTypeExec];
// Try executing command
rc = libssh2_channel_exec(self.channel, [command UTF8String]);
if (rc != 0) {
if (error) {
[userInfo setObject:[[self.session lastError] localizedDescription] forKey:NSLocalizedDescriptionKey];
[userInfo setObject:[NSString stringWithFormat:@"%i", rc] forKey:NSLocalizedFailureReasonErrorKey];
*error = [NSError errorWithDomain:@"NMSSH"
code:NMSSHChannelExecutionError
userInfo:userInfo];
}
NMSSHLogError(@"Error executing command");
[self closeChannel];
return nil;
}
// Set non-blocking mode
libssh2_session_set_blocking(self.session.rawSession, 0);
// Set the timeout for blocking session
CFAbsoluteTime time = CFAbsoluteTimeGetCurrent() + [timeout doubleValue];
// Fetch response from output buffer
NSMutableString *response = [[NSMutableString alloc] init];
for (;;) {
ssize_t rc;
char buffer[self.bufferSize];
char errorBuffer[self.bufferSize];
do {
rc = libssh2_channel_read(self.channel, buffer, (ssize_t)sizeof(buffer));
if (rc > 0) {
[response appendFormat:@"%@", [[NSString alloc] initWithBytes:buffer length:rc encoding:NSUTF8StringEncoding]];
}
// Store all errors that might occur
if (libssh2_channel_get_exit_status(self.channel)) {
if (error) {
ssize_t erc = libssh2_channel_read_stderr(self.channel, errorBuffer, (ssize_t)sizeof(errorBuffer));
NSString *desc = [[NSString alloc] initWithBytes:errorBuffer length:erc encoding:NSUTF8StringEncoding];
if (!desc) {
desc = @"An unspecified error occurred";
}
[userInfo setObject:desc forKey:NSLocalizedDescriptionKey];
[userInfo setObject:[NSString stringWithFormat:@"%zi", erc] forKey:NSLocalizedFailureReasonErrorKey];
*error = [NSError errorWithDomain:@"NMSSH"
code:NMSSHChannelExecutionError
userInfo:userInfo];
}
}
if (libssh2_channel_eof(self.channel) == 1 || rc == 0) {
while ((rc = libssh2_channel_read(self.channel, buffer, (ssize_t)sizeof(buffer))) > 0) {
[response appendFormat:@"%@", [[NSString alloc] initWithBytes:buffer length:rc encoding:NSUTF8StringEncoding] ];
}
[self setLastResponse:[response copy]];
[self closeChannel];
return self.lastResponse;
}
// Check if the connection timed out
if ([timeout longValue] > 0 && time < CFAbsoluteTimeGetCurrent()) {
if (error) {
NSString *desc = @"Connection timed out";
[userInfo setObject:desc forKey:NSLocalizedDescriptionKey];
*error = [NSError errorWithDomain:@"NMSSH"
code:NMSSHChannelExecutionTimeout
userInfo:userInfo];
}
while ((rc = libssh2_channel_read(self.channel, buffer, (ssize_t)sizeof(buffer))) > 0) {
[response appendFormat:@"%@", [[NSString alloc] initWithBytes:buffer length:rc encoding:NSUTF8StringEncoding] ];
}
[self setLastResponse:[response copy]];
[self closeChannel];
return self.lastResponse;
}
} while (rc > 0);
if (rc != LIBSSH2_ERROR_EAGAIN) {
break;
}
waitsocket(CFSocketGetNative([self.session socket]), self.session.rawSession);
}
// If we've got this far, it means fetching execution response failed
if (error) {
[userInfo setObject:[[self.session lastError] localizedDescription] forKey:NSLocalizedDescriptionKey];
*error = [NSError errorWithDomain:@"NMSSH"
code:NMSSHChannelExecutionResponseError
userInfo:userInfo];
}
NMSSHLogError(@"Error fetching response from command");
[self closeChannel];
return nil;
}
// -----------------------------------------------------------------------------
#pragma mark - REMOTE SHELL SESSION
// -----------------------------------------------------------------------------
- (BOOL)startShell:(NSError *__autoreleasing *)error {
NMSSHLogInfo(@"Starting shell");
if (![self openChannel:error]) {
return NO;
}
// Set non-blocking mode
libssh2_session_set_blocking(self.session.rawSession, 0);
// Fetch response from output buffer
#if !(OS_OBJECT_USE_OBJC)
if (self.source) {
dispatch_release(self.source);
}
#endif
[self setLastResponse:nil];
[self setSource:dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, CFSocketGetNative([self.session socket]),
0, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0))];
dispatch_source_set_event_handler(self.source, ^{
NMSSHLogVerbose(@"Data available on the socket!");
ssize_t rc, erc=0;
char buffer[self.bufferSize];
while (self.channel != NULL) {
rc = libssh2_channel_read(self.channel, buffer, (ssize_t)sizeof(buffer));
erc = libssh2_channel_read_stderr(self.channel, buffer, (ssize_t)sizeof(buffer));
if (!(rc >=0 || erc >= 0)) {
NMSSHLogVerbose(@"Return code of response %ld, error %ld", (long)rc, (long)erc);
if (rc == LIBSSH2_ERROR_SOCKET_RECV || erc == LIBSSH2_ERROR_SOCKET_RECV) {
NMSSHLogVerbose(@"Error received, closing channel...");
[self closeShell];
}
return;
}
else if (rc > 0) {
NSData *data = [[NSData alloc] initWithBytes:buffer length:rc];
NSString *response = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
[self setLastResponse:[response copy]];
if (response && self.delegate && [self.delegate respondsToSelector:@selector(channel:didReadData:)]) {
[self.delegate channel:self didReadData:self.lastResponse];
}
if (self.delegate && [self.delegate respondsToSelector:@selector(channel:didReadRawData:)]) {
[self.delegate channel:self didReadRawData:data];
}
}
else if (erc > 0) {
NSData *data = [[NSData alloc] initWithBytes:buffer length:erc];
NSString *response = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
if (response && self.delegate && [self.delegate respondsToSelector:@selector(channel:didReadError:)]) {
[self.delegate channel:self didReadError:response];
}
if (self.delegate && [self.delegate respondsToSelector:@selector(channel:didReadRawError:)]) {
[self.delegate channel:self didReadRawError:data];
}
}
else if (libssh2_channel_eof(self.channel) == 1) {
NMSSHLogVerbose(@"Host EOF received, closing channel...");
[self closeShell];
return;
}
}
});
dispatch_source_set_cancel_handler(self.source, ^{
NMSSHLogVerbose(@"Shell source cancelled");
if (self.delegate && [self.delegate respondsToSelector:@selector(channelShellDidClose:)]) {
[self.delegate channelShellDidClose:self];
}
});
dispatch_resume(self.source);
int rc = 0;
// Try opening the shell
while ((rc = libssh2_channel_shell(self.channel)) == LIBSSH2_ERROR_EAGAIN) {
waitsocket(CFSocketGetNative([self.session socket]), [self.session rawSession]);
}
if (rc != 0) {
NMSSHLogError(@"Shell request error");
if (error) {
*error = [NSError errorWithDomain:@"NMSSH"
code:NMSSHChannelRequestShellError
userInfo:@{ NSLocalizedDescriptionKey : [[self.session lastError] localizedDescription] }];
}
[self closeShell];
return NO;
}
NMSSHLogVerbose(@"Shell allocated");
[self setType:NMSSHChannelTypeShell];
return YES;
}
- (void)closeShell {
if (self.source) {
dispatch_source_cancel(self.source);
#if !(OS_OBJECT_USE_OBJC)
dispatch_release(self.source);
#endif
[self setSource: nil];
}
if (self.type == NMSSHChannelTypeShell) {
// Set blocking mode
libssh2_session_set_blocking(self.session.rawSession, 1);
[self sendEOF];
}
[self closeChannel];
}
- (BOOL)write:(NSString *)command error:(NSError *__autoreleasing *)error {
return [self write:command error:error timeout:@0];
}
- (BOOL)write:(NSString *)command error:(NSError *__autoreleasing *)error timeout:(NSNumber *)timeout {
return [self writeData:[command dataUsingEncoding:NSUTF8StringEncoding] error:error timeout:timeout];
}
- (BOOL)writeData:(NSData *)data error:(NSError *__autoreleasing *)error {
return [self writeData:data error:error timeout:@0];
}
- (BOOL)writeData:(NSData *)data error:(NSError *__autoreleasing *)error timeout:(NSNumber *)timeout {
if (self.type != NMSSHChannelTypeShell) {
NMSSHLogError(@"Shell required");
return NO;
}
ssize_t rc;
// Set the timeout
CFAbsoluteTime time = CFAbsoluteTimeGetCurrent() + [timeout doubleValue];
// Try writing on shell
while ((rc = libssh2_channel_write(self.channel, [data bytes], [data length])) == LIBSSH2_ERROR_EAGAIN) {
// Check if the connection timed out
if ([timeout longValue] > 0 && time < CFAbsoluteTimeGetCurrent()) {
if (error) {
NSString *description = @"Connection timed out";
*error = [NSError errorWithDomain:@"NMSSH"
code:NMSSHChannelExecutionTimeout
userInfo:@{ NSLocalizedDescriptionKey : description }];
}
return NO;
}
waitsocket(CFSocketGetNative([self.session socket]), self.session.rawSession);
}
if (rc < 0) {
NMSSHLogError(@"Error writing on the shell");
if (error) {
NSString *command = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
*error = [NSError errorWithDomain:@"NMSSH"
code:NMSSHChannelWriteError
userInfo:@{ NSLocalizedDescriptionKey : [[self.session lastError] localizedDescription],
@"command" : command }];
}
}
return YES;
}
- (BOOL)requestSizeWidth:(NSUInteger)width height:(NSUInteger)height {
int rc = libssh2_channel_request_pty_size(self.channel, (int)width, (int)height);
if (rc) {
NMSSHLogError(@"Request size failed with error %i", rc);
}
return rc == 0;
}
// -----------------------------------------------------------------------------
#pragma mark - SCP FILE TRANSFER
// -----------------------------------------------------------------------------
- (BOOL)uploadFile:(NSString *)localPath to:(NSString *)remotePath {
return [self uploadFile:localPath to:remotePath progress:NULL];
}
- (BOOL)uploadFile:(NSString *)localPath to:(NSString *)remotePath progress:(BOOL (^)(NSUInteger))progress {
if (self.channel != NULL) {
NMSSHLogWarn(@"The channel will be closed before continue");
if (self.type == NMSSHChannelTypeShell) {
[self closeShell];
}
else {
[self closeChannel];
}
}
localPath = [localPath stringByExpandingTildeInPath];
// Inherit file name if to: contains a directory
if ([remotePath hasSuffix:@"/"]) {
remotePath = [remotePath stringByAppendingString:
[[localPath componentsSeparatedByString:@"/"] lastObject]];
}
// Read local file
FILE *local = fopen([localPath UTF8String], "rb");
if (!local) {
NMSSHLogError(@"Can't read local file");
return NO;
}
// Set blocking mode
libssh2_session_set_blocking(self.session.rawSession, 1);
// Try to send a file via SCP.
struct stat fileinfo;
stat([localPath UTF8String], &fileinfo);
LIBSSH2_CHANNEL *channel = libssh2_scp_send64(self.session.rawSession, [remotePath UTF8String], fileinfo.st_mode & 0644,
(unsigned long)fileinfo.st_size, 0, 0);;
if (channel == NULL) {
NMSSHLogError(@"Unable to open SCP session");
fclose(local);
return NO;
}
[self setChannel:channel];
[self setType:NMSSHChannelTypeSCP];
// Wait for file transfer to finish
char mem[self.bufferSize];
size_t nread;
char *ptr;
long rc;
NSUInteger total = 0;
BOOL abort = NO;
while (!abort && (nread = fread(mem, 1, sizeof(mem), local)) > 0) {
ptr = mem;
do {
// Write the same data over and over, until error or completion
rc = libssh2_channel_write(self.channel, ptr, nread);
if (rc < 0) {
NMSSHLogError(@"Failed writing file");
[self closeChannel];
return NO;
}
else {
// rc indicates how many bytes were written this time
total += rc;
if (progress && !progress(total)) {
abort = YES;
break;
}
ptr += rc;
nread -= rc;
}
} while (nread);
};
fclose(local);
if ([self sendEOF]) {
[self waitEOF];
}
[self closeChannel];
return !abort;
}
- (BOOL)downloadFile:(NSString *)remotePath to:(NSString *)localPath {
return [self downloadFile:remotePath to:localPath progress:NULL];
}
- (BOOL)downloadFile:(NSString *)remotePath to:(NSString *)localPath progress:(BOOL (^)(NSUInteger, NSUInteger))progress {
if (self.channel != NULL) {
NMSSHLogWarn(@"The channel will be closed before continue");
if (self.type == NMSSHChannelTypeShell) {
[self closeShell];
}
else {
[self closeChannel];
}
}
localPath = [localPath stringByExpandingTildeInPath];
// Inherit file name if to: contains a directory
if ([localPath hasSuffix:@"/"]) {
localPath = [localPath stringByAppendingString:[[remotePath componentsSeparatedByString:@"/"] lastObject]];
}
// Set blocking mode
libssh2_session_set_blocking(self.session.rawSession, 1);
// Request a file via SCP
struct stat fileinfo;
LIBSSH2_CHANNEL *channel = libssh2_scp_recv(self.session.rawSession, [remotePath UTF8String], &fileinfo);
if (channel == NULL) {
NMSSHLogError(@"Unable to open SCP session");
return NO;
}
[self setChannel:channel];
[self setType:NMSSHChannelTypeSCP];
if ([[NSFileManager defaultManager] fileExistsAtPath:localPath]) {
NMSSHLogInfo(@"A file already exists at %@, it will be overwritten", localPath);
[[NSFileManager defaultManager] removeItemAtPath:localPath error:nil];
}
// Open local file in order to write to it
int localFile = open([localPath UTF8String], O_WRONLY|O_CREAT, 0644);
// Save data to local file
off_t got = 0;
while (got < fileinfo.st_size) {
char mem[self.bufferSize];
size_t amount = sizeof(mem);
if ((fileinfo.st_size - got) < amount) {
amount = (size_t)(fileinfo.st_size - got);
}
ssize_t rc = libssh2_channel_read(self.channel, mem, amount);
if (rc > 0) {
size_t n = write(localFile, mem, rc);
if (n < rc) {
NMSSHLogError(@"Failed to write to local file");
close(localFile);
[self closeChannel];
return NO;
}
got += rc;
if (progress && !progress((NSUInteger)got, (NSUInteger)fileinfo.st_size)) {
close(localFile);
[self closeChannel];
return NO;
}
}
else if (rc < 0) {
NMSSHLogError(@"Failed to read SCP data");
close(localFile);
[self closeChannel];
return NO;
}
memset(mem, 0x0, sizeof(mem));
}
close(localFile);
[self closeChannel];
return YES;
}
@end

View File

@@ -0,0 +1,39 @@
#import "NMSSH.h"
@class NMSSHHostConfig;
/**
NMSSHConfig parses ssh config files and returns matching entries for a given
host name.
*/
@interface NMSSHConfig : NSObject
/** The array of parsed NMSSHHostConfig objects. */
@property (nonatomic, nonnull, readonly) NSArray<NMSSHHostConfig *> *hostConfigs;
/**
Creates a new NMSSHConfig, reads the given {filename} and parses it.
@param filename Path to an ssh config file.
@returns NMSSHConfig instance or nil if the config file couldn't be parsed.
*/
+ (nullable instancetype)configFromFile:(nonnull NSString *)filename;
/**
Initializes an NMSSHConfig from a config file's contents in a string.
@param contents A config file's contents.
@returns An NMSSHConfig object or nil if the contents were malformed.
*/
- (nullable instancetype)initWithString:(nonnull NSString *)contents;
/**
Searches the config for an entry matching {host}.
@param host A host name to search for.
@returns An NMSSHHostConfig object whose patterns match host or nil if none is
found.
*/
- (nullable NMSSHHostConfig *)hostConfigForHost:(nonnull NSString *)host;
@end

View File

@@ -0,0 +1,407 @@
#import "NMSSHConfig.h"
#import "NMSSHHostConfig.h"
/** Describes how a host string matches a pattern in a NMSSHHostConfig. */
typedef enum {
/** Host matches a pattern */
NMSSHConfigMatchPositive,
/** Host matches a negated pattern */
NMSSHConfigMatchNegative,
/** Host matches no patterns */
NMSSHConfigMatchNone
} NMSSHConfigMatch;
@interface NMSSHConfig ()
@property(nonatomic, strong) NSArray *hostConfigs;
@end
@implementation NMSSHConfig
+ (instancetype)configFromFile:(NSString *)filename {
return [[self alloc] initWithFile:filename];
}
- (instancetype)initWithFile:(NSString *)filename {
NSString *contents = [NSString stringWithContentsOfFile:filename
encoding:NSUTF8StringEncoding
error:NULL];
return [self initWithString:contents];
}
- (instancetype)initWithString:(NSString *)contents {
if (contents == nil) {
return nil;
}
if ((self = [super init])) {
[self setHostConfigs:[self arrayFromString:contents]];
if (_hostConfigs == nil) {
return nil;
}
}
return self;
}
// -----------------------------------------------------------------------------
#pragma mark - PARSING
// -----------------------------------------------------------------------------
- (NSArray *)arrayFromString:(NSString *)contents {
if (contents == nil) {
return nil;
}
contents = [contents stringByReplacingOccurrencesOfString:@"\r\n"
withString:@"\n"];
NSArray *lines = [contents componentsSeparatedByString:@"\n"];
NSMutableArray *array = [NSMutableArray array];
for (NSString *line in lines) {
[self parseLine:line intoArray:array];
}
return [array copy];
}
- (void)parseLine:(NSString *)line intoArray:(NSMutableArray *)array {
// Trim spaces
line = [line stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
// Pull out the range of the first token.
NSString *arguments;
NSRange range = [self rangeOfFirstTokenInString:line suffix:&arguments];
if (range.location == NSNotFound) {
return;
}
// Get the string value of the first token.
NSString *keyword = [line substringWithRange:range];
if ([keyword hasPrefix:@"#"] ||
[keyword length] == 0) {
return;
}
// Parse the line based on the first token.
if ([keyword localizedCaseInsensitiveCompare:@"host"] == NSOrderedSame) {
[self parseHostWithArguments:arguments intoArray:array];
}
else if ([keyword localizedCaseInsensitiveCompare:@"hostname"] == NSOrderedSame) {
[self parseHostNameWithArguments:arguments intoArray:array];
}
else if ([keyword localizedCaseInsensitiveCompare:@"user"] == NSOrderedSame) {
[self parseUserWithArguments:arguments intoArray:array];
}
else if ([keyword localizedCaseInsensitiveCompare:@"port"] == NSOrderedSame) {
[self parsePortWithArguments:arguments intoArray:array];
}
else if ([keyword localizedCaseInsensitiveCompare:@"identityfile"] == NSOrderedSame) {
[self parseIdentityFileWithArguments:arguments intoArray:array];
}
}
- (void)parseHostWithArguments:(NSString *)arguments intoArray:(NSMutableArray *)array {
NMSSHHostConfig *config = [[NMSSHHostConfig alloc] init];
NSString *next;
NSRange hostRange = [self rangeOfFirstTokenInString:arguments suffix:&next];
while (hostRange.location != NSNotFound) {
if (hostRange.length > 0) {
NSString *hostPattern = [arguments substringWithRange:hostRange];
[config setHostPatterns:[config.hostPatterns arrayByAddingObject:hostPattern]];
}
arguments = next;
hostRange = [self rangeOfFirstTokenInString:arguments suffix:&next];
}
if ([config.hostPatterns count] > 0) {
[array addObject:config];
}
}
- (void)parseHostNameWithArguments:(NSString *)arguments
intoArray:(NSMutableArray *)array {
if ([array count] == 0) {
return;
}
NMSSHHostConfig *config = [array lastObject];
NSRange valueRange = [self rangeOfFirstTokenInString:arguments suffix:NULL];
if (valueRange.location != NSNotFound &&
valueRange.length > 0) {
[config setHostname:[arguments substringWithRange:valueRange]];
}
}
- (void)parseUserWithArguments:(NSString *)arguments
intoArray:(NSMutableArray *)array {
if ([array count] == 0) {
return;
}
NMSSHHostConfig *config = [array lastObject];
NSRange valueRange = [self rangeOfFirstTokenInString:arguments suffix:NULL];
if (valueRange.location != NSNotFound &&
valueRange.length > 0) {
[config setUser:[arguments substringWithRange:valueRange]];
}
}
- (void)parsePortWithArguments:(NSString *)arguments
intoArray:(NSMutableArray *)array {
if ([array count] == 0) {
return;
}
NMSSHHostConfig *config = [array lastObject];
NSRange valueRange = [self rangeOfFirstTokenInString:arguments suffix:NULL];
if (valueRange.location != NSNotFound &&
valueRange.length > 0) {
NSString *portString = [arguments substringWithRange:valueRange];
NSInteger port = [portString intValue];
if (port >= 0) {
[config setPort:@(port & 0xffff)];
}
}
}
- (void)parseIdentityFileWithArguments:(NSString *)arguments
intoArray:(NSMutableArray *)array {
if ([array count] == 0) {
return;
}
NMSSHHostConfig *config = [array lastObject];
NSRange valueRange = [self rangeOfFirstTokenInString:arguments suffix:NULL];
if (valueRange.location != NSNotFound &&
valueRange.length > 0) {
NSString *identityFile =
[[arguments substringWithRange:valueRange] stringByExpandingTildeInPath];
[config setIdentityFiles:[config.identityFiles arrayByAddingObject:identityFile]];
}
}
- (NSCharacterSet *)blanksCharacterSet {
NSMutableCharacterSet *blanksCharacterSet = [[NSMutableCharacterSet alloc] init];
[blanksCharacterSet addCharactersInRange:NSMakeRange(' ', 1)];
[blanksCharacterSet addCharactersInRange:NSMakeRange('\t', 1)];
return blanksCharacterSet;
}
// Returns the range of a quoted substring in line starting with a quote at location. If there is
// no matching close quote then a location of NSNotFound is returned. Otherwise, the range of the
// text inside the quotes (excluding the quotes) is returned.
- (NSRange)rangeOfQuotedSubstringInString:(NSString *)line
startingAtIndex:(NSUInteger)location {
NSUInteger start = location + 1;
NSRange possiblyQuotedRange = NSMakeRange(start,
[line length] - start);
NSRange rangeOfCloseQuote = [line rangeOfString:@"\""
options:0
range:possiblyQuotedRange];
if (rangeOfCloseQuote.location == NSNotFound) {
return NSMakeRange(NSNotFound, 0);
}
else {
return NSMakeRange(start, rangeOfCloseQuote.location - start);
}
}
- (NSRange)rangeInString:(NSString *)line fromLocationUntilBlankOrEnd:(NSUInteger)location {
NSRange tailRange = NSMakeRange(location,
[line length] - location);
NSRange terminatingBlank = [line rangeOfCharacterFromSet:[self blanksCharacterSet]
options:0
range:tailRange];
if (terminatingBlank.location == NSNotFound) {
return tailRange;
}
else {
return NSMakeRange(location, NSMaxRange(terminatingBlank) - location - 1);
}
}
- (NSRange)rangeOfFirstTokenInString:(NSString *)line suffix:(NSString **)suffixPtr {
NSCharacterSet *blanksCharacterSet = [self blanksCharacterSet];
NSMutableCharacterSet *nonBlanksCharacterSet = [blanksCharacterSet mutableCopy];
[nonBlanksCharacterSet invert];
NSRange rangeOfFirstNonBlank = [line rangeOfCharacterFromSet:nonBlanksCharacterSet];
if (rangeOfFirstNonBlank.location == NSNotFound) {
return rangeOfFirstNonBlank;
}
if ([line characterAtIndex:rangeOfFirstNonBlank.location] == '"') {
NSRange range = [self rangeOfQuotedSubstringInString:line
startingAtIndex:rangeOfFirstNonBlank.location];
if (suffixPtr != NULL && range.location != NSNotFound) {
*suffixPtr = [line substringFromIndex:NSMaxRange(range) + 1];
}
return range;
}
else {
NSRange range =
[self rangeInString:line fromLocationUntilBlankOrEnd:rangeOfFirstNonBlank.location];
if (suffixPtr != NULL) {
*suffixPtr = [line substringFromIndex:NSMaxRange(range)];
}
return range;
}
}
// -----------------------------------------------------------------------------
#pragma mark - MATCHING
// -----------------------------------------------------------------------------
- (NMSSHHostConfig *)hostConfigForHost:(NSString *)host {
NMSSHHostConfig *combinedConfig = [[NMSSHHostConfig alloc] init];
BOOL foundAny = NO;
for (NMSSHHostConfig *config in _hostConfigs) {
NMSSHConfigMatch match = NMSSHConfigMatchNone;
for (NSString *pattern in config.hostPatterns) {
switch ([self host:host matchesPatternList:pattern]) {
case NMSSHConfigMatchPositive:
match = NMSSHConfigMatchPositive;
break;
case NMSSHConfigMatchNegative:
match = NMSSHConfigMatchNegative;
break;
case NMSSHConfigMatchNone:
break;
}
if (match == NMSSHConfigMatchNegative) {
break;
}
}
if (match == NMSSHConfigMatchPositive) {
[combinedConfig mergeFrom:config];
foundAny = YES;
}
}
return foundAny ? combinedConfig : nil;
}
// A pattern list is a comma-delimited sequence of subpatterns. A subpattern is a string with
// wildcards optionally preceded by an !. If the host matches any negated subpattern then it is a
// negative match. Otherwise, if the host matches any non-negated subpattern then it is a positive
// match. If the host matches no patterns then it is a None match.
- (NMSSHConfigMatch)host:(NSString *)host matchesPatternList:(NSString *)patternList {
NSArray *patterns = [patternList componentsSeparatedByString:@","];
NMSSHConfigMatch match = NMSSHConfigMatchNone;
for (NSString *mixedCasePattern in patterns) {
NSString *pattern = [mixedCasePattern lowercaseString];
BOOL negated = NO;
if ([pattern hasPrefix:@"!"]) {
negated = YES;
pattern = [pattern substringFromIndex:1];
}
if ([self host:host matchesSubpattern:pattern]) {
if (negated) {
return NMSSHConfigMatchNegative;
}
else {
match = NMSSHConfigMatchPositive;
}
}
}
return match;
}
- (BOOL)host:(NSString *)host matchesSubpattern:(NSString *)subPattern {
if (host == nil || subPattern == nil) {
return NO;
}
NSUInteger patternIndex = 0;
NSUInteger patternLength = subPattern.length;
NSUInteger hostIndex = 0;
NSUInteger hostLength = host.length;
while (1) {
if (patternIndex == patternLength) {
return hostIndex == hostLength;
}
unichar patternChar = [subPattern characterAtIndex:patternIndex];
if (patternChar == '*') {
++patternIndex;
if (patternIndex == patternLength) {
// If at end of pattenr, accept immediately.
return YES;
}
// If next character in pattern is not a wildcard, optimize.
unichar patternPeek = [subPattern characterAtIndex:patternIndex];
if (patternPeek != '?' && patternPeek != '*') {
// Look for an instance in the host of the next char to match in the pattern.
for (; hostIndex < hostLength; hostIndex++) {
unichar hostChar = [host characterAtIndex:hostIndex];
if (hostChar == patternPeek) {
NSString *tailHost = [host substringFromIndex:hostIndex + 1];
NSString *tailSubpattern = [subPattern substringFromIndex:patternIndex + 1];
if ([self host:tailHost matchesSubpattern:tailSubpattern]) {
return YES;
}
}
}
// Failed.
return NO;
}
// Move ahead one char at a time and try to match at each position
for (; hostIndex < hostLength; ++hostIndex) {
NSString *tailHost = [host substringFromIndex:hostIndex];
NSString *tailPattern = [subPattern substringFromIndex:patternIndex];
if ([self host:tailHost matchesSubpattern:tailPattern]) {
return YES;
}
}
// Failed
return NO;
}
// There must be at least one more char in the string. If we reached the end, then fail.
if (hostIndex == hostLength) {
return NO;
}
unichar hostChar = [host characterAtIndex:hostIndex];
// Check if the next character of the string is acceptable.
if (patternChar != '?' && patternChar != hostChar) {
return NO;
}
++hostIndex;
++patternIndex;
}
// Unreachable code.
return NO;
}
@end

View File

@@ -0,0 +1,51 @@
#import "NMSSH.h"
/**
NMSSHHostConfig describes a single host's configuration.
*/
@interface NMSSHHostConfig : NSObject
/**
Patterns specified in the config file.
*/
@property(nonatomic, strong) NSArray *hostPatterns;
/**
Specifies the real host name to log into. If the hostname contains the
character sequence `%h', then the client should replace this with the
user-specified host name (this is useful for manipulating unqualified names).
This may be an IP address.
*/
@property(nonatomic, strong) NSString *hostname;
/**
Specifies the user name to log in as.
*/
@property(nonatomic, strong) NSString *user;
/**
Specifies the port number to connect on the remote host.
*/
@property(nonatomic, strong) NSNumber *port;
/**
Specifies alist of file names from which the user's DSA, ECDSA or RSA
authentication identity are read. It is empty by default. Tildes will be
expanded to the user's home directory. The client should perform the following
substitutions on each file name:
"%d" should be replaced with the local user home directory
"%u" should be replaced with the local user name
"%l" should be replaced with the local host name
"%h" should be replaced with the remote host name
"%r" should be replaced with the remote user name
If multiple identities are provided, the client should try them in order.
*/
@property(nonatomic, strong) NSArray *identityFiles;
/**
Values for {other} are copied to {self} if not already set. Arrays are
appended from {other} without adding duplicates.
*/
- (void)mergeFrom:(NMSSHHostConfig *)other;
@end

View File

@@ -0,0 +1,45 @@
#import "NMSSHHostConfig.h"
@implementation NMSSHHostConfig
- (id)init {
if ((self = [super init])) {
[self setHostPatterns:@[ ]];
[self setIdentityFiles:@[ ]];
}
return self;
}
- (NSArray *)arrayByRemovingDuplicateElementsFromArray:(NSArray *)array {
NSMutableArray *deduped = [NSMutableArray array];
for (NSObject *object in array) {
if (![deduped containsObject:object]) {
[deduped addObject:object];
}
}
return [deduped copy];
}
- (NSArray *)mergedArray:(NSArray *)firstArray withArray:(NSArray *)secondArray {
NSArray *concatenated = [firstArray arrayByAddingObjectsFromArray:secondArray];
return [self arrayByRemovingDuplicateElementsFromArray:concatenated];
}
- (void)mergeFrom:(NMSSHHostConfig *)other {
[self setHostPatterns:[self mergedArray:self.hostPatterns
withArray:other.hostPatterns]];
if (!self.hostname) {
[self setHostname:other.hostname];
}
if (!self.user) {
[self setUser:other.user];
}
if (self.port == nil) {
[self setPort:other.port];
}
[self setIdentityFiles:[self mergedArray:self.identityFiles
withArray:other.identityFiles]];
}
@end

View File

@@ -0,0 +1,348 @@
#import "NMSSH.h"
@class NMSSHHostConfig, NMSFTP;
@protocol NMSSHSessionDelegate;
typedef NS_ENUM(NSInteger, NMSSHSessionHash) {
NMSSHSessionHashMD5,
NMSSHSessionHashSHA1
};
typedef NS_ENUM(NSInteger, NMSSHKnownHostStatus) {
NMSSHKnownHostStatusMatch,
NMSSHKnownHostStatusMismatch,
NMSSHKnownHostStatusNotFound,
NMSSHKnownHostStatusFailure
};
/**
NMSSHSession provides the functionality required to setup a SSH connection
and authorize against it.
In its simplest form it works like this:
NMSSHSession *session = [NMSSHSession connectToHost:@"127.0.0.1:22"
withUsername:@"user"];
if (session.isConnected) {
NSLog(@"Successfully created a new session");
}
[session authenticateByPassword:@"pass"];
if (session.isAuthorized) {
NSLog(@"Successfully authorized");
}
## Thread safety
NMSSH classes are not thread safe, you should use them from the same thread
where you created the NMSSHSession instance.
If you want to use multiple NMSSHSession instances at once you should implement
the [crypto mutex callbacks](http://trac.libssh2.org/wiki/MultiThreading).
*/
@interface NMSSHSession : NSObject
/// ----------------------------------------------------------------------------
/// @name Setting the Delegate
/// ----------------------------------------------------------------------------
/**
The receivers `delegate`.
The `delegate` is sent messages when content is loading.
*/
@property (nonatomic, nullable, weak) id<NMSSHSessionDelegate> delegate;
/**
The synthesized config for the current host, produced by combining values from
all configs in the chain by priority, plus client-provided defaults. This is
only set if NMSSHSession was initialized with a config chain.
*/
@property (nonatomic, nullable, readonly) NMSSHHostConfig *hostConfig;
/// ----------------------------------------------------------------------------
/// @name Initialize a new SSH session
/// ----------------------------------------------------------------------------
- (nonnull instancetype)init NS_UNAVAILABLE;
/**
Shorthand method for initializing a NMSSHSession object and calling connect.
@param host The server hostname (a port number can be specified by appending
`@":{port}"`; for IPv6 addresses with a port, use
`@"[{host}]:{port}"`.
@param username A valid username the server will accept
@returns NMSSHSession instance
*/
+ (nonnull instancetype)connectToHost:(nonnull NSString *)host withUsername:(nonnull NSString *)username;
/**
Shorthand method for initializing a NMSSHSession object and calling connect,
(explicitly setting a port number).
@param host The server hostname
@param port The port number
@param username A valid username the server will accept
@returns NMSSHSession instance
*/
+ (nonnull instancetype)connectToHost:(nonnull NSString *)host port:(NSInteger)port withUsername:(nonnull NSString *)username;
/**
Create and setup a new NMSSH instance.
@param host The server hostname (a port number can be specified by appending
`@":{port}"`; for IPv6 addresses with a port, use
`@"[{host}]:{port}"`.
@param username A valid username the server will accept
@returns NMSSHSession instance
*/
- (nonnull instancetype)initWithHost:(nonnull NSString *)host andUsername:(nonnull NSString *)username;
/**
Create and setup a new NMSSH instance. This is the designated initializer.
@param host The server hostname
@param port The port number
@param username A valid username the server will accept
@returns NMSSHSession instance
*/
- (nonnull instancetype)initWithHost:(nonnull NSString *)host port:(NSInteger)port andUsername:(nonnull NSString *)username NS_DESIGNATED_INITIALIZER;
/**
Create and setup a new NMSSH instance using a chain of config files.
The {host} is used to perform a lookup in NMSSHConfig objects in the
{configs} array, which may alias the hostname and provide a port number or user
name. All information relevant to {host} in {configs} will be combined into
a single NMSSHHostConfig and saved in {self.hostConfig}. A config-
provided value will override {host}, {defaultUsername} or {defaultPort}. If no
match is found then {host} is used as the host name.
@param host Host to search {configs} with. If no matching config specifies a
hostname, then {host} is the server hostname.
@param configs An array of NMSSHHostConfig objects ordered from highest to
lowest priority
@param defaultPort The port number (may be overridden by a config)
@param defaultUsername A valid username the server will accept (may be
overridden by a config)
*/
- (nonnull instancetype)initWithHost:(nonnull NSString *)host
configs:(nonnull NSArray *)configs
withDefaultPort:(NSInteger)defaultPort
defaultUsername:(nonnull NSString *)defaultUsername;
/// ----------------------------------------------------------------------------
/// @name Connection settings
/// ----------------------------------------------------------------------------
/** Full server hostname in the format `@"{hostname}"`. */
@property (nonatomic, nonnull, readonly) NSString *host;
/** The server port to connect to. */
@property (nonatomic, nonnull, readonly) NSNumber *port;
/** Username that will authenticate against the server. */
@property (nonatomic, nonnull, readonly) NSString *username;
/** Timeout for libssh2 blocking functions. */
@property (nonatomic, nonnull, strong) NSNumber *timeout;
/** Last session error. */
@property (nonatomic, nullable, readonly) NSError *lastError;
/** The hash algorithm to use to encode the fingerprint during connection, default value is NMSSHSessionHashMD5. */
@property (nonatomic, assign) NMSSHSessionHash fingerprintHash;
/** The banner that will be sent to the remote host when the SSH session is started. */
@property (nonatomic, nullable, strong) NSString *banner;
/** The remote host banner. */
@property (nonatomic, nullable, readonly) NSString *remoteBanner;
/// ----------------------------------------------------------------------------
/// @name Raw libssh2 session and socket reference
/// ----------------------------------------------------------------------------
/** Raw libssh2 session instance. */
@property (nonatomic, nullable, readonly, getter = rawSession) LIBSSH2_SESSION *session;
/** Raw session socket. */
@property (nonatomic, nullable, readonly) CFSocketRef socket;
/// ----------------------------------------------------------------------------
/// @name Open/Close a connection to the server
/// ----------------------------------------------------------------------------
/**
A Boolean value indicating whether the session connected successfully
(read-only).
*/
@property (nonatomic, readonly, getter = isConnected) BOOL connected;
/**
Connect to the server using the default timeout (10 seconds)
@returns Connection status
*/
- (BOOL)connect;
/**
Connect to the server.
@param timeout The time, in seconds, to wait before giving up.
@returns Connection status
*/
- (BOOL)connectWithTimeout:(nonnull NSNumber *)timeout;
/**
Close the session
*/
- (void)disconnect;
/// ----------------------------------------------------------------------------
/// @name Authentication
/// ----------------------------------------------------------------------------
/**
A Boolean value indicating whether the session is successfully authorized
(read-only).
*/
@property (nonatomic, readonly, getter = isAuthorized) BOOL authorized;
/**
Authenticate by password
@param password Password for connected user
@returns Authentication success
*/
- (BOOL)authenticateByPassword:(nonnull NSString *)password;
/**
Authenticate by private key pair from file(s)
Use password:nil when the key is unencrypted
@param publicKey Filepath to public key
@param privateKey Filepath to private key
@param password Password for encrypted private key
@returns Authentication success
*/
- (BOOL)authenticateByPublicKey:(nonnull NSString *)publicKey
privateKey:(nonnull NSString *)privateKey
andPassword:(nullable NSString *)password;
/**
Authenticate by private key pair
Use password:nil when the key is unencrypted
@param publicKey public key
@param privateKey private key
@param password Password for encrypted private key
@returns Authentication success
*/
- (BOOL)authenticateByInMemoryPublicKey:(nonnull NSString *)publicKey
privateKey:(nonnull NSString *)privateKey
andPassword:(nullable NSString *)password;
/**
Authenticate by keyboard-interactive using delegate.
@returns Authentication success
*/
- (BOOL)authenticateByKeyboardInteractive;
/**
Authenticate by keyboard-interactive using block.
@param authenticationBlock The block to apply to server requests.
The block takes one argument:
_request_ - Question from server<br>
The block returns a NSString object that represents a valid response
to the given question.
@returns Authentication success
*/
- (BOOL)authenticateByKeyboardInteractiveUsingBlock:(NSString * _Nonnull(^_Nullable)( NSString * _Nonnull request))authenticationBlock;
/**
Setup and connect to an SSH agent
@returns Authentication success
*/
- (BOOL)connectToAgent;
/**
Get supported authentication methods
@returns Array of string descripting supported authentication methods
*/
- (nullable NSArray<NSString *> *)supportedAuthenticationMethods;
/**
Get the fingerprint of the remote host.
The session must be connected to an host.
@param hashType The hash algorithm to use to encode the fingerprint
@returns The host's fingerprint
*/
- (nullable NSString *)fingerprint:(NMSSHSessionHash)hashType;
/// ----------------------------------------------------------------------------
/// @name Known hosts
/// ----------------------------------------------------------------------------
/**
Checks if the hosts's key is recognized.
The session must be connected. Each file is checked in order,
returning as soon as the host is found.
@warning In a sandboxed Mac app or iOS app, _files_ must not be `nil` as the default files are not accessible.
@param files An array of filenames to check, or `nil` to use the default paths.
@returns Known host status for current host.
*/
- (NMSSHKnownHostStatus)knownHostStatusInFiles:(nullable NSArray<NSString *> *)files;
/**
Adds the passed-in host to the user's known hosts file.
_hostName_ may be a numerical IP address or a full name. If it includes
a port number, it should be formatted as `@"[{host}]:{port}"` (e.g., `@"[example.com]:2222"`).
If _salt_ is set, then _hostName_ must contain a hostname salted and hashed with SHA1 and then base64-encoded.
A simple example:
[session addKnownHostName:session.host
port:[session.port intValue]
toFile:nil
withSalt:nil];
@warning On Mac OS, the default filename is `~/.ssh/known_hosts`, and will not be writable in a sandboxed environment.
@param hostName The hostname or IP address to add.
@param port The port number of the host to add.
@param fileName The path to the known_hosts file, or `nil` for the default.
@param salt The base64-encoded salt used for hashing. May be `nil`.
@returns Success status.
*/
- (BOOL)addKnownHostName:(nonnull NSString *)hostName
port:(NSInteger)port
toFile:(nullable NSString *)fileName
withSalt:(nullable NSString *)salt;
/// ----------------------------------------------------------------------------
/// @name Quick channel/sftp access
/// ----------------------------------------------------------------------------
/** Get a pre-configured NMSSHChannel object for the current session (read-only). */
@property (nonatomic, nonnull, readonly) NMSSHChannel *channel;
/** Get a pre-configured NMSFTP object for the current session (read-only). */
@property (nonatomic, nonnull, readonly) NMSFTP *sftp;
@end

View File

@@ -0,0 +1,872 @@
#import "NMSSHSession.h"
#import "NMSSH+Protected.h"
#import "NMSSHConfig.h"
#import "NMSSHHostConfig.h"
@interface NMSSHSession ()
@property (nonatomic, assign) LIBSSH2_AGENT *agent;
@property (nonatomic, assign, getter = rawSession) LIBSSH2_SESSION *session;
@property (nonatomic, readwrite, getter = isConnected) BOOL connected;
@property (nonatomic, strong) NSString *host;
@property (nonatomic, strong) NSString *username;
@property (nonatomic, copy) NSString *(^kbAuthenticationBlock)(NSString *);
@property (nonatomic, strong) NMSSHChannel *channel;
@property (nonatomic, strong) NMSFTP *sftp;
@property (nonatomic, strong) NSNumber *port;
@property (nonatomic, strong) NMSSHHostConfig *hostConfig;
@property (nonatomic, assign) LIBSSH2_SESSION *sessionToFree;
@end
@implementation NMSSHSession
// -----------------------------------------------------------------------------
#pragma mark - INITIALIZE A NEW SSH SESSION
// -----------------------------------------------------------------------------
+ (instancetype)connectToHost:(NSString *)host port:(NSInteger)port withUsername:(NSString *)username {
NMSSHSession *session = [[NMSSHSession alloc] initWithHost:host
port:port
andUsername:username];
[session connect];
return session;
}
+ (instancetype)connectToHost:(NSString *)host withUsername:(NSString *)username {
NMSSHSession *session = [[NMSSHSession alloc] initWithHost:host
andUsername:username];
[session connect];
return session;
}
- (instancetype)initWithHost:(NSString *)host port:(NSInteger)port andUsername:(NSString *)username {
if ((self = [super init])) {
[self setHost:host];
[self setPort:@(port)];
[self setUsername:username];
[self setConnected:NO];
[self setFingerprintHash:NMSSHSessionHashMD5];
}
return self;
}
- (instancetype)initWithHost:(NSString *)host
configs:(NSArray *)configs
withDefaultPort:(NSInteger)defaultPort
defaultUsername:(NSString *)defaultUsername {
// Merge matching entries from configs together.
NMSSHHostConfig *hostConfig = [[NMSSHHostConfig alloc] init];
for (NMSSHConfig *config in configs) {
NMSSHHostConfig *matchingHostConfig = [config hostConfigForHost:host];
if (matchingHostConfig) {
[hostConfig mergeFrom:matchingHostConfig];
}
}
// Merge in defaults.
NMSSHHostConfig *defaultHostConfig = [[NMSSHHostConfig alloc] init];
[defaultHostConfig setHostname:host];
[defaultHostConfig setPort:@(defaultPort)];
[defaultHostConfig setUser:defaultUsername];
[hostConfig mergeFrom:defaultHostConfig];
// Initialize with resulting config.
self = [self initWithHost:hostConfig.hostname
port:[hostConfig.port integerValue]
andUsername:hostConfig.user];
if (self) {
[self setHostConfig:hostConfig];
}
return self;
}
- (instancetype)initWithHost:(NSString *)host andUsername:(NSString *)username {
NSURL *url = [[self class] URLForHost:host];
return [self initWithHost:[url host]
port:[([url port] ?: @22) intValue]
andUsername:username];
}
+ (NSURL *)URLForHost:(NSString *)host {
// Check if host is IPv6 and wrap in square brackets.
if ([[host componentsSeparatedByString:@":"] count] >= 3 &&
![host hasPrefix:@"["]) {
host = [NSString stringWithFormat:@"[%@]", host];
}
return [NSURL URLWithString:[@"ssh://" stringByAppendingString:host]];
}
- (void)dealloc {
if (self.sessionToFree) {
libssh2_session_free(self.sessionToFree);
}
}
// -----------------------------------------------------------------------------
#pragma mark - CONNECTION SETTINGS
// -----------------------------------------------------------------------------
- (NSArray *)hostIPAddresses {
NSArray *hostComponents = [_host componentsSeparatedByString:@":"];
NSInteger components = [hostComponents count];
NSString *address = hostComponents[0];
// Check if the host is [{IPv6}]:{port}
if (components >= 4 && [hostComponents[0] hasPrefix:@"["] && [hostComponents[components-2] hasSuffix:@"]"]) {
address = [_host substringWithRange:NSMakeRange(1, [_host rangeOfString:@"]" options:NSBackwardsSearch].location-1)];
} // Check if the host is {IPv6}
else if (components >= 3) {
address = _host;
}
CFHostRef host = CFHostCreateWithName(kCFAllocatorDefault, (__bridge CFStringRef)address);
CFStreamError error;
NSArray *addresses = nil;
if (host) {
NMSSHLogVerbose(@"Start %@ resolution", address);
if (CFHostStartInfoResolution(host, kCFHostAddresses, &error)) {
addresses = (__bridge NSArray *)(CFHostGetAddressing(host, NULL));
}
else {
NMSSHLogError(@"Unable to resolve host %@", address);
}
CFRelease(host);
}
else {
NMSSHLogError(@"Error allocating CFHost for %@", address);
}
return addresses;
}
- (NSNumber *)timeout {
if (self.session) {
return @(libssh2_session_get_timeout(self.session) / 1000);
}
return @0;
}
- (void)setTimeout:(NSNumber *)timeout {
if (self.session) {
libssh2_session_set_timeout(self.session, [timeout longValue] * 1000);
}
}
- (NSError *)lastError {
if(!self.rawSession) {
return [NSError errorWithDomain:@"libssh2" code:LIBSSH2_ERROR_NONE userInfo:@{NSLocalizedDescriptionKey : @"Error retrieving last session error due to absence of an active session."}];
}
char *message;
int error = libssh2_session_last_error(self.rawSession, &message, NULL, 0);
return [NSError errorWithDomain:@"libssh2"
code:error
userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithUTF8String:message] }];
}
- (NSString *)remoteBanner {
const char *banner = libssh2_session_banner_get(self.session);
if (!banner) {
return nil;
}
return [[NSString alloc] initWithCString:banner encoding:NSUTF8StringEncoding];
}
// -----------------------------------------------------------------------------
#pragma mark - OPEN/CLOSE A CONNECTION TO THE SERVER
// -----------------------------------------------------------------------------
- (BOOL)connect {
return [self connectWithTimeout:[NSNumber numberWithLong:10]];
}
- (BOOL)connectWithTimeout:(NSNumber *)timeout {
if (self.isConnected) {
[self disconnect];
}
__block BOOL initialized = YES;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// Try to initialize libssh2
if (libssh2_init(0) != 0) {
NMSSHLogError(@"libssh2 initialization failed");
initialized = NO;
}
NMSSHLogVerbose(@"libssh2 (v%s) initialized", libssh2_version(0));
});
if (!initialized) {
return NO;
}
// Try to establish a connection to the server
NSUInteger index = -1;
NSInteger port = [self.port integerValue];
NSArray *addresses = [self hostIPAddresses];
CFSocketError error = 1;
CFDataRef address = NULL;
SInt32 addressFamily;
while (addresses && ++index < [addresses count] && error) {
NSData *addressData = addresses[index];
NSString *ipAddress;
// IPv4
if ([addressData length] == sizeof(struct sockaddr_in)) {
struct sockaddr_in address4;
[addressData getBytes:&address4 length:sizeof(address4)];
address4.sin_port = htons(port);
char str[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &(address4.sin_addr), str, INET_ADDRSTRLEN);
ipAddress = [NSString stringWithCString:str encoding:NSUTF8StringEncoding];
addressFamily = AF_INET;
address = CFDataCreate(kCFAllocatorDefault, (UInt8 *)&address4, sizeof(address4));
} // IPv6
else if([addressData length] == sizeof(struct sockaddr_in6)) {
struct sockaddr_in6 address6;
[addressData getBytes:&address6 length:sizeof(address6)];
address6.sin6_port = htons(port);
char str[INET6_ADDRSTRLEN];
inet_ntop(AF_INET6, &(address6.sin6_addr), str, INET6_ADDRSTRLEN);
ipAddress = [NSString stringWithCString:str encoding:NSUTF8StringEncoding];
addressFamily = AF_INET6;
address = CFDataCreate(kCFAllocatorDefault, (UInt8 *)&address6, sizeof(address6));
}
else {
NMSSHLogVerbose(@"Unknown address, it's not IPv4 or IPv6!");
continue;
}
// Try to create the socket
_socket = CFSocketCreate(kCFAllocatorDefault, addressFamily, SOCK_STREAM, IPPROTO_IP, kCFSocketNoCallBack, NULL, NULL);
if (!_socket) {
NMSSHLogError(@"Error creating the socket");
CFRelease(address);
return NO;
}
// Set NOSIGPIPE
int set = 1;
if (setsockopt(CFSocketGetNative(_socket), SOL_SOCKET, SO_NOSIGPIPE, (void *)&set, sizeof(set)) != 0) {
NMSSHLogError(@"Error setting socket option");
CFRelease(address);
[self disconnect];
return NO;
}
error = CFSocketConnectToAddress(_socket, address, [timeout doubleValue]);
CFRelease(address);
if (error) {
NMSSHLogVerbose(@"Socket connection to %@ on port %ld failed with reason %li, trying next address...", ipAddress, (long)port, error);
}
else {
NMSSHLogInfo(@"Socket connection to %@ on port %ld succesful", ipAddress, (long)port);
}
}
if (error) {
NMSSHLogError(@"Failure establishing socket connection");
[self disconnect];
return NO;
}
// Create a session instance
[self setSession:libssh2_session_init_ex(NULL, NULL, NULL, (__bridge void *)(self))];
// Set a callback for disconnection
libssh2_session_callback_set(self.session, LIBSSH2_CALLBACK_DISCONNECT, &disconnect_callback);
// Set blocking mode
libssh2_session_set_blocking(self.session, 1);
// Set the custom banner
if (self.banner && libssh2_session_banner_set(self.session, [self.banner UTF8String])) {
NMSSHLogError(@"Failure setting the banner");
}
// Start the session
if (libssh2_session_handshake(self.session, CFSocketGetNative(_socket))) {
NMSSHLogError(@"Failure establishing SSH session");
[self disconnect];
return NO;
}
NMSSHLogVerbose(@"Remote host banner is %@", [self remoteBanner]);
// Get the fingerprint of the host
NSString *fingerprint = [self fingerprint:self.fingerprintHash];
NMSSHLogInfo(@"The host's fingerprint is %@", fingerprint);
if (self.delegate && [self.delegate respondsToSelector:@selector(session:shouldConnectToHostWithFingerprint:)] &&
![self.delegate session:self shouldConnectToHostWithFingerprint:fingerprint]) {
NMSSHLogWarn(@"Fingerprint refused, aborting connection...");
[self disconnect];
return NO;
}
NMSSHLogVerbose(@"SSH session started");
// We managed to successfully setup a connection
[self setConnected:YES];
return self.isConnected;
}
- (void)disconnect {
if (_channel) {
[_channel closeShell];
[self setChannel:nil];
}
if (_sftp) {
if ([_sftp isConnected]) {
[_sftp disconnect];
}
[self setSftp:nil];
}
if (self.agent) {
libssh2_agent_disconnect(self.agent);
libssh2_agent_free(self.agent);
[self setAgent:NULL];
}
if (self.session) {
libssh2_session_disconnect(self.session, "NMSSH: Disconnect");
[self setSessionToFree:self.session];
[self setSession:NULL];
}
if (_socket) {
CFSocketInvalidate(_socket);
CFRelease(_socket);
_socket = NULL;
}
NMSSHLogVerbose(@"Disconnected");
[self setConnected:NO];
}
// -----------------------------------------------------------------------------
#pragma mark - AUTHENTICATION
// -----------------------------------------------------------------------------
- (BOOL)isAuthorized {
if (self.session) {
return libssh2_userauth_authenticated(self.session) == 1;
}
return NO;
}
- (BOOL)authenticateByPassword:(NSString *)password {
if (!password) {
return NO;
}
if (![self supportsAuthenticationMethod:@"password"]) {
return NO;
}
// Try to authenticate by password
int error = libssh2_userauth_password(self.session, [self.username UTF8String], [password UTF8String]);
if (error) {
NMSSHLogError(@"Password authentication failed with reason %i", error);
return NO;
}
NMSSHLogVerbose(@"Password authentication succeeded.");
return self.isAuthorized;
}
- (BOOL)authenticateByPublicKey:(NSString *)publicKey
privateKey:(NSString *)privateKey
andPassword:(NSString *)password {
if (![self supportsAuthenticationMethod:@"publickey"]) {
return NO;
}
if (password == nil) {
password = @"";
}
// Get absolute paths for private/public key pair
const char *pubKey = [[publicKey stringByExpandingTildeInPath] UTF8String] ?: NULL;
const char *privKey = [[privateKey stringByExpandingTildeInPath] UTF8String] ?: NULL;
// Try to authenticate with key pair and password
int error = libssh2_userauth_publickey_fromfile(self.session,
[self.username UTF8String],
pubKey,
privKey,
[password UTF8String]);
if (error) {
NMSSHLogError(@"Public key authentication failed with reason %i", error);
return NO;
}
NMSSHLogVerbose(@"Public key authentication succeeded.");
return self.isAuthorized;
}
- (BOOL)authenticateByInMemoryPublicKey:(NSString *)publicKey
privateKey:(NSString *)privateKey
andPassword:(NSString *)password {
if (![self supportsAuthenticationMethod:@"publickey"]) {
return NO;
}
if (password == nil) {
password = @"";
}
// Try to authenticate with key pair and password
int error = libssh2_userauth_publickey_frommemory(self.session,
[self.username UTF8String],
[self.username length],
[publicKey UTF8String] ?: nil,
[publicKey length] ?: 0,
[privateKey UTF8String] ?: nil,
[privateKey length] ?: 0,
[password UTF8String]);
if (error) {
NMSSHLogError(@"Public key authentication failed with reason %i", error);
return NO;
}
NMSSHLogVerbose(@"Public key authentication succeeded.");
return self.isAuthorized;
}
- (BOOL)authenticateByKeyboardInteractive {
return [self authenticateByKeyboardInteractiveUsingBlock:nil];
}
- (BOOL)authenticateByKeyboardInteractiveUsingBlock:(NSString *(^)(NSString *request))authenticationBlock {
if (![self supportsAuthenticationMethod:@"keyboard-interactive"]) {
return NO;
}
self.kbAuthenticationBlock = authenticationBlock;
int rc = libssh2_userauth_keyboard_interactive(self.session, [self.username UTF8String], &kb_callback);
self.kbAuthenticationBlock = nil;
if (rc != 0) {
NMSSHLogError(@"Keyboard-interactive authentication failed with reason %i", rc);
return NO;
}
NMSSHLogVerbose(@"Keyboard-interactive authentication succeeded.");
return self.isAuthorized;
}
- (BOOL)connectToAgent {
if (![self supportsAuthenticationMethod:@"publickey"]) {
return NO;
}
// Try to setup a connection to the SSH-agent
[self setAgent:libssh2_agent_init(self.session)];
if (!self.agent) {
NMSSHLogError(@"Could not start a new agent");
return NO;
}
// Try connecting to the agent
if (libssh2_agent_connect(self.agent)) {
NMSSHLogError(@"Failed connection to agent");
return NO;
}
// Try to fetch available SSH identities
if (libssh2_agent_list_identities(self.agent)) {
NMSSHLogError(@"Failed to request agent identities");
return NO;
}
// Search for the correct identity and try to authenticate
struct libssh2_agent_publickey *identity, *prev_identity = NULL;
while (1) {
int error = libssh2_agent_get_identity(self.agent, &identity, prev_identity);
if (error) {
NMSSHLogError(@"Failed to find a valid identity for the agent");
return NO;
}
error = libssh2_agent_userauth(self.agent, [self.username UTF8String], identity);
if (!error) {
return self.isAuthorized;
}
prev_identity = identity;
}
return NO;
}
- (NSArray *)supportedAuthenticationMethods {
if (!self.session) {
return nil;
}
char *userauthlist = libssh2_userauth_list(self.session, [self.username UTF8String],
(unsigned int)strlen([self.username UTF8String]));
if (userauthlist == NULL){
NMSSHLogInfo(@"Failed to get authentication method for host %@:%@", self.host, self.port);
return nil;
}
NSString *authList = [NSString stringWithCString:userauthlist encoding:NSUTF8StringEncoding];
NMSSHLogVerbose(@"User auth list: %@", authList);
return [authList componentsSeparatedByString:@","];
}
- (BOOL)supportsAuthenticationMethod:(NSString *)method {
return [[self supportedAuthenticationMethods] containsObject:method];
}
- (NSString *)fingerprint:(NMSSHSessionHash)hashType {
if (!self.session) {
return nil;
}
int libssh2_hash, hashLength;
switch (hashType) {
case NMSSHSessionHashMD5:
libssh2_hash = LIBSSH2_HOSTKEY_HASH_MD5;
hashLength = 16;
break;
case NMSSHSessionHashSHA1:
libssh2_hash = LIBSSH2_HOSTKEY_HASH_SHA1;
hashLength = 20;
break;
}
const char *hash = libssh2_hostkey_hash(self.session, libssh2_hash);
if (!hash) {
NMSSHLogWarn(@"Unable to retrive host's fingerprint");
return nil;
}
NSMutableString *fingerprint = [[NSMutableString alloc] initWithFormat:@"%02X", (unsigned char)hash[0]];
for (int i = 1; i < hashLength; i++) {
[fingerprint appendFormat:@":%02X", (unsigned char)hash[i]];
}
return [fingerprint copy];
}
// -----------------------------------------------------------------------------
#pragma mark - KNOWN HOSTS
// -----------------------------------------------------------------------------
- (NSString *)applicationSupportDirectory {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);
NSString *applicationSupportDirectory = paths[0];
NSString *nmsshDirectory = [applicationSupportDirectory stringByAppendingPathComponent:@"NMSSH"];
if (![[NSFileManager defaultManager] fileExistsAtPath:nmsshDirectory]) {
[[NSFileManager defaultManager] createDirectoryAtPath:nmsshDirectory
withIntermediateDirectories:YES
attributes:nil
error:NULL];
}
return nmsshDirectory;
}
- (NSString *)userKnownHostsFileName {
#if TARGET_OS_IPHONE
return [[self applicationSupportDirectory] stringByAppendingPathComponent:@"known_hosts"];
#else
return [@"~/.ssh/known_hosts" stringByExpandingTildeInPath];
#endif
}
#if !TARGET_OS_IPHONE
- (NSString *)systemKnownHostsFileName {
return @"/etc/ssh/ssh_known_hosts";
}
#endif
- (NMSSHKnownHostStatus)knownHostStatusInFiles:(NSArray *)files {
if (!files) {
#if TARGET_OS_IPHONE
files = @[[self userKnownHostsFileName]];
#else
files = @[[self systemKnownHostsFileName], [self userKnownHostsFileName]];
#endif
}
NMSSHKnownHostStatus status = NMSSHKnownHostStatusFailure;
for (NSString *filename in files) {
status = [self knownHostStatusWithFile:filename];
if (status != NMSSHKnownHostStatusNotFound && status != NMSSHKnownHostStatusFailure) {
return status;
}
}
return status;
}
- (NMSSHKnownHostStatus)knownHostStatusWithFile:(NSString *)filename {
LIBSSH2_KNOWNHOSTS *knownHosts = libssh2_knownhost_init(self.session);
if (!knownHosts) {
return NMSSHKnownHostStatusFailure;
}
int rc = libssh2_knownhost_readfile(knownHosts,
[filename UTF8String],
LIBSSH2_KNOWNHOST_FILE_OPENSSH);
if (rc < 0) {
libssh2_knownhost_free(knownHosts);
if (rc == LIBSSH2_ERROR_FILE) {
NMSSHLogInfo(@"No known hosts file %@.", filename);
return NMSSHKnownHostStatusNotFound;
}
else {
NMSSHLogError(@"Failed to read known hosts file %@.", filename);
return NMSSHKnownHostStatusFailure;
}
}
int keytype;
size_t keylen;
const char *remotekey = libssh2_session_hostkey(self.session, &keylen, &keytype);
if (!remotekey) {
NMSSHLogError(@"Failed to get host key.");
libssh2_knownhost_free(knownHosts);
return NMSSHKnownHostStatusFailure;
}
int keybit = (keytype == LIBSSH2_HOSTKEY_TYPE_RSA ? LIBSSH2_KNOWNHOST_KEY_SSHRSA : LIBSSH2_KNOWNHOST_KEY_SSHDSS);
struct libssh2_knownhost *host;
NMSSHLogInfo(@"Check for host %@, port %@ in file %@", self.host, self.port, filename);
int check = libssh2_knownhost_checkp(knownHosts,
[self.host UTF8String],
[self.port intValue],
remotekey,
keylen,
(LIBSSH2_KNOWNHOST_TYPE_PLAIN |
LIBSSH2_KNOWNHOST_KEYENC_RAW |
keybit),
&host);
libssh2_knownhost_free(knownHosts);
switch (check) {
case LIBSSH2_KNOWNHOST_CHECK_MATCH:
NMSSHLogInfo(@"Match");
return NMSSHKnownHostStatusMatch;
case LIBSSH2_KNOWNHOST_CHECK_MISMATCH:
NMSSHLogInfo(@"Mismatch");
return NMSSHKnownHostStatusMismatch;
case LIBSSH2_KNOWNHOST_CHECK_NOTFOUND:
NMSSHLogInfo(@"Not found");
return NMSSHKnownHostStatusNotFound;
case LIBSSH2_KNOWNHOST_CHECK_FAILURE:
default:
NMSSHLogInfo(@"Failure");
return NMSSHKnownHostStatusFailure;
}
}
- (BOOL)addKnownHostName:(NSString *)host port:(NSInteger)port toFile:(NSString *)fileName withSalt:(NSString *)salt {
const char *hostkey;
size_t hklen;
int hktype;
NSString *hostname; // Formatted as {host} or [{host}]:{port}.
if (port == 22) {
hostname = host;
}
else {
hostname = [NSString stringWithFormat:@"[%@]:%d", host, (int)port];
}
if (!fileName) {
fileName = [self userKnownHostsFileName];
}
hostkey = libssh2_session_hostkey(self.session, &hklen, &hktype);
if (!hostkey) {
NMSSHLogError(@"Failed to get host key.");
return NO;
}
LIBSSH2_KNOWNHOSTS *knownHosts = libssh2_knownhost_init(self.session);
if (!knownHosts) {
NMSSHLogError(@"Failed to initialize knownhosts.");
return NO;
}
int rc = libssh2_knownhost_readfile(knownHosts, [fileName UTF8String], LIBSSH2_KNOWNHOST_FILE_OPENSSH);
if (rc < 0 && rc != LIBSSH2_ERROR_FILE) {
NMSSHLogError(@"Failed to read known hosts file.");
libssh2_knownhost_free(knownHosts);
return NO;
}
int keybit = LIBSSH2_KNOWNHOST_KEYENC_RAW;
if (hktype == LIBSSH2_HOSTKEY_TYPE_RSA) {
keybit |= LIBSSH2_KNOWNHOST_KEY_SSHRSA;
}
else {
keybit |= LIBSSH2_KNOWNHOST_KEY_SSHDSS;
}
if (salt) {
keybit |= LIBSSH2_KNOWNHOST_TYPE_SHA1;
}
else {
keybit |= LIBSSH2_KNOWNHOST_TYPE_PLAIN;
}
int result = libssh2_knownhost_addc(knownHosts,
[hostname UTF8String],
[salt UTF8String],
hostkey,
hklen,
NULL,
0,
keybit,
NULL);
if (result) {
NMSSHLogError(@"Failed to add host to known hosts: error %d (%@)",
result,
[self lastError]);
}
else {
result = libssh2_knownhost_writefile(knownHosts,
[fileName UTF8String],
LIBSSH2_KNOWNHOST_FILE_OPENSSH);
if (result < 0) {
NMSSHLogError(@"Couldn't write to %@: %@",
[self userKnownHostsFileName], [self lastError]);
}
else {
NMSSHLogInfo(@"Host added to known hosts.");
}
}
libssh2_knownhost_free(knownHosts);
return result == 0;
}
- (NSString *)keyboardInteractiveRequest:(NSString *)request {
NMSSHLogVerbose(@"Server request '%@'", request);
if (self.kbAuthenticationBlock) {
return self.kbAuthenticationBlock(request);
}
else if (self.delegate && [self.delegate respondsToSelector:@selector(session:keyboardInteractiveRequest:)]) {
return [self.delegate session:self keyboardInteractiveRequest:request];
}
NMSSHLogWarn(@"Keyboard interactive requires a delegate that responds to session:keyboardInteractiveRequest: or a block!");
return @"";
}
void kb_callback(const char *name, int name_len, const char *instr, int instr_len,
int num_prompts, const LIBSSH2_USERAUTH_KBDINT_PROMPT *prompts, LIBSSH2_USERAUTH_KBDINT_RESPONSE *res, void **abstract) {
int i;
NMSSHSession *self = (__bridge NMSSHSession *)*abstract;
for (i = 0; i < num_prompts; i++) {
NSString *request = [[NSString alloc] initWithBytes:prompts[i].text length:prompts[i].length encoding:NSUTF8StringEncoding];
NSString *response = [self keyboardInteractiveRequest:request];
if (!response) {
response = @"";
}
res[i].text = strdup([response UTF8String]);
res[i].length = (unsigned int)strlen([response UTF8String]);
}
}
void disconnect_callback(LIBSSH2_SESSION *session, int reason, const char *message, int message_len, const char *language, int language_len, void **abstract) {
NMSSHSession *self = (__bridge NMSSHSession *)*abstract;
// Build a raw error to encapsulate the disconnect
NSMutableDictionary *userInfo = [[NSMutableDictionary alloc] initWithCapacity:2];
if (message) {
NSString *string = [[NSString alloc] initWithBytes:message length:message_len encoding:NSUTF8StringEncoding];
[userInfo setObject:string forKey:NSLocalizedDescriptionKey];
}
if (language) {
NSString *string = [[NSString alloc] initWithBytes:language length:language_len encoding:NSUTF8StringEncoding];
[userInfo setObject:string forKey:@"language"];
}
NSError *error = [NSError errorWithDomain:@"NMSSH" code:reason userInfo:userInfo];
if (self.delegate && [self.delegate respondsToSelector:@selector(session:didDisconnectWithError:)]) {
[self.delegate session:self didDisconnectWithError:error];
}
[self disconnect];
}
// -----------------------------------------------------------------------------
#pragma mark - QUICK CHANNEL/SFTP ACCESS
// -----------------------------------------------------------------------------
- (NMSSHChannel *)channel {
if (!_channel) {
_channel = [[NMSSHChannel alloc] initWithSession:self];
}
return _channel;
}
- (NMSFTP *)sftp {
if (!_sftp) {
_sftp = [[NMSFTP alloc] initWithSession:self];
}
return _sftp;
}
@end

View File

@@ -0,0 +1,51 @@
#import "NMSSH.h"
@class NMSSHChannel;
/**
Protocol for registering to receive messages from an active NMSSHChannel.
*/
@protocol NMSSHChannelDelegate <NSObject>
@optional
/**
Called when a channel read new data on the socket.
@param channel The channel that read the message
@param message The message that the channel has read
*/
- (void)channel:(nonnull NMSSHChannel *)channel didReadData:(nonnull NSString *)message;
/**
Called when a channel read new error on the socket.
@param channel The channel that read the error
@param error The error that the channel has read
*/
- (void)channel:(nonnull NMSSHChannel *)channel didReadError:(nonnull NSString *)error;
/**
Called when a channel read new data on the socket.
@param channel The channel that read the message
@param data The bytes that the channel has read
*/
- (void)channel:(nonnull NMSSHChannel *)channel didReadRawData:(nonnull NSData *)data;
/**
Called when a channel read new error on the socket.
@param channel The channel that read the error
@param error The error that the channel has read
*/
- (void)channel:(nonnull NMSSHChannel *)channel didReadRawError:(nonnull NSData *)error;
/**
Called when a channel in shell mode has been closed.
@param channel The channel that has been closed
*/
- (void)channelShellDidClose:(nonnull NMSSHChannel *)channel;
@end

View File

@@ -0,0 +1,39 @@
#import "NMSSH.h"
@class NMSSHSession;
/**
Protocol for registering to receive messages from an active NMSSHSession.
*/
@protocol NMSSHSessionDelegate <NSObject>
@optional
/**
Called when the session is setup to use keyboard interactive authentication,
and the server is sending back a question (e.g. a password request).
@param session The session that is asking
@param request Question from server
@returns A valid response to the given question
*/
- (nonnull NSString *)session:(nonnull NMSSHSession *)session keyboardInteractiveRequest:(nonnull NSString *)request;
/**
Called when a session has failed and disconnected.
@param session The session that was disconnected
@param error A description of the error that caused the disconnect
*/
- (void)session:(nonnull NMSSHSession *)session didDisconnectWithError:(nonnull NSError *)error;
/**
Called when a session is connecting to a host, the fingerprint is used
to verify the authenticity of the host.
@param session The session that is connecting
@param fingerprint The host's fingerprint
@returns YES if the session should trust the host, otherwise NO.
*/
- (BOOL)session:(nonnull NMSSHSession *)session shouldConnectToHostWithFingerprint:(nonnull NSString *)fingerprint;
@end

View File

@@ -0,0 +1,36 @@
#import <XCTest/XCTest.h>
#import "NMSFTPFile.h"
@interface NMSFTPFileTests : XCTestCase
@end
@implementation NMSFTPFileTests {
NMSFTPFile *_file;
}
- (void)setUp {
[super setUp];
_file = [[NMSFTPFile alloc] initWithFilename:@"test.txt"];
}
/**
Tests whether the filename attribute is correct after initialization.
*/
- (void)testInitialization {
XCTAssertEqualObjects(_file.filename, @"test.txt", @"Filename attribut has not been set");
NMSFTPFile *anotherFile = [NMSFTPFile fileWithName:@"test.txt"];
XCTAssertEqualObjects(anotherFile.filename, @"test.txt", @"Filename attribut has not been set");
}
/**
Tests whether the permissions conversion from numeric to symbolic notation is correct
*/
-(void)testPermissionsConversion {
LIBSSH2_SFTP_ATTRIBUTES attributes;
attributes.permissions = 33188;
[_file populateValuesFromSFTPAttributes:attributes];
XCTAssertEqualObjects(_file.permissions, @"-rw-r--r--", @"The symbolic permissions notation is not correct.");
}
@end

View File

@@ -0,0 +1,4 @@
#import <XCTest/XCTest.h>
@interface NMSFTPTests : XCTestCase
@end

View File

@@ -0,0 +1,207 @@
#import "NMSFTPTests.h"
#import "ConfigHelper.h"
#import <NMSSH/NMSSH.h>
@interface NMSFTPTests () {
NSDictionary *settings;
NMSSHSession *session;
NMSFTP *sftp;
}
@end
@implementation NMSFTPTests
// -----------------------------------------------------------------------------
// TEST SETUP
// -----------------------------------------------------------------------------
- (void)setUp {
settings = [ConfigHelper valueForKey:@"valid_password_protected_server"];
session = [NMSSHSession connectToHost:[settings objectForKey:@"host"]
withUsername:[settings objectForKey:@"user"]];
[session authenticateByPassword:[settings objectForKey:@"password"]];
assert([session isAuthorized]);
sftp = [NMSFTP connectWithSession:session];
}
- (void)tearDown {
if (sftp) {
[sftp disconnect];
sftp = nil;
}
if (session) {
[session disconnect];
session = nil;
}
}
// -----------------------------------------------------------------------------
// CONNECTION TESTS
// -----------------------------------------------------------------------------
- (void)testConnectWithValidSession {
XCTAssertTrue([sftp isConnected], @"Test that connection worked");
}
// -----------------------------------------------------------------------------
// TEST MANIPULATING DIRECTORIES
// -----------------------------------------------------------------------------
- (void)testCreateMoveAndDeleteDirectoryAtWritablePathWorks {
NSString *path = [NSString stringWithFormat:@"%@mkdir_test",
[settings objectForKey:@"writable_dir"]];
NSString *destPath = [NSString stringWithFormat:@"%@mvdir_test",
[settings objectForKey:@"writable_dir"]];
XCTAssertTrue([sftp createDirectoryAtPath:path],
@"Try to create directory at valid path");
XCTAssertTrue([sftp directoryExistsAtPath:path],
@"Directory exists at path");
XCTAssertTrue([sftp moveItemAtPath:path toPath:destPath],
@"Try to move a directory");
XCTAssertTrue([sftp removeDirectoryAtPath:destPath],
@"Try to remove directory");
}
- (void)testCreateDirectoryAtNonWritablePathFails {
NSString *path = [NSString stringWithFormat:@"%@mkdir_test",
[settings objectForKey:@"non_writable_dir"]];
XCTAssertFalse([sftp createDirectoryAtPath:path],
@"Try to create directory at invalid path");
}
- (void)testListingContentsOfDirectory {
NSString *baseDir = [NSString stringWithFormat:@"%@listing/",
[settings objectForKey:@"writable_dir"]];
NSArray *dirs = @[@"a", @"b", @"c"];
NSArray *files = @[@"d.txt", @"e.txt", @"f.txt"];
// Setup basedir
[sftp createDirectoryAtPath:baseDir];
// Setup subdirs
for (NSString *dir in dirs) {
[sftp createDirectoryAtPath:[baseDir stringByAppendingString:dir]];
}
// Setup files
NSData *contents = [@"Hello World" dataUsingEncoding:NSUTF8StringEncoding];
for (NSString *file in files) {
[sftp writeContents:contents
toFileAtPath:[baseDir stringByAppendingString:file]];
}
// Test entry listing
NSArray *entries = @[[NMSFTPFile fileWithName:@"a/"],
[NMSFTPFile fileWithName:@"b/"],
[NMSFTPFile fileWithName:@"c/"],
[NMSFTPFile fileWithName:@"d.txt"],
[NMSFTPFile fileWithName:@"e.txt"],
[NMSFTPFile fileWithName:@"f.txt"]];
XCTAssertEqualObjects([sftp contentsOfDirectoryAtPath:baseDir], entries,
@"Get a list of directory entries");
// Cleanup subdirs
for (NSString *dir in dirs) {
[sftp removeDirectoryAtPath:[baseDir stringByAppendingString:dir]];
}
// Cleanup files
for (NSString *file in files) {
[sftp removeFileAtPath:[baseDir stringByAppendingString:file]];
}
// Cleanup basedir
[sftp removeDirectoryAtPath:baseDir];
}
// -----------------------------------------------------------------------------
// TEST MANIPULATING FILES AND SYMLINKS
// -----------------------------------------------------------------------------
- (void)testCreateAndDeleteSymlinkAtWritablePath {
// Set up a new directory to symlink to
NSString *path = [NSString stringWithFormat:@"%@mkdir_test",
[settings objectForKey:@"writable_dir"]];
[sftp createDirectoryAtPath:path];
// Create symlink
NSString *linkPath = [NSString stringWithFormat:@"%@symlink_test",
[settings objectForKey:@"writable_dir"]];
XCTAssertTrue([sftp createSymbolicLinkAtPath:linkPath
withDestinationPath:path], @"Create symbolic link");
// Remove symlink
XCTAssertTrue([sftp removeFileAtPath:linkPath], @"Remove symlink");
// Cleanup
[sftp removeDirectoryAtPath:path];
}
- (void)testCreateMoveAndDeleteFileAtWriteablePath {
NSString *path = [NSString stringWithFormat:@"%@file_test.txt",
[settings objectForKey:@"writable_dir"]];
NSString *destPath = [NSString stringWithFormat:@"%@mvfile_test.txt",
[settings objectForKey:@"writable_dir"]];
NSMutableData *contents = [[@"Hello World" dataUsingEncoding:NSUTF8StringEncoding]
mutableCopy];
XCTAssertTrue([sftp writeContents:contents toFileAtPath:path],
@"Write contents to file");
XCTAssertEqualObjects([sftp contentsAtPath:path], contents,
@"Read contents at path");
NSData *moreContents = [@"\nBye!" dataUsingEncoding:NSUTF8StringEncoding];
XCTAssertTrue([sftp appendContents:moreContents toFileAtPath:path],
@"Append contents to the end of a file");
[contents appendData:moreContents];
XCTAssertEqualObjects([sftp contentsAtPath:path], contents,
@"Read appended contents at path");
XCTAssertTrue([sftp moveItemAtPath:path toPath:destPath], @"Move a file");
XCTAssertTrue([sftp fileExistsAtPath:destPath], @"File exists at path");
XCTAssertFalse([sftp fileExistsAtPath:[settings objectForKey:@"writable_dir"]],
@"Should return false if a directory is provided");
XCTAssertTrue([sftp removeFileAtPath:destPath], @"Remove file");
}
-(void)testRetrievingFileInfo {
NSString *destPath = [[settings objectForKey:@"writable_dir"] stringByAppendingPathComponent: @"file_test.txt"];
NSString *destDirectoryPath = [[settings objectForKey:@"writable_dir"] stringByAppendingPathComponent: @"directory_test"];
XCTAssertTrue([sftp writeContents:[@"test" dataUsingEncoding:NSUTF8StringEncoding] toFileAtPath:destPath],@"Write contents to file");
XCTAssertTrue([sftp createDirectoryAtPath:destDirectoryPath], @"Couldn't create directory");
NMSFTPFile *fileInfo = [sftp infoForFileAtPath:destPath];
XCTAssertNotNil(fileInfo, @"Couldn't retrieve file info");
XCTAssertNotNil(fileInfo.filename, @"Couldn't retrieve filename");
XCTAssertNotNil(fileInfo.fileSize, @"Couldn't retrieve file size");
XCTAssertNotNil(fileInfo.permissions, @"Couldn't retrieve permissions");
XCTAssertNotNil(fileInfo.modificationDate, @"Couldn't retrieve modification date");
XCTAssertTrue(fileInfo.ownerGroupID > 0, @"Couldn't retrieve owner group ID");
XCTAssertTrue(fileInfo.ownerUserID > 0, @"Couldn't retrieve owner user ID");
XCTAssertFalse(fileInfo.isDirectory, @"File isn't a driectory");
NMSFTPFile *directoryInfo = [sftp infoForFileAtPath:destDirectoryPath];
XCTAssertTrue(directoryInfo.isDirectory, @"Target file is a directory");
XCTAssertTrue([sftp removeFileAtPath:destPath], @"Remove file");
XCTAssertTrue([sftp removeDirectoryAtPath:destDirectoryPath], @"Remove directory");
}
@end

View File

@@ -0,0 +1,4 @@
#import <XCTest/XCTest.h>
@interface NMSSHChannelTests : XCTestCase
@end

View File

@@ -0,0 +1,135 @@
#import "NMSSHChannelTests.h"
#import "ConfigHelper.h"
#import <NMSSH/NMSSH.h>
@interface NMSSHChannelTests () {
NSDictionary *settings;
NSString *localFilePath;
NMSSHChannel *channel;
NMSSHSession *session;
}
@end
@implementation NMSSHChannelTests
// -----------------------------------------------------------------------------
// TEST SETUP
// -----------------------------------------------------------------------------
- (void)setUp {
settings = [ConfigHelper valueForKey:@"valid_password_protected_server"];
session = [NMSSHSession connectToHost:[settings objectForKey:@"host"]
withUsername:[settings objectForKey:@"user"]];
[session authenticateByPassword:[settings objectForKey:@"password"]];
assert([session isAuthorized]);
// Setup test file for SCP
localFilePath = [@"~/nmssh-test.txt" stringByExpandingTildeInPath];
NSData *contents = [@"hello" dataUsingEncoding:NSUTF8StringEncoding];
[[NSFileManager defaultManager] createFileAtPath:localFilePath
contents:contents
attributes:nil];
}
- (void)tearDown {
if (channel) {
channel = nil;
}
if (session) {
[session disconnect];
session = nil;
}
// Cleanup SCP test files
if ([[NSFileManager defaultManager] fileExistsAtPath:localFilePath]) {
[[NSFileManager defaultManager] removeItemAtPath:localFilePath
error:nil];
}
}
// -----------------------------------------------------------------------------
// SHELL EXECUTION TESTS
// -----------------------------------------------------------------------------
- (void)testCreatingChannelWorks {
XCTAssertNoThrow(channel = [[NMSSHChannel alloc] initWithSession:session],
@"Setting up channel does not throw exception");
}
- (void)testExecutingShellCommand {
channel = [[NMSSHChannel alloc] initWithSession:session];
NSError *error = nil;
XCTAssertNoThrow([channel execute:[settings objectForKey:@"execute_command"]
error:&error],
@"Execution should not throw an exception");
XCTAssertEqualObjects([channel lastResponse],
[settings objectForKey:@"execute_expected_response"],
@"Execution returns the expected response");
}
// -----------------------------------------------------------------------------
// SCP FILE TRANSFER TESTS
// -----------------------------------------------------------------------------
- (void)testUploadingFileToWritableDirWorks {
channel = [[NMSSHChannel alloc] initWithSession:session];
NSString *dir = [settings objectForKey:@"writable_dir"];
XCTAssertTrue([dir hasSuffix:@"/"], @"Directory must end with a slash");
BOOL result;
XCTAssertNoThrow(result = [channel uploadFile:localFilePath to:dir],
@"Uploading file to writable dir doesn't throw exception");
XCTAssertTrue(result, @"Uploading to writable dir should work.");
}
- (void)testUploadingFileToNonWritableDirFails {
channel = [[NMSSHChannel alloc] initWithSession:session];
NSString *dir = [settings objectForKey:@"non_writable_dir"];
BOOL result;
XCTAssertNoThrow(result = [channel uploadFile:localFilePath to:dir],
@"Uploading file to non-writable dir doesn't throw"
@"exception");
XCTAssertFalse(result, @"Uploading to non-writable dir should not work.");
}
- (void)testDownloadingExistingFileWorks {
channel = [[NMSSHChannel alloc] initWithSession:session];
[[NSFileManager defaultManager] removeItemAtPath:localFilePath error:nil];
NSString *remoteFile = [[settings objectForKey:@"writable_dir"] stringByAppendingPathComponent:@"nmssh-test.txt"];
BOOL result;
XCTAssertNoThrow(result = [channel downloadFile:remoteFile to:localFilePath],
@"Downloading existing file doesn't throw exception");
XCTAssertTrue(result, @"Downloading existing file should work.");
XCTAssertTrue([[NSFileManager defaultManager] fileExistsAtPath:localFilePath],
@"A file has been created");
}
- (void)testDownloadingNonExistingFileFails {
channel = [[NMSSHChannel alloc] initWithSession:session];
[[NSFileManager defaultManager] removeItemAtPath:localFilePath error:nil];
NSString *remoteFile = [NSString stringWithFormat:@"%@nmssh-test.txt",
[settings objectForKey:@"non_writable_dir"]];
BOOL result;
XCTAssertNoThrow(result = [channel downloadFile:remoteFile to:localFilePath],
@"Downloading non-existing file doesn't throw exception");
XCTAssertFalse(result, @"Downloading non-existing file should not work.");
XCTAssertFalse([[NSFileManager defaultManager] fileExistsAtPath:localFilePath],
@"A file has not been created");
}
@end

View File

@@ -0,0 +1,612 @@
//
// NMSSHConfigTests.m
// NMSSH
//
// Created by George Nachman on 5/8/14.
//
//
#import <XCTest/XCTest.h>
#import "NMSSHConfig.h"
#import "NMSSHHostConfig.h"
@interface NMSSHConfigTests : XCTestCase
@end
@implementation NMSSHConfigTests
/**
Tests that an empty config file is ok
*/
- (void)testEmptyConfig {
NSString *contents = @"";
NMSSHConfig *config = [[NMSSHConfig alloc] initWithString:contents];
NSArray *hostConfigs = config.hostConfigs;
XCTAssertEqual([hostConfigs count], 0, @"Wrong number of configs read");
}
/**
Tests that a config file with all supported keywords is properly read.
*/
- (void)testAllKeywords {
NSString *contents =
@"Host pattern\n"
@" Hostname hostname\n"
@" User user\n"
@" Port 1234\n"
@" IdentityFile id_file\n";
NMSSHConfig *config = [[NMSSHConfig alloc] initWithString:contents];
NSArray *hostConfigs = config.hostConfigs;
XCTAssertEqual([hostConfigs count], 1, @"Wrong number of configs read");
NMSSHHostConfig *hostConfig = hostConfigs[0];
XCTAssertEqualObjects(hostConfig.hostPatterns, @[ @"pattern" ], @"Patterns don't match");
XCTAssertEqualObjects(hostConfig.hostname, @"hostname", @"Hostnames don't match");
XCTAssertEqualObjects(hostConfig.user, @"user", @"Users don't match");
XCTAssertEqualObjects(hostConfig.port, @1234, @"Port doesn't match");
XCTAssertEqualObjects(hostConfig.identityFiles, @[ @"id_file" ], @"Identity files don't match");
}
/**
Tests that comments are ignored.
*/
- (void)testCommentsIgnored {
NSString *contents =
@"# Comment\n"
@"Host pattern\n"
@"# Comment\n"
@" Hostname hostname\n"
@"# Comment\n"
@" Port 1234\n"
@"# Comment\n"
@" IdentityFile id_file\n"
@"# Comment\n";
NMSSHConfig *config = [[NMSSHConfig alloc] initWithString:contents];
NSArray *hostConfigs = config.hostConfigs;
XCTAssertEqual([hostConfigs count], 1, @"Wrong number of configs read");
NMSSHHostConfig *hostConfig = hostConfigs[0];
XCTAssertEqualObjects(hostConfig.hostPatterns, @[ @"pattern" ], @"Patterns don't match");
XCTAssertEqualObjects(hostConfig.hostname, @"hostname", @"Hostnames don't match");
XCTAssertEqualObjects(hostConfig.port, @1234, @"Port doesn't match");
XCTAssertEqualObjects(hostConfig.identityFiles, @[ @"id_file" ], @"Identity files don't match");
}
/**
Tests that empty lines are ignored.
*/
- (void)testEmptyLinesIgnored {
NSString *contents =
@"\n"
@"Host pattern\n"
@"\n"
@" Hostname hostname\n"
@"\n"
@" Port 1234\n"
@"\n"
@" IdentityFile id_file\n"
@"\n";
NMSSHConfig *config = [[NMSSHConfig alloc] initWithString:contents];
NSArray *hostConfigs = config.hostConfigs;
XCTAssertEqual([hostConfigs count], 1, @"Wrong number of configs read");
NMSSHHostConfig *hostConfig = hostConfigs[0];
XCTAssertEqualObjects(hostConfig.hostPatterns, @[ @"pattern" ], @"Patterns don't match");
XCTAssertEqualObjects(hostConfig.hostname, @"hostname", @"Hostnames don't match");
XCTAssertEqualObjects(hostConfig.port, @1234, @"Port doesn't match");
XCTAssertEqualObjects(hostConfig.identityFiles, @[ @"id_file" ], @"Identity files don't match");
}
/**
Tests that unknown keywords are ignored.
*/
- (void)testIgnoreUnknownKeywords {
NSString *contents =
@"Host pattern\n"
@" Hostname hostname\n"
@" Port 1234\n"
@" jfkldsajfdkl fjdkslafjdl fdjkla fjdslkf asdl\n"
@" IdentityFile id_file\n";
NMSSHConfig *config = [[NMSSHConfig alloc] initWithString:contents];
NSArray *hostConfigs = config.hostConfigs;
XCTAssertEqual([hostConfigs count], 1, @"Wrong number of configs read");
NMSSHHostConfig *hostConfig = hostConfigs[0];
XCTAssertEqualObjects(hostConfig.hostPatterns, @[ @"pattern" ], @"Patterns don't match");
XCTAssertEqualObjects(hostConfig.hostname, @"hostname", @"Hostnames don't match");
XCTAssertEqualObjects(hostConfig.port, @1234, @"Port doesn't match");
XCTAssertEqualObjects(hostConfig.identityFiles, @[ @"id_file" ], @"Identity files don't match");
}
/**
Tests that a malformed port line doesn't break parsing
*/
- (void)testMalformedPort {
NSString *contents =
@"Host pattern\n"
@" Hostname hostname\n"
@" Port\n"
@" IdentityFile id_file\n";
NMSSHConfig *config = [[NMSSHConfig alloc] initWithString:contents];
NSArray *hostConfigs = config.hostConfigs;
XCTAssertEqual([hostConfigs count], 1, @"Wrong number of configs read");
NMSSHHostConfig *hostConfig = hostConfigs[0];
XCTAssertEqualObjects(hostConfig.hostPatterns, @[ @"pattern" ], @"Patterns don't match");
XCTAssertEqualObjects(hostConfig.hostname, @"hostname", @"Hostnames don't match");
XCTAssertNil(hostConfig.port, @"Port should be nil");
XCTAssertEqualObjects(hostConfig.identityFiles, @[ @"id_file" ], @"Identity files don't match");
}
/**
Tests that multiple patterns are parsed properly
*/
- (void)testMultiplePatterns {
NSString *contents =
@"Host pattern1 pattern2\n";
NMSSHConfig *config = [[NMSSHConfig alloc] initWithString:contents];
NSArray *hostConfigs = config.hostConfigs;
XCTAssertEqual([hostConfigs count], 1, @"Wrong number of configs read");
NMSSHHostConfig *hostConfig = hostConfigs[0];
NSArray *expected = @[ @"pattern1", @"pattern2" ];
XCTAssertEqualObjects(hostConfig.hostPatterns, expected, @"Patterns don't match");
}
/**
Tests that quoted patterns are parsed properly
*/
- (void)testQuotedPatterns {
NSString *contents =
@"Host pattern1 \"a quoted pattern\" pattern2 \"foo bar\" \"baz\"\n";
NMSSHConfig *config = [[NMSSHConfig alloc] initWithString:contents];
NSArray *hostConfigs = config.hostConfigs;
XCTAssertEqual([hostConfigs count], 1, @"Wrong number of configs read");
NMSSHHostConfig *hostConfig = hostConfigs[0];
NSArray *expected = @[ @"pattern1", @"a quoted pattern", @"pattern2", @"foo bar", @"baz" ];
XCTAssertEqualObjects(hostConfig.hostPatterns, expected, @"Patterns don't match");
}
/**
Tests that an unterminated quoted patterns are ignored.
*/
- (void)testUnterminatedQuotation {
NSString *contents =
@"Host pattern1 \"unterminated quotation\n";
NMSSHConfig *config = [[NMSSHConfig alloc] initWithString:contents];
NSArray *hostConfigs = config.hostConfigs;
XCTAssertEqual([hostConfigs count], 1, @"Wrong number of configs read");
NMSSHHostConfig *hostConfig = hostConfigs[0];
NSArray *expected = @[ @"pattern1" ];
XCTAssertEqualObjects(hostConfig.hostPatterns, expected, @"Patterns don't match");
}
/**
Tests that multiple identity file commands are respected.
*/
- (void)testMultipleIdentityFile {
NSString *contents =
@"Host pattern\n"
@" Hostname hostname\n"
@" Port 1234\n"
@" IdentityFile id_file1\n"
@" IdentityFile id_file2\n";
NMSSHConfig *config = [[NMSSHConfig alloc] initWithString:contents];
NSArray *hostConfigs = config.hostConfigs;
XCTAssertEqual([hostConfigs count], 1, @"Wrong number of configs read");
NMSSHHostConfig *hostConfig = hostConfigs[0];
NSArray *expected = @[ @"id_file1", @"id_file2" ];
XCTAssertEqualObjects(hostConfig.identityFiles, expected, @"Identity files don't match");
}
/**
Tests that trailing and midline spaces are ignored
*/
- (void)testExtraSpaces {
NSString *contents =
@" Host pattern \"quoted pattern\" \" \" \n"
@" Hostname hostname \n";
NMSSHConfig *config = [[NMSSHConfig alloc] initWithString:contents];
NSArray *hostConfigs = config.hostConfigs;
XCTAssertEqual([hostConfigs count], 1, @"Wrong number of configs read");
NMSSHHostConfig *hostConfig = hostConfigs[0];
NSArray *expected = @[ @"pattern", @"quoted pattern", @" " ];
XCTAssertEqualObjects(hostConfig.hostPatterns, expected, @"Patterns don't match");
XCTAssertEqualObjects(hostConfig.hostname, @"hostname", @"Hostnames don't match");
}
/**
Tests multiple hosts
*/
- (void)testMultipleHosts {
NSString *contents =
@"Host pattern1\n"
@" Hostname hostname1\n"
@" Port 1\n"
@" IdentityFile id_file1\n"
@"Host pattern2\n"
@" Hostname hostname2\n"
@" Port 2\n"
@" IdentityFile id_file2\n";
NMSSHConfig *config = [[NMSSHConfig alloc] initWithString:contents];
NSArray *hostConfigs = config.hostConfigs;
XCTAssertEqual([hostConfigs count], 2, @"Wrong number of configs read");
NMSSHHostConfig *hostConfig = hostConfigs[0];
XCTAssertEqualObjects(hostConfig.hostPatterns, @[ @"pattern1" ], @"Patterns don't match");
XCTAssertEqualObjects(hostConfig.hostname, @"hostname1", @"Hostnames don't match");
XCTAssertEqualObjects(hostConfig.port, @1, @"Port doesn't match");
XCTAssertEqualObjects(hostConfig.identityFiles, @[ @"id_file1" ],
@"Identity files don't match");
hostConfig = hostConfigs[1];
XCTAssertEqualObjects(hostConfig.hostPatterns, @[ @"pattern2" ], @"Patterns don't match");
XCTAssertEqualObjects(hostConfig.hostname, @"hostname2", @"Hostnames don't match");
XCTAssertEqualObjects(hostConfig.port, @2, @"Port doesn't match");
XCTAssertEqualObjects(hostConfig.identityFiles, @[ @"id_file2" ],
@"Identity files don't match");
}
// -----------------------------------------------------------------------------
#pragma mark - TEST MATCHING
// -----------------------------------------------------------------------------
/**
Test matching a simple pattern
*/
- (void)testSimplestPossiblePattern {
NSString *contents =
@"Host pattern1\n"
@" Hostname hostname1\n";
NMSSHConfig *config = [[NMSSHConfig alloc] initWithString:contents];
NMSSHHostConfig *hostConfig = [config hostConfigForHost:@"pattern1"];
XCTAssertEqualObjects(hostConfig.hostname, @"hostname1", @"Match failed");
}
/**
Test that a simple pattern fails when it ought to.
*/
- (void)testSimplestPossiblePatternNoMatch {
NSString *contents =
@"Host pattern1\n"
@" Hostname hostname1\n";
NMSSHConfig *config = [[NMSSHConfig alloc] initWithString:contents];
NMSSHHostConfig *hostConfig = [config hostConfigForHost:@"pattern2"];
XCTAssertNil(hostConfig, @"Match should have failed but didn't");
}
/**
Test that a pattern list of simple patterns works.
*/
- (void)testSimplePatternList {
NSString *contents =
@"Host pattern1,pattern2\n"
@" Hostname hostname1\n";
NMSSHConfig *config = [[NMSSHConfig alloc] initWithString:contents];
NMSSHHostConfig *hostConfig = [config hostConfigForHost:@"pattern1"];
XCTAssertEqualObjects(hostConfig.hostname, @"hostname1", @"Match failed");
hostConfig = [config hostConfigForHost:@"pattern2"];
XCTAssertEqualObjects(hostConfig.hostname, @"hostname1", @"Match failed");
hostConfig = [config hostConfigForHost:@"pattern3"];
XCTAssertNil(hostConfig, @"Match should have failed but didn't");
}
/**
Test that a question mark wildcard works
*/
- (void)testSingleCharWildcard {
NSString *contents =
@"Host pattern?\n"
@" Hostname hostname1\n";
NMSSHConfig *config = [[NMSSHConfig alloc] initWithString:contents];
NMSSHHostConfig *hostConfig = [config hostConfigForHost:@"pattern1"];
XCTAssertEqualObjects(hostConfig.hostname, @"hostname1", @"Match failed");
hostConfig = [config hostConfigForHost:@"pattern2"];
XCTAssertEqualObjects(hostConfig.hostname, @"hostname1", @"Match failed");
hostConfig = [config hostConfigForHost:@"Xattern3"];
XCTAssertNil(hostConfig, @"Match should have failed but didn't");
}
/**
Test that a lone star matches everything
*/
- (void)testLoneStarMatchesAll {
NSString *contents =
@"Host *\n"
@" Hostname hostname1\n";
NMSSHConfig *config = [[NMSSHConfig alloc] initWithString:contents];
NMSSHHostConfig *hostConfig = [config hostConfigForHost:@"pattern1"];
XCTAssertEqualObjects(hostConfig.hostname, @"hostname1", @"Match failed");
hostConfig = [config hostConfigForHost:@"pattern2"];
XCTAssertEqualObjects(hostConfig.hostname, @"hostname1", @"Match failed");
hostConfig = [config hostConfigForHost:@""];
XCTAssertEqualObjects(hostConfig.hostname, @"hostname1", @"Match failed");
}
/**
Test that a star suffix matches all suffixes
*/
- (void)testStarSuffix {
NSString *contents =
@"Host a*\n"
@" Hostname hostname1\n";
NMSSHConfig *config = [[NMSSHConfig alloc] initWithString:contents];
NMSSHHostConfig *hostConfig = [config hostConfigForHost:@"abcdef"];
XCTAssertEqualObjects(hostConfig.hostname, @"hostname1", @"Match failed");
hostConfig = [config hostConfigForHost:@"a"];
XCTAssertEqualObjects(hostConfig.hostname, @"hostname1", @"Match failed");
hostConfig = [config hostConfigForHost:@""];
XCTAssertNil(hostConfig, @"Match should have failed but didn't");
}
/**
Test that a midline star works
*/
- (void)testMidlineStar {
NSString *contents =
@"Host abc*xyz\n"
@" Hostname hostname1\n";
NMSSHConfig *config = [[NMSSHConfig alloc] initWithString:contents];
NMSSHHostConfig *hostConfig = [config hostConfigForHost:@"abcxyz"];
XCTAssertEqualObjects(hostConfig.hostname, @"hostname1", @"Match failed");
hostConfig = [config hostConfigForHost:@"abc123xyz"];
XCTAssertEqualObjects(hostConfig.hostname, @"hostname1", @"Match failed");
hostConfig = [config hostConfigForHost:@"abc"];
XCTAssertNil(hostConfig, @"Match should have failed but didn't");
hostConfig = [config hostConfigForHost:@"xyz"];
XCTAssertNil(hostConfig, @"Match should have failed but didn't");
hostConfig = [config hostConfigForHost:@"abxyz"];
XCTAssertNil(hostConfig, @"Match should have failed but didn't");
hostConfig = [config hostConfigForHost:@"abcyz"];
XCTAssertNil(hostConfig, @"Match should have failed but didn't");
hostConfig = [config hostConfigForHost:@"abcabc"];
XCTAssertNil(hostConfig, @"Match should have failed but didn't");
}
/**
Test that a star prefix works
*/
- (void)testLeadingStar {
NSString *contents =
@"Host *xyz\n"
@" Hostname hostname1\n";
NMSSHConfig *config = [[NMSSHConfig alloc] initWithString:contents];
NMSSHHostConfig *hostConfig = [config hostConfigForHost:@"xyz"];
XCTAssertEqualObjects(hostConfig.hostname, @"hostname1", @"Match failed");
hostConfig = [config hostConfigForHost:@"123xyz"];
XCTAssertEqualObjects(hostConfig.hostname, @"hostname1", @"Match failed");
hostConfig = [config hostConfigForHost:@"xyz"];
XCTAssertEqualObjects(hostConfig.hostname, @"hostname1", @"Match failed");
hostConfig = [config hostConfigForHost:@"abc"];
XCTAssertNil(hostConfig, @"Match should have failed but didn't");
hostConfig = [config hostConfigForHost:@""];
XCTAssertNil(hostConfig, @"Match should have failed but didn't");
}
/**
Test that multiple disjoint stars work.
*/
- (void)testMultipleDisjointStars {
NSString *contents =
@"Host a*b*c\n"
@" Hostname hostname1\n";
NMSSHConfig *config = [[NMSSHConfig alloc] initWithString:contents];
NMSSHHostConfig *hostConfig = [config hostConfigForHost:@"a12b34c"];
XCTAssertEqualObjects(hostConfig.hostname, @"hostname1", @"Match failed");
hostConfig = [config hostConfigForHost:@"abc"];
XCTAssertEqualObjects(hostConfig.hostname, @"hostname1", @"Match failed");
hostConfig = [config hostConfigForHost:@"abc1"];
XCTAssertNil(hostConfig, @"Match should have failed but didn't");
hostConfig = [config hostConfigForHost:@""];
XCTAssertNil(hostConfig, @"Match should have failed but didn't");
}
/**
Test that two stars in a row work
*/
- (void)testConsecutiveStars {
NSString *contents =
@"Host a**z\n"
@" Hostname hostname1\n";
NMSSHConfig *config = [[NMSSHConfig alloc] initWithString:contents];
NMSSHHostConfig *hostConfig = [config hostConfigForHost:@"abcz"];
XCTAssertEqualObjects(hostConfig.hostname, @"hostname1", @"Match failed");
hostConfig = [config hostConfigForHost:@"abz"];
XCTAssertEqualObjects(hostConfig.hostname, @"hostname1", @"Match failed");
hostConfig = [config hostConfigForHost:@"az"];
XCTAssertEqualObjects(hostConfig.hostname, @"hostname1", @"Match failed");
hostConfig = [config hostConfigForHost:@"a"];
XCTAssertNil(hostConfig, @"Match should have failed but didn't");
hostConfig = [config hostConfigForHost:@""];
XCTAssertNil(hostConfig, @"Match should have failed but didn't");
}
/**
Test that a star followed by a question mark works
*/
- (void)testStarQuestionMark {
NSString *contents =
@"Host a*?z\n"
@" Hostname hostname1\n";
NMSSHConfig *config = [[NMSSHConfig alloc] initWithString:contents];
NMSSHHostConfig *hostConfig = [config hostConfigForHost:@"abcz"];
XCTAssertEqualObjects(hostConfig.hostname, @"hostname1", @"Match failed");
hostConfig = [config hostConfigForHost:@"abz"];
XCTAssertEqualObjects(hostConfig.hostname, @"hostname1", @"Match failed");
hostConfig = [config hostConfigForHost:@"az"];
XCTAssertNil(hostConfig, @"Match should have failed but didn't");
hostConfig = [config hostConfigForHost:@"a"];
XCTAssertNil(hostConfig, @"Match should have failed but didn't");
hostConfig = [config hostConfigForHost:@""];
XCTAssertNil(hostConfig, @"Match should have failed but didn't");
}
/**
Test a host with multiple pattern lists
*/
- (void)testMultiplePatternLists {
NSString *contents =
@"Host pattern1,pattern2 pattern3,pattern4\n"
@" Hostname hostname1\n";
NMSSHConfig *config = [[NMSSHConfig alloc] initWithString:contents];
NMSSHHostConfig *hostConfig = [config hostConfigForHost:@"pattern1"];
XCTAssertEqualObjects(hostConfig.hostname, @"hostname1", @"Match failed");
hostConfig = [config hostConfigForHost:@"pattern2"];
XCTAssertEqualObjects(hostConfig.hostname, @"hostname1", @"Match failed");
hostConfig = [config hostConfigForHost:@"pattern3"];
XCTAssertEqualObjects(hostConfig.hostname, @"hostname1", @"Match failed");
hostConfig = [config hostConfigForHost:@"pattern4"];
XCTAssertEqualObjects(hostConfig.hostname, @"hostname1", @"Match failed");
}
/**
Test negation alone
*/
- (void)testNegationAlone {
NSString *contents =
@"Host !pattern1\n"
@" Hostname hostname1\n";
NMSSHConfig *config = [[NMSSHConfig alloc] initWithString:contents];
NMSSHHostConfig *hostConfig = [config hostConfigForHost:@"pattern1"];
XCTAssertNil(hostConfig, @"Match should have failed but didn't");
hostConfig = [config hostConfigForHost:@"pattern2"];
XCTAssertNil(hostConfig, @"Match should have failed but didn't");
}
/**
Test negation in combination with a matchable pattern
*/
- (void)testNegationPlusMatchablePattern {
NSString *contents =
@"Host !*x* a*\n"
@" Hostname hostname1\n";
NMSSHConfig *config = [[NMSSHConfig alloc] initWithString:contents];
NMSSHHostConfig *hostConfig = [config hostConfigForHost:@"axy"];
XCTAssertNil(hostConfig, @"Match should have failed but didn't");
hostConfig = [config hostConfigForHost:@"abc"];
XCTAssertEqualObjects(hostConfig.hostname, @"hostname1", @"Match failed");
hostConfig = [config hostConfigForHost:@"b"];
XCTAssertNil(hostConfig, @"Match should have failed but didn't");
}
/**
Test two rules where the first negates and the second matches.
*/
- (void)testTwoRulesWhereFirstNegatesAndSecondMatches {
NSString *contents =
@"Host !*x* a*\n"
@" Hostname hostname1\n"
@"Host *z\n"
@" Hostname hostname2\n";
NMSSHConfig *config = [[NMSSHConfig alloc] initWithString:contents];
NMSSHHostConfig *hostConfig = [config hostConfigForHost:@"axy"];
XCTAssertNil(hostConfig, @"Match should have failed but didn't");
hostConfig = [config hostConfigForHost:@"abc"];
XCTAssertEqualObjects(hostConfig.hostname, @"hostname1", @"Match failed");
hostConfig = [config hostConfigForHost:@"axz"];
XCTAssertEqualObjects(hostConfig.hostname, @"hostname2", @"Match failed");
hostConfig = [config hostConfigForHost:@"xz"];
XCTAssertEqualObjects(hostConfig.hostname, @"hostname2", @"Match failed");
hostConfig = [config hostConfigForHost:@"z"];
XCTAssertEqualObjects(hostConfig.hostname, @"hostname2", @"Match failed");
hostConfig = [config hostConfigForHost:@"b"];
XCTAssertNil(hostConfig, @"Match should have failed but didn't");
}
/**
Test two rules that both match. They should be merged.
*/
- (void)testMergeTwoMatchingRules {
NSString *contents =
@"Host *\n"
@" Hostname hostname1\n"
@" Port 1\n"
@" IdentityFile id_file1\n"
@"Host *\n"
@" Hostname hostname2\n"
@" Port 2\n"
@" User user\n"
@" IdentityFile id_file2\n";
NMSSHConfig *config = [[NMSSHConfig alloc] initWithString:contents];
NMSSHHostConfig *hostConfig = [config hostConfigForHost:@"hostname"];
XCTAssertEqualObjects(hostConfig.hostname, @"hostname1", @"Hostnames don't match");
XCTAssertEqualObjects(hostConfig.port, @1, @"Port doesn't match");
XCTAssertEqualObjects(hostConfig.user, @"user", @"Users doesn't match");
NSArray *expected = @[ @"id_file1", @"id_file2" ];
XCTAssertEqualObjects(hostConfig.identityFiles, expected,
@"Identity files don't match");
}
@end

View File

@@ -0,0 +1,4 @@
#import <XCTest/XCTest.h>
@interface NMSSHSessionTests : XCTestCase
@end

View File

@@ -0,0 +1,334 @@
#import "NMSSHSessionTests.h"
#import "NMSSHConfig.h"
#import "NMSSHHostConfig.h"
#import "ConfigHelper.h"
#import <NMSSH/NMSSH.h>
@interface NMSSHSessionTests () {
NSDictionary *validPasswordProtectedServer;
NSDictionary *validPublicKeyProtectedServer;
NSDictionary *validAgentServer;
NSDictionary *invalidServer;
NMSSHSession *session;
}
@end
@implementation NMSSHSessionTests
// -----------------------------------------------------------------------------
// TEST SETUP
// -----------------------------------------------------------------------------
- (void)setUp {
validPasswordProtectedServer = [ConfigHelper valueForKey:
@"valid_password_protected_server"];
validPublicKeyProtectedServer = [ConfigHelper valueForKey:
@"valid_public_key_protected_server"];
invalidServer = [ConfigHelper valueForKey:@"invalid_server"];
validAgentServer = [ConfigHelper valueForKey:@"valid_agent_server"];
}
- (void)tearDown {
if (session) {
[session disconnect];
session = nil;
}
}
// -----------------------------------------------------------------------------
// CONNECTION TESTS
// -----------------------------------------------------------------------------
- (void)testConnectionToValidServerWorks {
NSString *host = [validPasswordProtectedServer objectForKey:@"host"];
NSString *username = [validPasswordProtectedServer
objectForKey:@"user"];
XCTAssertNoThrow(session = [NMSSHSession connectToHost:host
withUsername:username],
@"Connecting to a valid server does not throw exception");
XCTAssertTrue([session isConnected],
@"Connection to valid server should work");
}
- (void)testConnectionToInvalidServerFails {
NSString *host = [invalidServer objectForKey:@"host"];
NSString *username = [invalidServer objectForKey:@"user"];
XCTAssertNoThrow(session = [NMSSHSession connectToHost:host
withUsername:username],
@"Connecting to a invalid server does not throw exception");
XCTAssertFalse([session isConnected],
@"Connection to invalid server should not work");
}
// -----------------------------------------------------------------------------
// AUTHENTICATION TESTS
// -----------------------------------------------------------------------------
- (void)testPasswordAuthenticationWithValidPasswordWorks {
NSString *host = [validPasswordProtectedServer objectForKey:@"host"];
NSString *username = [validPasswordProtectedServer
objectForKey:@"user"];
NSString *password = [validPasswordProtectedServer
objectForKey:@"password"];
session = [NMSSHSession connectToHost:host withUsername:username];
XCTAssertNoThrow([session authenticateByPassword:password],
@"Authentication with valid password doesn't throw"
@"exception");
XCTAssertTrue([session isAuthorized],
@"Authentication with valid password should work");
}
- (void)testPasswordAuthenticationWithInvalidPasswordFails {
NSString *host = [validPasswordProtectedServer objectForKey:@"host"];
NSString *username = [validPasswordProtectedServer
objectForKey:@"user"];
NSString *password = [invalidServer objectForKey:@"password"];
session = [NMSSHSession connectToHost:host withUsername:username];
XCTAssertNoThrow([session authenticateByPassword:password],
@"Authentication with invalid password doesn't throw"
@"exception");
XCTAssertFalse([session isAuthorized],
@"Authentication with invalid password should not work");
}
- (void)testPasswordAuthenticationWithInvalidUserFails {
NSString *host = [validPasswordProtectedServer objectForKey:@"host"];
NSString *username = [invalidServer objectForKey:@"user"];
NSString *password = [invalidServer objectForKey:@"password"];
session = [NMSSHSession connectToHost:host withUsername:username];
XCTAssertNoThrow([session authenticateByPassword:password],
@"Authentication with invalid username/password doesn't"
@"throw exception");
XCTAssertFalse([session isAuthorized],
@"Authentication with invalid username/password should not"
@"work");
}
- (void)testPublicKeyAuthenticationWithValidKeyWorks {
NSString *host = [validPublicKeyProtectedServer objectForKey:@"host"];
NSString *username = [validPublicKeyProtectedServer objectForKey:@"user"];
NSString *publicKey = [validPublicKeyProtectedServer
objectForKey:@"valid_public_key"];
NSString *password = [validPublicKeyProtectedServer
objectForKey:@"password"];
session = [NMSSHSession connectToHost:host withUsername:username];
XCTAssertNoThrow([session authenticateByPublicKey:publicKey
privateKey:[publicKey stringByDeletingPathExtension]
andPassword:password],
@"Authentication with valid public key doesn't throw"
@"exception");
XCTAssertTrue([session isAuthorized],
@"Authentication with valid public key should work");
}
- (void)testPublicKeyAuthenticationWithInvalidPasswordFails {
NSString *host = [validPublicKeyProtectedServer objectForKey:@"host"];
NSString *username = [validPublicKeyProtectedServer objectForKey:@"user"];
NSString *publicKey = [validPublicKeyProtectedServer
objectForKey:@"valid_public_key"];
session = [NMSSHSession connectToHost:host withUsername:username];
XCTAssertNoThrow([session authenticateByPublicKey:publicKey
privateKey:[publicKey stringByDeletingPathExtension]
andPassword:nil],
@"Public key authentication with invalid password doesn't"
@"throw exception");
XCTAssertFalse([session isAuthorized],
@"Public key authentication with invalid password should not"
@"work");
}
- (void)testPublicKeyAuthenticationWithInvalidKeyFails {
NSString *host = [validPublicKeyProtectedServer objectForKey:@"host"];
NSString *username = [validPublicKeyProtectedServer objectForKey:@"user"];
NSString *publicKey = [validPublicKeyProtectedServer
objectForKey:@"invalid_public_key"];
session = [NMSSHSession connectToHost:host withUsername:username];
XCTAssertNoThrow([session authenticateByPublicKey:publicKey
privateKey:[publicKey stringByDeletingPathExtension]
andPassword:nil],
@"Authentication with invalid public key doesn't throw"
@"exception");
XCTAssertFalse([session isAuthorized],
@"Authentication with invalid public key should not work");
}
- (void)testPublicKeyAuthenticationWithInvalidUserFails {
NSString *host = [validPublicKeyProtectedServer objectForKey:@"host"];
NSString *username = [invalidServer objectForKey:@"user"];
NSString *publicKey = [validPublicKeyProtectedServer
objectForKey:@"valid_public_key"];
NSString *password = [validPublicKeyProtectedServer
objectForKey:@"password"];
session = [NMSSHSession connectToHost:host withUsername:username];
XCTAssertNoThrow([session authenticateByPublicKey:publicKey
privateKey:[publicKey stringByDeletingPathExtension]
andPassword:password],
@"Public key authentication with invalid user doesn't"
@"throw exception");
XCTAssertFalse([session isAuthorized],
@"Public key authentication with invalid user should not work");
}
- (void)testValidConnectionToAgent {
NSString *host = [validAgentServer objectForKey:@"host"];
NSString *username = [validAgentServer objectForKey:@"user"];
session = [NMSSHSession connectToHost:host withUsername:username];
XCTAssertNoThrow([session connectToAgent],
@"Valid connection to agent doesn't throw exception");
XCTAssertTrue([session isAuthorized],
@"Agent authentication with valid username should work");
}
- (void)testInvalidConnectionToAgent {
NSString *host = [validAgentServer objectForKey:@"host"];
NSString *username = [invalidServer objectForKey:@"user"];
session = [NMSSHSession connectToHost:host withUsername:username];
XCTAssertNoThrow([session connectToAgent],
@"Invalid connection to agent doesn't throw exception");
XCTAssertFalse([session isAuthorized],
@"Agent authentication with invalid username should not"
@"work");
}
// -----------------------------------------------------------------------------
// CONFIG TESTS
// -----------------------------------------------------------------------------
// Tests synthesis that uses some defaults, some global, and some local values,
// and merges identity files.
- (void)testConfigSynthesisFromChain {
NMSSHConfig *globalConfig = [[NMSSHConfig alloc] initWithString:
@"Host host\n"
@" Hostname globalHostname\n"
@" Port 9999\n"
@" IdentityFile idFile1\n"
@" IdentityFile idFile2"];
NMSSHConfig *userConfig = [[NMSSHConfig alloc] initWithString:
@"Host host\n"
@" Hostname localHostname\n"
@" IdentityFile idFile2\n"
@" IdentityFile idFile3"];
NSArray *configChain = @[ userConfig, globalConfig ];
session = [[NMSSHSession alloc] initWithHost:@"host"
configs:configChain
withDefaultPort:22
defaultUsername:@"defaultUsername"];
XCTAssertEqualObjects(session.hostConfig.hostname, @"localHostname",
@"Hostname not properly synthesized");
XCTAssertEqualObjects(session.hostConfig.port, @9999,
@"Port not properly synthesized");
XCTAssertEqualObjects(session.hostConfig.user, @"defaultUsername",
@"User not properly synthesized");
NSArray *expected = @[ @"idFile2", @"idFile3", @"idFile1" ];
XCTAssertEqualObjects(session.hostConfig.identityFiles, expected,
@"Identity files not properly synthesized");
}
// Tests that all default values can appear in the synthesized config.
- (void)testConfigSynthesisInheritsDefaults {
NMSSHConfig *config = [[NMSSHConfig alloc] initWithString:
@"Host nonMatchingHost\n"
@" Hostname badHostname\n"
@" Port 9999\n"
@" User badUser\n"
@" IdentityFile badIdFile\n"];
NSArray *configChain = @[ config ];
session = [[NMSSHSession alloc] initWithHost:@"goodHost"
configs:configChain
withDefaultPort:22
defaultUsername:@"goodUsername"];
XCTAssertEqualObjects(session.hostConfig.hostname, @"goodHost",
@"Hostname not properly synthesized");
XCTAssertEqualObjects(session.hostConfig.port, @22,
@"Port not properly synthesized");
XCTAssertEqualObjects(session.hostConfig.user, @"goodUsername",
@"User not properly synthesized");
NSArray *expected = @[ ];
XCTAssertEqualObjects(session.hostConfig.identityFiles, expected,
@"Identity files not properly synthesized");
}
// Tests that all values respect the priority hierarchy of the config chain.
- (void)testConfigSynthesisRespectsPriority {
NMSSHConfig *globalConfig = [[NMSSHConfig alloc] initWithString:
@"Host host\n"
@" Hostname globalHostname\n"
@" Port 9999\n"
@" User globalUser"];
NMSSHConfig *userConfig = [[NMSSHConfig alloc] initWithString:
@"Host host\n"
@" Hostname localHostname\n"
@" Port 8888\n"
@" User localUser"];
NSArray *configChain = @[ userConfig, globalConfig ];
session = [[NMSSHSession alloc] initWithHost:@"host"
configs:configChain
withDefaultPort:22
defaultUsername:@"defaultUsername"];
XCTAssertEqualObjects(session.hostConfig.hostname, @"localHostname",
@"Hostname not properly synthesized");
XCTAssertEqualObjects(session.hostConfig.port, @8888,
@"Port not properly synthesized");
XCTAssertEqualObjects(session.hostConfig.user, @"localUser",
@"User not properly synthesized");
}
// Tests that values from the config are used in creating the session.
- (void)testConfigIsUsed {
NMSSHConfig *config = [[NMSSHConfig alloc] initWithString:
@"Host host\n"
@" Hostname configHostname\n"
@" Port 9999\n"
@" User configUser\n"];
NSArray *configChain = @[ config ];
session = [[NMSSHSession alloc] initWithHost:@"host"
configs:configChain
withDefaultPort:22
defaultUsername:@"defaultUsername"];
XCTAssertEqualObjects(session.host, @"configHostname",
@"Hostname from config not used");
XCTAssertEqualObjects(session.port, @9999,
@"Port from config not used");
XCTAssertEqualObjects(session.username, @"configUser",
@"User from config not used");
}
@end

View File

@@ -0,0 +1,22 @@
<?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>en</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>

View File

@@ -0,0 +1,18 @@
#import <Foundation/Foundation.h>
@interface ConfigHelper : NSObject
/**
* Helper method to get a value from the configuration YAML.
*
* Assuming the values in the YAML file can be represented as NSDictionary, you
* can create a chain to get a deep value.
*
* Example:
*
* NSString *host = [ConfigHelper valueForKey:
* @"valid_password_protected_server.host"];
*/
+ (id)valueForKey:(NSString *)key;
@end

View File

@@ -0,0 +1,28 @@
#import "ConfigHelper.h"
#import <YAML/YAMLSerialization.h>
@implementation ConfigHelper
+ (id)valueForKey:(NSString *)key {
static id yaml;
if (!yaml) {
NSBundle *bundle = [NSBundle bundleForClass:[self class]];
NSString *path = [bundle pathForResource:@"config" ofType:@"yml"];
NSInputStream *stream = [[NSInputStream alloc] initWithFileAtPath:path];
yaml = [YAMLSerialization YAMLWithStream:stream
options:kYAMLReadOptionStringScalars
error:nil];
}
id data = [yaml objectAtIndex:0];
NSArray *keyList = [key componentsSeparatedByString:@"."];
for (NSString *keyPart in keyList) {
data = [data objectForKey:keyPart];
}
return data;
}
@end

View File

@@ -0,0 +1,47 @@
# Defines a valid, password protected server, and options for testing both
# valid and invalid user/password combinations as well as SCP to both a
# writable directory and one that is not writable by the user
valid_password_protected_server:
host: "127.0.0.1:22"
# User config
user: "user"
password: "password"
# Shell execution config
execute_command: "ls -1 /var/www/nmssh-tests/"
execute_expected_response: "invalid\nvalid\n"
# Directory config
writable_dir: "/var/www/nmssh-tests/valid/"
non_writable_dir: "/var/www/nmssh-tests/invalid/"
# Defines a valid, public key protected server, and options for testing both
# valid and invalid user/password combinations as well as SCP to both a
# writable directory and one that is not writable by the user
valid_public_key_protected_server:
host: "127.0.0.1:22"
# User config
user: "user"
# Public key path
valid_public_key: "~/.ssh/id_rsa.pub"
invalid_public_key: "~/.ssh/github_rsa.pub"
# Public key password
password: "password"
# Defines a valid server, that this computer can connect to via an agent.
valid_agent_server:
host: "127.0.0.1:22"
# User config
user: "user"
# Defines an invalid server and authentication credentials that are invalid for
# all defined test servers
invalid_server:
host: "0.0.0.0:22"
user: "user"
password: "pass"

View File

@@ -0,0 +1 @@
Versions/Current/Headers

View File

@@ -0,0 +1 @@
Versions/Current/Resources

View File

@@ -0,0 +1,58 @@
//
// YAMLSerialization.h
// YAML Serialization support by Mirek Rusin based on C library LibYAML by Kirill Simonov
// Released under MIT License
//
// Copyright 2010 Mirek Rusin
// Copyright 2010 Stanislav Yudin
//
#import <Foundation/Foundation.h>
#import "yaml.h"
// Mimics NSPropertyListMutabilityOptions
typedef enum {
kYAMLReadOptionImmutable = 0x0000000000000001,
kYAMLReadOptionMutableContainers = 0x0000000000000010,
kYAMLReadOptionMutableContainersAndLeaves = 0x0000000000000110,
kYAMLReadOptionStringScalars = 0x0000000000001000
} YAMLReadOptions;
typedef enum {
kYAMLErrorNoErrors,
kYAMLErrorCodeParserInitializationFailed,
kYAMLErrorCodeParseError,
kYAMLErrorCodeEmitterError,
kYAMLErrorInvalidOptions,
kYAMLErrorCodeOutOfMemory,
kYAMLErrorInvalidYamlObject,
} YAMLErrorCode;
typedef enum {
kYAMLWriteOptionSingleDocument = 0x0000000000000001,
kYAMLWriteOptionMultipleDocuments = 0x0000000000000010,
} YAMLWriteOptions;
extern NSString *const YAMLErrorDomain;
@interface YAMLSerialization : NSObject {
}
+ (void) writeYAML: (id) yaml
toStream: (NSOutputStream *) stream
options: (YAMLWriteOptions) opt
error: (NSError **) error;
+ (NSData *) dataFromYAML: (id) yaml
options: (YAMLWriteOptions) opt
error: (NSError **) error;
+ (NSMutableArray *) YAMLWithStream: (NSInputStream *) stream
options: (YAMLReadOptions) opt
error: (NSError **) error;
+ (NSMutableArray *) YAMLWithData: (NSData *) data
options: (YAMLReadOptions) opt
error: (NSError **) error;
@end

View File

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,40 @@
<?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>BuildMachineOSBuild</key>
<string>11E53</string>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
<string>YAML</string>
<key>CFBundleIdentifier</key>
<string>com.yourcompany.YAML</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>YAML</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>DTCompiler</key>
<string></string>
<key>DTPlatformBuild</key>
<string>4E3002</string>
<key>DTPlatformVersion</key>
<string>GM</string>
<key>DTSDKBuild</key>
<string>11E53</string>
<key>DTSDKName</key>
<string></string>
<key>DTXcode</key>
<string>0433</string>
<key>DTXcodeBuild</key>
<string>4E3002</string>
</dict>
</plist>

View File

Binary file not shown.

View File

@@ -0,0 +1 @@
A

View File

@@ -0,0 +1 @@
Versions/Current/YAML

96
Carthage/Checkouts/NMSSH/README.md vendored Normal file
View File

@@ -0,0 +1,96 @@
# NMSSH [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)
NMSSH is a clean, easy-to-use, unit tested framework for iOS and OSX that wraps libssh2.
## Questions & Issues
If you encounter an issue or have any questions about implementing NMSSH, please post them in [the issue tracker](https://github.com/NMSSH/NMSSH/issues) we do not offer free support via email.
## Installation
### CocoaPods
pod 'NMSSH'
### Carthage
github "NMSSH/NMSSH"
### Build from source
Consult the Wiki for detailed information about how to:
* [Build for OSX](https://github.com/NMSSH/NMSSH/wiki/Build-and-use-in-your-OSX-project) or
* [Build for iOS](https://github.com/NMSSH/NMSSH/wiki/Build-and-use-in-your-iOS-project).
### Include it in your project
Add `#import <NMSSH/NMSSH.h>` to your source file.
### OpenSSL and Libssh2 binaries
NMSSH includes a precompiled version of Libssh2 and OpenSSL compiled with [this script](https://github.com/Frugghi/iSSH2). You can easily recompile the libraries and replace the binaries.
## What does it look like?
```objc
NMSSHSession *session = [NMSSHSession connectToHost:@"127.0.0.1:22"
withUsername:@"user"];
if (session.isConnected) {
[session authenticateByPassword:@"pass"];
if (session.isAuthorized) {
NSLog(@"Authentication succeeded");
}
}
NSError *error = nil;
NSString *response = [session.channel execute:@"ls -l /var/www/" error:&error];
NSLog(@"List of my sites: %@", response);
BOOL success = [session.channel uploadFile:@"~/index.html" to:@"/var/www/9muses.se/"];
[session disconnect];
```
## API Documentation
API documentation for NMSSH is available at [http://cocoadocs.org/docsets/NMSSH/](http://cocoadocs.org/docsets/NMSSH/).
## Guidelines for contributions
* Follow the [code conventions](https://github.com/Lejdborg/cocoa-conventions/).
* Fork NMSSH and create a feature branch. Develop your feature.
* Open a pull request.
**Note:** Make sure that you have _documented your code_ and that you _follow the code conventions_ before opening a pull request.
## NMSSH is used in
* [iTerm2](https://github.com/gnachman/iTerm2)
* [DogeWallet](https://github.com/SlayterDev/DogeWallet)
## Developed by
### Core team
* [Christoffer Lejdborg (@Lejdborg)](https://github.com/Lejdborg) (creator)
* [Tommaso Madonia (@Frugghi)](https://github.com/Frugghi)
### Contributors
* [Sebastian Hunkeler (@lightforce)](https://github.com/lightforce)
* [Endika Gutiérrez (@endSly)](https://github.com/endSly)
* [Clemens Gruber (@clemensg)](https://github.com/clemensg)
* [@gnachman](https://github.com/gnachman)
* [@Shirk](https://github.com/Shirk)
* [@touta](https://github.com/touta)
## License
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.

2
Carthage/Checkouts/NMSSH/gendoc vendored Executable file
View File

@@ -0,0 +1,2 @@
#!/usr/bin/env sh
appledoc --project-name "NMSSH" --project-company "Nine Muses AB" --company-id "se.ninemuses" --output Documentation --no-create-docset --no-repeat-first-par NMSSH/

View File

@@ -13,6 +13,8 @@
A92538CD2DEE0744007E0A18 /* ShhShellTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A92538CB2DEE0744007E0A18 /* ShhShellTests.swift */; }; A92538CD2DEE0744007E0A18 /* ShhShellTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A92538CB2DEE0744007E0A18 /* ShhShellTests.swift */; };
A92538D12DEE0745007E0A18 /* ShhShellUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A92538CE2DEE0745007E0A18 /* ShhShellUITests.swift */; }; A92538D12DEE0745007E0A18 /* ShhShellUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A92538CE2DEE0745007E0A18 /* ShhShellUITests.swift */; };
A92538D22DEE0745007E0A18 /* ShhShellUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A92538CF2DEE0745007E0A18 /* ShhShellUITestsLaunchTests.swift */; }; A92538D22DEE0745007E0A18 /* ShhShellUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A92538CF2DEE0745007E0A18 /* ShhShellUITestsLaunchTests.swift */; };
A9C897DE2DF1A1C900EF9A5F /* NMSSH.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A9C897DC2DF1A1C200EF9A5F /* NMSSH.framework */; };
A9C897DF2DF1A1C900EF9A5F /* NMSSH.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = A9C897DC2DF1A1C200EF9A5F /* NMSSH.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */ /* Begin PBXContainerItemProxy section */
@@ -32,6 +34,20 @@
}; };
/* End PBXContainerItemProxy section */ /* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
A9C897E02DF1A1C900EF9A5F /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 10;
files = (
A9C897DF2DF1A1C900EF9A5F /* NMSSH.framework in Embed Frameworks */,
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
A925389A2DEE06DC007E0A18 /* ShhShell.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ShhShell.app; sourceTree = BUILT_PRODUCTS_DIR; }; A925389A2DEE06DC007E0A18 /* ShhShell.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ShhShell.app; sourceTree = BUILT_PRODUCTS_DIR; };
A92538A72DEE06DE007E0A18 /* ShhShellTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ShhShellTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; A92538A72DEE06DE007E0A18 /* ShhShellTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ShhShellTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -42,6 +58,8 @@
A92538CB2DEE0744007E0A18 /* ShhShellTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShhShellTests.swift; sourceTree = "<group>"; }; A92538CB2DEE0744007E0A18 /* ShhShellTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShhShellTests.swift; sourceTree = "<group>"; };
A92538CE2DEE0745007E0A18 /* ShhShellUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShhShellUITests.swift; sourceTree = "<group>"; }; A92538CE2DEE0745007E0A18 /* ShhShellUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShhShellUITests.swift; sourceTree = "<group>"; };
A92538CF2DEE0745007E0A18 /* ShhShellUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShhShellUITestsLaunchTests.swift; sourceTree = "<group>"; }; A92538CF2DEE0745007E0A18 /* ShhShellUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShhShellUITestsLaunchTests.swift; sourceTree = "<group>"; };
A9C897D22DF1A18C00EF9A5F /* NMSSH-iOS.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = "NMSSH-iOS.xcodeproj"; path = "Carthage/Checkouts/NMSSH/NMSSH-iOS.xcodeproj"; sourceTree = "<group>"; };
A9C897DC2DF1A1C200EF9A5F /* NMSSH.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = NMSSH.framework; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */ /* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */ /* Begin PBXFrameworksBuildPhase section */
@@ -49,6 +67,7 @@
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
A9C897DE2DF1A1C900EF9A5F /* NMSSH.framework in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@@ -76,6 +95,7 @@
A92538D42DEE0756007E0A18 /* Resources */, A92538D42DEE0756007E0A18 /* Resources */,
A92538CC2DEE0744007E0A18 /* ShhShellTests */, A92538CC2DEE0744007E0A18 /* ShhShellTests */,
A92538D02DEE0745007E0A18 /* ShhShellUITests */, A92538D02DEE0745007E0A18 /* ShhShellUITests */,
A9C897D22DF1A18C00EF9A5F /* NMSSH-iOS.xcodeproj */,
A9C8976F2DF1980900EF9A5F /* Frameworks */, A9C8976F2DF1980900EF9A5F /* Frameworks */,
A925389B2DEE06DC007E0A18 /* Products */, A925389B2DEE06DC007E0A18 /* Products */,
); );
@@ -136,10 +156,18 @@
A9C8976F2DF1980900EF9A5F /* Frameworks */ = { A9C8976F2DF1980900EF9A5F /* Frameworks */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
A9C897DC2DF1A1C200EF9A5F /* NMSSH.framework */,
); );
name = Frameworks; name = Frameworks;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
A9C897E12DF1A53B00EF9A5F /* Products */ = {
isa = PBXGroup;
children = (
);
name = Products;
sourceTree = "<group>";
};
/* End PBXGroup section */ /* End PBXGroup section */
/* Begin PBXNativeTarget section */ /* Begin PBXNativeTarget section */
@@ -150,6 +178,7 @@
A92538962DEE06DC007E0A18 /* Sources */, A92538962DEE06DC007E0A18 /* Sources */,
A92538972DEE06DC007E0A18 /* Frameworks */, A92538972DEE06DC007E0A18 /* Frameworks */,
A92538982DEE06DC007E0A18 /* Resources */, A92538982DEE06DC007E0A18 /* Resources */,
A9C897E02DF1A1C900EF9A5F /* Embed Frameworks */,
); );
buildRules = ( buildRules = (
); );
@@ -239,6 +268,12 @@
preferredProjectObjectVersion = 77; preferredProjectObjectVersion = 77;
productRefGroup = A925389B2DEE06DC007E0A18 /* Products */; productRefGroup = A925389B2DEE06DC007E0A18 /* Products */;
projectDirPath = ""; projectDirPath = "";
projectReferences = (
{
ProductGroup = A9C897E12DF1A53B00EF9A5F /* Products */;
ProjectRef = A9C897D22DF1A18C00EF9A5F /* NMSSH-iOS.xcodeproj */;
},
);
projectRoot = ""; projectRoot = "";
targets = ( targets = (
A92538992DEE06DC007E0A18 /* ShhShell */, A92538992DEE06DC007E0A18 /* ShhShell */,
@@ -452,6 +487,7 @@
INFOPLIST_KEY_UILaunchScreen_Generation = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES;
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
IPHONEOS_DEPLOYMENT_TARGET = 18;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
@@ -480,6 +516,7 @@
INFOPLIST_KEY_UILaunchScreen_Generation = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES;
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
IPHONEOS_DEPLOYMENT_TARGET = 18;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",

View File

@@ -0,0 +1,102 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1640"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"
buildArchitectures = "Automatic">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "A92538992DEE06DC007E0A18"
BuildableName = "ShhShell.app"
BlueprintName = "ShhShell"
ReferencedContainer = "container:ShhShell.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
<Testables>
<TestableReference
skipped = "NO"
parallelizable = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "A92538A62DEE06DE007E0A18"
BuildableName = "ShhShellTests.xctest"
BlueprintName = "ShhShellTests"
ReferencedContainer = "container:ShhShell.xcodeproj">
</BuildableReference>
</TestableReference>
<TestableReference
skipped = "NO"
parallelizable = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "A92538B02DEE06DE007E0A18"
BuildableName = "ShhShellUITests.xctest"
BlueprintName = "ShhShellUITests"
ReferencedContainer = "container:ShhShell.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "A92538992DEE06DC007E0A18"
BuildableName = "ShhShell.app"
BlueprintName = "ShhShell"
ReferencedContainer = "container:ShhShell.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "A92538992DEE06DC007E0A18"
BuildableName = "ShhShell.app"
BlueprintName = "ShhShell"
ReferencedContainer = "container:ShhShell.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -6,6 +6,7 @@
// //
import SwiftUI import SwiftUI
import NMSSH
struct ContentView: View { struct ContentView: View {
var body: some View { var body: some View {