From 3d1fb38a1bad4e48654c9f45e67017ad4c5396b9 Mon Sep 17 00:00:00 2001 From: Ben Davis Date: Sun, 27 Mar 2016 15:34:27 +0100 Subject: [PATCH] - Added BEncode project as a submodule - Added Crypto framework wrapper project so allow use in swift without Objective-C code - Created TorrentMetaInfo class and implemented parsing of info dictionary --- .gitmodules | 3 + BitTorrent.xcodeproj/project.pbxproj | 255 ++++++++++++++++- BitTorrent/BitTorrent.h | 2 - BitTorrent/Models/TorrentMetaInfo.swift | 170 +++++++++++ BitTorrent/Models/TorrentMetaInfoTests.swift | 286 +++++++++++++++++++ BitTorrent/Utilities/NSData+sha1.swift | 22 ++ CommonCrypto/CommonCrypto.xcconfig | 11 + CommonCrypto/Info.plist | 26 ++ CommonCrypto/iphoneos.modulemap | 4 + CommonCrypto/iphonesimulator.modulemap | 4 + CommonCrypto/macosx.modulemap | 4 + TestText.torrent | Bin 0 -> 210 bytes 12 files changed, 784 insertions(+), 3 deletions(-) create mode 100644 .gitmodules create mode 100644 BitTorrent/Models/TorrentMetaInfo.swift create mode 100644 BitTorrent/Models/TorrentMetaInfoTests.swift create mode 100644 BitTorrent/Utilities/NSData+sha1.swift create mode 100644 CommonCrypto/CommonCrypto.xcconfig create mode 100644 CommonCrypto/Info.plist create mode 100644 CommonCrypto/iphoneos.modulemap create mode 100644 CommonCrypto/iphonesimulator.modulemap create mode 100644 CommonCrypto/macosx.modulemap create mode 100644 TestText.torrent diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..3f0571a --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "Submodules/BEncodeSwift"] + path = Submodules/BEncodeSwift + url = git@github.com:imben123/BEncodeSwift.git diff --git a/BitTorrent.xcodeproj/project.pbxproj b/BitTorrent.xcodeproj/project.pbxproj index 3d3a12b..9c174ea 100644 --- a/BitTorrent.xcodeproj/project.pbxproj +++ b/BitTorrent.xcodeproj/project.pbxproj @@ -7,6 +7,12 @@ objects = { /* Begin PBXBuildFile section */ + B54D0C1F1CA53993004343BD /* BEncode.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B54D0C1A1CA53983004343BD /* BEncode.framework */; }; + B54D0C271CA56ADB004343BD /* TorrentMetaInfoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B54D0C261CA56ADB004343BD /* TorrentMetaInfoTests.swift */; }; + B54D0C761CA58993004343BD /* CommonCrypto.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B54D0C581CA58722004343BD /* CommonCrypto.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; + B54D0C7A1CA69FAD004343BD /* TorrentMetaInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = B54D0C231CA56A22004343BD /* TorrentMetaInfo.swift */; }; + B54D0C7B1CA69FD8004343BD /* NSData+sha1.swift in Sources */ = {isa = PBXBuildFile; fileRef = B54D0C2B1CA5787E004343BD /* NSData+sha1.swift */; }; + B55D3FC21CA81372005CBDFF /* TestText.torrent in Resources */ = {isa = PBXBuildFile; fileRef = B55D3FC11CA81372005CBDFF /* TestText.torrent */; }; B56A8A071C83539300426AC8 /* TestHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = B56A8A061C83539300426AC8 /* TestHelpers.swift */; }; B585AB781C3833450093FA41 /* BitTorrent.h in Headers */ = {isa = PBXBuildFile; fileRef = B585AB771C3833450093FA41 /* BitTorrent.h */; settings = {ATTRIBUTES = (Public, ); }; }; B585AB7F1C3833450093FA41 /* BitTorrent.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B585AB741C3833450093FA41 /* BitTorrent.framework */; }; @@ -14,6 +20,34 @@ /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ + B54D0C191CA53983004343BD /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = B54D0C141CA53983004343BD /* BEncode.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = B54D0BDB1CA53299004343BD; + remoteInfo = BEncode; + }; + B54D0C1B1CA53983004343BD /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = B54D0C141CA53983004343BD /* BEncode.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = B54D0BE51CA5329A004343BD; + remoteInfo = BEncodeTests; + }; + B54D0C1D1CA5398F004343BD /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = B54D0C141CA53983004343BD /* BEncode.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = B54D0BDA1CA53299004343BD; + remoteInfo = BEncode; + }; + B54D0C771CA589B4004343BD /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = B585AB6B1C3833450093FA41 /* Project object */; + proxyType = 1; + remoteGlobalIDString = B54D0C571CA58722004343BD; + remoteInfo = CommonCrypto; + }; B585AB801C3833450093FA41 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = B585AB6B1C3833450093FA41 /* Project object */; @@ -24,6 +58,17 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + B54D0C141CA53983004343BD /* BEncode.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = BEncode.xcodeproj; path = Submodules/BEncodeSwift/BEncode.xcodeproj; sourceTree = ""; }; + B54D0C231CA56A22004343BD /* TorrentMetaInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TorrentMetaInfo.swift; path = Models/TorrentMetaInfo.swift; sourceTree = ""; }; + B54D0C261CA56ADB004343BD /* TorrentMetaInfoTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TorrentMetaInfoTests.swift; path = Models/TorrentMetaInfoTests.swift; sourceTree = ""; }; + B54D0C2B1CA5787E004343BD /* NSData+sha1.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "NSData+sha1.swift"; path = "Utilities/NSData+sha1.swift"; sourceTree = ""; }; + B54D0C581CA58722004343BD /* CommonCrypto.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CommonCrypto.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + B54D0C6F1CA58749004343BD /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + B54D0C711CA58767004343BD /* CommonCrypto.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = CommonCrypto.xcconfig; sourceTree = ""; }; + B54D0C721CA5879E004343BD /* iphoneos.modulemap */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.module-map"; path = iphoneos.modulemap; sourceTree = ""; }; + B54D0C731CA587A9004343BD /* iphonesimulator.modulemap */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = "sourcecode.module-map"; path = iphonesimulator.modulemap; sourceTree = ""; }; + B54D0C741CA587B5004343BD /* macosx.modulemap */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = "sourcecode.module-map"; path = macosx.modulemap; sourceTree = ""; }; + B55D3FC11CA81372005CBDFF /* TestText.torrent */ = {isa = PBXFileReference; lastKnownFileType = file; path = TestText.torrent; sourceTree = ""; }; B56A8A061C83539300426AC8 /* TestHelpers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TestHelpers.swift; path = Utilities/TestHelpers.swift; sourceTree = ""; }; B585AB741C3833450093FA41 /* BitTorrent.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = BitTorrent.framework; sourceTree = BUILT_PRODUCTS_DIR; }; B585AB771C3833450093FA41 /* BitTorrent.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BitTorrent.h; sourceTree = ""; }; @@ -34,10 +79,19 @@ /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + B54D0C541CA58722004343BD /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; B585AB701C3833450093FA41 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + B54D0C761CA58993004343BD /* CommonCrypto.framework in Frameworks */, + B54D0C1F1CA53993004343BD /* BEncode.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -52,11 +106,68 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + B54D0C131CA53979004343BD /* Frameworks */ = { + isa = PBXGroup; + children = ( + B54D0C141CA53983004343BD /* BEncode.xcodeproj */, + ); + name = Frameworks; + sourceTree = ""; + }; + B54D0C151CA53983004343BD /* Products */ = { + isa = PBXGroup; + children = ( + B54D0C1A1CA53983004343BD /* BEncode.framework */, + B54D0C1C1CA53983004343BD /* BEncodeTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + B54D0C211CA56619004343BD /* Torrents */ = { + isa = PBXGroup; + children = ( + B55D3FC11CA81372005CBDFF /* TestText.torrent */, + ); + name = Torrents; + sourceTree = ""; + }; + B54D0C251CA56A25004343BD /* Models */ = { + isa = PBXGroup; + children = ( + B54D0C231CA56A22004343BD /* TorrentMetaInfo.swift */, + B54D0C261CA56ADB004343BD /* TorrentMetaInfoTests.swift */, + ); + name = Models; + sourceTree = ""; + }; + B54D0C291CA5785F004343BD /* Utilities */ = { + isa = PBXGroup; + children = ( + B54D0C2B1CA5787E004343BD /* NSData+sha1.swift */, + ); + name = Utilities; + sourceTree = ""; + }; + B54D0C591CA58722004343BD /* CommonCrypto */ = { + isa = PBXGroup; + children = ( + B54D0C6F1CA58749004343BD /* Info.plist */, + B54D0C711CA58767004343BD /* CommonCrypto.xcconfig */, + B54D0C721CA5879E004343BD /* iphoneos.modulemap */, + B54D0C731CA587A9004343BD /* iphonesimulator.modulemap */, + B54D0C741CA587B5004343BD /* macosx.modulemap */, + ); + path = CommonCrypto; + sourceTree = ""; + }; B585AB6A1C3833450093FA41 = { isa = PBXGroup; children = ( + B54D0C211CA56619004343BD /* Torrents */, B585AB761C3833450093FA41 /* BitTorrent */, B585AB821C3833450093FA41 /* BitTorrentTests */, + B54D0C591CA58722004343BD /* CommonCrypto */, + B54D0C131CA53979004343BD /* Frameworks */, B585AB751C3833450093FA41 /* Products */, ); sourceTree = ""; @@ -66,6 +177,7 @@ children = ( B585AB741C3833450093FA41 /* BitTorrent.framework */, B585AB7E1C3833450093FA41 /* BitTorrentTests.xctest */, + B54D0C581CA58722004343BD /* CommonCrypto.framework */, ); name = Products; sourceTree = ""; @@ -75,6 +187,8 @@ children = ( B585AB771C3833450093FA41 /* BitTorrent.h */, B585AB831C3833450093FA41 /* BitTorrentTests.swift */, + B54D0C251CA56A25004343BD /* Models */, + B54D0C291CA5785F004343BD /* Utilities */, B585AB791C3833450093FA41 /* Info.plist */, ); path = BitTorrent; @@ -92,6 +206,13 @@ /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ + B54D0C551CA58722004343BD /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; B585AB711C3833450093FA41 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; @@ -103,6 +224,24 @@ /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ + B54D0C571CA58722004343BD /* CommonCrypto */ = { + isa = PBXNativeTarget; + buildConfigurationList = B54D0C691CA58722004343BD /* Build configuration list for PBXNativeTarget "CommonCrypto" */; + buildPhases = ( + B54D0C531CA58722004343BD /* Sources */, + B54D0C541CA58722004343BD /* Frameworks */, + B54D0C551CA58722004343BD /* Headers */, + B54D0C561CA58722004343BD /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = CommonCrypto; + productName = CommonCrypto; + productReference = B54D0C581CA58722004343BD /* CommonCrypto.framework */; + productType = "com.apple.product-type.framework"; + }; B585AB731C3833450093FA41 /* BitTorrent */ = { isa = PBXNativeTarget; buildConfigurationList = B585AB881C3833450093FA41 /* Build configuration list for PBXNativeTarget "BitTorrent" */; @@ -115,6 +254,8 @@ buildRules = ( ); dependencies = ( + B54D0C781CA589B4004343BD /* PBXTargetDependency */, + B54D0C1E1CA5398F004343BD /* PBXTargetDependency */, ); name = BitTorrent; productName = BitTorrent; @@ -146,10 +287,13 @@ isa = PBXProject; attributes = { LastSwiftMigration = 0720; - LastSwiftUpdateCheck = 0720; + LastSwiftUpdateCheck = 0730; LastUpgradeCheck = 0720; ORGANIZATIONNAME = "Ben Davis"; TargetAttributes = { + B54D0C571CA58722004343BD = { + CreatedOnToolsVersion = 7.3; + }; B585AB731C3833450093FA41 = { CreatedOnToolsVersion = 7.2; }; @@ -168,15 +312,46 @@ mainGroup = B585AB6A1C3833450093FA41; productRefGroup = B585AB751C3833450093FA41 /* Products */; projectDirPath = ""; + projectReferences = ( + { + ProductGroup = B54D0C151CA53983004343BD /* Products */; + ProjectRef = B54D0C141CA53983004343BD /* BEncode.xcodeproj */; + }, + ); projectRoot = ""; targets = ( B585AB731C3833450093FA41 /* BitTorrent */, B585AB7D1C3833450093FA41 /* BitTorrentTests */, + B54D0C571CA58722004343BD /* CommonCrypto */, ); }; /* End PBXProject section */ +/* Begin PBXReferenceProxy section */ + B54D0C1A1CA53983004343BD /* BEncode.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = BEncode.framework; + remoteRef = B54D0C191CA53983004343BD /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + B54D0C1C1CA53983004343BD /* BEncodeTests.xctest */ = { + isa = PBXReferenceProxy; + fileType = wrapper.cfbundle; + path = BEncodeTests.xctest; + remoteRef = B54D0C1B1CA53983004343BD /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; +/* End PBXReferenceProxy section */ + /* Begin PBXResourcesBuildPhase section */ + B54D0C561CA58722004343BD /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; B585AB721C3833450093FA41 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -188,16 +363,26 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + B55D3FC21CA81372005CBDFF /* TestText.torrent in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + B54D0C531CA58722004343BD /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; B585AB6F1C3833450093FA41 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + B54D0C7A1CA69FAD004343BD /* TorrentMetaInfo.swift in Sources */, + B54D0C7B1CA69FD8004343BD /* NSData+sha1.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -207,12 +392,23 @@ files = ( B56A8A071C83539300426AC8 /* TestHelpers.swift in Sources */, B585AB841C3833450093FA41 /* BitTorrentTests.swift in Sources */, + B54D0C271CA56ADB004343BD /* TorrentMetaInfoTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ + B54D0C1E1CA5398F004343BD /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = BEncode; + targetProxy = B54D0C1D1CA5398F004343BD /* PBXContainerItemProxy */; + }; + B54D0C781CA589B4004343BD /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = B54D0C571CA58722004343BD /* CommonCrypto */; + targetProxy = B54D0C771CA589B4004343BD /* PBXContainerItemProxy */; + }; B585AB811C3833450093FA41 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = B585AB731C3833450093FA41 /* BitTorrent */; @@ -221,6 +417,44 @@ /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ + B54D0C6A1CA58722004343BD /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = B54D0C711CA58767004343BD /* CommonCrypto.xcconfig */; + buildSettings = { + CLANG_ANALYZER_NONNULL = YES; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = CommonCrypto/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 9.3; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = uk.co.bendavisapps.CommonCrypto; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + }; + name = Debug; + }; + B54D0C6B1CA58722004343BD /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = B54D0C711CA58767004343BD /* CommonCrypto.xcconfig */; + buildSettings = { + CLANG_ANALYZER_NONNULL = YES; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = CommonCrypto/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 9.3; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = uk.co.bendavisapps.CommonCrypto; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + }; + name = Release; + }; B585AB861C3833450093FA41 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -313,6 +547,7 @@ B585AB891C3833450093FA41 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = NO; CLANG_ENABLE_MODULES = YES; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; @@ -320,10 +555,12 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = BitTorrent/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 9.3; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = bendavis.BitTorrent; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; + SWIFT_OBJC_BRIDGING_HEADER = ""; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; }; name = Debug; @@ -331,6 +568,7 @@ B585AB8A1C3833450093FA41 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = NO; CLANG_ENABLE_MODULES = YES; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; @@ -338,10 +576,12 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = BitTorrent/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 9.3; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = bendavis.BitTorrent; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; + SWIFT_OBJC_BRIDGING_HEADER = ""; }; name = Release; }; @@ -349,9 +589,11 @@ isa = XCBuildConfiguration; buildSettings = { INFOPLIST_FILE = BitTorrentTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 9.3; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = bendavis.BitTorrentTests; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = ""; }; name = Debug; }; @@ -359,15 +601,26 @@ isa = XCBuildConfiguration; buildSettings = { INFOPLIST_FILE = BitTorrentTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 9.3; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = bendavis.BitTorrentTests; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = ""; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + B54D0C691CA58722004343BD /* Build configuration list for PBXNativeTarget "CommonCrypto" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + B54D0C6A1CA58722004343BD /* Debug */, + B54D0C6B1CA58722004343BD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; B585AB6E1C3833450093FA41 /* Build configuration list for PBXProject "BitTorrent" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/BitTorrent/BitTorrent.h b/BitTorrent/BitTorrent.h index 46fd8b1..bfe4aa5 100644 --- a/BitTorrent/BitTorrent.h +++ b/BitTorrent/BitTorrent.h @@ -15,5 +15,3 @@ FOUNDATION_EXPORT double BitTorrentVersionNumber; FOUNDATION_EXPORT const unsigned char BitTorrentVersionString[]; // In this header, you should import all the public headers of your framework using statements like #import - - diff --git a/BitTorrent/Models/TorrentMetaInfo.swift b/BitTorrent/Models/TorrentMetaInfo.swift new file mode 100644 index 0000000..3b3ef7b --- /dev/null +++ b/BitTorrent/Models/TorrentMetaInfo.swift @@ -0,0 +1,170 @@ +// +// TorrentMetaInfo.swift +// BitTorrent +// +// Created by Ben Davis on 25/03/2016. +// Copyright © 2016 Ben Davis. All rights reserved. +// + +import Foundation +import class BEncode.BEncoder + +class TorrentMetaInfo { + + var infoHash : NSData // this is the original bencoded dictionary, hashed + + var info: TorrentInfoDictionary +// var announce: String +// var announceList: [String]? +// var creationDate: NSDate? +// var comment: String? +// var createdBy: String? + + init?(data: NSData) { + print(try! BEncoder.decodeDictionaryKeysOnly(data)["info"]!) + self.infoHash = try! BEncoder.decodeDictionaryKeysOnly(data)["info"]!.sha1() + let decodedMetainfo = try! BEncoder.decodeStringKeyedDictionary(data) + if let info = TorrentInfoDictionary(decodedMetainfo["info"] as! [String : AnyObject]) { + self.info = info + } else { + return nil + } + } + +} + +class TorrentInfoDictionary { + + let name : String + let pieceLength : Int + let isPrivate : Bool + let files: [TorrentFileInfo] + let pieces : [NSData]? + let length: Int + + init?(_ dictionary: [String : AnyObject]) { + + if let nameData = dictionary["name"] as? NSData, name = String(asciiData: nameData) { + self.name = name + } else { + return nil + } + + if let pieceLength = dictionary["piece length"] as? Int { + self.pieceLength = pieceLength + } else { + return nil + } + + if let pieces = dictionary["pieces"] as? NSData, piecesArray = TorrentInfoDictionary.seperatePieces(pieces) { + self.pieces = piecesArray + } else { + return nil + } + + if let tuple = TorrentInfoDictionary.parseFilesAndLengthFromDictionary(dictionary, parsedName: name) { + self.files = tuple.files + self.length = tuple.totalLength + } else { + return nil + } + + if let isPrivate = dictionary["private"] as? Int { + self.isPrivate = (isPrivate == 1) + } else { + self.isPrivate = false + } + + } + + private class func parseFilesAndLengthFromDictionary(dictionary: [ String : AnyObject ], parsedName name: String) + -> (files: [TorrentFileInfo], totalLength: Int)? { + + if let files = dictionary["files"] as? [ [ String : AnyObject ] ] { + + if let tuple = TorrentInfoDictionary.parseFilesDictionaries(files) { + return tuple + } else { + return nil + } + + } else if let length = dictionary["length"] as? Int { + + let md5sumData = dictionary["md5sum"] as? NSData + let md5sum = String(asciiData: md5sumData) + let files = [ TorrentFileInfo(path: name, length: length, md5sum: md5sum) ] + + return (files, length) + + } else { + + return nil + + } + } + + private class func parseFilesDictionaries(files: [ [ String : AnyObject ] ]) + -> (files: [TorrentFileInfo], totalLength: Int)? { + + var totalLength = 0 + var result: [TorrentFileInfo] = [] + + for fileDictionary in files { + + if let file = TorrentFileInfo(dictionary: fileDictionary) { + + totalLength += file.length + result.append(file) + + } else { + return nil + } + + } + + return (files: result, totalLength: totalLength) + } + + private class func seperatePieces(pieces: NSData) -> [NSData]? { + if pieces.length % 20 != 0 { + return nil + } + + var result: [NSData] = [] + for index in 0.stride(to:pieces.length, by: 20) { + result.append(pieces.subdataWithRange(NSMakeRange(index, 20))) + } + return result + } +} + +class TorrentFileInfo { + let path: String + let length: Int + let md5sum: String? + + init(path: String, length: Int, md5sum: String?) { + self.path = path + self.length = length + self.md5sum = md5sum + } + + convenience init?(dictionary: [ String : AnyObject ]) { + + let pathData = dictionary["path"] as? NSData + let path = String(asciiData: pathData) + + let length = dictionary["length"] as? Int + + if let length = length, path = path { + + let md5sumData = dictionary["md5sum"] as? NSData + let md5sum = String(asciiData: md5sumData) + + self.init(path: path, length: length, md5sum: md5sum) + + } else { + return nil + } + } +} diff --git a/BitTorrent/Models/TorrentMetaInfoTests.swift b/BitTorrent/Models/TorrentMetaInfoTests.swift new file mode 100644 index 0000000..ec0413e --- /dev/null +++ b/BitTorrent/Models/TorrentMetaInfoTests.swift @@ -0,0 +1,286 @@ +// +// TorrentMetaInfoTests.swift +// BitTorrent +// +// Created by Ben Davis on 25/03/2016. +// Copyright © 2016 Ben Davis. All rights reserved. +// + +import XCTest +@testable import BitTorrent +import BEncode + +class TorrentMetaInfoTests: XCTestCase { + + let filePieceLength = 16384 + let singleFilePiece = NSData(byteArray: [0x3f, 0x3f, 0x11, 0x09, 0x64, 0x07, 0x00, 0x3f, 0x42, + 0x35, 0x3f, 0x3f, 0x59, 0x2e, 0x23, 0x13, 0x3f, 0x18, 0x23, 0x3e]) + let torrentName = "Torrent Name" + let singleFileName = "test.txt" + let singleFileLength = 117 + let singleFileMD5 = "c23d5ddec291bf9e27cb84657144388dc352472ca89709bbda80e680c82470e1" + let multipleFile1MD5 = "d8e624fda84296ba9e5af415faa5c2aa68960ce0578116a8507340438893bd85" + let multipleFile2MD5 = "e2ddfed708ea34db57b4a1ef7691776db5e24dff813706e4c695a6668f8fbabf" + let multipleFileLength1 = 116 + let multipleFileLength2 = 115 + let multipleFilePath1 = "/test/path" + let multipleFilePath2 = "/test/path2" + + override func setUp() { + super.setUp() + } + + override func tearDown() { + super.tearDown() + } + + // MARK: - Create some example dictionaries + + func singleFileInfoDictionary() -> [String:AnyObject] { + return [ + "piece length" : filePieceLength, + "pieces" : createMultiplePieces(), + "name" : singleFileName, + "length" : singleFileLength, + ] + } + + func createMultiplePieces() -> NSData { + return NSMutableData(data: singleFilePiece).andData(singleFilePiece) + } + + func multiFileInfoDictionary() -> [String:AnyObject] { + return multiFileInfoDictionary([ + testFileDictionary(multipleFileLength1, path: multipleFilePath1, md5sum: multipleFile1MD5), + testFileDictionary(multipleFileLength2, path: multipleFilePath2, md5sum: multipleFile2MD5), + ]) + } + + func multiFileInfoDictionary(files: [ [ String : AnyObject ] ]) -> [String:AnyObject] { + return [ + "piece length" : filePieceLength, + "pieces" : createMultiplePieces(), + "name" : torrentName, + "files" : files, + ] + } + + func testFileDictionary(length: Int, path: String, md5sum: String?) -> [ String : AnyObject ] { + var result: [ String : AnyObject ] = [ + "length" : length, + "path" : path, + ] + if let md5sum = md5sum { + result.updateValue(md5sum, forKey:"md5sum") + } + return result + } + + func exampleMetaInfoDictionary() -> NSData { + return self.metaInfoDictionaryWithInfoDictionary(singleFileInfoDictionary()) + } + + func metaInfoDictionaryWithInfoDictionary(infoDictionary: [String : AnyObject]) -> NSData { + return try! BEncode.BEncoder.encode( [ "info" : infoDictionary ] ) + } + + // MARK: - + + func testCanInitialiseWithDictionary() { + let metaInfo = TorrentMetaInfo(data: self.exampleMetaInfoDictionary())! + let _ = metaInfo.infoHash + let _ = metaInfo.info + } + + func testInfoDictionarySplitsPiecesInto20ByteChecksums() { + let metaInfo = TorrentMetaInfo(data: self.exampleMetaInfoDictionary())! + let info = metaInfo.info + XCTAssertEqual(info.pieces![0], singleFilePiece) + XCTAssertEqual(info.pieces![1], singleFilePiece) + } + + func testProducesCorrectInfoHash() { + let path = NSBundle(forClass: self.dynamicType).pathForResource("TestText", ofType: "torrent") + let data = NSData(contentsOfFile: path!)! + let metaInfo = TorrentMetaInfo(data: data)! + + let hash = NSData(byteArray:[ 0xf0, 0xb8, 0x71, 0x98, 0x99, 0x53, 0x97, 0x3f, 0xbf, 0xa9, 0x4d, + 0xc8, 0x14, 0x98, 0xee, 0x8d, 0x20, 0x5b, 0xb2, 0x23]) + + XCTAssertEqual(hash, metaInfo.infoHash) + } + + // MARK: - Test info dictionary + + func testProducesCorrectCommonInfoDictionaryProperties() { + let metaInfo = TorrentMetaInfo(data: self.exampleMetaInfoDictionary())! + let info = metaInfo.info + + XCTAssertEqual(info.name, singleFileName) + + XCTAssertEqual(info.pieceLength, filePieceLength) + + let piecesConcatenated = info.pieces!.reduce(NSMutableData()) { + (result: NSMutableData, item: NSData) in + return result.andData(item) + } + XCTAssertEqual(piecesConcatenated, createMultiplePieces()) + } + + func testIsPrivateIffPrivateKeyIsPresentAnd1() { + var infoDictionary = singleFileInfoDictionary() + + infoDictionary.updateValue(1, forKey: "private") + var metainfo = TorrentMetaInfo(data: metaInfoDictionaryWithInfoDictionary(infoDictionary))! + XCTAssertTrue(metainfo.info.isPrivate) + + infoDictionary.updateValue(0, forKey: "private") + metainfo = TorrentMetaInfo(data: metaInfoDictionaryWithInfoDictionary(infoDictionary))! + XCTAssertFalse(metainfo.info.isPrivate) + + infoDictionary.removeValueForKey("private") + metainfo = TorrentMetaInfo(data: metaInfoDictionaryWithInfoDictionary(infoDictionary))! + XCTAssertFalse(metainfo.info.isPrivate) + } + + // MARK: Single file info dictionary + + func testInfoDictionaryForSingleFileTorrent() { + let metaInfo = TorrentMetaInfo(data: self.exampleMetaInfoDictionary())! + let info = metaInfo.info + + XCTAssertEqual(info.length, singleFileLength) + + XCTAssertEqual(info.files.count, 1) + + let file = info.files[0] + XCTAssertEqual(file.length, singleFileLength) + XCTAssertEqual(file.path, singleFileName) + + } + + func testInfoDictionaryContainsMD5ChecksumIfPresentInSingleFile() { + var infoDictionary = singleFileInfoDictionary() + + var metaInfo = TorrentMetaInfo(data: metaInfoDictionaryWithInfoDictionary(infoDictionary))! + var file = metaInfo.info.files[0] + XCTAssertNil(file.md5sum) + + + infoDictionary.updateValue(singleFileMD5, forKey: "md5sum") + metaInfo = TorrentMetaInfo(data: metaInfoDictionaryWithInfoDictionary(infoDictionary))! + file = metaInfo.info.files[0] + + XCTAssertEqual(file.md5sum, singleFileMD5) + + } + + func testInitializerReturnsNilOnInvalidInfoDictionaryForSingleFile() { + + var infoDictionary = singleFileInfoDictionary() + infoDictionary.removeValueForKey("name") + var metaInfoDictionary = metaInfoDictionaryWithInfoDictionary(infoDictionary) + + XCTAssertNil(TorrentMetaInfo(data: metaInfoDictionary)) + + infoDictionary = singleFileInfoDictionary() + infoDictionary.removeValueForKey("piece length") + metaInfoDictionary = metaInfoDictionaryWithInfoDictionary(infoDictionary) + + XCTAssertNil(TorrentMetaInfo(data: metaInfoDictionary)) + + infoDictionary = singleFileInfoDictionary() + infoDictionary.removeValueForKey("pieces") + metaInfoDictionary = metaInfoDictionaryWithInfoDictionary(infoDictionary) + + XCTAssertNil(TorrentMetaInfo(data: metaInfoDictionary)) + + infoDictionary = singleFileInfoDictionary() + infoDictionary.updateValue(NSData(byteArray: [1,2,3]), forKey: "pieces") + metaInfoDictionary = metaInfoDictionaryWithInfoDictionary(infoDictionary) + + XCTAssertNil(TorrentMetaInfo(data: metaInfoDictionary)) + + infoDictionary = singleFileInfoDictionary() + infoDictionary.updateValue(NSMutableData(length: 21)!, forKey: "pieces") + metaInfoDictionary = metaInfoDictionaryWithInfoDictionary(infoDictionary) + + XCTAssertNil(TorrentMetaInfo(data: metaInfoDictionary)) + } + + // MARK: Multiple file info dictionary + + func testInfoDictionaryForMultipleFileTorrent() { + let infoDictionary = multiFileInfoDictionary() + let metaInfoDictionary = metaInfoDictionaryWithInfoDictionary(infoDictionary) + let metaInfo = TorrentMetaInfo(data: metaInfoDictionary)! + let info = metaInfo.info + + XCTAssertEqual(info.length, multipleFileLength1 + multipleFileLength2) + + XCTAssertEqual(info.files.count, 2) + + let file1 = info.files[0] + XCTAssertEqual(file1.length, multipleFileLength1) + XCTAssertEqual(file1.path, multipleFilePath1) + + let file2 = info.files[1] + XCTAssertEqual(file2.length, multipleFileLength2) + XCTAssertEqual(file2.path, multipleFilePath2) + + } + + func testInfoDictionaryContainsMD5ChecksumIfPresentInMultipleFiles() { + + var infoDictionary = multiFileInfoDictionary() + var metaInfoDictionary = metaInfoDictionaryWithInfoDictionary(infoDictionary) + var metaInfo = TorrentMetaInfo(data: metaInfoDictionary)! + var file1 = metaInfo.info.files[0] + var file2 = metaInfo.info.files[1] + + XCTAssertEqual(file1.md5sum, multipleFile1MD5) + XCTAssertEqual(file2.md5sum, multipleFile2MD5) + + + infoDictionary.updateValue(multipleFile1MD5, forKey: "md5sum") + + infoDictionary = multiFileInfoDictionary([ + [ "length" : multipleFileLength1, "path" : multipleFilePath1 ], + [ "length" : multipleFileLength2, "path" : multipleFilePath2 ], + ]) + metaInfoDictionary = metaInfoDictionaryWithInfoDictionary(infoDictionary) + metaInfo = TorrentMetaInfo(data: metaInfoDictionary)! + file1 = metaInfo.info.files[0] + file2 = metaInfo.info.files[1] + + XCTAssertNil(file1.md5sum) + XCTAssertNil(file2.md5sum) + + + } + + func testInitializerReturnsNilOnInvalidInfoDictionaryForMultipleFiles() { + + var infoDictionary = multiFileInfoDictionary() + infoDictionary.removeValueForKey("files") + var metaInfoDictionary = metaInfoDictionaryWithInfoDictionary(infoDictionary) + XCTAssertNil(TorrentMetaInfo(data: metaInfoDictionary)) + + infoDictionary = multiFileInfoDictionary([ + [ "length" : multipleFileLength1, "path" : multipleFilePath1 ], + [ "length" : multipleFileLength2 ], + ]) + metaInfoDictionary = metaInfoDictionaryWithInfoDictionary(infoDictionary) + XCTAssertNil(TorrentMetaInfo(data: metaInfoDictionary)) + + infoDictionary = multiFileInfoDictionary([ + [ "length" : multipleFileLength1, "path" : multipleFilePath1 ], + [ "path" : multipleFilePath2 ], + ]) + metaInfoDictionary = metaInfoDictionaryWithInfoDictionary(infoDictionary) + XCTAssertNil(TorrentMetaInfo(data: metaInfoDictionary)) + + } + + +} diff --git a/BitTorrent/Utilities/NSData+sha1.swift b/BitTorrent/Utilities/NSData+sha1.swift new file mode 100644 index 0000000..bb9483d --- /dev/null +++ b/BitTorrent/Utilities/NSData+sha1.swift @@ -0,0 +1,22 @@ +// +// NSData+sha1.swift +// BitTorrent +// +// Created by Ben Davis on 25/03/2016. +// Copyright © 2016 Ben Davis. All rights reserved. +// + +import Foundation +import CommonCrypto + +extension NSData { + + func sha1() -> NSData { + let outputLength = Int(CC_SHA1_DIGEST_LENGTH) + var digest = [UInt8](count:Int(CC_SHA1_DIGEST_LENGTH), repeatedValue: 0) + CC_SHA1(self.bytes, CC_LONG(self.length), &digest) + let bytesPointer = UnsafePointer(digest) + return NSData(bytes: bytesPointer, length: outputLength) + } + +} \ No newline at end of file diff --git a/CommonCrypto/CommonCrypto.xcconfig b/CommonCrypto/CommonCrypto.xcconfig new file mode 100644 index 0000000..713b121 --- /dev/null +++ b/CommonCrypto/CommonCrypto.xcconfig @@ -0,0 +1,11 @@ +// +// CommonCrypto.xcconfig +// BitTorrent +// +// Created by Ben Davis on 25/03/2016. +// Copyright © 2016 Ben Davis. All rights reserved. +// + +MODULEMAP_FILE[sdk=iphoneos*] = $(SRCROOT)/CommonCrypto/iphoneos.modulemap +MODULEMAP_FILE[sdk=iphonesimulator*] = $(SRCROOT)/CommonCrypto/iphonesimulator.modulemap +MODULEMAP_FILE[sdk=macosx*] = $(SRCROOT)/CommonCrypto/macosx.modulemap \ No newline at end of file diff --git a/CommonCrypto/Info.plist b/CommonCrypto/Info.plist new file mode 100644 index 0000000..d3de8ee --- /dev/null +++ b/CommonCrypto/Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + NSPrincipalClass + + + diff --git a/CommonCrypto/iphoneos.modulemap b/CommonCrypto/iphoneos.modulemap new file mode 100644 index 0000000..6b6c840 --- /dev/null +++ b/CommonCrypto/iphoneos.modulemap @@ -0,0 +1,4 @@ +module CommonCrypto [system] { + header "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/usr/include/CommonCrypto/CommonCrypto.h" + export * +} \ No newline at end of file diff --git a/CommonCrypto/iphonesimulator.modulemap b/CommonCrypto/iphonesimulator.modulemap new file mode 100644 index 0000000..e9df470 --- /dev/null +++ b/CommonCrypto/iphonesimulator.modulemap @@ -0,0 +1,4 @@ +module CommonCrypto [system] { + header "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/usr/include/CommonCrypto/CommonCrypto.h" + export * +} \ No newline at end of file diff --git a/CommonCrypto/macosx.modulemap b/CommonCrypto/macosx.modulemap new file mode 100644 index 0000000..0f5b646 --- /dev/null +++ b/CommonCrypto/macosx.modulemap @@ -0,0 +1,4 @@ +module CommonCrypto [system] { + header "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/usr/include/CommonCrypto/CommonCrypto.h" + export * +} \ No newline at end of file diff --git a/TestText.torrent b/TestText.torrent new file mode 100644 index 0000000000000000000000000000000000000000..dd8c3014bc292550a2bc6fd5ff6c8192a48592c6 GIT binary patch literal 210 zcmXxbu?_)25C&kKC`F^ZMD1>8?{05Rp-^}N3gx*SY{IOZk?p{!ya;VeEE!m%V*ZXkaN?rz z5MS-Y=t_}A;wCuH+Y*C?~heF8a#)iDbWXL#zQaw literal 0 HcmV?d00001