From 00fd0adf89e14603a4b04082b202beb5575a8fbd Mon Sep 17 00:00:00 2001 From: oscarz Date: Mon, 12 Aug 2024 10:49:20 +0800 Subject: [PATCH] Initial commit --- .../project.pbxproj | 18 +- .../contents.xcworkspacedata | 0 .../xcshareddata/IDEWorkspaceChecks.plist | 0 .../xcshareddata/xcschemes/AIGrammar.xcscheme | 0 .../xcschemes/xcschememanagement.plist | 0 .../contents.xcworkspacedata | 10 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/swiftpm/Package.resolved | 14 + AIGrammar/AIGrammar.entitlements | 5 +- AIGrammar/AIGrammarApp.swift | 12 +- AIGrammar/AllTabView.swift | 40 +- AIGrammar/CommView/LoadingView.swift | 8 +- AIGrammar/GrammarSubView/CameraView.swift | 83 +- AIGrammar/GrammarSubView/IAPTestView.swift | 190 +- AIGrammar/GrammarSubView/InputView.swift | 215 +- AIGrammar/GrammarSubView/ResultView.swift | 192 +- AIGrammar/GrammarSubView/RichText.swift | 187 +- AIGrammar/GrammarSubView/ShareSheet.swift | 15 +- AIGrammar/View/GrammarCheckView.swift | 169 +- AIGrammar/View/IAPView.swift | 182 +- AIGrammar/View/SettingsView.swift | 219 +- AIGrammar/View/TranslateView.swift | 374 +- AIGrammar/View/WordsView.swift | 200 +- AIGrammar/ViewModel/Config.swift | 81 + AIGrammar/ViewModel/GrammarData.swift | 129 +- AIGrammar/lib/ColorToString.swift | 13 + AIGrammar/lib/CommFunc.swift | 52 + AIGrammar/lib/IapManager.swift | 225 + AIGrammar/lib/InitAPP.swift | 83 + AIGrammar/lib/LogManager.swift | 35 + AIGrammar/lib/NetworkManager.swift | 168 +- Podfile | 26 + Podfile.lock | 65 + Pods/Alamofire/LICENSE | 19 + Pods/Alamofire/README.md | 269 ++ Pods/Alamofire/Source/Alamofire.swift | 43 + Pods/Alamofire/Source/Core/AFError.swift | 874 ++++ Pods/Alamofire/Source/Core/DataRequest.swift | 448 ++ .../Source/Core/DataStreamRequest.swift | 585 +++ .../Source/Core/DownloadRequest.swift | 588 +++ Pods/Alamofire/Source/Core/HTTPHeaders.swift | 466 +++ Pods/Alamofire/Source/Core/HTTPMethod.swift | 56 + .../Alamofire/Source/Core/Notifications.swift | 115 + .../Source/Core/ParameterEncoder.swift | 213 + .../Source/Core/ParameterEncoding.swift | 349 ++ Pods/Alamofire/Source/Core/Protected.swift | 168 + Pods/Alamofire/Source/Core/Request.swift | 1090 +++++ .../Source/Core/RequestTaskMap.swift | 150 + Pods/Alamofire/Source/Core/Response.swift | 453 ++ Pods/Alamofire/Source/Core/Session.swift | 1350 ++++++ .../Source/Core/SessionDelegate.swift | 387 ++ ...URLConvertible+URLRequestConvertible.swift | 105 + .../Alamofire/Source/Core/UploadRequest.swift | 174 + .../Source/Core/WebSocketRequest.swift | 564 +++ .../Extensions/DispatchQueue+Alamofire.swift | 37 + .../Extensions/OperationQueue+Alamofire.swift | 49 + .../Source/Extensions/Result+Alamofire.swift | 120 + .../Extensions/StringEncoding+Alamofire.swift | 55 + .../Extensions/URLRequest+Alamofire.swift | 39 + .../URLSessionConfiguration+Alamofire.swift | 46 + .../Source/Features/AlamofireExtended.swift | 61 + .../Features/AuthenticationInterceptor.swift | 402 ++ .../Features/CachedResponseHandler.swift | 107 + Pods/Alamofire/Source/Features/Combine.swift | 652 +++ .../Source/Features/Concurrency.swift | 962 +++++ .../Source/Features/EventMonitor.swift | 907 ++++ .../Source/Features/MultipartFormData.swift | 601 +++ .../Source/Features/MultipartUpload.swift | 89 + .../Features/NetworkReachabilityManager.swift | 292 ++ .../Source/Features/RedirectHandler.swift | 111 + .../Source/Features/RequestCompression.swift | 146 + .../Source/Features/RequestInterceptor.swift | 351 ++ .../Features/ResponseSerialization.swift | 525 +++ .../Source/Features/RetryPolicy.swift | 430 ++ .../Features/ServerTrustEvaluation.swift | 768 ++++ .../Features/URLEncodedFormEncoder.swift | 1151 ++++++ .../Source/Features/Validation.swift | 302 ++ Pods/Alamofire/Source/PrivacyInfo.xcprivacy | 23 + Pods/BlueCryptor/LICENSE | 176 + Pods/BlueCryptor/README.md | 235 ++ Pods/BlueCryptor/Sources/Cryptor/Crypto.swift | 128 + .../BlueCryptor/Sources/Cryptor/Cryptor.swift | 71 + Pods/BlueCryptor/Sources/Cryptor/Digest.swift | 279 ++ Pods/BlueCryptor/Sources/Cryptor/HMAC.swift | 335 ++ .../Sources/Cryptor/KeyDerivation.swift | 211 + Pods/BlueCryptor/Sources/Cryptor/Random.swift | 106 + .../Sources/Cryptor/SSLPointerTricks.swift | 100 + Pods/BlueCryptor/Sources/Cryptor/Status.swift | 288 ++ .../Sources/Cryptor/StreamCryptor.swift | 939 +++++ .../Sources/Cryptor/Updatable.swift | 107 + .../Sources/Cryptor/Utilities.swift | 262 ++ Pods/BlueECC/LICENSE.txt | 176 + Pods/BlueECC/README.md | 204 + Pods/BlueECC/Sources/CryptorECC/ASN1.swift | 92 + .../Sources/CryptorECC/Data+Extensions.swift | 39 + .../Sources/CryptorECC/ECDecryptable.swift | 146 + .../Sources/CryptorECC/ECEncryptable.swift | 177 + Pods/BlueECC/Sources/CryptorECC/ECError.swift | 72 + .../Sources/CryptorECC/ECPrivateKey.swift | 474 +++ .../Sources/CryptorECC/ECPublicKey.swift | 167 + .../Sources/CryptorECC/ECSignable.swift | 105 + .../Sources/CryptorECC/ECSignature.swift | 202 + .../Sources/CryptorECC/EllipticCurve.swift | 172 + .../Sources/CryptorECC/SSLPointerTricks.swift | 100 + Pods/BlueRSA/LICENSE | 176 + Pods/BlueRSA/README.md | 318 ++ .../Sources/CryptorRSA/CryptorRSA.swift | 1111 +++++ .../CryptorRSA/CryptorRSAConstants.swift | 74 + .../Sources/CryptorRSA/CryptorRSADigest.swift | 329 ++ .../Sources/CryptorRSA/CryptorRSAErrors.swift | 108 + .../Sources/CryptorRSA/CryptorRSAKey.swift | 932 +++++ .../CryptorRSA/CryptorRSAUtilities.swift | 393 ++ .../Sources/CryptorRSA/Data+Extensions.swift | 38 + .../Sources/CryptorRSA/SSLPointerTricks.swift | 100 + Pods/KituraContracts/LICENSE | 201 + Pods/KituraContracts/README.md | 87 + .../Sources/KituraContracts/BodyDecoder.swift | 28 + .../Sources/KituraContracts/BodyEncoder.swift | 28 + .../Sources/KituraContracts/BodyFormat.swift | 78 + .../KituraContracts/ClosureAliases.swift | 260 ++ .../KituraContracts/CodableQuery/Coder.swift | 68 + .../CodableQuery/Extensions.swift | 353 ++ .../CodableQuery/QueryDecoder.swift | 408 ++ .../CodableQuery/QueryEncoder.swift | 522 +++ .../Sources/KituraContracts/Contracts.swift | 1550 +++++++ Pods/Local Podspecs/Alamofire.podspec.json | 34 + Pods/Local Podspecs/SwiftyBeaver.podspec.json | 36 + Pods/LoggerAPI/LICENSE.txt | 177 + Pods/LoggerAPI/README.md | 94 + Pods/LoggerAPI/Sources/LoggerAPI/Logger.swift | 347 ++ Pods/Logging/LICENSE.txt | 202 + Pods/Logging/README.md | 276 ++ Pods/Logging/Sources/Logging/Locks.swift | 257 ++ Pods/Logging/Sources/Logging/LogHandler.swift | 188 + Pods/Logging/Sources/Logging/Logging.swift | 826 ++++ Pods/Manifest.lock | 65 + Pods/Pods.xcodeproj/project.pbxproj | 3641 +++++++++++++++++ .../xcschemes/Alamofire-Alamofire.xcscheme | 58 + .../xcschemes/Alamofire.xcscheme | 58 + .../xcschemes/BlueCryptor.xcscheme | 58 + .../xcschemes/BlueECC.xcscheme | 58 + .../xcschemes/BlueRSA.xcscheme | 58 + .../xcschemes/KituraContracts.xcscheme | 58 + .../xcschemes/LoggerAPI.xcscheme | 58 + .../xcschemes/Logging.xcscheme | 58 + .../Pods-AIGrammar-AIGrammarUITests.xcscheme | 58 + .../xcschemes/Pods-AIGrammar.xcscheme | 58 + .../xcschemes/Pods-AIGrammarTests.xcscheme | 58 + .../xcschemes/SwiftJWT.xcscheme | 58 + .../SwiftyBeaver-SwiftyBeaver.xcscheme | 58 + .../xcschemes/SwiftyBeaver.xcscheme | 58 + .../xcschemes/TrustDecision.xcscheme | 58 + .../xcschemes/xcschememanagement.plist | 86 + Pods/SwiftJWT/LICENSE | 177 + Pods/SwiftJWT/README.md | 245 ++ .../SwiftJWT/Sources/SwiftJWT/BlueECDSA.swift | 110 + Pods/SwiftJWT/Sources/SwiftJWT/BlueHMAC.swift | 85 + Pods/SwiftJWT/Sources/SwiftJWT/BlueRSA.swift | 128 + Pods/SwiftJWT/Sources/SwiftJWT/Claims.swift | 84 + .../ClaimsExamples/ClaimsMicroProfile.swift | 85 + .../ClaimsExamples/ClaimsOpenID.swift | 200 + .../ClaimsExamples/ClaimsStandardJWT.swift | 108 + .../SwiftJWT/Data+Base64URLEncoded.swift | 50 + Pods/SwiftJWT/Sources/SwiftJWT/Header.swift | 102 + Pods/SwiftJWT/Sources/SwiftJWT/JWT.swift | 139 + .../Sources/SwiftJWT/JWTDecoder.swift | 286 ++ .../Sources/SwiftJWT/JWTEncoder.swift | 234 ++ Pods/SwiftJWT/Sources/SwiftJWT/JWTError.swift | 66 + .../SwiftJWT/Sources/SwiftJWT/JWTSigner.swift | 147 + .../Sources/SwiftJWT/JWTVerifier.swift | 152 + .../Sources/SwiftJWT/NoneAlgorithm.swift | 32 + .../Sources/SwiftJWT/RSAKeyType.swift | 29 + .../Sources/SwiftJWT/SignerAlgorithm.swift | 20 + .../SwiftJWT/ValidateClaimsResult.swift | 52 + .../Sources/SwiftJWT/VerifierAlgorithm.swift | 21 + Pods/SwiftyBeaver/LICENSE | 21 + Pods/SwiftyBeaver/PrivacyInfo.xcprivacy | 27 + Pods/SwiftyBeaver/README.md | 249 ++ Pods/SwiftyBeaver/Sources/Base64.swift | 165 + .../Sources/BaseDestination.swift | 529 +++ .../Sources/ConsoleDestination.swift | 130 + Pods/SwiftyBeaver/Sources/Extensions.swift | 35 + .../Sources/FileDestination.swift | 240 ++ Pods/SwiftyBeaver/Sources/Filter.swift | 283 ++ .../Sources/FilterValidator.swift | 129 + .../Sources/GoogleCloudDestination.swift | 99 + Pods/SwiftyBeaver/Sources/SwiftyBeaver.swift | 246 ++ .../Alamofire/Alamofire-Info.plist | 26 + .../Alamofire/Alamofire-dummy.m | 5 + .../Alamofire/Alamofire-prefix.pch | 12 + .../Alamofire/Alamofire-umbrella.h | 16 + .../Alamofire/Alamofire.debug.xcconfig | 15 + .../Alamofire/Alamofire.modulemap | 6 + .../Alamofire/Alamofire.release.xcconfig | 15 + ...ourceBundle-Alamofire-Alamofire-Info.plist | 24 + .../BlueCryptor/BlueCryptor-Info.plist | 26 + .../BlueCryptor/BlueCryptor-dummy.m | 5 + .../BlueCryptor/BlueCryptor-prefix.pch | 12 + .../BlueCryptor/BlueCryptor-umbrella.h | 16 + .../BlueCryptor/BlueCryptor.debug.xcconfig | 15 + .../BlueCryptor/BlueCryptor.modulemap | 6 + .../BlueCryptor/BlueCryptor.release.xcconfig | 15 + .../BlueECC/BlueECC-Info.plist | 26 + .../BlueECC/BlueECC-dummy.m | 5 + .../BlueECC/BlueECC-prefix.pch | 12 + .../BlueECC/BlueECC-umbrella.h | 16 + .../BlueECC/BlueECC.debug.xcconfig | 14 + .../BlueECC/BlueECC.modulemap | 6 + .../BlueECC/BlueECC.release.xcconfig | 14 + .../BlueRSA/BlueRSA-Info.plist | 26 + .../BlueRSA/BlueRSA-dummy.m | 5 + .../BlueRSA/BlueRSA-prefix.pch | 12 + .../BlueRSA/BlueRSA-umbrella.h | 16 + .../BlueRSA/BlueRSA.debug.xcconfig | 15 + .../BlueRSA/BlueRSA.modulemap | 6 + .../BlueRSA/BlueRSA.release.xcconfig | 15 + .../KituraContracts-Info.plist | 26 + .../KituraContracts/KituraContracts-dummy.m | 5 + .../KituraContracts-prefix.pch | 12 + .../KituraContracts-umbrella.h | 16 + .../KituraContracts.debug.xcconfig | 16 + .../KituraContracts/KituraContracts.modulemap | 6 + .../KituraContracts.release.xcconfig | 16 + .../LoggerAPI/LoggerAPI-Info.plist | 26 + .../LoggerAPI/LoggerAPI-dummy.m | 5 + .../LoggerAPI/LoggerAPI-prefix.pch | 12 + .../LoggerAPI/LoggerAPI-umbrella.h | 16 + .../LoggerAPI/LoggerAPI.debug.xcconfig | 16 + .../LoggerAPI/LoggerAPI.modulemap | 6 + .../LoggerAPI/LoggerAPI.release.xcconfig | 16 + .../Logging/Logging-Info.plist | 26 + .../Logging/Logging-dummy.m | 5 + .../Logging/Logging-prefix.pch | 12 + .../Logging/Logging-umbrella.h | 16 + .../Logging/Logging.debug.xcconfig | 14 + .../Logging/Logging.modulemap | 6 + .../Logging/Logging.release.xcconfig | 14 + ...Pods-AIGrammar-AIGrammarUITests-Info.plist | 26 + ...AIGrammarUITests-acknowledgements.markdown | 1389 +++++++ ...ar-AIGrammarUITests-acknowledgements.plist | 1475 +++++++ .../Pods-AIGrammar-AIGrammarUITests-dummy.m | 5 + ...ts-frameworks-Debug-input-files.xcfilelist | 10 + ...s-frameworks-Debug-output-files.xcfilelist | 9 + ...-frameworks-Release-input-files.xcfilelist | 10 + ...frameworks-Release-output-files.xcfilelist | 9 + ...s-AIGrammar-AIGrammarUITests-frameworks.sh | 202 + ...Pods-AIGrammar-AIGrammarUITests-umbrella.h | 16 + ...-AIGrammar-AIGrammarUITests.debug.xcconfig | 15 + .../Pods-AIGrammar-AIGrammarUITests.modulemap | 6 + ...IGrammar-AIGrammarUITests.release.xcconfig | 15 + .../Pods-AIGrammar/Pods-AIGrammar-Info.plist | 26 + .../Pods-AIGrammar-acknowledgements.markdown | 1389 +++++++ .../Pods-AIGrammar-acknowledgements.plist | 1475 +++++++ .../Pods-AIGrammar/Pods-AIGrammar-dummy.m | 5 + ...ar-frameworks-Debug-input-files.xcfilelist | 10 + ...r-frameworks-Debug-output-files.xcfilelist | 9 + ...-frameworks-Release-input-files.xcfilelist | 10 + ...frameworks-Release-output-files.xcfilelist | 9 + .../Pods-AIGrammar-frameworks.sh | 202 + .../Pods-AIGrammar/Pods-AIGrammar-umbrella.h | 16 + .../Pods-AIGrammar.debug.xcconfig | 15 + .../Pods-AIGrammar/Pods-AIGrammar.modulemap | 6 + .../Pods-AIGrammar.release.xcconfig | 15 + .../Pods-AIGrammarTests-Info.plist | 26 + ...s-AIGrammarTests-acknowledgements.markdown | 3 + ...Pods-AIGrammarTests-acknowledgements.plist | 29 + .../Pods-AIGrammarTests-dummy.m | 5 + .../Pods-AIGrammarTests-umbrella.h | 16 + .../Pods-AIGrammarTests.debug.xcconfig | 11 + .../Pods-AIGrammarTests.modulemap | 6 + .../Pods-AIGrammarTests.release.xcconfig | 11 + .../SwiftJWT/SwiftJWT-Info.plist | 26 + .../SwiftJWT/SwiftJWT-dummy.m | 5 + .../SwiftJWT/SwiftJWT-prefix.pch | 12 + .../SwiftJWT/SwiftJWT-umbrella.h | 16 + .../SwiftJWT/SwiftJWT.debug.xcconfig | 16 + .../SwiftJWT/SwiftJWT.modulemap | 6 + .../SwiftJWT/SwiftJWT.release.xcconfig | 16 + ...undle-SwiftyBeaver-SwiftyBeaver-Info.plist | 24 + .../SwiftyBeaver/SwiftyBeaver-Info.plist | 26 + .../SwiftyBeaver/SwiftyBeaver-dummy.m | 5 + .../SwiftyBeaver/SwiftyBeaver-prefix.pch | 12 + .../SwiftyBeaver/SwiftyBeaver-umbrella.h | 16 + .../SwiftyBeaver/SwiftyBeaver.debug.xcconfig | 14 + .../SwiftyBeaver/SwiftyBeaver.modulemap | 6 + .../SwiftyBeaver.release.xcconfig | 14 + .../TrustDecision/TrustDecision-Info.plist | 26 + .../TrustDecision/TrustDecision-dummy.m | 5 + .../TrustDecision/TrustDecision-prefix.pch | 12 + .../TrustDecision/TrustDecision-umbrella.h | 18 + .../TrustDecision.debug.xcconfig | 12 + .../TrustDecision/TrustDecision.modulemap | 6 + .../TrustDecision.release.xcconfig | 12 + Pods/TrustDecision/LICENSE | 21 + Pods/TrustDecision/README.md | 268 ++ .../Collect/Info/Base/TDMobRiskBaseInfo.h | 13 + .../Collect/Info/Base/TDMobRiskBaseInfo.m | 46 + .../Classes/Collect/Info/TDMobRiskAppInfo.h | 24 + .../Classes/Collect/Info/TDMobRiskAppInfo.m | 126 + .../Classes/Collect/Info/TDMobRiskCpuInfo.h | 14 + .../Classes/Collect/Info/TDMobRiskCpuInfo.m | 20 + .../Collect/Info/TDMobRiskDeviceInfo.h | 32 + .../Collect/Info/TDMobRiskDeviceInfo.m | 95 + .../Collect/Info/TDMobRiskDeviceStatusInfo.h | 22 + .../Collect/Info/TDMobRiskDeviceStatusInfo.m | 160 + .../Collect/Info/TDMobRiskIdentifierInfo.h | 17 + .../Collect/Info/TDMobRiskIdentifierInfo.m | 37 + .../Classes/Collect/Info/TDMobRiskOSInfo.h | 20 + .../Classes/Collect/Info/TDMobRiskOSInfo.m | 40 + .../Classes/Collect/Info/TDMobRiskSpaceInfo.h | 22 + .../Classes/Collect/Info/TDMobRiskSpaceInfo.m | 62 + .../Classes/Collect/Info/TDMobRiskTimeInfo.h | 16 + .../Classes/Collect/Info/TDMobRiskTimeInfo.m | 34 + .../Classes/Collect/TDMobRiskCollector.h | 14 + .../Classes/Collect/TDMobRiskCollector.m | 62 + .../Containers/TDMobRiskSafeDictionary.h | 12 + .../Containers/TDMobRiskSafeDictionary.m | 225 + .../Classes/Core/TDMobRiskCalculator.h | 13 + .../Classes/Core/TDMobRiskCalculator.m | 24 + .../Classes/Core/TDMobRiskIdCalculator.h | 13 + .../Classes/Core/TDMobRiskIdCalculator.m | 21 + .../Classes/Helper/TDMobRiskAPIHelper.h | 13 + .../Classes/Helper/TDMobRiskAPIHelper.m | 40 + .../Classes/Helper/TDMobRiskEncodeHelper.h | 13 + .../Classes/Helper/TDMobRiskEncodeHelper.m | 30 + .../Classes/Helper/TDMobRiskKeychainsHelper.h | 14 + .../Classes/Helper/TDMobRiskKeychainsHelper.m | 78 + .../TrustDecision/Classes/TDMobRisk.h | 17 + .../TrustDecision/Classes/TDMobRiskHeader.h | 15 + .../TrustDecision/Classes/TDMobRiskManager.h | 31 + .../TrustDecision/Classes/TDMobRiskManager.m | 102 + 331 files changed, 53210 insertions(+), 130 deletions(-) rename "\t\t.xcodeproj/project.pbxproj" => AIGrammar.xcodeproj/project.pbxproj (98%) rename "\t\t.xcodeproj/project.xcworkspace/contents.xcworkspacedata" => AIGrammar.xcodeproj/project.xcworkspace/contents.xcworkspacedata (100%) rename "\t\t.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist" => AIGrammar.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist (100%) rename "\t\t.xcodeproj/xcshareddata/xcschemes/AIGrammar.xcscheme" => AIGrammar.xcodeproj/xcshareddata/xcschemes/AIGrammar.xcscheme (100%) rename "\t\t.xcodeproj/xcuserdata/oscar.xcuserdatad/xcschemes/xcschememanagement.plist" => AIGrammar.xcodeproj/xcuserdata/oscar.xcuserdatad/xcschemes/xcschememanagement.plist (100%) create mode 100644 AIGrammar.xcworkspace/contents.xcworkspacedata create mode 100644 AIGrammar.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 AIGrammar.xcworkspace/xcshareddata/swiftpm/Package.resolved create mode 100644 Podfile create mode 100644 Podfile.lock create mode 100644 Pods/Alamofire/LICENSE create mode 100644 Pods/Alamofire/README.md create mode 100644 Pods/Alamofire/Source/Alamofire.swift create mode 100644 Pods/Alamofire/Source/Core/AFError.swift create mode 100644 Pods/Alamofire/Source/Core/DataRequest.swift create mode 100644 Pods/Alamofire/Source/Core/DataStreamRequest.swift create mode 100644 Pods/Alamofire/Source/Core/DownloadRequest.swift create mode 100644 Pods/Alamofire/Source/Core/HTTPHeaders.swift create mode 100644 Pods/Alamofire/Source/Core/HTTPMethod.swift create mode 100644 Pods/Alamofire/Source/Core/Notifications.swift create mode 100644 Pods/Alamofire/Source/Core/ParameterEncoder.swift create mode 100644 Pods/Alamofire/Source/Core/ParameterEncoding.swift create mode 100644 Pods/Alamofire/Source/Core/Protected.swift create mode 100644 Pods/Alamofire/Source/Core/Request.swift create mode 100644 Pods/Alamofire/Source/Core/RequestTaskMap.swift create mode 100644 Pods/Alamofire/Source/Core/Response.swift create mode 100644 Pods/Alamofire/Source/Core/Session.swift create mode 100644 Pods/Alamofire/Source/Core/SessionDelegate.swift create mode 100644 Pods/Alamofire/Source/Core/URLConvertible+URLRequestConvertible.swift create mode 100644 Pods/Alamofire/Source/Core/UploadRequest.swift create mode 100644 Pods/Alamofire/Source/Core/WebSocketRequest.swift create mode 100644 Pods/Alamofire/Source/Extensions/DispatchQueue+Alamofire.swift create mode 100644 Pods/Alamofire/Source/Extensions/OperationQueue+Alamofire.swift create mode 100644 Pods/Alamofire/Source/Extensions/Result+Alamofire.swift create mode 100644 Pods/Alamofire/Source/Extensions/StringEncoding+Alamofire.swift create mode 100644 Pods/Alamofire/Source/Extensions/URLRequest+Alamofire.swift create mode 100644 Pods/Alamofire/Source/Extensions/URLSessionConfiguration+Alamofire.swift create mode 100644 Pods/Alamofire/Source/Features/AlamofireExtended.swift create mode 100644 Pods/Alamofire/Source/Features/AuthenticationInterceptor.swift create mode 100644 Pods/Alamofire/Source/Features/CachedResponseHandler.swift create mode 100644 Pods/Alamofire/Source/Features/Combine.swift create mode 100644 Pods/Alamofire/Source/Features/Concurrency.swift create mode 100644 Pods/Alamofire/Source/Features/EventMonitor.swift create mode 100644 Pods/Alamofire/Source/Features/MultipartFormData.swift create mode 100644 Pods/Alamofire/Source/Features/MultipartUpload.swift create mode 100644 Pods/Alamofire/Source/Features/NetworkReachabilityManager.swift create mode 100644 Pods/Alamofire/Source/Features/RedirectHandler.swift create mode 100644 Pods/Alamofire/Source/Features/RequestCompression.swift create mode 100644 Pods/Alamofire/Source/Features/RequestInterceptor.swift create mode 100644 Pods/Alamofire/Source/Features/ResponseSerialization.swift create mode 100644 Pods/Alamofire/Source/Features/RetryPolicy.swift create mode 100644 Pods/Alamofire/Source/Features/ServerTrustEvaluation.swift create mode 100644 Pods/Alamofire/Source/Features/URLEncodedFormEncoder.swift create mode 100644 Pods/Alamofire/Source/Features/Validation.swift create mode 100644 Pods/Alamofire/Source/PrivacyInfo.xcprivacy create mode 100644 Pods/BlueCryptor/LICENSE create mode 100644 Pods/BlueCryptor/README.md create mode 100755 Pods/BlueCryptor/Sources/Cryptor/Crypto.swift create mode 100755 Pods/BlueCryptor/Sources/Cryptor/Cryptor.swift create mode 100755 Pods/BlueCryptor/Sources/Cryptor/Digest.swift create mode 100755 Pods/BlueCryptor/Sources/Cryptor/HMAC.swift create mode 100755 Pods/BlueCryptor/Sources/Cryptor/KeyDerivation.swift create mode 100755 Pods/BlueCryptor/Sources/Cryptor/Random.swift create mode 100644 Pods/BlueCryptor/Sources/Cryptor/SSLPointerTricks.swift create mode 100755 Pods/BlueCryptor/Sources/Cryptor/Status.swift create mode 100755 Pods/BlueCryptor/Sources/Cryptor/StreamCryptor.swift create mode 100755 Pods/BlueCryptor/Sources/Cryptor/Updatable.swift create mode 100755 Pods/BlueCryptor/Sources/Cryptor/Utilities.swift create mode 100644 Pods/BlueECC/LICENSE.txt create mode 100644 Pods/BlueECC/README.md create mode 100644 Pods/BlueECC/Sources/CryptorECC/ASN1.swift create mode 100644 Pods/BlueECC/Sources/CryptorECC/Data+Extensions.swift create mode 100644 Pods/BlueECC/Sources/CryptorECC/ECDecryptable.swift create mode 100644 Pods/BlueECC/Sources/CryptorECC/ECEncryptable.swift create mode 100644 Pods/BlueECC/Sources/CryptorECC/ECError.swift create mode 100644 Pods/BlueECC/Sources/CryptorECC/ECPrivateKey.swift create mode 100644 Pods/BlueECC/Sources/CryptorECC/ECPublicKey.swift create mode 100644 Pods/BlueECC/Sources/CryptorECC/ECSignable.swift create mode 100644 Pods/BlueECC/Sources/CryptorECC/ECSignature.swift create mode 100644 Pods/BlueECC/Sources/CryptorECC/EllipticCurve.swift create mode 100644 Pods/BlueECC/Sources/CryptorECC/SSLPointerTricks.swift create mode 100644 Pods/BlueRSA/LICENSE create mode 100644 Pods/BlueRSA/README.md create mode 100644 Pods/BlueRSA/Sources/CryptorRSA/CryptorRSA.swift create mode 100644 Pods/BlueRSA/Sources/CryptorRSA/CryptorRSAConstants.swift create mode 100644 Pods/BlueRSA/Sources/CryptorRSA/CryptorRSADigest.swift create mode 100644 Pods/BlueRSA/Sources/CryptorRSA/CryptorRSAErrors.swift create mode 100644 Pods/BlueRSA/Sources/CryptorRSA/CryptorRSAKey.swift create mode 100644 Pods/BlueRSA/Sources/CryptorRSA/CryptorRSAUtilities.swift create mode 100644 Pods/BlueRSA/Sources/CryptorRSA/Data+Extensions.swift create mode 100644 Pods/BlueRSA/Sources/CryptorRSA/SSLPointerTricks.swift create mode 100644 Pods/KituraContracts/LICENSE create mode 100644 Pods/KituraContracts/README.md create mode 100644 Pods/KituraContracts/Sources/KituraContracts/BodyDecoder.swift create mode 100644 Pods/KituraContracts/Sources/KituraContracts/BodyEncoder.swift create mode 100644 Pods/KituraContracts/Sources/KituraContracts/BodyFormat.swift create mode 100644 Pods/KituraContracts/Sources/KituraContracts/ClosureAliases.swift create mode 100644 Pods/KituraContracts/Sources/KituraContracts/CodableQuery/Coder.swift create mode 100644 Pods/KituraContracts/Sources/KituraContracts/CodableQuery/Extensions.swift create mode 100644 Pods/KituraContracts/Sources/KituraContracts/CodableQuery/QueryDecoder.swift create mode 100644 Pods/KituraContracts/Sources/KituraContracts/CodableQuery/QueryEncoder.swift create mode 100644 Pods/KituraContracts/Sources/KituraContracts/Contracts.swift create mode 100644 Pods/Local Podspecs/Alamofire.podspec.json create mode 100644 Pods/Local Podspecs/SwiftyBeaver.podspec.json create mode 100644 Pods/LoggerAPI/LICENSE.txt create mode 100644 Pods/LoggerAPI/README.md create mode 100644 Pods/LoggerAPI/Sources/LoggerAPI/Logger.swift create mode 100644 Pods/Logging/LICENSE.txt create mode 100644 Pods/Logging/README.md create mode 100644 Pods/Logging/Sources/Logging/Locks.swift create mode 100644 Pods/Logging/Sources/Logging/LogHandler.swift create mode 100644 Pods/Logging/Sources/Logging/Logging.swift create mode 100644 Pods/Manifest.lock create mode 100644 Pods/Pods.xcodeproj/project.pbxproj create mode 100644 Pods/Pods.xcodeproj/xcuserdata/oscar.xcuserdatad/xcschemes/Alamofire-Alamofire.xcscheme create mode 100644 Pods/Pods.xcodeproj/xcuserdata/oscar.xcuserdatad/xcschemes/Alamofire.xcscheme create mode 100644 Pods/Pods.xcodeproj/xcuserdata/oscar.xcuserdatad/xcschemes/BlueCryptor.xcscheme create mode 100644 Pods/Pods.xcodeproj/xcuserdata/oscar.xcuserdatad/xcschemes/BlueECC.xcscheme create mode 100644 Pods/Pods.xcodeproj/xcuserdata/oscar.xcuserdatad/xcschemes/BlueRSA.xcscheme create mode 100644 Pods/Pods.xcodeproj/xcuserdata/oscar.xcuserdatad/xcschemes/KituraContracts.xcscheme create mode 100644 Pods/Pods.xcodeproj/xcuserdata/oscar.xcuserdatad/xcschemes/LoggerAPI.xcscheme create mode 100644 Pods/Pods.xcodeproj/xcuserdata/oscar.xcuserdatad/xcschemes/Logging.xcscheme create mode 100644 Pods/Pods.xcodeproj/xcuserdata/oscar.xcuserdatad/xcschemes/Pods-AIGrammar-AIGrammarUITests.xcscheme create mode 100644 Pods/Pods.xcodeproj/xcuserdata/oscar.xcuserdatad/xcschemes/Pods-AIGrammar.xcscheme create mode 100644 Pods/Pods.xcodeproj/xcuserdata/oscar.xcuserdatad/xcschemes/Pods-AIGrammarTests.xcscheme create mode 100644 Pods/Pods.xcodeproj/xcuserdata/oscar.xcuserdatad/xcschemes/SwiftJWT.xcscheme create mode 100644 Pods/Pods.xcodeproj/xcuserdata/oscar.xcuserdatad/xcschemes/SwiftyBeaver-SwiftyBeaver.xcscheme create mode 100644 Pods/Pods.xcodeproj/xcuserdata/oscar.xcuserdatad/xcschemes/SwiftyBeaver.xcscheme create mode 100644 Pods/Pods.xcodeproj/xcuserdata/oscar.xcuserdatad/xcschemes/TrustDecision.xcscheme create mode 100644 Pods/Pods.xcodeproj/xcuserdata/oscar.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100644 Pods/SwiftJWT/LICENSE create mode 100644 Pods/SwiftJWT/README.md create mode 100644 Pods/SwiftJWT/Sources/SwiftJWT/BlueECDSA.swift create mode 100644 Pods/SwiftJWT/Sources/SwiftJWT/BlueHMAC.swift create mode 100644 Pods/SwiftJWT/Sources/SwiftJWT/BlueRSA.swift create mode 100644 Pods/SwiftJWT/Sources/SwiftJWT/Claims.swift create mode 100644 Pods/SwiftJWT/Sources/SwiftJWT/ClaimsExamples/ClaimsMicroProfile.swift create mode 100644 Pods/SwiftJWT/Sources/SwiftJWT/ClaimsExamples/ClaimsOpenID.swift create mode 100644 Pods/SwiftJWT/Sources/SwiftJWT/ClaimsExamples/ClaimsStandardJWT.swift create mode 100644 Pods/SwiftJWT/Sources/SwiftJWT/Data+Base64URLEncoded.swift create mode 100644 Pods/SwiftJWT/Sources/SwiftJWT/Header.swift create mode 100644 Pods/SwiftJWT/Sources/SwiftJWT/JWT.swift create mode 100644 Pods/SwiftJWT/Sources/SwiftJWT/JWTDecoder.swift create mode 100644 Pods/SwiftJWT/Sources/SwiftJWT/JWTEncoder.swift create mode 100644 Pods/SwiftJWT/Sources/SwiftJWT/JWTError.swift create mode 100644 Pods/SwiftJWT/Sources/SwiftJWT/JWTSigner.swift create mode 100644 Pods/SwiftJWT/Sources/SwiftJWT/JWTVerifier.swift create mode 100644 Pods/SwiftJWT/Sources/SwiftJWT/NoneAlgorithm.swift create mode 100644 Pods/SwiftJWT/Sources/SwiftJWT/RSAKeyType.swift create mode 100644 Pods/SwiftJWT/Sources/SwiftJWT/SignerAlgorithm.swift create mode 100644 Pods/SwiftJWT/Sources/SwiftJWT/ValidateClaimsResult.swift create mode 100644 Pods/SwiftJWT/Sources/SwiftJWT/VerifierAlgorithm.swift create mode 100644 Pods/SwiftyBeaver/LICENSE create mode 100644 Pods/SwiftyBeaver/PrivacyInfo.xcprivacy create mode 100644 Pods/SwiftyBeaver/README.md create mode 100644 Pods/SwiftyBeaver/Sources/Base64.swift create mode 100644 Pods/SwiftyBeaver/Sources/BaseDestination.swift create mode 100644 Pods/SwiftyBeaver/Sources/ConsoleDestination.swift create mode 100644 Pods/SwiftyBeaver/Sources/Extensions.swift create mode 100644 Pods/SwiftyBeaver/Sources/FileDestination.swift create mode 100644 Pods/SwiftyBeaver/Sources/Filter.swift create mode 100644 Pods/SwiftyBeaver/Sources/FilterValidator.swift create mode 100644 Pods/SwiftyBeaver/Sources/GoogleCloudDestination.swift create mode 100644 Pods/SwiftyBeaver/Sources/SwiftyBeaver.swift create mode 100644 Pods/Target Support Files/Alamofire/Alamofire-Info.plist create mode 100644 Pods/Target Support Files/Alamofire/Alamofire-dummy.m create mode 100644 Pods/Target Support Files/Alamofire/Alamofire-prefix.pch create mode 100644 Pods/Target Support Files/Alamofire/Alamofire-umbrella.h create mode 100644 Pods/Target Support Files/Alamofire/Alamofire.debug.xcconfig create mode 100644 Pods/Target Support Files/Alamofire/Alamofire.modulemap create mode 100644 Pods/Target Support Files/Alamofire/Alamofire.release.xcconfig create mode 100644 Pods/Target Support Files/Alamofire/ResourceBundle-Alamofire-Alamofire-Info.plist create mode 100644 Pods/Target Support Files/BlueCryptor/BlueCryptor-Info.plist create mode 100644 Pods/Target Support Files/BlueCryptor/BlueCryptor-dummy.m create mode 100644 Pods/Target Support Files/BlueCryptor/BlueCryptor-prefix.pch create mode 100644 Pods/Target Support Files/BlueCryptor/BlueCryptor-umbrella.h create mode 100644 Pods/Target Support Files/BlueCryptor/BlueCryptor.debug.xcconfig create mode 100644 Pods/Target Support Files/BlueCryptor/BlueCryptor.modulemap create mode 100644 Pods/Target Support Files/BlueCryptor/BlueCryptor.release.xcconfig create mode 100644 Pods/Target Support Files/BlueECC/BlueECC-Info.plist create mode 100644 Pods/Target Support Files/BlueECC/BlueECC-dummy.m create mode 100644 Pods/Target Support Files/BlueECC/BlueECC-prefix.pch create mode 100644 Pods/Target Support Files/BlueECC/BlueECC-umbrella.h create mode 100644 Pods/Target Support Files/BlueECC/BlueECC.debug.xcconfig create mode 100644 Pods/Target Support Files/BlueECC/BlueECC.modulemap create mode 100644 Pods/Target Support Files/BlueECC/BlueECC.release.xcconfig create mode 100644 Pods/Target Support Files/BlueRSA/BlueRSA-Info.plist create mode 100644 Pods/Target Support Files/BlueRSA/BlueRSA-dummy.m create mode 100644 Pods/Target Support Files/BlueRSA/BlueRSA-prefix.pch create mode 100644 Pods/Target Support Files/BlueRSA/BlueRSA-umbrella.h create mode 100644 Pods/Target Support Files/BlueRSA/BlueRSA.debug.xcconfig create mode 100644 Pods/Target Support Files/BlueRSA/BlueRSA.modulemap create mode 100644 Pods/Target Support Files/BlueRSA/BlueRSA.release.xcconfig create mode 100644 Pods/Target Support Files/KituraContracts/KituraContracts-Info.plist create mode 100644 Pods/Target Support Files/KituraContracts/KituraContracts-dummy.m create mode 100644 Pods/Target Support Files/KituraContracts/KituraContracts-prefix.pch create mode 100644 Pods/Target Support Files/KituraContracts/KituraContracts-umbrella.h create mode 100644 Pods/Target Support Files/KituraContracts/KituraContracts.debug.xcconfig create mode 100644 Pods/Target Support Files/KituraContracts/KituraContracts.modulemap create mode 100644 Pods/Target Support Files/KituraContracts/KituraContracts.release.xcconfig create mode 100644 Pods/Target Support Files/LoggerAPI/LoggerAPI-Info.plist create mode 100644 Pods/Target Support Files/LoggerAPI/LoggerAPI-dummy.m create mode 100644 Pods/Target Support Files/LoggerAPI/LoggerAPI-prefix.pch create mode 100644 Pods/Target Support Files/LoggerAPI/LoggerAPI-umbrella.h create mode 100644 Pods/Target Support Files/LoggerAPI/LoggerAPI.debug.xcconfig create mode 100644 Pods/Target Support Files/LoggerAPI/LoggerAPI.modulemap create mode 100644 Pods/Target Support Files/LoggerAPI/LoggerAPI.release.xcconfig create mode 100644 Pods/Target Support Files/Logging/Logging-Info.plist create mode 100644 Pods/Target Support Files/Logging/Logging-dummy.m create mode 100644 Pods/Target Support Files/Logging/Logging-prefix.pch create mode 100644 Pods/Target Support Files/Logging/Logging-umbrella.h create mode 100644 Pods/Target Support Files/Logging/Logging.debug.xcconfig create mode 100644 Pods/Target Support Files/Logging/Logging.modulemap create mode 100644 Pods/Target Support Files/Logging/Logging.release.xcconfig create mode 100644 Pods/Target Support Files/Pods-AIGrammar-AIGrammarUITests/Pods-AIGrammar-AIGrammarUITests-Info.plist create mode 100644 Pods/Target Support Files/Pods-AIGrammar-AIGrammarUITests/Pods-AIGrammar-AIGrammarUITests-acknowledgements.markdown create mode 100644 Pods/Target Support Files/Pods-AIGrammar-AIGrammarUITests/Pods-AIGrammar-AIGrammarUITests-acknowledgements.plist create mode 100644 Pods/Target Support Files/Pods-AIGrammar-AIGrammarUITests/Pods-AIGrammar-AIGrammarUITests-dummy.m create mode 100644 Pods/Target Support Files/Pods-AIGrammar-AIGrammarUITests/Pods-AIGrammar-AIGrammarUITests-frameworks-Debug-input-files.xcfilelist create mode 100644 Pods/Target Support Files/Pods-AIGrammar-AIGrammarUITests/Pods-AIGrammar-AIGrammarUITests-frameworks-Debug-output-files.xcfilelist create mode 100644 Pods/Target Support Files/Pods-AIGrammar-AIGrammarUITests/Pods-AIGrammar-AIGrammarUITests-frameworks-Release-input-files.xcfilelist create mode 100644 Pods/Target Support Files/Pods-AIGrammar-AIGrammarUITests/Pods-AIGrammar-AIGrammarUITests-frameworks-Release-output-files.xcfilelist create mode 100755 Pods/Target Support Files/Pods-AIGrammar-AIGrammarUITests/Pods-AIGrammar-AIGrammarUITests-frameworks.sh create mode 100644 Pods/Target Support Files/Pods-AIGrammar-AIGrammarUITests/Pods-AIGrammar-AIGrammarUITests-umbrella.h create mode 100644 Pods/Target Support Files/Pods-AIGrammar-AIGrammarUITests/Pods-AIGrammar-AIGrammarUITests.debug.xcconfig create mode 100644 Pods/Target Support Files/Pods-AIGrammar-AIGrammarUITests/Pods-AIGrammar-AIGrammarUITests.modulemap create mode 100644 Pods/Target Support Files/Pods-AIGrammar-AIGrammarUITests/Pods-AIGrammar-AIGrammarUITests.release.xcconfig create mode 100644 Pods/Target Support Files/Pods-AIGrammar/Pods-AIGrammar-Info.plist create mode 100644 Pods/Target Support Files/Pods-AIGrammar/Pods-AIGrammar-acknowledgements.markdown create mode 100644 Pods/Target Support Files/Pods-AIGrammar/Pods-AIGrammar-acknowledgements.plist create mode 100644 Pods/Target Support Files/Pods-AIGrammar/Pods-AIGrammar-dummy.m create mode 100644 Pods/Target Support Files/Pods-AIGrammar/Pods-AIGrammar-frameworks-Debug-input-files.xcfilelist create mode 100644 Pods/Target Support Files/Pods-AIGrammar/Pods-AIGrammar-frameworks-Debug-output-files.xcfilelist create mode 100644 Pods/Target Support Files/Pods-AIGrammar/Pods-AIGrammar-frameworks-Release-input-files.xcfilelist create mode 100644 Pods/Target Support Files/Pods-AIGrammar/Pods-AIGrammar-frameworks-Release-output-files.xcfilelist create mode 100755 Pods/Target Support Files/Pods-AIGrammar/Pods-AIGrammar-frameworks.sh create mode 100644 Pods/Target Support Files/Pods-AIGrammar/Pods-AIGrammar-umbrella.h create mode 100644 Pods/Target Support Files/Pods-AIGrammar/Pods-AIGrammar.debug.xcconfig create mode 100644 Pods/Target Support Files/Pods-AIGrammar/Pods-AIGrammar.modulemap create mode 100644 Pods/Target Support Files/Pods-AIGrammar/Pods-AIGrammar.release.xcconfig create mode 100644 Pods/Target Support Files/Pods-AIGrammarTests/Pods-AIGrammarTests-Info.plist create mode 100644 Pods/Target Support Files/Pods-AIGrammarTests/Pods-AIGrammarTests-acknowledgements.markdown create mode 100644 Pods/Target Support Files/Pods-AIGrammarTests/Pods-AIGrammarTests-acknowledgements.plist create mode 100644 Pods/Target Support Files/Pods-AIGrammarTests/Pods-AIGrammarTests-dummy.m create mode 100644 Pods/Target Support Files/Pods-AIGrammarTests/Pods-AIGrammarTests-umbrella.h create mode 100644 Pods/Target Support Files/Pods-AIGrammarTests/Pods-AIGrammarTests.debug.xcconfig create mode 100644 Pods/Target Support Files/Pods-AIGrammarTests/Pods-AIGrammarTests.modulemap create mode 100644 Pods/Target Support Files/Pods-AIGrammarTests/Pods-AIGrammarTests.release.xcconfig create mode 100644 Pods/Target Support Files/SwiftJWT/SwiftJWT-Info.plist create mode 100644 Pods/Target Support Files/SwiftJWT/SwiftJWT-dummy.m create mode 100644 Pods/Target Support Files/SwiftJWT/SwiftJWT-prefix.pch create mode 100644 Pods/Target Support Files/SwiftJWT/SwiftJWT-umbrella.h create mode 100644 Pods/Target Support Files/SwiftJWT/SwiftJWT.debug.xcconfig create mode 100644 Pods/Target Support Files/SwiftJWT/SwiftJWT.modulemap create mode 100644 Pods/Target Support Files/SwiftJWT/SwiftJWT.release.xcconfig create mode 100644 Pods/Target Support Files/SwiftyBeaver/ResourceBundle-SwiftyBeaver-SwiftyBeaver-Info.plist create mode 100644 Pods/Target Support Files/SwiftyBeaver/SwiftyBeaver-Info.plist create mode 100644 Pods/Target Support Files/SwiftyBeaver/SwiftyBeaver-dummy.m create mode 100644 Pods/Target Support Files/SwiftyBeaver/SwiftyBeaver-prefix.pch create mode 100644 Pods/Target Support Files/SwiftyBeaver/SwiftyBeaver-umbrella.h create mode 100644 Pods/Target Support Files/SwiftyBeaver/SwiftyBeaver.debug.xcconfig create mode 100644 Pods/Target Support Files/SwiftyBeaver/SwiftyBeaver.modulemap create mode 100644 Pods/Target Support Files/SwiftyBeaver/SwiftyBeaver.release.xcconfig create mode 100644 Pods/Target Support Files/TrustDecision/TrustDecision-Info.plist create mode 100644 Pods/Target Support Files/TrustDecision/TrustDecision-dummy.m create mode 100644 Pods/Target Support Files/TrustDecision/TrustDecision-prefix.pch create mode 100644 Pods/Target Support Files/TrustDecision/TrustDecision-umbrella.h create mode 100644 Pods/Target Support Files/TrustDecision/TrustDecision.debug.xcconfig create mode 100644 Pods/Target Support Files/TrustDecision/TrustDecision.modulemap create mode 100644 Pods/Target Support Files/TrustDecision/TrustDecision.release.xcconfig create mode 100644 Pods/TrustDecision/LICENSE create mode 100644 Pods/TrustDecision/README.md create mode 100644 Pods/TrustDecision/TrustDecision/Classes/Collect/Info/Base/TDMobRiskBaseInfo.h create mode 100644 Pods/TrustDecision/TrustDecision/Classes/Collect/Info/Base/TDMobRiskBaseInfo.m create mode 100644 Pods/TrustDecision/TrustDecision/Classes/Collect/Info/TDMobRiskAppInfo.h create mode 100644 Pods/TrustDecision/TrustDecision/Classes/Collect/Info/TDMobRiskAppInfo.m create mode 100644 Pods/TrustDecision/TrustDecision/Classes/Collect/Info/TDMobRiskCpuInfo.h create mode 100644 Pods/TrustDecision/TrustDecision/Classes/Collect/Info/TDMobRiskCpuInfo.m create mode 100644 Pods/TrustDecision/TrustDecision/Classes/Collect/Info/TDMobRiskDeviceInfo.h create mode 100644 Pods/TrustDecision/TrustDecision/Classes/Collect/Info/TDMobRiskDeviceInfo.m create mode 100644 Pods/TrustDecision/TrustDecision/Classes/Collect/Info/TDMobRiskDeviceStatusInfo.h create mode 100644 Pods/TrustDecision/TrustDecision/Classes/Collect/Info/TDMobRiskDeviceStatusInfo.m create mode 100644 Pods/TrustDecision/TrustDecision/Classes/Collect/Info/TDMobRiskIdentifierInfo.h create mode 100644 Pods/TrustDecision/TrustDecision/Classes/Collect/Info/TDMobRiskIdentifierInfo.m create mode 100644 Pods/TrustDecision/TrustDecision/Classes/Collect/Info/TDMobRiskOSInfo.h create mode 100644 Pods/TrustDecision/TrustDecision/Classes/Collect/Info/TDMobRiskOSInfo.m create mode 100644 Pods/TrustDecision/TrustDecision/Classes/Collect/Info/TDMobRiskSpaceInfo.h create mode 100644 Pods/TrustDecision/TrustDecision/Classes/Collect/Info/TDMobRiskSpaceInfo.m create mode 100644 Pods/TrustDecision/TrustDecision/Classes/Collect/Info/TDMobRiskTimeInfo.h create mode 100644 Pods/TrustDecision/TrustDecision/Classes/Collect/Info/TDMobRiskTimeInfo.m create mode 100644 Pods/TrustDecision/TrustDecision/Classes/Collect/TDMobRiskCollector.h create mode 100644 Pods/TrustDecision/TrustDecision/Classes/Collect/TDMobRiskCollector.m create mode 100644 Pods/TrustDecision/TrustDecision/Classes/Containers/TDMobRiskSafeDictionary.h create mode 100644 Pods/TrustDecision/TrustDecision/Classes/Containers/TDMobRiskSafeDictionary.m create mode 100644 Pods/TrustDecision/TrustDecision/Classes/Core/TDMobRiskCalculator.h create mode 100644 Pods/TrustDecision/TrustDecision/Classes/Core/TDMobRiskCalculator.m create mode 100644 Pods/TrustDecision/TrustDecision/Classes/Core/TDMobRiskIdCalculator.h create mode 100644 Pods/TrustDecision/TrustDecision/Classes/Core/TDMobRiskIdCalculator.m create mode 100644 Pods/TrustDecision/TrustDecision/Classes/Helper/TDMobRiskAPIHelper.h create mode 100644 Pods/TrustDecision/TrustDecision/Classes/Helper/TDMobRiskAPIHelper.m create mode 100644 Pods/TrustDecision/TrustDecision/Classes/Helper/TDMobRiskEncodeHelper.h create mode 100644 Pods/TrustDecision/TrustDecision/Classes/Helper/TDMobRiskEncodeHelper.m create mode 100644 Pods/TrustDecision/TrustDecision/Classes/Helper/TDMobRiskKeychainsHelper.h create mode 100644 Pods/TrustDecision/TrustDecision/Classes/Helper/TDMobRiskKeychainsHelper.m create mode 100644 Pods/TrustDecision/TrustDecision/Classes/TDMobRisk.h create mode 100644 Pods/TrustDecision/TrustDecision/Classes/TDMobRiskHeader.h create mode 100755 Pods/TrustDecision/TrustDecision/Classes/TDMobRiskManager.h create mode 100755 Pods/TrustDecision/TrustDecision/Classes/TDMobRiskManager.m diff --git "a/\t\t.xcodeproj/project.pbxproj" b/AIGrammar.xcodeproj/project.pbxproj similarity index 98% rename from "\t\t.xcodeproj/project.pbxproj" rename to AIGrammar.xcodeproj/project.pbxproj index 86af8ad..5e166d1 100644 --- "a/\t\t.xcodeproj/project.pbxproj" +++ b/AIGrammar.xcodeproj/project.pbxproj @@ -36,6 +36,7 @@ 55D632FA2C0F125D00443894 /* NetworkManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55D632F92C0F125D00443894 /* NetworkManager.swift */; }; 55DAC6552BBA959500BDD4C8 /* ResultView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DAC6542BBA959500BDD4C8 /* ResultView.swift */; }; 55DAC6572BBA984B00BDD4C8 /* InputView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DAC6562BBA984B00BDD4C8 /* InputView.swift */; }; + 55E4E8F52C60CFFC00988503 /* CommFunc.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E4E8F42C60CFFC00988503 /* CommFunc.swift */; }; 55EF5C3C2C2250900060CE47 /* IAPTestView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55EF5C3B2C2250900060CE47 /* IAPTestView.swift */; }; 55EF5C3E2C2263460060CE47 /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 55EF5C3D2C2263460060CE47 /* StoreKit.framework */; }; 67F268687FCD2E4F2F4462C0 /* Pods_AIGrammar.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 12CA1A1A0FE978794C445DBF /* Pods_AIGrammar.framework */; }; @@ -97,6 +98,7 @@ 55D632F92C0F125D00443894 /* NetworkManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkManager.swift; sourceTree = ""; }; 55DAC6542BBA959500BDD4C8 /* ResultView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResultView.swift; sourceTree = ""; }; 55DAC6562BBA984B00BDD4C8 /* InputView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputView.swift; sourceTree = ""; }; + 55E4E8F42C60CFFC00988503 /* CommFunc.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommFunc.swift; sourceTree = ""; }; 55EF5C3B2C2250900060CE47 /* IAPTestView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IAPTestView.swift; sourceTree = ""; }; 55EF5C3D2C2263460060CE47 /* StoreKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = StoreKit.framework; path = System/Library/Frameworks/StoreKit.framework; sourceTree = SDKROOT; }; 5C33D84343808119C7E904A2 /* Pods-AIGrammarTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AIGrammarTests.debug.xcconfig"; path = "Target Support Files/Pods-AIGrammarTests/Pods-AIGrammarTests.debug.xcconfig"; sourceTree = ""; }; @@ -234,6 +236,7 @@ 55D632F92C0F125D00443894 /* NetworkManager.swift */, 559E6D7B2C34EAE700C971B9 /* IapManager.swift */, 559E6D7D2C35355200C971B9 /* LogManager.swift */, + 55E4E8F42C60CFFC00988503 /* CommFunc.swift */, ); path = lib; sourceTree = ""; @@ -536,6 +539,7 @@ 5509CEF12BB54DD10056C5C2 /* Config.swift in Sources */, 55BB127B2BBD653100D2BEA4 /* ShareSheet.swift in Sources */, 5500A3C82BB40ADE0065A1D3 /* SettingsView.swift in Sources */, + 55E4E8F52C60CFFC00988503 /* CommFunc.swift in Sources */, 55DAC6552BBA959500BDD4C8 /* ResultView.swift in Sources */, 5500A3C02BB40A3B0065A1D3 /* AllTabView.swift in Sources */, 559E6D7E2C35355200C971B9 /* LogManager.swift in Sources */, @@ -717,12 +721,13 @@ DEVELOPMENT_TEAM = G8UMWM9TLL; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_CFBundleDisplayName = EasyGrammar; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.education"; INFOPLIST_KEY_NSCameraUsageDescription = "用户拍照获取文本"; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; IPHONEOS_DEPLOYMENT_TARGET = 16.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -731,9 +736,11 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.easyprompts.aigrammar; PRODUCT_NAME = "$(TARGET_NAME)"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; }; name = Debug; }; @@ -750,12 +757,13 @@ DEVELOPMENT_TEAM = G8UMWM9TLL; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_CFBundleDisplayName = EasyGrammar; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.education"; INFOPLIST_KEY_NSCameraUsageDescription = "用户拍照获取文本"; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; IPHONEOS_DEPLOYMENT_TARGET = 16.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -764,9 +772,11 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.easyprompts.aigrammar; PRODUCT_NAME = "$(TARGET_NAME)"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; }; name = Release; }; diff --git "a/\t\t.xcodeproj/project.xcworkspace/contents.xcworkspacedata" b/AIGrammar.xcodeproj/project.xcworkspace/contents.xcworkspacedata similarity index 100% rename from "\t\t.xcodeproj/project.xcworkspace/contents.xcworkspacedata" rename to AIGrammar.xcodeproj/project.xcworkspace/contents.xcworkspacedata diff --git "a/\t\t.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist" b/AIGrammar.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from "\t\t.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist" rename to AIGrammar.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git "a/\t\t.xcodeproj/xcshareddata/xcschemes/AIGrammar.xcscheme" b/AIGrammar.xcodeproj/xcshareddata/xcschemes/AIGrammar.xcscheme similarity index 100% rename from "\t\t.xcodeproj/xcshareddata/xcschemes/AIGrammar.xcscheme" rename to AIGrammar.xcodeproj/xcshareddata/xcschemes/AIGrammar.xcscheme diff --git "a/\t\t.xcodeproj/xcuserdata/oscar.xcuserdatad/xcschemes/xcschememanagement.plist" b/AIGrammar.xcodeproj/xcuserdata/oscar.xcuserdatad/xcschemes/xcschememanagement.plist similarity index 100% rename from "\t\t.xcodeproj/xcuserdata/oscar.xcuserdatad/xcschemes/xcschememanagement.plist" rename to AIGrammar.xcodeproj/xcuserdata/oscar.xcuserdatad/xcschemes/xcschememanagement.plist diff --git a/AIGrammar.xcworkspace/contents.xcworkspacedata b/AIGrammar.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..a66c35d --- /dev/null +++ b/AIGrammar.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/AIGrammar.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/AIGrammar.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/AIGrammar.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/AIGrammar.xcworkspace/xcshareddata/swiftpm/Package.resolved b/AIGrammar.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 0000000..d977778 --- /dev/null +++ b/AIGrammar.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,14 @@ +{ + "pins" : [ + { + "identity" : "toastui", + "kind" : "remoteSourceControl", + "location" : "https://github.com/quanshousio/ToastUI.git", + "state" : { + "revision" : "1828801ea49e2ab900ca5e6bb18245312fda66a2", + "version" : "3.0.1" + } + } + ], + "version" : 2 +} diff --git a/AIGrammar/AIGrammar.entitlements b/AIGrammar/AIGrammar.entitlements index c6c92d9..0c67376 100644 --- a/AIGrammar/AIGrammar.entitlements +++ b/AIGrammar/AIGrammar.entitlements @@ -1,8 +1,5 @@ - - com.apple.developer.in-app-payments - - + diff --git a/AIGrammar/AIGrammarApp.swift b/AIGrammar/AIGrammarApp.swift index ba5d665..2d45c1e 100644 --- a/AIGrammar/AIGrammarApp.swift +++ b/AIGrammar/AIGrammarApp.swift @@ -6,15 +6,25 @@ // import SwiftUI +import TrustDecision @main struct AIGrammarApp: App { let persistenceController = PersistenceController.shared + + init() { + // 初始化部分 + setupLogging() + _ = InitApp.shared + } var body: some Scene { WindowGroup { - ContentView() + AllTabView() .environment(\.managedObjectContext, persistenceController.container.viewContext) + .environmentObject(IAPManager()) // 这里添加 IAPManager + .environmentObject(globalEnvironment) // 这里添加 IAPManager } } + } diff --git a/AIGrammar/AllTabView.swift b/AIGrammar/AllTabView.swift index 2115223..5b56cdd 100644 --- a/AIGrammar/AllTabView.swift +++ b/AIGrammar/AllTabView.swift @@ -7,16 +7,8 @@ import SwiftUI -@main -struct LearningToolApp: App { - var body: some Scene { - WindowGroup { - ContentView() - } - } -} -struct ContentView: View { +struct AllTabView: View { var body: some View { TabView { GrammarCheckView() @@ -46,35 +38,7 @@ struct ContentView: View { } } -struct GrammarCheckView: View { - var body: some View { - // Your Grammar Check View content goes here. - Text("Grammar Check") - } -} - -struct WordsView: View { - var body: some View { - // Your Words View content goes here. - Text("Words") - } -} - -struct TranslateView: View { - var body: some View { - // Your Translate View content goes here. - Text("Translate") - } -} - -struct SettingsView: View { - var body: some View { - // Your Settings View content goes here. - Text("Settings") - } -} - #Preview { - AllTab() + AllTabView() } diff --git a/AIGrammar/CommView/LoadingView.swift b/AIGrammar/CommView/LoadingView.swift index 46636d6..032014e 100644 --- a/AIGrammar/CommView/LoadingView.swift +++ b/AIGrammar/CommView/LoadingView.swift @@ -9,10 +9,16 @@ import SwiftUI struct LoadingView: View { var body: some View { - Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/) + ZStack { + Color.black.opacity(0.4).edgesIgnoringSafeArea(.all) // 使背景稍微变暗 + ProgressView() // iOS 14+ 的新进度指示器 + .progressViewStyle(CircularProgressViewStyle(tint: .white)) + .scaleEffect(1.5) // 放大指示器 + } } } + #Preview { LoadingView() } diff --git a/AIGrammar/GrammarSubView/CameraView.swift b/AIGrammar/GrammarSubView/CameraView.swift index 8e59d0a..253f317 100644 --- a/AIGrammar/GrammarSubView/CameraView.swift +++ b/AIGrammar/GrammarSubView/CameraView.swift @@ -6,13 +6,86 @@ // import SwiftUI +import UIKit +import Vision -struct CameraView: View { - var body: some View { - Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/) +func performOCR(on uiImage: UIImage, completion: @escaping (String) -> Void) { + guard let cgImage = uiImage.cgImage else { return } + + let handler = VNImageRequestHandler(cgImage: cgImage, options: [:]) + let request = VNRecognizeTextRequest { (request, error) in + guard let observations = request.results as? [VNRecognizedTextObservation] else { return } + let recognizedStrings = observations.compactMap { $0.topCandidates(1).first?.string } + completion(recognizedStrings.joined(separator: "\n")) + } + request.recognitionLanguages = ["en-US", "zh-Hans"] // 根据需要设置支持的语言 + request.usesLanguageCorrection = true + + do { + try handler.perform([request]) + } catch { + print("OCR失败: \(error)") } } -#Preview { - CameraView() + + +struct CameraView: UIViewControllerRepresentable { + @Binding var textInput: String + @Environment(\.presentationMode) var presentationMode + + func makeUIViewController(context: Context) -> UIImagePickerController { + let picker = UIImagePickerController() + picker.delegate = context.coordinator + picker.sourceType = .camera + picker.allowsEditing = true // 启用编辑模式 + return picker + } + + func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context) {} + + func makeCoordinator() -> Coordinator { + Coordinator(self) + } + + class Coordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate { + var parent: CameraView + + init(_ parent: CameraView) { + self.parent = parent + } + + func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) { + // 尝试获取裁剪后的图片 + if let editedImage = info[.editedImage] as? UIImage { + // 使用裁剪后的图片进行OCR + performOCR(on: editedImage) { recognizedText in + // 更新文本输入 + self.parent.textInput = recognizedText + self.parent.presentationMode.wrappedValue.dismiss() + } + } else if let originalImage = info[.originalImage] as? UIImage { + // 如果用户没有裁剪图片,回退到使用原始图片 + performOCR(on: originalImage) { recognizedText in + self.parent.textInput = recognizedText + self.parent.presentationMode.wrappedValue.dismiss() + } + } else { + // 如果获取图片失败,直接关闭相机视图 + self.parent.presentationMode.wrappedValue.dismiss() + } + } + + func imagePickerController2(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) { + if let uiImage = info[.originalImage] as? UIImage { + // 调用OCR处理函数 + performOCR(on: uiImage) { recognizedText in + // 更新父视图的文本输入 + self.parent.textInput = recognizedText + } + } + parent.presentationMode.wrappedValue.dismiss() + } + } } + diff --git a/AIGrammar/GrammarSubView/IAPTestView.swift b/AIGrammar/GrammarSubView/IAPTestView.swift index 112384c..53438da 100644 --- a/AIGrammar/GrammarSubView/IAPTestView.swift +++ b/AIGrammar/GrammarSubView/IAPTestView.swift @@ -6,13 +6,197 @@ // import SwiftUI +import StoreKit -struct IAPTestView: View { - var body: some View { - Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/) +enum IAPProductTest: String, CaseIterable { + case premiumFeature1 = "grammar_1_month" + case premiumFeature2 = "grammar_1_week" + +} + + +class IAPManagerTest: ObservableObject { + @Published var products: [Product] = [] + @Published var purchasedProducts: [Product] = [] + + init() { + Task { + await requestProducts() + await updatePurchasedProducts() + await listenForTransactions() + } + } + + func requestProducts() async { + do { + let products = try await Product.products(for: IAPProductTest.allCases.map { $0.rawValue }) + DispatchQueue.main.async { + // 按照产品的权重排序 + self.products = products + + // 打印每个产品及其相关变量 + for product in self.products { + print("--------------------------") + print("Product ID: \(product.id)") + print("Product Title: \(product.displayName)") + print("Product Description: \(product.description)") + print("Product Price: \(product.price)") + print("Product displayPrice: \(product.displayPrice)") + print("Product priceFormatStyle: \(product.priceFormatStyle)") + print("Product subscriptionPeriodFormatStyle: \(product.subscriptionPeriodFormatStyle)") + print("Product subscriptionPeriodUnitFormatStyle: \(product.subscriptionPeriodUnitFormatStyle)") + print("--------------------------") + } + } + } catch { + print("Failed to fetch products: \(error.localizedDescription)") + } + } + + func buy(product: Product) async { + do { + let uuid = UUID() + let token = Product.PurchaseOption.appAccountToken(uuid) + print("purchase appAccountToken: \(uuid.uuidString)") + + let result = try await product.purchase(options: [token]) + switch result { + case .success(let verification): + if case .verified(let transaction) = verification { + // 在这里不需要处理交易完成,详细处理放在 listenForTransactions 中 + // 如果购买成功了,再点击这个按钮,会直接到这里 + print("Purchase initiated for product: \(product.id)") + } + case .userCancelled: + print("User cancelled the purchase.") + case .pending: + print("Purchase is pending.") + default: + break + } + } catch { + print("Failed to purchase product: \(error.localizedDescription)") + } + } + + func updatePurchasedProducts() async { + for await result in Transaction.currentEntitlements { + if case .verified(let transaction) = result { + if let product = products.first(where: { $0.id == transaction.productID }) { + DispatchQueue.main.async { + self.purchasedProducts.append(product) + } + } + } + } + } + + + func listenForTransactions() async { + for await transactionResult in Transaction.updates { + if case .verified(let transaction) = transactionResult { + if let product = products.first(where: { $0.id == transaction.productID }) { + DispatchQueue.main.async { + self.purchasedProducts.append(product) + } + } + + // 打印详细的交易信息,为什么会有历史交易? + print("Transaction ID: \(transaction.id)") + print("Product ID: \(transaction.productID)") + print("Purchase Date: \(transaction.purchaseDate)") + //print("Transaction State: \(transaction.revocationReason ?? "None")") + //print("Original Transaction ID: \(transaction.originalID ?? "None")") + + // 获取票据数据 + + // 获取 jsonRepresentation + let jsonData = try transaction.jsonRepresentation + + // 将 Data 转换为 String + if let jsonString = String(data: jsonData, encoding: .utf8) { + print("Transaction Receipt: \(jsonString)") + } else { + print("Failed to convert JSON data to string.") + } + + await transaction.finish() + } + } + } + + func restorePurchases() async { + do { + try await AppStore.sync() + await updatePurchasedProducts() + print("Purchases restored") + } catch { + print("Failed to restore purchases: \(error.localizedDescription)") + } } } + +struct IAPTestView: View { + @StateObject var iapManager = IAPManager() + //@StateObject var iapManager = IAPManagerTest() + + var body: some View { + VStack(spacing: 20) { + if iapManager.products.isEmpty { + Text("Loading products...") + } else { + ForEach(iapManager.products, id: \.id) { product in + VStack { + Text(product.displayName) + .font(.title) + + Button("Buy \(product.displayName)") { + Task { + await iapManager.buy(product: product){ result in + switch result { + case .success(let message): + logger.info("succ") + case .failure(let error): + logger.error("error") + } + } + } + } + .padding() + .background(Color.blue) + .foregroundColor(.white) + .cornerRadius(10) + } + } + + Button("Restore Purchases") { + Task { + await iapManager.restorePurchases(){ result in + switch result { + case .success(let message): + logger.info("restore purchase succ. message: \(message)") + case .failure(let error): + logger.error("restore purchase error. message: \(error)") + } + } + } + } + .padding() + .background(Color.green) + .foregroundColor(.white) + .cornerRadius(10) + } + } + .onAppear { + Task { + //await iapManager.requestProducts() + } + } + } +} + + #Preview { IAPTestView() } diff --git a/AIGrammar/GrammarSubView/InputView.swift b/AIGrammar/GrammarSubView/InputView.swift index 693f8d7..8f40dcb 100644 --- a/AIGrammar/GrammarSubView/InputView.swift +++ b/AIGrammar/GrammarSubView/InputView.swift @@ -6,13 +6,224 @@ // import SwiftUI +import ToastUI + +fileprivate struct ProgressBar: View { + @Binding var value: Float + + var body: some View { + GeometryReader { geometry in + ZStack(alignment: .leading) { + Rectangle() + .foregroundColor(.gray.opacity(0.3)) + .frame(width: geometry.size.width, height: geometry.size.height) + Rectangle() + .foregroundColor(Color.green) + .frame(width: min(CGFloat(self.value) * geometry.size.width, geometry.size.width), height: geometry.size.height) + .animation(.linear, value: value) + } + .cornerRadius(45.0) + } + } +} struct InputView: View { + @Binding var textInput: String + @Binding var progressValue: Float + @Binding var showKeyboard: Bool + @Binding var showBuyProView: Bool + @Binding var showResult: Bool + @Binding var results : [GrammarRes] + @Binding var isLoading: Bool // 控制加载指示器的显示 + @Binding var showingToast: Bool // 控制是否显示toast + @Binding var toastText: String + + @FocusState private var isTextEditorFocused: Bool + var body: some View { - Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/) + VStack { + // TextEditor设计成无边框形式 + TextEditor(text: $textInput) + .onTapGesture { + // 用户点击TextEditor时更新状态 + self.showKeyboard = true // 显示键盘 + self.showBuyProView = false // 隐藏BuyProView + } + .focused($isTextEditorFocused) + .padding(5) + .background(Color.white) + .cornerRadius(5) + .padding(5) + .onAppear { + if showKeyboard { + DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { + self.isTextEditorFocused = true + } + } + } + + //Divider() // 添加分割线,区分TextEditor和下方控件 + + // 进度条和按钮 + ProgressBar(value: $progressValue).frame(height: 3) + + HStack { + Button("Clear") { + textInput = "" + progressValue = 0 + } + + Spacer() + + Button("Check") { + // 清除输入内容前后的空格 + let trimmedText = textInput.trimmingCharacters(in: .whitespacesAndNewlines) + + // 检查是否为空 + if trimmedText.isEmpty { + showingToast = true + toastText = "Please enter some text." + return // 提前返回,不执行网络请求 + } + + // 检查长度是否超过200个字符 + let MaxLen = globalEnvironment.isVip ? globalEnvironment.MaxLenGrammarCheckVIP : globalEnvironment.MaxLenGrammarCheckFree + if trimmedText.count > MaxLen { + showingToast = true + toastText = "Input too long. Please re-enter the text." + logger.info("input too lang, inputlen: \(trimmedText.count), maxlen: \(MaxLen), vip: \(globalEnvironment.isVip)") + return // 提前返回,不执行网络请求 + } + + // 检查是否含有非法字符 + let checkRes = CommonFunc.shared.validateInputEng(input: trimmedText) + if !checkRes.isValid { + showingToast = true + toastText = checkRes.message + return + } + /* + if trimmedText.range(of: "[^a-zA-Z0-9 .,;:!?'\"@-]", options: .regularExpression) != nil { + showingToast = true + toastText = "Please enter valid characters." + return // 提前返回,不执行网络请求 + } + */ + + loadData() // 提交的显示 + + // Send the request to the server + NetworkManager.shared.checkGrammar(inputText: trimmedText) { result in + switch result { + case .success(let results): + // Update the main UI with the results + loadComplete() + DispatchQueue.main.async { + self.results = results + self.showResult = true + self.showKeyboard = false + self.showBuyProView = false + hideKeyboard() + + logger.info("grammar check succ.") + } + case .failure(let error): + loadComplete() + switch error { + case .businessError(let ret, let message): + // 业务错误,比如无免费可用次数等,需要处理逻辑。 + logger.error("Business error - Ret: \(ret), Message: \(message)") + DispatchQueue.main.async { + showingToast = true + switch ret{ + case globalEnvironment.GrammarCheckOK: + toastText = globalEnvironment.GrammarOKToast + case globalEnvironment.RetCodeFreeLimited: + toastText = globalEnvironment.FreeLimitedToast + case globalEnvironment.RetCodeDirtyInput: + toastText = globalEnvironment.DirtyInputErrToast + default: + toastText = globalEnvironment.OtherServerErrToast + } + } + case .other(let error): + logger.error("network error occurred: \(error.localizedDescription)") + DispatchQueue.main.async { + showingToast = true + toastText = globalEnvironment.NetWorkErrToast + } + } + } + } + } + .padding(.vertical, 10) + .padding(.horizontal, 40) + .background(Color.green) + .foregroundColor(.white) + .cornerRadius(5) + .font(.subheadline) // 调整字体大小为标题大小 + } + .padding(.bottom) + } + .padding() // 为所有内容添加padding + .background( + RoundedRectangle(cornerRadius: 10) + .fill(Color.white) // 设置背景色为白色 + ) + .padding(5) + .onTapGesture { + // 点击背景时隐藏键盘 + self.isTextEditorFocused = false + showBuyProView = true + } + } + // 模拟加载数据 + func loadData() { + isLoading = true + } + func loadComplete(){ + isLoading = false + } + + // 用于外部调用的方法来控制焦点 + func toggleFocus() { + isTextEditorFocused.toggle() + } + + // 函数用于收起键盘 + private func hideKeyboard() { + UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil) + } +} + + +struct InputView_Preview: View{ + + @State private var textInput: String + @State private var progressValue: Float = 0 + @State private var showKeyboard: Bool = false + @State private var showBuyProView: Bool = true + @State private var showResult : Bool = false + @State private var results : [GrammarRes] + + // 提交等待,错误提示等 + @State private var isLoading = false // 控制加载指示器的显示 + @State private var showingToast = false // 控制是否显示toast + @State private var toastText = "" + + init(){ + let demoGrammarData = GrammarData.demoInstance() + self.textInput = demoGrammarData.inputText + self.results = demoGrammarData.results + } + + var body: some View { + VStack { + InputView(textInput: $textInput, progressValue: $progressValue, showKeyboard: $showKeyboard, showBuyProView: $showBuyProView, showResult: $showResult, results: $results, isLoading: $isLoading, showingToast: $showingToast, toastText: $toastText) + } } } #Preview { - InputView() + InputView_Preview() } diff --git a/AIGrammar/GrammarSubView/ResultView.swift b/AIGrammar/GrammarSubView/ResultView.swift index 42d86d3..276e712 100644 --- a/AIGrammar/GrammarSubView/ResultView.swift +++ b/AIGrammar/GrammarSubView/ResultView.swift @@ -6,13 +6,201 @@ // import SwiftUI +import Foundation + +// 确保SingleCorrectionCard只能在本文件内访问 +fileprivate struct SingleCorrectionCard: View { + let correction: String + + var body: some View { + Text(correction) + .padding(.vertical, 6) + .font(.system(size: UIFontMetrics.default.scaledValue(for: 15) * 4 / 5)) + .background(Color.yellow.opacity(0.5)) + .cornerRadius(5) + } +} + +struct GrammarDetailsCardView: View { + let res: GrammarRes + + var body: some View { + // 添加黄色虚线作为分隔 + Divider() + .background(Color.yellow) + .frame(height: 1) + .overlay( + Rectangle() + .stroke(style: StrokeStyle(lineWidth: 1, dash: [5])) + .foregroundColor(.yellow) + ) + .padding(3) + + VStack { + + Text("Error: \(res.plain)") + .frame(maxWidth: .infinity, alignment: .leading) // 横向铺满 + .padding(.vertical, 8) // 调整高度为默认高度的2/3 + .background(Color.gray.opacity(0.5)) // 设置背景色为Gray + .foregroundColor(.black) + .cornerRadius(5) + + Text("Reason: \(res.reason)") + .frame(maxWidth: .infinity, alignment: .leading) // 横向铺满 + + Text("Correction:") + .frame(maxWidth: .infinity, alignment: .leading) // 横向铺满 + .padding(.top, 6) // 在Correction上方添加一些间隔 + + ScrollView(.horizontal, showsIndicators: false) { + HStack { + ForEach(Array(res.correction.enumerated()), id: \.offset) { index, correction in + SingleCorrectionCard(correction: correction) + } + } + } + } + .padding(.horizontal, 3) // 在卡片的底部添加一些间隔 + .padding(.bottom, 3) // 在卡片的底部添加一些间隔 + .background(Color.gray.opacity(0.2)) // 设置整个卡片的背景色为LightGray + .cornerRadius(10) + } +} struct ResultView: View { + // 假设的错误数据和文本内容 + @Binding var textContent: String + @Binding var results: [GrammarRes] + @Binding var showResult : Bool + @Binding var showKeyboard : Bool + + @State private var selectedCardIndex: Int? = 0 + @State private var textRes = AttributedString() + + @State private var resContent: String = String("") + + func getColoredText(str : String, type : String, index : Int?) -> AttributedString { + var text = AttributedString( str ) + if(type == GrammarResType.ok.rawValue){ + return text + } + if (type == GrammarResType.grammar.rawValue ){ + text.foregroundColor = .red + }else + { + text.backgroundColor = .yellow + } + text.link = URL(string: String(index!)) + return text + } + + func styledText() -> Text { + var outstr = AttributedString() + var index = 0 + + let substr: String = textContent + var currentIndex = substr.startIndex + + for res in results { + if let range = substr.range(of: res.plain, range: currentIndex.. Text { + var outstr = AttributedString() + var index = 0 + + for res in results { + let tmp = getColoredText(str: res.plain, type: res.type, index: index) + outstr.append(tmp) + index = index + 1 + } + return Text(outstr) + } + */ + var body: some View { - Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/) + VStack { + // 为styledText提供滚动视图 + ScrollView { + styledText() + .padding() + .environment(\.openURL, OpenURLAction { url in + let path = url.absoluteString + print("index: \(path)") + self.selectedCardIndex = Int(path) + return .handled + }) + Spacer() // 使用Spacer确保Text组件横向拉伸 + } + .onTapGesture { + self.showResult = false + self.showKeyboard = true + } + .background(Color.white) // 设置背景色为Gray + .frame(maxHeight: .infinity) // 限制styledText占用半个屏幕 + + //与上面的文本进行联动,对每个卡片设置一个index + ScrollViewReader { scrollView in + ScrollView { + LazyVStack { + ForEach(Array(results.enumerated()), id: \.offset) { index, res in + if(res.type != GrammarResType.ok.rawValue){ + GrammarDetailsCardView(res: res) + .id(index) // 给每个CardView分配一个ID + } + } + } + } + .onChange(of: selectedCardIndex) { newIndex in + if let newIndex = newIndex { + withAnimation { + scrollView.scrollTo(newIndex, anchor: .top) + } + } + } + } + .frame(maxHeight: .infinity) // 限制ErrorCard占用半个屏幕 */ + } + .padding() // 为所有内容添加padding + .background( + RoundedRectangle(cornerRadius: 10) + .fill(Color.white) // 设置背景色为白色 + ) + .padding(5) + } + +} + + +struct ResultView_Preview: View { + + @State var input : String = "this is demo text" + @State var results : [GrammarRes] + @State var showResult : Bool = true + @State var showKeyboard : Bool = true + + init() { + let demoGrammarData = GrammarData.demoInstance() + self.input = demoGrammarData.inputText + self.results = demoGrammarData.results + } + + var body: some View { + ResultView(textContent: $input, results: $results, showResult: $showResult, showKeyboard: $showKeyboard) } } #Preview { - ResultView() + ResultView_Preview() } diff --git a/AIGrammar/GrammarSubView/RichText.swift b/AIGrammar/GrammarSubView/RichText.swift index d5fbd20..af7b342 100644 --- a/AIGrammar/GrammarSubView/RichText.swift +++ b/AIGrammar/GrammarSubView/RichText.swift @@ -7,12 +7,197 @@ import SwiftUI + struct RichText: View { + var text1: AttributedString { + var text = AttributedString(localized:"登录即表示同意") + text.foregroundColor = .gray + return text + } + var text2: AttributedString { + var text = AttributedString(localized:"用户协议") + text.link = URL(string: "111") + text.foregroundColor = .red + return text + } + var text3: AttributedString { + var text = AttributedString(localized:"和") + text.foregroundColor = .gray + return text + } + var text4: AttributedString { + var text = AttributedString(localized:"隐私协议") + text.link = URL(string: "222") + text.foregroundColor = .red + return text + } + + var text: AttributedString { + text1 + text2 + text3 + text4 + } + + var body: some View { - Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/) + VStack { + + Text(text) + .environment(\.openURL, OpenURLAction { url in + let path = url.absoluteString + if path.hasPrefix("111") { + print("111...") + } else if path.hasPrefix("222") { + print("222...") + } + return .handled + }) + + Text("Device ID: \(globalEnvironment.deviceID)") + } } } +/* +import UIKit +import SwiftUI + +class RichTextViewController: UIViewController, UITextViewDelegate { + var textView: UITextView! + + override func viewDidLoad() { + super.viewDidLoad() + + // 初始化 UITextView 并设置代理 + textView = UITextView(frame: self.view.bounds) + textView.delegate = self + + // 允许富文本交互 + textView.isEditable = false + textView.isSelectable = true + + // 创建富文本 + let attributedString = NSMutableAttributedString(string: "点击这里进行测试") + + // 设置点击部分的样式和链接 + let linkAttributes: [NSAttributedString.Key: Any] = [ + .link: URL(string: "http://baidu.com")!, // 使用自定义URL scheme + .foregroundColor: UIColor.blue + ] + + attributedString.setAttributes(linkAttributes, range: NSRange(location: 0, length: 4)) // 假设"点击这里"是可点击的 + + textView.attributedText = attributedString + + // 将 UITextView 添加到当前视图 + self.view.addSubview(textView) + } + + // 处理富文本链接点击事件 + func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool { + print("特定文本部分被点击") + print(URL.scheme as Any) + if URL.scheme == "http" { + // 在这里添加点击后的处理逻辑 + return false // 返回false表示不让系统处理这个URL + } + return true + } +} + +struct RichTextView: UIViewRepresentable { + func makeUIView(context: Context) -> UITextView { + // 确保 RichTextViewController 实例在这里创建,并立即返回 textView + // 这样可以避免 textView 在使用之前未被初始化 + let controller = RichTextViewController() + controller.loadViewIfNeeded() // 确保视图控制器的视图被加载,从而textView被初始化 + return controller.textView + } + + func updateUIView(_ uiView: UITextView, context: Context) { + // 更新UI视图(如果需要) + } +} + +struct RichText: View { + var body: some View { + VStack{ + Text("请点击下面的文本 请点击下面的文本 请点击下面的文本 请点击下面的文本 请点击下面的文本 请点击下面的文本 ") + // 使用我们的 RichTextView + RichTextView() + .frame(maxHeight: .infinity) // 设置一个合适的高度 + //.edgesIgnoringSafeArea(.all) // 让视图延伸到屏幕的边缘 + } + } +} + */ + +/* +import SwiftUI +import UIKit + +struct AttributedText: UIViewRepresentable { + var attributedString: NSAttributedString + + func makeUIView(context: Context) -> UILabel { + let label = UILabel() + label.numberOfLines = 0 // 支持多行显示 + label.attributedText = attributedString + + // 添加手势识别器 + let tapGesture = UITapGestureRecognizer(target: context.coordinator, action: #selector(Coordinator.labelTapped(_:))) + label.addGestureRecognizer(tapGesture) + label.isUserInteractionEnabled = true + + return label + } + + func updateUIView(_ uiView: UILabel, context: Context) { + // 更新富文本字符串 + uiView.attributedText = attributedString + } + + func makeCoordinator() -> Coordinator { + Coordinator(self) + } + + class Coordinator: NSObject { + var parent: AttributedText + + init(_ parent: AttributedText) { + self.parent = parent + } + + @objc func labelTapped(_ sender: UITapGestureRecognizer) { + // 处理点击事件,这里需要根据点击位置来确定用户点击的是哪一部分文本 + // 这部分较为复杂,可能需要使用UILabel的子类来自定义实现 + print("Label was tapped") + } + } +} + + +struct RichText: View { + var body: some View { + // 示例使用AttributedText + AttributedText(attributedString: attributedString) + } + + var attributedString: NSAttributedString { + let fullString = NSMutableAttributedString(string: "Tap on ") + + let clickablePart = NSAttributedString(string: "this text", attributes: [ + .foregroundColor: UIColor.blue, + .underlineStyle: NSUnderlineStyle.single.rawValue + ]) + + fullString.append(clickablePart) + fullString.append(NSAttributedString(string: " to see action.")) + + return fullString + } +} + + */ + #Preview { RichText() } diff --git a/AIGrammar/GrammarSubView/ShareSheet.swift b/AIGrammar/GrammarSubView/ShareSheet.swift index de56b03..ff6f457 100644 --- a/AIGrammar/GrammarSubView/ShareSheet.swift +++ b/AIGrammar/GrammarSubView/ShareSheet.swift @@ -6,13 +6,16 @@ // import SwiftUI +import UIKit -struct ShareSheet: View { - var body: some View { - Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/) +struct ShareSheet: UIViewControllerRepresentable { + var itemsToShare: [Any] + + func makeUIViewController(context: Context) -> UIActivityViewController { + let controller = UIActivityViewController(activityItems: itemsToShare, applicationActivities: nil) + return controller } + + func updateUIViewController(_ uiViewController: UIActivityViewController, context: Context) {} } -#Preview { - ShareSheet() -} diff --git a/AIGrammar/View/GrammarCheckView.swift b/AIGrammar/View/GrammarCheckView.swift index 1a58fc8..23b7f48 100644 --- a/AIGrammar/View/GrammarCheckView.swift +++ b/AIGrammar/View/GrammarCheckView.swift @@ -6,13 +6,174 @@ // import SwiftUI +import ToastUI -struct GrammarCheckView: View { +fileprivate struct BuyProView: View { + var onTryForFree: () -> Void + var body: some View { - Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/) + HStack { + VStack(alignment: .leading, spacing: 10) { + Text("Update to Pro") + .font(.headline) + + Text("Remove Character limits and ads. Use Grammar Check with higher quality.") + .font(.subheadline) + } + + Spacer() + + Button("Try for free") { + // 按钮动作 + // 触发显示VIP付费界面的事件 + onTryForFree() + } + .padding(.vertical, 6) // 调整高度为默认高度的2/3 + .padding(.horizontal, 20) + .background(Color.green) + .foregroundColor(.white) + .cornerRadius(5) + .font(.headline) // 调整字体大小为标题大小 + .alignmentGuide(.bottom) { d in d[.bottom] } // 对齐按钮的底部到 VStack 的底部 + } + .padding() + .background(Color.white) + .cornerRadius(5) + .padding(5) } } -#Preview { - GrammarCheckView() +enum ActiveSheet { + case camera, share +} +extension ActiveSheet: Identifiable { + var id: Self { self } +} + + +struct GrammarCheckView: View { + @EnvironmentObject var globalEnv: GlobalEnvironment // 引入环境对象 + + // 定义变量 + @State private var textInput: String + @State private var inputResult : String + @State private var progressValue: Float = 0 + @State private var showKeyboard: Bool = false + @State private var showBuyProView: Bool = true + @State private var showResult : Bool = false + @State var results : [GrammarRes] + @State private var showVIPPaymentView: Bool = false // 控制VIP付费界面的显示 + + // 提交等待,错误提示等 + @State private var isLoading = false // 控制加载指示器的显示 + @State private var showingToast = false // 控制是否显示toast + @State private var toastText = "" + + @State private var activeSheet: ActiveSheet? + + @State private var isTextEditorFocused: Bool = false // 这个变量会传递给 InputView + + // 使用默认文本进行初始化 + init() { + let demoGrammarData = GrammarData.demoInstance() + self.textInput = demoGrammarData.inputText + self.inputResult = demoGrammarData.correctText + self.results = demoGrammarData.results + } + + + var body: some View { + NavigationView { + ZStack { + Color.pink.opacity(0.2).edgesIgnoringSafeArea(.all) // 设置页面背景色 + + VStack { + if showResult { + ResultView(textContent: $textInput, results: $results, showResult: $showResult, showKeyboard: $showKeyboard) + } else { + InputView(textInput: $textInput, progressValue: $progressValue, showKeyboard: $showKeyboard, showBuyProView: $showBuyProView, showResult: $showResult, results: $results, isLoading: $isLoading, showingToast: $showingToast, toastText: $toastText) + // .environment(\.isTextEditorFocused, $isTextEditorFocused) // 将焦点状态传递给 InputView + } + if showBuyProView { + BuyProView(onTryForFree: { + showVIPPaymentView = true // 显示VIP付费界面 + }) + //BuyProView() + } + } + .fullScreenCover(isPresented: $showVIPPaymentView) { + VIPPaymentView() // 弹出VIP付费界面 + } + .navigationBarTitleDisplayMode(.inline) + .toolbar { + ToolbarItem(placement: .principal) { + HStack { + Text("Grammar & Spell Checker") + .font(.headline) // 设置标题字体 + if globalEnv.isVip { + Image("vipimg") // 示例中使用系统图标 + .resizable() // 使图片可调整大小 + .scaledToFit() // 保持图片的宽高比 + .frame(width: 24, height: 24) // 设置图标的具体尺寸 + .foregroundColor(.yellow) + .font(.subheadline) // 设置图标字体为比标题小 + .offset(y: -1) // 根据需要调整图标的垂直位置 + } + } + } + } + .foregroundColor(.black) + .navigationBarItems(leading: Button(action: { + // 相机按钮动作 + self.activeSheet = .camera + }) { + Image(systemName: "camera") + }, trailing: Button(action: { + // 分享按钮动作 + self.activeSheet = .share + }) { + Image(systemName: "square.and.arrow.up") + }) + .sheet(item: $activeSheet, onDismiss: { + // 如果需要在sheet关闭时执行某些操作,可以在这里添加 + }) { item in + switch item { + case .camera: + CameraView(textInput: $textInput) + case .share: + ShareSheet(itemsToShare: [textInput]) + } + } + .toast(isPresented: $showingToast, dismissAfter: globalEnvironment.toastPresentMsNormal) { + HStack { + Image(systemName: "exclamationmark.bubble") + .foregroundColor(.yellow) + Text(toastText) + .foregroundColor(.black) + } + .padding() + .background(Color.white) + .cornerRadius(8) + .shadow(radius: 10) + } + + // 加载指示器 + if isLoading { + LoadingView() + } + } + + } + } + +} + + +// MARK: 预览 +struct GrammarCheckView_Previews: PreviewProvider { + static var previews: some View { + GrammarCheckView() + .environmentObject(IAPManager()) // 这里添加 IAPManager + .environmentObject(globalEnvironment) // 这里添加 IAPManager + } } diff --git a/AIGrammar/View/IAPView.swift b/AIGrammar/View/IAPView.swift index 15609a6..ea7af6b 100644 --- a/AIGrammar/View/IAPView.swift +++ b/AIGrammar/View/IAPView.swift @@ -4,15 +4,187 @@ // // Created by oscar on 2024/7/9. // - import SwiftUI -struct IAPView: View { +struct VIPPaymentView: View { + @Environment(\.presentationMode) var presentationMode + + @State private var showingToast = false + @State private var toastText = "" + + let features = ["Grammar Check & Spelling Correction", "Words & Dictionary", "Translate"] + let freeValues = ["2 Times / Day", "2 Times / Day", "2 Times / Day"] + let premiumValues = ["Unlimited", "Unlimited", "Unlimited"] + let products = [ + ("Premium Weekly", "$4.99", "Billed Weekly", "weekly", IAPProduct.premiumFeature1), + ("Premium Monthly", "$9.99 / month", "Billed Monthly", "monthly", IAPProduct.premiumFeature2), + ("Premium Yearly", "$49.99 / year", "Billed Yearly", "yearly", IAPProduct.premiumFeature3) + ] + + @State private var selectedProductIndex: Int = 1 // 默认选择第二个商品 + @State private var selectedPlanText: String = "Billed Monthly" + + @EnvironmentObject var iapManager: IAPManager // 确保在上级视图中已提供 IAPManager + var body: some View { - Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/) + NavigationView { + VStack { + HStack { + Text("PREMIUM") + .bold() + .foregroundColor(Color(red: 1.0, green: 0.8, blue: 0.0)) // 自定义更亮的黄色 + //.foregroundColor(.yellow) + .padding() + .background(Color.orange) + .cornerRadius(20) + Spacer() + Button("Close") { + presentationMode.wrappedValue.dismiss() + } + } + .padding() + + List { + Section(header: Text("Get Premium Features").font(.headline)) { + HStack { + Text("Features") + .frame(width: 120, alignment: .leading) + Spacer() + Text("Free") + .frame(width: 120, alignment: .center) + Spacer() + Text("Premium") + .frame(width: 80, alignment: .center) + } + .font(.headline) + + ForEach(Array(zip(features, zip(freeValues, premiumValues))), id: \.0) { item in + HStack { + Text(item.0) + .frame(width: 120, alignment: .leading) + Spacer() + Text(item.1.0) // Free values + .frame(width: 120, alignment: .center) + Spacer() + Text(item.1.1) // Premium values + .frame(width: 80, alignment: .center) + } + .font(.subheadline) + } + } + } + + ForEach(products.indices, id: \.self) { index in + Button(action: { + // do someting + }) { + HStack { + Image(systemName: selectedProductIndex == index ? "checkmark.circle.fill" : "circle") + .foregroundColor(selectedProductIndex == index ? .green : .secondary) + .padding(.horizontal, 4) + + VStack(alignment: .leading) { + if products[index].0 == "Premium Yearly" { + // 特别为 "Premium Yearly" 添加图标和折扣信息 + Text(products[index].0) + Text(" ⚡️") + Text(" 58% off") + .bold() + .foregroundColor(.red) // 折扣信息使用红色 + .font(.subheadline) + } else { + Text(products[index].0) + } + Text(products[index].1) + .font(.subheadline) + } + Spacer() + Text(products[index].2) + .font(.subheadline) + .foregroundColor(.secondary) + } + .padding(.vertical, 10) + .background(self.selectedProductIndex == index ? Color.yellow : Color.clear) + .cornerRadius(5) + .padding(.horizontal, 20) // 增加水平缩进 + .onTapGesture { + self.selectedProductIndex = index + self.selectedPlanText = products[index].2 + } + } + } + Spacer() + + Button("Purchase") { + // Handle purchase logic here + buyProduct() + } + .padding(.vertical, 15) + .padding(.horizontal, 80) + .foregroundColor(.white) + .background(Color.green) + .cornerRadius(15) + .frame(minWidth: 0, maxWidth: .infinity) + .shadow(radius: 2) // 添加阴影效果 + .font(.headline) // 调整字体大小为标题大小 + .padding(.top, 50) + + Text((selectedPlanText ) + ", Cancel Anytime") + .font(.footnote) + .padding(.vertical, 5) + } + .navigationBarHidden(true) + } + .frame(maxWidth: .infinity, maxHeight: .infinity) + .background(Color(.systemBackground)) + .toast(isPresented: $showingToast, dismissAfter: 5.0, onDismiss: { + // Toast 消失后执行的动作 + presentationMode.wrappedValue.dismiss() // 关闭当前视图 + }) { + HStack { + Image(systemName: "exclamationmark.bubble") + .foregroundColor(.yellow) + Text(toastText) + .foregroundColor(.black) + } + .padding() + .background(Color.white) + .cornerRadius(8) + .shadow(radius: 10) + } + + } + + + private func buyProduct() { + let productId = products[selectedProductIndex].4 + logger.info("selected productId: \(productId.rawValue)") + if let product = iapManager.products.first(where: { $0.id == productId.rawValue }) { + Task { + await iapManager.buy(product: product) { result in + switch result { + case .success(let message): + self.toastText = message + self.showingToast = true + presentationMode.wrappedValue.dismiss() + + // 刷新用户的vip状态 + InitApp.shared.refreshUserInfo() + case .failure(let error): + self.toastText = error.localizedDescription + self.showingToast = true + } + } + } + } else { + logger.error("Product not found") + toastText = "Error loading product list, please try again later" + self.showingToast = true + } } } -#Preview { - IAPView() +struct VIPPaymentView_Previews: PreviewProvider { + static var previews: some View { + VIPPaymentView() + } } + diff --git a/AIGrammar/View/SettingsView.swift b/AIGrammar/View/SettingsView.swift index 2bfcd6a..4322c5f 100644 --- a/AIGrammar/View/SettingsView.swift +++ b/AIGrammar/View/SettingsView.swift @@ -6,13 +6,226 @@ // import SwiftUI +import ToastUI -struct SettingsView: View { - var body: some View { - Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/) +import SafariServices + +struct FullScreenSafariView: UIViewControllerRepresentable { + let url: URL + var onDismiss: (() -> Void)? + + func makeUIViewController(context: UIViewControllerRepresentableContext) -> SFSafariViewController { + let safariViewController = SFSafariViewController(url: url) + safariViewController.delegate = context.coordinator + return safariViewController + } + + func updateUIViewController(_ uiViewController: SFSafariViewController, context: UIViewControllerRepresentableContext) { + // 无需更新 + } + + func makeCoordinator() -> Coordinator { + Coordinator(self) + } + + class Coordinator: NSObject, SFSafariViewControllerDelegate { + var parent: FullScreenSafariView + + init(_ parent: FullScreenSafariView) { + self.parent = parent + } + + func safariViewControllerDidFinish(_ controller: SFSafariViewController) { + parent.onDismiss?() + } } } + +struct SettingsView: View { + @EnvironmentObject var globalEnv: GlobalEnvironment // 引入环境对象 + + @State private var isLoading = false // 控制加载指示器的显示 + @State private var showingToast = false // 控制是否显示toast + @State private var toastText = "" + @State private var showVIPPaymentView = false + + @EnvironmentObject var iapManager: IAPManager // 确保在上级视图中已提供 IAPManager + + @State private var showingFullSafari = false + + + @State private var showingAdvancedSettings = false // 控制高级设置显示的状态 + @State private var useSandboxEnvironment = false + @State private var useDevelopmentEnvironment = false + + + var body: some View { + NavigationView { + ScrollView { + VStack(spacing: 0) { + // Upgrade to Premium + settingItem(icon: "arrow.up.circle", text: "Upgrade to Premium", isBold: true) + .onTapGesture { + showVIPPaymentView = true + } + + // Feedback + settingItem(icon: "bubble.left", text: "Feedback") + .onTapGesture { + // Assuming there's a way to open App Store Feedback + openAppStoreFeedback() + } + + // About + settingItem(icon: "info.circle", text: "About") + .onTapGesture { + // Code to show About View + self.showingFullSafari = true + } + + // Restore Purchases + settingItem(icon: "arrow.clockwise.circle", text: "Restore Purchases", isBold: true) + .onTapGesture { + restorePurchase() + } + + } + .fullScreenCover(isPresented: $showingFullSafari) { + FullScreenSafariView(url: URL(string: globalEnvironment.userTermsURL)!, onDismiss: { + self.showingFullSafari = false + }) + } + + // 留出手势操作的空间,但UI上隐藏掉 + gestureArea + + if showingAdvancedSettings { + VStack { + Toggle("Sandbox", isOn: $useSandboxEnvironment) + Toggle("TestEnv", isOn: $useDevelopmentEnvironment) + Button("Save") { + saveSettings() + showingAdvancedSettings = false + } + } + .padding(.vertical,10) + .padding(.horizontal, 20) + .background(Color.white) // Ensure background fills the view + .cornerRadius(5) + } + + } + .background(Color.pink.opacity(0.2)) // Ensure background fills the view + .navigationBarTitle("Settings", displayMode: .inline) + .fullScreenCover(isPresented: $showVIPPaymentView) { + VIPPaymentView() + } + .onDisappear { + self.showingAdvancedSettings = false // 确保隐藏组件在视图消失时不显示 + } + } + .toast(isPresented: $showingToast, dismissAfter: globalEnvironment.toastPresentMsNormal) { + HStack { + Image(systemName: "exclamationmark.bubble") + .foregroundColor(.yellow) + Text(toastText) + .foregroundColor(.black) + } + .padding() + .background(Color.white) + .cornerRadius(8) + .shadow(radius: 10) + } + // 加载指示器 + if isLoading { + LoadingView() + } + } + + // 定义隐藏功能 + private var gestureArea: some View { + Color.clear + .contentShape(Rectangle()) // 为透明色定义一个矩形可命中区域 + .frame(height: 150) // 可以根据需要调整手势区域的大小 + .gesture( + DragGesture(minimumDistance: 50, coordinateSpace: .global) + .onEnded { value in + // 检查水平移动距离是否足够长,同时限制垂直移动不太大 + if abs(value.translation.width) > 50 && abs(value.translation.height) < 100 { + // 切换高级设置的显示状态 + logger.info("Advanced Settings.") + showingAdvancedSettings.toggle() + } + } + ) + } + + // 实现保存设置的逻辑 + private func saveSettings() { + globalEnvironment.SetEnv(isSandBox: useSandboxEnvironment, isTestEnv: useDevelopmentEnvironment) + } + + @ViewBuilder + private func settingItem(icon: String, text: String, isBold: Bool = false) -> some View { + HStack { + Image(systemName: icon) // Icon on the left side + .foregroundColor(.gray) + Text(text) + .font(.subheadline) + .fontWeight(isBold ? .bold : .regular) // Conditional bold based on isBold parameter + .padding(4) + Spacer() + } + .padding() + .background(Color.white) // Background for each setting item + .padding(.horizontal, 0) // Add some horizontal padding + + // Indent divider to align with the text and extend to the edge + Divider() + .padding(.leading, 30) + .padding(.trailing, 10) + } + + private func openAppStoreFeedback() { + let appID = globalEnvironment.APPID // 替换为您的应用ID + let urlStr = "https://apps.apple.com/app/id\(appID)?action=write-review" + if let url = URL(string: urlStr) { + UIApplication.shared.open(url, options: [:], completionHandler: nil) + } + } + + func restorePurchase(){ + Task { + await iapManager.restorePurchases() { result in + switch result { + case .success(_): + self.toastText = "Restored Sucess!" + self.showingToast = true + case .failure(_): + self.toastText = "Oh, something went wrong, please try again later." + self.showingToast = true + } + } + } + } + // 模拟加载数据 + func loadData() { + isLoading = true + } + func loadComplete(){ + isLoading = false + } + +} + +struct SettingsView_Previews: PreviewProvider { + static var previews: some View { + SettingsView() + } +} + + #Preview { SettingsView() } diff --git a/AIGrammar/View/TranslateView.swift b/AIGrammar/View/TranslateView.swift index b2aadaf..1ea0abe 100644 --- a/AIGrammar/View/TranslateView.swift +++ b/AIGrammar/View/TranslateView.swift @@ -6,13 +6,381 @@ // import SwiftUI +import AVFoundation +import ToastUI -struct TranslateView: View { - var body: some View { - Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/) +struct SubmitTextEditor: UIViewRepresentable { + @Binding var text: String + var onCommit: () -> Void + + func makeUIView(context: Context) -> UITextView { + let textView = UITextView() + textView.delegate = context.coordinator + textView.font = UIFont.preferredFont(forTextStyle: .body) + textView.isScrollEnabled = true + textView.returnKeyType = .send // 设置键盘的返回键类型为发送 + textView.enablesReturnKeyAutomatically = true + + textView.backgroundColor = UIColor.white.withAlphaComponent(0.5) // 设置背景色为半透明的白色 + textView.layer.cornerRadius = 10 // 设置圆角 + textView.layer.borderColor = UIColor.blue.cgColor // 设置边框颜色为蓝色 + textView.layer.borderWidth = 1 // 设置边框宽度 + return textView + } + + func updateUIView(_ textView: UITextView, context: Context) { + textView.text = text + } + + func makeCoordinator() -> Coordinator { + Coordinator(self) + } + + class Coordinator: NSObject, UITextViewDelegate { + var parent: SubmitTextEditor + + init(_ parent: SubmitTextEditor) { + self.parent = parent + } + + func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool { + if text == "\n" { // 检测到换行符,即用户按下了发送键 + parent.onCommit() + textView.resignFirstResponder() // 收起键盘 + return false + } + return true + } + + func textViewDidChange(_ textView: UITextView) { + parent.text = textView.text + } } } +struct TranslateView: View { + @EnvironmentObject var globalEnv: GlobalEnvironment // 引入环境对象 + + @State private var inputText = "" + @State private var translations: [Translation] = [ + Translation(input: "This is demo text", translation: "这是演示文本"), + ] + @State private var showMenu = false // 控制下拉菜单的显示 + @FocusState private var isInputFocused: Bool // 状态变量来控制TextEditor的聚焦 + + @State private var currentLang = "Chinese" // 用来显示当前语言 + @State private var langCode = "chs" // 默认语言代码 + + @State private var isLoading = false // 控制加载指示器的显示 + @State private var showingToast = false // 控制是否显示toast + @State private var toastText = "" + + var body: some View { + NavigationView { + ZStack { + // 设置背景色 + Color.pink.opacity(0.2).edgesIgnoringSafeArea(.all) + + VStack { + ScrollViewReader { scrollView in + ScrollView { + VStack(spacing: 10) { + ForEach(translations) { translation in + TranslateCardView(translation: translation, onEdit: onEdit, onCopy: onCopy, onFeedback: onFeedBack) + .id(translation.id) // Assign an ID to each card view + } + } + .padding() + } + .onChange(of: translations) { _ in + if let lastId = translations.last?.id { + withAnimation { + scrollView.scrollTo(lastId, anchor: .bottom) // Scroll to the last translation + } + } + } + } + + SubmitTextEditor(text: $inputText, onCommit: { + // 清除输入内容前后的空格 + let trimmedText = inputText.trimmingCharacters(in: .whitespacesAndNewlines) + + // 检查是否为空 + if trimmedText.isEmpty { + showingToast = true + toastText = "Please enter some text." + return // 提前返回,不执行网络请求 + } + + // 检查长度是否超过200个字符 + if trimmedText.count > globalEnvironment.MaxLenTranslate { + showingToast = true + toastText = "Input too long. Please re-enter the text." + return // 提前返回,不执行网络请求 + } + + // 检查是否含有非法字符 + /* + if trimmedText.range(of: "[^a-zA-Z0-9 .,;:!?'\"@-]", options: .regularExpression) != nil { + showingToast = true + toastText = "Please enter valid characters." + return // 提前返回,不执行网络请求 + }*/ + if currentLang == "Chinese" { + let checkRes = CommonFunc.shared.validateInputEng(input: trimmedText) + if !checkRes.isValid { + showingToast = true + toastText = checkRes.message + return + } + } else { + let checkRes = CommonFunc.shared.validateChineseInput(input: trimmedText) + if !checkRes.isValid { + showingToast = true + toastText = checkRes.message + return + } + } + + loadData() + NetworkManager.shared.translate(inputText: trimmedText, lang: langCode) { result in + switch result { + case .success(let translation): + addTranslation(output: translation.translation) + logger.info("translate succ.", context: ["input": inputText, "output":translation.translation, "lang":langCode]) + case .failure(let error): + switch error { + case .businessError(let ret, let message): + // 业务错误,比如无免费可用次数等,需要处理逻辑。 + logger.error("Business error - Ret: \(ret), Message: \(message)") + DispatchQueue.main.async { + showingToast = true + switch ret{ + case globalEnvironment.RetCodeFreeLimited: + toastText = globalEnvironment.FreeLimitedToast + case globalEnvironment.RetCodeDirtyInput: + toastText = globalEnvironment.DirtyInputErrToast + default: + toastText = globalEnvironment.OtherServerErrToast + } + } + case .other(let error): + logger.error("network error occurred: \(error.localizedDescription)") + DispatchQueue.main.async { + showingToast = true + toastText = globalEnvironment.NetWorkErrToast + } + } + } + loadComplete() + } + }) + .frame(height: 100) // 指定高度 + .padding() + .focused($isInputFocused) + } + .toast(isPresented: $showingToast, dismissAfter: globalEnvironment.toastPresentMsNormal) { + HStack { + Image(systemName: "exclamationmark.bubble") + .foregroundColor(.yellow) + Text(toastText) + .foregroundColor(.black) + } + .padding() + .background(Color.white) + .cornerRadius(8) + .shadow(radius: 10) + } + + + // 加载指示器 + if isLoading { + LoadingView() + } + } + .onTapGesture { + // 点击任何非TextField区域时收起键盘 + self.hideKeyboard() + } + .navigationBarTitle("AI Translate", displayMode: .inline) + .navigationBarItems(trailing: Menu(currentLang) { + Button("English -> Chinese") { + currentLang = "Chinese" + langCode = "chs" + } + Button("Chinese -> English") { + currentLang = "English" + langCode = "eng" + } + }) + } + } + // 模拟加载数据 + func loadData() { + isLoading = true + } + func loadComplete(){ + isLoading = false + } + + private func onEdit(text : String){ + self.inputText = text + self.isInputFocused = true + } + private func onCopy(text : String){ + UIPasteboard.general.string = text + } + private func onFeedBack( feedback: TranslateFeedback){ + let fb:Bool = feedback.good ? true : false + NetworkManager.shared.sendFeedback(input: feedback.input, output: feedback.translation, isPositive: fb) + } + + private func addTranslation(output: String) { + let newTranslation = Translation(input: inputText, translation: output) + translations.append(newTranslation) + if translations.count > 10 { + translations.removeFirst() + } + inputText = "" // 清空输入框 + } + + private func hideKeyboard() { + UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil) + } +} + +/* +struct Translation: Identifiable, Equatable { + let id = UUID() + var input: String + var translation: String +} + */ + + +struct TranslateFeedback { + var input: String + var translation: String + var good: Bool = false + var bad: Bool = false +} + + +struct TranslateCardView: View { + var translation: Translation + var onEdit: (String) -> Void + var onCopy: (String) -> Void + var onFeedback: (TranslateFeedback) -> Void + let synthesizer = AVSpeechSynthesizer() + + var body: some View { + VStack { + // 上半部分 + VStack(alignment: .leading) { + Text("Input") + .font(.system(size: UIFont.systemFontSize * 0.8)) + .foregroundColor(Color.black.opacity(0.6)) + .padding(.top, 0) // 留出顶部间距 + .padding(.bottom,3) // 减少与原文之间的间距 + Text(translation.input) + .padding(.bottom, 3) + HStack { + /* + Button(action: { + speakText(translation.input) + }) { + Image(systemName: "speaker.wave.2") + .foregroundColor(.blue) + .padding(.trailing) + } + */ + Spacer() + Button(action: { + onEdit(translation.input) + }) { + Image(systemName: "square.and.pencil") + .foregroundColor(.gray) + .frame(width: 15, height: 15) // 缩小图标大小 + } + } + } + .padding([.horizontal, .top]) + .padding(.bottom, 3) // 减少与原文之间的间距 + + Divider().dashed() + + // 下半部分 + VStack(alignment: .leading) { + Text(translation.translation) + .padding(.vertical, 1) // 减少与分割线的间距 + .padding(.bottom, 3) // 减少与原文之间的间距 + HStack { + /* + Button(action: { + speakText(translation.translation) + }) { + Image(systemName: "speaker.wave.2") + .foregroundColor(.blue) + } + */ + Spacer() + HStack(spacing: 15) {// 减小图标之间的间距 + Button(action: { + onCopy(translation.translation) + }) { + Image(systemName: "doc.on.doc") + .resizable() + .scaledToFit() + .frame(width: 15, height: 15) // 缩小图标大小 + } + + Button(action: { + onFeedback(TranslateFeedback(input: translation.input, translation: translation.translation, good: true)) + }) { + Image(systemName: "hand.thumbsup") + .resizable() + .scaledToFit() + .frame(width: 15, height: 15) // 缩小图标大小 + } + + + Button(action: { + onFeedback(TranslateFeedback(input: translation.input, translation: translation.translation, bad: true)) + }) { + Image(systemName: "hand.thumbsdown") + .resizable() + .scaledToFit() + .frame(width: 15, height: 15) // 缩小图标大小 + } + } + } + .padding(.bottom, 1) // 减少与原文之间的间距 + } + .padding() + } + .padding(0) + .background(Color.gray.opacity(0.2)) // 设置整个卡片的背景色为LightGray + .overlay(RoundedRectangle(cornerRadius: 10).stroke(Color.gray, lineWidth: 1)) + } + + func speakText(_ text: String) { + let utterance = AVSpeechUtterance(string: text) + utterance.voice = AVSpeechSynthesisVoice(language: "en-US") + synthesizer.speak(utterance) + } +} + +extension View { + func dashed() -> some View { + self.overlay( + Rectangle() + .fill(Color.clear) + //.border( StrokeStyle(lineWidth: 1, dash: [5])) + ) + } +} + + #Preview { TranslateView() } diff --git a/AIGrammar/View/WordsView.swift b/AIGrammar/View/WordsView.swift index f83f4a4..75569ad 100644 --- a/AIGrammar/View/WordsView.swift +++ b/AIGrammar/View/WordsView.swift @@ -6,13 +6,211 @@ // import SwiftUI +import ToastUI struct WordsView: View { + @EnvironmentObject var globalEnv: GlobalEnvironment // 引入环境对象 + + @State private var searchText = "" + @State private var showingResults = false + @State private var res = WordDetails() + @State private var isLoading = false // 控制加载指示器的显示 + @State private var showingToast = false // 控制是否显示toast + @State private var toastText = "" + var body: some View { - Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/) + NavigationView { + ZStack { + // 设置背景色 + Color.pink.opacity(0.2).edgesIgnoringSafeArea(.all) + + VStack(spacing: 0) { + // 搜索框区域 + HStack { + TextField("word", text: $searchText, onCommit: fetchWordData) + .padding(.horizontal, 40) + .padding(.vertical, 10) + .background(Color(.systemGray6)) + .cornerRadius(8) + .font(.headline) + .overlay( + Image(systemName: "magnifyingglass") + .foregroundColor(.gray) + .frame(minWidth: 0, maxWidth: .infinity, alignment: .leading).padding(.leading, 8) + ) + Spacer() + + Button("Cancel") { + self.searchText = "" + self.showingResults = false + self.hideKeyboard() + } + } + .background(Color(.systemPink).opacity(0.2)) + .cornerRadius(5) // 边框圆角 + .shadow(radius: 2) // 轻微阴影 + .padding(8) + + + // 使用 Spacer 来推动搜索框保持在顶部 + if !showingResults { + Spacer() + } + + if showingResults { + // 结果显示区域 + HStack { + List { + Section(header: Text("Definitions").font(.headline)) { + ForEach(res.explanations, id: \.self) { definition in + Text(definition) + .font(.system(.subheadline)) + .padding(.leading, 4) + } + } + + Section(header: Text("Common Phrases").font(.headline)) { + ForEach(res.phrases, id: \.self) { phrase in + Text(phrase) + .font(.system(.subheadline)) + .padding(.leading, 4) + } + } + + Section(header: Text("Synonyms").font(.system(size: UIFont.systemFontSize * 1.2))) { + ForEach(res.synonyms, id: \.self) { synonym in + Text(synonym) + .font(.system(.subheadline)) + .padding(.leading, 4) + } + } + } + .padding(8) + .cornerRadius(5) // 边框圆角 + .frame(maxHeight: .infinity) // 限制ErrorCard占用半个屏幕 */ + } + .listStyle(GroupedListStyle()) // 使用分组列表样式,以适应背景 + } + } + .toast(isPresented: $showingToast, dismissAfter: globalEnvironment.toastPresentMsNormal) { + HStack { + Image(systemName: "exclamationmark.bubble") + .foregroundColor(.yellow) + Text(toastText) + .foregroundColor(.black) + } + .padding() + .background(Color.white) + .cornerRadius(8) + .shadow(radius: 10) + } + + // 加载指示器 + if isLoading { + LoadingView() + } + } + .onTapGesture { + // 点击任何非TextField区域时收起键盘 + self.hideKeyboard() + + } + .navigationBarTitle("Words", displayMode: .inline) + } + + } + + private func hideKeyboard() { + UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil) + } + + // 模拟加载数据 + func loadData() { + isLoading = true + } + func loadComplete(){ + isLoading = false + } + + private func fetchWordData() { + // 清除输入内容前后的空格 + let trimmedText = searchText.trimmingCharacters(in: .whitespacesAndNewlines) + + // 检查是否为空 + if trimmedText.isEmpty { + showingToast = true + toastText = "Please enter some text." + return // 提前返回,不执行网络请求 + } + + // 检查长度是否超过200个字符 + if trimmedText.count > globalEnvironment.MaxLenWords { + showingToast = true + toastText = "Input too long. Please re-enter the text." + return // 提前返回,不执行网络请求 + } + + // 检查是否含有非法字符 + let checkRes = CommonFunc.shared.validateInputEng(input: trimmedText, isSingleWord: true) + if !checkRes.isValid { + showingToast = true + toastText = checkRes.message + return + } + /* + if trimmedText.range(of: "[^a-zA-Z]", options: .regularExpression) != nil { + showingToast = true + toastText = "Please enter valid characters." + return // 提前返回,不执行网络请求 + } + */ + + loadData() // 添加提交的提示 + NetworkManager.shared.fetchWordDetails(inputText: trimmedText) { result in + switch result { + case .success(let wordDetails): + DispatchQueue.main.async { + res = wordDetails + showingResults = true + } + logger.info("fetch words succ.", context: ["word":wordDetails.word, "explanations":wordDetails.explanations, "phrases":wordDetails.phrases, "synonyms":wordDetails.synonyms]) + case .failure(let error): + switch error { + case .businessError(let ret, let message): + // 业务错误,比如无免费可用次数等,需要处理逻辑。 + logger.error("Business error - Ret: \(ret), Message: \(message)") + DispatchQueue.main.async { + showingToast = true + switch ret{ + case globalEnvironment.RetCodeFreeLimited: + toastText = globalEnvironment.FreeLimitedToast + case globalEnvironment.RetCodeDirtyInput: + toastText = globalEnvironment.DirtyInputErrToast + default: + toastText = globalEnvironment.OtherServerErrToast + } + } + case .other(let error): + logger.error("network error occurred: \(error.localizedDescription)") + DispatchQueue.main.async { + showingToast = true + toastText = globalEnvironment.NetWorkErrToast + } + } + } + loadComplete() + } } } +struct WordsView_Previews: PreviewProvider { + static var previews: some View { + WordsView() + } +} + + + #Preview { WordsView() } diff --git a/AIGrammar/ViewModel/Config.swift b/AIGrammar/ViewModel/Config.swift index f29b2d0..ff9c35d 100644 --- a/AIGrammar/ViewModel/Config.swift +++ b/AIGrammar/ViewModel/Config.swift @@ -6,3 +6,84 @@ // import Foundation +import SwiftUI + +/* +class GlobalConfig : ObservableObject{ + @Published var backgroundColor : UInt = 0xFFE4E1 + +} + */ + +class GlobalEnvironment: ObservableObject { + @Published var backgroundColor : UInt = 0xFFE4E1 + + init(){ + SetEnv(isSandBox: false, isTestEnv: false) + } + + @Published var deviceID: String = "" + @Published var userID: String = "" + @Published var userName: String = "" + @Published var GID: Int = 0 + @Published var isVip:Bool = false + + // APP 信息 + let APPID = "6504465465" + + // toast 展示事件 + let toastPresentMsNormal = 1.5 + let toastPresentMsLong = 3.0 + let toastPresentMsShot = 0.5 + + // 定义各功能的输入长度限制 + let MaxLenGrammarCheckFree = 200 + let MaxLenGrammarCheckVIP = 2000 + let MaxLenWords = 50 + let MaxLenTranslate = 200 + + // 错误码及错误提示定义 + let RetCodeFreeLimited = 101000 + let RetCodeDirtyInput = 101001 + let GrammarCheckOK = 102000 + let GrammarOKToast = "Congratulations! There are no errors in your input." + let FreeLimitedToast = "Your free usage has been used up. Please upgrade to PREMIUM for unlimited usage." + let NetWorkErrToast = "Network Error. Please try again later." + let OtherServerErrToast = "Sorry, something went wrong on the server. Please try again later." + let DirtyInputErrToast = "The text you entered contains content that does not comply with regulations. Please re-enter." + + var jwtSecret: String = "mCTf-JhNRnhaaGJy_x" + + var userTermsURL: String = "https://grammar.easyprompt8.com/about/" + + // 请求地址,区分环境。 + // var baseHost: String = "http://192.168.2.2:1080" + var baseHost: String = "https://api.easyprompt8.com" + + // 业务请求URL + var feedbackURL: String { "\(baseHost)/grammar/feedback" } + var translateURL: String { "\(baseHost)/grammar/translate" } + var dictURL: String { "\(baseHost)/grammar/words" } + var grammarURL: String { "\(baseHost)/grammar/grammar" } + + // 用户请求URL + var userURL: String { "\(baseHost)/user/get" } + + // 验证appstore购买 + var iapVerifyURL : String { "\(baseHost)/iap/verify" } + + // 设置运行环境 + func SetEnv(isSandBox: Bool, isTestEnv: Bool){ + if(isTestEnv){ + self.baseHost = "https://dev.easyprompt8.com" + }else { + self.baseHost = "https://api.easyprompt8.com" + } + logger.info("baseHost: \(self.baseHost)") + // 以后定义SandBox的功能,主要是商品列表的区分。 + } + +} + +// 全局实例 +let globalEnvironment = GlobalEnvironment() diff --git a/AIGrammar/ViewModel/GrammarData.swift b/AIGrammar/ViewModel/GrammarData.swift index 591bca5..df839e2 100644 --- a/AIGrammar/ViewModel/GrammarData.swift +++ b/AIGrammar/ViewModel/GrammarData.swift @@ -7,16 +7,127 @@ import Foundation -struct GrammarRes { - var tPlain : String - var tType : String - var tReason : String - var tCorrection : [String] +// 确保GrammarRes遵循Codable协议,以支持JSON解析 +struct GrammarRes: Codable { + var plain: String + var type: String + var reason: String + var correction: [String] } -struct GrammarData { - var tInputText : String - var tCorrectText : String - var tResult : [GrammarRes] +enum GrammarResType : String { + case ok = "ok" + case grammar = "grammar" + case spell = "spell" +} + + +// 因为tResult是动态的,所以我们定义GrammarData为类 +class GrammarData { + var inputText: String + var correctText: String + var results: [GrammarRes] + + init(inputText: String, correctText: String, tResult: [GrammarRes] = []) { + self.inputText = inputText + self.correctText = correctText + self.results = tResult + } + + // 方法:解析长字符串为tResult + func parseResult(from jsonString: String) -> Bool { + // 移除字符串中可能出现的多余逗号和空格 + let cleanedString = jsonString.trimmingCharacters(in: .whitespacesAndNewlines).replacingOccurrences(of: ", ]", with: "]") + + guard let jsonData = cleanedString.data(using: .utf8) else { + print("Error: Cannot create jsonData") + return false + } + + do { + // 解析JSON数据 + self.results = try JSONDecoder().decode([GrammarRes].self, from: jsonData) + return true + } catch { + print("Error: \(error)") + return false + } + } +} + +extension GrammarData { + // 静态方法返回包含Demo数据的GrammarData实例 + static func demoInstance() -> GrammarData { + //let demoInputText = "This is a demo text with more complex grammar checking algorithm of the pro version. This app have been designed to help you to write correctly and appear more professional. I work really hard to make this app as god as possible. If you are happy with the results, please consider supporting the app by subscribing to the PRO plan. The text writen here is to show you how much the app is capable of with the pro version. is this something you would like to use? " + + let demoInputText = "Paris are hosting the Olypmic Games in 2024. Athletes from arround the world comes to compet in many sports, wich makes it an excting event to watch." + + let demoCorrectText = demoInputText + + // 假设的Demo错误数据 + let demoErrors = [ + GrammarRes(plain: "This is a demo text with more complex grammar checking algorithm of the pro version.", + type: "ok", + reason: "", + correction: []), + + GrammarRes( + plain: "This app have", + type: "grammar", + reason: "subject-verb agreement", + correction: ["This app has"] + ), + GrammarRes( + plain: "been designed to help you to write correctly and appear more professional.", + type: "ok", + reason: "", + correction: [] + ), + GrammarRes( + plain: "I work really hard", + type: "ok", + reason: "", + correction: [] + ), + GrammarRes( + plain: "to make this app as god", + type: "spell", + reason: "typo", + correction: ["as good"] + ), + GrammarRes( + plain: "as possible.", + type: "ok", + reason: "", + correction: [] + ), + GrammarRes( + plain: "If you are happy with the results, please consider supporting the app by subscribing to the PRO plan.", + type: "ok", + reason: "", + correction: [] + ), + GrammarRes( + plain: "The text writen", + type: "spell", + reason: "misspelling", + correction: ["written"] + ), + GrammarRes( + plain: "here is to show you how much the app is capable of with the pro version.", + type: "ok", + reason: "", + correction: [] + ), + GrammarRes( + plain: "is this something you would like to use?", + type: "grammar", + reason: "capitalization", + correction: ["Is this something you would like to use?"] + ) + ] + + return GrammarData(inputText: demoInputText, correctText: demoCorrectText, tResult: demoErrors) + } } diff --git a/AIGrammar/lib/ColorToString.swift b/AIGrammar/lib/ColorToString.swift index a6070d6..987b42b 100644 --- a/AIGrammar/lib/ColorToString.swift +++ b/AIGrammar/lib/ColorToString.swift @@ -6,3 +6,16 @@ // import Foundation +import SwiftUI + +extension Color { + static func hex(_ hex: UInt, alpha: Double = 1.0) -> Color { + return Color( + red: Double((hex >> 16) & 0xFF) / 255.0, + green: Double((hex >> 8) & 0xFF) / 255.0, + blue: Double(hex & 0xFF) / 255.0, + opacity: alpha + ) + } +} + diff --git a/AIGrammar/lib/CommFunc.swift b/AIGrammar/lib/CommFunc.swift index 3115377..3d86ade 100644 --- a/AIGrammar/lib/CommFunc.swift +++ b/AIGrammar/lib/CommFunc.swift @@ -6,3 +6,55 @@ // import Foundation + +class CommonFunc { + static let shared = CommonFunc() + private let profanityList: [String] = ["fuck", "shit", "porn", "习近平", "鸡巴", "阴茎"] // Example profanity words + + private init() {} // Private initializer to ensure singleton usage + + func validateInputEng(input: String, isSingleWord: Bool = false) -> (isValid: Bool, message: String) { + // Check for profanity + for badWord in profanityList { + if input.lowercased().contains(badWord) { + return (false, globalEnvironment.DirtyInputErrToast) + } + } + + // Check characters based on the isSingleWord flag + let pattern = isSingleWord ? "^[A-Za-z]+$" : "^[A-Za-z0-9 .,;:!?'\"@-]+$" + let regex = try! NSRegularExpression(pattern: pattern) + let range = NSRange(location: 0, length: input.utf16.count) + + if regex.firstMatch(in: input, options: [], range: range) == nil { + let characterSet = isSingleWord ? "english letters" : "english letters, numbers, and punctuation" + return (false, "Input should only contain \(characterSet).") + } + + return (true, "Input is valid.") + } + + func validateChineseInput(input: String) -> (isValid: Bool, message: String) { + // Check for profanity + for word in profanityList { + if input.contains(word) { + return (false, globalEnvironment.DirtyInputErrToast) + } + } + + // Check if all characters are valid Chinese characters or punctuation + for character in input { + if !character.isPunctuation && !(0x4E00...0x9FFF).contains(character.unicodeScalars.first!.value) && + !(0x3400...0x4DBF).contains(character.unicodeScalars.first!.value) && + !(0x20000...0x2A6DF).contains(character.unicodeScalars.first!.value) { + // 既不是中文,又不是英文,返回错误 + let res = self.validateInputEng(input: input) + if !res.isValid{ + return (false, "Input contains invalid characters.") + } + } + } + return (true, "Input is valid.") + } + +} diff --git a/AIGrammar/lib/IapManager.swift b/AIGrammar/lib/IapManager.swift index c308a8e..feeca42 100644 --- a/AIGrammar/lib/IapManager.swift +++ b/AIGrammar/lib/IapManager.swift @@ -6,3 +6,228 @@ // import Foundation +import StoreKit +import SwiftUI +import SwiftyBeaver + +enum IAPProduct: String, CaseIterable { + case premiumFeature1 = "grammar_1_week" + case premiumFeature2 = "grammar_1_month" + case premiumFeature3 = "grammar_1_year" + + var weight: Int { + switch self { + case .premiumFeature1: + return 1 + case .premiumFeature2: + return 2 + case .premiumFeature3: + return 3 + } + } +} + + +class IAPManager: ObservableObject { + @Published var products: [Product] = [] // 获取商品列表 + @Published var purchasedProducts: [Product] = [] // 已购买的商品列表 + private var processedTransactionIDs = Set() // 已经发到服务端校验的交易ID + + init() { + Task { + await requestProducts() + await updatePurchasedProducts() + await listenForTransactions() + } + } + + // 获取商品列表,并按照权重排序。 + func requestProducts() async { + do { + let products = try await Product.products(for: IAPProduct.allCases.map { $0.rawValue }) + DispatchQueue.main.async { + // 按照产品的权重排序 + self.products = products.sorted { product1, product2 in + let weight1 = IAPProduct(rawValue: product1.id)?.weight ?? 0 + let weight2 = IAPProduct(rawValue: product2.id)?.weight ?? 0 + return weight1 < weight2 + } + + // 打印每个产品及其相关变量 + for product in self.products { + if let receiptData = try? product.jsonRepresentation { + if let jsonString = String(data: receiptData, encoding: .utf8) { + logger.info("product details for \(product.id): \(jsonString)") + } else { + logger.info("product details for \(product.id): ", context: ["Tile": product.displayName, "Price": product.price, "DisplayPrice": product.displayPrice]) + } + }else { + logger.info("product details for \(product.id): ", context: ["Tile": product.displayName, "Price": product.price, "DisplayPrice": product.displayPrice]) + } + } + } + } catch { + logger.error("Failed to fetch products: \(error.localizedDescription)") + } + } + + // 商品购买 + func buy(product: Product, completion: @escaping (Result) -> Void) async { + do { + let result = try await product.purchase() + switch result { + case .success(let verification): + if case .verified(let transaction) = verification { + // 获取购买信息 + let appAccountToken = transaction.appAccountToken + let productId = transaction.productID + let transactionId = transaction.id + + // 打印日志 + if let receiptData = try? transaction.jsonRepresentation { + if let jsonString = String(data: receiptData, encoding: .utf8) { + logger.info("Transaction details for \(transactionId): \(jsonString)") + } else { + logger.info("Transaction details for \(transactionId)", context: ["ProductID" : productId, "AppAccountToken" : appAccountToken ?? "", "OriTransactionID":transaction.originalID.value]) + } + + } else { + logger.info("Transaction details for \(transactionId)", context: ["ProductID" : productId, "AppAccountToken" : appAccountToken ?? "", "OriTransactionID":transaction.originalID.value]) + } + + // 发送到服务端进行校验 + await validateReceipt(receiptData: transaction.jsonRepresentation, appAccountToken: appAccountToken, productId: productId, transactionId: transactionId, env: transaction.environment.rawValue) + + await transaction.finish() + await updatePurchasedProducts() + + DispatchQueue.main.async { + // 购买成功的消息 + completion(.success("Purchase Successful")) + } + } + case .userCancelled: + // 用户取消支付,这个行为最好能报上去,或者做点其他营销动作 + logger.error("User cancelled the purchase.") + + DispatchQueue.main.async { + // 用户取消购买 + completion(.failure(NSError(domain: "", code: 0, userInfo: [NSLocalizedDescriptionKey: "Purchase Cancelled"]))) + } + case .pending: + // 需要上报上去 + logger.error("Purchase is pending.") + + DispatchQueue.main.async { + // 购买失败 + completion(.failure(NSError(domain: "", code: 0, userInfo: [NSLocalizedDescriptionKey: "Purchase Pending"]))) + } + default: + break + } + } catch { + logger.error("Failed to purchase product: \(error.localizedDescription)") + + DispatchQueue.main.async { + completion(.failure(error)) + } + } + } + + // https://developer.apple.com/documentation/storekit/transaction/3851204-currententitlements + // A sequence of the latest transactions that entitle a user to in-app purchases and subscriptions. + func updatePurchasedProducts() async { + for await result in Transaction.currentEntitlements { + if case .verified(let transaction) = result { + if let product = products.first(where: { $0.id == transaction.productID }) { + DispatchQueue.main.async { + self.purchasedProducts.append(product) + } + } + } + } + } + + // https://developer.apple.com/documentation/storekit/transaction/3851206-updates + // The asynchronous sequence that emits a transaction when the system creates or updates transactions that occur outside of the app or on other devices. + // Note that after a successful in-app purchase on the same device, StoreKit returns the transaction through Product.PurchaseResult.success(_:). + func listenForTransactions() async { + for await transactionResult in Transaction.updates { + if case .verified(let transaction) = transactionResult { + if let product = products.first(where: { $0.id == transaction.productID }) { + DispatchQueue.main.async { + self.purchasedProducts.append(product) + } + } + + // 获取购买信息 + let appAccountToken = transaction.appAccountToken + let productId = transaction.productID + let transactionId = transaction.id + + // 打印日志 + if let receiptData = try? transaction.jsonRepresentation { + if let jsonString = String(data: receiptData, encoding: .utf8) { + logger.info("Transaction details for \(transactionId): \(jsonString)") + } else { + logger.info("Transaction details for \(transactionId)", context: ["ProductID" : productId, "AppAccountToken" : appAccountToken ?? "", "OriTransactionID":transaction.originalID.value]) + } + + } else { + logger.info("Transaction details for \(transactionId)", context: ["ProductID" : productId, "AppAccountToken" : appAccountToken ?? "", "OriTransactionID":transaction.originalID.value]) + } + + // 发送到服务端进行校验 + await validateReceipt(receiptData: transaction.jsonRepresentation, appAccountToken: appAccountToken, productId: productId, transactionId: transactionId, env: transaction.environment.rawValue) + await transaction.finish() + } + } + } + + // https://developer.apple.com/documentation/storekit/appstore/3791906-sync + // Synchronizes your app’s transaction information and subscription status with information from the App Store. + func restorePurchases(completion: @escaping (Result) -> Void) async { + do { + try await AppStore.sync() + await updatePurchasedProducts() + completion(.success("Purchase Successful")) + logger.info("Purchases restored") + } catch { + logger.error("Failed to restore purchases: \(error.localizedDescription)") + completion(.failure(NSError(domain: "", code: 0, userInfo: [NSLocalizedDescriptionKey: "Purchase Pending"]))) + } + } + + // 服务端校验收据 + func validateReceipt(receiptData: Data, appAccountToken: UUID?, productId: String, transactionId: UInt64, env: String) async { + // 调用NetworkManager中的IapVerify进行收据验证 + NetworkManager.shared.IapVerify(receiptData: receiptData, appAccountToken: appAccountToken, productId: productId, transactionId: transactionId, env: env) { result in + switch result { + case .success(let response): + + DispatchQueue.main.async { + logger.info("Receipt verification succeeded: \(response.ret)", context: ["ProductID":productId, "TransactionID":transactionId]) + + // 处理后续逻辑 + self.updatePurchasedStatus(productId: productId) + } + case .failure(let error): + // 处理错误情况 + DispatchQueue.main.async { + switch error { + case .businessError(let ret, let message): + logger.error("Business error - Ret: \(ret), Message: \(message)", context: ["ProductID":productId, "TransactionID":transactionId]) + case .other(let error): + logger.error("network occurred: \(error.localizedDescription)", context: ["ProductID":productId, "TransactionID":transactionId]) + } + } + } + } + } + + // 示例方法,用于更新购买状态 + private func updatePurchasedStatus(productId: String) { + // 逻辑来标记产品为已购买,例如更新本地数据库、发送通知等 + } +} + diff --git a/AIGrammar/lib/InitAPP.swift b/AIGrammar/lib/InitAPP.swift index 04161a6..61e8268 100644 --- a/AIGrammar/lib/InitAPP.swift +++ b/AIGrammar/lib/InitAPP.swift @@ -6,5 +6,88 @@ // import Foundation +import Alamofire +import TrustDecision +import SwiftJWT +import SwiftyBeaver +class InitApp { + static let shared = InitApp(environment: globalEnvironment) + + private let userDefaults = UserDefaults.standard + private let deviceIDKey = "DeviceID" + private let vipStatusKey = "VIPStatus" + private var deviceID: String = "" + private var vipStatus: Bool = false + var environment: GlobalEnvironment + + init(environment: GlobalEnvironment) { + self.environment = environment + initializeApp() + } + + private func initializeApp() { + fetchDeviceID() + } + + private func fetchDeviceID() { + if let savedDeviceID = userDefaults.string(forKey: deviceIDKey) { + self.environment.deviceID = savedDeviceID + logger.info("DeviceID from UserDefaults: \(savedDeviceID)") + // 获取用户状态 + getUser() + } else { + getDeviceIDFromTrustDecision() + } + } + + private func getDeviceIDFromTrustDecision() { + var options = [String : NSObject]() + let responseCallback: ([String : Any]) -> Void = { response in + DispatchQueue.main.async { // 确保在主线程更新UI + if let deviceId = response["device_id"] as? String { + self.deviceID = deviceId + self.userDefaults.set(deviceId, forKey: self.deviceIDKey) + self.environment.deviceID = deviceId + logger.info("Device ID from TrustDecision: \(self.deviceID)") + + // 获取VIP状态 + self.getUser() + } + } + } + options["callback"] = unsafeBitCast(responseCallback as @convention(block) ([String : Any]) -> Void, to: AnyObject.self) as? NSObject + let manager = TDMobRiskManager.sharedManager() + manager?.pointee.initWithOptions(options) + } + + + private func getUser() { + NetworkManager.shared.getUserProfile() { result in + DispatchQueue.main.async { + switch result { + case .success(let userData): + self.vipStatus = userData.vip == 1 + self.environment.GID = userData.id + self.environment.userID = userData.userid + self.environment.userName = userData.username + self.environment.isVip = userData.vip == 1 + logger.info("getUserProfile: ID: \(userData.id), userID: \(userData.userid), userName: \(userData.username), vip: \(userData.vip)") + case .failure(let error): + switch error { + case .businessError(let ret, let message): + logger.error("Business error - Ret: \(ret), Message: \(message)") + case .other(let error): + logger.error("network occurred: \(error.localizedDescription)") + } + } + } + } + } + + func refreshUserInfo(){ + getUser() + } + +} diff --git a/AIGrammar/lib/LogManager.swift b/AIGrammar/lib/LogManager.swift index 3fb527a..7932b5a 100644 --- a/AIGrammar/lib/LogManager.swift +++ b/AIGrammar/lib/LogManager.swift @@ -6,3 +6,38 @@ // import Foundation +import SwiftyBeaver + +let logger = SwiftyBeaver.self + +func setupLogging() { + let console = ConsoleDestination() + let file = FileDestination() + //file.logFileURL = URL(fileURLWithPath: "/path/to/your/log/file.log") + + // use custom format and set console output to short time, log level & message + // console.format = "$DHH:mm:ss$d $L $M" + // or use this for JSON output: + // console.format = "$J" + + // In Xcode 15, specifying the logging method as .logger to display color, subsystem, and category information in the console.(Relies on the OSLog API) + //console.logPrintWay = .logger(subsystem: "Main", category: "UI") + + // If you prefer not to use the OSLog API, you can use print instead. + // console.logPrintWay = .print + + + console.format = "$DHH:mm:ss$d $C$L$c $N.$F:$l - $M" + + // 自定义颜色 + console.levelColor.verbose = "⚪️ " // White + console.levelColor.debug = "🔵 " // Blue + console.levelColor.info = "🟢 " // Green + console.levelColor.warning = "🟡 " // Yellow + console.levelColor.error = "🔴 " // Red + + logger.addDestination(console) + logger.addDestination(file) + +} + diff --git a/AIGrammar/lib/NetworkManager.swift b/AIGrammar/lib/NetworkManager.swift index a73c3be..8cdfea6 100644 --- a/AIGrammar/lib/NetworkManager.swift +++ b/AIGrammar/lib/NetworkManager.swift @@ -10,35 +10,50 @@ import Alamofire import SwiftJWT import SwiftyBeaver +// jwt字段 struct MyClaims: Claims { var deviceID: String var gid: Int var exp1: Int } +// 接口的统一返回格式。{"ret":%d, "message":"%s", "data":jsonData} struct APIResponse: Decodable { let ret: Int let message: String let data: T? } +// 错误处理的封装 +enum NetworkError: Error { + case businessError(ret: Int, message: String) + case other(Error) +} + +// 改错功能,与服务器应答的数据结构;因为直接返回了数组,暂无使用 +struct GrammarCheckRsp : Codable{ + let data: [GrammarRes] +} + +// 翻译功能,与页面交互的数据结构 struct Translation: Identifiable, Codable, Equatable { var id = UUID() var input: String var translation: String } - +// 翻译功能,服务器应答的数据结构 struct TranslationResponse: Codable { let translation: String } +// 单词解析,与页面交互的数据结构 struct WordDetails { var word: String = "" var explanations: [String] = [] var phrases: [String] = [] var synonyms: [String] = [] } - +// 单词解析,服务器应答的数据结构 struct WordDetailsResponse: Codable { let word: String let explain: [String]? @@ -56,6 +71,7 @@ struct WordDetailsResponse: Codable { } } +// 查询用户信息,不涉及页面交互,统一使用服务端的返回结构 struct VIPStatusResponse: Codable { let id: Int let userid: String @@ -63,73 +79,122 @@ struct VIPStatusResponse: Codable { let vip: Int } +// 内购验证,不涉及页面交互,统一使用服务端的返回结构 +struct IAPVerifyRsp: Codable { + let ret: String + //let productType : String +} + +// 定义统一的网络交互,采用单例模式。 struct NetworkManager { static let shared = NetworkManager() private let jwtSecret = globalEnvironment.jwtSecret + // 获取时区 + func getHeaderTimezoneInfo() -> [String: String] { + let timezone = TimeZone.current + let timezoneIdentifier = timezone.identifier // 时区标识符,如"America/New_York" + let secondsFromGMT = timezone.secondsFromGMT() // 与GMT的秒数差 + + return [ + "timezone": timezoneIdentifier, + "secondsfromgmt": String(secondsFromGMT) + ] + } + // 语法检查功能 - func checkGrammar(inputText: String, completion: @escaping ([GrammarRes]?, Error?) -> Void) { + func checkGrammar(inputText: String, completion: @escaping (Result<[GrammarRes], NetworkError>) -> Void) { let url = globalEnvironment.grammarURL - let headers: HTTPHeaders = createAuthorizationHeader() + var headers: HTTPHeaders = createAuthorizationHeader() + let timezoneHeaders = getHeaderTimezoneInfo() + headers.add(name: "timezone", value: timezoneHeaders["timezone"]!) + headers.add(name: "secondsfromgmt", value: timezoneHeaders["secondsfromgmt"]!) + let parameters: [String: Any] = [ "input": inputText, "lang": "eng" ] - AF.request(url, method: .post, parameters: parameters, encoding: URLEncoding.httpBody, headers: headers) - .responseDecodable(of: [GrammarRes].self) { response in - switch response.result { - case .success(let results): - completion(results, nil) - case .failure(let error): - completion(nil, error) + + // 使用统一封装的网络请求,并解返回包 + performRequest( + endpoint: url, + parameters: parameters, + method: .post, + encoding: URLEncoding.httpBody, + headers: headers, + completion: { (result: Result<[GrammarRes], NetworkError>) in + switch result { + case .success(let results): + completion(.success(results)) + case .failure(let error): + // 透传错误 + completion(.failure(error)) + } } - } + ) } // 单词的词典功能 - func fetchWordDetails(inputText: String, lang: String = "eng", completion: @escaping (Result) -> Void) { + func fetchWordDetails(inputText: String, lang: String = "eng", completion: @escaping (Result) -> Void) { guard !inputText.isEmpty else { return } let parameters: [String: Any] = ["input": inputText, "lang": lang] let url = globalEnvironment.dictURL - let headers: HTTPHeaders = createAuthorizationHeader() + var headers: HTTPHeaders = createAuthorizationHeader() + let timezoneHeaders = getHeaderTimezoneInfo() + headers.add(name: "timezone", value: timezoneHeaders["timezone"]!) + headers.add(name: "secondsfromgmt", value: timezoneHeaders["secondsfromgmt"]!) - AF.request(url, method: .post, parameters: parameters, encoding: URLEncoding.httpBody, headers: headers) - .responseDecodable(of: WordDetailsResponse.self) { response in - switch response.result { + // 使用统一封装的网络请求,并解返回包 + performRequest( + endpoint: url, + parameters: parameters, + method: .post, + encoding: URLEncoding.httpBody, + headers: headers, + completion: { (result: Result) in + switch result { case .success(let detailsResponse): - print("Success: Received data for \(detailsResponse.word)") let details = detailsResponse.toWordDetails() // Convert here completion(.success(details)) case .failure(let error): - print("Error: \(error)") - print("Response Data: \(String(data: response.data ?? Data(), encoding: .utf8) ?? "No data")") - + // 透传错误 completion(.failure(error)) } } + ) } // 翻译功能 - func translate(inputText: String, lang: String = "chs", completion: @escaping (Result) -> Void) { - + func translate(inputText: String, lang: String = "chs", completion: @escaping (Result) -> Void) { guard !inputText.isEmpty else { return } let url = globalEnvironment.translateURL let parameters: [String: Any] = ["input": inputText, "lang": lang] - let headers: HTTPHeaders = createAuthorizationHeader() + var headers: HTTPHeaders = createAuthorizationHeader() + let timezoneHeaders = getHeaderTimezoneInfo() + headers.add(name: "timezone", value: timezoneHeaders["timezone"]!) + headers.add(name: "secondsfromgmt", value: timezoneHeaders["secondsfromgmt"]!) - AF.request(url, method: .post, parameters: parameters, encoding: URLEncoding.httpBody, headers: headers) - .responseDecodable(of: TranslationResponse.self) { response in - switch response.result { + // 使用统一封装的网络请求,并解返回包 + performRequest( + endpoint: url, + parameters: parameters, + method: .post, + encoding: URLEncoding.httpBody, + headers: headers, + completion: { (result: Result) in + switch result { case .success(let translationResponse): let newTranslation = Translation(input: inputText, translation: translationResponse.translation) completion(.success(newTranslation)) case .failure(let error): + // 透传错误 completion(.failure(error)) } } + ) } // 翻译的点赞、点踩功能 @@ -146,16 +211,15 @@ struct NetworkManager { AF.request(url, method: .post, parameters: parameters, encoding: URLEncoding.httpBody, headers: headers).response { response in switch response.result { case .success(let data): - print("Feedback sent successfully: \(String(describing: data))") + logger.info("Feedback sent successfully.", context: ["input":input, "isPositive": isPositive]) case .failure(let error): - print("Error sending feedback: \(error)") + logger.error("Error sending feedback: \(error)", context: ["input":input, "isPositive": isPositive]) } } - } // 查询VIP状态 - func getUserProfile(completion: @escaping (Result) -> Void) { + func getUserProfile(completion: @escaping (Result) -> Void) { let url = globalEnvironment.userURL let parameters: [String: Any] = [:] @@ -167,27 +231,52 @@ struct NetworkManager { method: .post, encoding: URLEncoding.httpBody, headers: headers, // 示例:使用JWT token - completion: { (result: Result) in + completion: { (result: Result) in switch result { case .success(let vipData): - logger.info("VIP Status: \(vipData.vip)") completion(.success(vipData)) case .failure(let error): - logger.error("Error: \(error.localizedDescription)") + // 无需处理,直接透传 completion(.failure(error)) } } ) } + + // 查询VIP订单 + func IapVerify(receiptData: Data, appAccountToken: UUID?, productId: String, transactionId: UInt64, env: String, completion: @escaping (Result) -> Void) { + let url = globalEnvironment.iapVerifyURL + let parameters: [String: Any] = ["transid":transactionId, "appaccounttoken": appAccountToken ?? "", "productid":productId, "receiptdata":receiptData, "env": env] + let headers: HTTPHeaders = createAuthorizationHeader() + + performRequest( + endpoint: url, + parameters: parameters, + method: .post, + encoding: URLEncoding.httpBody, + headers: headers, // 示例:使用JWT token + completion: { (result: Result) in + switch result { + case .success(let rsp): + completion(.success(rsp)) + case .failure(let error): + // 直接透传 + completion(.failure(error)) + } + } + ) + } + + // 发起网络请求的统一封装。包含统一应答的解包,异常处理等。 func performRequest( endpoint: String, parameters: Parameters, // 明确使用 Alamofire 的 Parameters 类型 method: HTTPMethod = .post, encoding: URLEncoding = .httpBody, headers: HTTPHeaders? = nil, - completion: @escaping (Result) -> Void + completion: @escaping (Result) -> Void ) { let url = endpoint let defaultHeaders: HTTPHeaders = [.contentType("application/x-www-form-urlencoded")] @@ -200,15 +289,18 @@ struct NetworkManager { if apiResponse.ret == 0, let data = apiResponse.data { completion(.success(data)) } else { - let error = NSError(domain: "", code: apiResponse.ret, userInfo: [NSLocalizedDescriptionKey: apiResponse.message]) - completion(.failure(error)) + completion(.failure(.businessError(ret: apiResponse.ret, message: apiResponse.message))) } case .failure(let error): - completion(.failure(error)) + if let httpResponse = response.response { + logger.error("network erorr.", context: ["url":url, "StatusCode":httpResponse.statusCode]) + } + completion(.failure(.other(error))) } } } + // 添加jwt token private func createAuthorizationHeader() -> HTTPHeaders { // Generate JWT and return headers guard let jwtToken = generateJWT(deviceID: globalEnvironment.deviceID, gID: globalEnvironment.GID, jwtSecret: jwtSecret) else { diff --git a/Podfile b/Podfile new file mode 100644 index 0000000..ed0a4eb --- /dev/null +++ b/Podfile @@ -0,0 +1,26 @@ +# Uncomment the next line to define a global platform for your project +# platform :ios, '9.0' + +target 'AIGrammar' do + # Comment the next line if you don't want to use dynamic frameworks + use_frameworks! + + # Pods for AIGrammar + # 当遇到 Pod install 出错,CDN资源不可用时,可以指定 git地址安装 + pod 'TrustDecision', '1.4' + pod 'Alamofire', :git => 'https://github.com/Alamofire/Alamofire.git' + pod 'SwiftJWT' + pod 'SwiftyBeaver', :git => 'https://github.com/SwiftyBeaver/SwiftyBeaver.git' + + + + target 'AIGrammarTests' do + inherit! :search_paths + # Pods for testing + end + + target 'AIGrammarUITests' do + # Pods for testing + end + +end diff --git a/Podfile.lock b/Podfile.lock new file mode 100644 index 0000000..44fc545 --- /dev/null +++ b/Podfile.lock @@ -0,0 +1,65 @@ +PODS: + - Alamofire (5.9.1) + - BlueCryptor (1.0.32) + - BlueECC (1.2.5) + - BlueRSA (1.0.200) + - KituraContracts (1.2.1): + - LoggerAPI (~> 1.7) + - LoggerAPI (1.9.200): + - Logging (~> 1.1) + - Logging (1.4.0) + - SwiftJWT (3.6.200): + - BlueCryptor (~> 1.0) + - BlueECC (~> 1.1) + - BlueRSA (~> 1.0) + - KituraContracts (~> 1.2) + - LoggerAPI (~> 1.7) + - SwiftyBeaver (2.1.1) + - TrustDecision (1.4) + +DEPENDENCIES: + - Alamofire (from `https://github.com/Alamofire/Alamofire.git`) + - SwiftJWT + - SwiftyBeaver (from `https://github.com/SwiftyBeaver/SwiftyBeaver.git`) + - TrustDecision (= 1.4) + +SPEC REPOS: + trunk: + - BlueCryptor + - BlueECC + - BlueRSA + - KituraContracts + - LoggerAPI + - Logging + - SwiftJWT + - TrustDecision + +EXTERNAL SOURCES: + Alamofire: + :git: https://github.com/Alamofire/Alamofire.git + SwiftyBeaver: + :git: https://github.com/SwiftyBeaver/SwiftyBeaver.git + +CHECKOUT OPTIONS: + Alamofire: + :commit: 6b3296bd3e975fb3e04cdf22761ddf44df7157a5 + :git: https://github.com/Alamofire/Alamofire.git + SwiftyBeaver: + :commit: 8e06e2ea17b8fc503f95e2d6026d503834413e60 + :git: https://github.com/SwiftyBeaver/SwiftyBeaver.git + +SPEC CHECKSUMS: + Alamofire: f36a35757af4587d8e4f4bfa223ad10be2422b8c + BlueCryptor: b0aee3d9b8f367b49b30de11cda90e1735571c24 + BlueECC: 0d18e93347d3ec6d41416de21c1ffa4d4cd3c2cc + BlueRSA: dfeef51db96bcc4edec654956c1581adbda4e6a3 + KituraContracts: e845e60dc8627ad0a76fa55ef20a45451d8f830b + LoggerAPI: ad9c4a6f1e32f518fdb43a1347ac14d765ab5e3d + Logging: beeb016c9c80cf77042d62e83495816847ef108b + SwiftJWT: 88c412708f58c169d431d344c87bc79a87c830ae + SwiftyBeaver: ade157e4f857812e7d7f15f2e3396bb8733f8a1c + TrustDecision: 201ad3e5e834567998dffc22536ca36d600beb16 + +PODFILE CHECKSUM: f88829971628b679a467fb6f7d0bc97d7284c177 + +COCOAPODS: 1.15.2 diff --git a/Pods/Alamofire/LICENSE b/Pods/Alamofire/LICENSE new file mode 100644 index 0000000..cae030a --- /dev/null +++ b/Pods/Alamofire/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2014-2022 Alamofire Software Foundation (http://alamofire.org/) + +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. diff --git a/Pods/Alamofire/README.md b/Pods/Alamofire/README.md new file mode 100644 index 0000000..3a454ab --- /dev/null +++ b/Pods/Alamofire/README.md @@ -0,0 +1,269 @@ +![Alamofire: Elegant Networking in Swift](https://raw.githubusercontent.com/Alamofire/Alamofire/master/Resources/AlamofireLogo.png) + +[![Swift](https://img.shields.io/badge/Swift-5.7_5.8_5.9-orange?style=flat-square)](https://img.shields.io/badge/Swift-5.7_5.8_5.9-Orange?style=flat-square) +[![Platforms](https://img.shields.io/badge/Platforms-macOS_iOS_tvOS_watchOS_visionOS_Linux_Windows_Android-yellowgreen?style=flat-square)](https://img.shields.io/badge/Platforms-macOS_iOS_tvOS_watchOS_vision_OS_Linux_Windows_Android-Green?style=flat-square) +[![CocoaPods Compatible](https://img.shields.io/cocoapods/v/Alamofire.svg?style=flat-square)](https://img.shields.io/cocoapods/v/Alamofire.svg) +[![Carthage Compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat-square)](https://github.com/Carthage/Carthage) +[![Swift Package Manager](https://img.shields.io/badge/Swift_Package_Manager-compatible-orange?style=flat-square)](https://img.shields.io/badge/Swift_Package_Manager-compatible-orange?style=flat-square) +[![Swift Forums](https://img.shields.io/badge/Swift_Forums-Alamofire-orange?style=flat-square)](https://forums.swift.org/c/related-projects/alamofire/37) + +Alamofire is an HTTP networking library written in Swift. + +- [Features](#features) +- [Component Libraries](#component-libraries) +- [Requirements](#requirements) +- [Migration Guides](#migration-guides) +- [Communication](#communication) +- [Installation](#installation) +- [Contributing](#contributing) +- [Usage](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#using-alamofire) + - [**Introduction -**](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#introduction) [Making Requests](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#making-requests), [Response Handling](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#response-handling), [Response Validation](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#response-validation), [Response Caching](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#response-caching) + - **HTTP -** [HTTP Methods](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#http-methods), [Parameters and Parameter Encoder](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md##request-parameters-and-parameter-encoders), [HTTP Headers](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#http-headers), [Authentication](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#authentication) + - **Large Data -** [Downloading Data to a File](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#downloading-data-to-a-file), [Uploading Data to a Server](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#uploading-data-to-a-server) + - **Tools -** [Statistical Metrics](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#statistical-metrics), [cURL Command Output](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#curl-command-output) +- [Advanced Usage](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md) + - **URL Session -** [Session Manager](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md#session), [Session Delegate](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md#sessiondelegate), [Request](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md#request) + - **Routing -** [Routing Requests](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md#routing-requests), [Adapting and Retrying Requests](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md#adapting-and-retrying-requests-with-requestinterceptor) + - **Model Objects -** [Custom Response Handlers](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md#customizing-response-handlers) + - **Advanced Concurrency -** [Swift Concurrency](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md#using-alamofire-with-swift-concurrency) and [Combine](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md#using-alamofire-with-combine) + - **Connection -** [Security](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md#security), [Network Reachability](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md#network-reachability) +- [Open Radars](#open-radars) +- [FAQ](#faq) +- [Credits](#credits) +- [Donations](#donations) +- [License](#license) + +## Features + +- [x] Chainable Request / Response Methods +- [x] Swift Concurrency Support Back to iOS 13, macOS 10.15, tvOS 13, and watchOS 6. +- [x] Combine Support +- [x] URL / JSON Parameter Encoding +- [x] Upload File / Data / Stream / MultipartFormData +- [x] Download File using Request or Resume Data +- [x] Authentication with `URLCredential` +- [x] HTTP Response Validation +- [x] Upload and Download Progress Closures with Progress +- [x] cURL Command Output +- [x] Dynamically Adapt and Retry Requests +- [x] TLS Certificate and Public Key Pinning +- [x] Network Reachability +- [x] Comprehensive Unit and Integration Test Coverage +- [x] [Complete Documentation](https://alamofire.github.io/Alamofire) + +## Write Requests Fast! + +Alamofire's compact syntax and extensive feature set allow requests with powerful features like automatic retry to be written in just a few lines of code. + +```swift +// Automatic String to URL conversion, Swift concurrency support, and automatic retry. +let response = await AF.request("https://httpbin.org/get", interceptor: .retryPolicy) + // Automatic HTTP Basic Auth. + .authenticate(username: "user", password: "pass") + // Caching customization. + .cacheResponse(using: .cache) + // Redirect customization. + .redirect(using: .follow) + // Validate response code and Content-Type. + .validate() + // Produce a cURL command for the request. + .cURLDescription { description in + print(description) + } + // Automatic Decodable support with background parsing. + .serializingDecodable(DecodableType.self) + // Await the full response with metrics and a parsed body. + .response +// Detailed response description for easy debugging. +debugPrint(response) +``` + +## Component Libraries + +In order to keep Alamofire focused specifically on core networking implementations, additional component libraries have been created by the [Alamofire Software Foundation](https://github.com/Alamofire/Foundation) to bring additional functionality to the Alamofire ecosystem. + +- [AlamofireImage](https://github.com/Alamofire/AlamofireImage) - An image library including image response serializers, `UIImage` and `UIImageView` extensions, custom image filters, an auto-purging in-memory cache, and a priority-based image downloading system. +- [AlamofireNetworkActivityIndicator](https://github.com/Alamofire/AlamofireNetworkActivityIndicator) - Controls the visibility of the network activity indicator on iOS using Alamofire. It contains configurable delay timers to help mitigate flicker and can support `URLSession` instances not managed by Alamofire. + +## Requirements + +| Platform | Minimum Swift Version | Installation | Status | +| ---------------------------------------------------- | --------------------- | -------------------------------------------------------------------------------------------------------------------- | ------------------------ | +| iOS 10.0+ / macOS 10.12+ / tvOS 10.0+ / watchOS 3.0+ | 5.7.1 / Xcode 14.1 | [CocoaPods](#cocoapods), [Carthage](#carthage), [Swift Package Manager](#swift-package-manager), [Manual](#manually) | Fully Tested | +| Linux | Latest Only | [Swift Package Manager](#swift-package-manager) | Building But Unsupported | +| Windows | Latest Only | [Swift Package Manager](#swift-package-manager) | Building But Unsupported | +| Android | Latest Only | [Swift Package Manager](#swift-package-manager) | Building But Unsupported | + +#### Known Issues on Linux and Windows + +Alamofire builds on Linux, Windows, and Android but there are missing features and many issues in the underlying `swift-corelibs-foundation` that prevent full functionality and may cause crashes. These include: + +- `ServerTrustManager` and associated certificate functionality is unavailable, so there is no certificate pinning and no client certificate support. +- Various methods of HTTP authentication may crash, including HTTP Basic and HTTP Digest. Crashes may occur if responses contain server challenges. +- Cache control through `CachedResponseHandler` and associated APIs is unavailable, as the underlying delegate methods aren't called. +- `URLSessionTaskMetrics` are never gathered. +- `WebSocketRequest` is not available. + +Due to these issues, Alamofire is unsupported on Linux, Windows, and Android. Please report any crashes to the [Swift bug reporter](https://bugs.swift.org). + +## Migration Guides + +- [Alamofire 5.0 Migration Guide](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Alamofire%205.0%20Migration%20Guide.md) +- [Alamofire 4.0 Migration Guide](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Alamofire%204.0%20Migration%20Guide.md) +- [Alamofire 3.0 Migration Guide](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Alamofire%203.0%20Migration%20Guide.md) +- [Alamofire 2.0 Migration Guide](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Alamofire%202.0%20Migration%20Guide.md) + +## Communication + +- If you **need help with making network requests** using Alamofire, use [Stack Overflow](https://stackoverflow.com/questions/tagged/alamofire) and tag `alamofire`. +- If you need to **find or understand an API**, check [our documentation](http://alamofire.github.io/Alamofire/) or [Apple's documentation for `URLSession`](https://developer.apple.com/documentation/foundation/url_loading_system), on top of which Alamofire is built. +- If you need **help with an Alamofire feature**, use [our forum on swift.org](https://forums.swift.org/c/related-projects/alamofire). +- If you'd like to **discuss Alamofire best practices**, use [our forum on swift.org](https://forums.swift.org/c/related-projects/alamofire). +- If you'd like to **discuss a feature request**, use [our forum on swift.org](https://forums.swift.org/c/related-projects/alamofire). +- If you **found a bug**, open an issue here on GitHub and follow the guide. The more detail the better! + +## Installation + +### Swift Package Manager + +The [Swift Package Manager](https://swift.org/package-manager/) is a tool for automating the distribution of Swift code and is integrated into the `swift` compiler. + +Once you have your Swift package set up, adding Alamofire as a dependency is as easy as adding it to the `dependencies` value of your `Package.swift` or the Package list in Xcode. + +```swift +dependencies: [ + .package(url: "https://github.com/Alamofire/Alamofire.git", .upToNextMajor(from: "5.9.1")) +] +``` + +Normally you'll want to depend on the `Alamofire` target: + +```swift +.product(name: "Alamofire", package: "Alamofire") +``` + +But if you want to force Alamofire to be dynamically linked (do not do this unless you're sure you need it), you can depend on the `AlamofireDynamic` target: + +```swift +.product(name: "AlamofireDynamic", package: "Alamofire") +``` + +### CocoaPods + +[CocoaPods](https://cocoapods.org) is a dependency manager for Cocoa projects. For usage and installation instructions, visit their website. To integrate Alamofire into your Xcode project using CocoaPods, specify it in your `Podfile`: + +```ruby +pod 'Alamofire' +``` + +### Carthage + +[Carthage](https://github.com/Carthage/Carthage) is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks. To integrate Alamofire into your Xcode project using Carthage, specify it in your `Cartfile`: + +```ogdl +github "Alamofire/Alamofire" +``` + +### Manually + +If you prefer not to use any of the aforementioned dependency managers, you can integrate Alamofire into your project manually. + +#### Embedded Framework + +- Open up Terminal, `cd` into your top-level project directory, and run the following command "if" your project is not initialized as a git repository: + + ```bash + $ git init + ``` + +- Add Alamofire as a git [submodule](https://git-scm.com/docs/git-submodule) by running the following command: + + ```bash + $ git submodule add https://github.com/Alamofire/Alamofire.git + ``` + +- Open the new `Alamofire` folder, and drag the `Alamofire.xcodeproj` into the Project Navigator of your application's Xcode project. + + > It should appear nested underneath your application's blue project icon. Whether it is above or below all the other Xcode groups does not matter. + +- Select the `Alamofire.xcodeproj` in the Project Navigator and verify the deployment target matches that of your application target. +- Next, select your application project in the Project Navigator (blue project icon) to navigate to the target configuration window and select the application target under the "Targets" heading in the sidebar. +- In the tab bar at the top of that window, open the "General" panel. +- Click on the `+` button under the "Embedded Binaries" section. +- You will see two different `Alamofire.xcodeproj` folders each with two different versions of the `Alamofire.framework` nested inside a `Products` folder. + + > It does not matter which `Products` folder you choose from, but it does matter whether you choose the top or bottom `Alamofire.framework`. + +- Select the top `Alamofire.framework` for iOS and the bottom one for macOS. + + > You can verify which one you selected by inspecting the build log for your project. The build target for `Alamofire` will be listed as `Alamofire iOS`, `Alamofire macOS`, `Alamofire tvOS`, or `Alamofire watchOS`. + +- And that's it! + + > The `Alamofire.framework` is automagically added as a target dependency, linked framework and embedded framework in a copy files build phase which is all you need to build on the simulator and a device. + +## Contributing + +Before contributing to Alamofire, please read the instructions detailed in our [contribution guide](https://github.com/Alamofire/Alamofire/blob/master/CONTRIBUTING.md). + +## Open Radars + +The following radars have some effect on the current implementation of Alamofire. + +- [`rdar://21349340`](http://www.openradar.me/radar?id=5517037090635776) - Compiler throwing warning due to toll-free bridging issue in the test case +- `rdar://26870455` - Background URL Session Configurations do not work in the simulator +- `rdar://26849668` - Some URLProtocol APIs do not properly handle `URLRequest` + +## Resolved Radars + +The following radars have been resolved over time after being filed against the Alamofire project. + +- [`rdar://26761490`](http://www.openradar.me/radar?id=5010235949318144) - Swift string interpolation causing memory leak with common usage. + - (Resolved): 9/1/17 in Xcode 9 beta 6. +- [`rdar://36082113`](http://openradar.appspot.com/radar?id=4942308441063424) - `URLSessionTaskMetrics` failing to link on watchOS 3.0+ + - (Resolved): Just add `CFNetwork` to your linked frameworks. +- `FB7624529` - `urlSession(_:task:didFinishCollecting:)` never called on watchOS + - (Resolved): Metrics now collected on watchOS 7+. + +## FAQ + +### What's the origin of the name Alamofire? + +Alamofire is named after the [Alamo Fire flower](https://aggie-horticulture.tamu.edu/wildseed/alamofire.html), a hybrid variant of the Bluebonnet, the official state flower of Texas. + +## Credits + +Alamofire is owned and maintained by the [Alamofire Software Foundation](http://alamofire.org). You can follow them on Twitter at [@AlamofireSF](https://twitter.com/AlamofireSF) for project updates and releases. + +### Security Disclosure + +If you believe you have identified a security vulnerability with Alamofire, you should report it as soon as possible via email to security@alamofire.org. Please do not post it to a public issue tracker. + +## Sponsorship + +The [ASF](https://github.com/Alamofire/Foundation#members) is looking to raise money to officially stay registered as a federal non-profit organization. +Registering will allow Foundation members to gain some legal protections and also allow us to put donations to use, tax-free. +Sponsoring the ASF will enable us to: + +- Pay our yearly legal fees to keep the non-profit in good status +- Pay for our mail servers to help us stay on top of all questions and security issues +- Potentially fund test servers to make it easier for us to test the edge cases +- Potentially fund developers to work on one of our projects full-time + +The community adoption of the ASF libraries has been amazing. +We are greatly humbled by your enthusiasm around the projects and want to continue to do everything we can to move the needle forward. +With your continued support, the ASF will be able to improve its reach and also provide better legal safety for the core members. +If you use any of our libraries for work, see if your employers would be interested in donating. +Any amount you can donate, whether once or monthly, to help us reach our goal would be greatly appreciated. + +[Sponsor Alamofire](https://github.com/sponsors/Alamofire) + +## Supporters + +[MacStadium](https://macstadium.com) provides Alamofire with a free, hosted Mac mini. + +![Powered by MacStadium](https://raw.githubusercontent.com/Alamofire/Alamofire/master/Resources/MacStadiumLogo.png) + +## License + +Alamofire is released under the MIT license. [See LICENSE](https://github.com/Alamofire/Alamofire/blob/master/LICENSE) for details. diff --git a/Pods/Alamofire/Source/Alamofire.swift b/Pods/Alamofire/Source/Alamofire.swift new file mode 100644 index 0000000..4607197 --- /dev/null +++ b/Pods/Alamofire/Source/Alamofire.swift @@ -0,0 +1,43 @@ +// +// Alamofire.swift +// +// Copyright (c) 2014-2021 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +import Dispatch +import Foundation +#if canImport(FoundationNetworking) +@_exported import FoundationNetworking +#endif + +// Enforce minimum Swift version for all platforms and build systems. +#if swift(<5.7.1) +#error("Alamofire doesn't support Swift versions below 5.7.1.") +#endif + +/// Reference to `Session.default` for quick bootstrapping and examples. +public let AF = Session.default + +/// Namespace for informational Alamofire values. +public enum AFInfo { + /// Current Alamofire version. + public static let version = "5.9.1" +} diff --git a/Pods/Alamofire/Source/Core/AFError.swift b/Pods/Alamofire/Source/Core/AFError.swift new file mode 100644 index 0000000..cb7aa40 --- /dev/null +++ b/Pods/Alamofire/Source/Core/AFError.swift @@ -0,0 +1,874 @@ +// +// AFError.swift +// +// Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +import Foundation + +#if canImport(Security) +import Security +#endif + +/// `AFError` is the error type returned by Alamofire. It encompasses a few different types of errors, each with +/// their own associated reasons. +public enum AFError: Error { + /// The underlying reason the `.multipartEncodingFailed` error occurred. + public enum MultipartEncodingFailureReason { + /// The `fileURL` provided for reading an encodable body part isn't a file `URL`. + case bodyPartURLInvalid(url: URL) + /// The filename of the `fileURL` provided has either an empty `lastPathComponent` or `pathExtension`. + case bodyPartFilenameInvalid(in: URL) + /// The file at the `fileURL` provided was not reachable. + case bodyPartFileNotReachable(at: URL) + /// Attempting to check the reachability of the `fileURL` provided threw an error. + case bodyPartFileNotReachableWithError(atURL: URL, error: Error) + /// The file at the `fileURL` provided is actually a directory. + case bodyPartFileIsDirectory(at: URL) + /// The size of the file at the `fileURL` provided was not returned by the system. + case bodyPartFileSizeNotAvailable(at: URL) + /// The attempt to find the size of the file at the `fileURL` provided threw an error. + case bodyPartFileSizeQueryFailedWithError(forURL: URL, error: Error) + /// An `InputStream` could not be created for the provided `fileURL`. + case bodyPartInputStreamCreationFailed(for: URL) + /// An `OutputStream` could not be created when attempting to write the encoded data to disk. + case outputStreamCreationFailed(for: URL) + /// The encoded body data could not be written to disk because a file already exists at the provided `fileURL`. + case outputStreamFileAlreadyExists(at: URL) + /// The `fileURL` provided for writing the encoded body data to disk is not a file `URL`. + case outputStreamURLInvalid(url: URL) + /// The attempt to write the encoded body data to disk failed with an underlying error. + case outputStreamWriteFailed(error: Error) + /// The attempt to read an encoded body part `InputStream` failed with underlying system error. + case inputStreamReadFailed(error: Error) + } + + /// Represents unexpected input stream length that occur when encoding the `MultipartFormData`. Instances will be + /// embedded within an `AFError.multipartEncodingFailed` `.inputStreamReadFailed` case. + public struct UnexpectedInputStreamLength: Error { + /// The expected byte count to read. + public var bytesExpected: UInt64 + /// The actual byte count read. + public var bytesRead: UInt64 + } + + /// The underlying reason the `.parameterEncodingFailed` error occurred. + public enum ParameterEncodingFailureReason { + /// The `URLRequest` did not have a `URL` to encode. + case missingURL + /// JSON serialization failed with an underlying system error during the encoding process. + case jsonEncodingFailed(error: Error) + /// Custom parameter encoding failed due to the associated `Error`. + case customEncodingFailed(error: Error) + } + + /// The underlying reason the `.parameterEncoderFailed` error occurred. + public enum ParameterEncoderFailureReason { + /// Possible missing components. + public enum RequiredComponent { + /// The `URL` was missing or unable to be extracted from the passed `URLRequest` or during encoding. + case url + /// The `HTTPMethod` could not be extracted from the passed `URLRequest`. + case httpMethod(rawValue: String) + } + + /// A `RequiredComponent` was missing during encoding. + case missingRequiredComponent(RequiredComponent) + /// The underlying encoder failed with the associated error. + case encoderFailed(error: Error) + } + + /// The underlying reason the `.responseValidationFailed` error occurred. + public enum ResponseValidationFailureReason { + /// The data file containing the server response did not exist. + case dataFileNil + /// The data file containing the server response at the associated `URL` could not be read. + case dataFileReadFailed(at: URL) + /// The response did not contain a `Content-Type` and the `acceptableContentTypes` provided did not contain a + /// wildcard type. + case missingContentType(acceptableContentTypes: [String]) + /// The response `Content-Type` did not match any type in the provided `acceptableContentTypes`. + case unacceptableContentType(acceptableContentTypes: [String], responseContentType: String) + /// The response status code was not acceptable. + case unacceptableStatusCode(code: Int) + /// Custom response validation failed due to the associated `Error`. + case customValidationFailed(error: Error) + } + + /// The underlying reason the response serialization error occurred. + public enum ResponseSerializationFailureReason { + /// The server response contained no data or the data was zero length. + case inputDataNilOrZeroLength + /// The file containing the server response did not exist. + case inputFileNil + /// The file containing the server response could not be read from the associated `URL`. + case inputFileReadFailed(at: URL) + /// String serialization failed using the provided `String.Encoding`. + case stringSerializationFailed(encoding: String.Encoding) + /// JSON serialization failed with an underlying system error. + case jsonSerializationFailed(error: Error) + /// A `DataDecoder` failed to decode the response due to the associated `Error`. + case decodingFailed(error: Error) + /// A custom response serializer failed due to the associated `Error`. + case customSerializationFailed(error: Error) + /// Generic serialization failed for an empty response that wasn't type `Empty` but instead the associated type. + case invalidEmptyResponse(type: String) + } + + #if canImport(Security) + /// Underlying reason a server trust evaluation error occurred. + public enum ServerTrustFailureReason { + /// The output of a server trust evaluation. + public struct Output { + /// The host for which the evaluation was performed. + public let host: String + /// The `SecTrust` value which was evaluated. + public let trust: SecTrust + /// The `OSStatus` of evaluation operation. + public let status: OSStatus + /// The result of the evaluation operation. + public let result: SecTrustResultType + + /// Creates an `Output` value from the provided values. + init(_ host: String, _ trust: SecTrust, _ status: OSStatus, _ result: SecTrustResultType) { + self.host = host + self.trust = trust + self.status = status + self.result = result + } + } + + /// No `ServerTrustEvaluator` was found for the associated host. + case noRequiredEvaluator(host: String) + /// No certificates were found with which to perform the trust evaluation. + case noCertificatesFound + /// No public keys were found with which to perform the trust evaluation. + case noPublicKeysFound + /// During evaluation, application of the associated `SecPolicy` failed. + case policyApplicationFailed(trust: SecTrust, policy: SecPolicy, status: OSStatus) + /// During evaluation, setting the associated anchor certificates failed. + case settingAnchorCertificatesFailed(status: OSStatus, certificates: [SecCertificate]) + /// During evaluation, creation of the revocation policy failed. + case revocationPolicyCreationFailed + /// `SecTrust` evaluation failed with the associated `Error`, if one was produced. + case trustEvaluationFailed(error: Error?) + /// Default evaluation failed with the associated `Output`. + case defaultEvaluationFailed(output: Output) + /// Host validation failed with the associated `Output`. + case hostValidationFailed(output: Output) + /// Revocation check failed with the associated `Output` and options. + case revocationCheckFailed(output: Output, options: RevocationTrustEvaluator.Options) + /// Certificate pinning failed. + case certificatePinningFailed(host: String, trust: SecTrust, pinnedCertificates: [SecCertificate], serverCertificates: [SecCertificate]) + /// Public key pinning failed. + case publicKeyPinningFailed(host: String, trust: SecTrust, pinnedKeys: [SecKey], serverKeys: [SecKey]) + /// Custom server trust evaluation failed due to the associated `Error`. + case customEvaluationFailed(error: Error) + } + #endif + + /// The underlying reason the `.urlRequestValidationFailed` error occurred. + public enum URLRequestValidationFailureReason { + /// URLRequest with GET method had body data. + case bodyDataInGETRequest(Data) + } + + /// `UploadableConvertible` threw an error in `createUploadable()`. + case createUploadableFailed(error: Error) + /// `URLRequestConvertible` threw an error in `asURLRequest()`. + case createURLRequestFailed(error: Error) + /// `SessionDelegate` threw an error while attempting to move downloaded file to destination URL. + case downloadedFileMoveFailed(error: Error, source: URL, destination: URL) + /// `Request` was explicitly cancelled. + case explicitlyCancelled + /// `URLConvertible` type failed to create a valid `URL`. + case invalidURL(url: URLConvertible) + /// Multipart form encoding failed. + case multipartEncodingFailed(reason: MultipartEncodingFailureReason) + /// `ParameterEncoding` threw an error during the encoding process. + case parameterEncodingFailed(reason: ParameterEncodingFailureReason) + /// `ParameterEncoder` threw an error while running the encoder. + case parameterEncoderFailed(reason: ParameterEncoderFailureReason) + /// `RequestAdapter` threw an error during adaptation. + case requestAdaptationFailed(error: Error) + /// `RequestRetrier` threw an error during the request retry process. + case requestRetryFailed(retryError: Error, originalError: Error) + /// Response validation failed. + case responseValidationFailed(reason: ResponseValidationFailureReason) + /// Response serialization failed. + case responseSerializationFailed(reason: ResponseSerializationFailureReason) + #if canImport(Security) + /// `ServerTrustEvaluating` instance threw an error during trust evaluation. + case serverTrustEvaluationFailed(reason: ServerTrustFailureReason) + #endif + /// `Session` which issued the `Request` was deinitialized, most likely because its reference went out of scope. + case sessionDeinitialized + /// `Session` was explicitly invalidated, possibly with the `Error` produced by the underlying `URLSession`. + case sessionInvalidated(error: Error?) + /// `URLSessionTask` completed with error. + case sessionTaskFailed(error: Error) + /// `URLRequest` failed validation. + case urlRequestValidationFailed(reason: URLRequestValidationFailureReason) +} + +extension Error { + /// Returns the instance cast as an `AFError`. + public var asAFError: AFError? { + self as? AFError + } + + /// Returns the instance cast as an `AFError`. If casting fails, a `fatalError` with the specified `message` is thrown. + public func asAFError(orFailWith message: @autoclosure () -> String, file: StaticString = #file, line: UInt = #line) -> AFError { + guard let afError = self as? AFError else { + fatalError(message(), file: file, line: line) + } + return afError + } + + /// Casts the instance as `AFError` or returns `defaultAFError` + func asAFError(or defaultAFError: @autoclosure () -> AFError) -> AFError { + self as? AFError ?? defaultAFError() + } +} + +// MARK: - Error Booleans + +extension AFError { + /// Returns whether the instance is `.sessionDeinitialized`. + public var isSessionDeinitializedError: Bool { + if case .sessionDeinitialized = self { return true } + return false + } + + /// Returns whether the instance is `.sessionInvalidated`. + public var isSessionInvalidatedError: Bool { + if case .sessionInvalidated = self { return true } + return false + } + + /// Returns whether the instance is `.explicitlyCancelled`. + public var isExplicitlyCancelledError: Bool { + if case .explicitlyCancelled = self { return true } + return false + } + + /// Returns whether the instance is `.invalidURL`. + public var isInvalidURLError: Bool { + if case .invalidURL = self { return true } + return false + } + + /// Returns whether the instance is `.parameterEncodingFailed`. When `true`, the `underlyingError` property will + /// contain the associated value. + public var isParameterEncodingError: Bool { + if case .parameterEncodingFailed = self { return true } + return false + } + + /// Returns whether the instance is `.parameterEncoderFailed`. When `true`, the `underlyingError` property will + /// contain the associated value. + public var isParameterEncoderError: Bool { + if case .parameterEncoderFailed = self { return true } + return false + } + + /// Returns whether the instance is `.multipartEncodingFailed`. When `true`, the `url` and `underlyingError` + /// properties will contain the associated values. + public var isMultipartEncodingError: Bool { + if case .multipartEncodingFailed = self { return true } + return false + } + + /// Returns whether the instance is `.requestAdaptationFailed`. When `true`, the `underlyingError` property will + /// contain the associated value. + public var isRequestAdaptationError: Bool { + if case .requestAdaptationFailed = self { return true } + return false + } + + /// Returns whether the instance is `.responseValidationFailed`. When `true`, the `acceptableContentTypes`, + /// `responseContentType`, `responseCode`, and `underlyingError` properties will contain the associated values. + public var isResponseValidationError: Bool { + if case .responseValidationFailed = self { return true } + return false + } + + /// Returns whether the instance is `.responseSerializationFailed`. When `true`, the `failedStringEncoding` and + /// `underlyingError` properties will contain the associated values. + public var isResponseSerializationError: Bool { + if case .responseSerializationFailed = self { return true } + return false + } + + #if canImport(Security) + /// Returns whether the instance is `.serverTrustEvaluationFailed`. When `true`, the `underlyingError` property will + /// contain the associated value. + public var isServerTrustEvaluationError: Bool { + if case .serverTrustEvaluationFailed = self { return true } + return false + } + #endif + + /// Returns whether the instance is `requestRetryFailed`. When `true`, the `underlyingError` property will + /// contain the associated value. + public var isRequestRetryError: Bool { + if case .requestRetryFailed = self { return true } + return false + } + + /// Returns whether the instance is `createUploadableFailed`. When `true`, the `underlyingError` property will + /// contain the associated value. + public var isCreateUploadableError: Bool { + if case .createUploadableFailed = self { return true } + return false + } + + /// Returns whether the instance is `createURLRequestFailed`. When `true`, the `underlyingError` property will + /// contain the associated value. + public var isCreateURLRequestError: Bool { + if case .createURLRequestFailed = self { return true } + return false + } + + /// Returns whether the instance is `downloadedFileMoveFailed`. When `true`, the `destination` and `underlyingError` properties will + /// contain the associated values. + public var isDownloadedFileMoveError: Bool { + if case .downloadedFileMoveFailed = self { return true } + return false + } + + /// Returns whether the instance is `createURLRequestFailed`. When `true`, the `underlyingError` property will + /// contain the associated value. + public var isSessionTaskError: Bool { + if case .sessionTaskFailed = self { return true } + return false + } +} + +// MARK: - Convenience Properties + +extension AFError { + /// The `URLConvertible` associated with the error. + public var urlConvertible: URLConvertible? { + guard case let .invalidURL(url) = self else { return nil } + return url + } + + /// The `URL` associated with the error. + public var url: URL? { + guard case let .multipartEncodingFailed(reason) = self else { return nil } + return reason.url + } + + /// The underlying `Error` responsible for generating the failure associated with `.sessionInvalidated`, + /// `.parameterEncodingFailed`, `.parameterEncoderFailed`, `.multipartEncodingFailed`, `.requestAdaptationFailed`, + /// `.responseSerializationFailed`, `.requestRetryFailed` errors. + public var underlyingError: Error? { + switch self { + case let .multipartEncodingFailed(reason): + return reason.underlyingError + case let .parameterEncodingFailed(reason): + return reason.underlyingError + case let .parameterEncoderFailed(reason): + return reason.underlyingError + case let .requestAdaptationFailed(error): + return error + case let .requestRetryFailed(retryError, _): + return retryError + case let .responseValidationFailed(reason): + return reason.underlyingError + case let .responseSerializationFailed(reason): + return reason.underlyingError + #if canImport(Security) + case let .serverTrustEvaluationFailed(reason): + return reason.underlyingError + #endif + case let .sessionInvalidated(error): + return error + case let .createUploadableFailed(error): + return error + case let .createURLRequestFailed(error): + return error + case let .downloadedFileMoveFailed(error, _, _): + return error + case let .sessionTaskFailed(error): + return error + case .explicitlyCancelled, + .invalidURL, + .sessionDeinitialized, + .urlRequestValidationFailed: + return nil + } + } + + /// The acceptable `Content-Type`s of a `.responseValidationFailed` error. + public var acceptableContentTypes: [String]? { + guard case let .responseValidationFailed(reason) = self else { return nil } + return reason.acceptableContentTypes + } + + /// The response `Content-Type` of a `.responseValidationFailed` error. + public var responseContentType: String? { + guard case let .responseValidationFailed(reason) = self else { return nil } + return reason.responseContentType + } + + /// The response code of a `.responseValidationFailed` error. + public var responseCode: Int? { + guard case let .responseValidationFailed(reason) = self else { return nil } + return reason.responseCode + } + + /// The `String.Encoding` associated with a failed `.stringResponse()` call. + public var failedStringEncoding: String.Encoding? { + guard case let .responseSerializationFailed(reason) = self else { return nil } + return reason.failedStringEncoding + } + + /// The `source` URL of a `.downloadedFileMoveFailed` error. + public var sourceURL: URL? { + guard case let .downloadedFileMoveFailed(_, source, _) = self else { return nil } + return source + } + + /// The `destination` URL of a `.downloadedFileMoveFailed` error. + public var destinationURL: URL? { + guard case let .downloadedFileMoveFailed(_, _, destination) = self else { return nil } + return destination + } + + #if canImport(Security) + /// The download resume data of any underlying network error. Only produced by `DownloadRequest`s. + public var downloadResumeData: Data? { + (underlyingError as? URLError)?.userInfo[NSURLSessionDownloadTaskResumeData] as? Data + } + #endif +} + +extension AFError.ParameterEncodingFailureReason { + var underlyingError: Error? { + switch self { + case let .jsonEncodingFailed(error), + let .customEncodingFailed(error): + return error + case .missingURL: + return nil + } + } +} + +extension AFError.ParameterEncoderFailureReason { + var underlyingError: Error? { + switch self { + case let .encoderFailed(error): + return error + case .missingRequiredComponent: + return nil + } + } +} + +extension AFError.MultipartEncodingFailureReason { + var url: URL? { + switch self { + case let .bodyPartURLInvalid(url), + let .bodyPartFilenameInvalid(url), + let .bodyPartFileNotReachable(url), + let .bodyPartFileIsDirectory(url), + let .bodyPartFileSizeNotAvailable(url), + let .bodyPartInputStreamCreationFailed(url), + let .outputStreamCreationFailed(url), + let .outputStreamFileAlreadyExists(url), + let .outputStreamURLInvalid(url), + let .bodyPartFileNotReachableWithError(url, _), + let .bodyPartFileSizeQueryFailedWithError(url, _): + return url + case .outputStreamWriteFailed, + .inputStreamReadFailed: + return nil + } + } + + var underlyingError: Error? { + switch self { + case let .bodyPartFileNotReachableWithError(_, error), + let .bodyPartFileSizeQueryFailedWithError(_, error), + let .outputStreamWriteFailed(error), + let .inputStreamReadFailed(error): + return error + case .bodyPartURLInvalid, + .bodyPartFilenameInvalid, + .bodyPartFileNotReachable, + .bodyPartFileIsDirectory, + .bodyPartFileSizeNotAvailable, + .bodyPartInputStreamCreationFailed, + .outputStreamCreationFailed, + .outputStreamFileAlreadyExists, + .outputStreamURLInvalid: + return nil + } + } +} + +extension AFError.ResponseValidationFailureReason { + var acceptableContentTypes: [String]? { + switch self { + case let .missingContentType(types), + let .unacceptableContentType(types, _): + return types + case .dataFileNil, + .dataFileReadFailed, + .unacceptableStatusCode, + .customValidationFailed: + return nil + } + } + + var responseContentType: String? { + switch self { + case let .unacceptableContentType(_, responseType): + return responseType + case .dataFileNil, + .dataFileReadFailed, + .missingContentType, + .unacceptableStatusCode, + .customValidationFailed: + return nil + } + } + + var responseCode: Int? { + switch self { + case let .unacceptableStatusCode(code): + return code + case .dataFileNil, + .dataFileReadFailed, + .missingContentType, + .unacceptableContentType, + .customValidationFailed: + return nil + } + } + + var underlyingError: Error? { + switch self { + case let .customValidationFailed(error): + return error + case .dataFileNil, + .dataFileReadFailed, + .missingContentType, + .unacceptableContentType, + .unacceptableStatusCode: + return nil + } + } +} + +extension AFError.ResponseSerializationFailureReason { + var failedStringEncoding: String.Encoding? { + switch self { + case let .stringSerializationFailed(encoding): + return encoding + case .inputDataNilOrZeroLength, + .inputFileNil, + .inputFileReadFailed(_), + .jsonSerializationFailed(_), + .decodingFailed(_), + .customSerializationFailed(_), + .invalidEmptyResponse: + return nil + } + } + + var underlyingError: Error? { + switch self { + case let .jsonSerializationFailed(error), + let .decodingFailed(error), + let .customSerializationFailed(error): + return error + case .inputDataNilOrZeroLength, + .inputFileNil, + .inputFileReadFailed, + .stringSerializationFailed, + .invalidEmptyResponse: + return nil + } + } +} + +#if canImport(Security) +extension AFError.ServerTrustFailureReason { + var output: AFError.ServerTrustFailureReason.Output? { + switch self { + case let .defaultEvaluationFailed(output), + let .hostValidationFailed(output), + let .revocationCheckFailed(output, _): + return output + case .noRequiredEvaluator, + .noCertificatesFound, + .noPublicKeysFound, + .policyApplicationFailed, + .settingAnchorCertificatesFailed, + .revocationPolicyCreationFailed, + .trustEvaluationFailed, + .certificatePinningFailed, + .publicKeyPinningFailed, + .customEvaluationFailed: + return nil + } + } + + var underlyingError: Error? { + switch self { + case let .customEvaluationFailed(error): + return error + case let .trustEvaluationFailed(error): + return error + case .noRequiredEvaluator, + .noCertificatesFound, + .noPublicKeysFound, + .policyApplicationFailed, + .settingAnchorCertificatesFailed, + .revocationPolicyCreationFailed, + .defaultEvaluationFailed, + .hostValidationFailed, + .revocationCheckFailed, + .certificatePinningFailed, + .publicKeyPinningFailed: + return nil + } + } +} +#endif + +// MARK: - Error Descriptions + +extension AFError: LocalizedError { + public var errorDescription: String? { + switch self { + case .explicitlyCancelled: + return "Request explicitly cancelled." + case let .invalidURL(url): + return "URL is not valid: \(url)" + case let .parameterEncodingFailed(reason): + return reason.localizedDescription + case let .parameterEncoderFailed(reason): + return reason.localizedDescription + case let .multipartEncodingFailed(reason): + return reason.localizedDescription + case let .requestAdaptationFailed(error): + return "Request adaption failed with error: \(error.localizedDescription)" + case let .responseValidationFailed(reason): + return reason.localizedDescription + case let .responseSerializationFailed(reason): + return reason.localizedDescription + case let .requestRetryFailed(retryError, originalError): + return """ + Request retry failed with retry error: \(retryError.localizedDescription), \ + original error: \(originalError.localizedDescription) + """ + case .sessionDeinitialized: + return """ + Session was invalidated without error, so it was likely deinitialized unexpectedly. \ + Be sure to retain a reference to your Session for the duration of your requests. + """ + case let .sessionInvalidated(error): + return "Session was invalidated with error: \(error?.localizedDescription ?? "No description.")" + #if canImport(Security) + case let .serverTrustEvaluationFailed(reason): + return "Server trust evaluation failed due to reason: \(reason.localizedDescription)" + #endif + case let .urlRequestValidationFailed(reason): + return "URLRequest validation failed due to reason: \(reason.localizedDescription)" + case let .createUploadableFailed(error): + return "Uploadable creation failed with error: \(error.localizedDescription)" + case let .createURLRequestFailed(error): + return "URLRequest creation failed with error: \(error.localizedDescription)" + case let .downloadedFileMoveFailed(error, source, destination): + return "Moving downloaded file from: \(source) to: \(destination) failed with error: \(error.localizedDescription)" + case let .sessionTaskFailed(error): + return "URLSessionTask failed with error: \(error.localizedDescription)" + } + } +} + +extension AFError.ParameterEncodingFailureReason { + var localizedDescription: String { + switch self { + case .missingURL: + return "URL request to encode was missing a URL" + case let .jsonEncodingFailed(error): + return "JSON could not be encoded because of error:\n\(error.localizedDescription)" + case let .customEncodingFailed(error): + return "Custom parameter encoder failed with error: \(error.localizedDescription)" + } + } +} + +extension AFError.ParameterEncoderFailureReason { + var localizedDescription: String { + switch self { + case let .missingRequiredComponent(component): + return "Encoding failed due to a missing request component: \(component)" + case let .encoderFailed(error): + return "The underlying encoder failed with the error: \(error)" + } + } +} + +extension AFError.MultipartEncodingFailureReason { + var localizedDescription: String { + switch self { + case let .bodyPartURLInvalid(url): + return "The URL provided is not a file URL: \(url)" + case let .bodyPartFilenameInvalid(url): + return "The URL provided does not have a valid filename: \(url)" + case let .bodyPartFileNotReachable(url): + return "The URL provided is not reachable: \(url)" + case let .bodyPartFileNotReachableWithError(url, error): + return """ + The system returned an error while checking the provided URL for reachability. + URL: \(url) + Error: \(error) + """ + case let .bodyPartFileIsDirectory(url): + return "The URL provided is a directory: \(url)" + case let .bodyPartFileSizeNotAvailable(url): + return "Could not fetch the file size from the provided URL: \(url)" + case let .bodyPartFileSizeQueryFailedWithError(url, error): + return """ + The system returned an error while attempting to fetch the file size from the provided URL. + URL: \(url) + Error: \(error) + """ + case let .bodyPartInputStreamCreationFailed(url): + return "Failed to create an InputStream for the provided URL: \(url)" + case let .outputStreamCreationFailed(url): + return "Failed to create an OutputStream for URL: \(url)" + case let .outputStreamFileAlreadyExists(url): + return "A file already exists at the provided URL: \(url)" + case let .outputStreamURLInvalid(url): + return "The provided OutputStream URL is invalid: \(url)" + case let .outputStreamWriteFailed(error): + return "OutputStream write failed with error: \(error)" + case let .inputStreamReadFailed(error): + return "InputStream read failed with error: \(error)" + } + } +} + +extension AFError.ResponseSerializationFailureReason { + var localizedDescription: String { + switch self { + case .inputDataNilOrZeroLength: + return "Response could not be serialized, input data was nil or zero length." + case .inputFileNil: + return "Response could not be serialized, input file was nil." + case let .inputFileReadFailed(url): + return "Response could not be serialized, input file could not be read: \(url)." + case let .stringSerializationFailed(encoding): + return "String could not be serialized with encoding: \(encoding)." + case let .jsonSerializationFailed(error): + return "JSON could not be serialized because of error:\n\(error.localizedDescription)" + case let .invalidEmptyResponse(type): + return """ + Empty response could not be serialized to type: \(type). \ + Use Empty as the expected type for such responses. + """ + case let .decodingFailed(error): + return "Response could not be decoded because of error:\n\(error.localizedDescription)" + case let .customSerializationFailed(error): + return "Custom response serializer failed with error:\n\(error.localizedDescription)" + } + } +} + +extension AFError.ResponseValidationFailureReason { + var localizedDescription: String { + switch self { + case .dataFileNil: + return "Response could not be validated, data file was nil." + case let .dataFileReadFailed(url): + return "Response could not be validated, data file could not be read: \(url)." + case let .missingContentType(types): + return """ + Response Content-Type was missing and acceptable content types \ + (\(types.joined(separator: ","))) do not match "*/*". + """ + case let .unacceptableContentType(acceptableTypes, responseType): + return """ + Response Content-Type "\(responseType)" does not match any acceptable types: \ + \(acceptableTypes.joined(separator: ",")). + """ + case let .unacceptableStatusCode(code): + return "Response status code was unacceptable: \(code)." + case let .customValidationFailed(error): + return "Custom response validation failed with error: \(error.localizedDescription)" + } + } +} + +#if canImport(Security) +extension AFError.ServerTrustFailureReason { + var localizedDescription: String { + switch self { + case let .noRequiredEvaluator(host): + return "A ServerTrustEvaluating value is required for host \(host) but none was found." + case .noCertificatesFound: + return "No certificates were found or provided for evaluation." + case .noPublicKeysFound: + return "No public keys were found or provided for evaluation." + case .policyApplicationFailed: + return "Attempting to set a SecPolicy failed." + case .settingAnchorCertificatesFailed: + return "Attempting to set the provided certificates as anchor certificates failed." + case .revocationPolicyCreationFailed: + return "Attempting to create a revocation policy failed." + case let .trustEvaluationFailed(error): + return "SecTrust evaluation failed with error: \(error?.localizedDescription ?? "None")" + case let .defaultEvaluationFailed(output): + return "Default evaluation failed for host \(output.host)." + case let .hostValidationFailed(output): + return "Host validation failed for host \(output.host)." + case let .revocationCheckFailed(output, _): + return "Revocation check failed for host \(output.host)." + case let .certificatePinningFailed(host, _, _, _): + return "Certificate pinning failed for host \(host)." + case let .publicKeyPinningFailed(host, _, _, _): + return "Public key pinning failed for host \(host)." + case let .customEvaluationFailed(error): + return "Custom trust evaluation failed with error: \(error.localizedDescription)" + } + } +} +#endif + +extension AFError.URLRequestValidationFailureReason { + var localizedDescription: String { + switch self { + case let .bodyDataInGETRequest(data): + return """ + Invalid URLRequest: Requests with GET method cannot have body data: + \(String(decoding: data, as: UTF8.self)) + """ + } + } +} diff --git a/Pods/Alamofire/Source/Core/DataRequest.swift b/Pods/Alamofire/Source/Core/DataRequest.swift new file mode 100644 index 0000000..e282ee5 --- /dev/null +++ b/Pods/Alamofire/Source/Core/DataRequest.swift @@ -0,0 +1,448 @@ +// +// DataRequest.swift +// +// Copyright (c) 2014-2024 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +import Foundation + +/// `Request` subclass which handles in-memory `Data` download using `URLSessionDataTask`. +public class DataRequest: Request { + /// `URLRequestConvertible` value used to create `URLRequest`s for this instance. + public let convertible: URLRequestConvertible + /// `Data` read from the server so far. + public var data: Data? { dataMutableState.data } + + private struct DataMutableState { + var data: Data? + var httpResponseHandler: (queue: DispatchQueue, + handler: (_ response: HTTPURLResponse, + _ completionHandler: @escaping (ResponseDisposition) -> Void) -> Void)? + } + + private let dataMutableState = Protected(DataMutableState()) + + /// Creates a `DataRequest` using the provided parameters. + /// + /// - Parameters: + /// - id: `UUID` used for the `Hashable` and `Equatable` implementations. `UUID()` by default. + /// - convertible: `URLRequestConvertible` value used to create `URLRequest`s for this instance. + /// - underlyingQueue: `DispatchQueue` on which all internal `Request` work is performed. + /// - serializationQueue: `DispatchQueue` on which all serialization work is performed. By default targets + /// `underlyingQueue`, but can be passed another queue from a `Session`. + /// - eventMonitor: `EventMonitor` called for event callbacks from internal `Request` actions. + /// - interceptor: `RequestInterceptor` used throughout the request lifecycle. + /// - delegate: `RequestDelegate` that provides an interface to actions not performed by the `Request`. + init(id: UUID = UUID(), + convertible: URLRequestConvertible, + underlyingQueue: DispatchQueue, + serializationQueue: DispatchQueue, + eventMonitor: EventMonitor?, + interceptor: RequestInterceptor?, + delegate: RequestDelegate) { + self.convertible = convertible + + super.init(id: id, + underlyingQueue: underlyingQueue, + serializationQueue: serializationQueue, + eventMonitor: eventMonitor, + interceptor: interceptor, + delegate: delegate) + } + + override func reset() { + super.reset() + + dataMutableState.write { mutableState in + mutableState.data = nil + } + } + + /// Called when `Data` is received by this instance. + /// + /// - Note: Also calls `updateDownloadProgress`. + /// + /// - Parameter data: The `Data` received. + func didReceive(data: Data) { + dataMutableState.write { mutableState in + if mutableState.data == nil { + mutableState.data = data + } else { + mutableState.data?.append(data) + } + } + + updateDownloadProgress() + } + + func didReceiveResponse(_ response: HTTPURLResponse, completionHandler: @escaping (URLSession.ResponseDisposition) -> Void) { + dataMutableState.read { dataMutableState in + guard let httpResponseHandler = dataMutableState.httpResponseHandler else { + underlyingQueue.async { completionHandler(.allow) } + return + } + + httpResponseHandler.queue.async { + httpResponseHandler.handler(response) { disposition in + if disposition == .cancel { + self.mutableState.write { mutableState in + mutableState.state = .cancelled + mutableState.error = mutableState.error ?? AFError.explicitlyCancelled + } + } + + self.underlyingQueue.async { + completionHandler(disposition.sessionDisposition) + } + } + } + } + } + + override func task(for request: URLRequest, using session: URLSession) -> URLSessionTask { + let copiedRequest = request + return session.dataTask(with: copiedRequest) + } + + /// Called to update the `downloadProgress` of the instance. + func updateDownloadProgress() { + let totalBytesReceived = Int64(data?.count ?? 0) + let totalBytesExpected = task?.response?.expectedContentLength ?? NSURLSessionTransferSizeUnknown + + downloadProgress.totalUnitCount = totalBytesExpected + downloadProgress.completedUnitCount = totalBytesReceived + + downloadProgressHandler?.queue.async { self.downloadProgressHandler?.handler(self.downloadProgress) } + } + + /// Validates the request, using the specified closure. + /// + /// - Note: If validation fails, subsequent calls to response handlers will have an associated error. + /// + /// - Parameter validation: `Validation` closure used to validate the response. + /// + /// - Returns: The instance. + @discardableResult + public func validate(_ validation: @escaping Validation) -> Self { + let validator: () -> Void = { [unowned self] in + guard error == nil, let response else { return } + + let result = validation(request, response, data) + + if case let .failure(error) = result { self.error = error.asAFError(or: .responseValidationFailed(reason: .customValidationFailed(error: error))) } + + eventMonitor?.request(self, + didValidateRequest: request, + response: response, + data: data, + withResult: result) + } + + validators.write { $0.append(validator) } + + return self + } + + /// Sets a closure called whenever the `DataRequest` produces an `HTTPURLResponse` and providing a completion + /// handler to return a `ResponseDisposition` value. + /// + /// - Parameters: + /// - queue: `DispatchQueue` on which the closure will be called. `.main` by default. + /// - handler: Closure called when the instance produces an `HTTPURLResponse`. The `completionHandler` provided + /// MUST be called, otherwise the request will never complete. + /// + /// - Returns: The instance. + @_disfavoredOverload + @discardableResult + public func onHTTPResponse( + on queue: DispatchQueue = .main, + perform handler: @escaping (_ response: HTTPURLResponse, + _ completionHandler: @escaping (ResponseDisposition) -> Void) -> Void + ) -> Self { + dataMutableState.write { mutableState in + mutableState.httpResponseHandler = (queue, handler) + } + + return self + } + + /// Sets a closure called whenever the `DataRequest` produces an `HTTPURLResponse`. + /// + /// - Parameters: + /// - queue: `DispatchQueue` on which the closure will be called. `.main` by default. + /// - handler: Closure called when the instance produces an `HTTPURLResponse`. + /// + /// - Returns: The instance. + @discardableResult + public func onHTTPResponse(on queue: DispatchQueue = .main, + perform handler: @escaping (HTTPURLResponse) -> Void) -> Self { + onHTTPResponse(on: queue) { response, completionHandler in + handler(response) + completionHandler(.allow) + } + + return self + } + + // MARK: Response Serialization + + /// Adds a handler to be called once the request has finished. + /// + /// - Parameters: + /// - queue: The queue on which the completion handler is dispatched. `.main` by default. + /// - completionHandler: The code to be executed once the request has finished. + /// + /// - Returns: The request. + @discardableResult + public func response(queue: DispatchQueue = .main, completionHandler: @escaping (AFDataResponse) -> Void) -> Self { + appendResponseSerializer { + // Start work that should be on the serialization queue. + let result = AFResult(value: self.data, error: self.error) + // End work that should be on the serialization queue. + + self.underlyingQueue.async { + let response = DataResponse(request: self.request, + response: self.response, + data: self.data, + metrics: self.metrics, + serializationDuration: 0, + result: result) + + self.eventMonitor?.request(self, didParseResponse: response) + + self.responseSerializerDidComplete { queue.async { completionHandler(response) } } + } + } + + return self + } + + private func _response(queue: DispatchQueue = .main, + responseSerializer: Serializer, + completionHandler: @escaping (AFDataResponse) -> Void) + -> Self { + appendResponseSerializer { + // Start work that should be on the serialization queue. + let start = ProcessInfo.processInfo.systemUptime + let result: AFResult = Result { + try responseSerializer.serialize(request: self.request, + response: self.response, + data: self.data, + error: self.error) + }.mapError { error in + error.asAFError(or: .responseSerializationFailed(reason: .customSerializationFailed(error: error))) + } + + let end = ProcessInfo.processInfo.systemUptime + // End work that should be on the serialization queue. + + self.underlyingQueue.async { + let response = DataResponse(request: self.request, + response: self.response, + data: self.data, + metrics: self.metrics, + serializationDuration: end - start, + result: result) + + self.eventMonitor?.request(self, didParseResponse: response) + + guard !self.isCancelled, let serializerError = result.failure, let delegate = self.delegate else { + self.responseSerializerDidComplete { queue.async { completionHandler(response) } } + return + } + + delegate.retryResult(for: self, dueTo: serializerError) { retryResult in + var didComplete: (() -> Void)? + + defer { + if let didComplete { + self.responseSerializerDidComplete { queue.async { didComplete() } } + } + } + + switch retryResult { + case .doNotRetry: + didComplete = { completionHandler(response) } + + case let .doNotRetryWithError(retryError): + let result: AFResult = .failure(retryError.asAFError(orFailWith: "Received retryError was not already AFError")) + + let response = DataResponse(request: self.request, + response: self.response, + data: self.data, + metrics: self.metrics, + serializationDuration: end - start, + result: result) + + didComplete = { completionHandler(response) } + + case .retry, .retryWithDelay: + delegate.retryRequest(self, withDelay: retryResult.delay) + } + } + } + } + + return self + } + + /// Adds a handler to be called once the request has finished. + /// + /// - Parameters: + /// - queue: The queue on which the completion handler is dispatched. `.main` by default + /// - responseSerializer: The response serializer responsible for serializing the request, response, and data. + /// - completionHandler: The code to be executed once the request has finished. + /// + /// - Returns: The request. + @discardableResult + public func response(queue: DispatchQueue = .main, + responseSerializer: Serializer, + completionHandler: @escaping (AFDataResponse) -> Void) + -> Self { + _response(queue: queue, responseSerializer: responseSerializer, completionHandler: completionHandler) + } + + /// Adds a handler to be called once the request has finished. + /// + /// - Parameters: + /// - queue: The queue on which the completion handler is dispatched. `.main` by default + /// - responseSerializer: The response serializer responsible for serializing the request, response, and data. + /// - completionHandler: The code to be executed once the request has finished. + /// + /// - Returns: The request. + @discardableResult + public func response(queue: DispatchQueue = .main, + responseSerializer: Serializer, + completionHandler: @escaping (AFDataResponse) -> Void) + -> Self { + _response(queue: queue, responseSerializer: responseSerializer, completionHandler: completionHandler) + } + + /// Adds a handler using a `DataResponseSerializer` to be called once the request has finished. + /// + /// - Parameters: + /// - queue: The queue on which the completion handler is called. `.main` by default. + /// - dataPreprocessor: `DataPreprocessor` which processes the received `Data` before calling the + /// `completionHandler`. `PassthroughPreprocessor()` by default. + /// - emptyResponseCodes: HTTP status codes for which empty responses are always valid. `[204, 205]` by default. + /// - emptyRequestMethods: `HTTPMethod`s for which empty responses are always valid. `[.head]` by default. + /// - completionHandler: A closure to be executed once the request has finished. + /// + /// - Returns: The request. + @discardableResult + public func responseData(queue: DispatchQueue = .main, + dataPreprocessor: DataPreprocessor = DataResponseSerializer.defaultDataPreprocessor, + emptyResponseCodes: Set = DataResponseSerializer.defaultEmptyResponseCodes, + emptyRequestMethods: Set = DataResponseSerializer.defaultEmptyRequestMethods, + completionHandler: @escaping (AFDataResponse) -> Void) -> Self { + response(queue: queue, + responseSerializer: DataResponseSerializer(dataPreprocessor: dataPreprocessor, + emptyResponseCodes: emptyResponseCodes, + emptyRequestMethods: emptyRequestMethods), + completionHandler: completionHandler) + } + + /// Adds a handler using a `StringResponseSerializer` to be called once the request has finished. + /// + /// - Parameters: + /// - queue: The queue on which the completion handler is dispatched. `.main` by default. + /// - dataPreprocessor: `DataPreprocessor` which processes the received `Data` before calling the + /// `completionHandler`. `PassthroughPreprocessor()` by default. + /// - encoding: The string encoding. Defaults to `nil`, in which case the encoding will be determined + /// from the server response, falling back to the default HTTP character set, `ISO-8859-1`. + /// - emptyResponseCodes: HTTP status codes for which empty responses are always valid. `[204, 205]` by default. + /// - emptyRequestMethods: `HTTPMethod`s for which empty responses are always valid. `[.head]` by default. + /// - completionHandler: A closure to be executed once the request has finished. + /// + /// - Returns: The request. + @discardableResult + public func responseString(queue: DispatchQueue = .main, + dataPreprocessor: DataPreprocessor = StringResponseSerializer.defaultDataPreprocessor, + encoding: String.Encoding? = nil, + emptyResponseCodes: Set = StringResponseSerializer.defaultEmptyResponseCodes, + emptyRequestMethods: Set = StringResponseSerializer.defaultEmptyRequestMethods, + completionHandler: @escaping (AFDataResponse) -> Void) -> Self { + response(queue: queue, + responseSerializer: StringResponseSerializer(dataPreprocessor: dataPreprocessor, + encoding: encoding, + emptyResponseCodes: emptyResponseCodes, + emptyRequestMethods: emptyRequestMethods), + completionHandler: completionHandler) + } + + /// Adds a handler using a `JSONResponseSerializer` to be called once the request has finished. + /// + /// - Parameters: + /// - queue: The queue on which the completion handler is dispatched. `.main` by default. + /// - dataPreprocessor: `DataPreprocessor` which processes the received `Data` before calling the + /// `completionHandler`. `PassthroughPreprocessor()` by default. + /// - emptyResponseCodes: HTTP status codes for which empty responses are always valid. `[204, 205]` by default. + /// - emptyRequestMethods: `HTTPMethod`s for which empty responses are always valid. `[.head]` by default. + /// - options: `JSONSerialization.ReadingOptions` used when parsing the response. `.allowFragments` + /// by default. + /// - completionHandler: A closure to be executed once the request has finished. + /// + /// - Returns: The request. + @available(*, deprecated, message: "responseJSON deprecated and will be removed in Alamofire 6. Use responseDecodable instead.") + @discardableResult + public func responseJSON(queue: DispatchQueue = .main, + dataPreprocessor: DataPreprocessor = JSONResponseSerializer.defaultDataPreprocessor, + emptyResponseCodes: Set = JSONResponseSerializer.defaultEmptyResponseCodes, + emptyRequestMethods: Set = JSONResponseSerializer.defaultEmptyRequestMethods, + options: JSONSerialization.ReadingOptions = .allowFragments, + completionHandler: @escaping (AFDataResponse) -> Void) -> Self { + response(queue: queue, + responseSerializer: JSONResponseSerializer(dataPreprocessor: dataPreprocessor, + emptyResponseCodes: emptyResponseCodes, + emptyRequestMethods: emptyRequestMethods, + options: options), + completionHandler: completionHandler) + } + + /// Adds a handler using a `DecodableResponseSerializer` to be called once the request has finished. + /// + /// - Parameters: + /// - type: `Decodable` type to decode from response data. + /// - queue: The queue on which the completion handler is dispatched. `.main` by default. + /// - dataPreprocessor: `DataPreprocessor` which processes the received `Data` before calling the + /// `completionHandler`. `PassthroughPreprocessor()` by default. + /// - decoder: `DataDecoder` to use to decode the response. `JSONDecoder()` by default. + /// - emptyResponseCodes: HTTP status codes for which empty responses are always valid. `[204, 205]` by default. + /// - emptyRequestMethods: `HTTPMethod`s for which empty responses are always valid. `[.head]` by default. + /// - completionHandler: A closure to be executed once the request has finished. + /// + /// - Returns: The request. + @discardableResult + public func responseDecodable(of type: T.Type = T.self, + queue: DispatchQueue = .main, + dataPreprocessor: DataPreprocessor = DecodableResponseSerializer.defaultDataPreprocessor, + decoder: DataDecoder = JSONDecoder(), + emptyResponseCodes: Set = DecodableResponseSerializer.defaultEmptyResponseCodes, + emptyRequestMethods: Set = DecodableResponseSerializer.defaultEmptyRequestMethods, + completionHandler: @escaping (AFDataResponse) -> Void) -> Self { + response(queue: queue, + responseSerializer: DecodableResponseSerializer(dataPreprocessor: dataPreprocessor, + decoder: decoder, + emptyResponseCodes: emptyResponseCodes, + emptyRequestMethods: emptyRequestMethods), + completionHandler: completionHandler) + } +} diff --git a/Pods/Alamofire/Source/Core/DataStreamRequest.swift b/Pods/Alamofire/Source/Core/DataStreamRequest.swift new file mode 100644 index 0000000..a3c29b0 --- /dev/null +++ b/Pods/Alamofire/Source/Core/DataStreamRequest.swift @@ -0,0 +1,585 @@ +// +// DataStreamRequest.swift +// +// Copyright (c) 2014-2024 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +import Foundation + +/// `Request` subclass which streams HTTP response `Data` through a `Handler` closure. +public final class DataStreamRequest: Request { + /// Closure type handling `DataStreamRequest.Stream` values. + public typealias Handler = (Stream) throws -> Void + + /// Type encapsulating an `Event` as it flows through the stream, as well as a `CancellationToken` which can be used + /// to stop the stream at any time. + public struct Stream { + /// Latest `Event` from the stream. + public let event: Event + /// Token used to cancel the stream. + public let token: CancellationToken + + /// Cancel the ongoing stream by canceling the underlying `DataStreamRequest`. + public func cancel() { + token.cancel() + } + } + + /// Type representing an event flowing through the stream. Contains either the `Result` of processing streamed + /// `Data` or the completion of the stream. + public enum Event { + /// Output produced every time the instance receives additional `Data`. The associated value contains the + /// `Result` of processing the incoming `Data`. + case stream(Result) + /// Output produced when the instance has completed, whether due to stream end, cancellation, or an error. + /// Associated `Completion` value contains the final state. + case complete(Completion) + } + + /// Value containing the state of a `DataStreamRequest` when the stream was completed. + public struct Completion { + /// Last `URLRequest` issued by the instance. + public let request: URLRequest? + /// Last `HTTPURLResponse` received by the instance. + public let response: HTTPURLResponse? + /// Last `URLSessionTaskMetrics` produced for the instance. + public let metrics: URLSessionTaskMetrics? + /// `AFError` produced for the instance, if any. + public let error: AFError? + } + + /// Type used to cancel an ongoing stream. + public struct CancellationToken { + weak var request: DataStreamRequest? + + init(_ request: DataStreamRequest) { + self.request = request + } + + /// Cancel the ongoing stream by canceling the underlying `DataStreamRequest`. + public func cancel() { + request?.cancel() + } + } + + /// `URLRequestConvertible` value used to create `URLRequest`s for this instance. + public let convertible: URLRequestConvertible + /// Whether or not the instance will be cancelled if stream parsing encounters an error. + public let automaticallyCancelOnStreamError: Bool + + /// Internal mutable state specific to this type. + struct StreamMutableState { + /// `OutputStream` bound to the `InputStream` produced by `asInputStream`, if it has been called. + var outputStream: OutputStream? + /// Stream closures called as `Data` is received. + var streams: [(_ data: Data) -> Void] = [] + /// Number of currently executing streams. Used to ensure completions are only fired after all streams are + /// enqueued. + var numberOfExecutingStreams = 0 + /// Completion calls enqueued while streams are still executing. + var enqueuedCompletionEvents: [() -> Void] = [] + /// Handler for any `HTTPURLResponse`s received. + var httpResponseHandler: (queue: DispatchQueue, + handler: (_ response: HTTPURLResponse, + _ completionHandler: @escaping (ResponseDisposition) -> Void) -> Void)? + } + + let streamMutableState = Protected(StreamMutableState()) + + /// Creates a `DataStreamRequest` using the provided parameters. + /// + /// - Parameters: + /// - id: `UUID` used for the `Hashable` and `Equatable` implementations. `UUID()` + /// by default. + /// - convertible: `URLRequestConvertible` value used to create `URLRequest`s for this + /// instance. + /// - automaticallyCancelOnStreamError: `Bool` indicating whether the instance will be cancelled when an `Error` + /// is thrown while serializing stream `Data`. + /// - underlyingQueue: `DispatchQueue` on which all internal `Request` work is performed. + /// - serializationQueue: `DispatchQueue` on which all serialization work is performed. By default + /// targets + /// `underlyingQueue`, but can be passed another queue from a `Session`. + /// - eventMonitor: `EventMonitor` called for event callbacks from internal `Request` actions. + /// - interceptor: `RequestInterceptor` used throughout the request lifecycle. + /// - delegate: `RequestDelegate` that provides an interface to actions not performed by + /// the `Request`. + init(id: UUID = UUID(), + convertible: URLRequestConvertible, + automaticallyCancelOnStreamError: Bool, + underlyingQueue: DispatchQueue, + serializationQueue: DispatchQueue, + eventMonitor: EventMonitor?, + interceptor: RequestInterceptor?, + delegate: RequestDelegate) { + self.convertible = convertible + self.automaticallyCancelOnStreamError = automaticallyCancelOnStreamError + + super.init(id: id, + underlyingQueue: underlyingQueue, + serializationQueue: serializationQueue, + eventMonitor: eventMonitor, + interceptor: interceptor, + delegate: delegate) + } + + override func task(for request: URLRequest, using session: URLSession) -> URLSessionTask { + let copiedRequest = request + return session.dataTask(with: copiedRequest) + } + + override func finish(error: AFError? = nil) { + streamMutableState.write { state in + state.outputStream?.close() + } + + super.finish(error: error) + } + + func didReceive(data: Data) { + streamMutableState.write { state in + #if !canImport(FoundationNetworking) // If we not using swift-corelibs-foundation. + if let stream = state.outputStream { + underlyingQueue.async { + var bytes = Array(data) + stream.write(&bytes, maxLength: bytes.count) + } + } + #endif + state.numberOfExecutingStreams += state.streams.count + let localState = state + underlyingQueue.async { localState.streams.forEach { $0(data) } } + } + } + + func didReceiveResponse(_ response: HTTPURLResponse, completionHandler: @escaping (URLSession.ResponseDisposition) -> Void) { + streamMutableState.read { dataMutableState in + guard let httpResponseHandler = dataMutableState.httpResponseHandler else { + underlyingQueue.async { completionHandler(.allow) } + return + } + + httpResponseHandler.queue.async { + httpResponseHandler.handler(response) { disposition in + if disposition == .cancel { + self.mutableState.write { mutableState in + mutableState.state = .cancelled + mutableState.error = mutableState.error ?? AFError.explicitlyCancelled + } + } + + self.underlyingQueue.async { + completionHandler(disposition.sessionDisposition) + } + } + } + } + } + + /// Validates the `URLRequest` and `HTTPURLResponse` received for the instance using the provided `Validation` closure. + /// + /// - Parameter validation: `Validation` closure used to validate the request and response. + /// + /// - Returns: The `DataStreamRequest`. + @discardableResult + public func validate(_ validation: @escaping Validation) -> Self { + let validator: () -> Void = { [unowned self] in + guard error == nil, let response else { return } + + let result = validation(request, response) + + if case let .failure(error) = result { + self.error = error.asAFError(or: .responseValidationFailed(reason: .customValidationFailed(error: error))) + } + + eventMonitor?.request(self, + didValidateRequest: request, + response: response, + withResult: result) + } + + validators.write { $0.append(validator) } + + return self + } + + #if !canImport(FoundationNetworking) // If we not using swift-corelibs-foundation. + /// Produces an `InputStream` that receives the `Data` received by the instance. + /// + /// - Note: The `InputStream` produced by this method must have `open()` called before being able to read `Data`. + /// Additionally, this method will automatically call `resume()` on the instance, regardless of whether or + /// not the creating session has `startRequestsImmediately` set to `true`. + /// + /// - Parameter bufferSize: Size, in bytes, of the buffer between the `OutputStream` and `InputStream`. + /// + /// - Returns: The `InputStream` bound to the internal `OutboundStream`. + public func asInputStream(bufferSize: Int = 1024) -> InputStream? { + defer { resume() } + + var inputStream: InputStream? + streamMutableState.write { state in + Foundation.Stream.getBoundStreams(withBufferSize: bufferSize, + inputStream: &inputStream, + outputStream: &state.outputStream) + state.outputStream?.open() + } + + return inputStream + } + #endif + + /// Sets a closure called whenever the `DataRequest` produces an `HTTPURLResponse` and providing a completion + /// handler to return a `ResponseDisposition` value. + /// + /// - Parameters: + /// - queue: `DispatchQueue` on which the closure will be called. `.main` by default. + /// - handler: Closure called when the instance produces an `HTTPURLResponse`. The `completionHandler` provided + /// MUST be called, otherwise the request will never complete. + /// + /// - Returns: The instance. + @_disfavoredOverload + @discardableResult + public func onHTTPResponse( + on queue: DispatchQueue = .main, + perform handler: @escaping (_ response: HTTPURLResponse, + _ completionHandler: @escaping (ResponseDisposition) -> Void) -> Void + ) -> Self { + streamMutableState.write { mutableState in + mutableState.httpResponseHandler = (queue, handler) + } + + return self + } + + /// Sets a closure called whenever the `DataRequest` produces an `HTTPURLResponse`. + /// + /// - Parameters: + /// - queue: `DispatchQueue` on which the closure will be called. `.main` by default. + /// - handler: Closure called when the instance produces an `HTTPURLResponse`. + /// + /// - Returns: The instance. + @discardableResult + public func onHTTPResponse(on queue: DispatchQueue = .main, + perform handler: @escaping (HTTPURLResponse) -> Void) -> Self { + onHTTPResponse(on: queue) { response, completionHandler in + handler(response) + completionHandler(.allow) + } + + return self + } + + func capturingError(from closure: () throws -> Void) { + do { + try closure() + } catch { + self.error = error.asAFError(or: .responseSerializationFailed(reason: .customSerializationFailed(error: error))) + cancel() + } + } + + func appendStreamCompletion(on queue: DispatchQueue, + stream: @escaping Handler) { + appendResponseSerializer { + self.underlyingQueue.async { + self.responseSerializerDidComplete { + self.streamMutableState.write { state in + guard state.numberOfExecutingStreams == 0 else { + state.enqueuedCompletionEvents.append { + self.enqueueCompletion(on: queue, stream: stream) + } + + return + } + + self.enqueueCompletion(on: queue, stream: stream) + } + } + } + } + } + + func enqueueCompletion(on queue: DispatchQueue, + stream: @escaping Handler) { + queue.async { + do { + let completion = Completion(request: self.request, + response: self.response, + metrics: self.metrics, + error: self.error) + try stream(.init(event: .complete(completion), token: .init(self))) + } catch { + // Ignore error, as errors on Completion can't be handled anyway. + } + } + } + + // MARK: Response Serialization + + /// Adds a `StreamHandler` which performs no parsing on incoming `Data`. + /// + /// - Parameters: + /// - queue: `DispatchQueue` on which to perform `StreamHandler` closure. + /// - stream: `StreamHandler` closure called as `Data` is received. May be called multiple times. + /// + /// - Returns: The `DataStreamRequest`. + @discardableResult + public func responseStream(on queue: DispatchQueue = .main, stream: @escaping Handler) -> Self { + let parser = { [unowned self] (data: Data) in + queue.async { + self.capturingError { + try stream(.init(event: .stream(.success(data)), token: .init(self))) + } + + self.updateAndCompleteIfPossible() + } + } + + streamMutableState.write { $0.streams.append(parser) } + appendStreamCompletion(on: queue, stream: stream) + + return self + } + + /// Adds a `StreamHandler` which uses the provided `DataStreamSerializer` to process incoming `Data`. + /// + /// - Parameters: + /// - serializer: `DataStreamSerializer` used to process incoming `Data`. Its work is done on the `serializationQueue`. + /// - queue: `DispatchQueue` on which to perform `StreamHandler` closure. + /// - stream: `StreamHandler` closure called as `Data` is received. May be called multiple times. + /// + /// - Returns: The `DataStreamRequest`. + @discardableResult + public func responseStream(using serializer: Serializer, + on queue: DispatchQueue = .main, + stream: @escaping Handler) -> Self { + let parser = { [unowned self] (data: Data) in + serializationQueue.async { + // Start work on serialization queue. + let result = Result { try serializer.serialize(data) } + .mapError { $0.asAFError(or: .responseSerializationFailed(reason: .customSerializationFailed(error: $0))) } + // End work on serialization queue. + self.underlyingQueue.async { + self.eventMonitor?.request(self, didParseStream: result) + + if result.isFailure, self.automaticallyCancelOnStreamError { + self.cancel() + } + + queue.async { + self.capturingError { + try stream(.init(event: .stream(result), token: .init(self))) + } + + self.updateAndCompleteIfPossible() + } + } + } + } + + streamMutableState.write { $0.streams.append(parser) } + appendStreamCompletion(on: queue, stream: stream) + + return self + } + + /// Adds a `StreamHandler` which parses incoming `Data` as a UTF8 `String`. + /// + /// - Parameters: + /// - queue: `DispatchQueue` on which to perform `StreamHandler` closure. + /// - stream: `StreamHandler` closure called as `Data` is received. May be called multiple times. + /// + /// - Returns: The `DataStreamRequest`. + @discardableResult + public func responseStreamString(on queue: DispatchQueue = .main, + stream: @escaping Handler) -> Self { + let parser = { [unowned self] (data: Data) in + serializationQueue.async { + // Start work on serialization queue. + let string = String(decoding: data, as: UTF8.self) + // End work on serialization queue. + self.underlyingQueue.async { + self.eventMonitor?.request(self, didParseStream: .success(string)) + + queue.async { + self.capturingError { + try stream(.init(event: .stream(.success(string)), token: .init(self))) + } + + self.updateAndCompleteIfPossible() + } + } + } + } + + streamMutableState.write { $0.streams.append(parser) } + appendStreamCompletion(on: queue, stream: stream) + + return self + } + + private func updateAndCompleteIfPossible() { + streamMutableState.write { state in + state.numberOfExecutingStreams -= 1 + + guard state.numberOfExecutingStreams == 0, !state.enqueuedCompletionEvents.isEmpty else { return } + + let completionEvents = state.enqueuedCompletionEvents + self.underlyingQueue.async { completionEvents.forEach { $0() } } + state.enqueuedCompletionEvents.removeAll() + } + } + + /// Adds a `StreamHandler` which parses incoming `Data` using the provided `DataDecoder`. + /// + /// - Parameters: + /// - type: `Decodable` type to parse incoming `Data` into. + /// - queue: `DispatchQueue` on which to perform `StreamHandler` closure. + /// - decoder: `DataDecoder` used to decode the incoming `Data`. + /// - preprocessor: `DataPreprocessor` used to process the incoming `Data` before it's passed to the `decoder`. + /// - stream: `StreamHandler` closure called as `Data` is received. May be called multiple times. + /// + /// - Returns: The `DataStreamRequest`. + @discardableResult + public func responseStreamDecodable(of type: T.Type = T.self, + on queue: DispatchQueue = .main, + using decoder: DataDecoder = JSONDecoder(), + preprocessor: DataPreprocessor = PassthroughPreprocessor(), + stream: @escaping Handler) -> Self { + responseStream(using: DecodableStreamSerializer(decoder: decoder, dataPreprocessor: preprocessor), + on: queue, + stream: stream) + } +} + +extension DataStreamRequest.Stream { + /// Incoming `Result` values from `Event.stream`. + public var result: Result? { + guard case let .stream(result) = event else { return nil } + + return result + } + + /// `Success` value of the instance, if any. + public var value: Success? { + guard case let .success(value) = result else { return nil } + + return value + } + + /// `Failure` value of the instance, if any. + public var error: Failure? { + guard case let .failure(error) = result else { return nil } + + return error + } + + /// `Completion` value of the instance, if any. + public var completion: DataStreamRequest.Completion? { + guard case let .complete(completion) = event else { return nil } + + return completion + } +} + +// MARK: - Serialization + +/// A type which can serialize incoming `Data`. +public protocol DataStreamSerializer { + /// Type produced from the serialized `Data`. + associatedtype SerializedObject + + /// Serializes incoming `Data` into a `SerializedObject` value. + /// + /// - Parameter data: `Data` to be serialized. + /// + /// - Throws: Any error produced during serialization. + func serialize(_ data: Data) throws -> SerializedObject +} + +/// `DataStreamSerializer` which uses the provided `DataPreprocessor` and `DataDecoder` to serialize the incoming `Data`. +public struct DecodableStreamSerializer: DataStreamSerializer { + /// `DataDecoder` used to decode incoming `Data`. + public let decoder: DataDecoder + /// `DataPreprocessor` incoming `Data` is passed through before being passed to the `DataDecoder`. + public let dataPreprocessor: DataPreprocessor + + /// Creates an instance with the provided `DataDecoder` and `DataPreprocessor`. + /// - Parameters: + /// - decoder: ` DataDecoder` used to decode incoming `Data`. `JSONDecoder()` by default. + /// - dataPreprocessor: `DataPreprocessor` used to process incoming `Data` before it's passed through the + /// `decoder`. `PassthroughPreprocessor()` by default. + public init(decoder: DataDecoder = JSONDecoder(), dataPreprocessor: DataPreprocessor = PassthroughPreprocessor()) { + self.decoder = decoder + self.dataPreprocessor = dataPreprocessor + } + + public func serialize(_ data: Data) throws -> T { + let processedData = try dataPreprocessor.preprocess(data) + do { + return try decoder.decode(T.self, from: processedData) + } catch { + throw AFError.responseSerializationFailed(reason: .decodingFailed(error: error)) + } + } +} + +/// `DataStreamSerializer` which performs no serialization on incoming `Data`. +public struct PassthroughStreamSerializer: DataStreamSerializer { + /// Creates an instance. + public init() {} + + public func serialize(_ data: Data) throws -> Data { data } +} + +/// `DataStreamSerializer` which serializes incoming stream `Data` into `UTF8`-decoded `String` values. +public struct StringStreamSerializer: DataStreamSerializer { + /// Creates an instance. + public init() {} + + public func serialize(_ data: Data) throws -> String { + String(decoding: data, as: UTF8.self) + } +} + +extension DataStreamSerializer { + /// Creates a `DecodableStreamSerializer` instance with the provided `DataDecoder` and `DataPreprocessor`. + /// + /// - Parameters: + /// - type: `Decodable` type to decode from stream data. + /// - decoder: ` DataDecoder` used to decode incoming `Data`. `JSONDecoder()` by default. + /// - dataPreprocessor: `DataPreprocessor` used to process incoming `Data` before it's passed through the + /// `decoder`. `PassthroughPreprocessor()` by default. + public static func decodable(of type: T.Type, + decoder: DataDecoder = JSONDecoder(), + dataPreprocessor: DataPreprocessor = PassthroughPreprocessor()) -> Self where Self == DecodableStreamSerializer { + DecodableStreamSerializer(decoder: decoder, dataPreprocessor: dataPreprocessor) + } +} + +extension DataStreamSerializer where Self == PassthroughStreamSerializer { + /// Provides a `PassthroughStreamSerializer` instance. + public static var passthrough: PassthroughStreamSerializer { PassthroughStreamSerializer() } +} + +extension DataStreamSerializer where Self == StringStreamSerializer { + /// Provides a `StringStreamSerializer` instance. + public static var string: StringStreamSerializer { StringStreamSerializer() } +} diff --git a/Pods/Alamofire/Source/Core/DownloadRequest.swift b/Pods/Alamofire/Source/Core/DownloadRequest.swift new file mode 100644 index 0000000..556c43e --- /dev/null +++ b/Pods/Alamofire/Source/Core/DownloadRequest.swift @@ -0,0 +1,588 @@ +// +// DownloadRequest.swift +// +// Copyright (c) 2014-2024 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +import Foundation + +/// `Request` subclass which downloads `Data` to a file on disk using `URLSessionDownloadTask`. +public final class DownloadRequest: Request { + /// A set of options to be executed prior to moving a downloaded file from the temporary `URL` to the destination + /// `URL`. + public struct Options: OptionSet { + /// Specifies that intermediate directories for the destination URL should be created. + public static let createIntermediateDirectories = Options(rawValue: 1 << 0) + /// Specifies that any previous file at the destination `URL` should be removed. + public static let removePreviousFile = Options(rawValue: 1 << 1) + + public let rawValue: Int + + public init(rawValue: Int) { + self.rawValue = rawValue + } + } + + // MARK: Destination + + /// A closure executed once a `DownloadRequest` has successfully completed in order to determine where to move the + /// temporary file written to during the download process. The closure takes two arguments: the temporary file URL + /// and the `HTTPURLResponse`, and returns two values: the file URL where the temporary file should be moved and + /// the options defining how the file should be moved. + /// + /// - Note: Downloads from a local `file://` `URL`s do not use the `Destination` closure, as those downloads do not + /// return an `HTTPURLResponse`. Instead the file is merely moved within the temporary directory. + public typealias Destination = (_ temporaryURL: URL, + _ response: HTTPURLResponse) -> (destinationURL: URL, options: Options) + + /// Creates a download file destination closure which uses the default file manager to move the temporary file to a + /// file URL in the first available directory with the specified search path directory and search path domain mask. + /// + /// - Parameters: + /// - directory: The search path directory. `.documentDirectory` by default. + /// - domain: The search path domain mask. `.userDomainMask` by default. + /// - options: `DownloadRequest.Options` used when moving the downloaded file to its destination. None by + /// default. + /// - Returns: The `Destination` closure. + public class func suggestedDownloadDestination(for directory: FileManager.SearchPathDirectory = .documentDirectory, + in domain: FileManager.SearchPathDomainMask = .userDomainMask, + options: Options = []) -> Destination { + { temporaryURL, response in + let directoryURLs = FileManager.default.urls(for: directory, in: domain) + let url = directoryURLs.first?.appendingPathComponent(response.suggestedFilename!) ?? temporaryURL + + return (url, options) + } + } + + /// Default `Destination` used by Alamofire to ensure all downloads persist. This `Destination` prepends + /// `Alamofire_` to the automatically generated download name and moves it within the temporary directory. Files + /// with this destination must be additionally moved if they should survive the system reclamation of temporary + /// space. + static let defaultDestination: Destination = { url, _ in + (defaultDestinationURL(url), []) + } + + /// Default `URL` creation closure. Creates a `URL` in the temporary directory with `Alamofire_` prepended to the + /// provided file name. + static let defaultDestinationURL: (URL) -> URL = { url in + let filename = "Alamofire_\(url.lastPathComponent)" + let destination = url.deletingLastPathComponent().appendingPathComponent(filename) + + return destination + } + + // MARK: Downloadable + + /// Type describing the source used to create the underlying `URLSessionDownloadTask`. + public enum Downloadable { + /// Download should be started from the `URLRequest` produced by the associated `URLRequestConvertible` value. + case request(URLRequestConvertible) + /// Download should be started from the associated resume `Data` value. + case resumeData(Data) + } + + // MARK: Mutable State + + /// Type containing all mutable state for `DownloadRequest` instances. + private struct DownloadRequestMutableState { + /// Possible resume `Data` produced when cancelling the instance. + var resumeData: Data? + /// `URL` to which `Data` is being downloaded. + var fileURL: URL? + } + + /// Protected mutable state specific to `DownloadRequest`. + private let mutableDownloadState = Protected(DownloadRequestMutableState()) + + /// If the download is resumable and is eventually cancelled or fails, this value may be used to resume the download + /// using the `download(resumingWith data:)` API. + /// + /// - Note: For more information about `resumeData`, see [Apple's documentation](https://developer.apple.com/documentation/foundation/urlsessiondownloadtask/1411634-cancel). + public var resumeData: Data? { + #if !canImport(FoundationNetworking) // If we not using swift-corelibs-foundation. + return mutableDownloadState.resumeData ?? error?.downloadResumeData + #else + return mutableDownloadState.resumeData + #endif + } + + /// If the download is successful, the `URL` where the file was downloaded. + public var fileURL: URL? { mutableDownloadState.fileURL } + + // MARK: Initial State + + /// `Downloadable` value used for this instance. + public let downloadable: Downloadable + /// The `Destination` to which the downloaded file is moved. + let destination: Destination + + /// Creates a `DownloadRequest` using the provided parameters. + /// + /// - Parameters: + /// - id: `UUID` used for the `Hashable` and `Equatable` implementations. `UUID()` by default. + /// - downloadable: `Downloadable` value used to create `URLSessionDownloadTasks` for the instance. + /// - underlyingQueue: `DispatchQueue` on which all internal `Request` work is performed. + /// - serializationQueue: `DispatchQueue` on which all serialization work is performed. By default targets + /// `underlyingQueue`, but can be passed another queue from a `Session`. + /// - eventMonitor: `EventMonitor` called for event callbacks from internal `Request` actions. + /// - interceptor: `RequestInterceptor` used throughout the request lifecycle. + /// - delegate: `RequestDelegate` that provides an interface to actions not performed by the `Request` + /// - destination: `Destination` closure used to move the downloaded file to its final location. + init(id: UUID = UUID(), + downloadable: Downloadable, + underlyingQueue: DispatchQueue, + serializationQueue: DispatchQueue, + eventMonitor: EventMonitor?, + interceptor: RequestInterceptor?, + delegate: RequestDelegate, + destination: @escaping Destination) { + self.downloadable = downloadable + self.destination = destination + + super.init(id: id, + underlyingQueue: underlyingQueue, + serializationQueue: serializationQueue, + eventMonitor: eventMonitor, + interceptor: interceptor, + delegate: delegate) + } + + override func reset() { + super.reset() + + mutableDownloadState.write { + $0.resumeData = nil + $0.fileURL = nil + } + } + + /// Called when a download has finished. + /// + /// - Parameters: + /// - task: `URLSessionTask` that finished the download. + /// - result: `Result` of the automatic move to `destination`. + func didFinishDownloading(using task: URLSessionTask, with result: Result) { + eventMonitor?.request(self, didFinishDownloadingUsing: task, with: result) + + switch result { + case let .success(url): mutableDownloadState.fileURL = url + case let .failure(error): self.error = error + } + } + + /// Updates the `downloadProgress` using the provided values. + /// + /// - Parameters: + /// - bytesWritten: Total bytes written so far. + /// - totalBytesExpectedToWrite: Total bytes expected to write. + func updateDownloadProgress(bytesWritten: Int64, totalBytesExpectedToWrite: Int64) { + downloadProgress.totalUnitCount = totalBytesExpectedToWrite + downloadProgress.completedUnitCount += bytesWritten + + downloadProgressHandler?.queue.async { self.downloadProgressHandler?.handler(self.downloadProgress) } + } + + override func task(for request: URLRequest, using session: URLSession) -> URLSessionTask { + session.downloadTask(with: request) + } + + /// Creates a `URLSessionTask` from the provided resume data. + /// + /// - Parameters: + /// - data: `Data` used to resume the download. + /// - session: `URLSession` used to create the `URLSessionTask`. + /// + /// - Returns: The `URLSessionTask` created. + public func task(forResumeData data: Data, using session: URLSession) -> URLSessionTask { + session.downloadTask(withResumeData: data) + } + + /// Cancels the instance. Once cancelled, a `DownloadRequest` can no longer be resumed or suspended. + /// + /// - Note: This method will NOT produce resume data. If you wish to cancel and produce resume data, use + /// `cancel(producingResumeData:)` or `cancel(byProducingResumeData:)`. + /// + /// - Returns: The instance. + @discardableResult + override public func cancel() -> Self { + cancel(producingResumeData: false) + } + + /// Cancels the instance, optionally producing resume data. Once cancelled, a `DownloadRequest` can no longer be + /// resumed or suspended. + /// + /// - Note: If `producingResumeData` is `true`, the `resumeData` property will be populated with any resume data, if + /// available. + /// + /// - Returns: The instance. + @discardableResult + public func cancel(producingResumeData shouldProduceResumeData: Bool) -> Self { + cancel(optionallyProducingResumeData: shouldProduceResumeData ? { _ in } : nil) + } + + /// Cancels the instance while producing resume data. Once cancelled, a `DownloadRequest` can no longer be resumed + /// or suspended. + /// + /// - Note: The resume data passed to the completion handler will also be available on the instance's `resumeData` + /// property. + /// + /// - Parameter completionHandler: The completion handler that is called when the download has been successfully + /// cancelled. It is not guaranteed to be called on a particular queue, so you may + /// want use an appropriate queue to perform your work. + /// + /// - Returns: The instance. + @discardableResult + public func cancel(byProducingResumeData completionHandler: @escaping (_ data: Data?) -> Void) -> Self { + cancel(optionallyProducingResumeData: completionHandler) + } + + /// Internal implementation of cancellation that optionally takes a resume data handler. If no handler is passed, + /// cancellation is performed without producing resume data. + /// + /// - Parameter completionHandler: Optional resume data handler. + /// + /// - Returns: The instance. + private func cancel(optionallyProducingResumeData completionHandler: ((_ resumeData: Data?) -> Void)?) -> Self { + mutableState.write { mutableState in + guard mutableState.state.canTransitionTo(.cancelled) else { return } + + mutableState.state = .cancelled + + underlyingQueue.async { self.didCancel() } + + guard let task = mutableState.tasks.last as? URLSessionDownloadTask, task.state != .completed else { + underlyingQueue.async { self.finish() } + return + } + + if let completionHandler { + // Resume to ensure metrics are gathered. + task.resume() + task.cancel { resumeData in + self.mutableDownloadState.resumeData = resumeData + self.underlyingQueue.async { self.didCancelTask(task) } + completionHandler(resumeData) + } + } else { + // Resume to ensure metrics are gathered. + task.resume() + task.cancel() + self.underlyingQueue.async { self.didCancelTask(task) } + } + } + + return self + } + + /// Validates the request, using the specified closure. + /// + /// - Note: If validation fails, subsequent calls to response handlers will have an associated error. + /// + /// - Parameter validation: `Validation` closure to validate the response. + /// + /// - Returns: The instance. + @discardableResult + public func validate(_ validation: @escaping Validation) -> Self { + let validator: () -> Void = { [unowned self] in + guard error == nil, let response else { return } + + let result = validation(request, response, fileURL) + + if case let .failure(error) = result { + self.error = error.asAFError(or: .responseValidationFailed(reason: .customValidationFailed(error: error))) + } + + eventMonitor?.request(self, + didValidateRequest: request, + response: response, + fileURL: fileURL, + withResult: result) + } + + validators.write { $0.append(validator) } + + return self + } + + // MARK: - Response Serialization + + /// Adds a handler to be called once the request has finished. + /// + /// - Parameters: + /// - queue: The queue on which the completion handler is dispatched. `.main` by default. + /// - completionHandler: The code to be executed once the request has finished. + /// + /// - Returns: The request. + @discardableResult + public func response(queue: DispatchQueue = .main, + completionHandler: @escaping (AFDownloadResponse) -> Void) + -> Self { + appendResponseSerializer { + // Start work that should be on the serialization queue. + let result = AFResult(value: self.fileURL, error: self.error) + // End work that should be on the serialization queue. + + self.underlyingQueue.async { + let response = DownloadResponse(request: self.request, + response: self.response, + fileURL: self.fileURL, + resumeData: self.resumeData, + metrics: self.metrics, + serializationDuration: 0, + result: result) + + self.eventMonitor?.request(self, didParseResponse: response) + + self.responseSerializerDidComplete { queue.async { completionHandler(response) } } + } + } + + return self + } + + private func _response(queue: DispatchQueue = .main, + responseSerializer: Serializer, + completionHandler: @escaping (AFDownloadResponse) -> Void) + -> Self { + appendResponseSerializer { + // Start work that should be on the serialization queue. + let start = ProcessInfo.processInfo.systemUptime + let result: AFResult = Result { + try responseSerializer.serializeDownload(request: self.request, + response: self.response, + fileURL: self.fileURL, + error: self.error) + }.mapError { error in + error.asAFError(or: .responseSerializationFailed(reason: .customSerializationFailed(error: error))) + } + let end = ProcessInfo.processInfo.systemUptime + // End work that should be on the serialization queue. + + self.underlyingQueue.async { + let response = DownloadResponse(request: self.request, + response: self.response, + fileURL: self.fileURL, + resumeData: self.resumeData, + metrics: self.metrics, + serializationDuration: end - start, + result: result) + + self.eventMonitor?.request(self, didParseResponse: response) + + guard let serializerError = result.failure, let delegate = self.delegate else { + self.responseSerializerDidComplete { queue.async { completionHandler(response) } } + return + } + + delegate.retryResult(for: self, dueTo: serializerError) { retryResult in + var didComplete: (() -> Void)? + + defer { + if let didComplete { + self.responseSerializerDidComplete { queue.async { didComplete() } } + } + } + + switch retryResult { + case .doNotRetry: + didComplete = { completionHandler(response) } + + case let .doNotRetryWithError(retryError): + let result: AFResult = .failure(retryError.asAFError(orFailWith: "Received retryError was not already AFError")) + + let response = DownloadResponse(request: self.request, + response: self.response, + fileURL: self.fileURL, + resumeData: self.resumeData, + metrics: self.metrics, + serializationDuration: end - start, + result: result) + + didComplete = { completionHandler(response) } + + case .retry, .retryWithDelay: + delegate.retryRequest(self, withDelay: retryResult.delay) + } + } + } + } + + return self + } + + /// Adds a handler to be called once the request has finished. + /// + /// - Parameters: + /// - queue: The queue on which the completion handler is dispatched. `.main` by default. + /// - responseSerializer: The response serializer responsible for serializing the request, response, and data + /// contained in the destination `URL`. + /// - completionHandler: The code to be executed once the request has finished. + /// + /// - Returns: The request. + @discardableResult + public func response(queue: DispatchQueue = .main, + responseSerializer: Serializer, + completionHandler: @escaping (AFDownloadResponse) -> Void) + -> Self { + _response(queue: queue, responseSerializer: responseSerializer, completionHandler: completionHandler) + } + + /// Adds a handler to be called once the request has finished. + /// + /// - Parameters: + /// - queue: The queue on which the completion handler is dispatched. `.main` by default. + /// - responseSerializer: The response serializer responsible for serializing the request, response, and data + /// contained in the destination `URL`. + /// - completionHandler: The code to be executed once the request has finished. + /// + /// - Returns: The request. + @discardableResult + public func response(queue: DispatchQueue = .main, + responseSerializer: Serializer, + completionHandler: @escaping (AFDownloadResponse) -> Void) + -> Self { + _response(queue: queue, responseSerializer: responseSerializer, completionHandler: completionHandler) + } + + /// Adds a handler using a `URLResponseSerializer` to be called once the request is finished. + /// + /// - Parameters: + /// - queue: The queue on which the completion handler is called. `.main` by default. + /// - completionHandler: A closure to be executed once the request has finished. + /// + /// - Returns: The request. + @discardableResult + public func responseURL(queue: DispatchQueue = .main, + completionHandler: @escaping (AFDownloadResponse) -> Void) -> Self { + response(queue: queue, responseSerializer: URLResponseSerializer(), completionHandler: completionHandler) + } + + /// Adds a handler using a `DataResponseSerializer` to be called once the request has finished. + /// + /// - Parameters: + /// - queue: The queue on which the completion handler is called. `.main` by default. + /// - dataPreprocessor: `DataPreprocessor` which processes the received `Data` before calling the + /// `completionHandler`. `PassthroughPreprocessor()` by default. + /// - emptyResponseCodes: HTTP status codes for which empty responses are always valid. `[204, 205]` by default. + /// - emptyRequestMethods: `HTTPMethod`s for which empty responses are always valid. `[.head]` by default. + /// - completionHandler: A closure to be executed once the request has finished. + /// + /// - Returns: The request. + @discardableResult + public func responseData(queue: DispatchQueue = .main, + dataPreprocessor: DataPreprocessor = DataResponseSerializer.defaultDataPreprocessor, + emptyResponseCodes: Set = DataResponseSerializer.defaultEmptyResponseCodes, + emptyRequestMethods: Set = DataResponseSerializer.defaultEmptyRequestMethods, + completionHandler: @escaping (AFDownloadResponse) -> Void) -> Self { + response(queue: queue, + responseSerializer: DataResponseSerializer(dataPreprocessor: dataPreprocessor, + emptyResponseCodes: emptyResponseCodes, + emptyRequestMethods: emptyRequestMethods), + completionHandler: completionHandler) + } + + /// Adds a handler using a `StringResponseSerializer` to be called once the request has finished. + /// + /// - Parameters: + /// - queue: The queue on which the completion handler is dispatched. `.main` by default. + /// - dataPreprocessor: `DataPreprocessor` which processes the received `Data` before calling the + /// `completionHandler`. `PassthroughPreprocessor()` by default. + /// - encoding: The string encoding. Defaults to `nil`, in which case the encoding will be determined + /// from the server response, falling back to the default HTTP character set, `ISO-8859-1`. + /// - emptyResponseCodes: HTTP status codes for which empty responses are always valid. `[204, 205]` by default. + /// - emptyRequestMethods: `HTTPMethod`s for which empty responses are always valid. `[.head]` by default. + /// - completionHandler: A closure to be executed once the request has finished. + /// + /// - Returns: The request. + @discardableResult + public func responseString(queue: DispatchQueue = .main, + dataPreprocessor: DataPreprocessor = StringResponseSerializer.defaultDataPreprocessor, + encoding: String.Encoding? = nil, + emptyResponseCodes: Set = StringResponseSerializer.defaultEmptyResponseCodes, + emptyRequestMethods: Set = StringResponseSerializer.defaultEmptyRequestMethods, + completionHandler: @escaping (AFDownloadResponse) -> Void) -> Self { + response(queue: queue, + responseSerializer: StringResponseSerializer(dataPreprocessor: dataPreprocessor, + encoding: encoding, + emptyResponseCodes: emptyResponseCodes, + emptyRequestMethods: emptyRequestMethods), + completionHandler: completionHandler) + } + + /// Adds a handler using a `JSONResponseSerializer` to be called once the request has finished. + /// + /// - Parameters: + /// - queue: The queue on which the completion handler is dispatched. `.main` by default. + /// - dataPreprocessor: `DataPreprocessor` which processes the received `Data` before calling the + /// `completionHandler`. `PassthroughPreprocessor()` by default. + /// - emptyResponseCodes: HTTP status codes for which empty responses are always valid. `[204, 205]` by default. + /// - emptyRequestMethods: `HTTPMethod`s for which empty responses are always valid. `[.head]` by default. + /// - options: `JSONSerialization.ReadingOptions` used when parsing the response. `.allowFragments` + /// by default. + /// - completionHandler: A closure to be executed once the request has finished. + /// + /// - Returns: The request. + @available(*, deprecated, message: "responseJSON deprecated and will be removed in Alamofire 6. Use responseDecodable instead.") + @discardableResult + public func responseJSON(queue: DispatchQueue = .main, + dataPreprocessor: DataPreprocessor = JSONResponseSerializer.defaultDataPreprocessor, + emptyResponseCodes: Set = JSONResponseSerializer.defaultEmptyResponseCodes, + emptyRequestMethods: Set = JSONResponseSerializer.defaultEmptyRequestMethods, + options: JSONSerialization.ReadingOptions = .allowFragments, + completionHandler: @escaping (AFDownloadResponse) -> Void) -> Self { + response(queue: queue, + responseSerializer: JSONResponseSerializer(dataPreprocessor: dataPreprocessor, + emptyResponseCodes: emptyResponseCodes, + emptyRequestMethods: emptyRequestMethods, + options: options), + completionHandler: completionHandler) + } + + /// Adds a handler using a `DecodableResponseSerializer` to be called once the request has finished. + /// + /// - Parameters: + /// - type: `Decodable` type to decode from response data. + /// - queue: The queue on which the completion handler is dispatched. `.main` by default. + /// - dataPreprocessor: `DataPreprocessor` which processes the received `Data` before calling the + /// `completionHandler`. `PassthroughPreprocessor()` by default. + /// - decoder: `DataDecoder` to use to decode the response. `JSONDecoder()` by default. + /// - emptyResponseCodes: HTTP status codes for which empty responses are always valid. `[204, 205]` by default. + /// - emptyRequestMethods: `HTTPMethod`s for which empty responses are always valid. `[.head]` by default. + /// - completionHandler: A closure to be executed once the request has finished. + /// + /// - Returns: The request. + @discardableResult + public func responseDecodable(of type: T.Type = T.self, + queue: DispatchQueue = .main, + dataPreprocessor: DataPreprocessor = DecodableResponseSerializer.defaultDataPreprocessor, + decoder: DataDecoder = JSONDecoder(), + emptyResponseCodes: Set = DecodableResponseSerializer.defaultEmptyResponseCodes, + emptyRequestMethods: Set = DecodableResponseSerializer.defaultEmptyRequestMethods, + completionHandler: @escaping (AFDownloadResponse) -> Void) -> Self { + response(queue: queue, + responseSerializer: DecodableResponseSerializer(dataPreprocessor: dataPreprocessor, + decoder: decoder, + emptyResponseCodes: emptyResponseCodes, + emptyRequestMethods: emptyRequestMethods), + completionHandler: completionHandler) + } +} diff --git a/Pods/Alamofire/Source/Core/HTTPHeaders.swift b/Pods/Alamofire/Source/Core/HTTPHeaders.swift new file mode 100644 index 0000000..29ca43f --- /dev/null +++ b/Pods/Alamofire/Source/Core/HTTPHeaders.swift @@ -0,0 +1,466 @@ +// +// HTTPHeaders.swift +// +// Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +import Foundation + +/// An order-preserving and case-insensitive representation of HTTP headers. +public struct HTTPHeaders: Equatable, Hashable, Sendable { + private var headers: [HTTPHeader] = [] + + /// Creates an empty instance. + public init() {} + + /// Creates an instance from an array of `HTTPHeader`s. Duplicate case-insensitive names are collapsed into the last + /// name and value encountered. + public init(_ headers: [HTTPHeader]) { + headers.forEach { update($0) } + } + + /// Creates an instance from a `[String: String]`. Duplicate case-insensitive names are collapsed into the last name + /// and value encountered. + public init(_ dictionary: [String: String]) { + dictionary.forEach { update(HTTPHeader(name: $0.key, value: $0.value)) } + } + + /// Case-insensitively updates or appends an `HTTPHeader` into the instance using the provided `name` and `value`. + /// + /// - Parameters: + /// - name: The `HTTPHeader` name. + /// - value: The `HTTPHeader` value. + public mutating func add(name: String, value: String) { + update(HTTPHeader(name: name, value: value)) + } + + /// Case-insensitively updates or appends the provided `HTTPHeader` into the instance. + /// + /// - Parameter header: The `HTTPHeader` to update or append. + public mutating func add(_ header: HTTPHeader) { + update(header) + } + + /// Case-insensitively updates or appends an `HTTPHeader` into the instance using the provided `name` and `value`. + /// + /// - Parameters: + /// - name: The `HTTPHeader` name. + /// - value: The `HTTPHeader` value. + public mutating func update(name: String, value: String) { + update(HTTPHeader(name: name, value: value)) + } + + /// Case-insensitively updates or appends the provided `HTTPHeader` into the instance. + /// + /// - Parameter header: The `HTTPHeader` to update or append. + public mutating func update(_ header: HTTPHeader) { + guard let index = headers.index(of: header.name) else { + headers.append(header) + return + } + + headers.replaceSubrange(index...index, with: [header]) + } + + /// Case-insensitively removes an `HTTPHeader`, if it exists, from the instance. + /// + /// - Parameter name: The name of the `HTTPHeader` to remove. + public mutating func remove(name: String) { + guard let index = headers.index(of: name) else { return } + + headers.remove(at: index) + } + + /// Sort the current instance by header name, case insensitively. + public mutating func sort() { + headers.sort { $0.name.lowercased() < $1.name.lowercased() } + } + + /// Returns an instance sorted by header name. + /// + /// - Returns: A copy of the current instance sorted by name. + public func sorted() -> HTTPHeaders { + var headers = self + headers.sort() + + return headers + } + + /// Case-insensitively find a header's value by name. + /// + /// - Parameter name: The name of the header to search for, case-insensitively. + /// + /// - Returns: The value of header, if it exists. + public func value(for name: String) -> String? { + guard let index = headers.index(of: name) else { return nil } + + return headers[index].value + } + + /// Case-insensitively access the header with the given name. + /// + /// - Parameter name: The name of the header. + public subscript(_ name: String) -> String? { + get { value(for: name) } + set { + if let value = newValue { + update(name: name, value: value) + } else { + remove(name: name) + } + } + } + + /// The dictionary representation of all headers. + /// + /// This representation does not preserve the current order of the instance. + public var dictionary: [String: String] { + let namesAndValues = headers.map { ($0.name, $0.value) } + + return Dictionary(namesAndValues, uniquingKeysWith: { _, last in last }) + } +} + +extension HTTPHeaders: ExpressibleByDictionaryLiteral { + public init(dictionaryLiteral elements: (String, String)...) { + elements.forEach { update(name: $0.0, value: $0.1) } + } +} + +extension HTTPHeaders: ExpressibleByArrayLiteral { + public init(arrayLiteral elements: HTTPHeader...) { + self.init(elements) + } +} + +extension HTTPHeaders: Sequence { + public func makeIterator() -> IndexingIterator<[HTTPHeader]> { + headers.makeIterator() + } +} + +extension HTTPHeaders: Collection { + public var startIndex: Int { + headers.startIndex + } + + public var endIndex: Int { + headers.endIndex + } + + public subscript(position: Int) -> HTTPHeader { + headers[position] + } + + public func index(after i: Int) -> Int { + headers.index(after: i) + } +} + +extension HTTPHeaders: CustomStringConvertible { + public var description: String { + headers.map(\.description) + .joined(separator: "\n") + } +} + +// MARK: - HTTPHeader + +/// A representation of a single HTTP header's name / value pair. +public struct HTTPHeader: Equatable, Hashable, Sendable { + /// Name of the header. + public let name: String + + /// Value of the header. + public let value: String + + /// Creates an instance from the given `name` and `value`. + /// + /// - Parameters: + /// - name: The name of the header. + /// - value: The value of the header. + public init(name: String, value: String) { + self.name = name + self.value = value + } +} + +extension HTTPHeader: CustomStringConvertible { + public var description: String { + "\(name): \(value)" + } +} + +extension HTTPHeader { + /// Returns an `Accept` header. + /// + /// - Parameter value: The `Accept` value. + /// - Returns: The header. + public static func accept(_ value: String) -> HTTPHeader { + HTTPHeader(name: "Accept", value: value) + } + + /// Returns an `Accept-Charset` header. + /// + /// - Parameter value: The `Accept-Charset` value. + /// - Returns: The header. + public static func acceptCharset(_ value: String) -> HTTPHeader { + HTTPHeader(name: "Accept-Charset", value: value) + } + + /// Returns an `Accept-Language` header. + /// + /// Alamofire offers a default Accept-Language header that accumulates and encodes the system's preferred languages. + /// Use `HTTPHeader.defaultAcceptLanguage`. + /// + /// - Parameter value: The `Accept-Language` value. + /// + /// - Returns: The header. + public static func acceptLanguage(_ value: String) -> HTTPHeader { + HTTPHeader(name: "Accept-Language", value: value) + } + + /// Returns an `Accept-Encoding` header. + /// + /// Alamofire offers a default accept encoding value that provides the most common values. Use + /// `HTTPHeader.defaultAcceptEncoding`. + /// + /// - Parameter value: The `Accept-Encoding` value. + /// + /// - Returns: The header + public static func acceptEncoding(_ value: String) -> HTTPHeader { + HTTPHeader(name: "Accept-Encoding", value: value) + } + + /// Returns a `Basic` `Authorization` header using the `username` and `password` provided. + /// + /// - Parameters: + /// - username: The username of the header. + /// - password: The password of the header. + /// + /// - Returns: The header. + public static func authorization(username: String, password: String) -> HTTPHeader { + let credential = Data("\(username):\(password)".utf8).base64EncodedString() + + return authorization("Basic \(credential)") + } + + /// Returns a `Bearer` `Authorization` header using the `bearerToken` provided. + /// + /// - Parameter bearerToken: The bearer token. + /// + /// - Returns: The header. + public static func authorization(bearerToken: String) -> HTTPHeader { + authorization("Bearer \(bearerToken)") + } + + /// Returns an `Authorization` header. + /// + /// Alamofire provides built-in methods to produce `Authorization` headers. For a Basic `Authorization` header use + /// `HTTPHeader.authorization(username:password:)`. For a Bearer `Authorization` header, use + /// `HTTPHeader.authorization(bearerToken:)`. + /// + /// - Parameter value: The `Authorization` value. + /// + /// - Returns: The header. + public static func authorization(_ value: String) -> HTTPHeader { + HTTPHeader(name: "Authorization", value: value) + } + + /// Returns a `Content-Disposition` header. + /// + /// - Parameter value: The `Content-Disposition` value. + /// + /// - Returns: The header. + public static func contentDisposition(_ value: String) -> HTTPHeader { + HTTPHeader(name: "Content-Disposition", value: value) + } + + /// Returns a `Content-Encoding` header. + /// + /// - Parameter value: The `Content-Encoding`. + /// + /// - Returns: The header. + public static func contentEncoding(_ value: String) -> HTTPHeader { + HTTPHeader(name: "Content-Encoding", value: value) + } + + /// Returns a `Content-Type` header. + /// + /// All Alamofire `ParameterEncoding`s and `ParameterEncoder`s set the `Content-Type` of the request, so it may not + /// be necessary to manually set this value. + /// + /// - Parameter value: The `Content-Type` value. + /// + /// - Returns: The header. + public static func contentType(_ value: String) -> HTTPHeader { + HTTPHeader(name: "Content-Type", value: value) + } + + /// Returns a `User-Agent` header. + /// + /// - Parameter value: The `User-Agent` value. + /// + /// - Returns: The header. + public static func userAgent(_ value: String) -> HTTPHeader { + HTTPHeader(name: "User-Agent", value: value) + } + + /// Returns a `Sec-WebSocket-Protocol` header. + /// + /// - Parameter value: The `Sec-WebSocket-Protocol` value. + /// - Returns: The header. + public static func websocketProtocol(_ value: String) -> HTTPHeader { + HTTPHeader(name: "Sec-WebSocket-Protocol", value: value) + } +} + +extension [HTTPHeader] { + /// Case-insensitively finds the index of an `HTTPHeader` with the provided name, if it exists. + func index(of name: String) -> Int? { + let lowercasedName = name.lowercased() + return firstIndex { $0.name.lowercased() == lowercasedName } + } +} + +// MARK: - Defaults + +extension HTTPHeaders { + /// The default set of `HTTPHeaders` used by Alamofire. Includes `Accept-Encoding`, `Accept-Language`, and + /// `User-Agent`. + public static let `default`: HTTPHeaders = [.defaultAcceptEncoding, + .defaultAcceptLanguage, + .defaultUserAgent] +} + +extension HTTPHeader { + /// Returns Alamofire's default `Accept-Encoding` header, appropriate for the encodings supported by particular OS + /// versions. + /// + /// See the [Accept-Encoding HTTP header documentation](https://tools.ietf.org/html/rfc7230#section-4.2.3) . + public static let defaultAcceptEncoding: HTTPHeader = { + let encodings: [String] + if #available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, *) { + encodings = ["br", "gzip", "deflate"] + } else { + encodings = ["gzip", "deflate"] + } + + return .acceptEncoding(encodings.qualityEncoded()) + }() + + /// Returns Alamofire's default `Accept-Language` header, generated by querying `Locale` for the user's + /// `preferredLanguages`. + /// + /// See the [Accept-Language HTTP header documentation](https://tools.ietf.org/html/rfc7231#section-5.3.5). + public static let defaultAcceptLanguage: HTTPHeader = .acceptLanguage(Locale.preferredLanguages.prefix(6).qualityEncoded()) + + /// Returns Alamofire's default `User-Agent` header. + /// + /// See the [User-Agent header documentation](https://tools.ietf.org/html/rfc7231#section-5.5.3). + /// + /// Example: `iOS Example/1.0 (org.alamofire.iOS-Example; build:1; iOS 13.0.0) Alamofire/5.0.0` + public static let defaultUserAgent: HTTPHeader = { + let info = Bundle.main.infoDictionary + let executable = (info?["CFBundleExecutable"] as? String) ?? + (ProcessInfo.processInfo.arguments.first?.split(separator: "/").last.map(String.init)) ?? + "Unknown" + let bundle = info?["CFBundleIdentifier"] as? String ?? "Unknown" + let appVersion = info?["CFBundleShortVersionString"] as? String ?? "Unknown" + let appBuild = info?["CFBundleVersion"] as? String ?? "Unknown" + + let osNameVersion: String = { + let version = ProcessInfo.processInfo.operatingSystemVersion + let versionString = "\(version.majorVersion).\(version.minorVersion).\(version.patchVersion)" + let osName: String = { + #if os(iOS) + #if targetEnvironment(macCatalyst) + return "macOS(Catalyst)" + #else + return "iOS" + #endif + #elseif os(watchOS) + return "watchOS" + #elseif os(tvOS) + return "tvOS" + #elseif os(macOS) + #if targetEnvironment(macCatalyst) + return "macOS(Catalyst)" + #else + return "macOS" + #endif + #elseif swift(>=5.9.2) && os(visionOS) + return "visionOS" + #elseif os(Linux) + return "Linux" + #elseif os(Windows) + return "Windows" + #elseif os(Android) + return "Android" + #else + return "Unknown" + #endif + }() + + return "\(osName) \(versionString)" + }() + + let alamofireVersion = "Alamofire/\(AFInfo.version)" + + let userAgent = "\(executable)/\(appVersion) (\(bundle); build:\(appBuild); \(osNameVersion)) \(alamofireVersion)" + + return .userAgent(userAgent) + }() +} + +extension Collection { + func qualityEncoded() -> String { + enumerated().map { index, encoding in + let quality = 1.0 - (Double(index) * 0.1) + return "\(encoding);q=\(quality)" + }.joined(separator: ", ") + } +} + +// MARK: - System Type Extensions + +extension URLRequest { + /// Returns `allHTTPHeaderFields` as `HTTPHeaders`. + public var headers: HTTPHeaders { + get { allHTTPHeaderFields.map(HTTPHeaders.init) ?? HTTPHeaders() } + set { allHTTPHeaderFields = newValue.dictionary } + } +} + +extension HTTPURLResponse { + /// Returns `allHeaderFields` as `HTTPHeaders`. + public var headers: HTTPHeaders { + (allHeaderFields as? [String: String]).map(HTTPHeaders.init) ?? HTTPHeaders() + } +} + +extension URLSessionConfiguration { + /// Returns `httpAdditionalHeaders` as `HTTPHeaders`. + public var headers: HTTPHeaders { + get { (httpAdditionalHeaders as? [String: String]).map(HTTPHeaders.init) ?? HTTPHeaders() } + set { httpAdditionalHeaders = newValue.dictionary } + } +} diff --git a/Pods/Alamofire/Source/Core/HTTPMethod.swift b/Pods/Alamofire/Source/Core/HTTPMethod.swift new file mode 100644 index 0000000..ed51b68 --- /dev/null +++ b/Pods/Alamofire/Source/Core/HTTPMethod.swift @@ -0,0 +1,56 @@ +// +// HTTPMethod.swift +// +// Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) +// +// 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. +// + +/// Type representing HTTP methods. Raw `String` value is stored and compared case-sensitively, so +/// `HTTPMethod.get != HTTPMethod(rawValue: "get")`. +/// +/// See https://tools.ietf.org/html/rfc7231#section-4.3 +public struct HTTPMethod: RawRepresentable, Equatable, Hashable, Sendable { + /// `CONNECT` method. + public static let connect = HTTPMethod(rawValue: "CONNECT") + /// `DELETE` method. + public static let delete = HTTPMethod(rawValue: "DELETE") + /// `GET` method. + public static let get = HTTPMethod(rawValue: "GET") + /// `HEAD` method. + public static let head = HTTPMethod(rawValue: "HEAD") + /// `OPTIONS` method. + public static let options = HTTPMethod(rawValue: "OPTIONS") + /// `PATCH` method. + public static let patch = HTTPMethod(rawValue: "PATCH") + /// `POST` method. + public static let post = HTTPMethod(rawValue: "POST") + /// `PUT` method. + public static let put = HTTPMethod(rawValue: "PUT") + /// `QUERY` method. + public static let query = HTTPMethod(rawValue: "QUERY") + /// `TRACE` method. + public static let trace = HTTPMethod(rawValue: "TRACE") + + public let rawValue: String + + public init(rawValue: String) { + self.rawValue = rawValue + } +} diff --git a/Pods/Alamofire/Source/Core/Notifications.swift b/Pods/Alamofire/Source/Core/Notifications.swift new file mode 100644 index 0000000..66434b6 --- /dev/null +++ b/Pods/Alamofire/Source/Core/Notifications.swift @@ -0,0 +1,115 @@ +// +// Notifications.swift +// +// Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +import Foundation + +extension Request { + /// Posted when a `Request` is resumed. The `Notification` contains the resumed `Request`. + public static let didResumeNotification = Notification.Name(rawValue: "org.alamofire.notification.name.request.didResume") + /// Posted when a `Request` is suspended. The `Notification` contains the suspended `Request`. + public static let didSuspendNotification = Notification.Name(rawValue: "org.alamofire.notification.name.request.didSuspend") + /// Posted when a `Request` is cancelled. The `Notification` contains the cancelled `Request`. + public static let didCancelNotification = Notification.Name(rawValue: "org.alamofire.notification.name.request.didCancel") + /// Posted when a `Request` is finished. The `Notification` contains the completed `Request`. + public static let didFinishNotification = Notification.Name(rawValue: "org.alamofire.notification.name.request.didFinish") + + /// Posted when a `URLSessionTask` is resumed. The `Notification` contains the `Request` associated with the `URLSessionTask`. + public static let didResumeTaskNotification = Notification.Name(rawValue: "org.alamofire.notification.name.request.didResumeTask") + /// Posted when a `URLSessionTask` is suspended. The `Notification` contains the `Request` associated with the `URLSessionTask`. + public static let didSuspendTaskNotification = Notification.Name(rawValue: "org.alamofire.notification.name.request.didSuspendTask") + /// Posted when a `URLSessionTask` is cancelled. The `Notification` contains the `Request` associated with the `URLSessionTask`. + public static let didCancelTaskNotification = Notification.Name(rawValue: "org.alamofire.notification.name.request.didCancelTask") + /// Posted when a `URLSessionTask` is completed. The `Notification` contains the `Request` associated with the `URLSessionTask`. + public static let didCompleteTaskNotification = Notification.Name(rawValue: "org.alamofire.notification.name.request.didCompleteTask") +} + +// MARK: - + +extension Notification { + /// The `Request` contained by the instance's `userInfo`, `nil` otherwise. + public var request: Request? { + userInfo?[String.requestKey] as? Request + } + + /// Convenience initializer for a `Notification` containing a `Request` payload. + /// + /// - Parameters: + /// - name: The name of the notification. + /// - request: The `Request` payload. + init(name: Notification.Name, request: Request) { + self.init(name: name, object: nil, userInfo: [String.requestKey: request]) + } +} + +extension NotificationCenter { + /// Convenience function for posting notifications with `Request` payloads. + /// + /// - Parameters: + /// - name: The name of the notification. + /// - request: The `Request` payload. + func postNotification(named name: Notification.Name, with request: Request) { + let notification = Notification(name: name, request: request) + post(notification) + } +} + +extension String { + /// User info dictionary key representing the `Request` associated with the notification. + fileprivate static let requestKey = "org.alamofire.notification.key.request" +} + +/// `EventMonitor` that provides Alamofire's notifications. +public final class AlamofireNotifications: EventMonitor { + public func requestDidResume(_ request: Request) { + NotificationCenter.default.postNotification(named: Request.didResumeNotification, with: request) + } + + public func requestDidSuspend(_ request: Request) { + NotificationCenter.default.postNotification(named: Request.didSuspendNotification, with: request) + } + + public func requestDidCancel(_ request: Request) { + NotificationCenter.default.postNotification(named: Request.didCancelNotification, with: request) + } + + public func requestDidFinish(_ request: Request) { + NotificationCenter.default.postNotification(named: Request.didFinishNotification, with: request) + } + + public func request(_ request: Request, didResumeTask task: URLSessionTask) { + NotificationCenter.default.postNotification(named: Request.didResumeTaskNotification, with: request) + } + + public func request(_ request: Request, didSuspendTask task: URLSessionTask) { + NotificationCenter.default.postNotification(named: Request.didSuspendTaskNotification, with: request) + } + + public func request(_ request: Request, didCancelTask task: URLSessionTask) { + NotificationCenter.default.postNotification(named: Request.didCancelTaskNotification, with: request) + } + + public func request(_ request: Request, didCompleteTask task: URLSessionTask, with error: AFError?) { + NotificationCenter.default.postNotification(named: Request.didCompleteTaskNotification, with: request) + } +} diff --git a/Pods/Alamofire/Source/Core/ParameterEncoder.swift b/Pods/Alamofire/Source/Core/ParameterEncoder.swift new file mode 100644 index 0000000..3d30c3f --- /dev/null +++ b/Pods/Alamofire/Source/Core/ParameterEncoder.swift @@ -0,0 +1,213 @@ +// +// ParameterEncoder.swift +// +// Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +import Foundation + +/// A type that can encode any `Encodable` type into a `URLRequest`. +public protocol ParameterEncoder { + /// Encode the provided `Encodable` parameters into `request`. + /// + /// - Parameters: + /// - parameters: The `Encodable` parameter value. + /// - request: The `URLRequest` into which to encode the parameters. + /// + /// - Returns: A `URLRequest` with the result of the encoding. + /// - Throws: An `Error` when encoding fails. For Alamofire provided encoders, this will be an instance of + /// `AFError.parameterEncoderFailed` with an associated `ParameterEncoderFailureReason`. + func encode(_ parameters: Parameters?, into request: URLRequest) throws -> URLRequest +} + +/// A `ParameterEncoder` that encodes types as JSON body data. +/// +/// If no `Content-Type` header is already set on the provided `URLRequest`s, it's set to `application/json`. +open class JSONParameterEncoder: ParameterEncoder { + /// Returns an encoder with default parameters. + public static var `default`: JSONParameterEncoder { JSONParameterEncoder() } + + /// Returns an encoder with `JSONEncoder.outputFormatting` set to `.prettyPrinted`. + public static var prettyPrinted: JSONParameterEncoder { + let encoder = JSONEncoder() + encoder.outputFormatting = .prettyPrinted + + return JSONParameterEncoder(encoder: encoder) + } + + /// Returns an encoder with `JSONEncoder.outputFormatting` set to `.sortedKeys`. + @available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *) + public static var sortedKeys: JSONParameterEncoder { + let encoder = JSONEncoder() + encoder.outputFormatting = .sortedKeys + + return JSONParameterEncoder(encoder: encoder) + } + + /// `JSONEncoder` used to encode parameters. + public let encoder: JSONEncoder + + /// Creates an instance with the provided `JSONEncoder`. + /// + /// - Parameter encoder: The `JSONEncoder`. `JSONEncoder()` by default. + public init(encoder: JSONEncoder = JSONEncoder()) { + self.encoder = encoder + } + + open func encode(_ parameters: Parameters?, + into request: URLRequest) throws -> URLRequest { + guard let parameters else { return request } + + var request = request + + do { + let data = try encoder.encode(parameters) + request.httpBody = data + if request.headers["Content-Type"] == nil { + request.headers.update(.contentType("application/json")) + } + } catch { + throw AFError.parameterEncodingFailed(reason: .jsonEncodingFailed(error: error)) + } + + return request + } +} + +extension ParameterEncoder where Self == JSONParameterEncoder { + /// Provides a default `JSONParameterEncoder` instance. + public static var json: JSONParameterEncoder { JSONParameterEncoder() } + + /// Creates a `JSONParameterEncoder` using the provided `JSONEncoder`. + /// + /// - Parameter encoder: `JSONEncoder` used to encode parameters. `JSONEncoder()` by default. + /// - Returns: The `JSONParameterEncoder`. + public static func json(encoder: JSONEncoder = JSONEncoder()) -> JSONParameterEncoder { + JSONParameterEncoder(encoder: encoder) + } +} + +/// A `ParameterEncoder` that encodes types as URL-encoded query strings to be set on the URL or as body data, depending +/// on the `Destination` set. +/// +/// If no `Content-Type` header is already set on the provided `URLRequest`s, it will be set to +/// `application/x-www-form-urlencoded; charset=utf-8`. +/// +/// Encoding behavior can be customized by passing an instance of `URLEncodedFormEncoder` to the initializer. +open class URLEncodedFormParameterEncoder: ParameterEncoder { + /// Defines where the URL-encoded string should be set for each `URLRequest`. + public enum Destination { + /// Applies the encoded query string to any existing query string for `.get`, `.head`, and `.delete` request. + /// Sets it to the `httpBody` for all other methods. + case methodDependent + /// Applies the encoded query string to any existing query string from the `URLRequest`. + case queryString + /// Applies the encoded query string to the `httpBody` of the `URLRequest`. + case httpBody + + /// Determines whether the URL-encoded string should be applied to the `URLRequest`'s `url`. + /// + /// - Parameter method: The `HTTPMethod`. + /// + /// - Returns: Whether the URL-encoded string should be applied to a `URL`. + func encodesParametersInURL(for method: HTTPMethod) -> Bool { + switch self { + case .methodDependent: return [.get, .head, .delete].contains(method) + case .queryString: return true + case .httpBody: return false + } + } + } + + /// Returns an encoder with default parameters. + public static var `default`: URLEncodedFormParameterEncoder { URLEncodedFormParameterEncoder() } + + /// The `URLEncodedFormEncoder` to use. + public let encoder: URLEncodedFormEncoder + + /// The `Destination` for the URL-encoded string. + public let destination: Destination + + /// Creates an instance with the provided `URLEncodedFormEncoder` instance and `Destination` value. + /// + /// - Parameters: + /// - encoder: The `URLEncodedFormEncoder`. `URLEncodedFormEncoder()` by default. + /// - destination: The `Destination`. `.methodDependent` by default. + public init(encoder: URLEncodedFormEncoder = URLEncodedFormEncoder(), destination: Destination = .methodDependent) { + self.encoder = encoder + self.destination = destination + } + + open func encode(_ parameters: Parameters?, + into request: URLRequest) throws -> URLRequest { + guard let parameters else { return request } + + var request = request + + guard let url = request.url else { + throw AFError.parameterEncoderFailed(reason: .missingRequiredComponent(.url)) + } + + guard let method = request.method else { + let rawValue = request.method?.rawValue ?? "nil" + throw AFError.parameterEncoderFailed(reason: .missingRequiredComponent(.httpMethod(rawValue: rawValue))) + } + + if destination.encodesParametersInURL(for: method), + var components = URLComponents(url: url, resolvingAgainstBaseURL: false) { + let query: String = try Result { try encoder.encode(parameters) } + .mapError { AFError.parameterEncoderFailed(reason: .encoderFailed(error: $0)) }.get() + let newQueryString = [components.percentEncodedQuery, query].compactMap { $0 }.joinedWithAmpersands() + components.percentEncodedQuery = newQueryString.isEmpty ? nil : newQueryString + + guard let newURL = components.url else { + throw AFError.parameterEncoderFailed(reason: .missingRequiredComponent(.url)) + } + + request.url = newURL + } else { + if request.headers["Content-Type"] == nil { + request.headers.update(.contentType("application/x-www-form-urlencoded; charset=utf-8")) + } + + request.httpBody = try Result { try encoder.encode(parameters) } + .mapError { AFError.parameterEncoderFailed(reason: .encoderFailed(error: $0)) }.get() + } + + return request + } +} + +extension ParameterEncoder where Self == URLEncodedFormParameterEncoder { + /// Provides a default `URLEncodedFormParameterEncoder` instance. + public static var urlEncodedForm: URLEncodedFormParameterEncoder { URLEncodedFormParameterEncoder() } + + /// Creates a `URLEncodedFormParameterEncoder` with the provided encoder and destination. + /// + /// - Parameters: + /// - encoder: `URLEncodedFormEncoder` used to encode the parameters. `URLEncodedFormEncoder()` by default. + /// - destination: `Destination` to which to encode the parameters. `.methodDependent` by default. + /// - Returns: The `URLEncodedFormParameterEncoder`. + public static func urlEncodedForm(encoder: URLEncodedFormEncoder = URLEncodedFormEncoder(), + destination: URLEncodedFormParameterEncoder.Destination = .methodDependent) -> URLEncodedFormParameterEncoder { + URLEncodedFormParameterEncoder(encoder: encoder, destination: destination) + } +} diff --git a/Pods/Alamofire/Source/Core/ParameterEncoding.swift b/Pods/Alamofire/Source/Core/ParameterEncoding.swift new file mode 100644 index 0000000..5b7d680 --- /dev/null +++ b/Pods/Alamofire/Source/Core/ParameterEncoding.swift @@ -0,0 +1,349 @@ +// +// ParameterEncoding.swift +// +// Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +import Foundation + +/// A dictionary of parameters to apply to a `URLRequest`. +public typealias Parameters = [String: Any] + +/// A type used to define how a set of parameters are applied to a `URLRequest`. +public protocol ParameterEncoding { + /// Creates a `URLRequest` by encoding parameters and applying them on the passed request. + /// + /// - Parameters: + /// - urlRequest: `URLRequestConvertible` value onto which parameters will be encoded. + /// - parameters: `Parameters` to encode onto the request. + /// + /// - Returns: The encoded `URLRequest`. + /// - Throws: Any `Error` produced during parameter encoding. + func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest +} + +// MARK: - + +/// Creates a url-encoded query string to be set as or appended to any existing URL query string or set as the HTTP +/// body of the URL request. Whether the query string is set or appended to any existing URL query string or set as +/// the HTTP body depends on the destination of the encoding. +/// +/// The `Content-Type` HTTP header field of an encoded request with HTTP body is set to +/// `application/x-www-form-urlencoded; charset=utf-8`. +/// +/// There is no published specification for how to encode collection types. By default the convention of appending +/// `[]` to the key for array values (`foo[]=1&foo[]=2`), and appending the key surrounded by square brackets for +/// nested dictionary values (`foo[bar]=baz`) is used. Optionally, `ArrayEncoding` can be used to omit the +/// square brackets appended to array keys. +/// +/// `BoolEncoding` can be used to configure how boolean values are encoded. The default behavior is to encode +/// `true` as 1 and `false` as 0. +public struct URLEncoding: ParameterEncoding { + // MARK: Helper Types + + /// Defines whether the url-encoded query string is applied to the existing query string or HTTP body of the + /// resulting URL request. + public enum Destination { + /// Applies encoded query string result to existing query string for `GET`, `HEAD` and `DELETE` requests and + /// sets as the HTTP body for requests with any other HTTP method. + case methodDependent + /// Sets or appends encoded query string result to existing query string. + case queryString + /// Sets encoded query string result as the HTTP body of the URL request. + case httpBody + + func encodesParametersInURL(for method: HTTPMethod) -> Bool { + switch self { + case .methodDependent: return [.get, .head, .delete].contains(method) + case .queryString: return true + case .httpBody: return false + } + } + } + + /// Configures how `Array` parameters are encoded. + public enum ArrayEncoding { + /// An empty set of square brackets is appended to the key for every value. This is the default behavior. + case brackets + /// No brackets are appended. The key is encoded as is. + case noBrackets + /// Brackets containing the item index are appended. This matches the jQuery and Node.js behavior. + case indexInBrackets + /// Provide a custom array key encoding with the given closure. + case custom((_ key: String, _ index: Int) -> String) + + func encode(key: String, atIndex index: Int) -> String { + switch self { + case .brackets: + return "\(key)[]" + case .noBrackets: + return key + case .indexInBrackets: + return "\(key)[\(index)]" + case let .custom(encoding): + return encoding(key, index) + } + } + } + + /// Configures how `Bool` parameters are encoded. + public enum BoolEncoding { + /// Encode `true` as `1` and `false` as `0`. This is the default behavior. + case numeric + /// Encode `true` and `false` as string literals. + case literal + + func encode(value: Bool) -> String { + switch self { + case .numeric: + return value ? "1" : "0" + case .literal: + return value ? "true" : "false" + } + } + } + + // MARK: Properties + + /// Returns a default `URLEncoding` instance with a `.methodDependent` destination. + public static var `default`: URLEncoding { URLEncoding() } + + /// Returns a `URLEncoding` instance with a `.queryString` destination. + public static var queryString: URLEncoding { URLEncoding(destination: .queryString) } + + /// Returns a `URLEncoding` instance with an `.httpBody` destination. + public static var httpBody: URLEncoding { URLEncoding(destination: .httpBody) } + + /// The destination defining where the encoded query string is to be applied to the URL request. + public let destination: Destination + + /// The encoding to use for `Array` parameters. + public let arrayEncoding: ArrayEncoding + + /// The encoding to use for `Bool` parameters. + public let boolEncoding: BoolEncoding + + // MARK: Initialization + + /// Creates an instance using the specified parameters. + /// + /// - Parameters: + /// - destination: `Destination` defining where the encoded query string will be applied. `.methodDependent` by + /// default. + /// - arrayEncoding: `ArrayEncoding` to use. `.brackets` by default. + /// - boolEncoding: `BoolEncoding` to use. `.numeric` by default. + public init(destination: Destination = .methodDependent, + arrayEncoding: ArrayEncoding = .brackets, + boolEncoding: BoolEncoding = .numeric) { + self.destination = destination + self.arrayEncoding = arrayEncoding + self.boolEncoding = boolEncoding + } + + // MARK: Encoding + + public func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest { + var urlRequest = try urlRequest.asURLRequest() + + guard let parameters else { return urlRequest } + + if let method = urlRequest.method, destination.encodesParametersInURL(for: method) { + guard let url = urlRequest.url else { + throw AFError.parameterEncodingFailed(reason: .missingURL) + } + + if var urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false), !parameters.isEmpty { + let percentEncodedQuery = (urlComponents.percentEncodedQuery.map { $0 + "&" } ?? "") + query(parameters) + urlComponents.percentEncodedQuery = percentEncodedQuery + urlRequest.url = urlComponents.url + } + } else { + if urlRequest.headers["Content-Type"] == nil { + urlRequest.headers.update(.contentType("application/x-www-form-urlencoded; charset=utf-8")) + } + + urlRequest.httpBody = Data(query(parameters).utf8) + } + + return urlRequest + } + + /// Creates a percent-escaped, URL encoded query string components from the given key-value pair recursively. + /// + /// - Parameters: + /// - key: Key of the query component. + /// - value: Value of the query component. + /// + /// - Returns: The percent-escaped, URL encoded query string components. + public func queryComponents(fromKey key: String, value: Any) -> [(String, String)] { + var components: [(String, String)] = [] + switch value { + case let dictionary as [String: Any]: + for (nestedKey, value) in dictionary { + components += queryComponents(fromKey: "\(key)[\(nestedKey)]", value: value) + } + case let array as [Any]: + for (index, value) in array.enumerated() { + components += queryComponents(fromKey: arrayEncoding.encode(key: key, atIndex: index), value: value) + } + case let number as NSNumber: + if number.isBool { + components.append((escape(key), escape(boolEncoding.encode(value: number.boolValue)))) + } else { + components.append((escape(key), escape("\(number)"))) + } + case let bool as Bool: + components.append((escape(key), escape(boolEncoding.encode(value: bool)))) + default: + components.append((escape(key), escape("\(value)"))) + } + return components + } + + /// Creates a percent-escaped string following RFC 3986 for a query string key or value. + /// + /// - Parameter string: `String` to be percent-escaped. + /// + /// - Returns: The percent-escaped `String`. + public func escape(_ string: String) -> String { + string.addingPercentEncoding(withAllowedCharacters: .afURLQueryAllowed) ?? string + } + + private func query(_ parameters: [String: Any]) -> String { + var components: [(String, String)] = [] + + for key in parameters.keys.sorted(by: <) { + let value = parameters[key]! + components += queryComponents(fromKey: key, value: value) + } + return components.map { "\($0)=\($1)" }.joined(separator: "&") + } +} + +// MARK: - + +/// Uses `JSONSerialization` to create a JSON representation of the parameters object, which is set as the body of the +/// request. The `Content-Type` HTTP header field of an encoded request is set to `application/json`. +public struct JSONEncoding: ParameterEncoding { + /// Error produced by `JSONEncoding`. + public enum Error: Swift.Error { + /// `JSONEncoding` attempted to encode an invalid JSON object. Usually this means the value included types not + /// representable in Objective-C. See `JSONSerialization.isValidJSONObject` for details. + case invalidJSONObject + } + + // MARK: Properties + + /// Returns a `JSONEncoding` instance with default writing options. + public static var `default`: JSONEncoding { JSONEncoding() } + + /// Returns a `JSONEncoding` instance with `.prettyPrinted` writing options. + public static var prettyPrinted: JSONEncoding { JSONEncoding(options: .prettyPrinted) } + + /// The options for writing the parameters as JSON data. + public let options: JSONSerialization.WritingOptions + + // MARK: Initialization + + /// Creates an instance using the specified `WritingOptions`. + /// + /// - Parameter options: `JSONSerialization.WritingOptions` to use. + public init(options: JSONSerialization.WritingOptions = []) { + self.options = options + } + + // MARK: Encoding + + public func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest { + var urlRequest = try urlRequest.asURLRequest() + + guard let parameters else { return urlRequest } + + guard JSONSerialization.isValidJSONObject(parameters) else { + throw AFError.parameterEncodingFailed(reason: .jsonEncodingFailed(error: Error.invalidJSONObject)) + } + + do { + let data = try JSONSerialization.data(withJSONObject: parameters, options: options) + + if urlRequest.headers["Content-Type"] == nil { + urlRequest.headers.update(.contentType("application/json")) + } + + urlRequest.httpBody = data + } catch { + throw AFError.parameterEncodingFailed(reason: .jsonEncodingFailed(error: error)) + } + + return urlRequest + } + + /// Encodes any JSON compatible object into a `URLRequest`. + /// + /// - Parameters: + /// - urlRequest: `URLRequestConvertible` value into which the object will be encoded. + /// - jsonObject: `Any` value (must be JSON compatible) to be encoded into the `URLRequest`. `nil` by default. + /// + /// - Returns: The encoded `URLRequest`. + /// - Throws: Any `Error` produced during encoding. + public func encode(_ urlRequest: URLRequestConvertible, withJSONObject jsonObject: Any? = nil) throws -> URLRequest { + var urlRequest = try urlRequest.asURLRequest() + + guard let jsonObject else { return urlRequest } + + guard JSONSerialization.isValidJSONObject(jsonObject) else { + throw AFError.parameterEncodingFailed(reason: .jsonEncodingFailed(error: Error.invalidJSONObject)) + } + + do { + let data = try JSONSerialization.data(withJSONObject: jsonObject, options: options) + + if urlRequest.headers["Content-Type"] == nil { + urlRequest.headers.update(.contentType("application/json")) + } + + urlRequest.httpBody = data + } catch { + throw AFError.parameterEncodingFailed(reason: .jsonEncodingFailed(error: error)) + } + + return urlRequest + } +} + +extension JSONEncoding.Error { + public var localizedDescription: String { + """ + Invalid JSON object provided for parameter or object encoding. \ + This is most likely due to a value which can't be represented in Objective-C. + """ + } +} + +// MARK: - + +extension NSNumber { + fileprivate var isBool: Bool { + // Use Obj-C type encoding to check whether the underlying type is a `Bool`, as it's guaranteed as part of + // swift-corelibs-foundation, per [this discussion on the Swift forums](https://forums.swift.org/t/alamofire-on-linux-possible-but-not-release-ready/34553/22). + String(cString: objCType) == "c" + } +} diff --git a/Pods/Alamofire/Source/Core/Protected.swift b/Pods/Alamofire/Source/Core/Protected.swift new file mode 100644 index 0000000..6756045 --- /dev/null +++ b/Pods/Alamofire/Source/Core/Protected.swift @@ -0,0 +1,168 @@ +// +// Protected.swift +// +// Copyright (c) 2014-2020 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +import Foundation + +private protocol Lock { + func lock() + func unlock() +} + +extension Lock { + /// Executes a closure returning a value while acquiring the lock. + /// + /// - Parameter closure: The closure to run. + /// + /// - Returns: The value the closure generated. + func around(_ closure: () throws -> T) rethrows -> T { + lock(); defer { unlock() } + return try closure() + } + + /// Execute a closure while acquiring the lock. + /// + /// - Parameter closure: The closure to run. + func around(_ closure: () throws -> Void) rethrows { + lock(); defer { unlock() } + try closure() + } +} + +#if canImport(Darwin) +/// An `os_unfair_lock` wrapper. +final class UnfairLock: Lock { + private let unfairLock: os_unfair_lock_t + + init() { + unfairLock = .allocate(capacity: 1) + unfairLock.initialize(to: os_unfair_lock()) + } + + deinit { + unfairLock.deinitialize(count: 1) + unfairLock.deallocate() + } + + fileprivate func lock() { + os_unfair_lock_lock(unfairLock) + } + + fileprivate func unlock() { + os_unfair_lock_unlock(unfairLock) + } +} + +#elseif canImport(Foundation) +extension NSLock: Lock {} +#else +#error("This platform needs a Lock-conforming type without Foundation.") +#endif + +/// A thread-safe wrapper around a value. +@dynamicMemberLookup +final class Protected { + #if canImport(Darwin) + private let lock = UnfairLock() + #elseif canImport(Foundation) + private let lock = NSLock() + #else + #error("This platform needs a Lock-conforming type without Foundation.") + #endif + private var value: Value + + init(_ value: Value) { + self.value = value + } + + /// Synchronously read or transform the contained value. + /// + /// - Parameter closure: The closure to execute. + /// + /// - Returns: The return value of the closure passed. + func read(_ closure: (Value) throws -> U) rethrows -> U { + try lock.around { try closure(self.value) } + } + + /// Synchronously modify the protected value. + /// + /// - Parameter closure: The closure to execute. + /// + /// - Returns: The modified value. + @discardableResult + func write(_ closure: (inout Value) throws -> U) rethrows -> U { + try lock.around { try closure(&self.value) } + } + + /// Synchronously update the protected value. + /// + /// - Parameter value: The `Value`. + func write(_ value: Value) { + write { $0 = value } + } + + subscript(dynamicMember keyPath: WritableKeyPath) -> Property { + get { lock.around { value[keyPath: keyPath] } } + set { lock.around { value[keyPath: keyPath] = newValue } } + } + + subscript(dynamicMember keyPath: KeyPath) -> Property { + lock.around { value[keyPath: keyPath] } + } +} + +extension Protected where Value == Request.MutableState { + /// Attempts to transition to the passed `State`. + /// + /// - Parameter state: The `State` to attempt transition to. + /// + /// - Returns: Whether the transition occurred. + func attemptToTransitionTo(_ state: Request.State) -> Bool { + lock.around { + guard value.state.canTransitionTo(state) else { return false } + + value.state = state + + return true + } + } + + /// Perform a closure while locked with the provided `Request.State`. + /// + /// - Parameter perform: The closure to perform while locked. + func withState(perform: (Request.State) -> Void) { + lock.around { perform(value.state) } + } +} + +extension Protected: Equatable where Value: Equatable { + static func ==(lhs: Protected, rhs: Protected) -> Bool { + lhs.read { left in rhs.read { right in left == right }} + } +} + +extension Protected: Hashable where Value: Hashable { + func hash(into hasher: inout Hasher) { + read { hasher.combine($0) } + } +} diff --git a/Pods/Alamofire/Source/Core/Request.swift b/Pods/Alamofire/Source/Core/Request.swift new file mode 100644 index 0000000..e1df52e --- /dev/null +++ b/Pods/Alamofire/Source/Core/Request.swift @@ -0,0 +1,1090 @@ +// +// Request.swift +// +// Copyright (c) 2014-2024 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +import Foundation + +/// `Request` is the common superclass of all Alamofire request types and provides common state, delegate, and callback +/// handling. +public class Request { + /// State of the `Request`, with managed transitions between states set when calling `resume()`, `suspend()`, or + /// `cancel()` on the `Request`. + public enum State { + /// Initial state of the `Request`. + case initialized + /// `State` set when `resume()` is called. Any tasks created for the `Request` will have `resume()` called on + /// them in this state. + case resumed + /// `State` set when `suspend()` is called. Any tasks created for the `Request` will have `suspend()` called on + /// them in this state. + case suspended + /// `State` set when `cancel()` is called. Any tasks created for the `Request` will have `cancel()` called on + /// them. Unlike `resumed` or `suspended`, once in the `cancelled` state, the `Request` can no longer transition + /// to any other state. + case cancelled + /// `State` set when all response serialization completion closures have been cleared on the `Request` and + /// enqueued on their respective queues. + case finished + + /// Determines whether `self` can be transitioned to the provided `State`. + func canTransitionTo(_ state: State) -> Bool { + switch (self, state) { + case (.initialized, _): + return true + case (_, .initialized), (.cancelled, _), (.finished, _): + return false + case (.resumed, .cancelled), (.suspended, .cancelled), (.resumed, .suspended), (.suspended, .resumed): + return true + case (.suspended, .suspended), (.resumed, .resumed): + return false + case (_, .finished): + return true + } + } + } + + // MARK: - Initial State + + /// `UUID` providing a unique identifier for the `Request`, used in the `Hashable` and `Equatable` conformances. + public let id: UUID + /// The serial queue for all internal async actions. + public let underlyingQueue: DispatchQueue + /// The queue used for all serialization actions. By default it's a serial queue that targets `underlyingQueue`. + public let serializationQueue: DispatchQueue + /// `EventMonitor` used for event callbacks. + public let eventMonitor: EventMonitor? + /// The `Request`'s interceptor. + public let interceptor: RequestInterceptor? + /// The `Request`'s delegate. + public private(set) weak var delegate: RequestDelegate? + + // MARK: - Mutable State + + /// Type encapsulating all mutable state that may need to be accessed from anything other than the `underlyingQueue`. + struct MutableState { + /// State of the `Request`. + var state: State = .initialized + /// `ProgressHandler` and `DispatchQueue` provided for upload progress callbacks. + var uploadProgressHandler: (handler: ProgressHandler, queue: DispatchQueue)? + /// `ProgressHandler` and `DispatchQueue` provided for download progress callbacks. + var downloadProgressHandler: (handler: ProgressHandler, queue: DispatchQueue)? + /// `RedirectHandler` provided for to handle request redirection. + var redirectHandler: RedirectHandler? + /// `CachedResponseHandler` provided to handle response caching. + var cachedResponseHandler: CachedResponseHandler? + /// Queue and closure called when the `Request` is able to create a cURL description of itself. + var cURLHandler: (queue: DispatchQueue, handler: (String) -> Void)? + /// Queue and closure called when the `Request` creates a `URLRequest`. + var urlRequestHandler: (queue: DispatchQueue, handler: (URLRequest) -> Void)? + /// Queue and closure called when the `Request` creates a `URLSessionTask`. + var urlSessionTaskHandler: (queue: DispatchQueue, handler: (URLSessionTask) -> Void)? + /// Response serialization closures that handle response parsing. + var responseSerializers: [() -> Void] = [] + /// Response serialization completion closures executed once all response serializers are complete. + var responseSerializerCompletions: [() -> Void] = [] + /// Whether response serializer processing is finished. + var responseSerializerProcessingFinished = false + /// `URLCredential` used for authentication challenges. + var credential: URLCredential? + /// All `URLRequest`s created by Alamofire on behalf of the `Request`. + var requests: [URLRequest] = [] + /// All `URLSessionTask`s created by Alamofire on behalf of the `Request`. + var tasks: [URLSessionTask] = [] + /// All `URLSessionTaskMetrics` values gathered by Alamofire on behalf of the `Request`. Should correspond + /// exactly the the `tasks` created. + var metrics: [URLSessionTaskMetrics] = [] + /// Number of times any retriers provided retried the `Request`. + var retryCount = 0 + /// Final `AFError` for the `Request`, whether from various internal Alamofire calls or as a result of a `task`. + var error: AFError? + /// Whether the instance has had `finish()` called and is running the serializers. Should be replaced with a + /// representation in the state machine in the future. + var isFinishing = false + /// Actions to run when requests are finished. Use for concurrency support. + var finishHandlers: [() -> Void] = [] + } + + /// Protected `MutableState` value that provides thread-safe access to state values. + let mutableState = Protected(MutableState()) + + /// `State` of the `Request`. + public var state: State { mutableState.state } + /// Returns whether `state` is `.initialized`. + public var isInitialized: Bool { state == .initialized } + /// Returns whether `state` is `.resumed`. + public var isResumed: Bool { state == .resumed } + /// Returns whether `state` is `.suspended`. + public var isSuspended: Bool { state == .suspended } + /// Returns whether `state` is `.cancelled`. + public var isCancelled: Bool { state == .cancelled } + /// Returns whether `state` is `.finished`. + public var isFinished: Bool { state == .finished } + + // MARK: Progress + + /// Closure type executed when monitoring the upload or download progress of a request. + public typealias ProgressHandler = (Progress) -> Void + + /// `Progress` of the upload of the body of the executed `URLRequest`. Reset to `0` if the `Request` is retried. + public let uploadProgress = Progress(totalUnitCount: 0) + /// `Progress` of the download of any response data. Reset to `0` if the `Request` is retried. + public let downloadProgress = Progress(totalUnitCount: 0) + /// `ProgressHandler` called when `uploadProgress` is updated, on the provided `DispatchQueue`. + public internal(set) var uploadProgressHandler: (handler: ProgressHandler, queue: DispatchQueue)? { + get { mutableState.uploadProgressHandler } + set { mutableState.uploadProgressHandler = newValue } + } + + /// `ProgressHandler` called when `downloadProgress` is updated, on the provided `DispatchQueue`. + public internal(set) var downloadProgressHandler: (handler: ProgressHandler, queue: DispatchQueue)? { + get { mutableState.downloadProgressHandler } + set { mutableState.downloadProgressHandler = newValue } + } + + // MARK: Redirect Handling + + /// `RedirectHandler` set on the instance. + public internal(set) var redirectHandler: RedirectHandler? { + get { mutableState.redirectHandler } + set { mutableState.redirectHandler = newValue } + } + + // MARK: Cached Response Handling + + /// `CachedResponseHandler` set on the instance. + public internal(set) var cachedResponseHandler: CachedResponseHandler? { + get { mutableState.cachedResponseHandler } + set { mutableState.cachedResponseHandler = newValue } + } + + // MARK: URLCredential + + /// `URLCredential` used for authentication challenges. Created by calling one of the `authenticate` methods. + public internal(set) var credential: URLCredential? { + get { mutableState.credential } + set { mutableState.credential = newValue } + } + + // MARK: Validators + + /// `Validator` callback closures that store the validation calls enqueued. + let validators = Protected<[() -> Void]>([]) + + // MARK: URLRequests + + /// All `URLRequest`s created on behalf of the `Request`, including original and adapted requests. + public var requests: [URLRequest] { mutableState.requests } + /// First `URLRequest` created on behalf of the `Request`. May not be the first one actually executed. + public var firstRequest: URLRequest? { requests.first } + /// Last `URLRequest` created on behalf of the `Request`. + public var lastRequest: URLRequest? { requests.last } + /// Current `URLRequest` created on behalf of the `Request`. + public var request: URLRequest? { lastRequest } + + /// `URLRequest`s from all of the `URLSessionTask`s executed on behalf of the `Request`. May be different from + /// `requests` due to `URLSession` manipulation. + public var performedRequests: [URLRequest] { mutableState.read { $0.tasks.compactMap(\.currentRequest) } } + + // MARK: HTTPURLResponse + + /// `HTTPURLResponse` received from the server, if any. If the `Request` was retried, this is the response of the + /// last `URLSessionTask`. + public var response: HTTPURLResponse? { lastTask?.response as? HTTPURLResponse } + + // MARK: Tasks + + /// All `URLSessionTask`s created on behalf of the `Request`. + public var tasks: [URLSessionTask] { mutableState.tasks } + /// First `URLSessionTask` created on behalf of the `Request`. + public var firstTask: URLSessionTask? { tasks.first } + /// Last `URLSessionTask` created on behalf of the `Request`. + public var lastTask: URLSessionTask? { tasks.last } + /// Current `URLSessionTask` created on behalf of the `Request`. + public var task: URLSessionTask? { lastTask } + + // MARK: Metrics + + /// All `URLSessionTaskMetrics` gathered on behalf of the `Request`. Should correspond to the `tasks` created. + public var allMetrics: [URLSessionTaskMetrics] { mutableState.metrics } + /// First `URLSessionTaskMetrics` gathered on behalf of the `Request`. + public var firstMetrics: URLSessionTaskMetrics? { allMetrics.first } + /// Last `URLSessionTaskMetrics` gathered on behalf of the `Request`. + public var lastMetrics: URLSessionTaskMetrics? { allMetrics.last } + /// Current `URLSessionTaskMetrics` gathered on behalf of the `Request`. + public var metrics: URLSessionTaskMetrics? { lastMetrics } + + // MARK: Retry Count + + /// Number of times the `Request` has been retried. + public var retryCount: Int { mutableState.retryCount } + + // MARK: Error + + /// `Error` returned from Alamofire internally, from the network request directly, or any validators executed. + public internal(set) var error: AFError? { + get { mutableState.error } + set { mutableState.error = newValue } + } + + /// Default initializer for the `Request` superclass. + /// + /// - Parameters: + /// - id: `UUID` used for the `Hashable` and `Equatable` implementations. `UUID()` by default. + /// - underlyingQueue: `DispatchQueue` on which all internal `Request` work is performed. + /// - serializationQueue: `DispatchQueue` on which all serialization work is performed. By default targets + /// `underlyingQueue`, but can be passed another queue from a `Session`. + /// - eventMonitor: `EventMonitor` called for event callbacks from internal `Request` actions. + /// - interceptor: `RequestInterceptor` used throughout the request lifecycle. + /// - delegate: `RequestDelegate` that provides an interface to actions not performed by the `Request`. + init(id: UUID = UUID(), + underlyingQueue: DispatchQueue, + serializationQueue: DispatchQueue, + eventMonitor: EventMonitor?, + interceptor: RequestInterceptor?, + delegate: RequestDelegate) { + self.id = id + self.underlyingQueue = underlyingQueue + self.serializationQueue = serializationQueue + self.eventMonitor = eventMonitor + self.interceptor = interceptor + self.delegate = delegate + } + + // MARK: - Internal Event API + + // All API must be called from underlyingQueue. + + /// Called when an initial `URLRequest` has been created on behalf of the instance. If a `RequestAdapter` is active, + /// the `URLRequest` will be adapted before being issued. + /// + /// - Parameter request: The `URLRequest` created. + func didCreateInitialURLRequest(_ request: URLRequest) { + dispatchPrecondition(condition: .onQueue(underlyingQueue)) + + mutableState.write { $0.requests.append(request) } + + eventMonitor?.request(self, didCreateInitialURLRequest: request) + } + + /// Called when initial `URLRequest` creation has failed, typically through a `URLRequestConvertible`. + /// + /// - Note: Triggers retry. + /// + /// - Parameter error: `AFError` thrown from the failed creation. + func didFailToCreateURLRequest(with error: AFError) { + dispatchPrecondition(condition: .onQueue(underlyingQueue)) + + self.error = error + + eventMonitor?.request(self, didFailToCreateURLRequestWithError: error) + + callCURLHandlerIfNecessary() + + retryOrFinish(error: error) + } + + /// Called when a `RequestAdapter` has successfully adapted a `URLRequest`. + /// + /// - Parameters: + /// - initialRequest: The `URLRequest` that was adapted. + /// - adaptedRequest: The `URLRequest` returned by the `RequestAdapter`. + func didAdaptInitialRequest(_ initialRequest: URLRequest, to adaptedRequest: URLRequest) { + dispatchPrecondition(condition: .onQueue(underlyingQueue)) + + mutableState.write { $0.requests.append(adaptedRequest) } + + eventMonitor?.request(self, didAdaptInitialRequest: initialRequest, to: adaptedRequest) + } + + /// Called when a `RequestAdapter` fails to adapt a `URLRequest`. + /// + /// - Note: Triggers retry. + /// + /// - Parameters: + /// - request: The `URLRequest` the adapter was called with. + /// - error: The `AFError` returned by the `RequestAdapter`. + func didFailToAdaptURLRequest(_ request: URLRequest, withError error: AFError) { + dispatchPrecondition(condition: .onQueue(underlyingQueue)) + + self.error = error + + eventMonitor?.request(self, didFailToAdaptURLRequest: request, withError: error) + + callCURLHandlerIfNecessary() + + retryOrFinish(error: error) + } + + /// Final `URLRequest` has been created for the instance. + /// + /// - Parameter request: The `URLRequest` created. + func didCreateURLRequest(_ request: URLRequest) { + dispatchPrecondition(condition: .onQueue(underlyingQueue)) + + mutableState.read { state in + state.urlRequestHandler?.queue.async { state.urlRequestHandler?.handler(request) } + } + + eventMonitor?.request(self, didCreateURLRequest: request) + + callCURLHandlerIfNecessary() + } + + /// Asynchronously calls any stored `cURLHandler` and then removes it from `mutableState`. + private func callCURLHandlerIfNecessary() { + mutableState.write { mutableState in + guard let cURLHandler = mutableState.cURLHandler else { return } + + cURLHandler.queue.async { cURLHandler.handler(self.cURLDescription()) } + + mutableState.cURLHandler = nil + } + } + + /// Called when a `URLSessionTask` is created on behalf of the instance. + /// + /// - Parameter task: The `URLSessionTask` created. + func didCreateTask(_ task: URLSessionTask) { + dispatchPrecondition(condition: .onQueue(underlyingQueue)) + + mutableState.write { state in + state.tasks.append(task) + + guard let urlSessionTaskHandler = state.urlSessionTaskHandler else { return } + + urlSessionTaskHandler.queue.async { urlSessionTaskHandler.handler(task) } + } + + eventMonitor?.request(self, didCreateTask: task) + } + + /// Called when resumption is completed. + func didResume() { + dispatchPrecondition(condition: .onQueue(underlyingQueue)) + + eventMonitor?.requestDidResume(self) + } + + /// Called when a `URLSessionTask` is resumed on behalf of the instance. + /// + /// - Parameter task: The `URLSessionTask` resumed. + func didResumeTask(_ task: URLSessionTask) { + dispatchPrecondition(condition: .onQueue(underlyingQueue)) + + eventMonitor?.request(self, didResumeTask: task) + } + + /// Called when suspension is completed. + func didSuspend() { + dispatchPrecondition(condition: .onQueue(underlyingQueue)) + + eventMonitor?.requestDidSuspend(self) + } + + /// Called when a `URLSessionTask` is suspended on behalf of the instance. + /// + /// - Parameter task: The `URLSessionTask` suspended. + func didSuspendTask(_ task: URLSessionTask) { + dispatchPrecondition(condition: .onQueue(underlyingQueue)) + + eventMonitor?.request(self, didSuspendTask: task) + } + + /// Called when cancellation is completed, sets `error` to `AFError.explicitlyCancelled`. + func didCancel() { + dispatchPrecondition(condition: .onQueue(underlyingQueue)) + + mutableState.write { mutableState in + mutableState.error = mutableState.error ?? AFError.explicitlyCancelled + } + + eventMonitor?.requestDidCancel(self) + } + + /// Called when a `URLSessionTask` is cancelled on behalf of the instance. + /// + /// - Parameter task: The `URLSessionTask` cancelled. + func didCancelTask(_ task: URLSessionTask) { + dispatchPrecondition(condition: .onQueue(underlyingQueue)) + + eventMonitor?.request(self, didCancelTask: task) + } + + /// Called when a `URLSessionTaskMetrics` value is gathered on behalf of the instance. + /// + /// - Parameter metrics: The `URLSessionTaskMetrics` gathered. + func didGatherMetrics(_ metrics: URLSessionTaskMetrics) { + dispatchPrecondition(condition: .onQueue(underlyingQueue)) + + mutableState.write { $0.metrics.append(metrics) } + + eventMonitor?.request(self, didGatherMetrics: metrics) + } + + /// Called when a `URLSessionTask` fails before it is finished, typically during certificate pinning. + /// + /// - Parameters: + /// - task: The `URLSessionTask` which failed. + /// - error: The early failure `AFError`. + func didFailTask(_ task: URLSessionTask, earlyWithError error: AFError) { + dispatchPrecondition(condition: .onQueue(underlyingQueue)) + + self.error = error + + // Task will still complete, so didCompleteTask(_:with:) will handle retry. + eventMonitor?.request(self, didFailTask: task, earlyWithError: error) + } + + /// Called when a `URLSessionTask` completes. All tasks will eventually call this method. + /// + /// - Note: Response validation is synchronously triggered in this step. + /// + /// - Parameters: + /// - task: The `URLSessionTask` which completed. + /// - error: The `AFError` `task` may have completed with. If `error` has already been set on the instance, this + /// value is ignored. + func didCompleteTask(_ task: URLSessionTask, with error: AFError?) { + dispatchPrecondition(condition: .onQueue(underlyingQueue)) + + self.error = self.error ?? error + + let validators = validators.read { $0 } + validators.forEach { $0() } + + eventMonitor?.request(self, didCompleteTask: task, with: error) + + retryOrFinish(error: self.error) + } + + /// Called when the `RequestDelegate` is going to retry this `Request`. Calls `reset()`. + func prepareForRetry() { + dispatchPrecondition(condition: .onQueue(underlyingQueue)) + + mutableState.write { $0.retryCount += 1 } + + reset() + + eventMonitor?.requestIsRetrying(self) + } + + /// Called to determine whether retry will be triggered for the particular error, or whether the instance should + /// call `finish()`. + /// + /// - Parameter error: The possible `AFError` which may trigger retry. + func retryOrFinish(error: AFError?) { + dispatchPrecondition(condition: .onQueue(underlyingQueue)) + + guard !isCancelled, let error, let delegate else { finish(); return } + + delegate.retryResult(for: self, dueTo: error) { retryResult in + switch retryResult { + case .doNotRetry: + self.finish() + case let .doNotRetryWithError(retryError): + self.finish(error: retryError.asAFError(orFailWith: "Received retryError was not already AFError")) + case .retry, .retryWithDelay: + delegate.retryRequest(self, withDelay: retryResult.delay) + } + } + } + + /// Finishes this `Request` and starts the response serializers. + /// + /// - Parameter error: The possible `Error` with which the instance will finish. + func finish(error: AFError? = nil) { + dispatchPrecondition(condition: .onQueue(underlyingQueue)) + + guard !mutableState.isFinishing else { return } + + mutableState.isFinishing = true + + if let error { self.error = error } + + // Start response handlers + processNextResponseSerializer() + + eventMonitor?.requestDidFinish(self) + } + + /// Appends the response serialization closure to the instance. + /// + /// - Note: This method will also `resume` the instance if `delegate.startImmediately` returns `true`. + /// + /// - Parameter closure: The closure containing the response serialization call. + func appendResponseSerializer(_ closure: @escaping () -> Void) { + mutableState.write { mutableState in + mutableState.responseSerializers.append(closure) + + if mutableState.state == .finished { + mutableState.state = .resumed + } + + if mutableState.responseSerializerProcessingFinished { + underlyingQueue.async { self.processNextResponseSerializer() } + } + + if mutableState.state.canTransitionTo(.resumed) { + underlyingQueue.async { if self.delegate?.startImmediately == true { self.resume() } } + } + } + } + + /// Returns the next response serializer closure to execute if there's one left. + /// + /// - Returns: The next response serialization closure, if there is one. + func nextResponseSerializer() -> (() -> Void)? { + var responseSerializer: (() -> Void)? + + mutableState.write { mutableState in + let responseSerializerIndex = mutableState.responseSerializerCompletions.count + + if responseSerializerIndex < mutableState.responseSerializers.count { + responseSerializer = mutableState.responseSerializers[responseSerializerIndex] + } + } + + return responseSerializer + } + + /// Processes the next response serializer and calls all completions if response serialization is complete. + func processNextResponseSerializer() { + guard let responseSerializer = nextResponseSerializer() else { + // Execute all response serializer completions and clear them + var completions: [() -> Void] = [] + + mutableState.write { mutableState in + completions = mutableState.responseSerializerCompletions + + // Clear out all response serializers and response serializer completions in mutable state since the + // request is complete. It's important to do this prior to calling the completion closures in case + // the completions call back into the request triggering a re-processing of the response serializers. + // An example of how this can happen is by calling cancel inside a response completion closure. + mutableState.responseSerializers.removeAll() + mutableState.responseSerializerCompletions.removeAll() + + if mutableState.state.canTransitionTo(.finished) { + mutableState.state = .finished + } + + mutableState.responseSerializerProcessingFinished = true + mutableState.isFinishing = false + } + + completions.forEach { $0() } + + // Cleanup the request + cleanup() + + return + } + + serializationQueue.async { responseSerializer() } + } + + /// Notifies the `Request` that the response serializer is complete. + /// + /// - Parameter completion: The completion handler provided with the response serializer, called when all serializers + /// are complete. + func responseSerializerDidComplete(completion: @escaping () -> Void) { + mutableState.write { $0.responseSerializerCompletions.append(completion) } + processNextResponseSerializer() + } + + /// Resets all task and response serializer related state for retry. + func reset() { + error = nil + + uploadProgress.totalUnitCount = 0 + uploadProgress.completedUnitCount = 0 + downloadProgress.totalUnitCount = 0 + downloadProgress.completedUnitCount = 0 + + mutableState.write { state in + state.isFinishing = false + state.responseSerializerCompletions = [] + } + } + + /// Called when updating the upload progress. + /// + /// - Parameters: + /// - totalBytesSent: Total bytes sent so far. + /// - totalBytesExpectedToSend: Total bytes expected to send. + func updateUploadProgress(totalBytesSent: Int64, totalBytesExpectedToSend: Int64) { + uploadProgress.totalUnitCount = totalBytesExpectedToSend + uploadProgress.completedUnitCount = totalBytesSent + + uploadProgressHandler?.queue.async { self.uploadProgressHandler?.handler(self.uploadProgress) } + } + + /// Perform a closure on the current `state` while locked. + /// + /// - Parameter perform: The closure to perform. + func withState(perform: (State) -> Void) { + mutableState.withState(perform: perform) + } + + // MARK: Task Creation + + /// Called when creating a `URLSessionTask` for this `Request`. Subclasses must override. + /// + /// - Parameters: + /// - request: `URLRequest` to use to create the `URLSessionTask`. + /// - session: `URLSession` which creates the `URLSessionTask`. + /// + /// - Returns: The `URLSessionTask` created. + func task(for request: URLRequest, using session: URLSession) -> URLSessionTask { + fatalError("Subclasses must override.") + } + + // MARK: - Public API + + // These APIs are callable from any queue. + + // MARK: State + + /// Cancels the instance. Once cancelled, a `Request` can no longer be resumed or suspended. + /// + /// - Returns: The instance. + @discardableResult + public func cancel() -> Self { + mutableState.write { mutableState in + guard mutableState.state.canTransitionTo(.cancelled) else { return } + + mutableState.state = .cancelled + + underlyingQueue.async { self.didCancel() } + + guard let task = mutableState.tasks.last, task.state != .completed else { + underlyingQueue.async { self.finish() } + return + } + + // Resume to ensure metrics are gathered. + task.resume() + task.cancel() + underlyingQueue.async { self.didCancelTask(task) } + } + + return self + } + + /// Suspends the instance. + /// + /// - Returns: The instance. + @discardableResult + public func suspend() -> Self { + mutableState.write { mutableState in + guard mutableState.state.canTransitionTo(.suspended) else { return } + + mutableState.state = .suspended + + underlyingQueue.async { self.didSuspend() } + + guard let task = mutableState.tasks.last, task.state != .completed else { return } + + task.suspend() + underlyingQueue.async { self.didSuspendTask(task) } + } + + return self + } + + /// Resumes the instance. + /// + /// - Returns: The instance. + @discardableResult + public func resume() -> Self { + mutableState.write { mutableState in + guard mutableState.state.canTransitionTo(.resumed) else { return } + + mutableState.state = .resumed + + underlyingQueue.async { self.didResume() } + + guard let task = mutableState.tasks.last, task.state != .completed else { return } + + task.resume() + underlyingQueue.async { self.didResumeTask(task) } + } + + return self + } + + // MARK: - Closure API + + /// Associates a credential using the provided values with the instance. + /// + /// - Parameters: + /// - username: The username. + /// - password: The password. + /// - persistence: The `URLCredential.Persistence` for the created `URLCredential`. `.forSession` by default. + /// + /// - Returns: The instance. + @discardableResult + public func authenticate(username: String, password: String, persistence: URLCredential.Persistence = .forSession) -> Self { + let credential = URLCredential(user: username, password: password, persistence: persistence) + + return authenticate(with: credential) + } + + /// Associates the provided credential with the instance. + /// + /// - Parameter credential: The `URLCredential`. + /// + /// - Returns: The instance. + @discardableResult + public func authenticate(with credential: URLCredential) -> Self { + mutableState.credential = credential + + return self + } + + /// Sets a closure to be called periodically during the lifecycle of the instance as data is read from the server. + /// + /// - Note: Only the last closure provided is used. + /// + /// - Parameters: + /// - queue: The `DispatchQueue` to execute the closure on. `.main` by default. + /// - closure: The closure to be executed periodically as data is read from the server. + /// + /// - Returns: The instance. + @discardableResult + public func downloadProgress(queue: DispatchQueue = .main, closure: @escaping ProgressHandler) -> Self { + mutableState.downloadProgressHandler = (handler: closure, queue: queue) + + return self + } + + /// Sets a closure to be called periodically during the lifecycle of the instance as data is sent to the server. + /// + /// - Note: Only the last closure provided is used. + /// + /// - Parameters: + /// - queue: The `DispatchQueue` to execute the closure on. `.main` by default. + /// - closure: The closure to be executed periodically as data is sent to the server. + /// + /// - Returns: The instance. + @discardableResult + public func uploadProgress(queue: DispatchQueue = .main, closure: @escaping ProgressHandler) -> Self { + mutableState.uploadProgressHandler = (handler: closure, queue: queue) + + return self + } + + // MARK: Redirects + + /// Sets the redirect handler for the instance which will be used if a redirect response is encountered. + /// + /// - Note: Attempting to set the redirect handler more than once is a logic error and will crash. + /// + /// - Parameter handler: The `RedirectHandler`. + /// + /// - Returns: The instance. + @discardableResult + public func redirect(using handler: RedirectHandler) -> Self { + mutableState.write { mutableState in + precondition(mutableState.redirectHandler == nil, "Redirect handler has already been set.") + mutableState.redirectHandler = handler + } + + return self + } + + // MARK: Cached Responses + + /// Sets the cached response handler for the `Request` which will be used when attempting to cache a response. + /// + /// - Note: Attempting to set the cache handler more than once is a logic error and will crash. + /// + /// - Parameter handler: The `CachedResponseHandler`. + /// + /// - Returns: The instance. + @discardableResult + public func cacheResponse(using handler: CachedResponseHandler) -> Self { + mutableState.write { mutableState in + precondition(mutableState.cachedResponseHandler == nil, "Cached response handler has already been set.") + mutableState.cachedResponseHandler = handler + } + + return self + } + + // MARK: - Lifetime APIs + + /// Sets a handler to be called when the cURL description of the request is available. + /// + /// - Note: When waiting for a `Request`'s `URLRequest` to be created, only the last `handler` will be called. + /// + /// - Parameters: + /// - queue: `DispatchQueue` on which `handler` will be called. + /// - handler: Closure to be called when the cURL description is available. + /// + /// - Returns: The instance. + @discardableResult + public func cURLDescription(on queue: DispatchQueue, calling handler: @escaping (String) -> Void) -> Self { + mutableState.write { mutableState in + if mutableState.requests.last != nil { + queue.async { handler(self.cURLDescription()) } + } else { + mutableState.cURLHandler = (queue, handler) + } + } + + return self + } + + /// Sets a handler to be called when the cURL description of the request is available. + /// + /// - Note: When waiting for a `Request`'s `URLRequest` to be created, only the last `handler` will be called. + /// + /// - Parameter handler: Closure to be called when the cURL description is available. Called on the instance's + /// `underlyingQueue` by default. + /// + /// - Returns: The instance. + @discardableResult + public func cURLDescription(calling handler: @escaping (String) -> Void) -> Self { + cURLDescription(on: underlyingQueue, calling: handler) + + return self + } + + /// Sets a closure to called whenever Alamofire creates a `URLRequest` for this instance. + /// + /// - Note: This closure will be called multiple times if the instance adapts incoming `URLRequest`s or is retried. + /// + /// - Parameters: + /// - queue: `DispatchQueue` on which `handler` will be called. `.main` by default. + /// - handler: Closure to be called when a `URLRequest` is available. + /// + /// - Returns: The instance. + @discardableResult + public func onURLRequestCreation(on queue: DispatchQueue = .main, perform handler: @escaping (URLRequest) -> Void) -> Self { + mutableState.write { state in + if let request = state.requests.last { + queue.async { handler(request) } + } + + state.urlRequestHandler = (queue, handler) + } + + return self + } + + /// Sets a closure to be called whenever the instance creates a `URLSessionTask`. + /// + /// - Note: This API should only be used to provide `URLSessionTask`s to existing API, like `NSFileProvider`. It + /// **SHOULD NOT** be used to interact with tasks directly, as that may be break Alamofire features. + /// Additionally, this closure may be called multiple times if the instance is retried. + /// + /// - Parameters: + /// - queue: `DispatchQueue` on which `handler` will be called. `.main` by default. + /// - handler: Closure to be called when the `URLSessionTask` is available. + /// + /// - Returns: The instance. + @discardableResult + public func onURLSessionTaskCreation(on queue: DispatchQueue = .main, perform handler: @escaping (URLSessionTask) -> Void) -> Self { + mutableState.write { state in + if let task = state.tasks.last { + queue.async { handler(task) } + } + + state.urlSessionTaskHandler = (queue, handler) + } + + return self + } + + // MARK: Cleanup + + /// Adds a `finishHandler` closure to be called when the request completes. + /// + /// - Parameter closure: Closure to be called when the request finishes. + func onFinish(perform finishHandler: @escaping () -> Void) { + guard !isFinished else { finishHandler(); return } + + mutableState.write { state in + state.finishHandlers.append(finishHandler) + } + } + + /// Final cleanup step executed when the instance finishes response serialization. + func cleanup() { + let handlers = mutableState.finishHandlers + handlers.forEach { $0() } + mutableState.write { state in + state.finishHandlers.removeAll() + } + + delegate?.cleanup(after: self) + } +} + +extension Request { + /// Type indicating how a `DataRequest` or `DataStreamRequest` should proceed after receiving an `HTTPURLResponse`. + public enum ResponseDisposition { + /// Allow the request to continue normally. + case allow + /// Cancel the request, similar to calling `cancel()`. + case cancel + + var sessionDisposition: URLSession.ResponseDisposition { + switch self { + case .allow: return .allow + case .cancel: return .cancel + } + } + } +} + +// MARK: - Protocol Conformances + +extension Request: Equatable { + public static func ==(lhs: Request, rhs: Request) -> Bool { + lhs.id == rhs.id + } +} + +extension Request: Hashable { + public func hash(into hasher: inout Hasher) { + hasher.combine(id) + } +} + +extension Request: CustomStringConvertible { + /// A textual representation of this instance, including the `HTTPMethod` and `URL` if the `URLRequest` has been + /// created, as well as the response status code, if a response has been received. + public var description: String { + guard let request = performedRequests.last ?? lastRequest, + let url = request.url, + let method = request.httpMethod else { return "No request created yet." } + + let requestDescription = "\(method) \(url.absoluteString)" + + return response.map { "\(requestDescription) (\($0.statusCode))" } ?? requestDescription + } +} + +extension Request { + /// cURL representation of the instance. + /// + /// - Returns: The cURL equivalent of the instance. + public func cURLDescription() -> String { + guard + let request = lastRequest, + let url = request.url, + let host = url.host, + let method = request.httpMethod else { return "$ curl command could not be created" } + + var components = ["$ curl -v"] + + components.append("-X \(method)") + + if let credentialStorage = delegate?.sessionConfiguration.urlCredentialStorage { + let protectionSpace = URLProtectionSpace(host: host, + port: url.port ?? 0, + protocol: url.scheme, + realm: host, + authenticationMethod: NSURLAuthenticationMethodHTTPBasic) + + if let credentials = credentialStorage.credentials(for: protectionSpace)?.values { + for credential in credentials { + guard let user = credential.user, let password = credential.password else { continue } + components.append("-u \(user):\(password)") + } + } else { + if let credential, let user = credential.user, let password = credential.password { + components.append("-u \(user):\(password)") + } + } + } + + if let configuration = delegate?.sessionConfiguration, configuration.httpShouldSetCookies { + if + let cookieStorage = configuration.httpCookieStorage, + let cookies = cookieStorage.cookies(for: url), !cookies.isEmpty { + let allCookies = cookies.map { "\($0.name)=\($0.value)" }.joined(separator: ";") + + components.append("-b \"\(allCookies)\"") + } + } + + var headers = HTTPHeaders() + + if let sessionHeaders = delegate?.sessionConfiguration.headers { + for header in sessionHeaders where header.name != "Cookie" { + headers[header.name] = header.value + } + } + + for header in request.headers where header.name != "Cookie" { + headers[header.name] = header.value + } + + for header in headers { + let escapedValue = header.value.replacingOccurrences(of: "\"", with: "\\\"") + components.append("-H \"\(header.name): \(escapedValue)\"") + } + + if let httpBodyData = request.httpBody { + let httpBody = String(decoding: httpBodyData, as: UTF8.self) + var escapedBody = httpBody.replacingOccurrences(of: "\\\"", with: "\\\\\"") + escapedBody = escapedBody.replacingOccurrences(of: "\"", with: "\\\"") + + components.append("-d \"\(escapedBody)\"") + } + + components.append("\"\(url.absoluteString)\"") + + return components.joined(separator: " \\\n\t") + } +} + +/// Protocol abstraction for `Request`'s communication back to the `SessionDelegate`. +public protocol RequestDelegate: AnyObject { + /// `URLSessionConfiguration` used to create the underlying `URLSessionTask`s. + var sessionConfiguration: URLSessionConfiguration { get } + + /// Determines whether the `Request` should automatically call `resume()` when adding the first response handler. + var startImmediately: Bool { get } + + /// Notifies the delegate the `Request` has reached a point where it needs cleanup. + /// + /// - Parameter request: The `Request` to cleanup after. + func cleanup(after request: Request) + + /// Asynchronously ask the delegate whether a `Request` will be retried. + /// + /// - Parameters: + /// - request: `Request` which failed. + /// - error: `Error` which produced the failure. + /// - completion: Closure taking the `RetryResult` for evaluation. + func retryResult(for request: Request, dueTo error: AFError, completion: @escaping (RetryResult) -> Void) + + /// Asynchronously retry the `Request`. + /// + /// - Parameters: + /// - request: `Request` which will be retried. + /// - timeDelay: `TimeInterval` after which the retry will be triggered. + func retryRequest(_ request: Request, withDelay timeDelay: TimeInterval?) +} diff --git a/Pods/Alamofire/Source/Core/RequestTaskMap.swift b/Pods/Alamofire/Source/Core/RequestTaskMap.swift new file mode 100644 index 0000000..e49e564 --- /dev/null +++ b/Pods/Alamofire/Source/Core/RequestTaskMap.swift @@ -0,0 +1,150 @@ +// +// RequestTaskMap.swift +// +// Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +import Foundation + +/// A type that maintains a two way, one to one map of `URLSessionTask`s to `Request`s. +struct RequestTaskMap { + private typealias Events = (completed: Bool, metricsGathered: Bool) + + private var tasksToRequests: [URLSessionTask: Request] + private var requestsToTasks: [Request: URLSessionTask] + private var taskEvents: [URLSessionTask: Events] + + var requests: [Request] { + Array(tasksToRequests.values) + } + + init(tasksToRequests: [URLSessionTask: Request] = [:], + requestsToTasks: [Request: URLSessionTask] = [:], + taskEvents: [URLSessionTask: (completed: Bool, metricsGathered: Bool)] = [:]) { + self.tasksToRequests = tasksToRequests + self.requestsToTasks = requestsToTasks + self.taskEvents = taskEvents + } + + subscript(_ request: Request) -> URLSessionTask? { + get { requestsToTasks[request] } + set { + guard let newValue else { + guard let task = requestsToTasks[request] else { + fatalError("RequestTaskMap consistency error: no task corresponding to request found.") + } + + requestsToTasks.removeValue(forKey: request) + tasksToRequests.removeValue(forKey: task) + taskEvents.removeValue(forKey: task) + + return + } + + requestsToTasks[request] = newValue + tasksToRequests[newValue] = request + taskEvents[newValue] = (completed: false, metricsGathered: false) + } + } + + subscript(_ task: URLSessionTask) -> Request? { + get { tasksToRequests[task] } + set { + guard let newValue else { + guard let request = tasksToRequests[task] else { + fatalError("RequestTaskMap consistency error: no request corresponding to task found.") + } + + tasksToRequests.removeValue(forKey: task) + requestsToTasks.removeValue(forKey: request) + taskEvents.removeValue(forKey: task) + + return + } + + tasksToRequests[task] = newValue + requestsToTasks[newValue] = task + taskEvents[task] = (completed: false, metricsGathered: false) + } + } + + var count: Int { + precondition(tasksToRequests.count == requestsToTasks.count, + "RequestTaskMap.count invalid, requests.count: \(tasksToRequests.count) != tasks.count: \(requestsToTasks.count)") + + return tasksToRequests.count + } + + var eventCount: Int { + precondition(taskEvents.count == count, "RequestTaskMap.eventCount invalid, count: \(count) != taskEvents.count: \(taskEvents.count)") + + return taskEvents.count + } + + var isEmpty: Bool { + precondition(tasksToRequests.isEmpty == requestsToTasks.isEmpty, + "RequestTaskMap.isEmpty invalid, requests.isEmpty: \(tasksToRequests.isEmpty) != tasks.isEmpty: \(requestsToTasks.isEmpty)") + + return tasksToRequests.isEmpty + } + + var isEventsEmpty: Bool { + precondition(taskEvents.isEmpty == isEmpty, "RequestTaskMap.isEventsEmpty invalid, isEmpty: \(isEmpty) != taskEvents.isEmpty: \(taskEvents.isEmpty)") + + return taskEvents.isEmpty + } + + mutating func disassociateIfNecessaryAfterGatheringMetricsForTask(_ task: URLSessionTask) -> Bool { + guard let events = taskEvents[task] else { + fatalError("RequestTaskMap consistency error: no events corresponding to task found.") + } + + switch (events.completed, events.metricsGathered) { + case (_, true): fatalError("RequestTaskMap consistency error: duplicate metricsGatheredForTask call.") + case (false, false): taskEvents[task] = (completed: false, metricsGathered: true); return false + case (true, false): self[task] = nil; return true + } + } + + mutating func disassociateIfNecessaryAfterCompletingTask(_ task: URLSessionTask) -> Bool { + guard let events = taskEvents[task] else { + fatalError("RequestTaskMap consistency error: no events corresponding to task found.") + } + + switch (events.completed, events.metricsGathered) { + case (true, _): fatalError("RequestTaskMap consistency error: duplicate completionReceivedForTask call.") + // swift-corelibs-foundation doesn't gather metrics, so unconditionally remove the reference and return true. + #if canImport(FoundationNetworking) + default: self[task] = nil; return true + #else + case (false, false): + if #available(macOS 10.12, iOS 10, watchOS 7, tvOS 10, *) { + taskEvents[task] = (completed: true, metricsGathered: false); return false + } else { + // watchOS < 7 doesn't gather metrics, so unconditionally remove the reference and return true. + self[task] = nil; return true + } + case (false, true): + self[task] = nil; return true + #endif + } + } +} diff --git a/Pods/Alamofire/Source/Core/Response.swift b/Pods/Alamofire/Source/Core/Response.swift new file mode 100644 index 0000000..8a9f089 --- /dev/null +++ b/Pods/Alamofire/Source/Core/Response.swift @@ -0,0 +1,453 @@ +// +// Response.swift +// +// Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +import Foundation + +/// Default type of `DataResponse` returned by Alamofire, with an `AFError` `Failure` type. +public typealias AFDataResponse = DataResponse +/// Default type of `DownloadResponse` returned by Alamofire, with an `AFError` `Failure` type. +public typealias AFDownloadResponse = DownloadResponse + +/// Type used to store all values associated with a serialized response of a `DataRequest` or `UploadRequest`. +public struct DataResponse { + /// The URL request sent to the server. + public let request: URLRequest? + + /// The server's response to the URL request. + public let response: HTTPURLResponse? + + /// The data returned by the server. + public let data: Data? + + /// The final metrics of the response. + /// + /// - Note: Due to `FB7624529`, collection of `URLSessionTaskMetrics` on watchOS is currently disabled.` + /// + public let metrics: URLSessionTaskMetrics? + + /// The time taken to serialize the response. + public let serializationDuration: TimeInterval + + /// The result of response serialization. + public let result: Result + + /// Returns the associated value of the result if it is a success, `nil` otherwise. + public var value: Success? { result.success } + + /// Returns the associated error value if the result if it is a failure, `nil` otherwise. + public var error: Failure? { result.failure } + + /// Creates a `DataResponse` instance with the specified parameters derived from the response serialization. + /// + /// - Parameters: + /// - request: The `URLRequest` sent to the server. + /// - response: The `HTTPURLResponse` from the server. + /// - data: The `Data` returned by the server. + /// - metrics: The `URLSessionTaskMetrics` of the `DataRequest` or `UploadRequest`. + /// - serializationDuration: The duration taken by serialization. + /// - result: The `Result` of response serialization. + public init(request: URLRequest?, + response: HTTPURLResponse?, + data: Data?, + metrics: URLSessionTaskMetrics?, + serializationDuration: TimeInterval, + result: Result) { + self.request = request + self.response = response + self.data = data + self.metrics = metrics + self.serializationDuration = serializationDuration + self.result = result + } +} + +// MARK: - + +extension DataResponse: CustomStringConvertible, CustomDebugStringConvertible { + /// The textual representation used when written to an output stream, which includes whether the result was a + /// success or failure. + public var description: String { + "\(result)" + } + + /// The debug textual representation used when written to an output stream, which includes (if available) a summary + /// of the `URLRequest`, the request's headers and body (if decodable as a `String` below 100KB); the + /// `HTTPURLResponse`'s status code, headers, and body; the duration of the network and serialization actions; and + /// the `Result` of serialization. + public var debugDescription: String { + guard let urlRequest = request else { return "[Request]: None\n[Result]: \(result)" } + + let requestDescription = DebugDescription.description(of: urlRequest) + + let responseDescription = response.map { response in + let responseBodyDescription = DebugDescription.description(for: data, headers: response.headers) + + return """ + \(DebugDescription.description(of: response)) + \(responseBodyDescription.indentingNewlines()) + """ + } ?? "[Response]: None" + + let networkDuration = metrics.map { "\($0.taskInterval.duration)s" } ?? "None" + + return """ + \(requestDescription) + \(responseDescription) + [Network Duration]: \(networkDuration) + [Serialization Duration]: \(serializationDuration)s + [Result]: \(result) + """ + } +} + +// MARK: - + +extension DataResponse { + /// Evaluates the specified closure when the result of this `DataResponse` is a success, passing the unwrapped + /// result value as a parameter. + /// + /// Use the `map` method with a closure that does not throw. For example: + /// + /// let possibleData: DataResponse = ... + /// let possibleInt = possibleData.map { $0.count } + /// + /// - parameter transform: A closure that takes the success value of the instance's result. + /// + /// - returns: A `DataResponse` whose result wraps the value returned by the given closure. If this instance's + /// result is a failure, returns a response wrapping the same failure. + public func map(_ transform: (Success) -> NewSuccess) -> DataResponse { + DataResponse(request: request, + response: response, + data: data, + metrics: metrics, + serializationDuration: serializationDuration, + result: result.map(transform)) + } + + /// Evaluates the given closure when the result of this `DataResponse` is a success, passing the unwrapped result + /// value as a parameter. + /// + /// Use the `tryMap` method with a closure that may throw an error. For example: + /// + /// let possibleData: DataResponse = ... + /// let possibleObject = possibleData.tryMap { + /// try JSONSerialization.jsonObject(with: $0) + /// } + /// + /// - parameter transform: A closure that takes the success value of the instance's result. + /// + /// - returns: A success or failure `DataResponse` depending on the result of the given closure. If this instance's + /// result is a failure, returns the same failure. + public func tryMap(_ transform: (Success) throws -> NewSuccess) -> DataResponse { + DataResponse(request: request, + response: response, + data: data, + metrics: metrics, + serializationDuration: serializationDuration, + result: result.tryMap(transform)) + } + + /// Evaluates the specified closure when the `DataResponse` is a failure, passing the unwrapped error as a parameter. + /// + /// Use the `mapError` function with a closure that does not throw. For example: + /// + /// let possibleData: DataResponse = ... + /// let withMyError = possibleData.mapError { MyError.error($0) } + /// + /// - Parameter transform: A closure that takes the error of the instance. + /// + /// - Returns: A `DataResponse` instance containing the result of the transform. + public func mapError(_ transform: (Failure) -> NewFailure) -> DataResponse { + DataResponse(request: request, + response: response, + data: data, + metrics: metrics, + serializationDuration: serializationDuration, + result: result.mapError(transform)) + } + + /// Evaluates the specified closure when the `DataResponse` is a failure, passing the unwrapped error as a parameter. + /// + /// Use the `tryMapError` function with a closure that may throw an error. For example: + /// + /// let possibleData: DataResponse = ... + /// let possibleObject = possibleData.tryMapError { + /// try someFailableFunction(taking: $0) + /// } + /// + /// - Parameter transform: A throwing closure that takes the error of the instance. + /// + /// - Returns: A `DataResponse` instance containing the result of the transform. + public func tryMapError(_ transform: (Failure) throws -> NewFailure) -> DataResponse { + DataResponse(request: request, + response: response, + data: data, + metrics: metrics, + serializationDuration: serializationDuration, + result: result.tryMapError(transform)) + } +} + +// MARK: - + +/// Used to store all data associated with a serialized response of a download request. +public struct DownloadResponse { + /// The URL request sent to the server. + public let request: URLRequest? + + /// The server's response to the URL request. + public let response: HTTPURLResponse? + + /// The final destination URL of the data returned from the server after it is moved. + public let fileURL: URL? + + /// The resume data generated if the request was cancelled. + public let resumeData: Data? + + /// The final metrics of the response. + /// + /// - Note: Due to `FB7624529`, collection of `URLSessionTaskMetrics` on watchOS is currently disabled.` + /// + public let metrics: URLSessionTaskMetrics? + + /// The time taken to serialize the response. + public let serializationDuration: TimeInterval + + /// The result of response serialization. + public let result: Result + + /// Returns the associated value of the result if it is a success, `nil` otherwise. + public var value: Success? { result.success } + + /// Returns the associated error value if the result if it is a failure, `nil` otherwise. + public var error: Failure? { result.failure } + + /// Creates a `DownloadResponse` instance with the specified parameters derived from response serialization. + /// + /// - Parameters: + /// - request: The `URLRequest` sent to the server. + /// - response: The `HTTPURLResponse` from the server. + /// - fileURL: The final destination URL of the data returned from the server after it is moved. + /// - resumeData: The resume `Data` generated if the request was cancelled. + /// - metrics: The `URLSessionTaskMetrics` of the `DownloadRequest`. + /// - serializationDuration: The duration taken by serialization. + /// - result: The `Result` of response serialization. + public init(request: URLRequest?, + response: HTTPURLResponse?, + fileURL: URL?, + resumeData: Data?, + metrics: URLSessionTaskMetrics?, + serializationDuration: TimeInterval, + result: Result) { + self.request = request + self.response = response + self.fileURL = fileURL + self.resumeData = resumeData + self.metrics = metrics + self.serializationDuration = serializationDuration + self.result = result + } +} + +// MARK: - + +extension DownloadResponse: CustomStringConvertible, CustomDebugStringConvertible { + /// The textual representation used when written to an output stream, which includes whether the result was a + /// success or failure. + public var description: String { + "\(result)" + } + + /// The debug textual representation used when written to an output stream, which includes the URL request, the URL + /// response, the temporary and destination URLs, the resume data, the durations of the network and serialization + /// actions, and the response serialization result. + public var debugDescription: String { + guard let urlRequest = request else { return "[Request]: None\n[Result]: \(result)" } + + let requestDescription = DebugDescription.description(of: urlRequest) + let responseDescription = response.map(DebugDescription.description(of:)) ?? "[Response]: None" + let networkDuration = metrics.map { "\($0.taskInterval.duration)s" } ?? "None" + let resumeDataDescription = resumeData.map { "\($0)" } ?? "None" + + return """ + \(requestDescription) + \(responseDescription) + [File URL]: \(fileURL?.path ?? "None") + [Resume Data]: \(resumeDataDescription) + [Network Duration]: \(networkDuration) + [Serialization Duration]: \(serializationDuration)s + [Result]: \(result) + """ + } +} + +// MARK: - + +extension DownloadResponse { + /// Evaluates the given closure when the result of this `DownloadResponse` is a success, passing the unwrapped + /// result value as a parameter. + /// + /// Use the `map` method with a closure that does not throw. For example: + /// + /// let possibleData: DownloadResponse = ... + /// let possibleInt = possibleData.map { $0.count } + /// + /// - parameter transform: A closure that takes the success value of the instance's result. + /// + /// - returns: A `DownloadResponse` whose result wraps the value returned by the given closure. If this instance's + /// result is a failure, returns a response wrapping the same failure. + public func map(_ transform: (Success) -> NewSuccess) -> DownloadResponse { + DownloadResponse(request: request, + response: response, + fileURL: fileURL, + resumeData: resumeData, + metrics: metrics, + serializationDuration: serializationDuration, + result: result.map(transform)) + } + + /// Evaluates the given closure when the result of this `DownloadResponse` is a success, passing the unwrapped + /// result value as a parameter. + /// + /// Use the `tryMap` method with a closure that may throw an error. For example: + /// + /// let possibleData: DownloadResponse = ... + /// let possibleObject = possibleData.tryMap { + /// try JSONSerialization.jsonObject(with: $0) + /// } + /// + /// - parameter transform: A closure that takes the success value of the instance's result. + /// + /// - returns: A success or failure `DownloadResponse` depending on the result of the given closure. If this + /// instance's result is a failure, returns the same failure. + public func tryMap(_ transform: (Success) throws -> NewSuccess) -> DownloadResponse { + DownloadResponse(request: request, + response: response, + fileURL: fileURL, + resumeData: resumeData, + metrics: metrics, + serializationDuration: serializationDuration, + result: result.tryMap(transform)) + } + + /// Evaluates the specified closure when the `DownloadResponse` is a failure, passing the unwrapped error as a parameter. + /// + /// Use the `mapError` function with a closure that does not throw. For example: + /// + /// let possibleData: DownloadResponse = ... + /// let withMyError = possibleData.mapError { MyError.error($0) } + /// + /// - Parameter transform: A closure that takes the error of the instance. + /// + /// - Returns: A `DownloadResponse` instance containing the result of the transform. + public func mapError(_ transform: (Failure) -> NewFailure) -> DownloadResponse { + DownloadResponse(request: request, + response: response, + fileURL: fileURL, + resumeData: resumeData, + metrics: metrics, + serializationDuration: serializationDuration, + result: result.mapError(transform)) + } + + /// Evaluates the specified closure when the `DownloadResponse` is a failure, passing the unwrapped error as a parameter. + /// + /// Use the `tryMapError` function with a closure that may throw an error. For example: + /// + /// let possibleData: DownloadResponse = ... + /// let possibleObject = possibleData.tryMapError { + /// try someFailableFunction(taking: $0) + /// } + /// + /// - Parameter transform: A throwing closure that takes the error of the instance. + /// + /// - Returns: A `DownloadResponse` instance containing the result of the transform. + public func tryMapError(_ transform: (Failure) throws -> NewFailure) -> DownloadResponse { + DownloadResponse(request: request, + response: response, + fileURL: fileURL, + resumeData: resumeData, + metrics: metrics, + serializationDuration: serializationDuration, + result: result.tryMapError(transform)) + } +} + +private enum DebugDescription { + static func description(of request: URLRequest) -> String { + let requestSummary = "\(request.httpMethod!) \(request)" + let requestHeadersDescription = DebugDescription.description(for: request.headers) + let requestBodyDescription = DebugDescription.description(for: request.httpBody, headers: request.headers) + + return """ + [Request]: \(requestSummary) + \(requestHeadersDescription.indentingNewlines()) + \(requestBodyDescription.indentingNewlines()) + """ + } + + static func description(of response: HTTPURLResponse) -> String { + """ + [Response]: + [Status Code]: \(response.statusCode) + \(DebugDescription.description(for: response.headers).indentingNewlines()) + """ + } + + static func description(for headers: HTTPHeaders) -> String { + guard !headers.isEmpty else { return "[Headers]: None" } + + let headerDescription = "\(headers.sorted())".indentingNewlines() + return """ + [Headers]: + \(headerDescription) + """ + } + + static func description(for data: Data?, + headers: HTTPHeaders, + allowingPrintableTypes printableTypes: [String] = ["json", "xml", "text"], + maximumLength: Int = 100_000) -> String { + guard let data, !data.isEmpty else { return "[Body]: None" } + + guard + data.count <= maximumLength, + printableTypes.compactMap({ headers["Content-Type"]?.contains($0) }).contains(true) + else { return "[Body]: \(data.count) bytes" } + + return """ + [Body]: + \(String(decoding: data, as: UTF8.self) + .trimmingCharacters(in: .whitespacesAndNewlines) + .indentingNewlines()) + """ + } +} + +extension String { + fileprivate func indentingNewlines(by spaceCount: Int = 4) -> String { + let spaces = String(repeating: " ", count: spaceCount) + return replacingOccurrences(of: "\n", with: "\n\(spaces)") + } +} diff --git a/Pods/Alamofire/Source/Core/Session.swift b/Pods/Alamofire/Source/Core/Session.swift new file mode 100644 index 0000000..0518dca --- /dev/null +++ b/Pods/Alamofire/Source/Core/Session.swift @@ -0,0 +1,1350 @@ +// +// Session.swift +// +// Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +import Foundation + +/// `Session` creates and manages Alamofire's `Request` types during their lifetimes. It also provides common +/// functionality for all `Request`s, including queuing, interception, trust management, redirect handling, and response +/// cache handling. +open class Session { + /// Shared singleton instance used by all `AF.request` APIs. Cannot be modified. + public static let `default` = Session() + + /// Underlying `URLSession` used to create `URLSessionTasks` for this instance, and for which this instance's + /// `delegate` handles `URLSessionDelegate` callbacks. + /// + /// - Note: This instance should **NOT** be used to interact with the underlying `URLSessionTask`s. Doing so will + /// break internal Alamofire logic that tracks those tasks. + /// + public let session: URLSession + /// Instance's `SessionDelegate`, which handles the `URLSessionDelegate` methods and `Request` interaction. + public let delegate: SessionDelegate + /// Root `DispatchQueue` for all internal callbacks and state update. **MUST** be a serial queue. + public let rootQueue: DispatchQueue + /// Value determining whether this instance automatically calls `resume()` on all created `Request`s. + public let startRequestsImmediately: Bool + /// `DispatchQueue` on which `URLRequest`s are created asynchronously. By default this queue uses `rootQueue` as its + /// `target`, but a separate queue can be used if request creation is determined to be a bottleneck. Always profile + /// and test before introducing an additional queue. + public let requestQueue: DispatchQueue + /// `DispatchQueue` passed to all `Request`s on which they perform their response serialization. By default this + /// queue uses `rootQueue` as its `target` but a separate queue can be used if response serialization is determined + /// to be a bottleneck. Always profile and test before introducing an additional queue. + public let serializationQueue: DispatchQueue + /// `RequestInterceptor` used for all `Request` created by the instance. `RequestInterceptor`s can also be set on a + /// per-`Request` basis, in which case the `Request`'s interceptor takes precedence over this value. + public let interceptor: RequestInterceptor? + /// `ServerTrustManager` instance used to evaluate all trust challenges and provide certificate and key pinning. + public let serverTrustManager: ServerTrustManager? + /// `RedirectHandler` instance used to provide customization for request redirection. + public let redirectHandler: RedirectHandler? + /// `CachedResponseHandler` instance used to provide customization of cached response handling. + public let cachedResponseHandler: CachedResponseHandler? + /// `CompositeEventMonitor` used to compose Alamofire's `defaultEventMonitors` and any passed `EventMonitor`s. + public let eventMonitor: CompositeEventMonitor + /// `EventMonitor`s included in all instances. `[AlamofireNotifications()]` by default. + public let defaultEventMonitors: [EventMonitor] = [AlamofireNotifications()] + + /// Internal map between `Request`s and any `URLSessionTasks` that may be in flight for them. + var requestTaskMap = RequestTaskMap() + /// `Set` of currently active `Request`s. + var activeRequests: Set = [] + /// Completion events awaiting `URLSessionTaskMetrics`. + var waitingCompletions: [URLSessionTask: () -> Void] = [:] + + /// Creates a `Session` from a `URLSession` and other parameters. + /// + /// - Note: When passing a `URLSession`, you must create the `URLSession` with a specific `delegateQueue` value and + /// pass the `delegateQueue`'s `underlyingQueue` as the `rootQueue` parameter of this initializer. + /// + /// - Parameters: + /// - session: Underlying `URLSession` for this instance. + /// - delegate: `SessionDelegate` that handles `session`'s delegate callbacks as well as `Request` + /// interaction. + /// - rootQueue: Root `DispatchQueue` for all internal callbacks and state updates. **MUST** be a + /// serial queue. + /// - startRequestsImmediately: Determines whether this instance will automatically start all `Request`s. `true` + /// by default. If set to `false`, all `Request`s created must have `.resume()` called. + /// on them for them to start. + /// - requestQueue: `DispatchQueue` on which to perform `URLRequest` creation. By default this queue + /// will use the `rootQueue` as its `target`. A separate queue can be used if it's + /// determined request creation is a bottleneck, but that should only be done after + /// careful testing and profiling. `nil` by default. + /// - serializationQueue: `DispatchQueue` on which to perform all response serialization. By default this + /// queue will use the `rootQueue` as its `target`. A separate queue can be used if + /// it's determined response serialization is a bottleneck, but that should only be + /// done after careful testing and profiling. `nil` by default. + /// - interceptor: `RequestInterceptor` to be used for all `Request`s created by this instance. `nil` + /// by default. + /// - serverTrustManager: `ServerTrustManager` to be used for all trust evaluations by this instance. `nil` + /// by default. + /// - redirectHandler: `RedirectHandler` to be used by all `Request`s created by this instance. `nil` by + /// default. + /// - cachedResponseHandler: `CachedResponseHandler` to be used by all `Request`s created by this instance. + /// `nil` by default. + /// - eventMonitors: Additional `EventMonitor`s used by the instance. Alamofire always adds a + /// `AlamofireNotifications` `EventMonitor` to the array passed here. `[]` by default. + public init(session: URLSession, + delegate: SessionDelegate, + rootQueue: DispatchQueue, + startRequestsImmediately: Bool = true, + requestQueue: DispatchQueue? = nil, + serializationQueue: DispatchQueue? = nil, + interceptor: RequestInterceptor? = nil, + serverTrustManager: ServerTrustManager? = nil, + redirectHandler: RedirectHandler? = nil, + cachedResponseHandler: CachedResponseHandler? = nil, + eventMonitors: [EventMonitor] = []) { + precondition(session.configuration.identifier == nil, + "Alamofire does not support background URLSessionConfigurations.") + precondition(session.delegateQueue.underlyingQueue === rootQueue, + "Session(session:) initializer must be passed the DispatchQueue used as the delegateQueue's underlyingQueue as rootQueue.") + + self.session = session + self.delegate = delegate + self.rootQueue = rootQueue + self.startRequestsImmediately = startRequestsImmediately + self.requestQueue = requestQueue ?? DispatchQueue(label: "\(rootQueue.label).requestQueue", target: rootQueue) + self.serializationQueue = serializationQueue ?? DispatchQueue(label: "\(rootQueue.label).serializationQueue", target: rootQueue) + self.interceptor = interceptor + self.serverTrustManager = serverTrustManager + self.redirectHandler = redirectHandler + self.cachedResponseHandler = cachedResponseHandler + eventMonitor = CompositeEventMonitor(monitors: defaultEventMonitors + eventMonitors) + delegate.eventMonitor = eventMonitor + delegate.stateProvider = self + } + + /// Creates a `Session` from a `URLSessionConfiguration`. + /// + /// - Note: This initializer lets Alamofire handle the creation of the underlying `URLSession` and its + /// `delegateQueue`, and is the recommended initializer for most uses. + /// + /// - Parameters: + /// - configuration: `URLSessionConfiguration` to be used to create the underlying `URLSession`. Changes + /// to this value after being passed to this initializer will have no effect. + /// `URLSessionConfiguration.af.default` by default. + /// - delegate: `SessionDelegate` that handles `session`'s delegate callbacks as well as `Request` + /// interaction. `SessionDelegate()` by default. + /// - rootQueue: Root `DispatchQueue` for all internal callbacks and state updates. **MUST** be a + /// serial queue. `DispatchQueue(label: "org.alamofire.session.rootQueue")` by default. + /// - startRequestsImmediately: Determines whether this instance will automatically start all `Request`s. `true` + /// by default. If set to `false`, all `Request`s created must have `.resume()` called. + /// on them for them to start. + /// - requestQueue: `DispatchQueue` on which to perform `URLRequest` creation. By default this queue + /// will use the `rootQueue` as its `target`. A separate queue can be used if it's + /// determined request creation is a bottleneck, but that should only be done after + /// careful testing and profiling. `nil` by default. + /// - serializationQueue: `DispatchQueue` on which to perform all response serialization. By default this + /// queue will use the `rootQueue` as its `target`. A separate queue can be used if + /// it's determined response serialization is a bottleneck, but that should only be + /// done after careful testing and profiling. `nil` by default. + /// - interceptor: `RequestInterceptor` to be used for all `Request`s created by this instance. `nil` + /// by default. + /// - serverTrustManager: `ServerTrustManager` to be used for all trust evaluations by this instance. `nil` + /// by default. + /// - redirectHandler: `RedirectHandler` to be used by all `Request`s created by this instance. `nil` by + /// default. + /// - cachedResponseHandler: `CachedResponseHandler` to be used by all `Request`s created by this instance. + /// `nil` by default. + /// - eventMonitors: Additional `EventMonitor`s used by the instance. Alamofire always adds a + /// `AlamofireNotifications` `EventMonitor` to the array passed here. `[]` by default. + public convenience init(configuration: URLSessionConfiguration = URLSessionConfiguration.af.default, + delegate: SessionDelegate = SessionDelegate(), + rootQueue: DispatchQueue = DispatchQueue(label: "org.alamofire.session.rootQueue"), + startRequestsImmediately: Bool = true, + requestQueue: DispatchQueue? = nil, + serializationQueue: DispatchQueue? = nil, + interceptor: RequestInterceptor? = nil, + serverTrustManager: ServerTrustManager? = nil, + redirectHandler: RedirectHandler? = nil, + cachedResponseHandler: CachedResponseHandler? = nil, + eventMonitors: [EventMonitor] = []) { + precondition(configuration.identifier == nil, "Alamofire does not support background URLSessionConfigurations.") + + // Retarget the incoming rootQueue for safety, unless it's the main queue, which we know is safe. + let serialRootQueue = (rootQueue === DispatchQueue.main) ? rootQueue : DispatchQueue(label: rootQueue.label, + target: rootQueue) + let delegateQueue = OperationQueue(maxConcurrentOperationCount: 1, underlyingQueue: serialRootQueue, name: "\(serialRootQueue.label).sessionDelegate") + let session = URLSession(configuration: configuration, delegate: delegate, delegateQueue: delegateQueue) + + self.init(session: session, + delegate: delegate, + rootQueue: serialRootQueue, + startRequestsImmediately: startRequestsImmediately, + requestQueue: requestQueue, + serializationQueue: serializationQueue, + interceptor: interceptor, + serverTrustManager: serverTrustManager, + redirectHandler: redirectHandler, + cachedResponseHandler: cachedResponseHandler, + eventMonitors: eventMonitors) + } + + deinit { + finishRequestsForDeinit() + session.invalidateAndCancel() + } + + // MARK: - All Requests API + + /// Perform an action on all active `Request`s. + /// + /// - Note: The provided `action` closure is performed asynchronously, meaning that some `Request`s may complete and + /// be unavailable by time it runs. Additionally, this action is performed on the instances's `rootQueue`, + /// so care should be taken that actions are fast. Once the work on the `Request`s is complete, any + /// additional work should be performed on another queue. + /// + /// - Parameters: + /// - action: Closure to perform with all `Request`s. + public func withAllRequests(perform action: @escaping (Set) -> Void) { + rootQueue.async { + action(self.activeRequests) + } + } + + /// Cancel all active `Request`s, optionally calling a completion handler when complete. + /// + /// - Note: This is an asynchronous operation and does not block the creation of future `Request`s. Cancelled + /// `Request`s may not cancel immediately due internal work, and may not cancel at all if they are close to + /// completion when cancelled. + /// + /// - Parameters: + /// - queue: `DispatchQueue` on which the completion handler is run. `.main` by default. + /// - completion: Closure to be called when all `Request`s have been cancelled. + public func cancelAllRequests(completingOnQueue queue: DispatchQueue = .main, completion: (() -> Void)? = nil) { + withAllRequests { requests in + requests.forEach { $0.cancel() } + queue.async { + completion?() + } + } + } + + // MARK: - DataRequest + + /// Closure which provides a `URLRequest` for mutation. + public typealias RequestModifier = (inout URLRequest) throws -> Void + + struct RequestConvertible: URLRequestConvertible { + let url: URLConvertible + let method: HTTPMethod + let parameters: Parameters? + let encoding: ParameterEncoding + let headers: HTTPHeaders? + let requestModifier: RequestModifier? + + func asURLRequest() throws -> URLRequest { + var request = try URLRequest(url: url, method: method, headers: headers) + try requestModifier?(&request) + + return try encoding.encode(request, with: parameters) + } + } + + /// Creates a `DataRequest` from a `URLRequest` created using the passed components and a `RequestInterceptor`. + /// + /// - Parameters: + /// - convertible: `URLConvertible` value to be used as the `URLRequest`'s `URL`. + /// - method: `HTTPMethod` for the `URLRequest`. `.get` by default. + /// - parameters: `Parameters` (a.k.a. `[String: Any]`) value to be encoded into the `URLRequest`. `nil` by + /// default. + /// - encoding: `ParameterEncoding` to be used to encode the `parameters` value into the `URLRequest`. + /// `URLEncoding.default` by default. + /// - headers: `HTTPHeaders` value to be added to the `URLRequest`. `nil` by default. + /// - interceptor: `RequestInterceptor` value to be used by the returned `DataRequest`. `nil` by default. + /// - requestModifier: `RequestModifier` which will be applied to the `URLRequest` created from the provided + /// parameters. `nil` by default. + /// + /// - Returns: The created `DataRequest`. + open func request(_ convertible: URLConvertible, + method: HTTPMethod = .get, + parameters: Parameters? = nil, + encoding: ParameterEncoding = URLEncoding.default, + headers: HTTPHeaders? = nil, + interceptor: RequestInterceptor? = nil, + requestModifier: RequestModifier? = nil) -> DataRequest { + let convertible = RequestConvertible(url: convertible, + method: method, + parameters: parameters, + encoding: encoding, + headers: headers, + requestModifier: requestModifier) + + return request(convertible, interceptor: interceptor) + } + + struct RequestEncodableConvertible: URLRequestConvertible { + let url: URLConvertible + let method: HTTPMethod + let parameters: Parameters? + let encoder: ParameterEncoder + let headers: HTTPHeaders? + let requestModifier: RequestModifier? + + func asURLRequest() throws -> URLRequest { + var request = try URLRequest(url: url, method: method, headers: headers) + try requestModifier?(&request) + + return try parameters.map { try encoder.encode($0, into: request) } ?? request + } + } + + /// Creates a `DataRequest` from a `URLRequest` created using the passed components, `Encodable` parameters, and a + /// `RequestInterceptor`. + /// + /// - Parameters: + /// - convertible: `URLConvertible` value to be used as the `URLRequest`'s `URL`. + /// - method: `HTTPMethod` for the `URLRequest`. `.get` by default. + /// - parameters: `Encodable` value to be encoded into the `URLRequest`. `nil` by default. + /// - encoder: `ParameterEncoder` to be used to encode the `parameters` value into the `URLRequest`. + /// `URLEncodedFormParameterEncoder.default` by default. + /// - headers: `HTTPHeaders` value to be added to the `URLRequest`. `nil` by default. + /// - interceptor: `RequestInterceptor` value to be used by the returned `DataRequest`. `nil` by default. + /// - requestModifier: `RequestModifier` which will be applied to the `URLRequest` created from + /// the provided parameters. `nil` by default. + /// + /// - Returns: The created `DataRequest`. + open func request(_ convertible: URLConvertible, + method: HTTPMethod = .get, + parameters: Parameters? = nil, + encoder: ParameterEncoder = URLEncodedFormParameterEncoder.default, + headers: HTTPHeaders? = nil, + interceptor: RequestInterceptor? = nil, + requestModifier: RequestModifier? = nil) -> DataRequest { + let convertible = RequestEncodableConvertible(url: convertible, + method: method, + parameters: parameters, + encoder: encoder, + headers: headers, + requestModifier: requestModifier) + + return request(convertible, interceptor: interceptor) + } + + /// Creates a `DataRequest` from a `URLRequestConvertible` value and a `RequestInterceptor`. + /// + /// - Parameters: + /// - convertible: `URLRequestConvertible` value to be used to create the `URLRequest`. + /// - interceptor: `RequestInterceptor` value to be used by the returned `DataRequest`. `nil` by default. + /// + /// - Returns: The created `DataRequest`. + open func request(_ convertible: URLRequestConvertible, interceptor: RequestInterceptor? = nil) -> DataRequest { + let request = DataRequest(convertible: convertible, + underlyingQueue: rootQueue, + serializationQueue: serializationQueue, + eventMonitor: eventMonitor, + interceptor: interceptor, + delegate: self) + + perform(request) + + return request + } + + // MARK: - DataStreamRequest + + /// Creates a `DataStreamRequest` from the passed components, `Encodable` parameters, and `RequestInterceptor`. + /// + /// - Parameters: + /// - convertible: `URLConvertible` value to be used as the `URLRequest`'s `URL`. + /// - method: `HTTPMethod` for the `URLRequest`. `.get` by default. + /// - parameters: `Encodable` value to be encoded into the `URLRequest`. `nil` by default. + /// - encoder: `ParameterEncoder` to be used to encode the `parameters` value into the + /// `URLRequest`. + /// `URLEncodedFormParameterEncoder.default` by default. + /// - headers: `HTTPHeaders` value to be added to the `URLRequest`. `nil` by default. + /// - automaticallyCancelOnStreamError: `Bool` indicating whether the instance should be canceled when an `Error` + /// is thrown while serializing stream `Data`. `false` by default. + /// - interceptor: `RequestInterceptor` value to be used by the returned `DataRequest`. `nil` + /// by default. + /// - requestModifier: `RequestModifier` which will be applied to the `URLRequest` created from + /// the provided parameters. `nil` by default. + /// + /// - Returns: The created `DataStream` request. + open func streamRequest(_ convertible: URLConvertible, + method: HTTPMethod = .get, + parameters: Parameters? = nil, + encoder: ParameterEncoder = URLEncodedFormParameterEncoder.default, + headers: HTTPHeaders? = nil, + automaticallyCancelOnStreamError: Bool = false, + interceptor: RequestInterceptor? = nil, + requestModifier: RequestModifier? = nil) -> DataStreamRequest { + let convertible = RequestEncodableConvertible(url: convertible, + method: method, + parameters: parameters, + encoder: encoder, + headers: headers, + requestModifier: requestModifier) + + return streamRequest(convertible, + automaticallyCancelOnStreamError: automaticallyCancelOnStreamError, + interceptor: interceptor) + } + + /// Creates a `DataStreamRequest` from the passed components and `RequestInterceptor`. + /// + /// - Parameters: + /// - convertible: `URLConvertible` value to be used as the `URLRequest`'s `URL`. + /// - method: `HTTPMethod` for the `URLRequest`. `.get` by default. + /// - headers: `HTTPHeaders` value to be added to the `URLRequest`. `nil` by default. + /// - automaticallyCancelOnStreamError: `Bool` indicating whether the instance should be canceled when an `Error` + /// is thrown while serializing stream `Data`. `false` by default. + /// - interceptor: `RequestInterceptor` value to be used by the returned `DataRequest`. `nil` + /// by default. + /// - requestModifier: `RequestModifier` which will be applied to the `URLRequest` created from + /// the provided parameters. `nil` by default. + /// + /// - Returns: The created `DataStream` request. + open func streamRequest(_ convertible: URLConvertible, + method: HTTPMethod = .get, + headers: HTTPHeaders? = nil, + automaticallyCancelOnStreamError: Bool = false, + interceptor: RequestInterceptor? = nil, + requestModifier: RequestModifier? = nil) -> DataStreamRequest { + let convertible = RequestEncodableConvertible(url: convertible, + method: method, + parameters: Empty?.none, + encoder: URLEncodedFormParameterEncoder.default, + headers: headers, + requestModifier: requestModifier) + + return streamRequest(convertible, + automaticallyCancelOnStreamError: automaticallyCancelOnStreamError, + interceptor: interceptor) + } + + /// Creates a `DataStreamRequest` from the passed `URLRequestConvertible` value and `RequestInterceptor`. + /// + /// - Parameters: + /// - convertible: `URLRequestConvertible` value to be used to create the `URLRequest`. + /// - automaticallyCancelOnStreamError: `Bool` indicating whether the instance should be canceled when an `Error` + /// is thrown while serializing stream `Data`. `false` by default. + /// - interceptor: `RequestInterceptor` value to be used by the returned `DataRequest`. `nil` + /// by default. + /// + /// - Returns: The created `DataStreamRequest`. + open func streamRequest(_ convertible: URLRequestConvertible, + automaticallyCancelOnStreamError: Bool = false, + interceptor: RequestInterceptor? = nil) -> DataStreamRequest { + let request = DataStreamRequest(convertible: convertible, + automaticallyCancelOnStreamError: automaticallyCancelOnStreamError, + underlyingQueue: rootQueue, + serializationQueue: serializationQueue, + eventMonitor: eventMonitor, + interceptor: interceptor, + delegate: self) + + perform(request) + + return request + } + + #if canImport(Darwin) && !canImport(FoundationNetworking) // Only Apple platforms support URLSessionWebSocketTask. + @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) + @_spi(WebSocket) open func webSocketRequest( + to url: URLConvertible, + configuration: WebSocketRequest.Configuration = .default, + headers: HTTPHeaders? = nil, + interceptor: RequestInterceptor? = nil, + requestModifier: RequestModifier? = nil + ) -> WebSocketRequest { + webSocketRequest( + to: url, + configuration: configuration, + parameters: Empty?.none, + encoder: URLEncodedFormParameterEncoder.default, + headers: headers, + interceptor: interceptor, + requestModifier: requestModifier + ) + } + + @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) + @_spi(WebSocket) open func webSocketRequest( + to url: URLConvertible, + configuration: WebSocketRequest.Configuration = .default, + parameters: Parameters? = nil, + encoder: ParameterEncoder = URLEncodedFormParameterEncoder.default, + headers: HTTPHeaders? = nil, + interceptor: RequestInterceptor? = nil, + requestModifier: RequestModifier? = nil + ) -> WebSocketRequest where Parameters: Encodable { + let convertible = RequestEncodableConvertible(url: url, + method: .get, + parameters: parameters, + encoder: encoder, + headers: headers, + requestModifier: requestModifier) + let request = WebSocketRequest(convertible: convertible, + configuration: configuration, + underlyingQueue: rootQueue, + serializationQueue: serializationQueue, + eventMonitor: eventMonitor, + interceptor: interceptor, + delegate: self) + + perform(request) + + return request + } + + @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) + @_spi(WebSocket) open func webSocketRequest(performing convertible: URLRequestConvertible, + configuration: WebSocketRequest.Configuration = .default, + interceptor: RequestInterceptor? = nil) -> WebSocketRequest { + let request = WebSocketRequest(convertible: convertible, + configuration: configuration, + underlyingQueue: rootQueue, + serializationQueue: serializationQueue, + eventMonitor: eventMonitor, + interceptor: interceptor, + delegate: self) + + perform(request) + + return request + } + #endif + + // MARK: - DownloadRequest + + /// Creates a `DownloadRequest` using a `URLRequest` created using the passed components, `RequestInterceptor`, and + /// `Destination`. + /// + /// - Parameters: + /// - convertible: `URLConvertible` value to be used as the `URLRequest`'s `URL`. + /// - method: `HTTPMethod` for the `URLRequest`. `.get` by default. + /// - parameters: `Parameters` (a.k.a. `[String: Any]`) value to be encoded into the `URLRequest`. `nil` by + /// default. + /// - encoding: `ParameterEncoding` to be used to encode the `parameters` value into the `URLRequest`. + /// Defaults to `URLEncoding.default`. + /// - headers: `HTTPHeaders` value to be added to the `URLRequest`. `nil` by default. + /// - interceptor: `RequestInterceptor` value to be used by the returned `DataRequest`. `nil` by default. + /// - requestModifier: `RequestModifier` which will be applied to the `URLRequest` created from the provided + /// parameters. `nil` by default. + /// - destination: `DownloadRequest.Destination` closure used to determine how and where the downloaded file + /// should be moved. `nil` by default. + /// + /// - Returns: The created `DownloadRequest`. + open func download(_ convertible: URLConvertible, + method: HTTPMethod = .get, + parameters: Parameters? = nil, + encoding: ParameterEncoding = URLEncoding.default, + headers: HTTPHeaders? = nil, + interceptor: RequestInterceptor? = nil, + requestModifier: RequestModifier? = nil, + to destination: DownloadRequest.Destination? = nil) -> DownloadRequest { + let convertible = RequestConvertible(url: convertible, + method: method, + parameters: parameters, + encoding: encoding, + headers: headers, + requestModifier: requestModifier) + + return download(convertible, interceptor: interceptor, to: destination) + } + + /// Creates a `DownloadRequest` from a `URLRequest` created using the passed components, `Encodable` parameters, and + /// a `RequestInterceptor`. + /// + /// - Parameters: + /// - convertible: `URLConvertible` value to be used as the `URLRequest`'s `URL`. + /// - method: `HTTPMethod` for the `URLRequest`. `.get` by default. + /// - parameters: Value conforming to `Encodable` to be encoded into the `URLRequest`. `nil` by default. + /// - encoder: `ParameterEncoder` to be used to encode the `parameters` value into the `URLRequest`. + /// Defaults to `URLEncodedFormParameterEncoder.default`. + /// - headers: `HTTPHeaders` value to be added to the `URLRequest`. `nil` by default. + /// - interceptor: `RequestInterceptor` value to be used by the returned `DataRequest`. `nil` by default. + /// - requestModifier: `RequestModifier` which will be applied to the `URLRequest` created from the provided + /// parameters. `nil` by default. + /// - destination: `DownloadRequest.Destination` closure used to determine how and where the downloaded file + /// should be moved. `nil` by default. + /// + /// - Returns: The created `DownloadRequest`. + open func download(_ convertible: URLConvertible, + method: HTTPMethod = .get, + parameters: Parameters? = nil, + encoder: ParameterEncoder = URLEncodedFormParameterEncoder.default, + headers: HTTPHeaders? = nil, + interceptor: RequestInterceptor? = nil, + requestModifier: RequestModifier? = nil, + to destination: DownloadRequest.Destination? = nil) -> DownloadRequest { + let convertible = RequestEncodableConvertible(url: convertible, + method: method, + parameters: parameters, + encoder: encoder, + headers: headers, + requestModifier: requestModifier) + + return download(convertible, interceptor: interceptor, to: destination) + } + + /// Creates a `DownloadRequest` from a `URLRequestConvertible` value, a `RequestInterceptor`, and a `Destination`. + /// + /// - Parameters: + /// - convertible: `URLRequestConvertible` value to be used to create the `URLRequest`. + /// - interceptor: `RequestInterceptor` value to be used by the returned `DataRequest`. `nil` by default. + /// - destination: `DownloadRequest.Destination` closure used to determine how and where the downloaded file + /// should be moved. `nil` by default. + /// + /// - Returns: The created `DownloadRequest`. + open func download(_ convertible: URLRequestConvertible, + interceptor: RequestInterceptor? = nil, + to destination: DownloadRequest.Destination? = nil) -> DownloadRequest { + let request = DownloadRequest(downloadable: .request(convertible), + underlyingQueue: rootQueue, + serializationQueue: serializationQueue, + eventMonitor: eventMonitor, + interceptor: interceptor, + delegate: self, + destination: destination ?? DownloadRequest.defaultDestination) + + perform(request) + + return request + } + + /// Creates a `DownloadRequest` from the `resumeData` produced from a previously cancelled `DownloadRequest`, as + /// well as a `RequestInterceptor`, and a `Destination`. + /// + /// - Note: If `destination` is not specified, the download will be moved to a temporary location determined by + /// Alamofire. The file will not be deleted until the system purges the temporary files. + /// + /// - Note: On some versions of all Apple platforms (iOS 10 - 10.2, macOS 10.12 - 10.12.2, tvOS 10 - 10.1, watchOS 3 - 3.1.1), + /// `resumeData` is broken on background URL session configurations. There's an underlying bug in the `resumeData` + /// generation logic where the data is written incorrectly and will always fail to resume the download. For more + /// information about the bug and possible workarounds, please refer to the [this Stack Overflow post](http://stackoverflow.com/a/39347461/1342462). + /// + /// - Parameters: + /// - data: The resume data from a previously cancelled `DownloadRequest` or `URLSessionDownloadTask`. + /// - interceptor: `RequestInterceptor` value to be used by the returned `DataRequest`. `nil` by default. + /// - destination: `DownloadRequest.Destination` closure used to determine how and where the downloaded file + /// should be moved. `nil` by default. + /// + /// - Returns: The created `DownloadRequest`. + open func download(resumingWith data: Data, + interceptor: RequestInterceptor? = nil, + to destination: DownloadRequest.Destination? = nil) -> DownloadRequest { + let request = DownloadRequest(downloadable: .resumeData(data), + underlyingQueue: rootQueue, + serializationQueue: serializationQueue, + eventMonitor: eventMonitor, + interceptor: interceptor, + delegate: self, + destination: destination ?? DownloadRequest.defaultDestination) + + perform(request) + + return request + } + + // MARK: - UploadRequest + + struct ParameterlessRequestConvertible: URLRequestConvertible { + let url: URLConvertible + let method: HTTPMethod + let headers: HTTPHeaders? + let requestModifier: RequestModifier? + + func asURLRequest() throws -> URLRequest { + var request = try URLRequest(url: url, method: method, headers: headers) + try requestModifier?(&request) + + return request + } + } + + struct Upload: UploadConvertible { + let request: URLRequestConvertible + let uploadable: UploadableConvertible + + func createUploadable() throws -> UploadRequest.Uploadable { + try uploadable.createUploadable() + } + + func asURLRequest() throws -> URLRequest { + try request.asURLRequest() + } + } + + // MARK: Data + + /// Creates an `UploadRequest` for the given `Data`, `URLRequest` components, and `RequestInterceptor`. + /// + /// - Parameters: + /// - data: The `Data` to upload. + /// - convertible: `URLConvertible` value to be used as the `URLRequest`'s `URL`. + /// - method: `HTTPMethod` for the `URLRequest`. `.post` by default. + /// - headers: `HTTPHeaders` value to be added to the `URLRequest`. `nil` by default. + /// - interceptor: `RequestInterceptor` value to be used by the returned `DataRequest`. `nil` by default. + /// - fileManager: `FileManager` instance to be used by the returned `UploadRequest`. `.default` instance by + /// default. + /// - requestModifier: `RequestModifier` which will be applied to the `URLRequest` created from the provided + /// parameters. `nil` by default. + /// + /// - Returns: The created `UploadRequest`. + open func upload(_ data: Data, + to convertible: URLConvertible, + method: HTTPMethod = .post, + headers: HTTPHeaders? = nil, + interceptor: RequestInterceptor? = nil, + fileManager: FileManager = .default, + requestModifier: RequestModifier? = nil) -> UploadRequest { + let convertible = ParameterlessRequestConvertible(url: convertible, + method: method, + headers: headers, + requestModifier: requestModifier) + + return upload(data, with: convertible, interceptor: interceptor, fileManager: fileManager) + } + + /// Creates an `UploadRequest` for the given `Data` using the `URLRequestConvertible` value and `RequestInterceptor`. + /// + /// - Parameters: + /// - data: The `Data` to upload. + /// - convertible: `URLRequestConvertible` value to be used to create the `URLRequest`. + /// - interceptor: `RequestInterceptor` value to be used by the returned `DataRequest`. `nil` by default. + /// - fileManager: `FileManager` instance to be used by the returned `UploadRequest`. `.default` instance by + /// default. + /// + /// - Returns: The created `UploadRequest`. + open func upload(_ data: Data, + with convertible: URLRequestConvertible, + interceptor: RequestInterceptor? = nil, + fileManager: FileManager = .default) -> UploadRequest { + upload(.data(data), with: convertible, interceptor: interceptor, fileManager: fileManager) + } + + // MARK: File + + /// Creates an `UploadRequest` for the file at the given file `URL`, using a `URLRequest` from the provided + /// components and `RequestInterceptor`. + /// + /// - Parameters: + /// - fileURL: The `URL` of the file to upload. + /// - convertible: `URLConvertible` value to be used as the `URLRequest`'s `URL`. + /// - method: `HTTPMethod` for the `URLRequest`. `.post` by default. + /// - headers: `HTTPHeaders` value to be added to the `URLRequest`. `nil` by default. + /// - interceptor: `RequestInterceptor` value to be used by the returned `UploadRequest`. `nil` by default. + /// - fileManager: `FileManager` instance to be used by the returned `UploadRequest`. `.default` instance by + /// default. + /// - requestModifier: `RequestModifier` which will be applied to the `URLRequest` created from the provided + /// parameters. `nil` by default. + /// + /// - Returns: The created `UploadRequest`. + open func upload(_ fileURL: URL, + to convertible: URLConvertible, + method: HTTPMethod = .post, + headers: HTTPHeaders? = nil, + interceptor: RequestInterceptor? = nil, + fileManager: FileManager = .default, + requestModifier: RequestModifier? = nil) -> UploadRequest { + let convertible = ParameterlessRequestConvertible(url: convertible, + method: method, + headers: headers, + requestModifier: requestModifier) + + return upload(fileURL, with: convertible, interceptor: interceptor, fileManager: fileManager) + } + + /// Creates an `UploadRequest` for the file at the given file `URL` using the `URLRequestConvertible` value and + /// `RequestInterceptor`. + /// + /// - Parameters: + /// - fileURL: The `URL` of the file to upload. + /// - convertible: `URLRequestConvertible` value to be used to create the `URLRequest`. + /// - interceptor: `RequestInterceptor` value to be used by the returned `DataRequest`. `nil` by default. + /// - fileManager: `FileManager` instance to be used by the returned `UploadRequest`. `.default` instance by + /// default. + /// + /// - Returns: The created `UploadRequest`. + open func upload(_ fileURL: URL, + with convertible: URLRequestConvertible, + interceptor: RequestInterceptor? = nil, + fileManager: FileManager = .default) -> UploadRequest { + upload(.file(fileURL, shouldRemove: false), with: convertible, interceptor: interceptor, fileManager: fileManager) + } + + // MARK: InputStream + + /// Creates an `UploadRequest` from the `InputStream` provided using a `URLRequest` from the provided components and + /// `RequestInterceptor`. + /// + /// - Parameters: + /// - stream: The `InputStream` that provides the data to upload. + /// - convertible: `URLConvertible` value to be used as the `URLRequest`'s `URL`. + /// - method: `HTTPMethod` for the `URLRequest`. `.post` by default. + /// - headers: `HTTPHeaders` value to be added to the `URLRequest`. `nil` by default. + /// - interceptor: `RequestInterceptor` value to be used by the returned `DataRequest`. `nil` by default. + /// - fileManager: `FileManager` instance to be used by the returned `UploadRequest`. `.default` instance by + /// default. + /// - requestModifier: `RequestModifier` which will be applied to the `URLRequest` created from the provided + /// parameters. `nil` by default. + /// + /// - Returns: The created `UploadRequest`. + open func upload(_ stream: InputStream, + to convertible: URLConvertible, + method: HTTPMethod = .post, + headers: HTTPHeaders? = nil, + interceptor: RequestInterceptor? = nil, + fileManager: FileManager = .default, + requestModifier: RequestModifier? = nil) -> UploadRequest { + let convertible = ParameterlessRequestConvertible(url: convertible, + method: method, + headers: headers, + requestModifier: requestModifier) + + return upload(stream, with: convertible, interceptor: interceptor, fileManager: fileManager) + } + + /// Creates an `UploadRequest` from the provided `InputStream` using the `URLRequestConvertible` value and + /// `RequestInterceptor`. + /// + /// - Parameters: + /// - stream: The `InputStream` that provides the data to upload. + /// - convertible: `URLRequestConvertible` value to be used to create the `URLRequest`. + /// - interceptor: `RequestInterceptor` value to be used by the returned `DataRequest`. `nil` by default. + /// - fileManager: `FileManager` instance to be used by the returned `UploadRequest`. `.default` instance by + /// default. + /// + /// - Returns: The created `UploadRequest`. + open func upload(_ stream: InputStream, + with convertible: URLRequestConvertible, + interceptor: RequestInterceptor? = nil, + fileManager: FileManager = .default) -> UploadRequest { + upload(.stream(stream), with: convertible, interceptor: interceptor, fileManager: fileManager) + } + + // MARK: MultipartFormData + + /// Creates an `UploadRequest` for the multipart form data built using a closure and sent using the provided + /// `URLRequest` components and `RequestInterceptor`. + /// + /// It is important to understand the memory implications of uploading `MultipartFormData`. If the cumulative + /// payload is small, encoding the data in-memory and directly uploading to a server is the by far the most + /// efficient approach. However, if the payload is too large, encoding the data in-memory could cause your app to + /// be terminated. Larger payloads must first be written to disk using input and output streams to keep the memory + /// footprint low, then the data can be uploaded as a stream from the resulting file. Streaming from disk MUST be + /// used for larger payloads such as video content. + /// + /// The `encodingMemoryThreshold` parameter allows Alamofire to automatically determine whether to encode in-memory + /// or stream from disk. If the content length of the `MultipartFormData` is below the `encodingMemoryThreshold`, + /// encoding takes place in-memory. If the content length exceeds the threshold, the data is streamed to disk + /// during the encoding process. Then the result is uploaded as data or as a stream depending on which encoding + /// technique was used. + /// + /// - Parameters: + /// - multipartFormData: `MultipartFormData` building closure. + /// - url: `URLConvertible` value to be used as the `URLRequest`'s `URL`. + /// - encodingMemoryThreshold: Byte threshold used to determine whether the form data is encoded into memory or + /// onto disk before being uploaded. `MultipartFormData.encodingMemoryThreshold` by + /// default. + /// - method: `HTTPMethod` for the `URLRequest`. `.post` by default. + /// - headers: `HTTPHeaders` value to be added to the `URLRequest`. `nil` by default. + /// - interceptor: `RequestInterceptor` value to be used by the returned `DataRequest`. `nil` by default. + /// - fileManager: `FileManager` to be used if the form data exceeds the memory threshold and is + /// written to disk before being uploaded. `.default` instance by default. + /// - requestModifier: `RequestModifier` which will be applied to the `URLRequest` created from the + /// provided parameters. `nil` by default. + /// + /// - Returns: The created `UploadRequest`. + open func upload(multipartFormData: @escaping (MultipartFormData) -> Void, + to url: URLConvertible, + usingThreshold encodingMemoryThreshold: UInt64 = MultipartFormData.encodingMemoryThreshold, + method: HTTPMethod = .post, + headers: HTTPHeaders? = nil, + interceptor: RequestInterceptor? = nil, + fileManager: FileManager = .default, + requestModifier: RequestModifier? = nil) -> UploadRequest { + let convertible = ParameterlessRequestConvertible(url: url, + method: method, + headers: headers, + requestModifier: requestModifier) + + let formData = MultipartFormData(fileManager: fileManager) + multipartFormData(formData) + + return upload(multipartFormData: formData, + with: convertible, + usingThreshold: encodingMemoryThreshold, + interceptor: interceptor, + fileManager: fileManager) + } + + /// Creates an `UploadRequest` using a `MultipartFormData` building closure, the provided `URLRequestConvertible` + /// value, and a `RequestInterceptor`. + /// + /// It is important to understand the memory implications of uploading `MultipartFormData`. If the cumulative + /// payload is small, encoding the data in-memory and directly uploading to a server is the by far the most + /// efficient approach. However, if the payload is too large, encoding the data in-memory could cause your app to + /// be terminated. Larger payloads must first be written to disk using input and output streams to keep the memory + /// footprint low, then the data can be uploaded as a stream from the resulting file. Streaming from disk MUST be + /// used for larger payloads such as video content. + /// + /// The `encodingMemoryThreshold` parameter allows Alamofire to automatically determine whether to encode in-memory + /// or stream from disk. If the content length of the `MultipartFormData` is below the `encodingMemoryThreshold`, + /// encoding takes place in-memory. If the content length exceeds the threshold, the data is streamed to disk + /// during the encoding process. Then the result is uploaded as data or as a stream depending on which encoding + /// technique was used. + /// + /// - Parameters: + /// - multipartFormData: `MultipartFormData` building closure. + /// - request: `URLRequestConvertible` value to be used to create the `URLRequest`. + /// - encodingMemoryThreshold: Byte threshold used to determine whether the form data is encoded into memory or + /// onto disk before being uploaded. `MultipartFormData.encodingMemoryThreshold` by + /// default. + /// - interceptor: `RequestInterceptor` value to be used by the returned `DataRequest`. `nil` by default. + /// - fileManager: `FileManager` to be used if the form data exceeds the memory threshold and is + /// written to disk before being uploaded. `.default` instance by default. + /// + /// - Returns: The created `UploadRequest`. + open func upload(multipartFormData: @escaping (MultipartFormData) -> Void, + with request: URLRequestConvertible, + usingThreshold encodingMemoryThreshold: UInt64 = MultipartFormData.encodingMemoryThreshold, + interceptor: RequestInterceptor? = nil, + fileManager: FileManager = .default) -> UploadRequest { + let formData = MultipartFormData(fileManager: fileManager) + multipartFormData(formData) + + return upload(multipartFormData: formData, + with: request, + usingThreshold: encodingMemoryThreshold, + interceptor: interceptor, + fileManager: fileManager) + } + + /// Creates an `UploadRequest` for the prebuilt `MultipartFormData` value using the provided `URLRequest` components + /// and `RequestInterceptor`. + /// + /// It is important to understand the memory implications of uploading `MultipartFormData`. If the cumulative + /// payload is small, encoding the data in-memory and directly uploading to a server is the by far the most + /// efficient approach. However, if the payload is too large, encoding the data in-memory could cause your app to + /// be terminated. Larger payloads must first be written to disk using input and output streams to keep the memory + /// footprint low, then the data can be uploaded as a stream from the resulting file. Streaming from disk MUST be + /// used for larger payloads such as video content. + /// + /// The `encodingMemoryThreshold` parameter allows Alamofire to automatically determine whether to encode in-memory + /// or stream from disk. If the content length of the `MultipartFormData` is below the `encodingMemoryThreshold`, + /// encoding takes place in-memory. If the content length exceeds the threshold, the data is streamed to disk + /// during the encoding process. Then the result is uploaded as data or as a stream depending on which encoding + /// technique was used. + /// + /// - Parameters: + /// - multipartFormData: `MultipartFormData` instance to upload. + /// - url: `URLConvertible` value to be used as the `URLRequest`'s `URL`. + /// - encodingMemoryThreshold: Byte threshold used to determine whether the form data is encoded into memory or + /// onto disk before being uploaded. `MultipartFormData.encodingMemoryThreshold` by + /// default. + /// - method: `HTTPMethod` for the `URLRequest`. `.post` by default. + /// - headers: `HTTPHeaders` value to be added to the `URLRequest`. `nil` by default. + /// - interceptor: `RequestInterceptor` value to be used by the returned `DataRequest`. `nil` by default. + /// - fileManager: `FileManager` to be used if the form data exceeds the memory threshold and is + /// written to disk before being uploaded. `.default` instance by default. + /// - requestModifier: `RequestModifier` which will be applied to the `URLRequest` created from the + /// provided parameters. `nil` by default. + /// + /// - Returns: The created `UploadRequest`. + open func upload(multipartFormData: MultipartFormData, + to url: URLConvertible, + usingThreshold encodingMemoryThreshold: UInt64 = MultipartFormData.encodingMemoryThreshold, + method: HTTPMethod = .post, + headers: HTTPHeaders? = nil, + interceptor: RequestInterceptor? = nil, + fileManager: FileManager = .default, + requestModifier: RequestModifier? = nil) -> UploadRequest { + let convertible = ParameterlessRequestConvertible(url: url, + method: method, + headers: headers, + requestModifier: requestModifier) + + let multipartUpload = MultipartUpload(encodingMemoryThreshold: encodingMemoryThreshold, + request: convertible, + multipartFormData: multipartFormData) + + return upload(multipartUpload, interceptor: interceptor, fileManager: fileManager) + } + + /// Creates an `UploadRequest` for the prebuilt `MultipartFormData` value using the providing `URLRequestConvertible` + /// value and `RequestInterceptor`. + /// + /// It is important to understand the memory implications of uploading `MultipartFormData`. If the cumulative + /// payload is small, encoding the data in-memory and directly uploading to a server is the by far the most + /// efficient approach. However, if the payload is too large, encoding the data in-memory could cause your app to + /// be terminated. Larger payloads must first be written to disk using input and output streams to keep the memory + /// footprint low, then the data can be uploaded as a stream from the resulting file. Streaming from disk MUST be + /// used for larger payloads such as video content. + /// + /// The `encodingMemoryThreshold` parameter allows Alamofire to automatically determine whether to encode in-memory + /// or stream from disk. If the content length of the `MultipartFormData` is below the `encodingMemoryThreshold`, + /// encoding takes place in-memory. If the content length exceeds the threshold, the data is streamed to disk + /// during the encoding process. Then the result is uploaded as data or as a stream depending on which encoding + /// technique was used. + /// + /// - Parameters: + /// - multipartFormData: `MultipartFormData` instance to upload. + /// - request: `URLRequestConvertible` value to be used to create the `URLRequest`. + /// - encodingMemoryThreshold: Byte threshold used to determine whether the form data is encoded into memory or + /// onto disk before being uploaded. `MultipartFormData.encodingMemoryThreshold` by + /// default. + /// - interceptor: `RequestInterceptor` value to be used by the returned `DataRequest`. `nil` by default. + /// - fileManager: `FileManager` instance to be used by the returned `UploadRequest`. `.default` instance by + /// default. + /// + /// - Returns: The created `UploadRequest`. + open func upload(multipartFormData: MultipartFormData, + with request: URLRequestConvertible, + usingThreshold encodingMemoryThreshold: UInt64 = MultipartFormData.encodingMemoryThreshold, + interceptor: RequestInterceptor? = nil, + fileManager: FileManager = .default) -> UploadRequest { + let multipartUpload = MultipartUpload(encodingMemoryThreshold: encodingMemoryThreshold, + request: request, + multipartFormData: multipartFormData) + + return upload(multipartUpload, interceptor: interceptor, fileManager: fileManager) + } + + // MARK: - Internal API + + // MARK: Uploadable + + func upload(_ uploadable: UploadRequest.Uploadable, + with convertible: URLRequestConvertible, + interceptor: RequestInterceptor?, + fileManager: FileManager) -> UploadRequest { + let uploadable = Upload(request: convertible, uploadable: uploadable) + + return upload(uploadable, interceptor: interceptor, fileManager: fileManager) + } + + func upload(_ upload: UploadConvertible, interceptor: RequestInterceptor?, fileManager: FileManager) -> UploadRequest { + let request = UploadRequest(convertible: upload, + underlyingQueue: rootQueue, + serializationQueue: serializationQueue, + eventMonitor: eventMonitor, + interceptor: interceptor, + fileManager: fileManager, + delegate: self) + + perform(request) + + return request + } + + // MARK: Perform + + /// Starts performing the provided `Request`. + /// + /// - Parameter request: The `Request` to perform. + func perform(_ request: Request) { + rootQueue.async { + guard !request.isCancelled else { return } + + self.activeRequests.insert(request) + + self.requestQueue.async { + // Leaf types must come first, otherwise they will cast as their superclass. + switch request { + case let r as UploadRequest: self.performUploadRequest(r) // UploadRequest must come before DataRequest due to subtype relationship. + case let r as DataRequest: self.performDataRequest(r) + case let r as DownloadRequest: self.performDownloadRequest(r) + case let r as DataStreamRequest: self.performDataStreamRequest(r) + default: + #if canImport(Darwin) && !canImport(FoundationNetworking) + if #available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *), + let request = request as? WebSocketRequest { + self.performWebSocketRequest(request) + } else { + fatalError("Attempted to perform unsupported Request subclass: \(type(of: request))") + } + #else + fatalError("Attempted to perform unsupported Request subclass: \(type(of: request))") + #endif + } + } + } + } + + func performDataRequest(_ request: DataRequest) { + dispatchPrecondition(condition: .onQueue(requestQueue)) + + performSetupOperations(for: request, convertible: request.convertible) + } + + func performDataStreamRequest(_ request: DataStreamRequest) { + dispatchPrecondition(condition: .onQueue(requestQueue)) + + performSetupOperations(for: request, convertible: request.convertible) + } + + #if canImport(Darwin) && !canImport(FoundationNetworking) + @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) + func performWebSocketRequest(_ request: WebSocketRequest) { + dispatchPrecondition(condition: .onQueue(requestQueue)) + + performSetupOperations(for: request, convertible: request.convertible) + } + #endif + + func performUploadRequest(_ request: UploadRequest) { + dispatchPrecondition(condition: .onQueue(requestQueue)) + + performSetupOperations(for: request, convertible: request.convertible) { + do { + let uploadable = try request.upload.createUploadable() + self.rootQueue.async { request.didCreateUploadable(uploadable) } + return true + } catch { + self.rootQueue.async { request.didFailToCreateUploadable(with: error.asAFError(or: .createUploadableFailed(error: error))) } + return false + } + } + } + + func performDownloadRequest(_ request: DownloadRequest) { + dispatchPrecondition(condition: .onQueue(requestQueue)) + + switch request.downloadable { + case let .request(convertible): + performSetupOperations(for: request, convertible: convertible) + case let .resumeData(resumeData): + rootQueue.async { self.didReceiveResumeData(resumeData, for: request) } + } + } + + func performSetupOperations(for request: Request, + convertible: URLRequestConvertible, + shouldCreateTask: @escaping () -> Bool = { true }) { + dispatchPrecondition(condition: .onQueue(requestQueue)) + + let initialRequest: URLRequest + + do { + initialRequest = try convertible.asURLRequest() + try initialRequest.validate() + } catch { + rootQueue.async { request.didFailToCreateURLRequest(with: error.asAFError(or: .createURLRequestFailed(error: error))) } + return + } + + rootQueue.async { request.didCreateInitialURLRequest(initialRequest) } + + guard !request.isCancelled else { return } + + guard let adapter = adapter(for: request) else { + guard shouldCreateTask() else { return } + rootQueue.async { self.didCreateURLRequest(initialRequest, for: request) } + return + } + + let adapterState = RequestAdapterState(requestID: request.id, session: self) + + adapter.adapt(initialRequest, using: adapterState) { result in + do { + let adaptedRequest = try result.get() + try adaptedRequest.validate() + + self.rootQueue.async { request.didAdaptInitialRequest(initialRequest, to: adaptedRequest) } + + guard shouldCreateTask() else { return } + + self.rootQueue.async { self.didCreateURLRequest(adaptedRequest, for: request) } + } catch { + self.rootQueue.async { request.didFailToAdaptURLRequest(initialRequest, withError: .requestAdaptationFailed(error: error)) } + } + } + } + + // MARK: - Task Handling + + func didCreateURLRequest(_ urlRequest: URLRequest, for request: Request) { + dispatchPrecondition(condition: .onQueue(rootQueue)) + + request.didCreateURLRequest(urlRequest) + + guard !request.isCancelled else { return } + + let task = request.task(for: urlRequest, using: session) + requestTaskMap[request] = task + request.didCreateTask(task) + + updateStatesForTask(task, request: request) + } + + func didReceiveResumeData(_ data: Data, for request: DownloadRequest) { + dispatchPrecondition(condition: .onQueue(rootQueue)) + + guard !request.isCancelled else { return } + + let task = request.task(forResumeData: data, using: session) + requestTaskMap[request] = task + request.didCreateTask(task) + + updateStatesForTask(task, request: request) + } + + func updateStatesForTask(_ task: URLSessionTask, request: Request) { + dispatchPrecondition(condition: .onQueue(rootQueue)) + + request.withState { state in + switch state { + case .initialized, .finished: + // Do nothing. + break + case .resumed: + task.resume() + rootQueue.async { request.didResumeTask(task) } + case .suspended: + task.suspend() + rootQueue.async { request.didSuspendTask(task) } + case .cancelled: + // Resume to ensure metrics are gathered. + task.resume() + task.cancel() + rootQueue.async { request.didCancelTask(task) } + } + } + } + + // MARK: - Adapters and Retriers + + func adapter(for request: Request) -> RequestAdapter? { + if let requestInterceptor = request.interceptor, let sessionInterceptor = interceptor { + return Interceptor(adapters: [requestInterceptor, sessionInterceptor]) + } else { + return request.interceptor ?? interceptor + } + } + + func retrier(for request: Request) -> RequestRetrier? { + if let requestInterceptor = request.interceptor, let sessionInterceptor = interceptor { + return Interceptor(retriers: [requestInterceptor, sessionInterceptor]) + } else { + return request.interceptor ?? interceptor + } + } + + // MARK: - Invalidation + + func finishRequestsForDeinit() { + for request in requestTaskMap.requests { + rootQueue.async { + request.finish(error: AFError.sessionDeinitialized) + } + } + } +} + +// MARK: - RequestDelegate + +extension Session: RequestDelegate { + public var sessionConfiguration: URLSessionConfiguration { + session.configuration + } + + public var startImmediately: Bool { startRequestsImmediately } + + public func cleanup(after request: Request) { + activeRequests.remove(request) + } + + public func retryResult(for request: Request, dueTo error: AFError, completion: @escaping (RetryResult) -> Void) { + guard let retrier = retrier(for: request) else { + rootQueue.async { completion(.doNotRetry) } + return + } + + retrier.retry(request, for: self, dueTo: error) { retryResult in + self.rootQueue.async { + guard let retryResultError = retryResult.error else { completion(retryResult); return } + + let retryError = AFError.requestRetryFailed(retryError: retryResultError, originalError: error) + completion(.doNotRetryWithError(retryError)) + } + } + } + + public func retryRequest(_ request: Request, withDelay timeDelay: TimeInterval?) { + rootQueue.async { + let retry: () -> Void = { + guard !request.isCancelled else { return } + + request.prepareForRetry() + self.perform(request) + } + + if let retryDelay = timeDelay { + self.rootQueue.after(retryDelay) { retry() } + } else { + retry() + } + } + } +} + +// MARK: - SessionStateProvider + +extension Session: SessionStateProvider { + func request(for task: URLSessionTask) -> Request? { + dispatchPrecondition(condition: .onQueue(rootQueue)) + + return requestTaskMap[task] + } + + func didGatherMetricsForTask(_ task: URLSessionTask) { + dispatchPrecondition(condition: .onQueue(rootQueue)) + + let didDisassociate = requestTaskMap.disassociateIfNecessaryAfterGatheringMetricsForTask(task) + + if didDisassociate { + waitingCompletions[task]?() + waitingCompletions[task] = nil + } + } + + func didCompleteTask(_ task: URLSessionTask, completion: @escaping () -> Void) { + dispatchPrecondition(condition: .onQueue(rootQueue)) + + let didDisassociate = requestTaskMap.disassociateIfNecessaryAfterCompletingTask(task) + + if didDisassociate { + completion() + } else { + waitingCompletions[task] = completion + } + } + + func credential(for task: URLSessionTask, in protectionSpace: URLProtectionSpace) -> URLCredential? { + dispatchPrecondition(condition: .onQueue(rootQueue)) + + return requestTaskMap[task]?.credential ?? + session.configuration.urlCredentialStorage?.defaultCredential(for: protectionSpace) + } + + func cancelRequestsForSessionInvalidation(with error: Error?) { + dispatchPrecondition(condition: .onQueue(rootQueue)) + + requestTaskMap.requests.forEach { $0.finish(error: AFError.sessionInvalidated(error: error)) } + } +} diff --git a/Pods/Alamofire/Source/Core/SessionDelegate.swift b/Pods/Alamofire/Source/Core/SessionDelegate.swift new file mode 100644 index 0000000..de0fcbb --- /dev/null +++ b/Pods/Alamofire/Source/Core/SessionDelegate.swift @@ -0,0 +1,387 @@ +// +// SessionDelegate.swift +// +// Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +import Foundation + +/// Class which implements the various `URLSessionDelegate` methods to connect various Alamofire features. +open class SessionDelegate: NSObject { + private let fileManager: FileManager + + weak var stateProvider: SessionStateProvider? + var eventMonitor: EventMonitor? + + /// Creates an instance from the given `FileManager`. + /// + /// - Parameter fileManager: `FileManager` to use for underlying file management, such as moving downloaded files. + /// `.default` by default. + public init(fileManager: FileManager = .default) { + self.fileManager = fileManager + } + + /// Internal method to find and cast requests while maintaining some integrity checking. + /// + /// - Parameters: + /// - task: The `URLSessionTask` for which to find the associated `Request`. + /// - type: The `Request` subclass type to cast any `Request` associate with `task`. + func request(for task: URLSessionTask, as type: R.Type) -> R? { + guard let provider = stateProvider else { + assertionFailure("StateProvider is nil for task \(task.taskIdentifier).") + return nil + } + + return provider.request(for: task) as? R + } +} + +/// Type which provides various `Session` state values. +protocol SessionStateProvider: AnyObject { + var serverTrustManager: ServerTrustManager? { get } + var redirectHandler: RedirectHandler? { get } + var cachedResponseHandler: CachedResponseHandler? { get } + + func request(for task: URLSessionTask) -> Request? + func didGatherMetricsForTask(_ task: URLSessionTask) + func didCompleteTask(_ task: URLSessionTask, completion: @escaping () -> Void) + func credential(for task: URLSessionTask, in protectionSpace: URLProtectionSpace) -> URLCredential? + func cancelRequestsForSessionInvalidation(with error: Error?) +} + +// MARK: URLSessionDelegate + +extension SessionDelegate: URLSessionDelegate { + open func urlSession(_ session: URLSession, didBecomeInvalidWithError error: Error?) { + eventMonitor?.urlSession(session, didBecomeInvalidWithError: error) + + stateProvider?.cancelRequestsForSessionInvalidation(with: error) + } +} + +// MARK: URLSessionTaskDelegate + +extension SessionDelegate: URLSessionTaskDelegate { + /// Result of a `URLAuthenticationChallenge` evaluation. + typealias ChallengeEvaluation = (disposition: URLSession.AuthChallengeDisposition, credential: URLCredential?, error: AFError?) + + open func urlSession(_ session: URLSession, + task: URLSessionTask, + didReceive challenge: URLAuthenticationChallenge, + completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) { + eventMonitor?.urlSession(session, task: task, didReceive: challenge) + + let evaluation: ChallengeEvaluation + switch challenge.protectionSpace.authenticationMethod { + case NSURLAuthenticationMethodHTTPBasic, NSURLAuthenticationMethodHTTPDigest, NSURLAuthenticationMethodNTLM, + NSURLAuthenticationMethodNegotiate: + evaluation = attemptCredentialAuthentication(for: challenge, belongingTo: task) + #if canImport(Security) + case NSURLAuthenticationMethodServerTrust: + evaluation = attemptServerTrustAuthentication(with: challenge) + case NSURLAuthenticationMethodClientCertificate: + evaluation = attemptCredentialAuthentication(for: challenge, belongingTo: task) + #endif + default: + evaluation = (.performDefaultHandling, nil, nil) + } + + if let error = evaluation.error { + stateProvider?.request(for: task)?.didFailTask(task, earlyWithError: error) + } + + completionHandler(evaluation.disposition, evaluation.credential) + } + + #if canImport(Security) + /// Evaluates the server trust `URLAuthenticationChallenge` received. + /// + /// - Parameter challenge: The `URLAuthenticationChallenge`. + /// + /// - Returns: The `ChallengeEvaluation`. + func attemptServerTrustAuthentication(with challenge: URLAuthenticationChallenge) -> ChallengeEvaluation { + let host = challenge.protectionSpace.host + + guard challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust, + let trust = challenge.protectionSpace.serverTrust + else { + return (.performDefaultHandling, nil, nil) + } + + do { + guard let evaluator = try stateProvider?.serverTrustManager?.serverTrustEvaluator(forHost: host) else { + return (.performDefaultHandling, nil, nil) + } + + try evaluator.evaluate(trust, forHost: host) + + return (.useCredential, URLCredential(trust: trust), nil) + } catch { + return (.cancelAuthenticationChallenge, nil, error.asAFError(or: .serverTrustEvaluationFailed(reason: .customEvaluationFailed(error: error)))) + } + } + #endif + + /// Evaluates the credential-based authentication `URLAuthenticationChallenge` received for `task`. + /// + /// - Parameters: + /// - challenge: The `URLAuthenticationChallenge`. + /// - task: The `URLSessionTask` which received the challenge. + /// + /// - Returns: The `ChallengeEvaluation`. + func attemptCredentialAuthentication(for challenge: URLAuthenticationChallenge, + belongingTo task: URLSessionTask) -> ChallengeEvaluation { + guard challenge.previousFailureCount == 0 else { + return (.rejectProtectionSpace, nil, nil) + } + + guard let credential = stateProvider?.credential(for: task, in: challenge.protectionSpace) else { + return (.performDefaultHandling, nil, nil) + } + + return (.useCredential, credential, nil) + } + + open func urlSession(_ session: URLSession, + task: URLSessionTask, + didSendBodyData bytesSent: Int64, + totalBytesSent: Int64, + totalBytesExpectedToSend: Int64) { + eventMonitor?.urlSession(session, + task: task, + didSendBodyData: bytesSent, + totalBytesSent: totalBytesSent, + totalBytesExpectedToSend: totalBytesExpectedToSend) + + stateProvider?.request(for: task)?.updateUploadProgress(totalBytesSent: totalBytesSent, + totalBytesExpectedToSend: totalBytesExpectedToSend) + } + + open func urlSession(_ session: URLSession, + task: URLSessionTask, + needNewBodyStream completionHandler: @escaping (InputStream?) -> Void) { + eventMonitor?.urlSession(session, taskNeedsNewBodyStream: task) + + guard let request = request(for: task, as: UploadRequest.self) else { + assertionFailure("needNewBodyStream did not find UploadRequest.") + completionHandler(nil) + return + } + + completionHandler(request.inputStream()) + } + + open func urlSession(_ session: URLSession, + task: URLSessionTask, + willPerformHTTPRedirection response: HTTPURLResponse, + newRequest request: URLRequest, + completionHandler: @escaping (URLRequest?) -> Void) { + eventMonitor?.urlSession(session, task: task, willPerformHTTPRedirection: response, newRequest: request) + + if let redirectHandler = stateProvider?.request(for: task)?.redirectHandler ?? stateProvider?.redirectHandler { + redirectHandler.task(task, willBeRedirectedTo: request, for: response, completion: completionHandler) + } else { + completionHandler(request) + } + } + + open func urlSession(_ session: URLSession, task: URLSessionTask, didFinishCollecting metrics: URLSessionTaskMetrics) { + eventMonitor?.urlSession(session, task: task, didFinishCollecting: metrics) + + stateProvider?.request(for: task)?.didGatherMetrics(metrics) + + stateProvider?.didGatherMetricsForTask(task) + } + + open func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) { +// NSLog("URLSession: \(session), task: \(task), didCompleteWithError: \(error)") + eventMonitor?.urlSession(session, task: task, didCompleteWithError: error) + + let request = stateProvider?.request(for: task) + + stateProvider?.didCompleteTask(task) { + request?.didCompleteTask(task, with: error.map { $0.asAFError(or: .sessionTaskFailed(error: $0)) }) + } + } + + @available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *) + open func urlSession(_ session: URLSession, taskIsWaitingForConnectivity task: URLSessionTask) { + eventMonitor?.urlSession(session, taskIsWaitingForConnectivity: task) + } +} + +// MARK: URLSessionDataDelegate + +extension SessionDelegate: URLSessionDataDelegate { + open func urlSession(_ session: URLSession, + dataTask: URLSessionDataTask, + didReceive response: URLResponse, + completionHandler: @escaping (URLSession.ResponseDisposition) -> Void) { + eventMonitor?.urlSession(session, dataTask: dataTask, didReceive: response) + + guard let response = response as? HTTPURLResponse else { completionHandler(.allow); return } + + if let request = request(for: dataTask, as: DataRequest.self) { + request.didReceiveResponse(response, completionHandler: completionHandler) + } else if let request = request(for: dataTask, as: DataStreamRequest.self) { + request.didReceiveResponse(response, completionHandler: completionHandler) + } else { + assertionFailure("dataTask did not find DataRequest or DataStreamRequest in didReceive response") + completionHandler(.allow) + return + } + } + + open func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) { + eventMonitor?.urlSession(session, dataTask: dataTask, didReceive: data) + + if let request = request(for: dataTask, as: DataRequest.self) { + request.didReceive(data: data) + } else if let request = request(for: dataTask, as: DataStreamRequest.self) { + request.didReceive(data: data) + } else { + assertionFailure("dataTask did not find DataRequest or DataStreamRequest in didReceive data") + return + } + } + + open func urlSession(_ session: URLSession, + dataTask: URLSessionDataTask, + willCacheResponse proposedResponse: CachedURLResponse, + completionHandler: @escaping (CachedURLResponse?) -> Void) { + eventMonitor?.urlSession(session, dataTask: dataTask, willCacheResponse: proposedResponse) + + if let handler = stateProvider?.request(for: dataTask)?.cachedResponseHandler ?? stateProvider?.cachedResponseHandler { + handler.dataTask(dataTask, willCacheResponse: proposedResponse, completion: completionHandler) + } else { + completionHandler(proposedResponse) + } + } +} + +// MARK: URLSessionWebSocketDelegate + +#if canImport(Darwin) && !canImport(FoundationNetworking) + +@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) +extension SessionDelegate: URLSessionWebSocketDelegate { + open func urlSession(_ session: URLSession, webSocketTask: URLSessionWebSocketTask, didOpenWithProtocol protocol: String?) { + // TODO: Add event monitor method. +// NSLog("URLSession: \(session), webSocketTask: \(webSocketTask), didOpenWithProtocol: \(`protocol` ?? "None")") + guard let request = request(for: webSocketTask, as: WebSocketRequest.self) else { + return + } + + request.didConnect(protocol: `protocol`) + } + + open func urlSession(_ session: URLSession, webSocketTask: URLSessionWebSocketTask, didCloseWith closeCode: URLSessionWebSocketTask.CloseCode, reason: Data?) { + // TODO: Add event monitor method. +// NSLog("URLSession: \(session), webSocketTask: \(webSocketTask), didCloseWithCode: \(closeCode.rawValue), reason: \(reason ?? Data())") + guard let request = request(for: webSocketTask, as: WebSocketRequest.self) else { + return + } + + // On 2021 OSes and above, empty reason is returned as empty Data rather than nil, so make it nil always. + let reason = (reason?.isEmpty == true) ? nil : reason + request.didDisconnect(closeCode: closeCode, reason: reason) + } +} + +#endif + +// MARK: URLSessionDownloadDelegate + +extension SessionDelegate: URLSessionDownloadDelegate { + open func urlSession(_ session: URLSession, + downloadTask: URLSessionDownloadTask, + didResumeAtOffset fileOffset: Int64, + expectedTotalBytes: Int64) { + eventMonitor?.urlSession(session, + downloadTask: downloadTask, + didResumeAtOffset: fileOffset, + expectedTotalBytes: expectedTotalBytes) + guard let downloadRequest = request(for: downloadTask, as: DownloadRequest.self) else { + assertionFailure("downloadTask did not find DownloadRequest.") + return + } + + downloadRequest.updateDownloadProgress(bytesWritten: fileOffset, + totalBytesExpectedToWrite: expectedTotalBytes) + } + + open func urlSession(_ session: URLSession, + downloadTask: URLSessionDownloadTask, + didWriteData bytesWritten: Int64, + totalBytesWritten: Int64, + totalBytesExpectedToWrite: Int64) { + eventMonitor?.urlSession(session, + downloadTask: downloadTask, + didWriteData: bytesWritten, + totalBytesWritten: totalBytesWritten, + totalBytesExpectedToWrite: totalBytesExpectedToWrite) + guard let downloadRequest = request(for: downloadTask, as: DownloadRequest.self) else { + assertionFailure("downloadTask did not find DownloadRequest.") + return + } + + downloadRequest.updateDownloadProgress(bytesWritten: bytesWritten, + totalBytesExpectedToWrite: totalBytesExpectedToWrite) + } + + open func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) { + eventMonitor?.urlSession(session, downloadTask: downloadTask, didFinishDownloadingTo: location) + + guard let request = request(for: downloadTask, as: DownloadRequest.self) else { + assertionFailure("downloadTask did not find DownloadRequest.") + return + } + + let (destination, options): (URL, DownloadRequest.Options) + if let response = request.response { + (destination, options) = request.destination(location, response) + } else { + // If there's no response this is likely a local file download, so generate the temporary URL directly. + (destination, options) = (DownloadRequest.defaultDestinationURL(location), []) + } + + eventMonitor?.request(request, didCreateDestinationURL: destination) + + do { + if options.contains(.removePreviousFile), fileManager.fileExists(atPath: destination.path) { + try fileManager.removeItem(at: destination) + } + + if options.contains(.createIntermediateDirectories) { + let directory = destination.deletingLastPathComponent() + try fileManager.createDirectory(at: directory, withIntermediateDirectories: true) + } + + try fileManager.moveItem(at: location, to: destination) + + request.didFinishDownloading(using: downloadTask, with: .success(destination)) + } catch { + request.didFinishDownloading(using: downloadTask, with: .failure(.downloadedFileMoveFailed(error: error, + source: location, + destination: destination))) + } + } +} diff --git a/Pods/Alamofire/Source/Core/URLConvertible+URLRequestConvertible.swift b/Pods/Alamofire/Source/Core/URLConvertible+URLRequestConvertible.swift new file mode 100644 index 0000000..4f3e6ec --- /dev/null +++ b/Pods/Alamofire/Source/Core/URLConvertible+URLRequestConvertible.swift @@ -0,0 +1,105 @@ +// +// URLConvertible+URLRequestConvertible.swift +// +// Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +import Foundation + +/// Types adopting the `URLConvertible` protocol can be used to construct `URL`s, which can then be used to construct +/// `URLRequest`s. +public protocol URLConvertible { + /// Returns a `URL` from the conforming instance or throws. + /// + /// - Returns: The `URL` created from the instance. + /// - Throws: Any error thrown while creating the `URL`. + func asURL() throws -> URL +} + +extension String: URLConvertible { + /// Returns a `URL` if `self` can be used to initialize a `URL` instance, otherwise throws. + /// + /// - Returns: The `URL` initialized with `self`. + /// - Throws: An `AFError.invalidURL` instance. + public func asURL() throws -> URL { + guard let url = URL(string: self) else { throw AFError.invalidURL(url: self) } + + return url + } +} + +extension URL: URLConvertible { + /// Returns `self`. + public func asURL() throws -> URL { self } +} + +extension URLComponents: URLConvertible { + /// Returns a `URL` if the `self`'s `url` is not nil, otherwise throws. + /// + /// - Returns: The `URL` from the `url` property. + /// - Throws: An `AFError.invalidURL` instance. + public func asURL() throws -> URL { + guard let url else { throw AFError.invalidURL(url: self) } + + return url + } +} + +// MARK: - + +/// Types adopting the `URLRequestConvertible` protocol can be used to safely construct `URLRequest`s. +public protocol URLRequestConvertible { + /// Returns a `URLRequest` or throws if an `Error` was encountered. + /// + /// - Returns: A `URLRequest`. + /// - Throws: Any error thrown while constructing the `URLRequest`. + func asURLRequest() throws -> URLRequest +} + +extension URLRequestConvertible { + /// The `URLRequest` returned by discarding any `Error` encountered. + public var urlRequest: URLRequest? { try? asURLRequest() } +} + +extension URLRequest: URLRequestConvertible { + /// Returns `self`. + public func asURLRequest() throws -> URLRequest { self } +} + +// MARK: - + +extension URLRequest { + /// Creates an instance with the specified `url`, `method`, and `headers`. + /// + /// - Parameters: + /// - url: The `URLConvertible` value. + /// - method: The `HTTPMethod`. + /// - headers: The `HTTPHeaders`, `nil` by default. + /// - Throws: Any error thrown while converting the `URLConvertible` to a `URL`. + public init(url: URLConvertible, method: HTTPMethod, headers: HTTPHeaders? = nil) throws { + let url = try url.asURL() + + self.init(url: url) + + httpMethod = method.rawValue + allHTTPHeaderFields = headers?.dictionary + } +} diff --git a/Pods/Alamofire/Source/Core/UploadRequest.swift b/Pods/Alamofire/Source/Core/UploadRequest.swift new file mode 100644 index 0000000..854373b --- /dev/null +++ b/Pods/Alamofire/Source/Core/UploadRequest.swift @@ -0,0 +1,174 @@ +// +// UploadRequest.swift +// +// Copyright (c) 2014-2024 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +import Foundation + +/// `DataRequest` subclass which handles `Data` upload from memory, file, or stream using `URLSessionUploadTask`. +public final class UploadRequest: DataRequest { + /// Type describing the origin of the upload, whether `Data`, file, or stream. + public enum Uploadable { + /// Upload from the provided `Data` value. + case data(Data) + /// Upload from the provided file `URL`, as well as a `Bool` determining whether the source file should be + /// automatically removed once uploaded. + case file(URL, shouldRemove: Bool) + /// Upload from the provided `InputStream`. + case stream(InputStream) + } + + // MARK: Initial State + + /// The `UploadableConvertible` value used to produce the `Uploadable` value for this instance. + public let upload: UploadableConvertible + + /// `FileManager` used to perform cleanup tasks, including the removal of multipart form encoded payloads written + /// to disk. + public let fileManager: FileManager + + // MARK: Mutable State + + /// `Uploadable` value used by the instance. + public var uploadable: Uploadable? + + /// Creates an `UploadRequest` using the provided parameters. + /// + /// - Parameters: + /// - id: `UUID` used for the `Hashable` and `Equatable` implementations. `UUID()` by default. + /// - convertible: `UploadConvertible` value used to determine the type of upload to be performed. + /// - underlyingQueue: `DispatchQueue` on which all internal `Request` work is performed. + /// - serializationQueue: `DispatchQueue` on which all serialization work is performed. By default targets + /// `underlyingQueue`, but can be passed another queue from a `Session`. + /// - eventMonitor: `EventMonitor` called for event callbacks from internal `Request` actions. + /// - interceptor: `RequestInterceptor` used throughout the request lifecycle. + /// - fileManager: `FileManager` used to perform cleanup tasks, including the removal of multipart form + /// encoded payloads written to disk. + /// - delegate: `RequestDelegate` that provides an interface to actions not performed by the `Request`. + init(id: UUID = UUID(), + convertible: UploadConvertible, + underlyingQueue: DispatchQueue, + serializationQueue: DispatchQueue, + eventMonitor: EventMonitor?, + interceptor: RequestInterceptor?, + fileManager: FileManager, + delegate: RequestDelegate) { + upload = convertible + self.fileManager = fileManager + + super.init(id: id, + convertible: convertible, + underlyingQueue: underlyingQueue, + serializationQueue: serializationQueue, + eventMonitor: eventMonitor, + interceptor: interceptor, + delegate: delegate) + } + + /// Called when the `Uploadable` value has been created from the `UploadConvertible`. + /// + /// - Parameter uploadable: The `Uploadable` that was created. + func didCreateUploadable(_ uploadable: Uploadable) { + self.uploadable = uploadable + + eventMonitor?.request(self, didCreateUploadable: uploadable) + } + + /// Called when the `Uploadable` value could not be created. + /// + /// - Parameter error: `AFError` produced by the failure. + func didFailToCreateUploadable(with error: AFError) { + self.error = error + + eventMonitor?.request(self, didFailToCreateUploadableWithError: error) + + retryOrFinish(error: error) + } + + override func task(for request: URLRequest, using session: URLSession) -> URLSessionTask { + guard let uploadable else { + fatalError("Attempting to create a URLSessionUploadTask when Uploadable value doesn't exist.") + } + + switch uploadable { + case let .data(data): return session.uploadTask(with: request, from: data) + case let .file(url, _): return session.uploadTask(with: request, fromFile: url) + case .stream: return session.uploadTask(withStreamedRequest: request) + } + } + + override func reset() { + // Uploadable must be recreated on every retry. + uploadable = nil + + super.reset() + } + + /// Produces the `InputStream` from `uploadable`, if it can. + /// + /// - Note: Calling this method with a non-`.stream` `Uploadable` is a logic error and will crash. + /// + /// - Returns: The `InputStream`. + func inputStream() -> InputStream { + guard let uploadable else { + fatalError("Attempting to access the input stream but the uploadable doesn't exist.") + } + + guard case let .stream(stream) = uploadable else { + fatalError("Attempted to access the stream of an UploadRequest that wasn't created with one.") + } + + eventMonitor?.request(self, didProvideInputStream: stream) + + return stream + } + + override public func cleanup() { + defer { super.cleanup() } + + guard + let uploadable, + case let .file(url, shouldRemove) = uploadable, + shouldRemove + else { return } + + try? fileManager.removeItem(at: url) + } +} + +/// A type that can produce an `UploadRequest.Uploadable` value. +public protocol UploadableConvertible { + /// Produces an `UploadRequest.Uploadable` value from the instance. + /// + /// - Returns: The `UploadRequest.Uploadable`. + /// - Throws: Any `Error` produced during creation. + func createUploadable() throws -> UploadRequest.Uploadable +} + +extension UploadRequest.Uploadable: UploadableConvertible { + public func createUploadable() throws -> UploadRequest.Uploadable { + self + } +} + +/// A type that can be converted to an upload, whether from an `UploadRequest.Uploadable` or `URLRequestConvertible`. +public protocol UploadConvertible: UploadableConvertible & URLRequestConvertible {} diff --git a/Pods/Alamofire/Source/Core/WebSocketRequest.swift b/Pods/Alamofire/Source/Core/WebSocketRequest.swift new file mode 100644 index 0000000..12b6c70 --- /dev/null +++ b/Pods/Alamofire/Source/Core/WebSocketRequest.swift @@ -0,0 +1,564 @@ +// +// WebSocketRequest.swift +// +// Copyright (c) 2014-2024 Alamofire Software Foundation (http://alamofire.org/) +// +// 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. +// + +#if canImport(Darwin) && !canImport(FoundationNetworking) // Only Apple platforms support URLSessionWebSocketTask. + +import Foundation + +/// `Request` subclass which manages a WebSocket connection using `URLSessionWebSocketTask`. +/// +/// - Note: This type is currently experimental. There will be breaking changes before the final public release, +/// especially around adoption of the typed throws feature in Swift 6. Please report any missing features or +/// bugs to https://github.com/Alamofire/Alamofire/issues. +@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) +@_spi(WebSocket) public final class WebSocketRequest: Request { + enum IncomingEvent { + case connected(protocol: String?) + case receivedMessage(URLSessionWebSocketTask.Message) + case disconnected(closeCode: URLSessionWebSocketTask.CloseCode, reason: Data?) + case completed(Completion) + } + + public struct Event { + public enum Kind { + case connected(protocol: String?) + case receivedMessage(Success) + case serializerFailed(Failure) + // Only received if the server disconnects or we cancel with code, not if we do a simple cancel or error. + case disconnected(closeCode: URLSessionWebSocketTask.CloseCode, reason: Data?) + case completed(Completion) + } + + weak var socket: WebSocketRequest? + + public let kind: Kind + public var message: Success? { + guard case let .receivedMessage(message) = kind else { return nil } + + return message + } + + init(socket: WebSocketRequest, kind: Kind) { + self.socket = socket + self.kind = kind + } + + public func close(sending closeCode: URLSessionWebSocketTask.CloseCode, reason: Data? = nil) { + socket?.close(sending: closeCode, reason: reason) + } + + public func cancel() { + socket?.cancel() + } + + public func sendPing(respondingOn queue: DispatchQueue = .main, onResponse: @escaping (PingResponse) -> Void) { + socket?.sendPing(respondingOn: queue, onResponse: onResponse) + } + } + + public struct Completion { + /// Last `URLRequest` issued by the instance. + public let request: URLRequest? + /// Last `HTTPURLResponse` received by the instance. + public let response: HTTPURLResponse? + /// Last `URLSessionTaskMetrics` produced for the instance. + public let metrics: URLSessionTaskMetrics? + /// `AFError` produced for the instance, if any. + public let error: AFError? + } + + public struct Configuration { + public static var `default`: Self { Self() } + + public static func `protocol`(_ protocol: String) -> Self { + Self(protocol: `protocol`) + } + + public static func maximumMessageSize(_ maximumMessageSize: Int) -> Self { + Self(maximumMessageSize: maximumMessageSize) + } + + public static func pingInterval(_ pingInterval: TimeInterval) -> Self { + Self(pingInterval: pingInterval) + } + + public let `protocol`: String? + public let maximumMessageSize: Int + public let pingInterval: TimeInterval? + + init(protocol: String? = nil, maximumMessageSize: Int = 1_048_576, pingInterval: TimeInterval? = nil) { + self.protocol = `protocol` + self.maximumMessageSize = maximumMessageSize + self.pingInterval = pingInterval + } + } + + /// Response to a sent ping. + public enum PingResponse { + public struct Pong { + let start: Date + let end: Date + let latency: TimeInterval + } + + /// Received a pong with the associated state. + case pong(Pong) + /// Received an error. + case error(Error) + /// Did not send the ping, the request is cancelled or suspended. + case unsent + } + + struct SocketMutableState { + var enqueuedSends: [(message: URLSessionWebSocketTask.Message, + queue: DispatchQueue, + completionHandler: (Result) -> Void)] = [] + var handlers: [(queue: DispatchQueue, handler: (_ event: IncomingEvent) -> Void)] = [] + var pingTimerItem: DispatchWorkItem? + } + + let socketMutableState = Protected(SocketMutableState()) + + var socket: URLSessionWebSocketTask? { + task as? URLSessionWebSocketTask + } + + public let convertible: URLRequestConvertible + public let configuration: Configuration + + init(id: UUID = UUID(), + convertible: URLRequestConvertible, + configuration: Configuration, + underlyingQueue: DispatchQueue, + serializationQueue: DispatchQueue, + eventMonitor: EventMonitor?, + interceptor: RequestInterceptor?, + delegate: RequestDelegate) { + self.convertible = convertible + self.configuration = configuration + + super.init(id: id, + underlyingQueue: underlyingQueue, + serializationQueue: serializationQueue, + eventMonitor: eventMonitor, + interceptor: interceptor, + delegate: delegate) + } + + override func task(for request: URLRequest, using session: URLSession) -> URLSessionTask { + var copiedRequest = request + let task: URLSessionWebSocketTask + if let `protocol` = configuration.protocol { + copiedRequest.headers.update(.websocketProtocol(`protocol`)) + task = session.webSocketTask(with: copiedRequest) + } else { + task = session.webSocketTask(with: copiedRequest) + } + task.maximumMessageSize = configuration.maximumMessageSize + + return task + } + + override func didCreateTask(_ task: URLSessionTask) { + super.didCreateTask(task) + + guard let webSocketTask = task as? URLSessionWebSocketTask else { + fatalError("Invalid task of type \(task.self) created for WebSocketRequest.") + } + // TODO: What about the any old tasks? Reset their receive? + listen(to: webSocketTask) + + // Empty pending messages. + socketMutableState.write { state in + guard !state.enqueuedSends.isEmpty else { return } + + let sends = state.enqueuedSends + self.underlyingQueue.async { + for send in sends { + webSocketTask.send(send.message) { error in + send.queue.async { + send.completionHandler(Result(value: (), error: error)) + } + } + } + } + + state.enqueuedSends = [] + } + } + + func didClose() { + dispatchPrecondition(condition: .onQueue(underlyingQueue)) + + mutableState.write { mutableState in + // Check whether error is cancellation or other websocket closing error. + // If so, remove it. + // Otherwise keep it. + if case let .sessionTaskFailed(error) = mutableState.error, (error as? URLError)?.code == .cancelled { + mutableState.error = nil + } + } + + // TODO: Still issue this event? + eventMonitor?.requestDidCancel(self) + } + + @discardableResult + public func close(sending closeCode: URLSessionWebSocketTask.CloseCode, reason: Data? = nil) -> Self { + cancelAutomaticPing() + + mutableState.write { mutableState in + guard mutableState.state.canTransitionTo(.cancelled) else { return } + + mutableState.state = .cancelled + + underlyingQueue.async { self.didClose() } + + guard let task = mutableState.tasks.last, task.state != .completed else { + underlyingQueue.async { self.finish() } + return + } + + // Resume to ensure metrics are gathered. + task.resume() + // Cast from state directly, not the property, otherwise the lock is recursive. + (mutableState.tasks.last as? URLSessionWebSocketTask)?.cancel(with: closeCode, reason: reason) + underlyingQueue.async { self.didCancelTask(task) } + } + + return self + } + + @discardableResult + override public func cancel() -> Self { + cancelAutomaticPing() + + return super.cancel() + } + + func didConnect(protocol: String?) { + dispatchPrecondition(condition: .onQueue(underlyingQueue)) + + socketMutableState.read { state in + // TODO: Capture HTTPURLResponse here too? + for handler in state.handlers { + // Saved handler calls out to serializationQueue immediately, then to handler's queue. + handler.handler(.connected(protocol: `protocol`)) + } + } + + if let pingInterval = configuration.pingInterval { + startAutomaticPing(every: pingInterval) + } + } + + public func sendPing(respondingOn queue: DispatchQueue = .main, onResponse: @escaping (PingResponse) -> Void) { + guard isResumed else { + queue.async { onResponse(.unsent) } + return + } + + let start = Date() + let startTimestamp = ProcessInfo.processInfo.systemUptime + socket?.sendPing { error in + // Calls back on delegate queue / rootQueue / underlyingQueue + if let error { + queue.async { + onResponse(.error(error)) + } + // TODO: What to do with failed ping? Configure for failure, auto retry, or stop pinging? + } else { + let end = Date() + let endTimestamp = ProcessInfo.processInfo.systemUptime + let pong = PingResponse.Pong(start: start, end: end, latency: endTimestamp - startTimestamp) + + queue.async { + onResponse(.pong(pong)) + } + } + } + } + + func startAutomaticPing(every pingInterval: TimeInterval) { + socketMutableState.write { mutableState in + guard isResumed else { + // Defer out of lock. + defer { cancelAutomaticPing() } + return + } + + let item = DispatchWorkItem { [weak self] in + guard let self, self.isResumed else { return } + + self.sendPing(respondingOn: self.underlyingQueue) { response in + guard case .pong = response else { return } + + self.startAutomaticPing(every: pingInterval) + } + } + + mutableState.pingTimerItem = item + underlyingQueue.asyncAfter(deadline: .now() + pingInterval, execute: item) + } + } + + #if swift(>=5.8) + @available(macOS 13, iOS 16, tvOS 16, watchOS 9, *) + func startAutomaticPing(every duration: Duration) { + let interval = TimeInterval(duration.components.seconds) + (Double(duration.components.attoseconds) / 1e18) + startAutomaticPing(every: interval) + } + #endif + + func cancelAutomaticPing() { + socketMutableState.write { mutableState in + mutableState.pingTimerItem?.cancel() + mutableState.pingTimerItem = nil + } + } + + func didDisconnect(closeCode: URLSessionWebSocketTask.CloseCode, reason: Data?) { + dispatchPrecondition(condition: .onQueue(underlyingQueue)) + + cancelAutomaticPing() + socketMutableState.read { state in + for handler in state.handlers { + // Saved handler calls out to serializationQueue immediately, then to handler's queue. + handler.handler(.disconnected(closeCode: closeCode, reason: reason)) + } + } + } + + private func listen(to task: URLSessionWebSocketTask) { + // TODO: Do we care about the cycle while receiving? + task.receive { result in + switch result { + case let .success(message): + self.socketMutableState.read { state in + for handler in state.handlers { + // Saved handler calls out to serializationQueue immediately, then to handler's queue. + handler.handler(.receivedMessage(message)) + } + } + + self.listen(to: task) + case .failure: + // It doesn't seem like any relevant errors are received here, just incorrect garbage, like errors when + // the socket disconnects. + break + } + } + } + + @discardableResult + public func streamSerializer( + _ serializer: Serializer, + on queue: DispatchQueue = .main, + handler: @escaping (_ event: Event) -> Void + ) -> Self where Serializer: WebSocketMessageSerializer, Serializer.Failure == Error { + forIncomingEvent(on: queue) { incomingEvent in + let event: Event + switch incomingEvent { + case let .connected(`protocol`): + event = .init(socket: self, kind: .connected(protocol: `protocol`)) + case let .receivedMessage(message): + do { + let serializedMessage = try serializer.decode(message) + event = .init(socket: self, kind: .receivedMessage(serializedMessage)) + } catch { + event = .init(socket: self, kind: .serializerFailed(error)) + } + case let .disconnected(closeCode, reason): + event = .init(socket: self, kind: .disconnected(closeCode: closeCode, reason: reason)) + case let .completed(completion): + event = .init(socket: self, kind: .completed(completion)) + } + + queue.async { handler(event) } + } + } + + @discardableResult + public func streamDecodableEvents( + _ type: Value.Type = Value.self, + on queue: DispatchQueue = .main, + using decoder: DataDecoder = JSONDecoder(), + handler: @escaping (_ event: Event) -> Void + ) -> Self where Value: Decodable { + streamSerializer(DecodableWebSocketMessageDecoder(decoder: decoder), on: queue, handler: handler) + } + + @discardableResult + public func streamDecodable( + _ type: Value.Type = Value.self, + on queue: DispatchQueue = .main, + using decoder: DataDecoder = JSONDecoder(), + handler: @escaping (_ value: Value) -> Void + ) -> Self where Value: Decodable { + streamDecodableEvents(Value.self, on: queue) { event in + event.message.map(handler) + } + } + + @discardableResult + public func streamMessageEvents( + on queue: DispatchQueue = .main, + handler: @escaping (_ event: Event) -> Void + ) -> Self { + forIncomingEvent(on: queue) { incomingEvent in + let event: Event + switch incomingEvent { + case let .connected(`protocol`): + event = .init(socket: self, kind: .connected(protocol: `protocol`)) + case let .receivedMessage(message): + event = .init(socket: self, kind: .receivedMessage(message)) + case let .disconnected(closeCode, reason): + event = .init(socket: self, kind: .disconnected(closeCode: closeCode, reason: reason)) + case let .completed(completion): + event = .init(socket: self, kind: .completed(completion)) + } + + queue.async { handler(event) } + } + } + + @discardableResult + public func streamMessages( + on queue: DispatchQueue = .main, + handler: @escaping (_ message: URLSessionWebSocketTask.Message) -> Void + ) -> Self { + streamMessageEvents(on: queue) { event in + event.message.map(handler) + } + } + + func forIncomingEvent(on queue: DispatchQueue, handler: @escaping (IncomingEvent) -> Void) -> Self { + socketMutableState.write { state in + state.handlers.append((queue: queue, handler: { incomingEvent in + self.serializationQueue.async { + handler(incomingEvent) + } + })) + } + + appendResponseSerializer { + self.responseSerializerDidComplete { + self.serializationQueue.async { + handler(.completed(.init(request: self.request, + response: self.response, + metrics: self.metrics, + error: self.error))) + } + } + } + + return self + } + + public func send(_ message: URLSessionWebSocketTask.Message, + queue: DispatchQueue = .main, + completionHandler: @escaping (Result) -> Void) { + guard !(isCancelled || isFinished) else { return } + + guard let socket else { + // URLSessionWebSocketTask not created yet, enqueue the send. + socketMutableState.write { mutableState in + mutableState.enqueuedSends.append((message, queue, completionHandler)) + } + + return + } + + socket.send(message) { error in + queue.async { + completionHandler(Result(value: (), error: error)) + } + } + } +} + +@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) +public protocol WebSocketMessageSerializer { + associatedtype Output + associatedtype Failure: Error = Error + + func decode(_ message: URLSessionWebSocketTask.Message) throws -> Output +} + +@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) +extension WebSocketMessageSerializer { + public static func json( + decoding _: Value.Type = Value.self, + using decoder: JSONDecoder = JSONDecoder() + ) -> DecodableWebSocketMessageDecoder where Self == DecodableWebSocketMessageDecoder { + Self(decoder: decoder) + } + + static var passthrough: PassthroughWebSocketMessageDecoder { + PassthroughWebSocketMessageDecoder() + } +} + +@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) +struct PassthroughWebSocketMessageDecoder: WebSocketMessageSerializer { + public typealias Failure = Never + + public func decode(_ message: URLSessionWebSocketTask.Message) -> URLSessionWebSocketTask.Message { + message + } +} + +@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) +public struct DecodableWebSocketMessageDecoder: WebSocketMessageSerializer { + public enum Error: Swift.Error { + case decoding(Swift.Error) + case unknownMessage(description: String) + } + + public let decoder: DataDecoder + + public init(decoder: DataDecoder) { + self.decoder = decoder + } + + public func decode(_ message: URLSessionWebSocketTask.Message) throws -> Value { + let data: Data + switch message { + case let .data(messageData): + data = messageData + case let .string(string): + data = Data(string.utf8) + @unknown default: + throw Error.unknownMessage(description: String(describing: message)) + } + + do { + return try decoder.decode(Value.self, from: data) + } catch { + throw Error.decoding(error) + } + } +} + +#endif diff --git a/Pods/Alamofire/Source/Extensions/DispatchQueue+Alamofire.swift b/Pods/Alamofire/Source/Extensions/DispatchQueue+Alamofire.swift new file mode 100644 index 0000000..10cd273 --- /dev/null +++ b/Pods/Alamofire/Source/Extensions/DispatchQueue+Alamofire.swift @@ -0,0 +1,37 @@ +// +// DispatchQueue+Alamofire.swift +// +// Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +import Dispatch +import Foundation + +extension DispatchQueue { + /// Execute the provided closure after a `TimeInterval`. + /// + /// - Parameters: + /// - delay: `TimeInterval` to delay execution. + /// - closure: Closure to execute. + func after(_ delay: TimeInterval, execute closure: @escaping () -> Void) { + asyncAfter(deadline: .now() + delay, execute: closure) + } +} diff --git a/Pods/Alamofire/Source/Extensions/OperationQueue+Alamofire.swift b/Pods/Alamofire/Source/Extensions/OperationQueue+Alamofire.swift new file mode 100644 index 0000000..b06a0cc --- /dev/null +++ b/Pods/Alamofire/Source/Extensions/OperationQueue+Alamofire.swift @@ -0,0 +1,49 @@ +// +// OperationQueue+Alamofire.swift +// +// Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +import Foundation + +extension OperationQueue { + /// Creates an instance using the provided parameters. + /// + /// - Parameters: + /// - qualityOfService: `QualityOfService` to be applied to the queue. `.default` by default. + /// - maxConcurrentOperationCount: Maximum concurrent operations. + /// `OperationQueue.defaultMaxConcurrentOperationCount` by default. + /// - underlyingQueue: Underlying `DispatchQueue`. `nil` by default. + /// - name: Name for the queue. `nil` by default. + /// - startSuspended: Whether the queue starts suspended. `false` by default. + convenience init(qualityOfService: QualityOfService = .default, + maxConcurrentOperationCount: Int = OperationQueue.defaultMaxConcurrentOperationCount, + underlyingQueue: DispatchQueue? = nil, + name: String? = nil, + startSuspended: Bool = false) { + self.init() + self.qualityOfService = qualityOfService + self.maxConcurrentOperationCount = maxConcurrentOperationCount + self.underlyingQueue = underlyingQueue + self.name = name + isSuspended = startSuspended + } +} diff --git a/Pods/Alamofire/Source/Extensions/Result+Alamofire.swift b/Pods/Alamofire/Source/Extensions/Result+Alamofire.swift new file mode 100644 index 0000000..0a28a69 --- /dev/null +++ b/Pods/Alamofire/Source/Extensions/Result+Alamofire.swift @@ -0,0 +1,120 @@ +// +// Result+Alamofire.swift +// +// Copyright (c) 2019 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +import Foundation + +/// Default type of `Result` returned by Alamofire, with an `AFError` `Failure` type. +public typealias AFResult = Result + +// MARK: - Internal APIs + +extension Result { + /// Returns whether the instance is `.success`. + var isSuccess: Bool { + guard case .success = self else { return false } + return true + } + + /// Returns whether the instance is `.failure`. + var isFailure: Bool { + !isSuccess + } + + /// Returns the associated value if the result is a success, `nil` otherwise. + var success: Success? { + guard case let .success(value) = self else { return nil } + return value + } + + /// Returns the associated error value if the result is a failure, `nil` otherwise. + var failure: Failure? { + guard case let .failure(error) = self else { return nil } + return error + } + + /// Initializes a `Result` from value or error. Returns `.failure` if the error is non-nil, `.success` otherwise. + /// + /// - Parameters: + /// - value: A value. + /// - error: An `Error`. + init(value: Success, error: Failure?) { + if let error { + self = .failure(error) + } else { + self = .success(value) + } + } + + /// Evaluates the specified closure when the `Result` is a success, passing the unwrapped value as a parameter. + /// + /// Use the `tryMap` method with a closure that may throw an error. For example: + /// + /// let possibleData: Result = .success(Data(...)) + /// let possibleObject = possibleData.tryMap { + /// try JSONSerialization.jsonObject(with: $0) + /// } + /// + /// - parameter transform: A closure that takes the success value of the instance. + /// + /// - returns: A `Result` containing the result of the given closure. If this instance is a failure, returns the + /// same failure. + func tryMap(_ transform: (Success) throws -> NewSuccess) -> Result { + switch self { + case let .success(value): + do { + return try .success(transform(value)) + } catch { + return .failure(error) + } + case let .failure(error): + return .failure(error) + } + } + + /// Evaluates the specified closure when the `Result` is a failure, passing the unwrapped error as a parameter. + /// + /// Use the `tryMapError` function with a closure that may throw an error. For example: + /// + /// let possibleData: Result = .success(Data(...)) + /// let possibleObject = possibleData.tryMapError { + /// try someFailableFunction(taking: $0) + /// } + /// + /// - Parameter transform: A throwing closure that takes the error of the instance. + /// + /// - Returns: A `Result` instance containing the result of the transform. If this instance is a success, returns + /// the same success. + func tryMapError(_ transform: (Failure) throws -> NewFailure) -> Result { + switch self { + case let .failure(error): + do { + return try .failure(transform(error)) + } catch { + return .failure(error) + } + case let .success(value): + return .success(value) + } + } +} diff --git a/Pods/Alamofire/Source/Extensions/StringEncoding+Alamofire.swift b/Pods/Alamofire/Source/Extensions/StringEncoding+Alamofire.swift new file mode 100644 index 0000000..8fa6133 --- /dev/null +++ b/Pods/Alamofire/Source/Extensions/StringEncoding+Alamofire.swift @@ -0,0 +1,55 @@ +// +// StringEncoding+Alamofire.swift +// +// Copyright (c) 2020 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +import Foundation + +extension String.Encoding { + /// Creates an encoding from the IANA charset name. + /// + /// - Notes: These mappings match those [provided by CoreFoundation](https://opensource.apple.com/source/CF/CF-476.18/CFStringUtilities.c.auto.html) + /// + /// - Parameter name: IANA charset name. + init?(ianaCharsetName name: String) { + switch name.lowercased() { + case "utf-8": + self = .utf8 + case "iso-8859-1": + self = .isoLatin1 + case "unicode-1-1", "iso-10646-ucs-2", "utf-16": + self = .utf16 + case "utf-16be": + self = .utf16BigEndian + case "utf-16le": + self = .utf16LittleEndian + case "utf-32": + self = .utf32 + case "utf-32be": + self = .utf32BigEndian + case "utf-32le": + self = .utf32LittleEndian + default: + return nil + } + } +} diff --git a/Pods/Alamofire/Source/Extensions/URLRequest+Alamofire.swift b/Pods/Alamofire/Source/Extensions/URLRequest+Alamofire.swift new file mode 100644 index 0000000..ab72fb5 --- /dev/null +++ b/Pods/Alamofire/Source/Extensions/URLRequest+Alamofire.swift @@ -0,0 +1,39 @@ +// +// URLRequest+Alamofire.swift +// +// Copyright (c) 2019 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +import Foundation + +extension URLRequest { + /// Returns the `httpMethod` as Alamofire's `HTTPMethod` type. + public var method: HTTPMethod? { + get { httpMethod.map(HTTPMethod.init) } + set { httpMethod = newValue?.rawValue } + } + + public func validate() throws { + if method == .get, let bodyData = httpBody { + throw AFError.urlRequestValidationFailed(reason: .bodyDataInGETRequest(bodyData)) + } + } +} diff --git a/Pods/Alamofire/Source/Extensions/URLSessionConfiguration+Alamofire.swift b/Pods/Alamofire/Source/Extensions/URLSessionConfiguration+Alamofire.swift new file mode 100644 index 0000000..292a8fe --- /dev/null +++ b/Pods/Alamofire/Source/Extensions/URLSessionConfiguration+Alamofire.swift @@ -0,0 +1,46 @@ +// +// URLSessionConfiguration+Alamofire.swift +// +// Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +import Foundation + +extension URLSessionConfiguration: AlamofireExtended {} +extension AlamofireExtension where ExtendedType: URLSessionConfiguration { + /// Alamofire's default configuration. Same as `URLSessionConfiguration.default` but adds Alamofire default + /// `Accept-Language`, `Accept-Encoding`, and `User-Agent` headers. + public static var `default`: URLSessionConfiguration { + let configuration = URLSessionConfiguration.default + configuration.headers = .default + + return configuration + } + + /// `.ephemeral` configuration with Alamofire's default `Accept-Language`, `Accept-Encoding`, and `User-Agent` + /// headers. + public static var ephemeral: URLSessionConfiguration { + let configuration = URLSessionConfiguration.ephemeral + configuration.headers = .default + + return configuration + } +} diff --git a/Pods/Alamofire/Source/Features/AlamofireExtended.swift b/Pods/Alamofire/Source/Features/AlamofireExtended.swift new file mode 100644 index 0000000..280c6de --- /dev/null +++ b/Pods/Alamofire/Source/Features/AlamofireExtended.swift @@ -0,0 +1,61 @@ +// +// AlamofireExtended.swift +// +// Copyright (c) 2019 Alamofire Software Foundation (http://alamofire.org/) +// +// 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. +// + +/// Type that acts as a generic extension point for all `AlamofireExtended` types. +public struct AlamofireExtension { + /// Stores the type or meta-type of any extended type. + public private(set) var type: ExtendedType + + /// Create an instance from the provided value. + /// + /// - Parameter type: Instance being extended. + public init(_ type: ExtendedType) { + self.type = type + } +} + +/// Protocol describing the `af` extension points for Alamofire extended types. +public protocol AlamofireExtended { + /// Type being extended. + associatedtype ExtendedType + + /// Static Alamofire extension point. + static var af: AlamofireExtension.Type { get set } + /// Instance Alamofire extension point. + var af: AlamofireExtension { get set } +} + +extension AlamofireExtended { + /// Static Alamofire extension point. + public static var af: AlamofireExtension.Type { + get { AlamofireExtension.self } + set {} + } + + /// Instance Alamofire extension point. + public var af: AlamofireExtension { + get { AlamofireExtension(self) } + set {} + } +} diff --git a/Pods/Alamofire/Source/Features/AuthenticationInterceptor.swift b/Pods/Alamofire/Source/Features/AuthenticationInterceptor.swift new file mode 100644 index 0000000..7a33f10 --- /dev/null +++ b/Pods/Alamofire/Source/Features/AuthenticationInterceptor.swift @@ -0,0 +1,402 @@ +// +// AuthenticationInterceptor.swift +// +// Copyright (c) 2020 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +import Foundation + +/// Types adopting the `AuthenticationCredential` protocol can be used to authenticate `URLRequest`s. +/// +/// One common example of an `AuthenticationCredential` is an OAuth2 credential containing an access token used to +/// authenticate all requests on behalf of a user. The access token generally has an expiration window of 60 minutes +/// which will then require a refresh of the credential using the refresh token to generate a new access token. +public protocol AuthenticationCredential { + /// Whether the credential requires a refresh. This property should always return `true` when the credential is + /// expired. It is also wise to consider returning `true` when the credential will expire in several seconds or + /// minutes depending on the expiration window of the credential. + /// + /// For example, if the credential is valid for 60 minutes, then it would be wise to return `true` when the + /// credential is only valid for 5 minutes or less. That ensures the credential will not expire as it is passed + /// around backend services. + var requiresRefresh: Bool { get } +} + +// MARK: - + +/// Types adopting the `Authenticator` protocol can be used to authenticate `URLRequest`s with an +/// `AuthenticationCredential` as well as refresh the `AuthenticationCredential` when required. +public protocol Authenticator: AnyObject { + /// The type of credential associated with the `Authenticator` instance. + associatedtype Credential: AuthenticationCredential + + /// Applies the `Credential` to the `URLRequest`. + /// + /// In the case of OAuth2, the access token of the `Credential` would be added to the `URLRequest` as a Bearer + /// token to the `Authorization` header. + /// + /// - Parameters: + /// - credential: The `Credential`. + /// - urlRequest: The `URLRequest`. + func apply(_ credential: Credential, to urlRequest: inout URLRequest) + + /// Refreshes the `Credential` and executes the `completion` closure with the `Result` once complete. + /// + /// Refresh can be called in one of two ways. It can be called before the `Request` is actually executed due to + /// a `requiresRefresh` returning `true` during the adapt portion of the `Request` creation process. It can also + /// be triggered by a failed `Request` where the authentication server denied access due to an expired or + /// invalidated access token. + /// + /// In the case of OAuth2, this method would use the refresh token of the `Credential` to generate a new + /// `Credential` using the authentication service. Once complete, the `completion` closure should be called with + /// the new `Credential`, or the error that occurred. + /// + /// In general, if the refresh call fails with certain status codes from the authentication server (commonly a 401), + /// the refresh token in the `Credential` can no longer be used to generate a valid `Credential`. In these cases, + /// you will need to reauthenticate the user with their username / password. + /// + /// Please note, these are just general examples of common use cases. They are not meant to solve your specific + /// authentication server challenges. Please work with your authentication server team to ensure your + /// `Authenticator` logic matches their expectations. + /// + /// - Parameters: + /// - credential: The `Credential` to refresh. + /// - session: The `Session` requiring the refresh. + /// - completion: The closure to be executed once the refresh is complete. + func refresh(_ credential: Credential, for session: Session, completion: @escaping (Result) -> Void) + + /// Determines whether the `URLRequest` failed due to an authentication error based on the `HTTPURLResponse`. + /// + /// If the authentication server **CANNOT** invalidate credentials after they are issued, then simply return `false` + /// for this method. If the authentication server **CAN** invalidate credentials due to security breaches, then you + /// will need to work with your authentication server team to understand how to identify when this occurs. + /// + /// In the case of OAuth2, where an authentication server can invalidate credentials, you will need to inspect the + /// `HTTPURLResponse` or possibly the `Error` for when this occurs. This is commonly handled by the authentication + /// server returning a 401 status code and some additional header to indicate an OAuth2 failure occurred. + /// + /// It is very important to understand how your authentication server works to be able to implement this correctly. + /// For example, if your authentication server returns a 401 when an OAuth2 error occurs, and your downstream + /// service also returns a 401 when you are not authorized to perform that operation, how do you know which layer + /// of the backend returned you a 401? You do not want to trigger a refresh unless you know your authentication + /// server is actually the layer rejecting the request. Again, work with your authentication server team to understand + /// how to identify an OAuth2 401 error vs. a downstream 401 error to avoid endless refresh loops. + /// + /// - Parameters: + /// - urlRequest: The `URLRequest`. + /// - response: The `HTTPURLResponse`. + /// - error: The `Error`. + /// + /// - Returns: `true` if the `URLRequest` failed due to an authentication error, `false` otherwise. + func didRequest(_ urlRequest: URLRequest, with response: HTTPURLResponse, failDueToAuthenticationError error: Error) -> Bool + + /// Determines whether the `URLRequest` is authenticated with the `Credential`. + /// + /// If the authentication server **CANNOT** invalidate credentials after they are issued, then simply return `true` + /// for this method. If the authentication server **CAN** invalidate credentials due to security breaches, then + /// read on. + /// + /// When an authentication server can invalidate credentials, it means that you may have a non-expired credential + /// that appears to be valid, but will be rejected by the authentication server when used. Generally when this + /// happens, a number of requests are all sent when the application is foregrounded, and all of them will be + /// rejected by the authentication server in the order they are received. The first failed request will trigger a + /// refresh internally, which will update the credential, and then retry all the queued requests with the new + /// credential. However, it is possible that some of the original requests will not return from the authentication + /// server until the refresh has completed. This is where this method comes in. + /// + /// When the authentication server rejects a credential, we need to check to make sure we haven't refreshed the + /// credential while the request was in flight. If it has already refreshed, then we don't need to trigger an + /// additional refresh. If it hasn't refreshed, then we need to refresh. + /// + /// Now that it is understood how the result of this method is used in the refresh lifecyle, let's walk through how + /// to implement it. You should return `true` in this method if the `URLRequest` is authenticated in a way that + /// matches the values in the `Credential`. In the case of OAuth2, this would mean that the Bearer token in the + /// `Authorization` header of the `URLRequest` matches the access token in the `Credential`. If it matches, then we + /// know the `Credential` was used to authenticate the `URLRequest` and should return `true`. If the Bearer token + /// did not match the access token, then you should return `false`. + /// + /// - Parameters: + /// - urlRequest: The `URLRequest`. + /// - credential: The `Credential`. + /// + /// - Returns: `true` if the `URLRequest` is authenticated with the `Credential`, `false` otherwise. + func isRequest(_ urlRequest: URLRequest, authenticatedWith credential: Credential) -> Bool +} + +// MARK: - + +/// Represents various authentication failures that occur when using the `AuthenticationInterceptor`. All errors are +/// still vended from Alamofire as `AFError` types. The `AuthenticationError` instances will be embedded within +/// `AFError` `.requestAdaptationFailed` or `.requestRetryFailed` cases. +public enum AuthenticationError: Error { + /// The credential was missing so the request could not be authenticated. + case missingCredential + /// The credential was refreshed too many times within the `RefreshWindow`. + case excessiveRefresh +} + +// MARK: - + +/// The `AuthenticationInterceptor` class manages the queuing and threading complexity of authenticating requests. +/// It relies on an `Authenticator` type to handle the actual `URLRequest` authentication and `Credential` refresh. +public class AuthenticationInterceptor: RequestInterceptor where AuthenticatorType: Authenticator { + // MARK: Typealiases + + /// Type of credential used to authenticate requests. + public typealias Credential = AuthenticatorType.Credential + + // MARK: Helper Types + + /// Type that defines a time window used to identify excessive refresh calls. When enabled, prior to executing a + /// refresh, the `AuthenticationInterceptor` compares the timestamp history of previous refresh calls against the + /// `RefreshWindow`. If more refreshes have occurred within the refresh window than allowed, the refresh is + /// cancelled and an `AuthorizationError.excessiveRefresh` error is thrown. + public struct RefreshWindow { + /// `TimeInterval` defining the duration of the time window before the current time in which the number of + /// refresh attempts is compared against `maximumAttempts`. For example, if `interval` is 30 seconds, then the + /// `RefreshWindow` represents the past 30 seconds. If more attempts occurred in the past 30 seconds than + /// `maximumAttempts`, an `.excessiveRefresh` error will be thrown. + public let interval: TimeInterval + + /// Total refresh attempts allowed within `interval` before throwing an `.excessiveRefresh` error. + public let maximumAttempts: Int + + /// Creates a `RefreshWindow` instance from the specified `interval` and `maximumAttempts`. + /// + /// - Parameters: + /// - interval: `TimeInterval` defining the duration of the time window before the current time. + /// - maximumAttempts: The maximum attempts allowed within the `TimeInterval`. + public init(interval: TimeInterval = 30.0, maximumAttempts: Int = 5) { + self.interval = interval + self.maximumAttempts = maximumAttempts + } + } + + private struct AdaptOperation { + let urlRequest: URLRequest + let session: Session + let completion: (Result) -> Void + } + + private enum AdaptResult { + case adapt(Credential) + case doNotAdapt(AuthenticationError) + case adaptDeferred + } + + private struct MutableState { + var credential: Credential? + + var isRefreshing = false + var refreshTimestamps: [TimeInterval] = [] + var refreshWindow: RefreshWindow? + + var adaptOperations: [AdaptOperation] = [] + var requestsToRetry: [(RetryResult) -> Void] = [] + } + + // MARK: Properties + + /// The `Credential` used to authenticate requests. + public var credential: Credential? { + get { mutableState.credential } + set { mutableState.credential = newValue } + } + + let authenticator: AuthenticatorType + let queue = DispatchQueue(label: "org.alamofire.authentication.inspector") + + private let mutableState: Protected + + // MARK: Initialization + + /// Creates an `AuthenticationInterceptor` instance from the specified parameters. + /// + /// A `nil` `RefreshWindow` will result in the `AuthenticationInterceptor` not checking for excessive refresh calls. + /// It is recommended to always use a `RefreshWindow` to avoid endless refresh cycles. + /// + /// - Parameters: + /// - authenticator: The `Authenticator` type. + /// - credential: The `Credential` if it exists. `nil` by default. + /// - refreshWindow: The `RefreshWindow` used to identify excessive refresh calls. `RefreshWindow()` by default. + public init(authenticator: AuthenticatorType, + credential: Credential? = nil, + refreshWindow: RefreshWindow? = RefreshWindow()) { + self.authenticator = authenticator + mutableState = Protected(MutableState(credential: credential, refreshWindow: refreshWindow)) + } + + // MARK: Adapt + + public func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result) -> Void) { + let adaptResult: AdaptResult = mutableState.write { mutableState in + // Queue the adapt operation if a refresh is already in place. + guard !mutableState.isRefreshing else { + let operation = AdaptOperation(urlRequest: urlRequest, session: session, completion: completion) + mutableState.adaptOperations.append(operation) + return .adaptDeferred + } + + // Throw missing credential error is the credential is missing. + guard let credential = mutableState.credential else { + let error = AuthenticationError.missingCredential + return .doNotAdapt(error) + } + + // Queue the adapt operation and trigger refresh operation if credential requires refresh. + guard !credential.requiresRefresh else { + let operation = AdaptOperation(urlRequest: urlRequest, session: session, completion: completion) + mutableState.adaptOperations.append(operation) + refresh(credential, for: session, insideLock: &mutableState) + return .adaptDeferred + } + + return .adapt(credential) + } + + switch adaptResult { + case let .adapt(credential): + var authenticatedRequest = urlRequest + authenticator.apply(credential, to: &authenticatedRequest) + completion(.success(authenticatedRequest)) + + case let .doNotAdapt(adaptError): + completion(.failure(adaptError)) + + case .adaptDeferred: + // No-op: adapt operation captured during refresh. + break + } + } + + // MARK: Retry + + public func retry(_ request: Request, for session: Session, dueTo error: Error, completion: @escaping (RetryResult) -> Void) { + // Do not attempt retry if there was not an original request and response from the server. + guard let urlRequest = request.request, let response = request.response else { + completion(.doNotRetry) + return + } + + // Do not attempt retry unless the `Authenticator` verifies failure was due to authentication error (i.e. 401 status code). + guard authenticator.didRequest(urlRequest, with: response, failDueToAuthenticationError: error) else { + completion(.doNotRetry) + return + } + + // Do not attempt retry if there is no credential. + guard let credential else { + let error = AuthenticationError.missingCredential + completion(.doNotRetryWithError(error)) + return + } + + // Retry the request if the `Authenticator` verifies it was authenticated with a previous credential. + guard authenticator.isRequest(urlRequest, authenticatedWith: credential) else { + completion(.retry) + return + } + + mutableState.write { mutableState in + mutableState.requestsToRetry.append(completion) + + guard !mutableState.isRefreshing else { return } + + refresh(credential, for: session, insideLock: &mutableState) + } + } + + // MARK: Refresh + + private func refresh(_ credential: Credential, for session: Session, insideLock mutableState: inout MutableState) { + guard !isRefreshExcessive(insideLock: &mutableState) else { + let error = AuthenticationError.excessiveRefresh + handleRefreshFailure(error, insideLock: &mutableState) + return + } + + mutableState.refreshTimestamps.append(ProcessInfo.processInfo.systemUptime) + mutableState.isRefreshing = true + + // Dispatch to queue to hop out of the lock in case authenticator.refresh is implemented synchronously. + queue.async { + self.authenticator.refresh(credential, for: session) { result in + self.mutableState.write { mutableState in + switch result { + case let .success(credential): + self.handleRefreshSuccess(credential, insideLock: &mutableState) + case let .failure(error): + self.handleRefreshFailure(error, insideLock: &mutableState) + } + } + } + } + } + + private func isRefreshExcessive(insideLock mutableState: inout MutableState) -> Bool { + guard let refreshWindow = mutableState.refreshWindow else { return false } + + let refreshWindowMin = ProcessInfo.processInfo.systemUptime - refreshWindow.interval + + let refreshAttemptsWithinWindow = mutableState.refreshTimestamps.reduce(into: 0) { attempts, refreshTimestamp in + guard refreshWindowMin <= refreshTimestamp else { return } + attempts += 1 + } + + let isRefreshExcessive = refreshAttemptsWithinWindow >= refreshWindow.maximumAttempts + + return isRefreshExcessive + } + + private func handleRefreshSuccess(_ credential: Credential, insideLock mutableState: inout MutableState) { + mutableState.credential = credential + + let adaptOperations = mutableState.adaptOperations + let requestsToRetry = mutableState.requestsToRetry + + mutableState.adaptOperations.removeAll() + mutableState.requestsToRetry.removeAll() + + mutableState.isRefreshing = false + + // Dispatch to queue to hop out of the mutable state lock + queue.async { + adaptOperations.forEach { self.adapt($0.urlRequest, for: $0.session, completion: $0.completion) } + requestsToRetry.forEach { $0(.retry) } + } + } + + private func handleRefreshFailure(_ error: Error, insideLock mutableState: inout MutableState) { + let adaptOperations = mutableState.adaptOperations + let requestsToRetry = mutableState.requestsToRetry + + mutableState.adaptOperations.removeAll() + mutableState.requestsToRetry.removeAll() + + mutableState.isRefreshing = false + + // Dispatch to queue to hop out of the mutable state lock + queue.async { + adaptOperations.forEach { $0.completion(.failure(error)) } + requestsToRetry.forEach { $0(.doNotRetryWithError(error)) } + } + } +} diff --git a/Pods/Alamofire/Source/Features/CachedResponseHandler.swift b/Pods/Alamofire/Source/Features/CachedResponseHandler.swift new file mode 100644 index 0000000..1371b6e --- /dev/null +++ b/Pods/Alamofire/Source/Features/CachedResponseHandler.swift @@ -0,0 +1,107 @@ +// +// CachedResponseHandler.swift +// +// Copyright (c) 2019 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +import Foundation + +/// A type that handles whether the data task should store the HTTP response in the cache. +public protocol CachedResponseHandler { + /// Determines whether the HTTP response should be stored in the cache. + /// + /// The `completion` closure should be passed one of three possible options: + /// + /// 1. The cached response provided by the server (this is the most common use case). + /// 2. A modified version of the cached response (you may want to modify it in some way before caching). + /// 3. A `nil` value to prevent the cached response from being stored in the cache. + /// + /// - Parameters: + /// - task: The data task whose request resulted in the cached response. + /// - response: The cached response to potentially store in the cache. + /// - completion: The closure to execute containing cached response, a modified response, or `nil`. + func dataTask(_ task: URLSessionDataTask, + willCacheResponse response: CachedURLResponse, + completion: @escaping (CachedURLResponse?) -> Void) +} + +// MARK: - + +/// `ResponseCacher` is a convenience `CachedResponseHandler` making it easy to cache, not cache, or modify a cached +/// response. +public struct ResponseCacher { + /// Defines the behavior of the `ResponseCacher` type. + public enum Behavior { + /// Stores the cached response in the cache. + case cache + /// Prevents the cached response from being stored in the cache. + case doNotCache + /// Modifies the cached response before storing it in the cache. + case modify((URLSessionDataTask, CachedURLResponse) -> CachedURLResponse?) + } + + /// Returns a `ResponseCacher` with a `.cache` `Behavior`. + public static let cache = ResponseCacher(behavior: .cache) + /// Returns a `ResponseCacher` with a `.doNotCache` `Behavior`. + public static let doNotCache = ResponseCacher(behavior: .doNotCache) + + /// The `Behavior` of the `ResponseCacher`. + public let behavior: Behavior + + /// Creates a `ResponseCacher` instance from the `Behavior`. + /// + /// - Parameter behavior: The `Behavior`. + public init(behavior: Behavior) { + self.behavior = behavior + } +} + +extension ResponseCacher: CachedResponseHandler { + public func dataTask(_ task: URLSessionDataTask, + willCacheResponse response: CachedURLResponse, + completion: @escaping (CachedURLResponse?) -> Void) { + switch behavior { + case .cache: + completion(response) + case .doNotCache: + completion(nil) + case let .modify(closure): + let response = closure(task, response) + completion(response) + } + } +} + +extension CachedResponseHandler where Self == ResponseCacher { + /// Provides a `ResponseCacher` which caches the response, if allowed. Equivalent to `ResponseCacher.cache`. + public static var cache: ResponseCacher { .cache } + + /// Provides a `ResponseCacher` which does not cache the response. Equivalent to `ResponseCacher.doNotCache`. + public static var doNotCache: ResponseCacher { .doNotCache } + + /// Creates a `ResponseCacher` which modifies the proposed `CachedURLResponse` using the provided closure. + /// + /// - Parameter closure: Closure used to modify the `CachedURLResponse`. + /// - Returns: The `ResponseCacher`. + public static func modify(using closure: @escaping ((URLSessionDataTask, CachedURLResponse) -> CachedURLResponse?)) -> ResponseCacher { + ResponseCacher(behavior: .modify(closure)) + } +} diff --git a/Pods/Alamofire/Source/Features/Combine.swift b/Pods/Alamofire/Source/Features/Combine.swift new file mode 100644 index 0000000..79fce0d --- /dev/null +++ b/Pods/Alamofire/Source/Features/Combine.swift @@ -0,0 +1,652 @@ +// +// Combine.swift +// +// Copyright (c) 2020 Alamofire Software Foundation (http://alamofire.org/) +// +// 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. +// + +#if !((os(iOS) && (arch(i386) || arch(arm))) || os(Windows) || os(Linux) || os(Android)) + +import Combine +import Dispatch +import Foundation + +// MARK: - DataRequest / UploadRequest + +/// A Combine `Publisher` that publishes the `DataResponse` of the provided `DataRequest`. +@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) +public struct DataResponsePublisher: Publisher { + public typealias Output = DataResponse + public typealias Failure = Never + + private typealias Handler = (@escaping (_ response: DataResponse) -> Void) -> DataRequest + + private let request: DataRequest + private let responseHandler: Handler + + /// Creates an instance which will serialize responses using the provided `ResponseSerializer`. + /// + /// - Parameters: + /// - request: `DataRequest` for which to publish the response. + /// - queue: `DispatchQueue` on which the `DataResponse` value will be published. `.main` by default. + /// - serializer: `ResponseSerializer` used to produce the published `DataResponse`. + public init(_ request: DataRequest, queue: DispatchQueue, serializer: Serializer) + where Value == Serializer.SerializedObject { + self.request = request + responseHandler = { request.response(queue: queue, responseSerializer: serializer, completionHandler: $0) } + } + + /// Creates an instance which will serialize responses using the provided `DataResponseSerializerProtocol`. + /// + /// - Parameters: + /// - request: `DataRequest` for which to publish the response. + /// - queue: `DispatchQueue` on which the `DataResponse` value will be published. `.main` by default. + /// - serializer: `DataResponseSerializerProtocol` used to produce the published `DataResponse`. + public init(_ request: DataRequest, + queue: DispatchQueue, + serializer: Serializer) + where Value == Serializer.SerializedObject { + self.request = request + responseHandler = { request.response(queue: queue, responseSerializer: serializer, completionHandler: $0) } + } + + /// Publishes only the `Result` of the `DataResponse` value. + /// + /// - Returns: The `AnyPublisher` publishing the `Result` value. + public func result() -> AnyPublisher, Never> { + map(\.result).eraseToAnyPublisher() + } + + /// Publishes the `Result` of the `DataResponse` as a single `Value` or fail with the `AFError` instance. + /// + /// - Returns: The `AnyPublisher` publishing the stream. + public func value() -> AnyPublisher { + setFailureType(to: AFError.self).flatMap(\.result.publisher).eraseToAnyPublisher() + } + + public func receive(subscriber: S) where S: Subscriber, DataResponsePublisher.Failure == S.Failure, DataResponsePublisher.Output == S.Input { + subscriber.receive(subscription: Inner(request: request, + responseHandler: responseHandler, + downstream: subscriber)) + } + + private final class Inner: Subscription + where Downstream.Input == Output { + typealias Failure = Downstream.Failure + + private let downstream: Protected + private let request: DataRequest + private let responseHandler: Handler + + init(request: DataRequest, responseHandler: @escaping Handler, downstream: Downstream) { + self.request = request + self.responseHandler = responseHandler + self.downstream = Protected(downstream) + } + + func request(_ demand: Subscribers.Demand) { + assert(demand > 0) + + guard let downstream = downstream.read({ $0 }) else { return } + + self.downstream.write(nil) + responseHandler { response in + _ = downstream.receive(response) + downstream.receive(completion: .finished) + }.resume() + } + + func cancel() { + request.cancel() + downstream.write(nil) + } + } +} + +@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) +extension DataResponsePublisher where Value == Data? { + /// Creates an instance which publishes a `DataResponse` value without serialization. + @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) + public init(_ request: DataRequest, queue: DispatchQueue) { + self.request = request + responseHandler = { request.response(queue: queue, completionHandler: $0) } + } +} + +extension DataRequest { + /// Creates a `DataResponsePublisher` for this instance using the given `ResponseSerializer` and `DispatchQueue`. + /// + /// - Parameters: + /// - serializer: `ResponseSerializer` used to serialize response `Data`. + /// - queue: `DispatchQueue` on which the `DataResponse` will be published. `.main` by default. + /// + /// - Returns: The `DataResponsePublisher`. + @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) + public func publishResponse(using serializer: Serializer, on queue: DispatchQueue = .main) -> DataResponsePublisher + where Serializer.SerializedObject == T { + DataResponsePublisher(self, queue: queue, serializer: serializer) + } + + /// Creates a `DataResponsePublisher` for this instance and uses a `DataResponseSerializer` to serialize the + /// response. + /// + /// - Parameters: + /// - queue: `DispatchQueue` on which the `DataResponse` will be published. `.main` by default. + /// - preprocessor: `DataPreprocessor` which filters the `Data` before serialization. `PassthroughPreprocessor()` + /// by default. + /// - emptyResponseCodes: `Set` of HTTP status codes for which empty responses are allowed. `[204, 205]` by + /// default. + /// - emptyRequestMethods: `Set` of `HTTPMethod`s for which empty responses are allowed, regardless of + /// status code. `[.head]` by default. + /// - Returns: The `DataResponsePublisher`. + @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) + public func publishData(queue: DispatchQueue = .main, + preprocessor: DataPreprocessor = DataResponseSerializer.defaultDataPreprocessor, + emptyResponseCodes: Set = DataResponseSerializer.defaultEmptyResponseCodes, + emptyRequestMethods: Set = DataResponseSerializer.defaultEmptyRequestMethods) -> DataResponsePublisher { + publishResponse(using: DataResponseSerializer(dataPreprocessor: preprocessor, + emptyResponseCodes: emptyResponseCodes, + emptyRequestMethods: emptyRequestMethods), + on: queue) + } + + /// Creates a `DataResponsePublisher` for this instance and uses a `StringResponseSerializer` to serialize the + /// response. + /// + /// - Parameters: + /// - queue: `DispatchQueue` on which the `DataResponse` will be published. `.main` by default. + /// - preprocessor: `DataPreprocessor` which filters the `Data` before serialization. `PassthroughPreprocessor()` + /// by default. + /// - encoding: `String.Encoding` to parse the response. `nil` by default, in which case the encoding + /// will be determined by the server response, falling back to the default HTTP character + /// set, `ISO-8859-1`. + /// - emptyResponseCodes: `Set` of HTTP status codes for which empty responses are allowed. `[204, 205]` by + /// default. + /// - emptyRequestMethods: `Set` of `HTTPMethod`s for which empty responses are allowed, regardless of + /// status code. `[.head]` by default. + /// + /// - Returns: The `DataResponsePublisher`. + @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) + public func publishString(queue: DispatchQueue = .main, + preprocessor: DataPreprocessor = StringResponseSerializer.defaultDataPreprocessor, + encoding: String.Encoding? = nil, + emptyResponseCodes: Set = StringResponseSerializer.defaultEmptyResponseCodes, + emptyRequestMethods: Set = StringResponseSerializer.defaultEmptyRequestMethods) -> DataResponsePublisher { + publishResponse(using: StringResponseSerializer(dataPreprocessor: preprocessor, + encoding: encoding, + emptyResponseCodes: emptyResponseCodes, + emptyRequestMethods: emptyRequestMethods), + on: queue) + } + + @_disfavoredOverload + @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) + @available(*, deprecated, message: "Renamed publishDecodable(type:queue:preprocessor:decoder:emptyResponseCodes:emptyRequestMethods).") + public func publishDecodable(type: T.Type = T.self, + queue: DispatchQueue = .main, + preprocessor: DataPreprocessor = DecodableResponseSerializer.defaultDataPreprocessor, + decoder: DataDecoder = JSONDecoder(), + emptyResponseCodes: Set = DecodableResponseSerializer.defaultEmptyResponseCodes, + emptyResponseMethods: Set = DecodableResponseSerializer.defaultEmptyRequestMethods) -> DataResponsePublisher { + publishResponse(using: DecodableResponseSerializer(dataPreprocessor: preprocessor, + decoder: decoder, + emptyResponseCodes: emptyResponseCodes, + emptyRequestMethods: emptyResponseMethods), + on: queue) + } + + /// Creates a `DataResponsePublisher` for this instance and uses a `DecodableResponseSerializer` to serialize the + /// response. + /// + /// - Parameters: + /// - type: `Decodable` type to which to decode response `Data`. Inferred from the context by + /// default. + /// - queue: `DispatchQueue` on which the `DataResponse` will be published. `.main` by default. + /// - preprocessor: `DataPreprocessor` which filters the `Data` before serialization. + /// `PassthroughPreprocessor()` by default. + /// - decoder: `DataDecoder` instance used to decode response `Data`. `JSONDecoder()` by default. + /// - emptyResponseCodes: `Set` of HTTP status codes for which empty responses are allowed. `[204, 205]` by + /// default. + /// - emptyRequestMethods: `Set` of `HTTPMethod`s for which empty responses are allowed, regardless of + /// status code. `[.head]` by default. + /// + /// - Returns: The `DataResponsePublisher`. + @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) + public func publishDecodable(type: T.Type = T.self, + queue: DispatchQueue = .main, + preprocessor: DataPreprocessor = DecodableResponseSerializer.defaultDataPreprocessor, + decoder: DataDecoder = JSONDecoder(), + emptyResponseCodes: Set = DecodableResponseSerializer.defaultEmptyResponseCodes, + emptyRequestMethods: Set = DecodableResponseSerializer.defaultEmptyRequestMethods) -> DataResponsePublisher { + publishResponse(using: DecodableResponseSerializer(dataPreprocessor: preprocessor, + decoder: decoder, + emptyResponseCodes: emptyResponseCodes, + emptyRequestMethods: emptyRequestMethods), + on: queue) + } + + /// Creates a `DataResponsePublisher` for this instance which does not serialize the response before publishing. + /// + /// - queue: `DispatchQueue` on which the `DataResponse` will be published. `.main` by default. + /// + /// - Returns: The `DataResponsePublisher`. + @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) + public func publishUnserialized(queue: DispatchQueue = .main) -> DataResponsePublisher { + DataResponsePublisher(self, queue: queue) + } +} + +// A Combine `Publisher` that publishes a sequence of `Stream` values received by the provided `DataStreamRequest`. +@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) +public struct DataStreamPublisher: Publisher { + public typealias Output = DataStreamRequest.Stream + public typealias Failure = Never + + private typealias Handler = (@escaping DataStreamRequest.Handler) -> DataStreamRequest + + private let request: DataStreamRequest + private let streamHandler: Handler + + /// Creates an instance which will serialize responses using the provided `DataStreamSerializer`. + /// + /// - Parameters: + /// - request: `DataStreamRequest` for which to publish the response. + /// - queue: `DispatchQueue` on which the `Stream` values will be published. `.main` by + /// default. + /// - serializer: `DataStreamSerializer` used to produce the published `Stream` values. + public init(_ request: DataStreamRequest, queue: DispatchQueue, serializer: Serializer) + where Value == Serializer.SerializedObject { + self.request = request + streamHandler = { request.responseStream(using: serializer, on: queue, stream: $0) } + } + + /// Publishes only the `Result` of the `DataStreamRequest.Stream`'s `Event`s. + /// + /// - Returns: The `AnyPublisher` publishing the `Result` value. + public func result() -> AnyPublisher, Never> { + compactMap { stream in + switch stream.event { + case let .stream(result): + return result + // If the stream has completed with an error, send the error value downstream as a `.failure`. + case let .complete(completion): + return completion.error.map(Result.failure) + } + } + .eraseToAnyPublisher() + } + + /// Publishes the streamed values of the `DataStreamRequest.Stream` as a sequence of `Value` or fail with the + /// `AFError` instance. + /// + /// - Returns: The `AnyPublisher` publishing the stream. + public func value() -> AnyPublisher { + result().setFailureType(to: AFError.self).flatMap(\.publisher).eraseToAnyPublisher() + } + + public func receive(subscriber: S) where S: Subscriber, DataStreamPublisher.Failure == S.Failure, DataStreamPublisher.Output == S.Input { + subscriber.receive(subscription: Inner(request: request, + streamHandler: streamHandler, + downstream: subscriber)) + } + + private final class Inner: Subscription + where Downstream.Input == Output { + typealias Failure = Downstream.Failure + + private let downstream: Protected + private let request: DataStreamRequest + private let streamHandler: Handler + + init(request: DataStreamRequest, streamHandler: @escaping Handler, downstream: Downstream) { + self.request = request + self.streamHandler = streamHandler + self.downstream = Protected(downstream) + } + + func request(_ demand: Subscribers.Demand) { + assert(demand > 0) + + guard let downstream = downstream.read({ $0 }) else { return } + + self.downstream.write(nil) + streamHandler { stream in + _ = downstream.receive(stream) + if case .complete = stream.event { + downstream.receive(completion: .finished) + } + }.resume() + } + + func cancel() { + request.cancel() + downstream.write(nil) + } + } +} + +extension DataStreamRequest { + /// Creates a `DataStreamPublisher` for this instance using the given `DataStreamSerializer` and `DispatchQueue`. + /// + /// - Parameters: + /// - serializer: `DataStreamSerializer` used to serialize the streamed `Data`. + /// - queue: `DispatchQueue` on which the `DataRequest.Stream` values will be published. `.main` by default. + /// - Returns: The `DataStreamPublisher`. + @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) + public func publishStream(using serializer: Serializer, + on queue: DispatchQueue = .main) -> DataStreamPublisher { + DataStreamPublisher(self, queue: queue, serializer: serializer) + } + + /// Creates a `DataStreamPublisher` for this instance which uses a `PassthroughStreamSerializer` to stream `Data` + /// unserialized. + /// + /// - Parameters: + /// - queue: `DispatchQueue` on which the `DataRequest.Stream` values will be published. `.main` by default. + /// - Returns: The `DataStreamPublisher`. + @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) + public func publishData(queue: DispatchQueue = .main) -> DataStreamPublisher { + publishStream(using: PassthroughStreamSerializer(), on: queue) + } + + /// Creates a `DataStreamPublisher` for this instance which uses a `StringStreamSerializer` to serialize stream + /// `Data` values into `String` values. + /// + /// - Parameters: + /// - queue: `DispatchQueue` on which the `DataRequest.Stream` values will be published. `.main` by default. + /// - Returns: The `DataStreamPublisher`. + @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) + public func publishString(queue: DispatchQueue = .main) -> DataStreamPublisher { + publishStream(using: StringStreamSerializer(), on: queue) + } + + /// Creates a `DataStreamPublisher` for this instance which uses a `DecodableStreamSerializer` with the provided + /// parameters to serialize stream `Data` values into the provided type. + /// + /// - Parameters: + /// - type: `Decodable` type to which to decode stream `Data`. Inferred from the context by default. + /// - queue: `DispatchQueue` on which the `DataRequest.Stream` values will be published. `.main` by default. + /// - decoder: `DataDecoder` instance used to decode stream `Data`. `JSONDecoder()` by default. + /// - preprocessor: `DataPreprocessor` which filters incoming stream `Data` before serialization. + /// `PassthroughPreprocessor()` by default. + /// - Returns: The `DataStreamPublisher`. + @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) + public func publishDecodable(type: T.Type = T.self, + queue: DispatchQueue = .main, + decoder: DataDecoder = JSONDecoder(), + preprocessor: DataPreprocessor = PassthroughPreprocessor()) -> DataStreamPublisher { + publishStream(using: DecodableStreamSerializer(decoder: decoder, + dataPreprocessor: preprocessor), + on: queue) + } +} + +/// A Combine `Publisher` that publishes the `DownloadResponse` of the provided `DownloadRequest`. +@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) +public struct DownloadResponsePublisher: Publisher { + public typealias Output = DownloadResponse + public typealias Failure = Never + + private typealias Handler = (@escaping (_ response: DownloadResponse) -> Void) -> DownloadRequest + + private let request: DownloadRequest + private let responseHandler: Handler + + /// Creates an instance which will serialize responses using the provided `ResponseSerializer`. + /// + /// - Parameters: + /// - request: `DownloadRequest` for which to publish the response. + /// - queue: `DispatchQueue` on which the `DownloadResponse` value will be published. `.main` by default. + /// - serializer: `ResponseSerializer` used to produce the published `DownloadResponse`. + public init(_ request: DownloadRequest, queue: DispatchQueue, serializer: Serializer) + where Value == Serializer.SerializedObject { + self.request = request + responseHandler = { request.response(queue: queue, responseSerializer: serializer, completionHandler: $0) } + } + + /// Creates an instance which will serialize responses using the provided `DownloadResponseSerializerProtocol` value. + /// + /// - Parameters: + /// - request: `DownloadRequest` for which to publish the response. + /// - queue: `DispatchQueue` on which the `DataResponse` value will be published. `.main` by default. + /// - serializer: `DownloadResponseSerializerProtocol` used to produce the published `DownloadResponse`. + @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) + public init(_ request: DownloadRequest, + queue: DispatchQueue, + serializer: Serializer) + where Value == Serializer.SerializedObject { + self.request = request + responseHandler = { request.response(queue: queue, responseSerializer: serializer, completionHandler: $0) } + } + + /// Publishes only the `Result` of the `DownloadResponse` value. + /// + /// - Returns: The `AnyPublisher` publishing the `Result` value. + public func result() -> AnyPublisher, Never> { + map(\.result).eraseToAnyPublisher() + } + + /// Publishes the `Result` of the `DownloadResponse` as a single `Value` or fail with the `AFError` instance. + /// + /// - Returns: The `AnyPublisher` publishing the stream. + public func value() -> AnyPublisher { + setFailureType(to: AFError.self).flatMap(\.result.publisher).eraseToAnyPublisher() + } + + public func receive(subscriber: S) where S: Subscriber, DownloadResponsePublisher.Failure == S.Failure, DownloadResponsePublisher.Output == S.Input { + subscriber.receive(subscription: Inner(request: request, + responseHandler: responseHandler, + downstream: subscriber)) + } + + private final class Inner: Subscription + where Downstream.Input == Output { + typealias Failure = Downstream.Failure + + private let downstream: Protected + private let request: DownloadRequest + private let responseHandler: Handler + + init(request: DownloadRequest, responseHandler: @escaping Handler, downstream: Downstream) { + self.request = request + self.responseHandler = responseHandler + self.downstream = Protected(downstream) + } + + func request(_ demand: Subscribers.Demand) { + assert(demand > 0) + + guard let downstream = downstream.read({ $0 }) else { return } + + self.downstream.write(nil) + responseHandler { response in + _ = downstream.receive(response) + downstream.receive(completion: .finished) + }.resume() + } + + func cancel() { + request.cancel() + downstream.write(nil) + } + } +} + +extension DownloadRequest { + /// Creates a `DownloadResponsePublisher` for this instance using the given `ResponseSerializer` and `DispatchQueue`. + /// + /// - Parameters: + /// - serializer: `ResponseSerializer` used to serialize the response `Data` from disk. + /// - queue: `DispatchQueue` on which the `DownloadResponse` will be published.`.main` by default. + /// + /// - Returns: The `DownloadResponsePublisher`. + @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) + public func publishResponse(using serializer: Serializer, on queue: DispatchQueue = .main) -> DownloadResponsePublisher + where Serializer.SerializedObject == T { + DownloadResponsePublisher(self, queue: queue, serializer: serializer) + } + + /// Creates a `DownloadResponsePublisher` for this instance using the given `DownloadResponseSerializerProtocol` and + /// `DispatchQueue`. + /// + /// - Parameters: + /// - serializer: `DownloadResponseSerializer` used to serialize the response `Data` from disk. + /// - queue: `DispatchQueue` on which the `DownloadResponse` will be published.`.main` by default. + /// + /// - Returns: The `DownloadResponsePublisher`. + @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) + public func publishResponse(using serializer: Serializer, on queue: DispatchQueue = .main) -> DownloadResponsePublisher + where Serializer.SerializedObject == T { + DownloadResponsePublisher(self, queue: queue, serializer: serializer) + } + + /// Creates a `DownloadResponsePublisher` for this instance and uses a `URLResponseSerializer` to serialize the + /// response. + /// + /// - Parameter queue: `DispatchQueue` on which the `DownloadResponse` will be published. `.main` by default. + /// + /// - Returns: The `DownloadResponsePublisher`. + @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) + public func publishURL(queue: DispatchQueue = .main) -> DownloadResponsePublisher { + publishResponse(using: URLResponseSerializer(), on: queue) + } + + /// Creates a `DownloadResponsePublisher` for this instance and uses a `DataResponseSerializer` to serialize the + /// response. + /// + /// - Parameters: + /// - queue: `DispatchQueue` on which the `DownloadResponse` will be published. `.main` by default. + /// - preprocessor: `DataPreprocessor` which filters the `Data` before serialization. `PassthroughPreprocessor()` + /// by default. + /// - emptyResponseCodes: `Set` of HTTP status codes for which empty responses are allowed. `[204, 205]` by + /// default. + /// - emptyRequestMethods: `Set` of `HTTPMethod`s for which empty responses are allowed, regardless of + /// status code. `[.head]` by default. + /// + /// - Returns: The `DownloadResponsePublisher`. + @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) + public func publishData(queue: DispatchQueue = .main, + preprocessor: DataPreprocessor = DataResponseSerializer.defaultDataPreprocessor, + emptyResponseCodes: Set = DataResponseSerializer.defaultEmptyResponseCodes, + emptyRequestMethods: Set = DataResponseSerializer.defaultEmptyRequestMethods) -> DownloadResponsePublisher { + publishResponse(using: DataResponseSerializer(dataPreprocessor: preprocessor, + emptyResponseCodes: emptyResponseCodes, + emptyRequestMethods: emptyRequestMethods), + on: queue) + } + + /// Creates a `DownloadResponsePublisher` for this instance and uses a `StringResponseSerializer` to serialize the + /// response. + /// + /// - Parameters: + /// - queue: `DispatchQueue` on which the `DataResponse` will be published. `.main` by default. + /// - preprocessor: `DataPreprocessor` which filters the `Data` before serialization. `PassthroughPreprocessor()` + /// by default. + /// - encoding: `String.Encoding` to parse the response. `nil` by default, in which case the encoding + /// will be determined by the server response, falling back to the default HTTP character + /// set, `ISO-8859-1`. + /// - emptyResponseCodes: `Set` of HTTP status codes for which empty responses are allowed. `[204, 205]` by + /// default. + /// - emptyRequestMethods: `Set` of `HTTPMethod`s for which empty responses are allowed, regardless of + /// status code. `[.head]` by default. + /// + /// - Returns: The `DownloadResponsePublisher`. + @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) + public func publishString(queue: DispatchQueue = .main, + preprocessor: DataPreprocessor = StringResponseSerializer.defaultDataPreprocessor, + encoding: String.Encoding? = nil, + emptyResponseCodes: Set = StringResponseSerializer.defaultEmptyResponseCodes, + emptyRequestMethods: Set = StringResponseSerializer.defaultEmptyRequestMethods) -> DownloadResponsePublisher { + publishResponse(using: StringResponseSerializer(dataPreprocessor: preprocessor, + encoding: encoding, + emptyResponseCodes: emptyResponseCodes, + emptyRequestMethods: emptyRequestMethods), + on: queue) + } + + @_disfavoredOverload + @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) + @available(*, deprecated, message: "Renamed publishDecodable(type:queue:preprocessor:decoder:emptyResponseCodes:emptyRequestMethods).") + public func publishDecodable(type: T.Type = T.self, + queue: DispatchQueue = .main, + preprocessor: DataPreprocessor = DecodableResponseSerializer.defaultDataPreprocessor, + decoder: DataDecoder = JSONDecoder(), + emptyResponseCodes: Set = DecodableResponseSerializer.defaultEmptyResponseCodes, + emptyResponseMethods: Set = DecodableResponseSerializer.defaultEmptyRequestMethods) -> DownloadResponsePublisher { + publishResponse(using: DecodableResponseSerializer(dataPreprocessor: preprocessor, + decoder: decoder, + emptyResponseCodes: emptyResponseCodes, + emptyRequestMethods: emptyResponseMethods), + on: queue) + } + + /// Creates a `DownloadResponsePublisher` for this instance and uses a `DecodableResponseSerializer` to serialize + /// the response. + /// + /// - Parameters: + /// - type: `Decodable` type to which to decode response `Data`. Inferred from the context by default. + /// - queue: `DispatchQueue` on which the `DataResponse` will be published. `.main` by default. + /// - preprocessor: `DataPreprocessor` which filters the `Data` before serialization. + /// `PassthroughPreprocessor()` by default. + /// - decoder: `DataDecoder` instance used to decode response `Data`. `JSONDecoder()` by default. + /// - emptyResponseCodes: `Set` of HTTP status codes for which empty responses are allowed. `[204, 205]` by + /// default. + /// - emptyRequestMethods: `Set` of `HTTPMethod`s for which empty responses are allowed, regardless + /// of status code. `[.head]` by default. + /// + /// - Returns: The `DownloadResponsePublisher`. + @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) + public func publishDecodable(type: T.Type = T.self, + queue: DispatchQueue = .main, + preprocessor: DataPreprocessor = DecodableResponseSerializer.defaultDataPreprocessor, + decoder: DataDecoder = JSONDecoder(), + emptyResponseCodes: Set = DecodableResponseSerializer.defaultEmptyResponseCodes, + emptyRequestMethods: Set = DecodableResponseSerializer.defaultEmptyRequestMethods) -> DownloadResponsePublisher { + publishResponse(using: DecodableResponseSerializer(dataPreprocessor: preprocessor, + decoder: decoder, + emptyResponseCodes: emptyResponseCodes, + emptyRequestMethods: emptyRequestMethods), + on: queue) + } +} + +@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) +extension DownloadResponsePublisher where Value == URL? { + /// Creates an instance which publishes a `DownloadResponse` value without serialization. + @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) + public init(_ request: DownloadRequest, queue: DispatchQueue) { + self.request = request + responseHandler = { request.response(queue: queue, completionHandler: $0) } + } +} + +extension DownloadRequest { + /// Creates a `DownloadResponsePublisher` for this instance which does not serialize the response before publishing. + /// + /// - Parameter queue: `DispatchQueue` on which the `DownloadResponse` will be published. `.main` by default. + /// + /// - Returns: The `DownloadResponsePublisher`. + @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) + public func publishUnserialized(on queue: DispatchQueue = .main) -> DownloadResponsePublisher { + DownloadResponsePublisher(self, queue: queue) + } +} + +#endif diff --git a/Pods/Alamofire/Source/Features/Concurrency.swift b/Pods/Alamofire/Source/Features/Concurrency.swift new file mode 100644 index 0000000..42eff7f --- /dev/null +++ b/Pods/Alamofire/Source/Features/Concurrency.swift @@ -0,0 +1,962 @@ +// +// Concurrency.swift +// +// Copyright (c) 2021 Alamofire Software Foundation (http://alamofire.org/) +// +// 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. +// + +#if canImport(_Concurrency) + +import Foundation + +// MARK: - Request Event Streams + +@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) +extension Request { + /// Creates a `StreamOf` for the instance's upload progress. + /// + /// - Parameter bufferingPolicy: `BufferingPolicy` that determines the stream's buffering behavior.`.unbounded` by default. + /// + /// - Returns: The `StreamOf`. + public func uploadProgress(bufferingPolicy: StreamOf.BufferingPolicy = .unbounded) -> StreamOf { + stream(bufferingPolicy: bufferingPolicy) { [unowned self] continuation in + uploadProgress(queue: underlyingQueue) { progress in + continuation.yield(progress) + } + } + } + + /// Creates a `StreamOf` for the instance's download progress. + /// + /// - Parameter bufferingPolicy: `BufferingPolicy` that determines the stream's buffering behavior.`.unbounded` by default. + /// + /// - Returns: The `StreamOf`. + public func downloadProgress(bufferingPolicy: StreamOf.BufferingPolicy = .unbounded) -> StreamOf { + stream(bufferingPolicy: bufferingPolicy) { [unowned self] continuation in + downloadProgress(queue: underlyingQueue) { progress in + continuation.yield(progress) + } + } + } + + /// Creates a `StreamOf` for the `URLRequest`s produced for the instance. + /// + /// - Parameter bufferingPolicy: `BufferingPolicy` that determines the stream's buffering behavior.`.unbounded` by default. + /// + /// - Returns: The `StreamOf`. + public func urlRequests(bufferingPolicy: StreamOf.BufferingPolicy = .unbounded) -> StreamOf { + stream(bufferingPolicy: bufferingPolicy) { [unowned self] continuation in + onURLRequestCreation(on: underlyingQueue) { request in + continuation.yield(request) + } + } + } + + /// Creates a `StreamOf` for the `URLSessionTask`s produced for the instance. + /// + /// - Parameter bufferingPolicy: `BufferingPolicy` that determines the stream's buffering behavior.`.unbounded` by default. + /// + /// - Returns: The `StreamOf`. + public func urlSessionTasks(bufferingPolicy: StreamOf.BufferingPolicy = .unbounded) -> StreamOf { + stream(bufferingPolicy: bufferingPolicy) { [unowned self] continuation in + onURLSessionTaskCreation(on: underlyingQueue) { task in + continuation.yield(task) + } + } + } + + /// Creates a `StreamOf` for the cURL descriptions produced for the instance. + /// + /// - Parameter bufferingPolicy: `BufferingPolicy` that determines the stream's buffering behavior.`.unbounded` by default. + /// + /// - Returns: The `StreamOf`. + public func cURLDescriptions(bufferingPolicy: StreamOf.BufferingPolicy = .unbounded) -> StreamOf { + stream(bufferingPolicy: bufferingPolicy) { [unowned self] continuation in + cURLDescription(on: underlyingQueue) { description in + continuation.yield(description) + } + } + } + + fileprivate func stream(of type: T.Type = T.self, + bufferingPolicy: StreamOf.BufferingPolicy = .unbounded, + yielder: @escaping (StreamOf.Continuation) -> Void) -> StreamOf { + StreamOf(bufferingPolicy: bufferingPolicy) { [unowned self] continuation in + yielder(continuation) + // Must come after serializers run in order to catch retry progress. + onFinish { + continuation.finish() + } + } + } +} + +// MARK: - DataTask + +/// Value used to `await` a `DataResponse` and associated values. +@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) +public struct DataTask { + /// `DataResponse` produced by the `DataRequest` and its response handler. + public var response: DataResponse { + get async { + if shouldAutomaticallyCancel { + return await withTaskCancellationHandler { + await task.value + } onCancel: { + cancel() + } + } else { + return await task.value + } + } + } + + /// `Result` of any response serialization performed for the `response`. + public var result: Result { + get async { await response.result } + } + + /// `Value` returned by the `response`. + public var value: Value { + get async throws { + try await result.get() + } + } + + private let request: DataRequest + private let task: Task, Never> + private let shouldAutomaticallyCancel: Bool + + fileprivate init(request: DataRequest, task: Task, Never>, shouldAutomaticallyCancel: Bool) { + self.request = request + self.task = task + self.shouldAutomaticallyCancel = shouldAutomaticallyCancel + } + + /// Cancel the underlying `DataRequest` and `Task`. + public func cancel() { + task.cancel() + } + + /// Resume the underlying `DataRequest`. + public func resume() { + request.resume() + } + + /// Suspend the underlying `DataRequest`. + public func suspend() { + request.suspend() + } +} + +@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) +extension DataRequest { + /// Creates a `StreamOf` for the instance's responses. + /// + /// - Parameter bufferingPolicy: `BufferingPolicy` that determines the stream's buffering behavior.`.unbounded` by default. + /// + /// - Returns: The `StreamOf`. + public func httpResponses(bufferingPolicy: StreamOf.BufferingPolicy = .unbounded) -> StreamOf { + stream(bufferingPolicy: bufferingPolicy) { [unowned self] continuation in + onHTTPResponse(on: underlyingQueue) { response in + continuation.yield(response) + } + } + } + + /// Sets an async closure returning a `Request.ResponseDisposition`, called whenever the `DataRequest` produces an + /// `HTTPURLResponse`. + /// + /// - Note: Most requests will only produce a single response for each outgoing attempt (initial + retries). + /// However, some types of response may trigger multiple `HTTPURLResponse`s, such as multipart streams, + /// where responses after the first will contain the part headers. + /// + /// - Parameters: + /// - handler: Async closure executed when a new `HTTPURLResponse` is received and returning a + /// `ResponseDisposition` value. This value determines whether to continue the request or cancel it as + /// if `cancel()` had been called on the instance. Note, this closure is called on an arbitrary thread, + /// so any synchronous calls in it will execute in that context. + /// + /// - Returns: The instance. + @_disfavoredOverload + @discardableResult + public func onHTTPResponse( + perform handler: @escaping @Sendable (_ response: HTTPURLResponse) async -> ResponseDisposition + ) -> Self { + onHTTPResponse(on: underlyingQueue) { response, completionHandler in + Task { + let disposition = await handler(response) + completionHandler(disposition) + } + } + + return self + } + + /// Sets an async closure called whenever the `DataRequest` produces an `HTTPURLResponse`. + /// + /// - Note: Most requests will only produce a single response for each outgoing attempt (initial + retries). + /// However, some types of response may trigger multiple `HTTPURLResponse`s, such as multipart streams, + /// where responses after the first will contain the part headers. + /// + /// - Parameters: + /// - handler: Async closure executed when a new `HTTPURLResponse` is received. Note, this closure is called on an + /// arbitrary thread, so any synchronous calls in it will execute in that context. + /// + /// - Returns: The instance. + @discardableResult + public func onHTTPResponse(perform handler: @escaping @Sendable (_ response: HTTPURLResponse) async -> Void) -> Self { + onHTTPResponse { response in + await handler(response) + return .allow + } + + return self + } + + /// Creates a `DataTask` to `await` a `Data` value. + /// + /// - Parameters: + /// - shouldAutomaticallyCancel: `Bool` determining whether or not the request should be cancelled when the + /// enclosing async context is cancelled. Only applies to `DataTask`'s async + /// properties. `true` by default. + /// - dataPreprocessor: `DataPreprocessor` which processes the received `Data` before completion. + /// - emptyResponseCodes: HTTP response codes for which empty responses are allowed. `[204, 205]` by default. + /// - emptyRequestMethods: `HTTPMethod`s for which empty responses are always valid. `[.head]` by default. + /// + /// - Returns: The `DataTask`. + public func serializingData(automaticallyCancelling shouldAutomaticallyCancel: Bool = true, + dataPreprocessor: DataPreprocessor = DataResponseSerializer.defaultDataPreprocessor, + emptyResponseCodes: Set = DataResponseSerializer.defaultEmptyResponseCodes, + emptyRequestMethods: Set = DataResponseSerializer.defaultEmptyRequestMethods) -> DataTask { + serializingResponse(using: DataResponseSerializer(dataPreprocessor: dataPreprocessor, + emptyResponseCodes: emptyResponseCodes, + emptyRequestMethods: emptyRequestMethods), + automaticallyCancelling: shouldAutomaticallyCancel) + } + + /// Creates a `DataTask` to `await` serialization of a `Decodable` value. + /// + /// - Parameters: + /// - type: `Decodable` type to decode from response data. + /// - shouldAutomaticallyCancel: `Bool` determining whether or not the request should be cancelled when the + /// enclosing async context is cancelled. Only applies to `DataTask`'s async + /// properties. `true` by default. + /// - dataPreprocessor: `DataPreprocessor` which processes the received `Data` before calling the serializer. + /// `PassthroughPreprocessor()` by default. + /// - decoder: `DataDecoder` to use to decode the response. `JSONDecoder()` by default. + /// - emptyResponseCodes: HTTP status codes for which empty responses are always valid. `[204, 205]` by default. + /// - emptyRequestMethods: `HTTPMethod`s for which empty responses are always valid. `[.head]` by default. + /// + /// - Returns: The `DataTask`. + public func serializingDecodable(_ type: Value.Type = Value.self, + automaticallyCancelling shouldAutomaticallyCancel: Bool = true, + dataPreprocessor: DataPreprocessor = DecodableResponseSerializer.defaultDataPreprocessor, + decoder: DataDecoder = JSONDecoder(), + emptyResponseCodes: Set = DecodableResponseSerializer.defaultEmptyResponseCodes, + emptyRequestMethods: Set = DecodableResponseSerializer.defaultEmptyRequestMethods) -> DataTask { + serializingResponse(using: DecodableResponseSerializer(dataPreprocessor: dataPreprocessor, + decoder: decoder, + emptyResponseCodes: emptyResponseCodes, + emptyRequestMethods: emptyRequestMethods), + automaticallyCancelling: shouldAutomaticallyCancel) + } + + /// Creates a `DataTask` to `await` serialization of a `String` value. + /// + /// - Parameters: + /// - shouldAutomaticallyCancel: `Bool` determining whether or not the request should be cancelled when the + /// enclosing async context is cancelled. Only applies to `DataTask`'s async + /// properties. `true` by default. + /// - dataPreprocessor: `DataPreprocessor` which processes the received `Data` before calling the serializer. + /// `PassthroughPreprocessor()` by default. + /// - encoding: `String.Encoding` to use during serialization. Defaults to `nil`, in which case + /// the encoding will be determined from the server response, falling back to the + /// default HTTP character set, `ISO-8859-1`. + /// - emptyResponseCodes: HTTP status codes for which empty responses are always valid. `[204, 205]` by default. + /// - emptyRequestMethods: `HTTPMethod`s for which empty responses are always valid. `[.head]` by default. + /// + /// - Returns: The `DataTask`. + public func serializingString(automaticallyCancelling shouldAutomaticallyCancel: Bool = true, + dataPreprocessor: DataPreprocessor = StringResponseSerializer.defaultDataPreprocessor, + encoding: String.Encoding? = nil, + emptyResponseCodes: Set = StringResponseSerializer.defaultEmptyResponseCodes, + emptyRequestMethods: Set = StringResponseSerializer.defaultEmptyRequestMethods) -> DataTask { + serializingResponse(using: StringResponseSerializer(dataPreprocessor: dataPreprocessor, + encoding: encoding, + emptyResponseCodes: emptyResponseCodes, + emptyRequestMethods: emptyRequestMethods), + automaticallyCancelling: shouldAutomaticallyCancel) + } + + /// Creates a `DataTask` to `await` serialization using the provided `ResponseSerializer` instance. + /// + /// - Parameters: + /// - serializer: `ResponseSerializer` responsible for serializing the request, response, and data. + /// - shouldAutomaticallyCancel: `Bool` determining whether or not the request should be cancelled when the + /// enclosing async context is cancelled. Only applies to `DataTask`'s async + /// properties. `true` by default. + /// + /// - Returns: The `DataTask`. + public func serializingResponse(using serializer: Serializer, + automaticallyCancelling shouldAutomaticallyCancel: Bool = true) + -> DataTask { + dataTask(automaticallyCancelling: shouldAutomaticallyCancel) { [self] in + response(queue: underlyingQueue, + responseSerializer: serializer, + completionHandler: $0) + } + } + + /// Creates a `DataTask` to `await` serialization using the provided `DataResponseSerializerProtocol` instance. + /// + /// - Parameters: + /// - serializer: `DataResponseSerializerProtocol` responsible for serializing the request, + /// response, and data. + /// - shouldAutomaticallyCancel: `Bool` determining whether or not the request should be cancelled when the + /// enclosing async context is cancelled. Only applies to `DataTask`'s async + /// properties. `true` by default. + /// + /// - Returns: The `DataTask`. + public func serializingResponse(using serializer: Serializer, + automaticallyCancelling shouldAutomaticallyCancel: Bool = true) + -> DataTask { + dataTask(automaticallyCancelling: shouldAutomaticallyCancel) { [self] in + response(queue: underlyingQueue, + responseSerializer: serializer, + completionHandler: $0) + } + } + + private func dataTask(automaticallyCancelling shouldAutomaticallyCancel: Bool, + forResponse onResponse: @escaping (@escaping (DataResponse) -> Void) -> Void) + -> DataTask { + let task = Task { + await withTaskCancellationHandler { + await withCheckedContinuation { continuation in + onResponse { + continuation.resume(returning: $0) + } + } + } onCancel: { + self.cancel() + } + } + + return DataTask(request: self, task: task, shouldAutomaticallyCancel: shouldAutomaticallyCancel) + } +} + +// MARK: - DownloadTask + +/// Value used to `await` a `DownloadResponse` and associated values. +@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) +public struct DownloadTask { + /// `DownloadResponse` produced by the `DownloadRequest` and its response handler. + public var response: DownloadResponse { + get async { + if shouldAutomaticallyCancel { + return await withTaskCancellationHandler { + await task.value + } onCancel: { + cancel() + } + } else { + return await task.value + } + } + } + + /// `Result` of any response serialization performed for the `response`. + public var result: Result { + get async { await response.result } + } + + /// `Value` returned by the `response`. + public var value: Value { + get async throws { + try await result.get() + } + } + + private let task: Task, Never> + private let request: DownloadRequest + private let shouldAutomaticallyCancel: Bool + + fileprivate init(request: DownloadRequest, task: Task, Never>, shouldAutomaticallyCancel: Bool) { + self.request = request + self.task = task + self.shouldAutomaticallyCancel = shouldAutomaticallyCancel + } + + /// Cancel the underlying `DownloadRequest` and `Task`. + public func cancel() { + task.cancel() + } + + /// Resume the underlying `DownloadRequest`. + public func resume() { + request.resume() + } + + /// Suspend the underlying `DownloadRequest`. + public func suspend() { + request.suspend() + } +} + +@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) +extension DownloadRequest { + /// Creates a `DownloadTask` to `await` a `Data` value. + /// + /// - Parameters: + /// - shouldAutomaticallyCancel: `Bool` determining whether or not the request should be cancelled when the + /// enclosing async context is cancelled. Only applies to `DownloadTask`'s async + /// properties. `true` by default. + /// - dataPreprocessor: `DataPreprocessor` which processes the received `Data` before completion. + /// - emptyResponseCodes: HTTP response codes for which empty responses are allowed. `[204, 205]` by default. + /// - emptyRequestMethods: `HTTPMethod`s for which empty responses are always valid. `[.head]` by default. + /// + /// - Returns: The `DownloadTask`. + public func serializingData(automaticallyCancelling shouldAutomaticallyCancel: Bool = true, + dataPreprocessor: DataPreprocessor = DataResponseSerializer.defaultDataPreprocessor, + emptyResponseCodes: Set = DataResponseSerializer.defaultEmptyResponseCodes, + emptyRequestMethods: Set = DataResponseSerializer.defaultEmptyRequestMethods) -> DownloadTask { + serializingDownload(using: DataResponseSerializer(dataPreprocessor: dataPreprocessor, + emptyResponseCodes: emptyResponseCodes, + emptyRequestMethods: emptyRequestMethods), + automaticallyCancelling: shouldAutomaticallyCancel) + } + + /// Creates a `DownloadTask` to `await` serialization of a `Decodable` value. + /// + /// - Note: This serializer reads the entire response into memory before parsing. + /// + /// - Parameters: + /// - type: `Decodable` type to decode from response data. + /// - shouldAutomaticallyCancel: `Bool` determining whether or not the request should be cancelled when the + /// enclosing async context is cancelled. Only applies to `DownloadTask`'s async + /// properties. `true` by default. + /// - dataPreprocessor: `DataPreprocessor` which processes the received `Data` before calling the serializer. + /// `PassthroughPreprocessor()` by default. + /// - decoder: `DataDecoder` to use to decode the response. `JSONDecoder()` by default. + /// - emptyResponseCodes: HTTP status codes for which empty responses are always valid. `[204, 205]` by default. + /// - emptyRequestMethods: `HTTPMethod`s for which empty responses are always valid. `[.head]` by default. + /// + /// - Returns: The `DownloadTask`. + public func serializingDecodable(_ type: Value.Type = Value.self, + automaticallyCancelling shouldAutomaticallyCancel: Bool = true, + dataPreprocessor: DataPreprocessor = DecodableResponseSerializer.defaultDataPreprocessor, + decoder: DataDecoder = JSONDecoder(), + emptyResponseCodes: Set = DecodableResponseSerializer.defaultEmptyResponseCodes, + emptyRequestMethods: Set = DecodableResponseSerializer.defaultEmptyRequestMethods) -> DownloadTask { + serializingDownload(using: DecodableResponseSerializer(dataPreprocessor: dataPreprocessor, + decoder: decoder, + emptyResponseCodes: emptyResponseCodes, + emptyRequestMethods: emptyRequestMethods), + automaticallyCancelling: shouldAutomaticallyCancel) + } + + /// Creates a `DownloadTask` to `await` serialization of the downloaded file's `URL` on disk. + /// + /// - Parameters: + /// - shouldAutomaticallyCancel: `Bool` determining whether or not the request should be cancelled when the + /// enclosing async context is cancelled. Only applies to `DownloadTask`'s async + /// properties. `true` by default. + /// + /// - Returns: The `DownloadTask`. + public func serializingDownloadedFileURL(automaticallyCancelling shouldAutomaticallyCancel: Bool = true) -> DownloadTask { + serializingDownload(using: URLResponseSerializer(), + automaticallyCancelling: shouldAutomaticallyCancel) + } + + /// Creates a `DownloadTask` to `await` serialization of a `String` value. + /// + /// - Parameters: + /// - shouldAutomaticallyCancel: `Bool` determining whether or not the request should be cancelled when the + /// enclosing async context is cancelled. Only applies to `DownloadTask`'s async + /// properties. `true` by default. + /// - dataPreprocessor: `DataPreprocessor` which processes the received `Data` before calling the + /// serializer. `PassthroughPreprocessor()` by default. + /// - encoding: `String.Encoding` to use during serialization. Defaults to `nil`, in which case + /// the encoding will be determined from the server response, falling back to the + /// default HTTP character set, `ISO-8859-1`. + /// - emptyResponseCodes: HTTP status codes for which empty responses are always valid. `[204, 205]` by default. + /// - emptyRequestMethods: `HTTPMethod`s for which empty responses are always valid. `[.head]` by default. + /// + /// - Returns: The `DownloadTask`. + public func serializingString(automaticallyCancelling shouldAutomaticallyCancel: Bool = true, + dataPreprocessor: DataPreprocessor = StringResponseSerializer.defaultDataPreprocessor, + encoding: String.Encoding? = nil, + emptyResponseCodes: Set = StringResponseSerializer.defaultEmptyResponseCodes, + emptyRequestMethods: Set = StringResponseSerializer.defaultEmptyRequestMethods) -> DownloadTask { + serializingDownload(using: StringResponseSerializer(dataPreprocessor: dataPreprocessor, + encoding: encoding, + emptyResponseCodes: emptyResponseCodes, + emptyRequestMethods: emptyRequestMethods), + automaticallyCancelling: shouldAutomaticallyCancel) + } + + /// Creates a `DownloadTask` to `await` serialization using the provided `ResponseSerializer` instance. + /// + /// - Parameters: + /// - serializer: `ResponseSerializer` responsible for serializing the request, response, and data. + /// - shouldAutomaticallyCancel: `Bool` determining whether or not the request should be cancelled when the + /// enclosing async context is cancelled. Only applies to `DownloadTask`'s async + /// properties. `true` by default. + /// + /// - Returns: The `DownloadTask`. + public func serializingDownload(using serializer: Serializer, + automaticallyCancelling shouldAutomaticallyCancel: Bool = true) + -> DownloadTask { + downloadTask(automaticallyCancelling: shouldAutomaticallyCancel) { [self] in + response(queue: underlyingQueue, + responseSerializer: serializer, + completionHandler: $0) + } + } + + /// Creates a `DownloadTask` to `await` serialization using the provided `DownloadResponseSerializerProtocol` + /// instance. + /// + /// - Parameters: + /// - serializer: `DownloadResponseSerializerProtocol` responsible for serializing the request, + /// response, and data. + /// - shouldAutomaticallyCancel: `Bool` determining whether or not the request should be cancelled when the + /// enclosing async context is cancelled. Only applies to `DownloadTask`'s async + /// properties. `true` by default. + /// + /// - Returns: The `DownloadTask`. + public func serializingDownload(using serializer: Serializer, + automaticallyCancelling shouldAutomaticallyCancel: Bool = true) + -> DownloadTask { + downloadTask(automaticallyCancelling: shouldAutomaticallyCancel) { [self] in + response(queue: underlyingQueue, + responseSerializer: serializer, + completionHandler: $0) + } + } + + private func downloadTask(automaticallyCancelling shouldAutomaticallyCancel: Bool, + forResponse onResponse: @escaping (@escaping (DownloadResponse) -> Void) -> Void) + -> DownloadTask { + let task = Task { + await withTaskCancellationHandler { + await withCheckedContinuation { continuation in + onResponse { + continuation.resume(returning: $0) + } + } + } onCancel: { + self.cancel() + } + } + + return DownloadTask(request: self, task: task, shouldAutomaticallyCancel: shouldAutomaticallyCancel) + } +} + +// MARK: - DataStreamTask + +@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) +public struct DataStreamTask { + // Type of created streams. + public typealias Stream = StreamOf> + + private let request: DataStreamRequest + + fileprivate init(request: DataStreamRequest) { + self.request = request + } + + /// Creates a `Stream` of `Data` values from the underlying `DataStreamRequest`. + /// + /// - Parameters: + /// - shouldAutomaticallyCancel: `Bool` indicating whether the underlying `DataStreamRequest` should be canceled + /// which observation of the stream stops. `true` by default. + /// - bufferingPolicy: ` BufferingPolicy` that determines the stream's buffering behavior.`.unbounded` by default. + /// + /// - Returns: The `Stream`. + public func streamingData(automaticallyCancelling shouldAutomaticallyCancel: Bool = true, bufferingPolicy: Stream.BufferingPolicy = .unbounded) -> Stream { + createStream(automaticallyCancelling: shouldAutomaticallyCancel, bufferingPolicy: bufferingPolicy) { onStream in + request.responseStream(on: .streamCompletionQueue(forRequestID: request.id), stream: onStream) + } + } + + /// Creates a `Stream` of `UTF-8` `String`s from the underlying `DataStreamRequest`. + /// + /// - Parameters: + /// - shouldAutomaticallyCancel: `Bool` indicating whether the underlying `DataStreamRequest` should be canceled + /// which observation of the stream stops. `true` by default. + /// - bufferingPolicy: ` BufferingPolicy` that determines the stream's buffering behavior.`.unbounded` by default. + /// - Returns: + public func streamingStrings(automaticallyCancelling shouldAutomaticallyCancel: Bool = true, bufferingPolicy: Stream.BufferingPolicy = .unbounded) -> Stream { + createStream(automaticallyCancelling: shouldAutomaticallyCancel, bufferingPolicy: bufferingPolicy) { onStream in + request.responseStreamString(on: .streamCompletionQueue(forRequestID: request.id), stream: onStream) + } + } + + /// Creates a `Stream` of `Decodable` values from the underlying `DataStreamRequest`. + /// + /// - Parameters: + /// - type: `Decodable` type to be serialized from stream payloads. + /// - shouldAutomaticallyCancel: `Bool` indicating whether the underlying `DataStreamRequest` should be canceled + /// which observation of the stream stops. `true` by default. + /// - bufferingPolicy: `BufferingPolicy` that determines the stream's buffering behavior.`.unbounded` by default. + /// + /// - Returns: The `Stream`. + public func streamingDecodables(_ type: T.Type = T.self, + automaticallyCancelling shouldAutomaticallyCancel: Bool = true, + bufferingPolicy: Stream.BufferingPolicy = .unbounded) + -> Stream where T: Decodable { + streamingResponses(serializedUsing: DecodableStreamSerializer(), + automaticallyCancelling: shouldAutomaticallyCancel, + bufferingPolicy: bufferingPolicy) + } + + /// Creates a `Stream` of values using the provided `DataStreamSerializer` from the underlying `DataStreamRequest`. + /// + /// - Parameters: + /// - serializer: `DataStreamSerializer` to use to serialize incoming `Data`. + /// - shouldAutomaticallyCancel: `Bool` indicating whether the underlying `DataStreamRequest` should be canceled + /// which observation of the stream stops. `true` by default. + /// - bufferingPolicy: `BufferingPolicy` that determines the stream's buffering behavior.`.unbounded` by default. + /// + /// - Returns: The `Stream`. + public func streamingResponses(serializedUsing serializer: Serializer, + automaticallyCancelling shouldAutomaticallyCancel: Bool = true, + bufferingPolicy: Stream.BufferingPolicy = .unbounded) + -> Stream { + createStream(automaticallyCancelling: shouldAutomaticallyCancel, bufferingPolicy: bufferingPolicy) { onStream in + request.responseStream(using: serializer, + on: .streamCompletionQueue(forRequestID: request.id), + stream: onStream) + } + } + + private func createStream(automaticallyCancelling shouldAutomaticallyCancel: Bool = true, + bufferingPolicy: Stream.BufferingPolicy = .unbounded, + forResponse onResponse: @escaping (@escaping (DataStreamRequest.Stream) -> Void) -> Void) + -> Stream { + StreamOf(bufferingPolicy: bufferingPolicy) { + guard shouldAutomaticallyCancel, + request.isInitialized || request.isResumed || request.isSuspended else { return } + + cancel() + } builder: { continuation in + onResponse { stream in + continuation.yield(stream) + if case .complete = stream.event { + continuation.finish() + } + } + } + } + + /// Cancel the underlying `DataStreamRequest`. + public func cancel() { + request.cancel() + } + + /// Resume the underlying `DataStreamRequest`. + public func resume() { + request.resume() + } + + /// Suspend the underlying `DataStreamRequest`. + public func suspend() { + request.suspend() + } +} + +@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) +extension DataStreamRequest { + /// Creates a `StreamOf` for the instance's responses. + /// + /// - Parameter bufferingPolicy: `BufferingPolicy` that determines the stream's buffering behavior.`.unbounded` by default. + /// + /// - Returns: The `StreamOf`. + public func httpResponses(bufferingPolicy: StreamOf.BufferingPolicy = .unbounded) -> StreamOf { + stream(bufferingPolicy: bufferingPolicy) { [unowned self] continuation in + onHTTPResponse(on: underlyingQueue) { response in + continuation.yield(response) + } + } + } + + /// Sets an async closure returning a `Request.ResponseDisposition`, called whenever the `DataStreamRequest` + /// produces an `HTTPURLResponse`. + /// + /// - Note: Most requests will only produce a single response for each outgoing attempt (initial + retries). + /// However, some types of response may trigger multiple `HTTPURLResponse`s, such as multipart streams, + /// where responses after the first will contain the part headers. + /// + /// - Parameters: + /// - handler: Async closure executed when a new `HTTPURLResponse` is received and returning a + /// `ResponseDisposition` value. This value determines whether to continue the request or cancel it as + /// if `cancel()` had been called on the instance. Note, this closure is called on an arbitrary thread, + /// so any synchronous calls in it will execute in that context. + /// + /// - Returns: The instance. + @_disfavoredOverload + @discardableResult + public func onHTTPResponse(perform handler: @escaping @Sendable (HTTPURLResponse) async -> ResponseDisposition) -> Self { + onHTTPResponse(on: underlyingQueue) { response, completionHandler in + Task { + let disposition = await handler(response) + completionHandler(disposition) + } + } + + return self + } + + /// Sets an async closure called whenever the `DataStreamRequest` produces an `HTTPURLResponse`. + /// + /// - Note: Most requests will only produce a single response for each outgoing attempt (initial + retries). + /// However, some types of response may trigger multiple `HTTPURLResponse`s, such as multipart streams, + /// where responses after the first will contain the part headers. + /// + /// - Parameters: + /// - handler: Async closure executed when a new `HTTPURLResponse` is received. Note, this closure is called on an + /// arbitrary thread, so any synchronous calls in it will execute in that context. + /// + /// - Returns: The instance. + @discardableResult + public func onHTTPResponse(perform handler: @escaping @Sendable (HTTPURLResponse) async -> Void) -> Self { + onHTTPResponse { response in + await handler(response) + return .allow + } + + return self + } + + /// Creates a `DataStreamTask` used to `await` streams of serialized values. + /// + /// - Returns: The `DataStreamTask`. + public func streamTask() -> DataStreamTask { + DataStreamTask(request: self) + } +} + +#if canImport(Darwin) && !canImport(FoundationNetworking) // Only Apple platforms support URLSessionWebSocketTask. +// - MARK: WebSocketTask + +@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) +@_spi(WebSocket) public struct WebSocketTask { + private let request: WebSocketRequest + + fileprivate init(request: WebSocketRequest) { + self.request = request + } + + public typealias EventStreamOf = StreamOf> + + public func streamingMessageEvents( + automaticallyCancelling shouldAutomaticallyCancel: Bool = true, + bufferingPolicy: EventStreamOf.BufferingPolicy = .unbounded + ) -> EventStreamOf { + createStream(automaticallyCancelling: shouldAutomaticallyCancel, + bufferingPolicy: bufferingPolicy, + transform: { $0 }) { onEvent in + request.streamMessageEvents(on: .streamCompletionQueue(forRequestID: request.id), handler: onEvent) + } + } + + public func streamingMessages( + automaticallyCancelling shouldAutomaticallyCancel: Bool = true, + bufferingPolicy: StreamOf.BufferingPolicy = .unbounded + ) -> StreamOf { + createStream(automaticallyCancelling: shouldAutomaticallyCancel, + bufferingPolicy: bufferingPolicy, + transform: { $0.message }) { onEvent in + request.streamMessageEvents(on: .streamCompletionQueue(forRequestID: request.id), handler: onEvent) + } + } + + public func streamingDecodableEvents( + _ type: Value.Type = Value.self, + automaticallyCancelling shouldAutomaticallyCancel: Bool = true, + using decoder: DataDecoder = JSONDecoder(), + bufferingPolicy: EventStreamOf.BufferingPolicy = .unbounded + ) -> EventStreamOf { + createStream(automaticallyCancelling: shouldAutomaticallyCancel, + bufferingPolicy: bufferingPolicy, + transform: { $0 }) { onEvent in + request.streamDecodableEvents(Value.self, + on: .streamCompletionQueue(forRequestID: request.id), + using: decoder, + handler: onEvent) + } + } + + public func streamingDecodable( + _ type: Value.Type = Value.self, + automaticallyCancelling shouldAutomaticallyCancel: Bool = true, + using decoder: DataDecoder = JSONDecoder(), + bufferingPolicy: StreamOf.BufferingPolicy = .unbounded + ) -> StreamOf { + createStream(automaticallyCancelling: shouldAutomaticallyCancel, + bufferingPolicy: bufferingPolicy, + transform: { $0.message }) { onEvent in + request.streamDecodableEvents(Value.self, + on: .streamCompletionQueue(forRequestID: request.id), + using: decoder, + handler: onEvent) + } + } + + private func createStream( + automaticallyCancelling shouldAutomaticallyCancel: Bool, + bufferingPolicy: StreamOf.BufferingPolicy, + transform: @escaping (WebSocketRequest.Event) -> Value?, + forResponse onResponse: @escaping (@escaping (WebSocketRequest.Event) -> Void) -> Void + ) -> StreamOf { + StreamOf(bufferingPolicy: bufferingPolicy) { + guard shouldAutomaticallyCancel, + request.isInitialized || request.isResumed || request.isSuspended else { return } + + cancel() + } builder: { continuation in + onResponse { event in + if let value = transform(event) { + continuation.yield(value) + } + + if case .completed = event.kind { + continuation.finish() + } + } + } + } + + /// Send a `URLSessionWebSocketTask.Message`. + /// + /// - Parameter message: The `Message`. + /// + public func send(_ message: URLSessionWebSocketTask.Message) async throws { + try await withCheckedThrowingContinuation { continuation in + request.send(message, queue: .streamCompletionQueue(forRequestID: request.id)) { result in + continuation.resume(with: result) + } + } + } + + /// Close the underlying `WebSocketRequest`. + public func close(sending closeCode: URLSessionWebSocketTask.CloseCode, reason: Data? = nil) { + request.close(sending: closeCode, reason: reason) + } + + /// Cancel the underlying `WebSocketRequest`. + /// + /// Cancellation will produce an `AFError.explicitlyCancelled` instance. + public func cancel() { + request.cancel() + } + + /// Resume the underlying `WebSocketRequest`. + public func resume() { + request.resume() + } + + /// Suspend the underlying `WebSocketRequest`. + public func suspend() { + request.suspend() + } +} + +@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) +extension WebSocketRequest { + public func webSocketTask() -> WebSocketTask { + WebSocketTask(request: self) + } +} +#endif + +extension DispatchQueue { + fileprivate static let singleEventQueue = DispatchQueue(label: "org.alamofire.concurrencySingleEventQueue", + attributes: .concurrent) + + fileprivate static func streamCompletionQueue(forRequestID id: UUID) -> DispatchQueue { + DispatchQueue(label: "org.alamofire.concurrencyStreamCompletionQueue-\(id)", target: .singleEventQueue) + } +} + +/// An asynchronous sequence generated from an underlying `AsyncStream`. Only produced by Alamofire. +@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) +public struct StreamOf: AsyncSequence { + public typealias AsyncIterator = Iterator + public typealias BufferingPolicy = AsyncStream.Continuation.BufferingPolicy + fileprivate typealias Continuation = AsyncStream.Continuation + + private let bufferingPolicy: BufferingPolicy + private let onTermination: (() -> Void)? + private let builder: (Continuation) -> Void + + fileprivate init(bufferingPolicy: BufferingPolicy = .unbounded, + onTermination: (() -> Void)? = nil, + builder: @escaping (Continuation) -> Void) { + self.bufferingPolicy = bufferingPolicy + self.onTermination = onTermination + self.builder = builder + } + + public func makeAsyncIterator() -> Iterator { + var continuation: AsyncStream.Continuation? + let stream = AsyncStream(bufferingPolicy: bufferingPolicy) { innerContinuation in + continuation = innerContinuation + builder(innerContinuation) + } + + return Iterator(iterator: stream.makeAsyncIterator()) { + continuation?.finish() + onTermination?() + } + } + + public struct Iterator: AsyncIteratorProtocol { + private final class Token { + private let onDeinit: () -> Void + + init(onDeinit: @escaping () -> Void) { + self.onDeinit = onDeinit + } + + deinit { + onDeinit() + } + } + + private var iterator: AsyncStream.AsyncIterator + private let token: Token + + init(iterator: AsyncStream.AsyncIterator, onCancellation: @escaping () -> Void) { + self.iterator = iterator + token = Token(onDeinit: onCancellation) + } + + public mutating func next() async -> Element? { + await iterator.next() + } + } +} + +#endif diff --git a/Pods/Alamofire/Source/Features/EventMonitor.swift b/Pods/Alamofire/Source/Features/EventMonitor.swift new file mode 100644 index 0000000..c997e71 --- /dev/null +++ b/Pods/Alamofire/Source/Features/EventMonitor.swift @@ -0,0 +1,907 @@ +// +// EventMonitor.swift +// +// Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +import Foundation + +/// Protocol outlining the lifetime events inside Alamofire. It includes both events received from the various +/// `URLSession` delegate protocols as well as various events from the lifetime of `Request` and its subclasses. +public protocol EventMonitor { + /// The `DispatchQueue` onto which Alamofire's root `CompositeEventMonitor` will dispatch events. `.main` by default. + var queue: DispatchQueue { get } + + // MARK: - URLSession Events + + // MARK: URLSessionDelegate Events + + /// Event called during `URLSessionDelegate`'s `urlSession(_:didBecomeInvalidWithError:)` method. + func urlSession(_ session: URLSession, didBecomeInvalidWithError error: Error?) + + // MARK: URLSessionTaskDelegate Events + + /// Event called during `URLSessionTaskDelegate`'s `urlSession(_:task:didReceive:completionHandler:)` method. + func urlSession(_ session: URLSession, task: URLSessionTask, didReceive challenge: URLAuthenticationChallenge) + + /// Event called during `URLSessionTaskDelegate`'s `urlSession(_:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:)` method. + func urlSession(_ session: URLSession, + task: URLSessionTask, + didSendBodyData bytesSent: Int64, + totalBytesSent: Int64, + totalBytesExpectedToSend: Int64) + + /// Event called during `URLSessionTaskDelegate`'s `urlSession(_:task:needNewBodyStream:)` method. + func urlSession(_ session: URLSession, taskNeedsNewBodyStream task: URLSessionTask) + + /// Event called during `URLSessionTaskDelegate`'s `urlSession(_:task:willPerformHTTPRedirection:newRequest:completionHandler:)` method. + func urlSession(_ session: URLSession, + task: URLSessionTask, + willPerformHTTPRedirection response: HTTPURLResponse, + newRequest request: URLRequest) + + /// Event called during `URLSessionTaskDelegate`'s `urlSession(_:task:didFinishCollecting:)` method. + func urlSession(_ session: URLSession, task: URLSessionTask, didFinishCollecting metrics: URLSessionTaskMetrics) + + /// Event called during `URLSessionTaskDelegate`'s `urlSession(_:task:didCompleteWithError:)` method. + func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) + + /// Event called during `URLSessionTaskDelegate`'s `urlSession(_:taskIsWaitingForConnectivity:)` method. + func urlSession(_ session: URLSession, taskIsWaitingForConnectivity task: URLSessionTask) + + // MARK: URLSessionDataDelegate Events + + /// Event called during `URLSessionDataDelegate`'s `urlSession(_:dataTask:didReceive:completionHandler:)` method. + func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse) + + /// Event called during `URLSessionDataDelegate`'s `urlSession(_:dataTask:didReceive:)` method. + func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) + + /// Event called during `URLSessionDataDelegate`'s `urlSession(_:dataTask:willCacheResponse:completionHandler:)` method. + func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, willCacheResponse proposedResponse: CachedURLResponse) + + // MARK: URLSessionDownloadDelegate Events + + /// Event called during `URLSessionDownloadDelegate`'s `urlSession(_:downloadTask:didResumeAtOffset:expectedTotalBytes:)` method. + func urlSession(_ session: URLSession, + downloadTask: URLSessionDownloadTask, + didResumeAtOffset fileOffset: Int64, + expectedTotalBytes: Int64) + + /// Event called during `URLSessionDownloadDelegate`'s `urlSession(_:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:)` method. + func urlSession(_ session: URLSession, + downloadTask: URLSessionDownloadTask, + didWriteData bytesWritten: Int64, + totalBytesWritten: Int64, + totalBytesExpectedToWrite: Int64) + + /// Event called during `URLSessionDownloadDelegate`'s `urlSession(_:downloadTask:didFinishDownloadingTo:)` method. + func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) + + // MARK: - Request Events + + /// Event called when a `URLRequest` is first created for a `Request`. If a `RequestAdapter` is active, the + /// `URLRequest` will be adapted before being issued. + func request(_ request: Request, didCreateInitialURLRequest urlRequest: URLRequest) + + /// Event called when the attempt to create a `URLRequest` from a `Request`'s original `URLRequestConvertible` value fails. + func request(_ request: Request, didFailToCreateURLRequestWithError error: AFError) + + /// Event called when a `RequestAdapter` adapts the `Request`'s initial `URLRequest`. + func request(_ request: Request, didAdaptInitialRequest initialRequest: URLRequest, to adaptedRequest: URLRequest) + + /// Event called when a `RequestAdapter` fails to adapt the `Request`'s initial `URLRequest`. + func request(_ request: Request, didFailToAdaptURLRequest initialRequest: URLRequest, withError error: AFError) + + /// Event called when a final `URLRequest` is created for a `Request`. + func request(_ request: Request, didCreateURLRequest urlRequest: URLRequest) + + /// Event called when a `URLSessionTask` subclass instance is created for a `Request`. + func request(_ request: Request, didCreateTask task: URLSessionTask) + + /// Event called when a `Request` receives a `URLSessionTaskMetrics` value. + func request(_ request: Request, didGatherMetrics metrics: URLSessionTaskMetrics) + + /// Event called when a `Request` fails due to an error created by Alamofire. e.g. When certificate pinning fails. + func request(_ request: Request, didFailTask task: URLSessionTask, earlyWithError error: AFError) + + /// Event called when a `Request`'s task completes, possibly with an error. A `Request` may receive this event + /// multiple times if it is retried. + func request(_ request: Request, didCompleteTask task: URLSessionTask, with error: AFError?) + + /// Event called when a `Request` is about to be retried. + func requestIsRetrying(_ request: Request) + + /// Event called when a `Request` finishes and response serializers are being called. + func requestDidFinish(_ request: Request) + + /// Event called when a `Request` receives a `resume` call. + func requestDidResume(_ request: Request) + + /// Event called when a `Request`'s associated `URLSessionTask` is resumed. + func request(_ request: Request, didResumeTask task: URLSessionTask) + + /// Event called when a `Request` receives a `suspend` call. + func requestDidSuspend(_ request: Request) + + /// Event called when a `Request`'s associated `URLSessionTask` is suspended. + func request(_ request: Request, didSuspendTask task: URLSessionTask) + + /// Event called when a `Request` receives a `cancel` call. + func requestDidCancel(_ request: Request) + + /// Event called when a `Request`'s associated `URLSessionTask` is cancelled. + func request(_ request: Request, didCancelTask task: URLSessionTask) + + // MARK: DataRequest Events + + /// Event called when a `DataRequest` calls a `Validation`. + func request(_ request: DataRequest, + didValidateRequest urlRequest: URLRequest?, + response: HTTPURLResponse, + data: Data?, + withResult result: Request.ValidationResult) + + /// Event called when a `DataRequest` creates a `DataResponse` value without calling a `ResponseSerializer`. + func request(_ request: DataRequest, didParseResponse response: DataResponse) + + /// Event called when a `DataRequest` calls a `ResponseSerializer` and creates a generic `DataResponse`. + func request(_ request: DataRequest, didParseResponse response: DataResponse) + + // MARK: DataStreamRequest Events + + /// Event called when a `DataStreamRequest` calls a `Validation` closure. + /// + /// - Parameters: + /// - request: `DataStreamRequest` which is calling the `Validation`. + /// - urlRequest: `URLRequest` of the request being validated. + /// - response: `HTTPURLResponse` of the request being validated. + /// - result: Produced `ValidationResult`. + func request(_ request: DataStreamRequest, + didValidateRequest urlRequest: URLRequest?, + response: HTTPURLResponse, + withResult result: Request.ValidationResult) + + /// Event called when a `DataStreamSerializer` produces a value from streamed `Data`. + /// + /// - Parameters: + /// - request: `DataStreamRequest` for which the value was serialized. + /// - result: `Result` of the serialization attempt. + func request(_ request: DataStreamRequest, didParseStream result: Result) + + // MARK: UploadRequest Events + + /// Event called when an `UploadRequest` creates its `Uploadable` value, indicating the type of upload it represents. + func request(_ request: UploadRequest, didCreateUploadable uploadable: UploadRequest.Uploadable) + + /// Event called when an `UploadRequest` failed to create its `Uploadable` value due to an error. + func request(_ request: UploadRequest, didFailToCreateUploadableWithError error: AFError) + + /// Event called when an `UploadRequest` provides the `InputStream` from its `Uploadable` value. This only occurs if + /// the `InputStream` does not wrap a `Data` value or file `URL`. + func request(_ request: UploadRequest, didProvideInputStream stream: InputStream) + + // MARK: DownloadRequest Events + + /// Event called when a `DownloadRequest`'s `URLSessionDownloadTask` finishes and the temporary file has been moved. + func request(_ request: DownloadRequest, didFinishDownloadingUsing task: URLSessionTask, with result: Result) + + /// Event called when a `DownloadRequest`'s `Destination` closure is called and creates the destination URL the + /// downloaded file will be moved to. + func request(_ request: DownloadRequest, didCreateDestinationURL url: URL) + + /// Event called when a `DownloadRequest` calls a `Validation`. + func request(_ request: DownloadRequest, + didValidateRequest urlRequest: URLRequest?, + response: HTTPURLResponse, + fileURL: URL?, + withResult result: Request.ValidationResult) + + /// Event called when a `DownloadRequest` creates a `DownloadResponse` without calling a `ResponseSerializer`. + func request(_ request: DownloadRequest, didParseResponse response: DownloadResponse) + + /// Event called when a `DownloadRequest` calls a `DownloadResponseSerializer` and creates a generic `DownloadResponse` + func request(_ request: DownloadRequest, didParseResponse response: DownloadResponse) +} + +extension EventMonitor { + /// The default queue on which `CompositeEventMonitor`s will call the `EventMonitor` methods. `.main` by default. + public var queue: DispatchQueue { .main } + + // MARK: Default Implementations + + public func urlSession(_ session: URLSession, didBecomeInvalidWithError error: Error?) {} + public func urlSession(_ session: URLSession, + task: URLSessionTask, + didReceive challenge: URLAuthenticationChallenge) {} + public func urlSession(_ session: URLSession, + task: URLSessionTask, + didSendBodyData bytesSent: Int64, + totalBytesSent: Int64, + totalBytesExpectedToSend: Int64) {} + public func urlSession(_ session: URLSession, taskNeedsNewBodyStream task: URLSessionTask) {} + public func urlSession(_ session: URLSession, + task: URLSessionTask, + willPerformHTTPRedirection response: HTTPURLResponse, + newRequest request: URLRequest) {} + public func urlSession(_ session: URLSession, + task: URLSessionTask, + didFinishCollecting metrics: URLSessionTaskMetrics) {} + public func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {} + public func urlSession(_ session: URLSession, taskIsWaitingForConnectivity task: URLSessionTask) {} + public func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse) {} + public func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {} + public func urlSession(_ session: URLSession, + dataTask: URLSessionDataTask, + willCacheResponse proposedResponse: CachedURLResponse) {} + public func urlSession(_ session: URLSession, + downloadTask: URLSessionDownloadTask, + didResumeAtOffset fileOffset: Int64, + expectedTotalBytes: Int64) {} + public func urlSession(_ session: URLSession, + downloadTask: URLSessionDownloadTask, + didWriteData bytesWritten: Int64, + totalBytesWritten: Int64, + totalBytesExpectedToWrite: Int64) {} + public func urlSession(_ session: URLSession, + downloadTask: URLSessionDownloadTask, + didFinishDownloadingTo location: URL) {} + public func request(_ request: Request, didCreateInitialURLRequest urlRequest: URLRequest) {} + public func request(_ request: Request, didFailToCreateURLRequestWithError error: AFError) {} + public func request(_ request: Request, + didAdaptInitialRequest initialRequest: URLRequest, + to adaptedRequest: URLRequest) {} + public func request(_ request: Request, + didFailToAdaptURLRequest initialRequest: URLRequest, + withError error: AFError) {} + public func request(_ request: Request, didCreateURLRequest urlRequest: URLRequest) {} + public func request(_ request: Request, didCreateTask task: URLSessionTask) {} + public func request(_ request: Request, didGatherMetrics metrics: URLSessionTaskMetrics) {} + public func request(_ request: Request, didFailTask task: URLSessionTask, earlyWithError error: AFError) {} + public func request(_ request: Request, didCompleteTask task: URLSessionTask, with error: AFError?) {} + public func requestIsRetrying(_ request: Request) {} + public func requestDidFinish(_ request: Request) {} + public func requestDidResume(_ request: Request) {} + public func request(_ request: Request, didResumeTask task: URLSessionTask) {} + public func requestDidSuspend(_ request: Request) {} + public func request(_ request: Request, didSuspendTask task: URLSessionTask) {} + public func requestDidCancel(_ request: Request) {} + public func request(_ request: Request, didCancelTask task: URLSessionTask) {} + public func request(_ request: DataRequest, + didValidateRequest urlRequest: URLRequest?, + response: HTTPURLResponse, + data: Data?, + withResult result: Request.ValidationResult) {} + public func request(_ request: DataRequest, didParseResponse response: DataResponse) {} + public func request(_ request: DataRequest, didParseResponse response: DataResponse) {} + public func request(_ request: DataStreamRequest, + didValidateRequest urlRequest: URLRequest?, + response: HTTPURLResponse, + withResult result: Request.ValidationResult) {} + public func request(_ request: DataStreamRequest, didParseStream result: Result) {} + public func request(_ request: UploadRequest, didCreateUploadable uploadable: UploadRequest.Uploadable) {} + public func request(_ request: UploadRequest, didFailToCreateUploadableWithError error: AFError) {} + public func request(_ request: UploadRequest, didProvideInputStream stream: InputStream) {} + public func request(_ request: DownloadRequest, didFinishDownloadingUsing task: URLSessionTask, with result: Result) {} + public func request(_ request: DownloadRequest, didCreateDestinationURL url: URL) {} + public func request(_ request: DownloadRequest, + didValidateRequest urlRequest: URLRequest?, + response: HTTPURLResponse, + fileURL: URL?, + withResult result: Request.ValidationResult) {} + public func request(_ request: DownloadRequest, didParseResponse response: DownloadResponse) {} + public func request(_ request: DownloadRequest, didParseResponse response: DownloadResponse) {} +} + +/// An `EventMonitor` which can contain multiple `EventMonitor`s and calls their methods on their queues. +public final class CompositeEventMonitor: EventMonitor { + public let queue = DispatchQueue(label: "org.alamofire.compositeEventMonitor") + + let monitors: [EventMonitor] + + init(monitors: [EventMonitor]) { + self.monitors = monitors + } + + func performEvent(_ event: @escaping (EventMonitor) -> Void) { + queue.async { + for monitor in self.monitors { + monitor.queue.async { event(monitor) } + } + } + } + + public func urlSession(_ session: URLSession, didBecomeInvalidWithError error: Error?) { + performEvent { $0.urlSession(session, didBecomeInvalidWithError: error) } + } + + public func urlSession(_ session: URLSession, + task: URLSessionTask, + didReceive challenge: URLAuthenticationChallenge) { + performEvent { $0.urlSession(session, task: task, didReceive: challenge) } + } + + public func urlSession(_ session: URLSession, + task: URLSessionTask, + didSendBodyData bytesSent: Int64, + totalBytesSent: Int64, + totalBytesExpectedToSend: Int64) { + performEvent { + $0.urlSession(session, + task: task, + didSendBodyData: bytesSent, + totalBytesSent: totalBytesSent, + totalBytesExpectedToSend: totalBytesExpectedToSend) + } + } + + public func urlSession(_ session: URLSession, taskNeedsNewBodyStream task: URLSessionTask) { + performEvent { + $0.urlSession(session, taskNeedsNewBodyStream: task) + } + } + + public func urlSession(_ session: URLSession, + task: URLSessionTask, + willPerformHTTPRedirection response: HTTPURLResponse, + newRequest request: URLRequest) { + performEvent { + $0.urlSession(session, + task: task, + willPerformHTTPRedirection: response, + newRequest: request) + } + } + + public func urlSession(_ session: URLSession, task: URLSessionTask, didFinishCollecting metrics: URLSessionTaskMetrics) { + performEvent { $0.urlSession(session, task: task, didFinishCollecting: metrics) } + } + + public func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) { + performEvent { $0.urlSession(session, task: task, didCompleteWithError: error) } + } + + @available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *) + public func urlSession(_ session: URLSession, taskIsWaitingForConnectivity task: URLSessionTask) { + performEvent { $0.urlSession(session, taskIsWaitingForConnectivity: task) } + } + + public func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse) { + performEvent { $0.urlSession(session, dataTask: dataTask, didReceive: response) } + } + + public func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) { + performEvent { $0.urlSession(session, dataTask: dataTask, didReceive: data) } + } + + public func urlSession(_ session: URLSession, + dataTask: URLSessionDataTask, + willCacheResponse proposedResponse: CachedURLResponse) { + performEvent { $0.urlSession(session, dataTask: dataTask, willCacheResponse: proposedResponse) } + } + + public func urlSession(_ session: URLSession, + downloadTask: URLSessionDownloadTask, + didResumeAtOffset fileOffset: Int64, + expectedTotalBytes: Int64) { + performEvent { + $0.urlSession(session, + downloadTask: downloadTask, + didResumeAtOffset: fileOffset, + expectedTotalBytes: expectedTotalBytes) + } + } + + public func urlSession(_ session: URLSession, + downloadTask: URLSessionDownloadTask, + didWriteData bytesWritten: Int64, + totalBytesWritten: Int64, + totalBytesExpectedToWrite: Int64) { + performEvent { + $0.urlSession(session, + downloadTask: downloadTask, + didWriteData: bytesWritten, + totalBytesWritten: totalBytesWritten, + totalBytesExpectedToWrite: totalBytesExpectedToWrite) + } + } + + public func urlSession(_ session: URLSession, + downloadTask: URLSessionDownloadTask, + didFinishDownloadingTo location: URL) { + performEvent { $0.urlSession(session, downloadTask: downloadTask, didFinishDownloadingTo: location) } + } + + public func request(_ request: Request, didCreateInitialURLRequest urlRequest: URLRequest) { + performEvent { $0.request(request, didCreateInitialURLRequest: urlRequest) } + } + + public func request(_ request: Request, didFailToCreateURLRequestWithError error: AFError) { + performEvent { $0.request(request, didFailToCreateURLRequestWithError: error) } + } + + public func request(_ request: Request, didAdaptInitialRequest initialRequest: URLRequest, to adaptedRequest: URLRequest) { + performEvent { $0.request(request, didAdaptInitialRequest: initialRequest, to: adaptedRequest) } + } + + public func request(_ request: Request, didFailToAdaptURLRequest initialRequest: URLRequest, withError error: AFError) { + performEvent { $0.request(request, didFailToAdaptURLRequest: initialRequest, withError: error) } + } + + public func request(_ request: Request, didCreateURLRequest urlRequest: URLRequest) { + performEvent { $0.request(request, didCreateURLRequest: urlRequest) } + } + + public func request(_ request: Request, didCreateTask task: URLSessionTask) { + performEvent { $0.request(request, didCreateTask: task) } + } + + public func request(_ request: Request, didGatherMetrics metrics: URLSessionTaskMetrics) { + performEvent { $0.request(request, didGatherMetrics: metrics) } + } + + public func request(_ request: Request, didFailTask task: URLSessionTask, earlyWithError error: AFError) { + performEvent { $0.request(request, didFailTask: task, earlyWithError: error) } + } + + public func request(_ request: Request, didCompleteTask task: URLSessionTask, with error: AFError?) { + performEvent { $0.request(request, didCompleteTask: task, with: error) } + } + + public func requestIsRetrying(_ request: Request) { + performEvent { $0.requestIsRetrying(request) } + } + + public func requestDidFinish(_ request: Request) { + performEvent { $0.requestDidFinish(request) } + } + + public func requestDidResume(_ request: Request) { + performEvent { $0.requestDidResume(request) } + } + + public func request(_ request: Request, didResumeTask task: URLSessionTask) { + performEvent { $0.request(request, didResumeTask: task) } + } + + public func requestDidSuspend(_ request: Request) { + performEvent { $0.requestDidSuspend(request) } + } + + public func request(_ request: Request, didSuspendTask task: URLSessionTask) { + performEvent { $0.request(request, didSuspendTask: task) } + } + + public func requestDidCancel(_ request: Request) { + performEvent { $0.requestDidCancel(request) } + } + + public func request(_ request: Request, didCancelTask task: URLSessionTask) { + performEvent { $0.request(request, didCancelTask: task) } + } + + public func request(_ request: DataRequest, + didValidateRequest urlRequest: URLRequest?, + response: HTTPURLResponse, + data: Data?, + withResult result: Request.ValidationResult) { + performEvent { $0.request(request, + didValidateRequest: urlRequest, + response: response, + data: data, + withResult: result) + } + } + + public func request(_ request: DataRequest, didParseResponse response: DataResponse) { + performEvent { $0.request(request, didParseResponse: response) } + } + + public func request(_ request: DataRequest, didParseResponse response: DataResponse) { + performEvent { $0.request(request, didParseResponse: response) } + } + + public func request(_ request: DataStreamRequest, + didValidateRequest urlRequest: URLRequest?, + response: HTTPURLResponse, + withResult result: Request.ValidationResult) { + performEvent { $0.request(request, + didValidateRequest: urlRequest, + response: response, + withResult: result) + } + } + + public func request(_ request: DataStreamRequest, didParseStream result: Result) { + performEvent { $0.request(request, didParseStream: result) } + } + + public func request(_ request: UploadRequest, didCreateUploadable uploadable: UploadRequest.Uploadable) { + performEvent { $0.request(request, didCreateUploadable: uploadable) } + } + + public func request(_ request: UploadRequest, didFailToCreateUploadableWithError error: AFError) { + performEvent { $0.request(request, didFailToCreateUploadableWithError: error) } + } + + public func request(_ request: UploadRequest, didProvideInputStream stream: InputStream) { + performEvent { $0.request(request, didProvideInputStream: stream) } + } + + public func request(_ request: DownloadRequest, didFinishDownloadingUsing task: URLSessionTask, with result: Result) { + performEvent { $0.request(request, didFinishDownloadingUsing: task, with: result) } + } + + public func request(_ request: DownloadRequest, didCreateDestinationURL url: URL) { + performEvent { $0.request(request, didCreateDestinationURL: url) } + } + + public func request(_ request: DownloadRequest, + didValidateRequest urlRequest: URLRequest?, + response: HTTPURLResponse, + fileURL: URL?, + withResult result: Request.ValidationResult) { + performEvent { $0.request(request, + didValidateRequest: urlRequest, + response: response, + fileURL: fileURL, + withResult: result) } + } + + public func request(_ request: DownloadRequest, didParseResponse response: DownloadResponse) { + performEvent { $0.request(request, didParseResponse: response) } + } + + public func request(_ request: DownloadRequest, didParseResponse response: DownloadResponse) { + performEvent { $0.request(request, didParseResponse: response) } + } +} + +/// `EventMonitor` that allows optional closures to be set to receive events. +open class ClosureEventMonitor: EventMonitor { + /// Closure called on the `urlSession(_:didBecomeInvalidWithError:)` event. + open var sessionDidBecomeInvalidWithError: ((URLSession, Error?) -> Void)? + + /// Closure called on the `urlSession(_:task:didReceive:completionHandler:)`. + open var taskDidReceiveChallenge: ((URLSession, URLSessionTask, URLAuthenticationChallenge) -> Void)? + + /// Closure that receives `urlSession(_:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:)` event. + open var taskDidSendBodyData: ((URLSession, URLSessionTask, Int64, Int64, Int64) -> Void)? + + /// Closure called on the `urlSession(_:task:needNewBodyStream:)` event. + open var taskNeedNewBodyStream: ((URLSession, URLSessionTask) -> Void)? + + /// Closure called on the `urlSession(_:task:willPerformHTTPRedirection:newRequest:completionHandler:)` event. + open var taskWillPerformHTTPRedirection: ((URLSession, URLSessionTask, HTTPURLResponse, URLRequest) -> Void)? + + /// Closure called on the `urlSession(_:task:didFinishCollecting:)` event. + open var taskDidFinishCollectingMetrics: ((URLSession, URLSessionTask, URLSessionTaskMetrics) -> Void)? + + /// Closure called on the `urlSession(_:task:didCompleteWithError:)` event. + open var taskDidComplete: ((URLSession, URLSessionTask, Error?) -> Void)? + + /// Closure called on the `urlSession(_:taskIsWaitingForConnectivity:)` event. + open var taskIsWaitingForConnectivity: ((URLSession, URLSessionTask) -> Void)? + + /// Closure called on the `urlSession(_:dataTask:didReceive:completionHandler:)` event. + open var dataTaskDidReceiveResponse: ((URLSession, URLSessionDataTask, URLResponse) -> Void)? + + /// Closure that receives the `urlSession(_:dataTask:didReceive:)` event. + open var dataTaskDidReceiveData: ((URLSession, URLSessionDataTask, Data) -> Void)? + + /// Closure called on the `urlSession(_:dataTask:willCacheResponse:completionHandler:)` event. + open var dataTaskWillCacheResponse: ((URLSession, URLSessionDataTask, CachedURLResponse) -> Void)? + + /// Closure called on the `urlSession(_:downloadTask:didFinishDownloadingTo:)` event. + open var downloadTaskDidFinishDownloadingToURL: ((URLSession, URLSessionDownloadTask, URL) -> Void)? + + /// Closure called on the `urlSession(_:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:)` + /// event. + open var downloadTaskDidWriteData: ((URLSession, URLSessionDownloadTask, Int64, Int64, Int64) -> Void)? + + /// Closure called on the `urlSession(_:downloadTask:didResumeAtOffset:expectedTotalBytes:)` event. + open var downloadTaskDidResumeAtOffset: ((URLSession, URLSessionDownloadTask, Int64, Int64) -> Void)? + + // MARK: - Request Events + + /// Closure called on the `request(_:didCreateInitialURLRequest:)` event. + open var requestDidCreateInitialURLRequest: ((Request, URLRequest) -> Void)? + + /// Closure called on the `request(_:didFailToCreateURLRequestWithError:)` event. + open var requestDidFailToCreateURLRequestWithError: ((Request, AFError) -> Void)? + + /// Closure called on the `request(_:didAdaptInitialRequest:to:)` event. + open var requestDidAdaptInitialRequestToAdaptedRequest: ((Request, URLRequest, URLRequest) -> Void)? + + /// Closure called on the `request(_:didFailToAdaptURLRequest:withError:)` event. + open var requestDidFailToAdaptURLRequestWithError: ((Request, URLRequest, AFError) -> Void)? + + /// Closure called on the `request(_:didCreateURLRequest:)` event. + open var requestDidCreateURLRequest: ((Request, URLRequest) -> Void)? + + /// Closure called on the `request(_:didCreateTask:)` event. + open var requestDidCreateTask: ((Request, URLSessionTask) -> Void)? + + /// Closure called on the `request(_:didGatherMetrics:)` event. + open var requestDidGatherMetrics: ((Request, URLSessionTaskMetrics) -> Void)? + + /// Closure called on the `request(_:didFailTask:earlyWithError:)` event. + open var requestDidFailTaskEarlyWithError: ((Request, URLSessionTask, AFError) -> Void)? + + /// Closure called on the `request(_:didCompleteTask:with:)` event. + open var requestDidCompleteTaskWithError: ((Request, URLSessionTask, AFError?) -> Void)? + + /// Closure called on the `requestIsRetrying(_:)` event. + open var requestIsRetrying: ((Request) -> Void)? + + /// Closure called on the `requestDidFinish(_:)` event. + open var requestDidFinish: ((Request) -> Void)? + + /// Closure called on the `requestDidResume(_:)` event. + open var requestDidResume: ((Request) -> Void)? + + /// Closure called on the `request(_:didResumeTask:)` event. + open var requestDidResumeTask: ((Request, URLSessionTask) -> Void)? + + /// Closure called on the `requestDidSuspend(_:)` event. + open var requestDidSuspend: ((Request) -> Void)? + + /// Closure called on the `request(_:didSuspendTask:)` event. + open var requestDidSuspendTask: ((Request, URLSessionTask) -> Void)? + + /// Closure called on the `requestDidCancel(_:)` event. + open var requestDidCancel: ((Request) -> Void)? + + /// Closure called on the `request(_:didCancelTask:)` event. + open var requestDidCancelTask: ((Request, URLSessionTask) -> Void)? + + /// Closure called on the `request(_:didValidateRequest:response:data:withResult:)` event. + open var requestDidValidateRequestResponseDataWithResult: ((DataRequest, URLRequest?, HTTPURLResponse, Data?, Request.ValidationResult) -> Void)? + + /// Closure called on the `request(_:didParseResponse:)` event. + open var requestDidParseResponse: ((DataRequest, DataResponse) -> Void)? + + /// Closure called on the `request(_:didValidateRequest:response:withResult:)` event. + open var requestDidValidateRequestResponseWithResult: ((DataStreamRequest, URLRequest?, HTTPURLResponse, Request.ValidationResult) -> Void)? + + /// Closure called on the `request(_:didCreateUploadable:)` event. + open var requestDidCreateUploadable: ((UploadRequest, UploadRequest.Uploadable) -> Void)? + + /// Closure called on the `request(_:didFailToCreateUploadableWithError:)` event. + open var requestDidFailToCreateUploadableWithError: ((UploadRequest, AFError) -> Void)? + + /// Closure called on the `request(_:didProvideInputStream:)` event. + open var requestDidProvideInputStream: ((UploadRequest, InputStream) -> Void)? + + /// Closure called on the `request(_:didFinishDownloadingUsing:with:)` event. + open var requestDidFinishDownloadingUsingTaskWithResult: ((DownloadRequest, URLSessionTask, Result) -> Void)? + + /// Closure called on the `request(_:didCreateDestinationURL:)` event. + open var requestDidCreateDestinationURL: ((DownloadRequest, URL) -> Void)? + + /// Closure called on the `request(_:didValidateRequest:response:temporaryURL:destinationURL:withResult:)` event. + open var requestDidValidateRequestResponseFileURLWithResult: ((DownloadRequest, URLRequest?, HTTPURLResponse, URL?, Request.ValidationResult) -> Void)? + + /// Closure called on the `request(_:didParseResponse:)` event. + open var requestDidParseDownloadResponse: ((DownloadRequest, DownloadResponse) -> Void)? + + public let queue: DispatchQueue + + /// Creates an instance using the provided queue. + /// + /// - Parameter queue: `DispatchQueue` on which events will fired. `.main` by default. + public init(queue: DispatchQueue = .main) { + self.queue = queue + } + + open func urlSession(_ session: URLSession, didBecomeInvalidWithError error: Error?) { + sessionDidBecomeInvalidWithError?(session, error) + } + + open func urlSession(_ session: URLSession, task: URLSessionTask, didReceive challenge: URLAuthenticationChallenge) { + taskDidReceiveChallenge?(session, task, challenge) + } + + open func urlSession(_ session: URLSession, + task: URLSessionTask, + didSendBodyData bytesSent: Int64, + totalBytesSent: Int64, + totalBytesExpectedToSend: Int64) { + taskDidSendBodyData?(session, task, bytesSent, totalBytesSent, totalBytesExpectedToSend) + } + + open func urlSession(_ session: URLSession, taskNeedsNewBodyStream task: URLSessionTask) { + taskNeedNewBodyStream?(session, task) + } + + open func urlSession(_ session: URLSession, + task: URLSessionTask, + willPerformHTTPRedirection response: HTTPURLResponse, + newRequest request: URLRequest) { + taskWillPerformHTTPRedirection?(session, task, response, request) + } + + open func urlSession(_ session: URLSession, task: URLSessionTask, didFinishCollecting metrics: URLSessionTaskMetrics) { + taskDidFinishCollectingMetrics?(session, task, metrics) + } + + open func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) { + taskDidComplete?(session, task, error) + } + + @available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *) + open func urlSession(_ session: URLSession, taskIsWaitingForConnectivity task: URLSessionTask) { + taskIsWaitingForConnectivity?(session, task) + } + + open func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse) { + dataTaskDidReceiveResponse?(session, dataTask, response) + } + + open func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) { + dataTaskDidReceiveData?(session, dataTask, data) + } + + open func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, willCacheResponse proposedResponse: CachedURLResponse) { + dataTaskWillCacheResponse?(session, dataTask, proposedResponse) + } + + open func urlSession(_ session: URLSession, + downloadTask: URLSessionDownloadTask, + didResumeAtOffset fileOffset: Int64, + expectedTotalBytes: Int64) { + downloadTaskDidResumeAtOffset?(session, downloadTask, fileOffset, expectedTotalBytes) + } + + open func urlSession(_ session: URLSession, + downloadTask: URLSessionDownloadTask, + didWriteData bytesWritten: Int64, + totalBytesWritten: Int64, + totalBytesExpectedToWrite: Int64) { + downloadTaskDidWriteData?(session, downloadTask, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite) + } + + open func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) { + downloadTaskDidFinishDownloadingToURL?(session, downloadTask, location) + } + + // MARK: Request Events + + open func request(_ request: Request, didCreateInitialURLRequest urlRequest: URLRequest) { + requestDidCreateInitialURLRequest?(request, urlRequest) + } + + open func request(_ request: Request, didFailToCreateURLRequestWithError error: AFError) { + requestDidFailToCreateURLRequestWithError?(request, error) + } + + open func request(_ request: Request, didAdaptInitialRequest initialRequest: URLRequest, to adaptedRequest: URLRequest) { + requestDidAdaptInitialRequestToAdaptedRequest?(request, initialRequest, adaptedRequest) + } + + open func request(_ request: Request, didFailToAdaptURLRequest initialRequest: URLRequest, withError error: AFError) { + requestDidFailToAdaptURLRequestWithError?(request, initialRequest, error) + } + + open func request(_ request: Request, didCreateURLRequest urlRequest: URLRequest) { + requestDidCreateURLRequest?(request, urlRequest) + } + + open func request(_ request: Request, didCreateTask task: URLSessionTask) { + requestDidCreateTask?(request, task) + } + + open func request(_ request: Request, didGatherMetrics metrics: URLSessionTaskMetrics) { + requestDidGatherMetrics?(request, metrics) + } + + open func request(_ request: Request, didFailTask task: URLSessionTask, earlyWithError error: AFError) { + requestDidFailTaskEarlyWithError?(request, task, error) + } + + open func request(_ request: Request, didCompleteTask task: URLSessionTask, with error: AFError?) { + requestDidCompleteTaskWithError?(request, task, error) + } + + open func requestIsRetrying(_ request: Request) { + requestIsRetrying?(request) + } + + open func requestDidFinish(_ request: Request) { + requestDidFinish?(request) + } + + open func requestDidResume(_ request: Request) { + requestDidResume?(request) + } + + public func request(_ request: Request, didResumeTask task: URLSessionTask) { + requestDidResumeTask?(request, task) + } + + open func requestDidSuspend(_ request: Request) { + requestDidSuspend?(request) + } + + public func request(_ request: Request, didSuspendTask task: URLSessionTask) { + requestDidSuspendTask?(request, task) + } + + open func requestDidCancel(_ request: Request) { + requestDidCancel?(request) + } + + public func request(_ request: Request, didCancelTask task: URLSessionTask) { + requestDidCancelTask?(request, task) + } + + open func request(_ request: DataRequest, + didValidateRequest urlRequest: URLRequest?, + response: HTTPURLResponse, + data: Data?, + withResult result: Request.ValidationResult) { + requestDidValidateRequestResponseDataWithResult?(request, urlRequest, response, data, result) + } + + open func request(_ request: DataRequest, didParseResponse response: DataResponse) { + requestDidParseResponse?(request, response) + } + + public func request(_ request: DataStreamRequest, didValidateRequest urlRequest: URLRequest?, response: HTTPURLResponse, withResult result: Request.ValidationResult) { + requestDidValidateRequestResponseWithResult?(request, urlRequest, response, result) + } + + open func request(_ request: UploadRequest, didCreateUploadable uploadable: UploadRequest.Uploadable) { + requestDidCreateUploadable?(request, uploadable) + } + + open func request(_ request: UploadRequest, didFailToCreateUploadableWithError error: AFError) { + requestDidFailToCreateUploadableWithError?(request, error) + } + + open func request(_ request: UploadRequest, didProvideInputStream stream: InputStream) { + requestDidProvideInputStream?(request, stream) + } + + open func request(_ request: DownloadRequest, didFinishDownloadingUsing task: URLSessionTask, with result: Result) { + requestDidFinishDownloadingUsingTaskWithResult?(request, task, result) + } + + open func request(_ request: DownloadRequest, didCreateDestinationURL url: URL) { + requestDidCreateDestinationURL?(request, url) + } + + open func request(_ request: DownloadRequest, + didValidateRequest urlRequest: URLRequest?, + response: HTTPURLResponse, + fileURL: URL?, + withResult result: Request.ValidationResult) { + requestDidValidateRequestResponseFileURLWithResult?(request, + urlRequest, + response, + fileURL, + result) + } + + open func request(_ request: DownloadRequest, didParseResponse response: DownloadResponse) { + requestDidParseDownloadResponse?(request, response) + } +} diff --git a/Pods/Alamofire/Source/Features/MultipartFormData.swift b/Pods/Alamofire/Source/Features/MultipartFormData.swift new file mode 100644 index 0000000..9c908b2 --- /dev/null +++ b/Pods/Alamofire/Source/Features/MultipartFormData.swift @@ -0,0 +1,601 @@ +// +// MultipartFormData.swift +// +// Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +import Foundation + +#if canImport(MobileCoreServices) +import MobileCoreServices +#elseif canImport(CoreServices) +import CoreServices +#endif + +/// Constructs `multipart/form-data` for uploads within an HTTP or HTTPS body. There are currently two ways to encode +/// multipart form data. The first way is to encode the data directly in memory. This is very efficient, but can lead +/// to memory issues if the dataset is too large. The second way is designed for larger datasets and will write all the +/// data to a single file on disk with all the proper boundary segmentation. The second approach MUST be used for +/// larger datasets such as video content, otherwise your app may run out of memory when trying to encode the dataset. +/// +/// For more information on `multipart/form-data` in general, please refer to the RFC-2388 and RFC-2045 specs as well +/// and the w3 form documentation. +/// +/// - https://www.ietf.org/rfc/rfc2388.txt +/// - https://www.ietf.org/rfc/rfc2045.txt +/// - https://www.w3.org/TR/html401/interact/forms.html#h-17.13 +open class MultipartFormData { + // MARK: - Helper Types + + enum EncodingCharacters { + static let crlf = "\r\n" + } + + enum BoundaryGenerator { + enum BoundaryType { + case initial, encapsulated, final + } + + static func randomBoundary() -> String { + let first = UInt32.random(in: UInt32.min...UInt32.max) + let second = UInt32.random(in: UInt32.min...UInt32.max) + + return String(format: "alamofire.boundary.%08x%08x", first, second) + } + + static func boundaryData(forBoundaryType boundaryType: BoundaryType, boundary: String) -> Data { + let boundaryText: String + + switch boundaryType { + case .initial: + boundaryText = "--\(boundary)\(EncodingCharacters.crlf)" + case .encapsulated: + boundaryText = "\(EncodingCharacters.crlf)--\(boundary)\(EncodingCharacters.crlf)" + case .final: + boundaryText = "\(EncodingCharacters.crlf)--\(boundary)--\(EncodingCharacters.crlf)" + } + + return Data(boundaryText.utf8) + } + } + + class BodyPart { + let headers: HTTPHeaders + let bodyStream: InputStream + let bodyContentLength: UInt64 + var hasInitialBoundary = false + var hasFinalBoundary = false + + init(headers: HTTPHeaders, bodyStream: InputStream, bodyContentLength: UInt64) { + self.headers = headers + self.bodyStream = bodyStream + self.bodyContentLength = bodyContentLength + } + } + + // MARK: - Properties + + /// Default memory threshold used when encoding `MultipartFormData`, in bytes. + public static let encodingMemoryThreshold: UInt64 = 10_000_000 + + /// The `Content-Type` header value containing the boundary used to generate the `multipart/form-data`. + open lazy var contentType: String = "multipart/form-data; boundary=\(self.boundary)" + + /// The content length of all body parts used to generate the `multipart/form-data` not including the boundaries. + public var contentLength: UInt64 { bodyParts.reduce(0) { $0 + $1.bodyContentLength } } + + /// The boundary used to separate the body parts in the encoded form data. + public let boundary: String + + let fileManager: FileManager + + private var bodyParts: [BodyPart] + private var bodyPartError: AFError? + private let streamBufferSize: Int + + // MARK: - Lifecycle + + /// Creates an instance. + /// + /// - Parameters: + /// - fileManager: `FileManager` to use for file operations, if needed. + /// - boundary: Boundary `String` used to separate body parts. + public init(fileManager: FileManager = .default, boundary: String? = nil) { + self.fileManager = fileManager + self.boundary = boundary ?? BoundaryGenerator.randomBoundary() + bodyParts = [] + + // + // The optimal read/write buffer size in bytes for input and output streams is 1024 (1KB). For more + // information, please refer to the following article: + // - https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Streams/Articles/ReadingInputStreams.html + // + streamBufferSize = 1024 + } + + // MARK: - Body Parts + + /// Creates a body part from the data and appends it to the instance. + /// + /// The body part data will be encoded using the following format: + /// + /// - `Content-Disposition: form-data; name=#{name}; filename=#{filename}` (HTTP Header) + /// - `Content-Type: #{mimeType}` (HTTP Header) + /// - Encoded file data + /// - Multipart form boundary + /// + /// - Parameters: + /// - data: `Data` to encoding into the instance. + /// - name: Name to associate with the `Data` in the `Content-Disposition` HTTP header. + /// - fileName: Filename to associate with the `Data` in the `Content-Disposition` HTTP header. + /// - mimeType: MIME type to associate with the data in the `Content-Type` HTTP header. + public func append(_ data: Data, withName name: String, fileName: String? = nil, mimeType: String? = nil) { + let headers = contentHeaders(withName: name, fileName: fileName, mimeType: mimeType) + let stream = InputStream(data: data) + let length = UInt64(data.count) + + append(stream, withLength: length, headers: headers) + } + + /// Creates a body part from the file and appends it to the instance. + /// + /// The body part data will be encoded using the following format: + /// + /// - `Content-Disposition: form-data; name=#{name}; filename=#{generated filename}` (HTTP Header) + /// - `Content-Type: #{generated mimeType}` (HTTP Header) + /// - Encoded file data + /// - Multipart form boundary + /// + /// The filename in the `Content-Disposition` HTTP header is generated from the last path component of the + /// `fileURL`. The `Content-Type` HTTP header MIME type is generated by mapping the `fileURL` extension to the + /// system associated MIME type. + /// + /// - Parameters: + /// - fileURL: `URL` of the file whose content will be encoded into the instance. + /// - name: Name to associate with the file content in the `Content-Disposition` HTTP header. + public func append(_ fileURL: URL, withName name: String) { + let fileName = fileURL.lastPathComponent + let pathExtension = fileURL.pathExtension + + if !fileName.isEmpty && !pathExtension.isEmpty { + let mime = mimeType(forPathExtension: pathExtension) + append(fileURL, withName: name, fileName: fileName, mimeType: mime) + } else { + setBodyPartError(withReason: .bodyPartFilenameInvalid(in: fileURL)) + } + } + + /// Creates a body part from the file and appends it to the instance. + /// + /// The body part data will be encoded using the following format: + /// + /// - Content-Disposition: form-data; name=#{name}; filename=#{filename} (HTTP Header) + /// - Content-Type: #{mimeType} (HTTP Header) + /// - Encoded file data + /// - Multipart form boundary + /// + /// - Parameters: + /// - fileURL: `URL` of the file whose content will be encoded into the instance. + /// - name: Name to associate with the file content in the `Content-Disposition` HTTP header. + /// - fileName: Filename to associate with the file content in the `Content-Disposition` HTTP header. + /// - mimeType: MIME type to associate with the file content in the `Content-Type` HTTP header. + public func append(_ fileURL: URL, withName name: String, fileName: String, mimeType: String) { + let headers = contentHeaders(withName: name, fileName: fileName, mimeType: mimeType) + + //============================================================ + // Check 1 - is file URL? + //============================================================ + + guard fileURL.isFileURL else { + setBodyPartError(withReason: .bodyPartURLInvalid(url: fileURL)) + return + } + + //============================================================ + // Check 2 - is file URL reachable? + //============================================================ + + #if !(os(Linux) || os(Windows) || os(Android)) + do { + let isReachable = try fileURL.checkPromisedItemIsReachable() + guard isReachable else { + setBodyPartError(withReason: .bodyPartFileNotReachable(at: fileURL)) + return + } + } catch { + setBodyPartError(withReason: .bodyPartFileNotReachableWithError(atURL: fileURL, error: error)) + return + } + #endif + + //============================================================ + // Check 3 - is file URL a directory? + //============================================================ + + var isDirectory: ObjCBool = false + let path = fileURL.path + + guard fileManager.fileExists(atPath: path, isDirectory: &isDirectory) && !isDirectory.boolValue else { + setBodyPartError(withReason: .bodyPartFileIsDirectory(at: fileURL)) + return + } + + //============================================================ + // Check 4 - can the file size be extracted? + //============================================================ + + let bodyContentLength: UInt64 + + do { + guard let fileSize = try fileManager.attributesOfItem(atPath: path)[.size] as? NSNumber else { + setBodyPartError(withReason: .bodyPartFileSizeNotAvailable(at: fileURL)) + return + } + + bodyContentLength = fileSize.uint64Value + } catch { + setBodyPartError(withReason: .bodyPartFileSizeQueryFailedWithError(forURL: fileURL, error: error)) + return + } + + //============================================================ + // Check 5 - can a stream be created from file URL? + //============================================================ + + guard let stream = InputStream(url: fileURL) else { + setBodyPartError(withReason: .bodyPartInputStreamCreationFailed(for: fileURL)) + return + } + + append(stream, withLength: bodyContentLength, headers: headers) + } + + /// Creates a body part from the stream and appends it to the instance. + /// + /// The body part data will be encoded using the following format: + /// + /// - `Content-Disposition: form-data; name=#{name}; filename=#{filename}` (HTTP Header) + /// - `Content-Type: #{mimeType}` (HTTP Header) + /// - Encoded stream data + /// - Multipart form boundary + /// + /// - Parameters: + /// - stream: `InputStream` to encode into the instance. + /// - length: Length, in bytes, of the stream. + /// - name: Name to associate with the stream content in the `Content-Disposition` HTTP header. + /// - fileName: Filename to associate with the stream content in the `Content-Disposition` HTTP header. + /// - mimeType: MIME type to associate with the stream content in the `Content-Type` HTTP header. + public func append(_ stream: InputStream, + withLength length: UInt64, + name: String, + fileName: String, + mimeType: String) { + let headers = contentHeaders(withName: name, fileName: fileName, mimeType: mimeType) + append(stream, withLength: length, headers: headers) + } + + /// Creates a body part with the stream, length, and headers and appends it to the instance. + /// + /// The body part data will be encoded using the following format: + /// + /// - HTTP headers + /// - Encoded stream data + /// - Multipart form boundary + /// + /// - Parameters: + /// - stream: `InputStream` to encode into the instance. + /// - length: Length, in bytes, of the stream. + /// - headers: `HTTPHeaders` for the body part. + public func append(_ stream: InputStream, withLength length: UInt64, headers: HTTPHeaders) { + let bodyPart = BodyPart(headers: headers, bodyStream: stream, bodyContentLength: length) + bodyParts.append(bodyPart) + } + + // MARK: - Data Encoding + + /// Encodes all appended body parts into a single `Data` value. + /// + /// - Note: This method will load all the appended body parts into memory all at the same time. This method should + /// only be used when the encoded data will have a small memory footprint. For large data cases, please use + /// the `writeEncodedData(to:))` method. + /// + /// - Returns: The encoded `Data`, if encoding is successful. + /// - Throws: An `AFError` if encoding encounters an error. + public func encode() throws -> Data { + if let bodyPartError { + throw bodyPartError + } + + var encoded = Data() + + bodyParts.first?.hasInitialBoundary = true + bodyParts.last?.hasFinalBoundary = true + + for bodyPart in bodyParts { + let encodedData = try encode(bodyPart) + encoded.append(encodedData) + } + + return encoded + } + + /// Writes all appended body parts to the given file `URL`. + /// + /// This process is facilitated by reading and writing with input and output streams, respectively. Thus, + /// this approach is very memory efficient and should be used for large body part data. + /// + /// - Parameter fileURL: File `URL` to which to write the form data. + /// - Throws: An `AFError` if encoding encounters an error. + public func writeEncodedData(to fileURL: URL) throws { + if let bodyPartError { + throw bodyPartError + } + + if fileManager.fileExists(atPath: fileURL.path) { + throw AFError.multipartEncodingFailed(reason: .outputStreamFileAlreadyExists(at: fileURL)) + } else if !fileURL.isFileURL { + throw AFError.multipartEncodingFailed(reason: .outputStreamURLInvalid(url: fileURL)) + } + + guard let outputStream = OutputStream(url: fileURL, append: false) else { + throw AFError.multipartEncodingFailed(reason: .outputStreamCreationFailed(for: fileURL)) + } + + outputStream.open() + defer { outputStream.close() } + + bodyParts.first?.hasInitialBoundary = true + bodyParts.last?.hasFinalBoundary = true + + for bodyPart in bodyParts { + try write(bodyPart, to: outputStream) + } + } + + // MARK: - Private - Body Part Encoding + + private func encode(_ bodyPart: BodyPart) throws -> Data { + var encoded = Data() + + let initialData = bodyPart.hasInitialBoundary ? initialBoundaryData() : encapsulatedBoundaryData() + encoded.append(initialData) + + let headerData = encodeHeaders(for: bodyPart) + encoded.append(headerData) + + let bodyStreamData = try encodeBodyStream(for: bodyPart) + encoded.append(bodyStreamData) + + if bodyPart.hasFinalBoundary { + encoded.append(finalBoundaryData()) + } + + return encoded + } + + private func encodeHeaders(for bodyPart: BodyPart) -> Data { + let headerText = bodyPart.headers.map { "\($0.name): \($0.value)\(EncodingCharacters.crlf)" } + .joined() + + EncodingCharacters.crlf + + return Data(headerText.utf8) + } + + private func encodeBodyStream(for bodyPart: BodyPart) throws -> Data { + let inputStream = bodyPart.bodyStream + inputStream.open() + defer { inputStream.close() } + + var encoded = Data() + + while inputStream.hasBytesAvailable { + var buffer = [UInt8](repeating: 0, count: streamBufferSize) + let bytesRead = inputStream.read(&buffer, maxLength: streamBufferSize) + + if let error = inputStream.streamError { + throw AFError.multipartEncodingFailed(reason: .inputStreamReadFailed(error: error)) + } + + if bytesRead > 0 { + encoded.append(buffer, count: bytesRead) + } else { + break + } + } + + guard UInt64(encoded.count) == bodyPart.bodyContentLength else { + let error = AFError.UnexpectedInputStreamLength(bytesExpected: bodyPart.bodyContentLength, + bytesRead: UInt64(encoded.count)) + throw AFError.multipartEncodingFailed(reason: .inputStreamReadFailed(error: error)) + } + + return encoded + } + + // MARK: - Private - Writing Body Part to Output Stream + + private func write(_ bodyPart: BodyPart, to outputStream: OutputStream) throws { + try writeInitialBoundaryData(for: bodyPart, to: outputStream) + try writeHeaderData(for: bodyPart, to: outputStream) + try writeBodyStream(for: bodyPart, to: outputStream) + try writeFinalBoundaryData(for: bodyPart, to: outputStream) + } + + private func writeInitialBoundaryData(for bodyPart: BodyPart, to outputStream: OutputStream) throws { + let initialData = bodyPart.hasInitialBoundary ? initialBoundaryData() : encapsulatedBoundaryData() + return try write(initialData, to: outputStream) + } + + private func writeHeaderData(for bodyPart: BodyPart, to outputStream: OutputStream) throws { + let headerData = encodeHeaders(for: bodyPart) + return try write(headerData, to: outputStream) + } + + private func writeBodyStream(for bodyPart: BodyPart, to outputStream: OutputStream) throws { + let inputStream = bodyPart.bodyStream + + inputStream.open() + defer { inputStream.close() } + + var bytesLeftToRead = bodyPart.bodyContentLength + while inputStream.hasBytesAvailable && bytesLeftToRead > 0 { + let bufferSize = min(streamBufferSize, Int(bytesLeftToRead)) + var buffer = [UInt8](repeating: 0, count: bufferSize) + let bytesRead = inputStream.read(&buffer, maxLength: bufferSize) + + if let streamError = inputStream.streamError { + throw AFError.multipartEncodingFailed(reason: .inputStreamReadFailed(error: streamError)) + } + + if bytesRead > 0 { + if buffer.count != bytesRead { + buffer = Array(buffer[0.. 0, outputStream.hasSpaceAvailable { + let bytesWritten = outputStream.write(buffer, maxLength: bytesToWrite) + + if let error = outputStream.streamError { + throw AFError.multipartEncodingFailed(reason: .outputStreamWriteFailed(error: error)) + } + + bytesToWrite -= bytesWritten + + if bytesToWrite > 0 { + buffer = Array(buffer[bytesWritten.. HTTPHeaders { + var disposition = "form-data; name=\"\(name)\"" + if let fileName { disposition += "; filename=\"\(fileName)\"" } + + var headers: HTTPHeaders = [.contentDisposition(disposition)] + if let mimeType { headers.add(.contentType(mimeType)) } + + return headers + } + + // MARK: - Private - Boundary Encoding + + private func initialBoundaryData() -> Data { + BoundaryGenerator.boundaryData(forBoundaryType: .initial, boundary: boundary) + } + + private func encapsulatedBoundaryData() -> Data { + BoundaryGenerator.boundaryData(forBoundaryType: .encapsulated, boundary: boundary) + } + + private func finalBoundaryData() -> Data { + BoundaryGenerator.boundaryData(forBoundaryType: .final, boundary: boundary) + } + + // MARK: - Private - Errors + + private func setBodyPartError(withReason reason: AFError.MultipartEncodingFailureReason) { + guard bodyPartError == nil else { return } + bodyPartError = AFError.multipartEncodingFailed(reason: reason) + } +} + +#if canImport(UniformTypeIdentifiers) +import UniformTypeIdentifiers + +extension MultipartFormData { + // MARK: - Private - Mime Type + + private func mimeType(forPathExtension pathExtension: String) -> String { + #if swift(>=5.9) + if #available(iOS 14, macOS 11, tvOS 14, watchOS 7, visionOS 1, *) { + return UTType(filenameExtension: pathExtension)?.preferredMIMEType ?? "application/octet-stream" + } else { + if + let id = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, pathExtension as CFString, nil)?.takeRetainedValue(), + let contentType = UTTypeCopyPreferredTagWithClass(id, kUTTagClassMIMEType)?.takeRetainedValue() { + return contentType as String + } + + return "application/octet-stream" + } + #else + if #available(iOS 14, macOS 11, tvOS 14, watchOS 7, *) { + return UTType(filenameExtension: pathExtension)?.preferredMIMEType ?? "application/octet-stream" + } else { + if + let id = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, pathExtension as CFString, nil)?.takeRetainedValue(), + let contentType = UTTypeCopyPreferredTagWithClass(id, kUTTagClassMIMEType)?.takeRetainedValue() { + return contentType as String + } + + return "application/octet-stream" + } + #endif + } +} + +#else + +extension MultipartFormData { + // MARK: - Private - Mime Type + + private func mimeType(forPathExtension pathExtension: String) -> String { + #if canImport(CoreServices) || canImport(MobileCoreServices) + if + let id = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, pathExtension as CFString, nil)?.takeRetainedValue(), + let contentType = UTTypeCopyPreferredTagWithClass(id, kUTTagClassMIMEType)?.takeRetainedValue() { + return contentType as String + } + #endif + + return "application/octet-stream" + } +} + +#endif diff --git a/Pods/Alamofire/Source/Features/MultipartUpload.swift b/Pods/Alamofire/Source/Features/MultipartUpload.swift new file mode 100644 index 0000000..ae905bd --- /dev/null +++ b/Pods/Alamofire/Source/Features/MultipartUpload.swift @@ -0,0 +1,89 @@ +// +// MultipartUpload.swift +// +// Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +import Foundation + +/// Internal type which encapsulates a `MultipartFormData` upload. +final class MultipartUpload { + lazy var result = Result { try build() } + + private let multipartFormData: Protected + + let encodingMemoryThreshold: UInt64 + let request: URLRequestConvertible + let fileManager: FileManager + + init(encodingMemoryThreshold: UInt64, + request: URLRequestConvertible, + multipartFormData: MultipartFormData) { + self.encodingMemoryThreshold = encodingMemoryThreshold + self.request = request + fileManager = multipartFormData.fileManager + self.multipartFormData = Protected(multipartFormData) + } + + func build() throws -> UploadRequest.Uploadable { + let uploadable: UploadRequest.Uploadable + if multipartFormData.contentLength < encodingMemoryThreshold { + let data = try multipartFormData.read { try $0.encode() } + + uploadable = .data(data) + } else { + let tempDirectoryURL = fileManager.temporaryDirectory + let directoryURL = tempDirectoryURL.appendingPathComponent("org.alamofire.manager/multipart.form.data") + let fileName = UUID().uuidString + let fileURL = directoryURL.appendingPathComponent(fileName) + + try fileManager.createDirectory(at: directoryURL, withIntermediateDirectories: true, attributes: nil) + + do { + try multipartFormData.read { try $0.writeEncodedData(to: fileURL) } + } catch { + // Cleanup after attempted write if it fails. + try? fileManager.removeItem(at: fileURL) + throw error + } + + uploadable = .file(fileURL, shouldRemove: true) + } + + return uploadable + } +} + +extension MultipartUpload: UploadConvertible { + func asURLRequest() throws -> URLRequest { + var urlRequest = try request.asURLRequest() + + multipartFormData.read { multipartFormData in + urlRequest.headers.add(.contentType(multipartFormData.contentType)) + } + + return urlRequest + } + + func createUploadable() throws -> UploadRequest.Uploadable { + try result.get() + } +} diff --git a/Pods/Alamofire/Source/Features/NetworkReachabilityManager.swift b/Pods/Alamofire/Source/Features/NetworkReachabilityManager.swift new file mode 100644 index 0000000..6150d0d --- /dev/null +++ b/Pods/Alamofire/Source/Features/NetworkReachabilityManager.swift @@ -0,0 +1,292 @@ +// +// NetworkReachabilityManager.swift +// +// Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) +// +// 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. +// + +#if canImport(SystemConfiguration) + +import Foundation +import SystemConfiguration + +/// The `NetworkReachabilityManager` class listens for reachability changes of hosts and addresses for both cellular and +/// WiFi network interfaces. +/// +/// Reachability can be used to determine background information about why a network operation failed, or to retry +/// network requests when a connection is established. It should not be used to prevent a user from initiating a network +/// request, as it's possible that an initial request may be required to establish reachability. +open class NetworkReachabilityManager { + /// Defines the various states of network reachability. + public enum NetworkReachabilityStatus { + /// It is unknown whether the network is reachable. + case unknown + /// The network is not reachable. + case notReachable + /// The network is reachable on the associated `ConnectionType`. + case reachable(ConnectionType) + + init(_ flags: SCNetworkReachabilityFlags) { + guard flags.isActuallyReachable else { self = .notReachable; return } + + var networkStatus: NetworkReachabilityStatus = .reachable(.ethernetOrWiFi) + + if flags.isCellular { networkStatus = .reachable(.cellular) } + + self = networkStatus + } + + /// Defines the various connection types detected by reachability flags. + public enum ConnectionType { + /// The connection type is either over Ethernet or WiFi. + case ethernetOrWiFi + /// The connection type is a cellular connection. + case cellular + } + } + + /// A closure executed when the network reachability status changes. The closure takes a single argument: the + /// network reachability status. + public typealias Listener = (NetworkReachabilityStatus) -> Void + + /// Default `NetworkReachabilityManager` for the zero address and a `listenerQueue` of `.main`. + public static let `default` = NetworkReachabilityManager() + + // MARK: - Properties + + /// Whether the network is currently reachable. + open var isReachable: Bool { isReachableOnCellular || isReachableOnEthernetOrWiFi } + + /// Whether the network is currently reachable over the cellular interface. + /// + /// - Note: Using this property to decide whether to make a high or low bandwidth request is not recommended. + /// Instead, set the `allowsCellularAccess` on any `URLRequest`s being issued. + /// + open var isReachableOnCellular: Bool { status == .reachable(.cellular) } + + /// Whether the network is currently reachable over Ethernet or WiFi interface. + open var isReachableOnEthernetOrWiFi: Bool { status == .reachable(.ethernetOrWiFi) } + + /// `DispatchQueue` on which reachability will update. + public let reachabilityQueue = DispatchQueue(label: "org.alamofire.reachabilityQueue") + + /// Flags of the current reachability type, if any. + open var flags: SCNetworkReachabilityFlags? { + var flags = SCNetworkReachabilityFlags() + + return SCNetworkReachabilityGetFlags(reachability, &flags) ? flags : nil + } + + /// The current network reachability status. + open var status: NetworkReachabilityStatus { + flags.map(NetworkReachabilityStatus.init) ?? .unknown + } + + /// Mutable state storage. + struct MutableState { + /// A closure executed when the network reachability status changes. + var listener: Listener? + /// `DispatchQueue` on which listeners will be called. + var listenerQueue: DispatchQueue? + /// Previously calculated status. + var previousStatus: NetworkReachabilityStatus? + } + + /// `SCNetworkReachability` instance providing notifications. + private let reachability: SCNetworkReachability + + /// Protected storage for mutable state. + private let mutableState = Protected(MutableState()) + + // MARK: - Initialization + + /// Creates an instance with the specified host. + /// + /// - Note: The `host` value must *not* contain a scheme, just the hostname. + /// + /// - Parameters: + /// - host: Host used to evaluate network reachability. Must *not* include the scheme (e.g. `https`). + public convenience init?(host: String) { + guard let reachability = SCNetworkReachabilityCreateWithName(nil, host) else { return nil } + + self.init(reachability: reachability) + } + + /// Creates an instance that monitors the address 0.0.0.0. + /// + /// Reachability treats the 0.0.0.0 address as a special token that causes it to monitor the general routing + /// status of the device, both IPv4 and IPv6. + public convenience init?() { + var zero = sockaddr() + zero.sa_len = UInt8(MemoryLayout.size) + zero.sa_family = sa_family_t(AF_INET) + + guard let reachability = SCNetworkReachabilityCreateWithAddress(nil, &zero) else { return nil } + + self.init(reachability: reachability) + } + + private init(reachability: SCNetworkReachability) { + self.reachability = reachability + } + + deinit { + stopListening() + } + + // MARK: - Listening + + /// Starts listening for changes in network reachability status. + /// + /// - Note: Stops and removes any existing listener. + /// + /// - Parameters: + /// - queue: `DispatchQueue` on which to call the `listener` closure. `.main` by default. + /// - listener: `Listener` closure called when reachability changes. + /// + /// - Returns: `true` if listening was started successfully, `false` otherwise. + @discardableResult + open func startListening(onQueue queue: DispatchQueue = .main, + onUpdatePerforming listener: @escaping Listener) -> Bool { + stopListening() + + mutableState.write { state in + state.listenerQueue = queue + state.listener = listener + } + + let weakManager = WeakManager(manager: self) + + var context = SCNetworkReachabilityContext( + version: 0, + info: Unmanaged.passUnretained(weakManager).toOpaque(), + retain: { info in + let unmanaged = Unmanaged.fromOpaque(info) + _ = unmanaged.retain() + + return UnsafeRawPointer(unmanaged.toOpaque()) + }, + release: { info in + let unmanaged = Unmanaged.fromOpaque(info) + unmanaged.release() + }, + copyDescription: { info in + let unmanaged = Unmanaged.fromOpaque(info) + let weakManager = unmanaged.takeUnretainedValue() + let description = weakManager.manager?.flags?.readableDescription ?? "nil" + + return Unmanaged.passRetained(description as CFString) + } + ) + let callback: SCNetworkReachabilityCallBack = { _, flags, info in + guard let info else { return } + + let weakManager = Unmanaged.fromOpaque(info).takeUnretainedValue() + weakManager.manager?.notifyListener(flags) + } + + let queueAdded = SCNetworkReachabilitySetDispatchQueue(reachability, reachabilityQueue) + let callbackAdded = SCNetworkReachabilitySetCallback(reachability, callback, &context) + + // Manually call listener to give initial state, since the framework may not. + if let currentFlags = flags { + reachabilityQueue.async { + self.notifyListener(currentFlags) + } + } + + return callbackAdded && queueAdded + } + + /// Stops listening for changes in network reachability status. + open func stopListening() { + SCNetworkReachabilitySetCallback(reachability, nil, nil) + SCNetworkReachabilitySetDispatchQueue(reachability, nil) + mutableState.write { state in + state.listener = nil + state.listenerQueue = nil + state.previousStatus = nil + } + } + + // MARK: - Internal - Listener Notification + + /// Calls the `listener` closure of the `listenerQueue` if the computed status hasn't changed. + /// + /// - Note: Should only be called from the `reachabilityQueue`. + /// + /// - Parameter flags: `SCNetworkReachabilityFlags` to use to calculate the status. + func notifyListener(_ flags: SCNetworkReachabilityFlags) { + let newStatus = NetworkReachabilityStatus(flags) + + mutableState.write { state in + guard state.previousStatus != newStatus else { return } + + state.previousStatus = newStatus + + let listener = state.listener + state.listenerQueue?.async { listener?(newStatus) } + } + } + + private final class WeakManager { + weak var manager: NetworkReachabilityManager? + + init(manager: NetworkReachabilityManager?) { + self.manager = manager + } + } +} + +// MARK: - + +extension NetworkReachabilityManager.NetworkReachabilityStatus: Equatable {} + +extension SCNetworkReachabilityFlags { + var isReachable: Bool { contains(.reachable) } + var isConnectionRequired: Bool { contains(.connectionRequired) } + var canConnectAutomatically: Bool { contains(.connectionOnDemand) || contains(.connectionOnTraffic) } + var canConnectWithoutUserInteraction: Bool { canConnectAutomatically && !contains(.interventionRequired) } + var isActuallyReachable: Bool { isReachable && (!isConnectionRequired || canConnectWithoutUserInteraction) } + var isCellular: Bool { + #if os(iOS) || os(tvOS) || (swift(>=5.9) && os(visionOS)) + return contains(.isWWAN) + #else + return false + #endif + } + + /// Human readable `String` for all states, to help with debugging. + var readableDescription: String { + let W = isCellular ? "W" : "-" + let R = isReachable ? "R" : "-" + let c = isConnectionRequired ? "c" : "-" + let t = contains(.transientConnection) ? "t" : "-" + let i = contains(.interventionRequired) ? "i" : "-" + let C = contains(.connectionOnTraffic) ? "C" : "-" + let D = contains(.connectionOnDemand) ? "D" : "-" + let l = contains(.isLocalAddress) ? "l" : "-" + let d = contains(.isDirect) ? "d" : "-" + let a = contains(.connectionAutomatic) ? "a" : "-" + + return "\(W)\(R) \(c)\(t)\(i)\(C)\(D)\(l)\(d)\(a)" + } +} +#endif diff --git a/Pods/Alamofire/Source/Features/RedirectHandler.swift b/Pods/Alamofire/Source/Features/RedirectHandler.swift new file mode 100644 index 0000000..cf88abf --- /dev/null +++ b/Pods/Alamofire/Source/Features/RedirectHandler.swift @@ -0,0 +1,111 @@ +// +// RedirectHandler.swift +// +// Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +import Foundation + +/// A type that handles how an HTTP redirect response from a remote server should be redirected to the new request. +public protocol RedirectHandler { + /// Determines how the HTTP redirect response should be redirected to the new request. + /// + /// The `completion` closure should be passed one of three possible options: + /// + /// 1. The new request specified by the redirect (this is the most common use case). + /// 2. A modified version of the new request (you may want to route it somewhere else). + /// 3. A `nil` value to deny the redirect request and return the body of the redirect response. + /// + /// - Parameters: + /// - task: The `URLSessionTask` whose request resulted in a redirect. + /// - request: The `URLRequest` to the new location specified by the redirect response. + /// - response: The `HTTPURLResponse` containing the server's response to the original request. + /// - completion: The closure to execute containing the new `URLRequest`, a modified `URLRequest`, or `nil`. + func task(_ task: URLSessionTask, + willBeRedirectedTo request: URLRequest, + for response: HTTPURLResponse, + completion: @escaping (URLRequest?) -> Void) +} + +// MARK: - + +/// `Redirector` is a convenience `RedirectHandler` making it easy to follow, not follow, or modify a redirect. +public struct Redirector { + /// Defines the behavior of the `Redirector` type. + public enum Behavior { + /// Follow the redirect as defined in the response. + case follow + /// Do not follow the redirect defined in the response. + case doNotFollow + /// Modify the redirect request defined in the response. + case modify((URLSessionTask, URLRequest, HTTPURLResponse) -> URLRequest?) + } + + /// Returns a `Redirector` with a `.follow` `Behavior`. + public static let follow = Redirector(behavior: .follow) + /// Returns a `Redirector` with a `.doNotFollow` `Behavior`. + public static let doNotFollow = Redirector(behavior: .doNotFollow) + + /// The `Behavior` of the `Redirector`. + public let behavior: Behavior + + /// Creates a `Redirector` instance from the `Behavior`. + /// + /// - Parameter behavior: The `Behavior`. + public init(behavior: Behavior) { + self.behavior = behavior + } +} + +// MARK: - + +extension Redirector: RedirectHandler { + public func task(_ task: URLSessionTask, + willBeRedirectedTo request: URLRequest, + for response: HTTPURLResponse, + completion: @escaping (URLRequest?) -> Void) { + switch behavior { + case .follow: + completion(request) + case .doNotFollow: + completion(nil) + case let .modify(closure): + let request = closure(task, request, response) + completion(request) + } + } +} + +extension RedirectHandler where Self == Redirector { + /// Provides a `Redirector` which follows redirects. Equivalent to `Redirector.follow`. + public static var follow: Redirector { .follow } + + /// Provides a `Redirector` which does not follow redirects. Equivalent to `Redirector.doNotFollow`. + public static var doNotFollow: Redirector { .doNotFollow } + + /// Creates a `Redirector` which modifies the redirected `URLRequest` using the provided closure. + /// + /// - Parameter closure: Closure used to modify the redirect. + /// - Returns: The `Redirector`. + public static func modify(using closure: @escaping (URLSessionTask, URLRequest, HTTPURLResponse) -> URLRequest?) -> Redirector { + Redirector(behavior: .modify(closure)) + } +} diff --git a/Pods/Alamofire/Source/Features/RequestCompression.swift b/Pods/Alamofire/Source/Features/RequestCompression.swift new file mode 100644 index 0000000..1f84890 --- /dev/null +++ b/Pods/Alamofire/Source/Features/RequestCompression.swift @@ -0,0 +1,146 @@ +// +// RequestCompression.swift +// +// Copyright (c) 2023 Alamofire Software Foundation (http://alamofire.org/) +// +// 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. +// + +#if canImport(zlib) +import Foundation +import zlib + +/// `RequestAdapter` which compresses outgoing `URLRequest` bodies using the `deflate` `Content-Encoding` and adds the +/// appropriate header. +/// +/// - Note: Most requests to most APIs are small and so would only be slowed down by applying this adapter. Measure the +/// size of your request bodies and the performance impact of using this adapter before use. Using this adapter +/// with already compressed data, such as images, will, at best, have no effect. Additionally, body compression +/// is a synchronous operation, so measuring the performance impact may be important to determine whether you +/// want to use a dedicated `requestQueue` in your `Session` instance. Finally, not all servers support request +/// compression, so test with all of your server configurations before deploying. +@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) +public struct DeflateRequestCompressor: RequestInterceptor { + /// Type that determines the action taken when the `URLRequest` already has a `Content-Encoding` header. + public enum DuplicateHeaderBehavior { + /// Throws a `DuplicateHeaderError`. The default. + case error + /// Replaces the existing header value with `deflate`. + case replace + /// Silently skips compression when the header exists. + case skip + } + + /// `Error` produced when the outgoing `URLRequest` already has a `Content-Encoding` header, when the instance has + /// been configured to produce an error. + public struct DuplicateHeaderError: Error {} + + /// Behavior to use when the outgoing `URLRequest` already has a `Content-Encoding` header. + public let duplicateHeaderBehavior: DuplicateHeaderBehavior + /// Closure which determines whether the outgoing body data should be compressed. + public let shouldCompressBodyData: (_ bodyData: Data) -> Bool + + /// Creates an instance with the provided parameters. + /// + /// - Parameters: + /// - duplicateHeaderBehavior: `DuplicateHeaderBehavior` to use. `.error` by default. + /// - shouldCompressBodyData: Closure which determines whether the outgoing body data should be compressed. `true` by default. + public init(duplicateHeaderBehavior: DuplicateHeaderBehavior = .error, + shouldCompressBodyData: @escaping (_ bodyData: Data) -> Bool = { _ in true }) { + self.duplicateHeaderBehavior = duplicateHeaderBehavior + self.shouldCompressBodyData = shouldCompressBodyData + } + + public func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result) -> Void) { + // No need to compress unless we have body data. No support for compressing streams. + guard let bodyData = urlRequest.httpBody else { + completion(.success(urlRequest)) + return + } + + guard shouldCompressBodyData(bodyData) else { + completion(.success(urlRequest)) + return + } + + if urlRequest.headers.value(for: "Content-Encoding") != nil { + switch duplicateHeaderBehavior { + case .error: + completion(.failure(DuplicateHeaderError())) + return + case .replace: + // Header will be replaced once the body data is compressed. + break + case .skip: + completion(.success(urlRequest)) + return + } + } + + var compressedRequest = urlRequest + + do { + compressedRequest.httpBody = try deflate(bodyData) + compressedRequest.headers.update(.contentEncoding("deflate")) + completion(.success(compressedRequest)) + } catch { + completion(.failure(error)) + } + } + + func deflate(_ data: Data) throws -> Data { + var output = Data([0x78, 0x5E]) // Header + try output.append((data as NSData).compressed(using: .zlib) as Data) + var checksum = adler32Checksum(of: data).bigEndian + output.append(Data(bytes: &checksum, count: MemoryLayout.size)) + + return output + } + + func adler32Checksum(of data: Data) -> UInt32 { + data.withUnsafeBytes { buffer in + UInt32(adler32(1, buffer.baseAddress, UInt32(buffer.count))) + } + } +} + +@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) +extension RequestInterceptor where Self == DeflateRequestCompressor { + /// Create a `DeflateRequestCompressor` with default `duplicateHeaderBehavior` and `shouldCompressBodyData` values. + public static var deflateCompressor: DeflateRequestCompressor { + DeflateRequestCompressor() + } + + /// Creates a `DeflateRequestCompressor` with the provided `DuplicateHeaderBehavior` and `shouldCompressBodyData` + /// closure. + /// + /// - Parameters: + /// - duplicateHeaderBehavior: `DuplicateHeaderBehavior` to use. + /// - shouldCompressBodyData: Closure which determines whether the outgoing body data should be compressed. `true` by default. + /// + /// - Returns: The `DeflateRequestCompressor`. + public static func deflateCompressor( + duplicateHeaderBehavior: DeflateRequestCompressor.DuplicateHeaderBehavior = .error, + shouldCompressBodyData: @escaping (_ bodyData: Data) -> Bool = { _ in true } + ) -> DeflateRequestCompressor { + DeflateRequestCompressor(duplicateHeaderBehavior: duplicateHeaderBehavior, + shouldCompressBodyData: shouldCompressBodyData) + } +} +#endif diff --git a/Pods/Alamofire/Source/Features/RequestInterceptor.swift b/Pods/Alamofire/Source/Features/RequestInterceptor.swift new file mode 100644 index 0000000..9acd986 --- /dev/null +++ b/Pods/Alamofire/Source/Features/RequestInterceptor.swift @@ -0,0 +1,351 @@ +// +// RequestInterceptor.swift +// +// Copyright (c) 2019 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +import Foundation + +/// Stores all state associated with a `URLRequest` being adapted. +public struct RequestAdapterState { + /// The `UUID` of the `Request` associated with the `URLRequest` to adapt. + public let requestID: UUID + + /// The `Session` associated with the `URLRequest` to adapt. + public let session: Session +} + +// MARK: - + +/// A type that can inspect and optionally adapt a `URLRequest` in some manner if necessary. +public protocol RequestAdapter { + /// Inspects and adapts the specified `URLRequest` in some manner and calls the completion handler with the Result. + /// + /// - Parameters: + /// - urlRequest: The `URLRequest` to adapt. + /// - session: The `Session` that will execute the `URLRequest`. + /// - completion: The completion handler that must be called when adaptation is complete. + func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result) -> Void) + + /// Inspects and adapts the specified `URLRequest` in some manner and calls the completion handler with the Result. + /// + /// - Parameters: + /// - urlRequest: The `URLRequest` to adapt. + /// - state: The `RequestAdapterState` associated with the `URLRequest`. + /// - completion: The completion handler that must be called when adaptation is complete. + func adapt(_ urlRequest: URLRequest, using state: RequestAdapterState, completion: @escaping (Result) -> Void) +} + +extension RequestAdapter { + public func adapt(_ urlRequest: URLRequest, using state: RequestAdapterState, completion: @escaping (Result) -> Void) { + adapt(urlRequest, for: state.session, completion: completion) + } +} + +// MARK: - + +/// Outcome of determination whether retry is necessary. +public enum RetryResult { + /// Retry should be attempted immediately. + case retry + /// Retry should be attempted after the associated `TimeInterval`. + case retryWithDelay(TimeInterval) + /// Do not retry. + case doNotRetry + /// Do not retry due to the associated `Error`. + case doNotRetryWithError(Error) +} + +extension RetryResult { + var retryRequired: Bool { + switch self { + case .retry, .retryWithDelay: return true + default: return false + } + } + + var delay: TimeInterval? { + switch self { + case let .retryWithDelay(delay): return delay + default: return nil + } + } + + var error: Error? { + guard case let .doNotRetryWithError(error) = self else { return nil } + return error + } +} + +/// A type that determines whether a request should be retried after being executed by the specified session manager +/// and encountering an error. +public protocol RequestRetrier { + /// Determines whether the `Request` should be retried by calling the `completion` closure. + /// + /// This operation is fully asynchronous. Any amount of time can be taken to determine whether the request needs + /// to be retried. The one requirement is that the completion closure is called to ensure the request is properly + /// cleaned up after. + /// + /// - Parameters: + /// - request: `Request` that failed due to the provided `Error`. + /// - session: `Session` that produced the `Request`. + /// - error: `Error` encountered while executing the `Request`. + /// - completion: Completion closure to be executed when a retry decision has been determined. + func retry(_ request: Request, for session: Session, dueTo error: Error, completion: @escaping (RetryResult) -> Void) +} + +// MARK: - + +/// Type that provides both `RequestAdapter` and `RequestRetrier` functionality. +public protocol RequestInterceptor: RequestAdapter, RequestRetrier {} + +extension RequestInterceptor { + public func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result) -> Void) { + completion(.success(urlRequest)) + } + + public func retry(_ request: Request, + for session: Session, + dueTo error: Error, + completion: @escaping (RetryResult) -> Void) { + completion(.doNotRetry) + } +} + +/// `RequestAdapter` closure definition. +public typealias AdaptHandler = (URLRequest, Session, _ completion: @escaping (Result) -> Void) -> Void +/// `RequestRetrier` closure definition. +public typealias RetryHandler = (Request, Session, Error, _ completion: @escaping (RetryResult) -> Void) -> Void + +// MARK: - + +/// Closure-based `RequestAdapter`. +open class Adapter: RequestInterceptor { + private let adaptHandler: AdaptHandler + + /// Creates an instance using the provided closure. + /// + /// - Parameter adaptHandler: `AdaptHandler` closure to be executed when handling request adaptation. + public init(_ adaptHandler: @escaping AdaptHandler) { + self.adaptHandler = adaptHandler + } + + open func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result) -> Void) { + adaptHandler(urlRequest, session, completion) + } + + open func adapt(_ urlRequest: URLRequest, using state: RequestAdapterState, completion: @escaping (Result) -> Void) { + adaptHandler(urlRequest, state.session, completion) + } +} + +extension RequestAdapter where Self == Adapter { + /// Creates an `Adapter` using the provided `AdaptHandler` closure. + /// + /// - Parameter closure: `AdaptHandler` to use to adapt the request. + /// - Returns: The `Adapter`. + public static func adapter(using closure: @escaping AdaptHandler) -> Adapter { + Adapter(closure) + } +} + +// MARK: - + +/// Closure-based `RequestRetrier`. +open class Retrier: RequestInterceptor { + private let retryHandler: RetryHandler + + /// Creates an instance using the provided closure. + /// + /// - Parameter retryHandler: `RetryHandler` closure to be executed when handling request retry. + public init(_ retryHandler: @escaping RetryHandler) { + self.retryHandler = retryHandler + } + + open func retry(_ request: Request, + for session: Session, + dueTo error: Error, + completion: @escaping (RetryResult) -> Void) { + retryHandler(request, session, error, completion) + } +} + +extension RequestRetrier where Self == Retrier { + /// Creates a `Retrier` using the provided `RetryHandler` closure. + /// + /// - Parameter closure: `RetryHandler` to use to retry the request. + /// - Returns: The `Retrier`. + public static func retrier(using closure: @escaping RetryHandler) -> Retrier { + Retrier(closure) + } +} + +// MARK: - + +/// `RequestInterceptor` which can use multiple `RequestAdapter` and `RequestRetrier` values. +open class Interceptor: RequestInterceptor { + /// All `RequestAdapter`s associated with the instance. These adapters will be run until one fails. + public let adapters: [RequestAdapter] + /// All `RequestRetrier`s associated with the instance. These retriers will be run one at a time until one triggers retry. + public let retriers: [RequestRetrier] + + /// Creates an instance from `AdaptHandler` and `RetryHandler` closures. + /// + /// - Parameters: + /// - adaptHandler: `AdaptHandler` closure to be used. + /// - retryHandler: `RetryHandler` closure to be used. + public init(adaptHandler: @escaping AdaptHandler, retryHandler: @escaping RetryHandler) { + adapters = [Adapter(adaptHandler)] + retriers = [Retrier(retryHandler)] + } + + /// Creates an instance from `RequestAdapter` and `RequestRetrier` values. + /// + /// - Parameters: + /// - adapter: `RequestAdapter` value to be used. + /// - retrier: `RequestRetrier` value to be used. + public init(adapter: RequestAdapter, retrier: RequestRetrier) { + adapters = [adapter] + retriers = [retrier] + } + + /// Creates an instance from the arrays of `RequestAdapter` and `RequestRetrier` values. + /// + /// - Parameters: + /// - adapters: `RequestAdapter` values to be used. + /// - retriers: `RequestRetrier` values to be used. + /// - interceptors: `RequestInterceptor`s to be used. + public init(adapters: [RequestAdapter] = [], retriers: [RequestRetrier] = [], interceptors: [RequestInterceptor] = []) { + self.adapters = adapters + interceptors + self.retriers = retriers + interceptors + } + + open func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result) -> Void) { + adapt(urlRequest, for: session, using: adapters, completion: completion) + } + + private func adapt(_ urlRequest: URLRequest, + for session: Session, + using adapters: [RequestAdapter], + completion: @escaping (Result) -> Void) { + var pendingAdapters = adapters + + guard !pendingAdapters.isEmpty else { completion(.success(urlRequest)); return } + + let adapter = pendingAdapters.removeFirst() + + adapter.adapt(urlRequest, for: session) { result in + switch result { + case let .success(urlRequest): + self.adapt(urlRequest, for: session, using: pendingAdapters, completion: completion) + case .failure: + completion(result) + } + } + } + + open func adapt(_ urlRequest: URLRequest, using state: RequestAdapterState, completion: @escaping (Result) -> Void) { + adapt(urlRequest, using: state, adapters: adapters, completion: completion) + } + + private func adapt(_ urlRequest: URLRequest, + using state: RequestAdapterState, + adapters: [RequestAdapter], + completion: @escaping (Result) -> Void) { + var pendingAdapters = adapters + + guard !pendingAdapters.isEmpty else { completion(.success(urlRequest)); return } + + let adapter = pendingAdapters.removeFirst() + + adapter.adapt(urlRequest, using: state) { result in + switch result { + case let .success(urlRequest): + self.adapt(urlRequest, using: state, adapters: pendingAdapters, completion: completion) + case .failure: + completion(result) + } + } + } + + open func retry(_ request: Request, + for session: Session, + dueTo error: Error, + completion: @escaping (RetryResult) -> Void) { + retry(request, for: session, dueTo: error, using: retriers, completion: completion) + } + + private func retry(_ request: Request, + for session: Session, + dueTo error: Error, + using retriers: [RequestRetrier], + completion: @escaping (RetryResult) -> Void) { + var pendingRetriers = retriers + + guard !pendingRetriers.isEmpty else { completion(.doNotRetry); return } + + let retrier = pendingRetriers.removeFirst() + + retrier.retry(request, for: session, dueTo: error) { result in + switch result { + case .retry, .retryWithDelay, .doNotRetryWithError: + completion(result) + case .doNotRetry: + // Only continue to the next retrier if retry was not triggered and no error was encountered + self.retry(request, for: session, dueTo: error, using: pendingRetriers, completion: completion) + } + } + } +} + +extension RequestInterceptor where Self == Interceptor { + /// Creates an `Interceptor` using the provided `AdaptHandler` and `RetryHandler` closures. + /// + /// - Parameters: + /// - adapter: `AdapterHandler`to use to adapt the request. + /// - retrier: `RetryHandler` to use to retry the request. + /// - Returns: The `Interceptor`. + public static func interceptor(adapter: @escaping AdaptHandler, retrier: @escaping RetryHandler) -> Interceptor { + Interceptor(adaptHandler: adapter, retryHandler: retrier) + } + + /// Creates an `Interceptor` using the provided `RequestAdapter` and `RequestRetrier` instances. + /// - Parameters: + /// - adapter: `RequestAdapter` to use to adapt the request + /// - retrier: `RequestRetrier` to use to retry the request. + /// - Returns: The `Interceptor`. + public static func interceptor(adapter: RequestAdapter, retrier: RequestRetrier) -> Interceptor { + Interceptor(adapter: adapter, retrier: retrier) + } + + /// Creates an `Interceptor` using the provided `RequestAdapter`s, `RequestRetrier`s, and `RequestInterceptor`s. + /// - Parameters: + /// - adapters: `RequestAdapter`s to use to adapt the request. These adapters will be run until one fails. + /// - retriers: `RequestRetrier`s to use to retry the request. These retriers will be run one at a time until + /// a retry is triggered. + /// - interceptors: `RequestInterceptor`s to use to intercept the request. + /// - Returns: The `Interceptor`. + public static func interceptor(adapters: [RequestAdapter] = [], + retriers: [RequestRetrier] = [], + interceptors: [RequestInterceptor] = []) -> Interceptor { + Interceptor(adapters: adapters, retriers: retriers, interceptors: interceptors) + } +} diff --git a/Pods/Alamofire/Source/Features/ResponseSerialization.swift b/Pods/Alamofire/Source/Features/ResponseSerialization.swift new file mode 100644 index 0000000..b765bdb --- /dev/null +++ b/Pods/Alamofire/Source/Features/ResponseSerialization.swift @@ -0,0 +1,525 @@ +// +// ResponseSerialization.swift +// +// Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +import Foundation + +/// The type to which all data response serializers must conform in order to serialize a response. +public protocol DataResponseSerializerProtocol { + /// The type of serialized object to be created. + associatedtype SerializedObject + + /// Serialize the response `Data` into the provided type. + /// + /// - Parameters: + /// - request: `URLRequest` which was used to perform the request, if any. + /// - response: `HTTPURLResponse` received from the server, if any. + /// - data: `Data` returned from the server, if any. + /// - error: `Error` produced by Alamofire or the underlying `URLSession` during the request. + /// + /// - Returns: The `SerializedObject`. + /// - Throws: Any `Error` produced during serialization. + func serialize(request: URLRequest?, response: HTTPURLResponse?, data: Data?, error: Error?) throws -> SerializedObject +} + +/// The type to which all download response serializers must conform in order to serialize a response. +public protocol DownloadResponseSerializerProtocol { + /// The type of serialized object to be created. + associatedtype SerializedObject + + /// Serialize the downloaded response `Data` from disk into the provided type. + /// + /// - Parameters: + /// - request: `URLRequest` which was used to perform the request, if any. + /// - response: `HTTPURLResponse` received from the server, if any. + /// - fileURL: File `URL` to which the response data was downloaded. + /// - error: `Error` produced by Alamofire or the underlying `URLSession` during the request. + /// + /// - Returns: The `SerializedObject`. + /// - Throws: Any `Error` produced during serialization. + func serializeDownload(request: URLRequest?, response: HTTPURLResponse?, fileURL: URL?, error: Error?) throws -> SerializedObject +} + +/// A serializer that can handle both data and download responses. +public protocol ResponseSerializer: DataResponseSerializerProtocol & DownloadResponseSerializerProtocol { + /// `DataPreprocessor` used to prepare incoming `Data` for serialization. + var dataPreprocessor: DataPreprocessor { get } + /// `HTTPMethod`s for which empty response bodies are considered appropriate. + var emptyRequestMethods: Set { get } + /// HTTP response codes for which empty response bodies are considered appropriate. + var emptyResponseCodes: Set { get } +} + +/// Type used to preprocess `Data` before it handled by a serializer. +public protocol DataPreprocessor { + /// Process `Data` before it's handled by a serializer. + /// - Parameter data: The raw `Data` to process. + func preprocess(_ data: Data) throws -> Data +} + +/// `DataPreprocessor` that returns passed `Data` without any transform. +public struct PassthroughPreprocessor: DataPreprocessor { + /// Creates an instance. + public init() {} + + public func preprocess(_ data: Data) throws -> Data { data } +} + +/// `DataPreprocessor` that trims Google's typical `)]}',\n` XSSI JSON header. +public struct GoogleXSSIPreprocessor: DataPreprocessor { + /// Creates an instance. + public init() {} + + public func preprocess(_ data: Data) throws -> Data { + (data.prefix(6) == Data(")]}',\n".utf8)) ? data.dropFirst(6) : data + } +} + +extension DataPreprocessor where Self == PassthroughPreprocessor { + /// Provides a `PassthroughPreprocessor` instance. + public static var passthrough: PassthroughPreprocessor { PassthroughPreprocessor() } +} + +extension DataPreprocessor where Self == GoogleXSSIPreprocessor { + /// Provides a `GoogleXSSIPreprocessor` instance. + public static var googleXSSI: GoogleXSSIPreprocessor { GoogleXSSIPreprocessor() } +} + +extension ResponseSerializer { + /// Default `DataPreprocessor`. `PassthroughPreprocessor` by default. + public static var defaultDataPreprocessor: DataPreprocessor { PassthroughPreprocessor() } + /// Default `HTTPMethod`s for which empty response bodies are always considered appropriate. `[.head]` by default. + public static var defaultEmptyRequestMethods: Set { [.head] } + /// HTTP response codes for which empty response bodies are always considered appropriate. `[204, 205]` by default. + public static var defaultEmptyResponseCodes: Set { [204, 205] } + + public var dataPreprocessor: DataPreprocessor { Self.defaultDataPreprocessor } + public var emptyRequestMethods: Set { Self.defaultEmptyRequestMethods } + public var emptyResponseCodes: Set { Self.defaultEmptyResponseCodes } + + /// Determines whether the `request` allows empty response bodies, if `request` exists. + /// + /// - Parameter request: `URLRequest` to evaluate. + /// + /// - Returns: `Bool` representing the outcome of the evaluation, or `nil` if `request` was `nil`. + public func requestAllowsEmptyResponseData(_ request: URLRequest?) -> Bool? { + request.flatMap(\.httpMethod) + .flatMap(HTTPMethod.init) + .map { emptyRequestMethods.contains($0) } + } + + /// Determines whether the `response` allows empty response bodies, if `response` exists. + /// + /// - Parameter response: `HTTPURLResponse` to evaluate. + /// + /// - Returns: `Bool` representing the outcome of the evaluation, or `nil` if `response` was `nil`. + public func responseAllowsEmptyResponseData(_ response: HTTPURLResponse?) -> Bool? { + response.map(\.statusCode) + .map { emptyResponseCodes.contains($0) } + } + + /// Determines whether `request` and `response` allow empty response bodies. + /// + /// - Parameters: + /// - request: `URLRequest` to evaluate. + /// - response: `HTTPURLResponse` to evaluate. + /// + /// - Returns: `true` if `request` or `response` allow empty bodies, `false` otherwise. + public func emptyResponseAllowed(forRequest request: URLRequest?, response: HTTPURLResponse?) -> Bool { + (requestAllowsEmptyResponseData(request) == true) || (responseAllowsEmptyResponseData(response) == true) + } +} + +/// By default, any serializer declared to conform to both types will get file serialization for free, as it just feeds +/// the data read from disk into the data response serializer. +extension DownloadResponseSerializerProtocol where Self: DataResponseSerializerProtocol { + public func serializeDownload(request: URLRequest?, response: HTTPURLResponse?, fileURL: URL?, error: Error?) throws -> Self.SerializedObject { + guard error == nil else { throw error! } + + guard let fileURL else { + throw AFError.responseSerializationFailed(reason: .inputFileNil) + } + + let data: Data + do { + data = try Data(contentsOf: fileURL) + } catch { + throw AFError.responseSerializationFailed(reason: .inputFileReadFailed(at: fileURL)) + } + + do { + return try serialize(request: request, response: response, data: data, error: error) + } catch { + throw error + } + } +} + +// MARK: - URL + +/// A `DownloadResponseSerializerProtocol` that performs only `Error` checking and ensures that a downloaded `fileURL` +/// is present. +public struct URLResponseSerializer: DownloadResponseSerializerProtocol { + /// Creates an instance. + public init() {} + + public func serializeDownload(request: URLRequest?, + response: HTTPURLResponse?, + fileURL: URL?, + error: Error?) throws -> URL { + guard error == nil else { throw error! } + + guard let url = fileURL else { + throw AFError.responseSerializationFailed(reason: .inputFileNil) + } + + return url + } +} + +extension DownloadResponseSerializerProtocol where Self == URLResponseSerializer { + /// Provides a `URLResponseSerializer` instance. + public static var url: URLResponseSerializer { URLResponseSerializer() } +} + +// MARK: - Data + +/// A `ResponseSerializer` that performs minimal response checking and returns any response `Data` as-is. By default, a +/// request returning `nil` or no data is considered an error. However, if the request has an `HTTPMethod` or the +/// response has an HTTP status code valid for empty responses, then an empty `Data` value is returned. +public final class DataResponseSerializer: ResponseSerializer { + public let dataPreprocessor: DataPreprocessor + public let emptyResponseCodes: Set + public let emptyRequestMethods: Set + + /// Creates a `DataResponseSerializer` using the provided parameters. + /// + /// - Parameters: + /// - dataPreprocessor: `DataPreprocessor` used to prepare the received `Data` for serialization. + /// - emptyResponseCodes: The HTTP response codes for which empty responses are allowed. `[204, 205]` by default. + /// - emptyRequestMethods: The HTTP request methods for which empty responses are allowed. `[.head]` by default. + public init(dataPreprocessor: DataPreprocessor = DataResponseSerializer.defaultDataPreprocessor, + emptyResponseCodes: Set = DataResponseSerializer.defaultEmptyResponseCodes, + emptyRequestMethods: Set = DataResponseSerializer.defaultEmptyRequestMethods) { + self.dataPreprocessor = dataPreprocessor + self.emptyResponseCodes = emptyResponseCodes + self.emptyRequestMethods = emptyRequestMethods + } + + public func serialize(request: URLRequest?, response: HTTPURLResponse?, data: Data?, error: Error?) throws -> Data { + guard error == nil else { throw error! } + + guard var data, !data.isEmpty else { + guard emptyResponseAllowed(forRequest: request, response: response) else { + throw AFError.responseSerializationFailed(reason: .inputDataNilOrZeroLength) + } + + return Data() + } + + data = try dataPreprocessor.preprocess(data) + + return data + } +} + +extension ResponseSerializer where Self == DataResponseSerializer { + /// Provides a default `DataResponseSerializer` instance. + public static var data: DataResponseSerializer { DataResponseSerializer() } + + /// Creates a `DataResponseSerializer` using the provided parameters. + /// + /// - Parameters: + /// - dataPreprocessor: `DataPreprocessor` used to prepare the received `Data` for serialization. + /// - emptyResponseCodes: The HTTP response codes for which empty responses are allowed. `[204, 205]` by default. + /// - emptyRequestMethods: The HTTP request methods for which empty responses are allowed. `[.head]` by default. + /// + /// - Returns: The `DataResponseSerializer`. + public static func data(dataPreprocessor: DataPreprocessor = DataResponseSerializer.defaultDataPreprocessor, + emptyResponseCodes: Set = DataResponseSerializer.defaultEmptyResponseCodes, + emptyRequestMethods: Set = DataResponseSerializer.defaultEmptyRequestMethods) -> DataResponseSerializer { + DataResponseSerializer(dataPreprocessor: dataPreprocessor, + emptyResponseCodes: emptyResponseCodes, + emptyRequestMethods: emptyRequestMethods) + } +} + +// MARK: - String + +/// A `ResponseSerializer` that decodes the response data as a `String`. By default, a request returning `nil` or no +/// data is considered an error. However, if the request has an `HTTPMethod` or the response has an HTTP status code +/// valid for empty responses, then an empty `String` is returned. +public final class StringResponseSerializer: ResponseSerializer { + public let dataPreprocessor: DataPreprocessor + /// Optional string encoding used to validate the response. + public let encoding: String.Encoding? + public let emptyResponseCodes: Set + public let emptyRequestMethods: Set + + /// Creates an instance with the provided values. + /// + /// - Parameters: + /// - dataPreprocessor: `DataPreprocessor` used to prepare the received `Data` for serialization. + /// - encoding: A string encoding. Defaults to `nil`, in which case the encoding will be determined + /// from the server response, falling back to the default HTTP character set, `ISO-8859-1`. + /// - emptyResponseCodes: The HTTP response codes for which empty responses are allowed. `[204, 205]` by default. + /// - emptyRequestMethods: The HTTP request methods for which empty responses are allowed. `[.head]` by default. + public init(dataPreprocessor: DataPreprocessor = StringResponseSerializer.defaultDataPreprocessor, + encoding: String.Encoding? = nil, + emptyResponseCodes: Set = StringResponseSerializer.defaultEmptyResponseCodes, + emptyRequestMethods: Set = StringResponseSerializer.defaultEmptyRequestMethods) { + self.dataPreprocessor = dataPreprocessor + self.encoding = encoding + self.emptyResponseCodes = emptyResponseCodes + self.emptyRequestMethods = emptyRequestMethods + } + + public func serialize(request: URLRequest?, response: HTTPURLResponse?, data: Data?, error: Error?) throws -> String { + guard error == nil else { throw error! } + + guard var data, !data.isEmpty else { + guard emptyResponseAllowed(forRequest: request, response: response) else { + throw AFError.responseSerializationFailed(reason: .inputDataNilOrZeroLength) + } + + return "" + } + + data = try dataPreprocessor.preprocess(data) + + var convertedEncoding = encoding + + if let encodingName = response?.textEncodingName, convertedEncoding == nil { + convertedEncoding = String.Encoding(ianaCharsetName: encodingName) + } + + let actualEncoding = convertedEncoding ?? .isoLatin1 + + guard let string = String(data: data, encoding: actualEncoding) else { + throw AFError.responseSerializationFailed(reason: .stringSerializationFailed(encoding: actualEncoding)) + } + + return string + } +} + +extension ResponseSerializer where Self == StringResponseSerializer { + /// Provides a default `StringResponseSerializer` instance. + public static var string: StringResponseSerializer { StringResponseSerializer() } + + /// Creates a `StringResponseSerializer` with the provided values. + /// + /// - Parameters: + /// - dataPreprocessor: `DataPreprocessor` used to prepare the received `Data` for serialization. + /// - encoding: A string encoding. Defaults to `nil`, in which case the encoding will be determined + /// from the server response, falling back to the default HTTP character set, `ISO-8859-1`. + /// - emptyResponseCodes: The HTTP response codes for which empty responses are allowed. `[204, 205]` by default. + /// - emptyRequestMethods: The HTTP request methods for which empty responses are allowed. `[.head]` by default. + /// + /// - Returns: The `StringResponseSerializer`. + public static func string(dataPreprocessor: DataPreprocessor = StringResponseSerializer.defaultDataPreprocessor, + encoding: String.Encoding? = nil, + emptyResponseCodes: Set = StringResponseSerializer.defaultEmptyResponseCodes, + emptyRequestMethods: Set = StringResponseSerializer.defaultEmptyRequestMethods) -> StringResponseSerializer { + StringResponseSerializer(dataPreprocessor: dataPreprocessor, + encoding: encoding, + emptyResponseCodes: emptyResponseCodes, + emptyRequestMethods: emptyRequestMethods) + } +} + +// MARK: - JSON + +/// A `ResponseSerializer` that decodes the response data using `JSONSerialization`. By default, a request returning +/// `nil` or no data is considered an error. However, if the request has an `HTTPMethod` or the response has an +/// HTTP status code valid for empty responses, then an `NSNull` value is returned. +/// +/// - Note: This serializer is deprecated and should not be used. Instead, create concrete types conforming to +/// `Decodable` and use a `DecodableResponseSerializer`. +@available(*, deprecated, message: "JSONResponseSerializer deprecated and will be removed in Alamofire 6. Use DecodableResponseSerializer instead.") +public final class JSONResponseSerializer: ResponseSerializer { + public let dataPreprocessor: DataPreprocessor + public let emptyResponseCodes: Set + public let emptyRequestMethods: Set + /// `JSONSerialization.ReadingOptions` used when serializing a response. + public let options: JSONSerialization.ReadingOptions + + /// Creates an instance with the provided values. + /// + /// - Parameters: + /// - dataPreprocessor: `DataPreprocessor` used to prepare the received `Data` for serialization. + /// - emptyResponseCodes: The HTTP response codes for which empty responses are allowed. `[204, 205]` by default. + /// - emptyRequestMethods: The HTTP request methods for which empty responses are allowed. `[.head]` by default. + /// - options: The options to use. `.allowFragments` by default. + public init(dataPreprocessor: DataPreprocessor = JSONResponseSerializer.defaultDataPreprocessor, + emptyResponseCodes: Set = JSONResponseSerializer.defaultEmptyResponseCodes, + emptyRequestMethods: Set = JSONResponseSerializer.defaultEmptyRequestMethods, + options: JSONSerialization.ReadingOptions = .allowFragments) { + self.dataPreprocessor = dataPreprocessor + self.emptyResponseCodes = emptyResponseCodes + self.emptyRequestMethods = emptyRequestMethods + self.options = options + } + + public func serialize(request: URLRequest?, response: HTTPURLResponse?, data: Data?, error: Error?) throws -> Any { + guard error == nil else { throw error! } + + guard var data, !data.isEmpty else { + guard emptyResponseAllowed(forRequest: request, response: response) else { + throw AFError.responseSerializationFailed(reason: .inputDataNilOrZeroLength) + } + + return NSNull() + } + + data = try dataPreprocessor.preprocess(data) + + do { + return try JSONSerialization.jsonObject(with: data, options: options) + } catch { + throw AFError.responseSerializationFailed(reason: .jsonSerializationFailed(error: error)) + } + } +} + +// MARK: - Empty + +/// Protocol representing an empty response. Use `T.emptyValue()` to get an instance. +public protocol EmptyResponse { + /// Empty value for the conforming type. + /// + /// - Returns: Value of `Self` to use for empty values. + static func emptyValue() -> Self +} + +/// Type representing an empty value. Use `Empty.value` to get the static instance. +public struct Empty: Codable, Sendable { + /// Static `Empty` instance used for all `Empty` responses. + public static let value = Empty() +} + +extension Empty: EmptyResponse { + public static func emptyValue() -> Empty { + value + } +} + +// MARK: - DataDecoder Protocol + +/// Any type which can decode `Data` into a `Decodable` type. +public protocol DataDecoder { + /// Decode `Data` into the provided type. + /// + /// - Parameters: + /// - type: The `Type` to be decoded. + /// - data: The `Data` to be decoded. + /// + /// - Returns: The decoded value of type `D`. + /// - Throws: Any error that occurs during decode. + func decode(_ type: D.Type, from data: Data) throws -> D +} + +/// `JSONDecoder` automatically conforms to `DataDecoder`. +extension JSONDecoder: DataDecoder {} +/// `PropertyListDecoder` automatically conforms to `DataDecoder`. +extension PropertyListDecoder: DataDecoder {} + +// MARK: - Decodable + +/// A `ResponseSerializer` that decodes the response data as a generic value using any type that conforms to +/// `DataDecoder`. By default, this is an instance of `JSONDecoder`. Additionally, a request returning `nil` or no data +/// is considered an error. However, if the request has an `HTTPMethod` or the response has an HTTP status code valid +/// for empty responses then an empty value will be returned. If the decoded type conforms to `EmptyResponse`, the +/// type's `emptyValue()` will be returned. If the decoded type is `Empty`, the `.value` instance is returned. If the +/// decoded type *does not* conform to `EmptyResponse` and isn't `Empty`, an error will be produced. +public final class DecodableResponseSerializer: ResponseSerializer { + public let dataPreprocessor: DataPreprocessor + /// The `DataDecoder` instance used to decode responses. + public let decoder: DataDecoder + public let emptyResponseCodes: Set + public let emptyRequestMethods: Set + + /// Creates an instance using the values provided. + /// + /// - Parameters: + /// - dataPreprocessor: `DataPreprocessor` used to prepare the received `Data` for serialization. + /// - decoder: The `DataDecoder`. `JSONDecoder()` by default. + /// - emptyResponseCodes: The HTTP response codes for which empty responses are allowed. `[204, 205]` by default. + /// - emptyRequestMethods: The HTTP request methods for which empty responses are allowed. `[.head]` by default. + public init(dataPreprocessor: DataPreprocessor = DecodableResponseSerializer.defaultDataPreprocessor, + decoder: DataDecoder = JSONDecoder(), + emptyResponseCodes: Set = DecodableResponseSerializer.defaultEmptyResponseCodes, + emptyRequestMethods: Set = DecodableResponseSerializer.defaultEmptyRequestMethods) { + self.dataPreprocessor = dataPreprocessor + self.decoder = decoder + self.emptyResponseCodes = emptyResponseCodes + self.emptyRequestMethods = emptyRequestMethods + } + + public func serialize(request: URLRequest?, response: HTTPURLResponse?, data: Data?, error: Error?) throws -> T { + guard error == nil else { throw error! } + + guard var data, !data.isEmpty else { + guard emptyResponseAllowed(forRequest: request, response: response) else { + throw AFError.responseSerializationFailed(reason: .inputDataNilOrZeroLength) + } + + guard let emptyResponseType = T.self as? EmptyResponse.Type, let emptyValue = emptyResponseType.emptyValue() as? T else { + throw AFError.responseSerializationFailed(reason: .invalidEmptyResponse(type: "\(T.self)")) + } + + return emptyValue + } + + data = try dataPreprocessor.preprocess(data) + + do { + return try decoder.decode(T.self, from: data) + } catch { + throw AFError.responseSerializationFailed(reason: .decodingFailed(error: error)) + } + } +} + +extension ResponseSerializer { + /// Creates a `DecodableResponseSerializer` using the values provided. + /// + /// - Parameters: + /// - type: `Decodable` type to decode from response data. + /// - dataPreprocessor: `DataPreprocessor` used to prepare the received `Data` for serialization. + /// - decoder: The `DataDecoder`. `JSONDecoder()` by default. + /// - emptyResponseCodes: The HTTP response codes for which empty responses are allowed. `[204, 205]` by default. + /// - emptyRequestMethods: The HTTP request methods for which empty responses are allowed. `[.head]` by default. + /// + /// - Returns: The `DecodableResponseSerializer`. + public static func decodable(of type: T.Type, + dataPreprocessor: DataPreprocessor = DecodableResponseSerializer.defaultDataPreprocessor, + decoder: DataDecoder = JSONDecoder(), + emptyResponseCodes: Set = DecodableResponseSerializer.defaultEmptyResponseCodes, + emptyRequestMethods: Set = DecodableResponseSerializer.defaultEmptyRequestMethods) -> DecodableResponseSerializer where Self == DecodableResponseSerializer { + DecodableResponseSerializer(dataPreprocessor: dataPreprocessor, + decoder: decoder, + emptyResponseCodes: emptyResponseCodes, + emptyRequestMethods: emptyRequestMethods) + } +} diff --git a/Pods/Alamofire/Source/Features/RetryPolicy.swift b/Pods/Alamofire/Source/Features/RetryPolicy.swift new file mode 100644 index 0000000..bb52c6c --- /dev/null +++ b/Pods/Alamofire/Source/Features/RetryPolicy.swift @@ -0,0 +1,430 @@ +// +// RetryPolicy.swift +// +// Copyright (c) 2019-2020 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +import Foundation + +/// A retry policy that retries requests using an exponential backoff for allowed HTTP methods and HTTP status codes +/// as well as certain types of networking errors. +open class RetryPolicy: RequestInterceptor { + /// The default retry limit for retry policies. + public static let defaultRetryLimit: UInt = 2 + + /// The default exponential backoff base for retry policies (must be a minimum of 2). + public static let defaultExponentialBackoffBase: UInt = 2 + + /// The default exponential backoff scale for retry policies. + public static let defaultExponentialBackoffScale: Double = 0.5 + + /// The default HTTP methods to retry. + /// See [RFC 2616 - Section 9.1.2](https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html) for more information. + public static let defaultRetryableHTTPMethods: Set = [.delete, // [Delete](https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.7) - not always idempotent + .get, // [GET](https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.3) - generally idempotent + .head, // [HEAD](https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.4) - generally idempotent + .options, // [OPTIONS](https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.2) - inherently idempotent + .put, // [PUT](https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.6) - not always idempotent + .trace // [TRACE](https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.8) - inherently idempotent + ] + + /// The default HTTP status codes to retry. + /// See [RFC 2616 - Section 10](https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10) for more information. + public static let defaultRetryableHTTPStatusCodes: Set = [408, // [Request Timeout](https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.9) + 500, // [Internal Server Error](https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.1) + 502, // [Bad Gateway](https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.3) + 503, // [Service Unavailable](https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.4) + 504 // [Gateway Timeout](https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.5) + ] + + /// The default URL error codes to retry. + public static let defaultRetryableURLErrorCodes: Set = [ // [Security] App Transport Security disallowed a connection because there is no secure network connection. + // - [Disabled] ATS settings do not change at runtime. + // .appTransportSecurityRequiresSecureConnection, + + // [System] An app or app extension attempted to connect to a background session that is already connected to a + // process. + // - [Enabled] The other process could release the background session. + .backgroundSessionInUseByAnotherProcess, + + // [System] The shared container identifier of the URL session configuration is needed but has not been set. + // - [Disabled] Cannot change at runtime. + // .backgroundSessionRequiresSharedContainer, + + // [System] The app is suspended or exits while a background data task is processing. + // - [Enabled] App can be foregrounded or launched to recover. + .backgroundSessionWasDisconnected, + + // [Network] The URL Loading system received bad data from the server. + // - [Enabled] Server could return valid data when retrying. + .badServerResponse, + + // [Resource] A malformed URL prevented a URL request from being initiated. + // - [Disabled] URL was most likely constructed incorrectly. + // .badURL, + + // [System] A connection was attempted while a phone call is active on a network that does not support + // simultaneous phone and data communication (EDGE or GPRS). + // - [Enabled] Phone call could be ended to allow request to recover. + .callIsActive, + + // [Client] An asynchronous load has been canceled. + // - [Disabled] Request was cancelled by the client. + // .cancelled, + + // [File System] A download task couldn’t close the downloaded file on disk. + // - [Disabled] File system error is unlikely to recover with retry. + // .cannotCloseFile, + + // [Network] An attempt to connect to a host failed. + // - [Enabled] Server or DNS lookup could recover during retry. + .cannotConnectToHost, + + // [File System] A download task couldn’t create the downloaded file on disk because of an I/O failure. + // - [Disabled] File system error is unlikely to recover with retry. + // .cannotCreateFile, + + // [Data] Content data received during a connection request had an unknown content encoding. + // - [Disabled] Server is unlikely to modify the content encoding during a retry. + // .cannotDecodeContentData, + + // [Data] Content data received during a connection request could not be decoded for a known content encoding. + // - [Disabled] Server is unlikely to modify the content encoding during a retry. + // .cannotDecodeRawData, + + // [Network] The host name for a URL could not be resolved. + // - [Enabled] Server or DNS lookup could recover during retry. + .cannotFindHost, + + // [Network] A request to load an item only from the cache could not be satisfied. + // - [Enabled] Cache could be populated during a retry. + .cannotLoadFromNetwork, + + // [File System] A download task was unable to move a downloaded file on disk. + // - [Disabled] File system error is unlikely to recover with retry. + // .cannotMoveFile, + + // [File System] A download task was unable to open the downloaded file on disk. + // - [Disabled] File system error is unlikely to recover with retry. + // .cannotOpenFile, + + // [Data] A task could not parse a response. + // - [Disabled] Invalid response is unlikely to recover with retry. + // .cannotParseResponse, + + // [File System] A download task was unable to remove a downloaded file from disk. + // - [Disabled] File system error is unlikely to recover with retry. + // .cannotRemoveFile, + + // [File System] A download task was unable to write to the downloaded file on disk. + // - [Disabled] File system error is unlikely to recover with retry. + // .cannotWriteToFile, + + // [Security] A client certificate was rejected. + // - [Disabled] Client certificate is unlikely to change with retry. + // .clientCertificateRejected, + + // [Security] A client certificate was required to authenticate an SSL connection during a request. + // - [Disabled] Client certificate is unlikely to be provided with retry. + // .clientCertificateRequired, + + // [Data] The length of the resource data exceeds the maximum allowed. + // - [Disabled] Resource will likely still exceed the length maximum on retry. + // .dataLengthExceedsMaximum, + + // [System] The cellular network disallowed a connection. + // - [Enabled] WiFi connection could be established during retry. + .dataNotAllowed, + + // [Network] The host address could not be found via DNS lookup. + // - [Enabled] DNS lookup could succeed during retry. + .dnsLookupFailed, + + // [Data] A download task failed to decode an encoded file during the download. + // - [Enabled] Server could correct the decoding issue with retry. + .downloadDecodingFailedMidStream, + + // [Data] A download task failed to decode an encoded file after downloading. + // - [Enabled] Server could correct the decoding issue with retry. + .downloadDecodingFailedToComplete, + + // [File System] A file does not exist. + // - [Disabled] File system error is unlikely to recover with retry. + // .fileDoesNotExist, + + // [File System] A request for an FTP file resulted in the server responding that the file is not a plain file, + // but a directory. + // - [Disabled] FTP directory is not likely to change to a file during a retry. + // .fileIsDirectory, + + // [Network] A redirect loop has been detected or the threshold for number of allowable redirects has been + // exceeded (currently 16). + // - [Disabled] The redirect loop is unlikely to be resolved within the retry window. + // .httpTooManyRedirects, + + // [System] The attempted connection required activating a data context while roaming, but international roaming + // is disabled. + // - [Enabled] WiFi connection could be established during retry. + .internationalRoamingOff, + + // [Connectivity] A client or server connection was severed in the middle of an in-progress load. + // - [Enabled] A network connection could be established during retry. + .networkConnectionLost, + + // [File System] A resource couldn’t be read because of insufficient permissions. + // - [Disabled] Permissions are unlikely to be granted during retry. + // .noPermissionsToReadFile, + + // [Connectivity] A network resource was requested, but an internet connection has not been established and + // cannot be established automatically. + // - [Enabled] A network connection could be established during retry. + .notConnectedToInternet, + + // [Resource] A redirect was specified by way of server response code, but the server did not accompany this + // code with a redirect URL. + // - [Disabled] The redirect URL is unlikely to be supplied during a retry. + // .redirectToNonExistentLocation, + + // [Client] A body stream is needed but the client did not provide one. + // - [Disabled] The client will be unlikely to supply a body stream during retry. + // .requestBodyStreamExhausted, + + // [Resource] A requested resource couldn’t be retrieved. + // - [Disabled] The resource is unlikely to become available during the retry window. + // .resourceUnavailable, + + // [Security] An attempt to establish a secure connection failed for reasons that can’t be expressed more + // specifically. + // - [Enabled] The secure connection could be established during a retry given the lack of specificity + // provided by the error. + .secureConnectionFailed, + + // [Security] A server certificate had a date which indicates it has expired, or is not yet valid. + // - [Enabled] The server certificate could become valid within the retry window. + .serverCertificateHasBadDate, + + // [Security] A server certificate was not signed by any root server. + // - [Disabled] The server certificate is unlikely to change during the retry window. + // .serverCertificateHasUnknownRoot, + + // [Security] A server certificate is not yet valid. + // - [Enabled] The server certificate could become valid within the retry window. + .serverCertificateNotYetValid, + + // [Security] A server certificate was signed by a root server that isn’t trusted. + // - [Disabled] The server certificate is unlikely to become trusted within the retry window. + // .serverCertificateUntrusted, + + // [Network] An asynchronous operation timed out. + // - [Enabled] The request timed out for an unknown reason and should be retried. + .timedOut + + // [System] The URL Loading System encountered an error that it can’t interpret. + // - [Disabled] The error could not be interpreted and is unlikely to be recovered from during a retry. + // .unknown, + + // [Resource] A properly formed URL couldn’t be handled by the framework. + // - [Disabled] The URL is unlikely to change during a retry. + // .unsupportedURL, + + // [Client] Authentication is required to access a resource. + // - [Disabled] The user authentication is unlikely to be provided by retrying. + // .userAuthenticationRequired, + + // [Client] An asynchronous request for authentication has been canceled by the user. + // - [Disabled] The user cancelled authentication and explicitly took action to not retry. + // .userCancelledAuthentication, + + // [Resource] A server reported that a URL has a non-zero content length, but terminated the network connection + // gracefully without sending any data. + // - [Disabled] The server is unlikely to provide data during the retry window. + // .zeroByteResource, + ] + + /// The total number of times the request is allowed to be retried. + public let retryLimit: UInt + + /// The base of the exponential backoff policy (should always be greater than or equal to 2). + public let exponentialBackoffBase: UInt + + /// The scale of the exponential backoff. + public let exponentialBackoffScale: Double + + /// The HTTP methods that are allowed to be retried. + public let retryableHTTPMethods: Set + + /// The HTTP status codes that are automatically retried by the policy. + public let retryableHTTPStatusCodes: Set + + /// The URL error codes that are automatically retried by the policy. + public let retryableURLErrorCodes: Set + + /// Creates a `RetryPolicy` from the specified parameters. + /// + /// - Parameters: + /// - retryLimit: The total number of times the request is allowed to be retried. `2` by default. + /// - exponentialBackoffBase: The base of the exponential backoff policy. `2` by default. + /// - exponentialBackoffScale: The scale of the exponential backoff. `0.5` by default. + /// - retryableHTTPMethods: The HTTP methods that are allowed to be retried. + /// `RetryPolicy.defaultRetryableHTTPMethods` by default. + /// - retryableHTTPStatusCodes: The HTTP status codes that are automatically retried by the policy. + /// `RetryPolicy.defaultRetryableHTTPStatusCodes` by default. + /// - retryableURLErrorCodes: The URL error codes that are automatically retried by the policy. + /// `RetryPolicy.defaultRetryableURLErrorCodes` by default. + public init(retryLimit: UInt = RetryPolicy.defaultRetryLimit, + exponentialBackoffBase: UInt = RetryPolicy.defaultExponentialBackoffBase, + exponentialBackoffScale: Double = RetryPolicy.defaultExponentialBackoffScale, + retryableHTTPMethods: Set = RetryPolicy.defaultRetryableHTTPMethods, + retryableHTTPStatusCodes: Set = RetryPolicy.defaultRetryableHTTPStatusCodes, + retryableURLErrorCodes: Set = RetryPolicy.defaultRetryableURLErrorCodes) { + precondition(exponentialBackoffBase >= 2, "The `exponentialBackoffBase` must be a minimum of 2.") + + self.retryLimit = retryLimit + self.exponentialBackoffBase = exponentialBackoffBase + self.exponentialBackoffScale = exponentialBackoffScale + self.retryableHTTPMethods = retryableHTTPMethods + self.retryableHTTPStatusCodes = retryableHTTPStatusCodes + self.retryableURLErrorCodes = retryableURLErrorCodes + } + + open func retry(_ request: Request, + for session: Session, + dueTo error: Error, + completion: @escaping (RetryResult) -> Void) { + if request.retryCount < retryLimit, shouldRetry(request: request, dueTo: error) { + completion(.retryWithDelay(pow(Double(exponentialBackoffBase), Double(request.retryCount)) * exponentialBackoffScale)) + } else { + completion(.doNotRetry) + } + } + + /// Determines whether or not to retry the provided `Request`. + /// + /// - Parameters: + /// - request: `Request` that failed due to the provided `Error`. + /// - error: `Error` encountered while executing the `Request`. + /// + /// - Returns: `Bool` determining whether or not to retry the `Request`. + open func shouldRetry(request: Request, dueTo error: Error) -> Bool { + guard let httpMethod = request.request?.method, retryableHTTPMethods.contains(httpMethod) else { return false } + + if let statusCode = request.response?.statusCode, retryableHTTPStatusCodes.contains(statusCode) { + return true + } else { + let errorCode = (error as? URLError)?.code + let afErrorCode = (error.asAFError?.underlyingError as? URLError)?.code + + guard let code = errorCode ?? afErrorCode else { return false } + + return retryableURLErrorCodes.contains(code) + } + } +} + +extension RequestInterceptor where Self == RetryPolicy { + /// Provides a default `RetryPolicy` instance. + public static var retryPolicy: RetryPolicy { RetryPolicy() } + + /// Creates an `RetryPolicy` from the specified parameters. + /// + /// - Parameters: + /// - retryLimit: The total number of times the request is allowed to be retried. `2` by default. + /// - exponentialBackoffBase: The base of the exponential backoff policy. `2` by default. + /// - exponentialBackoffScale: The scale of the exponential backoff. `0.5` by default. + /// - retryableHTTPMethods: The HTTP methods that are allowed to be retried. + /// `RetryPolicy.defaultRetryableHTTPMethods` by default. + /// - retryableHTTPStatusCodes: The HTTP status codes that are automatically retried by the policy. + /// `RetryPolicy.defaultRetryableHTTPStatusCodes` by default. + /// - retryableURLErrorCodes: The URL error codes that are automatically retried by the policy. + /// `RetryPolicy.defaultRetryableURLErrorCodes` by default. + /// + /// - Returns: The `RetryPolicy` + public static func retryPolicy(retryLimit: UInt = RetryPolicy.defaultRetryLimit, + exponentialBackoffBase: UInt = RetryPolicy.defaultExponentialBackoffBase, + exponentialBackoffScale: Double = RetryPolicy.defaultExponentialBackoffScale, + retryableHTTPMethods: Set = RetryPolicy.defaultRetryableHTTPMethods, + retryableHTTPStatusCodes: Set = RetryPolicy.defaultRetryableHTTPStatusCodes, + retryableURLErrorCodes: Set = RetryPolicy.defaultRetryableURLErrorCodes) -> RetryPolicy { + RetryPolicy(retryLimit: retryLimit, + exponentialBackoffBase: exponentialBackoffBase, + exponentialBackoffScale: exponentialBackoffScale, + retryableHTTPMethods: retryableHTTPMethods, + retryableHTTPStatusCodes: retryableHTTPStatusCodes, + retryableURLErrorCodes: retryableURLErrorCodes) + } +} + +// MARK: - + +/// A retry policy that automatically retries idempotent requests for network connection lost errors. For more +/// information about retrying network connection lost errors, please refer to Apple's +/// [technical document](https://developer.apple.com/library/content/qa/qa1941/_index.html). +open class ConnectionLostRetryPolicy: RetryPolicy { + /// Creates a `ConnectionLostRetryPolicy` instance from the specified parameters. + /// + /// - Parameters: + /// - retryLimit: The total number of times the request is allowed to be retried. + /// `RetryPolicy.defaultRetryLimit` by default. + /// - exponentialBackoffBase: The base of the exponential backoff policy. + /// `RetryPolicy.defaultExponentialBackoffBase` by default. + /// - exponentialBackoffScale: The scale of the exponential backoff. + /// `RetryPolicy.defaultExponentialBackoffScale` by default. + /// - retryableHTTPMethods: The idempotent http methods to retry. + /// `RetryPolicy.defaultRetryableHTTPMethods` by default. + public init(retryLimit: UInt = RetryPolicy.defaultRetryLimit, + exponentialBackoffBase: UInt = RetryPolicy.defaultExponentialBackoffBase, + exponentialBackoffScale: Double = RetryPolicy.defaultExponentialBackoffScale, + retryableHTTPMethods: Set = RetryPolicy.defaultRetryableHTTPMethods) { + super.init(retryLimit: retryLimit, + exponentialBackoffBase: exponentialBackoffBase, + exponentialBackoffScale: exponentialBackoffScale, + retryableHTTPMethods: retryableHTTPMethods, + retryableHTTPStatusCodes: [], + retryableURLErrorCodes: [.networkConnectionLost]) + } +} + +extension RequestInterceptor where Self == ConnectionLostRetryPolicy { + /// Provides a default `ConnectionLostRetryPolicy` instance. + public static var connectionLostRetryPolicy: ConnectionLostRetryPolicy { ConnectionLostRetryPolicy() } + + /// Creates a `ConnectionLostRetryPolicy` instance from the specified parameters. + /// + /// - Parameters: + /// - retryLimit: The total number of times the request is allowed to be retried. + /// `RetryPolicy.defaultRetryLimit` by default. + /// - exponentialBackoffBase: The base of the exponential backoff policy. + /// `RetryPolicy.defaultExponentialBackoffBase` by default. + /// - exponentialBackoffScale: The scale of the exponential backoff. + /// `RetryPolicy.defaultExponentialBackoffScale` by default. + /// - retryableHTTPMethods: The idempotent http methods to retry. + /// + /// - Returns: The `ConnectionLostRetryPolicy`. + public static func connectionLostRetryPolicy(retryLimit: UInt = RetryPolicy.defaultRetryLimit, + exponentialBackoffBase: UInt = RetryPolicy.defaultExponentialBackoffBase, + exponentialBackoffScale: Double = RetryPolicy.defaultExponentialBackoffScale, + retryableHTTPMethods: Set = RetryPolicy.defaultRetryableHTTPMethods) -> ConnectionLostRetryPolicy { + ConnectionLostRetryPolicy(retryLimit: retryLimit, + exponentialBackoffBase: exponentialBackoffBase, + exponentialBackoffScale: exponentialBackoffScale, + retryableHTTPMethods: retryableHTTPMethods) + } +} diff --git a/Pods/Alamofire/Source/Features/ServerTrustEvaluation.swift b/Pods/Alamofire/Source/Features/ServerTrustEvaluation.swift new file mode 100644 index 0000000..7e6cb10 --- /dev/null +++ b/Pods/Alamofire/Source/Features/ServerTrustEvaluation.swift @@ -0,0 +1,768 @@ +// +// ServerTrustEvaluation.swift +// +// Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +import Foundation + +/// Responsible for managing the mapping of `ServerTrustEvaluating` values to given hosts. +open class ServerTrustManager { + /// Determines whether all hosts for this `ServerTrustManager` must be evaluated. `true` by default. + public let allHostsMustBeEvaluated: Bool + + /// The dictionary of policies mapped to a particular host. + public let evaluators: [String: ServerTrustEvaluating] + + /// Initializes the `ServerTrustManager` instance with the given evaluators. + /// + /// Since different servers and web services can have different leaf certificates, intermediate and even root + /// certificates, it is important to have the flexibility to specify evaluation policies on a per host basis. This + /// allows for scenarios such as using default evaluation for host1, certificate pinning for host2, public key + /// pinning for host3 and disabling evaluation for host4. + /// + /// - Parameters: + /// - allHostsMustBeEvaluated: The value determining whether all hosts for this instance must be evaluated. `true` + /// by default. + /// - evaluators: A dictionary of evaluators mapped to hosts. + public init(allHostsMustBeEvaluated: Bool = true, evaluators: [String: ServerTrustEvaluating]) { + self.allHostsMustBeEvaluated = allHostsMustBeEvaluated + self.evaluators = evaluators + } + + #if canImport(Security) + /// Returns the `ServerTrustEvaluating` value for the given host, if one is set. + /// + /// By default, this method will return the policy that perfectly matches the given host. Subclasses could override + /// this method and implement more complex mapping implementations such as wildcards. + /// + /// - Parameter host: The host to use when searching for a matching policy. + /// + /// - Returns: The `ServerTrustEvaluating` value for the given host if found, `nil` otherwise. + /// - Throws: `AFError.serverTrustEvaluationFailed` if `allHostsMustBeEvaluated` is `true` and no matching + /// evaluators are found. + open func serverTrustEvaluator(forHost host: String) throws -> ServerTrustEvaluating? { + guard let evaluator = evaluators[host] else { + if allHostsMustBeEvaluated { + throw AFError.serverTrustEvaluationFailed(reason: .noRequiredEvaluator(host: host)) + } + + return nil + } + + return evaluator + } + #endif +} + +/// A protocol describing the API used to evaluate server trusts. +public protocol ServerTrustEvaluating { + #if !canImport(Security) + // Implement this once other platforms have API for evaluating server trusts. + #else + /// Evaluates the given `SecTrust` value for the given `host`. + /// + /// - Parameters: + /// - trust: The `SecTrust` value to evaluate. + /// - host: The host for which to evaluate the `SecTrust` value. + /// + /// - Returns: A `Bool` indicating whether the evaluator considers the `SecTrust` value valid for `host`. + func evaluate(_ trust: SecTrust, forHost host: String) throws + #endif +} + +// MARK: - Server Trust Evaluators + +#if canImport(Security) +/// An evaluator which uses the default server trust evaluation while allowing you to control whether to validate the +/// host provided by the challenge. Applications are encouraged to always validate the host in production environments +/// to guarantee the validity of the server's certificate chain. +public final class DefaultTrustEvaluator: ServerTrustEvaluating { + private let validateHost: Bool + + /// Creates a `DefaultTrustEvaluator`. + /// + /// - Parameter validateHost: Determines whether or not the evaluator should validate the host. `true` by default. + public init(validateHost: Bool = true) { + self.validateHost = validateHost + } + + public func evaluate(_ trust: SecTrust, forHost host: String) throws { + if validateHost { + try trust.af.performValidation(forHost: host) + } + + try trust.af.performDefaultValidation(forHost: host) + } +} + +/// An evaluator which Uses the default and revoked server trust evaluations allowing you to control whether to validate +/// the host provided by the challenge as well as specify the revocation flags for testing for revoked certificates. +/// Apple platforms did not start testing for revoked certificates automatically until iOS 10.1, macOS 10.12 and tvOS +/// 10.1 which is demonstrated in our TLS tests. Applications are encouraged to always validate the host in production +/// environments to guarantee the validity of the server's certificate chain. +public final class RevocationTrustEvaluator: ServerTrustEvaluating { + /// Represents the options to be use when evaluating the status of a certificate. + /// Only Revocation Policy Constants are valid, and can be found in [Apple's documentation](https://developer.apple.com/documentation/security/certificate_key_and_trust_services/policies/1563600-revocation_policy_constants). + public struct Options: OptionSet { + /// Perform revocation checking using the CRL (Certification Revocation List) method. + public static let crl = Options(rawValue: kSecRevocationCRLMethod) + /// Consult only locally cached replies; do not use network access. + public static let networkAccessDisabled = Options(rawValue: kSecRevocationNetworkAccessDisabled) + /// Perform revocation checking using OCSP (Online Certificate Status Protocol). + public static let ocsp = Options(rawValue: kSecRevocationOCSPMethod) + /// Prefer CRL revocation checking over OCSP; by default, OCSP is preferred. + public static let preferCRL = Options(rawValue: kSecRevocationPreferCRL) + /// Require a positive response to pass the policy. If the flag is not set, revocation checking is done on a + /// "best attempt" basis, where failure to reach the server is not considered fatal. + public static let requirePositiveResponse = Options(rawValue: kSecRevocationRequirePositiveResponse) + /// Perform either OCSP or CRL checking. The checking is performed according to the method(s) specified in the + /// certificate and the value of `preferCRL`. + public static let any = Options(rawValue: kSecRevocationUseAnyAvailableMethod) + + /// The raw value of the option. + public let rawValue: CFOptionFlags + + /// Creates an `Options` value with the given `CFOptionFlags`. + /// + /// - Parameter rawValue: The `CFOptionFlags` value to initialize with. + public init(rawValue: CFOptionFlags) { + self.rawValue = rawValue + } + } + + private let performDefaultValidation: Bool + private let validateHost: Bool + private let options: Options + + /// Creates a `RevocationTrustEvaluator` using the provided parameters. + /// + /// - Note: Default and host validation will fail when using this evaluator with self-signed certificates. Use + /// `PinnedCertificatesTrustEvaluator` if you need to use self-signed certificates. + /// + /// - Parameters: + /// - performDefaultValidation: Determines whether default validation should be performed in addition to + /// evaluating the pinned certificates. `true` by default. + /// - validateHost: Determines whether or not the evaluator should validate the host, in addition to + /// performing the default evaluation, even if `performDefaultValidation` is `false`. + /// `true` by default. + /// - options: The `Options` to use to check the revocation status of the certificate. `.any` by + /// default. + public init(performDefaultValidation: Bool = true, validateHost: Bool = true, options: Options = .any) { + self.performDefaultValidation = performDefaultValidation + self.validateHost = validateHost + self.options = options + } + + public func evaluate(_ trust: SecTrust, forHost host: String) throws { + if performDefaultValidation { + try trust.af.performDefaultValidation(forHost: host) + } + + if validateHost { + try trust.af.performValidation(forHost: host) + } + + #if swift(>=5.9) + if #available(iOS 12, macOS 10.14, tvOS 12, watchOS 5, visionOS 1, *) { + try trust.af.evaluate(afterApplying: SecPolicy.af.revocation(options: options)) + } else { + try trust.af.validate(policy: SecPolicy.af.revocation(options: options)) { status, result in + AFError.serverTrustEvaluationFailed(reason: .revocationCheckFailed(output: .init(host, trust, status, result), options: options)) + } + } + #else + if #available(iOS 12, macOS 10.14, tvOS 12, watchOS 5, *) { + try trust.af.evaluate(afterApplying: SecPolicy.af.revocation(options: options)) + } else { + try trust.af.validate(policy: SecPolicy.af.revocation(options: options)) { status, result in + AFError.serverTrustEvaluationFailed(reason: .revocationCheckFailed(output: .init(host, trust, status, result), options: options)) + } + } + #endif + } +} + +extension ServerTrustEvaluating where Self == RevocationTrustEvaluator { + /// Provides a default `RevocationTrustEvaluator` instance. + public static var revocationChecking: RevocationTrustEvaluator { RevocationTrustEvaluator() } + + /// Creates a `RevocationTrustEvaluator` using the provided parameters. + /// + /// - Note: Default and host validation will fail when using this evaluator with self-signed certificates. Use + /// `PinnedCertificatesTrustEvaluator` if you need to use self-signed certificates. + /// + /// - Parameters: + /// - performDefaultValidation: Determines whether default validation should be performed in addition to + /// evaluating the pinned certificates. `true` by default. + /// - validateHost: Determines whether or not the evaluator should validate the host, in addition + /// to performing the default evaluation, even if `performDefaultValidation` is + /// `false`. `true` by default. + /// - options: The `Options` to use to check the revocation status of the certificate. `.any` + /// by default. + /// - Returns: The `RevocationTrustEvaluator`. + public static func revocationChecking(performDefaultValidation: Bool = true, + validateHost: Bool = true, + options: RevocationTrustEvaluator.Options = .any) -> RevocationTrustEvaluator { + RevocationTrustEvaluator(performDefaultValidation: performDefaultValidation, + validateHost: validateHost, + options: options) + } +} + +/// Uses the pinned certificates to validate the server trust. The server trust is considered valid if one of the pinned +/// certificates match one of the server certificates. By validating both the certificate chain and host, certificate +/// pinning provides a very secure form of server trust validation mitigating most, if not all, MITM attacks. +/// Applications are encouraged to always validate the host and require a valid certificate chain in production +/// environments. +public final class PinnedCertificatesTrustEvaluator: ServerTrustEvaluating { + private let certificates: [SecCertificate] + private let acceptSelfSignedCertificates: Bool + private let performDefaultValidation: Bool + private let validateHost: Bool + + /// Creates a `PinnedCertificatesTrustEvaluator` from the provided parameters. + /// + /// - Parameters: + /// - certificates: The certificates to use to evaluate the trust. All `cer`, `crt`, and `der` + /// certificates in `Bundle.main` by default. + /// - acceptSelfSignedCertificates: Adds the provided certificates as anchors for the trust evaluation, allowing + /// self-signed certificates to pass. `false` by default. THIS SETTING SHOULD BE + /// FALSE IN PRODUCTION! + /// - performDefaultValidation: Determines whether default validation should be performed in addition to + /// evaluating the pinned certificates. `true` by default. + /// - validateHost: Determines whether or not the evaluator should validate the host, in addition + /// to performing the default evaluation, even if `performDefaultValidation` is + /// `false`. `true` by default. + public init(certificates: [SecCertificate] = Bundle.main.af.certificates, + acceptSelfSignedCertificates: Bool = false, + performDefaultValidation: Bool = true, + validateHost: Bool = true) { + self.certificates = certificates + self.acceptSelfSignedCertificates = acceptSelfSignedCertificates + self.performDefaultValidation = performDefaultValidation + self.validateHost = validateHost + } + + public func evaluate(_ trust: SecTrust, forHost host: String) throws { + guard !certificates.isEmpty else { + throw AFError.serverTrustEvaluationFailed(reason: .noCertificatesFound) + } + + if acceptSelfSignedCertificates { + try trust.af.setAnchorCertificates(certificates) + } + + if performDefaultValidation { + try trust.af.performDefaultValidation(forHost: host) + } + + if validateHost { + try trust.af.performValidation(forHost: host) + } + + let serverCertificatesData = Set(trust.af.certificateData) + let pinnedCertificatesData = Set(certificates.af.data) + let pinnedCertificatesInServerData = !serverCertificatesData.isDisjoint(with: pinnedCertificatesData) + if !pinnedCertificatesInServerData { + throw AFError.serverTrustEvaluationFailed(reason: .certificatePinningFailed(host: host, + trust: trust, + pinnedCertificates: certificates, + serverCertificates: trust.af.certificates)) + } + } +} + +extension ServerTrustEvaluating where Self == PinnedCertificatesTrustEvaluator { + /// Provides a default `PinnedCertificatesTrustEvaluator` instance. + public static var pinnedCertificates: PinnedCertificatesTrustEvaluator { PinnedCertificatesTrustEvaluator() } + + /// Creates a `PinnedCertificatesTrustEvaluator` using the provided parameters. + /// + /// - Parameters: + /// - certificates: The certificates to use to evaluate the trust. All `cer`, `crt`, and `der` + /// certificates in `Bundle.main` by default. + /// - acceptSelfSignedCertificates: Adds the provided certificates as anchors for the trust evaluation, allowing + /// self-signed certificates to pass. `false` by default. THIS SETTING SHOULD BE + /// FALSE IN PRODUCTION! + /// - performDefaultValidation: Determines whether default validation should be performed in addition to + /// evaluating the pinned certificates. `true` by default. + /// - validateHost: Determines whether or not the evaluator should validate the host, in addition + /// to performing the default evaluation, even if `performDefaultValidation` is + /// `false`. `true` by default. + public static func pinnedCertificates(certificates: [SecCertificate] = Bundle.main.af.certificates, + acceptSelfSignedCertificates: Bool = false, + performDefaultValidation: Bool = true, + validateHost: Bool = true) -> PinnedCertificatesTrustEvaluator { + PinnedCertificatesTrustEvaluator(certificates: certificates, + acceptSelfSignedCertificates: acceptSelfSignedCertificates, + performDefaultValidation: performDefaultValidation, + validateHost: validateHost) + } +} + +/// Uses the pinned public keys to validate the server trust. The server trust is considered valid if one of the pinned +/// public keys match one of the server certificate public keys. By validating both the certificate chain and host, +/// public key pinning provides a very secure form of server trust validation mitigating most, if not all, MITM attacks. +/// Applications are encouraged to always validate the host and require a valid certificate chain in production +/// environments. +public final class PublicKeysTrustEvaluator: ServerTrustEvaluating { + private let keys: [SecKey] + private let performDefaultValidation: Bool + private let validateHost: Bool + + /// Creates a `PublicKeysTrustEvaluator` from the provided parameters. + /// + /// - Note: Default and host validation will fail when using this evaluator with self-signed certificates. Use + /// `PinnedCertificatesTrustEvaluator` if you need to use self-signed certificates. + /// + /// - Parameters: + /// - keys: The `SecKey`s to use to validate public keys. Defaults to the public keys of all + /// certificates included in the main bundle. + /// - performDefaultValidation: Determines whether default validation should be performed in addition to + /// evaluating the pinned certificates. `true` by default. + /// - validateHost: Determines whether or not the evaluator should validate the host, in addition to + /// performing the default evaluation, even if `performDefaultValidation` is `false`. + /// `true` by default. + public init(keys: [SecKey] = Bundle.main.af.publicKeys, + performDefaultValidation: Bool = true, + validateHost: Bool = true) { + self.keys = keys + self.performDefaultValidation = performDefaultValidation + self.validateHost = validateHost + } + + public func evaluate(_ trust: SecTrust, forHost host: String) throws { + guard !keys.isEmpty else { + throw AFError.serverTrustEvaluationFailed(reason: .noPublicKeysFound) + } + + if performDefaultValidation { + try trust.af.performDefaultValidation(forHost: host) + } + + if validateHost { + try trust.af.performValidation(forHost: host) + } + + let pinnedKeysInServerKeys: Bool = { + for serverPublicKey in trust.af.publicKeys { + if keys.contains(serverPublicKey) { + return true + } + } + return false + }() + + if !pinnedKeysInServerKeys { + throw AFError.serverTrustEvaluationFailed(reason: .publicKeyPinningFailed(host: host, + trust: trust, + pinnedKeys: keys, + serverKeys: trust.af.publicKeys)) + } + } +} + +extension ServerTrustEvaluating where Self == PublicKeysTrustEvaluator { + /// Provides a default `PublicKeysTrustEvaluator` instance. + public static var publicKeys: PublicKeysTrustEvaluator { PublicKeysTrustEvaluator() } + + /// Creates a `PublicKeysTrustEvaluator` from the provided parameters. + /// + /// - Note: Default and host validation will fail when using this evaluator with self-signed certificates. Use + /// `PinnedCertificatesTrustEvaluator` if you need to use self-signed certificates. + /// + /// - Parameters: + /// - keys: The `SecKey`s to use to validate public keys. Defaults to the public keys of all + /// certificates included in the main bundle. + /// - performDefaultValidation: Determines whether default validation should be performed in addition to + /// evaluating the pinned certificates. `true` by default. + /// - validateHost: Determines whether or not the evaluator should validate the host, in addition to + /// performing the default evaluation, even if `performDefaultValidation` is `false`. + /// `true` by default. + public static func publicKeys(keys: [SecKey] = Bundle.main.af.publicKeys, + performDefaultValidation: Bool = true, + validateHost: Bool = true) -> PublicKeysTrustEvaluator { + PublicKeysTrustEvaluator(keys: keys, performDefaultValidation: performDefaultValidation, validateHost: validateHost) + } +} + +/// Uses the provided evaluators to validate the server trust. The trust is only considered valid if all of the +/// evaluators consider it valid. +public final class CompositeTrustEvaluator: ServerTrustEvaluating { + private let evaluators: [ServerTrustEvaluating] + + /// Creates a `CompositeTrustEvaluator` from the provided evaluators. + /// + /// - Parameter evaluators: The `ServerTrustEvaluating` values used to evaluate the server trust. + public init(evaluators: [ServerTrustEvaluating]) { + self.evaluators = evaluators + } + + public func evaluate(_ trust: SecTrust, forHost host: String) throws { + try evaluators.evaluate(trust, forHost: host) + } +} + +extension ServerTrustEvaluating where Self == CompositeTrustEvaluator { + /// Creates a `CompositeTrustEvaluator` from the provided evaluators. + /// + /// - Parameter evaluators: The `ServerTrustEvaluating` values used to evaluate the server trust. + public static func composite(evaluators: [ServerTrustEvaluating]) -> CompositeTrustEvaluator { + CompositeTrustEvaluator(evaluators: evaluators) + } +} + +/// Disables all evaluation which in turn will always consider any server trust as valid. +/// +/// - Note: Instead of disabling server trust evaluation, it's a better idea to configure systems to properly trust test +/// certificates, as outlined in [this Apple tech note](https://developer.apple.com/library/archive/qa/qa1948/_index.html). +/// +/// **THIS EVALUATOR SHOULD NEVER BE USED IN PRODUCTION!** +@available(*, deprecated, renamed: "DisabledTrustEvaluator", message: "DisabledEvaluator has been renamed DisabledTrustEvaluator.") +public typealias DisabledEvaluator = DisabledTrustEvaluator + +/// Disables all evaluation which in turn will always consider any server trust as valid. +/// +/// +/// - Note: Instead of disabling server trust evaluation, it's a better idea to configure systems to properly trust test +/// certificates, as outlined in [this Apple tech note](https://developer.apple.com/library/archive/qa/qa1948/_index.html). +/// +/// **THIS EVALUATOR SHOULD NEVER BE USED IN PRODUCTION!** +public final class DisabledTrustEvaluator: ServerTrustEvaluating { + /// Creates an instance. + public init() {} + + public func evaluate(_ trust: SecTrust, forHost host: String) throws {} +} + +// MARK: - Extensions + +extension [ServerTrustEvaluating] { + #if os(Linux) || os(Windows) || os(Android) + // Add this same convenience method for Linux/Windows. + #else + /// Evaluates the given `SecTrust` value for the given `host`. + /// + /// - Parameters: + /// - trust: The `SecTrust` value to evaluate. + /// - host: The host for which to evaluate the `SecTrust` value. + /// + /// - Returns: Whether or not the evaluator considers the `SecTrust` value valid for `host`. + public func evaluate(_ trust: SecTrust, forHost host: String) throws { + for evaluator in self { + try evaluator.evaluate(trust, forHost: host) + } + } + #endif +} + +extension Bundle: AlamofireExtended {} +extension AlamofireExtension where ExtendedType: Bundle { + /// Returns all valid `cer`, `crt`, and `der` certificates in the bundle. + public var certificates: [SecCertificate] { + paths(forResourcesOfTypes: [".cer", ".CER", ".crt", ".CRT", ".der", ".DER"]).compactMap { path in + guard + let certificateData = try? Data(contentsOf: URL(fileURLWithPath: path)) as CFData, + let certificate = SecCertificateCreateWithData(nil, certificateData) else { return nil } + + return certificate + } + } + + /// Returns all public keys for the valid certificates in the bundle. + public var publicKeys: [SecKey] { + certificates.af.publicKeys + } + + /// Returns all pathnames for the resources identified by the provided file extensions. + /// + /// - Parameter types: The filename extensions locate. + /// + /// - Returns: All pathnames for the given filename extensions. + public func paths(forResourcesOfTypes types: [String]) -> [String] { + Array(Set(types.flatMap { type.paths(forResourcesOfType: $0, inDirectory: nil) })) + } +} + +extension SecTrust: AlamofireExtended {} +extension AlamofireExtension where ExtendedType == SecTrust { + /// Evaluates `self` after applying the `SecPolicy` value provided. + /// + /// - Parameter policy: The `SecPolicy` to apply to `self` before evaluation. + /// + /// - Throws: Any `Error` from applying the `SecPolicy` or from evaluation. + @available(iOS 12, macOS 10.14, tvOS 12, watchOS 5, *) + public func evaluate(afterApplying policy: SecPolicy) throws { + try apply(policy: policy).af.evaluate() + } + + /// Attempts to validate `self` using the `SecPolicy` provided and transforming any error produced using the closure passed. + /// + /// - Parameters: + /// - policy: The `SecPolicy` used to evaluate `self`. + /// - errorProducer: The closure used transform the failed `OSStatus` and `SecTrustResultType`. + /// - Throws: Any `Error` from applying the `policy`, or the result of `errorProducer` if validation fails. + @available(iOS, introduced: 10, deprecated: 12, renamed: "evaluate(afterApplying:)") + @available(macOS, introduced: 10.12, deprecated: 10.14, renamed: "evaluate(afterApplying:)") + @available(tvOS, introduced: 10, deprecated: 12, renamed: "evaluate(afterApplying:)") + @available(watchOS, introduced: 3, deprecated: 5, renamed: "evaluate(afterApplying:)") + public func validate(policy: SecPolicy, errorProducer: (_ status: OSStatus, _ result: SecTrustResultType) -> Error) throws { + try apply(policy: policy).af.validate(errorProducer: errorProducer) + } + + /// Applies a `SecPolicy` to `self`, throwing if it fails. + /// + /// - Parameter policy: The `SecPolicy`. + /// + /// - Returns: `self`, with the policy applied. + /// - Throws: An `AFError.serverTrustEvaluationFailed` instance with a `.policyApplicationFailed` reason. + public func apply(policy: SecPolicy) throws -> SecTrust { + let status = SecTrustSetPolicies(type, policy) + + guard status.af.isSuccess else { + throw AFError.serverTrustEvaluationFailed(reason: .policyApplicationFailed(trust: type, + policy: policy, + status: status)) + } + + return type + } + + /// Evaluate `self`, throwing an `Error` if evaluation fails. + /// + /// - Throws: `AFError.serverTrustEvaluationFailed` with reason `.trustValidationFailed` and associated error from + /// the underlying evaluation. + @available(iOS 12, macOS 10.14, tvOS 12, watchOS 5, *) + public func evaluate() throws { + var error: CFError? + let evaluationSucceeded = SecTrustEvaluateWithError(type, &error) + + if !evaluationSucceeded { + throw AFError.serverTrustEvaluationFailed(reason: .trustEvaluationFailed(error: error)) + } + } + + /// Validate `self`, passing any failure values through `errorProducer`. + /// + /// - Parameter errorProducer: The closure used to transform the failed `OSStatus` and `SecTrustResultType` into an + /// `Error`. + /// - Throws: The `Error` produced by the `errorProducer` closure. + @available(iOS, introduced: 10, deprecated: 12, renamed: "evaluate()") + @available(macOS, introduced: 10.12, deprecated: 10.14, renamed: "evaluate()") + @available(tvOS, introduced: 10, deprecated: 12, renamed: "evaluate()") + @available(watchOS, introduced: 3, deprecated: 5, renamed: "evaluate()") + public func validate(errorProducer: (_ status: OSStatus, _ result: SecTrustResultType) -> Error) throws { + var result = SecTrustResultType.invalid + let status = SecTrustEvaluate(type, &result) + + guard status.af.isSuccess && result.af.isSuccess else { + throw errorProducer(status, result) + } + } + + /// Sets a custom certificate chain on `self`, allowing full validation of a self-signed certificate and its chain. + /// + /// - Parameter certificates: The `SecCertificate`s to add to the chain. + /// - Throws: Any error produced when applying the new certificate chain. + public func setAnchorCertificates(_ certificates: [SecCertificate]) throws { + // Add additional anchor certificates. + let status = SecTrustSetAnchorCertificates(type, certificates as CFArray) + guard status.af.isSuccess else { + throw AFError.serverTrustEvaluationFailed(reason: .settingAnchorCertificatesFailed(status: status, + certificates: certificates)) + } + + // Trust only the set anchor certs. + let onlyStatus = SecTrustSetAnchorCertificatesOnly(type, true) + guard onlyStatus.af.isSuccess else { + throw AFError.serverTrustEvaluationFailed(reason: .settingAnchorCertificatesFailed(status: onlyStatus, + certificates: certificates)) + } + } + + /// The public keys contained in `self`. + public var publicKeys: [SecKey] { + certificates.af.publicKeys + } + + /// The `SecCertificate`s contained in `self`. + public var certificates: [SecCertificate] { + #if swift(>=5.9) + if #available(iOS 15, macOS 12, tvOS 15, watchOS 8, visionOS 1, *) { + return (SecTrustCopyCertificateChain(type) as? [SecCertificate]) ?? [] + } else { + return (0..=5.9) + if #available(iOS 12, macOS 10.14, tvOS 12, watchOS 5, visionOS 1, *) { + try evaluate(afterApplying: SecPolicy.af.default) + } else { + try validate(policy: SecPolicy.af.default) { status, result in + AFError.serverTrustEvaluationFailed(reason: .defaultEvaluationFailed(output: .init(host, type, status, result))) + } + } + #else + if #available(iOS 12, macOS 10.14, tvOS 12, watchOS 5, *) { + try evaluate(afterApplying: SecPolicy.af.default) + } else { + try validate(policy: SecPolicy.af.default) { status, result in + AFError.serverTrustEvaluationFailed(reason: .defaultEvaluationFailed(output: .init(host, type, status, result))) + } + } + #endif + } + + /// Validates `self` after applying `SecPolicy.af.hostname(host)`, which performs the default validation as well as + /// hostname validation. + /// + /// - Parameter host: The hostname to use in the validation. + /// - Throws: An `AFError.serverTrustEvaluationFailed` instance with a `.defaultEvaluationFailed` reason. + public func performValidation(forHost host: String) throws { + #if swift(>=5.9) + if #available(iOS 12, macOS 10.14, tvOS 12, watchOS 5, visionOS 1, *) { + try evaluate(afterApplying: SecPolicy.af.hostname(host)) + } else { + try validate(policy: SecPolicy.af.hostname(host)) { status, result in + AFError.serverTrustEvaluationFailed(reason: .hostValidationFailed(output: .init(host, type, status, result))) + } + } + #else + if #available(iOS 12, macOS 10.14, tvOS 12, watchOS 5, *) { + try evaluate(afterApplying: SecPolicy.af.hostname(host)) + } else { + try validate(policy: SecPolicy.af.hostname(host)) { status, result in + AFError.serverTrustEvaluationFailed(reason: .hostValidationFailed(output: .init(host, type, status, result))) + } + } + #endif + } +} + +extension SecPolicy: AlamofireExtended {} +extension AlamofireExtension where ExtendedType == SecPolicy { + /// Creates a `SecPolicy` instance which will validate server certificates but not require a host name match. + public static let `default` = SecPolicyCreateSSL(true, nil) + + /// Creates a `SecPolicy` instance which will validate server certificates and much match the provided hostname. + /// + /// - Parameter hostname: The hostname to validate against. + /// + /// - Returns: The `SecPolicy`. + public static func hostname(_ hostname: String) -> SecPolicy { + SecPolicyCreateSSL(true, hostname as CFString) + } + + /// Creates a `SecPolicy` which checks the revocation of certificates. + /// + /// - Parameter options: The `RevocationTrustEvaluator.Options` for evaluation. + /// + /// - Returns: The `SecPolicy`. + /// - Throws: An `AFError.serverTrustEvaluationFailed` error with reason `.revocationPolicyCreationFailed` + /// if the policy cannot be created. + public static func revocation(options: RevocationTrustEvaluator.Options) throws -> SecPolicy { + guard let policy = SecPolicyCreateRevocation(options.rawValue) else { + throw AFError.serverTrustEvaluationFailed(reason: .revocationPolicyCreationFailed) + } + + return policy + } +} + +extension Array: AlamofireExtended {} +extension AlamofireExtension where ExtendedType == [SecCertificate] { + /// All `Data` values for the contained `SecCertificate`s. + public var data: [Data] { + type.map { SecCertificateCopyData($0) as Data } + } + + /// All public `SecKey` values for the contained `SecCertificate`s. + public var publicKeys: [SecKey] { + type.compactMap(\.af.publicKey) + } +} + +extension SecCertificate: AlamofireExtended {} +extension AlamofireExtension where ExtendedType == SecCertificate { + /// The public key for `self`, if it can be extracted. + /// + /// - Note: On 2020 OSes and newer, only RSA and ECDSA keys are supported. + /// + public var publicKey: SecKey? { + let policy = SecPolicyCreateBasicX509() + var trust: SecTrust? + let trustCreationStatus = SecTrustCreateWithCertificates(type, policy, &trust) + + guard let createdTrust = trust, trustCreationStatus == errSecSuccess else { return nil } + + #if swift(>=5.9) + if #available(iOS 14, macOS 11, tvOS 14, watchOS 7, visionOS 1, *) { + return SecTrustCopyKey(createdTrust) + } else { + return SecTrustCopyPublicKey(createdTrust) + } + #else + if #available(iOS 14, macOS 11, tvOS 14, watchOS 7, *) { + return SecTrustCopyKey(createdTrust) + } else { + return SecTrustCopyPublicKey(createdTrust) + } + #endif + } +} + +extension OSStatus: AlamofireExtended {} +extension AlamofireExtension where ExtendedType == OSStatus { + /// Returns whether `self` is `errSecSuccess`. + public var isSuccess: Bool { type == errSecSuccess } +} + +extension SecTrustResultType: AlamofireExtended {} +extension AlamofireExtension where ExtendedType == SecTrustResultType { + /// Returns whether `self` is `.unspecified` or `.proceed`. + public var isSuccess: Bool { + type == .unspecified || type == .proceed + } +} +#endif diff --git a/Pods/Alamofire/Source/Features/URLEncodedFormEncoder.swift b/Pods/Alamofire/Source/Features/URLEncodedFormEncoder.swift new file mode 100644 index 0000000..a2156b1 --- /dev/null +++ b/Pods/Alamofire/Source/Features/URLEncodedFormEncoder.swift @@ -0,0 +1,1151 @@ +// +// URLEncodedFormEncoder.swift +// +// Copyright (c) 2019 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +import Foundation + +/// An object that encodes instances into URL-encoded query strings. +/// +/// `ArrayEncoding` can be used to configure how `Array` values are encoded. By default, the `.brackets` encoding is +/// used, encoding array values with brackets for each value. e.g `array[]=1&array[]=2`. +/// +/// `BoolEncoding` can be used to configure how `Bool` values are encoded. By default, the `.numeric` encoding is used, +/// encoding `true` as `1` and `false` as `0`. +/// +/// `DataEncoding` can be used to configure how `Data` values are encoded. By default, the `.deferredToData` encoding is +/// used, which encodes `Data` values using their default `Encodable` implementation. +/// +/// `DateEncoding` can be used to configure how `Date` values are encoded. By default, the `.deferredToDate` +/// encoding is used, which encodes `Date`s using their default `Encodable` implementation. +/// +/// `KeyEncoding` can be used to configure how keys are encoded. By default, the `.useDefaultKeys` encoding is used, +/// which encodes the keys directly from the `Encodable` implementation. +/// +/// `KeyPathEncoding` can be used to configure how paths within nested objects are encoded. By default, the `.brackets` +/// encoding is used, which encodes each sub-key in brackets. e.g. `parent[child][grandchild]=value`. +/// +/// `NilEncoding` can be used to configure how `nil` `Optional` values are encoded. By default, the `.dropKey` encoding +/// is used, which drops `nil` key / value pairs from the output entirely. +/// +/// `SpaceEncoding` can be used to configure how spaces are encoded. By default, the `.percentEscaped` encoding is used, +/// replacing spaces with `%20`. +/// +/// This type is largely based on Vapor's [`url-encoded-form`](https://github.com/vapor/url-encoded-form) project. +public final class URLEncodedFormEncoder { + /// Encoding to use for `Array` values. + public enum ArrayEncoding { + /// An empty set of square brackets ("[]") are appended to the key for every value. This is the default encoding. + case brackets + /// No brackets are appended to the key and the key is encoded as is. + case noBrackets + /// Brackets containing the item index are appended. This matches the jQuery and Node.js behavior. + case indexInBrackets + /// Provide a custom array key encoding with the given closure. + case custom((_ key: String, _ index: Int) -> String) + + /// Encodes the key according to the encoding. + /// + /// - Parameters: + /// - key: The `key` to encode. + /// - index: When this enum instance is `.indexInBrackets`, the `index` to encode. + /// + /// - Returns: The encoded key. + func encode(_ key: String, atIndex index: Int) -> String { + switch self { + case .brackets: return "\(key)[]" + case .noBrackets: return key + case .indexInBrackets: return "\(key)[\(index)]" + case let .custom(encoding): return encoding(key, index) + } + } + } + + /// Encoding to use for `Bool` values. + public enum BoolEncoding { + /// Encodes `true` as `1`, `false` as `0`. + case numeric + /// Encodes `true` as "true", `false` as "false". This is the default encoding. + case literal + + /// Encodes the given `Bool` as a `String`. + /// + /// - Parameter value: The `Bool` to encode. + /// + /// - Returns: The encoded `String`. + func encode(_ value: Bool) -> String { + switch self { + case .numeric: return value ? "1" : "0" + case .literal: return value ? "true" : "false" + } + } + } + + /// Encoding to use for `Data` values. + public enum DataEncoding { + /// Defers encoding to the `Data` type. + case deferredToData + /// Encodes `Data` as a Base64-encoded string. This is the default encoding. + case base64 + /// Encode the `Data` as a custom value encoded by the given closure. + case custom((Data) throws -> String) + + /// Encodes `Data` according to the encoding. + /// + /// - Parameter data: The `Data` to encode. + /// + /// - Returns: The encoded `String`, or `nil` if the `Data` should be encoded according to its + /// `Encodable` implementation. + func encode(_ data: Data) throws -> String? { + switch self { + case .deferredToData: return nil + case .base64: return data.base64EncodedString() + case let .custom(encoding): return try encoding(data) + } + } + } + + /// Encoding to use for `Date` values. + public enum DateEncoding { + /// ISO8601 and RFC3339 formatter. + private static let iso8601Formatter: ISO8601DateFormatter = { + let formatter = ISO8601DateFormatter() + formatter.formatOptions = .withInternetDateTime + return formatter + }() + + /// Defers encoding to the `Date` type. This is the default encoding. + case deferredToDate + /// Encodes `Date`s as seconds since midnight UTC on January 1, 1970. + case secondsSince1970 + /// Encodes `Date`s as milliseconds since midnight UTC on January 1, 1970. + case millisecondsSince1970 + /// Encodes `Date`s according to the ISO8601 and RFC3339 standards. + case iso8601 + /// Encodes `Date`s using the given `DateFormatter`. + case formatted(DateFormatter) + /// Encodes `Date`s using the given closure. + case custom((Date) throws -> String) + + /// Encodes the date according to the encoding. + /// + /// - Parameter date: The `Date` to encode. + /// + /// - Returns: The encoded `String`, or `nil` if the `Date` should be encoded according to its + /// `Encodable` implementation. + func encode(_ date: Date) throws -> String? { + switch self { + case .deferredToDate: + return nil + case .secondsSince1970: + return String(date.timeIntervalSince1970) + case .millisecondsSince1970: + return String(date.timeIntervalSince1970 * 1000.0) + case .iso8601: + return DateEncoding.iso8601Formatter.string(from: date) + case let .formatted(formatter): + return formatter.string(from: date) + case let .custom(closure): + return try closure(date) + } + } + } + + /// Encoding to use for keys. + /// + /// This type is derived from [`JSONEncoder`'s `KeyEncodingStrategy`](https://github.com/apple/swift/blob/6aa313b8dd5f05135f7f878eccc1db6f9fbe34ff/stdlib/public/Darwin/Foundation/JSONEncoder.swift#L128) + /// and [`XMLEncoder`s `KeyEncodingStrategy`](https://github.com/MaxDesiatov/XMLCoder/blob/master/Sources/XMLCoder/Encoder/XMLEncoder.swift#L102). + public enum KeyEncoding { + /// Use the keys specified by each type. This is the default encoding. + case useDefaultKeys + /// Convert from "camelCaseKeys" to "snake_case_keys" before writing a key. + /// + /// Capital characters are determined by testing membership in + /// `CharacterSet.uppercaseLetters` and `CharacterSet.lowercaseLetters` + /// (Unicode General Categories Lu and Lt). + /// The conversion to lower case uses `Locale.system`, also known as + /// the ICU "root" locale. This means the result is consistent + /// regardless of the current user's locale and language preferences. + /// + /// Converting from camel case to snake case: + /// 1. Splits words at the boundary of lower-case to upper-case + /// 2. Inserts `_` between words + /// 3. Lowercases the entire string + /// 4. Preserves starting and ending `_`. + /// + /// For example, `oneTwoThree` becomes `one_two_three`. `_oneTwoThree_` becomes `_one_two_three_`. + /// + /// - Note: Using a key encoding strategy has a nominal performance cost, as each string key has to be converted. + case convertToSnakeCase + /// Same as convertToSnakeCase, but using `-` instead of `_`. + /// For example `oneTwoThree` becomes `one-two-three`. + case convertToKebabCase + /// Capitalize the first letter only. + /// For example `oneTwoThree` becomes `OneTwoThree`. + case capitalized + /// Uppercase all letters. + /// For example `oneTwoThree` becomes `ONETWOTHREE`. + case uppercased + /// Lowercase all letters. + /// For example `oneTwoThree` becomes `onetwothree`. + case lowercased + /// A custom encoding using the provided closure. + case custom((String) -> String) + + func encode(_ key: String) -> String { + switch self { + case .useDefaultKeys: return key + case .convertToSnakeCase: return convertToSnakeCase(key) + case .convertToKebabCase: return convertToKebabCase(key) + case .capitalized: return String(key.prefix(1).uppercased() + key.dropFirst()) + case .uppercased: return key.uppercased() + case .lowercased: return key.lowercased() + case let .custom(encoding): return encoding(key) + } + } + + private func convertToSnakeCase(_ key: String) -> String { + convert(key, usingSeparator: "_") + } + + private func convertToKebabCase(_ key: String) -> String { + convert(key, usingSeparator: "-") + } + + private func convert(_ key: String, usingSeparator separator: String) -> String { + guard !key.isEmpty else { return key } + + var words: [Range] = [] + // The general idea of this algorithm is to split words on + // transition from lower to upper case, then on transition of >1 + // upper case characters to lowercase + // + // myProperty -> my_property + // myURLProperty -> my_url_property + // + // It is assumed, per Swift naming conventions, that the first character of the key is lowercase. + var wordStart = key.startIndex + var searchRange = key.index(after: wordStart)..1 capital letters. Turn those into a word, stopping at the capital before + // the lower case character. + let beforeLowerIndex = key.index(before: lowerCaseRange.lowerBound) + words.append(upperCaseRange.lowerBound.. String + + /// Creates an instance with the encoding closure called for each sub-key in a key path. + /// + /// - Parameter encoding: Closure used to perform the encoding. + public init(encoding: @escaping (_ subkey: String) -> String) { + self.encoding = encoding + } + + func encodeKeyPath(_ keyPath: String) -> String { + encoding(keyPath) + } + } + + /// Encoding to use for `nil` values. + public struct NilEncoding { + /// Encodes `nil` by dropping the entire key / value pair. + public static let dropKey = NilEncoding { nil } + /// Encodes `nil` by dropping only the value. e.g. `value1=one&nilValue=&value2=two`. + public static let dropValue = NilEncoding { "" } + /// Encodes `nil` as `null`. + public static let null = NilEncoding { "null" } + + private let encoding: () -> String? + + /// Creates an instance with the encoding closure called for `nil` values. + /// + /// - Parameter encoding: Closure used to perform the encoding. + public init(encoding: @escaping () -> String?) { + self.encoding = encoding + } + + func encodeNil() -> String? { + encoding() + } + } + + /// Encoding to use for spaces. + public enum SpaceEncoding { + /// Encodes spaces using percent escaping (`%20`). + case percentEscaped + /// Encodes spaces as `+`. + case plusReplaced + + /// Encodes the string according to the encoding. + /// + /// - Parameter string: The `String` to encode. + /// + /// - Returns: The encoded `String`. + func encode(_ string: String) -> String { + switch self { + case .percentEscaped: return string.replacingOccurrences(of: " ", with: "%20") + case .plusReplaced: return string.replacingOccurrences(of: " ", with: "+") + } + } + } + + /// `URLEncodedFormEncoder` error. + public enum Error: Swift.Error { + /// An invalid root object was created by the encoder. Only keyed values are valid. + case invalidRootObject(String) + + var localizedDescription: String { + switch self { + case let .invalidRootObject(object): + return "URLEncodedFormEncoder requires keyed root object. Received \(object) instead." + } + } + } + + /// Whether or not to sort the encoded key value pairs. + /// + /// - Note: This setting ensures a consistent ordering for all encodings of the same parameters. When set to `false`, + /// encoded `Dictionary` values may have a different encoded order each time they're encoded due to + /// ` Dictionary`'s random storage order, but `Encodable` types will maintain their encoded order. + public let alphabetizeKeyValuePairs: Bool + /// The `ArrayEncoding` to use. + public let arrayEncoding: ArrayEncoding + /// The `BoolEncoding` to use. + public let boolEncoding: BoolEncoding + /// THe `DataEncoding` to use. + public let dataEncoding: DataEncoding + /// The `DateEncoding` to use. + public let dateEncoding: DateEncoding + /// The `KeyEncoding` to use. + public let keyEncoding: KeyEncoding + /// The `KeyPathEncoding` to use. + public let keyPathEncoding: KeyPathEncoding + /// The `NilEncoding` to use. + public let nilEncoding: NilEncoding + /// The `SpaceEncoding` to use. + public let spaceEncoding: SpaceEncoding + /// The `CharacterSet` of allowed (non-escaped) characters. + public var allowedCharacters: CharacterSet + + /// Creates an instance from the supplied parameters. + /// + /// - Parameters: + /// - alphabetizeKeyValuePairs: Whether or not to sort the encoded key value pairs. `true` by default. + /// - arrayEncoding: The `ArrayEncoding` to use. `.brackets` by default. + /// - boolEncoding: The `BoolEncoding` to use. `.numeric` by default. + /// - dataEncoding: The `DataEncoding` to use. `.base64` by default. + /// - dateEncoding: The `DateEncoding` to use. `.deferredToDate` by default. + /// - keyEncoding: The `KeyEncoding` to use. `.useDefaultKeys` by default. + /// - nilEncoding: The `NilEncoding` to use. `.drop` by default. + /// - spaceEncoding: The `SpaceEncoding` to use. `.percentEscaped` by default. + /// - allowedCharacters: The `CharacterSet` of allowed (non-escaped) characters. `.afURLQueryAllowed` by + /// default. + public init(alphabetizeKeyValuePairs: Bool = true, + arrayEncoding: ArrayEncoding = .brackets, + boolEncoding: BoolEncoding = .numeric, + dataEncoding: DataEncoding = .base64, + dateEncoding: DateEncoding = .deferredToDate, + keyEncoding: KeyEncoding = .useDefaultKeys, + keyPathEncoding: KeyPathEncoding = .brackets, + nilEncoding: NilEncoding = .dropKey, + spaceEncoding: SpaceEncoding = .percentEscaped, + allowedCharacters: CharacterSet = .afURLQueryAllowed) { + self.alphabetizeKeyValuePairs = alphabetizeKeyValuePairs + self.arrayEncoding = arrayEncoding + self.boolEncoding = boolEncoding + self.dataEncoding = dataEncoding + self.dateEncoding = dateEncoding + self.keyEncoding = keyEncoding + self.keyPathEncoding = keyPathEncoding + self.nilEncoding = nilEncoding + self.spaceEncoding = spaceEncoding + self.allowedCharacters = allowedCharacters + } + + func encode(_ value: Encodable) throws -> URLEncodedFormComponent { + let context = URLEncodedFormContext(.object([])) + let encoder = _URLEncodedFormEncoder(context: context, + boolEncoding: boolEncoding, + dataEncoding: dataEncoding, + dateEncoding: dateEncoding, + nilEncoding: nilEncoding) + try value.encode(to: encoder) + + return context.component + } + + /// Encodes the `value` as a URL form encoded `String`. + /// + /// - Parameter value: The `Encodable` value. + /// + /// - Returns: The encoded `String`. + /// - Throws: An `Error` or `EncodingError` instance if encoding fails. + public func encode(_ value: Encodable) throws -> String { + let component: URLEncodedFormComponent = try encode(value) + + guard case let .object(object) = component else { + throw Error.invalidRootObject("\(component)") + } + + let serializer = URLEncodedFormSerializer(alphabetizeKeyValuePairs: alphabetizeKeyValuePairs, + arrayEncoding: arrayEncoding, + keyEncoding: keyEncoding, + keyPathEncoding: keyPathEncoding, + spaceEncoding: spaceEncoding, + allowedCharacters: allowedCharacters) + let query = serializer.serialize(object) + + return query + } + + /// Encodes the value as `Data`. This is performed by first creating an encoded `String` and then returning the + /// `.utf8` data. + /// + /// - Parameter value: The `Encodable` value. + /// + /// - Returns: The encoded `Data`. + /// + /// - Throws: An `Error` or `EncodingError` instance if encoding fails. + public func encode(_ value: Encodable) throws -> Data { + let string: String = try encode(value) + + return Data(string.utf8) + } +} + +final class _URLEncodedFormEncoder { + var codingPath: [CodingKey] + // Returns an empty dictionary, as this encoder doesn't support userInfo. + var userInfo: [CodingUserInfoKey: Any] { [:] } + + let context: URLEncodedFormContext + + private let boolEncoding: URLEncodedFormEncoder.BoolEncoding + private let dataEncoding: URLEncodedFormEncoder.DataEncoding + private let dateEncoding: URLEncodedFormEncoder.DateEncoding + private let nilEncoding: URLEncodedFormEncoder.NilEncoding + + init(context: URLEncodedFormContext, + codingPath: [CodingKey] = [], + boolEncoding: URLEncodedFormEncoder.BoolEncoding, + dataEncoding: URLEncodedFormEncoder.DataEncoding, + dateEncoding: URLEncodedFormEncoder.DateEncoding, + nilEncoding: URLEncodedFormEncoder.NilEncoding) { + self.context = context + self.codingPath = codingPath + self.boolEncoding = boolEncoding + self.dataEncoding = dataEncoding + self.dateEncoding = dateEncoding + self.nilEncoding = nilEncoding + } +} + +extension _URLEncodedFormEncoder: Encoder { + func container(keyedBy type: Key.Type) -> KeyedEncodingContainer where Key: CodingKey { + let container = _URLEncodedFormEncoder.KeyedContainer(context: context, + codingPath: codingPath, + boolEncoding: boolEncoding, + dataEncoding: dataEncoding, + dateEncoding: dateEncoding, + nilEncoding: nilEncoding) + return KeyedEncodingContainer(container) + } + + func unkeyedContainer() -> UnkeyedEncodingContainer { + _URLEncodedFormEncoder.UnkeyedContainer(context: context, + codingPath: codingPath, + boolEncoding: boolEncoding, + dataEncoding: dataEncoding, + dateEncoding: dateEncoding, + nilEncoding: nilEncoding) + } + + func singleValueContainer() -> SingleValueEncodingContainer { + _URLEncodedFormEncoder.SingleValueContainer(context: context, + codingPath: codingPath, + boolEncoding: boolEncoding, + dataEncoding: dataEncoding, + dateEncoding: dateEncoding, + nilEncoding: nilEncoding) + } +} + +final class URLEncodedFormContext { + var component: URLEncodedFormComponent + + init(_ component: URLEncodedFormComponent) { + self.component = component + } +} + +enum URLEncodedFormComponent { + typealias Object = [(key: String, value: URLEncodedFormComponent)] + + case string(String) + case array([URLEncodedFormComponent]) + case object(Object) + + /// Converts self to an `[URLEncodedFormData]` or returns `nil` if not convertible. + var array: [URLEncodedFormComponent]? { + switch self { + case let .array(array): return array + default: return nil + } + } + + /// Converts self to an `Object` or returns `nil` if not convertible. + var object: Object? { + switch self { + case let .object(object): return object + default: return nil + } + } + + /// Sets self to the supplied value at a given path. + /// + /// data.set(to: "hello", at: ["path", "to", "value"]) + /// + /// - parameters: + /// - value: Value of `Self` to set at the supplied path. + /// - path: `CodingKey` path to update with the supplied value. + public mutating func set(to value: URLEncodedFormComponent, at path: [CodingKey]) { + set(&self, to: value, at: path) + } + + /// Recursive backing method to `set(to:at:)`. + private func set(_ context: inout URLEncodedFormComponent, to value: URLEncodedFormComponent, at path: [CodingKey]) { + guard !path.isEmpty else { + context = value + return + } + + let end = path[0] + var child: URLEncodedFormComponent + switch path.count { + case 1: + child = value + case 2...: + if let index = end.intValue { + let array = context.array ?? [] + if array.count > index { + child = array[index] + } else { + child = .array([]) + } + set(&child, to: value, at: Array(path[1...])) + } else { + child = context.object?.first { $0.key == end.stringValue }?.value ?? .object(.init()) + set(&child, to: value, at: Array(path[1...])) + } + default: fatalError("Unreachable") + } + + if let index = end.intValue { + if var array = context.array { + if array.count > index { + array[index] = child + } else { + array.append(child) + } + context = .array(array) + } else { + context = .array([child]) + } + } else { + if var object = context.object { + if let index = object.firstIndex(where: { $0.key == end.stringValue }) { + object[index] = (key: end.stringValue, value: child) + } else { + object.append((key: end.stringValue, value: child)) + } + context = .object(object) + } else { + context = .object([(key: end.stringValue, value: child)]) + } + } + } +} + +struct AnyCodingKey: CodingKey, Hashable { + let stringValue: String + let intValue: Int? + + init?(stringValue: String) { + self.stringValue = stringValue + intValue = nil + } + + init?(intValue: Int) { + stringValue = "\(intValue)" + self.intValue = intValue + } + + init(_ base: Key) where Key: CodingKey { + if let intValue = base.intValue { + self.init(intValue: intValue)! + } else { + self.init(stringValue: base.stringValue)! + } + } +} + +extension _URLEncodedFormEncoder { + final class KeyedContainer where Key: CodingKey { + var codingPath: [CodingKey] + + private let context: URLEncodedFormContext + private let boolEncoding: URLEncodedFormEncoder.BoolEncoding + private let dataEncoding: URLEncodedFormEncoder.DataEncoding + private let dateEncoding: URLEncodedFormEncoder.DateEncoding + private let nilEncoding: URLEncodedFormEncoder.NilEncoding + + init(context: URLEncodedFormContext, + codingPath: [CodingKey], + boolEncoding: URLEncodedFormEncoder.BoolEncoding, + dataEncoding: URLEncodedFormEncoder.DataEncoding, + dateEncoding: URLEncodedFormEncoder.DateEncoding, + nilEncoding: URLEncodedFormEncoder.NilEncoding) { + self.context = context + self.codingPath = codingPath + self.boolEncoding = boolEncoding + self.dataEncoding = dataEncoding + self.dateEncoding = dateEncoding + self.nilEncoding = nilEncoding + } + + private func nestedCodingPath(for key: CodingKey) -> [CodingKey] { + codingPath + [key] + } + } +} + +extension _URLEncodedFormEncoder.KeyedContainer: KeyedEncodingContainerProtocol { + func encodeNil(forKey key: Key) throws { + guard let nilValue = nilEncoding.encodeNil() else { return } + + try encode(nilValue, forKey: key) + } + + func encodeIfPresent(_ value: Bool?, forKey key: Key) throws { + try _encodeIfPresent(value, forKey: key) + } + + func encodeIfPresent(_ value: String?, forKey key: Key) throws { + try _encodeIfPresent(value, forKey: key) + } + + func encodeIfPresent(_ value: Double?, forKey key: Key) throws { + try _encodeIfPresent(value, forKey: key) + } + + func encodeIfPresent(_ value: Float?, forKey key: Key) throws { + try _encodeIfPresent(value, forKey: key) + } + + func encodeIfPresent(_ value: Int?, forKey key: Key) throws { + try _encodeIfPresent(value, forKey: key) + } + + func encodeIfPresent(_ value: Int8?, forKey key: Key) throws { + try _encodeIfPresent(value, forKey: key) + } + + func encodeIfPresent(_ value: Int16?, forKey key: Key) throws { + try _encodeIfPresent(value, forKey: key) + } + + func encodeIfPresent(_ value: Int32?, forKey key: Key) throws { + try _encodeIfPresent(value, forKey: key) + } + + func encodeIfPresent(_ value: Int64?, forKey key: Key) throws { + try _encodeIfPresent(value, forKey: key) + } + + func encodeIfPresent(_ value: UInt?, forKey key: Key) throws { + try _encodeIfPresent(value, forKey: key) + } + + func encodeIfPresent(_ value: UInt8?, forKey key: Key) throws { + try _encodeIfPresent(value, forKey: key) + } + + func encodeIfPresent(_ value: UInt16?, forKey key: Key) throws { + try _encodeIfPresent(value, forKey: key) + } + + func encodeIfPresent(_ value: UInt32?, forKey key: Key) throws { + try _encodeIfPresent(value, forKey: key) + } + + func encodeIfPresent(_ value: UInt64?, forKey key: Key) throws { + try _encodeIfPresent(value, forKey: key) + } + + func encodeIfPresent(_ value: Value?, forKey key: Key) throws where Value: Encodable { + try _encodeIfPresent(value, forKey: key) + } + + func _encodeIfPresent(_ value: Value?, forKey key: Key) throws where Value: Encodable { + if let value { + try encode(value, forKey: key) + } else { + try encodeNil(forKey: key) + } + } + + func encode(_ value: T, forKey key: Key) throws where T: Encodable { + var container = nestedSingleValueEncoder(for: key) + try container.encode(value) + } + + func nestedSingleValueEncoder(for key: Key) -> SingleValueEncodingContainer { + let container = _URLEncodedFormEncoder.SingleValueContainer(context: context, + codingPath: nestedCodingPath(for: key), + boolEncoding: boolEncoding, + dataEncoding: dataEncoding, + dateEncoding: dateEncoding, + nilEncoding: nilEncoding) + + return container + } + + func nestedUnkeyedContainer(forKey key: Key) -> UnkeyedEncodingContainer { + let container = _URLEncodedFormEncoder.UnkeyedContainer(context: context, + codingPath: nestedCodingPath(for: key), + boolEncoding: boolEncoding, + dataEncoding: dataEncoding, + dateEncoding: dateEncoding, + nilEncoding: nilEncoding) + + return container + } + + func nestedContainer(keyedBy keyType: NestedKey.Type, forKey key: Key) -> KeyedEncodingContainer where NestedKey: CodingKey { + let container = _URLEncodedFormEncoder.KeyedContainer(context: context, + codingPath: nestedCodingPath(for: key), + boolEncoding: boolEncoding, + dataEncoding: dataEncoding, + dateEncoding: dateEncoding, + nilEncoding: nilEncoding) + + return KeyedEncodingContainer(container) + } + + func superEncoder() -> Encoder { + _URLEncodedFormEncoder(context: context, + codingPath: codingPath, + boolEncoding: boolEncoding, + dataEncoding: dataEncoding, + dateEncoding: dateEncoding, + nilEncoding: nilEncoding) + } + + func superEncoder(forKey key: Key) -> Encoder { + _URLEncodedFormEncoder(context: context, + codingPath: nestedCodingPath(for: key), + boolEncoding: boolEncoding, + dataEncoding: dataEncoding, + dateEncoding: dateEncoding, + nilEncoding: nilEncoding) + } +} + +extension _URLEncodedFormEncoder { + final class SingleValueContainer { + var codingPath: [CodingKey] + + private var canEncodeNewValue = true + + private let context: URLEncodedFormContext + private let boolEncoding: URLEncodedFormEncoder.BoolEncoding + private let dataEncoding: URLEncodedFormEncoder.DataEncoding + private let dateEncoding: URLEncodedFormEncoder.DateEncoding + private let nilEncoding: URLEncodedFormEncoder.NilEncoding + + init(context: URLEncodedFormContext, + codingPath: [CodingKey], + boolEncoding: URLEncodedFormEncoder.BoolEncoding, + dataEncoding: URLEncodedFormEncoder.DataEncoding, + dateEncoding: URLEncodedFormEncoder.DateEncoding, + nilEncoding: URLEncodedFormEncoder.NilEncoding) { + self.context = context + self.codingPath = codingPath + self.boolEncoding = boolEncoding + self.dataEncoding = dataEncoding + self.dateEncoding = dateEncoding + self.nilEncoding = nilEncoding + } + + private func checkCanEncode(value: Any?) throws { + guard canEncodeNewValue else { + let context = EncodingError.Context(codingPath: codingPath, + debugDescription: "Attempt to encode value through single value container when previously value already encoded.") + throw EncodingError.invalidValue(value as Any, context) + } + } + } +} + +extension _URLEncodedFormEncoder.SingleValueContainer: SingleValueEncodingContainer { + func encodeNil() throws { + guard let nilValue = nilEncoding.encodeNil() else { return } + + try encode(nilValue) + } + + func encode(_ value: Bool) throws { + try encode(value, as: String(boolEncoding.encode(value))) + } + + func encode(_ value: String) throws { + try encode(value, as: value) + } + + func encode(_ value: Double) throws { + try encode(value, as: String(value)) + } + + func encode(_ value: Float) throws { + try encode(value, as: String(value)) + } + + func encode(_ value: Int) throws { + try encode(value, as: String(value)) + } + + func encode(_ value: Int8) throws { + try encode(value, as: String(value)) + } + + func encode(_ value: Int16) throws { + try encode(value, as: String(value)) + } + + func encode(_ value: Int32) throws { + try encode(value, as: String(value)) + } + + func encode(_ value: Int64) throws { + try encode(value, as: String(value)) + } + + func encode(_ value: UInt) throws { + try encode(value, as: String(value)) + } + + func encode(_ value: UInt8) throws { + try encode(value, as: String(value)) + } + + func encode(_ value: UInt16) throws { + try encode(value, as: String(value)) + } + + func encode(_ value: UInt32) throws { + try encode(value, as: String(value)) + } + + func encode(_ value: UInt64) throws { + try encode(value, as: String(value)) + } + + private func encode(_ value: T, as string: String) throws where T: Encodable { + try checkCanEncode(value: value) + defer { canEncodeNewValue = false } + + context.component.set(to: .string(string), at: codingPath) + } + + func encode(_ value: T) throws where T: Encodable { + switch value { + case let date as Date: + guard let string = try dateEncoding.encode(date) else { + try attemptToEncode(value) + return + } + + try encode(value, as: string) + case let data as Data: + guard let string = try dataEncoding.encode(data) else { + try attemptToEncode(value) + return + } + + try encode(value, as: string) + case let decimal as Decimal: + // Decimal's `Encodable` implementation returns an object, not a single value, so override it. + try encode(value, as: String(describing: decimal)) + default: + try attemptToEncode(value) + } + } + + private func attemptToEncode(_ value: T) throws where T: Encodable { + try checkCanEncode(value: value) + defer { canEncodeNewValue = false } + + let encoder = _URLEncodedFormEncoder(context: context, + codingPath: codingPath, + boolEncoding: boolEncoding, + dataEncoding: dataEncoding, + dateEncoding: dateEncoding, + nilEncoding: nilEncoding) + try value.encode(to: encoder) + } +} + +extension _URLEncodedFormEncoder { + final class UnkeyedContainer { + var codingPath: [CodingKey] + + var count = 0 + var nestedCodingPath: [CodingKey] { + codingPath + [AnyCodingKey(intValue: count)!] + } + + private let context: URLEncodedFormContext + private let boolEncoding: URLEncodedFormEncoder.BoolEncoding + private let dataEncoding: URLEncodedFormEncoder.DataEncoding + private let dateEncoding: URLEncodedFormEncoder.DateEncoding + private let nilEncoding: URLEncodedFormEncoder.NilEncoding + + init(context: URLEncodedFormContext, + codingPath: [CodingKey], + boolEncoding: URLEncodedFormEncoder.BoolEncoding, + dataEncoding: URLEncodedFormEncoder.DataEncoding, + dateEncoding: URLEncodedFormEncoder.DateEncoding, + nilEncoding: URLEncodedFormEncoder.NilEncoding) { + self.context = context + self.codingPath = codingPath + self.boolEncoding = boolEncoding + self.dataEncoding = dataEncoding + self.dateEncoding = dateEncoding + self.nilEncoding = nilEncoding + } + } +} + +extension _URLEncodedFormEncoder.UnkeyedContainer: UnkeyedEncodingContainer { + func encodeNil() throws { + guard let nilValue = nilEncoding.encodeNil() else { return } + + try encode(nilValue) + } + + func encode(_ value: T) throws where T: Encodable { + var container = nestedSingleValueContainer() + try container.encode(value) + } + + func nestedSingleValueContainer() -> SingleValueEncodingContainer { + defer { count += 1 } + + return _URLEncodedFormEncoder.SingleValueContainer(context: context, + codingPath: nestedCodingPath, + boolEncoding: boolEncoding, + dataEncoding: dataEncoding, + dateEncoding: dateEncoding, + nilEncoding: nilEncoding) + } + + func nestedContainer(keyedBy keyType: NestedKey.Type) -> KeyedEncodingContainer where NestedKey: CodingKey { + defer { count += 1 } + let container = _URLEncodedFormEncoder.KeyedContainer(context: context, + codingPath: nestedCodingPath, + boolEncoding: boolEncoding, + dataEncoding: dataEncoding, + dateEncoding: dateEncoding, + nilEncoding: nilEncoding) + + return KeyedEncodingContainer(container) + } + + func nestedUnkeyedContainer() -> UnkeyedEncodingContainer { + defer { count += 1 } + + return _URLEncodedFormEncoder.UnkeyedContainer(context: context, + codingPath: nestedCodingPath, + boolEncoding: boolEncoding, + dataEncoding: dataEncoding, + dateEncoding: dateEncoding, + nilEncoding: nilEncoding) + } + + func superEncoder() -> Encoder { + defer { count += 1 } + + return _URLEncodedFormEncoder(context: context, + codingPath: codingPath, + boolEncoding: boolEncoding, + dataEncoding: dataEncoding, + dateEncoding: dateEncoding, + nilEncoding: nilEncoding) + } +} + +final class URLEncodedFormSerializer { + private let alphabetizeKeyValuePairs: Bool + private let arrayEncoding: URLEncodedFormEncoder.ArrayEncoding + private let keyEncoding: URLEncodedFormEncoder.KeyEncoding + private let keyPathEncoding: URLEncodedFormEncoder.KeyPathEncoding + private let spaceEncoding: URLEncodedFormEncoder.SpaceEncoding + private let allowedCharacters: CharacterSet + + init(alphabetizeKeyValuePairs: Bool, + arrayEncoding: URLEncodedFormEncoder.ArrayEncoding, + keyEncoding: URLEncodedFormEncoder.KeyEncoding, + keyPathEncoding: URLEncodedFormEncoder.KeyPathEncoding, + spaceEncoding: URLEncodedFormEncoder.SpaceEncoding, + allowedCharacters: CharacterSet) { + self.alphabetizeKeyValuePairs = alphabetizeKeyValuePairs + self.arrayEncoding = arrayEncoding + self.keyEncoding = keyEncoding + self.keyPathEncoding = keyPathEncoding + self.spaceEncoding = spaceEncoding + self.allowedCharacters = allowedCharacters + } + + func serialize(_ object: URLEncodedFormComponent.Object) -> String { + var output: [String] = [] + for (key, component) in object { + let value = serialize(component, forKey: key) + output.append(value) + } + output = alphabetizeKeyValuePairs ? output.sorted() : output + + return output.joinedWithAmpersands() + } + + func serialize(_ component: URLEncodedFormComponent, forKey key: String) -> String { + switch component { + case let .string(string): return "\(escape(keyEncoding.encode(key)))=\(escape(string))" + case let .array(array): return serialize(array, forKey: key) + case let .object(object): return serialize(object, forKey: key) + } + } + + func serialize(_ object: URLEncodedFormComponent.Object, forKey key: String) -> String { + var segments: [String] = object.map { subKey, value in + let keyPath = keyPathEncoding.encodeKeyPath(subKey) + return serialize(value, forKey: key + keyPath) + } + segments = alphabetizeKeyValuePairs ? segments.sorted() : segments + + return segments.joinedWithAmpersands() + } + + func serialize(_ array: [URLEncodedFormComponent], forKey key: String) -> String { + var segments: [String] = array.enumerated().map { index, component in + let keyPath = arrayEncoding.encode(key, atIndex: index) + return serialize(component, forKey: keyPath) + } + segments = alphabetizeKeyValuePairs ? segments.sorted() : segments + + return segments.joinedWithAmpersands() + } + + func escape(_ query: String) -> String { + var allowedCharactersWithSpace = allowedCharacters + allowedCharactersWithSpace.insert(charactersIn: " ") + let escapedQuery = query.addingPercentEncoding(withAllowedCharacters: allowedCharactersWithSpace) ?? query + let spaceEncodedQuery = spaceEncoding.encode(escapedQuery) + + return spaceEncodedQuery + } +} + +extension [String] { + func joinedWithAmpersands() -> String { + joined(separator: "&") + } +} + +extension CharacterSet { + /// Creates a CharacterSet from RFC 3986 allowed characters. + /// + /// RFC 3986 states that the following characters are "reserved" characters. + /// + /// - General Delimiters: ":", "#", "[", "]", "@", "?", "/" + /// - Sub-Delimiters: "!", "$", "&", "'", "(", ")", "*", "+", ",", ";", "=" + /// + /// In RFC 3986 - Section 3.4, it states that the "?" and "/" characters should not be escaped to allow + /// query strings to include a URL. Therefore, all "reserved" characters with the exception of "?" and "/" + /// should be percent-escaped in the query string. + public static let afURLQueryAllowed: CharacterSet = { + let generalDelimitersToEncode = ":#[]@" // does not include "?" or "/" due to RFC 3986 - Section 3.4 + let subDelimitersToEncode = "!$&'()*+,;=" + let encodableDelimiters = CharacterSet(charactersIn: "\(generalDelimitersToEncode)\(subDelimitersToEncode)") + + return CharacterSet.urlQueryAllowed.subtracting(encodableDelimiters) + }() +} diff --git a/Pods/Alamofire/Source/Features/Validation.swift b/Pods/Alamofire/Source/Features/Validation.swift new file mode 100644 index 0000000..eab4576 --- /dev/null +++ b/Pods/Alamofire/Source/Features/Validation.swift @@ -0,0 +1,302 @@ +// +// Validation.swift +// +// Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +import Foundation + +extension Request { + // MARK: Helper Types + + fileprivate typealias ErrorReason = AFError.ResponseValidationFailureReason + + /// Used to represent whether a validation succeeded or failed. + public typealias ValidationResult = Result + + fileprivate struct MIMEType { + let type: String + let subtype: String + + var isWildcard: Bool { type == "*" && subtype == "*" } + + init?(_ string: String) { + let components: [String] = { + let stripped = string.trimmingCharacters(in: .whitespacesAndNewlines) + let split = stripped[..<(stripped.range(of: ";")?.lowerBound ?? stripped.endIndex)] + + return split.components(separatedBy: "/") + }() + + if let type = components.first, let subtype = components.last { + self.type = type + self.subtype = subtype + } else { + return nil + } + } + + func matches(_ mime: MIMEType) -> Bool { + switch (type, subtype) { + case (mime.type, mime.subtype), (mime.type, "*"), ("*", mime.subtype), ("*", "*"): + return true + default: + return false + } + } + } + + // MARK: Properties + + fileprivate var acceptableStatusCodes: Range { 200..<300 } + + fileprivate var acceptableContentTypes: [String] { + if let accept = request?.value(forHTTPHeaderField: "Accept") { + return accept.components(separatedBy: ",") + } + + return ["*/*"] + } + + // MARK: Status Code + + fileprivate func validate(statusCode acceptableStatusCodes: S, + response: HTTPURLResponse) + -> ValidationResult + where S.Iterator.Element == Int { + if acceptableStatusCodes.contains(response.statusCode) { + return .success(()) + } else { + let reason: ErrorReason = .unacceptableStatusCode(code: response.statusCode) + return .failure(AFError.responseValidationFailed(reason: reason)) + } + } + + // MARK: Content Type + + fileprivate func validate(contentType acceptableContentTypes: S, + response: HTTPURLResponse, + data: Data?) + -> ValidationResult + where S.Iterator.Element == String { + guard let data, !data.isEmpty else { return .success(()) } + + return validate(contentType: acceptableContentTypes, response: response) + } + + fileprivate func validate(contentType acceptableContentTypes: S, + response: HTTPURLResponse) + -> ValidationResult + where S.Iterator.Element == String { + guard + let responseContentType = response.mimeType, + let responseMIMEType = MIMEType(responseContentType) + else { + for contentType in acceptableContentTypes { + if let mimeType = MIMEType(contentType), mimeType.isWildcard { + return .success(()) + } + } + + let error: AFError = { + let reason: ErrorReason = .missingContentType(acceptableContentTypes: acceptableContentTypes.sorted()) + return AFError.responseValidationFailed(reason: reason) + }() + + return .failure(error) + } + + for contentType in acceptableContentTypes { + if let acceptableMIMEType = MIMEType(contentType), acceptableMIMEType.matches(responseMIMEType) { + return .success(()) + } + } + + let error: AFError = { + let reason: ErrorReason = .unacceptableContentType(acceptableContentTypes: acceptableContentTypes.sorted(), + responseContentType: responseContentType) + + return AFError.responseValidationFailed(reason: reason) + }() + + return .failure(error) + } +} + +// MARK: - + +extension DataRequest { + /// A closure used to validate a request that takes a URL request, a URL response and data, and returns whether the + /// request was valid. + public typealias Validation = (URLRequest?, HTTPURLResponse, Data?) -> ValidationResult + + /// Validates that the response has a status code in the specified sequence. + /// + /// If validation fails, subsequent calls to response handlers will have an associated error. + /// + /// - Parameter acceptableStatusCodes: `Sequence` of acceptable response status codes. + /// + /// - Returns: The instance. + @discardableResult + public func validate(statusCode acceptableStatusCodes: S) -> Self where S.Iterator.Element == Int { + validate { [unowned self] _, response, _ in + self.validate(statusCode: acceptableStatusCodes, response: response) + } + } + + /// Validates that the response has a content type in the specified sequence. + /// + /// If validation fails, subsequent calls to response handlers will have an associated error. + /// + /// - parameter contentType: The acceptable content types, which may specify wildcard types and/or subtypes. + /// + /// - returns: The request. + @discardableResult + public func validate(contentType acceptableContentTypes: @escaping @autoclosure () -> S) -> Self where S.Iterator.Element == String { + validate { [unowned self] _, response, data in + self.validate(contentType: acceptableContentTypes(), response: response, data: data) + } + } + + /// Validates that the response has a status code in the default acceptable range of 200...299, and that the content + /// type matches any specified in the Accept HTTP header field. + /// + /// If validation fails, subsequent calls to response handlers will have an associated error. + /// + /// - returns: The request. + @discardableResult + public func validate() -> Self { + let contentTypes: () -> [String] = { [unowned self] in + acceptableContentTypes + } + return validate(statusCode: acceptableStatusCodes).validate(contentType: contentTypes()) + } +} + +extension DataStreamRequest { + /// A closure used to validate a request that takes a `URLRequest` and `HTTPURLResponse` and returns whether the + /// request was valid. + public typealias Validation = (_ request: URLRequest?, _ response: HTTPURLResponse) -> ValidationResult + + /// Validates that the response has a status code in the specified sequence. + /// + /// If validation fails, subsequent calls to response handlers will have an associated error. + /// + /// - Parameter acceptableStatusCodes: `Sequence` of acceptable response status codes. + /// + /// - Returns: The instance. + @discardableResult + public func validate(statusCode acceptableStatusCodes: S) -> Self where S.Iterator.Element == Int { + validate { [unowned self] _, response in + self.validate(statusCode: acceptableStatusCodes, response: response) + } + } + + /// Validates that the response has a content type in the specified sequence. + /// + /// If validation fails, subsequent calls to response handlers will have an associated error. + /// + /// - parameter contentType: The acceptable content types, which may specify wildcard types and/or subtypes. + /// + /// - returns: The request. + @discardableResult + public func validate(contentType acceptableContentTypes: @escaping @autoclosure () -> S) -> Self where S.Iterator.Element == String { + validate { [unowned self] _, response in + self.validate(contentType: acceptableContentTypes(), response: response) + } + } + + /// Validates that the response has a status code in the default acceptable range of 200...299, and that the content + /// type matches any specified in the Accept HTTP header field. + /// + /// If validation fails, subsequent calls to response handlers will have an associated error. + /// + /// - Returns: The instance. + @discardableResult + public func validate() -> Self { + let contentTypes: () -> [String] = { [unowned self] in + acceptableContentTypes + } + return validate(statusCode: acceptableStatusCodes).validate(contentType: contentTypes()) + } +} + +// MARK: - + +extension DownloadRequest { + /// A closure used to validate a request that takes a URL request, a URL response, a temporary URL and a + /// destination URL, and returns whether the request was valid. + public typealias Validation = (_ request: URLRequest?, + _ response: HTTPURLResponse, + _ fileURL: URL?) + -> ValidationResult + + /// Validates that the response has a status code in the specified sequence. + /// + /// If validation fails, subsequent calls to response handlers will have an associated error. + /// + /// - Parameter acceptableStatusCodes: `Sequence` of acceptable response status codes. + /// + /// - Returns: The instance. + @discardableResult + public func validate(statusCode acceptableStatusCodes: S) -> Self where S.Iterator.Element == Int { + validate { [unowned self] _, response, _ in + self.validate(statusCode: acceptableStatusCodes, response: response) + } + } + + /// Validates that the response has a content type in the specified sequence. + /// + /// If validation fails, subsequent calls to response handlers will have an associated error. + /// + /// - parameter contentType: The acceptable content types, which may specify wildcard types and/or subtypes. + /// + /// - returns: The request. + @discardableResult + public func validate(contentType acceptableContentTypes: @escaping @autoclosure () -> S) -> Self where S.Iterator.Element == String { + validate { [unowned self] _, response, fileURL in + guard let validFileURL = fileURL else { + return .failure(AFError.responseValidationFailed(reason: .dataFileNil)) + } + + do { + let data = try Data(contentsOf: validFileURL) + return self.validate(contentType: acceptableContentTypes(), response: response, data: data) + } catch { + return .failure(AFError.responseValidationFailed(reason: .dataFileReadFailed(at: validFileURL))) + } + } + } + + /// Validates that the response has a status code in the default acceptable range of 200...299, and that the content + /// type matches any specified in the Accept HTTP header field. + /// + /// If validation fails, subsequent calls to response handlers will have an associated error. + /// + /// - returns: The request. + @discardableResult + public func validate() -> Self { + let contentTypes = { [unowned self] in + acceptableContentTypes + } + return validate(statusCode: acceptableStatusCodes).validate(contentType: contentTypes()) + } +} diff --git a/Pods/Alamofire/Source/PrivacyInfo.xcprivacy b/Pods/Alamofire/Source/PrivacyInfo.xcprivacy new file mode 100644 index 0000000..488cbb1 --- /dev/null +++ b/Pods/Alamofire/Source/PrivacyInfo.xcprivacy @@ -0,0 +1,23 @@ + + + + + NSPrivacyTracking + + NSPrivacyTrackingDomains + + NSPrivacyCollectedDataTypes + + NSPrivacyAccessedAPITypes + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategorySystemBootTime + NSPrivacyAccessedAPITypeReasons + + 35F9.1 + + + + + diff --git a/Pods/BlueCryptor/LICENSE b/Pods/BlueCryptor/LICENSE new file mode 100644 index 0000000..d9a10c0 --- /dev/null +++ b/Pods/BlueCryptor/LICENSE @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/Pods/BlueCryptor/README.md b/Pods/BlueCryptor/README.md new file mode 100644 index 0000000..e033264 --- /dev/null +++ b/Pods/BlueCryptor/README.md @@ -0,0 +1,235 @@ +

+ + APIDoc + + + Build Status - Master + + macOS + iOS + Linux + Apache 2 + + Slack Status + +

+ +# BlueCryptor +Swift cross-platform crypto library derived from [IDZSwiftCommonCrypto](https://github.com/iosdevzone/IDZSwiftCommonCrypto). + +**IMPORTANT NOTE:** This release is **NOT** entirely source code compatible with previous releases. There are instances where *exceptions* are thrown now instead of the framework calling *fatalError()*. This means that there are more *recoverable* errors in the library than before. The only time that *fatalError()* is called is to indicate either a *programming error* or a *non-recoverable system error*. + +**Note:** On macOS and iOS, _BlueCryptor_ uses the Apple provided *CommonCrypto* library. On Linux, it uses *libcrypto from the OpenSSL project*. + +## Prerequisites + +### Swift + +* Swift Open Source `swift-4.0.0-RELEASE` toolchain (**Minimum REQUIRED for latest release**) +* Swift Open Source `swift-4.2-RELEASE` toolchain (**Recommended**) +* Swift toolchain included in *Xcode Version 10.0 (10A255) or higher*. + +### macOS + +* macOS 10.11.6 (*El Capitan*) or higher. +* Xcode Version 9.0 or higher using one of the above toolchains. +* Xcode Version 10.0 (10A255) or higher using the included toolchain (*Recommended*). +* CommonCrypto is provided by macOS. + +### iOS + +* iOS 10.0 or higher +* Xcode Version 9.0 or higher using one of the above toolchains. +* Xcode Version 10.0 (10A255) or higher using the included toolchain (*Recommended*). +* CommonCrypto is provided by iOS. + +### Linux + +* Ubuntu 16.04 (or 16.10 but only tested on 16.04) and 18.04. +* One of the Swift Open Source toolchain listed above. +* OpenSSL is provided by the distribution. **Note:** 1.0.x, 1.1.x and later releases of OpenSSL are supported. +* The appropriate **libssl-dev** package is required to be installed when building. + +## Build + +To build **Cryptor** from the command line: + +``` +% cd +% swift build +``` + +## Testing + +To run the supplied unit tests for **Cryptor** from the command line: + +``` +% cd +% swift build +% swift test +``` + +## Getting started + +### Including in your project + +#### Swift Package Manager + +To include BlueCryptor into a Swift Package Manager package, add it to the `dependencies` attribute defined in your `Package.swift` file. You can select the version using the `majorVersion` and `minor` parameters. For example: +``` + dependencies: [ + .Package(url: "https://github.com/IBM-Swift/BlueCryptor.git", majorVersion: , minor: ) + ] +``` + +#### Carthage +To include BlueCryptor in a project using Carthage, add a line to your `Cartfile` with the GitHub organization and project names and version. For example: +``` + github "IBM-Swift/BlueCryptor" ~> . +``` + +#### CocoaPods +To include BlueCryptor in a project using CocoaPods, you just add `BlueCryptor` to your `Podfile`, for example: +``` + platform :ios, '10.0' + + target 'MyApp' do + use_frameworks! + pod 'BlueCryptor' + end +``` + +### Before starting + +The first thing you need to do is import the Cryptor framework. This is done by the following: +```swift +import Cryptor +``` + +## API + +### Cryptor + +The following code demonstrates encryption and decryption using `AES` single block CBC mode using optional chaining. +```swift +let key = CryptoUtils.byteArray(fromHex: "2b7e151628aed2a6abf7158809cf4f3c") +let iv = CryptoUtils.byteArray(fromHex: "00000000000000000000000000000000") +let plainText = CryptoUtils.byteArray(fromHex: "6bc1bee22e409f96e93d7e117393172a") + +var textToCipher = plainText +if plainText.count % Cryptor.Algorithm.aes.blockSize != 0 { + textToCipher = CryptoUtils.zeroPad(byteArray: plainText, blockSize: Cryptor.Algorithm.aes.blockSize) +} +do { + let cipherText = try Cryptor(operation: .encrypt, algorithm: .aes, options: .none, key: key, iv: iv).update(byteArray: textToCipher)?.final() + + print(CryptoUtils.hexString(from: cipherText!)) + + let decryptedText = try Cryptor(operation: .decrypt, algorithm: .aes, options: .none, key: key, iv: iv).update(byteArray: cipherText!)?.final() + + print(CryptoUtils.hexString(from: decryptedText!)) +} catch let error { + guard let err = error as? CryptorError else { + // Handle non-Cryptor error... + return + } + // Handle Cryptor error... (See Status.swift for types of errors thrown) +} +``` + +### Digest + +The following example illustrates generating an `MD5` digest from both a `String` and an instance of `NSData`. +```swift +let qbfBytes : [UInt8] = [0x54,0x68,0x65,0x20,0x71,0x75,0x69,0x63,0x6b,0x20,0x62,0x72,0x6f,0x77,0x6e,0x20,0x66,0x6f,0x78,0x20,0x6a,0x75,0x6d,0x70,0x73,0x20,0x6f,0x76,0x65,0x72,0x20,0x74,0x68,0x65,0x20,0x6c,0x61,0x7a,0x79,0x20,0x64,0x6f,0x67,0x2e] +let qbfString = "The quick brown fox jumps over the lazy dog." + +// String... +let md5 = Digest(using: .md5) +md5.update(string: qfbString) +let digest = md5.final() + +// NSData using optional chaining... +let qbfData = CryptoUtils.data(from: qbfBytes) +let digest = Digest(using: .md5).update(data: qbfData)?.final() +``` + +### HMAC + +The following demonstrates generating an `SHA256` HMAC using byte arrays for keys and data. +```swift +let myKeyData = "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b" +let myData = "4869205468657265" +let key = CryptoUtils.byteArray(fromHex: myKeyData) +let data : [UInt8] = CryptoUtils.byteArray(fromHex: myData) + +let hmac = HMAC(using: HMAC.Algorithm.sha256, key: key).update(byteArray: data)?.final() +``` + +### Key Derivation + +The following illustrates generating a key using a password, salt, number of rounds and a specified derived key length using the SHA1 algorithm. Then it shows how to generate a `String` from resultant key. +```swift +let password = "password" +let salt = salt +let rounds: UInt = 2 +let derivedKeyLen = 20 +do { + let key = PBKDF.deriveKey(fromPassword: password, salt: salt, prf: .sha1, rounds: rounds, derivedKeyLength: derivedKeyLen) + let keyString = CryptoUtils.hexString(from: key) +} catch let error { + guard let err = error as? CryptorError else { + // Handle non-Cryptor error... + return + } + // Handle Cryptor error... (See Status.swift for types of errors thrown) +} +``` + +### Random Byte Generation + +The following demonstrates generating random bytes of a given length. +```swift +let numberOfBytes = 256*256 +do { + let randomBytes = try Random.generate(byteCount: numberOfBytes) +} catch { + print("Error generating random bytes") +} +``` + +### Utilities + +**Cryptor** also provides a set of data manipulation utility functions for conversion of data from various formats: +- To byteArray (`[UInt8]`) + - From hex string + - From UTF8 string +- To `Data` + - From hex string + - From byte array (`[UInt8]`) +- To `NSData` + - From hex string + - From byte array (`[UInt8]`) +- To `NSString` + - From byte array (`[UInt8]`) +- To hexList (`String`) + - From byte array (`[UInt8]`) + +Also provided are an API to pad a byte array (`[UInt8]`) such that it is an integral number of `block size in bytes` long. +- ```func zeroPad(byteArray: [UInt8], blockSize: Int) -> [UInt8]``` +- ```func zeroPad(string: String, blockSize: Int) -> [UInt8]``` + +## Restrictions + +The following algorithm is not available on Linux since it is not supported by *OpenSSL*. +- Digest: MD2 + +In all cases, use of unsupported APIs or algorithms will result in a Swift `fatalError()`, terminating the program and should be treated as a programming error. + +## Community + +We love to talk server-side Swift and Kitura. Join our [Slack](http://swift-at-ibm-slack.mybluemix.net/) to meet the team! + +## License + +This library is licensed under Apache 2.0. Full license text is available in [LICENSE](https://github.com/IBM-Swift/BlueCryptor/blob/master/LICENSE). diff --git a/Pods/BlueCryptor/Sources/Cryptor/Crypto.swift b/Pods/BlueCryptor/Sources/Cryptor/Crypto.swift new file mode 100755 index 0000000..f41e453 --- /dev/null +++ b/Pods/BlueCryptor/Sources/Cryptor/Crypto.swift @@ -0,0 +1,128 @@ +// +// Crypto.swift +// Cryptor +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +/// +/// Implements a simplified API for calculating digests over single buffers +/// +public protocol CryptoDigest { + + /// Calculates a message digest + func digest(using algorithm: Digest.Algorithm) -> Self +} + +/// +/// Extension to the CryptoDigest to return the digest appropriate to the selected algorithm. +/// +extension CryptoDigest { + + /// An MD2 digest of this object + public var md2: Self { + return self.digest(using: .md2) + } + + /// An MD4 digest of this object + public var md4: Self { + return self.digest(using: .md4) + } + + /// An MD5 digest of this object + public var md5: Self { + return self.digest(using: .md5) + } + + /// An SHA1 digest of this object + public var sha1: Self { + return self.digest(using: .sha1) + } + + /// An SHA224 digest of this object + public var sha224: Self { + return self.digest(using: .sha224) + } + + /// An SHA256 digest of this object + public var sha256: Self { + return self.digest(using: .sha256) + } + + /// An SHA384 digest of this object + public var sha384: Self { + return self.digest(using: .sha384) + } + + /// An SHA512 digest of this object + public var sha512: Self { + return self.digest(using: .sha512) + } +} + +/// +/// Extension for Data to return an Data object containing the digest. +/// +extension Data: CryptoDigest { + /// + /// Calculates the Message Digest for this data. + /// + /// - Parameter algorithm: The digest algorithm to use + /// + /// - Returns: An `Data` object containing the message digest + /// + public func digest(using algorithm: Digest.Algorithm) -> Data { + + // This force unwrap may look scary but for CommonCrypto this cannot fail. + // The API allows for optionals to support the OpenSSL implementation which can. + #if swift(>=5.0) + return self.withUnsafeBytes() { + + let result = (Digest(using: algorithm).update(from: $0.baseAddress!, byteCount: self.count)?.final())! + let data = type(of: self).init(bytes: result, count: result.count) + return data + } + #else + return self.withUnsafeBytes() { (buffer: UnsafePointer) -> Data in + + let result = (Digest(using: algorithm).update(from: buffer, byteCount: self.count)?.final())! + let data = type(of: self).init(bytes: result, count: result.count) + return data + } + #endif + } +} + +/// +/// Extension for String to return a String containing the digest. +/// +extension String: CryptoDigest { + /// + /// Calculates the Message Digest for this string. + /// The string is converted to raw data using UTF8. + /// + /// - Parameter algorithm: The digest algorithm to use + /// + /// - Returns: A hex string of the calculated digest + /// + public func digest(using algorithm: Digest.Algorithm) -> String { + + // This force unwrap may look scary but for CommonCrypto this cannot fail. + // The API allows for optionals to support the OpenSSL implementation which can. + let result = (Digest(using: algorithm).update(string: self as String)?.final())! + return CryptoUtils.hexString(from: result) + + } +} diff --git a/Pods/BlueCryptor/Sources/Cryptor/Cryptor.swift b/Pods/BlueCryptor/Sources/Cryptor/Cryptor.swift new file mode 100755 index 0000000..1f3bf25 --- /dev/null +++ b/Pods/BlueCryptor/Sources/Cryptor/Cryptor.swift @@ -0,0 +1,71 @@ +// +// Cryptor.swift +// Cryptor +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +/// +/// Encrypts or decrypts, accumulating result. +/// +/// Useful for small in-memory buffers. +/// +/// For large files or network streams use StreamCryptor. +/// +public class Cryptor: StreamCryptor, Updatable { + + /// Internal accumulator for gathering data from the update() and final() functions. + var accumulator: [UInt8] = [] + + /// + /// Retrieves the encrypted or decrypted data. + /// + ///- Returns: the encrypted or decrypted data or nil if an error occured. + /// + public func final() -> [UInt8]? { + + let byteCount = Int(self.getOutputLength(inputByteCount: 0, isFinal: true)) + var dataOut = Array(repeating: 0, count:byteCount) + var dataOutMoved = 0 + (dataOutMoved, self.status) = final(byteArrayOut: &dataOut) + if self.status != .success { + return nil + } + accumulator += dataOut[0.. Self? { + + let outputLength = Int(self.getOutputLength(inputByteCount: byteCount, isFinal: false)) + var dataOut = Array(repeating: 0, count:outputLength) + var dataOutMoved = 0 + _ = update(bufferIn: buffer, byteCountIn: byteCount, bufferOut: &dataOut, byteCapacityOut: dataOut.count, byteCountOut: &dataOutMoved) + if self.status != .success { + return nil + } + accumulator += dataOut[0..(initializer:CC_MD2_Init, updater:CC_MD2_Update, finalizer:CC_MD2_Final, length:CC_MD2_DIGEST_LENGTH) + #elseif os(Linux) + fatalError("MD2 digest not supported by OpenSSL") + #endif + + case .md4: + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + engine = DigestEngineCC(initializer:CC_MD4_Init, updater:CC_MD4_Update, finalizer:CC_MD4_Final, length:CC_MD4_DIGEST_LENGTH) + #elseif os(Linux) + engine = DigestEngineCC(initializer:MD4_Init, updater:MD4_Update, finalizer:MD4_Final, length:MD4_DIGEST_LENGTH) + #endif + + case .md5: + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + engine = DigestEngineCC(initializer:CC_MD5_Init, updater:CC_MD5_Update, finalizer:CC_MD5_Final, length:CC_MD5_DIGEST_LENGTH) + #elseif os(Linux) + engine = DigestEngineCC(initializer:MD5_Init, updater:MD5_Update, finalizer:MD5_Final, length:MD5_DIGEST_LENGTH) + #endif + + case .sha1: + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + engine = DigestEngineCC(initializer:CC_SHA1_Init, updater:CC_SHA1_Update, finalizer:CC_SHA1_Final, length:CC_SHA1_DIGEST_LENGTH) + #elseif os(Linux) + engine = DigestEngineCC(initializer:SHA1_Init, updater:SHA1_Update, finalizer:SHA1_Final, length:SHA_DIGEST_LENGTH) + #endif + + case .sha224: + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + engine = DigestEngineCC(initializer:CC_SHA224_Init, updater:CC_SHA224_Update, finalizer:CC_SHA224_Final, length:CC_SHA224_DIGEST_LENGTH) + #elseif os(Linux) + engine = DigestEngineCC(initializer:SHA224_Init, updater:SHA224_Update, finalizer:SHA224_Final, length:SHA224_DIGEST_LENGTH) + #endif + + case .sha256: + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + engine = DigestEngineCC(initializer:CC_SHA256_Init, updater:CC_SHA256_Update, finalizer:CC_SHA256_Final, length:CC_SHA256_DIGEST_LENGTH) + #elseif os(Linux) + engine = DigestEngineCC(initializer: SHA256_Init, updater:SHA256_Update, finalizer:SHA256_Final, length:SHA256_DIGEST_LENGTH) + #endif + + case .sha384: + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + engine = DigestEngineCC(initializer:CC_SHA384_Init, updater:CC_SHA384_Update, finalizer:CC_SHA384_Final, length:CC_SHA384_DIGEST_LENGTH) + #elseif os(Linux) + engine = DigestEngineCC(initializer:SHA384_Init, updater:SHA384_Update, finalizer:SHA384_Final, length:SHA384_DIGEST_LENGTH) + #endif + + case .sha512: + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + engine = DigestEngineCC(initializer:CC_SHA512_Init, updater:CC_SHA512_Update, finalizer:CC_SHA512_Final, length:CC_SHA512_DIGEST_LENGTH) + #elseif os(Linux) + engine = DigestEngineCC(initializer:SHA512_Init, updater:SHA512_Update, finalizer:SHA512_Final, length:SHA512_DIGEST_LENGTH) + #endif + } + } + + /// + /// Low-level update routine. Updates the message digest calculation with + /// the contents of a byte buffer. + /// + /// - Parameters: + /// - buffer: The buffer + /// - byteCount: Number of bytes in buffer + /// + /// - Returns: This Digest object (for optional chaining) + /// + public func update(from buffer: UnsafeRawPointer, byteCount: size_t) -> Self? { + + engine.update(buffer: buffer, byteCount: CC_LONG(byteCount)) + return self + } + + /// + /// Completes the calculate of the messge digest + /// + /// - Returns: The message digest + /// + public func final() -> [UInt8] { + + return engine.final() + } +} + +// MARK: Internal Classes + +/// +/// Defines the interface between the Digest class and an +/// algorithm specific DigestEngine +/// +private protocol DigestEngine { + + /// + /// Update method + /// + /// - Parameters: + /// - buffer: The buffer to add. + /// - byteCount: The length of the buffer. + /// + func update(buffer: UnsafeRawPointer, byteCount: CC_LONG) + + /// + /// Finalizer routine + /// + /// - Returns: Byte array containing the digest. + /// + func final() -> [UInt8] +} + +/// +/// Wraps the underlying algorithm specific structures and calls +/// in a generic interface. +/// +/// - Parameter CTX: The context for the digest. +/// +private class DigestEngineCC: DigestEngine { + + typealias Context = UnsafeMutablePointer + typealias Buffer = UnsafeRawPointer + typealias Digest = UnsafeMutablePointer + typealias Initializer = (Context) -> (Int32) + typealias Updater = (Context, Buffer, CC_LONG) -> (Int32) + typealias Finalizer = (Digest, Context) -> (Int32) + + let context = Context.allocate(capacity: 1) + var initializer: Initializer + var updater: Updater + var finalizer: Finalizer + var length: Int32 + + /// + /// Default initializer + /// + /// - Parameters: + /// - initializer: The digest initializer routine. + /// - updater: The digest updater routine. + /// - finalizer: The digest finalizer routine. + /// - length: The digest length. + /// + init(initializer: @escaping Initializer, updater: @escaping Updater, finalizer: @escaping Finalizer, length: Int32) { + + self.initializer = initializer + self.updater = updater + self.finalizer = finalizer + self.length = length + _ = initializer(context) + } + + /// + /// Cleanup + /// + deinit { + + #if swift(>=4.1) + context.deallocate() + #else + context.deallocate(capacity: 1) + #endif + } + + /// + /// Update method + /// + /// - Parameters: + /// - buffer: The buffer to add. + /// - byteCount: The length of the buffer. + /// + func update(buffer: Buffer, byteCount: CC_LONG) { + + _ = updater(context, buffer, byteCount) + } + + /// + /// Finalizer routine + /// + /// - Returns: Byte array containing the digest. + /// + func final() -> [UInt8] { + + let digestLength = Int(self.length) + var digest = Array(repeating: 0, count:digestLength) + _ = finalizer(&digest, context) + return digest + } +} + + + + + diff --git a/Pods/BlueCryptor/Sources/Cryptor/HMAC.swift b/Pods/BlueCryptor/Sources/Cryptor/HMAC.swift new file mode 100755 index 0000000..086cd62 --- /dev/null +++ b/Pods/BlueCryptor/Sources/Cryptor/HMAC.swift @@ -0,0 +1,335 @@ +// +// HMAC.swift +// Cryptor +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + import CommonCrypto +#elseif os(Linux) + import OpenSSL +#endif + +/// +/// Calculates a cryptographic Hash-Based Message Authentication Code (HMAC). +/// +public class HMAC: Updatable { + + /// + /// Enumerates available algorithms. + /// + public enum Algorithm { + + /// Message Digest 5 + case md5 + + /// Secure Hash Algorithm 1 + case sha1 + + /// Secure Hash Algorithm 2 224-bit + case sha224 + + /// Secure Hash Algorithm 2 256-bit + case sha256 + + /// Secure Hash Algorithm 2 384-bit + case sha384 + + /// Secure Hash Algorithm 2 512-bit + case sha512 + + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + + static let fromNative: [CCHmacAlgorithm: Algorithm] = [ + CCHmacAlgorithm(kCCHmacAlgSHA1): .sha1, + CCHmacAlgorithm(kCCHmacAlgSHA1): .md5, + CCHmacAlgorithm(kCCHmacAlgSHA256): .sha256, + CCHmacAlgorithm(kCCHmacAlgSHA384): .sha384, + CCHmacAlgorithm(kCCHmacAlgSHA512): .sha512, + CCHmacAlgorithm(kCCHmacAlgSHA224): .sha224 + ] + + static func fromNativeValue(nativeAlg: CCHmacAlgorithm) -> Algorithm? { + + return fromNative[nativeAlg] + } + + func nativeValue() -> CCHmacAlgorithm { + + switch self { + + case .sha1: + return CCHmacAlgorithm(kCCHmacAlgSHA1) + case .md5: + return CCHmacAlgorithm(kCCHmacAlgMD5) + case .sha224: + return CCHmacAlgorithm(kCCHmacAlgSHA224) + case .sha256: + return CCHmacAlgorithm(kCCHmacAlgSHA256) + case .sha384: + return CCHmacAlgorithm(kCCHmacAlgSHA384) + case .sha512: + return CCHmacAlgorithm(kCCHmacAlgSHA512) + } + } + + #elseif os(Linux) + + func nativeValue() -> OpaquePointer? { + + switch self { + + case .sha1: + return .init(EVP_sha1()) + case .md5: + return .init(EVP_md5()) + case .sha224: + return .init(EVP_sha224()) + case .sha256: + return .init(EVP_sha256()) + case .sha384: + return .init(EVP_sha384()) + case .sha512: + return .init(EVP_sha512()) + } + } + + #endif + + /// + /// Obtains the digest length produced by this algorithm (in bytes). + /// + public func digestLength() -> Int { + + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + + switch self { + + case .sha1: + return Int(CC_SHA1_DIGEST_LENGTH) + case .md5: + return Int(CC_MD5_DIGEST_LENGTH) + case .sha224: + return Int(CC_SHA224_DIGEST_LENGTH) + case .sha256: + return Int(CC_SHA256_DIGEST_LENGTH) + case .sha384: + return Int(CC_SHA384_DIGEST_LENGTH) + case .sha512: + return Int(CC_SHA512_DIGEST_LENGTH) + } + + #elseif os(Linux) + + switch self { + + case .sha1: + return Int(SHA_DIGEST_LENGTH) + case .md5: + return Int(MD5_DIGEST_LENGTH) + case .sha224: + return Int(SHA224_DIGEST_LENGTH) + case .sha256: + return Int(SHA256_DIGEST_LENGTH) + case .sha384: + return Int(SHA384_DIGEST_LENGTH) + case .sha512: + return Int(SHA512_DIGEST_LENGTH) + } + + #endif + + } + } + + /// Context + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + + typealias Context = UnsafeMutablePointer + + #elseif os(Linux) + + typealias Context = OpaquePointer? + + #endif + + /// Status of the calculation + public internal(set) var status: Status = .success + + #if os(Linux) + private let context = HMAC_CTX_new_wrapper() + #else + private let context = Context.allocate(capacity: 1) + #endif + private var algorithm: Algorithm + + // MARK: Lifecycle Methods + + /// + /// Creates a new HMAC instance with the specified algorithm and key. + /// + /// - Parameters: + /// - algorithm: Selects the algorithm + /// - keyBuffer: Specifies pointer to the key + /// - keyByteCount: Number of bytes on keyBuffer + /// + init(using algorithm: Algorithm, keyBuffer: UnsafeRawPointer, keyByteCount: Int) { + + self.algorithm = algorithm + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + CCHmacInit(context, algorithm.nativeValue(), keyBuffer, size_t(keyByteCount)) + #elseif os(Linux) + HMAC_Init_wrapper(context, keyBuffer, Int32(keyByteCount), .make(optional: algorithm.nativeValue())) + #endif + } + + /// + /// Creates a new HMAC instance with the specified algorithm and key. + /// + /// - Parameters: + /// - algorithm: Selects the algorithm + /// - key: Specifies the key as Data + /// + public init(using algorithm: Algorithm, key: Data) { + + self.algorithm = algorithm + #if swift(>=5.0) + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + key.withUnsafeBytes() { + CCHmacInit(context, algorithm.nativeValue(), $0.baseAddress, size_t(key.count)) + } + #elseif os(Linux) + _ = key.withUnsafeBytes() { + HMAC_Init_wrapper(context, $0.baseAddress, Int32(key.count), .make(optional: algorithm.nativeValue())) + } + #endif + #else + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + key.withUnsafeBytes() { (buffer: UnsafePointer) in + CCHmacInit(context, algorithm.nativeValue(), buffer, size_t(key.count)) + } + #elseif os(Linux) + _ = key.withUnsafeBytes() { (buffer: UnsafePointer) in + HMAC_Init_wrapper(context, buffer, Int32(key.count), .make(optional: algorithm.nativeValue())) + } + #endif + #endif + } + + /// + /// Creates a new HMAC instance with the specified algorithm and key. + /// + /// - Parameters: + /// - algorithm: Selects the algorithm + /// - key: Specifies the key as NSData + /// + public init(using algorithm: Algorithm, key: NSData) { + + self.algorithm = algorithm + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + CCHmacInit(context, algorithm.nativeValue(), key.bytes, size_t(key.length)) + #elseif os(Linux) + HMAC_Init_wrapper(context, key.bytes, Int32(key.length), .make(optional: algorithm.nativeValue())) + #endif + } + + /// + /// Creates a new HMAC instance with the specified algorithm and key. + /// + /// - Parameters: + /// - algorithm: Selects the algorithm + /// - key: Specifies the key as byte array. + /// + public init(using algorithm: Algorithm, key: [UInt8]) { + + self.algorithm = algorithm + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + CCHmacInit(context, algorithm.nativeValue(), key, size_t(key.count)) + #elseif os(Linux) + HMAC_Init_wrapper(context, key, Int32(key.count), .make(optional: algorithm.nativeValue())) + #endif + } + + /// + /// Creates a new HMAC instance with the specified algorithm and key string. + /// The key string is converted to bytes using UTF8 encoding. + /// + /// - Parameters: + /// - algorithm: Selects the algorithm + /// - key: Specifies the key as String + /// + public init(using algorithm: Algorithm, key: String) { + + self.algorithm = algorithm + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + CCHmacInit(context, algorithm.nativeValue(), key, size_t(key.lengthOfBytes(using: String.Encoding.utf8))) + #elseif os(Linux) + HMAC_Init_wrapper(context, key, Int32(key.utf8.count), .make(optional: algorithm.nativeValue())) + #endif + } + + /// + /// Cleanup + /// + deinit { + #if os(Linux) + HMAC_CTX_free_wrapper(.make(optional: context)) + #else + #if swift(>=4.1) + context.deallocate() + #else + context.deallocate(capacity: 1) + #endif + #endif + } + + // MARK: Public Methods + + /// + /// Updates the calculation of the HMAC with the contents of a buffer. + /// + /// - Parameter buffer: Update buffer + /// + /// - Returns: The 'in-progress' calculated HMAC + /// + public func update(from buffer: UnsafeRawPointer, byteCount: size_t) -> Self? { + + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + CCHmacUpdate(context, buffer, byteCount) + #elseif os(Linux) + HMAC_Update(context, buffer.assumingMemoryBound(to: UInt8.self), byteCount) + #endif + return self + } + + /// + /// Finalizes the HMAC calculation + /// + /// - Returns: The final calculated HMAC + /// + public func final() -> [UInt8] { + + var hmac = Array(repeating: 0, count:algorithm.digestLength()) + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + CCHmacFinal(context, &hmac) + #elseif os(Linux) + var length: UInt32 = 0 + HMAC_Final(context, &hmac, &length) + #endif + return hmac + } +} + diff --git a/Pods/BlueCryptor/Sources/Cryptor/KeyDerivation.swift b/Pods/BlueCryptor/Sources/Cryptor/KeyDerivation.swift new file mode 100755 index 0000000..7fa04d5 --- /dev/null +++ b/Pods/BlueCryptor/Sources/Cryptor/KeyDerivation.swift @@ -0,0 +1,211 @@ +// +// KeyDerivation.swift +// Cryptor +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + import CommonCrypto +#elseif os(Linux) + import OpenSSL +#endif + +/// +/// Derives key material from a password or passphrase. +/// +public class PBKDF { + + /// Enumerates available pseudo random algorithms + public enum PseudoRandomAlgorithm { + + /// Secure Hash Algorithm 1 + case sha1 + + /// Secure Hash Algorithm 2 224-bit + case sha224 + + /// Secure Hash Algorithm 2 256-bit + case sha256 + + /// Secure Hash Algorithm 2 384-bit + case sha384 + + /// Secure Hash Algorithm 2 512-bit + case sha512 + + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + /// Return the OS native value + func nativeValue() -> CCPseudoRandomAlgorithm { + + switch self { + + case .sha1: + return CCPseudoRandomAlgorithm(kCCPRFHmacAlgSHA1) + case .sha224: + return CCPseudoRandomAlgorithm(kCCPRFHmacAlgSHA224) + case .sha256: + return CCPseudoRandomAlgorithm(kCCPRFHmacAlgSHA256) + case .sha384: + return CCPseudoRandomAlgorithm(kCCPRFHmacAlgSHA384) + case .sha512: + return CCPseudoRandomAlgorithm(kCCPRFHmacAlgSHA512) + } + } + + #elseif os(Linux) + + /// Return the OS native value + func nativeValue() -> OpaquePointer? { + + switch self { + + case .sha1: + return .init(EVP_sha1()) + case .sha224: + return .init(EVP_sha224()) + case .sha256: + return .init(EVP_sha256()) + case .sha384: + return .init(EVP_sha384()) + case .sha512: + return .init(EVP_sha512()) + } + } + + #endif + } + + /// + /// Determines the (approximate) number of iterations of the key derivation algorithm that need + /// to be run to achieve a particular delay (or calculation time). + /// + /// - Parameters: + /// - passwordLength: Password length in bytes + /// - saltLength: Salt length in bytes + /// - algorithm: The PseudoRandomAlgorithm to use + /// - derivedKeyLength: The desired key length + /// - msec: The desired calculation time + /// + /// - Returns: The number of times the algorithm should be run + /// + public class func calibrate(passwordLength: Int, saltLength: Int, algorithm: PseudoRandomAlgorithm, derivedKeyLength: Int, msec: UInt32) -> UInt { + + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + return UInt(CCCalibratePBKDF(CCPBKDFAlgorithm(kCCPBKDF2), passwordLength, saltLength, algorithm.nativeValue(), derivedKeyLength, msec)) + #elseif os(Linux) + // Value as per RFC 2898. + return UInt(1000 * UInt(msec)) + #endif + } + + + /// + /// Derives key material from a password and salt. + /// + /// - Parameters: + /// - password: The password string, will be converted using UTF8 + /// - salt: The salt string will be converted using UTF8 + /// - prf: The pseudo random function + /// - round: The number of rounds + /// - derivedKeyLength: The length of the desired derived key, in bytes. + /// + /// - Returns: The derived key + /// + public class func deriveKey(fromPassword password: String, salt: String, prf: PseudoRandomAlgorithm, rounds: uint, derivedKeyLength: UInt) throws -> [UInt8] { + + var derivedKey = Array(repeating: 0, count:Int(derivedKeyLength)) + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + let status: Int32 = CCKeyDerivationPBKDF(CCPBKDFAlgorithm(kCCPBKDF2), password, password.utf8.count, salt, salt.utf8.count, prf.nativeValue(), rounds, &derivedKey, derivedKey.count) + if status != Int32(kCCSuccess) { + + throw CryptorError.fail(status, "ERROR: CCKeyDerivationPBDK failed with status \(status).") + } + #elseif os(Linux) + let status = PKCS5_PBKDF2_HMAC(password, Int32(password.utf8.count), salt, Int32(salt.utf8.count), Int32(rounds), .make(optional: prf.nativeValue()), Int32(derivedKey.count), &derivedKey) + if status != 1 { + let error = ERR_get_error() + + throw CryptorError.fail(Int32(error), "ERROR: PKCS5_PBKDF2_HMAC failed, reason: \(errToString(ERR_error_string(error, nil)))") + } + #endif + return derivedKey + } + + /// + /// Derives key material from a password and salt. + /// + /// - Parameters: + /// - password: The password string, will be converted using UTF8 + /// - salt: The salt array of bytes + /// - prf: The pseudo random function + /// - round: The number of rounds + /// - derivedKeyLength: The length of the desired derived key, in bytes. + /// + /// - Returns: The derived key + /// + public class func deriveKey(fromPassword password: String, salt: [UInt8], prf: PseudoRandomAlgorithm, rounds: uint, derivedKeyLength: UInt) throws -> [UInt8] { + + var derivedKey = Array(repeating: 0, count:Int(derivedKeyLength)) + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + let status: Int32 = CCKeyDerivationPBKDF(CCPBKDFAlgorithm(kCCPBKDF2), password, password.utf8.count, salt, salt.count, prf.nativeValue(), rounds, &derivedKey, derivedKey.count) + if status != Int32(kCCSuccess) { + + throw CryptorError.fail(status, "ERROR: CCKeyDerivationPBDK failed with status \(status).") + } + #elseif os(Linux) + let status = PKCS5_PBKDF2_HMAC(password, Int32(password.utf8.count), salt, Int32(salt.count), Int32(rounds), .make(optional: prf.nativeValue()), Int32(derivedKey.count), &derivedKey) + if status != 1 { + let error = ERR_get_error() + + throw CryptorError.fail(Int32(error), "ERROR: PKCS5_PBKDF2_HMAC failed, reason: \(errToString(ERR_error_string(error, nil)))") + } + #endif + return derivedKey + } + + /// + /// Derives key material from a password buffer. + /// + /// - Parameters: + /// - password: Pointer to the password buffer + /// - passwordLength: Password length in bytes + /// - salt: Pointer to the salt buffer + /// - saltLength: Salt length in bytes + /// - prf: The PseudoRandomAlgorithm to use + /// - rounds: The number of rounds of the algorithm to use + /// - derivedKey: Pointer to the derived key buffer. + /// - derivedKeyLength: The desired key length + /// + /// - Returns: The number of times the algorithm should be run + /// + public class func deriveKey(fromPassword password: UnsafePointer, passwordLen: Int, salt: UnsafePointer, saltLen: Int, prf: PseudoRandomAlgorithm, rounds: uint, derivedKey: UnsafeMutablePointer, derivedKeyLen: Int) throws { + + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + let status: Int32 = CCKeyDerivationPBKDF(CCPBKDFAlgorithm(kCCPBKDF2), password, passwordLen, salt, saltLen, prf.nativeValue(), rounds, derivedKey, derivedKeyLen) + if status != Int32(kCCSuccess) { + + throw CryptorError.fail(status, "ERROR: CCKeyDerivationPBDK failed with status \(status).") + } + #elseif os(Linux) + let status = PKCS5_PBKDF2_HMAC(password, Int32(passwordLen), salt, Int32(saltLen), Int32(rounds), .make(optional: prf.nativeValue()), Int32(derivedKeyLen), derivedKey) + if status != 1 { + let error = ERR_get_error() + + throw CryptorError.fail(Int32(error), "ERROR: PKCS5_PBKDF2_HMAC failed, reason: \(errToString(ERR_error_string(error, nil)))") + } + #endif + } +} diff --git a/Pods/BlueCryptor/Sources/Cryptor/Random.swift b/Pods/BlueCryptor/Sources/Cryptor/Random.swift new file mode 100755 index 0000000..33266c6 --- /dev/null +++ b/Pods/BlueCryptor/Sources/Cryptor/Random.swift @@ -0,0 +1,106 @@ +// +// Random.swift +// Cryptor +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + import CommonCrypto +#elseif os(Linux) + import OpenSSL +#endif + +public typealias RNGStatus = Status + +/// +/// Generates buffers of random bytes. +/// +public class Random { + + /// + /// Wraps native call. + /// + /// - Note: CCRNGStatus is typealiased to CCStatus but this routine can only return kCCSuccess or kCCRNGFailure + /// + /// - Parameter bytes: A pointer to the buffer that will receive the bytes + /// + /// - Returns: `.success` or `.rngFailure` as appropriate. + /// + public class func generate(bytes: UnsafeMutablePointer, byteCount: Int) -> RNGStatus { + + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + let statusCode = CCRandomGenerateBytes(bytes, byteCount) + guard let status = Status(rawValue: statusCode) else { + fatalError("CCRandomGenerateBytes returned unexpected status code: \(statusCode)") + } + return status + #elseif os(Linux) + let statusCode = RAND_bytes(bytes, Int32(byteCount)) + if statusCode != 1 { + + let errCode = ERR_get_error() + return Status.rngFailure(errCode) + } + return Status.success + #endif + } + + /// + /// Generates an array of random bytes. + /// + /// - Parameter bytesCount: Number of random bytes to generate + /// + /// - Returns: an array of random bytes + /// + /// - Throws: `.success` or an `.rngFailure` on failure + /// + public class func generate(byteCount: Int) throws -> [UInt8] { + + guard byteCount > 0 else { + throw RNGStatus.paramError + } + + var bytes = Array(repeating: UInt8(0), count:byteCount) + let status = generate(bytes: &bytes, byteCount: byteCount) + + if status != .success { + throw status + } + + return bytes + } + + /// + /// A version of generateBytes that always throws an error. + /// + /// Use it to test that code handles this. + /// + /// - Parameter bytesCount: Number of random bytes to generate + /// + /// - Returns: An array of random bytes + /// + public class func generateBytesThrow(byteCount: Int) throws -> [UInt8] { + + if byteCount <= 0 { + + fatalError("generate: byteCount must be positve and non-zero") + } + var bytes: [UInt8] = Array(repeating: UInt8(0), count:byteCount) + let status = generate(bytes: &bytes, byteCount: byteCount) + throw status + //return bytes + } +} diff --git a/Pods/BlueCryptor/Sources/Cryptor/SSLPointerTricks.swift b/Pods/BlueCryptor/Sources/Cryptor/SSLPointerTricks.swift new file mode 100644 index 0000000..d986598 --- /dev/null +++ b/Pods/BlueCryptor/Sources/Cryptor/SSLPointerTricks.swift @@ -0,0 +1,100 @@ +//===----------------------------------------------------------------------===// +// +// This source file is taken from SwiftNIO open source project +// +// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors +// Licensed under Apache License v2.0 +// +//===----------------------------------------------------------------------===// + +// MARK:- Awful code begins here +// Hello dear reader. Let me explain what we're doing here. +// +// From OpenSSL 1.0 to OpenSSL 1.1 one of the major breaking changes was the so-called +// "great opaquifiying". Essentially, OpenSSL took all of its public structures and made +// them opaque, such that they cannot be introspected from client code. This is a great +// forward step, and brings them more in line with modern C library practices. +// +// However, it's an *enormous* inconvenience from Swift code. This is because the Swift +// translation of the C type `SSL_CTX *` changed from `UnsafeMutablePointer` to +// `OpaquePointer`. +// +// This change exists for reasonable enough reasons in Swift land (see +// https://forums.swift.org/t/opaque-pointers-in-swift/6875 for a discussion), but +// nonetheless causes enormous problems in our codebase. +// +// Our cheap way out is to make everything an OpaquePointer, and then provide initializers +// between OpaquePointer and the typed pointers. This allows us to tolerate either pointer +// type in our Swift code by bridging them over to OpaquePointer and back, and lets the +// compiler worry about how exactly to make that work. +// +// Now, in fact, Swift already has initializers between the pointer types. What it does +// not have is self-initializers: the ability to create an `OpaquePointer` from an `OpaquePointer`, +// or an `UnsafePointer` from an `UnsafePointer`. We add those two initializers here. +// We also add a special "make" function that exists to handle the special case of optional pointer +// values, which we mostly encounter in the ALPN callbacks. +// +// The *downside* of this approach is that we totally break the pointer type system. It becomes +// trivially possible to alias a pointer of type T to type U through two calls to init. This +// is not a thing we want to widely promote. For this reason, these extensions are hidden in +// this file, where we can laugh and jeer at them and generally make them feel bad about +// themselves. +// +// Hopefully, in time, these extensions can be removed. + +extension UnsafePointer { + init(_ ptr: UnsafePointer) { + self = ptr + } + + static func make(optional ptr: UnsafePointer?) -> UnsafePointer? { + return ptr.map(UnsafePointer.init) + } + + static func make(optional ptr: OpaquePointer?) -> UnsafePointer? { + return ptr.map(UnsafePointer.init) + } +} + +extension UnsafeMutablePointer { + init(_ ptr: UnsafeMutableRawPointer) { + let x = UnsafeMutablePointer(bitPattern: UInt(bitPattern: ptr))! + self = x + } + + static func make(optional ptr: UnsafeMutablePointer?) -> UnsafeMutablePointer? { + return ptr.map(UnsafeMutablePointer.init) + } + + static func make(optional ptr: UnsafeMutableRawPointer?) -> UnsafeMutablePointer? { + return ptr.map(UnsafeMutablePointer.init) + } + + static func make(optional ptr: OpaquePointer?) -> UnsafeMutablePointer? { + return ptr.map(UnsafeMutablePointer.init) + } +} + +extension UnsafeMutableRawPointer { + static func make(optional ptr: OpaquePointer?) -> UnsafeMutableRawPointer? { + return ptr.map(UnsafeMutableRawPointer.init) + } +} + +extension OpaquePointer { + init(_ ptr: OpaquePointer) { + self = ptr + } + + static func make(optional ptr: OpaquePointer?) -> OpaquePointer? { + return ptr.map(OpaquePointer.init) + } + + static func make(optional ptr: UnsafeMutableRawPointer?) -> OpaquePointer? { + return ptr.map(OpaquePointer.init) + } + + static func make(optional ptr: UnsafeMutablePointer?) -> OpaquePointer? { + return ptr.map(OpaquePointer.init) + } +} diff --git a/Pods/BlueCryptor/Sources/Cryptor/Status.swift b/Pods/BlueCryptor/Sources/Cryptor/Status.swift new file mode 100755 index 0000000..be87926 --- /dev/null +++ b/Pods/BlueCryptor/Sources/Cryptor/Status.swift @@ -0,0 +1,288 @@ +// +// Status.swift +// Cryptor +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + import CommonCrypto +#elseif os(Linux) + import OpenSSL +#endif + +#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) +/// +/// Links the native CommonCryptoStatus enumeration to Swift versions. +/// +public enum Status: CCCryptorStatus, Swift.Error, CustomStringConvertible { + + /// Successful + case success + + /// Parameter Error + case paramError + + /// Buffer too Small + case bufferTooSmall + + /// Memory Failure + case memoryFailure + + /// Alignment Error + case alignmentError + + /// Decode Error + case decodeError + + /// Unimplemented + case unimplemented + + /// Overflow + case overflow + + /// Random Number Generator Err + case rngFailure + + /// + /// Converts this value to a native `CCCryptorStatus` value. + /// + public func toRaw() -> CCCryptorStatus { + + switch self { + + case .success: + return CCCryptorStatus(kCCSuccess) + case .paramError: + return CCCryptorStatus(kCCParamError) + case .bufferTooSmall: + return CCCryptorStatus(kCCBufferTooSmall) + case .memoryFailure: + return CCCryptorStatus(kCCMemoryFailure) + case .alignmentError: + return CCCryptorStatus(kCCAlignmentError) + case .decodeError: + return CCCryptorStatus(kCCDecodeError) + case .unimplemented: + return CCCryptorStatus(kCCUnimplemented) + case .overflow: + return CCCryptorStatus(kCCOverflow) + case .rngFailure: + return CCCryptorStatus(kCCRNGFailure) + } + } + + /// + /// Human readable descriptions of the values. (Not needed in Swift 2.0?) + /// + static let descriptions = [ + success: "Success", + paramError: "ParamError", + bufferTooSmall: "BufferTooSmall", + memoryFailure: "MemoryFailure", + alignmentError: "AlignmentError", + decodeError: "DecodeError", + unimplemented: "Unimplemented", + overflow: "Overflow", + rngFailure: "RNGFailure" + ] + + /// + /// Obtain human-readable string from enum value. + /// + public var description: String { + + return (Status.descriptions[self] != nil) ? Status.descriptions[self]! : "" + } + + /// + /// Create enum value from raw `CCCryptorStatus` value. + /// + public static func fromRaw(status: CCCryptorStatus) -> Status? { + + let from = [ + kCCSuccess: success, + kCCParamError: paramError, + kCCBufferTooSmall: bufferTooSmall, + kCCMemoryFailure: memoryFailure, + kCCAlignmentError: alignmentError, + kCCDecodeError: decodeError, + kCCUnimplemented: unimplemented, + kCCOverflow: overflow, + kCCRNGFailure: rngFailure + ] + + return from[Int(status)] + + } +} + +#elseif os(Linux) + +/// +/// Error status +/// +public enum Status: Swift.Error, CustomStringConvertible { + + /// Success + case success + + /// Unimplemented with reason + case unimplemented(String) + + /// Not supported with reason + case notSupported(String) + + /// Parameter Error + case paramError + + /// Failure with error code + case fail(UInt) + + /// Random Byte Generator Failure with error code + case rngFailure(UInt) + + /// The error code itself + public var code: Int { + + switch self { + + case .success: + return 0 + + case .notSupported: + return -1 + + case .unimplemented: + return -2 + + case .paramError: + return -3 + + case .fail(let code): + return Int(code) + + case .rngFailure(let code): + return Int(code) + } + } + + /// + /// Create enum value from raw `SSL error code` value. + /// + public static func fromRaw(status: UInt) -> Status? { + + return Status.fail(status) + } + + /// + /// Obtain human-readable string for the error code. + /// + public var description: String { + + switch self { + + case .success: + return "No error" + + case .notSupported(let reason): + return "Not supported: \(reason)" + + case .unimplemented(let reason): + return "Not implemented: \(reason)" + + case .paramError: + return "Invalid parameters passed" + + case .fail(let errorCode): + return "ERROR: code: \(errorCode), reason: \(errToString(ERR_error_string(UInt(errorCode), nil)))" + + case .rngFailure(let errorCode): + return "Random Byte Generator ERROR: code: \(errorCode), reason: \(errToString(ERR_error_string(UInt(errorCode), nil)))" + } + } +} + +// MARK: Operators + +func == (lhs: Status, rhs: Status) -> Bool { + + return lhs.code == rhs.code +} + +func != (lhs: Status, rhs: Status) -> Bool { + + return lhs.code != rhs.code +} + +#endif + +/// +/// CryptorError +/// Thrown in caaes where a _fatalError()_ is **NOT** appropriate. +/// +public enum CryptorError: Swift.Error, CustomStringConvertible { + + /// Success + case success + + /// Invalid key size + case invalidKeySize + + /// Invalid IV size + case invalidIVSizeOrLength + + /// Fail with code and string + case fail(Int32, String) + + /// The error code itself + public var errCode: Int32 { + + switch self { + + case .success: + return 0 + + case .invalidKeySize: + return -1 + + case .invalidIVSizeOrLength: + return -2 + + case .fail(let errCode, _): + return Int32(errCode) + } + } + + /// Error Description + public var description: String { + + switch self { + + case .success: + return "Success" + + case .invalidKeySize: + return "Invalid key size." + + case .invalidIVSizeOrLength: + return "Invalid IV size or length." + + case .fail(_, let reason): + return reason + } + } +} + diff --git a/Pods/BlueCryptor/Sources/Cryptor/StreamCryptor.swift b/Pods/BlueCryptor/Sources/Cryptor/StreamCryptor.swift new file mode 100755 index 0000000..b9cbcb0 --- /dev/null +++ b/Pods/BlueCryptor/Sources/Cryptor/StreamCryptor.swift @@ -0,0 +1,939 @@ +// +// StreamCryptor.swift +// Cryptor +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + import CommonCrypto +#elseif os(Linux) + import OpenSSL +#endif + +/// +/// Encrypts or decrypts return results as they become available. +/// +/// - Note: The underlying cipher may be a block or a stream cipher. +/// +/// Use for large files or network streams. +/// +/// For small, in-memory buffers Cryptor may be easier to use. +/// +public class StreamCryptor { + + #if os(Linux) + + // + // Key sizes + // + static let kCCKeySizeAES128 = 16 + static let kCCKeySizeAES192 = 24 + static let kCCKeySizeAES256 = 32 + static let kCCKeySizeDES = 8 + static let kCCKeySize3DES = 24 + static let kCCKeySizeMinCAST = 5 + static let kCCKeySizeMaxCAST = 16 + static let kCCKeySizeMinRC2 = 1 + static let kCCKeySizeMaxRC2 = 128 + static let kCCKeySizeMinBlowfish = 8 + static let kCCKeySizeMaxBlowfish = 56 + + // + // Block sizes + // + static let kCCBlockSizeAES128 = 16 + static let kCCBlockSizeDES = 8 + static let kCCBlockSize3DES = 8 + static let kCCBlockSizeCAST = 8 + static let kCCBlockSizeRC2 = 8 + static let kCCBlockSizeBlowfish = 8 + + #endif + + /// + /// Enumerates Cryptor operations + /// + public enum Operation { + + /// Encrypting + case encrypt + + /// Decrypting + case decrypt + + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + + /// Convert to native `CCOperation` + func nativeValue() -> CCOperation { + + switch self { + + case .encrypt: + return CCOperation(kCCEncrypt) + + case .decrypt: + return CCOperation(kCCDecrypt) + } + } + + #elseif os(Linux) + + /// Convert to native value + func nativeValue() -> UInt32 { + + switch self { + + case .encrypt: + return 0 + + case .decrypt: + return 1 + } + } + + #endif + } + + /// + /// Enumerates valid key sizes. + /// + public enum ValidKeySize { + + case fixed(Int) + case discrete([Int]) + case range(Int, Int) + + /// + /// Determines if a given `keySize` is valid for this algorithm. + /// + /// - Parameter keySize: The size to test for validity. + /// + /// - Returns: True if valid, false otherwise. + /// + func isValidKeySize(keySize: Int) -> Bool { + + switch self { + + case .fixed(let fixed): + return (fixed == keySize) + + case .range(let min, let max): + return ((keySize >= min) && (keySize <= max)) + + case .discrete(let values): + return values.contains(keySize) + } + } + + /// + /// Determines the next valid key size; that is, the first valid key size larger + /// than the given value. + /// + /// - Parameter keySize: The size for which the `next` size is desired. + /// + /// - Returns: Will return `nil` if the passed in `keySize` is greater than the max. + /// + func paddedKeySize(keySize: Int) -> Int? { + + switch self { + + case .fixed(let fixed): + return (keySize <= fixed) ? fixed : nil + + case .range(let min, let max): + return (keySize > max) ? nil : ((keySize < min) ? min : keySize) + + case .discrete(let values): + return values.sorted().reduce(nil) { answer, current in + return answer ?? ((current >= keySize) ? current : nil) + } + } + } + + + } + + /// + /// Maps CommonCryptoOptions onto a Swift struct. + /// + public struct Options: OptionSet { + + public typealias RawValue = Int + public let rawValue: RawValue + + /// Convert from a native value (i.e. `0`, `kCCOptionpkcs7Padding`, `kCCOptionECBMode`) + public init(rawValue: RawValue) { + self.rawValue = rawValue + } + + /// Convert from a native value (i.e. `0`, `kCCOptionpkcs7Padding`, `kCCOptionECBMode`) + public init(_ rawValue: RawValue) { + self.init(rawValue: rawValue) + } + + /// No options + public static let none = Options(rawValue: 0) + + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + + /// Use padding. Needed unless the input is a integral number of blocks long. + public static var pkcs7Padding = Options(rawValue:kCCOptionPKCS7Padding) + + /// Electronic Code Book Mode. Don't use this. + public static var ecbMode = Options(rawValue:kCCOptionECBMode) + + #elseif os(Linux) + + /// Use padding. Needed unless the input is a integral number of blocks long. + public static var pkcs7Padding = Options(rawValue:0x0001) + + /// Electronic Code Book Mode. Don't use this. + public static var ecbMode = Options(rawValue:0x0002) + + #endif + } + + /// + /// Enumerates available algorithms + /// + public enum Algorithm { + + /// Advanced Encryption Standard + /// - Note: aes and aes128 are equivalent. + case aes, aes128, aes192, aes256 + + /// Data Encryption Standard + case des + + /// Triple des + case tripleDes + + /// cast + case cast + + /// rc2 + case rc2 + + /// blowfish + case blowfish + + /// Blocksize, in bytes, of algorithm. + public var blockSize: Int { + + switch self { + + case .aes, .aes128, .aes192, .aes256: + return kCCBlockSizeAES128 + + case .des: + return kCCBlockSizeDES + + case .tripleDes: + return kCCBlockSize3DES + + case .cast: + return kCCBlockSizeCAST + + case .rc2: + return kCCBlockSizeRC2 + + case .blowfish: + return kCCBlockSizeBlowfish + } + } + + public var defaultKeySize: Int { + + switch self { + + case .aes, .aes128: + return kCCKeySizeAES128 + + case .aes192: + return kCCKeySizeAES192 + + case .aes256: + return kCCKeySizeAES256 + + case .des: + return kCCKeySizeDES + + case .tripleDes: + return kCCKeySize3DES + + case .cast: + return kCCKeySizeMinCAST + + case .rc2: + return kCCKeySizeMinRC2 + + case .blowfish: + return kCCKeySizeMinBlowfish + } + } + + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + + /// Native, CommonCrypto constant for algorithm. + func nativeValue() -> CCAlgorithm { + + switch self { + + case .aes, .aes128, .aes192, .aes256: + return CCAlgorithm(kCCAlgorithmAES) + + case .des: + return CCAlgorithm(kCCAlgorithmDES) + + case .tripleDes: + return CCAlgorithm(kCCAlgorithm3DES) + + case .cast: + return CCAlgorithm(kCCAlgorithmCAST) + + case .rc2: + return CCAlgorithm(kCCAlgorithmRC2) + + case .blowfish: + return CCAlgorithm(kCCAlgorithmBlowfish) + } + } + + #elseif os(Linux) + + /// Native, OpenSSL function for algorithm. + func nativeValue(options: Options) -> OpaquePointer? { + + if options == .pkcs7Padding || options == .none { + + switch self { + + case .aes, .aes128: + return .init(EVP_aes_128_cbc()) + + case .aes192: + return .init(EVP_aes_192_cbc()) + + case .aes256: + return .init(EVP_aes_256_cbc()) + + case .des: + return .init(EVP_des_cbc()) + + case .tripleDes: + return .init(EVP_des_ede3_cbc()) + + case .cast: + return .init(EVP_cast5_cbc()) + + case .rc2: + return .init(EVP_rc2_cbc()) + + case .blowfish: + return .init(EVP_bf_cbc()) + } + } + + if options == .ecbMode { + + switch self { + + case .aes, .aes128: + return .init(EVP_aes_128_ecb()) + + case .aes192: + return .init(EVP_aes_192_ecb()) + + case .aes256: + return .init(EVP_aes_256_ecb()) + + case .des: + return .init(EVP_des_ecb()) + + case .tripleDes: + return .init(EVP_des_ede3_ecb()) + + case .cast: + return .init(EVP_cast5_ecb()) + + case .rc2: + return .init(EVP_rc2_ecb()) + + case .blowfish: + return .init(EVP_bf_ecb()) + } + } + + fatalError("Unsupported options and/or algorithm.") + } + + #endif + + /// + /// Determines the valid key size for this algorithm + /// + /// - Returns: Valid key size for this algorithm. + /// + func validKeySize() -> ValidKeySize { + + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + + switch self { + + case .aes, .aes128, .aes192, .aes256: + return .discrete([kCCKeySizeAES128, kCCKeySizeAES192, kCCKeySizeAES256]) + + case .des: + return .fixed(kCCKeySizeDES) + + case .tripleDes: + return .fixed(kCCKeySize3DES) + + case .cast: + return .range(kCCKeySizeMinCAST, kCCKeySizeMaxCAST) + + case .rc2: + return .range(kCCKeySizeMinRC2, kCCKeySizeMaxRC2) + + case .blowfish: + return .range(kCCKeySizeMinBlowfish, kCCKeySizeMaxBlowfish) + } + + #elseif os(Linux) + + switch self { + + case .aes, .aes128: + return .fixed(kCCKeySizeAES128) + + case .aes192: + return .fixed(kCCKeySizeAES192) + + case .aes256: + return .fixed(kCCKeySizeAES256) + + case .des: + return .fixed(kCCKeySizeDES) + + case .tripleDes: + return .fixed(kCCKeySize3DES) + + case .cast: + return .range(kCCKeySizeMinCAST, kCCKeySizeMaxCAST) + + case .rc2: + return .range(kCCKeySizeMinRC2, kCCKeySizeMaxRC2) + + case .blowfish: + return .range(kCCKeySizeMinBlowfish, kCCKeySizeMaxBlowfish) + } + + #endif + } + + /// + /// Tests if a given keySize is valid for this algorithm + /// + /// - Parameter keySize: The key size to be validated. + /// + /// - Returns: True if valid, false otherwise. + /// + func isValidKeySize(keySize: Int) -> Bool { + return self.validKeySize().isValidKeySize(keySize: keySize) + } + + /// + /// Calculates the next, if any, valid keySize greater or equal to a given `keySize` for this algorithm + /// + /// - Parameter keySize: Key size for which the next size is requested. + /// + /// - Returns: Next key size or nil + /// + func paddedKeySize(keySize: Int) -> Int? { + return self.validKeySize().paddedKeySize(keySize: keySize) + } + } + + /// + /// The status code resulting from the last method call to this Cryptor. + /// Used to get additional information when optional chaining collapes. + /// + public internal(set) var status: Status = .success + + /// + /// Context obtained. True if we have it, false otherwise. + /// + private var haveContext: Bool = false + + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + + /// CommonCrypto Context + private var context = UnsafeMutablePointer.allocate(capacity: 1) + + #elseif os(Linux) + + /// OpenSSL Cipher Context + private let context: OpaquePointer? = .init(EVP_CIPHER_CTX_new()) + + /// Operation + private var operation: Operation = .encrypt + + /// The algorithm + private var algorithm: Algorithm + + #endif + + + // MARK: Lifecycle Methods + + /// + /// Default Initializer + /// + /// - Parameters: + /// - operation: The operation to perform see Operation (Encrypt, Decrypt) + /// - algorithm: The algorithm to use see Algorithm (AES, des, tripleDes, cast, rc2, blowfish) + /// - keyBuffer: Pointer to key buffer + /// - keyByteCount: Number of bytes in the key + /// - ivBuffer: Initialization vector buffer + /// - ivLength: Length of the ivBuffer + /// + /// - Returns: New StreamCryptor instance. + /// + public init(operation: Operation, algorithm: Algorithm, options: Options, keyBuffer: [UInt8], keyByteCount: Int, ivBuffer: UnsafePointer, ivLength: Int = 0) throws { + + guard algorithm.isValidKeySize(keySize: keyByteCount) else { + throw CryptorError.invalidKeySize + } + + guard options.contains(.ecbMode) || ivLength == algorithm.blockSize else { + throw CryptorError.invalidIVSizeOrLength + } + + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + + let rawStatus = CCCryptorCreate(operation.nativeValue(), algorithm.nativeValue(), CCOptions(options.rawValue), keyBuffer, keyByteCount, ivBuffer, self.context) + + if let status = Status.fromRaw(status: rawStatus) { + + self.status = status + + } else { + + throw CryptorError.fail(rawStatus, "Cryptor init returned unexpected status.") + } + + self.haveContext = true + + #elseif os(Linux) + + self.algorithm = algorithm + self.operation = operation + + var rawStatus: Int32 + + switch self.operation { + + case .encrypt: + rawStatus = EVP_EncryptInit_ex(.make(optional: self.context), .make(optional: algorithm.nativeValue(options: options)), nil, keyBuffer, ivBuffer) + + case .decrypt: + rawStatus = EVP_DecryptInit_ex(.make(optional: self.context), .make(optional: algorithm.nativeValue(options: options)), nil, keyBuffer, ivBuffer) + } + + if rawStatus == 0 { + + let errorCode = ERR_get_error() + if let status = Status.fromRaw(status: errorCode) { + self.status = status + } else { + + throw CryptorError.fail(Int32(errorCode), "Cryptor init returned unexpected status.") + } + } + + self.haveContext = true + + // Default to no padding... + var needPadding: Int32 = 0 + if options == .pkcs7Padding { + needPadding = 1 + } + + // Note: This call must be AFTER the init call above... + EVP_CIPHER_CTX_set_padding(.make(optional: self.context), needPadding) + + self.status = Status.success + + #endif + + } + + /// + /// Creates a new StreamCryptor + /// + /// - Parameters: + /// - operation: The operation to perform see Operation (Encrypt, Decrypt) + /// - algorithm: The algorithm to use see Algorithm (AES, des, tripleDes, cast, rc2, blowfish) + /// - key: A byte array containing key data + /// - iv: A byte array containing initialization vector + /// + /// - Returns: New StreamCryptor instance. + /// + public convenience init(operation: Operation, algorithm: Algorithm, options: Options, key: [UInt8], iv: [UInt8]) throws { + + guard let paddedKeySize = algorithm.paddedKeySize(keySize: key.count) else { + throw CryptorError.invalidKeySize + } + + try self.init(operation:operation, + algorithm:algorithm, + options:options, + keyBuffer:CryptoUtils.zeroPad(byteArray:key, blockSize: paddedKeySize), + keyByteCount:paddedKeySize, + ivBuffer:iv, + ivLength:iv.count) + } + + /// + /// Creates a new StreamCryptor + /// + /// - Parameters: + /// - operation: The operation to perform see Operation (Encrypt, Decrypt) + /// - algorithm: The algorithm to use see Algorithm (AES, des, tripleDes, cast, rc2, blowfish) + /// - key: A string containing key data (will be interpreted as UTF8) + /// - iv: A string containing initialization vector data (will be interpreted as UTF8) + /// + /// - Returns: New StreamCryptor instance. + /// + public convenience init(operation: Operation, algorithm: Algorithm, options: Options, key: String, iv: String) throws { + + let keySize = key.utf8.count + guard let paddedKeySize = algorithm.paddedKeySize(keySize: keySize) else { + throw CryptorError.invalidKeySize + } + + try self.init(operation:operation, + algorithm:algorithm, + options:options, + keyBuffer:CryptoUtils.zeroPad(string: key, blockSize: paddedKeySize), + keyByteCount:paddedKeySize, + ivBuffer:iv, + ivLength:iv.utf8.count) + } + + /// + /// Cleanup + /// + deinit { + + // Ensure we've got a context before attempting to get rid of it... + if self.haveContext == false { + return + } + + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + + // Ensure we've got a context before attempting to get rid of it... + if self.context.pointee == nil { + return + } + + let rawStatus = CCCryptorRelease(self.context.pointee) + if let status = Status.fromRaw(status: rawStatus) { + + if status != .success { + + NSLog("WARNING: CCCryptoRelease failed with status \(rawStatus).") + } + + } else { + + fatalError("CCCryptorUpdate returned unexpected status.") + } + + #if swift(>=4.1) + context.deallocate() + #else + context.deallocate(capacity: 1) + #endif + + self.haveContext = false + + #elseif os(Linux) + + EVP_CIPHER_CTX_free(.make(optional: self.context)) + self.haveContext = false + + #endif + } + + // MARK: Public Methods + + /// + /// Add the contents of an Data buffer to the current encryption/decryption operation. + /// + /// - Parameters: + /// - dataIn: The input data + /// - byteArrayOut: Output data + /// + /// - Returns: A tuple containing the number of output bytes produced and the status (see Status) + /// + public func update(dataIn: Data, byteArrayOut: inout [UInt8]) -> (Int, Status) { + + let dataOutAvailable = byteArrayOut.count + var dataOutMoved = 0 + #if swift(>=5.0) + dataIn.withUnsafeBytes() { + _ = update(bufferIn: $0.baseAddress!, byteCountIn: dataIn.count, bufferOut: &byteArrayOut, byteCapacityOut: dataOutAvailable, byteCountOut: &dataOutMoved) + } + #else + dataIn.withUnsafeBytes() { (buffer: UnsafePointer) in + _ = update(bufferIn: buffer, byteCountIn: dataIn.count, bufferOut: &byteArrayOut, byteCapacityOut: dataOutAvailable, byteCountOut: &dataOutMoved) + } + #endif + return (dataOutMoved, self.status) + } + + /// + /// Add the contents of an NSData buffer to the current encryption/decryption operation. + /// + /// - Parameters: + /// - dataIn: The input data + /// - byteArrayOut: Output data + /// + /// - Returns: A tuple containing the number of output bytes produced and the status (see Status) + /// + public func update(dataIn: NSData, byteArrayOut: inout [UInt8]) -> (Int, Status) { + + let dataOutAvailable = byteArrayOut.count + var dataOutMoved = 0 + var ptr = dataIn.bytes.assumingMemoryBound(to: UInt8.self).pointee + _ = update(bufferIn: &ptr, byteCountIn: dataIn.length, bufferOut: &byteArrayOut, byteCapacityOut: dataOutAvailable, byteCountOut: &dataOutMoved) + return (dataOutMoved, self.status) + } + + /// + /// Add the contents of a byte array to the current encryption/decryption operation. + /// + /// - Parameters: + /// - byteArrayIn: The input data + /// - byteArrayOut: Output data + /// + /// - Returns: A tuple containing the number of output bytes produced and the status (see Status) + /// + public func update(byteArrayIn: [UInt8], byteArrayOut: inout [UInt8]) -> (Int, Status) { + + let dataOutAvailable = byteArrayOut.count + var dataOutMoved = 0 + _ = update(bufferIn: byteArrayIn, byteCountIn: byteArrayIn.count, bufferOut: &byteArrayOut, byteCapacityOut: dataOutAvailable, byteCountOut: &dataOutMoved) + return (dataOutMoved, self.status) + } + + /// + /// Add the contents of a string (interpreted as UTF8) to the current encryption/decryption operation. + /// + /// - Parameters: + /// - byteArrayIn: The input data + /// - byteArrayOut: Output data + /// + /// - Returns: A tuple containing the number of output bytes produced and the status (see Status) + /// + public func update(stringIn: String, byteArrayOut: inout [UInt8]) -> (Int, Status) { + + let dataOutAvailable = byteArrayOut.count + var dataOutMoved = 0 + _ = update(bufferIn: stringIn, byteCountIn: stringIn.utf8.count, bufferOut: &byteArrayOut, byteCapacityOut: dataOutAvailable, byteCountOut: &dataOutMoved) + return (dataOutMoved, self.status) + } + + /// + /// Retrieves all remaining encrypted or decrypted data from this cryptor. + /// + /// - Note: If the underlying algorithm is an block cipher and the padding option has + /// not been specified and the cumulative input to the cryptor has not been an integral + /// multiple of the block length this will fail with an alignment error. + /// + /// - Note: This method updates the status property + /// + /// - Parameter byteArrayOut: The output bffer + /// + /// - Returns: a tuple containing the number of output bytes produced and the status (see Status) + /// + public func final(byteArrayOut: inout [UInt8]) -> (Int, Status) { + + let dataOutAvailable = byteArrayOut.count + var dataOutMoved = 0 + _ = final(bufferOut: &byteArrayOut, byteCapacityOut: dataOutAvailable, byteCountOut: &dataOutMoved) + return (dataOutMoved, self.status) + } + + // MARK: - Low-level interface + + /// + /// Update the buffer + /// + /// - Parameters: + /// - bufferIn: Pointer to input buffer + /// - inByteCount: Number of bytes contained in input buffer + /// - bufferOut: Pointer to output buffer + /// - outByteCapacity: Capacity of the output buffer in bytes + /// - outByteCount: On successful completion, the number of bytes written to the output buffer + /// + /// - Returns: Status of the update + /// + public func update(bufferIn: UnsafeRawPointer, byteCountIn: Int, bufferOut: UnsafeMutablePointer, byteCapacityOut: Int, byteCountOut: inout Int) -> Status { + + if self.status == .success { + + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + + let rawStatus = CCCryptorUpdate(self.context.pointee, bufferIn, byteCountIn, bufferOut, byteCapacityOut, &byteCountOut) + if let status = Status.fromRaw(status: rawStatus) { + self.status = status + } else { + fatalError("CCCryptorUpdate returned unexpected status.") + } + + #elseif os(Linux) + + var rawStatus: Int32 + var outLength: Int32 = 0 + + switch self.operation { + + case .encrypt: + rawStatus = EVP_EncryptUpdate(.make(optional: self.context), bufferOut, &outLength, bufferIn.assumingMemoryBound(to: UInt8.self), Int32(byteCountIn)) + + case .decrypt: + rawStatus = EVP_DecryptUpdate(.make(optional: self.context), bufferOut, &outLength, bufferIn.assumingMemoryBound(to: UInt8.self), Int32(byteCountIn)) + } + + byteCountOut = Int(outLength) + + if rawStatus == 0 { + + let errorCode = ERR_get_error() + if let status = Status.fromRaw(status: errorCode) { + self.status = status + } else { + fatalError("Cryptor update returned unexpected status.") + } + + } else { + + self.status = Status.success + } + + #endif + + } + + return self.status + } + + /// + /// Retrieves all remaining encrypted or decrypted data from this cryptor. + /// + /// - Note: If the underlying algorithm is an block cipher and the padding option has + /// not been specified and the cumulative input to the cryptor has not been an integral + /// multiple of the block length this will fail with an alignment error. + /// + /// - Note: This method updates the status property + /// + /// - Parameters: + /// - bufferOut: Pointer to output buffer + /// - outByteCapacity: Capacity of the output buffer in bytes + /// - outByteCount: On successful completion, the number of bytes written to the output buffer + /// + /// - Returns: Status of the update + /// + public func final(bufferOut: UnsafeMutablePointer, byteCapacityOut: Int, byteCountOut: inout Int) -> Status { + + if self.status == Status.success { + + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + + let rawStatus = CCCryptorFinal(self.context.pointee, bufferOut, byteCapacityOut, &byteCountOut) + if let status = Status.fromRaw(status: rawStatus) { + self.status = status + } else { + fatalError("CCCryptorUpdate returned unexpected status.") + } + + #elseif os(Linux) + + var rawStatus: Int32 + var outLength: Int32 = Int32(byteCapacityOut) + + switch self.operation { + + case .encrypt: + rawStatus = EVP_EncryptFinal_ex(.make(optional: self.context), bufferOut, &outLength) + + case .decrypt: + rawStatus = EVP_DecryptFinal_ex(.make(optional: self.context), bufferOut, &outLength) + } + + byteCountOut = Int(outLength) + + if rawStatus == 0 { + + let errorCode = ERR_get_error() + if let status = Status.fromRaw(status: errorCode) { + self.status = status + } else { + fatalError("Cryptor final returned unexpected status.") + } + + } else { + + self.status = Status.success + } + + #endif + } + + return self.status + } + + /// + /// Determines the number of bytes that will be output by this Cryptor if inputBytes of additional + /// data is input. + /// + /// - Parameters: + /// - inputByteCount: Number of bytes that will be input. + /// - isFinal: True if buffer to be input will be the last input buffer, false otherwise. + /// + /// - Returns: The final output length + /// + public func getOutputLength(inputByteCount: Int, isFinal: Bool = false) -> Int { + + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + + return CCCryptorGetOutputLength(self.context.pointee, inputByteCount, isFinal) + + #elseif os(Linux) + + if inputByteCount == 0 { + return self.algorithm.blockSize + } + + return (inputByteCount + self.algorithm.blockSize - (inputByteCount % self.algorithm.blockSize)) + + #endif + } + +} diff --git a/Pods/BlueCryptor/Sources/Cryptor/Updatable.swift b/Pods/BlueCryptor/Sources/Cryptor/Updatable.swift new file mode 100755 index 0000000..5f98ab7 --- /dev/null +++ b/Pods/BlueCryptor/Sources/Cryptor/Updatable.swift @@ -0,0 +1,107 @@ +// +// Updateable.swift +// Cryptor +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +/// +/// A protocol for calculations that can be updated with incremental data buffers. +/// +public protocol Updatable { + + /// Status of the calculation. + var status: Status { get } + + /// + /// Low-level update routine. + /// Updates the calculation with the contents of a data buffer. + /// + /// - Parameters: + /// - buffer: Pointer to the data buffer + /// - byteCount: Length of the buffer in bytes + /// + /// - Returns: `Self` if no error for optional chaining, nil otherwise + /// + func update(from buffer: UnsafeRawPointer, byteCount: size_t) -> Self? +} + +/// +/// Factors out common update code from Digest, HMAC and Cryptor. +/// +extension Updatable { + /// + /// Updates the current calculation with data contained in an `NSData` object. + /// + /// - Parameter data: The `NSData` object + /// + /// - Returns: Optional `Self` or nil + /// + public func update(data: NSData) -> Self? { + + _ = update(from: data.bytes, byteCount: size_t(data.length)) + return self.status == .success ? self : nil + } + + /// + /// Updates the current calculation with data contained in an `Data` object. + /// + /// - Parameters data: The `Data` object + /// + /// - Returns: Optional `Self` or nil + /// + public func update(data: Data) -> Self? { + + #if swift(>=5.0) + _ = data.withUnsafeBytes() { + + _ = update(from: $0.baseAddress!, byteCount: size_t(data.count)) + } + #else + _ = data.withUnsafeBytes() { (buffer: UnsafePointer) in + + _ = update(from: buffer, byteCount: size_t(data.count)) + } + #endif + return self.status == .success ? self : nil + } + + /// + /// Updates the current calculation with data contained in a byte array. + /// + /// - Parameter byteArray: The byte array + /// + /// - Returns: Optional `Self` or nil + /// + public func update(byteArray: [UInt8]) -> Self? { + + _ = update(from: byteArray, byteCount: size_t(byteArray.count)) + return self.status == .success ? self : nil + } + + /// + /// Updates the current calculation with data contained in a String. + /// The corresponding data will be generated using UTF8 encoding. + /// + /// - Parameter string: The string of data + /// + /// - Returns: Optional `Self` or nil + /// + public func update(string: String) -> Self? { + + _ = update(from: string, byteCount: size_t(string.utf8.count)) + return self.status == .success ? self : nil + } +} diff --git a/Pods/BlueCryptor/Sources/Cryptor/Utilities.swift b/Pods/BlueCryptor/Sources/Cryptor/Utilities.swift new file mode 100755 index 0000000..835c934 --- /dev/null +++ b/Pods/BlueCryptor/Sources/Cryptor/Utilities.swift @@ -0,0 +1,262 @@ +// +// Utilities.swift +// Cryptor +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +// +// Replaces Swift's native `fatalError` function to allow redirection +// For more details about how this all works see: +// https://marcosantadev.com/test-swift-fatalerror/ +// +func fatalError(_ message: @autoclosure () -> String = "", file: StaticString = #file, line: UInt = #line) -> Never { + + FatalErrorUtil.fatalErrorClosure(message(), file, line) +} + +// Convert an UnsafeMutablePointer? to a String, providing a +// default value of empty string if the pointer is nil. +// +// - Parameter ptr: Pointer to string to be converted. +// +// - Returns: Converted string. +// +func errToString(_ ptr: UnsafeMutablePointer?) -> String { + if let ptr = ptr { + return String(cString: ptr) + } else { + return "" + } +} + +/// +/// Allows redirection of `fatalError` for Unit Testing or for +/// library users that want to handle such errors in another way. +/// +struct FatalErrorUtil { + + static var fatalErrorClosure: (String, StaticString, UInt) -> Never = defaultFatalErrorClosure + private static let defaultFatalErrorClosure = { Swift.fatalError($0, file: $1, line: $2) } + static func replaceFatalError(closure: @escaping (String, StaticString, UInt) -> Never) { + fatalErrorClosure = closure + } + static func restoreFatalError() { + fatalErrorClosure = defaultFatalErrorClosure + } + +} + +/// +/// Various utility functions for conversions +/// +public struct CryptoUtils { + + /// + /// Converts a single hexadecimal digit encoded as a Unicode Scalar to it's corresponding value. + /// + /// - Parameter digit: A Unicode scalar in the set 0..9a..fA..F + /// + /// - Returns: The hexadecimal value of the digit + /// + static func convert(hexDigit digit: UnicodeScalar) -> UInt8? { + + switch digit { + + case UnicodeScalar(unicodeScalarLiteral:"0")...UnicodeScalar(unicodeScalarLiteral:"9"): + return UInt8(digit.value - UnicodeScalar(unicodeScalarLiteral:"0").value) + + case UnicodeScalar(unicodeScalarLiteral:"a")...UnicodeScalar(unicodeScalarLiteral:"f"): + return UInt8(digit.value - UnicodeScalar(unicodeScalarLiteral:"a").value + 0xa) + + case UnicodeScalar(unicodeScalarLiteral:"A")...UnicodeScalar(unicodeScalarLiteral:"F"): + return UInt8(digit.value - UnicodeScalar(unicodeScalarLiteral:"A").value + 0xa) + + default: + return nil + } + } + + /// + /// Converts a string of hexadecimal digits to a byte array. + /// + /// - Parameter string: The hex string (must contain an even number of digits) + /// + /// - Returns: A byte array or [] if the input is not valid hexadecimal + /// + public static func byteArray(fromHex string: String) -> [UInt8] { + + var iterator = string.unicodeScalars.makeIterator() + var byteArray: [UInt8] = [] + while let msn = iterator.next() { + + if let lsn = iterator.next(), + let hexMSN = convert(hexDigit: msn) , + let hexLSN = convert(hexDigit: lsn) { + + byteArray += [ (hexMSN << 4 | hexLSN) ] + + } else { + + // @TODO: In the next major release this function should throw instead of returning an empty array. + return [] + } + } + return byteArray + } + + /// + /// Converts a UTF-8 String to a byte array. + /// + /// - Parameter string: the string + /// + /// - Returns: A byte array + /// + public static func byteArray(from string: String) -> [UInt8] { + + let array = [UInt8](string.utf8) + return array + } + + /// + /// Converts a string of hexadecimal digits to an `NSData` object. + /// + /// - Parameter string: The hex string (must contain an even number of digits) + /// + /// - Returns: An `NSData` object + /// + public static func data(fromHex string: String) -> NSData { + + let a = byteArray(fromHex: string) + return NSData(bytes:a, length:a.count) + } + + /// + /// Converts a string of hexadecimal digits to an `Data` object. + /// + /// - Parameter string: The hex string (must contain an even number of digits) + /// + /// - Returns: An `Data` object + /// + public static func data(fromHex string: String) -> Data { + + let a = byteArray(fromHex: string) + return Data(bytes: a, count: a.count) + } + + /// + /// Converts a byte array to an `NSData` object. + /// + /// - Parameter byteArray: The byte array + /// + /// - Returns: An `NSData` object + /// + public static func data(from byteArray: [UInt8]) -> NSData { + + return NSData(bytes:byteArray, length:byteArray.count) + } + + /// + /// Converts a byte array to an `Data` object. + /// + /// - Parameter byteArray: The byte array + /// + /// - Returns: An `Data` object + /// + public static func data(from byteArray: [UInt8]) -> Data { + + return Data(bytes: byteArray, count: byteArray.count) + } + + /// + /// Converts a byte array to a string of hexadecimal digits. + /// + /// - Parameters: + /// - byteArray: The Swift array + /// - uppercase: True to use uppercase for letter digits, lowercase otherwise + /// + /// - Returns: A String + /// + public static func hexString(from byteArray: [UInt8], uppercase: Bool = false) -> String { + + return byteArray.map() { String(format: (uppercase) ? "%02X" : "%02x", $0) }.reduce("", +) + } + + /// + /// Converts a Swift array to an `NSString` object. + /// + /// - Parameters: + /// - byteArray: The Swift array + /// - uppercase: True to use uppercase for letter digits, lowercase otherwise + /// + /// - Returns: An `NSString` object + /// + public static func hexNSString(from byteArray: [UInt8], uppercase: Bool = false) -> NSString { + + let formatString = (uppercase) ? "%02X" : "%02x" + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + return byteArray.map() { String(format: formatString, $0) }.reduce("", +) as NSString + #else + let aString = byteArray.map() { String(format: formatString, $0) }.reduce("", +) + return NSString(string: aString) + #endif + } + + /// + /// Converts a byte array to a String containing a comma separated list of bytes. + /// This is used to generate test data programmatically. + /// + /// - Parameter byteArray: The byte array + /// + /// - Returns: A String + /// + public static func hexList(from byteArray: [UInt8]) -> String { + + return byteArray.map() { String(format:"0x%02x, ", $0) }.reduce("", +) + } + + /// + /// Zero pads a byte array such that it is an integral number of `blockSizeinBytes` long. + /// + /// - Parameters: + /// - byteArray: The byte array + /// - blockSizeInBytes: The block size in bytes. + /// + /// - Returns: A Swift string + /// + public static func zeroPad(byteArray: [UInt8], blockSize: Int) -> [UInt8] { + + let pad = blockSize - (byteArray.count % blockSize) + guard pad != 0 else { + return byteArray + } + return byteArray + Array(repeating: 0, count: pad) + } + + /// + /// Zero pads a String (after UTF8 conversion) such that it is an integral number of `blockSizeinBytes` long. + /// + /// - Parameters: + /// - string: The String + /// - blockSizeInBytes: The block size in bytes + /// + /// - Returns: A byte array + /// + public static func zeroPad(string: String, blockSize: Int) -> [UInt8] { + + return zeroPad(byteArray: Array(string.utf8), blockSize: blockSize) + } + +} diff --git a/Pods/BlueECC/LICENSE.txt b/Pods/BlueECC/LICENSE.txt new file mode 100644 index 0000000..d9a10c0 --- /dev/null +++ b/Pods/BlueECC/LICENSE.txt @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/Pods/BlueECC/README.md b/Pods/BlueECC/README.md new file mode 100644 index 0000000..4d11534 --- /dev/null +++ b/Pods/BlueECC/README.md @@ -0,0 +1,204 @@ +

+ + Kitura + +

+ + +

+ + APIDoc + + + Build Status - Master + + macOS + Linux + Apache 2 + + Slack Status + +

+ +# BlueECC + +A cross platform Swift implementation of Elliptic Curve Digital Signature Algorithm (ECDSA) and Elliptic Curve Integrated Encryption Scheme (ECIES). This allows you to sign, verify, encrypt and decrypt using elliptic curve keys. + +## Swift version + +The latest version of BlueECC requires **Swift 4.1** or later. You can download this version of the Swift binaries by following this [link](https://swift.org/download/). Compatibility with other Swift versions is not guaranteed. + +## Usage + +#### Add dependencies + +Add the `BlueECC` package to the dependencies within your application’s `Package.swift` file. Substitute `"x.x.x"` with the latest `BlueECC` [release](https://github.com/IBM-Swift/BlueECC/releases). + +```swift +.package(url: "https://github.com/IBM-Swift/BlueECC.git", from: "x.x.x") +``` + +Add `CryptorECC` to your target's dependencies: + +```swift +.target(name: "example", dependencies: ["CryptorECC"]), +``` + +#### Import package + +```swift +import CryptorECC +``` + +### Getting Started + +#### Elliptic curve private key + +you can generate an ECPrivate key using BlueECC. + +```swift +let p256PrivateKey = try ECPrivateKey.make(for: .prime256v1) +``` + +You can then view the key in it's PEM format as follows: + +```swift +let privateKeyPEM = p256PrivateKey.pemString +``` + +The following curves are supported: +- prime256v1 +- secp384r1 +- secp521r1 + +Alternatively, you may generate private key using a third party provider: + +- You can generate a `p-256` private key as a `.p8` file for Apple services from [https://developer.apple.com/account/ios/authkey](https://developer.apple.com/account/ios/authkey/). This will produce a key that should be formatted as follows: +```swift +let privateKey = +""" +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQglf7ztYnsaHX2yiHJ +meHFl5dg05y4a/hD7wwuB7hSRpmhRANCAASKRzmboLbG0NZ54B5PXxYSU7fvO8U7 +PyniQCWG+Agc3bdcgKU0RKApWYuBJKrZqyqLB2tTlgdtwcWSB0AEzVI8 +-----END PRIVATE KEY----- +""" +``` + +- You can use OpenSSL [Command Line Elliptic Curve Operations](https://wiki.openssl.org/index.php/Command_Line_Elliptic_Curve_Operations). + +The following commands generate private keys for the three supported curves as `.pem` files: +``` +// p-256 +$ openssl ecparam -name prime256v1 -genkey -noout -out key.pem +// p-384 +$ openssl ecparam -name secp384r1 -genkey -noout -out key.pem +// p-521 +$ openssl ecparam -name secp521r1 -genkey -noout -out key.pem +``` +These keys will be formatted as follows: +```swift +let privateKey = +""" +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIJX+87WJ7Gh19sohyZnhxZeXYNOcuGv4Q+8MLge4UkaZoAoGCCqGSM49 +AwEHoUQDQgAEikc5m6C2xtDWeeAeT18WElO37zvFOz8p4kAlhvgIHN23XIClNESg +KVmLgSSq2asqiwdrU5YHbcHFkgdABM1SPA== +-----END EC PRIVATE KEY----- +""" +``` + +The key string can then be used to initialize an `ECPrivateKey` instance: +```swift +let eccPrivateKey = try ECPrivateKey(key: privateKey) +``` + +#### Elliptic curve public key + +You can use OpenSSL to generate an elliptic curve public key `.pem` file from any of the above elliptic curve private key files: +``` +$ openssl ec -in key.pem -pubout -out public.pem +``` +This will produce a public key formatted as follows: +```swift +let publicKey = +""" +-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEikc5m6C2xtDWeeAeT18WElO37zvF +Oz8p4kAlhvgIHN23XIClNESgKVmLgSSq2asqiwdrU5YHbcHFkgdABM1SPA== +-----END PUBLIC KEY----- +""" +``` +These keys can then be used to initialize an `ECPrivateKey` instance: +```swift +let eccPublicKey = try ECPublicKey(key: publicKey) +``` + +Alternatively, you can extract the public key from your `ECPrivateKey`: + +```swift +let eccPublicKey = try eccPrivateKey.extractPublicKey() +print(eccPublicKey.pemString) +``` + +#### Signing String or Data + +BlueECC extends `String` and `Data` so you can call sign directly on your plaintext using an EC private key. This creates an `ECSignature` containing the r and s signature values: + +```swift +let message = "hello world" +let signature = try message.sign(with: eccPrivateKey) +``` + +#### Verifying the signature + +Use the public key to verify the signature for the plaintext: +```swift +let verified = signature.verify(plaintext: message, using: eccPublicKey) +if verified { + print("Signature is valid for provided plaintext") +} +``` + +#### Encrypting String or Data + +Use the public key to encrypt your plaintext String or Data to encrypted Data or an encrypted Base64Encoded String: +```swift +let encryptedData = try "Hello World".encrypt(with: eccPublicKey) +print(encryptedData.base64EncodedString()) +``` + +#### Decrypting to plaintext + +Use the private key to decrypt the encrypted Data or Base64Encoded String to plaintext Data or UTF8 String: + +```swift +let decryptedData = try encryptedData.decrypt(with: eccPrivateKey) +print(String(data: decryptedData, encoding: .utf8)) +``` + +#### Encryption interoperability + +Cross platform encryption and decryption is currently only supported with `prime256v1` curves. The `secp384r1` and `secp521r1` curves do not support Linux encryption with Apple platform decryption and vice versa. + +If you would like to interoperate with this repo, +The following describes the encryption process: +- Generate an ephemeral EC key pair +- Use ECDH of your EC pair to generate a symmetric key +- Use SHA256 ANSI x9.63 Key Derivation Function with the ephemeral public key to generate a 32 byte key +- Use the first 16 bytes as an AES-GCM key +- Use the second 16 bytes as the initialization vector (IV) +- Use aes_128_gcm to encrypt the plaintext and generate a 16 byte GCM tag +- Send the ephemeral public key, encrypted data and GCM tag + +This is equivalent to: `kSecKeyAlgorithmECIESEncryptionStandardVariableIVX963SHA256AESGCM` when using apple security. + +## API Documentation + +For more information visit our [API reference](https://ibm-swift.github.io/BlueECC/index.html). + +## Community +We love to talk server-side Swift, and Kitura. Join our [Slack](http://swift-at-ibm-slack.mybluemix.net/) to meet the team! + +## License +This library is licensed under Apache 2.0. Full license text is available in [LICENSE](https://github.com/IBM-Swift/BlueECC/blob/master/LICENSE.txt). diff --git a/Pods/BlueECC/Sources/CryptorECC/ASN1.swift b/Pods/BlueECC/Sources/CryptorECC/ASN1.swift new file mode 100644 index 0000000..21d82ff --- /dev/null +++ b/Pods/BlueECC/Sources/CryptorECC/ASN1.swift @@ -0,0 +1,92 @@ +/** + * Copyright IBM Corporation 2019 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +import Foundation + +// Private and public keys are stores in ASN1 format. +// The following code is used to parse the data and retrieve the required elements. +struct ASN1 { + + indirect enum ASN1Element { + case seq(elements: [ASN1Element]) + case integer(int: Int) + case bytes(data: Data) + case constructed(tag: Int, elem: ASN1Element) + case unknown + } + + static func toASN1Element(data: Data) -> (ASN1Element, Int) { + guard data.count >= 2 else { + // format error + return (.unknown, data.count) + } + + switch data[0] { + case 0x30: // sequence + let (length, lengthOfLength) = readLength(data: data.advanced(by: 1)) + var result: [ASN1Element] = [] + var subdata = data.advanced(by: 1 + lengthOfLength) + var alreadyRead = 0 + + while alreadyRead < length { + let (e, l) = toASN1Element(data: subdata) + result.append(e) + subdata = subdata.count > l ? subdata.advanced(by: l) : Data() + alreadyRead += l + } + return (.seq(elements: result), 1 + lengthOfLength + length) + + case 0x02: // integer + let (length, lengthOfLength) = readLength(data: data.advanced(by: 1)) + if length < 8 { + var result: Int = 0 + let subdata = data.advanced(by: 1 + lengthOfLength) + // ignore negative case + for i in 0.. (Int, Int) { + if data[0] & 0x80 == 0x00 { // short form + return (Int(data[0]), 1) + } else { + let lenghOfLength = Int(data[0] & 0x7F) + var result: Int = 0 + for i in 1..<(1 + lenghOfLength) { + result = 256 * result + Int(data[i]) + } + return (result, 1 + lenghOfLength) + } + } +} diff --git a/Pods/BlueECC/Sources/CryptorECC/Data+Extensions.swift b/Pods/BlueECC/Sources/CryptorECC/Data+Extensions.swift new file mode 100644 index 0000000..08808c8 --- /dev/null +++ b/Pods/BlueECC/Sources/CryptorECC/Data+Extensions.swift @@ -0,0 +1,39 @@ +// Copyright © 2019 IBM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +#if !swift(>=5.0) +// Extension to allow Swift 5 `withUnsafeBytes` API for earlier versions +internal extension Data { + func withUnsafeBytes(_ body: (UnsafeRawBufferPointer) throws -> T) rethrows -> T { + let c = count + return try withUnsafeBytes { (p: UnsafePointer) throws -> T in + try body(UnsafeRawBufferPointer(start: p, count: c)) + } + } + + mutating func withUnsafeMutableBytes(_ body: (UnsafeMutableRawBufferPointer) throws -> T) rethrows -> T { + let c = count + return try withUnsafeMutableBytes { (p: UnsafeMutablePointer) throws -> T in + try body(UnsafeMutableRawBufferPointer(start: p, count: c)) + } + } + + init(_ bytes: [UInt8]) { + self.init(bytes: bytes) + } +} +#endif diff --git a/Pods/BlueECC/Sources/CryptorECC/ECDecryptable.swift b/Pods/BlueECC/Sources/CryptorECC/ECDecryptable.swift new file mode 100644 index 0000000..2a95bff --- /dev/null +++ b/Pods/BlueECC/Sources/CryptorECC/ECDecryptable.swift @@ -0,0 +1,146 @@ +// Copyright © 2019 IBM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) +import CommonCrypto +#elseif os(Linux) +import OpenSSL +#endif + +/// Extensions for encrypting, decrypting or signing `Data` using the appropriate algorithm determined by the key's curve with the provided `ECPrivateKey` or `ECPublicKey`. +@available(macOS 10.13, iOS 11, watchOS 4.0, tvOS 11.0, *) +extension Data { + + /// Decrypt the encrypted data using the provided `ECPrivateKey`. + /// The signing algorithm used is determined based on the private key's elliptic curve. + /// - Parameter ecPrivateKey: The elliptic curve private key. + /// - Returns: The plaintext Data. + /// - Throws: An ECError if the Encrypted data fails to be decrypted. + public func decrypt(with key: ECPrivateKey) throws -> Data { + #if os(Linux) + // Initialize the decryption context. + let rsaDecryptCtx = EVP_CIPHER_CTX_new() + EVP_CIPHER_CTX_init_wrapper(rsaDecryptCtx) + + let tagLength = 16 + let encKeyLength = key.curve.keySize + let encryptedDataLength = Int(self.count) - encKeyLength - tagLength + // Extract encryptedAESKey, encryptedData, GCM tag from data + let encryptedKey = self.subdata(in: 0...allocate(capacity: skey_len) + let decrypted = UnsafeMutablePointer.allocate(capacity: Int(encryptedData.count + 16)) + defer { + // On completion deallocate the memory + EVP_CIPHER_CTX_reset_wrapper(rsaDecryptCtx) + EVP_CIPHER_CTX_free_wrapper(rsaDecryptCtx) + #if swift(>=4.1) + symKey.deallocate() + decrypted.deallocate() + #else + symKey.deallocate(capacity: skey_len) + decrypted.deallocate(capacity: Int(encryptedData.count + 16)) + #endif + } + + // Get public key point from key + let pubk_point = EC_POINT_new(ec_group) + defer { + EC_POINT_free(pubk_point) + } + encryptedKey.withUnsafeBytes({ (pubk: UnsafeRawBufferPointer) in + let pubk_bn = BN_bin2bn(pubk.baseAddress?.assumingMemoryBound(to: UInt8.self), Int32(encryptedKey.count), nil) + let pubk_bn_ctx = BN_CTX_new() + BN_CTX_start(pubk_bn_ctx) + EC_POINT_bn2point(ec_group, pubk_bn, pubk_point, pubk_bn_ctx) + BN_CTX_end(pubk_bn_ctx) + BN_CTX_free(pubk_bn_ctx) + BN_clear_free(pubk_bn) + }) + + // calculate symmetric key + ECDH_compute_key(symKey, skey_len, pubk_point, key.nativeKey, nil) + // processedLen is the number of bytes that each EVP_DecryptUpdate/EVP_DecryptFinal decrypts. + // The sum of processedLen is the total size of the decrypted message (decMsgLen) + var processedLen: Int32 = 0 + var decMsgLen: Int32 = 0 + + // get aes key and iv using ANSI x9.63 Key Derivation Function + let symKeyData = Data(bytes: symKey, count: skey_len) + let counterData = Data([0x00, 0x00, 0x00, 0x01]) + let preHashKey = symKeyData + counterData + encryptedKey + let hashedKey = key.curve.digest(data: preHashKey) + let aesKey = [UInt8](hashedKey.subdata(in: 0 ..< 16)) + let iv = [UInt8](hashedKey.subdata(in: 16 ..< 32)) + + // Set the IV length to be 16 bytes. + // Set the envelope decryption algorithm as 128 bit AES-GCM. + guard EVP_DecryptInit_ex(rsaDecryptCtx, EVP_aes_128_gcm(), nil, nil, nil) == 1 else { + throw ECError.failedEvpInit + } + guard EVP_CIPHER_CTX_ctrl(rsaDecryptCtx, EVP_CTRL_GCM_SET_IVLEN, 16, nil) == 1, + // Set the AES key to be 16 bytes. + EVP_CIPHER_CTX_set_key_length(rsaDecryptCtx, 16) == 1 + else { + throw ECError.failedDecryptionAlgorithm + } + + // Set the envelope decryption context AES key and IV. + guard EVP_DecryptInit_ex(rsaDecryptCtx, nil, nil, aesKey, iv) == 1 else { + throw ECError.failedDecryptionAlgorithm + } + + // Decrypt the encrypted data using the symmetric key. + guard encryptedData.withUnsafeBytes({ (enc: UnsafeRawBufferPointer) -> Int32 in + return EVP_DecryptUpdate(rsaDecryptCtx, decrypted, &processedLen, enc.baseAddress?.assumingMemoryBound(to: UInt8.self), Int32(encryptedData.count)) + }) != 0 else { + throw ECError.failedDecryptionAlgorithm + } + decMsgLen += processedLen + // Verify the provided GCM tag. + guard tagData.withUnsafeMutableBytes({ (tag: UnsafeMutableRawBufferPointer) -> Int32 in + return EVP_CIPHER_CTX_ctrl(rsaDecryptCtx, EVP_CTRL_GCM_SET_TAG, 16, tag.baseAddress) + }) == 1 + else { + throw ECError.failedDecryptionAlgorithm + } + guard EVP_DecryptFinal_ex(rsaDecryptCtx, decrypted.advanced(by: Int(decMsgLen)), &processedLen) == 1 else { + throw ECError.failedDecryptionAlgorithm + } + decMsgLen += processedLen + // return the decrypted plaintext. + return Data(bytes: decrypted, count: Int(decMsgLen)) + #else + var error: Unmanaged? = nil + guard let eData = SecKeyCreateDecryptedData(key.nativeKey, + key.curve.encryptionAlgorithm, + self as CFData, + &error) + else { + guard let error = error?.takeRetainedValue() else { + throw ECError.failedEncryptionAlgorithm + } + throw error + } + return eData as Data + #endif + } +} diff --git a/Pods/BlueECC/Sources/CryptorECC/ECEncryptable.swift b/Pods/BlueECC/Sources/CryptorECC/ECEncryptable.swift new file mode 100644 index 0000000..2c6a515 --- /dev/null +++ b/Pods/BlueECC/Sources/CryptorECC/ECEncryptable.swift @@ -0,0 +1,177 @@ +// Copyright © 2019 IBM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) +import CommonCrypto +#elseif os(Linux) +import OpenSSL +#endif + +/// A protocol for encrypting an instance of some object to generate some encrypted data. +@available(macOS 10.13, iOS 11, watchOS 4.0, tvOS 11.0, *) +protocol ECEncryptable { + /// Encrypt the object using ECIES and produce some encrypted `Data`. + func encrypt(with: ECPublicKey) throws -> Data +} + +/// Extensions for encrypting or signing a `String` by converting it to UTF8 Data, then using the appropriate algorithm determined by the key's curve with the provided `ECPrivateKey` or `ECPublicKey`. +@available(macOS 10.13, iOS 11, watchOS 4.0, tvOS 11.0, *) +extension String: ECEncryptable { + + /// UTF8 encode the String to Data and encrypt it using the `ECPublicKey`. + /// This either uses the `SecKeyAlgorithm`: `eciesEncryptionStandardVariableIVX963SHA256AESGCM`, + /// or the equivalent OpenSSL implementation. + /// - Parameter ecPrivateKey: The elliptic curve private key. + /// - Returns: The encrypted Data. + /// - Throws: An ECError is the plaintext fails to be encrypted. + public func encrypt(with key: ECPublicKey) throws -> Data { + return try Data(self.utf8).encrypt(with: key) + } +} + +/// Extension for signing `Data` with an `ECPrivateKey` and the algorithm determined by the key's curve. +@available(macOS 10.13, iOS 11, watchOS 4.0, tvOS 11.0, *) +extension Data: ECEncryptable { + + /// Encrypt the data using the `ECPublicKey`. + /// This either uses the `SecKeyAlgorithm`: `eciesEncryptionStandardVariableIVX963SHA256AESGCM`, + /// or the equivalent OpenSSL implementation. + /// - Parameter ecPrivateKey: The elliptic curve private key. + /// - Returns: The encrypted Data. + /// - Throws: An ECError is the plaintext fails to be encrypted. + public func encrypt(with key: ECPublicKey) throws -> Data { + #if os(Linux) + // Compute symmetric key + let ec_key = EC_KEY_new_by_curve_name(key.curve.nativeCurve) + defer { + EC_KEY_free(ec_key) + } + EC_KEY_generate_key(ec_key) + let ec_group = EC_KEY_get0_group(ec_key) + let symKey_len = Int((EC_GROUP_get_degree(ec_group) + 7) / 8) + let symKey = UnsafeMutablePointer.allocate(capacity: symKey_len) + ECDH_compute_key(symKey, symKey_len, EC_KEY_get0_public_key(key.nativeKey), ec_key, nil) + + // get temp public key data + let pub_bn_ctx = BN_CTX_new() + BN_CTX_start(pub_bn_ctx) + let pub = EC_KEY_get0_public_key(ec_key) + let pub_bn = BN_new() + EC_POINT_point2bn(ec_group, pub, POINT_CONVERSION_UNCOMPRESSED, pub_bn, pub_bn_ctx) + let pubk = UnsafeMutablePointer.allocate(capacity: key.curve.keySize) + BN_bn2bin(pub_bn, pubk) + defer { + BN_CTX_end(pub_bn_ctx) + BN_CTX_free(pub_bn_ctx) + BN_clear_free(pub_bn) + #if swift(>=4.1) + pubk.deallocate() + symKey.deallocate() + #else + pubk.deallocate(capacity: key.curve.keySize) + symKey.deallocate(capacity: symKey_len) + #endif + } + + // get aes key and iv using ANSI x9.63 Key Derivation Function + let symKeyData = Data(bytes: symKey, count: symKey_len) + let counterData = Data([0x00, 0x00, 0x00, 0x01]) + let sharedInfo = Data(bytes: pubk, count: key.curve.keySize) + let preHashKey = symKeyData + counterData + sharedInfo + let hashedKey = key.curve.digest(data: preHashKey) + let aesKey = [UInt8](hashedKey.subdata(in: 0 ..< (hashedKey.count - 16))) + let iv = [UInt8](hashedKey.subdata(in: (hashedKey.count - 16) ..< hashedKey.count)) + + + // AES encrypt data + // Initialize encryption context + let rsaEncryptCtx = EVP_CIPHER_CTX_new_wrapper() + EVP_CIPHER_CTX_init_wrapper(rsaEncryptCtx) + + // Allocate encryption memory + let tag = UnsafeMutablePointer.allocate(capacity: 16) + let encrypted = UnsafeMutablePointer.allocate(capacity: self.count + 16) + defer { + // On completion deallocate the memory + EVP_CIPHER_CTX_reset_wrapper(rsaEncryptCtx) + EVP_CIPHER_CTX_free_wrapper(rsaEncryptCtx) + + #if swift(>=4.1) + tag.deallocate() + encrypted.deallocate() + #else + tag.deallocate(capacity: 16) + encrypted.deallocate(capacity: self.count + 16) + #endif + } + + var processedLength: Int32 = 0 + var encLength: Int32 = 0 + guard EVP_EncryptInit_ex(rsaEncryptCtx, EVP_aes_128_gcm(), nil, nil, nil) == 1 else { + throw ECError.failedEvpInit + } + // Set the IV length to be 16 to match Apple. + guard EVP_CIPHER_CTX_ctrl(rsaEncryptCtx, EVP_CTRL_GCM_SET_IVLEN, 16, nil) == 1 + // Add the aad to the encryption context. + // This is used in generating the GCM tag. We don't use this processedLength. + else { + throw ECError.failedEncryptionAlgorithm + } + guard EVP_EncryptInit_ex(rsaEncryptCtx, nil, nil, aesKey, iv) == 1 else { + throw ECError.failedDecryptionAlgorithm + } + // Encrypt the plaintext into encrypted using gcmAlgorithm with the random aes key and all 0 iv. + guard(self.withUnsafeBytes({ (plaintext: UnsafeRawBufferPointer) -> Int32 in + return EVP_EncryptUpdate(rsaEncryptCtx, encrypted, &processedLength, plaintext.baseAddress?.assumingMemoryBound(to: UInt8.self), Int32(self.count)) + })) == 1 else { + throw ECError.failedEncryptionAlgorithm + } + + encLength += processedLength + // Finalize the encryption so no more data will be added and generate the GCM tag. + guard EVP_EncryptFinal_ex(rsaEncryptCtx, encrypted.advanced(by: Int(encLength)), &processedLength) == 1 else { + throw ECError.failedEncryptionAlgorithm + } + + encLength += processedLength + // Get the 16 byte GCM tag. + guard EVP_CIPHER_CTX_ctrl(rsaEncryptCtx, EVP_CTRL_GCM_GET_TAG, 16, tag) == 1 else { + throw ECError.failedEncryptionAlgorithm + } + + // Construct the envelope by combining the encrypted AES key, the encrypted date and the GCM tag. + let ekFinal = Data(bytes: pubk, count: key.curve.keySize) + let cipher = Data(bytes: encrypted, count: Int(encLength)) + let tagFinal = Data(bytes: tag, count: 16) + return ekFinal + cipher + tagFinal + #else + var error: Unmanaged? = nil + guard let eData = SecKeyCreateEncryptedData(key.nativeKey, + key.curve.encryptionAlgorithm, + self as CFData, + &error) + else { + guard let error = error?.takeRetainedValue() else { + throw ECError.failedEncryptionAlgorithm + } + throw error + } + + return eData as Data + #endif + } +} diff --git a/Pods/BlueECC/Sources/CryptorECC/ECError.swift b/Pods/BlueECC/Sources/CryptorECC/ECError.swift new file mode 100644 index 0000000..605d391 --- /dev/null +++ b/Pods/BlueECC/Sources/CryptorECC/ECError.swift @@ -0,0 +1,72 @@ +/** + * Copyright IBM Corporation 2019 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +import Foundation + +/// A struct representing the different errors that can be thrown by BlueECC. +public struct ECError: Error, Equatable { + + /// A human readable description of the error. + public let localizedDescription: String + + private let internalError: InternalError + + private enum InternalError { + case invalidPEMString, unknownPEMHeader, failedBase64Encoding, failedASN1Decoding, unsupportedCurve, failedNativeKeyCreation, failedEvpInit, failedSigningAlgorithm, invalidRSLength, failedEncryptionAlgorithm, failedUTF8Decoding, failedDecryptionAlgorithm + } + + /// Error thrown when an invalid PEM String used to initialize a key. + public static let invalidPEMString = ECError(localizedDescription: "Input was not a valid PEM String", internalError: .invalidPEMString) + + /// Error thrown when the PEM header is not recognized. + public static let unknownPEMHeader = ECError(localizedDescription: "Input PEM header was not recognized", internalError: .unknownPEMHeader) + + /// Error thrown when a String fails to be Base64 encoded. + public static let failedBase64Encoding = ECError(localizedDescription: "Failed to base64 encode the String", internalError: .failedBase64Encoding) + + /// Error thrown when the ASN1 data could not be decoded to the expected structure. + public static let failedASN1Decoding = ECError(localizedDescription: "ASN1 data could not be decoded to expected structure", internalError: .failedASN1Decoding) + + /// Error thrown when the key's object identifier is for a curve that is not supported. + public static let unsupportedCurve = ECError(localizedDescription: "The key object identifier is for a non-supported curve", internalError: .unsupportedCurve) + + /// Error thrown when the key could not be converted to a native key (`SecKey` for Apple, `EC_KEY` for linux). + public static let failedNativeKeyCreation = ECError(localizedDescription: "The key data could not be converted to a native key", internalError: .failedNativeKeyCreation) + + /// Error thrown when the encryption envelope fails to initialize. + public static let failedEvpInit = ECError(localizedDescription: "Failed to initialize the signing envelope", internalError: .failedEvpInit) + + /// Error thrown when the signing algorithm could not create the signature. + public static let failedSigningAlgorithm = ECError(localizedDescription: "Signing algorithm failed to create the signature", internalError: .failedSigningAlgorithm) + + /// Error thrown when the provided R and S Data was not a valid length. + /// They must be the same length and either 32, 48 or 66 bytes (depending on the curve used). + public static let invalidRSLength = ECError(localizedDescription: "The provided R and S values were not a valid length", internalError: .invalidRSLength) + + /// Error thrown when the encryption algorithm could not encrypt the plaintext. + public static let failedEncryptionAlgorithm = ECError(localizedDescription: "Encryption algorithm failed to encrypt the data", internalError: .failedEncryptionAlgorithm) + + /// Error thrown when the decryption algorithm could not decrypt the encrypted Data. + public static let failedDecryptionAlgorithm = ECError(localizedDescription: "Decryption algorithm failed to decrypt the data", internalError: .failedDecryptionAlgorithm) + + /// Error thrown when the Data could not be decoded into a UTF8 String. + public static let failedUTF8Decoding = ECError(localizedDescription: "Data could not be decoded as a UTF8 String", internalError: .failedUTF8Decoding) + + /// Checks if ECSigningErrors are equal, required for Equatable protocol. + public static func == (lhs: ECError, rhs: ECError) -> Bool { + return lhs.internalError == rhs.internalError + } +} diff --git a/Pods/BlueECC/Sources/CryptorECC/ECPrivateKey.swift b/Pods/BlueECC/Sources/CryptorECC/ECPrivateKey.swift new file mode 100644 index 0000000..4091cf3 --- /dev/null +++ b/Pods/BlueECC/Sources/CryptorECC/ECPrivateKey.swift @@ -0,0 +1,474 @@ +// Copyright © 2019 IBM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +import Foundation +#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) +import CommonCrypto +#elseif os(Linux) +import OpenSSL +#endif + + +/** + Represents an elliptic curve private key. + Supported curves are: + - prime256v1 + - secp384r1 + - NID_secp521r1 + You can generate an elliptic curve Key using OpenSSL: + https://wiki.openssl.org/index.php/Command_Line_Elliptic_Curve_Operations#Generating_EC_Keys_and_Parameters + + ### Usage Example: + ```swift + let pemKey = """ + -----BEGIN EC PRIVATE KEY----- + MHcCAQEEIJX+87WJ7Gh19sohyZnhxZeXYNOcuGv4Q+8MLge4UkaZoAoGCCqGSM49 + AwEHoUQDQgAEikc5m6C2xtDWeeAeT18WElO37zvFOz8p4kAlhvgIHN23XIClNESg + KVmLgSSq2asqiwdrU5YHbcHFkgdABM1SPA== + -----END EC PRIVATE KEY----- + """ + let privateKey = try ECPrivateKey(key: pemKey) + let signature = "Hello world".sign(with: privateKey) + ``` + */ +@available(macOS 10.13, iOS 11, watchOS 4.0, tvOS 11.0, *) +public class ECPrivateKey { + /// A String description of the curve this key was generated from. + public let curveId: String + + /// The `EllipticCurve` this key was generated from. + public let curve: EllipticCurve + + /// The private key represented as a PEM String. + public let pemString: String + + #if os(Linux) + typealias NativeKey = OpaquePointer? + deinit { EC_KEY_free(.make(optional: self.nativeKey)) } + #else + typealias NativeKey = SecKey + #endif + let nativeKey: NativeKey + let pubKeyBytes: Data + private var stripped: Bool = false + + + /** + Initialize an ECPrivateKey from a PEM String. + This can either be from a `.p8` file with the header "-----BEGIN PRIVATE KEY-----", + or from a `.pem` file with the header "-----BEGIN EC PRIVATE KEY-----". + ### Usage Example: ### + ```swift + let privateKeyString = """ + -----BEGIN EC PRIVATE KEY----- + MHcCAQEEIJX+87WJ7Gh19sohyZnhxZeXYNOcuGv4Q+8MLge4UkaZoAoGCCqGSM49 + AwEHoUQDQgAEikc5m6C2xtDWeeAeT18WElO37zvFOz8p4kAlhvgIHN23XIClNESg + KVmLgSSq2asqiwdrU5YHbcHFkgdABM1SPA== + -----END EC PRIVATE KEY----- + """ + let key = try ECPrivateKey(key: privateKeyString) + ``` + - Parameter key: The elliptic curve private key as a PEM string. + - Returns: An ECPrivateKey. + - Throws: An ECError if the PEM string can't be decoded or is not a valid key. + */ + public convenience init(key: String) throws { + // Strip whitespace characters + let strippedKey = String(key.filter { !" \n\t\r".contains($0) }) + var pemComponents = strippedKey.components(separatedBy: "-----") + guard pemComponents.count >= 5 else { + throw ECError.invalidPEMString + } + // Remove any EC parameters since Curve is determined by OID + if pemComponents[1] == "BEGINECPARAMETERS" { + pemComponents.removeFirst(5) + guard pemComponents.count >= 5 else { + throw ECError.invalidPEMString + } + } + guard let der = Data(base64Encoded: pemComponents[2]) else { + throw ECError.failedBase64Encoding + } + if pemComponents[1] == "BEGINECPRIVATEKEY" { + try self.init(sec1DER: der) + } else if pemComponents[1] == "BEGINPRIVATEKEY" { + try self.init(pkcs8DER: der) + } else { + throw ECError.unknownPEMHeader + } + } + + /// Initialize an ECPrivateKey from a PKCS8 `.der` file data. + /// This is equivalent to a PEM String that has had the "-----BEGIN PRIVATE KEY-----" + /// header and footer stripped and been base64 encoded to ASN1 Data. + /// - Parameter pkcs8DER: The elliptic curve private key Data. + /// - Returns: An ECPrivateKey. + /// - Throws: An ECError if the Data can't be decoded or is not a valid key. + public init(pkcs8DER: Data) throws { + let (result, _) = ASN1.toASN1Element(data: pkcs8DER) + guard case let ASN1.ASN1Element.seq(elements: es) = result, + es.count > 2, + case let ASN1.ASN1Element.seq(elements: ids) = es[1], + ids.count > 1, + case let ASN1.ASN1Element.bytes(data: privateKeyID) = ids[1] + else { + throw ECError.failedASN1Decoding + } + self.curve = try EllipticCurve.objectToCurve(ObjectIdentifier: privateKeyID) + guard case let ASN1.ASN1Element.bytes(data: privateOctest) = es[2] else { + throw ECError.failedASN1Decoding + } + let (octest, _) = ASN1.toASN1Element(data: privateOctest) + guard case let ASN1.ASN1Element.seq(elements: seq) = octest, + seq.count >= 3, + case let ASN1.ASN1Element.bytes(data: privateKeyData) = seq[1] + else { + throw ECError.failedASN1Decoding + } + let publicKeyData: Data + if case let ASN1.ASN1Element.constructed(tag: 1, elem: publicElement) = seq[2], + case let ASN1.ASN1Element.bytes(data: pubKeyData) = publicElement + { + publicKeyData = pubKeyData + } else if seq.count >= 4, + case let ASN1.ASN1Element.constructed(tag: 1, elem: publicElement) = seq[3], + case let ASN1.ASN1Element.bytes(data: pubKeyData) = publicElement + { + publicKeyData = pubKeyData + } else { + throw ECError.failedASN1Decoding + } + let trimmedPubBytes = publicKeyData.drop(while: { $0 == 0x00}) + if trimmedPubBytes.count != publicKeyData.count { + stripped = true + } + self.nativeKey = try ECPrivateKey.bytesToNativeKey(privateKeyData: privateKeyData, + publicKeyData: trimmedPubBytes, + curve: curve) + let derData = ECPrivateKey.generateASN1(privateKey: privateKeyData, + publicKey: publicKeyData, + curve: curve) + self.pemString = ECPrivateKey.derToPrivatePEM(derData: derData) + self.pubKeyBytes = trimmedPubBytes + self.curveId = curve.description + } + + /// Initialize an ECPrivateKey from a SEC1 `.der` file data. + /// This is equivalent to a PEM String that has had the "-----BEGIN EC PRIVATE KEY-----" + /// header and footer stripped and been base64 encoded to ASN1 Data. + /// - Parameter sec1DER: The elliptic curve private key Data. + /// - Returns: An ECPrivateKey. + /// - Throws: An ECError if the Data can't be decoded or is not a valid key. + public init(sec1DER: Data) throws { + self.pemString = ECPrivateKey.derToPrivatePEM(derData: sec1DER) + let (result, _) = ASN1.toASN1Element(data: sec1DER) + guard case let ASN1.ASN1Element.seq(elements: seq) = result, + seq.count > 3, + case let ASN1.ASN1Element.constructed(tag: _, elem: objectElement) = seq[2], + case let ASN1.ASN1Element.bytes(data: objectId) = objectElement, + case let ASN1.ASN1Element.bytes(data: privateKeyData) = seq[1] + else { + throw ECError.failedASN1Decoding + } + self.curve = try EllipticCurve.objectToCurve(ObjectIdentifier: objectId) + guard case let ASN1.ASN1Element.constructed(tag: _, elem: publicElement) = seq[3], + case let ASN1.ASN1Element.bytes(data: publicKeyData) = publicElement + else { + throw ECError.failedASN1Decoding + } + let trimmedPubBytes = publicKeyData.drop(while: { $0 == 0x00}) + if trimmedPubBytes.count != publicKeyData.count { + stripped = true + } + self.nativeKey = try ECPrivateKey.bytesToNativeKey(privateKeyData: privateKeyData, + publicKeyData: trimmedPubBytes, + curve: curve) + self.pubKeyBytes = trimmedPubBytes + self.curveId = curve.description + } + + /// Initialize the `ECPublicKey`for this private key by extracting the public key bytes. + /// - Returns: An ECPublicKey. + /// - Throws: An ECError if the public key fails to be initialized from this private key. + public func extractPublicKey() throws -> ECPublicKey { + let keyHeader: Data + // Add the ASN1 header for the public key. The bytes have the following structure: + // SEQUENCE (2 elem) + // SEQUENCE (2 elem) + // OBJECT IDENTIFIER + // OBJECT IDENTIFIER + // BIT STRING (This is the `pubKeyBytes` added afterwards) + if self.curve == .prime256v1 { + keyHeader = Data([0x30, 0x59, + 0x30, 0x13, + 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01, + 0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07, 0x03, 0x42]) + } else if self.curve == .secp384r1 { + keyHeader = Data([0x30, 0x76, + 0x30, 0x10, + 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01, + 0x06, 0x05, 0x2B, 0x81, 0x04, 0x00, 0x22, 0x03, 0x62]) + } else if self.curve == .secp521r1 { + keyHeader = Data([0x30, 0x81, 0x9B, + 0x30, 0x10, + 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01, + 0x06, 0x05, 0x2B, 0x81, 0x04, 0x00, 0x23, 0x03, 0x81, 0x86]) + } else { + throw ECError.unsupportedCurve + } + // If we stripped the leading zero earlier, add it back here + var pubBytes = self.pubKeyBytes + if stripped { + pubBytes = Data(count: 1) + self.pubKeyBytes + } + return try ECPublicKey(der: keyHeader + pubBytes) + } + + /** + Make an new ECPrivate key from a supported `EllipticCurve`. + - Parameter for curve: The elliptic curve that is used to generate the key. + - Returns: An ECPrivateKey. + - Throws: An ECError if the key fails to be created. + */ + public static func make(for curve: EllipticCurve) throws -> ECPrivateKey { + return try ECPrivateKey(for: curve) + } + + /** + Initialise an new ECPrivate key from a supported `Curve` + - Parameter for curve: The elliptic curve that is used to generate the key. + - Returns: An ECPrivateKey. + - Throws: An ECError if the key fails to be created. + */ + private init(for curve: EllipticCurve) throws { + self.curve = curve + self.curveId = curve.description + self.stripped = true + #if os(Linux) + let ec_key = EC_KEY_new_by_curve_name(curve.nativeCurve) + EC_KEY_generate_key(ec_key) + self.nativeKey = ec_key + let pub_bn_ctx = BN_CTX_new() + BN_CTX_start(pub_bn_ctx) + let pub = EC_KEY_get0_public_key(ec_key) + let ec_group = EC_KEY_get0_group(ec_key) + let pub_bn = BN_new() + EC_POINT_point2bn(ec_group, pub, POINT_CONVERSION_UNCOMPRESSED, pub_bn, pub_bn_ctx) + let pubk = UnsafeMutablePointer.allocate(capacity: curve.keySize) + BN_bn2bin(pub_bn, pubk) + self.pubKeyBytes = Data(bytes: pubk, count: curve.keySize) + defer { + BN_CTX_end(pub_bn_ctx) + BN_CTX_free(pub_bn_ctx) + BN_clear_free(pub_bn) + #if swift(>=4.1) + pubk.deallocate() + #else + pubk.deallocate(capacity: curve.keySize) + #endif + } + #else + let kAsymmetricCryptoManagerKeyType = kSecAttrKeyTypeECSECPrimeRandom + let kAsymmetricCryptoManagerKeySize: Int + if curve == .prime256v1 { + kAsymmetricCryptoManagerKeySize = 256 + } else if curve == .secp384r1 { + kAsymmetricCryptoManagerKeySize = 384 + } else { + kAsymmetricCryptoManagerKeySize = 521 + } + // parameters + let parameters: [String: AnyObject] = [ + kSecAttrKeyType as String: kAsymmetricCryptoManagerKeyType, + kSecAttrKeySizeInBits as String: kAsymmetricCryptoManagerKeySize as AnyObject, + ] + var pubKey, privKey: SecKey? + let status = SecKeyGeneratePair(parameters as CFDictionary, &pubKey, &privKey) + guard status == 0, let newPubKey = pubKey, let newPrivKey = privKey else { + throw ECError.failedNativeKeyCreation + } + var error: Unmanaged? = nil + guard let pubBytes = SecKeyCopyExternalRepresentation(newPubKey, &error) else { + guard let error = error?.takeRetainedValue() else { + throw ECError.failedNativeKeyCreation + } + throw error + } + self.pubKeyBytes = pubBytes as Data + self.nativeKey = newPrivKey + #endif + self.pemString = try ECPrivateKey.decodeToPEM(nativeKey: self.nativeKey, curve: self.curve) + } + + /// Decode this ECPrivateKey to it's PEM format + private static func decodeToPEM(nativeKey: NativeKey, curve: EllipticCurve) throws -> String { + #if os(Linux) + let asn1Bio = BIO_new(BIO_s_mem()) + defer { BIO_free_all(asn1Bio) } + // The return value of i2d_ECPrivateKey_bio is supposed to be the DER size. + // However it is just returning 1 for success. + // Since the size is fixed we have just used the known values here. + guard i2d_ECPrivateKey_bio(asn1Bio, nativeKey) >= 0 else { + throw ECError.failedNativeKeyCreation + } + let asn1Size: Int32 + if curve == .prime256v1 { + asn1Size = 364 + } else if curve == .secp384r1 { + asn1Size = 510 + } else { + asn1Size = 673 + } + let asn1 = UnsafeMutablePointer.allocate(capacity: Int(asn1Size)) + let readLength = BIO_read(asn1Bio, asn1, asn1Size) + guard readLength > 0 else { + throw ECError.failedASN1Decoding + } + let asn1Data = Data(bytes: asn1, count: Int(readLength)) + // OpenSSL 1.1 already returns the shortened ANS1 so can return it straight away + if readLength < asn1Size - 1 { + return ECPrivateKey.derToPrivatePEM(derData: asn1Data) + } + // Otherwise need to decode ASN1 to get public and private key + #if swift(>=4.1) + asn1.deallocate() + #else + asn1.deallocate(capacity: Int(asn1Size)) + #endif + let (result, _) = ASN1.toASN1Element(data: asn1Data) + guard case let ASN1.ASN1Element.seq(elements: seq) = result, + seq.count > 3, + case let ASN1.ASN1Element.bytes(data: privateKeyData) = seq[1] + else { + throw ECError.failedASN1Decoding + } + guard case let ASN1.ASN1Element.constructed(tag: _, elem: publicElement) = seq[3], + case let ASN1.ASN1Element.bytes(data: publicKeyData) = publicElement + else { + throw ECError.failedASN1Decoding + } + #else + var error: Unmanaged? = nil + /* + From Apple docs: + For an elliptic curve private key, `SecKeyCopyExternalRepresentation` output is formatted as the public key concatenated with the big endian encoding of the secret scalar, or 04 || X || Y || K. + */ + guard let keyBytes = SecKeyCopyExternalRepresentation(nativeKey, &error) else { + guard let error = error?.takeRetainedValue() else { + throw ECError.failedNativeKeyCreation + } + throw error + } + let keyData = keyBytes as Data + let privateKeyData = keyData.dropFirst(curve.keySize) + let publicKeyData = Data(count: 1) + keyData.dropLast(keyData.count - curve.keySize) + #endif + let derData = ECPrivateKey.generateASN1(privateKey: privateKeyData, publicKey: publicKeyData, curve: curve) + return ECPrivateKey.derToPrivatePEM(derData: derData) + } + + private static func generateASN1(privateKey: Data, publicKey: Data, curve: EllipticCurve) -> Data { + var keyHeader: Data + // Add the ASN1 header for the private key. The bytes have the following structure: + // SEQUENCE (4 elem) + // INTEGER 1 + // OCTET STRING (32 byte) (This is the `privateKeyBytes`) + // [0] (1 elem) + // OBJECT IDENTIFIER + // [1] (1 elem) + // BIT STRING (This is the `pubKeyBytes`) + if curve == .prime256v1 { + keyHeader = Data([0x30, 0x77, + 0x02, 0x01, 0x01, + 0x04, 0x20]) + keyHeader += privateKey + keyHeader += Data([0xA0, + 0x0A, 0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07, + 0xA1, + 0x44, 0x03, 0x42]) + keyHeader += publicKey + } else if curve == .secp384r1 { + keyHeader = Data([0x30, 0x81, 0xA4, + 0x02, 0x01, 0x01, + 0x04, 0x30]) + keyHeader += privateKey + keyHeader += Data([0xA0, + 0x07, 0x06, 0x05, 0x2B, 0x81, 0x04, 0x00, 0x22, + 0xA1, + 0x64, 0x03, 0x62]) + keyHeader += publicKey + } else { + // 521 Private key can be 65 or 66 bytes long + if privateKey.count == 65 { + keyHeader = Data([0x30, 0x81, 0xDB, + 0x02, 0x01, 0x01, + 0x04, 0x41]) + } else { + keyHeader = Data([0x30, 0x81, 0xDC, + 0x02, 0x01, 0x01, + 0x04, 0x42]) + } + keyHeader += privateKey + keyHeader += Data([0xA0, + 0x07, 0x06, 0x05, 0x2B, 0x81, 0x04, 0x00, 0x23, + 0xA1, + 0x81, 0x89, 0x03, 0x81, 0x86]) + keyHeader += publicKey + } + return keyHeader + } + + private static func bytesToNativeKey(privateKeyData: Data, publicKeyData: Data, curve: EllipticCurve) throws -> NativeKey { + #if os(Linux) + let bigNum = BN_new() + defer { + BN_free(bigNum) + } + privateKeyData.withUnsafeBytes({ (privateKeyBytes: UnsafeRawBufferPointer) -> Void in + BN_bin2bn(privateKeyBytes.baseAddress?.assumingMemoryBound(to: UInt8.self), Int32(privateKeyData.count), bigNum) + }) + let ecKey = EC_KEY_new_by_curve_name(curve.nativeCurve) + guard EC_KEY_set_private_key(ecKey, bigNum) == 1 else { + EC_KEY_free(ecKey) + throw ECError.failedNativeKeyCreation + } + return ecKey + #else + let keyData = publicKeyData + privateKeyData + var error: Unmanaged? = nil + guard let secKey = SecKeyCreateWithData(keyData as CFData, + [kSecAttrKeyType: kSecAttrKeyTypeECSECPrimeRandom, + kSecAttrKeyClass: kSecAttrKeyClassPrivate] as CFDictionary, + &error) + else { + if let secError = error?.takeRetainedValue() { + throw secError + } else { + throw ECError.failedNativeKeyCreation + } + } + return secKey + #endif + } + + private static func derToPrivatePEM(derData: Data) -> String { + // First convert the DER data to a base64 string... + let base64String = derData.base64EncodedString() + // Split the string into strings of length 64. + let lines = base64String.split(to: 64) + // Join those lines with a new line... + let joinedLines = lines.joined(separator: "\n") + return "-----BEGIN EC PRIVATE KEY-----\n" + joinedLines + "\n-----END EC PRIVATE KEY-----" + } +} diff --git a/Pods/BlueECC/Sources/CryptorECC/ECPublicKey.swift b/Pods/BlueECC/Sources/CryptorECC/ECPublicKey.swift new file mode 100644 index 0000000..4189b00 --- /dev/null +++ b/Pods/BlueECC/Sources/CryptorECC/ECPublicKey.swift @@ -0,0 +1,167 @@ +// Copyright © 2019 IBM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation +#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) +import CommonCrypto +#elseif os(Linux) +import OpenSSL +#endif + +/** + Represents an elliptic curve public key. + Supported curves are: + - prime256v1 + - secp384r1 + - NID_secp521r1 + You can generate an elliptic curve Key using OpenSSL: + https://wiki.openssl.org/index.php/Command_Line_Elliptic_Curve_Operations#Generating_EC_Keys_and_Parameters + + ### Usage Example: + ```swift + let pemKey = """ + -----BEGIN PUBLIC KEY----- + MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEikc5m6C2xtDWeeAeT18WElO37zvF + Oz8p4kAlhvgIHN23XIClNESgKVmLgSSq2asqiwdrU5YHbcHFkgdABM1SPA== + -----END PUBLIC KEY----- + """ + let publicKey = try ECPublicKey(key: pemKey) + + let base64Sig = "MEYCIQCvgBLn+tQoBDBR3D2G3485GloYGNxuk6PqR4qjr5GDqAIhAKNvsqvesVBD/MLub/KAyzLLNGtUZyQDxYZj/4vmHwWF" + let signature = try ECSignature(asn1: Data(base64Encoded: base64Sig)) + + let verified = signature.verify(plaintext: "Hello world", using: publicKey) + ``` + */ +@available(macOS 10.13, iOS 11, watchOS 4.0, tvOS 11.0, *) +public class ECPublicKey { + /// A String description of the curve this key was generated from. + public let curveId: String + + /// The `EllipticCurve` this key was generated from. + public let curve: EllipticCurve + #if os(Linux) + typealias NativeKey = OpaquePointer? + let pubKeyBytes: Data + deinit { EC_KEY_free(.make(optional: self.nativeKey)) } + #else + typealias NativeKey = SecKey + #endif + let nativeKey: NativeKey + + /// The public key represented as a PEM String. + public let pemString: String + + /** + Initialize an ECPublicKey from a `.pem` file format. + ### Usage Example: ### + ```swift + let publicKeyString = """ + -----BEGIN PUBLIC KEY----- + MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEikc5m6C2xtDWeeAeT18WElO37zvF + Oz8p4kAlhvgIHN23XIClNESgKVmLgSSq2asqiwdrU5YHbcHFkgdABM1SPA== + -----END PUBLIC KEY----- + """ + let pemKey = try ECPublicKey(key: publicKeyString) + ``` + - Parameter key: The elliptic curve public key as a PEM string. + - Returns: An ECPublicKey. + - Throws: An ECError if the PEM string can't be decoded or is not a valid key. + */ + public convenience init(key: String) throws { + let strippedKey = String(key.filter { !" \n\t\r".contains($0) }) + let pemComponents = strippedKey.components(separatedBy: "-----") + guard pemComponents.count == 5 else { + throw ECError.invalidPEMString + } + guard let der = Data(base64Encoded: pemComponents[2]) else { + throw ECError.invalidPEMString + } + if pemComponents[1] == "BEGINPUBLICKEY" { + try self.init(der: der) + } else { + throw ECError.unknownPEMHeader + } + } + + /// Initialize an ECPublicKey from `.der` file data. + /// This is equivalent to a PEM String that has had the "-----BEGIN PUBLIC KEY-----" + /// header and footer stripped and been base64 encoded to ASN1 Data. + /// - Parameter der: The elliptic curve public key Data. + /// - Returns: An ECPublicKey. + /// - Throws: An ECError if the Data can't be decoded or is not a valid key. + public init(der: Data) throws { + pemString = ECPublicKey.derToPublicPEM(derData: der) + let (result, _) = ASN1.toASN1Element(data: der) + guard case let ASN1.ASN1Element.seq(elements: seq) = result, + seq.count > 1, + case let ASN1.ASN1Element.seq(elements: ids) = seq[0], + ids.count > 1, + case let ASN1.ASN1Element.bytes(data: privateKeyID) = ids[1], + case let ASN1.ASN1Element.bytes(data: publicKeyData) = seq[1] + else { + throw ECError.failedASN1Decoding + } + self.curve = try EllipticCurve.objectToCurve(ObjectIdentifier: privateKeyID) + self.curveId = curve.description + let keyData = publicKeyData.drop(while: { $0 == 0x00}) + #if os(Linux) + self.pubKeyBytes = keyData + let bigNum = BN_new() + defer { + BN_free(bigNum) + } + publicKeyData.withUnsafeBytes({ (pubKeyBytes: UnsafeRawBufferPointer) -> Void in + BN_bin2bn(pubKeyBytes.baseAddress?.assumingMemoryBound(to: UInt8.self), Int32(publicKeyData.count), bigNum) + }) + let ecKey = EC_KEY_new_by_curve_name(curve.nativeCurve) + let ecGroup = EC_KEY_get0_group(ecKey) + let ecPoint = EC_POINT_new(ecGroup) + defer { + EC_POINT_free(ecPoint) + } + EC_POINT_bn2point(ecGroup, bigNum, ecPoint, nil) + guard EC_KEY_set_public_key(ecKey, ecPoint) == 1 else { + EC_KEY_free(ecKey) + throw ECError.failedNativeKeyCreation + } + self.nativeKey = ecKey + #else + var error: Unmanaged? = nil + guard let secKey = SecKeyCreateWithData(keyData as CFData, + [kSecAttrKeyType: kSecAttrKeyTypeECSECPrimeRandom, + kSecAttrKeyClass: kSecAttrKeyClassPublic] as CFDictionary, + &error) + else { + if let secError = error?.takeRetainedValue() { + throw secError + } else { + throw ECError.failedNativeKeyCreation + } + } + self.nativeKey = secKey + #endif + } + + private static func derToPublicPEM(derData: Data) -> String { + // First convert the DER data to a base64 string... + let base64String = derData.base64EncodedString() + // Split the string into strings of length 64. + let lines = base64String.split(to: 64) + // Join those lines with a new line... + let joinedLines = lines.joined(separator: "\n") + return "-----BEGIN PUBLIC KEY-----\n" + joinedLines + "\n-----END PUBLIC KEY-----" + } +} diff --git a/Pods/BlueECC/Sources/CryptorECC/ECSignable.swift b/Pods/BlueECC/Sources/CryptorECC/ECSignable.swift new file mode 100644 index 0000000..194ca48 --- /dev/null +++ b/Pods/BlueECC/Sources/CryptorECC/ECSignable.swift @@ -0,0 +1,105 @@ +// Copyright © 2019 IBM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) +import CommonCrypto +#elseif os(Linux) +import OpenSSL +#endif + +/// A protocol for signing an instance of some object to generate an `ECSignature`. +@available(macOS 10.13, iOS 11, watchOS 4.0, tvOS 11.0, *) +protocol ECSignable { + /// Sign the object using ECDSA and produce an `ECSignature`. + func sign(with: ECPrivateKey) throws -> ECSignature +} + +@available(macOS 10.13, iOS 11, watchOS 4.0, tvOS 11.0, *) +extension String: ECSignable { + /// UTF8 encode the String to Data and sign it using the `ECPrivateKey`. + /// The Data is signed using ECDSA with either SHA256, SHA384 or SHA512, depending on the key's curve. + /// - Parameter with key: The elliptic curve private key. + /// - Returns: An ECSignature on failure. + /// - Throws: An ECError if a valid signature is unable to be created. + public func sign(with key: ECPrivateKey) throws -> ECSignature { + return try Data(self.utf8).sign(with: key) + } +} + +@available(macOS 10.13, iOS 11, watchOS 4.0, tvOS 11.0, *) +extension Data: ECSignable { + /// Sign the plaintext data using the provided `ECPrivateKey`. + /// The Data is signed using ECDSA with either SHA256, SHA384 or SHA512, depending on the key's curve. + /// - Parameter with key: The elliptic curve private key. + /// - Returns: An ECSignature on failure. + /// - Throws: An ECError if a valid signature is unable to be created. + public func sign(with key: ECPrivateKey) throws -> ECSignature { + #if os(Linux) + let md_ctx = EVP_MD_CTX_new_wrapper() + let evp_key = EVP_PKEY_new() + defer { + EVP_PKEY_free(evp_key) + EVP_MD_CTX_free_wrapper(md_ctx) + } + guard EVP_PKEY_set1_EC_KEY(evp_key, .make(optional: key.nativeKey)) == 1 else { + throw ECError.failedNativeKeyCreation + } + + guard EVP_DigestSignInit(md_ctx, nil, .make(optional: key.curve.signingAlgorithm), nil, evp_key) == 1 else { + throw ECError.failedEvpInit + } + + guard self.withUnsafeBytes({ (message: UnsafeRawBufferPointer) -> Int32 in + return EVP_DigestUpdate(md_ctx, message.baseAddress?.assumingMemoryBound(to: UInt8.self), self.count) + }) == 1 else { + throw ECError.failedSigningAlgorithm + } + var sig_len: Int = 0 + EVP_DigestSignFinal(md_ctx, nil, &sig_len) + let sig = UnsafeMutablePointer.allocate(capacity: sig_len) + defer { + #if swift(>=4.1) + sig.deallocate() + #else + sig.deallocate(capacity: sig_len) + #endif + } + guard EVP_DigestSignFinal(md_ctx, sig, &sig_len) == 1 else { + throw ECError.failedSigningAlgorithm + } + return try ECSignature(asn1: Data(bytes: sig, count: sig_len)) + #else + let hash = key.curve.digest(data: self) + + // Memory storage for error from SecKeyCreateSignature + var error: Unmanaged? = nil + // cfSignature is CFData that is ANS1 encoded as a sequence of two UInts (r and s) + guard let cfSignature = SecKeyCreateSignature(key.nativeKey, + key.curve.signingAlgorithm, + hash as CFData, + &error) + else { + if let thrownError = error?.takeRetainedValue() { + throw thrownError + } else { + throw ECError.failedSigningAlgorithm + } + } + return try ECSignature(asn1: cfSignature as Data) + #endif + } +} diff --git a/Pods/BlueECC/Sources/CryptorECC/ECSignature.swift b/Pods/BlueECC/Sources/CryptorECC/ECSignature.swift new file mode 100644 index 0000000..143427b --- /dev/null +++ b/Pods/BlueECC/Sources/CryptorECC/ECSignature.swift @@ -0,0 +1,202 @@ +// Copyright © 2019 IBM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) +import CommonCrypto +#elseif os(Linux) +import OpenSSL +#endif + +/// The signature produced by applying an Elliptic Curve Digital Signature Algorithm to some Plaintext data. +/// It consists of two binary unsigned integers, `r` and `s`. +@available(macOS 10.13, iOS 11, watchOS 4.0, tvOS 11.0, *) +public struct ECSignature { + + // MARK: Signature Values + + /// The r value of the signature. + /// The size of the signature data depends on the Secure Hash Algorithm used; it will be 32 bytes of data for SHA256, 48 bytes for SHA384, or 66 bytes for SHA512. + public let r: Data + + /// The s value of the signature. + /// The size of the signature data depends on the Secure Hash Algorithm used; it will be 32 bytes of data for SHA256, 48 bytes for SHA384, or 66 bytes for SHA512. + public let s: Data + + /// The r and s values of the signature encoded into an ASN1 sequence. + public let asn1: Data + + // MARK: Initializers + + /// Initialize an ECSignature by providing the r and s values. + /// These must be the same length and either 32, 48 or 66 bytes (Depending on the curve used). + /// - Parameter r: The r value of the signature as raw data. + /// - Parameter s: The s value of the signature as raw data. + /// - Returns: A new instance of `ECSignature`. + /// - Throws: An ECError if the r or s values are not a valid length. + public init(r: Data, s: Data) throws { + let asn1 = try ECSignature.rsSigToASN1(r: r, s: s) + self.r = r + self.s = s + self.asn1 = asn1 + } + + /// Initialize an ECSignature by providing an ASN1 encoded sequence containing the r and s values. + /// - Parameter asn1: The r and s values of the signature encoded as an ASN1 sequence. + /// - Returns: A new instance of `ECSignature`. + /// - Throws: An ECError if the ASN1 data can't be decoded. + public init(asn1: Data) throws { + self.asn1 = asn1 + let (r,s) = try ECSignature.asn1ToRSSig(asn1: asn1) + self.r = r + self.s = s + } + + // MARK: Verify Signature + + /// Verify the signature for a given String using the provided public key. + /// The Data is verified using ECDSA with either SHA256, SHA384 or SHA512, depending on the key's curve. + /// - Parameter plaintext: The String that was originally signed to produce the signature. + /// - Parameter using ecPublicKey: The ECPublicKey that will be used to verify the plaintext. + /// - Returns: true if the plaintext is valid for the provided signature. Otherwise it returns false. + public func verify(plaintext: String, using ecPublicKey: ECPublicKey) -> Bool { + let plainTextData = Data(plaintext.utf8) + return verify(plaintext: plainTextData, using: ecPublicKey) + } + + /// Verify the signature for the given Data using the provided public key. + /// The Data is verified using ECDSA with either SHA256, SHA384 or SHA512, depending on the key's curve. + /// - Parameter plaintext: The Data that was originally signed to produce the signature. + /// - Parameter using ecPublicKey: The ECPublicKey that will be used to verify the plaintext. + /// - Returns: true if the plaintext is valid for the provided signature. Otherwise it returns false. + public func verify(plaintext: Data, using ecPublicKey: ECPublicKey) -> Bool { + #if os(Linux) + let md_ctx = EVP_MD_CTX_new_wrapper() + let evp_key = EVP_PKEY_new() + defer { + EVP_PKEY_free(evp_key) + EVP_MD_CTX_free_wrapper(md_ctx) + } + guard EVP_PKEY_set1_EC_KEY(evp_key, .make(optional: ecPublicKey.nativeKey)) == 1 else { + return false + } + + EVP_DigestVerifyInit(md_ctx, nil, .make(optional: ecPublicKey.curve.signingAlgorithm), nil, evp_key) + guard plaintext.withUnsafeBytes({ (message: UnsafeRawBufferPointer) -> Int32 in + return EVP_DigestUpdate(md_ctx, message.baseAddress?.assumingMemoryBound(to: UInt8.self), plaintext.count) + }) == 1 else { + return false + } + let rc = self.asn1.withUnsafeBytes({ (sig: UnsafeRawBufferPointer) -> Int32 in + return SSL_EVP_digestVerifyFinal_wrapper(md_ctx, sig.baseAddress?.assumingMemoryBound(to: UInt8.self), self.asn1.count) + }) + return rc == 1 + #else + let hash = ecPublicKey.curve.digest(data: plaintext) + + // Memory storage for error from SecKeyVerifySignature + var error: Unmanaged? = nil + return SecKeyVerifySignature(ecPublicKey.nativeKey, + ecPublicKey.curve.signingAlgorithm, + hash as CFData, + self.asn1 as CFData, + &error) + #endif + } + // ASN1 encode the r and s values. + static func rsSigToASN1(r: Data, s: Data) throws -> Data { + + guard r.count == s.count, r.count == 32 || r.count == 48 || r.count == 66 else { + throw ECError.invalidRSLength + } + // Convert r,s signature to ASN1 for SecKeyVerifySignature + var asnSignature = Data() + // r value is first 32 bytes + var rSig = r + // If first bit is 1, add a 00 byte to mark it as positive for ASN1 + if rSig[0] == 0 { + rSig = rSig.advanced(by: 1) + } + if rSig[0].leadingZeroBitCount == 0 { + rSig = Data(count: 1) + rSig + } + // r value is last 32 bytes + var sSig = s + // If first bit is 1, add a 00 byte to mark it as positive for ASN1 + if sSig[0] == 0 { + sSig = sSig.advanced(by: 1) + } + if sSig[0].leadingZeroBitCount == 0 { + sSig = Data(count: 1) + sSig + } + // Count Byte lengths for ASN1 length bytes + let rLengthByte = UInt8(rSig.count) + let sLengthByte = UInt8(sSig.count) + // total bytes is r + s + rLengthByte + sLengthByte byte + Integer marking bytes + let tLengthByte = rLengthByte + sLengthByte + 4 + // 0x30 means sequence, 0x02 means Integer + if tLengthByte > 127 { + asnSignature.append(contentsOf: [0x30, 0x81, tLengthByte]) + } else { + asnSignature.append(contentsOf: [0x30, tLengthByte]) + } + asnSignature.append(contentsOf: [0x02, rLengthByte]) + asnSignature.append(rSig) + asnSignature.append(contentsOf: [0x02, sLengthByte]) + asnSignature.append(sSig) + return asnSignature + } + + static func asn1ToRSSig(asn1: Data) throws -> (Data, Data) { + + let signatureLength: Int + if asn1.count < 96 { + signatureLength = 64 + } else if asn1.count < 132 { + signatureLength = 96 + } else { + signatureLength = 132 + } + + // Parse ASN into just r,s data as defined in: + // https://tools.ietf.org/html/rfc7518#section-3.4 + let (asnSig, _) = ASN1.toASN1Element(data: asn1) + guard case let ASN1.ASN1Element.seq(elements: seq) = asnSig, + seq.count >= 2, + case let ASN1.ASN1Element.bytes(data: rData) = seq[0], + case let ASN1.ASN1Element.bytes(data: sData) = seq[1] + else { + throw ECError.failedASN1Decoding + } + // ASN adds 00 bytes in front of negative Int to mark it as positive. + // These must be removed to make r,a a valid EC signature + let trimmedRData: Data + let trimmedSData: Data + let rExtra = rData.count - signatureLength/2 + if rExtra < 0 { + trimmedRData = Data(count: 1) + rData + } else { + trimmedRData = rData.dropFirst(rExtra) + } + let sExtra = sData.count - signatureLength/2 + if sExtra < 0 { + trimmedSData = Data(count: 1) + sData + } else { + trimmedSData = sData.dropFirst(sExtra) + } + return (trimmedRData, trimmedSData) + } +} diff --git a/Pods/BlueECC/Sources/CryptorECC/EllipticCurve.swift b/Pods/BlueECC/Sources/CryptorECC/EllipticCurve.swift new file mode 100644 index 0000000..004cc7e --- /dev/null +++ b/Pods/BlueECC/Sources/CryptorECC/EllipticCurve.swift @@ -0,0 +1,172 @@ +// Copyright © 2019 IBM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + import CommonCrypto +#elseif os(Linux) + import OpenSSL +#endif + +/// An extensible list of elliptic curves supported by this repository. +@available(macOS 10.13, iOS 11, watchOS 4.0, tvOS 11.0, *) +public struct EllipticCurve: Equatable, CustomStringConvertible { + + private let internalRepresentation: InternalRepresentation + + // enum for faster comparisons + private enum InternalRepresentation: String { + case prime256v1, secp384r1, secp521r1 + } + + /// A prime256v1 curve. + public static let prime256v1 = EllipticCurve.p256 + + /// A secp384r1 curve. + public static let secp384r1 = EllipticCurve.p384 + + /// A secp521r1 curve. + public static let secp521r1 = EllipticCurve.p521 + + /// Checks if two Curves are equal, required for Equatable protocol. + public static func == (lhs: EllipticCurve, rhs: EllipticCurve) -> Bool { + return lhs.internalRepresentation == rhs.internalRepresentation + } + + /// A String description of the Curve. Required for CustomStringConvertible protocol. + public var description: String { + return internalRepresentation.rawValue + } + + #if os(Linux) + typealias CC_LONG = size_t + let signingAlgorithm: OpaquePointer? + let nativeCurve: Int32 + let hashEngine = SHA256 + let hashLength = CC_LONG(SHA256_DIGEST_LENGTH) + #else + let signingAlgorithm: SecKeyAlgorithm + let encryptionAlgorithm = SecKeyAlgorithm.eciesEncryptionStandardVariableIVX963SHA256AESGCM + let hashEngine: (_ data: UnsafeRawPointer?, _ len: CC_LONG, _ md: UnsafeMutablePointer?) -> UnsafeMutablePointer? + let hashLength: CC_LONG + #endif + let keySize: Int + + #if os(Linux) + /// Secure Hash Algorithm 2 256-bit + static let p256 = EllipticCurve(internalRepresentation: .prime256v1, + signingAlgorithm: .init(EVP_sha256()), + nativeCurve: NID_X9_62_prime256v1, + keySize: 65) + + /// Secure Hash Algorithm 2 384-bit + static let p384 = EllipticCurve(internalRepresentation: .secp384r1, + signingAlgorithm: .init(EVP_sha384()), + nativeCurve: NID_secp384r1, + keySize: 97) + + /// Secure Hash Algorithm 512-bit + static let p521 = EllipticCurve(internalRepresentation: .secp521r1, + signingAlgorithm: .init(EVP_sha512()), + nativeCurve: NID_secp521r1, + keySize: 133) + #else + /// Secure Hash Algorithm 2 256-bit + static let p256 = EllipticCurve(internalRepresentation: .prime256v1, + signingAlgorithm: .ecdsaSignatureDigestX962SHA256, + hashEngine: CC_SHA256, + hashLength: CC_LONG(CC_SHA256_DIGEST_LENGTH), + keySize: 65) + + /// Secure Hash Algorithm 2 384-bit + static let p384 = EllipticCurve(internalRepresentation: .secp384r1, + signingAlgorithm: .ecdsaSignatureDigestX962SHA384, + hashEngine: CC_SHA384, + hashLength: CC_LONG(CC_SHA384_DIGEST_LENGTH), + keySize: 97) + + /// Secure Hash Algorithm 512-bit + static let p521 = EllipticCurve(internalRepresentation: .secp521r1, + signingAlgorithm: .ecdsaSignatureDigestX962SHA512, + hashEngine: CC_SHA512, + hashLength: CC_LONG(CC_SHA512_DIGEST_LENGTH), + keySize: 133) + #endif + + // Select the ECAlgorithm based on the object identifier (OID) extracted from the EC key. + static func objectToCurve(ObjectIdentifier: Data) throws -> EllipticCurve { + + if [UInt8](ObjectIdentifier) == [0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07] { + // p-256 (e.g: prime256v1, secp256r1) private key + return .prime256v1 + } else if [UInt8](ObjectIdentifier) == [0x2B, 0x81, 0x04, 0x00, 0x22] { + // p-384 (e.g: secp384r1) private key + return .secp384r1 + } else if [UInt8](ObjectIdentifier) == [0x2B, 0x81, 0x04, 0x00, 0x23] { + // p-521 (e.g: secp521r1) private key + return .secp521r1 + } else { + throw ECError.unsupportedCurve + } + } + + /// Return a digest of the data based on the hashEngine. + func digest(data: Data) -> Data { + + var hash = [UInt8](repeating: 0, count: Int(self.hashLength)) + data.withUnsafeBytes { ptr in + guard let baseAddress = ptr.baseAddress else { return } + _ = self.hashEngine(baseAddress.assumingMemoryBound(to: UInt8.self), CC_LONG(data.count), &hash) + } + return Data(hash) + } +} + +extension String { + + /// + /// Split a string to a specified length. + /// + /// - Parameters: + /// - length: Length of each split string. + /// + /// - Returns: `[String]` containing each string. + /// + func split(to length: Int) -> [String] { + + var result = [String]() + var collectedCharacters = [Character]() + collectedCharacters.reserveCapacity(length) + var count = 0 + + for character in self { + collectedCharacters.append(character) + count += 1 + if count == length { + // Reached the desired length + count = 0 + result.append(String(collectedCharacters)) + collectedCharacters.removeAll(keepingCapacity: true) + } + } + + // Append the remainder + if !collectedCharacters.isEmpty { + result.append(String(collectedCharacters)) + } + return result + } +} diff --git a/Pods/BlueECC/Sources/CryptorECC/SSLPointerTricks.swift b/Pods/BlueECC/Sources/CryptorECC/SSLPointerTricks.swift new file mode 100644 index 0000000..d986598 --- /dev/null +++ b/Pods/BlueECC/Sources/CryptorECC/SSLPointerTricks.swift @@ -0,0 +1,100 @@ +//===----------------------------------------------------------------------===// +// +// This source file is taken from SwiftNIO open source project +// +// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors +// Licensed under Apache License v2.0 +// +//===----------------------------------------------------------------------===// + +// MARK:- Awful code begins here +// Hello dear reader. Let me explain what we're doing here. +// +// From OpenSSL 1.0 to OpenSSL 1.1 one of the major breaking changes was the so-called +// "great opaquifiying". Essentially, OpenSSL took all of its public structures and made +// them opaque, such that they cannot be introspected from client code. This is a great +// forward step, and brings them more in line with modern C library practices. +// +// However, it's an *enormous* inconvenience from Swift code. This is because the Swift +// translation of the C type `SSL_CTX *` changed from `UnsafeMutablePointer` to +// `OpaquePointer`. +// +// This change exists for reasonable enough reasons in Swift land (see +// https://forums.swift.org/t/opaque-pointers-in-swift/6875 for a discussion), but +// nonetheless causes enormous problems in our codebase. +// +// Our cheap way out is to make everything an OpaquePointer, and then provide initializers +// between OpaquePointer and the typed pointers. This allows us to tolerate either pointer +// type in our Swift code by bridging them over to OpaquePointer and back, and lets the +// compiler worry about how exactly to make that work. +// +// Now, in fact, Swift already has initializers between the pointer types. What it does +// not have is self-initializers: the ability to create an `OpaquePointer` from an `OpaquePointer`, +// or an `UnsafePointer` from an `UnsafePointer`. We add those two initializers here. +// We also add a special "make" function that exists to handle the special case of optional pointer +// values, which we mostly encounter in the ALPN callbacks. +// +// The *downside* of this approach is that we totally break the pointer type system. It becomes +// trivially possible to alias a pointer of type T to type U through two calls to init. This +// is not a thing we want to widely promote. For this reason, these extensions are hidden in +// this file, where we can laugh and jeer at them and generally make them feel bad about +// themselves. +// +// Hopefully, in time, these extensions can be removed. + +extension UnsafePointer { + init(_ ptr: UnsafePointer) { + self = ptr + } + + static func make(optional ptr: UnsafePointer?) -> UnsafePointer? { + return ptr.map(UnsafePointer.init) + } + + static func make(optional ptr: OpaquePointer?) -> UnsafePointer? { + return ptr.map(UnsafePointer.init) + } +} + +extension UnsafeMutablePointer { + init(_ ptr: UnsafeMutableRawPointer) { + let x = UnsafeMutablePointer(bitPattern: UInt(bitPattern: ptr))! + self = x + } + + static func make(optional ptr: UnsafeMutablePointer?) -> UnsafeMutablePointer? { + return ptr.map(UnsafeMutablePointer.init) + } + + static func make(optional ptr: UnsafeMutableRawPointer?) -> UnsafeMutablePointer? { + return ptr.map(UnsafeMutablePointer.init) + } + + static func make(optional ptr: OpaquePointer?) -> UnsafeMutablePointer? { + return ptr.map(UnsafeMutablePointer.init) + } +} + +extension UnsafeMutableRawPointer { + static func make(optional ptr: OpaquePointer?) -> UnsafeMutableRawPointer? { + return ptr.map(UnsafeMutableRawPointer.init) + } +} + +extension OpaquePointer { + init(_ ptr: OpaquePointer) { + self = ptr + } + + static func make(optional ptr: OpaquePointer?) -> OpaquePointer? { + return ptr.map(OpaquePointer.init) + } + + static func make(optional ptr: UnsafeMutableRawPointer?) -> OpaquePointer? { + return ptr.map(OpaquePointer.init) + } + + static func make(optional ptr: UnsafeMutablePointer?) -> OpaquePointer? { + return ptr.map(OpaquePointer.init) + } +} diff --git a/Pods/BlueRSA/LICENSE b/Pods/BlueRSA/LICENSE new file mode 100644 index 0000000..d9a10c0 --- /dev/null +++ b/Pods/BlueRSA/LICENSE @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/Pods/BlueRSA/README.md b/Pods/BlueRSA/README.md new file mode 100644 index 0000000..8cfe513 --- /dev/null +++ b/Pods/BlueRSA/README.md @@ -0,0 +1,318 @@ +

+ + APIDoc + + + Build Status - Master + + macOS + iOS + Linux + Apache 2 + + Slack Status + +

+ +# BlueRSA + +Swift cross-platform RSA wrapper library for RSA encryption and signing. Works on supported Apple platforms (using Security framework). Linux (using OpenSSL) is working but is still somewhat of a work in progress. + +## Contents + +* CryptorRSA: Utility functions for RSA encryption and signing. Pure Swift + +## Prerequisites + +### Swift + +* Swift Open Source `swift-4.0.0-RELEASE` toolchain (**Minimum REQUIRED for latest release**) +* Swift Open Source `swift-4.2-RELEASE` toolchain (**Recommended**) +* Swift toolchain included in *Xcode Version 10.0 (10A255) or higher*. + +### macOS + +* macOS 10.12.0 (*Sierra*) or higher +* Xcode Version 9.0 (9A325) or higher using the included toolchain (**Minimum REQUIRED for latest release**). +* Xcode Version 10.0 (10A255) or higher using the included toolchain (**Recommended**). + +### iOS + +* iOS 10.3 or higher +* Xcode Version 9.0 (9A325) or higher using the included toolchain (**Minimum REQUIRED for latest release**). +* Xcode Version 10.0 (10A255) or higher using the included toolchain (**Recommended**). + +### Linux + +* Ubuntu 16.04 (or 16.10 but only tested on 16.04) and 18.04. +* One of the Swift Open Source toolchain listed above. +* OpenSSL is provided by the distribution. **Note:** 1.0.x, 1.1.x and later releases of OpenSSL are supported. +* The appropriate **libssl-dev** package is required to be installed when building. + + +## Build + +To build CryptorRSA from the command line: + +``` +% cd +% swift build +``` + +## Testing + +To run the supplied unit tests for **CryptorRSA** from the command line: + +``` +% cd +% swift build +% swift test + +``` + +## Using CryptorRSA + +### Including in your project + +#### Swift Package Manager + +To include BlueRSA into a Swift Package Manager package, add it to the `dependencies` attribute defined in your `Package.swift` file. You can select the version using the `majorVersion` and `minor` parameters. For example: +``` + dependencies: [ + .Package(url: "https://github.com/IBM-Swift/BlueRSA", majorVersion: , minor: ) + ] +``` + +#### Carthage + +To include BlueRSA in a project using Carthage, add a line to your `Cartfile` with the GitHub organization and project names and version. For example: +``` + github "IBM-Swift/BlueRSA" ~> . +``` + +### Before starting + +The first you need to do is import the CryptorRSA framework. This is done by the following: + +``` +import CryptorRSA +``` + +### Data Types + +BlueRSA supports the following *major* data types: + +* Key Handling + - `CryptorRSA.PublicKey` - Represents an RSA Public Key. + - `CryptorRSA.PrivateKey` - Represents an RSA Private Key. + +* Data Handling + - `CryptorRSA.EncryptedData` - Represents encrypted data. + - `CryptorRSA.PlaintextData` - Represents plaintext or decrypted data. + - `CryptorRSA.SignedData` - Represents signed data. + +### Key Handling + +**BlueRSA** provides seven (7) functions each for creating public and private keys from data. They are as follows (where *createXXXX* is either `createPublicKey` or `createPrivateKey` depending on what you're trying to create): + +- `CryptorRSA.createXXXX(with data: Data) throws` - This creates either a private or public key containing the data provided. *It is assumed that the data being provided is in the proper format.* +- `CryptorRSA.createXXXX(withBase64 base64String: String) throws` - This creates either a private or public key using the `Base64 encoded String` provided. +- `CryptorRSA.createXXXX(withPEM pemString: String) throws` - This creates either a private or public key using the `PEM encoded String` provided. +- `CryptorRSA.createXXXX(withPEMNamed pemName: String, onPath path: String) throws` - This creates either a private or public key using the `PEM encoded file` pointed at by the `pemName` and located on the path specified by `path` provided. +- `CryptorRSA.createXXXX(withDERNamed derName: String, onPath path: String) throws` - This creates either a private or public key using the `DER encoded file` pointed at by the `derName` and located on the path specified by `path` provided. +- `CryptorRSA.createXXXX(withPEMNamed pemName: String, in bundle: Bundle = Bundle.main) throws` - This creates either a private or public key using the `PEM encoded file` pointed at by the `pemName` and located in the `Bundle` specified by `bundle` provided. By default this API will look in the `main` bundle. **Note: Apple Platforms Only** +- `CryptorRSA.createXXXX(withDERNamed derName: String, in bundle: Bundle = Bundle.main) throws` - This creates either a private or public key using the `DER encoded file` pointed at by the `derName` and located in the `Bundle` specified by `bundle` provided. By default this API will look in the `main` bundle. **Note: Apple Platforms Only** + +Additionally, there are three APIs for creating a *public key* by extracting the key from a PEM formatted certificate: They are: + +- `CryptorRSA.createPublicKey(extractingFrom data: Data) throws` - This creates either a public key by extracting from the `PEM encoded certificate` pointed at by the `data`. +- `CryptorRSA.createPublicKey(extractingFrom certName: String, onPath path: String) throws` - This creates a public key by extracting from the `PEM encoded certificate` pointed at by the `certName` and located on the path specified by `path` provided. +- `CryptorRSA.createPublicKey(extractingFrom certName: String, in bundle: Bundle = Bundle.main) throws` - This creates a public key using the `PEM encoded certificate` pointed at by the `derName` and located in the `Bundle` specified by `bundle` provided. By default this API will look in the `main` bundle. **Note: Apple Platforms Only** + + +**Example** + +The following example illustrates creating a public key given PEM encoded file located on a certain path. *Note: Exception handling omitted for brevity. + +``` +import Foundation +import CryptorRSA + +... + +let keyName = ... +let keyPath = ... + +let publicKey = try CryptorRSA.createPublicKey(withPEMNamed: keyName, onPath: keyPath) + +... + + + +``` + +### Data Encryption and Decryption Handling + +**BlueRSA** provides functions for the creation of each of the three (3) data handling types: + +**Plaintext Data Handling and Signing** + +There are two class level functions for creating a `PlaintextData` object. These are: + +- `CryptorRSA.createPlaintext(with data: Data) -> PlaintextData` - This function creates a `PlaintextData` containing the specified `data`. +- `CryptorRSA.createPlaintext(with string: String, using encoding: String.Encoding) throws -> PlaintextData` - This function creates a `PlaintextData` object using the `string` encoded with the specified `encoding` as the data. + +Once the `PlaintextData` object is created, there are two instance functions that can be used to manipulate the contained data. These are: + +- `encrypted(with key: PublicKey, algorithm: Data.Algorithm) throws -> EncryptedData?` - This function allows you to encrypt containing data using the public `key` and `algorithm` specified. This function returns an optional `EncryptedData` object containing the encryped data. +- `signed(with key: PrivateKey, algorithm: Data.Algorithm) throws -> SignedData?` - This function allows you to sign the contained data using the private `key` and `algorithm` specified. This function returns an optional `SignedData` object containing the signature of the signed data. + +**Example** + +- *Encryption*: **Note:** Exception handling omitted for brevity. + +``` +import Foundation +import CryptorRSA + +... + +let keyName = ... +let keyPath = ... + +let myData: Data = <... Data to be encrypted ...> + +let publicKey = try CryptorRSA.createPublicKey(withPEMNamed: keyName, onPath: keyPath) +let myPlaintext = CryptorRSA.createPlaintext(with: myData) +let encryptedData = try myPlaintext.encrypt(with: publicKey, algorithm: .sha1) + +... + +< Do something with the encrypted data...> + +``` + +- *Signing*: **Note:** Exception handling omitted for brevity. + +``` +import Foundation +import CryptorRSA + +... + +let keyName = ... +let keyPath = ... + +let myData: Data = <... Data to be signed ...> + +let privateKey = try CryptorRSA.createPrivateKey(withPEMNamed: keyName, onPath: keyPath) +let myPlaintext = CryptorRSA.createPlaintext(with: myData) +let signedData = try myPlaintext.signed(with: privateKey, algorithm: .sha1) + +... + +< Do something with the signed data...> + +``` +**Encrypted Data Handling** + +There are two class level functions for creating a `EncryptedData` object. These are: + +- `CryptorRSA.createEncrypted(with data: Data) -> EncryptedData` - This function creates a `EncryptedData` containing the specified encrypted `data`. +- `CryptorRSA.createEncrypted(with base64String: String) throws -> EncryptedData` - This function creates a `EncrpytedData` using the *Base64* representation of already encrypted data. + +Once the `EncryptedData` object is created, there is an instance function that can be used to decrypt the enclosed data: + +- `decrypted(with key: PrivateKey, algorithm: Data.Algorithm) throws -> DecryptedData?` - This function allows you to decrypt containing data using the public `key` and `algorithm` specified. This function returns an optional `DecryptedData` object containing the encryped data. + +BlueRSA currently supports `OAEP` padding, which is the recommended padding algorithm. + +**Example** + +- *Decryption*: **Note**: Exception handling omitted for brevity. + +``` +import Foundation +import CryptorRSA + +... + +let keyName = ... +let keyPath = ... +let publicKey = try CryptorRSA.createPublicKey(withPEMNamed: keyName, onPath: keyPath) + +let pkeyName = ... +let pkeyPath = ... +let privateKey = try CryptorRSA.createPrivateKey(withPEMNamed: pkeyName, onPath: pkeyPath) + +let myData: Data = <... Data to be encrypted ...> + +let myPlaintext = CryptorRSA.createPlaintext(with: myData) +let encryptedData = try myPlaintext.encrypt(with: publicKey, algorithm: .sha1) + +let decryptedData = try encryptedData.decrypt(with: privateKey, algorithm: .sha1) + +... + +< Do something with the decrypted data...> + + +``` + + +### Signature Verification Handling + +There is a single class level function that can be used to create a `SignedData` object. It is: + +- `CryptorRSA.createSigned(with data: Data) -> SignedData` - This function creates a `SignedData` containing the specified signed `data`. + +Once created or obtained `PlaintextData` and `SignedData`, there is an instance function which can be used to verify the signature contained therein: + +- `verify(with key: PublicKey, signature: SignedData, algorithm: Data.Algorithm) throws -> Bool` - This function is used to verify, using the public `key` and `algorithm`, the `signature`. Returns true if the signature is valid, false otherwise. + +- *Verifying*: **Note:** Exception handling omitted for brevity. + +``` +import Foundation +import CryptorRSA + +... + +let keyName = ... +let keyPath = ... +let publicKey = try CryptorRSA.createPublicKey(withPEMNamed: keyName, onPath: keyPath) + +let pkeyName = ... +let pkeyPath = ... +let privateKey = try CryptorRSA.createPrivateKey(withPEMNamed: pkeyName, onPath: pkeyPath) + +let myData: Data = <... Data to be signed ...> + +let myPlaintext = CryptorRSA.createPlaintext(with: myData) +let signedData = try myPlaintext.signed(with: privateKey, algorithm: .sha1) + +if try myPlaintext.verify(with: publicKey, signature: signedData, algorithm: .sha1) { + + print("Signature verified") + +} else { + + print("Signature Verification Failed") +} + +``` + +### Data Type Utility Functions + +All three of the data handling types have two common utility instance functions. These are: + +- `digest(using algorithm: Data.Algorithm) throws -> Data` - This function returns a `Data` object containing a digest constructed using the specified `algorithm`. +- `string(using encoding: String.Encoding) throws -> String` - This functions returns a `String` representation of the data using the specified `encoding`. + +## Community + +We love to talk server-side Swift and Kitura. Join our [Slack](http://swift-at-ibm-slack.mybluemix.net/) to meet the team! + +## License + +This library is licensed under Apache 2.0. Full license text is available in [LICENSE](https://github.com/IBM-Swift/BlueRSA/blob/master/LICENSE). diff --git a/Pods/BlueRSA/Sources/CryptorRSA/CryptorRSA.swift b/Pods/BlueRSA/Sources/CryptorRSA/CryptorRSA.swift new file mode 100644 index 0000000..d49d716 --- /dev/null +++ b/Pods/BlueRSA/Sources/CryptorRSA/CryptorRSA.swift @@ -0,0 +1,1111 @@ +// +// CryptorRSA.swift +// CryptorRSA +// +// Created by Bill Abt on 1/17/17. +// +// Copyright © 2017 IBM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +#if os(Linux) + import OpenSSL +#endif + +// MARK: - + +// MARK: - + +/// +/// RSA Encryption/Decryption, Signing/Verification +/// +@available(macOS 10.12, iOS 10.3, watchOS 3.3, tvOS 12.0, *) +public class CryptorRSA { + + // MARK: Class Functions + + /// + /// Create a plaintext data container. + /// + /// - Parameters: + /// - data: `Data` containing the key data. + /// + /// - Returns: Newly initialized `PlaintextData`. + /// + public class func createPlaintext(with data: Data) -> PlaintextData { + + return PlaintextData(with: data) + } + + /// + /// Creates a message from a plaintext string, with the specified encoding. + /// + /// - Parameters: + /// - string: String value of the plaintext message + /// - encoding: Encoding to use to generate the clear data + /// + /// - Returns: Newly initialized `PlaintextData`. + /// + public class func createPlaintext(with string: String, using encoding: String.Encoding) throws -> PlaintextData { + + return try PlaintextData(with: string, using: encoding) + } + + /// + /// Create an encrypted data container. + /// + /// - Parameters: + /// - data: `Data` containing the encrypted data. + /// + /// - Returns: Newly initialized `EncryptedData`. + /// + public class func createEncrypted(with data: Data) -> EncryptedData { + + return EncryptedData(with: data) + } + + /// + /// Creates a message with a encrypted base64-encoded string. + /// + /// - Parameters: + /// - base64String: Base64-encoded data of an encrypted message + /// + /// - Returns: Newly initialized `EncryptedData`. + /// + public class func createEncrypted(with base64String: String) throws -> EncryptedData { + + return try EncryptedData(withBase64: base64String) + } + + /// + /// Create an signed data container. + /// + /// - Parameters: + /// - data: `Data` containing the signed data. + /// + /// - Returns: Newly initialized `SignedData`. + /// + public class func createSigned(with data: Data) -> SignedData { + + return SignedData(with: data) + } + + /// + /// RSA Data Object: Allows for RSA Encryption/Decryption, Signing/Verification and various utility functions. + /// + public class RSAData { + + // MARK: Enums + + /// Denotes the type of data this represents. + public enum DataType { + + /// Plaintext + case plaintextType + + /// Encrypted + case encryptedType + + /// Signed + case signedType + } + + // MARK: -- Properties + + /// Data of the message + public let data: Data + + /// Represents the type of data contained. + public internal(set) var type: DataType = .plaintextType + + /// Base64-encoded string of the message data + public var base64String: String { + + return data.base64EncodedString() + } + + // MARK: -- Initializers + + /// + /// Initialize a new RSAData object. + /// + /// - Parameters: + /// - data: `Data` containing the data. + /// - type: Type of data contained. + /// + /// - Returns: Newly initialized `RSAData`. + /// + internal init(with data: Data, type: DataType) { + + self.data = data + self.type = type + } + + /// + /// Creates a RSAData with a encrypted base64-encoded string. + /// + /// - Parameters: + /// - base64String: Base64-encoded data of an encrypted message + /// + /// - Returns: Newly initialized `RSAData`. + /// + internal init(withBase64 base64String: String) throws { + + guard let data = Data(base64Encoded: base64String) else { + + throw Error(code: CryptorRSA.ERR_BASE64_PEM_DATA, reason: "Couldn't convert base 64 encoded string ") + } + + self.data = data + self.type = .encryptedType + } + + /// + /// Creates a message from a plaintext string, with the specified encoding. + /// + /// - Parameters: + /// - string: String value of the plaintext message + /// - encoding: Encoding to use to generate the clear data + /// + /// - Returns: Newly initialized `RSAData`. + /// + internal init(with string: String, using encoding: String.Encoding) throws { + + guard let data = string.data(using: encoding) else { + + throw Error(code: CryptorRSA.ERR_STRING_ENCODING, reason: "Couldn't convert string to data using specified encoding") + } + + self.data = data + self.type = .plaintextType + } + + + // MARK: -- Functions + + // MARK: --- Encrypt/Decrypt + + /// + /// Encrypt the data. + /// + /// - Parameters: + /// - key: The `PublicKey` + /// - algorithm: The algorithm to use (`Data.Algorithm`). + /// + /// - Returns: A new optional `EncryptedData` containing the encrypted data. + /// + public func encrypted(with key: PublicKey, algorithm: Data.Algorithm) throws -> EncryptedData? { + + // Must be plaintext... + guard self.type == .plaintextType else { + + throw Error(code: CryptorRSA.ERR_NOT_PLAINTEXT, reason: "Data is not plaintext") + } + + // Key must be public... + guard key.type == .publicType else { + + throw Error(code: CryptorRSA.ERR_KEY_NOT_PUBLIC, reason: "Supplied key is not public") + } + + #if os(Linux) + switch algorithm { + case .gcm: + return try encryptedGCM(with: key) + case .sha1, .sha224, .sha256, .sha384, .sha512: + // Same algorithm is used regardless of sha + return try encryptedCBC(with: key) + } + #else + + var response: Unmanaged? = nil + let eData = SecKeyCreateEncryptedData(key.reference, algorithm.alogrithmForEncryption, self.data as CFData, &response) + if response != nil { + + guard let error = response?.takeRetainedValue() else { + + throw Error(code: CryptorRSA.ERR_ENCRYPTION_FAILED, reason: "Encryption failed. Unable to determine error.") + } + + throw Error(code: CryptorRSA.ERR_ENCRYPTION_FAILED, reason: "Encryption failed with error: \(error)") + } + + return EncryptedData(with: eData! as Data) + + #endif + } + + /// + /// Decrypt the data. + /// + /// - Parameters: + /// - key: The `PrivateKey` + /// - algorithm: The algorithm to use (`Data.Algorithm`). + /// + /// - Returns: A new optional `PlaintextData` containing the decrypted data. + /// + public func decrypted(with key: PrivateKey, algorithm: Data.Algorithm) throws -> PlaintextData? { + + // Must be encrypted... + guard self.type == .encryptedType else { + + throw Error(code: CryptorRSA.ERR_NOT_ENCRYPTED, reason: "Data is plaintext") + } + + // Key must be private... + guard key.type == .privateType else { + + throw Error(code: CryptorRSA.ERR_KEY_NOT_PUBLIC, reason: "Supplied key is not private") + } + + #if os(Linux) + + switch algorithm { + case .gcm: + return try decryptedGCM(with: key) + case .sha1, .sha224, .sha256, .sha384, .sha512: + // Same algorithm is used regardless of sha + return try decryptedCBC(with: key) + } + + #else + + var response: Unmanaged? = nil + let pData = SecKeyCreateDecryptedData(key.reference, algorithm.alogrithmForEncryption, self.data as CFData, &response) + if response != nil { + + guard let error = response?.takeRetainedValue() else { + + throw Error(code: CryptorRSA.ERR_DECRYPTION_FAILED, reason: "Decryption failed. Unable to determine error.") + } + + throw Error(code: CryptorRSA.ERR_DECRYPTION_FAILED, reason: "Decryption failed with error: \(error)") + } + + return PlaintextData(with: pData! as Data) + + #endif + } + + #if os(Linux) + /// + /// Encrypt the data using AES GCM SHA1 for cross platform support. + /// + /// - Parameters: + /// - key: Public key to use. + /// + /// - Returns: Encrypted data object. + /// + func encryptedGCM(with key: PublicKey) throws -> EncryptedData? { + // Initialize encryption context + let rsaEncryptCtx = EVP_CIPHER_CTX_new_wrapper() + EVP_CIPHER_CTX_init_wrapper(rsaEncryptCtx) + defer { + // On completion deallocate the memory + EVP_CIPHER_CTX_reset_wrapper(rsaEncryptCtx) + EVP_CIPHER_CTX_free_wrapper(rsaEncryptCtx) + } + // get rsaKey + guard let rsaKey = EVP_PKEY_get1_RSA(.make(optional: key.reference)) else { + let source = "Couldn't create key reference from key data" + if let reason = CryptorRSA.getLastError(source: source) { + throw Error(code: ERR_ADD_KEY, reason: reason) + } + throw Error(code: ERR_ADD_KEY, reason: source + ": No OpenSSL error reported.") + } + defer { + RSA_free(rsaKey) + } + + // Set the additional authenticated data (aad) as the RSA key modulus and publicExponent in an ASN1 sequence. + guard let aad = key.publicKeyBytes else { + let source = "Encryption failed" + throw Error(code: ERR_ENCRYPTION_FAILED, reason: source + ": Failed to decode public key") + } + // if the RSA key is >= 4096 bits, use aes_256_gcm. + let encryptedCapacity: Int + let keySize: Int + if aad.count > 525 { + // Set the rsaEncryptCtx to use EVP_aes_256_gcm encryption. + guard EVP_EncryptInit_ex(rsaEncryptCtx, EVP_aes_256_gcm(), nil, nil, nil) == 1 else { + throw Error(code: ERR_ENCRYPTION_FAILED, reason: "Encryption failed: Failed to initialize encryption context") + } + encryptedCapacity = 512 + keySize = 32 + } else { + // Set the rsaEncryptCtx to use EVP_aes_128_gcm encryption. + guard EVP_EncryptInit_ex(rsaEncryptCtx, EVP_aes_128_gcm(), nil, nil, nil) == 1 else { + throw Error(code: ERR_ENCRYPTION_FAILED, reason: "Encryption failed: Failed to initialize encryption context") + } + if aad.count > 300 { + encryptedCapacity = 384 + } else if aad.count > 260 { + encryptedCapacity = 256 + } else { + encryptedCapacity = 128 + } + keySize = 16 + } + + // Allocate encryption memory + let aeskey = UnsafeMutablePointer.allocate(capacity: keySize) + let encryptedKey = UnsafeMutablePointer.allocate(capacity: encryptedCapacity) + let tag = UnsafeMutablePointer.allocate(capacity: 16) + let encrypted = UnsafeMutablePointer.allocate(capacity: data.count + 16) + defer { + #if swift(>=4.1) + aeskey.deallocate() + encryptedKey.deallocate() + tag.deallocate() + encrypted.deallocate() + #else + aeskey.deallocate(capacity: keySize) + encryptedKey.deallocate(capacity: encryptedCapacity) + tag.deallocate(capacity: 16) + encrypted.deallocate(capacity: data.count + 16) + #endif + } + + var processedLength: Int32 = 0 + var encLength: Int32 = 0 + // Apple use a 16 byte all 0 IV. This is allowed since a random key is generated for each encryption. + let iv = [UInt8](repeating: 0, count: 16) + + // Set the IV length to be 16 to match Apple. + guard EVP_CIPHER_CTX_ctrl(rsaEncryptCtx, EVP_CTRL_GCM_SET_IVLEN, 16, nil) == 1, + // Generate 16/32 random bytes that will be used as the AES key. + EVP_CIPHER_CTX_rand_key(rsaEncryptCtx, aeskey) == 1, + // Set the aeskey and iv for the symmetric encryption. + EVP_EncryptInit_ex(rsaEncryptCtx, nil, nil, aeskey, iv) == 1, + // Encrypt the aes key using the rsa public key with SHA1, OAEP padding. + RSA_public_encrypt(Int32(keySize), aeskey, encryptedKey, .make(optional: rsaKey), RSA_PKCS1_OAEP_PADDING) == encryptedCapacity, + // Add the aad to the encryption context. + // This is used in generating the GCM tag. We don't use this processedLength. + EVP_EncryptUpdate(rsaEncryptCtx, nil, &processedLength, [UInt8](aad), Int32(aad.count)) == 1 + else { + let source = "Encryption failed" + if let reason = CryptorRSA.getLastError(source: source) { + throw Error(code: ERR_ENCRYPTION_FAILED, reason: reason) + } + throw Error(code: ERR_ENCRYPTION_FAILED, reason: source + ": No OpenSSL error reported.") + } + + // Encrypt the plaintext into encrypted using gcmAlgorithm with the random aes key and all 0 iv. + guard(self.data.withUnsafeBytes({ (plaintext: UnsafeRawBufferPointer) -> Int32 in + return EVP_EncryptUpdate(rsaEncryptCtx, encrypted, &processedLength, plaintext.baseAddress?.assumingMemoryBound(to: UInt8.self), Int32(data.count)) + })) == 1 else { + let source = "Encryption failed" + if let reason = CryptorRSA.getLastError(source: source) { + throw Error(code: ERR_ENCRYPTION_FAILED, reason: reason) + } + throw Error(code: ERR_ENCRYPTION_FAILED, reason: source + ": No OpenSSL error reported.") + } + encLength += processedLength + // Finalize the encryption so no more data will be added and generate the GCM tag. + guard EVP_EncryptFinal_ex(rsaEncryptCtx, encrypted.advanced(by: Int(encLength)), &processedLength) == 1 else { + let source = "Encryption failed" + if let reason = CryptorRSA.getLastError(source: source) { + throw Error(code: ERR_ENCRYPTION_FAILED, reason: reason) + } + throw Error(code: ERR_ENCRYPTION_FAILED, reason: source + ": No OpenSSL error reported.") + } + encLength += processedLength + // Get the 16 byte GCM tag. + guard EVP_CIPHER_CTX_ctrl(rsaEncryptCtx, EVP_CTRL_GCM_GET_TAG, 16, tag) == 1 else { + let source = "Encryption failed" + if let reason = CryptorRSA.getLastError(source: source) { + throw Error(code: ERR_ENCRYPTION_FAILED, reason: reason) + } + throw Error(code: ERR_ENCRYPTION_FAILED, reason: source + ": No OpenSSL error reported.") + } + + // Construct the envelope by combining the encrypted AES key, the encrypted date and the GCM tag. + let ekFinal = Data(bytes: encryptedKey, count: encryptedCapacity) + let cipher = Data(bytes: encrypted, count: Int(encLength)) + let tagFinal = Data(bytes: tag, count: 16) + return EncryptedData(with: ekFinal + cipher + tagFinal) + } + + /// + /// Encrypt the data using CBC for cross platform support. + /// + /// - Parameters: + /// - key: Public key to use. + /// + /// - Returns: Encrypted data object. + /// + func encryptedCBC(with key: PublicKey) throws -> EncryptedData? { + // Copy the EVP Key + var evp_key = EVP_PKEY_new() + let rsa = EVP_PKEY_get1_RSA(.make(optional: key.reference)) + EVP_PKEY_set1_RSA(evp_key, rsa) + RSA_free(rsa) + defer { + EVP_PKEY_free(evp_key) + } + + // TODO: hash type option is not being used right now. + let enc = EVP_aes_256_cbc() + let padding = RSA_PKCS1_OAEP_PADDING + + let rsaEncryptCtx = EVP_CIPHER_CTX_new_wrapper() + + defer { + EVP_CIPHER_CTX_reset_wrapper(rsaEncryptCtx) + EVP_CIPHER_CTX_free_wrapper(rsaEncryptCtx) + } + + EVP_CIPHER_CTX_set_padding(rsaEncryptCtx, padding) + + // Initialize the AES encryption key array (of size 1) + typealias UInt8Ptr = UnsafeMutablePointer? + var ek: UInt8Ptr + ek = UnsafeMutablePointer.allocate(capacity: Int(EVP_PKEY_size(.make(optional: key.reference)))) + let ekPtr = UnsafeMutablePointer.allocate(capacity: MemoryLayout.size) + ekPtr.pointee = ek + + // Assign size of the corresponding cipher's IV + let IVLength = EVP_CIPHER_iv_length(.make(optional: enc)) + let iv = UnsafeMutablePointer.allocate(capacity: Int(IVLength)) + let encrypted = UnsafeMutablePointer.allocate(capacity: self.data.count + Int(IVLength)) + defer { + #if swift(>=4.1) + ek?.deallocate() + ekPtr.deallocate() + iv.deallocate() + encrypted.deallocate() + #else + ek?.deallocate(capacity: Int(EVP_PKEY_size(.make(optional: key.reference)))) + ekPtr.deallocate(capacity: MemoryLayout.size) + iv.deallocate(capacity: Int(IVLength)) + encrypted.deallocate(capacity: self.data.count + Int(IVLength)) + #endif + } + var encKeyLength: Int32 = 0 + var processedLength: Int32 = 0 + var encLength: Int32 = 0 + + // Initializes a cipher context ctx for encryption with cipher type using a random secret key and IV. + // The secret key is encrypted using the public key (evp_key can be an array of public keys) + // Here we are using just 1 public key + var status = EVP_SealInit(rsaEncryptCtx, .make(optional: enc), ekPtr, &encKeyLength, iv, &evp_key, 1) + + // SealInit should return the number of public keys that were input, here it is only 1 + guard status == 1 else { + let source = "Encryption failed" + if let reason = CryptorRSA.getLastError(source: source) { + + throw Error(code: ERR_ENCRYPTION_FAILED, reason: reason) + } + throw Error(code: ERR_ENCRYPTION_FAILED, reason: source + ": No OpenSSL error reported.") + } + + // EVP_SealUpdate is a complex macros and therefore the compiler doesnt + // convert it directly to swift. From /usr/local/opt/openssl/include/openssl/evp.h: + _ = self.data.withUnsafeBytes({ (plaintext: UnsafeRawBufferPointer) -> Int32 in + return EVP_EncryptUpdate(rsaEncryptCtx, encrypted, &processedLength, plaintext.baseAddress?.assumingMemoryBound(to: UInt8.self), Int32(self.data.count)) + }) + encLength = processedLength + + status = EVP_SealFinal(rsaEncryptCtx, encrypted.advanced(by: Int(encLength)), &processedLength) + guard status == 1 else { + let source = "Encryption failed" + if let reason = CryptorRSA.getLastError(source: source) { + + throw Error(code: ERR_ENCRYPTION_FAILED, reason: reason) + } + throw Error(code: ERR_ENCRYPTION_FAILED, reason: source + ": No OpenSSL error reported.") + } + encLength += processedLength + + let cipher = Data(bytes: encrypted, count: Int(encLength)) + let ekFinal = Data(bytes: ek!, count: Int(encKeyLength)) + let ivFinal = Data(bytes: iv, count: Int(IVLength)) + + return EncryptedData(with: ekFinal + cipher + ivFinal) + } + + /// + /// Decrypt the data using AES GCM for cross platform support. + /// + /// - Parameters: + /// - key: Private key to use. + /// + /// - Returns: Decrypted data object. + /// + func decryptedGCM(with key: PrivateKey) throws -> PlaintextData? { + + // Initialize the decryption context. + let rsaDecryptCtx = EVP_CIPHER_CTX_new() + EVP_CIPHER_CTX_init_wrapper(rsaDecryptCtx) + defer { + // On completion deallocate the memory + EVP_CIPHER_CTX_reset_wrapper(rsaDecryptCtx) + EVP_CIPHER_CTX_free_wrapper(rsaDecryptCtx) + } + // get rsaKey + guard let rsaKey = EVP_PKEY_get1_RSA(.make(optional: key.reference)) else { + let source = "Couldn't create key reference from key data" + if let reason = CryptorRSA.getLastError(source: source) { + throw Error(code: ERR_ADD_KEY, reason: reason) + } + throw Error(code: ERR_ADD_KEY, reason: source + ": No OpenSSL error reported.") + } + defer { + RSA_free(rsaKey) + } + + // Set the additional authenticated data (aad) as the RSA key modulus and publicExponent in an ASN1 sequence. + guard let aad = key.publicKeyBytes else { + let source = "Decryption failed" + throw Error(code: ERR_DECRYPTION_FAILED, reason: source + ": Failed to decode public key") + } + // if the RSA key is larger than 4096 bits, use aes_256_gcm. + let encKeyLength: Int + let keySize: Int + if aad.count > 525 { + // Set the envelope decryption algorithm as 128 bit AES-GCM. + guard EVP_DecryptInit_ex(rsaDecryptCtx, EVP_aes_256_gcm(), nil, nil, nil) == 1 else { + throw Error(code: ERR_DECRYPTION_FAILED, reason: "Decryption failed: Failed to initialize decryption context") + } + encKeyLength = 512 + keySize = 32 + } else { + // Set the envelope decryption algorithm as 128 bit AES-GCM. + guard EVP_DecryptInit_ex(rsaDecryptCtx, EVP_aes_128_gcm(), nil, nil, nil) == 1 else { + throw Error(code: ERR_DECRYPTION_FAILED, reason: "Decryption failed: Failed to initialize decryption context") + } + if aad.count > 300 { + encKeyLength = 384 + } else if aad.count > 260 { + encKeyLength = 256 + } else { + encKeyLength = 128 + } + keySize = 16 + } + + let tagLength = 16 + let encryptedDataLength = Int(data.count) - encKeyLength - tagLength + + // Extract encryptedAESKey, encryptedData, GCM tag from data + let encryptedKey = data.subdata(in: 0...allocate(capacity: keySize) + let decrypted = UnsafeMutablePointer.allocate(capacity: Int(encryptedData.count + 16)) + defer { + // On completion deallocate the memory + #if swift(>=4.1) + aeskey.deallocate() + decrypted.deallocate() + #else + aeskey.deallocate(capacity: keySize) + decrypted.deallocate(capacity: Int(encryptedData.count + 16)) + #endif + } + // processedLen is the number of bytes that each EVP_DecryptUpdate/EVP_DecryptFinal decrypts. + // The sum of processedLen is the total size of the decrypted message (decMsgLen) + var processedLen: Int32 = 0 + var decMsgLen: Int32 = 0 + // Use a 16-byte all zero initialization vector (IV) to match Apple Security. + let iv = [UInt8](repeating: 0, count: 16) + + // Decrypt the encryptedKey into the aeskey using the RSA private key + guard RSA_private_decrypt(Int32(encryptedKey.count), [UInt8](encryptedKey), aeskey, rsaKey, RSA_PKCS1_OAEP_PADDING) != 0, + // Set the IV length to be 16 bytes. + EVP_CIPHER_CTX_ctrl(rsaDecryptCtx, EVP_CTRL_GCM_SET_IVLEN, 16, nil) == 1, + // Set the AES key to be 16 bytes. + EVP_CIPHER_CTX_set_key_length(rsaDecryptCtx, Int32(keySize)) == 1, + // Set the envelope decryption context AES key and IV. + EVP_DecryptInit_ex(rsaDecryptCtx, nil, nil, aeskey, iv) == 1, + EVP_DecryptUpdate(rsaDecryptCtx, nil, &processedLen, [UInt8](aad), Int32(aad.count)) == 1 + else { + let source = "Decryption failed" + if let reason = CryptorRSA.getLastError(source: source) { + + throw Error(code: ERR_DECRYPTION_FAILED, reason: reason) + } + throw Error(code: ERR_DECRYPTION_FAILED, reason: source + ": No OpenSSL error reported.") + } + // Decrypt the encrypted data using the symmetric key. + guard encryptedData.withUnsafeBytes({ (enc: UnsafeRawBufferPointer) -> Int32 in + return EVP_DecryptUpdate(rsaDecryptCtx, decrypted, &processedLen, enc.baseAddress?.assumingMemoryBound(to: UInt8.self), Int32(encryptedData.count)) + }) != 0 else { + let source = "Decryption failed" + if let reason = CryptorRSA.getLastError(source: source) { + throw Error(code: ERR_DECRYPTION_FAILED, reason: reason) + } + throw Error(code: ERR_DECRYPTION_FAILED, reason: source + ": No OpenSSL error reported.") + } + decMsgLen += processedLen + + // Verify the provided GCM tag. + guard tagData.withUnsafeMutableBytes({ (tag: UnsafeMutableRawBufferPointer) -> Int32 in + return EVP_CIPHER_CTX_ctrl(rsaDecryptCtx, EVP_CTRL_GCM_SET_TAG, 16, tag.baseAddress) + }) == 1, + EVP_DecryptFinal_ex(rsaDecryptCtx, decrypted.advanced(by: Int(decMsgLen)), &processedLen) == 1 + else { + let source = "Decryption failed" + if let reason = CryptorRSA.getLastError(source: source) { + throw Error(code: ERR_DECRYPTION_FAILED, reason: reason) + } + throw Error(code: ERR_DECRYPTION_FAILED, reason: source + ": No OpenSSL error reported.") + } + decMsgLen += processedLen + // return the decrypted plaintext. + return PlaintextData(with: Data(bytes: decrypted, count: Int(decMsgLen))) + } + + /// + /// Decrypt the data using CBC for cross platform support. + /// + /// - Parameters: + /// - key: Private key to use. + /// + /// - Returns: Decrypted data object. + /// + func decryptedCBC(with key: PrivateKey) throws -> PlaintextData? { + // Convert RSA key to EVP + let encType = EVP_aes_256_cbc() + let padding = RSA_PKCS1_OAEP_PADDING + + // Size of symmetric encryption + let encKeyLength = Int(EVP_PKEY_size(.make(optional: key.reference))) + // Size of the corresponding cipher's IV + let encIVLength = Int(EVP_CIPHER_iv_length(.make(optional: encType))) + // Size of encryptedKey + let encryptedDataLength = Int(self.data.count) - encKeyLength - encIVLength + + // Extract encryptedKey, encryptedData, encryptedIV from data + // self.data = encryptedKey + encryptedData + encryptedIV + let encryptedKey = self.data.subdata(in: 0...allocate(capacity: Int(encryptedData.count + encryptedIV.count)) + defer { + #if swift(>=4.1) + decrypted.deallocate() + #else + decrypted.deallocate(capacity: Int(encryptedData.count + encryptedIV.count)) + #endif + } + // EVP_OpenInit returns 0 on error or the recovered secret key size if successful + var status = encryptedKey.withUnsafeBytes({ (ek: UnsafeRawBufferPointer) -> Int32 in + return encryptedIV.withUnsafeBytes({ (iv: UnsafeRawBufferPointer) -> Int32 in + return EVP_OpenInit(rsaDecryptCtx, .make(optional: encType), ek.baseAddress?.assumingMemoryBound(to: UInt8.self), Int32(encryptedKey.count), iv.baseAddress?.assumingMemoryBound(to: UInt8.self), .make(optional: key.reference)) + }) + }) + guard status != 0 else { + let source = "Decryption failed" + if let reason = CryptorRSA.getLastError(source: source) { + + throw Error(code: ERR_DECRYPTION_FAILED, reason: reason) + } + throw Error(code: ERR_DECRYPTION_FAILED, reason: source + ": No OpenSSL error reported.") + } + + // EVP_OpenUpdate is a complex macros and therefore the compiler doesnt + // convert it directly to Swift. From /usr/local/opt/openssl/include/openssl/evp.h: + _ = encryptedData.withUnsafeBytes({ (enc: UnsafeRawBufferPointer) -> Int32 in + return EVP_DecryptUpdate(rsaDecryptCtx, decrypted, &processedLen, enc.baseAddress?.assumingMemoryBound(to: UInt8.self), Int32(encryptedData.count)) + }) + decMsgLen = processedLen + + status = EVP_OpenFinal(rsaDecryptCtx, decrypted.advanced(by: Int(decMsgLen)), &processedLen) + guard status != 0 else { + let source = "Decryption failed" + if let reason = CryptorRSA.getLastError(source: source) { + + throw Error(code: ERR_DECRYPTION_FAILED, reason: reason) + } + throw Error(code: ERR_DECRYPTION_FAILED, reason: source + ": No OpenSSL error reported.") + } + decMsgLen += processedLen + + return PlaintextData(with: Data(bytes: decrypted, count: Int(decMsgLen))) + } + + #endif + + // MARK: --- Sign/Verification + + /// + /// Sign the data + /// + /// - Parameters: + /// - key: The `PrivateKey`. + /// - algorithm: The algorithm to use (`Data.Algorithm`). + /// - usePSS: Bool stating whether or not to use RSA-PSS (Probabilistic signature scheme). + /// + /// - Returns: A new optional `SignedData` containing the digital signature. + /// + public func signed(with key: PrivateKey, algorithm: Data.Algorithm, usePSS: Bool = false) throws -> SignedData? { + + // Must be plaintext... + guard self.type == .plaintextType else { + + throw Error(code: CryptorRSA.ERR_NOT_PLAINTEXT, reason: "Data is not plaintext") + } + + // Key must be private... + guard key.type == .privateType else { + + throw Error(code: CryptorRSA.ERR_KEY_NOT_PRIVATE, reason: "Supplied key is not private") + } + + #if os(Linux) + + let md_ctx = EVP_MD_CTX_new_wrapper() + + defer { + EVP_MD_CTX_free_wrapper(md_ctx) + } + + + let (md, _) = algorithm.algorithmForSignature + + if usePSS { + // If using PSS padding, create a PKEY and set it's padding, mask generation function and salt length. + // NID_rsassaPss is `EVP_PKEY_RSA_PSS` as defined in evp.h + var pkey_ctx = EVP_PKEY_CTX_new_id(NID_rsassaPss, nil) + EVP_DigestSignInit(md_ctx, &pkey_ctx, .make(optional: md), nil, .make(optional: key.reference)) + EVP_PKEY_CTX_ctrl(pkey_ctx, EVP_PKEY_RSA, -1, EVP_PKEY_CTRL_RSA_PADDING, RSA_PKCS1_PSS_PADDING, nil) + EVP_PKEY_CTX_ctrl(pkey_ctx, -1, EVP_PKEY_OP_SIGN, EVP_PKEY_CTRL_RSA_MGF1_MD, 0, .make(optional: md)) + // Sets salt length to be equal to message digest length + EVP_PKEY_CTX_ctrl(pkey_ctx, -1, EVP_PKEY_OP_SIGN, EVP_PKEY_CTRL_RSA_PSS_SALTLEN, -1, .make(optional: md)) + } else { + // Provide a pkey_ctx to EVP_DigestSignInit so that the EVP_PKEY_CTX of the signing operation + // is written to it, to allow alternative signing options to be set + EVP_DigestSignInit(md_ctx, nil, .make(optional: md), nil, .make(optional: key.reference)) + } + + // Convert Data to UnsafeRawPointer! + _ = self.data.withUnsafeBytes({ (message: UnsafeRawBufferPointer) -> Int32 in + return EVP_DigestUpdate(md_ctx, message.baseAddress?.assumingMemoryBound(to: UInt8.self), self.data.count) + }) + + // Determine the size of the actual signature + var sig_len: Int = 0 + EVP_DigestSignFinal(md_ctx, nil, &sig_len) + let sig = UnsafeMutablePointer.allocate(capacity: sig_len) + + defer { + #if swift(>=4.1) + sig.deallocate() + #else + sig.deallocate(capacity: sig_len) + #endif + } + + let rc = EVP_DigestSignFinal(md_ctx, sig, &sig_len) + guard rc == 1, sig_len > 0 else { + let source = "Signing failed." + if let reason = CryptorRSA.getLastError(source: source) { + + throw Error(code: ERR_SIGNING_FAILED, reason: reason) + } + throw Error(code: ERR_SIGNING_FAILED, reason: source + ": No OpenSSL error reported.") + } + + return SignedData(with: Data(bytes: sig, count: sig_len)) + + #else + + let signingAlgorithm: SecKeyAlgorithm + if usePSS { + if #available(macOS 10.13, iOS 11.0, watchOS 4.0, *) { + signingAlgorithm = usePSS ? algorithm.algorithmForPssSignature : algorithm.algorithmForSignature + } else { + throw Error(code: ERR_NOT_IMPLEMENTED, reason: "RSA-PSS only supported on macOS 10.13/iOS 10.0 and above.") + } + } else { + signingAlgorithm = algorithm.algorithmForSignature + } + + var response: Unmanaged? = nil + let sData = SecKeyCreateSignature(key.reference, signingAlgorithm, self.data as CFData, &response) + if response != nil { + + guard let error = response?.takeRetainedValue() else { + + throw Error(code: CryptorRSA.ERR_SIGNING_FAILED, reason: "Signing failed. Unable to determine error.") + } + + throw Error(code: CryptorRSA.ERR_SIGNING_FAILED, reason: "Signing failed with error: \(error)") + } + + return SignedData(with: sData! as Data) + + #endif + } + + /// + /// Verify the signature + /// + /// - Parameters: + /// - key: The `PublicKey`. + /// - signature: The `SignedData` containing the signature to verify against. + /// - algorithm: The algorithm to use (`Data.Algorithm`). + /// - usePSS: Bool stating whether or not to use RSA-PSS (Probabilistic signature scheme). + /// + /// - Returns: True if verification is successful, false otherwise + /// + public func verify(with key: PublicKey, signature: SignedData, algorithm: Data.Algorithm, usePSS: Bool = false) throws -> Bool { + + // Must be plaintext... + guard self.type == .plaintextType else { + + throw Error(code: CryptorRSA.ERR_NOT_PLAINTEXT, reason: "Data is not plaintext") + } + + // Key must be public... + guard key.type == .publicType else { + + throw Error(code: CryptorRSA.ERR_KEY_NOT_PRIVATE, reason: "Supplied key is not public") + } + // Signature must be signed data... + guard signature.type == .signedType else { + + throw Error(code: CryptorRSA.ERR_NOT_SIGNED_DATA, reason: "Supplied signature is not of signed data type") + } + + #if os(Linux) + + let md_ctx = EVP_MD_CTX_new_wrapper() + + defer { + EVP_MD_CTX_free_wrapper(md_ctx) + } + + + let (md, _) = algorithm.algorithmForSignature + + if usePSS { + // If using PSS padding, create a PKEY and set it's padding and mask generation function. + var pkey_ctx = EVP_PKEY_CTX_new_id(NID_rsassaPss, nil) + EVP_DigestVerifyInit(md_ctx, &pkey_ctx, .make(optional: md), nil, .make(optional: key.reference)) + EVP_PKEY_CTX_ctrl(pkey_ctx, EVP_PKEY_RSA, -1, EVP_PKEY_CTRL_RSA_PADDING, RSA_PKCS1_PSS_PADDING, nil) + EVP_PKEY_CTX_ctrl(pkey_ctx, -1, EVP_PKEY_OP_VERIFY, EVP_PKEY_CTRL_RSA_MGF1_MD, 0, .make(optional: md)) + } else { + // Provide a pkey_ctx to EVP_DigestSignInit so that the EVP_PKEY_CTX of the signing operation + // is written to it, to allow alternative signing options to be set + EVP_DigestVerifyInit(md_ctx, nil, .make(optional: md), nil, .make(optional: key.reference)) + } + + + var rc = self.data.withUnsafeBytes({ (message: UnsafeRawBufferPointer) -> Int32 in + return EVP_DigestUpdate(md_ctx, message.baseAddress?.assumingMemoryBound(to: UInt8.self), self.data.count) + }) + guard rc == 1 else { + let source = "Signature verification failed." + if let reason = CryptorRSA.getLastError(source: source) { + + throw Error(code: ERR_VERIFICATION_FAILED, reason: reason) + } + throw Error(code: ERR_VERIFICATION_FAILED, reason: source + ": No OpenSSL error reported.") + } + + // Unlike other return values above, this return indicates if signature verifies or not + rc = signature.data.withUnsafeBytes({ (sig: UnsafeRawBufferPointer) -> Int32 in + // Wrapper for OpenSSL EVP_DigestVerifyFinal function defined in + // IBM-Swift/OpenSSL/shim.h, to provide compatibility with OpenSSL + // 1.0.1 and 1.0.2 on Ubuntu 14.04 and 16.04, respectively. + return SSL_EVP_digestVerifyFinal_wrapper(md_ctx, sig.baseAddress?.assumingMemoryBound(to: UInt8.self), signature.data.count) + }) + + return (rc == 1) ? true : false + + #else + + let signingAlgorithm: SecKeyAlgorithm + if usePSS { + if #available(macOS 10.13, iOS 11.0, watchOS 4.0, *) { + signingAlgorithm = usePSS ? algorithm.algorithmForPssSignature : algorithm.algorithmForSignature + } else { + throw Error(code: ERR_NOT_IMPLEMENTED, reason: "RSA-PSS only supported on macOS 10.13/iOS 10.0 and above.") + } + } else { + signingAlgorithm = algorithm.algorithmForSignature + } + + var response: Unmanaged? = nil + let result = SecKeyVerifySignature(key.reference, signingAlgorithm, self.data as CFData, signature.data as CFData, &response) + if response != nil { + + guard let error = response?.takeRetainedValue() else { + + throw Error(code: CryptorRSA.ERR_VERIFICATION_FAILED, reason: "Signature verification failed. Unable to determine error.") + } + + throw Error(code: CryptorRSA.ERR_VERIFICATION_FAILED, reason: "Signature verification failed with error: \(error)") + } + + return result + + #endif + } + + // MARK: --- Utility + + /// + /// Retrieve a digest of the data using the specified algorithm. + /// + /// - Parameters: + /// - algorithm: Algoririthm to use. + /// + /// - Returns: `Data` containing the digest. + /// + public func digest(using algorithm: Data.Algorithm) throws -> Data { + + return try self.data.digest(using: algorithm) + } + + /// + /// String representation of message in specified string encoding. + /// + /// - Parameters: + /// - encoding: Encoding to use during the string conversion + /// + /// - Returns: String representation of the message + /// + public func string(using encoding: String.Encoding) throws -> String { + + guard let str = String(data: data, encoding: encoding) else { + + throw Error(code: CryptorRSA.ERR_STRING_ENCODING, reason: "Couldn't convert data to string representation") + } + + return str + } + + } + + // MARK: - + + /// + /// Plaintext Data - Represents data not encrypted or signed. + /// + public class PlaintextData: RSAData { + + // MARK: Initializers + + /// + /// Initialize a new PlaintextData object. + /// + /// - Parameters: + /// - data: `Data` containing the data. + /// + /// - Returns: Newly initialized `PlaintextData`. + /// + internal init(with data: Data) { + + super.init(with: data, type: .plaintextType) + } + + /// + /// Creates a message from a plaintext string, with the specified encoding. + /// + /// - Parameters: + /// - string: String value of the plaintext message + /// - encoding: Encoding to use to generate the clear data + /// + /// - Returns: Newly initialized `RSAData`. + /// + internal override init(with string: String, using encoding: String.Encoding) throws { + + try super.init(with: string, using: encoding) + } + } + + // MARK: - + + /// + /// Encrypted Data - Represents data encrypted. + /// + public class EncryptedData: RSAData { + + // MARK: Initializers + + /// + /// Initialize a new EncryptedData object. + /// + /// - Parameters: + /// - data: `Data` containing the data. + /// + /// - Returns: Newly initialized EncryptedData`. + /// + public init(with data: Data) { + + super.init(with: data, type: .encryptedType) + } + + /// + /// Creates a RSAData with a encrypted base64-encoded string. + /// + /// - Parameters: + /// - base64String: Base64-encoded data of an encrypted message + /// + /// - Returns: Newly initialized `RSAData`. + /// + public override init(withBase64 base64String: String) throws { + + try super.init(withBase64: base64String) + } + } + + // MARK: - + + /// + /// Signed Data - Represents data that is signed. + /// + public class SignedData: RSAData { + + // MARK: -- Initializers + + /// + /// Initialize a new SignedData object. + /// + /// - Parameters: + /// - data: `Data` containing the data. + /// + /// - Returns: Newly initialized `SignedData`. + /// + public init(with data: Data) { + + super.init(with: data, type: .signedType) + } + + } + +} + diff --git a/Pods/BlueRSA/Sources/CryptorRSA/CryptorRSAConstants.swift b/Pods/BlueRSA/Sources/CryptorRSA/CryptorRSAConstants.swift new file mode 100644 index 0000000..24caf74 --- /dev/null +++ b/Pods/BlueRSA/Sources/CryptorRSA/CryptorRSAConstants.swift @@ -0,0 +1,74 @@ +// +// CryptorRSAConstants.swift +// CryptorRSA +// +// Created by Bill Abt on 1/18/17. +// +// Copyright © 2017 IBM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +// MARK: - + +@available(macOS 10.12, iOS 10.3, watchOS 3.3, tvOS 12.0, *) +public extension CryptorRSA { + + // MARK: Constants + + // MARK: Certificate Suffixes + + /// X509 Certificate Extension + static let CER_SUFFIX: String = ".cer" + + /// PEM Suffix + static let PEM_SUFFIX: String = ".pem" + + /// DER Suffix + static let DER_SUFFIX: String = ".der" + + // MARK: PEM Certificate Markers + + /// PEM Begin Marker + static let PEM_BEGIN_MARKER: String = "-----BEGIN CERTIFICATE-----" + + /// PEM End Marker + static let PEM_END_MARKER: String = "-----END CERTIFICATE-----" + + // MARK: Public Key Markers + + /// PK Begin Marker + static let PK_BEGIN_MARKER: String = "-----BEGIN PUBLIC KEY-----" + + /// PK End Marker + static let PK_END_MARKER: String = "-----END PUBLIC KEY-----" + + // MARK: Private Key Markers + + /// SK Begin Marker + static let SK_BEGIN_MARKER: String = "-----BEGIN RSA PRIVATE KEY-----" + + /// SK End Marker + static let SK_END_MARKER: String = "-----END RSA PRIVATE KEY-----" + + // MARK: Generic Key Markers + + /// Generic Begin Marker + static let GENERIC_BEGIN_MARKER: String = "-----BEGIN" + + /// Generic End Marker + static let GENERIC_END_MARKER: String = "-----END" + +} diff --git a/Pods/BlueRSA/Sources/CryptorRSA/CryptorRSADigest.swift b/Pods/BlueRSA/Sources/CryptorRSA/CryptorRSADigest.swift new file mode 100644 index 0000000..072c870 --- /dev/null +++ b/Pods/BlueRSA/Sources/CryptorRSA/CryptorRSADigest.swift @@ -0,0 +1,329 @@ +// +// CryptorRSADigest.swift +// CryptorRSA +// +// Created by Bill Abt on 1/18/17. +// +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + import CommonCrypto +#elseif os(Linux) + import OpenSSL + public typealias CC_LONG = size_t +#endif + +import Foundation + +// MARK: -- RSA Digest Extension for Data + +/// +/// Digest Handling Extension +/// +extension Data { + + // MARK: Enums + + /// + /// Enumerates available Digest algorithms + /// + public enum Algorithm { + + /// Secure Hash Algorithm 1 + case sha1 + + /// Secure Hash Algorithm 2 224-bit + case sha224 + + /// Secure Hash Algorithm 2 256-bit + case sha256 + + /// Secure Hash Algorithm 2 384-bit + case sha384 + + /// Secure Hash Algorithm 2 512-bit + case sha512 + + /// Secure Hash Algorithm 1 using AES-GCM envelope encryption. + /// use this algorithm for cross platform encryption/decryption. + case gcm + + /// Digest Length + public var length: CC_LONG { + + #if os(Linux) + + switch self { + + case .sha1: + return CC_LONG(SHA_DIGEST_LENGTH) + + case .sha224: + return CC_LONG(SHA224_DIGEST_LENGTH) + + case .sha256: + return CC_LONG(SHA256_DIGEST_LENGTH) + + case .sha384: + return CC_LONG(SHA384_DIGEST_LENGTH) + + case .sha512: + return CC_LONG(SHA512_DIGEST_LENGTH) + + case .gcm: + return CC_LONG(SHA_DIGEST_LENGTH) + } + + #else + + switch self { + + case .sha1: + return CC_LONG(CC_SHA1_DIGEST_LENGTH) + + case .sha224: + return CC_LONG(CC_SHA224_DIGEST_LENGTH) + + case .sha256: + return CC_LONG(CC_SHA256_DIGEST_LENGTH) + + case .sha384: + return CC_LONG(CC_SHA384_DIGEST_LENGTH) + + case .sha512: + return CC_LONG(CC_SHA512_DIGEST_LENGTH) + + case .gcm: + return CC_LONG(CC_SHA1_DIGEST_LENGTH) + } + + #endif + } + + #if os(Linux) + + // Hash, padding type + public var algorithmForSignature: (OpaquePointer?, Int32) { + + switch self { + + case .sha1: + return (.init(EVP_sha1()), RSA_PKCS1_PADDING) + + case .sha224: + return (.init(EVP_sha224()), RSA_PKCS1_PADDING) + + case .sha256: + return (.init(EVP_sha256()), RSA_PKCS1_PADDING) + + case .sha384: + return (.init(EVP_sha384()), RSA_PKCS1_PADDING) + + case .sha512: + return (.init(EVP_sha512()), RSA_PKCS1_PADDING) + + case .gcm: + return (.init(EVP_sha1()), RSA_PKCS1_PADDING) + + } + } + + // HMAC type, symmetric encryption, padding type + public var alogrithmForEncryption: (OpaquePointer?, OpaquePointer?, Int32) { + + switch self { + + case .sha1: + return (.init(EVP_sha1()), .init(EVP_aes_256_cbc()), RSA_PKCS1_OAEP_PADDING) + + case .sha224: + return (.init(EVP_sha224()), .init(EVP_aes_256_cbc()), RSA_PKCS1_OAEP_PADDING) + + case .sha256: + return (.init(EVP_sha256()), .init(EVP_aes_256_cbc()), RSA_PKCS1_OAEP_PADDING) + + case .sha384: + return (.init(EVP_sha384()), .init(EVP_aes_256_cbc()), RSA_PKCS1_OAEP_PADDING) + + case .sha512: + return (.init(EVP_sha512()), .init(EVP_aes_128_gcm()), RSA_PKCS1_OAEP_PADDING) + + case .gcm: + return (.init(EVP_sha1()), .init(EVP_aes_128_gcm()), RSA_PKCS1_OAEP_PADDING) + + } + } + + #else + + @available(macOS 10.12, iOS 10.0, watchOS 3.3, tvOS 12.0, *) + public var algorithmForSignature: SecKeyAlgorithm { + + switch self { + + case .sha1: + return .rsaSignatureMessagePKCS1v15SHA1 + + case .sha224: + return .rsaSignatureMessagePKCS1v15SHA224 + + case .sha256: + return .rsaSignatureMessagePKCS1v15SHA256 + + case .sha384: + return .rsaSignatureMessagePKCS1v15SHA384 + + case .sha512: + return .rsaSignatureMessagePKCS1v15SHA512 + + case .gcm: + return .rsaSignatureMessagePKCS1v15SHA1 + } + } + + @available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *) + var algorithmForPssSignature: SecKeyAlgorithm { + switch self { + + case .sha1: + return .rsaSignatureMessagePSSSHA1 + + case .sha224: + return .rsaSignatureMessagePSSSHA224 + + case .sha256: + return .rsaSignatureMessagePSSSHA256 + + case .sha384: + return .rsaSignatureMessagePSSSHA384 + + case .sha512: + return .rsaSignatureMessagePSSSHA512 + + case .gcm: + return .rsaSignatureMessagePSSSHA1 + + } + } + + @available(macOS 10.12, iOS 10.0, watchOS 3.3, tvOS 12.0, *) + public var alogrithmForEncryption: SecKeyAlgorithm { + + switch self { + + case .sha1: + return .rsaEncryptionOAEPSHA1AESGCM + + case .sha224: + return .rsaEncryptionOAEPSHA224AESGCM + + case .sha256: + return .rsaEncryptionOAEPSHA256AESGCM + + case .sha384: + return .rsaEncryptionOAEPSHA384AESGCM + + case .sha512: + return .rsaEncryptionOAEPSHA512AESGCM + + case .gcm: + return .rsaEncryptionOAEPSHA1AESGCM + + } + } + + #endif + + /// The platform/alogorithm dependent function to be used. + /// (UnsafePointer!, Int, UnsafeMutablePointer!) -> UnsafeMutablePointer! + #if os(Linux) + + public var engine: (_ data: UnsafePointer, _ len: CC_LONG, _ md: UnsafeMutablePointer) -> UnsafeMutablePointer? { + + switch self { + + case .sha1: + return SHA1 + + case .sha224: + return SHA224 + + case .sha256: + return SHA256 + + case .sha384: + return SHA384 + + case .sha512: + return SHA512 + + case .gcm: + return SHA1 + + } + } + + #else + + public var engine: (_ data: UnsafeRawPointer, _ len: CC_LONG, _ md: UnsafeMutablePointer) -> UnsafeMutablePointer? { + + switch self { + + case .sha1: + return CC_SHA1 + + case .sha224: + return CC_SHA224 + + case .sha256: + return CC_SHA256 + + case .sha384: + return CC_SHA384 + + case .sha512: + return CC_SHA512 + + case .gcm: + return CC_SHA1 + } + } + + #endif + } + + + // MARK: Functions + + /// + /// Return a digest of the data based on the alogorithm selected. + /// + /// - Parameters: + /// - alogorithm: The digest `Alogorithm` to use. + /// + /// - Returns: `Data` containing the data in digest form. + /// + public func digest(using alogorithm: Algorithm) throws -> Data { + + var hash = [UInt8](repeating: 0, count: Int(alogorithm.length)) + + self.withUnsafeBytes { ptr in + guard let baseAddress = ptr.baseAddress else { return } + _ = alogorithm.engine(baseAddress.assumingMemoryBound(to: UInt8.self), CC_LONG(self.count), &hash) + } + + return Data(hash) + } +} diff --git a/Pods/BlueRSA/Sources/CryptorRSA/CryptorRSAErrors.swift b/Pods/BlueRSA/Sources/CryptorRSA/CryptorRSAErrors.swift new file mode 100644 index 0000000..5c56db1 --- /dev/null +++ b/Pods/BlueRSA/Sources/CryptorRSA/CryptorRSAErrors.swift @@ -0,0 +1,108 @@ +// +// CryptorRSAErrors.swift +// CryptorRSA +// +// Created by Bill Abt on 1/18/17. +// +// Copyright © 2017 IBM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +// MARK: - + +@available(macOS 10.12, iOS 10.3, watchOS 3.3, tvOS 12.0, *) +extension CryptorRSA { + + // MARK: Constants + + // MARK: -- Generic + + // MARK: -- Errors: Domain and Codes + + public static let ERR_DOMAIN = "com.ibm.oss.CryptorRSA.ErrorDomain" + + public static let ERR_ADD_KEY = -9999 + public static let ERR_DELETE_KEY = -9998 + public static let ERR_STRIP_PK_HEADER = -9997 + public static let ERR_INIT_PK = -9996 + public static let ERR_BASE64_PEM_DATA = -9995 + public static let ERR_STRING_ENCODING = -9994 + public static let ERR_KEY_NOT_PUBLIC = -9993 + public static let ERR_KEY_NOT_PRIVATE = -9992 + public static let ERR_NOT_ENCRYPTED = -9991 + public static let ERR_ENCRYPTION_FAILED = -9990 + public static let ERR_NOT_SIGNED_DATA = -9989 + public static let ERR_NOT_PLAINTEXT = -9988 + public static let ERR_DECRYPTION_FAILED = -9997 + public static let ERR_SIGNING_FAILED = -9986 + public static let ERR_VERIFICATION_FAILED = -9985 + public static let ERR_CREATE_CERT_FAILED = -9984 + public static let ERR_EXTRACT_PUBLIC_KEY_FAILED = -9983 + public static let ERR_EXTRACT_PRIVATE_KEY_FAILED = -9983 + public static let ERR_NOT_IMPLEMENTED = -9982 + + // MARK: -- Error + + /// + /// `RSA` specific error structure. + /// + public struct Error: Swift.Error, CustomStringConvertible { + + // MARK: -- Public Properties + + /// + /// The error domain. + /// + public let domain: String = ERR_DOMAIN + + /// + /// The error code: **see constants above for possible errors** (Readonly) + /// + public internal(set) var errorCode: Int32 + + /// + /// The reason for the error **(if available)** (Readonly) + /// + public internal(set) var errorReason: String? + + /// + /// Returns a string description of the error. (Readonly) + /// + public var description: String { + + let reason: String = self.errorReason ?? "Reason: Unavailable" + return "Error code: \(self.errorCode)(0x\(String(self.errorCode, radix: 16, uppercase: true))), \(reason)" + } + + // MARK: -- Public Functions + + /// + /// Initializes an Error Instance + /// + /// - Parameters: + /// - code: Error code + /// - reason: Optional Error Reason + /// + /// - Returns: Error instance + /// + public init(code: Int, reason: String?) { + + self.errorCode = Int32(code) + self.errorReason = reason + } + + } +} diff --git a/Pods/BlueRSA/Sources/CryptorRSA/CryptorRSAKey.swift b/Pods/BlueRSA/Sources/CryptorRSA/CryptorRSAKey.swift new file mode 100644 index 0000000..5f60317 --- /dev/null +++ b/Pods/BlueRSA/Sources/CryptorRSA/CryptorRSAKey.swift @@ -0,0 +1,932 @@ +// +// CryptorRSAKey.swift +// CryptorRSA +// +// Created by Bill Abt on 1/18/17. +// +// Copyright © 2017 IBM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + import CommonCrypto +#elseif os(Linux) + import OpenSSL +#endif + +import Foundation + +// MARK: - + +@available(macOS 10.12, iOS 10.3, watchOS 3.3, tvOS 12.0, *) +extension CryptorRSA { + + // MARK: Type Aliases + + #if os(Linux) + + public typealias NativeKey = OpaquePointer? + + #else + + public typealias NativeKey = SecKey + + #endif + + // MARK: Class Functions + + // MARK: -- Public Key Creation + + /// + /// Creates a public key with DER data. + /// + /// - Parameters: + /// - data: Key data + /// + /// - Returns: New `PublicKey` instance. + /// + public class func createPublicKey(with data: Data) throws -> PublicKey { + + return try PublicKey(with: data) + } + + /// + /// Creates a public key by extracting it from a certificate. + /// + /// - Parameters: + /// - data: `Data` representing the certificate. + /// + /// - Returns: New `PublicKey` instance. + /// + public class func createPublicKey(extractingFrom data: Data) throws -> PublicKey { + + // Extact the data as a base64 string... + let str = String(data: data, encoding: .utf8) + guard let tmp = str else { + + throw Error(code: ERR_CREATE_CERT_FAILED, reason: "Unable to create certificate from certificate data, incorrect format.") + } + + // Get the Base64 representation of the PEM encoded string after stripping off the PEM markers... + let base64 = try CryptorRSA.base64String(for: tmp) + let data = Data(base64Encoded: base64)! + + // Call the internal function to finish up... + return try CryptorRSA.createPublicKey(data: data) + + } + + /// + /// Creates a key with a base64-encoded string. + /// + /// - Parameters: + /// - base64String: Base64-encoded key data + /// + /// - Returns: New `PublicKey` instance. + /// + public class func createPublicKey(withBase64 base64String: String) throws -> PublicKey { + + guard let data = Data(base64Encoded: base64String, options: [.ignoreUnknownCharacters]) else { + + throw Error(code: ERR_INIT_PK, reason: "Couldn't decode base64 string") + } + + return try PublicKey(with: data) + } + + /// + /// Creates a key with a PEM string. + /// + /// - Parameters: + /// - pemString: PEM-encoded key string + /// + /// - Returns: New `PublicKey` instance. + /// + public class func createPublicKey(withPEM pemString: String) throws -> PublicKey { + + // Get the Base64 representation of the PEM encoded string after stripping off the PEM markers + let base64String = try CryptorRSA.base64String(for: pemString) + + return try createPublicKey(withBase64: base64String) + + } + + /// + /// Creates a key with a PEM file. + /// + /// - Parameters: + /// - pemName: Name of the PEM file + /// - path: Path where the file is located. + /// + /// - Returns: New `PublicKey` instance. + /// + public class func createPublicKey(withPEMNamed pemName: String, onPath path: String) throws -> PublicKey { + + var certName = pemName + if !pemName.hasSuffix(PEM_SUFFIX) { + + certName = pemName.appending(PEM_SUFFIX) + } + + let fullPath = URL(fileURLWithPath: #file).appendingPathComponent( path.appending(certName) ).standardized + + let keyString = try String(contentsOf: fullPath, encoding: .utf8) + + return try createPublicKey(withPEM: keyString) + } + + /// + /// Creates a key with a DER file. + /// + /// - Parameters: + /// - derName: Name of the DER file + /// - path: Path where the file is located. + /// + /// - Returns: New `PublicKey` instance. + /// + public class func createPublicKey(withDERNamed derName: String, onPath path: String) throws -> PublicKey { + + var certName = derName + if !derName.hasSuffix(DER_SUFFIX) { + + certName = derName.appending(DER_SUFFIX) + } + + let fullPath = URL(fileURLWithPath: #file).appendingPathComponent( path.appending(certName) ).standardized + + let data = try Data(contentsOf: fullPath) + + return try PublicKey(with: data) + } + + /// + /// Creates a public key by extracting it from a certificate. + /// + /// - Parameters: + /// - certName: Name of the certificate file. + /// - path: Path where the file is located. + /// + /// - Returns: New `PublicKey` instance. + /// + public class func createPublicKey(extractingFrom certName: String, onPath path: String) throws -> PublicKey { + + var certNameFull = certName + if !certName.hasSuffix(CER_SUFFIX) { + + certNameFull = certName.appending(CER_SUFFIX) + } + + let fullPath = URL(fileURLWithPath: #file).appendingPathComponent( path.appending(certNameFull) ).standardized + + // Get the Base64 representation of the PEM encoded string after stripping off the PEM markers... + let tmp = try String(contentsOf: fullPath, encoding: .utf8) + let base64 = try CryptorRSA.base64String(for: tmp) + let data = Data(base64Encoded: base64)! + + return try CryptorRSA.createPublicKey(data: data) + } + + /// + /// Creates a key with a PEM file. + /// + /// - Parameters: + /// - pemName: Name of the PEM file + /// - bundle: Bundle in which to look for the PEM file. Defaults to the main bundle. + /// + /// - Returns: New `PublicKey` instance. + /// + public class func createPublicKey(withPEMNamed pemName: String, in bundle: Bundle = Bundle.main) throws -> PublicKey { + + guard let path = bundle.path(forResource: pemName, ofType: PEM_SUFFIX) else { + + throw Error(code: ERR_INIT_PK, reason: "Couldn't find a PEM file named '\(pemName)'") + } + + let keyString = try String(contentsOf: URL(fileURLWithPath: path), encoding: .utf8) + + return try createPublicKey(withPEM: keyString) + } + + /// + /// Creates a key with a DER file. + /// + /// - Parameters: + /// - derName: Name of the DER file + /// - bundle: Bundle in which to look for the DER file. Defaults to the main bundle. + /// + /// - Returns: New `PublicKey` instance. + /// + public class func createPublicKey(withDERNamed derName: String, in bundle: Bundle = Bundle.main) throws -> PublicKey { + + guard let path = bundle.path(forResource: derName, ofType: DER_SUFFIX) else { + + throw Error(code: ERR_INIT_PK, reason: "Couldn't find a DER file named '\(derName)'") + } + + let data = try Data(contentsOf: URL(fileURLWithPath: path)) + + return try PublicKey(with: data) + } + + /// + /// Creates a public key by extracting it from a certificate. + /// + /// - Parameters: + /// - certName: Name of the certificate file. + /// - bundle: Bundle in which to look for the DER file. Defaults to the main bundle. + /// + /// - Returns: New `PublicKey` instance. + /// + public class func createPublicKey(extractingFrom certName: String, in bundle: Bundle = Bundle.main) throws -> PublicKey { + + guard let path = bundle.path(forResource: certName, ofType: CER_SUFFIX) else { + + throw Error(code: ERR_INIT_PK, reason: "Couldn't find a certificate file named '\(certName)'") + } + + // Import the data from the file... + let tmp = try String(contentsOf: URL(fileURLWithPath: path)) + let base64 = try CryptorRSA.base64String(for: tmp) + let data = Data(base64Encoded: base64)! + + // Call the internal function to finish up... + return try CryptorRSA.createPublicKey(data: data) + } + + /// + /// Creates a public key by extracting it from certificate data. + /// + /// - Parameters: + /// - data: `Data` representing the certificate. + /// + /// - Returns: New `PublicKey` instance. + /// + internal class func createPublicKey(data: Data) throws -> PublicKey { + + #if os(Linux) + + let certbio = BIO_new(BIO_s_mem()) + defer { + BIO_free(certbio) + } + + // Move the key data to BIO + try data.withUnsafeBytes() { (buffer: UnsafeRawBufferPointer) in + + let len = BIO_write(certbio, buffer.baseAddress?.assumingMemoryBound(to: UInt8.self), Int32(data.count)) + guard len != 0 else { + let source = "Couldn't create BIO reference from key data" + if let reason = CryptorRSA.getLastError(source: source) { + + throw Error(code: ERR_ADD_KEY, reason: reason) + } + throw Error(code: ERR_ADD_KEY, reason: source + ": No OpenSSL error reported.") + } + + // The below is equivalent of BIO_flush... + BIO_ctrl(certbio, BIO_CTRL_FLUSH, 0, nil) + } + let cert = d2i_X509_bio(certbio, nil) + if cert == nil { + print("Error loading cert into memory\n") + throw Error(code: ERR_CREATE_CERT_FAILED, reason: "Error loading cert into memory.") + } + + // Extract the certificate's public key data. + let evp_key: OpaquePointer? = .init(X509_get_pubkey(cert)) + if evp_key == nil { + throw Error(code: ERR_CREATE_CERT_FAILED, reason: "Error getting public key from certificate") + } + + return PublicKey(with: evp_key) + + #else + + // Create a DER-encoded X.509 certificate object from the DER data... + let certificateData = SecCertificateCreateWithData(nil, data as CFData) + guard let certData = certificateData else { + + throw Error(code: ERR_CREATE_CERT_FAILED, reason: "Unable to create certificate from certificate data.") + } + + var key: SecKey? = nil + + #if swift(>=4.2) + #if os(macOS) + if #available(macOS 10.14, *) { + key = SecCertificateCopyKey(certData) + } else { + // Now extract the public key from it... + let status: OSStatus = withUnsafeMutablePointer(to: &key) { ptr in + + // Retrieves the public key from a certificate... + SecCertificateCopyPublicKey(certData, UnsafeMutablePointer(ptr)) + } + + if status != errSecSuccess { + + throw Error(code: ERR_EXTRACT_PUBLIC_KEY_FAILED, reason: "Unable to extract public key from data.") + } + } + #else + let copyKey: (SecCertificate) -> SecKey? + + #if targetEnvironment(macCatalyst) + copyKey = SecCertificateCopyKey + #else + if #available(iOS 12.0, watchOS 5.0, *) { + copyKey = SecCertificateCopyKey + } else { + copyKey = SecCertificateCopyPublicKey + } + #endif + + key = copyKey(certData) + #endif + #else + #if os(macOS) + + // Now extract the public key from it... + let status: OSStatus = withUnsafeMutablePointer(to: &key) { ptr in + + // Retrieves the public key from a certificate... + SecCertificateCopyPublicKey(certData, UnsafeMutablePointer(ptr)) + } + + if status != errSecSuccess { + + throw Error(code: ERR_EXTRACT_PUBLIC_KEY_FAILED, reason: "Unable to extract public key from data.") + } + + #else + + key = SecCertificateCopyPublicKey(certData) + + #endif + #endif + + guard let createdKey = key else { + + throw Error(code: ERR_EXTRACT_PUBLIC_KEY_FAILED, reason: "Unable to extract public key from data.") + } + + return PublicKey(with: createdKey) + + #endif + } + + // MARK: -- Private Key Creation + + /// + /// Creates a private key with data. + /// + /// - Parameters: + /// - data: Key data + /// + /// - Returns: New `PrivateKey` instance. + /// + public class func createPrivateKey(with data: Data) throws -> PrivateKey { + + return try PrivateKey(with: data) + } + + /// + /// Creates a key with a base64-encoded string. + /// + /// - Parameters: + /// - base64String: Base64-encoded key data + /// + /// - Returns: New `PrivateKey` instance. + /// + public class func createPrivateKey(withBase64 base64String: String) throws -> PrivateKey { + + guard let data = Data(base64Encoded: base64String, options: [.ignoreUnknownCharacters]) else { + + throw Error(code: ERR_INIT_PK, reason: "Couldn't decode base 64 string") + } + + return try PrivateKey(with: data) + } + + /// + /// Creates a key with a PEM string. + /// + /// - Parameters: + /// - pemString: PEM-encoded key string + /// + /// - Returns: New `PrivateKey` instance. + /// + public class func createPrivateKey(withPEM pemString: String) throws -> PrivateKey { + + let base64String = try CryptorRSA.base64String(for: pemString) + + return try CryptorRSA.createPrivateKey(withBase64: base64String) + + } + + /// + /// Creates a key with a PEM file. + /// + /// - Parameters: + /// - pemName: Name of the PEM file + /// - path: Path where the file is located. + /// + /// - Returns: New `PrivateKey` instance. + /// + public class func createPrivateKey(withPEMNamed pemName: String, onPath path: String) throws -> PrivateKey { + + var certName = pemName + if !pemName.hasSuffix(PEM_SUFFIX) { + + certName = pemName.appending(PEM_SUFFIX) + } + let fullPath = URL(fileURLWithPath: #file).appendingPathComponent( path.appending(certName) ).standardized + + let keyString = try String(contentsOf: fullPath, encoding: .utf8) + + return try CryptorRSA.createPrivateKey(withPEM: keyString) + } + + /// + /// Creates a key with a DER file. + /// + /// - Parameters: + /// - derName: Name of the DER file + /// - path: Path where the file is located. + /// + /// - Returns: New `PrivateKey` instance. + /// + public class func createPrivateKey(withDERNamed derName: String, onPath path: String) throws -> PrivateKey { + + var certName = derName + if !derName.hasSuffix(DER_SUFFIX) { + + certName = derName.appending(DER_SUFFIX) + } + let fullPath = URL(fileURLWithPath: #file).appendingPathComponent( path.appending(certName) ).standardized + + let data = try Data(contentsOf: fullPath) + + return try PrivateKey(with: data) + } + + /// + /// Creates a key with a PEM file. + /// + /// - Parameters: + /// - pemName: Name of the PEM file + /// - bundle: Bundle in which to look for the PEM file. Defaults to the main bundle. + /// + /// - Returns: New `PrivateKey` instance. + /// + public class func createPrivateKey(withPEMNamed pemName: String, in bundle: Bundle = Bundle.main) throws -> PrivateKey { + + guard let path = bundle.path(forResource: pemName, ofType: PEM_SUFFIX) else { + + throw Error(code: ERR_INIT_PK, reason: "Couldn't find a PEM file named '\(pemName)'") + } + + let keyString = try String(contentsOf: URL(fileURLWithPath: path), encoding: .utf8) + + return try CryptorRSA.createPrivateKey(withPEM: keyString) + } + + /// + /// Creates a key with a DER file. + /// + /// - Parameters: + /// - derName: Name of the DER file + /// - bundle: Bundle in which to look for the DER file. Defaults to the main bundle. + /// + /// - Returns: New `PrivateKey` instance. + /// + public class func createPrivateKey(withDERNamed derName: String, in bundle: Bundle = Bundle.main) throws -> PrivateKey { + + guard let path = bundle.path(forResource: derName, ofType: DER_SUFFIX) else { + + throw Error(code: ERR_INIT_PK, reason: "Couldn't find a DER file named '\(derName)'") + } + + let data = try Data(contentsOf: URL(fileURLWithPath: path)) + + return try PrivateKey(with: data) + } + + /// Create a new RSA public/private key pair. + /// + /// - Parameters: + /// - keySize: The size of the generated RSA keys in bits. + /// + /// - Returns: A tuple containing the (`PrivateKey`, `PublicKey`) instances. + /// + public class func makeKeyPair(_ keySize: RSAKey.KeySize) throws -> (PrivateKey, PublicKey) { + + #if os(Linux) + var pkey = EVP_PKEY_new() + let ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, nil) + defer { + EVP_PKEY_CTX_free(ctx) + } + guard EVP_PKEY_keygen_init(ctx) == 1, + EVP_PKEY_CTX_ctrl(ctx, -1, EVP_PKEY_OP_KEYGEN, EVP_PKEY_CTRL_RSA_KEYGEN_BITS, Int32(keySize.bits), nil) == 1, + EVP_PKEY_keygen(ctx, &pkey) == 1 + else { + EVP_PKEY_free(pkey) + throw Error(code: ERR_INIT_PK, reason: "Could not generate rsa pair for \(keySize.bits) bits") + } + let privKey = PrivateKey(with: .make(optional: pkey)) + let publicPem = try RSAKey.getPEMString(reference: privKey.reference, keyType: .publicType, stripped: false) + let pubKey = try CryptorRSA.createPublicKey(withPEM: publicPem) + + return(privKey, pubKey) + + #else + + let parameters: [String: AnyObject] = [ + kSecAttrKeyType as String: kSecAttrKeyTypeRSA, + kSecAttrKeySizeInBits as String: keySize.bits as AnyObject, + kSecPublicKeyAttrs as String: [ kSecAttrIsPermanent as String: true as AnyObject ] as AnyObject, + kSecPrivateKeyAttrs as String: [ kSecAttrIsPermanent as String: true as AnyObject ] as AnyObject, + ] + var pubKey, privKey: SecKey? + let status = SecKeyGeneratePair(parameters as CFDictionary, &pubKey, &privKey) + guard status == 0, let newPubKey = pubKey, let newPrivKey = privKey else { + throw Error(code: ERR_INIT_PK, reason: "Could not generate rsa pair for \(keySize.bits) bits") + } + let privateKey = PrivateKey(with: newPrivKey) + let publicKey = PublicKey(with: newPubKey) + + return (privateKey, publicKey) + #endif + } + + + // MARK: - + + /// + /// RSA Key Creation and Handling + /// + public class RSAKey { + + // MARK: Enums + + /// Denotes the type of key this represents. + public enum KeyType { + + /// Public + case publicType + + /// Private + case privateType + } + + /// Denotes the size of the RSA key. + public struct KeySize { + let bits: Int + /// A 1024 bit RSA key. Not recommended since this may become breakable in the near future. + public static let bits1024 = KeySize(bits: 1024) + /// A 2048 bit RSA key. Recommended if security will not be required beyond 2030. + public static let bits2048 = KeySize(bits: 2048) + /// A 3072 bit RSA key. Recommended if security is required beyond 2030. + public static let bits3072 = KeySize(bits: 3072) + /// A 4096 bit RSA key. + public static let bits4096 = KeySize(bits: 4096) + } + + // MARK: Properties + + /// The RSA key as a PKCS#1 PEM String + public let pemString: String + + /// The stored key + internal let reference: NativeKey + + #if os(Linux) + var publicKeyBytes: Data? + deinit { + EVP_PKEY_free(.make(optional: reference)) + } + #endif + + /// Represents the type of key data contained. + public internal(set) var type: KeyType = .publicType + + // MARK: Initializers + + /// + /// Create a key using key data (in DER format). + /// + /// - Parameters: + /// - data: Key data. + /// - type: Type of key data. + /// + /// - Returns: New `RSAKey` instance. + /// + internal init(with data: Data, type: KeyType) throws { + + var data = data + + // If data is a PEM String, strip the headers and convert to der. + if let pemString = String(data: data, encoding: .utf8), + let base64String = try? CryptorRSA.base64String(for: pemString), + let base64Data = Data(base64Encoded: base64String) { + data = base64Data + } + data = try CryptorRSA.stripX509CertificateHeader(for: data) + self.pemString = CryptorRSA.convertDerToPem(from: data, type: type) + self.type = type + reference = try CryptorRSA.createKey(from: data, type: type) + #if os(Linux) + if let pubString = try? RSAKey.getPEMString(reference: reference, keyType: .publicType, stripped: true), + let base64String = try? CryptorRSA.base64String(for: pubString), + let derData = Data(base64Encoded: base64String) { + self.publicKeyBytes = derData + } + #endif + } + + /// + /// Create a key using a native key. + /// + /// - Parameters: + /// - nativeKey: Native key representation. + /// - type: Type of key. + /// + /// - Returns: New `RSAKey` instance. + /// + internal init(with nativeKey: NativeKey, type: KeyType) { + + self.type = type + self.reference = nativeKey + self.pemString = (try? RSAKey.getPEMString(reference: nativeKey, type: type)) ?? "" + #if os(Linux) + if let base64String = try? CryptorRSA.base64String(for: pemString), + let derData = Data(base64Encoded: base64String) { + self.publicKeyBytes = derData + } + #endif + } + + #if os(Linux) && !swift(>=4.1) + + /// + /// Create a key using a native key. + /// + /// - Parameters: + /// - nativeKey: Pointer to RSA key structure. + /// - type: Type of key. + /// + /// - Returns: New `RSAKey` instance. + /// + internal init(with nativeKey: UnsafeMutablePointer, type: KeyType) { + + self.type = type + self.reference = .make(optional: nativeKey) + self.pemString = (try? RSAKey.getPEMString(reference: .init(nativeKey), type: type)) ?? "" + if let base64String = try? CryptorRSA.base64String(for: pemString), + let derData = Data(base64Encoded: base64String) { + self.publicKeyBytes = derData + } + } + + #endif + + /// + /// Get the RSA key as a PEM String. + /// + /// - Returns: The RSA Key in PEM format. + /// + static func getPEMString(reference: NativeKey, type: KeyType) throws -> String { + + #if os(Linux) + return try getPEMString(reference: reference, keyType: type, stripped: true) + #else + var error: Unmanaged? = nil + guard let keyBytes = SecKeyCopyExternalRepresentation(reference, &error) else { + guard let error = error?.takeRetainedValue() else { + throw Error(code: ERR_INIT_PK, reason: "Couldn't read PEM String") + } + throw error + } + return CryptorRSA.convertDerToPem(from: keyBytes as Data, type: type) + #endif + } + + #if os(Linux) + /// + /// Get a PEM string of a native key. + /// + /// - Parameters: + /// - reference: Native key. + /// - keyType: Type of key. + /// - stripped: `true` to return string stripped, `false` otherwise. + /// + /// - Returns: The PEM string. + /// + static func getPEMString(reference: NativeKey, keyType: KeyType, stripped: Bool) throws -> String { + let asn1Bio = BIO_new(BIO_s_mem()) + defer { BIO_free_all(asn1Bio) } + if keyType == .publicType { + PEM_write_bio_PUBKEY(asn1Bio, .make(optional: reference)) + } else { + PEM_write_bio_PrivateKey(asn1Bio, .make(optional: reference), nil, nil, 0, nil, nil) + } + // 4096 bit rsa PEM key is 3272 bytes of data + let asn1 = UnsafeMutablePointer.allocate(capacity: 3500) + let readLength = BIO_read(asn1Bio, asn1, 3500) + let pemData = Data(bytes: asn1, count: Int(readLength)) + #if swift(>=4.1) + asn1.deallocate() + #else + asn1.deallocate(capacity: 3500) + #endif + guard let pemString = String(data: pemData, encoding: .utf8) else { + throw Error(code: ERR_INIT_PK, reason: "Couldn't utf8 decode pemString") + } + if !stripped { + return pemString + } else { + let derString = try CryptorRSA.base64String(for: pemString) + guard let derData = Data(base64Encoded: derString) else { + throw Error(code: ERR_INIT_PK, reason: "Couldn't read PEM String") + } + let strippedDer = try CryptorRSA.stripX509CertificateHeader(for: derData) + let pkcs1PEM = CryptorRSA.convertDerToPem(from: strippedDer, type: keyType) + return pkcs1PEM + } + } + #endif + } + // MARK: - + + /// + /// Public Key - Represents public key data. + /// + public class PublicKey: RSAKey { + + /// MARK: Statics + + /// Regular expression for the PK using the begin and end markers. + static let publicKeyRegex: NSRegularExpression? = { + + let publicKeyRegex = "(\(CryptorRSA.PK_BEGIN_MARKER).+?\(CryptorRSA.PK_END_MARKER))" + return try? NSRegularExpression(pattern: publicKeyRegex, options: .dotMatchesLineSeparators) + }() + + // MARK: -- Static Functions + + /// + /// Takes an input string, scans for public key sections, and then returns a Key for any valid keys found + /// - This method scans the file for public key armor - if no keys are found, an empty array is returned + /// - Each public key block found is "parsed" by `publicKeyFromPEMString()` + /// - should that method throw, the error is _swallowed_ and not rethrown + /// + /// - Parameters: + /// - pemString: The string to use to parse out values + /// + /// - Returns: An array of `PublicKey` objects containing just public keys. + /// + public static func publicKeys(withPEM pemString: String) -> [PublicKey] { + + // If our regexp isn't valid, or the input string is empty, we can't move forward… + guard let publicKeyRegexp = publicKeyRegex, pemString.count > 0 else { + return [] + } + + let all = NSRange( + location: 0, + length: pemString.count + ) + + let matches = publicKeyRegexp.matches( + in: pemString, + options: NSRegularExpression.MatchingOptions(rawValue: 0), + range: all + ) + + #if swift(>=4.1) + + let keys = matches.compactMap { result -> PublicKey? in + + let match = result.range(at: 1) + let start = pemString.index(pemString.startIndex, offsetBy: match.location) + let end = pemString.index(start, offsetBy: match.length) + + let range = start.. PublicKey? in + + let match = result.range(at: 1) + let start = pemString.index(pemString.startIndex, offsetBy: match.location) + let end = pemString.index(start, offsetBy: match.length) + + let range = start..=4.1) + + /// + /// Create a key using a native key. + /// + /// - Parameters: + /// - nativeKey: Pointer to RSA key structure. + /// + /// - Returns: New `RSAKey` instance. + /// + public init(with nativeKey: UnsafeMutablePointer) { + + super.init(with: nativeKey, type: .publicType) + } + + #endif + } + + // MARK: - + + /// + /// Private Key - Represents private key data. + /// + public class PrivateKey: RSAKey { + + // MARK: -- Initializers + + /// + /// Create a private key using key data. + /// + /// - Parameters: + /// - data: Key data + /// + /// - Returns: New `PrivateKey` instance. + /// + public init(with data: Data) throws { + try super.init(with: data, type: .privateType) + } + + /// + /// Create a key using a native key. + /// + /// - Parameters: + /// - nativeKey: Native key representation. + /// + /// - Returns: New `PrivateKey` instance. + /// + public init(with nativeKey: NativeKey) { + + super.init(with: nativeKey, type: .privateType) + } + } +} diff --git a/Pods/BlueRSA/Sources/CryptorRSA/CryptorRSAUtilities.swift b/Pods/BlueRSA/Sources/CryptorRSA/CryptorRSAUtilities.swift new file mode 100644 index 0000000..6e36dfc --- /dev/null +++ b/Pods/BlueRSA/Sources/CryptorRSA/CryptorRSAUtilities.swift @@ -0,0 +1,393 @@ +// +// Utilities.swift +// CryptorRSA +// +// Created by Bill Abt on 1/17/17. +// +// Copyright © 2017 IBM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + import CommonCrypto +#elseif os(Linux) + import OpenSSL +#endif + +import Foundation + +// MARK: -- RSAUtilities + +/// +/// Various RSA Related Utility Functions +/// +@available(macOS 10.12, iOS 10.3, watchOS 3.3, tvOS 12.0, *) +public extension CryptorRSA { + +#if os(Linux) + + /// + /// Create a key from key data. + /// + /// - Parameters: + /// - keyData: `Data` representation of the key. + /// - type: Type of key data. + /// + /// - Returns: `RSA` representation of the key. + /// + static func createKey(from keyData: Data, type: CryptorRSA.RSAKey.KeyType) throws -> NativeKey { + + var keyData = keyData + + // If data is a PEM String, strip the headers and convert to der. + if let pemString = String(data: keyData, encoding: .utf8), + let base64String = try? CryptorRSA.base64String(for: pemString), + let base64Data = Data(base64Encoded: base64String) { + keyData = base64Data + } + + let headerKey = CryptorRSA.addX509CertificateHeader(for: keyData) + + // Create a memory BIO... + let bio = BIO_new(BIO_s_mem()) + + defer { + BIO_free(bio) + } + // Create a BIO object with the key data... + try headerKey.withUnsafeBytes() { (buffer: UnsafeRawBufferPointer) in + + let len = BIO_write(bio, buffer.baseAddress?.assumingMemoryBound(to: UInt8.self), Int32(headerKey.count)) + guard len != 0 else { + let source = "Couldn't create BIO reference from key data" + if let reason = CryptorRSA.getLastError(source: source) { + + throw Error(code: ERR_ADD_KEY, reason: reason) + } + throw Error(code: ERR_ADD_KEY, reason: source + ": No OpenSSL error reported.") + } + // The below is equivalent of BIO_flush... + BIO_ctrl(bio, BIO_CTRL_FLUSH, 0, nil) + } + + var evp_key: OpaquePointer? + + // Read in the key data and process depending on key type... + if type == .publicType { + + evp_key = .init(d2i_PUBKEY_bio(bio, nil)) + + } else { + + evp_key = .init(d2i_PrivateKey_bio(bio, nil)) + } + return evp_key + } + + /// + /// Retrieve the OpenSSL error and text. + /// + /// - Parameters: + /// - source: The string describing the error. + /// + /// - Returns: `String` containing the error or `nil` if no error found. + /// + static func getLastError(source: String) -> String? { + + var errorString: String + + let errorCode = Int32(ERR_get_error()) + + if errorCode == 0 { + return nil + } + + if let errorStr = ERR_reason_error_string(UInt(errorCode)) { + errorString = String(validatingUTF8: errorStr)! + } else { + errorString = "Could not determine error reason." + } + + let reason = "ERROR: \(source), code: \(errorCode), reason: \(errorString)" + return reason + } + +#else + + /// + /// Create a key from key data. + /// + /// - Parameters: + /// - keyData: `Data` representation of the key. + /// - type: Type of key data. + /// + /// - Returns: `SecKey` representation of the key. + /// + static func createKey(from keyData: Data, type: CryptorRSA.RSAKey.KeyType) throws -> NativeKey { + + let keyClass = type == .publicType ? kSecAttrKeyClassPublic : kSecAttrKeyClassPrivate + + let sizeInBits = keyData.count * MemoryLayout.size + let keyDict: [CFString: Any] = [ + kSecAttrKeyType: kSecAttrKeyTypeRSA, + kSecAttrKeyClass: keyClass, + kSecAttrKeySizeInBits: NSNumber(value: sizeInBits) + ] + + guard let key = SecKeyCreateWithData(keyData as CFData, keyDict as CFDictionary, nil) else { + + throw Error(code: ERR_ADD_KEY, reason: "Couldn't create key reference from key data") + } + + return key + + } + +#endif + + /// + /// Convert DER data to PEM data. + /// + /// - Parameters: + /// - derData: `Data` in DER format. + /// - type: Type of key data. + /// + /// - Returns: PEM `Data` representation. + /// + static func convertDerToPem(from derData: Data, type: CryptorRSA.RSAKey.KeyType) -> String { + + // First convert the DER data to a base64 string... + let base64String = derData.base64EncodedString() + + // Split the string into strings of length 65... + let lines = base64String.split(to: 65) + + // Join those lines with a new line... + let joinedLines = lines.joined(separator: "\n") + + // Add the appropriate header and footer depending on whether the key is public or private... + if type == .publicType { + + return ("-----BEGIN RSA PUBLIC KEY-----\n" + joinedLines + "\n-----END RSA PUBLIC KEY-----") + + } else { + + return (CryptorRSA.SK_BEGIN_MARKER + "\n" + joinedLines + "\n" + CryptorRSA.SK_END_MARKER) + } + } + + /// + /// Get the Base64 representation of a PEM encoded string after stripping off the PEM markers. + /// + /// - Parameters: + /// - pemString: `String` containing PEM formatted data. + /// + /// - Returns: Base64 encoded `String` containing the data. + /// + static func base64String(for pemString: String) throws -> String { + + // Filter looking for new lines... + var lines = pemString.components(separatedBy: "\n").filter { line in + return !line.hasPrefix(CryptorRSA.GENERIC_BEGIN_MARKER) && !line.hasPrefix(CryptorRSA.GENERIC_END_MARKER) + } + + // No lines, no data... + guard lines.count != 0 else { + throw Error(code: ERR_BASE64_PEM_DATA, reason: "Couldn't get data from PEM key: no data available after stripping headers.") + } + + // Strip off any carriage returns... + lines = lines.map { $0.replacingOccurrences(of: "\r", with: "") } + + return lines.joined(separator: "") + } + + /// + /// This function strips the x509 from a provided ASN.1 DER public key. If the key doesn't contain a header, + /// the DER data is returned as is. + /// + /// - Parameters: + /// - keyData: `Data` containing the public key with or without the x509 header. + /// + /// - Returns: `Data` containing the public with header (if present) removed. + /// + static func stripX509CertificateHeader(for keyData: Data) throws -> Data { + + // If private key in pkcs8 format, strip the header + if keyData[26] == 0x30 { + return(keyData.advanced(by: 26)) + } + + let count = keyData.count / MemoryLayout.size + + guard count > 0 else { + + throw Error(code: ERR_STRIP_PK_HEADER, reason: "Provided public key is empty") + } + + let byteArray = [UInt8](keyData) + + var index = 0 + guard byteArray[index] == 0x30 else { + + throw Error(code: ERR_STRIP_PK_HEADER, reason: "Provided key doesn't have a valid ASN.1 structure (first byte should be 0x30 == SEQUENCE)") + } + + index += 1 + if byteArray[index] > 0x80 { + index += Int(byteArray[index]) - 0x80 + 1 + } else { + index += 1 + } + + // If current byte marks an integer (0x02), it means the key doesn't have a X509 header and just + // contains its modulo & public exponent. In this case, we can just return the provided DER data as is. + if Int(byteArray[index]) == 0x02 { + return keyData + } + + // Now that we've excluded the possibility of headerless key, we're looking for a valid X509 header sequence. + // It should look like this: + // 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00 + guard Int(byteArray[index]) == 0x30 else { + + throw Error(code: ERR_STRIP_PK_HEADER, reason: "Provided key doesn't have a valid X509 header") + } + + index += 15 + if byteArray[index] != 0x03 { + + throw Error(code: ERR_STRIP_PK_HEADER, reason: "Invalid byte at index \(index - 1) (\(byteArray[index - 1])) for public key header") + } + + index += 1 + if byteArray[index] > 0x80 { + index += Int(byteArray[index]) - 0x80 + 1 + } else { + index += 1 + } + + guard byteArray[index] == 0 else { + + throw Error(code: ERR_STRIP_PK_HEADER, reason: "Invalid byte at index \(index - 1) (\(byteArray[index - 1])) for public key header") + } + + index += 1 + + let strippedKeyBytes = [UInt8](byteArray[index...keyData.count - 1]) + let data = Data(bytes: UnsafePointer(strippedKeyBytes), count: keyData.count - index) + return data + } + + /// + /// Add an X509 certificate header to key data. + /// + /// - Parameters: + /// - keyData: Data to add the header to. + /// + /// - Returns: The modified key data. + /// + static func addX509CertificateHeader(for keyData: Data) -> Data { + + if keyData.count == 140 { + return Data([0x30, 0x81, 0x9F, + 0x30, 0x0D, + 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, + 0x05, 0x00, + 0x03, 0x81, 0x8D, 0x00]) + keyData + } else if keyData.count == 270 { + return Data([0x30, 0x82, 0x01, 0x22, + 0x30, 0x0D, + 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, + 0x05, 0x00, + 0x03, 0x82, 0x01, 0x0F, 0x00]) + keyData + } else if keyData.count == 398 { + return Data([0x30, 0x82, 0x01, 0xA2, + 0x30, 0x0D, + 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, + 0x05, 0x00, + 0x03, 0x82, 0x01, 0x8F, 0x00]) + keyData + } else if keyData.count == 526 { + return Data([0x30, 0x82, 0x02, 0x22, + 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, + 0x05, 0x00, + 0x03, 0x82, 0x02, 0x0F, 0x00]) + keyData + } else { + return keyData + } + } + +} + +extension String { + + /// + /// Split a string to a specified length. + /// + /// - Parameters: + /// - length: Length of each split string. + /// + /// - Returns: `[String]` containing each string. + /// + func split(to length: Int) -> [String] { + + var result = [String]() + var collectedCharacters = [Character]() + collectedCharacters.reserveCapacity(length) + var count = 0 + + for character in self { + collectedCharacters.append(character) + count += 1 + if count == length { + // Reached the desired length + count = 0 + result.append(String(collectedCharacters)) + collectedCharacters.removeAll(keepingCapacity: true) + } + } + + // Append the remainder + if !collectedCharacters.isEmpty { + result.append(String(collectedCharacters)) + } + + return result + } +} + + +// MARK: - + +#if !os(Linux) + + // MARK: -- CFString Extension for Hashing + + /// + /// Extension to CFString to make it hashable. + /// + extension CFString: Hashable { + + /// Return the hash value of a CFString + public var hashValue: Int { + return (self as String).hashValue + } + + /// Comparison of CFStrings + static public func == (lhs: CFString, rhs: CFString) -> Bool { + return lhs as String == rhs as String + } + } + +#endif diff --git a/Pods/BlueRSA/Sources/CryptorRSA/Data+Extensions.swift b/Pods/BlueRSA/Sources/CryptorRSA/Data+Extensions.swift new file mode 100644 index 0000000..f7f9bca --- /dev/null +++ b/Pods/BlueRSA/Sources/CryptorRSA/Data+Extensions.swift @@ -0,0 +1,38 @@ +// Copyright © 2019 IBM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +import Foundation + +#if !swift(>=5.0) +// Extension to allow Swift 5 `withUnsafeBytes` API for earlier versions +internal extension Data { + func withUnsafeBytes(_ body: (UnsafeRawBufferPointer) throws -> T) rethrows -> T { + let c = count + return try withUnsafeBytes { (p: UnsafePointer) throws -> T in + try body(UnsafeRawBufferPointer(start: p, count: c)) + } + } + + mutating func withUnsafeMutableBytes(_ body: (UnsafeMutableRawBufferPointer) throws -> T) rethrows -> T { + let c = count + return try withUnsafeMutableBytes { (p: UnsafeMutablePointer) throws -> T in + try body(UnsafeMutableRawBufferPointer(start: p, count: c)) + } + } + + init(_ bytes: [UInt8]) { + self.init(bytes: bytes) + } +} +#endif diff --git a/Pods/BlueRSA/Sources/CryptorRSA/SSLPointerTricks.swift b/Pods/BlueRSA/Sources/CryptorRSA/SSLPointerTricks.swift new file mode 100644 index 0000000..d986598 --- /dev/null +++ b/Pods/BlueRSA/Sources/CryptorRSA/SSLPointerTricks.swift @@ -0,0 +1,100 @@ +//===----------------------------------------------------------------------===// +// +// This source file is taken from SwiftNIO open source project +// +// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors +// Licensed under Apache License v2.0 +// +//===----------------------------------------------------------------------===// + +// MARK:- Awful code begins here +// Hello dear reader. Let me explain what we're doing here. +// +// From OpenSSL 1.0 to OpenSSL 1.1 one of the major breaking changes was the so-called +// "great opaquifiying". Essentially, OpenSSL took all of its public structures and made +// them opaque, such that they cannot be introspected from client code. This is a great +// forward step, and brings them more in line with modern C library practices. +// +// However, it's an *enormous* inconvenience from Swift code. This is because the Swift +// translation of the C type `SSL_CTX *` changed from `UnsafeMutablePointer` to +// `OpaquePointer`. +// +// This change exists for reasonable enough reasons in Swift land (see +// https://forums.swift.org/t/opaque-pointers-in-swift/6875 for a discussion), but +// nonetheless causes enormous problems in our codebase. +// +// Our cheap way out is to make everything an OpaquePointer, and then provide initializers +// between OpaquePointer and the typed pointers. This allows us to tolerate either pointer +// type in our Swift code by bridging them over to OpaquePointer and back, and lets the +// compiler worry about how exactly to make that work. +// +// Now, in fact, Swift already has initializers between the pointer types. What it does +// not have is self-initializers: the ability to create an `OpaquePointer` from an `OpaquePointer`, +// or an `UnsafePointer` from an `UnsafePointer`. We add those two initializers here. +// We also add a special "make" function that exists to handle the special case of optional pointer +// values, which we mostly encounter in the ALPN callbacks. +// +// The *downside* of this approach is that we totally break the pointer type system. It becomes +// trivially possible to alias a pointer of type T to type U through two calls to init. This +// is not a thing we want to widely promote. For this reason, these extensions are hidden in +// this file, where we can laugh and jeer at them and generally make them feel bad about +// themselves. +// +// Hopefully, in time, these extensions can be removed. + +extension UnsafePointer { + init(_ ptr: UnsafePointer) { + self = ptr + } + + static func make(optional ptr: UnsafePointer?) -> UnsafePointer? { + return ptr.map(UnsafePointer.init) + } + + static func make(optional ptr: OpaquePointer?) -> UnsafePointer? { + return ptr.map(UnsafePointer.init) + } +} + +extension UnsafeMutablePointer { + init(_ ptr: UnsafeMutableRawPointer) { + let x = UnsafeMutablePointer(bitPattern: UInt(bitPattern: ptr))! + self = x + } + + static func make(optional ptr: UnsafeMutablePointer?) -> UnsafeMutablePointer? { + return ptr.map(UnsafeMutablePointer.init) + } + + static func make(optional ptr: UnsafeMutableRawPointer?) -> UnsafeMutablePointer? { + return ptr.map(UnsafeMutablePointer.init) + } + + static func make(optional ptr: OpaquePointer?) -> UnsafeMutablePointer? { + return ptr.map(UnsafeMutablePointer.init) + } +} + +extension UnsafeMutableRawPointer { + static func make(optional ptr: OpaquePointer?) -> UnsafeMutableRawPointer? { + return ptr.map(UnsafeMutableRawPointer.init) + } +} + +extension OpaquePointer { + init(_ ptr: OpaquePointer) { + self = ptr + } + + static func make(optional ptr: OpaquePointer?) -> OpaquePointer? { + return ptr.map(OpaquePointer.init) + } + + static func make(optional ptr: UnsafeMutableRawPointer?) -> OpaquePointer? { + return ptr.map(OpaquePointer.init) + } + + static func make(optional ptr: UnsafeMutablePointer?) -> OpaquePointer? { + return ptr.map(OpaquePointer.init) + } +} diff --git a/Pods/KituraContracts/LICENSE b/Pods/KituraContracts/LICENSE new file mode 100644 index 0000000..8dada3e --- /dev/null +++ b/Pods/KituraContracts/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/Pods/KituraContracts/README.md b/Pods/KituraContracts/README.md new file mode 100644 index 0000000..3d955bb --- /dev/null +++ b/Pods/KituraContracts/README.md @@ -0,0 +1,87 @@ +

+ +Kitura + +

+ + +

+ +APIDoc + + +Build Status - Master + +macOS +Linux +Apache 2 + +Slack Status + +

+ +# KituraContracts + +## Summary + +KituraContracts is a library containing type definitions shared by client (e.g. [KituraKit](https://ibm-swift.github.io/KituraKit/)) and server (e.g. [Kitura](https://ibm-swift.github.io/Kitura)) code. These shared type definitions include [Codable Closure Aliases](https://ibm-swift.github.io/KituraContracts/Typealiases.html), [RequestError](https://ibm-swift.github.io/KituraContracts/Structs/RequestError.html), [QueryEncoder](https://ibm-swift.github.io/KituraContracts/Classes/QueryEncoder.html), [QueryDecoder](https://ibm-swift.github.io/KituraContracts/Classes/QueryDecoder.html), [Coder](https://ibm-swift.github.io/KituraContracts/Classes/Coder.html), [Identifier Protocol](https://ibm-swift.github.io/KituraContracts/Protocols/Identifier.html#/s:15KituraContracts10IdentifierP5valueSSv) and [Extensions](https://ibm-swift.github.io/KituraContracts/Extensions.html#/s:SS) to String and Int, which add conformity to the Identifier protocol. + +## Usage + +KituraContracts represents the types and protocols that are common to both the [Kitura](https://github.com/IBM-Swift/Kitura) server and [KituraKit](https://github.com/IBM-Swift/KituraKit) client side library. If you are using Kitura or KituraKit, your project does not need to depend on KituraContracts explicitly. + +#### Add dependencies + +Add the `KituraContracts` package to the dependencies within your application’s `Package.swift` file. Substitute `"x.x.x"` with the latest `KituraContracts` [release](https://github.com/IBM-Swift/KituraContracts/releases). + +```swift +.package(url: "https://github.com/IBM-Swift/KituraContracts.git", from: "x.x.x") +``` + +Add `KituraContracts` to your target's dependencies: + +```swift +.target(name: "example", dependencies: ["KituraContracts"]), +``` + +#### Import package + +```swift +import KituraContracts +``` + +## Example + +This example, shows how to use a shared type definition for `RequestError` within a router POST method on `users`. If no errors occurred and you have a `User` you can respond with the user and pass nil as the `RequestError?` value. If there has been an error you can respond with an appropriate error and pass nil for the `User?`. + +````swift +public struct User: Codable { + ... +} + +router.post("/users") { (user: User, respondWith: (User?, RequestError?) -> Void) in + + if databaseConnectionIsOk { + ... + respondWith(user, nil) + } else { + ... + respondWith(nil, .internalServerError) + } +} +```` + +## Swift version + +The 1.x.x releases were tested on macOS and Linux using the Swift 4.1 binaries. Please note that this is the default version of Swift that is included in [Xcode 9.3](https://developer.apple.com/xcode/). + +## API Documentation +For more information visit our [API reference](https://ibm-swift.github.io/KituraContracts/index.html). + +## Community + +We love to talk server-side Swift and Kitura. Join our [Slack](http://swift-at-ibm-slack.mybluemix.net/) to meet the team! + +## License + +This library is licensed under Apache 2.0. Full license text is available in [LICENSE](https://github.com/IBM-Swift/KituraContracts/blob/master/LICENSE). diff --git a/Pods/KituraContracts/Sources/KituraContracts/BodyDecoder.swift b/Pods/KituraContracts/Sources/KituraContracts/BodyDecoder.swift new file mode 100644 index 0000000..9055d09 --- /dev/null +++ b/Pods/KituraContracts/Sources/KituraContracts/BodyDecoder.swift @@ -0,0 +1,28 @@ +/** + * Copyright IBM Corporation 2018 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +import Foundation + +/** + A class that conforms to `BodyDecoder` must be able to decode from `Data` into a `Codable` type. + This class can then be used to produce input objects for a Codable route. + */ +public protocol BodyDecoder: AnyObject { + /// Decode a `Decodable` type from a `Data`, using this `BodyDecoder`. + func decode(_ type: T.Type, from data: Data) throws -> T +} + +extension JSONDecoder: BodyDecoder {} diff --git a/Pods/KituraContracts/Sources/KituraContracts/BodyEncoder.swift b/Pods/KituraContracts/Sources/KituraContracts/BodyEncoder.swift new file mode 100644 index 0000000..0a1f9d3 --- /dev/null +++ b/Pods/KituraContracts/Sources/KituraContracts/BodyEncoder.swift @@ -0,0 +1,28 @@ +/** + * Copyright IBM Corporation 2018 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +import Foundation + +/** + A class that conforms to `BodyEncoder` must be able to encode a `Codable` type into `Data`. + This class can then be used to produce output objects for a Codable route. + */ +public protocol BodyEncoder: AnyObject { + /// Encode an `Encodable` type to a `Data`, using this `BodyEncoder`. + func encode(_ value: T) throws -> Data +} +extension JSONEncoder: BodyEncoder {} + diff --git a/Pods/KituraContracts/Sources/KituraContracts/BodyFormat.swift b/Pods/KituraContracts/Sources/KituraContracts/BodyFormat.swift new file mode 100644 index 0000000..4707d19 --- /dev/null +++ b/Pods/KituraContracts/Sources/KituraContracts/BodyFormat.swift @@ -0,0 +1,78 @@ +/** + * Copyright IBM Corporation 2018 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +import Foundation + +/** + A set of values representing the format of a response body. + + This struct is intended to be "enum-like" and values should be + accessed via the public static stored properties. + + - Note: An enum was not used here because currently enums are + always exhaustive. This means adding a case to an enum + is a breaking change. In order to keep such additions + non-breaking we have used an "enum-like" struct instead. + This means code using `BodyFormat` should always handle + unrecognised `BodyFormat` values (eg in a default case + of a switch). `UnsupportedBodyFormatError` may be used + in this situation. + + ### Usage Example: ### + ````swift + let format = BodyFormat.json + ```` + */ +public struct BodyFormat: Equatable { + + /** + A String value of the type that the body format will be represented in, which is used to ensure that both the left-hand side and the right-hand side are of the same type in the response body. + */ + public let type: String + + private init(_ type: String) { + self.type = type + } + + /** + This function checks that both the left-hand side and the right-hand side of the response body are of the same type. + */ + public static func == (_ lhs: BodyFormat, _ rhs: BodyFormat) -> Bool { + return lhs.type == rhs.type + } + + /** + The JSON representation of the response body. + */ + public static let json = BodyFormat("application/json") +} + +/** + An error that may be thrown when a particular instance of `BodyFormat` + is not supported. + */ +public struct UnsupportedBodyFormatError: Error { + /** + The format of the body. + */ + public let bodyFormat: BodyFormat + /** + Initialize `UnsupportedBodyFormatError` with the format of the body. + */ + public init(_ bodyFormat: BodyFormat) { + self.bodyFormat = bodyFormat + } +} diff --git a/Pods/KituraContracts/Sources/KituraContracts/ClosureAliases.swift b/Pods/KituraContracts/Sources/KituraContracts/ClosureAliases.swift new file mode 100644 index 0000000..05fb62c --- /dev/null +++ b/Pods/KituraContracts/Sources/KituraContracts/ClosureAliases.swift @@ -0,0 +1,260 @@ +/** + * Copyright IBM Corporation 2017 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +// MARK: Codable Type Aliases + +/** +The `ResultClosure` is used by other `Codable` aliases when responding with only a `RequestError` is needed. + +The following two typealiases make use of `ResultClosure`: + ````swift + public typealias NonCodableClosure = (@escaping ResultClosure) -> Void + + public typealias IdentifierNonCodableClosure = (Id, @escaping ResultClosure) -> Void + ```` + */ +public typealias ResultClosure = (RequestError?) -> Void + +/** +The `CodableResultClosure` is used by other `Codable` aliases when responding with an object which conforms to `Codable`, or a `RequestError` is needed. + +The following two typealiases make use of `CodableResultClosure`: + ````swift + public typealias IdentifierCodableClosure = (Id, I, @escaping CodableResultClosure) -> Void + + public typealias CodableClosure = (I, @escaping CodableResultClosure) -> Void + ```` + */ +public typealias CodableResultClosure = (O?, RequestError?) -> Void + +/** +The `CodableArrayResultClosure` is used by other `Codable` aliases when responding with an array of objects which conform to `Codable`, or a `RequestError` is needed. + + The following typealias makes use of `CodableArrayResultClosure`: + ````swift + public typealias CodableArrayClosure = (@escaping CodableArrayResultClosure) -> Void + ```` + */ +public typealias CodableArrayResultClosure = ([O]?, RequestError?) -> Void + +/** +The `IdentifierCodableArrayResultClosure` is used by other `Codable` aliases when responding with an array of tuples containing an identifier and a `Codable` object, or a `RequestError`. + + The following typealias makes use of `IdentifierCodableArrayResultClosure`: + ````swift + public typealias CodableIdentifierClosure = (I, @escaping IdentifierCodableResultClosure<[Id, O]?>) -> Void + ```` +*/ +public typealias IdentifierCodableArrayResultClosure = ([(Id, O)]?, RequestError?) -> Void + +/** +This is used to perform a series of actions which use an object conforming to `Identifier` and an object conforming to `Codable`. After which you want to respond with an object which conforms to `Codable`, which is of the same type as the object passed as a parameter, or respond with an `Identifier` or `RequestError`. + + The following typealias makes use of `IdentifierCodableResultClosure`: + ````swift + public typealias CodableIdentifierClosure = (I, @escaping IdentifierCodableResultClosure) -> Void + ```` + */ +public typealias IdentifierCodableResultClosure = (Id?, O?, RequestError?) -> Void + +/** +The `IdentifierCodableClosure` is used to perform a series of actions utilising an object conforming to `Identifier` and an object conforming to 'Codable', then respond with an object which conforms to `Codable`, which is of the same type as the object passed as a parameter, or responding with a `RequestError` in the form of a `CodableResultClosure`. + +By default `Int` has conformity to `Identifier`. In this example, if there has been an error you can use the `respondWith` call to respond with an appropriate error, passing nil for the `User?`. If no errors occurred and you have a `User`, you can just respond with the user, passing nil as the `RequestError?` value. +### Usage Example: ### +````swift +public struct User: Codable { + ... +} + +var userStore: [Int, User] = [...] + +router.put("/users") { (id: Int, user: User, respondWith: (User?, RequestError?) -> Void) in + guard let oldUser = self.userStore[id] else { + respondWith(nil, .notFound) + return + } + + ... + + respondWith(user, nil) +} +```` +*/ +public typealias IdentifierCodableClosure = (Id, I, @escaping CodableResultClosure) -> Void + +/** +The `CodableClosure` is used to perform a series of actions utilising an object conforming to `Identifier`, then respond with an object which conforms to `Codable`, which is of the same type as the object passed as a parameter, or responding with a `RequestError` in the form of a `CodableResultClosure`. + +If no errors occurred and you have a `User`, you can just respond with the user by passing nil as the `RequestError?` value. In this example, if there has been an error you can use the `respondWith` call to respond with an appropriate error and passing nil for the `User?`. +### Usage Example: ### +````swift +public struct User: Codable { + ... +} + +router.post("/users") { (user: User, respondWith: (User?, RequestError?) -> Void) in + if databaseConnectionIsOk { + ... + respondWith(user, nil) + } else { + ... + respondWith(nil, .internalServerError) + } +} +```` +*/ +public typealias CodableClosure = (I, @escaping CodableResultClosure) -> Void + +/** +The `CodableIdentifierClosure` is used to perform a series of actions utilising an object conforming to `Identifier`, then respond with an object which conforms to `Codable`, and/or an object conforming to `Identifier` or responding with a `RequestError` in the form of a `IdentifierCodableResultClosure`. + + If no errors occurred and you have a `User` and the corresponding identifier, you can just respond with the identifier and user, and pass nil as the `RequestError?` value. In this example, if there has been an error you can use the `respondWith` call to respond with an appropriate error and passing nil for `Int?` and nil for `User?`. +### Usage Example: ### +````swift +public struct User: Codable { + ... +} + +router.post("/users") { (user: User, respondWith: (Int?, User?, RequestError?) -> Void) in + if databaseConnectionIsOk { + ... + respondWith(id, user, nil) + } else { + ... + respondWith(nil, nil, .internalServerError) + } +} +```` +*/ +public typealias CodableIdentifierClosure = (I, @escaping IdentifierCodableResultClosure) -> Void + +/** +The `NonCodableClosure` is used to perform a series of actions then respond with a `RequestError` in the form of a `ResultClosure`. + +If no errors occurred you can just pass nil as the `RequestError?` value. In this example, if there has been an error you can use the `respondWith` call to respond with an appropriate error. +### Usage Example: ### +````swift +router.delete("/users") { (respondWith: (RequestError?) -> Void) in + if databaseConnectionIsOk { + ... + respondWith(nil) + } else { + respondWith(.internalServerError) + ... + } +} +```` +*/ +public typealias NonCodableClosure = (@escaping ResultClosure) -> Void + +/** +The `IdentifierNonCodableClosure` is used to perform a series of actions utilising an object which conforms to `Identifier`, then respond with a `RequestError` in the form of a `ResultClosure`. + +If no errors occurred you can just pass nil as the `RequestError?` value. In this example, if there has been an error you can use the `respondWith` call to respond with an appropriate error. +### Usage Example: ### +````swift +router.delete("/users") { (id: Int, respondWith: (RequestError?) -> Void) in + if databaseConnectionIsOk { + ... + respondWith(nil) + } else { + ... + respondWith(.internalServerError) + } +} +```` +*/ +public typealias IdentifierNonCodableClosure = (Id, @escaping ResultClosure) -> Void + +/** +The `CodableArrayClosure` is used to perform a series of actions then respond with an array of objects conforming to `Codable` or a `RequestError` in the form of a `CodableArrayResultClosure`. + +If no errors occurred and you have an array of `Users` you can just respond with the users by passing nil as the `RequestError?` value. In this example, if there has been an error you can use the `respondWith` call to respond with an appropriate error and passing nil for the `[User]?`. +### Usage Example: ### +````swift +router.get("/users") { (respondWith: ([User]?, RequestError?) -> Void) in + if databaseConnectionIsOk { + ... + respondWith(users, nil) + } else { + ... + respondWith(nil, .internalServerError) + } +} +```` +*/ +public typealias CodableArrayClosure = (@escaping CodableArrayResultClosure) -> Void + +/** + The `IdentifierCodableArrayClosure` is used to perform a series of actions then respond with an array of tuples containing an identifier and a Codable object, or a `RequestError`, in the form of a `IdentifierCodableArrayResultClosure`. + + If no errors occurred and you have an array of `Users` you can just respond with the users by passing nil as the `RequestError?` value. In this example, if there has been an error you can use the `respondWith` call to respond with an appropriate error and passing nil for the `[User]?`. + ### Usage Example: ### + ````swift + router.get("/users") { (respondWith: ([(Int, User)]?, RequestError?) -> Void) in + if databaseConnectionIsOk { + ... + respondWith([(Int, User)], nil) + } else { + ... + respondWith(nil, .internalServerError) + } + } + ```` + */ +public typealias IdentifierCodableArrayClosure = (@escaping IdentifierCodableArrayResultClosure) -> Void + +/** +The `SimpleCodableClosure` is used to perform a series of actions, then respond with an object conforming to `Codable` or a `RequestError` in the form of a `CodableResultClosure`. + +### Usage Example: ### +````swift +public struct Status: Codable { + ... +} + +router.get("/status") { (respondWith: (Status?, RequestError?) -> Void) in + ... + respondWith(status, nil) +} +```` +*/ +public typealias SimpleCodableClosure = (@escaping CodableResultClosure) -> Void + +/** +The `IdentifierSimpleCodableClosure` is used to perform a series of actions utilising an object which conforms to `Identifier`, then respond with an object conforming to `Codable` or a `RequestError` in the form of a `CodableResultClosure`. + +If there has been an error you can use the `respondWith` call to respond with an appropriate error and passing nil for the `User?`. In this example, if no errors occurred and you have a `User` you can just respond with the user by passing nil as the `RequestError?` value. +### Usage Example: ### +````swift +public struct User: Codable { + ... +} + +var userStore: [Int, User] = (...) + +router.get("/users") { (id: Int, respondWith: (User?, RequestError?) -> Void) in + guard let user = self.userStore[id] else { + respondWith(nil, .notFound) + return + } + ... + respondWith(user, nil) +} +```` +*/ +public typealias IdentifierSimpleCodableClosure = (Id, @escaping CodableResultClosure) -> Void diff --git a/Pods/KituraContracts/Sources/KituraContracts/CodableQuery/Coder.swift b/Pods/KituraContracts/Sources/KituraContracts/CodableQuery/Coder.swift new file mode 100644 index 0000000..aadf7bb --- /dev/null +++ b/Pods/KituraContracts/Sources/KituraContracts/CodableQuery/Coder.swift @@ -0,0 +1,68 @@ +/* + * Copyright IBM Corporation 2017 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import Foundation + +/** + Class defining shared resources for the [QueryDecoder](https://github.com/IBM-Swift/KituraContracts/blob/master/Sources/KituraContracts/CodableQuery/QueryDecoder.swift) and [QueryEncoder](https://github.com/IBM-Swift/KituraContracts/blob/master/Sources/KituraContracts/CodableQuery/QueryEncoder.swift). + + ### Usage Example: ### + ````swift + let date = Coder.defaultDateFormatter.date(from: "2017-10-31T16:15:56+0000")! + ```` + */ +public class Coder { + + @available(*, deprecated, message: "Use Coder.defaultDateFormatter instead") + public let dateFormatter: DateFormatter = Coder.defaultDateFormatter + + /** + The default [DateFormatter](https://developer.apple.com/documentation/foundation/dateformatter) used for encoding and decoding query parameters. It uses the "UTC" timezone and "yyyy-MM-dd'T'HH:mm:ssZ" date format. + + ### Usage Example: ### + ````swift + let date = Coder.defaultDateFormatter.date(from: "2017-10-31T16:15:56+0000") + ```` + */ + public static let defaultDateFormatter: DateFormatter = { + let value = DateFormatter() + value.timeZone = TimeZone(identifier: "UTC") + value.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ" + return value + }() + + /** + Initializes a `Coder` instance with a `DateFormatter` + using the "UTC" timezone and "yyyy-MM-dd'T'HH:mm:ssZ" date format. + */ + public init() {} + + /** + Helper method to extract the field name from a `CodingKey` array. + + ### Usage Example: ### + ````swift + let fieldName = Coder.getFieldName(from: codingPath) + ```` + */ + public static func getFieldName(from codingPath: [CodingKey]) -> String { + #if swift(>=4.1) + return codingPath.compactMap({$0.stringValue}).joined(separator: ".") + #else + return codingPath.flatMap({$0.stringValue}).joined(separator: ".") + #endif + } +} diff --git a/Pods/KituraContracts/Sources/KituraContracts/CodableQuery/Extensions.swift b/Pods/KituraContracts/Sources/KituraContracts/CodableQuery/Extensions.swift new file mode 100644 index 0000000..d0a21e4 --- /dev/null +++ b/Pods/KituraContracts/Sources/KituraContracts/CodableQuery/Extensions.swift @@ -0,0 +1,353 @@ +/* + * Copyright IBM Corporation 2017 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import Foundation +import LoggerAPI + +/// Codable String Conversion Extension. +extension String { + + /// Converts the given String to an Int?. + public var int: Int? { + return Int(self) + } + + /// Converts the given String to a Int8?. + public var int8: Int8? { + return Int8(self) + } + + /// Converts the given String to a Int16?. + public var int16: Int16? { + return Int16(self) + } + + /// Converts the given String to a Int32?. + public var int32: Int32? { + return Int32(self) + } + + /// Converts the given String to a Int64?. + public var int64: Int64? { + return Int64(self) + } + + /// Converts the given String to a UInt?. + public var uInt: UInt? { + return UInt(self) + } + + /// Converts the given String to a UInt8?. + public var uInt8: UInt8? { + return UInt8(self) + } + + /// Converts the given String to a UInt16?. + public var uInt16: UInt16? { + return UInt16(self) + } + + /// Converts the given String to a UInt32?. + public var uInt32: UInt32? { + return UInt32(self) + } + + /// Converts the given String to a UInt64?. + public var uInt64: UInt64? { + return UInt64(self) + } + + /// Converts the given String to a Float?. + public var float: Float? { + return Float(self) + } + + /// Converts the given String to a Double?. + public var double: Double? { + return Double(self) + } + + /// Converts the given String to a Bool?. + public var boolean: Bool? { + return !self.isEmpty ? Bool(self) : false + } + + /// Converts the given String to a String. + public var string: String { + return self + } + + /// Converts the given String to an [Int]?. + public var intArray: [Int]? { + return decodeArray(Int.self) + } + + /// Converts the given String to an [Int8]?. + public var int8Array: [Int8]? { + return decodeArray(Int8.self) + } + + /// Converts the given String to an [Int16]?. + public var int16Array: [Int16]? { + return decodeArray(Int16.self) + } + + /// Converts the given String to an [Int32]?. + public var int32Array: [Int32]? { + return decodeArray(Int32.self) + } + + /// Converts the given String to an [Int64]?. + public var int64Array: [Int64]? { + return decodeArray(Int64.self) + } + + /// Converts the given String to an [UInt]?. + public var uIntArray: [UInt]? { + return decodeArray(UInt.self) + } + + /// Converts the given String to an [UInt8]?. + public var uInt8Array: [UInt8]? { + return decodeArray(UInt8.self) + } + + /// Converts the given String to an [UInt16]?. + public var uInt16Array: [UInt16]? { + return decodeArray(UInt16.self) + } + + /// Converts the given String to an [UInt32]?. + public var uInt32Array: [UInt32]? { + return decodeArray(UInt32.self) + } + + /// Converts the given String to an [UInt64]?. + public var uInt64Array: [UInt64]? { + return decodeArray(UInt64.self) + } + + /// Converts the given String to a [Float]?. + public var floatArray: [Float]? { + return decodeArray(Float.self) + } + + /// Converts the given String to a [Double]?. + public var doubleArray: [Double]? { + return decodeArray(Double.self) + } + + /// Converts the given String to a [Bool]?. + public var booleanArray: [Bool]? { + return decodeArray(Bool.self) + } + + /// Converts the given String to a [String]. + public var stringArray: [String] { + let strs: [String] = self.components(separatedBy: ",") + return strs + } + + /** + Method used to decode a string into the given type T. + + - Parameter type: The Decodable type to convert the string into. + - Returns: The Date? object. Some on success / nil on failure. + */ + public func decodable(_ type: T.Type) -> T? { + guard let data = self.data(using: .utf8) else { + return nil + } + let obj: T? = try? JSONDecoder().decode(type, from: data) + return obj + } + + /** + Converts the given String to a Date?. + + - Parameter formatter: The designated DateFormatter to convert the string with. + - Returns: The Date? object. Some on success / nil on failure. + */ + public func date(_ formatter: DateFormatter) -> Date? { + return formatter.date(from: self) + } + + /** + Converts the given String to a [Date]?. + + - Parameter formatter: The designated DateFormatter to convert the string with. + - Returns: The [Date]? object. Some on success / nil on failure. + */ + public func dateArray(_ formatter: DateFormatter) -> [Date]? { + let strs: [String] = self.components(separatedBy: ",") + let dates = strs.map { formatter.date(from: $0) }.filter { $0 != nil }.map { $0! } + if dates.count == strs.count { + return dates + } + return nil + } + + /** + Converts the given String to a [Date]? object using the dateDecodingStrategy supplied. + + - Parameter formatter: The designated `DateFormatter` to convert the string with. + - Parameter decoderStrategy: The `JSON.dateDecodingStrategy` that should be used to decode the specifed Date. Default is set to .formatted with default dateFormatter. + - Parameter decoder: The `Decoder` parameter is only used for the custom strategy. + - Returns: The [Date]? object. Some on success / nil on failure. + */ + public func dateArray(decoderStrategy: JSONDecoder.DateDecodingStrategy = .formatted(Coder.defaultDateFormatter), decoder: Decoder?=nil) -> [Date]? { + + switch decoderStrategy { + case .formatted(let formatter): + let strs: [String] = self.components(separatedBy: ",") + let dates = strs.map { formatter.date(from: $0) }.filter { $0 != nil }.map { $0! } + if dates.count == strs.count { + return dates + } + return nil + case .deferredToDate: + let strs: [String] = self.components(separatedBy: ",") + #if swift(>=4.1) + let dbs = strs.compactMap(Double.init) + #else + let dbs = strs.flatMap(Double.init) + #endif + let dates = dbs.map { Date(timeIntervalSinceReferenceDate: $0) } + if dates.count == dbs.count { + return dates + } + return nil + case .secondsSince1970: + let strs: [String] = self.components(separatedBy: ",") + #if swift(>=4.1) + let dbs = strs.compactMap(Double.init) + #else + let dbs = strs.flatMap(Double.init) + #endif + let dates = dbs.map { Date(timeIntervalSince1970: $0) } + if dates.count == dbs.count { + return dates + } + return nil + case .millisecondsSince1970: + let strs: [String] = self.components(separatedBy: ",") + #if swift(>=4.1) + let dbs = strs.compactMap(Double.init) + #else + let dbs = strs.flatMap(Double.init) + #endif + let dates = dbs.map { Date(timeIntervalSince1970: ($0)/1000) } + if dates.count == dbs.count { + return dates + } + return nil + case .iso8601: + let strs: [String] = self.components(separatedBy: ",") + if #available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) { + let dates = strs.map { _iso8601Formatter.date(from: $0) } + if dates.count == strs.count { + return dates as? [Date] + } + return nil + } else { + fatalError("ISO8601DateFormatter is unavailable on this platform.") + } + case .custom(let closure): + var dateArray: [Date] = [] + guard let decoder = decoder else {return dateArray} + var fieldValueArray = self.split(separator: ",") + for _ in fieldValueArray { + // Call closure to decode value + guard let date = try? closure(decoder) else { + return nil + } + dateArray.append(date) + // Delete from array after use + fieldValueArray.removeFirst() + } + return dateArray + #if swift(>=5) && !os(Linux) + @unknown default: + Log.error("Decoding strategy not found") + fatalError() + #endif + } + } + /// Helper Method to decode a string to an LosslessStringConvertible array types. + private func decodeArray(_ type: T.Type) -> [T]? { + let strs: [String] = self.components(separatedBy: ",") + let values: [T] = strs.map { T($0) }.filter { $0 != nil }.map { $0! } + return values.count == strs.count ? values : nil + } + + /// Parses percent encoded string into query parameters with comma-separated + /// values. + var urlDecodedFieldValuePairs: [String: String] { + var result: [String: String] = [:] + for item in self.components(separatedBy: "&") { + let (key, value) = item.keyAndDecodedValue + if let value = value { + // If value already exists for this key, append it + if let existingValue = result[key] { + result[key] = "\(existingValue),\(value)" + } + else { + result[key] = value + } + } + } + return result + } + + /// Splits a URL-encoded key and value pair (e.g. "foo=bar") into a tuple + /// with corresponding "key" and "value" values, with the value being URL + /// unencoded. + var keyAndDecodedValue: (key: String, value: String?) { + guard let range = self.range(of: "=") else { + return (key: self, value: nil) + } + let key = String(self[..(_ type: T.Type, from data: Data) throws -> T { + guard let urlString = String(data: data, encoding: .utf8) else { + throw RequestError.unprocessableEntity + } + let decoder = QueryDecoder(dictionary: urlString.urlDecodedFieldValuePairs) + decoder.dateDecodingStrategy = dateDecodingStrategy + if let Q = T.self as? QueryParams.Type { + decoder.dateDecodingStrategy = Q.dateDecodingStrategy + } + return try T(from: decoder) + } + + /** + Decodes a `[String: String]` mapping to its Decodable object representation. + + - Parameter value: The Decodable object to decode the dictionary into. + + ### Usage Example: ### + ````swift + guard let query = try? QueryDecoder(dictionary: expectedDict).decode(MyQuery.self) else { + print("Failed to decode query to MyQuery Object") + return + } + ```` + */ + public func decode(_ type: T.Type) throws -> T { + if let Q = T.self as? QueryParams.Type { + dateDecodingStrategy = Q.dateDecodingStrategy + } + let fieldName = Coder.getFieldName(from: codingPath) + let fieldValue = dictionary[fieldName] + Log.verbose("fieldName: \(fieldName), fieldValue: \(String(describing: fieldValue))") + + switch type { + /// Bool + case is Bool.Type: + return try decodeType(fieldValue?.boolean, to: T.self) + /// Ints + case is Int.Type: + return try decodeType(fieldValue?.int, to: T.self) + case is Int8.Type: + return try decodeType(fieldValue?.int8, to: T.self) + case is Int16.Type: + return try decodeType(fieldValue?.int16, to: T.self) + case is Int32.Type: + return try decodeType(fieldValue?.int32, to: T.self) + case is Int64.Type: + return try decodeType(fieldValue?.int64, to: T.self) + /// Int Arrays + case is [Int].Type: + return try decodeType(fieldValue?.intArray, to: T.self) + case is [Int8].Type: + return try decodeType(fieldValue?.int8Array, to: T.self) + case is [Int16].Type: + return try decodeType(fieldValue?.int16Array, to: T.self) + case is [Int32].Type: + return try decodeType(fieldValue?.int32Array, to: T.self) + case is [Int64].Type: + return try decodeType(fieldValue?.int64Array, to: T.self) + /// UInts + case is UInt.Type: + return try decodeType(fieldValue?.uInt, to: T.self) + case is UInt8.Type: + return try decodeType(fieldValue?.uInt8, to: T.self) + case is UInt16.Type: + return try decodeType(fieldValue?.uInt16, to: T.self) + case is UInt32.Type: + return try decodeType(fieldValue?.uInt32, to: T.self) + case is UInt64.Type: + return try decodeType(fieldValue?.uInt64, to: T.self) + /// UInt Arrays + case is [UInt].Type: + return try decodeType(fieldValue?.uIntArray, to: T.self) + case is [UInt8].Type: + return try decodeType(fieldValue?.uInt8Array, to: T.self) + case is [UInt16].Type: + return try decodeType(fieldValue?.uInt16Array, to: T.self) + case is [UInt32].Type: + return try decodeType(fieldValue?.uInt32Array, to: T.self) + case is [UInt64].Type: + return try decodeType(fieldValue?.uInt64Array, to: T.self) + /// Floats + case is Float.Type: + return try decodeType(fieldValue?.float, to: T.self) + case is [Float].Type: + return try decodeType(fieldValue?.floatArray, to: T.self) + /// Doubles + case is Double.Type: + return try decodeType(fieldValue?.double, to: T.self) + case is [Double].Type: + return try decodeType(fieldValue?.doubleArray, to: T.self) + /// Dates + case is Date.Type: + switch dateDecodingStrategy { + case .deferredToDate: + guard let doubleValue = fieldValue?.double else {return try decodeType(fieldValue, to: T.self)} + return try decodeType(Date(timeIntervalSinceReferenceDate: (doubleValue)), to: T.self) + case .secondsSince1970: + guard let doubleValue = fieldValue?.double else {return try decodeType(fieldValue, to: T.self)} + return try decodeType(Date(timeIntervalSince1970: (doubleValue)), to: T.self) + case .millisecondsSince1970: + guard let doubleValue = fieldValue?.double else {return try decodeType(fieldValue, to: T.self)} + return try decodeType(Date(timeIntervalSince1970: (doubleValue)), to: T.self) + case .iso8601: + if #available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) { + guard let stringValue = fieldValue?.string else {return try decodeType(fieldValue, to: T.self)} + guard let date = _iso8601Formatter.date(from: stringValue) else { + throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "Expected date string to be ISO8601-formatted.")) + } + return try decodeType(date, to: T.self) + } else { + fatalError("ISO8601DateFormatter is unavailable on this platform.") + } + case .formatted(let formatted): + return try decodeType(fieldValue?.date(formatted), to: T.self) + case .custom(let closure): + return try decodeType(closure(self), to: T.self) + #if swift(>=5) && !os(Linux) + @unknown default: + throw DateError.unknownStrategy + #endif + } + case is [Date].Type: + switch dateDecodingStrategy { + case .deferredToDate: + return try decodeType(fieldValue?.dateArray(decoderStrategy: .deferredToDate), to: T.self) + case .secondsSince1970: + return try decodeType(fieldValue?.dateArray(decoderStrategy: .secondsSince1970), to: T.self) + case .millisecondsSince1970: + return try decodeType(fieldValue?.dateArray(decoderStrategy: .millisecondsSince1970), to: T.self) + case .iso8601: + if #available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) { + return try decodeType(fieldValue?.dateArray(decoderStrategy: .iso8601), to: T.self) + } else { + fatalError("ISO8601DateFormatter is unavailable on this platform.") + } + case .formatted(let formatter): + return try decodeType(fieldValue?.dateArray(formatter), to: T.self) + case .custom(let closure): + return try decodeType(fieldValue?.dateArray(decoderStrategy: .custom(closure), decoder: self), to: T.self) + #if swift(>=5) && !os(Linux) + @unknown default: + throw DateError.unknownStrategy + #endif + } + /// Strings + case is String.Type: + return try decodeType(fieldValue?.string, to: T.self) + case is [String].Type: + return try decodeType(fieldValue?.stringArray, to: T.self) + case is Operation.Type: + if let oType = type as? Operation.Type, + let value = fieldValue?.string { + let result = try oType.init(string: value) + if let castedValue = result as? T { + return castedValue + } + } + return try decodeType(fieldValue?.decodable(T.self), to: T.self) + case is Ordering.Type: + if let oType = type as? Ordering.Type, + let value = fieldValue?.string { + let result = try oType.init(string: value) + if let castedValue = result as? T { + return castedValue + } + } + return try decodeType(fieldValue?.decodable(T.self), to: T.self) + case is Pagination.Type: + if let oType = type as? Pagination.Type, + let value = fieldValue?.string { + let result = try oType.init(string: value) + if let castedValue = result as? T { + return castedValue + } + } + return try decodeType(fieldValue?.decodable(T.self), to: T.self) + default: + Log.verbose("Decoding Custom Type: \(T.Type.self)") + if fieldName.isEmpty { + return try T(from: self) + } else { + // Processing an instance member of the class/struct + return try decodeType(fieldValue?.decodable(T.self), to: T.self) + } + } + } + + /** + Returns a keyed decoding container based on the key type. + + ### Usage Example: ### + ````swift + decoder.container(keyedBy: keyType) + ```` + */ + public func container(keyedBy type: Key.Type) throws -> KeyedDecodingContainer where Key : CodingKey { + return KeyedDecodingContainer(KeyedContainer(decoder: self)) + } + + /** + Returns an unkeyed decoding container. + + ### Usage Example: ### + ````swift + decoder.unkeyedContainer() + ```` + */ + public func unkeyedContainer() throws -> UnkeyedDecodingContainer { + return UnkeyedContainer(decoder: self) + } + + /** + Returns a single value decoding container based on the key type. + + ### Usage Example: ### + ````swift + decoder.singleValueContainer() + ```` + */ + public func singleValueContainer() throws -> SingleValueDecodingContainer { + return UnkeyedContainer(decoder: self) + } + + private func decodeType(_ object: S, to type: T.Type) throws -> T { + if let values = object as? T { + return values + } else { + throw decodingError() + } + } + + private func decodingError() -> DecodingError { + let fieldName = Coder.getFieldName(from: codingPath) + let errorMsg = "Could not process field named '\(fieldName)'." + Log.error(errorMsg) + let errorCtx = DecodingError.Context(codingPath: codingPath, debugDescription: errorMsg) + return DecodingError.dataCorrupted(errorCtx) + } + + private struct KeyedContainer: KeyedDecodingContainerProtocol { + var decoder: QueryDecoder + + var codingPath: [CodingKey] { return [] } + + var allKeys: [Key] { return [] } + + func contains(_ key: Key) -> Bool { + return decoder.dictionary[key.stringValue] != nil + } + + func decode(_ type: T.Type, forKey key: Key) throws -> T where T : Decodable { + self.decoder.codingPath.append(key) + defer { self.decoder.codingPath.removeLast() } + return try decoder.decode(T.self) + } + + // If it is not in the dictionary or it is a empty string it should be nil + func decodeNil(forKey key: Key) throws -> Bool { + return decoder.dictionary[key.stringValue]?.isEmpty ?? true + } + + func nestedContainer(keyedBy type: NestedKey.Type, forKey key: Key) throws -> KeyedDecodingContainer where NestedKey : CodingKey { + return try decoder.container(keyedBy: type) + } + + func nestedUnkeyedContainer(forKey key: Key) throws -> UnkeyedDecodingContainer { + return try decoder.unkeyedContainer() + } + + func superDecoder() throws -> Decoder { + return decoder + } + + func superDecoder(forKey key: Key) throws -> Decoder { + return decoder + } + } + + private struct UnkeyedContainer: UnkeyedDecodingContainer, SingleValueDecodingContainer { + var decoder: QueryDecoder + + var codingPath: [CodingKey] { return [] } + + var count: Int? { return nil } + + var currentIndex: Int { return 0 } + + var isAtEnd: Bool { return false } + + func decode(_ type: T.Type) throws -> T where T : Decodable { + return try decoder.decode(type) + } + + func decodeNil() -> Bool { + return true + } + + func nestedContainer(keyedBy type: NestedKey.Type) throws -> KeyedDecodingContainer where NestedKey : CodingKey { + return try decoder.container(keyedBy: type) + } + + func nestedUnkeyedContainer() throws -> UnkeyedDecodingContainer { + return self + } + + func superDecoder() throws -> Decoder { + return decoder + } + } + +} diff --git a/Pods/KituraContracts/Sources/KituraContracts/CodableQuery/QueryEncoder.swift b/Pods/KituraContracts/Sources/KituraContracts/CodableQuery/QueryEncoder.swift new file mode 100644 index 0000000..63f9bf2 --- /dev/null +++ b/Pods/KituraContracts/Sources/KituraContracts/CodableQuery/QueryEncoder.swift @@ -0,0 +1,522 @@ +/* + * Copyright IBM Corporation 2017 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import Foundation +import LoggerAPI + +extension CharacterSet { + static let customURLQueryAllowed = CharacterSet(charactersIn: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~=:&") +} + +/** + Query Parameter Encoder. + + Encodes an `Encodable` object to a query parameter string, a `URLQueryItemArray`, or to a `[String: String]` dictionary. The encode function takes the `Encodable` object to encode as the parameter. + + ### Usage Example: ### + ````swift + let date = Coder().dateFormatter.date(from: "2017-10-31T16:15:56+0000") + let query = MyQuery(intField: -1, optionalIntField: 282, stringField: "a string", intArray: [1, -1, 3], dateField: date, optionalDateField: date, nested: Nested(nestedIntField: 333, nestedStringField: "nested string")) + + guard let myQueryDict: [String: String] = try? QueryEncoder().encode(query) else { + print("Failed to encode query to [String: String]") + return + } + ```` + */ +public class QueryEncoder: Coder, Encoder, BodyEncoder { + + /** + A `[String: String]` dictionary. + */ + internal var dictionary: [String: String] + + internal var anyDictionary: [String: Any] + + /** + The coding key path. + + ### Usage Example: ### + ````swift + let fieldName = Coder.getFieldName(from: codingPath) + ```` + */ + public var codingPath: [CodingKey] = [] + + /** + The coding user info key. + */ + public var userInfo: [CodingUserInfoKey: Any] = [:] + + // A `JSONDecoder.DateEncodingStrategy` date encoder used to determine what strategy + // to use when encoding the specific date. + private var dateEncodingStrategy: JSONEncoder.DateEncodingStrategy + + /** + Initializer for the dictionary, which initializes an empty `[String: String]` dictionary. + */ + public override init() { + self.dateEncodingStrategy = .formatted(Coder.defaultDateFormatter) + self.dictionary = [:] + self.anyDictionary = [:] + super.init() + } + + /** + Encodes an Encodable object to a query parameter string. + + - Parameter value: The Encodable object to encode to its String representation. + + ### Usage Example: ### + ````swift + guard let myQueryStr: String = try? QueryEncoder().encode(query) else { + print("Failed to encode query to String") + return + } + ```` + */ + public func encode(_ value: T) throws -> String { + let dict: [String : String] = try encode(value) + let desc: String = dict.map { key, value in "\(key)=\(value)" } + .reduce("") {pair1, pair2 in "\(pair1)&\(pair2)"} + .addingPercentEncoding(withAllowedCharacters: CharacterSet.customURLQueryAllowed)! + return "?" + String(desc.dropFirst()) + } + + /** + Encodes an Encodable object to Data. + + - Parameter value: The Encodable object to encode to its Data representation. + + ### Usage Example: ### + ````swift + guard let myQueryStr: Data = try? QueryEncoder().encode(query) else { + print("Failed to encode query to Data") + return + } + ```` + */ + public func encode(_ value: T) throws -> Data { + let dict: [String : String] = try encode(value) + let desc: String? = dict.map { key, value in "\(key)=\(value)" } + .reduce("") {pair1, pair2 in "\(pair1)&\(pair2)"} + .addingPercentEncoding(withAllowedCharacters: CharacterSet.customURLQueryAllowed) + guard let data = desc?.data(using: .utf8) else { + throw RequestError.unprocessableEntity + } + return data + } + + /** + Encodes an Encodable object to a URLQueryItem array. + + - Parameter value: The Encodable object to encode to its [URLQueryItem] representation. + + ### Usage Example: ### + ````swift + guard let myQueryArray: [URLQueryItem] = try? QueryEncoder().encode(query) else { + print("Failed to encode query to [URLQueryItem]") + return + } + ```` + */ + public func encode(_ value: T) throws -> [URLQueryItem] { + if let Q = T.self as? QueryParams.Type { + dateEncodingStrategy = Q.dateEncodingStrategy + } + let dict: [String : String] = try encode(value) + return dict.reduce([URLQueryItem]()) { array, element in + var array = array + array.append(URLQueryItem(name: element.key, value: element.value)) + return array + } + } + + /** + Encodes an Encodable object to a `[String: String]` dictionary. + + - Parameter value: The Encodable object to encode to its `[String: String]` representation. + + ### Usage Example: ### + ````swift + guard let myQueryDict: [String: String] = try? QueryEncoder().encode(query) else { + print("Failed to encode query to [String: String]") + return + } + ```` + */ + public func encode(_ value: T) throws -> [String : String] { + let encoder = QueryEncoder() + encoder.dateEncodingStrategy = self.dateEncodingStrategy + if let Q = T.self as? QueryParams.Type { + encoder.dateEncodingStrategy = Q.dateEncodingStrategy + } + try value.encode(to: encoder) + return encoder.dictionary + } + + /// Encodes an Encodable object to a String -> String dictionary + /// + /// - Parameter _ value: The Encodable object to encode to its [String: String] representation + public func encode(_ value: T) throws -> [String : Any] { + let encoder = QueryEncoder() + encoder.dateEncodingStrategy = self.dateEncodingStrategy + if let Q = T.self as? QueryParams.Type { + encoder.dateEncodingStrategy = Q.dateEncodingStrategy + } + try value.encode(to: encoder) + return encoder.anyDictionary + } + + /** + Returns a keyed encoding container based on the key type. + + ### Usage Example: ### + ````swift + encoder.container(keyedBy: keyType) + ```` + */ + + public func container(keyedBy type: Key.Type) -> KeyedEncodingContainer where Key : CodingKey { + return KeyedEncodingContainer(KeyedContainer(encoder: self)) + } + + /** + Returns an unkeyed encoding container. + + ### Usage Example: ### + ````swift + encoder.unkeyedContainer() + ```` + */ + public func unkeyedContainer() -> UnkeyedEncodingContainer { + return UnkeyedContainer(encoder: self) + } + + /** + Returns an single value encoding container based on the key type. + + ### Usage Example: ### + ````swift + encoder.singleValueContainer() + ```` + */ + public func singleValueContainer() -> SingleValueEncodingContainer { + return UnkeyedContainer(encoder: self) + } + + /// Decode a value for the current field, determined by this encoder's state (codingPath). Some + /// paths through this function are recursive (for handling custom Date encodings). + /// + /// Both the keyed and unkeyed containers call this function. The keyed container first sets the + /// encoder's codingPath, which determines the field name we encode. + /// + /// If a custom encoding is defined for Date, the custom closure will call this encoder back. It + /// is expected that any such custom encoding produces a single value, calling back via the + /// unkeyed container. + /// + /// When custom encoding Date arrays, this function will be invoked multiple times for the same + /// key. The =+= operator is used to build a comma-separated list of values for a key. + internal func _encode(value: T) throws { + let encoder = self + let fieldName = Coder.getFieldName(from: encoder.codingPath) + + switch value { + /// Ints + case let fieldValue as Int: + encoder.dictionary[fieldName] =+= String(fieldValue) + encoder.anyDictionary[fieldName] = fieldValue + case let fieldValue as Int8: + encoder.dictionary[fieldName] =+= String(fieldValue) + encoder.anyDictionary[fieldName] = fieldValue + case let fieldValue as Int16: + encoder.dictionary[fieldName] =+= String(fieldValue) + encoder.anyDictionary[fieldName] = fieldValue + case let fieldValue as Int32: + encoder.dictionary[fieldName] =+= String(fieldValue) + encoder.anyDictionary[fieldName] = fieldValue + case let fieldValue as Int64: + encoder.dictionary[fieldName] =+= String(fieldValue) + encoder.anyDictionary[fieldName] = fieldValue + /// Int Arrays + case let fieldValue as [Int]: + let strs: [String] = fieldValue.map { String($0) } + encoder.dictionary[fieldName] = strs.joined(separator: ",") + encoder.anyDictionary[fieldName] = fieldValue + case let fieldValue as [Int8]: + let strs: [String] = fieldValue.map { String($0) } + encoder.dictionary[fieldName] = strs.joined(separator: ",") + encoder.anyDictionary[fieldName] = fieldValue + case let fieldValue as [Int16]: + let strs: [String] = fieldValue.map { String($0) } + encoder.dictionary[fieldName] = strs.joined(separator: ",") + encoder.anyDictionary[fieldName] = fieldValue + case let fieldValue as [Int32]: + let strs: [String] = fieldValue.map { String($0) } + encoder.dictionary[fieldName] = strs.joined(separator: ",") + encoder.anyDictionary[fieldName] = fieldValue + case let fieldValue as [Int64]: + let strs: [String] = fieldValue.map { String($0) } + encoder.dictionary[fieldName] = strs.joined(separator: ",") + encoder.anyDictionary[fieldName] = fieldValue + /// UInts + case let fieldValue as UInt: + encoder.dictionary[fieldName] =+= String(fieldValue) + encoder.anyDictionary[fieldName] = fieldValue + /// Int Arrays + case let fieldValue as UInt8: + encoder.dictionary[fieldName] =+= String(fieldValue) + encoder.anyDictionary[fieldName] = fieldValue + /// Int Arrays + case let fieldValue as UInt16: + encoder.dictionary[fieldName] =+= String(fieldValue) + encoder.anyDictionary[fieldName] = fieldValue + /// Int Arrays + case let fieldValue as UInt32: + encoder.dictionary[fieldName] =+= String(fieldValue) + encoder.anyDictionary[fieldName] = fieldValue + /// Int Arrays + case let fieldValue as UInt64: + encoder.dictionary[fieldName] =+= String(fieldValue) + encoder.anyDictionary[fieldName] = fieldValue + /// Int Arrays + /// UInt Arrays + case let fieldValue as [UInt]: + let strs: [String] = fieldValue.map { String($0) } + encoder.dictionary[fieldName] = strs.joined(separator: ",") + encoder.anyDictionary[fieldName] = fieldValue + /// Int Arrays + case let fieldValue as [UInt8]: + let strs: [String] = fieldValue.map { String($0) } + encoder.dictionary[fieldName] = strs.joined(separator: ",") + encoder.anyDictionary[fieldName] = fieldValue + case let fieldValue as [UInt16]: + let strs: [String] = fieldValue.map { String($0) } + encoder.dictionary[fieldName] = strs.joined(separator: ",") + encoder.anyDictionary[fieldName] = fieldValue + case let fieldValue as [UInt32]: + let strs: [String] = fieldValue.map { String($0) } + encoder.dictionary[fieldName] = strs.joined(separator: ",") + encoder.anyDictionary[fieldName] = fieldValue + case let fieldValue as [UInt64]: + let strs: [String] = fieldValue.map { String($0) } + encoder.dictionary[fieldName] = strs.joined(separator: ",") + encoder.anyDictionary[fieldName] = fieldValue + /// Floats + case let fieldValue as Float: + encoder.dictionary[fieldName] =+= String(fieldValue) + encoder.anyDictionary[fieldName] = fieldValue + case let fieldValue as [Float]: + let strs: [String] = fieldValue.map { String($0) } + encoder.dictionary[fieldName] = strs.joined(separator: ",") + encoder.anyDictionary[fieldName] = fieldValue + /// Doubles + case let fieldValue as Double: + encoder.dictionary[fieldName] =+= String(fieldValue) + encoder.anyDictionary[fieldName] = fieldValue + case let fieldValue as [Double]: + let strs: [String] = fieldValue.map { String($0) } + encoder.dictionary[fieldName] = strs.joined(separator: ",") + encoder.anyDictionary[fieldName] = fieldValue + /// Bools + case let fieldValue as Bool: + encoder.dictionary[fieldName] = String(fieldValue) + encoder.anyDictionary[fieldName] = fieldValue + case let fieldValue as [Bool]: + let strs: [String] = fieldValue.map { String($0) } + encoder.dictionary[fieldName] = strs.joined(separator: ",") + encoder.anyDictionary[fieldName] = fieldValue + /// Strings + case let fieldValue as String: + encoder.dictionary[fieldName] =+= fieldValue + encoder.anyDictionary[fieldName] = fieldValue + case let fieldValue as [String]: + encoder.dictionary[fieldName] = fieldValue.joined(separator: ",") + encoder.anyDictionary[fieldName] = fieldValue + /// Dates + case let fieldValue as Date: + switch encoder.dateEncodingStrategy { + case .formatted(let formatter): + encoder.dictionary[fieldName] = formatter.string(from: fieldValue) + encoder.anyDictionary[fieldName] = fieldValue + case .deferredToDate: + let date = NSNumber(value: fieldValue.timeIntervalSinceReferenceDate) + encoder.dictionary[fieldName] = date.stringValue + encoder.anyDictionary[fieldName] = fieldValue + case .secondsSince1970: + let date = NSNumber(value: fieldValue.timeIntervalSince1970) + encoder.dictionary[fieldName] = date.stringValue + encoder.anyDictionary[fieldName] = fieldValue + case .millisecondsSince1970: + let date = NSNumber(value: 1000 * fieldValue.timeIntervalSince1970) + encoder.dictionary[fieldName] = date.stringValue + encoder.anyDictionary[fieldName] = fieldValue + case .iso8601: + if #available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) { + encoder.dictionary[fieldName] = _iso8601Formatter.string(from: fieldValue) + encoder.anyDictionary[fieldName] = fieldValue + } else { + fatalError("ISO8601DateFormatter is unavailable on this platform.") + } + case .custom(let closure): + try closure(fieldValue, encoder) + #if swift(>=5) && !os(Linux) + @unknown default: + throw DateError.unknownStrategy + #endif + } + case let fieldValue as [Date]: + switch encoder.dateEncodingStrategy { + case .deferredToDate: + let dbs: [NSNumber] = fieldValue.map { NSNumber(value: $0.timeIntervalSinceReferenceDate) } + let strs: [String] = dbs.map { ($0).stringValue} + encoder.dictionary[fieldName] = strs.joined(separator: ",") + encoder.anyDictionary[fieldName] = fieldValue + case .secondsSince1970: + let dbs: [NSNumber] = fieldValue.map { NSNumber(value: $0.timeIntervalSince1970) } + let strs: [String] = dbs.map { ($0).stringValue} + encoder.dictionary[fieldName] = strs.joined(separator: ",") + encoder.anyDictionary[fieldName] = fieldValue + case .millisecondsSince1970: + let dbs: [NSNumber] = fieldValue.map { NSNumber(value: ($0.timeIntervalSince1970)/1000) } + let strs: [String] = dbs.map { ($0).stringValue} + encoder.dictionary[fieldName] = strs.joined(separator: ",") + encoder.anyDictionary[fieldName] = fieldValue + case .iso8601: + if #available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) { + let strs: [String] = fieldValue.map { _iso8601Formatter.string(from: $0) } + encoder.dictionary[fieldName] = strs.joined(separator: ",") + encoder.anyDictionary[fieldName] = fieldValue + } else { + fatalError("ISO8601DateFormatter is unavailable on this platform.") + } + case .formatted(let formatter): + let strs: [String] = fieldValue.map { formatter.string(from: $0) } + encoder.dictionary[fieldName] = strs.joined(separator: ",") + encoder.anyDictionary[fieldName] = fieldValue + // This calls us back with each serialized element individually, with the same fieldName key, + // which builds a comma-separated list using the '=+=' operator. + case .custom(let closure): + for element in fieldValue { + try closure(element, encoder) + } + #if swift(>=5) && !os(Linux) + @unknown default: + throw DateError.unknownStrategy + #endif + } + case let fieldValue as Operation: + encoder.dictionary[fieldName] = fieldValue.getStringValue() + encoder.anyDictionary[fieldName] = fieldValue + case let fieldValue as Ordering: + encoder.dictionary[fieldName] = fieldValue.getStringValue() + encoder.anyDictionary[fieldName] = fieldValue + case let fieldValue as Pagination: + encoder.dictionary[fieldName] = fieldValue.getStringValue() + encoder.anyDictionary[fieldName] = fieldValue + default: + if fieldName.isEmpty { + encoder.dictionary = [:] // Make encoder instance reusable + encoder.anyDictionary = [:] // Make encoder instance reusable + try value.encode(to: encoder) + } else { + do { + let jsonData = try JSONEncoder().encode(value) + encoder.dictionary[fieldName] = String(data: jsonData, encoding: .utf8) + encoder.anyDictionary[fieldName] = jsonData + } catch let error { + throw encoder.encodingError(value, underlyingError: error) + } + } + } + } + + internal func encodingError(_ value: Any, underlyingError: Swift.Error?) -> EncodingError { + let fieldName = Coder.getFieldName(from: codingPath) + let errorCtx = EncodingError.Context(codingPath: codingPath, debugDescription: "Could not process field named '\(fieldName)'.", underlyingError: underlyingError) + return EncodingError.invalidValue(value, errorCtx) + } + + private struct KeyedContainer: KeyedEncodingContainerProtocol { + var encoder: QueryEncoder + var codingPath: [CodingKey] { return [] } + + /// The typical path for encoding a QueryParams (keyed) type. This encode will be called + /// for each field in turn. + func encode(_ value: T, forKey key: Key) throws where T : Encodable { + self.encoder.codingPath.append(key) + defer { self.encoder.codingPath.removeLast() } + try encoder._encode(value: value) + } + + func encodeNil(forKey: Key) throws {} + + func nestedContainer(keyedBy keyType: NestedKey.Type, forKey key: Key) -> KeyedEncodingContainer where NestedKey : CodingKey { + return encoder.container(keyedBy: keyType) + } + + func nestedUnkeyedContainer(forKey key: Key) -> UnkeyedEncodingContainer { + return encoder.unkeyedContainer() + } + + func superEncoder() -> Encoder { + return encoder + } + + func superEncoder(forKey key: Key) -> Encoder { + return encoder + } + } + + private struct UnkeyedContainer: UnkeyedEncodingContainer, SingleValueEncodingContainer { + var encoder: QueryEncoder + + var codingPath: [CodingKey] { return [] } + + var count: Int { return 0 } + + func nestedContainer(keyedBy keyType: NestedKey.Type) -> KeyedEncodingContainer where NestedKey : CodingKey { + return encoder.container(keyedBy: keyType) + } + + func nestedUnkeyedContainer() -> UnkeyedEncodingContainer { + return self + } + + func superEncoder() -> Encoder { + return encoder + } + + func encodeNil() throws {} + + /// This unkeyed encode will be called by a custom Date encoder. The correct key (field + /// name) will already have been set by a call to the KeyedEncodingContainer. + func encode(_ value: T) throws where T : Encodable { + try encoder._encode(value: value) + } + } +} + +// The '=+=' operator builds a comma-separated list of values for a given fieldName when encoding a [Date] that uses a custom formatting. +infix operator =+= + func =+= (lhs: inout String?, rhs: String) { + if let lhsValue = lhs { + lhs = lhsValue + "," + rhs + } else { + lhs = rhs + } + } + diff --git a/Pods/KituraContracts/Sources/KituraContracts/Contracts.swift b/Pods/KituraContracts/Sources/KituraContracts/Contracts.swift new file mode 100644 index 0000000..fd0b12d --- /dev/null +++ b/Pods/KituraContracts/Sources/KituraContracts/Contracts.swift @@ -0,0 +1,1550 @@ +/** + * Copyright IBM Corporation 2017 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + + import Foundation + +// MARK + +/** + An error representing a failed request. + This definition is intended to be used by both the client side (e.g. KituraKit) + and server side (e.g. Kitura) of the request (typically a HTTP REST request). + + ### Usage Example: ### + + In this example, the `RequestError` is used in a Kitura server Codable route handler to + indicate the request has failed because the requested record was not found. + ````swift + router.get("/users") { (id: Int, respondWith: (User?, RequestError?) -> Void) in + ... + respondWith(nil, RequestError.notFound) + ... + } + ```` + */ +public struct RequestError: RawRepresentable, Equatable, Hashable, Comparable, Error, CustomStringConvertible { + /** + A typealias representing the type of error that has occurred. + The range of error codes from 100 up to 599 are reserved for HTTP status codes. + Custom error codes may be used and must not conflict with this range. + */ + public typealias RawValue = Int + + /** + Representation of the error body. + May be a type-erased Codable object or a Data (in a particular format). + */ + public enum ErrorBody { + /// Codable object. + case codable(Codable) + /// Data object. + case data(Data, BodyFormat) + } + + // MARK: Creating a RequestError from a numeric code + /** + Creates an error representing the given error code. + + - parameter rawValue: An Int indicating an error code representing the type of error that has occurred. + */ + public init(rawValue: Int) { + self.rawValue = rawValue + self.reason = "error_\(rawValue)" + } + + /** + Creates an error representing the given error code and reason string. + + - parameter rawValue: An Int indicating an error code representing the type of error that has occurred. + - parameter reason: A human-readable description of the error code. + */ + public init(rawValue: Int, reason: String) { + self.rawValue = rawValue + self.reason = reason + } + + /** + Creates an error representing the given base error, with a custom + response body given as a Codable. + + - parameter base: A `RequestError` object. + - parameter body: A representation of the error body - an object representing further details of the failure. + */ + public init(_ base: RequestError, body: Body) { + self.rawValue = base.rawValue + self.reason = base.reason + self.body = .codable(body) + self.bodyDataEncoder = { format in + switch format { + case .json: return try JSONEncoder().encode(body) + default: throw UnsupportedBodyFormatError(format) + } + } + } + + /** + Creates an error respresenting the given base error, with a custom + response body given as Data and a BodyFormat. + + - parameter base: A `RequestError` object. + - parameter bodyData: A `Data` object. + - parameter format: A `BodyFormat` object used to check whether it is legal JSON. + - throws: An `UnsupportedBodyFormatError` if the provided `BodyFormat` + is not supported. + */ + public init(_ base: RequestError, bodyData: Data, format: BodyFormat) throws { + self.rawValue = base.rawValue + self.reason = base.reason + self.body = .data(bodyData, format) + switch format { + case .json: break + default: throw UnsupportedBodyFormatError(format) + } + } + + // MARK: Accessing information about the error + + /** + An error code representing the type of error that has occurred. + The range of error codes from 100 up to 599 are reserved for HTTP status codes. + Custom error codes may be used and must not conflict with this range. + */ + public let rawValue: Int + + /** + A human-readable description of the error code. + */ + public let reason: String + + /** + Representation of the error body - an object representing further + details of the failure. + + The value may be: + - `nil` if there is no body + - a (type-erased) Codable object if the error was initialized with `init(_:body:)` + - bytes of data and a signifier of the format in which they are stored (eg: JSON) + if the error was initialized with `init(_:bodyData:format:)` + + ### Usage example: ### + ````swift + if let errorBody = error.body { + switch error.body { + case let .codable(body): ... // body is Codable + case let .data(bytes, format): ... // bytes is Data, format is BodyFormat + } + } + ```` + + - Note: If you need a Codable representation and the body is data, you + can call the `bodyAs(_:)` function to get the converted value. + */ + public private(set) var body: ErrorBody? = nil + + // A closure used to hide the generic type of the Codable body + // for later encoding to Data. + private var bodyDataEncoder: ((BodyFormat) throws -> Data)? = nil + + /** + Returns the Codable error body encoded into bytes in a given format (eg: JSON). + + This function should be used if the RequestError was created using + `init(_:body:)`, otherwise it will return `nil`. + + - Note: This function is primarily intended for use by the Kitura Router so + that it can encode and send a custom error body returned from + a codable route. + + ### Usage Example: ### + ````swift + do { + if let errorBodyData = try error.encodeBody(.json) { + ... + } + } catch { + // Handle the failure to encode + } + ```` + - parameter format: Describes the format that should be used + (for example: `BodyFormat.json`). + - returns: The `Data` object or `nil` if there is no body, or if the + error was not initialized with `init(_:body:)`. + - throws: An `EncodingError` if the encoding fails. + - throws: An `UnsupportedBodyFormatError` if the provided `BodyFormat` + is not supported. + */ + public func encodeBody(_ format: BodyFormat) throws -> Data? { + guard case .codable? = body else { return nil } + return try bodyDataEncoder?(format) + } + + /** + Returns the Data error body as the requested `Codable` type. + + This function should be used if the RequestError was created using + `init(_:bodyData:format:)`, otherwise it will return `nil`. + + This function throws; you can use `bodyAs(_:)` instead if you want + to ignore DecodingErrors. + + - Note: This function is primarily intended for use by users of KituraKit + or similar client-side code that needs to convert a custom error + response from `Data` to a `Codable` type. + + ### Usage Example: ### + ````swift + do { + if let errorBody = try error.decodeBody(MyCodableType.self) { + ... + } + } catch { + // Handle failure to decode + } + ```` + - parameter type: The type of the value to decode from the body data + (for example: `MyCodableType.self`). + - returns: The `Codable` object or `nil` if there is no body or if the + error was not initialized with `init(_:bodyData:format:)`. + - throws: A `DecodingError` if decoding fails. + */ + public func decodeBody(_ type: Body.Type) throws -> Body? { + guard case let .data(bodyData, format)? = body else { return nil } + switch format { + case .json: return try JSONDecoder().decode(type, from: bodyData) + default: throw UnsupportedBodyFormatError(format) + } + } + + /** + Returns the Data error body as the requested `Codable` type. + + This function should be used if the RequestError was created using + `init(_:bodyData:format:)`, otherwise it will return `nil`. + + This function ignores DecodingErrors, and returns `nil` if decoding + fails. If you want DecodingErrors to be thrown, use `decodeBody(_:)` + instead. + + - Note: This function is primarily intended for use by users of KituraKit + or similar client-side code that needs to convert a custom error + response from `Data` to a `Codable` type. + + ### Usage Example: ### + ````swift + if let errorBody = error.bodyAs(MyCodableType.self) { + ... + } + ```` + - parameter type: The type of the value to decode from the body data + (for example: `MyCodableType.self`). + - returns: The `Codable` object or `nil` if there is no body, or if the + error was not initialized with `init(_:bodyData:format:)`, or + if decoding fails. + */ + public func bodyAs(_ type: Body.Type) -> Body? { + return (try? decodeBody(type)) ?? nil + } + + // MARK: Comparing RequestErrors + + /** + Returns a Boolean value indicating whether the value of the first argument is less than that of the second argument. + */ + public static func < (lhs: RequestError, rhs: RequestError) -> Bool { + return lhs.rawValue < rhs.rawValue + } + + /** + Indicates whether two URLs are the same. + */ + public static func == (lhs: RequestError, rhs: RequestError) -> Bool { + return (lhs.rawValue == rhs.rawValue && lhs.reason == rhs.reason) + } + + // MARK: Describing a RequestError + + /** + A textual description of the RequestError instance containing the error code and reason. + */ + public var description: String { + return "\(rawValue) : \(reason)" + } + + /** + The computed hash value for the RequestError instance. + */ + public var hashValue: Int { + let str = reason + String(rawValue) + return str.hashValue + } +} + +/** + Extends `RequestError` to provide HTTP specific error code and reason values. + */ +extension RequestError { + + /** + The HTTP status code for the error. + This value should be a valid HTTP status code if inside the range 100 to 599, + however, it may take a value outside that range when representing other types + of error. + */ + public var httpCode: Int { + return rawValue + } + + /** + Creates an error representing a HTTP status code. + - Parameter httpCode: A standard HTTP status code. + */ + public init(httpCode: Int) { + self.rawValue = httpCode + self.reason = RequestError.reason(forHTTPCode: httpCode) + } + + // MARK: Accessing constants representing HTTP status codes + /// HTTP code 100 - Continue + public static let `continue` = RequestError(httpCode: 100) + /// HTTP code 101 - Switching Protocols + public static let switchingProtocols = RequestError(httpCode: 101) + /// HTTP code 200 - OK + public static let ok = RequestError(httpCode: 200) + /// HTTP code 201 - Created + public static let created = RequestError(httpCode: 201) + /// HTTP code 202 - Accepted + public static let accepted = RequestError(httpCode: 202) + /// HTTP code 203 - Non Authoritative Information + public static let nonAuthoritativeInformation = RequestError(httpCode: 203) + /// HTTP code 204 - No Content + public static let noContent = RequestError(httpCode: 204) + /// HTTP code 205 - Reset Content + public static let resetContent = RequestError(httpCode: 205) + /// HTTP code 206 - Partial Content + public static let partialContent = RequestError(httpCode: 206) + /// HTTP code 207 - Multi Status + public static let multiStatus = RequestError(httpCode: 207) + /// HTTP code 208 - Already Reported + public static let alreadyReported = RequestError(httpCode: 208) + /// HTTP code 226 - IM Used + public static let imUsed = RequestError(httpCode: 226) + /// HTTP code 300 - Multiple Choices + public static let multipleChoices = RequestError(httpCode: 300) + /// HTTP code 301 - Moved Permanently + public static let movedPermanently = RequestError(httpCode: 301) + /// HTTP code 302 - Found + public static let found = RequestError(httpCode: 302) + /// HTTP code 303 - See Other + public static let seeOther = RequestError(httpCode: 303) + /// HTTP code 304 - Not Modified + public static let notModified = RequestError(httpCode: 304) + /// HTTP code 305 - Use Proxy + public static let useProxy = RequestError(httpCode: 305) + /// HTTP code 307 - Temporary Redirect + public static let temporaryRedirect = RequestError(httpCode: 307) + /// HTTP code 308 - Permanent Redirect + public static let permanentRedirect = RequestError(httpCode: 308) + /// HTTP code 400 - Bad Request + public static let badRequest = RequestError(httpCode: 400) + /// HTTP code 401 - Unauthorized + public static let unauthorized = RequestError(httpCode: 401) + /// HTTP code 402 - Payment Required + public static let paymentRequired = RequestError(httpCode: 402) + /// HTTP code 403 - Forbidden + public static let forbidden = RequestError(httpCode: 403) + /// HTTP code 404 - Not Found + public static let notFound = RequestError(httpCode: 404) + /// HTTP code 405 - Method Not Allowed + public static let methodNotAllowed = RequestError(httpCode: 405) + /// HTTP code 406 - Not Acceptable + public static let notAcceptable = RequestError(httpCode: 406) + /// HTTP code 407 - Proxy Authentication Required + public static let proxyAuthenticationRequired = RequestError(httpCode: 407) + /// HTTP code 408 - Request Timeout + public static let requestTimeout = RequestError(httpCode: 408) + /// HTTP code 409 - Conflict + public static let conflict = RequestError(httpCode: 409) + /// HTTP code 410 - Gone + public static let gone = RequestError(httpCode: 410) + /// HTTP code 411 - Length Required + public static let lengthRequired = RequestError(httpCode: 411) + /// HTTP code 412 - Precondition Failed + public static let preconditionFailed = RequestError(httpCode: 412) + /// HTTP code 413 - Payload Too Large + public static let payloadTooLarge = RequestError(httpCode: 413) + /// HTTP code 414 - URI Too Long + public static let uriTooLong = RequestError(httpCode: 414) + /// HTTP code 415 - Unsupported Media Type + public static let unsupportedMediaType = RequestError(httpCode: 415) + /// HTTP code 416 - Range Not Satisfiable + public static let rangeNotSatisfiable = RequestError(httpCode: 416) + /// HTTP code 417 - Expectation Failed + public static let expectationFailed = RequestError(httpCode: 417) + /// HTTP code 421 - Misdirected Request + public static let misdirectedRequest = RequestError(httpCode: 421) + /// HTTP code 422 - Unprocessable Entity + public static let unprocessableEntity = RequestError(httpCode: 422) + /// HTTP code 423 - Locked + public static let locked = RequestError(httpCode: 423) + /// HTTP code 424 - Failed Dependency + public static let failedDependency = RequestError(httpCode: 424) + /// HTTP code 426 - Upgrade Required + public static let upgradeRequired = RequestError(httpCode: 426) + /// HTTP code 428 - Precondition Required + public static let preconditionRequired = RequestError(httpCode: 428) + /// HTTP code 429 - Too Many Requests + public static let tooManyRequests = RequestError(httpCode: 429) + /// HTTP code 431 - Request Header Fields Too Large + public static let requestHeaderFieldsTooLarge = RequestError(httpCode: 431) + /// HTTP code 451 - Unavailable For Legal Reasons + public static let unavailableForLegalReasons = RequestError(httpCode: 451) + /// HTTP code 500 - Internal Server Error + public static let internalServerError = RequestError(httpCode: 500) + /// HTTP code 501 - Not Implemented + public static let notImplemented = RequestError(httpCode: 501) + /// HTTP code 502 - Bad Gateway + public static let badGateway = RequestError(httpCode: 502) + /// HTTP code 503 - Service Unavailable + public static let serviceUnavailable = RequestError(httpCode: 503) + /// HTTP code 504 - Gateway Timeout + public static let gatewayTimeout = RequestError(httpCode: 504) + /// HTTP code 505 - HTTP Version Not Supported + public static let httpVersionNotSupported = RequestError(httpCode: 505) + /// HTTP code 506 - Variant Also Negotiates + public static let variantAlsoNegotiates = RequestError(httpCode: 506) + /// HTTP code 507 - Insufficient Storage + public static let insufficientStorage = RequestError(httpCode: 507) + /// HTTP code 508 - Loop Detected + public static let loopDetected = RequestError(httpCode: 508) + /// HTTP code 510 - Not Extended + public static let notExtended = RequestError(httpCode: 510) + /// HTTP code 511 - Network Authentication Required + public static let networkAuthenticationRequired = RequestError(httpCode: 511) + + private static func reason(forHTTPCode code: Int) -> String { + switch code { + case 100: return "Continue" + case 101: return "Switching Protocols" + case 200: return "OK" + case 201: return "Created" + case 202: return "Accepted" + case 203: return "Non-Authoritative Information" + case 204: return "No Content" + case 205: return "Reset Content" + case 206: return "Partial Content" + case 207: return "Multi-Status" + case 208: return "Already Reported" + case 226: return "IM Used" + case 300: return "Multiple Choices" + case 301: return "Moved Permanently" + case 302: return "Found" + case 303: return "See Other" + case 304: return "Not Modified" + case 305: return "Use Proxy" + case 307: return "Temporary Redirect" + case 308: return "Permanent Redirect" + case 400: return "Bad Request" + case 401: return "Unauthorized" + case 402: return "Payment Required" + case 403: return "Forbidden" + case 404: return "Not Found" + case 405: return "Method Not Allowed" + case 406: return "Not Acceptable" + case 407: return "Proxy Authentication Required" + case 408: return "Request Timeout" + case 409: return "Conflict" + case 410: return "Gone" + case 411: return "Length Required" + case 412: return "Precondition Failed" + case 413: return "Payload Too Large" + case 414: return "URI Too Long" + case 415: return "Unsupported Media Type" + case 416: return "Range Not Satisfiable" + case 417: return "Expectation Failed" + case 421: return "Misdirected Request" + case 422: return "Unprocessable Entity" + case 423: return "Locked" + case 424: return "Failed Dependency" + case 426: return "Upgrade Required" + case 428: return "Precondition Required" + case 429: return "Too Many Requests" + case 431: return "Request Header Fields Too Large" + case 451: return "Unavailable For Legal Reasons" + case 500: return "Internal Server Error" + case 501: return "Not Implemented" + case 502: return "Bad Gateway" + case 503: return "Service Unavailable" + case 504: return "Gateway Timeout" + case 505: return "HTTP Version Not Supported" + case 506: return "Variant Also Negotiates" + case 507: return "Insufficient Storage" + case 508: return "Loop Detected" + case 510: return "Not Extended" + case 511: return "Network Authentication Required" + default: return "http_\(code)" + } + } +} + +/** + An object that conforms to QueryParams is identified as being decodable from URLEncoded data. + This can be applied to a Codable route to define the names and types of the expected query parameters, and provide type-safe access to their values. The `QueryDecoder` is used to decode the URL encoded parameters into an instance of the conforming type. + ### Usage Example: ### + ```swift + struct Query: QueryParams { + let id: Int + } + router.get("/user") { (query: Query, respondWith: (User?, RequestError?) -> Void) in + guard let user: User = userArray[query.id] else { + return respondWith(nil, .notFound) + } + respondWith(user, nil) + } + ``` + ### Decoding Empty Values: + When an HTML form is sent with an empty or unchecked field, the corresponding key/value pair is sent with an empty value (i.e. `&key1=&key2=`). + The corresponding mapping to Swift types performed by `QueryDecoder` is as follows: + - Any Optional type (including `String?`) defaults to `nil` + - Non-optional `String` successfully decodes to `""` + - Non-optional `Bool` decodes to `false` + - All other non-optional types throw a decoding error + */ +public protocol QueryParams: Codable { + + /** + The decoding strategy for Dates. + The variable can be defined within your QueryParams object and tells the `QueryDecoder` how dates should be decoded. The enum used for the DateDecodingStrategy is the same one found in the `JSONDecoder`. + + ### Usage Example: ### + + ```swift + struct MyQuery: QueryParams { + let date: Date + static let dateDecodingStrategy: JSONDecoder.DateDecodingStrategy = .iso8601 + static let dateEncodingStrategy: JSONEncoder.DateEncodingStrategy = .iso8601 + } + + let queryParams = ["date": "2019-09-06T10:14:41+0000"] + + let query = try QueryDecoder(dictionary: queryParams).decode(MyQuery.self) + ``` + */ + static var dateDecodingStrategy: JSONDecoder.DateDecodingStrategy { get } + + /** + The encoding strategy for Dates. + The variable would be defined within your QueryParams object and tells the `QueryEncoder` how dates should be encoded. The enum used for the DateEncodingStrategy is the same one found in the `JSONEncoder`. + + ### Usage Example: ### + + ```swift + struct MyQuery: QueryParams { + let date: Date + static let dateDecodingStrategy: JSONDecoder.DateDecodingStrategy = .iso8601 + static let dateEncodingStrategy: JSONEncoder.DateEncodingStrategy = .iso8601 + } + + let query = MyQuery(date: Date(timeIntervalSinceNow: 0)) + + let myQueryDict: [String: String] = try QueryEncoder().encode(query) + ``` + */ + static var dateEncodingStrategy: JSONEncoder.DateEncodingStrategy { get } +} + +/// Defines default values for the `dateDecodingStrategy` and `dateEncodingStrategy`. The +/// default formatting for a `Date` in a `QueryParams` type is defined by `Coder.dateFormatter`, +/// which uses the "UTC" timezone and "yyyy-MM-dd'T'HH:mm:ssZ" date format. +extension QueryParams { + + /// Default value: `Coder.defaultDateFormatter` + public static var dateDecodingStrategy: JSONDecoder.DateDecodingStrategy { + return .formatted(Coder.defaultDateFormatter) + } + + /// Default value: `Coder.defaultDateFormatter` + public static var dateEncodingStrategy: JSONEncoder.DateEncodingStrategy { + return .formatted(Coder.defaultDateFormatter) + } + +} + +/** + An error representing a failure to create an `Identifier`. + +### Usage Example: ### + + An `QueryParamsError.invalidValue` may be thrown if the given type cannot be constructed from the given string. + ````swift + throw QueryParamsError.invalidValue + ```` + */ +public enum QueryParamsError: Error { + /// Represents a failure to create a given filtering type from a given `String` representation. + case invalidValue + +} + +/** + An error representing a failure to create an `Identifier`. + +### Usage Example: ### + + An `IdentifierError.invalidValue` may be thrown if the given string cannot be converted to an integer when using an `Identifier`. + ````swift + throw IdentifierError.invalidValue + ```` + */ +public enum IdentifierError: Error { + /// Represents a failure to create an `Identifier` from a given `String` representation. + case invalidValue +} + +/** + An identifier for an entity with a string representation. + +### Usage Example: ### + ````swift + // Used in the Id field. + public typealias IdentifierCodableClosure = (Id, I, @escaping CodableResultClosure) -> Void + ```` + */ +public protocol Identifier: Codable { + /// Creates an identifier from a given string value. + /// - Throws: An IdentifierError.invalidValue if the given string is not a valid representation. + init(value: String) throws + + /// The string representation of the identifier. + var value: String { get } +} + +/** + Extends `String` to comply to the `Identifier` protocol. + +### Usage Example: ### + ````swift + // The Identifier used in the Id field could be a `String`. + public typealias IdentifierCodableClosure = (Id, I, @escaping CodableResultClosure) -> Void + ```` + */ +extension String: Identifier { + /// Creates a string identifier from a given string value. + public init(value: String) { + self.init(value) + } + + /// The string representation of the identifier. + public var value: String { + return self + } +} + +/** + Extends `Int` to comply to the `Identifier` protocol. + +### Usage Example: ### + ````swift + // The Identifier used in the Id field could be an `Int`. + public typealias IdentifierCodableClosure = (Id, I, @escaping CodableResultClosure) -> Void + ```` + */ +extension Int: Identifier { + /// Creates an integer identifier from a given string representation. + /// - Throws: An `IdentifierError.invalidValue` if the given string cannot be converted to an integer. + public init(value: String) throws { + if let id = Int(value) { + self = id + } else { + throw IdentifierError.invalidValue + } + } + + /// The string representation of the identifier. + public var value: String { + return String(describing: self) + } +} + +/** + Extends `Int8` to comply to the `Identifier` protocol. + +### Usage Example: ### + ````swift + // The Identifier used in the Id field could be an `Int8`. + public typealias IdentifierCodableClosure = (Id, I, @escaping CodableResultClosure) -> Void + ```` + */ +extension Int8: Identifier { + /// Creates an integer identifier from a given string representation. + /// - Throws: An `IdentifierError.invalidValue` if the given string cannot be converted to an integer. + public init(value: String) throws { + if let id = Int8(value) { + self = id + } else { + throw IdentifierError.invalidValue + } + } + + /// The string representation of the identifier. + public var value: String { + return String(describing: self) + } +} + +/** + Extends `Int16` to comply to the `Identifier` protocol. + +### Usage Example: ### + ````swift + // The Identifier used in the Id field could be an `Int16`. + public typealias IdentifierCodableClosure = (Id, I, @escaping CodableResultClosure) -> Void + ```` + */ +extension Int16: Identifier { + /// Creates an integer identifier from a given string representation. + /// - Throws: An `IdentifierError.invalidValue` if the given string cannot be converted to an integer. + public init(value: String) throws { + if let id = Int16(value) { + self = id + } else { + throw IdentifierError.invalidValue + } + } + + /// The string representation of the identifier. + public var value: String { + return String(describing: self) + } +} + +/** + Extends `Int32` to comply to the `Identifier` protocol. + +### Usage Example: ### + ````swift + // The Identifier used in the Id field could be an `Int32`. + public typealias IdentifierCodableClosure = (Id, I, @escaping CodableResultClosure) -> Void + ```` + */ +extension Int32: Identifier { + /// Creates an integer identifier from a given string representation. + /// - Throws: An `IdentifierError.invalidValue` if the given string cannot be converted to an integer. + public init(value: String) throws { + if let id = Int32(value) { + self = id + } else { + throw IdentifierError.invalidValue + } + } + + /// The string representation of the identifier. + public var value: String { + return String(describing: self) + } +} + +/** + Extends `Int64` to comply to the `Identifier` protocol. + +### Usage Example: ### + ````swift + // The Identifier used in the Id field could be an `Int64`. + public typealias IdentifierCodableClosure = (Id, I, @escaping CodableResultClosure) -> Void + ```` + */ +extension Int64: Identifier { + /// Creates an integer identifier from a given string representation. + /// - Throws: An `IdentifierError.invalidValue` if the given string cannot be converted to an integer. + public init(value: String) throws { + if let id = Int64(value) { + self = id + } else { + throw IdentifierError.invalidValue + } + } + + /// The string representation of the identifier. + public var value: String { + return String(describing: self) + } +} + +/** + Extends `UInt` to comply to the `Identifier` protocol. + +### Usage Example: ### + ````swift + // The Identifier used in the Id field could be an `UInt`. + public typealias IdentifierCodableClosure = (Id, I, @escaping CodableResultClosure) -> Void + ```` + */ +extension UInt: Identifier { + /// Creates an unsigned integer identifier from a given string representation. + /// - Throws: An `IdentifierError.invalidValue` if the given string cannot be converted to an unsigned integer. + public init(value: String) throws { + if let id = UInt(value) { + self = id + } else { + throw IdentifierError.invalidValue + } + } + + /// The string representation of the identifier. + public var value: String { + return String(describing: self) + } +} + +/** + Extends `UInt8` to comply to the `Identifier` protocol. + +### Usage Example: ### + ````swift + // The Identifier used in the Id field could be an `UInt8`. + public typealias IdentifierCodableClosure = (Id, I, @escaping CodableResultClosure) -> Void + ```` + */ +extension UInt8: Identifier { + /// Creates an unsigned integer identifier from a given string representation. + /// - Throws: An `IdentifierError.invalidValue` if the given string cannot be converted to an unsigned integer. + public init(value: String) throws { + if let id = UInt8(value) { + self = id + } else { + throw IdentifierError.invalidValue + } + } + + /// The string representation of the identifier. + public var value: String { + return String(describing: self) + } +} + +/** + Extends `UInt16` to comply to the `Identifier` protocol. + +### Usage Example: ### + ````swift + // The Identifier used in the Id field could be an `UInt16`. + public typealias IdentifierCodableClosure = (Id, I, @escaping CodableResultClosure) -> Void + ```` + */ +extension UInt16: Identifier { + /// Creates an unsigned integer identifier from a given string representation. + /// - Throws: An `IdentifierError.invalidValue` if the given string cannot be converted to an unsigned integer. + public init(value: String) throws { + if let id = UInt16(value) { + self = id + } else { + throw IdentifierError.invalidValue + } + } + + /// The string representation of the identifier. + public var value: String { + return String(describing: self) + } +} + +/** + Extends `UInt32` to comply to the `Identifier` protocol. + +### Usage Example: ### + ````swift + // The Identifier used in the Id field could be an `UInt32`. + public typealias IdentifierCodableClosure = (Id, I, @escaping CodableResultClosure) -> Void + ```` + */ +extension UInt32: Identifier { + /// Creates an unsigned integer identifier from a given string representation. + /// - Throws: An `IdentifierError.invalidValue` if the given string cannot be converted to an unsigned integer. + public init(value: String) throws { + if let id = UInt32(value) { + self = id + } else { + throw IdentifierError.invalidValue + } + } + + /// The string representation of the identifier. + public var value: String { + return String(describing: self) + } +} + +/** + Extends `UInt64` to comply to the `Identifier` protocol. + +### Usage Example: ### + ````swift + // The Identifier used in the Id field could be an `UInt64`. + public typealias IdentifierCodableClosure = (Id, I, @escaping CodableResultClosure) -> Void + ```` + */ +extension UInt64: Identifier { + /// Creates an unsigned integer identifier from a given string representation. + /// - Throws: An `IdentifierError.invalidValue` if the given string cannot be converted to an unsigned integer. + public init(value: String) throws { + if let id = UInt64(value) { + self = id + } else { + throw IdentifierError.invalidValue + } + } + + /// The string representation of the identifier. + public var value: String { + return String(describing: self) + } +} + +extension Double: Identifier { + /// Creates a double identifier from a given string representation. + /// - Throws: An `IdentifierError.invalidValue` if the given string cannot be converted to a Double. + public init(value: String) throws { + if let id = Double(value) { + self = id + } else { + throw IdentifierError.invalidValue + } + } + + /// The string representation of the identifier. + public var value: String { + return String(describing: self) + } +} + +extension Float: Identifier { + /// Creates a float identifier from a given string representation. + /// - Throws: An `IdentifierError.invalidValue` if the given string cannot be converted to a Float. + public init(value: String) throws { + if let id = Float(value) { + self = id + } else { + throw IdentifierError.invalidValue + } + } + + /// The string representation of the identifier. + public var value: String { + return String(describing: self) + } +} + +extension Bool: Identifier { + /// Creates a bool identifier from a given string representation. + /// - Throws: An `IdentifierError.invalidValue` if the given string cannot be converted to a Bool. + public init(value: String) throws { + if let id = Bool(value) { + self = id + } else { + throw IdentifierError.invalidValue + } + } + + /// The string representation of the identifier. + public var value: String { + return String(describing: self) + } +} + +extension UUID: Identifier { + /// Creates a UUID identifier from a given string representation. + /// - Throws: An `IdentifierError.invalidValue` if the given string cannot be converted to a UUID. + public init(value: String) throws { + if let id = UUID(uuidString: value) { + self = id + } else { + throw IdentifierError.invalidValue + } + } + + /// The string representation of the identifier. + public var value: String { + return self.uuidString + } +} + +/** + An enum containing the ordering information + ### Usage Example: ### + To order ascending by name, we would write: + ```swift + Order.asc("name") + ``` +*/ + +public enum Order: Codable { + + /// Represents an ascending order with an associated String value + case asc(String) + /// Represents a descending order with an associated String value + case desc(String) + + // Coding Keys for encoding and decoding + enum CodingKeys: CodingKey { + case asc + case desc + } + + // Function to encode enum case + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + switch self { + case .asc(let value): + try container.encode(value, forKey: .asc) + case .desc(let value): + try container.encode(value, forKey: .desc) + } + } + + // Function to decode enum case + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + do { + let ascValue = try container.decode(String.self, forKey: .asc) + self = .asc(ascValue) + } catch { + let descValue = try container.decode(String.self, forKey: .desc) + self = .desc(descValue) + } + } + + /// Description of the enum case + public var description: String { + switch self { + case let .asc(value): + return "asc(\(value))" + case let .desc(value): + return "desc(\(value))" + } + } + + /// Associated value of the enum case + public var value: String { + switch self { + case let .asc(value): + return value + case let .desc(value): + return value + } + } +} + +/** + A codable struct containing the ordering information + ### Usage Example: ### + To order ascending by name and descending by age, we would write: + ```swift + Ordering(by: .asc("name"), .desc("age")) + ``` +*/ +public struct Ordering: Codable { + /// Array of Orders + var order: [Order]! + + /// Creates an Ordering instance from one or more Orders + public init(by order: Order...) { + self.order = order + } + + /// Creates an Ordering instance from a given array of Orders. + public init(by order: [Order]) { + self.order = order + } + + /// Creates an Ordering instance from a given string value. + internal init(string value: String) throws { + if !value.contains(",") { + let extractedValue = try extractValue(value) + if value.contains("asc") { + self.order = [.asc(extractedValue)] + } else if value.contains("desc") { + self.order = [.desc(extractedValue)] + } else { + throw QueryParamsError.invalidValue + } + } else { + self.order = try value.split(separator: ",").map { String($0) }.map { + let extractedValue = try extractValue($0) + if $0.contains("asc") { + return .asc(extractedValue) + } else if $0.contains("desc") { + return .desc(extractedValue) + } else { + throw QueryParamsError.invalidValue + } + } + } + } + + // Function to extract the String value from the Order enum case + private func extractValue(_ value: String) throws -> String { +#if swift(>=4.2) + guard var startIndex = value.firstIndex(of: "("), + let endIndex = value.firstIndex(of: ")") else { + throw QueryParamsError.invalidValue + } +#else + guard var startIndex = value.index(of: "("), + let endIndex = value.index(of: ")") else { + throw QueryParamsError.invalidValue + } +#endif + + startIndex = value.index(startIndex, offsetBy: 1) + let extractedValue = value[startIndex.. String { + return self.order.map{ $0.description } .joined(separator: ",") + } + + /// Returns an array of Orders + public func getValues() -> [Order] { + return self.order + } +} + + +/** + A codable struct containing the pagination information + ### Usage Example: ### + To get only the first 10 values, we would write: + ```swift + Pagination(size: 10) + ``` + To get the 11th to 20th values, we would write: + ```swift + Pagination(start: 10, size: 10) + ``` +*/ +public struct Pagination: Codable { + private var start: Int + private var size: Int + + /// Creates a Pagination instance from start and size Int values + public init(start: Int = 0, size: Int) { + self.start = start + self.size = size + } + + internal init(string value: String) throws { + let array = value.split(separator: ",") + if array.count != 2 { + throw QueryParamsError.invalidValue + } + self.start = try Int(value: String(array[0])) + self.size = try Int(value: String(array[1])) + } + + internal func getStringValue() -> String { + return "\(start),\(size)" + } + + /// Returns a tuple containing the start and size Int values + public func getValues() -> (start: Int, size: Int) { + return (start, size) + } +} + +/** + An enum defining the available logical operators + ### Usage Example: ### + To use the OR Operator, we would write: + ```swift + Operator.or + ``` +*/ +public enum Operator: String, Codable { + /// OR Operator + case or + /// Equal Operator + case equal + /// LowerThan Operator + case lowerThan + /// LowerThanOrEqual Operator + case lowerThanOrEqual + /// GreaterThan Operator + case greaterThan + /// GreaterThanOrEqual Operator + case greaterThanOrEqual + /// ExclusiveRange Operator + case exclusiveRange + /// InclusiveRange Operator + case inclusiveRange +} + + +/** + An identifier for an operation object. +*/ +public protocol Operation: Codable { + /// Creates an Operation from a string value + init(string: String) throws + + /// Returns the string representation of the parameters for an Operation to be used in the URL. + /// + /// ```swift + /// let range = InclusiveRange(start: 5, end: 10) + /// ``` + /// would be represented as `"5,10"`, which in the URL would translate to: `?range=5,10` + /// This URL format is not an API but an implementation detail that could change in the future. + /// The URL doesn't encode the operator itself instead it is inferred at + /// decoding time by the type information associated with that key. + /// The type information used to decode this URL format is defined by + /// the QueryParams structure associated with a route. + /// The key name in the url maps to the field name in the QueryParams structure. + func getStringValue() -> String + + /// Returns the Operator associated with the Operation. + /// + /// `InclusiveRange(start: 5, end: 10)` will have the operator `Operator.inclusiveRange` + func getOperator() -> Operator +} + + +/** + A codable struct enabling greater than filtering + ### Usage Example: ### + To filter with greaterThan on age which is an Integer, we would write: + ```swift + struct MyQuery: QueryParams { + let age: GreaterThan + } + let query = MyQuery(age: GreaterThan(value: 8)) + ``` + In a URL it would translate to: + ``` + ?age=8 + ``` + + Note: The "age=8" format is not an API but an implementation detail that could change in the future. +*/ +public struct GreaterThan: Operation { + private var value: I + private let `operator`: Operator = .greaterThan + + /// Creates a GreaterThan instance from a given Identifier value + public init(value: I) { + self.value = value + } + + /// Creates a GreaterThan instance from a given String value + public init(string value: String) throws { + self.value = try I(value: value) + } + + /// Returns the stored value + public func getValue() -> I { + return self.value + } + + /// Returns the stored value as a String + public func getStringValue() -> String { + return self.value.value + } + + /// Returns the Operator + public func getOperator() -> Operator { + return self.`operator` + } +} + +/** + A codable struct enabling greater than or equal filtering + ### Usage Example: ### + To filter with greater than or equal on age which is an Integer, we would write: + ```swift + struct MyQuery: QueryParams { + let age: GreaterThanOrEqual + } + let query = MyQuery(age: GreaterThanOrEqual>(value: 8)) + ``` + In a URL it would translate to: + ``` + ?age=8 + ``` + + Note: The "age=8" format is not an API but an implementation detail that could change in the future. +*/ +public struct GreaterThanOrEqual: Operation { + private var value: I + private let `operator`: Operator = .greaterThanOrEqual + + /// Creates a GreaterThanOrEqual instance from a given Identifier value + public init(value: I) { + self.value = value + } + + /// Creates a GreaterThanOrEqual instance from a given String value + public init(string value: String) throws { + self.value = try I(value: value) + } + + /// Returns the stored value + public func getValue() -> I { + return self.value + } + + /// Returns the stored value as a String + public func getStringValue() -> String { + return self.value.value + } + + /// Returns the Operator + public func getOperator() -> Operator { + return self.`operator` + } +} + +/** + A codable struct enabling lower than filtering + ### Usage Example: ### + To filter with lower than on age, we would write: + ```swift + struct MyQuery: QueryParams { + let age: LowerThan + } + let query = MyQuery(age: LowerThan(value: 8)) + ``` + In a URL it would translate to: + ``` + ?age=8 + ``` + + Note: The "age=8" format is not an API but an implementation detail that could change in the future. + ``` +*/ +public struct LowerThan: Operation { + private var value: I + private let `operator`: Operator = .lowerThan + + /// Creates a LowerThan instance from a given Identifier value + public init(value: I) { + self.value = value + } + + /// Creates a LowerThan instance from a given String value + public init(string value: String) throws { + self.value = try I(value: value) + } + + /// Returns the stored value + public func getValue() -> I { + return self.value + } + + /// Returns the stored value as a String + public func getStringValue() -> String { + return String(describing: value) + } + + /// Returns the Operator + public func getOperator() -> Operator { + return self.`operator` + } +} + +/** + A codable struct enabling lower than or equal filtering + ### Usage Example: ### + To filter with lower than or equal on age, we would write: + ```swift + struct MyQuery: QueryParams { + let age: LowerThanOrEqual + } + let query = MyQuery(age: LowerThanOrEqual(value: 8)) + ``` + In a URL it would translate to: + ``` + ?age=8 + ``` + + Note: The "age=8" format is not an API but an implementation detail that could change in the future. +*/ +public struct LowerThanOrEqual: Operation { + private var value: I + private let `operator`: Operator = .lowerThanOrEqual + + /// Creates a LowerThan instance from a given Identifier value + public init(value: I) { + self.value = value + } + + /// Creates a LowerThan instance from a given String value + public init(string value: String) throws { + self.value = try I(value: value) + } + + /// Returns the stored value + public func getValue() -> I { + return self.value + } + + /// Returns the stored value as a String + public func getStringValue() -> String { + return String(describing: value) + } + + /// Returns the Operator + public func getOperator() -> Operator { + return self.`operator` + } +} + +/** + A codable struct enabling to filter with an inclusive range + ### Usage Example: ### + To filter on age using an inclusive range, we would write: + ```swift + struct MyQuery: QueryParams { + let age: InclusiveRange + } + let query = MyQuery(age: InclusiveRange(start: 8, end: 14)) + ``` + In a URL it would translate to: + ``` + ?age=8,14 + ``` + + Note: The "age=8,14" format is not an API but an implementation detail that could change in the future. +*/ +public struct InclusiveRange: Operation { + private var start: I + private var end: I + private let `operator`: Operator = .inclusiveRange + + /// Creates a InclusiveRange instance from given start and end values + public init(start: I, end: I) { + self.start = start + self.end = end + } + + /// Creates a InclusiveRange instance from a given String value + public init(string value: String) throws { + let array = value.split(separator: ",") + if array.count != 2 { + throw QueryParamsError.invalidValue + } + self.start = try I(value: String(array[0])) + self.end = try I(value: String(array[1])) + } + + /// Returns the stored values as a tuple + public func getValue() -> (start: I, end: I) { + return (start: self.start, end: self.end) + } + + /// Returns the stored value as a String + public func getStringValue() -> String { + return "\(start),\(end)" + } + + /// Returns the Operator + public func getOperator() -> Operator { + return self.`operator` + } +} + +/** + A codable struct enabling to filter with an exlusive range + ### Usage Example: ### + To filter on age using an exclusive range, we would write: + ```swift + struct MyQuery: QueryParams { + let age: ExclusiveRange + } + let query = MyQuery(age: ExclusiveRange(start: 8, end: 14)) + ``` + In a URL it would translate to: + ``` + ?age=8,14 + ``` + + Note: The "age=8,14" format is not an API but an implementation detail that could change in the future. +*/ +public struct ExclusiveRange: Operation { + private var start: I + private var end: I + private let `operator`: Operator = .exclusiveRange + + /// Creates a ExclusiveRange instance from given start and end values + public init(start: I, end: I) { + self.start = start + self.end = end + } + + /// Creates a ExclusiveRange instance from a given String value + public init(string value: String) throws { + let array = value.split(separator: ",") + if array.count != 2 { + throw QueryParamsError.invalidValue + } + self.start = try I(value: String(array[0])) + self.end = try I(value: String(array[1])) + } + + /// Returns the stored values as a tuple + public func getValue() -> (start: I, end: I) { + return (start: self.start, end: self.end) + } + + /// Returns the stored value as a String + public func getStringValue() -> String { + return "\(start),\(end)" + } + + /// Returns the Operator + public func getOperator() -> Operator { + return self.`operator` + } +} + +//public protocol Persistable: Codable { +// // Related types +// associatedtype Id: Identifier +// +// // Create +// static func create(model: Self, respondWith: @escaping (Self?, RequestError?) -> Void) +// // Read +// static func read(id: Id, respondWith: @escaping (Self?, RequestError?) -> Void) +// // Read all +// static func read(respondWith: @escaping ([Self]?, RequestError?) -> Void) +// // Update +// static func update(id: Id, model: Self, respondWith: @escaping (Self?, RequestError?) -> Void) +// // How about returning Identifer instances for the delete operations? +// // Delete +// static func delete(id: Id, respondWith: @escaping (RequestError?) -> Void) +// // Delete all +// static func delete(respondWith: @escaping (RequestError?) -> Void) +//} +// +//// Provides utility methods for getting the type and routes for the class +//// conforming to Persistable +//public extension Persistable { +// // Set up name space based on name of model (e.g. User -> user(s)) +// static var type: String { +// let kind = String(describing: Swift.type(of: self)) +// return String(kind.characters.dropLast(5)) +// } +// static var typeLowerCased: String { return "\(type.lowercased())" } +// static var route: String { return "/\(typeLowerCased)s" } +//} diff --git a/Pods/Local Podspecs/Alamofire.podspec.json b/Pods/Local Podspecs/Alamofire.podspec.json new file mode 100644 index 0000000..0eb1d34 --- /dev/null +++ b/Pods/Local Podspecs/Alamofire.podspec.json @@ -0,0 +1,34 @@ +{ + "name": "Alamofire", + "version": "5.9.1", + "license": "MIT", + "summary": "Elegant HTTP Networking in Swift", + "homepage": "https://github.com/Alamofire/Alamofire", + "authors": { + "Alamofire Software Foundation": "info@alamofire.org" + }, + "source": { + "git": "https://github.com/Alamofire/Alamofire.git", + "tag": "5.9.1" + }, + "documentation_url": "https://alamofire.github.io/Alamofire/", + "cocoapods_version": ">= 1.13.0", + "platforms": { + "ios": "10.0", + "osx": "10.12", + "tvos": "10.0", + "visionos": "1.0", + "watchos": "3.0" + }, + "swift_versions": [ + "5" + ], + "source_files": "Source/**/*.swift", + "frameworks": "CFNetwork", + "resource_bundles": { + "Alamofire": [ + "Source/PrivacyInfo.xcprivacy" + ] + }, + "swift_version": "5" +} diff --git a/Pods/Local Podspecs/SwiftyBeaver.podspec.json b/Pods/Local Podspecs/SwiftyBeaver.podspec.json new file mode 100644 index 0000000..f51eafb --- /dev/null +++ b/Pods/Local Podspecs/SwiftyBeaver.podspec.json @@ -0,0 +1,36 @@ +{ + "name": "SwiftyBeaver", + "version": "2.1.1", + "summary": "Convenient logging during development & release in Swift 4 & 5.", + "description": "Easy-to-use, extensible & powerful logging & analytics for Swift 4 and Swift 5.\nGreat for development & release due to its support for many logging destinations & platforms.", + "homepage": "https://github.com/SwiftyBeaver/SwiftyBeaver", + "screenshots": [ + "https://cloud.githubusercontent.com/assets/564725/11452558/17fd5f04-95ec-11e5-96d2-427f62ed4f05.jpg", + "https://cloud.githubusercontent.com/assets/564725/11452560/33225d16-95ec-11e5-8461-78f50b9e8da7.jpg" + ], + "license": "MIT", + "authors": { + "Sebastian Kreutzberger": "s.kreutzberger@googlemail.com" + }, + "platforms": { + "ios": "13.4", + "watchos": "6.2", + "tvos": "13.4", + "osx": "10.15.4" + }, + "source": { + "git": "https://github.com/SwiftyBeaver/SwiftyBeaver.git", + "tag": "2.1.1" + }, + "source_files": "Sources", + "swift_versions": [ + "4.0", + "4.2", + "5.0", + "5.1" + ], + "resource_bundles": { + "SwiftyBeaver": "PrivacyInfo.xcprivacy" + }, + "swift_version": "5.1" +} diff --git a/Pods/LoggerAPI/LICENSE.txt b/Pods/LoggerAPI/LICENSE.txt new file mode 100644 index 0000000..f433b1a --- /dev/null +++ b/Pods/LoggerAPI/LICENSE.txt @@ -0,0 +1,177 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/Pods/LoggerAPI/README.md b/Pods/LoggerAPI/README.md new file mode 100644 index 0000000..8ef5e0e --- /dev/null +++ b/Pods/LoggerAPI/README.md @@ -0,0 +1,94 @@ +

+ + Kitura + +

+ + +

+ + APIDoc + + + Build Status - Master + + macOS + Linux + Apache 2 + + Slack Status + +

+ +# LoggerAPI + +A logger protocol that provides a common logging interface for different kinds of loggers. In addition, a class with a set of static functions for logging within your code is provided. + +[Kitura](https://github.com/Kitura/Kitura) uses this API throughout its implementation when logging. + +## Usage + +#### Add dependencies + +Add the `LoggerAPI` package to the dependencies within your application’s `Package.swift` file. Substitute `"x.x.x"` with the latest `LoggerAPI` [release](https://github.com/Kitura/LoggerAPI/releases): + +```swift +.package(url: "https://github.com/Kitura/LoggerAPI.git", from: "x.x.x") +``` +Add `LoggerAPI` to your target's dependencies: +```swift +.target(name: "example", dependencies: ["LoggerAPI"]), +``` + +#### Import package + +```swift +import LoggerAPI +```` + +#### Log messages + +Add log messages to your application: +```swift +Log.warning("This is a warning.") +Log.error("This is an error.") +``` + +#### Define a logger + +You need to define a `logger` in order to output these messages. You may wish to write your own `Logger` implementation, or you can use `HeliumLogger` that writes to standard output: +```swift +import LoggerAPI +import HeliumLogger + +let myLogger = HeliumLogger(.info) +Log.logger = myLogger +``` +You can find out more about HeliumLogger [here](https://github.com/Kitura/HeliumLogger/blob/master/README.md). + +#### Logging messages to swift-log + +You can direct your log messages to be logged via [swift-log](https://github.com/apple/swift-log) by setting the `swiftLogger` property: +```swift +import LoggerAPI +import Logging + +let myLogger = Logging.Logger(label: "myLogger") +myLogger.logLevel = .notice +Log.swiftLogger = myLogger +``` +If both `logger` and `swiftLogger` are set, then log messages will be sent to both logging backends. The log level configured for a LoggerAPI Logger and a swift-log Logger are independent, so may be used to log at different levels. + +Note that because the hierarchy of log levels defined by LoggerAPI and swift-log is slightly different, a mapping is defined between the levels. See the documentation for `Log.isLogging()` for details. + +## API documentation + +For more information visit our [API reference](http://ibm-swift.github.io/LoggerAPI/). + +## Community + +We love to talk server-side Swift, and Kitura. Join our [Slack](http://swift-at-ibm-slack.mybluemix.net/) to meet the team! + +## License + +This library is licensed under Apache 2.0. Full license text is available in [LICENSE](https://github.com/Kitura/LoggerAPI/blob/master/LICENSE.txt). diff --git a/Pods/LoggerAPI/Sources/LoggerAPI/Logger.swift b/Pods/LoggerAPI/Sources/LoggerAPI/Logger.swift new file mode 100644 index 0000000..94bb738 --- /dev/null +++ b/Pods/LoggerAPI/Sources/LoggerAPI/Logger.swift @@ -0,0 +1,347 @@ +/** + * Copyright IBM Corporation 2016 - 2019 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +import Logging +import Foundation + +/// Implement the `CustomStringConvertible` protocol for the `LoggerMessageType` enum +extension LoggerMessageType: CustomStringConvertible { + /// Convert a `LoggerMessageType` into a printable format. + public var description: String { + switch self { + case .entry: + return "ENTRY" + case .exit: + return "EXIT" + case .debug: + return "DEBUG" + case .verbose: + return "VERBOSE" + case .info: + return "INFO" + case .warning: + return "WARNING" + case .error: + return "ERROR" + } + } +} + +/// A logger protocol implemented by Logger implementations. This API is used by Kitura +/// throughout its implementation when logging. +public protocol Logger { + + /// Output a logged message. + /// + /// - Parameter type: The type of the message (`LoggerMessageType`) being logged. + /// - Parameter msg: The message to be logged. + /// - Parameter functionName: The name of the function invoking the logger API. + /// - Parameter lineNum: The line in the source code of the function invoking the + /// logger API. + /// - Parameter fileName: The file containing the source code of the function invoking the + /// logger API. + func log(_ type: LoggerMessageType, msg: String, + functionName: String, lineNum: Int, fileName: String) + + /// Indicates if a message with a specified type (`LoggerMessageType`) will be in the logger + /// output (i.e. will not be filtered out). + /// + /// - Parameter type: The type of message (`LoggerMessageType`). + /// + /// - Returns: A Boolean indicating whether a message of the specified type + /// (`LoggerMessageType`) will be in the logger output. + func isLogging(_ level: LoggerMessageType) -> Bool + +} + +extension NSLock { + func withLock(_ body: () throws -> T) rethrows -> T { + self.lock() + defer { + self.unlock() + } + return try body() + } +} + +/// A class of static members used by anyone who wants to log messages. +public class Log { + + private static var _logger: Logger? + private static var _loggerLock: NSLock = NSLock() + + /// An instance of the logger. It should usually be the one and only reference + /// of the `Logger` protocol implementation in the system. + /// This can be used in addition to `swiftLogger`, in which case log messages + /// will be sent to both loggers. + public static var logger: Logger? { + get { + return self._loggerLock.withLock { self._logger } + } + set { + self._loggerLock.withLock { self._logger = newValue } + } + } + + private static var _swiftLogger: Logging.Logger? + private static var _swiftLoggerLock: NSLock = NSLock() + + /// An instance of a swift-log Logger. If set, LoggerAPI will direct log messages + /// to swift-log. This can be used in addition to `logger`, in which case log + /// messages will be sent to both loggers. + public static var swiftLogger: Logging.Logger? { + get { + return self._swiftLoggerLock.withLock { self._swiftLogger } + } + set { + self._swiftLoggerLock.withLock { self._swiftLogger = newValue } + } + } + + /// Log a message for use when in verbose logging mode. + /// + /// - Parameter msg: The message to be logged. + /// - Parameter functionName: The name of the function invoking the logger API. + /// Defaults to the name of the function invoking + /// this function. + /// - Parameter lineNum: The line in the source code of the function invoking the + /// logger API. Defaults to the line of the + /// function invoking this function. + /// - Parameter fileName: The file containing the source code of the function invoking the + /// logger API. Defaults to the name of the file containing the function + /// which invokes this function. + public static func verbose(_ msg: @autoclosure () -> String, functionName: String = #function, + lineNum: Int = #line, fileName: String = #file ) { + if let logger = logger, logger.isLogging(.verbose) { + logger.log( .verbose, msg: msg(), + functionName: functionName, lineNum: lineNum, fileName: fileName) + } + swiftLogger?.info("\(msg())") + } + + /// Log an informational message. + /// + /// - Parameter msg: The message to be logged. + /// - Parameter functionName: The name of the function invoking the logger API. + /// Defaults to the name of the function invoking + /// this function. + /// - Parameter lineNum: The line in the source code of the function invoking the + /// logger API. Defaults to the line of the + /// function invoking this function. + /// - Parameter fileName: The file containing the source code of the function invoking the + /// logger API. Defaults to the name of the file containing the function + /// which invokes this function. + public class func info(_ msg: @autoclosure () -> String, functionName: String = #function, + lineNum: Int = #line, fileName: String = #file) { + if let logger = logger, logger.isLogging(.info) { + logger.log( .info, msg: msg(), + functionName: functionName, lineNum: lineNum, fileName: fileName) + } + swiftLogger?.notice("\(msg())") + } + + /// Log a warning message. + /// + /// - Parameter msg: The message to be logged. + /// - Parameter functionName: The name of the function invoking the logger API. + /// Defaults to the name of the function invoking + /// this function. + /// - Parameter lineNum: The line in the source code of the function invoking the + /// logger API. Defaults to the line of the + /// function invoking this function. + /// - Parameter fileName: The file containing the source code of the function invoking the + /// logger API. Defaults to the name of the file containing the function + /// which invokes this function. + public class func warning(_ msg: @autoclosure () -> String, functionName: String = #function, + lineNum: Int = #line, fileName: String = #file) { + if let logger = logger, logger.isLogging(.warning) { + logger.log( .warning, msg: msg(), + functionName: functionName, lineNum: lineNum, fileName: fileName) + } + swiftLogger?.warning("\(msg())") + } + + /// Log an error message. + /// + /// - Parameter msg: The message to be logged. + /// - Parameter functionName: The name of the function invoking the logger API. + /// Defaults to the name of the function invoking + /// this function. + /// - Parameter lineNum: The line in the source code of the function invoking the + /// logger API. Defaults to the line of the + /// function invoking this function. + /// - Parameter fileName: The file containing the source code of the function invoking the + /// logger API. Defaults to the name of the file containing the function + /// which invokes this function. + public class func error(_ msg: @autoclosure () -> String, functionName: String = #function, + lineNum: Int = #line, fileName: String = #file) { + if let logger = logger, logger.isLogging(.error) { + logger.log( .error, msg: msg(), + functionName: functionName, lineNum: lineNum, fileName: fileName) + } + swiftLogger?.error("\(msg())") + } + + /// Log a debugging message. + /// + /// - Parameter msg: The message to be logged. + /// - Parameter functionName: The name of the function invoking the logger API. + /// Defaults to the name of the function invoking + /// this function. + /// - Parameter lineNum: The line in the source code of the function invoking the + /// logger API. Defaults to the line of the + /// function invoking this function. + /// - Parameter fileName: The file containing the source code of the function invoking the + /// logger API. Defaults to the name of the file containing the function + /// which invokes this function. + public class func debug(_ msg: @autoclosure () -> String, functionName: String = #function, + lineNum: Int = #line, fileName: String = #file) { + if let logger = logger, logger.isLogging(.debug) { + logger.log( .debug, msg: msg(), + functionName: functionName, lineNum: lineNum, fileName: fileName) + } + swiftLogger?.debug("\(msg())") + } + + /// Log a message when entering a function. + /// + /// - Parameter msg: The message to be logged. + /// - Parameter functionName: The name of the function invoking the logger API. + /// Defaults to the name of the function invoking + /// this function. + /// - Parameter lineNum: The line in the source code of the function invoking the + /// logger API. Defaults to the line of the + /// function invoking this function. + /// - Parameter fileName: The file containing the source code of the function invoking the + /// logger API. Defaults to the name of the file containing the function + /// which invokes this function. + public class func entry(_ msg: @autoclosure () -> String, functionName: String = #function, + lineNum: Int = #line, fileName: String = #file) { + if let logger = logger, logger.isLogging(.entry) { + logger.log(.entry, msg: msg(), + functionName: functionName, lineNum: lineNum, fileName: fileName) + } + swiftLogger?.trace("\(msg())") + } + + /// Log a message when exiting a function. + /// + /// - Parameter msg: The message to be logged. + /// - Parameter functionName: The name of the function invoking the logger API. + /// Defaults to the name of the function invoking + /// this function. + /// - Parameter lineNum: The line in the source code of the function invoking the + /// logger API. Defaults to the line of the + /// function invoking this function. + /// - Parameter fileName: The file containing the source code of the function invoking the + /// logger API. Defaults to the name of the file containing the function + /// which invokes this function. + public class func exit(_ msg: @autoclosure () -> String, functionName: String = #function, + lineNum: Int = #line, fileName: String = #file) { + if let logger = logger, logger.isLogging(.exit) { + logger.log(.exit, msg: msg(), + functionName: functionName, lineNum: lineNum, fileName: fileName) + } + swiftLogger?.trace("\(msg())") + } + + /// Indicates if a message with a specified type (`LoggerMessageType`) will be logged + /// by some configured logger (i.e. will not be filtered out). This could be a Logger + /// conforming to LoggerAPI, swift-log or both. + /// + /// Note that due to differences in the log levels defined by LoggerAPI and swift-log, + /// their equivalence is mapped as follows: + /// ``` + /// LoggerAPI: swift-log: + /// .error -> .error + /// .warning -> .warning + /// .info -> .notice + /// .verbose -> .info + /// .debug -> .debug + /// .entry -> .trace + /// .exit -> .trace + /// ``` + /// + /// For example, a swift-log Logger configured to log at the `.notice` level will log + /// messages from LoggerAPI at a level of `.info` or higher. + /// + /// - Parameter level: The type of message (`LoggerMessageType`). + /// + /// - Returns: A Boolean indicating whether a message of the specified type + /// (`LoggerMessageType`) will be logged. + public class func isLogging(_ level: LoggerMessageType) -> Bool { + return isLoggingToLoggerAPI(level) || isLoggingToSwiftLog(level) + } + + /// Indicates whether a LoggerAPI Logger is configured to log at the specified level. + /// + /// - Parameter level: The type of message (`LoggerMessageType`). + /// + /// - Returns: A Boolean indicating whether a message of the specified type + /// will be logged via the registered `LoggerAPI.Logger`. + private class func isLoggingToLoggerAPI(_ level: LoggerMessageType) -> Bool { + guard let logger = logger else { + return false + } + return logger.isLogging(level) + } + + /// Indicates whether a swift-log Logger is configured to log at the specified level. + /// + /// - Parameter level: The type of message (`LoggerMessageType`). + /// + /// - Returns: A Boolean indicating whether a message of the specified type + /// will be logged via the registered `Logging.Logger`. + private class func isLoggingToSwiftLog(_ level: LoggerMessageType) -> Bool { + guard let logger = swiftLogger else { + return false + } + switch level { + case .error: + return logger.logLevel <= .error + case .warning: + return logger.logLevel <= .warning + case .info: + return logger.logLevel <= .notice + case .verbose: + return logger.logLevel <= .info + case .debug: + return logger.logLevel <= .debug + case .entry, .exit: + return logger.logLevel <= .trace + } + } +} + +/// The type of a particular log message. It is passed with the message to be logged to the +/// actual logger implementation. It is also used to enable filtering of the log based +/// on the minimal type to log. +public enum LoggerMessageType: Int { + /// Log message type for logging when entering into a function. + case entry = 1 + /// Log message type for logging when exiting from a function. + case exit = 2 + /// Log message type for logging a debugging message. + case debug = 3 + /// Log message type for logging messages in verbose mode. + case verbose = 4 + /// Log message type for logging an informational message. + case info = 5 + /// Log message type for logging a warning message. + case warning = 6 + /// Log message type for logging an error message. + case error = 7 +} diff --git a/Pods/Logging/LICENSE.txt b/Pods/Logging/LICENSE.txt new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/Pods/Logging/LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/Pods/Logging/README.md b/Pods/Logging/README.md new file mode 100644 index 0000000..e6db511 --- /dev/null +++ b/Pods/Logging/README.md @@ -0,0 +1,276 @@ +# SwiftLog + +A Logging API package for Swift. Version `1.0.0` requires Swift 5.0 but there is a version `0.x.y` series available for Swift 4 to ease your transition towards Swift 5. If you intend to use or support SwiftLog for Swift 4, please check the [paragraph](#help-i-need-swift-4) at the end of the document. + +First things first: This is the beginning of a community-driven open-source project actively seeking contributions, be it code, documentation, or ideas. Apart from contributing to `SwiftLog` itself, there's another huge gap at the moment: `SwiftLog` is an _API package_ which tries to establish a common API the ecosystem can use. To make logging really work for real-world workloads, we need `SwiftLog`-compatible _logging backends_ which then either persist the log messages in files, render them in nicer colors on the terminal, or send them over to Splunk or ELK. + +What `SwiftLog` provides today can be found in the [API docs][api-docs]. + +## Getting started + +If you have a server-side Swift application, or maybe a cross-platform (for example Linux & macOS) app/library, and you would like to log, we think targeting this logging API package is a great idea. Below you'll find all you need to know to get started. + +#### Adding the dependency + +`SwiftLog` is designed for Swift 5, the `1.0.0` release requires Swift 5 (however we will soon tag a `0.x` version that will work with Swift 4 for the transition period). To depend on the logging API package, you need to declare your dependency in your `Package.swift`: + +```swift +.package(url: "https://github.com/apple/swift-log.git", from: "1.0.0"), +``` + +and to your application/library target, add `"Logging"` to your `dependencies`, e.g. like this: + +```swift +.target(name: "BestExampleApp", dependencies: ["Logging"]), +``` + +#### Let's log + +```swift +// 1) let's import the logging API package +import Logging + +// 2) we need to create a logger, the label works similarly to a DispatchQueue label +let logger = Logger(label: "com.example.BestExampleApp.main") + +// 3) we're now ready to use it +logger.info("Hello World!") +``` + +#### Output + +``` +2019-03-13T15:46:38+0000 info: Hello World! +``` + +#### Default `Logger` behavior + +`SwiftLog` provides for very basic console logging out-of-the-box by way of `StreamLogHandler`. It is possible to switch the default output to `stderr` like so: +```swift +LoggingSystem.bootstrap(StreamLogHandler.standardError) +``` + +`StreamLogHandler` is primarily a convenience only and does not provide any substantial customization. Library maintainers who aim to build their own logging backends for integration and consumption should implement the `LogHandler` protocol directly as laid out in [the "On the implementation of a logging backend" section](#on-the-implementation-of-a-logging-backend-a-loghandler). + +For further information, please check the [API documentation][api-docs]. + + +#### Selecting a logging backend implementation (applications only) + +As the API has just launched, not many implementations exist yet. If you are interested in implementing one see the "Implementation considerations" section below explaining how to do so. List of existing SwiftLog API compatible libraries: + +| Repository | Handler Description| +| ----------- | ----------- | +| [IBM-Swift/HeliumLogger](https://github.com/IBM-Swift/HeliumLogger) |a logging backend widely used in the Kitura ecosystem | +| [ianpartridge/swift-log-**syslog**](https://github.com/ianpartridge/swift-log-syslog) | a [syslog](https://en.wikipedia.org/wiki/Syslog) backend| +| [Adorkable/swift-log-**format-and-pipe**](https://github.com/Adorkable/swift-log-format-and-pipe) | a backend that allows customization of the output format and the resulting destination | +| [chrisaljoudi/swift-log-**oslog**](https://github.com/chrisaljoudi/swift-log-oslog) | an OSLog [Unified Logging](https://developer.apple.com/documentation/os/logging) backend for use on Apple platforms. **Important Note:** we recommend using os_log directly as decribed [here](https://developer.apple.com/documentation/os/logging). Using os_log through swift-log using this backend will be less efficient and will also prevent specifying the privacy of the message. The backend always uses `%{public}@` as the format string and eagerly converts all string interpolations to strings. This has two drawbacks: 1. the static components of the string interpolation would be eagerly copied by the unified logging system, which will result in loss of performance. 2. It makes all messages public, which changes the default privacy policy of os_log, and doesn't allow specifying fine-grained privacy of sections of the message. In a separate on-going work, Swift APIs for os_log are being improved and made to align closely with swift-log APIs. References: [Unifying Logging Levels](https://forums.swift.org/t/custom-string-interpolation-and-compile-time-interpretation-applied-to-logging/18799), [Making os_log accept string interpolations using compile-time interpretation](https://forums.swift.org/t/logging-levels-for-swifts-server-side-logging-apis-and-new-os-log-apis/20365). | +| [Brainfinance/StackdriverLogging](https://github.com/Brainfinance/StackdriverLogging) | a structured JSON logging backend for use on Google Cloud Platform with the [Stackdriver logging agent](https://cloud.google.com/logging/docs/agent) | +| [vapor/console-kit](https://github.com/vapor/console-kit/) | print log messages to a terminal with stylized ([ANSI](https://en.wikipedia.org/wiki/ANSI_escape_code)) output | +| [neallester/swift-log-testing](https://github.com/neallester/swift-log-testing) | provides access to log messages for use in assertions (within test targets) | +| [wlisac/swift-log-slack](https://github.com/wlisac/swift-log-slack) | a logging backend that sends critical log messages to Slack | +| [NSHipster/swift-log-github-actions](https://github.com/NSHipster/swift-log-github-actions) | a logging backend that translates logging messages into [workflow commands for GitHub Actions](https://help.github.com/en/actions/reference/workflow-commands-for-github-actions). | +| [stevapple/swift-log-telegram](https://github.com/stevapple/swift-log-telegram) | a logging backend that sends log messages to any Telegram chat (Inspired by and forked from [wlisac/swift-log-slack](https://github.com/wlisac/swift-log-slack)) | +| [jagreenwood/swift-log-datadog](https://github.com/jagreenwood/swift-log-datadog) | a logging backend which sends log messages to the [Datadog](https://www.datadoghq.com/log-management/) log management service | +| Your library? | [Get in touch!](https://forums.swift.org/c/server) | + +## What is an API package? + +Glad you asked. We believe that for the Swift on Server ecosystem, it's crucial to have a logging API that can be adopted by anybody so a multitude of libraries from different parties can all log to a shared destination. More concretely this means that we believe all the log messages from all libraries end up in the same file, database, Elastic Stack/Splunk instance, or whatever you may choose. + +In the real-world however, there are so many opinions over how exactly a logging system should behave, what a log message should be formatted like, and where/how it should be persisted. We think it's not feasible to wait for one logging package to support everything that a specific deployment needs whilst still being easy enough to use and remain performant. That's why we decided to cut the problem in half: + +1. a logging API +2. a logging backend implementation + +This package only provides the logging API itself and therefore `SwiftLog` is a 'logging API package'. `SwiftLog` (using `LoggingSystem.bootstrap`) can be configured to choose any compatible logging backend implementation. This way packages can adopt the API and the _application_ can choose any compatible logging backend implementation without requiring any changes from any of the libraries. + +Just for completeness sake: This API package does actually include an overly simplistic and non-configurable logging backend implementation which simply writes all log messages to `stdout`. The reason to include this overly simplistic logging backend implementation is to improve the first-time usage experience. Let's assume you start a project and try out `SwiftLog` for the first time, it's just a whole lot better to see something you logged appear on `stdout` in a simplistic format rather than nothing happening at all. For any real-world application, we advise configuring another logging backend implementation that logs in the style you like. + +## The core concepts + +### Loggers + +`Logger`s are used to emit log messages and therefore the most important type in `SwiftLog`, so their use should be as simple as possible. Most commonly, they are used to emit log messages in a certain log level. For example: + +```swift +// logging an informational message +logger.info("Hello World!") + +// ouch, something went wrong +logger.error("Houston, we have a problem: \(problem)") +``` + +### Log levels + +The following log levels are supported: + + - `trace` + - `debug` + - `info` + - `notice` + - `warning` + - `error` + - `critical` + +The log level of a given logger can be changed, but the change will only affect the specific logger you changed it on. You could say the `Logger` is a _value type_ regarding the log level. + + +### Logging metadata + +Logging metadata is metadata that can be attached to loggers to add information that is crucial when debugging a problem. In servers, the usual example is attaching a request UUID to a logger that will then be present on all log messages logged with that logger. Example: + +```swift +var logger = logger +logger[metadataKey: "request-uuid"] = "\(UUID())" +logger.info("hello world") +``` + +will print + +``` +2019-03-13T18:30:02+0000 info: request-uuid=F8633013-3DD8-481C-9256-B296E43443ED hello world +``` + +with the default logging backend implementation that ships with `SwiftLog`. Needless to say, the format is fully defined by the logging backend you choose. + +## On the implementation of a logging backend (a `LogHandler`) + +Note: If you don't want to implement a custom logging backend, everything in this section is probably not very relevant, so please feel free to skip. + +To become a compatible logging backend that all `SwiftLog` consumers can use, you need to do two things: 1) Implement a type (usually a `struct`) that implements `LogHandler`, a protocol provided by `SwiftLog` and 2) instruct `SwiftLog` to use your logging backend implementation. + +A `LogHandler` or logging backend implementation is anything that conforms to the following protocol + +```swift +public protocol LogHandler { + func log(level: Logger.Level, message: Logger.Message, metadata: Logger.Metadata?, file: String, function: String, line: UInt) + + subscript(metadataKey _: String) -> Logger.Metadata.Value? { get set } + + var metadata: Logger.Metadata { get set } + + var logLevel: Logger.Level { get set } +} +``` + +Instructing `SwiftLog` to use your logging backend as the one the whole application (including all libraries) should use is very simple: + + LoggingSystem.bootstrap(MyLogHandler.init) + +### Implementation considerations + +`LogHandler`s control most parts of the logging system: + +#### Under control of a `LogHandler` + +##### Configuration + +`LogHandler`s control the two crucial pieces of `Logger` configuration, namely: + +- log level (`logger.logLevel` property) +- logging metadata (`logger[metadataKey:]` and `logger.metadata`) + +For the system to work, however, it is important that `LogHandler` treat the configuration as _value types_. This means that `LogHandler`s should be `struct`s and a change in log level or logging metadata should only affect the very `LogHandler` it was changed on. + +However, in special cases, it is acceptable that a `LogHandler` provides some global log level override that may affect all `LogHandler`s created. + +##### Emitting +- emitting the log message itself + +### Not under control of `LogHandler`s + +`LogHandler`s do not control if a message should be logged or not. `Logger` will only invoke the `log` function of a `LogHandler` if `Logger` determines that a log message should be emitted given the configured log level. + +## Source vs Label + +A `Logger` carries an (immutable) `label` and each log message carries a `source` parameter (since SwiftLog 1.3.0). The `Logger`'s label +identifies the creator of the `Logger`. If you are using structured logging by preserving metadata across multiple modules, the `Logger`'s +`label` is not a good way to identify where a log message originated from as it identifies the creator of a `Logger` which is often passed +around between libraries to preserve metadata and the like. + +If you want to filter all log messages originating from a certain subsystem, filter by `source` which defaults to the module that is emitting the +log message. + +## SwiftLog for Swift 4 + + +First of, SwiftLog 1.0.x and SwiftLog 0.0.x are mostly compatible so don't be afraid. In fact, SwiftLog 0.0.0 is the same source code as SwiftLog 1.0.0 with a few changes made to make it Swift 4 compatible. + +### How can I use SwiftLog 0 library or application? + +If you have a application or a library that needs to be compatible with both Swift 4 and 5, then we recommend using the following in your `Package.swift`: + +```swift +.package(url: "https://github.com/apple/swift-log.git", Version("0.0.0") ..< Version("2.0.0")), +``` + +This will instruct SwiftPM to allow any SwiftLog 0 and any SwiftLog 1 version. This is an unusual form because usually packages don't support multiple major versions of a package. Because SwiftLog 0 and 1 are mostly compatible however, this should not be a real issue and will enable everybody to get the best. If compiled with a Swift 4 compiler, this will be a SwiftLog 0 version but if compiled with a Swift 5 compiler everybody will get the best experience and performance delivered by SwiftLog 1. + +In most cases, there is only one thing you need to remember: Always use _string literals_ and _string interpolations_ in the log methods and don't rely on the fact that SwiftLog 0 also allows `String`. + +Good: + +```swift +logger.info("hello world") +``` + +Bad: + +```swift +let message = "hello world" +logger.info(message) +``` + +If you have a `String` that you received from elsewhere, please use + +```swift +logger.info("\(stringIAlreadyHave)") +``` + +For more details, have a look in the next section. + + +### What are the differences between SwiftLog 1 and 0? + +- SwiftLog 0 does not use `@inlinable`. +- Apart from accepting `Logger.Message` for the message, SwiftLog 0 has a `String` overload. +- In SwiftLog 0, `Logger.Message` is not `ExpressibleByStringLiteral` or `ExpressibleByStringInterpolation`. +- In SwiftLog 0, `Logger.MetadataValue` is not `ExpressibleByStringLiteral` or `ExpressibleByStringInterpolation`. + +#### Why these differences? + +##### @inlinable + +Swift 4.0 & 4.1 don't support `@inlinable`, so SwiftLog 0 can't use them. + +##### Logger.Message +Because all Swift 4 versions don't have a (non-deprecated) mechanism for a type to be `ExpressibleByStringInterpolation` we couldn't make `Logger.Message` expressible by string literals. Unfortunately, the most basic form of our logging API is `logger.info("Hello \(world)")`. For this to work however, `"Hello \(world)"` needs to be accepted and because we can't make `Logger.Message` `ExpressibleByStringInterpolation` we added an overload for all the logging methods to also accept `String`. In most cases, you won't even notice that with SwiftLog 0 you're creating a `String` (which is then transformed to a `Logger.Message`) and with SwiftLog 1 you're creating a `Logger.Message` directly. That is because both `String` and `Logger.Message` will accept all forms of string literals and string interpolations. +Unfortunately, there is code that will make this seemingly small difference visible. If you write + +```swift +let message = "Hello world" +logger.info(message) +``` + +then this will only work in SwiftLog 0 and not in SwiftLog 1. Why? Because SwiftLog 1 will want a `Logger.Message` but `let message = "Hello world"` will make `message` to be of type `String` and in SwiftLog 1, the logging methods don't accept `String`s. + +So if you intend to be compatible with SwiftLog 0 and 1 at the same time, please make sure to always use a _string literal_ or a _string interpolation_ inside of the logging methods. + +In the case that you already have a `String` handy that you want to log, don't worry at all, just use + +```swift +let message = "Hello world" +logger.info("\(message)") +``` + +and again, you will be okay with SwiftLog 0 and 1. + +## Design + +This logging API was designed with the contributors to the Swift on Server community and approved by the [SSWG (Swift Server Work Group)](https://swift.org/server/) to the 'sandbox level' of the SSWG's [incubation process](https://github.com/swift-server/sswg/blob/master/process/incubation.md). + +- [pitch](https://forums.swift.org/t/logging/16027), [discussion](https://forums.swift.org/t/discussion-server-logging-api/18834), [feedback](https://forums.swift.org/t/feedback-server-logging-api-with-revisions/19375) +- [log levels](https://forums.swift.org/t/logging-levels-for-swifts-server-side-logging-apis-and-new-os-log-apis/20365) + +[api-docs]: https://apple.github.io/swift-log/docs/current/Logging/Structs/Logger.html diff --git a/Pods/Logging/Sources/Logging/Locks.swift b/Pods/Logging/Sources/Logging/Locks.swift new file mode 100644 index 0000000..5c4d32d --- /dev/null +++ b/Pods/Logging/Sources/Logging/Locks.swift @@ -0,0 +1,257 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Logging API open source project +// +// Copyright (c) 2018-2019 Apple Inc. and the Swift Logging API project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift Logging API project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +// +// This source file is part of the SwiftNIO open source project +// +// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of SwiftNIO project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) +import Darwin +#elseif os(Windows) +import WinSDK +#else +import Glibc +#endif + +/// A threading lock based on `libpthread` instead of `libdispatch`. +/// +/// This object provides a lock on top of a single `pthread_mutex_t`. This kind +/// of lock is safe to use with `libpthread`-based threading models, such as the +/// one used by NIO. On Windows, the lock is based on the substantially similar +/// `SRWLOCK` type. +internal final class Lock { + #if os(Windows) + fileprivate let mutex: UnsafeMutablePointer = + UnsafeMutablePointer.allocate(capacity: 1) + #else + fileprivate let mutex: UnsafeMutablePointer = + UnsafeMutablePointer.allocate(capacity: 1) + #endif + + /// Create a new lock. + public init() { + #if os(Windows) + InitializeSRWLock(self.mutex) + #else + var attr = pthread_mutexattr_t() + pthread_mutexattr_init(&attr) + pthread_mutexattr_settype(&attr, .init(PTHREAD_MUTEX_ERRORCHECK)) + + let err = pthread_mutex_init(self.mutex, &attr) + precondition(err == 0, "\(#function) failed in pthread_mutex with error \(err)") + #endif + } + + deinit { + #if os(Windows) + // SRWLOCK does not need to be free'd + #else + let err = pthread_mutex_destroy(self.mutex) + precondition(err == 0, "\(#function) failed in pthread_mutex with error \(err)") + #endif + self.mutex.deallocate() + } + + /// Acquire the lock. + /// + /// Whenever possible, consider using `withLock` instead of this method and + /// `unlock`, to simplify lock handling. + public func lock() { + #if os(Windows) + AcquireSRWLockExclusive(self.mutex) + #else + let err = pthread_mutex_lock(self.mutex) + precondition(err == 0, "\(#function) failed in pthread_mutex with error \(err)") + #endif + } + + /// Release the lock. + /// + /// Whenever possible, consider using `withLock` instead of this method and + /// `lock`, to simplify lock handling. + public func unlock() { + #if os(Windows) + ReleaseSRWLockExclusive(self.mutex) + #else + let err = pthread_mutex_unlock(self.mutex) + precondition(err == 0, "\(#function) failed in pthread_mutex with error \(err)") + #endif + } +} + +extension Lock { + /// Acquire the lock for the duration of the given block. + /// + /// This convenience method should be preferred to `lock` and `unlock` in + /// most situations, as it ensures that the lock will be released regardless + /// of how `body` exits. + /// + /// - Parameter body: The block to execute while holding the lock. + /// - Returns: The value returned by the block. + @inlinable + internal func withLock(_ body: () throws -> T) rethrows -> T { + self.lock() + defer { + self.unlock() + } + return try body() + } + + // specialise Void return (for performance) + @inlinable + internal func withLockVoid(_ body: () throws -> Void) rethrows { + try self.withLock(body) + } +} + +/// A reader/writer threading lock based on `libpthread` instead of `libdispatch`. +/// +/// This object provides a lock on top of a single `pthread_rwlock_t`. This kind +/// of lock is safe to use with `libpthread`-based threading models, such as the +/// one used by NIO. On Windows, the lock is based on the substantially similar +/// `SRWLOCK` type. +internal final class ReadWriteLock { + #if os(Windows) + fileprivate let rwlock: UnsafeMutablePointer = + UnsafeMutablePointer.allocate(capacity: 1) + fileprivate var shared: Bool = true + #else + fileprivate let rwlock: UnsafeMutablePointer = + UnsafeMutablePointer.allocate(capacity: 1) + #endif + + /// Create a new lock. + public init() { + #if os(Windows) + InitializeSRWLock(self.rwlock) + #else + let err = pthread_rwlock_init(self.rwlock, nil) + precondition(err == 0, "\(#function) failed in pthread_rwlock with error \(err)") + #endif + } + + deinit { + #if os(Windows) + // SRWLOCK does not need to be free'd + #else + let err = pthread_rwlock_destroy(self.rwlock) + precondition(err == 0, "\(#function) failed in pthread_rwlock with error \(err)") + #endif + self.rwlock.deallocate() + } + + /// Acquire a reader lock. + /// + /// Whenever possible, consider using `withReaderLock` instead of this + /// method and `unlock`, to simplify lock handling. + public func lockRead() { + #if os(Windows) + AcquireSRWLockShared(self.rwlock) + self.shared = true + #else + let err = pthread_rwlock_rdlock(self.rwlock) + precondition(err == 0, "\(#function) failed in pthread_rwlock with error \(err)") + #endif + } + + /// Acquire a writer lock. + /// + /// Whenever possible, consider using `withWriterLock` instead of this + /// method and `unlock`, to simplify lock handling. + public func lockWrite() { + #if os(Windows) + AcquireSRWLockExclusive(self.rwlock) + self.shared = false + #else + let err = pthread_rwlock_wrlock(self.rwlock) + precondition(err == 0, "\(#function) failed in pthread_rwlock with error \(err)") + #endif + } + + /// Release the lock. + /// + /// Whenever possible, consider using `withReaderLock` and `withWriterLock` + /// instead of this method and `lockRead` and `lockWrite`, to simplify lock + /// handling. + public func unlock() { + #if os(Windows) + if self.shared { + ReleaseSRWLockShared(self.rwlock) + } else { + ReleaseSRWLockExclusive(self.rwlock) + } + #else + let err = pthread_rwlock_unlock(self.rwlock) + precondition(err == 0, "\(#function) failed in pthread_rwlock with error \(err)") + #endif + } +} + +extension ReadWriteLock { + /// Acquire the reader lock for the duration of the given block. + /// + /// This convenience method should be preferred to `lockRead` and `unlock` + /// in most situations, as it ensures that the lock will be released + /// regardless of how `body` exits. + /// + /// - Parameter body: The block to execute while holding the reader lock. + /// - Returns: The value returned by the block. + @inlinable + internal func withReaderLock(_ body: () throws -> T) rethrows -> T { + self.lockRead() + defer { + self.unlock() + } + return try body() + } + + /// Acquire the writer lock for the duration of the given block. + /// + /// This convenience method should be preferred to `lockWrite` and `unlock` + /// in most situations, as it ensures that the lock will be released + /// regardless of how `body` exits. + /// + /// - Parameter body: The block to execute while holding the writer lock. + /// - Returns: The value returned by the block. + @inlinable + internal func withWriterLock(_ body: () throws -> T) rethrows -> T { + self.lockWrite() + defer { + self.unlock() + } + return try body() + } + + // specialise Void return (for performance) + @inlinable + internal func withReaderLockVoid(_ body: () throws -> Void) rethrows { + try self.withReaderLock(body) + } + + // specialise Void return (for performance) + @inlinable + internal func withWriterLockVoid(_ body: () throws -> Void) rethrows { + try self.withWriterLock(body) + } +} diff --git a/Pods/Logging/Sources/Logging/LogHandler.swift b/Pods/Logging/Sources/Logging/LogHandler.swift new file mode 100644 index 0000000..6b52b12 --- /dev/null +++ b/Pods/Logging/Sources/Logging/LogHandler.swift @@ -0,0 +1,188 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Logging API open source project +// +// Copyright (c) 2018-2019 Apple Inc. and the Swift Logging API project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift Logging API project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +/// A `LogHandler` is an implementation of a logging backend. +/// +/// This type is an implementation detail and should not normally be used, unless implementing your own logging backend. +/// To use the SwiftLog API, please refer to the documentation of `Logger`. +/// +/// # Implementation requirements +/// +/// To implement your own `LogHandler` you should respect a few requirements that are necessary so applications work +/// as expected regardless of the selected `LogHandler` implementation. +/// +/// - The `LogHandler` must be a `struct`. +/// - The metadata and `logLevel` properties must be implemented so that setting them on a `Logger` does not affect +/// other `Logger`s. +/// +/// ### Treat log level & metadata as values +/// +/// When developing your `LogHandler`, please make sure the following test works. +/// +/// ```swift +/// LoggingSystem.bootstrap(MyLogHandler.init) // your LogHandler might have a different bootstrapping step +/// var logger1 = Logger(label: "first logger") +/// logger1.logLevel = .debug +/// logger1[metadataKey: "only-on"] = "first" +/// +/// var logger2 = logger1 +/// logger2.logLevel = .error // this must not override `logger1`'s log level +/// logger2[metadataKey: "only-on"] = "second" // this must not override `logger1`'s metadata +/// +/// XCTAssertEqual(.debug, logger1.logLevel) +/// XCTAssertEqual(.error, logger2.logLevel) +/// XCTAssertEqual("first", logger1[metadataKey: "only-on"]) +/// XCTAssertEqual("second", logger2[metadataKey: "only-on"]) +/// ``` +/// +/// ### Special cases +/// +/// In certain special cases, the log level behaving like a value on `Logger` might not be what you want. For example, +/// you might want to set the log level across _all_ `Logger`s to `.debug` when say a signal (eg. `SIGUSR1`) is received +/// to be able to debug special failures in production. This special case is acceptable but we urge you to create a +/// solution specific to your `LogHandler` implementation to achieve that. Please find an example implementation of this +/// behavior below, on reception of the signal you would call +/// `LogHandlerWithGlobalLogLevelOverride.overrideGlobalLogLevel = .debug`, for example. +/// +/// ```swift +/// import class Foundation.NSLock +/// +/// public struct LogHandlerWithGlobalLogLevelOverride: LogHandler { +/// // the static properties hold the globally overridden log level (if overridden) +/// private static let overrideLock = NSLock() +/// private static var overrideLogLevel: Logger.Level? = nil +/// +/// // this holds the log level if not overridden +/// private var _logLevel: Logger.Level = .info +/// +/// // metadata storage +/// public var metadata: Logger.Metadata = [:] +/// +/// public init(label: String) { +/// // [...] +/// } +/// +/// public var logLevel: Logger.Level { +/// // when we get asked for the log level, we check if it was globally overridden or not +/// get { +/// LogHandlerWithGlobalLogLevelOverride.overrideLock.lock() +/// defer { LogHandlerWithGlobalLogLevelOverride.overrideLock.unlock() } +/// return LogHandlerWithGlobalLogLevelOverride.overrideLogLevel ?? self._logLevel +/// } +/// // we set the log level whenever we're asked (note: this might not have an effect if globally +/// // overridden) +/// set { +/// self._logLevel = newValue +/// } +/// } +/// +/// public func log(level: Logger.Level, message: Logger.Message, metadata: Logger.Metadata?, +/// source: String, file: String, function: String, line: UInt) { +/// // [...] +/// } +/// +/// public subscript(metadataKey metadataKey: String) -> Logger.Metadata.Value? { +/// get { +/// return self.metadata[metadataKey] +/// } +/// set(newValue) { +/// self.metadata[metadataKey] = newValue +/// } +/// } +/// +/// // this is the function to globally override the log level, it is not part of the `LogHandler` protocol +/// public static func overrideGlobalLogLevel(_ logLevel: Logger.Level) { +/// LogHandlerWithGlobalLogLevelOverride.overrideLock.lock() +/// defer { LogHandlerWithGlobalLogLevelOverride.overrideLock.unlock() } +/// LogHandlerWithGlobalLogLevelOverride.overrideLogLevel = logLevel +/// } +/// } +/// ``` +/// +/// Please note that the above `LogHandler` will still pass the 'log level is a value' test above it iff the global log +/// level has not been overridden. And most importantly it passes the requirement listed above: A change to the log +/// level on one `Logger` should not affect the log level of another `Logger` variable. +public protocol LogHandler { + /// This method is called when a `LogHandler` must emit a log message. There is no need for the `LogHandler` to + /// check if the `level` is above or below the configured `logLevel` as `Logger` already performed this check and + /// determined that a message should be logged. + /// + /// - parameters: + /// - level: The log level the message was logged at. + /// - message: The message to log. To obtain a `String` representation call `message.description`. + /// - metadata: The metadata associated to this log message. + /// - source: The source where the log message originated, for example the logging module. + /// - file: The file the log message was emitted from. + /// - function: The function the log line was emitted from. + /// - line: The line the log message was emitted from. + func log(level: Logger.Level, + message: Logger.Message, + metadata: Logger.Metadata?, + source: String, + file: String, + function: String, + line: UInt) + + /// SwiftLog 1.0 compatibility method. Please do _not_ implement, implement + /// `log(level:message:metadata:source:file:function:line:)` instead. + @available(*, deprecated, renamed: "log(level:message:metadata:source:file:function:line:)") + func log(level: Logging.Logger.Level, message: Logging.Logger.Message, metadata: Logging.Logger.Metadata?, file: String, function: String, line: UInt) + + /// Add, remove, or change the logging metadata. + /// + /// - note: `LogHandler`s must treat logging metadata as a value type. This means that the change in metadata must + /// only affect this very `LogHandler`. + /// + /// - parameters: + /// - metadataKey: The key for the metadata item + subscript(metadataKey _: String) -> Logger.Metadata.Value? { get set } + + /// Get or set the entire metadata storage as a dictionary. + /// + /// - note: `LogHandler`s must treat logging metadata as a value type. This means that the change in metadata must + /// only affect this very `LogHandler`. + var metadata: Logger.Metadata { get set } + + /// Get or set the configured log level. + /// + /// - note: `LogHandler`s must treat the log level as a value type. This means that the change in metadata must + /// only affect this very `LogHandler`. It is acceptable to provide some form of global log level override + /// that means a change in log level on a particular `LogHandler` might not be reflected in any + /// `LogHandler`. + var logLevel: Logger.Level { get set } +} + +extension LogHandler { + @available(*, deprecated, message: "You should implement this method instead of using the default implementation") + public func log(level: Logger.Level, + message: Logger.Message, + metadata: Logger.Metadata?, + source: String, + file: String, + function: String, + line: UInt) { + self.log(level: level, message: message, metadata: metadata, file: file, function: function, line: line) + } + + @available(*, deprecated, renamed: "log(level:message:metadata:source:file:function:line:)") + public func log(level: Logging.Logger.Level, message: Logging.Logger.Message, metadata: Logging.Logger.Metadata?, file: String, function: String, line: UInt) { + self.log(level: level, + message: message, + metadata: metadata, + source: Logger.currentModule(filePath: file), + file: file, + function: function, + line: line) + } +} diff --git a/Pods/Logging/Sources/Logging/Logging.swift b/Pods/Logging/Sources/Logging/Logging.swift new file mode 100644 index 0000000..434eb92 --- /dev/null +++ b/Pods/Logging/Sources/Logging/Logging.swift @@ -0,0 +1,826 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Logging API open source project +// +// Copyright (c) 2018-2019 Apple Inc. and the Swift Logging API project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift Logging API project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) +import Darwin +#elseif os(Windows) +import MSVCRT +#else +import Glibc +#endif + +/// A `Logger` is the central type in `SwiftLog`. Its central function is to emit log messages using one of the methods +/// corresponding to a log level. +/// +/// `Logger`s are value types with respect to the `logLevel` and the `metadata` (as well as the immutable `label` +/// and the selected `LogHandler`). Therefore, `Logger`s are suitable to be passed around between libraries if you want +/// to preserve metadata across libraries. +/// +/// The most basic usage of a `Logger` is +/// +/// logger.info("Hello World!") +/// +public struct Logger { + @usableFromInline + var handler: LogHandler + + /// An identifier of the creator of this `Logger`. + public let label: String + + internal init(label: String, _ handler: LogHandler) { + self.label = label + self.handler = handler + } +} + +extension Logger { + /// Log a message passing the log level as a parameter. + /// + /// If the `logLevel` passed to this method is more severe than the `Logger`'s `logLevel`, it will be logged, + /// otherwise nothing will happen. + /// + /// - parameters: + /// - level: The log level to log `message` at. For the available log levels, see `Logger.Level`. + /// - message: The message to be logged. `message` can be used with any string interpolation literal. + /// - metadata: One-off metadata to attach to this log message. + /// - source: The source this log messages originates to. Currently, it defaults to the folder containing the + /// file that is emitting the log message, which usually is the module. + /// - file: The file this log message originates from (there's usually no need to pass it explicitly as it + /// defaults to `#file`). + /// - function: The function this log message originates from (there's usually no need to pass it explicitly as + /// it defaults to `#function`). + /// - line: The line this log message originates from (there's usually no need to pass it explicitly as it + /// defaults to `#line`). + @inlinable + public func log(level: Logger.Level, + _ message: @autoclosure () -> Logger.Message, + metadata: @autoclosure () -> Logger.Metadata? = nil, + source: @autoclosure () -> String? = nil, + file: String = #file, function: String = #function, line: UInt = #line) { + if self.logLevel <= level { + self.handler.log(level: level, + message: message(), + metadata: metadata(), + source: source() ?? Logger.currentModule(filePath: (file)), + file: file, function: function, line: line) + } + } + + /// Add, change, or remove a logging metadata item. + /// + /// - note: Logging metadata behaves as a value that means a change to the logging metadata will only affect the + /// very `Logger` it was changed on. + @inlinable + public subscript(metadataKey metadataKey: String) -> Logger.Metadata.Value? { + get { + return self.handler[metadataKey: metadataKey] + } + set { + self.handler[metadataKey: metadataKey] = newValue + } + } + + /// Get or set the log level configured for this `Logger`. + /// + /// - note: `Logger`s treat `logLevel` as a value. This means that a change in `logLevel` will only affect this + /// very `Logger`. It it acceptable for logging backends to have some form of global log level override + /// that affects multiple or even all loggers. This means a change in `logLevel` to one `Logger` might in + /// certain cases have no effect. + @inlinable + public var logLevel: Logger.Level { + get { + return self.handler.logLevel + } + set { + self.handler.logLevel = newValue + } + } +} + +extension Logger { + /// Log a message passing with the `Logger.Level.trace` log level. + /// + /// If `.trace` is at least as severe as the `Logger`'s `logLevel`, it will be logged, + /// otherwise nothing will happen. + /// + /// - parameters: + /// - message: The message to be logged. `message` can be used with any string interpolation literal. + /// - metadata: One-off metadata to attach to this log message + /// - source: The source this log messages originates to. Currently, it defaults to the folder containing the + /// file that is emitting the log message, which usually is the module. + /// - file: The file this log message originates from (there's usually no need to pass it explicitly as it + /// defaults to `#file`). + /// - function: The function this log message originates from (there's usually no need to pass it explicitly as + /// it defaults to `#function`). + /// - line: The line this log message originates from (there's usually no need to pass it explicitly as it + /// defaults to `#line`). + @inlinable + public func trace(_ message: @autoclosure () -> Logger.Message, + metadata: @autoclosure () -> Logger.Metadata? = nil, + source: @autoclosure () -> String? = nil, + file: String = #file, function: String = #function, line: UInt = #line) { + self.log(level: .trace, message(), metadata: metadata(), source: source(), file: file, function: function, line: line) + } + + /// Log a message passing with the `Logger.Level.debug` log level. + /// + /// If `.debug` is at least as severe as the `Logger`'s `logLevel`, it will be logged, + /// otherwise nothing will happen. + /// + /// - parameters: + /// - message: The message to be logged. `message` can be used with any string interpolation literal. + /// - metadata: One-off metadata to attach to this log message. + /// - source: The source this log messages originates to. Currently, it defaults to the folder containing the + /// file that is emitting the log message, which usually is the module. + /// - file: The file this log message originates from (there's usually no need to pass it explicitly as it + /// defaults to `#file`). + /// - function: The function this log message originates from (there's usually no need to pass it explicitly as + /// it defaults to `#function`). + /// - line: The line this log message originates from (there's usually no need to pass it explicitly as it + /// defaults to `#line`). + @inlinable + public func debug(_ message: @autoclosure () -> Logger.Message, + metadata: @autoclosure () -> Logger.Metadata? = nil, + source: @autoclosure () -> String? = nil, + file: String = #file, function: String = #function, line: UInt = #line) { + self.log(level: .debug, message(), metadata: metadata(), source: source(), file: file, function: function, line: line) + } + + /// Log a message passing with the `Logger.Level.info` log level. + /// + /// If `.info` is at least as severe as the `Logger`'s `logLevel`, it will be logged, + /// otherwise nothing will happen. + /// + /// - parameters: + /// - message: The message to be logged. `message` can be used with any string interpolation literal. + /// - metadata: One-off metadata to attach to this log message. + /// - source: The source this log messages originates to. Currently, it defaults to the folder containing the + /// file that is emitting the log message, which usually is the module. + /// - file: The file this log message originates from (there's usually no need to pass it explicitly as it + /// defaults to `#file`). + /// - function: The function this log message originates from (there's usually no need to pass it explicitly as + /// it defaults to `#function`). + /// - line: The line this log message originates from (there's usually no need to pass it explicitly as it + /// defaults to `#line`). + @inlinable + public func info(_ message: @autoclosure () -> Logger.Message, + metadata: @autoclosure () -> Logger.Metadata? = nil, + source: @autoclosure () -> String? = nil, + file: String = #file, function: String = #function, line: UInt = #line) { + self.log(level: .info, message(), metadata: metadata(), source: source(), file: file, function: function, line: line) + } + + /// Log a message passing with the `Logger.Level.notice` log level. + /// + /// If `.notice` is at least as severe as the `Logger`'s `logLevel`, it will be logged, + /// otherwise nothing will happen. + /// + /// - parameters: + /// - message: The message to be logged. `message` can be used with any string interpolation literal. + /// - metadata: One-off metadata to attach to this log message. + /// - source: The source this log messages originates to. Currently, it defaults to the folder containing the + /// file that is emitting the log message, which usually is the module. + /// - file: The file this log message originates from (there's usually no need to pass it explicitly as it + /// defaults to `#file`). + /// - function: The function this log message originates from (there's usually no need to pass it explicitly as + /// it defaults to `#function`). + /// - line: The line this log message originates from (there's usually no need to pass it explicitly as it + /// defaults to `#line`). + @inlinable + public func notice(_ message: @autoclosure () -> Logger.Message, + metadata: @autoclosure () -> Logger.Metadata? = nil, + source: @autoclosure () -> String? = nil, + file: String = #file, function: String = #function, line: UInt = #line) { + self.log(level: .notice, message(), metadata: metadata(), source: source(), file: file, function: function, line: line) + } + + /// Log a message passing with the `Logger.Level.warning` log level. + /// + /// If `.warning` is at least as severe as the `Logger`'s `logLevel`, it will be logged, + /// otherwise nothing will happen. + /// + /// - parameters: + /// - message: The message to be logged. `message` can be used with any string interpolation literal. + /// - metadata: One-off metadata to attach to this log message. + /// - source: The source this log messages originates to. Currently, it defaults to the folder containing the + /// file that is emitting the log message, which usually is the module. + /// - file: The file this log message originates from (there's usually no need to pass it explicitly as it + /// defaults to `#file`). + /// - function: The function this log message originates from (there's usually no need to pass it explicitly as + /// it defaults to `#function`). + /// - line: The line this log message originates from (there's usually no need to pass it explicitly as it + /// defaults to `#line`). + @inlinable + public func warning(_ message: @autoclosure () -> Logger.Message, + metadata: @autoclosure () -> Logger.Metadata? = nil, + source: @autoclosure () -> String? = nil, + file: String = #file, function: String = #function, line: UInt = #line) { + self.log(level: .warning, message(), metadata: metadata(), source: source(), file: file, function: function, line: line) + } + + /// Log a message passing with the `Logger.Level.error` log level. + /// + /// If `.error` is at least as severe as the `Logger`'s `logLevel`, it will be logged, + /// otherwise nothing will happen. + /// + /// - parameters: + /// - message: The message to be logged. `message` can be used with any string interpolation literal. + /// - metadata: One-off metadata to attach to this log message. + /// - source: The source this log messages originates to. Currently, it defaults to the folder containing the + /// file that is emitting the log message, which usually is the module. + /// - file: The file this log message originates from (there's usually no need to pass it explicitly as it + /// defaults to `#file`). + /// - function: The function this log message originates from (there's usually no need to pass it explicitly as + /// it defaults to `#function`). + /// - line: The line this log message originates from (there's usually no need to pass it explicitly as it + /// defaults to `#line`). + @inlinable + public func error(_ message: @autoclosure () -> Logger.Message, + metadata: @autoclosure () -> Logger.Metadata? = nil, + source: @autoclosure () -> String? = nil, + file: String = #file, function: String = #function, line: UInt = #line) { + self.log(level: .error, message(), metadata: metadata(), source: source(), file: file, function: function, line: line) + } + + /// Log a message passing with the `Logger.Level.critical` log level. + /// + /// `.critical` messages will always be logged. + /// + /// - parameters: + /// - message: The message to be logged. `message` can be used with any string interpolation literal. + /// - metadata: One-off metadata to attach to this log message. + /// - source: The source this log messages originates to. Currently, it defaults to the folder containing the + /// file that is emitting the log message, which usually is the module. + /// - file: The file this log message originates from (there's usually no need to pass it explicitly as it + /// defaults to `#file`). + /// - function: The function this log message originates from (there's usually no need to pass it explicitly as + /// it defaults to `#function`). + /// - line: The line this log message originates from (there's usually no need to pass it explicitly as it + /// defaults to `#line`). + @inlinable + public func critical(_ message: @autoclosure () -> Logger.Message, + metadata: @autoclosure () -> Logger.Metadata? = nil, + source: @autoclosure () -> String? = nil, + file: String = #file, function: String = #function, line: UInt = #line) { + self.log(level: .critical, message(), metadata: metadata(), source: source(), file: file, function: function, line: line) + } +} + +/// The `LoggingSystem` is a global facility where the default logging backend implementation (`LogHandler`) can be +/// configured. `LoggingSystem` is set up just once in a given program to set up the desired logging backend +/// implementation. +public enum LoggingSystem { + fileprivate static let lock = ReadWriteLock() + fileprivate static var factory: (String) -> LogHandler = StreamLogHandler.standardOutput + fileprivate static var initialized = false + + /// `bootstrap` is a one-time configuration function which globally selects the desired logging backend + /// implementation. `bootstrap` can be called at maximum once in any given program, calling it more than once will + /// lead to undefined behavior, most likely a crash. + /// + /// - parameters: + /// - factory: A closure that given a `Logger` identifier, produces an instance of the `LogHandler`. + public static func bootstrap(_ factory: @escaping (String) -> LogHandler) { + self.lock.withWriterLock { + precondition(!self.initialized, "logging system can only be initialized once per process.") + self.factory = factory + self.initialized = true + } + } + + // for our testing we want to allow multiple bootstraping + internal static func bootstrapInternal(_ factory: @escaping (String) -> LogHandler) { + self.lock.withWriterLock { + self.factory = factory + } + } +} + +extension Logger { + /// `Metadata` is a typealias for `[String: Logger.MetadataValue]` the type of the metadata storage. + public typealias Metadata = [String: MetadataValue] + + /// A logging metadata value. `Logger.MetadataValue` is string, array, and dictionary literal convertible. + /// + /// `MetadataValue` provides convenient conformances to `ExpressibleByStringInterpolation`, + /// `ExpressibleByStringLiteral`, `ExpressibleByArrayLiteral`, and `ExpressibleByDictionaryLiteral` which means + /// that when constructing `MetadataValue`s you should default to using Swift's usual literals. + /// + /// Examples: + /// - prefer `logger.info("user logged in", metadata: ["user-id": "\(user.id)"])` over + /// `..., metadata: ["user-id": .string(user.id.description)])` + /// - prefer `logger.info("user selected colors", metadata: ["colors": ["\(user.topColor)", "\(user.secondColor)"]])` + /// over `..., metadata: ["colors": .array([.string("\(user.topColor)"), .string("\(user.secondColor)")])` + /// - prefer `logger.info("nested info", metadata: ["nested": ["fave-numbers": ["\(1)", "\(2)", "\(3)"], "foo": "bar"]])` + /// over `..., metadata: ["nested": .dictionary(["fave-numbers": ...])])` + public enum MetadataValue { + /// A metadata value which is a `String`. + /// + /// Because `MetadataValue` implements `ExpressibleByStringInterpolation`, and `ExpressibleByStringLiteral`, + /// you don't need to type `.string(someType.description)` you can use the string interpolation `"\(someType)"`. + case string(String) + + /// A metadata value which is some `CustomStringConvertible`. + case stringConvertible(CustomStringConvertible) + + /// A metadata value which is a dictionary from `String` to `Logger.MetadataValue`. + /// + /// Because `MetadataValue` implements `ExpressibleByDictionaryLiteral`, you don't need to type + /// `.dictionary(["foo": .string("bar \(buz)")])`, you can just use the more natural `["foo": "bar \(buz)"]`. + case dictionary(Metadata) + + /// A metadata value which is an array of `Logger.MetadataValue`s. + /// + /// Because `MetadataValue` implements `ExpressibleByArrayLiteral`, you don't need to type + /// `.array([.string("foo"), .string("bar \(buz)")])`, you can just use the more natural `["foo", "bar \(buz)"]`. + case array([Metadata.Value]) + } + + /// The log level. + /// + /// Log levels are ordered by their severity, with `.trace` being the least severe and + /// `.critical` being the most severe. + public enum Level: String, Codable, CaseIterable { + /// Appropriate for messages that contain information normally of use only when + /// tracing the execution of a program. + case trace + + /// Appropriate for messages that contain information normally of use only when + /// debugging a program. + case debug + + /// Appropriate for informational messages. + case info + + /// Appropriate for conditions that are not error conditions, but that may require + /// special handling. + case notice + + /// Appropriate for messages that are not error conditions, but more severe than + /// `.notice`. + case warning + + /// Appropriate for error conditions. + case error + + /// Appropriate for critical error conditions that usually require immediate + /// attention. + /// + /// When a `critical` message is logged, the logging backend (`LogHandler`) is free to perform + /// more heavy-weight operations to capture system state (such as capturing stack traces) to facilitate + /// debugging. + case critical + } + + /// Construct a `Logger` given a `label` identifying the creator of the `Logger`. + /// + /// The `label` should identify the creator of the `Logger`. This can be an application, a sub-system, or even + /// a datatype. + /// + /// - parameters: + /// - label: An identifier for the creator of a `Logger`. + public init(label: String) { + self = LoggingSystem.lock.withReaderLock { Logger(label: label, LoggingSystem.factory(label)) } + } + + /// Construct a `Logger` given a `label` identifying the creator of the `Logger` or a non-standard `LogHandler`. + /// + /// The `label` should identify the creator of the `Logger`. This can be an application, a sub-system, or even + /// a datatype. + /// + /// This initializer provides an escape hatch in case the global default logging backend implementation (set up + /// using `LoggingSystem.bootstrap` is not appropriate for this particular logger. + /// + /// - parameters: + /// - label: An identifier for the creator of a `Logger`. + /// - factory: A closure creating non-standard `LogHandler`s. + public init(label: String, factory: (String) -> LogHandler) { + self = Logger(label: label, factory(label)) + } +} + +extension Logger.Level { + internal var naturalIntegralValue: Int { + switch self { + case .trace: + return 0 + case .debug: + return 1 + case .info: + return 2 + case .notice: + return 3 + case .warning: + return 4 + case .error: + return 5 + case .critical: + return 6 + } + } +} + +extension Logger.Level: Comparable { + public static func < (lhs: Logger.Level, rhs: Logger.Level) -> Bool { + return lhs.naturalIntegralValue < rhs.naturalIntegralValue + } +} + +// Extension has to be done on explicit type rather than Logger.Metadata.Value as workaround for +// https://bugs.swift.org/browse/SR-9687 +// Then we could write it as follows and it would work under Swift 5 and not only 4 as it does currently: +// extension Logger.Metadata.Value: Equatable { +extension Logger.MetadataValue: Equatable { + public static func == (lhs: Logger.Metadata.Value, rhs: Logger.Metadata.Value) -> Bool { + switch (lhs, rhs) { + case (.string(let lhs), .string(let rhs)): + return lhs == rhs + case (.stringConvertible(let lhs), .stringConvertible(let rhs)): + return lhs.description == rhs.description + case (.array(let lhs), .array(let rhs)): + return lhs == rhs + case (.dictionary(let lhs), .dictionary(let rhs)): + return lhs == rhs + default: + return false + } + } +} + +extension Logger { + /// `Logger.Message` represents a log message's text. It is usually created using string literals. + /// + /// Example creating a `Logger.Message`: + /// + /// let world: String = "world" + /// let myLogMessage: Logger.Message = "Hello \(world)" + /// + /// Most commonly, `Logger.Message`s appear simply as the parameter to a logging method such as: + /// + /// logger.info("Hello \(world)") + /// + public struct Message: ExpressibleByStringLiteral, Equatable, CustomStringConvertible, ExpressibleByStringInterpolation { + public typealias StringLiteralType = String + + private var value: String + + public init(stringLiteral value: String) { + self.value = value + } + + public var description: String { + return self.value + } + } +} + +/// A pseudo-`LogHandler` that can be used to send messages to multiple other `LogHandler`s. +/// +/// ### Effective Logger.Level +/// +/// When first initialized the multiplex log handlers' log level is automatically set to the minimum of all the +/// passed in log handlers. This ensures that each of the handlers will be able to log at their appropriate level +/// any log events they might be interested in. +/// +/// Example: +/// If log handler `A` is logging at `.debug` level, and log handler `B` is logging at `.info` level, the constructed +/// `MultiplexLogHandler([A, B])`'s effective log level will be set to `.debug`, meaning that debug messages will be +/// handled by this handler, while only logged by the underlying `A` log handler (since `B`'s log level is `.info` +/// and thus it would not actually log that log message). +/// +/// If the log level is _set_ on a `Logger` backed by an `MultiplexLogHandler` the log level will apply to *all* +/// underlying log handlers, allowing a logger to still select at what level it wants to log regardless of if the underlying +/// handler is a multiplex or a normal one. If for some reason one might want to not allow changing a log level of a specific +/// handler passed into the multiplex log handler, this is possible by wrapping it in a handler which ignores any log level changes. +/// +/// ### Effective Logger.Metadata +/// +/// Since a `MultiplexLogHandler` is a combination of multiple log handlers, the handling of metadata can be non-obvious. +/// For example, the underlying log handlers may have metadata of their own set before they are used to initialize the multiplex log handler. +/// +/// The multiplex log handler acts purely as proxy and does not make any changes to underlying handler metadata other than +/// proxying writes that users made on a `Logger` instance backed by this handler. +/// +/// Setting metadata is always proxied through to _all_ underlying handlers, meaning that if a modification like +/// `logger[metadataKey: "x"] = "y"` is made, all underlying log handlers that this multiplex handler was initiated with +/// will observe this change. +/// +/// Reading metadata from the multiplex log handler MAY need to pick one of conflicting values if the underlying log handlers +/// were already initiated with some metadata before passing them into the multiplex handler. The multiplex handler uses +/// the order in which the handlers were passed in during its initialization as a priority indicator - the first handler's +/// values are more important than the next handlers values, etc. +/// +/// Example: +/// If the multiplex log handler was initiated with two handlers like this: `MultiplexLogHandler([handler1, handler2])`. +/// The handlers each have some already set metadata: `handler1` has metadata values for keys `one` and `all`, and `handler2` +/// has values for keys `two` and `all`. +/// +/// A query through the multiplex log handler the key `one` naturally returns `handler1`'s value, and a query for `two` +/// naturally returns `handler2`'s value. Querying for the key `all` will return `handler1`'s value, as that handler was indicated +/// "more important" than the second handler. The same rule applies when querying for the `metadata` property of the +/// multiplex log handler - it constructs `Metadata` uniquing values. +public struct MultiplexLogHandler: LogHandler { + private var handlers: [LogHandler] + private var effectiveLogLevel: Logger.Level + + /// Create a `MultiplexLogHandler`. + /// + /// - parameters: + /// - handlers: An array of `LogHandler`s, each of which will receive the log messages sent to this `Logger`. + /// The array must not be empty. + public init(_ handlers: [LogHandler]) { + assert(!handlers.isEmpty, "MultiplexLogHandler.handlers MUST NOT be empty") + self.handlers = handlers + self.effectiveLogLevel = handlers.map { $0.logLevel }.min() ?? .trace + } + + public var logLevel: Logger.Level { + get { + return self.effectiveLogLevel + } + set { + self.mutatingForEachHandler { $0.logLevel = newValue } + self.effectiveLogLevel = newValue + } + } + + public func log(level: Logger.Level, + message: Logger.Message, + metadata: Logger.Metadata?, + source: String, + file: String, + function: String, + line: UInt) { + for handler in self.handlers where handler.logLevel <= level { + handler.log(level: level, message: message, metadata: metadata, source: source, file: file, function: function, line: line) + } + } + + public var metadata: Logger.Metadata { + get { + var effectiveMetadata: Logger.Metadata = [:] + // as a rough estimate we assume that the underlying handlers have a similar metadata count, + // and we use the first one's current count to estimate how big of a dictionary we need to allocate: + effectiveMetadata.reserveCapacity(self.handlers.first!.metadata.count) // !-safe, we always have at least one handler + return self.handlers.reduce(into: effectiveMetadata) { effectiveMetadata, handler in + effectiveMetadata.merge(handler.metadata, uniquingKeysWith: { l, _ in l }) + } + } + set { + self.mutatingForEachHandler { $0.metadata = newValue } + } + } + + public subscript(metadataKey metadataKey: Logger.Metadata.Key) -> Logger.Metadata.Value? { + get { + for handler in self.handlers { + if let value = handler[metadataKey: metadataKey] { + return value + } + } + return nil + } + set { + self.mutatingForEachHandler { $0[metadataKey: metadataKey] = newValue } + } + } + + private mutating func mutatingForEachHandler(_ mutator: (inout LogHandler) -> Void) { + for index in self.handlers.indices { + mutator(&self.handlers[index]) + } + } +} + +/// A wrapper to facilitate `print`-ing to stderr and stdio that +/// ensures access to the underlying `FILE` is locked to prevent +/// cross-thread interleaving of output. +internal struct StdioOutputStream: TextOutputStream { + internal let file: UnsafeMutablePointer + internal let flushMode: FlushMode + + internal func write(_ string: String) { + string.withCString { ptr in + #if os(Windows) + _lock_file(self.file) + #else + flockfile(self.file) + #endif + defer { + #if os(Windows) + _unlock_file(self.file) + #else + funlockfile(self.file) + #endif + } + _ = fputs(ptr, self.file) + if case .always = self.flushMode { + self.flush() + } + } + } + + /// Flush the underlying stream. + /// This has no effect when using the `.always` flush mode, which is the default + internal func flush() { + _ = fflush(self.file) + } + + internal static let stderr = StdioOutputStream(file: systemStderr, flushMode: .always) + internal static let stdout = StdioOutputStream(file: systemStdout, flushMode: .always) + + /// Defines the flushing strategy for the underlying stream. + internal enum FlushMode { + case undefined + case always + } +} + +// Prevent name clashes +#if os(macOS) || os(tvOS) || os(iOS) || os(watchOS) +let systemStderr = Darwin.stderr +let systemStdout = Darwin.stdout +#elseif os(Windows) +let systemStderr = MSVCRT.stderr +let systemStdout = MSVCRT.stdout +#else +let systemStderr = Glibc.stderr! +let systemStdout = Glibc.stdout! +#endif + +/// `StreamLogHandler` is a simple implementation of `LogHandler` for directing +/// `Logger` output to either `stderr` or `stdout` via the factory methods. +public struct StreamLogHandler: LogHandler { + /// Factory that makes a `StreamLogHandler` to directs its output to `stdout` + public static func standardOutput(label: String) -> StreamLogHandler { + return StreamLogHandler(label: label, stream: StdioOutputStream.stdout) + } + + /// Factory that makes a `StreamLogHandler` to directs its output to `stderr` + public static func standardError(label: String) -> StreamLogHandler { + return StreamLogHandler(label: label, stream: StdioOutputStream.stderr) + } + + private let stream: TextOutputStream + private let label: String + + public var logLevel: Logger.Level = .info + + private var prettyMetadata: String? + public var metadata = Logger.Metadata() { + didSet { + self.prettyMetadata = self.prettify(self.metadata) + } + } + + public subscript(metadataKey metadataKey: String) -> Logger.Metadata.Value? { + get { + return self.metadata[metadataKey] + } + set { + self.metadata[metadataKey] = newValue + } + } + + // internal for testing only + internal init(label: String, stream: TextOutputStream) { + self.label = label + self.stream = stream + } + + public func log(level: Logger.Level, + message: Logger.Message, + metadata: Logger.Metadata?, + source: String, + file: String, + function: String, + line: UInt) { + let prettyMetadata = metadata?.isEmpty ?? true + ? self.prettyMetadata + : self.prettify(self.metadata.merging(metadata!, uniquingKeysWith: { _, new in new })) + + var stream = self.stream + stream.write("\(self.timestamp()) \(level) \(self.label) :\(prettyMetadata.map { " \($0)" } ?? "") \(message)\n") + } + + private func prettify(_ metadata: Logger.Metadata) -> String? { + return !metadata.isEmpty ? metadata.map { "\($0)=\($1)" }.joined(separator: " ") : nil + } + + private func timestamp() -> String { + var buffer = [Int8](repeating: 0, count: 255) + var timestamp = time(nil) + let localTime = localtime(×tamp) + strftime(&buffer, buffer.count, "%Y-%m-%dT%H:%M:%S%z", localTime) + return buffer.withUnsafeBufferPointer { + $0.withMemoryRebound(to: CChar.self) { + String(cString: $0.baseAddress!) + } + } + } +} + +/// No operation LogHandler, used when no logging is required +public struct SwiftLogNoOpLogHandler: LogHandler { + public init() {} + + @inlinable public func log(level: Logger.Level, message: Logger.Message, metadata: Logger.Metadata?, file: String, function: String, line: UInt) {} + + @inlinable public subscript(metadataKey _: String) -> Logger.Metadata.Value? { + get { + return nil + } + set {} + } + + @inlinable public var metadata: Logger.Metadata { + get { + return [:] + } + set {} + } + + @inlinable public var logLevel: Logger.Level { + get { + return .critical + } + set {} + } +} + +extension Logger { + @inlinable + internal static func currentModule(filePath: String = #file) -> String { + let utf8All = filePath.utf8 + return filePath.utf8.lastIndex(of: UInt8(ascii: "/")).flatMap { lastSlash -> Substring? in + utf8All[.. Substring in + filePath[utf8All.index(after: secondLastSlash) ..< lastSlash] + } + }.map { + String($0) + } ?? "n/a" + } +} + +// Extension has to be done on explicit type rather than Logger.Metadata.Value as workaround for +// https://bugs.swift.org/browse/SR-9686 +extension Logger.MetadataValue: ExpressibleByStringLiteral { + public typealias StringLiteralType = String + + public init(stringLiteral value: String) { + self = .string(value) + } +} + +// Extension has to be done on explicit type rather than Logger.Metadata.Value as workaround for +// https://bugs.swift.org/browse/SR-9686 +extension Logger.MetadataValue: CustomStringConvertible { + public var description: String { + switch self { + case .dictionary(let dict): + return dict.mapValues { $0.description }.description + case .array(let list): + return list.map { $0.description }.description + case .string(let str): + return str + case .stringConvertible(let repr): + return repr.description + } + } +} + +// Extension has to be done on explicit type rather than Logger.Metadata.Value as workaround for +// https://bugs.swift.org/browse/SR-9687 +extension Logger.MetadataValue: ExpressibleByStringInterpolation {} + +// Extension has to be done on explicit type rather than Logger.Metadata.Value as workaround for +// https://bugs.swift.org/browse/SR-9686 +extension Logger.MetadataValue: ExpressibleByDictionaryLiteral { + public typealias Key = String + public typealias Value = Logger.Metadata.Value + + public init(dictionaryLiteral elements: (String, Logger.Metadata.Value)...) { + self = .dictionary(.init(uniqueKeysWithValues: elements)) + } +} + +// Extension has to be done on explicit type rather than Logger.Metadata.Value as workaround for +// https://bugs.swift.org/browse/SR-9686 +extension Logger.MetadataValue: ExpressibleByArrayLiteral { + public typealias ArrayLiteralElement = Logger.Metadata.Value + + public init(arrayLiteral elements: Logger.Metadata.Value...) { + self = .array(elements) + } +} diff --git a/Pods/Manifest.lock b/Pods/Manifest.lock new file mode 100644 index 0000000..44fc545 --- /dev/null +++ b/Pods/Manifest.lock @@ -0,0 +1,65 @@ +PODS: + - Alamofire (5.9.1) + - BlueCryptor (1.0.32) + - BlueECC (1.2.5) + - BlueRSA (1.0.200) + - KituraContracts (1.2.1): + - LoggerAPI (~> 1.7) + - LoggerAPI (1.9.200): + - Logging (~> 1.1) + - Logging (1.4.0) + - SwiftJWT (3.6.200): + - BlueCryptor (~> 1.0) + - BlueECC (~> 1.1) + - BlueRSA (~> 1.0) + - KituraContracts (~> 1.2) + - LoggerAPI (~> 1.7) + - SwiftyBeaver (2.1.1) + - TrustDecision (1.4) + +DEPENDENCIES: + - Alamofire (from `https://github.com/Alamofire/Alamofire.git`) + - SwiftJWT + - SwiftyBeaver (from `https://github.com/SwiftyBeaver/SwiftyBeaver.git`) + - TrustDecision (= 1.4) + +SPEC REPOS: + trunk: + - BlueCryptor + - BlueECC + - BlueRSA + - KituraContracts + - LoggerAPI + - Logging + - SwiftJWT + - TrustDecision + +EXTERNAL SOURCES: + Alamofire: + :git: https://github.com/Alamofire/Alamofire.git + SwiftyBeaver: + :git: https://github.com/SwiftyBeaver/SwiftyBeaver.git + +CHECKOUT OPTIONS: + Alamofire: + :commit: 6b3296bd3e975fb3e04cdf22761ddf44df7157a5 + :git: https://github.com/Alamofire/Alamofire.git + SwiftyBeaver: + :commit: 8e06e2ea17b8fc503f95e2d6026d503834413e60 + :git: https://github.com/SwiftyBeaver/SwiftyBeaver.git + +SPEC CHECKSUMS: + Alamofire: f36a35757af4587d8e4f4bfa223ad10be2422b8c + BlueCryptor: b0aee3d9b8f367b49b30de11cda90e1735571c24 + BlueECC: 0d18e93347d3ec6d41416de21c1ffa4d4cd3c2cc + BlueRSA: dfeef51db96bcc4edec654956c1581adbda4e6a3 + KituraContracts: e845e60dc8627ad0a76fa55ef20a45451d8f830b + LoggerAPI: ad9c4a6f1e32f518fdb43a1347ac14d765ab5e3d + Logging: beeb016c9c80cf77042d62e83495816847ef108b + SwiftJWT: 88c412708f58c169d431d344c87bc79a87c830ae + SwiftyBeaver: ade157e4f857812e7d7f15f2e3396bb8733f8a1c + TrustDecision: 201ad3e5e834567998dffc22536ca36d600beb16 + +PODFILE CHECKSUM: f88829971628b679a467fb6f7d0bc97d7284c177 + +COCOAPODS: 1.15.2 diff --git a/Pods/Pods.xcodeproj/project.pbxproj b/Pods/Pods.xcodeproj/project.pbxproj new file mode 100644 index 0000000..0b5d9ba --- /dev/null +++ b/Pods/Pods.xcodeproj/project.pbxproj @@ -0,0 +1,3641 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 56; + objects = { + +/* Begin PBXBuildFile section */ + 0036E06D0609E0B31EB5442647D101AD /* ECError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C65A2761A821E52402905728179B0E6 /* ECError.swift */; }; + 0066FADAEF420F018864EFCA93BF9D51 /* Pods-AIGrammar-AIGrammarUITests-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = DCEF6AD6EA12C622897B5A150809D31C /* Pods-AIGrammar-AIGrammarUITests-dummy.m */; }; + 010973465C0F48B522806795CBED2C43 /* TDMobRiskBaseInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 4D184B3B83E23958D53A660A885302A5 /* TDMobRiskBaseInfo.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 01B316CFCCE7A94AC0024D0EC7552B3D /* ASN1.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F268A015D32AC8A5EF256EE1FB4F275 /* ASN1.swift */; }; + 025746DE54B1BD3CD48FB6AB5BC52E8A /* TDMobRiskDeviceStatusInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 243E07B2A753D83022AF70810CE0F51C /* TDMobRiskDeviceStatusInfo.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 0298D4F92276C089224598E73367645A /* TDMobRiskSafeDictionary.h in Headers */ = {isa = PBXBuildFile; fileRef = 869244A039B4AEF1173344F937467FB2 /* TDMobRiskSafeDictionary.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 047724F1C4CDDFA71F46E33C39F10FA3 /* TDMobRiskDeviceStatusInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 68071DC69EBD9745A4D6FE436BEE0B60 /* TDMobRiskDeviceStatusInfo.m */; }; + 06FF7A39EBED7D5057DD920FF643BF94 /* TrustDecision-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = B585A4FDCCFFC4757E3EC966BF9F7A3A /* TrustDecision-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0894595B90BB9BE837CB328F57255E2F /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FD0CE05D5D076B1B5190EE5DF97FD54E /* Foundation.framework */; }; + 09ED348CC514F53C55D29C4F1BA520E2 /* LoggerAPI-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 77CDA4F1303C101741D8D884748582E7 /* LoggerAPI-dummy.m */; }; + 0DA8A599A871E6A64B7B9E796B0F3308 /* JWTEncoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 491F1F10F433DABACAF796F3CEAC28AD /* JWTEncoder.swift */; }; + 0F1D68554CA1AC595168E8FB4E1A6E63 /* CachedResponseHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3810ED50A59A43DDB58559CB0A2DD95 /* CachedResponseHandler.swift */; }; + 106C9BE8D2D5BD88A722A83B881445AD /* Claims.swift in Sources */ = {isa = PBXBuildFile; fileRef = 48C5A8279AE18BA47E526CEB57CF30C7 /* Claims.swift */; }; + 126BC665C3D2B5B2FBE7E50258E0F2A2 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FD0CE05D5D076B1B5190EE5DF97FD54E /* Foundation.framework */; }; + 15488E69FEF316F5B5164AC0C6F0C19A /* FilterValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4E6F491F04DB8DEAE3EBDDA334F9BD4 /* FilterValidator.swift */; }; + 177809E275ECC628EE7CEF6F52B4411A /* RSAKeyType.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBAA6599A306CC8A82E45B827837EEDC /* RSAKeyType.swift */; }; + 1A80A5EFB1FC3DD4C03EA654BBA99642 /* TDMobRiskManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 832CDE9FCBDE0ACA627FBDFDFC05EB04 /* TDMobRiskManager.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 1AF6FD4A74E160188340F26D28982A50 /* CryptorRSAConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0703E706DAB6E667510EB7FE90232848 /* CryptorRSAConstants.swift */; }; + 1BA4F0B05D6A17790291304AF7438DA5 /* VerifierAlgorithm.swift in Sources */ = {isa = PBXBuildFile; fileRef = C73756698B68F4AFF41B3FFF9DBA4251 /* VerifierAlgorithm.swift */; }; + 1BEC012FE149C38578B49F0F4FF8249A /* HMAC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83090DF6151AD289A7468FB1EFC7AAF9 /* HMAC.swift */; }; + 1D34A567EB1420E9C030E3EEF0C71521 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FD0CE05D5D076B1B5190EE5DF97FD54E /* Foundation.framework */; }; + 1E702BFC502F8DE460A5322A547E6569 /* TDMobRiskTimeInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = ECCAC82F36F378492C29E5A7F9F46054 /* TDMobRiskTimeInfo.m */; }; + 1EDF0A2FA38DB263B4FBFE0818FC14B3 /* BlueCryptor-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 13C3816F00015DAD5FC9A1BE64A68E4A /* BlueCryptor-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 1F1526584488D1931184FF11B162E028 /* SwiftyBeaver-SwiftyBeaver in Resources */ = {isa = PBXBuildFile; fileRef = 9215295657AC6ABB8D4E934AEA62EEF6 /* SwiftyBeaver-SwiftyBeaver */; }; + 20B2985EF70D6DB110B5583A67AD3005 /* KituraContracts-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = B24E52BEF3A28B56D25E7D0C7CE1BFFF /* KituraContracts-dummy.m */; }; + 214538563382AF40DEA798179B61330F /* BlueECC-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = E759574BAAB768A64F21FCBFD9C9C07A /* BlueECC-dummy.m */; }; + 22291180549B1139F56D620A999310A4 /* Contracts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4DB7622F2FD4E0C696DB0820105830EF /* Contracts.swift */; }; + 22FAFA41450EC40132CF4B0EEE7E6788 /* ParameterEncoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A2B2BE5F08F2F77B5EB5CFD5CC41606 /* ParameterEncoder.swift */; }; + 237BB5C061F7CF700102421C6B742AEF /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FD0CE05D5D076B1B5190EE5DF97FD54E /* Foundation.framework */; }; + 25B07B4343220D9047066923803DABBE /* ECEncryptable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33755DAC4C8B85CF949B377E66699E4E /* ECEncryptable.swift */; }; + 266FD53F10617065B64F0320DAA3392C /* Logging-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 7F87309C6423A0A9C50F2DEF99D38554 /* Logging-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2695886313F517E6222ECB65F17B4E85 /* SwiftJWT-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = C3270135E3419F33C49DE89845D175F9 /* SwiftJWT-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 276CCE7D6298C49428EB1454B1EDFECA /* SSLPointerTricks.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8485BEA233D326789838FC6128E22AD /* SSLPointerTricks.swift */; }; + 27DB5683101E144F1AAD42C1B838F44D /* Pods-AIGrammar-AIGrammarUITests-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = EA07ACC7BF1A7582EA98FA3E3E93D5BD /* Pods-AIGrammar-AIGrammarUITests-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2B230B24827053BA3E9DA0C78A796BC2 /* ResponseSerialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0B30FED051A553715ECA1AC0ECF2A33 /* ResponseSerialization.swift */; }; + 2B42D035AFF52D62722161A7772C6C08 /* HTTPHeaders.swift in Sources */ = {isa = PBXBuildFile; fileRef = 662C410BFDFE5AC1417F6C89C711FE1F /* HTTPHeaders.swift */; }; + 2B4DA0685B8685618DF2202C945A544E /* BlueECC-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 8F8324F2F461CF1C0D396AFC7B2D17B5 /* BlueECC-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2BB25365C113482240B7B4821B1DD079 /* TDMobRiskTimeInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 8622A3C999AC771B6099F507E898C79C /* TDMobRiskTimeInfo.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 2C03C28338745B50B7E5956C03884CE0 /* Data+Base64URLEncoded.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EA950F020634AAE684368F0E0070BAD /* Data+Base64URLEncoded.swift */; }; + 2C4C08BB733A2101D945E8C37256F78F /* UploadRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD850EA07C378A4E02B1E71ABCD81ABF /* UploadRequest.swift */; }; + 2D70DC58C89E33AB723759672D36B683 /* CryptorRSADigest.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEF32F0849D37EA29405C9A49E0CA868 /* CryptorRSADigest.swift */; }; + 2D7CC4DD61485FE8246D415A813A94DB /* JWT.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C133093FE42E10E4DDDFCFEEFC488E3 /* JWT.swift */; }; + 2DF5E6E3C6C4011A9BCB21161BAA5E65 /* ValidateClaimsResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95AAEE18597743F2A43E1193696D1285 /* ValidateClaimsResult.swift */; }; + 3063E9B1F5C56B51525FC8D0D64BB07B /* EllipticCurve.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7EA940210EB25D87E6D6F406E35DD53 /* EllipticCurve.swift */; }; + 3109B5207E71C65D27B3E817A7B35698 /* SwiftJWT-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 5538703EE7D927436BA0AACD7DEBC0E5 /* SwiftJWT-dummy.m */; }; + 31394629250EE04C1D437CEAA9844825 /* BlueRSA.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE6068C90EA6887140053C17E77A6178 /* BlueRSA.swift */; }; + 31E5C60B94CC53FED9FA1A84DA0F5CE8 /* TDMobRiskKeychainsHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 54BF6577FDC51011E96E713B07746713 /* TDMobRiskKeychainsHelper.m */; }; + 31EABF7A2B9FAAF4E726C46C52DE6DB1 /* Filter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 733081B93BFE4B932A5ADB0F3B4B4084 /* Filter.swift */; }; + 32737A3500F39EFCEB89291850AABFB3 /* TDMobRisk.h in Headers */ = {isa = PBXBuildFile; fileRef = 12F26A1570034B67193B9CCFEBF5F631 /* TDMobRisk.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 36C78069A72BECAEB66B31FF794A09ED /* Request.swift in Sources */ = {isa = PBXBuildFile; fileRef = 876E0727CCD8BBCDA80A05BFAD527FBB /* Request.swift */; }; + 3738B495C7511E2BADB5683646C9A3F1 /* TDMobRiskCollector.h in Headers */ = {isa = PBXBuildFile; fileRef = 534C5BE15FDE9098DA9C216919720B48 /* TDMobRiskCollector.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 37E5ACD84B22B3D3D777DE097A8DF082 /* KituraContracts-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = BC37E03AD9D5C7753C1EC5D1C8B64BBA /* KituraContracts-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 3B901DF82C6576019330391D0F470B31 /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8D3D57353834825F7B52B816066B7789 /* CFNetwork.framework */; }; + 3FF50DC751EB78BC4107C7A553A5CF92 /* BlueRSA-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 988D32A7D1801EF8A4BA41D9B2A8FE44 /* BlueRSA-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 40F3B4F630766574D4E03A3801065D98 /* TDMobRiskDeviceInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A3FD2CD4F40F02A905467ADF425F1DF /* TDMobRiskDeviceInfo.m */; }; + 4337931D8B8E3F3BA03C77C1B496BEAD /* HTTPMethod.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6ADE50A624DA0A490DB11BF26D9ADCF /* HTTPMethod.swift */; }; + 4373F7703D3D5A4BF068B4D57C88EEF3 /* TDMobRiskEncodeHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 23D00A9BC39B5AE73B06BC2CF41C8570 /* TDMobRiskEncodeHelper.m */; }; + 44C23ACB771B26CC0159357E19CAB785 /* SwiftyBeaver.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE4EF9C2E7BB71F10E9802BEC52C110C /* SwiftyBeaver.swift */; }; + 459F1BDAF057FEDA89B5DC0F62CD6F36 /* Data+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBA9A11E7F68C23FF6F46981F796B1A5 /* Data+Extensions.swift */; }; + 4B1ED39638712D17BE9FBCAAFEF1468E /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FD0CE05D5D076B1B5190EE5DF97FD54E /* Foundation.framework */; }; + 4E90056906076DE3ED54B042EE9319B6 /* TDMobRiskAppInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = A8338BDCE31B41B2160BCA0B55406356 /* TDMobRiskAppInfo.m */; }; + 4F4B9DFA352D9958C7494D7BC24631D8 /* Alamofire-Alamofire in Resources */ = {isa = PBXBuildFile; fileRef = 085DBCE7DD98588B2ED103B1C1F36026 /* Alamofire-Alamofire */; }; + 5075DC82A63A9807DFC390B4CE8046CD /* Session.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6811741973DBDA55D9F914A30DDCF00B /* Session.swift */; }; + 50AD6B8BBDAA111F49780DA337DEF57D /* SignerAlgorithm.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22BB5B783523427143025AE99247D2AA /* SignerAlgorithm.swift */; }; + 50FEE1B272074B47D6A63561E0F4D58C /* Pods-AIGrammar-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 4783076A010B9367D74FA8E67F7F9DF4 /* Pods-AIGrammar-dummy.m */; }; + 52CF1E46EF69738B27E92F4B718E614A /* LogHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6DC35251A3740B925DB25F5D8177B0C /* LogHandler.swift */; }; + 552A488AA665EDA3778FD07705402F43 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FD0CE05D5D076B1B5190EE5DF97FD54E /* Foundation.framework */; }; + 5601110C7BCC4CD041B927D7CECB72EB /* Base64.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D05134B7184B60302FA90AA82875594 /* Base64.swift */; }; + 56D4A4B5FAC428E6380C5EBE6E21A8F9 /* TDMobRiskAPIHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = B04EE592F79E079A1F8015AE4B06E735 /* TDMobRiskAPIHelper.m */; }; + 571142A2747CD90165F631A8AE980A0A /* Alamofire-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 0D1F5608163D81BC4C86A365F0B1C200 /* Alamofire-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 5830C6260CA2B7CD6DC74054FB29CDD1 /* DownloadRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C77EF3C4CA522913D103AE9599FC3CC7 /* DownloadRequest.swift */; }; + 58E936B1E7E42C0BFC119D428B70F1D8 /* Alamofire.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EB192AE20CEDC37883201E33976FE0D /* Alamofire.swift */; }; + 5956158B14273C892F1E596A8FC907A3 /* TrustDecision-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 48017312199755B883FA88AE24C3F52F /* TrustDecision-dummy.m */; }; + 5AD4E5B4118A1DC7D639F611044B4159 /* DataRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBE29F8D97EBC95C31D5B7F1DC7CDA83 /* DataRequest.swift */; }; + 5BFED9BCB02678A257FD5D93176AC40E /* JWTVerifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = E202407A4414327A583F639E2AC5175C /* JWTVerifier.swift */; }; + 5DD9B8B026B60B309CD0054BD6B74E8B /* NoneAlgorithm.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89C272A5253A1E7AB3A70F666DEC3E2B /* NoneAlgorithm.swift */; }; + 5E1B0B6F334802C66E484E4AB564E96A /* BlueECDSA.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21EDE1DEFEE5948C0B9DEAAE808439AC /* BlueECDSA.swift */; }; + 612AE0ABB9BCD3AF0E1D29B4C063CA62 /* StringEncoding+Alamofire.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0298855D08D828FC69889FC243C0848 /* StringEncoding+Alamofire.swift */; }; + 6209BD7060E0A2330F0F1A5FF91EF89A /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 3B43DA6F3C13A7961B5F4F241E71032F /* PrivacyInfo.xcprivacy */; }; + 6262CEE6306F8826EDFAEF0A72943B25 /* Updatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F9B67C5521BE31868B661965440B6EB /* Updatable.swift */; }; + 6264A3B49DA8FF7DBA0371361E079962 /* TDMobRiskAppInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 80AE637320B93B5141AC353887787E5C /* TDMobRiskAppInfo.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 63A6CB6A62954270DBE6B5E7A05CA74E /* FileDestination.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B403C17E2C156687F869C48F8BE97FC /* FileDestination.swift */; }; + 65345F2C02726D947133E79CCA5CA0CC /* JWTError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 99F092B1CB4A09E75ED6552E26797F18 /* JWTError.swift */; }; + 67BC1B3150CB7E4089FA2AD72FC6B1DA /* TDMobRiskIdCalculator.m in Sources */ = {isa = PBXBuildFile; fileRef = 72EC99CD19E57499D369D9DF538B32DB /* TDMobRiskIdCalculator.m */; }; + 68238227D42B2511FA6A26BF71E92520 /* Alamofire-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 6B6CC99473EA621E30F92D9FC480D4F0 /* Alamofire-dummy.m */; }; + 68995B28EE5B539CEA5A1133E4623927 /* MultipartFormData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E8D97359B7E3E8D1F506B517B4FD4DE /* MultipartFormData.swift */; }; + 68A74F13F8FEBAA7E0EA9344DED0458B /* OperationQueue+Alamofire.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE478042A493AEAE7D4406796D54096A /* OperationQueue+Alamofire.swift */; }; + 69261B5D3B53EBF7109D5E1DA3768CAC /* AuthenticationInterceptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53143B0A3BDE20B977A2AA6DB503D7CC /* AuthenticationInterceptor.swift */; }; + 6AF293CFF3CE6DD506AB54A3C9E2722D /* Digest.swift in Sources */ = {isa = PBXBuildFile; fileRef = A43B0C746A61DE112316E0B9EAB941CD /* Digest.swift */; }; + 6C1AB8C0B3CAE8E895E194F6C7A8F7FC /* ECSignable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D07C9E9E7C28A5395314FCF71FFB6C97 /* ECSignable.swift */; }; + 6CC7E7C00730B1BF42A28B2E23CA01D6 /* URLSessionConfiguration+Alamofire.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F4B57E4C4F825B0ADD7FEA34C7519BE /* URLSessionConfiguration+Alamofire.swift */; }; + 6F2E0DC7D8598283D088A989FDB8E5F6 /* ParameterEncoding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 929730BDA29BF52A91A72E2C4EDFCE2B /* ParameterEncoding.swift */; }; + 7288FF043A6E21DCB18F534663FB03A7 /* TDMobRiskAPIHelper.h in Headers */ = {isa = PBXBuildFile; fileRef = 6A200B3395C57BCAE75DC005A6FC4D68 /* TDMobRiskAPIHelper.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 72C19C762FADC82517C344E9F47D7E50 /* AlamofireExtended.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCA13C5342DCE444EA5A16641E18A370 /* AlamofireExtended.swift */; }; + 7326CA047952CBD38697917BAB8E0A5E /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FD0CE05D5D076B1B5190EE5DF97FD54E /* Foundation.framework */; }; + 74991B968DEE7DC8432DBBE23F3257C3 /* StreamCryptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4239DDB5499812B3F70D8891DFBAEC25 /* StreamCryptor.swift */; }; + 794FC38D15336AB502B73B012005E9BD /* Validation.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAADEE493105DA2C10DA9D20A0146483 /* Validation.swift */; }; + 795681285B4E2B121B5CD420131168F8 /* MultipartUpload.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64FC8A2FF9CC8B36C3CFA8B354418A1F /* MultipartUpload.swift */; }; + 79D835F73197376AE844D95C90933226 /* BlueCryptor-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F1CA1ABED23EE81D8D58D84BA1E0D46 /* BlueCryptor-dummy.m */; }; + 7AE2E0B382A14D58BE7DAA0C852DCD02 /* ServerTrustEvaluation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A06C1FA4D99468574D3E158A255DFD0 /* ServerTrustEvaluation.swift */; }; + 7BBEE2A1BF4C42D44EA22C22E7D8C342 /* BodyFormat.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B713DF1DB67473D5E1963376E430165 /* BodyFormat.swift */; }; + 7DDA219DF77250F5CBB3242438DAF486 /* Header.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5131D386EC321F08E42AD63FCD4508 /* Header.swift */; }; + 7E0C38ADB86609510208A4F310931D9A /* TDMobRiskDeviceInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 287DB6BBB76AC431B7E798DCE29214F3 /* TDMobRiskDeviceInfo.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 7E462830E8D66124921BCBB93A76F1B0 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68D536CC3C636F95B3293EC0D32B0E2E /* Extensions.swift */; }; + 7EAF26A63E3F93A45B220B1A817E4144 /* TDMobRiskIdCalculator.h in Headers */ = {isa = PBXBuildFile; fileRef = F5203D713011FBCEAA2EAD0C75373B89 /* TDMobRiskIdCalculator.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 7F9E3C12DB861DF9C8A604963E14E92E /* TDMobRiskHeader.h in Headers */ = {isa = PBXBuildFile; fileRef = 62C3820AACAF7B3112CEB93D9C7FA0A2 /* TDMobRiskHeader.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 804CC711F5AA0EB4E00C9A993FEEB0CA /* BlueHMAC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3367FC7EC454EEC5AD75D267FF6A084A /* BlueHMAC.swift */; }; + 84BEB9E439780B1E0DEF56459E3D3352 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FD0CE05D5D076B1B5190EE5DF97FD54E /* Foundation.framework */; }; + 86806FB25A08BE03CD091F292BB7C554 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FD0CE05D5D076B1B5190EE5DF97FD54E /* Foundation.framework */; }; + 88626113053B820D5D65F7FF2841D541 /* ClaimsStandardJWT.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EA4013B0A0049D3A724191668E63D72 /* ClaimsStandardJWT.swift */; }; + 887DB52C63E52FBD3B88F42DD8CFB421 /* RequestCompression.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A19D5C20FB97FA8EF39C799393B005F /* RequestCompression.swift */; }; + 8B355855806FB7FEF614CC32DE5E49C6 /* Coder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CD686ADC4CB81B528886B963B9C1828 /* Coder.swift */; }; + 8C035F4049ED17F3FD68B55A15501CCC /* TDMobRiskBaseInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = D0EBA2D68F16B6B7545ABA33971F51B1 /* TDMobRiskBaseInfo.m */; }; + 8C82F446D46AB5E4D4B89FD4AAE32F77 /* SwiftyBeaver-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = F60A6F6866F5C7582FE80D041C0B6C61 /* SwiftyBeaver-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 8DA66F3A55C0A3211EA5D64A8A3473F1 /* Locks.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F65B2DAF529F5A3BA936F019B9319A /* Locks.swift */; }; + 8DC2C2BC0F42E9B820EB962B1D76644D /* ClaimsOpenID.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92E49E42D6B37B7245374BEAE591800D /* ClaimsOpenID.swift */; }; + 8DD46EE7FB9503E7634E929DDE1CBA31 /* Notifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB136D877786EA9186E0D70824F64124 /* Notifications.swift */; }; + 902D9829701BE63A4BEEC90F9871852D /* Logging.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73E1D15B67083210B44DB207A1D2C6F2 /* Logging.swift */; }; + 903FBD5C1D823F2C0A6C8E3D147482FF /* Pods-AIGrammarTests-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = D5F8F15D16473C9044C70AE5DFDE5C5F /* Pods-AIGrammarTests-dummy.m */; }; + 90D847B19214926EDE5210D44A08F3C7 /* RequestTaskMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 630B69516E15545819AC3D4920444608 /* RequestTaskMap.swift */; }; + 918C9188314B4577DF6136410E13653E /* TDMobRiskSpaceInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = BFB1FC8C927909529E1917BB884F5D65 /* TDMobRiskSpaceInfo.m */; }; + 92138A77DFEB4F76FCB582E97633896D /* URLRequest+Alamofire.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78CDD5BA5983187CCFE2D688079F203A /* URLRequest+Alamofire.swift */; }; + 93771D67060533037C5E36F89CA0E6C5 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FD0CE05D5D076B1B5190EE5DF97FD54E /* Foundation.framework */; }; + 9378157945D7B405C862A05B0D6B971B /* Result+Alamofire.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8FFBC53FB844D0960B1114D23CF14D0 /* Result+Alamofire.swift */; }; + 940B1A39D1AECDD5896FE5E4F721FED8 /* SwiftyBeaver-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = E50A1F879186C0C8BF11EE0F3811B6EE /* SwiftyBeaver-dummy.m */; }; + 94256662B883B8D44E1424A6748CB18D /* ECDecryptable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB27254D562084F118968075D5089AF4 /* ECDecryptable.swift */; }; + 96B4E052EFA52F1F185DC01777763360 /* BodyDecoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80BDD3D1E3C81B099102A2E4EED59D63 /* BodyDecoder.swift */; }; + 97D7D91FC818805D8344C373CC098C32 /* SessionDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB0ED87C259D986A05C2DCC593F9D6D3 /* SessionDelegate.swift */; }; + 99BC73949BA89A03FB1ED130B43A8B02 /* CryptorRSAUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9E5E7CDE2913E6028137FEF15DC8895 /* CryptorRSAUtilities.swift */; }; + 9C7D314BE45AB79E96B260656C36BAEC /* RedirectHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5718E48F5BA586D70BD7789BF8F08A26 /* RedirectHandler.swift */; }; + 9D8BED7F3F86BB39E7C0923D92E73F8B /* Combine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29C7CEDCBF288A6C6917F7EE85028DC8 /* Combine.swift */; }; + A1506893FF52AA466B130E8B05FBE868 /* NetworkReachabilityManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F168D4AB45E86068B5001DF872CD260 /* NetworkReachabilityManager.swift */; }; + A22A2ACF53FDC243AAAFB009005A710F /* Concurrency.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B5F75FE6085A717CA97C96457789561 /* Concurrency.swift */; }; + A22F501CEF8902F55BC9C721C5B7AD17 /* Status.swift in Sources */ = {isa = PBXBuildFile; fileRef = 10D842A0766986149C1F9177445BB967 /* Status.swift */; }; + A3FD52DF5584364FFD56965394C36CF2 /* URLConvertible+URLRequestConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84069E63D3914B1B386F2077DF5CC027 /* URLConvertible+URLRequestConvertible.swift */; }; + A442E3E925B8E3B705B6910DD907E2A8 /* BodyEncoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6C93D5561E5331B2D8CF02C657D306E /* BodyEncoder.swift */; }; + A4BA9EF2EF5DCE2451F70318DC067CA3 /* Pods-AIGrammarTests-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 33DE377395ED69A94A9504A142DB6D62 /* Pods-AIGrammarTests-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A54126CFE40B126B362093AC65EB80F0 /* BaseDestination.swift in Sources */ = {isa = PBXBuildFile; fileRef = AEE91752539B32E2D44EE738CEF5C757 /* BaseDestination.swift */; }; + A5906CD1B9E027686B14C572817F9059 /* GoogleCloudDestination.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FD54C9608C8ED573AFF3057B4A733F5 /* GoogleCloudDestination.swift */; }; + A98B69EE86D815F50D8B9AF37939B4B9 /* Cryptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDF80F042B66695A034D41F43F13E296 /* Cryptor.swift */; }; + A9FDC28AAC04AAF28754E3C9F7D921F7 /* ECPublicKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1DD10AFBE5209EB9F17651B72CCF910 /* ECPublicKey.swift */; }; + AA53DAEB5CA0378FED32961B2052A01B /* ClaimsMicroProfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B0653202020E8A7231A4897466FB81 /* ClaimsMicroProfile.swift */; }; + AA602A49B1DC7FDED565CAD8BB89EAC1 /* AFError.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC691038C9D3660690548A6D2399A977 /* AFError.swift */; }; + AC8C84031DC30CD75716F6B35B404D1D /* CryptorRSAErrors.swift in Sources */ = {isa = PBXBuildFile; fileRef = B763964583DBEC6EEB7C4A3CBBAE54E1 /* CryptorRSAErrors.swift */; }; + AD86E160D9AF005D4C79997189A78541 /* Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = A402371D02417A424D88CEBD571105F4 /* Utilities.swift */; }; + AF43CE9A8089BBA86874EE26CC38684E /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FD0CE05D5D076B1B5190EE5DF97FD54E /* Foundation.framework */; }; + B5B90B89912260E463C6ED44CFF05DFD /* TDMobRiskManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 3FC1DF34E1F489D064A233732A348583 /* TDMobRiskManager.m */; }; + B5C66B48EB624FEC4D2F64A50F143716 /* DataStreamRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E331842372E70735184CC7D193F8A09 /* DataStreamRequest.swift */; }; + B952E724E1063E096813E083647B831F /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 4CEBE2F64EAD7A1A5D85DDFFB343E060 /* PrivacyInfo.xcprivacy */; }; + BC0A0C473B63B817926F4D58611281BB /* URLEncodedFormEncoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = C21A3C218E21F198893329B25487B128 /* URLEncodedFormEncoder.swift */; }; + BD4FBDE5F2A710292C8287735137C509 /* QueryDecoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 989EE2B8BC4C8CC05BB5728892FAE8F2 /* QueryDecoder.swift */; }; + BF67C108B6259651384C3918E40EE800 /* Crypto.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B3858F856AAABA72257963942A66EAB /* Crypto.swift */; }; + C2253D5C9C3BA63FA70DC4C7410A209D /* TDMobRiskKeychainsHelper.h in Headers */ = {isa = PBXBuildFile; fileRef = 1607C1D0D659D1D0245D98DEC664ADE5 /* TDMobRiskKeychainsHelper.h */; settings = {ATTRIBUTES = (Project, ); }; }; + C22CD856FDC788515376347CF56327CC /* SSLPointerTricks.swift in Sources */ = {isa = PBXBuildFile; fileRef = E14DC8B22552D898C97C440275A962B2 /* SSLPointerTricks.swift */; }; + C2C69A82C314BF2092AAB5492EF9E4CD /* CryptorRSAKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CFF0F226982FD136D312688E5249F52 /* CryptorRSAKey.swift */; }; + C39D15F3433EAED94EF5DCAB5BB81AF0 /* TDMobRiskCalculator.h in Headers */ = {isa = PBXBuildFile; fileRef = 340C6CC65E0DA5634F65CD153F9A91E3 /* TDMobRiskCalculator.h */; settings = {ATTRIBUTES = (Project, ); }; }; + C3E84BE76BE3AD640327004B5A7868D9 /* CryptorRSA.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39FF1DFC24CE15B6CA5E22F4711BB076 /* CryptorRSA.swift */; }; + C55064815CA00F3A52BE0F2C5A2AC7B1 /* TDMobRiskCollector.m in Sources */ = {isa = PBXBuildFile; fileRef = 3CD33B5467C85B9FFD9A5932A2903196 /* TDMobRiskCollector.m */; }; + C558C4D208FD62FC36CD471577B02470 /* TDMobRiskCpuInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = DF4A0058E84C6F5CF1BAD8913C6C9B8F /* TDMobRiskCpuInfo.h */; settings = {ATTRIBUTES = (Project, ); }; }; + C64590868B5F22BBA9D880F9993355B6 /* TDMobRiskCpuInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 4EBDCF010C819FC8CC703DAFFA5A2AF9 /* TDMobRiskCpuInfo.m */; }; + C6C3A38CB352735109B000DD52B6F3C9 /* Logging-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = FF4614067693F95E7F86545487487790 /* Logging-dummy.m */; }; + C81D71ED01A3BA1E01C74CB15C850140 /* TDMobRiskIdentifierInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = C43B179EF14EB33B4AAED7779901F7F7 /* TDMobRiskIdentifierInfo.m */; }; + C8EB219BB93084757B55B890B5F3FD04 /* TDMobRiskSafeDictionary.m in Sources */ = {isa = PBXBuildFile; fileRef = 0062E35E61C1EA1458F0EC82042EAAEE /* TDMobRiskSafeDictionary.m */; }; + C94C4CA45ED2CAFFB6F697DCA7C5E55A /* LoggerAPI-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = B3BEF546883082266053DB6B7FA761FF /* LoggerAPI-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + CA96BF27B2693ED1519801E4DB145A16 /* Data+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFC6F3EFF82F3B3DA129EEA58A2889DF /* Data+Extensions.swift */; }; + CB2A998AD60A9C8653AB550B8411C2E5 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FD0CE05D5D076B1B5190EE5DF97FD54E /* Foundation.framework */; }; + CBEE15376AD8AE6E3776D52A107229E0 /* BlueRSA-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = A99514216510012C970D274771E8287F /* BlueRSA-dummy.m */; }; + CCBDC055488D19C9B00E28AABA041D55 /* TDMobRiskOSInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = C43BC732DE0BE391AC8584D4D3D073B5 /* TDMobRiskOSInfo.h */; settings = {ATTRIBUTES = (Project, ); }; }; + CDCA01B605A086576DBB75F8C3A24337 /* RetryPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7300F85D79226F3B2A89B62CF0A570B0 /* RetryPolicy.swift */; }; + CDDC7DEEDC472BF67D03D94D589EE9A6 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDEF713C27A68A8296E587E26E9FEB2E /* Extensions.swift */; }; + CE1947C18B5F91D328AFB8A26E5F827E /* TDMobRiskEncodeHelper.h in Headers */ = {isa = PBXBuildFile; fileRef = 308F95F04AB38A2160C97B9B7499C136 /* TDMobRiskEncodeHelper.h */; settings = {ATTRIBUTES = (Project, ); }; }; + D29C9D92076A43DCCAF9F0FAE0911321 /* JWTDecoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D134F8273D13F4CD72FA2E7751D14D0 /* JWTDecoder.swift */; }; + D3A529382E524CB7387F0A7A860C28DE /* TDMobRiskCalculator.m in Sources */ = {isa = PBXBuildFile; fileRef = 3EB880D057E12D4BC1F6807EEC268767 /* TDMobRiskCalculator.m */; }; + D53DA8941E8E06804078BD9BF3DE398E /* TDMobRiskSpaceInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 1BE6220B98F71C3CF0C19656186F4C64 /* TDMobRiskSpaceInfo.h */; settings = {ATTRIBUTES = (Project, ); }; }; + D82331BC251B87EEFAD29F53061F5E7A /* ConsoleDestination.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FAF1A4CAA1CF22A6859D3CB1795CE28 /* ConsoleDestination.swift */; }; + DBDEFD5E67253B069025C2C08541AF8B /* ClosureAliases.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB5A71FC79A73135DF7400B009BF4B72 /* ClosureAliases.swift */; }; + DE6D5CC0FDCEF991781D529DB3266156 /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = F576D3A881CB8F727F2496DEE7B942D3 /* Logger.swift */; }; + DE896085DFDD686BDBDEFB776F0D683A /* DispatchQueue+Alamofire.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC1DF16C12A92AF271A53666D9EE5CDE /* DispatchQueue+Alamofire.swift */; }; + DE8F5B68839128A005EE3549A1149B09 /* RequestInterceptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1549056E8195C0EB0F38EB6923E4866A /* RequestInterceptor.swift */; }; + DF539117F395B3F97BE24B6CB69DEAF8 /* TDMobRiskOSInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 451F228C8AA303567EF3D34720EEF26D /* TDMobRiskOSInfo.m */; }; + E1FAE2E561064E7B6D82A872FF21EE22 /* Random.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57EF66504584E6A9C7EB93805DBAB256 /* Random.swift */; }; + E4D29BF0611B19578C6954E36C8221D1 /* ECSignature.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06F307D7561CF717981F44FB7F38BA7A /* ECSignature.swift */; }; + E67B00531A5F5C7ED8832A189AEC767B /* ECPrivateKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E9538B10524E3DA3A00132BA6B657ED /* ECPrivateKey.swift */; }; + E6ED06AC318A34F7744B32CEC759CDA9 /* Response.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FA85E2AA20E77DB8B3E326375D1A365 /* Response.swift */; }; + EA679C848DC90955BC12EAE0A954CBE0 /* QueryEncoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04152FFEF08BB6EED23BE2BC8B5ABAD6 /* QueryEncoder.swift */; }; + ED737EB4F72FC3FA3CF93565FC349DD2 /* JWTSigner.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE93FFB7350B277DF57759B57510EF75 /* JWTSigner.swift */; }; + F2F45400CF0F61F8FB7E817988515032 /* TDMobRiskIdentifierInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = A299D2B42A5AC13BC25A4FDC8C3677C1 /* TDMobRiskIdentifierInfo.h */; settings = {ATTRIBUTES = (Project, ); }; }; + F4DD0AD58DDD5641BDEAEA6CF44FF0ED /* Protected.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5EB01D39F86EDF150078E43C92619E1 /* Protected.swift */; }; + F7E576E007A81E0EFD2E0849CB17878D /* WebSocketRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA48B294C8FB72B6E2E564FF293A8ACA /* WebSocketRequest.swift */; }; + F84CE1CC51A9AB580C183782A91BDD1A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FD0CE05D5D076B1B5190EE5DF97FD54E /* Foundation.framework */; }; + F8754CE53F48DF0322C92E13E907327E /* SSLPointerTricks.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE28C75C985DCD7C9856DC03D5DB4C86 /* SSLPointerTricks.swift */; }; + F8D7C3AA037914AE4B5487CADBAB6A89 /* KeyDerivation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A45D9F225B60F3E1D797AE627ACCEE3 /* KeyDerivation.swift */; }; + FCE62086E1AB54A4F61EBCDBA15C1510 /* EventMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 444BE0852EDA865B3FF167E5A5C15975 /* EventMonitor.swift */; }; + FF2EEBD0DA36CD81FCAD357C48AEDC1A /* Pods-AIGrammar-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 2B4185A37C0478BC369029DA98B6BC1E /* Pods-AIGrammar-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 0C9449DEE482F22B48C86887B3D65219 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = E7F041D10DB8131E7E8B866AA3D62E32; + remoteInfo = SwiftJWT; + }; + 188BBF2A90C4C5903DC5A035F274B3FD /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = EAAA1AD3A8A1B59AB91319EE40752C6D; + remoteInfo = Alamofire; + }; + 1F7C42DAB51F62A354F25D8D4B5A1295 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = E7F041D10DB8131E7E8B866AA3D62E32; + remoteInfo = SwiftJWT; + }; + 234F2BDFB323886FC27710EDD5E46BF8 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 8E6E41C7F38055ADB1C69E0CA8CB8C1F; + remoteInfo = TrustDecision; + }; + 3531027AA1157A8BE3DECDCC638D922C /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 921D74A1881BD89FA651ED7078F37128; + remoteInfo = KituraContracts; + }; + 3AB6DC35BD651E02B02C72F580CA05AD /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 9FF604BE1801EF6B67EED51EDA9D63C9; + remoteInfo = BlueECC; + }; + 43BCC2D5C58B1F571B35F7023B5DBBDF /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 2ABF3F8EC6CE525E1E02C51D72C64E94; + remoteInfo = Logging; + }; + 45CC75751F65E21C75B7190370454D3F /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = EAAA1AD3A8A1B59AB91319EE40752C6D; + remoteInfo = Alamofire; + }; + 49A421A9F538D81B6BD413E01CE47DEF /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 3CA5FD5A45C3F901A76060FE0F4A9B5F; + remoteInfo = BlueRSA; + }; + 4DBCD985D26936EBC05E4B6614A030E9 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 9FF604BE1801EF6B67EED51EDA9D63C9; + remoteInfo = BlueECC; + }; + 5CEA60AAFB925BF79E3B54021E2F5BCD /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 4090B1B7FD0B799BF794751E6A9D826F; + remoteInfo = LoggerAPI; + }; + 5E8FEB6AF4017FCC82C8DE8880DEC0BF /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 921D74A1881BD89FA651ED7078F37128; + remoteInfo = KituraContracts; + }; + 5EE536CF73B18381A88A6C862DEC4905 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 0A2AABD7B36B6E5F1847893C445204F1; + remoteInfo = "Pods-AIGrammar"; + }; + 60F6C539AF08A2936A070327FE5704D0 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = A28ABE059EE78F37B646540BEAB934E0; + remoteInfo = BlueCryptor; + }; + 72305A9935B8511F65620F8BFB0149E3 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 4090B1B7FD0B799BF794751E6A9D826F; + remoteInfo = LoggerAPI; + }; + 7A3E9ED06C5D6930AD3E6663682901CE /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 1740FECD5A74A42ADAE96F3A8246787A; + remoteInfo = SwiftyBeaver; + }; + 7A49A9F30FE4BE6998B0888EFC83238E /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 3CA5FD5A45C3F901A76060FE0F4A9B5F; + remoteInfo = BlueRSA; + }; + 903DD7481559FB1BA5854B7F5CFC6EE6 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 9FF604BE1801EF6B67EED51EDA9D63C9; + remoteInfo = BlueECC; + }; + 936B5B9D1C7A180775D7C76F9A9E5834 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 921D74A1881BD89FA651ED7078F37128; + remoteInfo = KituraContracts; + }; + 949C3EDF388ACE75C4C30242A49A6096 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = A28ABE059EE78F37B646540BEAB934E0; + remoteInfo = BlueCryptor; + }; + 998461E1F3F211B8950FA0BF893A61DF /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 3CA5FD5A45C3F901A76060FE0F4A9B5F; + remoteInfo = BlueRSA; + }; + 9B6D73F898F70705641F61F401236820 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 2ABF3F8EC6CE525E1E02C51D72C64E94; + remoteInfo = Logging; + }; + 9E63986C3DF23E5E58D8F046D0CDB353 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 4090B1B7FD0B799BF794751E6A9D826F; + remoteInfo = LoggerAPI; + }; + BE532875E85AFEB3C3F8AF57005C5496 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 8E6E41C7F38055ADB1C69E0CA8CB8C1F; + remoteInfo = TrustDecision; + }; + CA217DF27E83341B005ECE09D4B88A4A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 44FE1AF38BF7DF3E88AEEE6BD4DA40CD; + remoteInfo = "SwiftyBeaver-SwiftyBeaver"; + }; + CA5EC40ECDDAD0EF7FEB47C27DF7044E /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 4090B1B7FD0B799BF794751E6A9D826F; + remoteInfo = LoggerAPI; + }; + D994FCC0307F0E8A7F6EA799D174ECF8 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 1740FECD5A74A42ADAE96F3A8246787A; + remoteInfo = SwiftyBeaver; + }; + F7476C7BF967247633BF398F11513F14 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 2ABF3F8EC6CE525E1E02C51D72C64E94; + remoteInfo = Logging; + }; + F98E14D7ACFA4568034FD2853277A627 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 976126A1CE06DC6E162563800E1BDF14; + remoteInfo = "Alamofire-Alamofire"; + }; + FAD9CC331B1E1C0113387ADD622DC16A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = A28ABE059EE78F37B646540BEAB934E0; + remoteInfo = BlueCryptor; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 0062E35E61C1EA1458F0EC82042EAAEE /* TDMobRiskSafeDictionary.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = TDMobRiskSafeDictionary.m; path = TrustDecision/Classes/Containers/TDMobRiskSafeDictionary.m; sourceTree = ""; }; + 04152FFEF08BB6EED23BE2BC8B5ABAD6 /* QueryEncoder.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = QueryEncoder.swift; path = Sources/KituraContracts/CodableQuery/QueryEncoder.swift; sourceTree = ""; }; + 06F307D7561CF717981F44FB7F38BA7A /* ECSignature.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ECSignature.swift; path = Sources/CryptorECC/ECSignature.swift; sourceTree = ""; }; + 0703E706DAB6E667510EB7FE90232848 /* CryptorRSAConstants.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CryptorRSAConstants.swift; path = Sources/CryptorRSA/CryptorRSAConstants.swift; sourceTree = ""; }; + 0772105F0C37E2D5ECBE60425F8CB1E5 /* Pods-AIGrammar-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-AIGrammar-Info.plist"; sourceTree = ""; }; + 085DBCE7DD98588B2ED103B1C1F36026 /* Alamofire-Alamofire */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; name = "Alamofire-Alamofire"; path = Alamofire.bundle; sourceTree = BUILT_PRODUCTS_DIR; }; + 0B27561A5C6CD6C04D7057C8989055DD /* Logging.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = Logging.release.xcconfig; sourceTree = ""; }; + 0D1F5608163D81BC4C86A365F0B1C200 /* Alamofire-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Alamofire-umbrella.h"; sourceTree = ""; }; + 0EF2AA497DAF2C29FA3EB06BDE44553F /* BlueCryptor */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = BlueCryptor; path = Cryptor.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 0F0CAB7F2C7DF7A61E1A9CD5D7E12D76 /* TrustDecision-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "TrustDecision-prefix.pch"; sourceTree = ""; }; + 10D842A0766986149C1F9177445BB967 /* Status.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Status.swift; path = Sources/Cryptor/Status.swift; sourceTree = ""; }; + 10D8544DE75F5F119B166CEBBFB05BFE /* Alamofire.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = Alamofire.release.xcconfig; sourceTree = ""; }; + 12F26A1570034B67193B9CCFEBF5F631 /* TDMobRisk.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = TDMobRisk.h; path = TrustDecision/Classes/TDMobRisk.h; sourceTree = ""; }; + 13C3816F00015DAD5FC9A1BE64A68E4A /* BlueCryptor-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "BlueCryptor-umbrella.h"; sourceTree = ""; }; + 13DBB992142C26E7749EA1F79E78F2B2 /* LoggerAPI.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = LoggerAPI.release.xcconfig; sourceTree = ""; }; + 1549056E8195C0EB0F38EB6923E4866A /* RequestInterceptor.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RequestInterceptor.swift; path = Source/Features/RequestInterceptor.swift; sourceTree = ""; }; + 1607C1D0D659D1D0245D98DEC664ADE5 /* TDMobRiskKeychainsHelper.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = TDMobRiskKeychainsHelper.h; path = TrustDecision/Classes/Helper/TDMobRiskKeychainsHelper.h; sourceTree = ""; }; + 16B1B41F43B18E93821D646FAE2330A4 /* Pods-AIGrammar */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = "Pods-AIGrammar"; path = Pods_AIGrammar.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 17361A3F72DF686B73CCA2EA43FB253F /* Pods-AIGrammarTests-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-AIGrammarTests-acknowledgements.markdown"; sourceTree = ""; }; + 18F06465805140E928122396070E55D0 /* SwiftyBeaver-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "SwiftyBeaver-Info.plist"; sourceTree = ""; }; + 1BE6220B98F71C3CF0C19656186F4C64 /* TDMobRiskSpaceInfo.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = TDMobRiskSpaceInfo.h; path = TrustDecision/Classes/Collect/Info/TDMobRiskSpaceInfo.h; sourceTree = ""; }; + 1CBD7D1B3E51C1259905FD7BE0CB911A /* Pods-AIGrammar-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-AIGrammar-acknowledgements.markdown"; sourceTree = ""; }; + 1CD686ADC4CB81B528886B963B9C1828 /* Coder.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Coder.swift; path = Sources/KituraContracts/CodableQuery/Coder.swift; sourceTree = ""; }; + 1CFF0F226982FD136D312688E5249F52 /* CryptorRSAKey.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CryptorRSAKey.swift; path = Sources/CryptorRSA/CryptorRSAKey.swift; sourceTree = ""; }; + 1D05134B7184B60302FA90AA82875594 /* Base64.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Base64.swift; path = Sources/Base64.swift; sourceTree = ""; }; + 1D134F8273D13F4CD72FA2E7751D14D0 /* JWTDecoder.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = JWTDecoder.swift; path = Sources/SwiftJWT/JWTDecoder.swift; sourceTree = ""; }; + 1D7A7899F0C30F86BFF25B50766DE489 /* KituraContracts-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "KituraContracts-Info.plist"; sourceTree = ""; }; + 1F9B67C5521BE31868B661965440B6EB /* Updatable.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Updatable.swift; path = Sources/Cryptor/Updatable.swift; sourceTree = ""; }; + 21EDE1DEFEE5948C0B9DEAAE808439AC /* BlueECDSA.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BlueECDSA.swift; path = Sources/SwiftJWT/BlueECDSA.swift; sourceTree = ""; }; + 22AC8CED52EAAE1BC6F8494CA8A43050 /* KituraContracts.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = KituraContracts.debug.xcconfig; sourceTree = ""; }; + 22BB5B783523427143025AE99247D2AA /* SignerAlgorithm.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SignerAlgorithm.swift; path = Sources/SwiftJWT/SignerAlgorithm.swift; sourceTree = ""; }; + 23D00A9BC39B5AE73B06BC2CF41C8570 /* TDMobRiskEncodeHelper.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = TDMobRiskEncodeHelper.m; path = TrustDecision/Classes/Helper/TDMobRiskEncodeHelper.m; sourceTree = ""; }; + 243E07B2A753D83022AF70810CE0F51C /* TDMobRiskDeviceStatusInfo.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = TDMobRiskDeviceStatusInfo.h; path = TrustDecision/Classes/Collect/Info/TDMobRiskDeviceStatusInfo.h; sourceTree = ""; }; + 27B5E7D21308F0025ED3E0115E1D24F2 /* TrustDecision */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = TrustDecision; path = TrustDecision.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 287DB6BBB76AC431B7E798DCE29214F3 /* TDMobRiskDeviceInfo.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = TDMobRiskDeviceInfo.h; path = TrustDecision/Classes/Collect/Info/TDMobRiskDeviceInfo.h; sourceTree = ""; }; + 2933AF5131B9637CCCB2E3A5EEF86C75 /* Pods-AIGrammarTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-AIGrammarTests.release.xcconfig"; sourceTree = ""; }; + 29C7CEDCBF288A6C6917F7EE85028DC8 /* Combine.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Combine.swift; path = Source/Features/Combine.swift; sourceTree = ""; }; + 2A19D5C20FB97FA8EF39C799393B005F /* RequestCompression.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RequestCompression.swift; path = Source/Features/RequestCompression.swift; sourceTree = ""; }; + 2A2B2BE5F08F2F77B5EB5CFD5CC41606 /* ParameterEncoder.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ParameterEncoder.swift; path = Source/Core/ParameterEncoder.swift; sourceTree = ""; }; + 2B4185A37C0478BC369029DA98B6BC1E /* Pods-AIGrammar-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-AIGrammar-umbrella.h"; sourceTree = ""; }; + 2C9350326F251EA74192A5F7194F7680 /* TrustDecision.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = TrustDecision.debug.xcconfig; sourceTree = ""; }; + 2D48C3FEA8F9B5DF83F5A9499AC8B49A /* Pods-AIGrammar.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = "Pods-AIGrammar.modulemap"; sourceTree = ""; }; + 2EEEBED94E28C75C03A4E8BA1B2150C1 /* LoggerAPI-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "LoggerAPI-Info.plist"; sourceTree = ""; }; + 2F10E968E0EA4CAA7858662305EF4A7A /* Logging.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = Logging.debug.xcconfig; sourceTree = ""; }; + 2FA1160BC562B21D74917556707CD264 /* BlueRSA-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "BlueRSA-prefix.pch"; sourceTree = ""; }; + 308F95F04AB38A2160C97B9B7499C136 /* TDMobRiskEncodeHelper.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = TDMobRiskEncodeHelper.h; path = TrustDecision/Classes/Helper/TDMobRiskEncodeHelper.h; sourceTree = ""; }; + 31EB261ADE24930729954E9FCFC880EB /* KituraContracts-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "KituraContracts-prefix.pch"; sourceTree = ""; }; + 3367FC7EC454EEC5AD75D267FF6A084A /* BlueHMAC.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BlueHMAC.swift; path = Sources/SwiftJWT/BlueHMAC.swift; sourceTree = ""; }; + 33755DAC4C8B85CF949B377E66699E4E /* ECEncryptable.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ECEncryptable.swift; path = Sources/CryptorECC/ECEncryptable.swift; sourceTree = ""; }; + 33DE377395ED69A94A9504A142DB6D62 /* Pods-AIGrammarTests-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-AIGrammarTests-umbrella.h"; sourceTree = ""; }; + 340C6CC65E0DA5634F65CD153F9A91E3 /* TDMobRiskCalculator.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = TDMobRiskCalculator.h; path = TrustDecision/Classes/Core/TDMobRiskCalculator.h; sourceTree = ""; }; + 3628EC52F3791A80D6C9FFEF3899A2A5 /* BlueRSA-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "BlueRSA-Info.plist"; sourceTree = ""; }; + 39FF1DFC24CE15B6CA5E22F4711BB076 /* CryptorRSA.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CryptorRSA.swift; path = Sources/CryptorRSA/CryptorRSA.swift; sourceTree = ""; }; + 3A2D7EA43DFC7B5C2800C5DE10AB0478 /* Alamofire.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = Alamofire.modulemap; sourceTree = ""; }; + 3A3FD2CD4F40F02A905467ADF425F1DF /* TDMobRiskDeviceInfo.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = TDMobRiskDeviceInfo.m; path = TrustDecision/Classes/Collect/Info/TDMobRiskDeviceInfo.m; sourceTree = ""; }; + 3B43DA6F3C13A7961B5F4F241E71032F /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; name = PrivacyInfo.xcprivacy; path = Source/PrivacyInfo.xcprivacy; sourceTree = ""; }; + 3BEBBF1632E94E587CC6383656FF9731 /* LoggerAPI-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "LoggerAPI-prefix.pch"; sourceTree = ""; }; + 3CD33B5467C85B9FFD9A5932A2903196 /* TDMobRiskCollector.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = TDMobRiskCollector.m; path = TrustDecision/Classes/Collect/TDMobRiskCollector.m; sourceTree = ""; }; + 3EB880D057E12D4BC1F6807EEC268767 /* TDMobRiskCalculator.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = TDMobRiskCalculator.m; path = TrustDecision/Classes/Core/TDMobRiskCalculator.m; sourceTree = ""; }; + 3F168D4AB45E86068B5001DF872CD260 /* NetworkReachabilityManager.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NetworkReachabilityManager.swift; path = Source/Features/NetworkReachabilityManager.swift; sourceTree = ""; }; + 3F4B57E4C4F825B0ADD7FEA34C7519BE /* URLSessionConfiguration+Alamofire.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "URLSessionConfiguration+Alamofire.swift"; path = "Source/Extensions/URLSessionConfiguration+Alamofire.swift"; sourceTree = ""; }; + 3FC1DF34E1F489D064A233732A348583 /* TDMobRiskManager.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = TDMobRiskManager.m; path = TrustDecision/Classes/TDMobRiskManager.m; sourceTree = ""; }; + 4087105BBBFA96A7006EF61D099C4D08 /* KituraContracts.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = KituraContracts.modulemap; sourceTree = ""; }; + 4239DDB5499812B3F70D8891DFBAEC25 /* StreamCryptor.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = StreamCryptor.swift; path = Sources/Cryptor/StreamCryptor.swift; sourceTree = ""; }; + 444BE0852EDA865B3FF167E5A5C15975 /* EventMonitor.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = EventMonitor.swift; path = Source/Features/EventMonitor.swift; sourceTree = ""; }; + 451F228C8AA303567EF3D34720EEF26D /* TDMobRiskOSInfo.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = TDMobRiskOSInfo.m; path = TrustDecision/Classes/Collect/Info/TDMobRiskOSInfo.m; sourceTree = ""; }; + 474BCF3BC2CEFA443D99D91BB396A148 /* Alamofire-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Alamofire-prefix.pch"; sourceTree = ""; }; + 4783076A010B9367D74FA8E67F7F9DF4 /* Pods-AIGrammar-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-AIGrammar-dummy.m"; sourceTree = ""; }; + 48017312199755B883FA88AE24C3F52F /* TrustDecision-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "TrustDecision-dummy.m"; sourceTree = ""; }; + 48C5A8279AE18BA47E526CEB57CF30C7 /* Claims.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Claims.swift; path = Sources/SwiftJWT/Claims.swift; sourceTree = ""; }; + 491F1F10F433DABACAF796F3CEAC28AD /* JWTEncoder.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = JWTEncoder.swift; path = Sources/SwiftJWT/JWTEncoder.swift; sourceTree = ""; }; + 49DC68DCD598D7E998E7F413F7175874 /* SwiftJWT.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = SwiftJWT.debug.xcconfig; sourceTree = ""; }; + 4A45D9F225B60F3E1D797AE627ACCEE3 /* KeyDerivation.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = KeyDerivation.swift; path = Sources/Cryptor/KeyDerivation.swift; sourceTree = ""; }; + 4CEBE2F64EAD7A1A5D85DDFFB343E060 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; + 4D184B3B83E23958D53A660A885302A5 /* TDMobRiskBaseInfo.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = TDMobRiskBaseInfo.h; path = TrustDecision/Classes/Collect/Info/Base/TDMobRiskBaseInfo.h; sourceTree = ""; }; + 4DB7622F2FD4E0C696DB0820105830EF /* Contracts.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Contracts.swift; path = Sources/KituraContracts/Contracts.swift; sourceTree = ""; }; + 4E331842372E70735184CC7D193F8A09 /* DataStreamRequest.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = DataStreamRequest.swift; path = Source/Core/DataStreamRequest.swift; sourceTree = ""; }; + 4EBDCF010C819FC8CC703DAFFA5A2AF9 /* TDMobRiskCpuInfo.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = TDMobRiskCpuInfo.m; path = TrustDecision/Classes/Collect/Info/TDMobRiskCpuInfo.m; sourceTree = ""; }; + 4FAF1A4CAA1CF22A6859D3CB1795CE28 /* ConsoleDestination.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ConsoleDestination.swift; path = Sources/ConsoleDestination.swift; sourceTree = ""; }; + 53143B0A3BDE20B977A2AA6DB503D7CC /* AuthenticationInterceptor.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AuthenticationInterceptor.swift; path = Source/Features/AuthenticationInterceptor.swift; sourceTree = ""; }; + 534C5BE15FDE9098DA9C216919720B48 /* TDMobRiskCollector.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = TDMobRiskCollector.h; path = TrustDecision/Classes/Collect/TDMobRiskCollector.h; sourceTree = ""; }; + 53D3E35E7EB25621342FA0CA77C0167B /* BlueECC */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = BlueECC; path = CryptorECC.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 54BF6577FDC51011E96E713B07746713 /* TDMobRiskKeychainsHelper.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = TDMobRiskKeychainsHelper.m; path = TrustDecision/Classes/Helper/TDMobRiskKeychainsHelper.m; sourceTree = ""; }; + 5538703EE7D927436BA0AACD7DEBC0E5 /* SwiftJWT-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "SwiftJWT-dummy.m"; sourceTree = ""; }; + 5718E48F5BA586D70BD7789BF8F08A26 /* RedirectHandler.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RedirectHandler.swift; path = Source/Features/RedirectHandler.swift; sourceTree = ""; }; + 57EF66504584E6A9C7EB93805DBAB256 /* Random.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Random.swift; path = Sources/Cryptor/Random.swift; sourceTree = ""; }; + 5909292CACF3FD0B79E4348B60B024AD /* SwiftyBeaver.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = SwiftyBeaver.debug.xcconfig; sourceTree = ""; }; + 5B3858F856AAABA72257963942A66EAB /* Crypto.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Crypto.swift; path = Sources/Cryptor/Crypto.swift; sourceTree = ""; }; + 5D797E9A5C5782CE845840781FA1CC81 /* Alamofire */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Alamofire; path = Alamofire.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 5EB192AE20CEDC37883201E33976FE0D /* Alamofire.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Alamofire.swift; path = Source/Alamofire.swift; sourceTree = ""; }; + 5F1CA1ABED23EE81D8D58D84BA1E0D46 /* BlueCryptor-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "BlueCryptor-dummy.m"; sourceTree = ""; }; + 5F268A015D32AC8A5EF256EE1FB4F275 /* ASN1.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ASN1.swift; path = Sources/CryptorECC/ASN1.swift; sourceTree = ""; }; + 5F46B5BFB9A87CD3FB6D5EFCD2C8DD31 /* BlueECC.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = BlueECC.debug.xcconfig; sourceTree = ""; }; + 5FA85E2AA20E77DB8B3E326375D1A365 /* Response.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Response.swift; path = Source/Core/Response.swift; sourceTree = ""; }; + 62C3820AACAF7B3112CEB93D9C7FA0A2 /* TDMobRiskHeader.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = TDMobRiskHeader.h; path = TrustDecision/Classes/TDMobRiskHeader.h; sourceTree = ""; }; + 630B69516E15545819AC3D4920444608 /* RequestTaskMap.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RequestTaskMap.swift; path = Source/Core/RequestTaskMap.swift; sourceTree = ""; }; + 64FC8A2FF9CC8B36C3CFA8B354418A1F /* MultipartUpload.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = MultipartUpload.swift; path = Source/Features/MultipartUpload.swift; sourceTree = ""; }; + 662C410BFDFE5AC1417F6C89C711FE1F /* HTTPHeaders.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = HTTPHeaders.swift; path = Source/Core/HTTPHeaders.swift; sourceTree = ""; }; + 68071DC69EBD9745A4D6FE436BEE0B60 /* TDMobRiskDeviceStatusInfo.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = TDMobRiskDeviceStatusInfo.m; path = TrustDecision/Classes/Collect/Info/TDMobRiskDeviceStatusInfo.m; sourceTree = ""; }; + 6811741973DBDA55D9F914A30DDCF00B /* Session.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Session.swift; path = Source/Core/Session.swift; sourceTree = ""; }; + 683761A68E578D6A23100651857E9B34 /* Pods-AIGrammar-AIGrammarUITests-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-AIGrammar-AIGrammarUITests-Info.plist"; sourceTree = ""; }; + 68D536CC3C636F95B3293EC0D32B0E2E /* Extensions.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Extensions.swift; path = Sources/KituraContracts/CodableQuery/Extensions.swift; sourceTree = ""; }; + 6918EEAE564D0A473D5B5A011A21A685 /* TrustDecision-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "TrustDecision-Info.plist"; sourceTree = ""; }; + 6A200B3395C57BCAE75DC005A6FC4D68 /* TDMobRiskAPIHelper.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = TDMobRiskAPIHelper.h; path = TrustDecision/Classes/Helper/TDMobRiskAPIHelper.h; sourceTree = ""; }; + 6B6CC99473EA621E30F92D9FC480D4F0 /* Alamofire-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Alamofire-dummy.m"; sourceTree = ""; }; + 6C133093FE42E10E4DDDFCFEEFC488E3 /* JWT.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = JWT.swift; path = Sources/SwiftJWT/JWT.swift; sourceTree = ""; }; + 6DEBE158485E8400CD58C4ED18BD52C0 /* Pods-AIGrammar-AIGrammarUITests-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-AIGrammar-AIGrammarUITests-acknowledgements.plist"; sourceTree = ""; }; + 6E8D97359B7E3E8D1F506B517B4FD4DE /* MultipartFormData.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = MultipartFormData.swift; path = Source/Features/MultipartFormData.swift; sourceTree = ""; }; + 6FD54C9608C8ED573AFF3057B4A733F5 /* GoogleCloudDestination.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = GoogleCloudDestination.swift; path = Sources/GoogleCloudDestination.swift; sourceTree = ""; }; + 72EC99CD19E57499D369D9DF538B32DB /* TDMobRiskIdCalculator.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = TDMobRiskIdCalculator.m; path = TrustDecision/Classes/Core/TDMobRiskIdCalculator.m; sourceTree = ""; }; + 7300F85D79226F3B2A89B62CF0A570B0 /* RetryPolicy.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RetryPolicy.swift; path = Source/Features/RetryPolicy.swift; sourceTree = ""; }; + 733081B93BFE4B932A5ADB0F3B4B4084 /* Filter.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Filter.swift; path = Sources/Filter.swift; sourceTree = ""; }; + 73E1D15B67083210B44DB207A1D2C6F2 /* Logging.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Logging.swift; path = Sources/Logging/Logging.swift; sourceTree = ""; }; + 778371035164E03619A2E38FA577B03C /* SwiftyBeaver.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = SwiftyBeaver.modulemap; sourceTree = ""; }; + 77CDA4F1303C101741D8D884748582E7 /* LoggerAPI-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "LoggerAPI-dummy.m"; sourceTree = ""; }; + 78CDD5BA5983187CCFE2D688079F203A /* URLRequest+Alamofire.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "URLRequest+Alamofire.swift"; path = "Source/Extensions/URLRequest+Alamofire.swift"; sourceTree = ""; }; + 7A5F7001277980B8F3EEA75BCD346E53 /* SwiftyBeaver */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = SwiftyBeaver; path = SwiftyBeaver.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 7F87309C6423A0A9C50F2DEF99D38554 /* Logging-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Logging-umbrella.h"; sourceTree = ""; }; + 80AE637320B93B5141AC353887787E5C /* TDMobRiskAppInfo.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = TDMobRiskAppInfo.h; path = TrustDecision/Classes/Collect/Info/TDMobRiskAppInfo.h; sourceTree = ""; }; + 80BDD3D1E3C81B099102A2E4EED59D63 /* BodyDecoder.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BodyDecoder.swift; path = Sources/KituraContracts/BodyDecoder.swift; sourceTree = ""; }; + 83090DF6151AD289A7468FB1EFC7AAF9 /* HMAC.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = HMAC.swift; path = Sources/Cryptor/HMAC.swift; sourceTree = ""; }; + 832CDE9FCBDE0ACA627FBDFDFC05EB04 /* TDMobRiskManager.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = TDMobRiskManager.h; path = TrustDecision/Classes/TDMobRiskManager.h; sourceTree = ""; }; + 83F6EF98AFBD4D35B938CCD75660D9DA /* BlueRSA.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = BlueRSA.modulemap; sourceTree = ""; }; + 84069E63D3914B1B386F2077DF5CC027 /* URLConvertible+URLRequestConvertible.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "URLConvertible+URLRequestConvertible.swift"; path = "Source/Core/URLConvertible+URLRequestConvertible.swift"; sourceTree = ""; }; + 86196CD2BF23040E2C37D5AF356CBBC3 /* Pods-AIGrammar-AIGrammarUITests.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = "Pods-AIGrammar-AIGrammarUITests.modulemap"; sourceTree = ""; }; + 8622A3C999AC771B6099F507E898C79C /* TDMobRiskTimeInfo.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = TDMobRiskTimeInfo.h; path = TrustDecision/Classes/Collect/Info/TDMobRiskTimeInfo.h; sourceTree = ""; }; + 869244A039B4AEF1173344F937467FB2 /* TDMobRiskSafeDictionary.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = TDMobRiskSafeDictionary.h; path = TrustDecision/Classes/Containers/TDMobRiskSafeDictionary.h; sourceTree = ""; }; + 876E0727CCD8BBCDA80A05BFAD527FBB /* Request.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Request.swift; path = Source/Core/Request.swift; sourceTree = ""; }; + 878A3266C4D7514338A19E8F3F7C3F92 /* BlueRSA.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = BlueRSA.debug.xcconfig; sourceTree = ""; }; + 881A178E10B4EB1EAE869F754E904097 /* Pods-AIGrammar.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-AIGrammar.debug.xcconfig"; sourceTree = ""; }; + 89C272A5253A1E7AB3A70F666DEC3E2B /* NoneAlgorithm.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NoneAlgorithm.swift; path = Sources/SwiftJWT/NoneAlgorithm.swift; sourceTree = ""; }; + 89CA742EB8DB549ABC50A25AB729AFBD /* ResourceBundle-Alamofire-Alamofire-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "ResourceBundle-Alamofire-Alamofire-Info.plist"; sourceTree = ""; }; + 8A06C1FA4D99468574D3E158A255DFD0 /* ServerTrustEvaluation.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ServerTrustEvaluation.swift; path = Source/Features/ServerTrustEvaluation.swift; sourceTree = ""; }; + 8B5F75FE6085A717CA97C96457789561 /* Concurrency.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Concurrency.swift; path = Source/Features/Concurrency.swift; sourceTree = ""; }; + 8B713DF1DB67473D5E1963376E430165 /* BodyFormat.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BodyFormat.swift; path = Sources/KituraContracts/BodyFormat.swift; sourceTree = ""; }; + 8C65A2761A821E52402905728179B0E6 /* ECError.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ECError.swift; path = Sources/CryptorECC/ECError.swift; sourceTree = ""; }; + 8D3D57353834825F7B52B816066B7789 /* CFNetwork.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CFNetwork.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS14.0.sdk/System/Library/Frameworks/CFNetwork.framework; sourceTree = DEVELOPER_DIR; }; + 8E3678944C2CAE232380D0899E593E47 /* SwiftJWT-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "SwiftJWT-prefix.pch"; sourceTree = ""; }; + 8EA4013B0A0049D3A724191668E63D72 /* ClaimsStandardJWT.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ClaimsStandardJWT.swift; path = Sources/SwiftJWT/ClaimsExamples/ClaimsStandardJWT.swift; sourceTree = ""; }; + 8EA950F020634AAE684368F0E0070BAD /* Data+Base64URLEncoded.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "Data+Base64URLEncoded.swift"; path = "Sources/SwiftJWT/Data+Base64URLEncoded.swift"; sourceTree = ""; }; + 8F8324F2F461CF1C0D396AFC7B2D17B5 /* BlueECC-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "BlueECC-umbrella.h"; sourceTree = ""; }; + 916DA5F53A3E5C39B71F77F53E989430 /* Logging-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Logging-prefix.pch"; sourceTree = ""; }; + 9215295657AC6ABB8D4E934AEA62EEF6 /* SwiftyBeaver-SwiftyBeaver */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; name = "SwiftyBeaver-SwiftyBeaver"; path = SwiftyBeaver.bundle; sourceTree = BUILT_PRODUCTS_DIR; }; + 929730BDA29BF52A91A72E2C4EDFCE2B /* ParameterEncoding.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ParameterEncoding.swift; path = Source/Core/ParameterEncoding.swift; sourceTree = ""; }; + 92E49E42D6B37B7245374BEAE591800D /* ClaimsOpenID.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ClaimsOpenID.swift; path = Sources/SwiftJWT/ClaimsExamples/ClaimsOpenID.swift; sourceTree = ""; }; + 95AAEE18597743F2A43E1193696D1285 /* ValidateClaimsResult.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ValidateClaimsResult.swift; path = Sources/SwiftJWT/ValidateClaimsResult.swift; sourceTree = ""; }; + 98526239ED2F634672C32610933FB8E7 /* BlueCryptor.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = BlueCryptor.release.xcconfig; sourceTree = ""; }; + 988D32A7D1801EF8A4BA41D9B2A8FE44 /* BlueRSA-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "BlueRSA-umbrella.h"; sourceTree = ""; }; + 989EE2B8BC4C8CC05BB5728892FAE8F2 /* QueryDecoder.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = QueryDecoder.swift; path = Sources/KituraContracts/CodableQuery/QueryDecoder.swift; sourceTree = ""; }; + 98C21BC262D1B2A150C9130FB95CB482 /* SwiftJWT.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = SwiftJWT.modulemap; sourceTree = ""; }; + 99F092B1CB4A09E75ED6552E26797F18 /* JWTError.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = JWTError.swift; path = Sources/SwiftJWT/JWTError.swift; sourceTree = ""; }; + 9A3E80A5CCD67032BA2B4420062D4792 /* KituraContracts.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = KituraContracts.release.xcconfig; sourceTree = ""; }; + 9B403C17E2C156687F869C48F8BE97FC /* FileDestination.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = FileDestination.swift; path = Sources/FileDestination.swift; sourceTree = ""; }; + 9D5DD9C5C9F76F2EE09D8BAAC659B245 /* SwiftyBeaver-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "SwiftyBeaver-prefix.pch"; sourceTree = ""; }; + 9D940727FF8FB9C785EB98E56350EF41 /* Podfile */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; indentWidth = 2; lastKnownFileType = text; name = Podfile; path = ../Podfile; sourceTree = SOURCE_ROOT; tabWidth = 2; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; + 9DB8603F150521A9B8621FD6ACE6FAB3 /* BlueECC.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = BlueECC.release.xcconfig; sourceTree = ""; }; + 9E9538B10524E3DA3A00132BA6B657ED /* ECPrivateKey.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ECPrivateKey.swift; path = Sources/CryptorECC/ECPrivateKey.swift; sourceTree = ""; }; + 9F644688D2AEFF5768BE065C6536B7BB /* Pods-AIGrammarTests-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-AIGrammarTests-Info.plist"; sourceTree = ""; }; + A299D2B42A5AC13BC25A4FDC8C3677C1 /* TDMobRiskIdentifierInfo.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = TDMobRiskIdentifierInfo.h; path = TrustDecision/Classes/Collect/Info/TDMobRiskIdentifierInfo.h; sourceTree = ""; }; + A314EC770E60E516BCD8B61346D6878F /* Pods-AIGrammar-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-AIGrammar-acknowledgements.plist"; sourceTree = ""; }; + A3B85B880B894F1FF5BAFD94E036CC04 /* LoggerAPI */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = LoggerAPI; path = LoggerAPI.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + A402371D02417A424D88CEBD571105F4 /* Utilities.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Utilities.swift; path = Sources/Cryptor/Utilities.swift; sourceTree = ""; }; + A43B0C746A61DE112316E0B9EAB941CD /* Digest.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Digest.swift; path = Sources/Cryptor/Digest.swift; sourceTree = ""; }; + A4A161573F1639985114A1C9B4133353 /* Pods-AIGrammar-AIGrammarUITests-frameworks.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-AIGrammar-AIGrammarUITests-frameworks.sh"; sourceTree = ""; }; + A5CF16B4996DB7D0E4981082FB86ED82 /* BlueRSA */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = BlueRSA; path = CryptorRSA.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + A6513E0A6CB60623515716E73996F0F5 /* Logging */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Logging; path = Logging.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + A674E5F2502FEEB09E600793F3CF15CF /* BlueECC.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = BlueECC.modulemap; sourceTree = ""; }; + A6DC35251A3740B925DB25F5D8177B0C /* LogHandler.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = LogHandler.swift; path = Sources/Logging/LogHandler.swift; sourceTree = ""; }; + A6F5720C1061539D34EF7E105D07E86B /* Pods-AIGrammarTests-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-AIGrammarTests-acknowledgements.plist"; sourceTree = ""; }; + A8338BDCE31B41B2160BCA0B55406356 /* TDMobRiskAppInfo.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = TDMobRiskAppInfo.m; path = TrustDecision/Classes/Collect/Info/TDMobRiskAppInfo.m; sourceTree = ""; }; + A99514216510012C970D274771E8287F /* BlueRSA-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "BlueRSA-dummy.m"; sourceTree = ""; }; + AD6ECF5E77BCA6AC38C6B041223E889D /* Pods-AIGrammarTests */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = "Pods-AIGrammarTests"; path = Pods_AIGrammarTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + AED9455D30C035D76AE0DA62675CAD12 /* Logging.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = Logging.modulemap; sourceTree = ""; }; + AEE91752539B32E2D44EE738CEF5C757 /* BaseDestination.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BaseDestination.swift; path = Sources/BaseDestination.swift; sourceTree = ""; }; + AFC6F3EFF82F3B3DA129EEA58A2889DF /* Data+Extensions.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "Data+Extensions.swift"; path = "Sources/CryptorRSA/Data+Extensions.swift"; sourceTree = ""; }; + B04EE592F79E079A1F8015AE4B06E735 /* TDMobRiskAPIHelper.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = TDMobRiskAPIHelper.m; path = TrustDecision/Classes/Helper/TDMobRiskAPIHelper.m; sourceTree = ""; }; + B24E52BEF3A28B56D25E7D0C7CE1BFFF /* KituraContracts-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "KituraContracts-dummy.m"; sourceTree = ""; }; + B3810ED50A59A43DDB58559CB0A2DD95 /* CachedResponseHandler.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CachedResponseHandler.swift; path = Source/Features/CachedResponseHandler.swift; sourceTree = ""; }; + B3BEF546883082266053DB6B7FA761FF /* LoggerAPI-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "LoggerAPI-umbrella.h"; sourceTree = ""; }; + B3F3A2726E30CE89E01164BE4896D098 /* LoggerAPI.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = LoggerAPI.debug.xcconfig; sourceTree = ""; }; + B4E6F491F04DB8DEAE3EBDDA334F9BD4 /* FilterValidator.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = FilterValidator.swift; path = Sources/FilterValidator.swift; sourceTree = ""; }; + B53E2E24B42EDD2D1DCD7190097B67C3 /* ResourceBundle-SwiftyBeaver-SwiftyBeaver-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "ResourceBundle-SwiftyBeaver-SwiftyBeaver-Info.plist"; sourceTree = ""; }; + B585A4FDCCFFC4757E3EC966BF9F7A3A /* TrustDecision-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "TrustDecision-umbrella.h"; sourceTree = ""; }; + B6ADE50A624DA0A490DB11BF26D9ADCF /* HTTPMethod.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = HTTPMethod.swift; path = Source/Core/HTTPMethod.swift; sourceTree = ""; }; + B70B8C024B6F0A02E43F26BC3E31D597 /* BlueCryptor-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "BlueCryptor-prefix.pch"; sourceTree = ""; }; + B763964583DBEC6EEB7C4A3CBBAE54E1 /* CryptorRSAErrors.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CryptorRSAErrors.swift; path = Sources/CryptorRSA/CryptorRSAErrors.swift; sourceTree = ""; }; + B944789BECD07D60B5D498BACE7FC050 /* BlueCryptor.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = BlueCryptor.modulemap; sourceTree = ""; }; + B9E276AB0F6AE3009D9751E19C8A0E0F /* KituraContracts */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = KituraContracts; path = KituraContracts.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + BA155A3B8F6E8AA5A625E76BDB822789 /* Pods-AIGrammar-AIGrammarUITests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-AIGrammar-AIGrammarUITests.release.xcconfig"; sourceTree = ""; }; + BC198EEAA48B17C1A01B2FD7CF631C45 /* SwiftyBeaver.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = SwiftyBeaver.release.xcconfig; sourceTree = ""; }; + BC37E03AD9D5C7753C1EC5D1C8B64BBA /* KituraContracts-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "KituraContracts-umbrella.h"; sourceTree = ""; }; + BC691038C9D3660690548A6D2399A977 /* AFError.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AFError.swift; path = Source/Core/AFError.swift; sourceTree = ""; }; + BD2DF58C5BB328219EABF947ED5CD389 /* Pods-AIGrammar-frameworks.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-AIGrammar-frameworks.sh"; sourceTree = ""; }; + BDC8A99F14C97CA85C839872E2DC482A /* Pods-AIGrammar-AIGrammarUITests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-AIGrammar-AIGrammarUITests.debug.xcconfig"; sourceTree = ""; }; + BE93FFB7350B277DF57759B57510EF75 /* JWTSigner.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = JWTSigner.swift; path = Sources/SwiftJWT/JWTSigner.swift; sourceTree = ""; }; + BFB1FC8C927909529E1917BB884F5D65 /* TDMobRiskSpaceInfo.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = TDMobRiskSpaceInfo.m; path = TrustDecision/Classes/Collect/Info/TDMobRiskSpaceInfo.m; sourceTree = ""; }; + C0298855D08D828FC69889FC243C0848 /* StringEncoding+Alamofire.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "StringEncoding+Alamofire.swift"; path = "Source/Extensions/StringEncoding+Alamofire.swift"; sourceTree = ""; }; + C0B30FED051A553715ECA1AC0ECF2A33 /* ResponseSerialization.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ResponseSerialization.swift; path = Source/Features/ResponseSerialization.swift; sourceTree = ""; }; + C1DD10AFBE5209EB9F17651B72CCF910 /* ECPublicKey.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ECPublicKey.swift; path = Sources/CryptorECC/ECPublicKey.swift; sourceTree = ""; }; + C21A3C218E21F198893329B25487B128 /* URLEncodedFormEncoder.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = URLEncodedFormEncoder.swift; path = Source/Features/URLEncodedFormEncoder.swift; sourceTree = ""; }; + C3270135E3419F33C49DE89845D175F9 /* SwiftJWT-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "SwiftJWT-umbrella.h"; sourceTree = ""; }; + C43B179EF14EB33B4AAED7779901F7F7 /* TDMobRiskIdentifierInfo.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = TDMobRiskIdentifierInfo.m; path = TrustDecision/Classes/Collect/Info/TDMobRiskIdentifierInfo.m; sourceTree = ""; }; + C43BC732DE0BE391AC8584D4D3D073B5 /* TDMobRiskOSInfo.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = TDMobRiskOSInfo.h; path = TrustDecision/Classes/Collect/Info/TDMobRiskOSInfo.h; sourceTree = ""; }; + C4F65B2DAF529F5A3BA936F019B9319A /* Locks.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Locks.swift; path = Sources/Logging/Locks.swift; sourceTree = ""; }; + C6B48ECD2D14D6E16DDF3CB731D6480D /* BlueCryptor.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = BlueCryptor.debug.xcconfig; sourceTree = ""; }; + C73756698B68F4AFF41B3FFF9DBA4251 /* VerifierAlgorithm.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = VerifierAlgorithm.swift; path = Sources/SwiftJWT/VerifierAlgorithm.swift; sourceTree = ""; }; + C77EF3C4CA522913D103AE9599FC3CC7 /* DownloadRequest.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = DownloadRequest.swift; path = Source/Core/DownloadRequest.swift; sourceTree = ""; }; + C7EA940210EB25D87E6D6F406E35DD53 /* EllipticCurve.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = EllipticCurve.swift; path = Sources/CryptorECC/EllipticCurve.swift; sourceTree = ""; }; + C8485BEA233D326789838FC6128E22AD /* SSLPointerTricks.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SSLPointerTricks.swift; path = Sources/Cryptor/SSLPointerTricks.swift; sourceTree = ""; }; + C8B0653202020E8A7231A4897466FB81 /* ClaimsMicroProfile.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ClaimsMicroProfile.swift; path = Sources/SwiftJWT/ClaimsExamples/ClaimsMicroProfile.swift; sourceTree = ""; }; + C8FFBC53FB844D0960B1114D23CF14D0 /* Result+Alamofire.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "Result+Alamofire.swift"; path = "Source/Extensions/Result+Alamofire.swift"; sourceTree = ""; }; + C9E5E7CDE2913E6028137FEF15DC8895 /* CryptorRSAUtilities.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CryptorRSAUtilities.swift; path = Sources/CryptorRSA/CryptorRSAUtilities.swift; sourceTree = ""; }; + CB0ED87C259D986A05C2DCC593F9D6D3 /* SessionDelegate.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SessionDelegate.swift; path = Source/Core/SessionDelegate.swift; sourceTree = ""; }; + CBAA6599A306CC8A82E45B827837EEDC /* RSAKeyType.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RSAKeyType.swift; path = Sources/SwiftJWT/RSAKeyType.swift; sourceTree = ""; }; + CC1DF16C12A92AF271A53666D9EE5CDE /* DispatchQueue+Alamofire.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "DispatchQueue+Alamofire.swift"; path = "Source/Extensions/DispatchQueue+Alamofire.swift"; sourceTree = ""; }; + CC8269303CF6762EC44FE4C80EAD89D7 /* BlueRSA.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = BlueRSA.release.xcconfig; sourceTree = ""; }; + CD850EA07C378A4E02B1E71ABCD81ABF /* UploadRequest.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = UploadRequest.swift; path = Source/Core/UploadRequest.swift; sourceTree = ""; }; + CE28C75C985DCD7C9856DC03D5DB4C86 /* SSLPointerTricks.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SSLPointerTricks.swift; path = Sources/CryptorECC/SSLPointerTricks.swift; sourceTree = ""; }; + CE6068C90EA6887140053C17E77A6178 /* BlueRSA.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BlueRSA.swift; path = Sources/SwiftJWT/BlueRSA.swift; sourceTree = ""; }; + CFE372985A232CD8C5825DC8943D6944 /* SwiftJWT.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = SwiftJWT.release.xcconfig; sourceTree = ""; }; + D07C9E9E7C28A5395314FCF71FFB6C97 /* ECSignable.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ECSignable.swift; path = Sources/CryptorECC/ECSignable.swift; sourceTree = ""; }; + D0EBA2D68F16B6B7545ABA33971F51B1 /* TDMobRiskBaseInfo.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = TDMobRiskBaseInfo.m; path = TrustDecision/Classes/Collect/Info/Base/TDMobRiskBaseInfo.m; sourceTree = ""; }; + D524CCF7B0C79F5EAED147491AA95A2E /* Pods-AIGrammarTests.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = "Pods-AIGrammarTests.modulemap"; sourceTree = ""; }; + D529EEFB86C3A6BE5BCBBF53F43F0B94 /* SwiftJWT-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "SwiftJWT-Info.plist"; sourceTree = ""; }; + D5E011D8AD8603675BA8A5634EC21B9B /* SwiftJWT */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = SwiftJWT; path = SwiftJWT.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + D5EB01D39F86EDF150078E43C92619E1 /* Protected.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Protected.swift; path = Source/Core/Protected.swift; sourceTree = ""; }; + D5F8F15D16473C9044C70AE5DFDE5C5F /* Pods-AIGrammarTests-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-AIGrammarTests-dummy.m"; sourceTree = ""; }; + DAADEE493105DA2C10DA9D20A0146483 /* Validation.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Validation.swift; path = Source/Features/Validation.swift; sourceTree = ""; }; + DB2D26CA5BC68099012CEC8C47BB5F71 /* Pods-AIGrammarTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-AIGrammarTests.debug.xcconfig"; sourceTree = ""; }; + DB5A71FC79A73135DF7400B009BF4B72 /* ClosureAliases.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ClosureAliases.swift; path = Sources/KituraContracts/ClosureAliases.swift; sourceTree = ""; }; + DBA9A11E7F68C23FF6F46981F796B1A5 /* Data+Extensions.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "Data+Extensions.swift"; path = "Sources/CryptorECC/Data+Extensions.swift"; sourceTree = ""; }; + DBE29F8D97EBC95C31D5B7F1DC7CDA83 /* DataRequest.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = DataRequest.swift; path = Source/Core/DataRequest.swift; sourceTree = ""; }; + DCEF6AD6EA12C622897B5A150809D31C /* Pods-AIGrammar-AIGrammarUITests-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-AIGrammar-AIGrammarUITests-dummy.m"; sourceTree = ""; }; + DE478042A493AEAE7D4406796D54096A /* OperationQueue+Alamofire.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "OperationQueue+Alamofire.swift"; path = "Source/Extensions/OperationQueue+Alamofire.swift"; sourceTree = ""; }; + DE4EF9C2E7BB71F10E9802BEC52C110C /* SwiftyBeaver.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SwiftyBeaver.swift; path = Sources/SwiftyBeaver.swift; sourceTree = ""; }; + DF4A0058E84C6F5CF1BAD8913C6C9B8F /* TDMobRiskCpuInfo.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = TDMobRiskCpuInfo.h; path = TrustDecision/Classes/Collect/Info/TDMobRiskCpuInfo.h; sourceTree = ""; }; + DFFFBEA8078EEA5D813090CB65321D32 /* BlueCryptor-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "BlueCryptor-Info.plist"; sourceTree = ""; }; + E04D9067664BC83DC94186A11A8804B8 /* Pods-AIGrammar-AIGrammarUITests-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-AIGrammar-AIGrammarUITests-acknowledgements.markdown"; sourceTree = ""; }; + E14DC8B22552D898C97C440275A962B2 /* SSLPointerTricks.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SSLPointerTricks.swift; path = Sources/CryptorRSA/SSLPointerTricks.swift; sourceTree = ""; }; + E202407A4414327A583F639E2AC5175C /* JWTVerifier.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = JWTVerifier.swift; path = Sources/SwiftJWT/JWTVerifier.swift; sourceTree = ""; }; + E2FC465426FC711458050969DBA6745B /* Alamofire-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Alamofire-Info.plist"; sourceTree = ""; }; + E407CE80883A3D49F9FC8077A3BD2E00 /* BlueECC-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "BlueECC-prefix.pch"; sourceTree = ""; }; + E47125F8E2493F8FAED57E57649F552A /* LoggerAPI.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = LoggerAPI.modulemap; sourceTree = ""; }; + E50A1F879186C0C8BF11EE0F3811B6EE /* SwiftyBeaver-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "SwiftyBeaver-dummy.m"; sourceTree = ""; }; + E6C93D5561E5331B2D8CF02C657D306E /* BodyEncoder.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BodyEncoder.swift; path = Sources/KituraContracts/BodyEncoder.swift; sourceTree = ""; }; + E759574BAAB768A64F21FCBFD9C9C07A /* BlueECC-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "BlueECC-dummy.m"; sourceTree = ""; }; + EA07ACC7BF1A7582EA98FA3E3E93D5BD /* Pods-AIGrammar-AIGrammarUITests-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-AIGrammar-AIGrammarUITests-umbrella.h"; sourceTree = ""; }; + EA48B294C8FB72B6E2E564FF293A8ACA /* WebSocketRequest.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = WebSocketRequest.swift; path = Source/Core/WebSocketRequest.swift; sourceTree = ""; }; + EB136D877786EA9186E0D70824F64124 /* Notifications.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Notifications.swift; path = Source/Core/Notifications.swift; sourceTree = ""; }; + EB27254D562084F118968075D5089AF4 /* ECDecryptable.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ECDecryptable.swift; path = Sources/CryptorECC/ECDecryptable.swift; sourceTree = ""; }; + ECCAC82F36F378492C29E5A7F9F46054 /* TDMobRiskTimeInfo.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = TDMobRiskTimeInfo.m; path = TrustDecision/Classes/Collect/Info/TDMobRiskTimeInfo.m; sourceTree = ""; }; + EDF80F042B66695A034D41F43F13E296 /* Cryptor.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Cryptor.swift; path = Sources/Cryptor/Cryptor.swift; sourceTree = ""; }; + EEF32F0849D37EA29405C9A49E0CA868 /* CryptorRSADigest.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CryptorRSADigest.swift; path = Sources/CryptorRSA/CryptorRSADigest.swift; sourceTree = ""; }; + F159560B6CF5A07FB73DEE950C5DA525 /* BlueECC-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "BlueECC-Info.plist"; sourceTree = ""; }; + F1F4E4377F64BC660B42EC1EEC039857 /* Pods-AIGrammar-AIGrammarUITests */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = "Pods-AIGrammar-AIGrammarUITests"; path = Pods_AIGrammar_AIGrammarUITests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + F5203D713011FBCEAA2EAD0C75373B89 /* TDMobRiskIdCalculator.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = TDMobRiskIdCalculator.h; path = TrustDecision/Classes/Core/TDMobRiskIdCalculator.h; sourceTree = ""; }; + F576D3A881CB8F727F2496DEE7B942D3 /* Logger.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Logger.swift; path = Sources/LoggerAPI/Logger.swift; sourceTree = ""; }; + F5F737E00583E80ADCB86FC6D15652CB /* Logging-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Logging-Info.plist"; sourceTree = ""; }; + F60A6F6866F5C7582FE80D041C0B6C61 /* SwiftyBeaver-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "SwiftyBeaver-umbrella.h"; sourceTree = ""; }; + F909BC45DBBB78625AF9DA9AA3ED368E /* Pods-AIGrammar.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-AIGrammar.release.xcconfig"; sourceTree = ""; }; + FAF9946AEACD96D9BA9F39482C7FE96A /* Alamofire.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = Alamofire.debug.xcconfig; sourceTree = ""; }; + FCA13C5342DCE444EA5A16641E18A370 /* AlamofireExtended.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AlamofireExtended.swift; path = Source/Features/AlamofireExtended.swift; sourceTree = ""; }; + FD0CE05D5D076B1B5190EE5DF97FD54E /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS14.0.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; + FD56BD79AE646D1F70ACBC198957FAB3 /* TrustDecision.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = TrustDecision.modulemap; sourceTree = ""; }; + FDEF713C27A68A8296E587E26E9FEB2E /* Extensions.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Extensions.swift; path = Sources/Extensions.swift; sourceTree = ""; }; + FEA8A1FBC489607A393CFD60E70FFB94 /* TrustDecision.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = TrustDecision.release.xcconfig; sourceTree = ""; }; + FF4614067693F95E7F86545487487790 /* Logging-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Logging-dummy.m"; sourceTree = ""; }; + FF5131D386EC321F08E42AD63FCD4508 /* Header.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Header.swift; path = Sources/SwiftJWT/Header.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 0FEBE4C79C58CB2EBAC0CF9528EB2DD2 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 93771D67060533037C5E36F89CA0E6C5 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 1E9D0BDEE022F2950BB0D08091728A8D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 237BB5C061F7CF700102421C6B742AEF /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 1FBC55F931045E93F4D1A4D3412A0930 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + AF43CE9A8089BBA86874EE26CC38684E /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 2DF8CA787A2B783D265EBEC91905867B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 328D9762612ED966D12B6503AE8C8720 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 4B1ED39638712D17BE9FBCAAFEF1468E /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 50D7C24C6C7A2421110CA5837D08E0FF /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 0894595B90BB9BE837CB328F57255E2F /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 93691C7BC1F0D78FC3B0061CF5EED1FE /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + F84CE1CC51A9AB580C183782A91BDD1A /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + A5C342523C3577E7122B4B43929BA8F3 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 3B901DF82C6576019330391D0F470B31 /* CFNetwork.framework in Frameworks */, + 84BEB9E439780B1E0DEF56459E3D3352 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + A7BB9D329BF0AFA41BF2DD4584B581B4 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 1D34A567EB1420E9C030E3EEF0C71521 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + B06267CBEDC638D9B908833ECAF3E195 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 7326CA047952CBD38697917BAB8E0A5E /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + C4ACB3447ACC697712FB82D1F826EA6D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 552A488AA665EDA3778FD07705402F43 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D0C260E31F71D5926EB4D0B93F465C83 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D10B4517EBECE50110E22B22BB649A68 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 86806FB25A08BE03CD091F292BB7C554 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DC4FF28FDC9A6B9232A590419F6341D3 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 126BC665C3D2B5B2FBE7E50258E0F2A2 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + E8641C352248614128D0F0AA4467DA34 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + CB2A998AD60A9C8653AB550B8411C2E5 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 0390D584E8F5229323BF9DBC007B4CF0 /* Logging */ = { + isa = PBXGroup; + children = ( + C4F65B2DAF529F5A3BA936F019B9319A /* Locks.swift */, + 73E1D15B67083210B44DB207A1D2C6F2 /* Logging.swift */, + A6DC35251A3740B925DB25F5D8177B0C /* LogHandler.swift */, + 866FE1DA01628F397D586C859C618C1D /* Support Files */, + ); + name = Logging; + path = Logging; + sourceTree = ""; + }; + 1628BF05B4CAFDCC3549A101F5A10A17 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 61C8CC330A5CA84DA2F5F1D32AB07069 /* iOS */, + ); + name = Frameworks; + sourceTree = ""; + }; + 2FE00B7B784844F6E9C1827244881949 /* Alamofire */ = { + isa = PBXGroup; + children = ( + BC691038C9D3660690548A6D2399A977 /* AFError.swift */, + 5EB192AE20CEDC37883201E33976FE0D /* Alamofire.swift */, + FCA13C5342DCE444EA5A16641E18A370 /* AlamofireExtended.swift */, + 53143B0A3BDE20B977A2AA6DB503D7CC /* AuthenticationInterceptor.swift */, + B3810ED50A59A43DDB58559CB0A2DD95 /* CachedResponseHandler.swift */, + 29C7CEDCBF288A6C6917F7EE85028DC8 /* Combine.swift */, + 8B5F75FE6085A717CA97C96457789561 /* Concurrency.swift */, + DBE29F8D97EBC95C31D5B7F1DC7CDA83 /* DataRequest.swift */, + 4E331842372E70735184CC7D193F8A09 /* DataStreamRequest.swift */, + CC1DF16C12A92AF271A53666D9EE5CDE /* DispatchQueue+Alamofire.swift */, + C77EF3C4CA522913D103AE9599FC3CC7 /* DownloadRequest.swift */, + 444BE0852EDA865B3FF167E5A5C15975 /* EventMonitor.swift */, + 662C410BFDFE5AC1417F6C89C711FE1F /* HTTPHeaders.swift */, + B6ADE50A624DA0A490DB11BF26D9ADCF /* HTTPMethod.swift */, + 6E8D97359B7E3E8D1F506B517B4FD4DE /* MultipartFormData.swift */, + 64FC8A2FF9CC8B36C3CFA8B354418A1F /* MultipartUpload.swift */, + 3F168D4AB45E86068B5001DF872CD260 /* NetworkReachabilityManager.swift */, + EB136D877786EA9186E0D70824F64124 /* Notifications.swift */, + DE478042A493AEAE7D4406796D54096A /* OperationQueue+Alamofire.swift */, + 2A2B2BE5F08F2F77B5EB5CFD5CC41606 /* ParameterEncoder.swift */, + 929730BDA29BF52A91A72E2C4EDFCE2B /* ParameterEncoding.swift */, + D5EB01D39F86EDF150078E43C92619E1 /* Protected.swift */, + 5718E48F5BA586D70BD7789BF8F08A26 /* RedirectHandler.swift */, + 876E0727CCD8BBCDA80A05BFAD527FBB /* Request.swift */, + 2A19D5C20FB97FA8EF39C799393B005F /* RequestCompression.swift */, + 1549056E8195C0EB0F38EB6923E4866A /* RequestInterceptor.swift */, + 630B69516E15545819AC3D4920444608 /* RequestTaskMap.swift */, + 5FA85E2AA20E77DB8B3E326375D1A365 /* Response.swift */, + C0B30FED051A553715ECA1AC0ECF2A33 /* ResponseSerialization.swift */, + C8FFBC53FB844D0960B1114D23CF14D0 /* Result+Alamofire.swift */, + 7300F85D79226F3B2A89B62CF0A570B0 /* RetryPolicy.swift */, + 8A06C1FA4D99468574D3E158A255DFD0 /* ServerTrustEvaluation.swift */, + 6811741973DBDA55D9F914A30DDCF00B /* Session.swift */, + CB0ED87C259D986A05C2DCC593F9D6D3 /* SessionDelegate.swift */, + C0298855D08D828FC69889FC243C0848 /* StringEncoding+Alamofire.swift */, + CD850EA07C378A4E02B1E71ABCD81ABF /* UploadRequest.swift */, + 84069E63D3914B1B386F2077DF5CC027 /* URLConvertible+URLRequestConvertible.swift */, + C21A3C218E21F198893329B25487B128 /* URLEncodedFormEncoder.swift */, + 78CDD5BA5983187CCFE2D688079F203A /* URLRequest+Alamofire.swift */, + 3F4B57E4C4F825B0ADD7FEA34C7519BE /* URLSessionConfiguration+Alamofire.swift */, + DAADEE493105DA2C10DA9D20A0146483 /* Validation.swift */, + EA48B294C8FB72B6E2E564FF293A8ACA /* WebSocketRequest.swift */, + 9704B8E602FC4D7B0922A90A0766908A /* Resources */, + 43C65DB0755EA205C12A72FA66A99F13 /* Support Files */, + ); + name = Alamofire; + path = Alamofire; + sourceTree = ""; + }; + 317623756CBD253F1DE42434E67D024D /* Support Files */ = { + isa = PBXGroup; + children = ( + B53E2E24B42EDD2D1DCD7190097B67C3 /* ResourceBundle-SwiftyBeaver-SwiftyBeaver-Info.plist */, + 778371035164E03619A2E38FA577B03C /* SwiftyBeaver.modulemap */, + E50A1F879186C0C8BF11EE0F3811B6EE /* SwiftyBeaver-dummy.m */, + 18F06465805140E928122396070E55D0 /* SwiftyBeaver-Info.plist */, + 9D5DD9C5C9F76F2EE09D8BAAC659B245 /* SwiftyBeaver-prefix.pch */, + F60A6F6866F5C7582FE80D041C0B6C61 /* SwiftyBeaver-umbrella.h */, + 5909292CACF3FD0B79E4348B60B024AD /* SwiftyBeaver.debug.xcconfig */, + BC198EEAA48B17C1A01B2FD7CF631C45 /* SwiftyBeaver.release.xcconfig */, + ); + name = "Support Files"; + path = "../Target Support Files/SwiftyBeaver"; + sourceTree = ""; + }; + 3185EC4C2A8A3279753F63E94AC87E7D /* SwiftJWT */ = { + isa = PBXGroup; + children = ( + 21EDE1DEFEE5948C0B9DEAAE808439AC /* BlueECDSA.swift */, + 3367FC7EC454EEC5AD75D267FF6A084A /* BlueHMAC.swift */, + CE6068C90EA6887140053C17E77A6178 /* BlueRSA.swift */, + 48C5A8279AE18BA47E526CEB57CF30C7 /* Claims.swift */, + C8B0653202020E8A7231A4897466FB81 /* ClaimsMicroProfile.swift */, + 92E49E42D6B37B7245374BEAE591800D /* ClaimsOpenID.swift */, + 8EA4013B0A0049D3A724191668E63D72 /* ClaimsStandardJWT.swift */, + 8EA950F020634AAE684368F0E0070BAD /* Data+Base64URLEncoded.swift */, + FF5131D386EC321F08E42AD63FCD4508 /* Header.swift */, + 6C133093FE42E10E4DDDFCFEEFC488E3 /* JWT.swift */, + 1D134F8273D13F4CD72FA2E7751D14D0 /* JWTDecoder.swift */, + 491F1F10F433DABACAF796F3CEAC28AD /* JWTEncoder.swift */, + 99F092B1CB4A09E75ED6552E26797F18 /* JWTError.swift */, + BE93FFB7350B277DF57759B57510EF75 /* JWTSigner.swift */, + E202407A4414327A583F639E2AC5175C /* JWTVerifier.swift */, + 89C272A5253A1E7AB3A70F666DEC3E2B /* NoneAlgorithm.swift */, + CBAA6599A306CC8A82E45B827837EEDC /* RSAKeyType.swift */, + 22BB5B783523427143025AE99247D2AA /* SignerAlgorithm.swift */, + 95AAEE18597743F2A43E1193696D1285 /* ValidateClaimsResult.swift */, + C73756698B68F4AFF41B3FFF9DBA4251 /* VerifierAlgorithm.swift */, + 4DE7F6B14ECDEF8E239072248F97510C /* Support Files */, + ); + name = SwiftJWT; + path = SwiftJWT; + sourceTree = ""; + }; + 385C7ADE5A837971B7ABB60C3191F603 /* Pods-AIGrammar */ = { + isa = PBXGroup; + children = ( + 2D48C3FEA8F9B5DF83F5A9499AC8B49A /* Pods-AIGrammar.modulemap */, + 1CBD7D1B3E51C1259905FD7BE0CB911A /* Pods-AIGrammar-acknowledgements.markdown */, + A314EC770E60E516BCD8B61346D6878F /* Pods-AIGrammar-acknowledgements.plist */, + 4783076A010B9367D74FA8E67F7F9DF4 /* Pods-AIGrammar-dummy.m */, + BD2DF58C5BB328219EABF947ED5CD389 /* Pods-AIGrammar-frameworks.sh */, + 0772105F0C37E2D5ECBE60425F8CB1E5 /* Pods-AIGrammar-Info.plist */, + 2B4185A37C0478BC369029DA98B6BC1E /* Pods-AIGrammar-umbrella.h */, + 881A178E10B4EB1EAE869F754E904097 /* Pods-AIGrammar.debug.xcconfig */, + F909BC45DBBB78625AF9DA9AA3ED368E /* Pods-AIGrammar.release.xcconfig */, + ); + name = "Pods-AIGrammar"; + path = "Target Support Files/Pods-AIGrammar"; + sourceTree = ""; + }; + 388536146D98CFA2046FCC7405E520E7 /* LoggerAPI */ = { + isa = PBXGroup; + children = ( + F576D3A881CB8F727F2496DEE7B942D3 /* Logger.swift */, + 81FFABF1456793AD0A7C93FA6A8B7B67 /* Support Files */, + ); + name = LoggerAPI; + path = LoggerAPI; + sourceTree = ""; + }; + 41F556A8B2F444A67A6178473BEBF10F /* TrustDecision */ = { + isa = PBXGroup; + children = ( + 12F26A1570034B67193B9CCFEBF5F631 /* TDMobRisk.h */, + 6A200B3395C57BCAE75DC005A6FC4D68 /* TDMobRiskAPIHelper.h */, + B04EE592F79E079A1F8015AE4B06E735 /* TDMobRiskAPIHelper.m */, + 80AE637320B93B5141AC353887787E5C /* TDMobRiskAppInfo.h */, + A8338BDCE31B41B2160BCA0B55406356 /* TDMobRiskAppInfo.m */, + 4D184B3B83E23958D53A660A885302A5 /* TDMobRiskBaseInfo.h */, + D0EBA2D68F16B6B7545ABA33971F51B1 /* TDMobRiskBaseInfo.m */, + 340C6CC65E0DA5634F65CD153F9A91E3 /* TDMobRiskCalculator.h */, + 3EB880D057E12D4BC1F6807EEC268767 /* TDMobRiskCalculator.m */, + 534C5BE15FDE9098DA9C216919720B48 /* TDMobRiskCollector.h */, + 3CD33B5467C85B9FFD9A5932A2903196 /* TDMobRiskCollector.m */, + DF4A0058E84C6F5CF1BAD8913C6C9B8F /* TDMobRiskCpuInfo.h */, + 4EBDCF010C819FC8CC703DAFFA5A2AF9 /* TDMobRiskCpuInfo.m */, + 287DB6BBB76AC431B7E798DCE29214F3 /* TDMobRiskDeviceInfo.h */, + 3A3FD2CD4F40F02A905467ADF425F1DF /* TDMobRiskDeviceInfo.m */, + 243E07B2A753D83022AF70810CE0F51C /* TDMobRiskDeviceStatusInfo.h */, + 68071DC69EBD9745A4D6FE436BEE0B60 /* TDMobRiskDeviceStatusInfo.m */, + 308F95F04AB38A2160C97B9B7499C136 /* TDMobRiskEncodeHelper.h */, + 23D00A9BC39B5AE73B06BC2CF41C8570 /* TDMobRiskEncodeHelper.m */, + 62C3820AACAF7B3112CEB93D9C7FA0A2 /* TDMobRiskHeader.h */, + F5203D713011FBCEAA2EAD0C75373B89 /* TDMobRiskIdCalculator.h */, + 72EC99CD19E57499D369D9DF538B32DB /* TDMobRiskIdCalculator.m */, + A299D2B42A5AC13BC25A4FDC8C3677C1 /* TDMobRiskIdentifierInfo.h */, + C43B179EF14EB33B4AAED7779901F7F7 /* TDMobRiskIdentifierInfo.m */, + 1607C1D0D659D1D0245D98DEC664ADE5 /* TDMobRiskKeychainsHelper.h */, + 54BF6577FDC51011E96E713B07746713 /* TDMobRiskKeychainsHelper.m */, + 832CDE9FCBDE0ACA627FBDFDFC05EB04 /* TDMobRiskManager.h */, + 3FC1DF34E1F489D064A233732A348583 /* TDMobRiskManager.m */, + C43BC732DE0BE391AC8584D4D3D073B5 /* TDMobRiskOSInfo.h */, + 451F228C8AA303567EF3D34720EEF26D /* TDMobRiskOSInfo.m */, + 869244A039B4AEF1173344F937467FB2 /* TDMobRiskSafeDictionary.h */, + 0062E35E61C1EA1458F0EC82042EAAEE /* TDMobRiskSafeDictionary.m */, + 1BE6220B98F71C3CF0C19656186F4C64 /* TDMobRiskSpaceInfo.h */, + BFB1FC8C927909529E1917BB884F5D65 /* TDMobRiskSpaceInfo.m */, + 8622A3C999AC771B6099F507E898C79C /* TDMobRiskTimeInfo.h */, + ECCAC82F36F378492C29E5A7F9F46054 /* TDMobRiskTimeInfo.m */, + AF637DF91C957CE4E44412632BE1FDFE /* Support Files */, + ); + name = TrustDecision; + path = TrustDecision; + sourceTree = ""; + }; + 43C65DB0755EA205C12A72FA66A99F13 /* Support Files */ = { + isa = PBXGroup; + children = ( + 3A2D7EA43DFC7B5C2800C5DE10AB0478 /* Alamofire.modulemap */, + 6B6CC99473EA621E30F92D9FC480D4F0 /* Alamofire-dummy.m */, + E2FC465426FC711458050969DBA6745B /* Alamofire-Info.plist */, + 474BCF3BC2CEFA443D99D91BB396A148 /* Alamofire-prefix.pch */, + 0D1F5608163D81BC4C86A365F0B1C200 /* Alamofire-umbrella.h */, + FAF9946AEACD96D9BA9F39482C7FE96A /* Alamofire.debug.xcconfig */, + 10D8544DE75F5F119B166CEBBFB05BFE /* Alamofire.release.xcconfig */, + 89CA742EB8DB549ABC50A25AB729AFBD /* ResourceBundle-Alamofire-Alamofire-Info.plist */, + ); + name = "Support Files"; + path = "../Target Support Files/Alamofire"; + sourceTree = ""; + }; + 4B85750E14F5A3F382E44102FDBF8B5E /* Support Files */ = { + isa = PBXGroup; + children = ( + 4087105BBBFA96A7006EF61D099C4D08 /* KituraContracts.modulemap */, + B24E52BEF3A28B56D25E7D0C7CE1BFFF /* KituraContracts-dummy.m */, + 1D7A7899F0C30F86BFF25B50766DE489 /* KituraContracts-Info.plist */, + 31EB261ADE24930729954E9FCFC880EB /* KituraContracts-prefix.pch */, + BC37E03AD9D5C7753C1EC5D1C8B64BBA /* KituraContracts-umbrella.h */, + 22AC8CED52EAAE1BC6F8494CA8A43050 /* KituraContracts.debug.xcconfig */, + 9A3E80A5CCD67032BA2B4420062D4792 /* KituraContracts.release.xcconfig */, + ); + name = "Support Files"; + path = "../Target Support Files/KituraContracts"; + sourceTree = ""; + }; + 4C0BABA1E65F36934BAD0E2CAF1B1EC2 /* BlueRSA */ = { + isa = PBXGroup; + children = ( + 39FF1DFC24CE15B6CA5E22F4711BB076 /* CryptorRSA.swift */, + 0703E706DAB6E667510EB7FE90232848 /* CryptorRSAConstants.swift */, + EEF32F0849D37EA29405C9A49E0CA868 /* CryptorRSADigest.swift */, + B763964583DBEC6EEB7C4A3CBBAE54E1 /* CryptorRSAErrors.swift */, + 1CFF0F226982FD136D312688E5249F52 /* CryptorRSAKey.swift */, + C9E5E7CDE2913E6028137FEF15DC8895 /* CryptorRSAUtilities.swift */, + AFC6F3EFF82F3B3DA129EEA58A2889DF /* Data+Extensions.swift */, + E14DC8B22552D898C97C440275A962B2 /* SSLPointerTricks.swift */, + B28D2E0656E2302526F6ED19EEBC40F8 /* Support Files */, + ); + name = BlueRSA; + path = BlueRSA; + sourceTree = ""; + }; + 4DE7F6B14ECDEF8E239072248F97510C /* Support Files */ = { + isa = PBXGroup; + children = ( + 98C21BC262D1B2A150C9130FB95CB482 /* SwiftJWT.modulemap */, + 5538703EE7D927436BA0AACD7DEBC0E5 /* SwiftJWT-dummy.m */, + D529EEFB86C3A6BE5BCBBF53F43F0B94 /* SwiftJWT-Info.plist */, + 8E3678944C2CAE232380D0899E593E47 /* SwiftJWT-prefix.pch */, + C3270135E3419F33C49DE89845D175F9 /* SwiftJWT-umbrella.h */, + 49DC68DCD598D7E998E7F413F7175874 /* SwiftJWT.debug.xcconfig */, + CFE372985A232CD8C5825DC8943D6944 /* SwiftJWT.release.xcconfig */, + ); + name = "Support Files"; + path = "../Target Support Files/SwiftJWT"; + sourceTree = ""; + }; + 5FF9972ED6294E7278D3EF52E2F2A027 /* Pods */ = { + isa = PBXGroup; + children = ( + 2FE00B7B784844F6E9C1827244881949 /* Alamofire */, + E2839639081D894943A6AFFD532BAC41 /* BlueCryptor */, + 7B98FF01B4259B135D6889D4C3DBEC06 /* BlueECC */, + 4C0BABA1E65F36934BAD0E2CAF1B1EC2 /* BlueRSA */, + 746B6E3644ABC34F3A76954302992AC5 /* KituraContracts */, + 388536146D98CFA2046FCC7405E520E7 /* LoggerAPI */, + 0390D584E8F5229323BF9DBC007B4CF0 /* Logging */, + 3185EC4C2A8A3279753F63E94AC87E7D /* SwiftJWT */, + E6CFDE1E47579A328272A2C4E5A09BF4 /* SwiftyBeaver */, + 41F556A8B2F444A67A6178473BEBF10F /* TrustDecision */, + ); + name = Pods; + sourceTree = ""; + }; + 61C8CC330A5CA84DA2F5F1D32AB07069 /* iOS */ = { + isa = PBXGroup; + children = ( + 8D3D57353834825F7B52B816066B7789 /* CFNetwork.framework */, + FD0CE05D5D076B1B5190EE5DF97FD54E /* Foundation.framework */, + ); + name = iOS; + sourceTree = ""; + }; + 723399BFA828F5FAB10FF41469ACC8D9 /* Pods-AIGrammarTests */ = { + isa = PBXGroup; + children = ( + D524CCF7B0C79F5EAED147491AA95A2E /* Pods-AIGrammarTests.modulemap */, + 17361A3F72DF686B73CCA2EA43FB253F /* Pods-AIGrammarTests-acknowledgements.markdown */, + A6F5720C1061539D34EF7E105D07E86B /* Pods-AIGrammarTests-acknowledgements.plist */, + D5F8F15D16473C9044C70AE5DFDE5C5F /* Pods-AIGrammarTests-dummy.m */, + 9F644688D2AEFF5768BE065C6536B7BB /* Pods-AIGrammarTests-Info.plist */, + 33DE377395ED69A94A9504A142DB6D62 /* Pods-AIGrammarTests-umbrella.h */, + DB2D26CA5BC68099012CEC8C47BB5F71 /* Pods-AIGrammarTests.debug.xcconfig */, + 2933AF5131B9637CCCB2E3A5EEF86C75 /* Pods-AIGrammarTests.release.xcconfig */, + ); + name = "Pods-AIGrammarTests"; + path = "Target Support Files/Pods-AIGrammarTests"; + sourceTree = ""; + }; + 746B6E3644ABC34F3A76954302992AC5 /* KituraContracts */ = { + isa = PBXGroup; + children = ( + 80BDD3D1E3C81B099102A2E4EED59D63 /* BodyDecoder.swift */, + E6C93D5561E5331B2D8CF02C657D306E /* BodyEncoder.swift */, + 8B713DF1DB67473D5E1963376E430165 /* BodyFormat.swift */, + DB5A71FC79A73135DF7400B009BF4B72 /* ClosureAliases.swift */, + 1CD686ADC4CB81B528886B963B9C1828 /* Coder.swift */, + 4DB7622F2FD4E0C696DB0820105830EF /* Contracts.swift */, + 68D536CC3C636F95B3293EC0D32B0E2E /* Extensions.swift */, + 989EE2B8BC4C8CC05BB5728892FAE8F2 /* QueryDecoder.swift */, + 04152FFEF08BB6EED23BE2BC8B5ABAD6 /* QueryEncoder.swift */, + 4B85750E14F5A3F382E44102FDBF8B5E /* Support Files */, + ); + name = KituraContracts; + path = KituraContracts; + sourceTree = ""; + }; + 7B98FF01B4259B135D6889D4C3DBEC06 /* BlueECC */ = { + isa = PBXGroup; + children = ( + 5F268A015D32AC8A5EF256EE1FB4F275 /* ASN1.swift */, + DBA9A11E7F68C23FF6F46981F796B1A5 /* Data+Extensions.swift */, + EB27254D562084F118968075D5089AF4 /* ECDecryptable.swift */, + 33755DAC4C8B85CF949B377E66699E4E /* ECEncryptable.swift */, + 8C65A2761A821E52402905728179B0E6 /* ECError.swift */, + 9E9538B10524E3DA3A00132BA6B657ED /* ECPrivateKey.swift */, + C1DD10AFBE5209EB9F17651B72CCF910 /* ECPublicKey.swift */, + D07C9E9E7C28A5395314FCF71FFB6C97 /* ECSignable.swift */, + 06F307D7561CF717981F44FB7F38BA7A /* ECSignature.swift */, + C7EA940210EB25D87E6D6F406E35DD53 /* EllipticCurve.swift */, + CE28C75C985DCD7C9856DC03D5DB4C86 /* SSLPointerTricks.swift */, + 82B397D1604296C8D0286A340E09F786 /* Support Files */, + ); + name = BlueECC; + path = BlueECC; + sourceTree = ""; + }; + 81FFABF1456793AD0A7C93FA6A8B7B67 /* Support Files */ = { + isa = PBXGroup; + children = ( + E47125F8E2493F8FAED57E57649F552A /* LoggerAPI.modulemap */, + 77CDA4F1303C101741D8D884748582E7 /* LoggerAPI-dummy.m */, + 2EEEBED94E28C75C03A4E8BA1B2150C1 /* LoggerAPI-Info.plist */, + 3BEBBF1632E94E587CC6383656FF9731 /* LoggerAPI-prefix.pch */, + B3BEF546883082266053DB6B7FA761FF /* LoggerAPI-umbrella.h */, + B3F3A2726E30CE89E01164BE4896D098 /* LoggerAPI.debug.xcconfig */, + 13DBB992142C26E7749EA1F79E78F2B2 /* LoggerAPI.release.xcconfig */, + ); + name = "Support Files"; + path = "../Target Support Files/LoggerAPI"; + sourceTree = ""; + }; + 82B397D1604296C8D0286A340E09F786 /* Support Files */ = { + isa = PBXGroup; + children = ( + A674E5F2502FEEB09E600793F3CF15CF /* BlueECC.modulemap */, + E759574BAAB768A64F21FCBFD9C9C07A /* BlueECC-dummy.m */, + F159560B6CF5A07FB73DEE950C5DA525 /* BlueECC-Info.plist */, + E407CE80883A3D49F9FC8077A3BD2E00 /* BlueECC-prefix.pch */, + 8F8324F2F461CF1C0D396AFC7B2D17B5 /* BlueECC-umbrella.h */, + 5F46B5BFB9A87CD3FB6D5EFCD2C8DD31 /* BlueECC.debug.xcconfig */, + 9DB8603F150521A9B8621FD6ACE6FAB3 /* BlueECC.release.xcconfig */, + ); + name = "Support Files"; + path = "../Target Support Files/BlueECC"; + sourceTree = ""; + }; + 866FE1DA01628F397D586C859C618C1D /* Support Files */ = { + isa = PBXGroup; + children = ( + AED9455D30C035D76AE0DA62675CAD12 /* Logging.modulemap */, + FF4614067693F95E7F86545487487790 /* Logging-dummy.m */, + F5F737E00583E80ADCB86FC6D15652CB /* Logging-Info.plist */, + 916DA5F53A3E5C39B71F77F53E989430 /* Logging-prefix.pch */, + 7F87309C6423A0A9C50F2DEF99D38554 /* Logging-umbrella.h */, + 2F10E968E0EA4CAA7858662305EF4A7A /* Logging.debug.xcconfig */, + 0B27561A5C6CD6C04D7057C8989055DD /* Logging.release.xcconfig */, + ); + name = "Support Files"; + path = "../Target Support Files/Logging"; + sourceTree = ""; + }; + 9704B8E602FC4D7B0922A90A0766908A /* Resources */ = { + isa = PBXGroup; + children = ( + 3B43DA6F3C13A7961B5F4F241E71032F /* PrivacyInfo.xcprivacy */, + ); + name = Resources; + sourceTree = ""; + }; + AF637DF91C957CE4E44412632BE1FDFE /* Support Files */ = { + isa = PBXGroup; + children = ( + FD56BD79AE646D1F70ACBC198957FAB3 /* TrustDecision.modulemap */, + 48017312199755B883FA88AE24C3F52F /* TrustDecision-dummy.m */, + 6918EEAE564D0A473D5B5A011A21A685 /* TrustDecision-Info.plist */, + 0F0CAB7F2C7DF7A61E1A9CD5D7E12D76 /* TrustDecision-prefix.pch */, + B585A4FDCCFFC4757E3EC966BF9F7A3A /* TrustDecision-umbrella.h */, + 2C9350326F251EA74192A5F7194F7680 /* TrustDecision.debug.xcconfig */, + FEA8A1FBC489607A393CFD60E70FFB94 /* TrustDecision.release.xcconfig */, + ); + name = "Support Files"; + path = "../Target Support Files/TrustDecision"; + sourceTree = ""; + }; + B231CE814E17B12B59DE6A0029E89B3F /* Resources */ = { + isa = PBXGroup; + children = ( + 4CEBE2F64EAD7A1A5D85DDFFB343E060 /* PrivacyInfo.xcprivacy */, + ); + name = Resources; + sourceTree = ""; + }; + B28D2E0656E2302526F6ED19EEBC40F8 /* Support Files */ = { + isa = PBXGroup; + children = ( + 83F6EF98AFBD4D35B938CCD75660D9DA /* BlueRSA.modulemap */, + A99514216510012C970D274771E8287F /* BlueRSA-dummy.m */, + 3628EC52F3791A80D6C9FFEF3899A2A5 /* BlueRSA-Info.plist */, + 2FA1160BC562B21D74917556707CD264 /* BlueRSA-prefix.pch */, + 988D32A7D1801EF8A4BA41D9B2A8FE44 /* BlueRSA-umbrella.h */, + 878A3266C4D7514338A19E8F3F7C3F92 /* BlueRSA.debug.xcconfig */, + CC8269303CF6762EC44FE4C80EAD89D7 /* BlueRSA.release.xcconfig */, + ); + name = "Support Files"; + path = "../Target Support Files/BlueRSA"; + sourceTree = ""; + }; + CC6BBC6A214CCCF3CDE48D467F72DA59 /* Pods-AIGrammar-AIGrammarUITests */ = { + isa = PBXGroup; + children = ( + 86196CD2BF23040E2C37D5AF356CBBC3 /* Pods-AIGrammar-AIGrammarUITests.modulemap */, + E04D9067664BC83DC94186A11A8804B8 /* Pods-AIGrammar-AIGrammarUITests-acknowledgements.markdown */, + 6DEBE158485E8400CD58C4ED18BD52C0 /* Pods-AIGrammar-AIGrammarUITests-acknowledgements.plist */, + DCEF6AD6EA12C622897B5A150809D31C /* Pods-AIGrammar-AIGrammarUITests-dummy.m */, + A4A161573F1639985114A1C9B4133353 /* Pods-AIGrammar-AIGrammarUITests-frameworks.sh */, + 683761A68E578D6A23100651857E9B34 /* Pods-AIGrammar-AIGrammarUITests-Info.plist */, + EA07ACC7BF1A7582EA98FA3E3E93D5BD /* Pods-AIGrammar-AIGrammarUITests-umbrella.h */, + BDC8A99F14C97CA85C839872E2DC482A /* Pods-AIGrammar-AIGrammarUITests.debug.xcconfig */, + BA155A3B8F6E8AA5A625E76BDB822789 /* Pods-AIGrammar-AIGrammarUITests.release.xcconfig */, + ); + name = "Pods-AIGrammar-AIGrammarUITests"; + path = "Target Support Files/Pods-AIGrammar-AIGrammarUITests"; + sourceTree = ""; + }; + CF1408CF629C7361332E53B88F7BD30C = { + isa = PBXGroup; + children = ( + 9D940727FF8FB9C785EB98E56350EF41 /* Podfile */, + 1628BF05B4CAFDCC3549A101F5A10A17 /* Frameworks */, + 5FF9972ED6294E7278D3EF52E2F2A027 /* Pods */, + FCFBB24DB8C3F286420323A960E77398 /* Products */, + E1E1EA3186C88E287AF3A2D619561313 /* Targets Support Files */, + ); + sourceTree = ""; + }; + DEFB895318EBD27A0155D62A88C1986A /* Support Files */ = { + isa = PBXGroup; + children = ( + B944789BECD07D60B5D498BACE7FC050 /* BlueCryptor.modulemap */, + 5F1CA1ABED23EE81D8D58D84BA1E0D46 /* BlueCryptor-dummy.m */, + DFFFBEA8078EEA5D813090CB65321D32 /* BlueCryptor-Info.plist */, + B70B8C024B6F0A02E43F26BC3E31D597 /* BlueCryptor-prefix.pch */, + 13C3816F00015DAD5FC9A1BE64A68E4A /* BlueCryptor-umbrella.h */, + C6B48ECD2D14D6E16DDF3CB731D6480D /* BlueCryptor.debug.xcconfig */, + 98526239ED2F634672C32610933FB8E7 /* BlueCryptor.release.xcconfig */, + ); + name = "Support Files"; + path = "../Target Support Files/BlueCryptor"; + sourceTree = ""; + }; + E1E1EA3186C88E287AF3A2D619561313 /* Targets Support Files */ = { + isa = PBXGroup; + children = ( + 385C7ADE5A837971B7ABB60C3191F603 /* Pods-AIGrammar */, + CC6BBC6A214CCCF3CDE48D467F72DA59 /* Pods-AIGrammar-AIGrammarUITests */, + 723399BFA828F5FAB10FF41469ACC8D9 /* Pods-AIGrammarTests */, + ); + name = "Targets Support Files"; + sourceTree = ""; + }; + E2839639081D894943A6AFFD532BAC41 /* BlueCryptor */ = { + isa = PBXGroup; + children = ( + 5B3858F856AAABA72257963942A66EAB /* Crypto.swift */, + EDF80F042B66695A034D41F43F13E296 /* Cryptor.swift */, + A43B0C746A61DE112316E0B9EAB941CD /* Digest.swift */, + 83090DF6151AD289A7468FB1EFC7AAF9 /* HMAC.swift */, + 4A45D9F225B60F3E1D797AE627ACCEE3 /* KeyDerivation.swift */, + 57EF66504584E6A9C7EB93805DBAB256 /* Random.swift */, + C8485BEA233D326789838FC6128E22AD /* SSLPointerTricks.swift */, + 10D842A0766986149C1F9177445BB967 /* Status.swift */, + 4239DDB5499812B3F70D8891DFBAEC25 /* StreamCryptor.swift */, + 1F9B67C5521BE31868B661965440B6EB /* Updatable.swift */, + A402371D02417A424D88CEBD571105F4 /* Utilities.swift */, + DEFB895318EBD27A0155D62A88C1986A /* Support Files */, + ); + name = BlueCryptor; + path = BlueCryptor; + sourceTree = ""; + }; + E6CFDE1E47579A328272A2C4E5A09BF4 /* SwiftyBeaver */ = { + isa = PBXGroup; + children = ( + 1D05134B7184B60302FA90AA82875594 /* Base64.swift */, + AEE91752539B32E2D44EE738CEF5C757 /* BaseDestination.swift */, + 4FAF1A4CAA1CF22A6859D3CB1795CE28 /* ConsoleDestination.swift */, + FDEF713C27A68A8296E587E26E9FEB2E /* Extensions.swift */, + 9B403C17E2C156687F869C48F8BE97FC /* FileDestination.swift */, + 733081B93BFE4B932A5ADB0F3B4B4084 /* Filter.swift */, + B4E6F491F04DB8DEAE3EBDDA334F9BD4 /* FilterValidator.swift */, + 6FD54C9608C8ED573AFF3057B4A733F5 /* GoogleCloudDestination.swift */, + DE4EF9C2E7BB71F10E9802BEC52C110C /* SwiftyBeaver.swift */, + B231CE814E17B12B59DE6A0029E89B3F /* Resources */, + 317623756CBD253F1DE42434E67D024D /* Support Files */, + ); + name = SwiftyBeaver; + path = SwiftyBeaver; + sourceTree = ""; + }; + FCFBB24DB8C3F286420323A960E77398 /* Products */ = { + isa = PBXGroup; + children = ( + 5D797E9A5C5782CE845840781FA1CC81 /* Alamofire */, + 085DBCE7DD98588B2ED103B1C1F36026 /* Alamofire-Alamofire */, + 0EF2AA497DAF2C29FA3EB06BDE44553F /* BlueCryptor */, + 53D3E35E7EB25621342FA0CA77C0167B /* BlueECC */, + A5CF16B4996DB7D0E4981082FB86ED82 /* BlueRSA */, + B9E276AB0F6AE3009D9751E19C8A0E0F /* KituraContracts */, + A3B85B880B894F1FF5BAFD94E036CC04 /* LoggerAPI */, + A6513E0A6CB60623515716E73996F0F5 /* Logging */, + 16B1B41F43B18E93821D646FAE2330A4 /* Pods-AIGrammar */, + F1F4E4377F64BC660B42EC1EEC039857 /* Pods-AIGrammar-AIGrammarUITests */, + AD6ECF5E77BCA6AC38C6B041223E889D /* Pods-AIGrammarTests */, + D5E011D8AD8603675BA8A5634EC21B9B /* SwiftJWT */, + 7A5F7001277980B8F3EEA75BCD346E53 /* SwiftyBeaver */, + 9215295657AC6ABB8D4E934AEA62EEF6 /* SwiftyBeaver-SwiftyBeaver */, + 27B5E7D21308F0025ED3E0115E1D24F2 /* TrustDecision */, + ); + name = Products; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 0BB78ACC51F43370D562DC1F351005BA /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 32737A3500F39EFCEB89291850AABFB3 /* TDMobRisk.h in Headers */, + 7288FF043A6E21DCB18F534663FB03A7 /* TDMobRiskAPIHelper.h in Headers */, + 6264A3B49DA8FF7DBA0371361E079962 /* TDMobRiskAppInfo.h in Headers */, + 010973465C0F48B522806795CBED2C43 /* TDMobRiskBaseInfo.h in Headers */, + C39D15F3433EAED94EF5DCAB5BB81AF0 /* TDMobRiskCalculator.h in Headers */, + 3738B495C7511E2BADB5683646C9A3F1 /* TDMobRiskCollector.h in Headers */, + C558C4D208FD62FC36CD471577B02470 /* TDMobRiskCpuInfo.h in Headers */, + 7E0C38ADB86609510208A4F310931D9A /* TDMobRiskDeviceInfo.h in Headers */, + 025746DE54B1BD3CD48FB6AB5BC52E8A /* TDMobRiskDeviceStatusInfo.h in Headers */, + CE1947C18B5F91D328AFB8A26E5F827E /* TDMobRiskEncodeHelper.h in Headers */, + 7F9E3C12DB861DF9C8A604963E14E92E /* TDMobRiskHeader.h in Headers */, + 7EAF26A63E3F93A45B220B1A817E4144 /* TDMobRiskIdCalculator.h in Headers */, + F2F45400CF0F61F8FB7E817988515032 /* TDMobRiskIdentifierInfo.h in Headers */, + C2253D5C9C3BA63FA70DC4C7410A209D /* TDMobRiskKeychainsHelper.h in Headers */, + 1A80A5EFB1FC3DD4C03EA654BBA99642 /* TDMobRiskManager.h in Headers */, + CCBDC055488D19C9B00E28AABA041D55 /* TDMobRiskOSInfo.h in Headers */, + 0298D4F92276C089224598E73367645A /* TDMobRiskSafeDictionary.h in Headers */, + D53DA8941E8E06804078BD9BF3DE398E /* TDMobRiskSpaceInfo.h in Headers */, + 2BB25365C113482240B7B4821B1DD079 /* TDMobRiskTimeInfo.h in Headers */, + 06FF7A39EBED7D5057DD920FF643BF94 /* TrustDecision-umbrella.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 11CC227AD931640F0671463A8782A0C5 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + FF2EEBD0DA36CD81FCAD357C48AEDC1A /* Pods-AIGrammar-umbrella.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 1C9916DA304610AD51F847B48DB808FB /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 8C82F446D46AB5E4D4B89FD4AAE32F77 /* SwiftyBeaver-umbrella.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 3A0A1CE0D1CE1D61F1C385B696476287 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 37E5ACD84B22B3D3D777DE097A8DF082 /* KituraContracts-umbrella.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 3D874A84FF4835ADCAAAFE8EC4FBBDF7 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 2B4DA0685B8685618DF2202C945A544E /* BlueECC-umbrella.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 40CCDE9410CF64ABFB4C994EA00E282A /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 3FF50DC751EB78BC4107C7A553A5CF92 /* BlueRSA-umbrella.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4140FB12380878DE7AADF997DC2A9EF0 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + C94C4CA45ED2CAFFB6F697DCA7C5E55A /* LoggerAPI-umbrella.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 483528F309AEABCF53CEA0D7AB7BC1DA /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 2695886313F517E6222ECB65F17B4E85 /* SwiftJWT-umbrella.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 50DE7A210F4E4C7D4834E5900149D258 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 266FD53F10617065B64F0320DAA3392C /* Logging-umbrella.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + AB7B98C48F259D8F6A1F2E8335299C8C /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 27DB5683101E144F1AAD42C1B838F44D /* Pods-AIGrammar-AIGrammarUITests-umbrella.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D8B16693C88B02BEB0FF2924C5B39222 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + A4BA9EF2EF5DCE2451F70318DC067CA3 /* Pods-AIGrammarTests-umbrella.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DB3F5EA19841D5A3486C9C9A020AA313 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 571142A2747CD90165F631A8AE980A0A /* Alamofire-umbrella.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DC9FCF5EED47008C2297DD13915FB238 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 1EDF0A2FA38DB263B4FBFE0818FC14B3 /* BlueCryptor-umbrella.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 0A2AABD7B36B6E5F1847893C445204F1 /* Pods-AIGrammar */ = { + isa = PBXNativeTarget; + buildConfigurationList = 0F37CC2D26A01275673014D311767686 /* Build configuration list for PBXNativeTarget "Pods-AIGrammar" */; + buildPhases = ( + 11CC227AD931640F0671463A8782A0C5 /* Headers */, + 650161F8F88C01E5F65EC0EFF2F88E4C /* Sources */, + A7BB9D329BF0AFA41BF2DD4584B581B4 /* Frameworks */, + CB814308F426C687DEE99DA6C0FF8B4F /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 6DDFC62C45203314BA056B79D115D108 /* PBXTargetDependency */, + FAE053B06E526FA13B3DB99309D13D04 /* PBXTargetDependency */, + 1D829654E5FEDFC59A59B6BA4C2B3503 /* PBXTargetDependency */, + EE921A71B5F057C273D1E19769546079 /* PBXTargetDependency */, + DFE25C3B9E4C86B123AFFCA2E2ED9DA1 /* PBXTargetDependency */, + 7F66E73EE3FB1B75A5D0235BB262B8A9 /* PBXTargetDependency */, + 60AC36689D88EFB67F235310F997F3C4 /* PBXTargetDependency */, + 970F2C0438573909C0DC1470D99F7DB4 /* PBXTargetDependency */, + 722140DEA43241D91942D2A6ABFF859F /* PBXTargetDependency */, + 6E7D5BF1E9AC52F66E9C82329C0ADEF7 /* PBXTargetDependency */, + ); + name = "Pods-AIGrammar"; + productName = Pods_AIGrammar; + productReference = 16B1B41F43B18E93821D646FAE2330A4 /* Pods-AIGrammar */; + productType = "com.apple.product-type.framework"; + }; + 1740FECD5A74A42ADAE96F3A8246787A /* SwiftyBeaver */ = { + isa = PBXNativeTarget; + buildConfigurationList = A46F003C01F7C71DB37AD599ADD77799 /* Build configuration list for PBXNativeTarget "SwiftyBeaver" */; + buildPhases = ( + 1C9916DA304610AD51F847B48DB808FB /* Headers */, + E8DE1ADC2E08F426B1BF860EBE4C3F74 /* Sources */, + DC4FF28FDC9A6B9232A590419F6341D3 /* Frameworks */, + 8AE6DCCA5C542B91559AACEBB63973A3 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 85FEE0AA2C7E58C16CFCF8B8D201153B /* PBXTargetDependency */, + ); + name = SwiftyBeaver; + productName = SwiftyBeaver; + productReference = 7A5F7001277980B8F3EEA75BCD346E53 /* SwiftyBeaver */; + productType = "com.apple.product-type.framework"; + }; + 1DF4EE179DC932E808519031CB538D5A /* Pods-AIGrammarTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 40392276BA824C7639B8D52571DF4C31 /* Build configuration list for PBXNativeTarget "Pods-AIGrammarTests" */; + buildPhases = ( + D8B16693C88B02BEB0FF2924C5B39222 /* Headers */, + 205332C71171CC24577DA190FC2E6FF6 /* Sources */, + 93691C7BC1F0D78FC3B0061CF5EED1FE /* Frameworks */, + 96231BFD6514C03BD54AC7A22771C100 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 428FE971132BA0AEA2DFF1B9D0D74AEB /* PBXTargetDependency */, + ); + name = "Pods-AIGrammarTests"; + productName = Pods_AIGrammarTests; + productReference = AD6ECF5E77BCA6AC38C6B041223E889D /* Pods-AIGrammarTests */; + productType = "com.apple.product-type.framework"; + }; + 2ABF3F8EC6CE525E1E02C51D72C64E94 /* Logging */ = { + isa = PBXNativeTarget; + buildConfigurationList = 5D3C6CDB3FEB6232A30D8F4BF7EF89E7 /* Build configuration list for PBXNativeTarget "Logging" */; + buildPhases = ( + 50DE7A210F4E4C7D4834E5900149D258 /* Headers */, + 85188C0A04E504AEB75971C6A567FDAF /* Sources */, + D10B4517EBECE50110E22B22BB649A68 /* Frameworks */, + DAD4F71E4E2E5AEFA626C282D230EB6A /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Logging; + productName = Logging; + productReference = A6513E0A6CB60623515716E73996F0F5 /* Logging */; + productType = "com.apple.product-type.framework"; + }; + 3CA5FD5A45C3F901A76060FE0F4A9B5F /* BlueRSA */ = { + isa = PBXNativeTarget; + buildConfigurationList = 12BEBFE4FF4C9C0834ED6E2450BD21AC /* Build configuration list for PBXNativeTarget "BlueRSA" */; + buildPhases = ( + 40CCDE9410CF64ABFB4C994EA00E282A /* Headers */, + A77124DD555E84572F5CD6187B54FC24 /* Sources */, + 328D9762612ED966D12B6503AE8C8720 /* Frameworks */, + 7B0B5E9BC13A39437E795FD805116073 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = BlueRSA; + productName = CryptorRSA; + productReference = A5CF16B4996DB7D0E4981082FB86ED82 /* BlueRSA */; + productType = "com.apple.product-type.framework"; + }; + 4090B1B7FD0B799BF794751E6A9D826F /* LoggerAPI */ = { + isa = PBXNativeTarget; + buildConfigurationList = E8073BA5B14E50E5EA109E0EE5B0886A /* Build configuration list for PBXNativeTarget "LoggerAPI" */; + buildPhases = ( + 4140FB12380878DE7AADF997DC2A9EF0 /* Headers */, + 3E17457D1F5C52A9515403343186EB38 /* Sources */, + 1E9D0BDEE022F2950BB0D08091728A8D /* Frameworks */, + 6127E1B0917387006D859BFE231A0260 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 30E8A17F7CD61A106A6E0DE3B18AC7CC /* PBXTargetDependency */, + ); + name = LoggerAPI; + productName = LoggerAPI; + productReference = A3B85B880B894F1FF5BAFD94E036CC04 /* LoggerAPI */; + productType = "com.apple.product-type.framework"; + }; + 44FE1AF38BF7DF3E88AEEE6BD4DA40CD /* SwiftyBeaver-SwiftyBeaver */ = { + isa = PBXNativeTarget; + buildConfigurationList = 0F6221E72FA47FA16485B1AFA5FD6EFA /* Build configuration list for PBXNativeTarget "SwiftyBeaver-SwiftyBeaver" */; + buildPhases = ( + FC5DA9AECD178828616F6DBC2E67FA4E /* Sources */, + 2DF8CA787A2B783D265EBEC91905867B /* Frameworks */, + DA97F1F13C5E9A8D7C318A3C1AD7006B /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "SwiftyBeaver-SwiftyBeaver"; + productName = SwiftyBeaver; + productReference = 9215295657AC6ABB8D4E934AEA62EEF6 /* SwiftyBeaver-SwiftyBeaver */; + productType = "com.apple.product-type.bundle"; + }; + 77B84D406B03F3DFD9C5B49BC54CCF26 /* Pods-AIGrammar-AIGrammarUITests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 2BB2CC43CF1E3A5FD5CF42C64535B098 /* Build configuration list for PBXNativeTarget "Pods-AIGrammar-AIGrammarUITests" */; + buildPhases = ( + AB7B98C48F259D8F6A1F2E8335299C8C /* Headers */, + FED9758F43D44B51EF540B2DFAAB508E /* Sources */, + 50D7C24C6C7A2421110CA5837D08E0FF /* Frameworks */, + BAE4E492E34C11A6F465AAFAA4683E5C /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 62166F02A4708558EF951DB1D88B98FF /* PBXTargetDependency */, + C2AC3630BAADFB3BD954A71561CDD537 /* PBXTargetDependency */, + 55D3648A2545E95E78ED776FB8B15819 /* PBXTargetDependency */, + 6C2EF6132DCC86162DB06F600A45542B /* PBXTargetDependency */, + C6702916B1D5D7C32097BB56BC3F3841 /* PBXTargetDependency */, + 1D4C4247B2152679313F86CAB0FD7E71 /* PBXTargetDependency */, + B71B5C656CBB57BEFD89C208B88B11BD /* PBXTargetDependency */, + D5088D067CC754BAC9F69C80AF844658 /* PBXTargetDependency */, + C9F20906D0C1FA10C57BA164D351501C /* PBXTargetDependency */, + DB23FE79F6F6D736116533F8CDE7D639 /* PBXTargetDependency */, + ); + name = "Pods-AIGrammar-AIGrammarUITests"; + productName = Pods_AIGrammar_AIGrammarUITests; + productReference = F1F4E4377F64BC660B42EC1EEC039857 /* Pods-AIGrammar-AIGrammarUITests */; + productType = "com.apple.product-type.framework"; + }; + 8E6E41C7F38055ADB1C69E0CA8CB8C1F /* TrustDecision */ = { + isa = PBXNativeTarget; + buildConfigurationList = B5691EDC0B336F545FF5FD0D14C865F9 /* Build configuration list for PBXNativeTarget "TrustDecision" */; + buildPhases = ( + 0BB78ACC51F43370D562DC1F351005BA /* Headers */, + 1DF4724AA0371094EF4AC365CE832B8A /* Sources */, + 1FBC55F931045E93F4D1A4D3412A0930 /* Frameworks */, + F02D44F27BC5FDC9CD49B4D9DC113ACB /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = TrustDecision; + productName = TrustDecision; + productReference = 27B5E7D21308F0025ED3E0115E1D24F2 /* TrustDecision */; + productType = "com.apple.product-type.framework"; + }; + 921D74A1881BD89FA651ED7078F37128 /* KituraContracts */ = { + isa = PBXNativeTarget; + buildConfigurationList = 2A64AF72071AF5E51B7A66AAA76306ED /* Build configuration list for PBXNativeTarget "KituraContracts" */; + buildPhases = ( + 3A0A1CE0D1CE1D61F1C385B696476287 /* Headers */, + EEFD65F27468CB3F0C6DFCE2C263CCFD /* Sources */, + 0FEBE4C79C58CB2EBAC0CF9528EB2DD2 /* Frameworks */, + ECE18D6D82FC9DE718D971552DE58110 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 9C57EC3B4D5F6402BD822932C0145421 /* PBXTargetDependency */, + ); + name = KituraContracts; + productName = KituraContracts; + productReference = B9E276AB0F6AE3009D9751E19C8A0E0F /* KituraContracts */; + productType = "com.apple.product-type.framework"; + }; + 976126A1CE06DC6E162563800E1BDF14 /* Alamofire-Alamofire */ = { + isa = PBXNativeTarget; + buildConfigurationList = 78CF2AECA59ABDD5FEBA1ECBFE5FBE21 /* Build configuration list for PBXNativeTarget "Alamofire-Alamofire" */; + buildPhases = ( + C6AD7FE17221730EC49EA9AA4EFB0048 /* Sources */, + D0C260E31F71D5926EB4D0B93F465C83 /* Frameworks */, + CC5ECEDA5E890314DB27179143E8BBCC /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "Alamofire-Alamofire"; + productName = Alamofire; + productReference = 085DBCE7DD98588B2ED103B1C1F36026 /* Alamofire-Alamofire */; + productType = "com.apple.product-type.bundle"; + }; + 9FF604BE1801EF6B67EED51EDA9D63C9 /* BlueECC */ = { + isa = PBXNativeTarget; + buildConfigurationList = 7602F5D278AA2D1C563D4F665525B719 /* Build configuration list for PBXNativeTarget "BlueECC" */; + buildPhases = ( + 3D874A84FF4835ADCAAAFE8EC4FBBDF7 /* Headers */, + 75C69CB4D38951ED9FD3D5A21C92AE02 /* Sources */, + E8641C352248614128D0F0AA4467DA34 /* Frameworks */, + 1DBFD8A2B043DE36A0FF084059910CF2 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = BlueECC; + productName = CryptorECC; + productReference = 53D3E35E7EB25621342FA0CA77C0167B /* BlueECC */; + productType = "com.apple.product-type.framework"; + }; + A28ABE059EE78F37B646540BEAB934E0 /* BlueCryptor */ = { + isa = PBXNativeTarget; + buildConfigurationList = 5DAC03B644034D84EE2DFC8C5B6F83AC /* Build configuration list for PBXNativeTarget "BlueCryptor" */; + buildPhases = ( + DC9FCF5EED47008C2297DD13915FB238 /* Headers */, + E5D193B2F29C460045D229E24EA65DDB /* Sources */, + B06267CBEDC638D9B908833ECAF3E195 /* Frameworks */, + 8ECE089CB8D313C3B22AD14B673D4A62 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = BlueCryptor; + productName = Cryptor; + productReference = 0EF2AA497DAF2C29FA3EB06BDE44553F /* BlueCryptor */; + productType = "com.apple.product-type.framework"; + }; + E7F041D10DB8131E7E8B866AA3D62E32 /* SwiftJWT */ = { + isa = PBXNativeTarget; + buildConfigurationList = D8D6D21B85F08E28672F2885BBF5D200 /* Build configuration list for PBXNativeTarget "SwiftJWT" */; + buildPhases = ( + 483528F309AEABCF53CEA0D7AB7BC1DA /* Headers */, + 2B7B098787E66C510DDF19AE84EFDE07 /* Sources */, + C4ACB3447ACC697712FB82D1F826EA6D /* Frameworks */, + 98D638507820B591087D3DB690ADB3C1 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + FBC8C7D964CE5E64FF65E6D3BF9E34D5 /* PBXTargetDependency */, + F7D085669CE4E3A2CF1900F27401B03E /* PBXTargetDependency */, + B5987EAF462D7F300673534359BCEC76 /* PBXTargetDependency */, + 1A41E7250D0D61B409D74636131F4EC5 /* PBXTargetDependency */, + 0DFCBD11E5531E0116457BA4E91112B7 /* PBXTargetDependency */, + ); + name = SwiftJWT; + productName = SwiftJWT; + productReference = D5E011D8AD8603675BA8A5634EC21B9B /* SwiftJWT */; + productType = "com.apple.product-type.framework"; + }; + EAAA1AD3A8A1B59AB91319EE40752C6D /* Alamofire */ = { + isa = PBXNativeTarget; + buildConfigurationList = FA92BF783257A026FB1E05B4B536DD6E /* Build configuration list for PBXNativeTarget "Alamofire" */; + buildPhases = ( + DB3F5EA19841D5A3486C9C9A020AA313 /* Headers */, + E0C6F893D34CB935A73FBD4BD75299E5 /* Sources */, + A5C342523C3577E7122B4B43929BA8F3 /* Frameworks */, + 5CEFF03EE9A7B5D7514E35F3AD2A314D /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 7B3E142D7B8D816C705C1B3DBDD00555 /* PBXTargetDependency */, + ); + name = Alamofire; + productName = Alamofire; + productReference = 5D797E9A5C5782CE845840781FA1CC81 /* Alamofire */; + productType = "com.apple.product-type.framework"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + BFDFE7DC352907FC980B868725387E98 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 1500; + LastUpgradeCheck = 1500; + }; + buildConfigurationList = 4821239608C13582E20E6DA73FD5F1F9 /* Build configuration list for PBXProject "Pods" */; + compatibilityVersion = "Xcode 14.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + Base, + en, + ); + mainGroup = CF1408CF629C7361332E53B88F7BD30C; + productRefGroup = FCFBB24DB8C3F286420323A960E77398 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + EAAA1AD3A8A1B59AB91319EE40752C6D /* Alamofire */, + 976126A1CE06DC6E162563800E1BDF14 /* Alamofire-Alamofire */, + A28ABE059EE78F37B646540BEAB934E0 /* BlueCryptor */, + 9FF604BE1801EF6B67EED51EDA9D63C9 /* BlueECC */, + 3CA5FD5A45C3F901A76060FE0F4A9B5F /* BlueRSA */, + 921D74A1881BD89FA651ED7078F37128 /* KituraContracts */, + 4090B1B7FD0B799BF794751E6A9D826F /* LoggerAPI */, + 2ABF3F8EC6CE525E1E02C51D72C64E94 /* Logging */, + 0A2AABD7B36B6E5F1847893C445204F1 /* Pods-AIGrammar */, + 77B84D406B03F3DFD9C5B49BC54CCF26 /* Pods-AIGrammar-AIGrammarUITests */, + 1DF4EE179DC932E808519031CB538D5A /* Pods-AIGrammarTests */, + E7F041D10DB8131E7E8B866AA3D62E32 /* SwiftJWT */, + 1740FECD5A74A42ADAE96F3A8246787A /* SwiftyBeaver */, + 44FE1AF38BF7DF3E88AEEE6BD4DA40CD /* SwiftyBeaver-SwiftyBeaver */, + 8E6E41C7F38055ADB1C69E0CA8CB8C1F /* TrustDecision */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 1DBFD8A2B043DE36A0FF084059910CF2 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 5CEFF03EE9A7B5D7514E35F3AD2A314D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4F4B9DFA352D9958C7494D7BC24631D8 /* Alamofire-Alamofire in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 6127E1B0917387006D859BFE231A0260 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 7B0B5E9BC13A39437E795FD805116073 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 8AE6DCCA5C542B91559AACEBB63973A3 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 1F1526584488D1931184FF11B162E028 /* SwiftyBeaver-SwiftyBeaver in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 8ECE089CB8D313C3B22AD14B673D4A62 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 96231BFD6514C03BD54AC7A22771C100 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 98D638507820B591087D3DB690ADB3C1 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + BAE4E492E34C11A6F465AAFAA4683E5C /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + CB814308F426C687DEE99DA6C0FF8B4F /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + CC5ECEDA5E890314DB27179143E8BBCC /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 6209BD7060E0A2330F0F1A5FF91EF89A /* PrivacyInfo.xcprivacy in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DA97F1F13C5E9A8D7C318A3C1AD7006B /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + B952E724E1063E096813E083647B831F /* PrivacyInfo.xcprivacy in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DAD4F71E4E2E5AEFA626C282D230EB6A /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + ECE18D6D82FC9DE718D971552DE58110 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F02D44F27BC5FDC9CD49B4D9DC113ACB /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 1DF4724AA0371094EF4AC365CE832B8A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 56D4A4B5FAC428E6380C5EBE6E21A8F9 /* TDMobRiskAPIHelper.m in Sources */, + 4E90056906076DE3ED54B042EE9319B6 /* TDMobRiskAppInfo.m in Sources */, + 8C035F4049ED17F3FD68B55A15501CCC /* TDMobRiskBaseInfo.m in Sources */, + D3A529382E524CB7387F0A7A860C28DE /* TDMobRiskCalculator.m in Sources */, + C55064815CA00F3A52BE0F2C5A2AC7B1 /* TDMobRiskCollector.m in Sources */, + C64590868B5F22BBA9D880F9993355B6 /* TDMobRiskCpuInfo.m in Sources */, + 40F3B4F630766574D4E03A3801065D98 /* TDMobRiskDeviceInfo.m in Sources */, + 047724F1C4CDDFA71F46E33C39F10FA3 /* TDMobRiskDeviceStatusInfo.m in Sources */, + 4373F7703D3D5A4BF068B4D57C88EEF3 /* TDMobRiskEncodeHelper.m in Sources */, + 67BC1B3150CB7E4089FA2AD72FC6B1DA /* TDMobRiskIdCalculator.m in Sources */, + C81D71ED01A3BA1E01C74CB15C850140 /* TDMobRiskIdentifierInfo.m in Sources */, + 31E5C60B94CC53FED9FA1A84DA0F5CE8 /* TDMobRiskKeychainsHelper.m in Sources */, + B5B90B89912260E463C6ED44CFF05DFD /* TDMobRiskManager.m in Sources */, + DF539117F395B3F97BE24B6CB69DEAF8 /* TDMobRiskOSInfo.m in Sources */, + C8EB219BB93084757B55B890B5F3FD04 /* TDMobRiskSafeDictionary.m in Sources */, + 918C9188314B4577DF6136410E13653E /* TDMobRiskSpaceInfo.m in Sources */, + 1E702BFC502F8DE460A5322A547E6569 /* TDMobRiskTimeInfo.m in Sources */, + 5956158B14273C892F1E596A8FC907A3 /* TrustDecision-dummy.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 205332C71171CC24577DA190FC2E6FF6 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 903FBD5C1D823F2C0A6C8E3D147482FF /* Pods-AIGrammarTests-dummy.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 2B7B098787E66C510DDF19AE84EFDE07 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 5E1B0B6F334802C66E484E4AB564E96A /* BlueECDSA.swift in Sources */, + 804CC711F5AA0EB4E00C9A993FEEB0CA /* BlueHMAC.swift in Sources */, + 31394629250EE04C1D437CEAA9844825 /* BlueRSA.swift in Sources */, + 106C9BE8D2D5BD88A722A83B881445AD /* Claims.swift in Sources */, + AA53DAEB5CA0378FED32961B2052A01B /* ClaimsMicroProfile.swift in Sources */, + 8DC2C2BC0F42E9B820EB962B1D76644D /* ClaimsOpenID.swift in Sources */, + 88626113053B820D5D65F7FF2841D541 /* ClaimsStandardJWT.swift in Sources */, + 2C03C28338745B50B7E5956C03884CE0 /* Data+Base64URLEncoded.swift in Sources */, + 7DDA219DF77250F5CBB3242438DAF486 /* Header.swift in Sources */, + 2D7CC4DD61485FE8246D415A813A94DB /* JWT.swift in Sources */, + D29C9D92076A43DCCAF9F0FAE0911321 /* JWTDecoder.swift in Sources */, + 0DA8A599A871E6A64B7B9E796B0F3308 /* JWTEncoder.swift in Sources */, + 65345F2C02726D947133E79CCA5CA0CC /* JWTError.swift in Sources */, + ED737EB4F72FC3FA3CF93565FC349DD2 /* JWTSigner.swift in Sources */, + 5BFED9BCB02678A257FD5D93176AC40E /* JWTVerifier.swift in Sources */, + 5DD9B8B026B60B309CD0054BD6B74E8B /* NoneAlgorithm.swift in Sources */, + 177809E275ECC628EE7CEF6F52B4411A /* RSAKeyType.swift in Sources */, + 50AD6B8BBDAA111F49780DA337DEF57D /* SignerAlgorithm.swift in Sources */, + 3109B5207E71C65D27B3E817A7B35698 /* SwiftJWT-dummy.m in Sources */, + 2DF5E6E3C6C4011A9BCB21161BAA5E65 /* ValidateClaimsResult.swift in Sources */, + 1BA4F0B05D6A17790291304AF7438DA5 /* VerifierAlgorithm.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 3E17457D1F5C52A9515403343186EB38 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + DE6D5CC0FDCEF991781D529DB3266156 /* Logger.swift in Sources */, + 09ED348CC514F53C55D29C4F1BA520E2 /* LoggerAPI-dummy.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 650161F8F88C01E5F65EC0EFF2F88E4C /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 50FEE1B272074B47D6A63561E0F4D58C /* Pods-AIGrammar-dummy.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 75C69CB4D38951ED9FD3D5A21C92AE02 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 01B316CFCCE7A94AC0024D0EC7552B3D /* ASN1.swift in Sources */, + 214538563382AF40DEA798179B61330F /* BlueECC-dummy.m in Sources */, + 459F1BDAF057FEDA89B5DC0F62CD6F36 /* Data+Extensions.swift in Sources */, + 94256662B883B8D44E1424A6748CB18D /* ECDecryptable.swift in Sources */, + 25B07B4343220D9047066923803DABBE /* ECEncryptable.swift in Sources */, + 0036E06D0609E0B31EB5442647D101AD /* ECError.swift in Sources */, + E67B00531A5F5C7ED8832A189AEC767B /* ECPrivateKey.swift in Sources */, + A9FDC28AAC04AAF28754E3C9F7D921F7 /* ECPublicKey.swift in Sources */, + 6C1AB8C0B3CAE8E895E194F6C7A8F7FC /* ECSignable.swift in Sources */, + E4D29BF0611B19578C6954E36C8221D1 /* ECSignature.swift in Sources */, + 3063E9B1F5C56B51525FC8D0D64BB07B /* EllipticCurve.swift in Sources */, + F8754CE53F48DF0322C92E13E907327E /* SSLPointerTricks.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 85188C0A04E504AEB75971C6A567FDAF /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 8DA66F3A55C0A3211EA5D64A8A3473F1 /* Locks.swift in Sources */, + 902D9829701BE63A4BEEC90F9871852D /* Logging.swift in Sources */, + C6C3A38CB352735109B000DD52B6F3C9 /* Logging-dummy.m in Sources */, + 52CF1E46EF69738B27E92F4B718E614A /* LogHandler.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + A77124DD555E84572F5CD6187B54FC24 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + CBEE15376AD8AE6E3776D52A107229E0 /* BlueRSA-dummy.m in Sources */, + C3E84BE76BE3AD640327004B5A7868D9 /* CryptorRSA.swift in Sources */, + 1AF6FD4A74E160188340F26D28982A50 /* CryptorRSAConstants.swift in Sources */, + 2D70DC58C89E33AB723759672D36B683 /* CryptorRSADigest.swift in Sources */, + AC8C84031DC30CD75716F6B35B404D1D /* CryptorRSAErrors.swift in Sources */, + C2C69A82C314BF2092AAB5492EF9E4CD /* CryptorRSAKey.swift in Sources */, + 99BC73949BA89A03FB1ED130B43A8B02 /* CryptorRSAUtilities.swift in Sources */, + CA96BF27B2693ED1519801E4DB145A16 /* Data+Extensions.swift in Sources */, + C22CD856FDC788515376347CF56327CC /* SSLPointerTricks.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + C6AD7FE17221730EC49EA9AA4EFB0048 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + E0C6F893D34CB935A73FBD4BD75299E5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + AA602A49B1DC7FDED565CAD8BB89EAC1 /* AFError.swift in Sources */, + 58E936B1E7E42C0BFC119D428B70F1D8 /* Alamofire.swift in Sources */, + 68238227D42B2511FA6A26BF71E92520 /* Alamofire-dummy.m in Sources */, + 72C19C762FADC82517C344E9F47D7E50 /* AlamofireExtended.swift in Sources */, + 69261B5D3B53EBF7109D5E1DA3768CAC /* AuthenticationInterceptor.swift in Sources */, + 0F1D68554CA1AC595168E8FB4E1A6E63 /* CachedResponseHandler.swift in Sources */, + 9D8BED7F3F86BB39E7C0923D92E73F8B /* Combine.swift in Sources */, + A22A2ACF53FDC243AAAFB009005A710F /* Concurrency.swift in Sources */, + 5AD4E5B4118A1DC7D639F611044B4159 /* DataRequest.swift in Sources */, + B5C66B48EB624FEC4D2F64A50F143716 /* DataStreamRequest.swift in Sources */, + DE896085DFDD686BDBDEFB776F0D683A /* DispatchQueue+Alamofire.swift in Sources */, + 5830C6260CA2B7CD6DC74054FB29CDD1 /* DownloadRequest.swift in Sources */, + FCE62086E1AB54A4F61EBCDBA15C1510 /* EventMonitor.swift in Sources */, + 2B42D035AFF52D62722161A7772C6C08 /* HTTPHeaders.swift in Sources */, + 4337931D8B8E3F3BA03C77C1B496BEAD /* HTTPMethod.swift in Sources */, + 68995B28EE5B539CEA5A1133E4623927 /* MultipartFormData.swift in Sources */, + 795681285B4E2B121B5CD420131168F8 /* MultipartUpload.swift in Sources */, + A1506893FF52AA466B130E8B05FBE868 /* NetworkReachabilityManager.swift in Sources */, + 8DD46EE7FB9503E7634E929DDE1CBA31 /* Notifications.swift in Sources */, + 68A74F13F8FEBAA7E0EA9344DED0458B /* OperationQueue+Alamofire.swift in Sources */, + 22FAFA41450EC40132CF4B0EEE7E6788 /* ParameterEncoder.swift in Sources */, + 6F2E0DC7D8598283D088A989FDB8E5F6 /* ParameterEncoding.swift in Sources */, + F4DD0AD58DDD5641BDEAEA6CF44FF0ED /* Protected.swift in Sources */, + 9C7D314BE45AB79E96B260656C36BAEC /* RedirectHandler.swift in Sources */, + 36C78069A72BECAEB66B31FF794A09ED /* Request.swift in Sources */, + 887DB52C63E52FBD3B88F42DD8CFB421 /* RequestCompression.swift in Sources */, + DE8F5B68839128A005EE3549A1149B09 /* RequestInterceptor.swift in Sources */, + 90D847B19214926EDE5210D44A08F3C7 /* RequestTaskMap.swift in Sources */, + E6ED06AC318A34F7744B32CEC759CDA9 /* Response.swift in Sources */, + 2B230B24827053BA3E9DA0C78A796BC2 /* ResponseSerialization.swift in Sources */, + 9378157945D7B405C862A05B0D6B971B /* Result+Alamofire.swift in Sources */, + CDCA01B605A086576DBB75F8C3A24337 /* RetryPolicy.swift in Sources */, + 7AE2E0B382A14D58BE7DAA0C852DCD02 /* ServerTrustEvaluation.swift in Sources */, + 5075DC82A63A9807DFC390B4CE8046CD /* Session.swift in Sources */, + 97D7D91FC818805D8344C373CC098C32 /* SessionDelegate.swift in Sources */, + 612AE0ABB9BCD3AF0E1D29B4C063CA62 /* StringEncoding+Alamofire.swift in Sources */, + 2C4C08BB733A2101D945E8C37256F78F /* UploadRequest.swift in Sources */, + A3FD52DF5584364FFD56965394C36CF2 /* URLConvertible+URLRequestConvertible.swift in Sources */, + BC0A0C473B63B817926F4D58611281BB /* URLEncodedFormEncoder.swift in Sources */, + 92138A77DFEB4F76FCB582E97633896D /* URLRequest+Alamofire.swift in Sources */, + 6CC7E7C00730B1BF42A28B2E23CA01D6 /* URLSessionConfiguration+Alamofire.swift in Sources */, + 794FC38D15336AB502B73B012005E9BD /* Validation.swift in Sources */, + F7E576E007A81E0EFD2E0849CB17878D /* WebSocketRequest.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + E5D193B2F29C460045D229E24EA65DDB /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 79D835F73197376AE844D95C90933226 /* BlueCryptor-dummy.m in Sources */, + BF67C108B6259651384C3918E40EE800 /* Crypto.swift in Sources */, + A98B69EE86D815F50D8B9AF37939B4B9 /* Cryptor.swift in Sources */, + 6AF293CFF3CE6DD506AB54A3C9E2722D /* Digest.swift in Sources */, + 1BEC012FE149C38578B49F0F4FF8249A /* HMAC.swift in Sources */, + F8D7C3AA037914AE4B5487CADBAB6A89 /* KeyDerivation.swift in Sources */, + E1FAE2E561064E7B6D82A872FF21EE22 /* Random.swift in Sources */, + 276CCE7D6298C49428EB1454B1EDFECA /* SSLPointerTricks.swift in Sources */, + A22F501CEF8902F55BC9C721C5B7AD17 /* Status.swift in Sources */, + 74991B968DEE7DC8432DBBE23F3257C3 /* StreamCryptor.swift in Sources */, + 6262CEE6306F8826EDFAEF0A72943B25 /* Updatable.swift in Sources */, + AD86E160D9AF005D4C79997189A78541 /* Utilities.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + E8DE1ADC2E08F426B1BF860EBE4C3F74 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 5601110C7BCC4CD041B927D7CECB72EB /* Base64.swift in Sources */, + A54126CFE40B126B362093AC65EB80F0 /* BaseDestination.swift in Sources */, + D82331BC251B87EEFAD29F53061F5E7A /* ConsoleDestination.swift in Sources */, + CDDC7DEEDC472BF67D03D94D589EE9A6 /* Extensions.swift in Sources */, + 63A6CB6A62954270DBE6B5E7A05CA74E /* FileDestination.swift in Sources */, + 31EABF7A2B9FAAF4E726C46C52DE6DB1 /* Filter.swift in Sources */, + 15488E69FEF316F5B5164AC0C6F0C19A /* FilterValidator.swift in Sources */, + A5906CD1B9E027686B14C572817F9059 /* GoogleCloudDestination.swift in Sources */, + 44C23ACB771B26CC0159357E19CAB785 /* SwiftyBeaver.swift in Sources */, + 940B1A39D1AECDD5896FE5E4F721FED8 /* SwiftyBeaver-dummy.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + EEFD65F27468CB3F0C6DFCE2C263CCFD /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 96B4E052EFA52F1F185DC01777763360 /* BodyDecoder.swift in Sources */, + A442E3E925B8E3B705B6910DD907E2A8 /* BodyEncoder.swift in Sources */, + 7BBEE2A1BF4C42D44EA22C22E7D8C342 /* BodyFormat.swift in Sources */, + DBDEFD5E67253B069025C2C08541AF8B /* ClosureAliases.swift in Sources */, + 8B355855806FB7FEF614CC32DE5E49C6 /* Coder.swift in Sources */, + 22291180549B1139F56D620A999310A4 /* Contracts.swift in Sources */, + 7E462830E8D66124921BCBB93A76F1B0 /* Extensions.swift in Sources */, + 20B2985EF70D6DB110B5583A67AD3005 /* KituraContracts-dummy.m in Sources */, + BD4FBDE5F2A710292C8287735137C509 /* QueryDecoder.swift in Sources */, + EA679C848DC90955BC12EAE0A954CBE0 /* QueryEncoder.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FC5DA9AECD178828616F6DBC2E67FA4E /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FED9758F43D44B51EF540B2DFAAB508E /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 0066FADAEF420F018864EFCA93BF9D51 /* Pods-AIGrammar-AIGrammarUITests-dummy.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 0DFCBD11E5531E0116457BA4E91112B7 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = LoggerAPI; + target = 4090B1B7FD0B799BF794751E6A9D826F /* LoggerAPI */; + targetProxy = 5CEA60AAFB925BF79E3B54021E2F5BCD /* PBXContainerItemProxy */; + }; + 1A41E7250D0D61B409D74636131F4EC5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = KituraContracts; + target = 921D74A1881BD89FA651ED7078F37128 /* KituraContracts */; + targetProxy = 5E8FEB6AF4017FCC82C8DE8880DEC0BF /* PBXContainerItemProxy */; + }; + 1D4C4247B2152679313F86CAB0FD7E71 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = LoggerAPI; + target = 4090B1B7FD0B799BF794751E6A9D826F /* LoggerAPI */; + targetProxy = 72305A9935B8511F65620F8BFB0149E3 /* PBXContainerItemProxy */; + }; + 1D829654E5FEDFC59A59B6BA4C2B3503 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = BlueECC; + target = 9FF604BE1801EF6B67EED51EDA9D63C9 /* BlueECC */; + targetProxy = 3AB6DC35BD651E02B02C72F580CA05AD /* PBXContainerItemProxy */; + }; + 30E8A17F7CD61A106A6E0DE3B18AC7CC /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = Logging; + target = 2ABF3F8EC6CE525E1E02C51D72C64E94 /* Logging */; + targetProxy = F7476C7BF967247633BF398F11513F14 /* PBXContainerItemProxy */; + }; + 428FE971132BA0AEA2DFF1B9D0D74AEB /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = "Pods-AIGrammar"; + target = 0A2AABD7B36B6E5F1847893C445204F1 /* Pods-AIGrammar */; + targetProxy = 5EE536CF73B18381A88A6C862DEC4905 /* PBXContainerItemProxy */; + }; + 55D3648A2545E95E78ED776FB8B15819 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = BlueECC; + target = 9FF604BE1801EF6B67EED51EDA9D63C9 /* BlueECC */; + targetProxy = 4DBCD985D26936EBC05E4B6614A030E9 /* PBXContainerItemProxy */; + }; + 60AC36689D88EFB67F235310F997F3C4 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = Logging; + target = 2ABF3F8EC6CE525E1E02C51D72C64E94 /* Logging */; + targetProxy = 9B6D73F898F70705641F61F401236820 /* PBXContainerItemProxy */; + }; + 62166F02A4708558EF951DB1D88B98FF /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = Alamofire; + target = EAAA1AD3A8A1B59AB91319EE40752C6D /* Alamofire */; + targetProxy = 188BBF2A90C4C5903DC5A035F274B3FD /* PBXContainerItemProxy */; + }; + 6C2EF6132DCC86162DB06F600A45542B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = BlueRSA; + target = 3CA5FD5A45C3F901A76060FE0F4A9B5F /* BlueRSA */; + targetProxy = 49A421A9F538D81B6BD413E01CE47DEF /* PBXContainerItemProxy */; + }; + 6DDFC62C45203314BA056B79D115D108 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = Alamofire; + target = EAAA1AD3A8A1B59AB91319EE40752C6D /* Alamofire */; + targetProxy = 45CC75751F65E21C75B7190370454D3F /* PBXContainerItemProxy */; + }; + 6E7D5BF1E9AC52F66E9C82329C0ADEF7 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = TrustDecision; + target = 8E6E41C7F38055ADB1C69E0CA8CB8C1F /* TrustDecision */; + targetProxy = BE532875E85AFEB3C3F8AF57005C5496 /* PBXContainerItemProxy */; + }; + 722140DEA43241D91942D2A6ABFF859F /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = SwiftyBeaver; + target = 1740FECD5A74A42ADAE96F3A8246787A /* SwiftyBeaver */; + targetProxy = D994FCC0307F0E8A7F6EA799D174ECF8 /* PBXContainerItemProxy */; + }; + 7B3E142D7B8D816C705C1B3DBDD00555 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = "Alamofire-Alamofire"; + target = 976126A1CE06DC6E162563800E1BDF14 /* Alamofire-Alamofire */; + targetProxy = F98E14D7ACFA4568034FD2853277A627 /* PBXContainerItemProxy */; + }; + 7F66E73EE3FB1B75A5D0235BB262B8A9 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = LoggerAPI; + target = 4090B1B7FD0B799BF794751E6A9D826F /* LoggerAPI */; + targetProxy = CA5EC40ECDDAD0EF7FEB47C27DF7044E /* PBXContainerItemProxy */; + }; + 85FEE0AA2C7E58C16CFCF8B8D201153B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = "SwiftyBeaver-SwiftyBeaver"; + target = 44FE1AF38BF7DF3E88AEEE6BD4DA40CD /* SwiftyBeaver-SwiftyBeaver */; + targetProxy = CA217DF27E83341B005ECE09D4B88A4A /* PBXContainerItemProxy */; + }; + 970F2C0438573909C0DC1470D99F7DB4 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = SwiftJWT; + target = E7F041D10DB8131E7E8B866AA3D62E32 /* SwiftJWT */; + targetProxy = 1F7C42DAB51F62A354F25D8D4B5A1295 /* PBXContainerItemProxy */; + }; + 9C57EC3B4D5F6402BD822932C0145421 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = LoggerAPI; + target = 4090B1B7FD0B799BF794751E6A9D826F /* LoggerAPI */; + targetProxy = 9E63986C3DF23E5E58D8F046D0CDB353 /* PBXContainerItemProxy */; + }; + B5987EAF462D7F300673534359BCEC76 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = BlueRSA; + target = 3CA5FD5A45C3F901A76060FE0F4A9B5F /* BlueRSA */; + targetProxy = 998461E1F3F211B8950FA0BF893A61DF /* PBXContainerItemProxy */; + }; + B71B5C656CBB57BEFD89C208B88B11BD /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = Logging; + target = 2ABF3F8EC6CE525E1E02C51D72C64E94 /* Logging */; + targetProxy = 43BCC2D5C58B1F571B35F7023B5DBBDF /* PBXContainerItemProxy */; + }; + C2AC3630BAADFB3BD954A71561CDD537 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = BlueCryptor; + target = A28ABE059EE78F37B646540BEAB934E0 /* BlueCryptor */; + targetProxy = 60F6C539AF08A2936A070327FE5704D0 /* PBXContainerItemProxy */; + }; + C6702916B1D5D7C32097BB56BC3F3841 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = KituraContracts; + target = 921D74A1881BD89FA651ED7078F37128 /* KituraContracts */; + targetProxy = 936B5B9D1C7A180775D7C76F9A9E5834 /* PBXContainerItemProxy */; + }; + C9F20906D0C1FA10C57BA164D351501C /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = SwiftyBeaver; + target = 1740FECD5A74A42ADAE96F3A8246787A /* SwiftyBeaver */; + targetProxy = 7A3E9ED06C5D6930AD3E6663682901CE /* PBXContainerItemProxy */; + }; + D5088D067CC754BAC9F69C80AF844658 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = SwiftJWT; + target = E7F041D10DB8131E7E8B866AA3D62E32 /* SwiftJWT */; + targetProxy = 0C9449DEE482F22B48C86887B3D65219 /* PBXContainerItemProxy */; + }; + DB23FE79F6F6D736116533F8CDE7D639 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = TrustDecision; + target = 8E6E41C7F38055ADB1C69E0CA8CB8C1F /* TrustDecision */; + targetProxy = 234F2BDFB323886FC27710EDD5E46BF8 /* PBXContainerItemProxy */; + }; + DFE25C3B9E4C86B123AFFCA2E2ED9DA1 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = KituraContracts; + target = 921D74A1881BD89FA651ED7078F37128 /* KituraContracts */; + targetProxy = 3531027AA1157A8BE3DECDCC638D922C /* PBXContainerItemProxy */; + }; + EE921A71B5F057C273D1E19769546079 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = BlueRSA; + target = 3CA5FD5A45C3F901A76060FE0F4A9B5F /* BlueRSA */; + targetProxy = 7A49A9F30FE4BE6998B0888EFC83238E /* PBXContainerItemProxy */; + }; + F7D085669CE4E3A2CF1900F27401B03E /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = BlueECC; + target = 9FF604BE1801EF6B67EED51EDA9D63C9 /* BlueECC */; + targetProxy = 903DD7481559FB1BA5854B7F5CFC6EE6 /* PBXContainerItemProxy */; + }; + FAE053B06E526FA13B3DB99309D13D04 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = BlueCryptor; + target = A28ABE059EE78F37B646540BEAB934E0 /* BlueCryptor */; + targetProxy = FAD9CC331B1E1C0113387ADD622DC16A /* PBXContainerItemProxy */; + }; + FBC8C7D964CE5E64FF65E6D3BF9E34D5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = BlueCryptor; + target = A28ABE059EE78F37B646540BEAB934E0 /* BlueCryptor */; + targetProxy = 949C3EDF388ACE75C4C30242A49A6096 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 04D0F2DC69230A2680873BF4808BC9E6 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 5909292CACF3FD0B79E4348B60B024AD /* SwiftyBeaver.debug.xcconfig */; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = NO; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/SwiftyBeaver/SwiftyBeaver-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/SwiftyBeaver/SwiftyBeaver-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 13.4; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MODULEMAP_FILE = "Target Support Files/SwiftyBeaver/SwiftyBeaver.modulemap"; + PRODUCT_MODULE_NAME = SwiftyBeaver; + PRODUCT_NAME = SwiftyBeaver; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_VERSION = 5.1; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 09BB305219738FF38BD3F3797074BA73 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = BDC8A99F14C97CA85C839872E2DC482A /* Pods-AIGrammar-AIGrammarUITests.debug.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; + CLANG_ENABLE_OBJC_WEAK = NO; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = "Target Support Files/Pods-AIGrammar-AIGrammarUITests/Pods-AIGrammar-AIGrammarUITests-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MACH_O_TYPE = staticlib; + MODULEMAP_FILE = "Target Support Files/Pods-AIGrammar-AIGrammarUITests/Pods-AIGrammar-AIGrammarUITests.modulemap"; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 0AC09DABDFF4C9A7B4F2D4D1E23767C3 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = FEA8A1FBC489607A393CFD60E70FFB94 /* TrustDecision.release.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/TrustDecision/TrustDecision-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/TrustDecision/TrustDecision-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MACH_O_TYPE = staticlib; + MODULEMAP_FILE = "Target Support Files/TrustDecision/TrustDecision.modulemap"; + PRODUCT_MODULE_NAME = TrustDecision; + PRODUCT_NAME = TrustDecision; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 16A185DEA550E557645C7C2D9BA6AEFA /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = BA155A3B8F6E8AA5A625E76BDB822789 /* Pods-AIGrammar-AIGrammarUITests.release.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; + CLANG_ENABLE_OBJC_WEAK = NO; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = "Target Support Files/Pods-AIGrammar-AIGrammarUITests/Pods-AIGrammar-AIGrammarUITests-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MACH_O_TYPE = staticlib; + MODULEMAP_FILE = "Target Support Files/Pods-AIGrammar-AIGrammarUITests/Pods-AIGrammar-AIGrammarUITests.modulemap"; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 1A06525791970D35560E5992D78207B8 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = CC8269303CF6762EC44FE4C80EAD89D7 /* BlueRSA.release.xcconfig */; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = NO; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/BlueRSA/BlueRSA-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/BlueRSA/BlueRSA-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 10.3; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MODULEMAP_FILE = "Target Support Files/BlueRSA/BlueRSA.modulemap"; + PRODUCT_MODULE_NAME = CryptorRSA; + PRODUCT_NAME = CryptorRSA; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 1D85203FE6F13773EAC8C7E9C7B6134C /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 49DC68DCD598D7E998E7F413F7175874 /* SwiftJWT.debug.xcconfig */; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = NO; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/SwiftJWT/SwiftJWT-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/SwiftJWT/SwiftJWT-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MODULEMAP_FILE = "Target Support Files/SwiftJWT/SwiftJWT.modulemap"; + PRODUCT_MODULE_NAME = SwiftJWT; + PRODUCT_NAME = SwiftJWT; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_VERSION = 5.1; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 290C81E84B84039A818C93E547BDACC1 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 0B27561A5C6CD6C04D7057C8989055DD /* Logging.release.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/Logging/Logging-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/Logging/Logging-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MODULEMAP_FILE = "Target Support Files/Logging/Logging.modulemap"; + PRODUCT_MODULE_NAME = Logging; + PRODUCT_NAME = Logging; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 2AF779BF8BF109C4877209F7063ACAEF /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 5F46B5BFB9A87CD3FB6D5EFCD2C8DD31 /* BlueECC.debug.xcconfig */; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = NO; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/BlueECC/BlueECC-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/BlueECC/BlueECC-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MODULEMAP_FILE = "Target Support Files/BlueECC/BlueECC.modulemap"; + PRODUCT_MODULE_NAME = CryptorECC; + PRODUCT_NAME = CryptorECC; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 2C249EB49B0EDA41E6F30F8FA921A0AB /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 10D8544DE75F5F119B166CEBBFB05BFE /* Alamofire.release.xcconfig */; + buildSettings = { + CODE_SIGNING_ALLOWED = NO; + CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/Alamofire"; + IBSC_MODULE = Alamofire; + INFOPLIST_FILE = "Target Support Files/Alamofire/ResourceBundle-Alamofire-Alamofire-Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + PRODUCT_NAME = Alamofire; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + WRAPPER_EXTENSION = bundle; + }; + name = Release; + }; + 30E0B9EFD9A5C45D0D351231E81B30B3 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = 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_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "POD_CONFIGURATION_RELEASE=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + STRIP_INSTALLED_PRODUCT = NO; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + SWIFT_VERSION = 5.0; + SYMROOT = "${SRCROOT}/../build"; + }; + name = Release; + }; + 4229CDA84F6FFB91EB3582D768947DA1 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9A3E80A5CCD67032BA2B4420062D4792 /* KituraContracts.release.xcconfig */; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = NO; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/KituraContracts/KituraContracts-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/KituraContracts/KituraContracts-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MODULEMAP_FILE = "Target Support Files/KituraContracts/KituraContracts.modulemap"; + PRODUCT_MODULE_NAME = KituraContracts; + PRODUCT_NAME = KituraContracts; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 5AC55F7197A90179C135ABBDABEDFEFF /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = F909BC45DBBB78625AF9DA9AA3ED368E /* Pods-AIGrammar.release.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; + CLANG_ENABLE_OBJC_WEAK = NO; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = "Target Support Files/Pods-AIGrammar/Pods-AIGrammar-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MACH_O_TYPE = staticlib; + MODULEMAP_FILE = "Target Support Files/Pods-AIGrammar/Pods-AIGrammar.modulemap"; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 6C82A65C53CE46EC576298B3275BC3C2 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = FAF9946AEACD96D9BA9F39482C7FE96A /* Alamofire.debug.xcconfig */; + buildSettings = { + CODE_SIGNING_ALLOWED = NO; + CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/Alamofire"; + IBSC_MODULE = Alamofire; + INFOPLIST_FILE = "Target Support Files/Alamofire/ResourceBundle-Alamofire-Alamofire-Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + PRODUCT_NAME = Alamofire; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + WRAPPER_EXTENSION = bundle; + }; + name = Debug; + }; + 714A038955EDD712335B7293B4D7DAB3 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = FAF9946AEACD96D9BA9F39482C7FE96A /* Alamofire.debug.xcconfig */; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = NO; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/Alamofire/Alamofire-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/Alamofire/Alamofire-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MODULEMAP_FILE = "Target Support Files/Alamofire/Alamofire.modulemap"; + PRODUCT_MODULE_NAME = Alamofire; + PRODUCT_NAME = Alamofire; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_VERSION = 5; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 7635AFB9061AE494EBF0FEFC6A6F5BF4 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = BC198EEAA48B17C1A01B2FD7CF631C45 /* SwiftyBeaver.release.xcconfig */; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = NO; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/SwiftyBeaver/SwiftyBeaver-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/SwiftyBeaver/SwiftyBeaver-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 13.4; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MODULEMAP_FILE = "Target Support Files/SwiftyBeaver/SwiftyBeaver.modulemap"; + PRODUCT_MODULE_NAME = SwiftyBeaver; + PRODUCT_NAME = SwiftyBeaver; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_VERSION = 5.1; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 7EEA66CBEDC2A5249A25E176800F21F0 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 2F10E968E0EA4CAA7858662305EF4A7A /* Logging.debug.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/Logging/Logging-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/Logging/Logging-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MODULEMAP_FILE = "Target Support Files/Logging/Logging.modulemap"; + PRODUCT_MODULE_NAME = Logging; + PRODUCT_NAME = Logging; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 8471D00649AB05CDBB684CF7817B106B /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = CFE372985A232CD8C5825DC8943D6944 /* SwiftJWT.release.xcconfig */; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = NO; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/SwiftJWT/SwiftJWT-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/SwiftJWT/SwiftJWT-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MODULEMAP_FILE = "Target Support Files/SwiftJWT/SwiftJWT.modulemap"; + PRODUCT_MODULE_NAME = SwiftJWT; + PRODUCT_NAME = SwiftJWT; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_VERSION = 5.1; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 897D76141C04F119EB50C50F68E097B6 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 22AC8CED52EAAE1BC6F8494CA8A43050 /* KituraContracts.debug.xcconfig */; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = NO; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/KituraContracts/KituraContracts-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/KituraContracts/KituraContracts-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MODULEMAP_FILE = "Target Support Files/KituraContracts/KituraContracts.modulemap"; + PRODUCT_MODULE_NAME = KituraContracts; + PRODUCT_NAME = KituraContracts; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 921DE7A623D75F52E1686A6B9C698703 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 878A3266C4D7514338A19E8F3F7C3F92 /* BlueRSA.debug.xcconfig */; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = NO; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/BlueRSA/BlueRSA-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/BlueRSA/BlueRSA-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 10.3; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MODULEMAP_FILE = "Target Support Files/BlueRSA/BlueRSA.modulemap"; + PRODUCT_MODULE_NAME = CryptorRSA; + PRODUCT_NAME = CryptorRSA; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 9A3CEFFD69741B5BA93A669A2F8FA76D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 881A178E10B4EB1EAE869F754E904097 /* Pods-AIGrammar.debug.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; + CLANG_ENABLE_OBJC_WEAK = NO; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = "Target Support Files/Pods-AIGrammar/Pods-AIGrammar-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MACH_O_TYPE = staticlib; + MODULEMAP_FILE = "Target Support Files/Pods-AIGrammar/Pods-AIGrammar.modulemap"; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 9FED8A91E572D0ADA2100D102063F093 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = DB2D26CA5BC68099012CEC8C47BB5F71 /* Pods-AIGrammarTests.debug.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; + CLANG_ENABLE_OBJC_WEAK = NO; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = "Target Support Files/Pods-AIGrammarTests/Pods-AIGrammarTests-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MACH_O_TYPE = staticlib; + MODULEMAP_FILE = "Target Support Files/Pods-AIGrammarTests/Pods-AIGrammarTests.modulemap"; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + A31A448F8B5BD280EC849B6D7AD9E578 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 2933AF5131B9637CCCB2E3A5EEF86C75 /* Pods-AIGrammarTests.release.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; + CLANG_ENABLE_OBJC_WEAK = NO; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = "Target Support Files/Pods-AIGrammarTests/Pods-AIGrammarTests-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MACH_O_TYPE = staticlib; + MODULEMAP_FILE = "Target Support Files/Pods-AIGrammarTests/Pods-AIGrammarTests.modulemap"; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + A93A58912B57D4586286858408261DF1 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = BC198EEAA48B17C1A01B2FD7CF631C45 /* SwiftyBeaver.release.xcconfig */; + buildSettings = { + CODE_SIGNING_ALLOWED = NO; + CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/SwiftyBeaver"; + IBSC_MODULE = SwiftyBeaver; + INFOPLIST_FILE = "Target Support Files/SwiftyBeaver/ResourceBundle-SwiftyBeaver-SwiftyBeaver-Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 13.4; + PRODUCT_NAME = SwiftyBeaver; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + WRAPPER_EXTENSION = bundle; + }; + name = Release; + }; + B6907E049004B070A9FD38C7FBA7C345 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 13DBB992142C26E7749EA1F79E78F2B2 /* LoggerAPI.release.xcconfig */; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = NO; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/LoggerAPI/LoggerAPI-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/LoggerAPI/LoggerAPI-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MODULEMAP_FILE = "Target Support Files/LoggerAPI/LoggerAPI.modulemap"; + PRODUCT_MODULE_NAME = LoggerAPI; + PRODUCT_NAME = LoggerAPI; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_VERSION = 5.1; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + BE9EB8EE6C2C6B744AFC5F497A58AC67 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 98526239ED2F634672C32610933FB8E7 /* BlueCryptor.release.xcconfig */; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = NO; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/BlueCryptor/BlueCryptor-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/BlueCryptor/BlueCryptor-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MODULEMAP_FILE = "Target Support Files/BlueCryptor/BlueCryptor.modulemap"; + PRODUCT_MODULE_NAME = Cryptor; + PRODUCT_NAME = Cryptor; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + C223968F4C9E8FF3B495C4ACC36BAC20 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = B3F3A2726E30CE89E01164BE4896D098 /* LoggerAPI.debug.xcconfig */; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = NO; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/LoggerAPI/LoggerAPI-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/LoggerAPI/LoggerAPI-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MODULEMAP_FILE = "Target Support Files/LoggerAPI/LoggerAPI.modulemap"; + PRODUCT_MODULE_NAME = LoggerAPI; + PRODUCT_NAME = LoggerAPI; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_VERSION = 5.1; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + CA60CF70D0AF64CB6C7F697460FBE2FE /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 10D8544DE75F5F119B166CEBBFB05BFE /* Alamofire.release.xcconfig */; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = NO; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/Alamofire/Alamofire-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/Alamofire/Alamofire-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MODULEMAP_FILE = "Target Support Files/Alamofire/Alamofire.modulemap"; + PRODUCT_MODULE_NAME = Alamofire; + PRODUCT_NAME = Alamofire; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_VERSION = 5; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + CB2DCDB4F0CC18E7271328F5C050CA78 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = C6B48ECD2D14D6E16DDF3CB731D6480D /* BlueCryptor.debug.xcconfig */; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = NO; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/BlueCryptor/BlueCryptor-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/BlueCryptor/BlueCryptor-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MODULEMAP_FILE = "Target Support Files/BlueCryptor/BlueCryptor.modulemap"; + PRODUCT_MODULE_NAME = Cryptor; + PRODUCT_NAME = Cryptor; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + DED5F4D11EC4AE264F4172C2623330E0 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 2C9350326F251EA74192A5F7194F7680 /* TrustDecision.debug.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/TrustDecision/TrustDecision-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/TrustDecision/TrustDecision-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MACH_O_TYPE = staticlib; + MODULEMAP_FILE = "Target Support Files/TrustDecision/TrustDecision.modulemap"; + PRODUCT_MODULE_NAME = TrustDecision; + PRODUCT_NAME = TrustDecision; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + E22AE5DE7301FDDC2CE55272DEAAB184 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 5909292CACF3FD0B79E4348B60B024AD /* SwiftyBeaver.debug.xcconfig */; + buildSettings = { + CODE_SIGNING_ALLOWED = NO; + CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/SwiftyBeaver"; + IBSC_MODULE = SwiftyBeaver; + INFOPLIST_FILE = "Target Support Files/SwiftyBeaver/ResourceBundle-SwiftyBeaver-SwiftyBeaver-Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 13.4; + PRODUCT_NAME = SwiftyBeaver; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + WRAPPER_EXTENSION = bundle; + }; + name = Debug; + }; + F0C8E14ED1EC5B4A431525BB73893268 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9DB8603F150521A9B8621FD6ACE6FAB3 /* BlueECC.release.xcconfig */; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = NO; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/BlueECC/BlueECC-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/BlueECC/BlueECC-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MODULEMAP_FILE = "Target Support Files/BlueECC/BlueECC.modulemap"; + PRODUCT_MODULE_NAME = CryptorECC; + PRODUCT_NAME = CryptorECC; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + F4FF6A0D1970CA9705974E3CB2134802 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = 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_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "POD_CONFIGURATION_DEBUG=1", + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + STRIP_INSTALLED_PRODUCT = NO; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + SYMROOT = "${SRCROOT}/../build"; + }; + name = Debug; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 0F37CC2D26A01275673014D311767686 /* Build configuration list for PBXNativeTarget "Pods-AIGrammar" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 9A3CEFFD69741B5BA93A669A2F8FA76D /* Debug */, + 5AC55F7197A90179C135ABBDABEDFEFF /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 0F6221E72FA47FA16485B1AFA5FD6EFA /* Build configuration list for PBXNativeTarget "SwiftyBeaver-SwiftyBeaver" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + E22AE5DE7301FDDC2CE55272DEAAB184 /* Debug */, + A93A58912B57D4586286858408261DF1 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 12BEBFE4FF4C9C0834ED6E2450BD21AC /* Build configuration list for PBXNativeTarget "BlueRSA" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 921DE7A623D75F52E1686A6B9C698703 /* Debug */, + 1A06525791970D35560E5992D78207B8 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 2A64AF72071AF5E51B7A66AAA76306ED /* Build configuration list for PBXNativeTarget "KituraContracts" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 897D76141C04F119EB50C50F68E097B6 /* Debug */, + 4229CDA84F6FFB91EB3582D768947DA1 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 2BB2CC43CF1E3A5FD5CF42C64535B098 /* Build configuration list for PBXNativeTarget "Pods-AIGrammar-AIGrammarUITests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 09BB305219738FF38BD3F3797074BA73 /* Debug */, + 16A185DEA550E557645C7C2D9BA6AEFA /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 40392276BA824C7639B8D52571DF4C31 /* Build configuration list for PBXNativeTarget "Pods-AIGrammarTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 9FED8A91E572D0ADA2100D102063F093 /* Debug */, + A31A448F8B5BD280EC849B6D7AD9E578 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 4821239608C13582E20E6DA73FD5F1F9 /* Build configuration list for PBXProject "Pods" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F4FF6A0D1970CA9705974E3CB2134802 /* Debug */, + 30E0B9EFD9A5C45D0D351231E81B30B3 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 5D3C6CDB3FEB6232A30D8F4BF7EF89E7 /* Build configuration list for PBXNativeTarget "Logging" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7EEA66CBEDC2A5249A25E176800F21F0 /* Debug */, + 290C81E84B84039A818C93E547BDACC1 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 5DAC03B644034D84EE2DFC8C5B6F83AC /* Build configuration list for PBXNativeTarget "BlueCryptor" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + CB2DCDB4F0CC18E7271328F5C050CA78 /* Debug */, + BE9EB8EE6C2C6B744AFC5F497A58AC67 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 7602F5D278AA2D1C563D4F665525B719 /* Build configuration list for PBXNativeTarget "BlueECC" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 2AF779BF8BF109C4877209F7063ACAEF /* Debug */, + F0C8E14ED1EC5B4A431525BB73893268 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 78CF2AECA59ABDD5FEBA1ECBFE5FBE21 /* Build configuration list for PBXNativeTarget "Alamofire-Alamofire" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 6C82A65C53CE46EC576298B3275BC3C2 /* Debug */, + 2C249EB49B0EDA41E6F30F8FA921A0AB /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + A46F003C01F7C71DB37AD599ADD77799 /* Build configuration list for PBXNativeTarget "SwiftyBeaver" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 04D0F2DC69230A2680873BF4808BC9E6 /* Debug */, + 7635AFB9061AE494EBF0FEFC6A6F5BF4 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + B5691EDC0B336F545FF5FD0D14C865F9 /* Build configuration list for PBXNativeTarget "TrustDecision" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + DED5F4D11EC4AE264F4172C2623330E0 /* Debug */, + 0AC09DABDFF4C9A7B4F2D4D1E23767C3 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + D8D6D21B85F08E28672F2885BBF5D200 /* Build configuration list for PBXNativeTarget "SwiftJWT" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1D85203FE6F13773EAC8C7E9C7B6134C /* Debug */, + 8471D00649AB05CDBB684CF7817B106B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + E8073BA5B14E50E5EA109E0EE5B0886A /* Build configuration list for PBXNativeTarget "LoggerAPI" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C223968F4C9E8FF3B495C4ACC36BAC20 /* Debug */, + B6907E049004B070A9FD38C7FBA7C345 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FA92BF783257A026FB1E05B4B536DD6E /* Build configuration list for PBXNativeTarget "Alamofire" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 714A038955EDD712335B7293B4D7DAB3 /* Debug */, + CA60CF70D0AF64CB6C7F697460FBE2FE /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = BFDFE7DC352907FC980B868725387E98 /* Project object */; +} diff --git a/Pods/Pods.xcodeproj/xcuserdata/oscar.xcuserdatad/xcschemes/Alamofire-Alamofire.xcscheme b/Pods/Pods.xcodeproj/xcuserdata/oscar.xcuserdatad/xcschemes/Alamofire-Alamofire.xcscheme new file mode 100644 index 0000000..ba024d1 --- /dev/null +++ b/Pods/Pods.xcodeproj/xcuserdata/oscar.xcuserdatad/xcschemes/Alamofire-Alamofire.xcscheme @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Pods/Pods.xcodeproj/xcuserdata/oscar.xcuserdatad/xcschemes/Alamofire.xcscheme b/Pods/Pods.xcodeproj/xcuserdata/oscar.xcuserdatad/xcschemes/Alamofire.xcscheme new file mode 100644 index 0000000..fe38b07 --- /dev/null +++ b/Pods/Pods.xcodeproj/xcuserdata/oscar.xcuserdatad/xcschemes/Alamofire.xcscheme @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Pods/Pods.xcodeproj/xcuserdata/oscar.xcuserdatad/xcschemes/BlueCryptor.xcscheme b/Pods/Pods.xcodeproj/xcuserdata/oscar.xcuserdatad/xcschemes/BlueCryptor.xcscheme new file mode 100644 index 0000000..f13b277 --- /dev/null +++ b/Pods/Pods.xcodeproj/xcuserdata/oscar.xcuserdatad/xcschemes/BlueCryptor.xcscheme @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Pods/Pods.xcodeproj/xcuserdata/oscar.xcuserdatad/xcschemes/BlueECC.xcscheme b/Pods/Pods.xcodeproj/xcuserdata/oscar.xcuserdatad/xcschemes/BlueECC.xcscheme new file mode 100644 index 0000000..e8fcd95 --- /dev/null +++ b/Pods/Pods.xcodeproj/xcuserdata/oscar.xcuserdatad/xcschemes/BlueECC.xcscheme @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Pods/Pods.xcodeproj/xcuserdata/oscar.xcuserdatad/xcschemes/BlueRSA.xcscheme b/Pods/Pods.xcodeproj/xcuserdata/oscar.xcuserdatad/xcschemes/BlueRSA.xcscheme new file mode 100644 index 0000000..989d4e9 --- /dev/null +++ b/Pods/Pods.xcodeproj/xcuserdata/oscar.xcuserdatad/xcschemes/BlueRSA.xcscheme @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Pods/Pods.xcodeproj/xcuserdata/oscar.xcuserdatad/xcschemes/KituraContracts.xcscheme b/Pods/Pods.xcodeproj/xcuserdata/oscar.xcuserdatad/xcschemes/KituraContracts.xcscheme new file mode 100644 index 0000000..eafb5e2 --- /dev/null +++ b/Pods/Pods.xcodeproj/xcuserdata/oscar.xcuserdatad/xcschemes/KituraContracts.xcscheme @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Pods/Pods.xcodeproj/xcuserdata/oscar.xcuserdatad/xcschemes/LoggerAPI.xcscheme b/Pods/Pods.xcodeproj/xcuserdata/oscar.xcuserdatad/xcschemes/LoggerAPI.xcscheme new file mode 100644 index 0000000..b613429 --- /dev/null +++ b/Pods/Pods.xcodeproj/xcuserdata/oscar.xcuserdatad/xcschemes/LoggerAPI.xcscheme @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Pods/Pods.xcodeproj/xcuserdata/oscar.xcuserdatad/xcschemes/Logging.xcscheme b/Pods/Pods.xcodeproj/xcuserdata/oscar.xcuserdatad/xcschemes/Logging.xcscheme new file mode 100644 index 0000000..e0b52f4 --- /dev/null +++ b/Pods/Pods.xcodeproj/xcuserdata/oscar.xcuserdatad/xcschemes/Logging.xcscheme @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Pods/Pods.xcodeproj/xcuserdata/oscar.xcuserdatad/xcschemes/Pods-AIGrammar-AIGrammarUITests.xcscheme b/Pods/Pods.xcodeproj/xcuserdata/oscar.xcuserdatad/xcschemes/Pods-AIGrammar-AIGrammarUITests.xcscheme new file mode 100644 index 0000000..142915c --- /dev/null +++ b/Pods/Pods.xcodeproj/xcuserdata/oscar.xcuserdatad/xcschemes/Pods-AIGrammar-AIGrammarUITests.xcscheme @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Pods/Pods.xcodeproj/xcuserdata/oscar.xcuserdatad/xcschemes/Pods-AIGrammar.xcscheme b/Pods/Pods.xcodeproj/xcuserdata/oscar.xcuserdatad/xcschemes/Pods-AIGrammar.xcscheme new file mode 100644 index 0000000..94db269 --- /dev/null +++ b/Pods/Pods.xcodeproj/xcuserdata/oscar.xcuserdatad/xcschemes/Pods-AIGrammar.xcscheme @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Pods/Pods.xcodeproj/xcuserdata/oscar.xcuserdatad/xcschemes/Pods-AIGrammarTests.xcscheme b/Pods/Pods.xcodeproj/xcuserdata/oscar.xcuserdatad/xcschemes/Pods-AIGrammarTests.xcscheme new file mode 100644 index 0000000..dba47c7 --- /dev/null +++ b/Pods/Pods.xcodeproj/xcuserdata/oscar.xcuserdatad/xcschemes/Pods-AIGrammarTests.xcscheme @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Pods/Pods.xcodeproj/xcuserdata/oscar.xcuserdatad/xcschemes/SwiftJWT.xcscheme b/Pods/Pods.xcodeproj/xcuserdata/oscar.xcuserdatad/xcschemes/SwiftJWT.xcscheme new file mode 100644 index 0000000..98aec8e --- /dev/null +++ b/Pods/Pods.xcodeproj/xcuserdata/oscar.xcuserdatad/xcschemes/SwiftJWT.xcscheme @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Pods/Pods.xcodeproj/xcuserdata/oscar.xcuserdatad/xcschemes/SwiftyBeaver-SwiftyBeaver.xcscheme b/Pods/Pods.xcodeproj/xcuserdata/oscar.xcuserdatad/xcschemes/SwiftyBeaver-SwiftyBeaver.xcscheme new file mode 100644 index 0000000..2b2fc2a --- /dev/null +++ b/Pods/Pods.xcodeproj/xcuserdata/oscar.xcuserdatad/xcschemes/SwiftyBeaver-SwiftyBeaver.xcscheme @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Pods/Pods.xcodeproj/xcuserdata/oscar.xcuserdatad/xcschemes/SwiftyBeaver.xcscheme b/Pods/Pods.xcodeproj/xcuserdata/oscar.xcuserdatad/xcschemes/SwiftyBeaver.xcscheme new file mode 100644 index 0000000..2aea446 --- /dev/null +++ b/Pods/Pods.xcodeproj/xcuserdata/oscar.xcuserdatad/xcschemes/SwiftyBeaver.xcscheme @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Pods/Pods.xcodeproj/xcuserdata/oscar.xcuserdatad/xcschemes/TrustDecision.xcscheme b/Pods/Pods.xcodeproj/xcuserdata/oscar.xcuserdatad/xcschemes/TrustDecision.xcscheme new file mode 100644 index 0000000..778e599 --- /dev/null +++ b/Pods/Pods.xcodeproj/xcuserdata/oscar.xcuserdatad/xcschemes/TrustDecision.xcscheme @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Pods/Pods.xcodeproj/xcuserdata/oscar.xcuserdatad/xcschemes/xcschememanagement.plist b/Pods/Pods.xcodeproj/xcuserdata/oscar.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..c80d170 --- /dev/null +++ b/Pods/Pods.xcodeproj/xcuserdata/oscar.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,86 @@ + + + + + SchemeUserState + + Alamofire-Alamofire.xcscheme + + isShown + + + Alamofire.xcscheme + + isShown + + + BlueCryptor.xcscheme + + isShown + + + BlueECC.xcscheme + + isShown + + + BlueRSA.xcscheme + + isShown + + + KituraContracts.xcscheme + + isShown + + + LoggerAPI.xcscheme + + isShown + + + Logging.xcscheme + + isShown + + + Pods-AIGrammar-AIGrammarUITests.xcscheme + + isShown + + + Pods-AIGrammar.xcscheme + + isShown + + + Pods-AIGrammarTests.xcscheme + + isShown + + + SwiftJWT.xcscheme + + isShown + + + SwiftyBeaver-SwiftyBeaver.xcscheme + + isShown + + + SwiftyBeaver.xcscheme + + isShown + + + TrustDecision.xcscheme + + isShown + + + + SuppressBuildableAutocreation + + + diff --git a/Pods/SwiftJWT/LICENSE b/Pods/SwiftJWT/LICENSE new file mode 100644 index 0000000..f433b1a --- /dev/null +++ b/Pods/SwiftJWT/LICENSE @@ -0,0 +1,177 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/Pods/SwiftJWT/README.md b/Pods/SwiftJWT/README.md new file mode 100644 index 0000000..665ad78 --- /dev/null +++ b/Pods/SwiftJWT/README.md @@ -0,0 +1,245 @@ +

+ +Kitura + +

+ + +

+ +APIDoc + + +Build Status - Master + +macOS +Linux +Apache 2 + +Slack Status + +

+ + +# SwiftJWT +An implementation of [JSON Web Token](https://tools.ietf.org/html/rfc7519) using Swift. JWTs offer a lightweight and compact format for transmitting information between parties, and the information can be verified and trusted due to JWTs being digitally signed. + +For more information on JSON Web Tokens, their use cases and how they work, we recommend visiting [jwt.io](https://jwt.io/introduction/). + +**Reminder:** JWTs sent as JWS do **not** encrypt data, so never send anything sensitive or confidential in a JWT. This library does not currently support JWE. + +## Swift version +The latest version of Swift-JWT requires **Swift 4.0** or later. You can download this version of the Swift binaries by following this [link](https://swift.org/download/). Compatibility with other Swift versions is not guaranteed. + +## Usage + +### Swift Package Manager + +#### Add dependencies +Add the `Swift-JWT` package to the dependencies within your application’s `Package.swift` file. Substitute `"x.x.x"` with the latest `Swift-JWT` [release](https://github.com/IBM-Swift/Swift-JWT/releases). +```swift +.package(url: "https://github.com/IBM-Swift/Swift-JWT.git", from: "x.x.x") +``` +Add `SwiftJWT` to your target's dependencies: +```swift +.target(name: "example", dependencies: ["SwiftJWT"]), +``` +#### Import package +```swift +import SwiftJWT +``` + +### Cocoapods + +To include `Swift-JWT` in a project using CocoaPods, add `SwiftJWT` to your Podfile: +``` +pod 'SwiftJWT' +``` +## Getting Started + +### The JWT model + +In its compact form, a JSON Web Tokens consist of three sections of Base64Url encoded JSON, separated by dots (.). +These section are: Headers, Claims and the Signature. +Therefore, a JWT typically looks like the following: xxxxx.yyyyy.zzzzz + +#### Header + +The Header struct contains the fields of the JSON Web Token header as defined by [RFC7515](https://tools.ietf.org/html/rfc7515#section-4). +The "typ" header will default to "JWT". The "alg" header will be set to the algorithm name when you sign the JWT. +The other Header fields can be set when initializing the Header or by changing them directly on the Header object. + +```swift +let myHeader = Header(kid: "KeyID1") +``` + +#### Claims + +Claims are statements about an entity (typically, the user) and additional data. +The Claims are defined by creating a Swift type that conforms to the `Claims` protocol. The fields of this type represent the information that will be shared using the JWT. + +A list of recommended claims is defined in [RFC7519](https://tools.ietf.org/html/rfc7519#section-4.1). + +```swift +struct MyClaims: Claims { + let iss: String + let sub: String + let exp: Date + let admin: Bool +} +let myClaims = MyClaims(iss: "Kitura", sub: "John", exp: Date(timeIntervalSinceNow: 3600), admin: true) +``` +##### ClaimsExamples + +This library includes some example `Claims` structs as defined by their online specifications: + - `ClaimsStandardJWT` as defined in [RFC7519](https://tools.ietf.org/html/rfc7519#section-4.1). + - `ClaimsMicroProfile` as defined [here](http://microprofile.io/project/eclipse/microprofile-jwt-auth/spec/src/main/asciidoc/interoperability.asciidoc). + - `ClaimsOpenID.swift` as defined [here](https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims). + +#### JWT + +The JWT struct represents the `Header` and `Claims` of a JSON Web Token. +You can initialize a JWT by decoding a JWT String, or by providing the JWT Header and Claims. + +```swift +let myJWT = JWT(header: myHeader, claims: myClaims) +``` + +### Signing and Verifying JSON web tokens + +#### Creating public and private keys + +To sign and verify a JWT using an RSA algorithm, you must provide a public and private key. This could be the contents of a .key file generated via the following Terminal commands: + +``` +$ ssh-keygen -t rsa -b 4096 -m PEM -f privateKey.key +# Don't add a passphrase +$ openssl rsa -in privateKey.key -pubout -outform PEM -out privateKey.key.pub +``` + +This will create a public and private key pair on your system, and the contents of the private key can be passed into a Swift variable using the following code: + +```swift +let privateKeyPath = URL(fileURLWithPath: getAbsolutePath(relativePath: "/path/to/privateKey.key")) +let privateKey: Data = try Data(contentsOf: privateKeyPath, options: .alwaysMapped) +let publicKeyPath = URL(fileURLWithPath: getAbsolutePath(relativePath: "/path/to/publicKey.key")) +let publicKey: Data = try Data(contentsOf: publicKeyPath, options: .alwaysMapped) +``` + +For details on creating elliptic curve public and private keys, view the [BlueECC README.txt](https://github.com/IBM-Swift/BlueECC). + +#### Sign a JWT using a JWTSigner + +The struct JWTSigner contains the algorithms that can be used to sign a JWT. + +Initialize a JWTSigner using the static function corresponding to the desired RSA algorithm: + +```swift +let jwtSigner = JWTSigner.rs256(privateKey: privateKey) +``` +To generate a signed JWT string, call the `sign` function on your JWT instance, passing in a JWTSigner: + +```swift +let signedJWT = try myJWT.sign(using: jwtSigner) +``` + +The resulting `signedJWT` will be a `String` of the form: +``` +.. +``` +**Note:** The sign function sets the alg (algorithm) field of the header. + +#### Verify a JWT using JWTVerifier + +The struct JWTVerifier contains the algorithms that can be used to verify a JWT. + +Initialize a JWTVerifier using the static function corresponding to the desired RSA algorithm: + +```swift +let jwtVerifier = JWTVerifier.rs256(publicKey: publicKey) +``` +To verify a signed JWT string, call the static `verify` function, passing in your JWT string and the JWTVerifier: + +```swift +let verified = JWT.verify(signedJWT, using: jwtVerifier) +``` +The `verified` field will be a `bool` that is true if the signature is verified. + + +#### Supported Algorithms + +The supported algorithms for signing and verifying JWTs are: + +* RS256 - RSASSA-PKCS1-v1_5 using SHA-256 +* RS384 - RSASSA-PKCS1-v1_5 using SHA-384 +* RS512 - RSASSA-PKCS1-v1_5 using SHA-512 +* HS256 - HMAC using using SHA-256 +* HS384 - HMAC using using SHA-384 +* HS512 - HMAC using using SHA-512 +* ES256 - ECDSA using using SHA-256 and a P-256 curve +* ES384 - ECDSA using using SHA-384 and a P-384 curve +* ES512 - ECDSA using using SHA-512 and a P-521 curve +* PS256 - RSA-PSS using SHA-256 +* PS384 - RSA-PSS using SHA-384 +* PS512 - RSA-PSS using SHA-512 +* none - Don't sign or verify the JWT + +Note: ECDSA and RSA-PSS algorithms require a minimum Swift version of 4.1. + +### Validate claims + +The `validateClaims` function validates the standard `Date` claims of a JWT instance. +The following claims are validated if they are present in the `Claims` object: +- exp (expiration date) +- nbf (not before date) +- iat (issued at date) + +The method returns `ValidateClaimsResult` - an struct that list the various reasons for validation failure. +If the validation succeeds `ValidateClaimsResult.success` is returned. +The `leeway` parameter is the `TimeInterval` in seconds that a standard `Date` claim will be valid outside of the specified time. This can be used to account for clock skew between issuers and verifiers. + +```swift +let validationResult = verified.validateClaims(leeway: 10) +if validationResult != .success { + print("Claims validation failed: ", validationResult) +} +``` + +### Decode a JWT from a JWT string + +A JWT struct can be initialized from a JWT string. If a JWTVerifier is provided it will be used to verify the signature before initialization + +```swift +let newJWT = try JWT(jwtString: signedJWT, verifier: jwtVerifier) +``` + +### JWTEncoder and JWTDecoder + +The JWTEncoder and JWTDecoder classes encode and decode JWT Strings using the same API as JSONEncoder and JSONDecoder: + +```swift + let jwtEncoder = JWTEncoder(jwtSigner: jwtSigner) + let jwtString = try jwtEncoder.encodeToString(myJWT) + + let jwtDecoder = JWTDecoder(jwtVerifier: jwtVerifier) + let jwt = try jwtDecoder.decode(JWT.self, fromString: jwtString) +``` + +Because JWTEncoder and JWTDecoder conform to [KituraContract's](https://github.com/IBM-Swift/KituraContracts/blob/master/Sources/KituraContracts/Contracts.swift) BodyEncoder and BodyDecoder protocols, they can be used as a [custom coder](https://developer.ibm.com/swift/2018/09/01/kitura-custom-encoders-and-decoders/) in Codable routes for sending and receiving JWTs: + +```swift + router.encoders[MediaType(type: .application, subType: "jwt")] = { return jwtEncoder } + router.decoders[MediaType(type: .application, subType: "jwt")] = { return jwtDecoder } +``` + +This allows for the use of JWT's in information exchange. By sending and receiving JWT's you can ensure the sending is who they say they are and verify the content hasn't been tampered with. + +## API Documentation +For more information visit our [API reference](https://ibm-swift.github.io/Swift-JWT/index.html). + +## Community + +We love to talk server-side Swift, and Kitura. Join our [Slack](http://swift-at-ibm-slack.mybluemix.net/) to meet the team! + +## License +This library is licensed under Apache 2.0. Full license text is available in [LICENSE](https://github.com/IBM-Swift/Swift-JWT/blob/master/LICENSE). diff --git a/Pods/SwiftJWT/Sources/SwiftJWT/BlueECDSA.swift b/Pods/SwiftJWT/Sources/SwiftJWT/BlueECDSA.swift new file mode 100644 index 0000000..4f7e0f4 --- /dev/null +++ b/Pods/SwiftJWT/Sources/SwiftJWT/BlueECDSA.swift @@ -0,0 +1,110 @@ +/** + * Copyright IBM Corporation 2019 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +import CryptorECC +import LoggerAPI +import Foundation + +// Class for ECDSA signing using BlueECC +@available(OSX 10.13, iOS 11, tvOS 11.0, watchOS 4.0, *) +class BlueECSigner: SignerAlgorithm { + let name: String = "ECDSA" + + private let key: Data + private let curve: EllipticCurve + + // Initialize a signer using .utf8 encoded PEM private key. + init(key: Data, curve: EllipticCurve) { + self.key = key + self.curve = curve + } + + // Sign the header and claims to produce a signed JWT String + func sign(header: String, claims: String) throws -> String { + let unsignedJWT = header + "." + claims + guard let unsignedData = unsignedJWT.data(using: .utf8) else { + throw JWTError.invalidJWTString + } + let signature = try sign(unsignedData) + let signatureString = JWTEncoder.base64urlEncodedString(data: signature) + return header + "." + claims + "." + signatureString + } + + // send utf8 encoded `header.claims` to BlueECC for signing + private func sign(_ data: Data) throws -> Data { + guard let keyString = String(data: key, encoding: .utf8) else { + throw JWTError.invalidPrivateKey + } + let privateKey = try ECPrivateKey(key: keyString) + guard privateKey.curve == curve else { + throw JWTError.invalidPrivateKey + } + let signedData = try data.sign(with: privateKey) + return signedData.r + signedData.s + } +} + +// Class for ECDSA verifying using BlueECC +@available(OSX 10.13, iOS 11, tvOS 11.0, watchOS 4.0, *) +class BlueECVerifier: VerifierAlgorithm { + + let name: String = "ECDSA" + + private let key: Data + private let curve: EllipticCurve + + // Initialize a verifier using .utf8 encoded PEM public key. + init(key: Data, curve: EllipticCurve) { + self.key = key + self.curve = curve + } + + // Verify a signed JWT String + func verify(jwt: String) -> Bool { + let components = jwt.components(separatedBy: ".") + if components.count == 3 { + guard let signature = JWTDecoder.data(base64urlEncoded: components[2]), + let jwtData = (components[0] + "." + components[1]).data(using: .utf8) + else { + return false + } + return self.verify(signature: signature, for: jwtData) + } else { + return false + } + } + + // Send the base64URLencoded signature and `header.claims` to BlueECC for verification. + private func verify(signature: Data, for data: Data) -> Bool { + do { + guard let keyString = String(data: key, encoding: .utf8) else { + return false + } + let r = signature.subdata(in: 0 ..< signature.count/2) + let s = signature.subdata(in: signature.count/2 ..< signature.count) + let signature = try ECSignature(r: r, s: s) + let publicKey = try ECPublicKey(key: keyString) + guard publicKey.curve == curve else { + return false + } + return signature.verify(plaintext: data, using: publicKey) + } + catch { + Log.error("Verification failed: \(error)") + return false + } + } +} diff --git a/Pods/SwiftJWT/Sources/SwiftJWT/BlueHMAC.swift b/Pods/SwiftJWT/Sources/SwiftJWT/BlueHMAC.swift new file mode 100644 index 0000000..f04a10c --- /dev/null +++ b/Pods/SwiftJWT/Sources/SwiftJWT/BlueHMAC.swift @@ -0,0 +1,85 @@ +/** + * Copyright IBM Corporation 2017 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +import Cryptor +import LoggerAPI +import Foundation + +class BlueHMAC: SignerAlgorithm, VerifierAlgorithm { + let name: String = "HMAC" + + private let key: Data + private let algorithm: HMAC.Algorithm + + init(key: Data, algorithm: HMAC.Algorithm) { + self.key = key + self.algorithm = algorithm + } + + func sign(header: String, claims: String) throws -> String { + let unsignedJWT = header + "." + claims + guard let unsignedData = unsignedJWT.data(using: .utf8) else { + throw JWTError.invalidJWTString + } + let signature = try sign(unsignedData) + let signatureString = JWTEncoder.base64urlEncodedString(data: signature) + return header + "." + claims + "." + signatureString + } + + func sign(_ data: Data) throws -> Data { + guard #available(macOS 10.12, iOS 10.0, *) else { + Log.error("macOS 10.12.0 (Sierra) or higher or iOS 10.0 or higher is required by Cryptor") + throw JWTError.osVersionToLow + } + guard let hmac = HMAC(using: algorithm, key: key).update(data: data)?.final() else { + throw JWTError.invalidPrivateKey + } + #if swift(>=5.0) + return Data(hmac) + #else + return Data(bytes: hmac) + #endif + } + + + func verify(jwt: String) -> Bool { + let components = jwt.components(separatedBy: ".") + if components.count == 3 { + guard let signature = JWTDecoder.data(base64urlEncoded: components[2]), + let jwtData = (components[0] + "." + components[1]).data(using: .utf8) + else { + return false + } + return self.verify(signature: signature, for: jwtData) + } else { + return false + } + } + + func verify(signature: Data, for data: Data) -> Bool { + guard #available(macOS 10.12, iOS 10.0, *) else { + return false + } + do { + let expectedHMAC = try sign(data) + return expectedHMAC == signature + } + catch { + Log.error("Verification failed: \(error)") + return false + } + } +} diff --git a/Pods/SwiftJWT/Sources/SwiftJWT/BlueRSA.swift b/Pods/SwiftJWT/Sources/SwiftJWT/BlueRSA.swift new file mode 100644 index 0000000..fcf2d45 --- /dev/null +++ b/Pods/SwiftJWT/Sources/SwiftJWT/BlueRSA.swift @@ -0,0 +1,128 @@ +/** + * Copyright IBM Corporation 2017 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +import CryptorRSA +import LoggerAPI + +import Foundation + +class BlueRSA: SignerAlgorithm, VerifierAlgorithm { + let name: String = "RSA" + + private let key: Data + private let keyType: RSAKeyType + private let algorithm: Data.Algorithm + private let usePSS: Bool + + init(key: Data, keyType: RSAKeyType?=nil, algorithm: Data.Algorithm, usePSS: Bool = false) { + self.key = key + self.keyType = keyType ?? .publicKey + self.algorithm = algorithm + self.usePSS = usePSS + } + + func sign(header: String, claims: String) throws -> String { + let unsignedJWT = header + "." + claims + guard let unsignedData = unsignedJWT.data(using: .utf8) else { + throw JWTError.invalidJWTString + } + let signature = try sign(unsignedData) + let signatureString = JWTEncoder.base64urlEncodedString(data: signature) + return header + "." + claims + "." + signatureString + } + + func sign(_ data: Data) throws -> Data { + guard #available(macOS 10.12, iOS 10.3, tvOS 12.0, watchOS 3.3, *) else { + Log.error("macOS 10.12.0 (Sierra) or higher or iOS 10.0 or higher is required by CryptorRSA") + throw JWTError.osVersionToLow + } + // Convert PEM format to DER + let keyDer: Data + if let keyString = String(data: key, encoding: .utf8) { + let strippedKey = String(keyString.filter { !" \n\t\r".contains($0) }) + let pemComponents = strippedKey.components(separatedBy: "-----") + guard pemComponents.count >= 5 else { + throw JWTError.missingPEMHeaders + } + guard let der = Data(base64Encoded: pemComponents[2]) else { + throw JWTError.invalidPrivateKey + } + keyDer = der + } else { + keyDer = key + } + let privateKey = try CryptorRSA.createPrivateKey(with: keyDer) + let myPlaintext = CryptorRSA.createPlaintext(with: data) + guard let signedData = try myPlaintext.signed(with: privateKey, algorithm: algorithm, usePSS: usePSS) else { + throw JWTError.invalidPrivateKey + } + return signedData.data + } + + + func verify(jwt: String) -> Bool { + let components = jwt.components(separatedBy: ".") + if components.count == 3 { + guard let signature = JWTDecoder.data(base64urlEncoded: components[2]), + let jwtData = (components[0] + "." + components[1]).data(using: .utf8) + else { + return false + } + return self.verify(signature: signature, for: jwtData) + } else { + return false + } + } + + func verify(signature: Data, for data: Data) -> Bool { + guard #available(macOS 10.12, iOS 10.3, tvOS 12.0, watchOS 3.3, *) else { + return false + } + do { + var publicKey: CryptorRSA.PublicKey + switch keyType { + case .privateKey: + return false + case .publicKey: + // Convert PEM format to DER + let keyDer: Data + if let keyString = String(data: key, encoding: .utf8) { + let strippedKey = String(keyString.filter { !" \n\t\r".contains($0) }) + let pemComponents = strippedKey.components(separatedBy: "-----") + guard pemComponents.count >= 5 else { + return false + } + guard let der = Data(base64Encoded: pemComponents[2]) else { + return false + } + keyDer = der + } else { + keyDer = key + } + publicKey = try CryptorRSA.createPublicKey(with: keyDer) + case .certificate: + publicKey = try CryptorRSA.createPublicKey(extractingFrom: key) + } + let myPlaintext = CryptorRSA.createPlaintext(with: data) + let signedData = CryptorRSA.createSigned(with: signature) + return try myPlaintext.verify(with: publicKey, signature: signedData, algorithm: algorithm, usePSS: usePSS) + } + catch { + Log.error("Verification failed: \(error)") + return false + } + } +} diff --git a/Pods/SwiftJWT/Sources/SwiftJWT/Claims.swift b/Pods/SwiftJWT/Sources/SwiftJWT/Claims.swift new file mode 100644 index 0000000..a858fff --- /dev/null +++ b/Pods/SwiftJWT/Sources/SwiftJWT/Claims.swift @@ -0,0 +1,84 @@ +/** + * Copyright IBM Corporation 2018 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +import Foundation + +// MARK: Claims +/** + A protocol for representing the claims on a JSON web token. + https://tools.ietf.org/html/rfc7519#section-4.1 +### Usage Example: ### +```swift +struct AdminClaims: Claims { + var sub: String + var isAdmin: Bool + var exp: Date? +} + let jwt = JWT(claims: AdminClaims(sub: "Kitura", isAdmin: true, exp: Date(timeIntervalSinceNow: 3600))) +``` +*/ +public protocol Claims: Codable { + + /** + The "exp" (expiration time) claim identifies the expiration time on + or after which the JWT MUST NOT be accepted for processing. The + processing of the "exp" claim requires that the current date/time + MUST be before the expiration date/time listed in the "exp" claim. + Implementers MAY provide for some small leeway, usually no more than + a few minutes, to account for clock skew. + */ + var exp: Date? { get } + + /** + The "nbf" (not before) claim identifies the time before which the JWT + MUST NOT be accepted for processing. The processing of the "nbf" + claim requires that the current date/time MUST be after or equal to + the not-before date/time listed in the "nbf" claim. Implementers MAY + provide for some small leeway, usually no more than a few minutes, to + account for clock skew. + */ + var nbf: Date? { get } + + /** + The "iat" (issued at) claim identifies the time at which the JWT was + issued. This claim can be used to determine the age of the JWT. + */ + var iat: Date? { get } + + /// Encode the Claim object as a Base64 String. + func encode() throws -> String +} +public extension Claims { + + var exp: Date? { + return nil + } + + var nbf: Date? { + return nil + } + + var iat: Date? { + return nil + } + + func encode() throws -> String { + let jsonEncoder = JSONEncoder() + jsonEncoder.dateEncodingStrategy = .secondsSince1970 + let data = try jsonEncoder.encode(self) + return JWTEncoder.base64urlEncodedString(data: data) + } +} diff --git a/Pods/SwiftJWT/Sources/SwiftJWT/ClaimsExamples/ClaimsMicroProfile.swift b/Pods/SwiftJWT/Sources/SwiftJWT/ClaimsExamples/ClaimsMicroProfile.swift new file mode 100644 index 0000000..c77fba7 --- /dev/null +++ b/Pods/SwiftJWT/Sources/SwiftJWT/ClaimsExamples/ClaimsMicroProfile.swift @@ -0,0 +1,85 @@ +/** + * Copyright IBM Corporation 2018 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +import Foundation + +// MARK ClaimsMicroProfile + +/// A class representing the MicroProfile claims as listed in [MicroProfile specs](http://microprofile.io/project/eclipse/microprofile-jwt-auth/spec/src/main/asciidoc/interoperability.asciidoc). +public class ClaimsMicroProfile: Claims { + + /// Initialize a `ClaimsMicroProfile` + public init( + iss: String, + sub: String, + exp: Date, + iat: Date, + jti: String, + upn: String, + groups: [String] + ) { + self.iss = iss + self.sub = sub + self.exp = exp + self.iat = iat + self.jti = jti + self.upn = upn + self.groups = groups + } + + /** + The MP-JWT issuer. [RFC7519, Section 4.1.1](https://tools.ietf.org/html/rfc7519#section-4.1.1) + */ + public var iss: String + + /** + Identifies the principal that is the subject of the JWT. + */ + public var sub: String + + /** + Identifies the expiration time on or after which the JWT MUST NOT be accepted for processing. + */ + public var exp: Date + + /** + Identifies the time at which the JWT was issued. + */ + public var iat: Date + + /** + The "jti" (JWT ID) claim provides a unique identifier for the JWT. + The identifier value MUST be assigned in a manner that ensures that + there is a negligible probability that the same value will be + accidentally assigned to a different data object. + */ + public var jti: String + + /** + This MP-JWT custom claim is the user principal name in the java.security.Principal interface, and is the caller principal name in javax.security.enterprise.identitystore.IdentityStore. If this claim is missing, fallback to the "preferred_username", should be attempted, and if that claim is missing, fallback to the "sub" claim should be used. + */ + public var upn: String? + + /** + Shorthand name by which the End-User wishes to be referred to at the RP, such as janedoe or j.doe. This value MAY be any valid JSON string including special characters such as @, /, or whitespace. + */ + public var preferred_username: String? + + /** + This MP-JWT custom claim is the list of group names that have been assigned to the principal of the MP-JWT. This typically will required a mapping at the application container level to application deployment roles, but a one-to-one between group names and application role names is required to be performed in addition to any other mapping. + */ + public var groups: [String] +} diff --git a/Pods/SwiftJWT/Sources/SwiftJWT/ClaimsExamples/ClaimsOpenID.swift b/Pods/SwiftJWT/Sources/SwiftJWT/ClaimsExamples/ClaimsOpenID.swift new file mode 100644 index 0000000..2da906e --- /dev/null +++ b/Pods/SwiftJWT/Sources/SwiftJWT/ClaimsExamples/ClaimsOpenID.swift @@ -0,0 +1,200 @@ +/** + * Copyright IBM Corporation 2018 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +import Foundation + +// MARK ClaimsOpenID + +/// A class representing OpenID related claims as decsribed in [OpenID specs](http://openid.net/specs/openid-connect-core-1_0.html). +public class ClaimsOpenID: Claims { + + /// Initalise the `ClaimsOpenID` + public init( + iss: String, + sub: String, + aud: [String], + exp: Date, + iat: Date, + auth_time: Date? = nil, + nonce: String? = nil, + acr: String? = nil, + amr: [String]? = nil, + azp: String? = nil, + name: String? = nil, + given_name: String? = nil, + family_name: String? = nil, + middle_name: String? = nil, + nickname: String? = nil, + preferred_username: String? = nil, + profile: String? = nil, + picture: String? = nil, + website: String? = nil, + email: String? = nil, + email_verified: Bool? = nil, + gender: String? = nil, + birthdate: String? = nil, + zoneinfo: String? = nil, + locale: String? = nil, + phone_number: String? = nil, + phone_number_verified: Bool? = nil, + address: AddressClaim? = nil, + updated_at: Date? = nil + ) { + self.iss = iss + self.sub = sub + self.aud = aud + self.exp = exp + self.iat = iat + self.auth_time = auth_time + self.nonce = nonce + self.acr = acr + self.amr = amr + self.azp = azp + self.name = name + self.given_name = given_name + self.family_name = family_name + self.middle_name = middle_name + self.nickname = nickname + self.preferred_username = preferred_username + self.profile = profile + self.picture = picture + self.website = website + self.email = email + self.email_verified = email_verified + self.gender = gender + self.birthdate = birthdate + self.zoneinfo = zoneinfo + self.locale = locale + self.phone_number = phone_number + self.phone_number_verified = phone_number_verified + self.address = address + self.updated_at = updated_at + } + + // MARK: ID Token + + /// Issuer Identifier for the Issuer of the response. The iss value is a case sensitive URL using the https scheme that contains scheme, host, and optionally, port number and path components and no query or fragment components. + public var iss: String + + /// Subject Identifier. A locally unique and never reassigned identifier within the Issuer for the End-User, which is intended to be consumed by the Client, e.g., 24400320 or AItOawmwtWwcT0k51BayewNvutrJUqsvl6qs7A4. It MUST NOT exceed 255 ASCII characters in length. The sub value is case sensitive. + public var sub: String + + /// Audience(s) that this ID Token is intended for. It MUST contain the OAuth 2.0 client_id of the Relying Party as an audience value. It MAY also contain identifiers for other audiences. + public var aud: [String] + + /// Expiration time on or after which the ID Token MUST NOT be accepted for processing. The processing of this parameter requires that the current date/time MUST be before the expiration date/time listed in the value. Implementers MAY provide for some small leeway, usually no more than a few minutes, to account for clock skew. + public var exp: Date + + /// Time at which the JWT was issued. + public var iat: Date + + /// Time when the End-User authentication occurred. + public var auth_time: Date? + + /// String value used to associate a Client session with an ID Token, and to mitigate replay attacks. The value is passed through unmodified from the Authentication Request to the ID Token. If present in the ID Token, Clients MUST verify that the nonce Claim Value is equal to the value of the nonce parameter sent in the Authentication Request. If present in the Authentication Request, Authorization Servers MUST include a nonce Claim in the ID Token with the Claim Value being the nonce value sent in the Authentication Request. Authorization Servers SHOULD perform no other processing on nonce values used. + public var nonce: String? + + /// Authentication Context Class Reference. String specifying an Authentication Context Class Reference value that identifies the Authentication Context Class that the authentication performed satisfied. The value "0" indicates the End-User authentication did not meet the requirements of ISO/IEC 29115 level 1. Authentications with level 0 SHOULD NOT be used to authorize access to any resource of any monetary value. Parties using this claim will need to agree upon the meanings of the values used, which may be context-specific. + public var acr: String? + + /// Authentication Methods References. JSON array of strings that are identifiers for authentication methods used in the authentication. For instance, values might indicate that both password and OTP authentication methods were used. Parties using this claim will need to agree upon the meanings of the values used, which may be context-specific. + public var amr: [String]? + + /// Authorized party - the party to which the ID Token was issued. If present, it MUST contain the OAuth 2.0 Client ID of this party. This Claim is only needed when the ID Token has a single audience value and that audience is different than the authorized party. It MAY be included even when the authorized party is the same as the sole audience. + public var azp: String? + + // MARK: Standard Claims + + /// End-User's full name in displayable form including all name parts, possibly including titles and suffixes, ordered according to the End-User's locale and preferences. + public var name: String? + + /// Given name(s) or first name(s) of the End-User. Note that in some cultures, people can have multiple given names; all can be present, with the names being separated by space characters. + public var given_name: String? + + /// Surname(s) or last name(s) of the End-User. Note that in some cultures, people can have multiple family names or no family name; all can be present, with the names being separated by space characters. + public var family_name: String? + + /// Middle name(s) of the End-User. Note that in some cultures, people can have multiple middle names; all can be present, with the names being separated by space characters. Also note that in some cultures, middle names are not used. + public var middle_name: String? + + /// Casual name of the End-User that may or may not be the same as the given_name. For instance, a nickname value of Mike might be returned alongside a given_name value of Michael. + public var nickname: String? + + /// Shorthand name by which the End-User wishes to be referred to at the RP, such as janedoe or j.doe. This value MAY be any valid JSON string including special characters such as @, /, or whitespace. + public var preferred_username: String? + + /// URL of the End-User's profile page. The contents of this Web page SHOULD be about the End-User. + public var profile: String? + + /// URL of the End-User's profile picture. This URL MUST refer to an image file (for example, a PNG, JPEG, or GIF image file), rather than to a Web page containing an image. Note that this URL SHOULD specifically reference a profile photo of the End-User suitable for displaying when describing the End-User, rather than an arbitrary photo taken by the End-User. + public var picture: String? + + /// URL of the End-User's Web page or blog. This Web page SHOULD contain information published by the End-User or an organization that the End-User is affiliated with. + public var website: String? + + /// End-User's preferred e-mail address. + public var email: String? + + /// True if the End-User's e-mail address has been verified; otherwise false. When this Claim Value is true, this means that the OP took affirmative steps to ensure that this e-mail address was controlled by the End-User at the time the verification was performed. The means by which an e-mail address is verified is context-specific, and dependent upon the trust framework or contractual agreements within which the parties are operating. + public var email_verified: Bool? + + /// End-User's gender. Values defined by this specification are female and male. Other values MAY be used when neither of the defined values are applicable. + public var gender: String? + + /// End-User's birthday, represented as an ISO 8601:2004 YYYY-MM-DD format. The year MAY be 0000, indicating that it is omitted. To represent only the year, YYYY format is allowed. + public var birthdate: String? + + /// String from zoneinfo time zone database representing the End-User's time zone. For example, Europe/Paris or America/Los_Angeles. + public var zoneinfo: String? + + /// End-User's locale, represented as a BCP47 language tag. This is typically an ISO 639-1 Alpha-2 language code in lowercase and an ISO 3166-1 Alpha-2 country code in uppercase, separated by a dash. For example, en-US or fr-CA. As a compatibility note, some implementations have used an underscore as the separator rather than a dash, for example, en_US; Relying Parties MAY choose to accept this locale syntax as well. + public var locale: String? + + /// End-User's preferred telephone number. E.164 is RECOMMENDED as the format of this Claim, for example, +1 (425) 555-1212 or +56 (2) 687 2400. + public var phone_number: String? + + /// True if the End-User's phone number has been verified; otherwise false. When this Claim Value is true, this means that the OP took affirmative steps to ensure that this phone number was controlled by the End-User at the time the verification was performed. The means by which a phone number is verified is context-specific, and dependent upon the trust framework or contractual agreements within which the parties are operating. When true, the phone_number Claim MUST be in E.164 format and any extensions MUST be represented in RFC 3966 format. + public var phone_number_verified: Bool? + + /// End-User's preferred postal address. + public var address: AddressClaim? + + /// Time the End-User's information was last updated. + public var updated_at: Date? +} + +/// Struct representing an AddressClaim as defined in the [OpenID specs](http://openid.net/specs/openid-connect-core-1_0.html). +public struct AddressClaim: Codable { + + /// Full mailing address, formatted for display or use on a mailing label. This field MAY contain multiple lines, separated by newlines. Newlines can be represented either as a carriage return/line feed pair ("\r\n") or as a single line feed character ("\n"). + public var formatted: String? + + /// Full street address component, which MAY include house number, street name, Post Office Box, and multi-line extended street address information. This field MAY contain multiple lines, separated by newlines. Newlines can be represented either as a carriage return/line feed pair ("\r\n") or as a single line feed character ("\n"). + public var street_address: String? + + /// City or locality component. + public var locality: String? + + /// State, province, prefecture, or region component. + public var region: String? + + /// Zip code or postal code component. + public var postal_code: String? + + /// Country name component. + public var country: String? + +} diff --git a/Pods/SwiftJWT/Sources/SwiftJWT/ClaimsExamples/ClaimsStandardJWT.swift b/Pods/SwiftJWT/Sources/SwiftJWT/ClaimsExamples/ClaimsStandardJWT.swift new file mode 100644 index 0000000..96a1a8c --- /dev/null +++ b/Pods/SwiftJWT/Sources/SwiftJWT/ClaimsExamples/ClaimsStandardJWT.swift @@ -0,0 +1,108 @@ +/** + * Copyright IBM Corporation 2018 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +import Foundation + +// MARK ClaimsStandardJWT + +/// A class representing the Standard JWT claims as described in [RFC7519](https://tools.ietf.org/html/rfc7519#section-4.1). +public class ClaimsStandardJWT: Claims { + + /// Initialize a `ClaimsStandardJWT` + public init( + iss: String? = nil, + sub: String? = nil, + aud: [String]? = nil, + exp: Date? = nil, + nbf: Date? = nil, + iat: Date? = nil, + jti: String? = nil + ) { + self.iss = iss + self.sub = sub + self.aud = aud + self.exp = exp + self.nbf = nbf + self.iat = iat + self.jti = jti + } + + /** + The "iss" (issuer) claim identifies the principal that issued the + JWT. The processing of this claim is generally application specific. + The "iss" value is a case-sensitive. + */ + public var iss: String? + + /** + The "sub" (subject) claim identifies the principal that is the + subject of the JWT. The claims in a JWT are normally statements + about the subject. The subject value MUST either be scoped to be + locally unique in the context of the issuer or be globally unique. + The processing of this claim is generally application specific. The + "sub" value is case-sensitive. + */ + public var sub: String? + + /** + The "aud" (audience) claim identifies the recipients that the JWT is + intended for. Each principal intended to process the JWT MUST + identify itself with a value in the audience claim. If the principal + processing the claim does not identify itself with a value in the + "aud" claim when this claim is present, then the JWT MUST be + rejected. The interpretation of audience values is generally application specific. + The "aud" value is case-sensitive. + */ + public var aud: [String]? + + /** + The "exp" (expiration time) claim identifies the expiration time on + or after which the JWT MUST NOT be accepted for processing. The + processing of the "exp" claim requires that the current date/time + MUST be before the expiration date/time listed in the "exp" claim. + Implementers MAY provide for some small leeway, usually no more than + a few minutes, to account for clock skew. + */ + public var exp: Date? + + /** + The "nbf" (not before) claim identifies the time before which the JWT + MUST NOT be accepted for processing. The processing of the "nbf" + claim requires that the current date/time MUST be after or equal to + the not-before date/time listed in the "nbf" claim. Implementers MAY + provide for some small leeway, usually no more than a few minutes, to + account for clock skew. + */ + public var nbf: Date? + + /** + The "iat" (issued at) claim identifies the time at which the JWT was + issued. This claim can be used to determine the age of the JWT. + */ + public var iat: Date? + + /** + The "jti" (JWT ID) claim provides a unique identifier for the JWT. + The identifier value MUST be assigned in a manner that ensures that + there is a negligible probability that the same value will be + accidentally assigned to a different data object; if the application + uses multiple issuers, collisions MUST be prevented among values + produced by different issuers as well. The "jti" claim can be used + to prevent the JWT from being replayed. The "jti" value is case- + sensitive + */ + public var jti: String? +} diff --git a/Pods/SwiftJWT/Sources/SwiftJWT/Data+Base64URLEncoded.swift b/Pods/SwiftJWT/Sources/SwiftJWT/Data+Base64URLEncoded.swift new file mode 100644 index 0000000..d9c9204 --- /dev/null +++ b/Pods/SwiftJWT/Sources/SwiftJWT/Data+Base64URLEncoded.swift @@ -0,0 +1,50 @@ +/** + * Copyright IBM Corporation 2017-2019 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +import Foundation + +/// Convenience extension for encoding a `Data` as a base64url-encoded `String`. +extension JWTEncoder { + + /// Returns a `String` representation of this data, encoded in base64url format + /// as defined in RFC4648 (https://tools.ietf.org/html/rfc4648). + /// + /// This is the appropriate format for encoding the header and claims of a JWT. + public static func base64urlEncodedString(data: Data) -> String { + let result = data.base64EncodedString() + return result.replacingOccurrences(of: "+", with: "-") + .replacingOccurrences(of: "/", with: "_") + .replacingOccurrences(of: "=", with: "") + } +} + +/// Convenience extension for decoding a `Data` from a base64url-encoded `String`. +extension JWTDecoder { + + /// Initializes a new `Data` from the base64url-encoded `String` provided. The + /// base64url encoding is defined in RFC4648 (https://tools.ietf.org/html/rfc4648). + /// + /// This is appropriate for reading the header or claims portion of a JWT string. + public static func data(base64urlEncoded: String) -> Data? { + let paddingLength = 4 - base64urlEncoded.count % 4 + let padding = (paddingLength < 4) ? String(repeating: "=", count: paddingLength) : "" + let base64EncodedString = base64urlEncoded + .replacingOccurrences(of: "-", with: "+") + .replacingOccurrences(of: "_", with: "/") + + padding + return Data(base64Encoded: base64EncodedString) + } +} diff --git a/Pods/SwiftJWT/Sources/SwiftJWT/Header.swift b/Pods/SwiftJWT/Sources/SwiftJWT/Header.swift new file mode 100644 index 0000000..7f6860e --- /dev/null +++ b/Pods/SwiftJWT/Sources/SwiftJWT/Header.swift @@ -0,0 +1,102 @@ +/** + * Copyright IBM Corporation 2018 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +import Foundation + +// MARK: Header + +/** + A representation of a JSON Web Token header. + https://tools.ietf.org/html/rfc7515#section-4.1 + ### Usage Example: ### + ```swift + struct MyClaims: Claims { + var name: String + } + let myHeader = Header(kid: "keyID") + let jwt = JWT(header: myHeader, claims: MyClaims(name: "Kitura")) + ``` + */ +public struct Header: Codable { + + /// Type Header Parameter + public var typ: String? + /// Algorithm Header Parameter + public internal(set) var alg: String? + /// JSON Web Token Set URL Header Parameter + public var jku : String? + /// JSON Web Key Header Parameter + public var jwk: String? + /// Key ID Header Parameter + public var kid: String? + /// X.509 URL Header Parameter + public var x5u: String? + /// X.509 Certificate Chain Header Parameter + public var x5c: [String]? + /// X.509 Certificate SHA-1 Thumbprint Header Parameter + public var x5t: String? + /// X.509 Certificate SHA-256 Thumbprint Header Parameter + public var x5tS256: String? + /// Content Type Header Parameter + public var cty: String? + /// Critical Header Parameter + public var crit: [String]? + + /// Initialize a `Header` instance. + /// + /// - Parameter typ: The Type Header Parameter + /// - Parameter jku: The JSON Web Token Set URL Header Parameter + /// - Parameter jwk: The JSON Web Key Header Parameter + /// - Parameter kid: The Key ID Header Parameter + /// - Parameter x5u: The X.509 URL Header Parameter + /// - Parameter x5c: The X.509 Certificate Chain Header Parameter + /// - Parameter x5t: The X.509 Certificate SHA-1 Thumbprint Header Parameter + /// - Parameter x5tS256: X.509 Certificate SHA-256 Thumbprint Header Parameter + /// - Parameter cty: The Content Type Header Parameter + /// - Parameter crit: The Critical Header Parameter + /// - Returns: A new instance of `Header`. + public init( + typ: String? = "JWT", + jku: String? = nil, + jwk: String? = nil, + kid: String? = nil, + x5u: String? = nil, + x5c: [String]? = nil, + x5t: String? = nil, + x5tS256: String? = nil, + cty: String? = nil, + crit: [String]? = nil + ) { + self.typ = typ + self.alg = nil + self.jku = jku + self.jwk = jwk + self.kid = kid + self.x5u = x5u + self.x5c = x5c + self.x5t = x5t + self.x5tS256 = x5tS256 + self.cty = cty + self.crit = crit + } + + func encode() throws -> String { + let jsonEncoder = JSONEncoder() + jsonEncoder.dateEncodingStrategy = .secondsSince1970 + let data = try jsonEncoder.encode(self) + return JWTEncoder.base64urlEncodedString(data: data) + } +} diff --git a/Pods/SwiftJWT/Sources/SwiftJWT/JWT.swift b/Pods/SwiftJWT/Sources/SwiftJWT/JWT.swift new file mode 100644 index 0000000..fad09ff --- /dev/null +++ b/Pods/SwiftJWT/Sources/SwiftJWT/JWT.swift @@ -0,0 +1,139 @@ +/** + * Copyright IBM Corporation 2017 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +import Foundation + +// MARK: JWT + +/** + + A struct representing the `Header` and `Claims` of a JSON Web Token. + + ### Usage Example: ### + ```swift + struct MyClaims: Claims { + var name: String + } + let jwt = JWT(claims: MyClaims(name: "Kitura")) + let key = "".data(using: .utf8)! + let signedJWT: String? = try? jwt.sign(using: .rs256(key: key, keyType: .privateKey)) + ``` + */ + +public struct JWT: Codable { + + /// The JWT header. + public var header: Header + + /// The JWT claims + public var claims: T + + /// Initialize a `JWT` instance from a `Header` and `Claims`. + /// + /// - Parameter header: A JSON Web Token header object. + /// - Parameter claims: A JSON Web Token claims object. + /// - Returns: A new instance of `JWT`. + public init(header: Header = Header(), claims: T) { + self.header = header + self.claims = claims + } + + /// Initialize a `JWT` instance from a JWT String. + /// The signature will be verified using the provided JWTVerifier. + /// The time based standard JWT claims will be verified with `validateClaims()`. + /// If the string is not a valid JWT, or the verification fails, the initializer returns nil. + /// + /// - Parameter jwt: A String with the encoded and signed JWT. + /// - Parameter verifier: The `JWTVerifier` used to verify the JWT. + /// - Returns: An instance of `JWT` if the decoding succeeds. + /// - Throws: `JWTError.invalidJWTString` if the provided String is not in the form mandated by the JWT specification. + /// - Throws: `JWTError.failedVerification` if the verifier fails to verify the jwtString. + /// - Throws: A DecodingError if the JSONDecoder throws an error while decoding the JWT. + public init(jwtString: String, verifier: JWTVerifier = .none ) throws { + let components = jwtString.components(separatedBy: ".") + guard components.count == 2 || components.count == 3, + let headerData = JWTDecoder.data(base64urlEncoded: components[0]), + let claimsData = JWTDecoder.data(base64urlEncoded: components[1]) + else { + throw JWTError.invalidJWTString + } + guard JWT.verify(jwtString, using: verifier) else { + throw JWTError.failedVerification + } + let jsonDecoder = JSONDecoder() + jsonDecoder.dateDecodingStrategy = .secondsSince1970 + let header = try jsonDecoder.decode(Header.self, from: headerData) + let claims = try jsonDecoder.decode(T.self, from: claimsData) + self.header = header + self.claims = claims + } + + /// Sign the JWT using the given algorithm and encode the header, claims and signature as a JWT String. + /// + /// - Note: This function will set header.alg field to the name of the signing algorithm. + /// + /// - Parameter using algorithm: The algorithm to sign with. + /// - Returns: A String with the encoded and signed JWT. + /// - Throws: An EncodingError if the JSONEncoder throws an error while encoding the JWT. + /// - Throws: `JWTError.osVersionToLow` if not using macOS 10.12.0 (Sierra) or iOS 10.0 or higher. + /// - Throws: A Signing error if the jwtSigner is unable to sign the JWT with the provided key. + public mutating func sign(using jwtSigner: JWTSigner) throws -> String { + var tempHeader = header + tempHeader.alg = jwtSigner.name + let headerString = try tempHeader.encode() + let claimsString = try claims.encode() + header.alg = tempHeader.alg + return try jwtSigner.sign(header: headerString, claims: claimsString) + } + + /// Verify the signature of the encoded JWT using the given algorithm. + /// + /// - Parameter jwt: A String with the encoded and signed JWT. + /// - Parameter using algorithm: The algorithm to verify with. + /// - Returns: A Bool indicating whether the verification was successful. + public static func verify(_ jwt: String, using jwtVerifier: JWTVerifier) -> Bool { + return jwtVerifier.verify(jwt: jwt) + } + + /// Validate the time based standard JWT claims. + /// This function checks that the "exp" (expiration time) is in the future + /// and the "iat" (issued at) and "nbf" (not before) headers are in the past, + /// + /// - Parameter leeway: The time in seconds that the JWT can be invalid but still accepted to account for clock differences. + /// - Returns: A value of `ValidateClaimsResult`. + public func validateClaims(leeway: TimeInterval = 0) -> ValidateClaimsResult { + if let expirationDate = claims.exp { + if expirationDate + leeway < Date() { + return .expired + } + } + + if let notBeforeDate = claims.nbf { + if notBeforeDate > Date() + leeway { + return .notBefore + } + } + + if let issuedAtDate = claims.iat { + if issuedAtDate > Date() + leeway { + return .issuedAt + } + } + + return .success + } +} + diff --git a/Pods/SwiftJWT/Sources/SwiftJWT/JWTDecoder.swift b/Pods/SwiftJWT/Sources/SwiftJWT/JWTDecoder.swift new file mode 100644 index 0000000..d855204 --- /dev/null +++ b/Pods/SwiftJWT/Sources/SwiftJWT/JWTDecoder.swift @@ -0,0 +1,286 @@ +/** + * Copyright IBM Corporation 2018 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +import Foundation +import KituraContracts + +// MARK: JWTDecoder + +/** + A thread safe decoder that decodes either Data or a JWT String as a `JWT` instance and verifies the signiture using the provided algorithm. + + ### Usage Example: ### + ```swift + struct MyClaims: Claims { + var name: String + } + let publicKey = "".data(using: .utf8)! + let rsaJWTDecoder = JWTDecoder(jwtVerifier: JWTVerifier.rs256(publicKey: publicKey)) + do { + let jwt = try rsaJWTDecoder.decode(JWT.self, fromString: exampleJWTString) + } catch { + print("Failed to decode JWT: \(error)") + } + ``` + */ +public class JWTDecoder: BodyDecoder { + + let keyIDToVerifier: (String) -> JWTVerifier? + let jwtVerifier: JWTVerifier? + + // MARK: Initializers + + /// Initialize a `JWTDecoder` instance with a single `JWTVerifier`. + /// + /// - Parameter JWTVerifier: The `JWTVerifier` that will be used to verify the signiture of the JWT. + /// - Returns: A new instance of `JWTDecoder`. + public init(jwtVerifier: JWTVerifier) { + self.keyIDToVerifier = {_ in return jwtVerifier } + self.jwtVerifier = jwtVerifier + } + + /// Initialize a `JWTDecoder` instance with a function to generate the `JWTVerifier` from the JWT `kid` header. + /// + /// - Parameter keyIDToVerifier: The function that will generate the `JWTVerifier` using the "kid" header. + /// - Returns: A new instance of `JWTDecoder`. + public init(keyIDToVerifier: @escaping (String) -> JWTVerifier?) { + self.keyIDToVerifier = keyIDToVerifier + self.jwtVerifier = nil + } + + // MARK: Decode + + /// Decode a `JWT` instance from a JWT String. + /// + /// - Parameter type: The JWT type the String will be decoded as. + /// - Parameter fromString: The JWT String that will be decoded. + /// - Returns: A `JWT` instance of the provided type. + /// - throws: `JWTError.invalidJWTString` if the provided String is not in the form mandated by the JWT specification. + /// - throws: `JWTError.invalidKeyID` if the KeyID `kid` header fails to generate a jwtVerifier. + /// - throws: `JWTError.failedVerification` if the `JWTVerifier` fails to verify the decoded String. + /// - throws: `DecodingError` if the decoder fails to decode the String as the provided type. + public func decode(_ type: T.Type, fromString: String) throws -> T { + // Seperate the JWT into the headers and claims. + let components = fromString.components(separatedBy: ".") + guard components.count > 1, + let headerData = JWTDecoder.data(base64urlEncoded: components[0]), + let claimsData = JWTDecoder.data(base64urlEncoded: components[1]) + else { + throw JWTError.invalidJWTString + } + + // Decode the JWT headers and claims data into a _JWTDecoder. + let decoder = _JWTDecoder(header: headerData, claims: claimsData) + let jwt = try decoder.decode(type) + + let _jwtVerifier: JWTVerifier + // Verify the JWT String using the JWTDecoder constant jwtVerifier. + if let jwtVerifier = jwtVerifier { + _jwtVerifier = jwtVerifier + } else { + // The JWTVerifier is generated using the kid Header that was read inside the _JWTDecoder + // and then used to verify the JWT. + guard let keyID = decoder.keyID, let jwtVerifier = keyIDToVerifier(keyID) else { + throw JWTError.invalidKeyID + } + _jwtVerifier = jwtVerifier + } + guard _jwtVerifier.verify(jwt: fromString) else { + throw JWTError.failedVerification + } + return jwt + } + + /// Decode a `JWT` instance from a utf8 encoded JWT String. + /// + /// - Parameter type: The JWT type the Data will be decoded as. + /// - Parameter data: The utf8 encoded JWT String that will be decoded. + /// - Returns: A `JWT` instance of the provided type. + /// - throws: `JWTError.invalidUTF8Data` if the provided Data can't be decoded to a String. + /// - throws: `JWTError.invalidJWTString` if the provided String is not in the form mandated by the JWT specification. + /// - throws: `JWTError.invalidKeyID` if the KeyID `kid` header fails to generate a `JWTVerifier`. + /// - throws: `JWTError.failedVerification` if the `JWTVerifier` fails to verify the decoded String. + /// - throws: `DecodingError` if the decoder fails to decode the String as the provided type. + public func decode(_ type: T.Type, from data: Data) throws -> T { + guard let jwtString = String(data: data, encoding: .utf8) else { + throw JWTError.invalidUTF8Data + } + return try decode(type, fromString: jwtString) + } +} + +/* + The JWTDecoder creates it's own instance of _JWTDecoder everytime the decode function is called. + This is because the _JWTDecoder changes it's own value so we can only have one thread using it at a time. + The following is the code generated by codable and called by JWTDecoder.decode(type:, fromString:) for a JWT struct: + ``` + enum MyStructKeys: String, CodingKey { + case header, claims + } + init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: MyStructKeys.self) // defining our (keyed) container + let header: Header = try container.decode(Header.self, forKey: .header) // extracting the data + let claims: MyClaims = try container.decode(MyClaims.self, forKey: .claims) // extracting the data + self.init(header: header, claims: claims) // initializing our struct + } + ``` + Where decoder is a _JWTDecoder instance, and MyClaims is the user defined object conforming to Claims. + */ +fileprivate class _JWTDecoder: Decoder { + + init(header: Data, claims: Data) { + self.header = header + self.claims = claims + } + + var header: Data + + var claims: Data + + var keyID: String? + + var codingPath: [CodingKey] = [] + + var userInfo: [CodingUserInfoKey : Any] = [:] + + // Call the Codable Types init from decoder function. + public func decode(_ type: T.Type) throws -> T { + return try type.init(from: self) + } + + // JWT should only be a Keyed container + func container(keyedBy type: Key.Type) throws -> KeyedDecodingContainer { + let container = _JWTKeyedDecodingContainer(decoder: self, header: header, claims: claims) + return KeyedDecodingContainer(container) + } + + // This function should not be called when decoding a JWT + func unkeyedContainer() throws -> UnkeyedDecodingContainer { + return UnkeyedContainer(decoder: self) + } + + // This function should not be called when decoding a JWT + func singleValueContainer() throws -> SingleValueDecodingContainer { + return UnkeyedContainer(decoder: self) + } +} + +private struct _JWTKeyedDecodingContainer: KeyedDecodingContainerProtocol { + + // A reference to the Decoder the container is inside + let decoder: _JWTDecoder + + var header: Data + + var claims: Data + + var codingPath: [CodingKey] + + public var allKeys: [Key] + { + #if swift(>=4.1) + return ["header", "claims"].compactMap { Key(stringValue: $0) } + #else + return ["header", "claims"].flatMap { Key(stringValue: $0) } + #endif + } + + fileprivate init(decoder: _JWTDecoder, header: Data, claims: Data) { + self.decoder = decoder + self.header = header + self.claims = claims + self.codingPath = decoder.codingPath + } + + public func contains(_ key: Key) -> Bool { + return key.stringValue == "header" || key.stringValue == "claims" + } + + // The JWT Class should only have to decode Decodable types + // Those types will be a `Header` object and a generic `Claims` object. + func decode(_ type: T.Type, forKey key: Key) throws -> T { + decoder.codingPath.append(key) + let jsonDecoder = JSONDecoder() + jsonDecoder.dateDecodingStrategy = .secondsSince1970 + if key.stringValue == "header" { + let header = try jsonDecoder.decode(Header.self, from: self.header) + decoder.keyID = header.kid + guard let decodedHeader = header as? T else { + throw DecodingError.typeMismatch(T.self, DecodingError.Context(codingPath: codingPath, debugDescription: "Type of header key was not a JWT Header")) + } + return decodedHeader + } else + if key.stringValue == "claims" { + return try jsonDecoder.decode(type, from: claims) + } else { + throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: codingPath, debugDescription: "value not found for provided key")) + } + } + +// No functions beyond this point should be called when decoding JWT, However the functions are required by KeyedDecodingContainerProtocol. + func decodeNil(forKey key: Key) throws -> Bool { + throw DecodingError.typeMismatch(Key.self, DecodingError.Context(codingPath: codingPath, debugDescription: "JWTDecoder can only Decode JWT tokens")) + } + + func nestedContainer(keyedBy type: NestedKey.Type, forKey key: Key) throws -> KeyedDecodingContainer where NestedKey : CodingKey { + return try decoder.container(keyedBy: type) + } + + func nestedUnkeyedContainer(forKey key: Key) throws -> UnkeyedDecodingContainer { + return try decoder.unkeyedContainer() + } + + func superDecoder() throws -> Decoder { + return decoder + } + + func superDecoder(forKey key: Key) throws -> Decoder { + return decoder + } +} + +// When decoding a JWT you should not have an UnkeyedContainer +private struct UnkeyedContainer: UnkeyedDecodingContainer, SingleValueDecodingContainer { + var decoder: _JWTDecoder + + var codingPath: [CodingKey] { return [] } + + var count: Int? { return nil } + + var currentIndex: Int { return 0 } + + var isAtEnd: Bool { return false } + + func decode(_ type: T.Type) throws -> T { + throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: codingPath, debugDescription: "JWTDecoder can only Decode JWT tokens")) + } + + func decodeNil() -> Bool { + return true + } + + func nestedContainer(keyedBy type: NestedKey.Type) throws -> KeyedDecodingContainer where NestedKey : CodingKey { + return try decoder.container(keyedBy: type) + } + + func nestedUnkeyedContainer() throws -> UnkeyedDecodingContainer { + return self + } + + func superDecoder() throws -> Decoder { + return decoder + } +} diff --git a/Pods/SwiftJWT/Sources/SwiftJWT/JWTEncoder.swift b/Pods/SwiftJWT/Sources/SwiftJWT/JWTEncoder.swift new file mode 100644 index 0000000..2ae6813 --- /dev/null +++ b/Pods/SwiftJWT/Sources/SwiftJWT/JWTEncoder.swift @@ -0,0 +1,234 @@ +/** + * Copyright IBM Corporation 2018 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +import Foundation +import KituraContracts + +// MARK: JWTEncoder + +/** + A thread safe encoder that signs the JWT header and claims using the provided algorithm and encodes a `JWT` instance as either Data or a JWT String. + + ### Usage Example: ### + ```swift + struct MyClaims: Claims { + var name: String + } + var jwt = JWT(claims: MyClaims(name: "John Doe")) + let privateKey = "".data(using: .utf8)! + let rsaJWTEncoder = JWTEncoder(jwtSigner: JWTSigner.rs256(privateKey: privateKey)) + do { + let jwtString = try rsaJWTEncoder.encodeToString(jwt) + } catch { + print("Failed to encode JWT: \(error)") + } + ``` + */ +public class JWTEncoder: BodyEncoder { + + let keyIDToSigner: (String) -> JWTSigner? + let jwtSigner: JWTSigner? + + // MARK: Initializers + + /// Initialize a `JWTEncoder` instance with a single `JWTSigner`. + /// + /// - Parameter jwtSigner: The `JWTSigner` that will be used to sign the JWT. + /// - Returns: A new instance of `JWTEncoder`. + public init(jwtSigner: JWTSigner) { + self.keyIDToSigner = {_ in return jwtSigner } + self.jwtSigner = jwtSigner + } + + /// Initialize a `JWTEncoder` instance with a function to generate the `JWTSigner` from the JWT `kid` header. + /// + /// - Parameter keyIDToSigner: The function to generate the `JWTSigner` from the JWT `kid` header. + /// - Returns: A new instance of `JWTEncoder`. + public init(keyIDToSigner: @escaping (String) -> JWTSigner?) { + self.keyIDToSigner = keyIDToSigner + self.jwtSigner = nil + } + + // MARK: Encode + + /// Encode a `JWT` instance into a UTF8 encoded JWT String. + /// + /// - Parameter value: The JWT instance to be encoded as Data. + /// - Returns: The UTF8 encoded JWT String. + /// - throws: `JWTError.invalidUTF8Data` if the provided Data can't be decoded to a String. + /// - throws: `JWTError.invalidKeyID` if the KeyID `kid` header fails to generate a jwtSigner. + /// - throws: `EncodingError` if the encoder fails to encode the object as Data. + public func encode(_ value: T) throws -> Data { + guard let jwt = try self.encodeToString(value).data(using: .utf8) else { + throw JWTError.invalidUTF8Data + } + return jwt + } + + /// Encode a `JWT` instance as a JWT String. + /// + /// - Parameter value: The JWT instance to be encoded as a JWT String. + /// - Returns: A JWT String. + /// - throws: `JWTError.invalidKeyID` if the KeyID `kid` header fails to generate a jwtSigner. + /// - throws: `EncodingError` if the encoder fails to encode the object as Data. + public func encodeToString(_ value: T) throws -> String { + let encoder = _JWTEncoder(jwtSigner: jwtSigner, keyIDToSigner: keyIDToSigner) + try value.encode(to: encoder) + guard let header = encoder.header, + let claims = encoder.claims, + let jwtSigner = encoder.jwtSigner + else { + throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: [], debugDescription: "Failed to sign JWT Header and Claims")) + } + return try jwtSigner.sign(header: header, claims: claims) + } +} + +/* + The JWTEncoder creates it's own instance of _JWTEncoder everytime the encode function is called. + This is because the _JWTEncoder changes it's own value so we can only have one thread using it at a time. + The following is the code generated by codable and called by JWTEncoder.encode() -> String for a JWT struct: + ``` + enum MyStructKeys: String, CodingKey { + case header, claims + } + extension JWT: Encodable { + func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(header, forKey: .header) + try container.encode(claims, forKey: .claims) + } + } + ``` + Where encoder is a _JWTEncoder instance, and MyClaims is the user defined object conforming to Claims. + */ +fileprivate class _JWTEncoder: Encoder { + + init(jwtSigner: JWTSigner?, keyIDToSigner: @escaping (String) -> JWTSigner?) { + self.jwtSigner = jwtSigner + self.keyIDToSigner = keyIDToSigner + } + + var claims: String? + + var header: String? + + var jwtSigner: JWTSigner? + + let keyIDToSigner: (String) -> JWTSigner? + + var codingPath: [CodingKey] = [] + + var userInfo: [CodingUserInfoKey : Any] = [:] + + // We will be provided a keyed container representing the JWT instance + func container(keyedBy type: Key.Type) -> KeyedEncodingContainer { + let container = _JWTKeyedEncodingContainer(encoder: self, codingPath: self.codingPath) + return KeyedEncodingContainer(container) + } + + private struct _JWTKeyedEncodingContainer: KeyedEncodingContainerProtocol { + + /// A reference to the encoder we're writing to. + let encoder: _JWTEncoder + + var codingPath: [CodingKey] + + // Set the Encoder header and claims Strings using the container + mutating func encode(_ value: T, forKey key: Key) throws { + self.codingPath.append(key) + let fieldName = key.stringValue + let jsonEncoder = JSONEncoder() + jsonEncoder.dateEncodingStrategy = .secondsSince1970 + if fieldName == "header" { + guard var _header = value as? Header else { + throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: [], debugDescription: "Failed to encode into header CodingKey")) + } + // Set the jwtSigner while you have acces to the keyID + if encoder.jwtSigner == nil { + guard let keyID = _header.kid, let keyIDJWTSigner = encoder.keyIDToSigner(keyID) else { + throw JWTError.invalidKeyID + } + encoder.jwtSigner = keyIDJWTSigner + } + _header.alg = encoder.jwtSigner?.name + let data = try jsonEncoder.encode(_header) + encoder.header = JWTEncoder.base64urlEncodedString(data: data) + } else if fieldName == "claims" { + let data = try jsonEncoder.encode(value) + encoder.claims = JWTEncoder.base64urlEncodedString(data: data) + } + } + + // No functions beyond this point should be called for encoding a JWT token + mutating func nestedContainer(keyedBy keyType: NestedKey.Type, forKey key: Key) -> KeyedEncodingContainer where NestedKey : CodingKey { + return encoder.container(keyedBy: keyType) + } + + mutating func nestedUnkeyedContainer(forKey key: Key) -> UnkeyedEncodingContainer { + return encoder.unkeyedContainer() + } + + mutating func superEncoder() -> Encoder { + return encoder + } + + mutating func superEncoder(forKey key: Key) -> Encoder { + return encoder + } + + // Throw if trying to encode something other than a JWT token + mutating func encodeNil(forKey key: Key) throws { + throw EncodingError.invalidValue(key, EncodingError.Context(codingPath: codingPath, debugDescription: "JWTEncoder can only encode JWT tokens")) + } + + } + + func unkeyedContainer() -> UnkeyedEncodingContainer { + return UnkeyedContainer(encoder: self) + } + + func singleValueContainer() -> SingleValueEncodingContainer { + return UnkeyedContainer(encoder: self) + } + + // This Decoder should not be used to decode UnkeyedContainer + private struct UnkeyedContainer: UnkeyedEncodingContainer, SingleValueEncodingContainer { + var encoder: _JWTEncoder + + var codingPath: [CodingKey] { return [] } + + var count: Int { return 0 } + + func nestedContainer(keyedBy keyType: NestedKey.Type) -> KeyedEncodingContainer where NestedKey : CodingKey { + return encoder.container(keyedBy: keyType) + } + + func nestedUnkeyedContainer() -> UnkeyedEncodingContainer { + return self + } + + func superEncoder() -> Encoder { + return encoder + } + + func encodeNil() throws {} + + func encode(_ value: T) throws where T : Encodable { + throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: codingPath, debugDescription: "JWTEncoder can only encode JWT tokens")) + } + } +} diff --git a/Pods/SwiftJWT/Sources/SwiftJWT/JWTError.swift b/Pods/SwiftJWT/Sources/SwiftJWT/JWTError.swift new file mode 100644 index 0000000..02765aa --- /dev/null +++ b/Pods/SwiftJWT/Sources/SwiftJWT/JWTError.swift @@ -0,0 +1,66 @@ +/** + * Copyright IBM Corporation 2018 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +import Foundation + +// MARK: JWTError + +/// A struct representing the different errors that can be thrown by SwiftJWT +public struct JWTError: Error, Equatable { + + /// A human readable description of the error. + public let localizedDescription: String + + private let internalError: InternalError + + private enum InternalError { + case invalidJWTString, failedVerification, osVersionToLow, invalidPrivateKey, invalidData, invalidKeyID, missingPEMHeaders + } + + /// Error when an invalid JWT String is provided + public static let invalidJWTString = JWTError(localizedDescription: "Input was not a valid JWT String", internalError: .invalidJWTString) + + /// Error when the JWT signiture fails verification. + public static let failedVerification = JWTError(localizedDescription: "JWT verifier failed to verify the JWT String signiture", internalError: .failedVerification) + + /// Error when using RSA encryption with an OS version that is too low. + public static let osVersionToLow = JWTError(localizedDescription: "macOS 10.12.0 (Sierra) or higher or iOS 10.0 or higher is required by CryptorRSA", internalError: .osVersionToLow) + + /// Error when an invalid private key is provided for RSA encryption. + public static let invalidPrivateKey = JWTError(localizedDescription: "Provided private key could not be used to sign JWT", internalError: .invalidPrivateKey) + + /// Error when the provided Data cannot be decoded to a String + public static let invalidUTF8Data = JWTError(localizedDescription: "Could not decode Data from UTF8 to String", internalError: .invalidData) + + /// Error when the KeyID field `kid` in the JWT header fails to generate a JWTSigner or JWTVerifier + public static let invalidKeyID = JWTError(localizedDescription: "The JWT KeyID `kid` header failed to generate a JWTSigner/JWTVerifier", internalError: .invalidKeyID) + + /// Error when a PEM string is provided without the expected PEM headers/footers. (e.g. -----BEGIN PRIVATE KEY-----) + public static let missingPEMHeaders = JWTError(localizedDescription: "The provided key did not have the expected PEM headers/footers", internalError: .missingPEMHeaders) + + /// Function to check if JWTErrors are equal. Required for equatable protocol. + public static func == (lhs: JWTError, rhs: JWTError) -> Bool { + return lhs.internalError == rhs.internalError + } + + /// Function to enable pattern matching against generic Errors. + public static func ~= (lhs: JWTError, rhs: Error) -> Bool { + guard let rhs = rhs as? JWTError else { + return false + } + return lhs == rhs + } +} diff --git a/Pods/SwiftJWT/Sources/SwiftJWT/JWTSigner.swift b/Pods/SwiftJWT/Sources/SwiftJWT/JWTSigner.swift new file mode 100644 index 0000000..4af3bfd --- /dev/null +++ b/Pods/SwiftJWT/Sources/SwiftJWT/JWTSigner.swift @@ -0,0 +1,147 @@ +/** + * Copyright IBM Corporation 2018 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +import Foundation + +// MARK: JWTSigner + +/** + A struct that will be used to sign the JWT `Header` and `Claims` and generate a signed JWT. + For RSA and ECDSA, the provided key should be a .utf8 encoded PEM String. + ### Usage Example: ### + ```swift + let pemString = """ + -----BEGIN RSA PRIVATE KEY----- + MIICWwIBAAKBgQDdlatRjRjogo3WojgGHFHYLugdUWAY9iR3fy4arWNA1KoS8kVw + 33cJibXr8bvwUAUparCwlvdbH6dvEOfou0/gCFQsHUfQrSDv+MuSUMAe8jzKE4qW + +jK+xQU9a03GUnKHkkle+Q0pX/g6jXZ7r1/xAK5Do2kQ+X5xK9cipRgEKwIDAQAB + AoGAD+onAtVye4ic7VR7V50DF9bOnwRwNXrARcDhq9LWNRrRGElESYYTQ6EbatXS + 3MCyjjX2eMhu/aF5YhXBwkppwxg+EOmXeh+MzL7Zh284OuPbkglAaGhV9bb6/5Cp + uGb1esyPbYW+Ty2PC0GSZfIXkXs76jXAu9TOBvD0ybc2YlkCQQDywg2R/7t3Q2OE + 2+yo382CLJdrlSLVROWKwb4tb2PjhY4XAwV8d1vy0RenxTB+K5Mu57uVSTHtrMK0 + GAtFr833AkEA6avx20OHo61Yela/4k5kQDtjEf1N0LfI+BcWZtxsS3jDM3i1Hp0K + Su5rsCPb8acJo5RO26gGVrfAsDcIXKC+bQJAZZ2XIpsitLyPpuiMOvBbzPavd4gY + 6Z8KWrfYzJoI/Q9FuBo6rKwl4BFoToD7WIUS+hpkagwWiz+6zLoX1dbOZwJACmH5 + fSSjAkLRi54PKJ8TFUeOP15h9sQzydI8zJU+upvDEKZsZc/UhT/SySDOxQ4G/523 + Y0sz/OZtSWcol/UMgQJALesy++GdvoIDLfJX5GBQpuFgFenRiRDabxrE9MNUZ2aP + FaFp+DyAe+b4nDwuJaW2LURbr8AEZga7oQj0uYxcYw== + -----END RSA PRIVATE KEY----- + """ + let privateKey = pemString.data(using: .utf8)! + let jwtSigner = JWTSigner.rs256(privateKey: privateKey) + struct MyClaims: Claims { + var name: String + } + let jwt = JWT(claims: MyClaims(name: "Kitura")) + let signedJWT = try? jwt.sign(using: jwtSigner) + ``` + */ +public struct JWTSigner { + + /// The name of the algorithm that will be set in the "alg" header + let name: String + + let signerAlgorithm: SignerAlgorithm + + init(name: String, signerAlgorithm: SignerAlgorithm) { + self.name = name + self.signerAlgorithm = signerAlgorithm + } + + func sign(header: String, claims: String) throws -> String { + return try signerAlgorithm.sign(header: header, claims: claims) + } + + /// Initialize a JWTSigner using the RSA 256 bits algorithm and the provided privateKey. + /// - Parameter privateKey: The UTF8 encoded PEM private key, with a "BEGIN RSA PRIVATE KEY" header. + public static func rs256(privateKey: Data) -> JWTSigner { + return JWTSigner(name: "RS256", signerAlgorithm: BlueRSA(key: privateKey, keyType: .privateKey, algorithm: .sha256)) + } + + /// Initialize a JWTSigner using the RSA 384 bits algorithm and the provided privateKey. + /// - Parameter privateKey: The UTF8 encoded PEM private key, with a "BEGIN RSA PRIVATE KEY" header. + public static func rs384(privateKey: Data) -> JWTSigner { + return JWTSigner(name: "RS384", signerAlgorithm: BlueRSA(key: privateKey, keyType: .privateKey, algorithm: .sha384)) + } + + /// Initialize a JWTSigner using the RSA 512 bits algorithm and the provided privateKey. + /// - Parameter privateKey: The UTF8 encoded PEM private key, with a "BEGIN RSA PRIVATE KEY" header. + public static func rs512(privateKey: Data) -> JWTSigner { + return JWTSigner(name: "RS512", signerAlgorithm: BlueRSA(key: privateKey, keyType: .privateKey, algorithm: .sha512)) + } + + /// Initialize a JWTSigner using the RSA-PSS 256 bits algorithm and the provided privateKey. + /// - Parameter privateKey: The UTF8 encoded PEM private key, with a "BEGIN RSA PRIVATE KEY" header. + public static func ps256(privateKey: Data) -> JWTSigner { + return JWTSigner(name: "PS256", signerAlgorithm: BlueRSA(key: privateKey, keyType: .privateKey, algorithm: .sha256, usePSS: true)) + } + + /// Initialize a JWTSigner using the RSA-PSS 384 bits algorithm and the provided privateKey. + /// - Parameter privateKey: The UTF8 encoded PEM private key, with a "BEGIN RSA PRIVATE KEY" header. + public static func ps384(privateKey: Data) -> JWTSigner { + return JWTSigner(name: "PS384", signerAlgorithm: BlueRSA(key: privateKey, keyType: .privateKey, algorithm: .sha384, usePSS: true)) + } + + /// Initialize a JWTSigner using the RSA-PSS 512 bits algorithm and the provided privateKey. + /// This signer requires at least a 2048 bit RSA key. + /// - Parameter privateKey: The UTF8 encoded PEM private key, with a "BEGIN RSA PRIVATE KEY" header. + public static func ps512(privateKey: Data) -> JWTSigner { + return JWTSigner(name: "PS512", signerAlgorithm: BlueRSA(key: privateKey, keyType: .privateKey, algorithm: .sha512, usePSS: true)) + } + + /// Initialize a JWTSigner using the HMAC 256 bits algorithm and the provided privateKey. + /// - Parameter key: The HMAC symmetric password data. + public static func hs256(key: Data) -> JWTSigner { + return JWTSigner(name: "HS256", signerAlgorithm: BlueHMAC(key: key, algorithm: .sha256)) + } + + /// Initialize a JWTSigner using the HMAC 384 bits algorithm and the provided privateKey. + /// - Parameter key: The HMAC symmetric password data. + public static func hs384(key: Data) -> JWTSigner { + return JWTSigner(name: "HS384", signerAlgorithm: BlueHMAC(key: key, algorithm: .sha384)) + } + + /// Initialize a JWTSigner using the HMAC 512 bits algorithm and the provided privateKey. + /// - Parameter key: The HMAC symmetric password data. + public static func hs512(key: Data) -> JWTSigner { + return JWTSigner(name: "HS512", signerAlgorithm: BlueHMAC(key: key, algorithm: .sha512)) + } + + /// Initialize a JWTSigner using the ECDSA SHA256 algorithm and the provided privateKey. + /// - Parameter privateKey: The UTF8 encoded PEM private key, with either a "BEGIN EC PRIVATE KEY" or "BEGIN PRIVATE KEY" header. + @available(OSX 10.13, iOS 11, tvOS 11.0, watchOS 4.0, *) + public static func es256(privateKey: Data) -> JWTSigner { + return JWTSigner(name: "ES256", signerAlgorithm: BlueECSigner(key: privateKey, curve: .prime256v1)) + } + + /// Initialize a JWTSigner using the ECDSA SHA384 algorithm and the provided privateKey. + /// - Parameter privateKey: The UTF8 encoded PEM private key, with either a "BEGIN EC PRIVATE KEY" or "BEGIN PRIVATE KEY" header. + @available(OSX 10.13, iOS 11, tvOS 11.0, watchOS 4.0, *) + public static func es384(privateKey: Data) -> JWTSigner { + return JWTSigner(name: "ES384", signerAlgorithm: BlueECSigner(key: privateKey, curve: .secp384r1)) + } + + /// Initialize a JWTSigner using the ECDSA SHA512 algorithm and the provided privateKey. + /// - Parameter privateKey: The UTF8 encoded PEM private key, with either a "BEGIN EC PRIVATE KEY" or "BEGIN PRIVATE KEY" header. + @available(OSX 10.13, iOS 11, tvOS 11.0, watchOS 4.0, *) + public static func es512(privateKey: Data) -> JWTSigner { + return JWTSigner(name: "ES512", signerAlgorithm: BlueECSigner(key: privateKey, curve: .secp521r1)) + } + + /// Initialize a JWTSigner that will not sign the JWT. This is equivelent to using the "none" alg header. + public static let none = JWTSigner(name: "none", signerAlgorithm: NoneAlgorithm()) +} + diff --git a/Pods/SwiftJWT/Sources/SwiftJWT/JWTVerifier.swift b/Pods/SwiftJWT/Sources/SwiftJWT/JWTVerifier.swift new file mode 100644 index 0000000..35df293 --- /dev/null +++ b/Pods/SwiftJWT/Sources/SwiftJWT/JWTVerifier.swift @@ -0,0 +1,152 @@ +/** + * Copyright IBM Corporation 2018 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +import Foundation + +// MARK: JWTVerifier + +/** + + A struct that will be used to verify the signature of a JWT is valid for the provided `Header` and `Claims`. + For RSA and ECDSA, the provided key should be a .utf8 encoded PEM String. + ### Usage Example: ### + ```swift + let pemString = """ + -----BEGIN PUBLIC KEY----- + MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDdlatRjRjogo3WojgGHFHYLugd + UWAY9iR3fy4arWNA1KoS8kVw33cJibXr8bvwUAUparCwlvdbH6dvEOfou0/gCFQs + HUfQrSDv+MuSUMAe8jzKE4qW+jK+xQU9a03GUnKHkkle+Q0pX/g6jXZ7r1/xAK5D + o2kQ+X5xK9cipRgEKwIDAQAB + -----END PUBLIC KEY----- + """ + let signedJWT = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiS2l0dXJhIn0.o2Rv_w1W6qfkldgb6FwzC3tAFEzo7WyYcLyykijCEqDbW8A7TwoFev85KGo_Bi7eNaSgZ6Q8jgkA31r8EDQWtSRg3_o5Zlq-ZCndyVeibgbyM2BMVUGcGzkUD2ikARfnb6GNGHr2waVeFSDehTN8WTLl0mGFxUE6wx5ZugR7My0" + struct MyClaims: Claims { + var name: String + } + let jwt = JWT(claims: MyClaims(name: "Kitura")) + let publicKey = pemString.data(using: .utf8)! + let jwtVerifier = JWTVerifier.rs256(publicKey: publicKey) + let verified: Bool = jwt.verify(signedJWT, using: jwtVerifier) + ``` + */ +public struct JWTVerifier { + let verifierAlgorithm: VerifierAlgorithm + + init(verifierAlgorithm: VerifierAlgorithm) { + self.verifierAlgorithm = verifierAlgorithm + } + + func verify(jwt: String) -> Bool { + return verifierAlgorithm.verify(jwt: jwt) + } + + /// Initialize a JWTVerifier using the RSA 256 bits algorithm and the provided publicKey. + /// - Parameter publicKey: The UTF8 encoded PEM public key, with a "BEGIN PUBLIC KEY" header. + public static func rs256(publicKey: Data) -> JWTVerifier { + return JWTVerifier(verifierAlgorithm: BlueRSA(key: publicKey, keyType: .publicKey, algorithm: .sha256)) + } + + /// Initialize a JWTVerifier using the RSA 384 bits algorithm and the provided publicKey. + /// - Parameter publicKey: The UTF8 encoded PEM public key, with a "BEGIN PUBLIC KEY" header. + public static func rs384(publicKey: Data) -> JWTVerifier { + return JWTVerifier(verifierAlgorithm: BlueRSA(key: publicKey, keyType: .publicKey, algorithm: .sha384)) + } + + /// Initialize a JWTVerifier using the RSA 512 bits algorithm and the provided publicKey. + /// - Parameter publicKey: The UTF8 encoded PEM public key, with a "BEGIN PUBLIC KEY" header. + public static func rs512(publicKey: Data) -> JWTVerifier { + return JWTVerifier(verifierAlgorithm: BlueRSA(key: publicKey, keyType: .publicKey, algorithm: .sha512)) + } + + /// Initialize a JWTVerifier using the RSA 256 bits algorithm and the provided certificate. + /// - Parameter publicKey: The UTF8 encoded PEM public key, with a "BEGIN CERTIFICATE" header. + public static func rs256(certificate: Data) -> JWTVerifier { + return JWTVerifier(verifierAlgorithm: BlueRSA(key: certificate, keyType: .certificate, algorithm: .sha256)) + } + + /// Initialize a JWTVerifier using the RSA 384 bits algorithm and the provided certificate. + /// - Parameter publicKey: The UTF8 encoded PEM public key, with a "BEGIN CERTIFICATE" header. + public static func rs384(certificate: Data) -> JWTVerifier { + return JWTVerifier(verifierAlgorithm: BlueRSA(key: certificate, keyType: .certificate, algorithm: .sha384)) + } + + /// Initialize a JWTVerifier using the RSA 512 bits algorithm and the provided certificate. + /// - Parameter publicKey: The UTF8 encoded PEM public key, with a "BEGIN CERTIFICATE" header. + public static func rs512(certificate: Data) -> JWTVerifier { + return JWTVerifier(verifierAlgorithm: BlueRSA(key: certificate, keyType: .certificate, algorithm: .sha512)) + } + + /// Initialize a JWTVerifier using the RSA-PSS 256 bits algorithm and the provided publicKey. + /// - Parameter publicKey: The UTF8 encoded PEM public key, with a "BEGIN PUBLIC KEY" header. + public static func ps256(publicKey: Data) -> JWTVerifier { + return JWTVerifier(verifierAlgorithm: BlueRSA(key: publicKey, keyType: .publicKey, algorithm: .sha256, usePSS: true)) + } + + /// Initialize a JWTVerifier using the RSA-PSS 384 bits algorithm and the provided publicKey. + /// - Parameter publicKey: The UTF8 encoded PEM public key, with a "BEGIN PUBLIC KEY" header. + public static func ps384(publicKey: Data) -> JWTVerifier { + return JWTVerifier(verifierAlgorithm: BlueRSA(key: publicKey, keyType: .publicKey, algorithm: .sha384, usePSS: true)) + } + + /// Initialize a JWTVerifier using the RSA-PSS 512 bits algorithm and the provided publicKey. + /// This verifier requires at least a 2048 bit RSA key. + /// - Parameter publicKey: The UTF8 encoded PEM public key, with a "BEGIN PUBLIC KEY" header. + public static func ps512(publicKey: Data) -> JWTVerifier { + return JWTVerifier(verifierAlgorithm: BlueRSA(key: publicKey, keyType: .publicKey, algorithm: .sha512, usePSS: true)) + } + + /// Initialize a JWTSigner using the HMAC 256 bits algorithm and the provided privateKey. + /// - Parameter key: The HMAC symmetric password data. + public static func hs256(key: Data) -> JWTVerifier { + return JWTVerifier(verifierAlgorithm: BlueHMAC(key: key, algorithm: .sha256)) + } + + /// Initialize a JWTSigner using the HMAC 384 bits algorithm and the provided privateKey. + /// - Parameter key: The HMAC symmetric password data. + public static func hs384(key: Data) -> JWTVerifier { + return JWTVerifier(verifierAlgorithm: BlueHMAC(key: key, algorithm: .sha384)) + } + + /// Initialize a JWTSigner using the HMAC 512 bits algorithm and the provided privateKey. + /// - Parameter key: The HMAC symmetric password data. + public static func hs512(key: Data) -> JWTVerifier { + return JWTVerifier(verifierAlgorithm: BlueHMAC(key: key, algorithm: .sha512)) + } + + /// Initialize a JWTVerifier using the ECDSA SHA 256 algorithm and the provided public key. + /// - Parameter publicKey: The UTF8 encoded PEM public key, with a "BEGIN PUBLIC KEY" header. + @available(OSX 10.13, iOS 11, tvOS 11.0, watchOS 4.0, *) + public static func es256(publicKey: Data) -> JWTVerifier { + return JWTVerifier(verifierAlgorithm: BlueECVerifier(key: publicKey, curve: .prime256v1)) + } + + /// Initialize a JWTVerifier using the ECDSA SHA 384 algorithm and the provided public key. + /// - Parameter publicKey: The UTF8 encoded PEM public key, with a "BEGIN PUBLIC KEY" header. + @available(OSX 10.13, iOS 11, tvOS 11.0, watchOS 4.0, *) + public static func es384(publicKey: Data) -> JWTVerifier { + return JWTVerifier(verifierAlgorithm: BlueECVerifier(key: publicKey, curve: .secp384r1)) + } + + /// Initialize a JWTVerifier using the ECDSA SHA 512 algorithm and the provided public key. + /// - Parameter publicKey: The UTF8 encoded PEM public key, with a "BEGIN PUBLIC KEY" header. + @available(OSX 10.13, iOS 11, tvOS 11.0, watchOS 4.0, *) + public static func es512(publicKey: Data) -> JWTVerifier { + return JWTVerifier(verifierAlgorithm: BlueECVerifier(key: publicKey, curve: .secp521r1)) + } + + /// Initialize a JWTVerifier that will always return true when verifying the JWT. This is equivelent to using the "none" alg header. + public static let none = JWTVerifier(verifierAlgorithm: NoneAlgorithm()) +} diff --git a/Pods/SwiftJWT/Sources/SwiftJWT/NoneAlgorithm.swift b/Pods/SwiftJWT/Sources/SwiftJWT/NoneAlgorithm.swift new file mode 100644 index 0000000..c809280 --- /dev/null +++ b/Pods/SwiftJWT/Sources/SwiftJWT/NoneAlgorithm.swift @@ -0,0 +1,32 @@ +/** + * Copyright IBM Corporation 2017 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +import Foundation + +/// An EncryptionAlgorithm representing an alg of "none" in a JWT. +/// Using this algorithm means the header and claims will not be signed or verified. +struct NoneAlgorithm: VerifierAlgorithm, SignerAlgorithm { + + let name: String = "none" + + func sign(header: String, claims: String) -> String { + return header + "." + claims + } + + func verify(jwt: String) -> Bool { + return true + } +} diff --git a/Pods/SwiftJWT/Sources/SwiftJWT/RSAKeyType.swift b/Pods/SwiftJWT/Sources/SwiftJWT/RSAKeyType.swift new file mode 100644 index 0000000..048e4cf --- /dev/null +++ b/Pods/SwiftJWT/Sources/SwiftJWT/RSAKeyType.swift @@ -0,0 +1,29 @@ +/** + * Copyright IBM Corporation 2017 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +// MARK RSAKeyType + +/// The type of the key used in the RSA algorithm. +enum RSAKeyType { + /// The key is a certificate containing both the private and the public keys. + case certificate + + /// The key is an RSA public key. + case publicKey + + /// The key is an RSA private key. + case privateKey +} diff --git a/Pods/SwiftJWT/Sources/SwiftJWT/SignerAlgorithm.swift b/Pods/SwiftJWT/Sources/SwiftJWT/SignerAlgorithm.swift new file mode 100644 index 0000000..67cfa4a --- /dev/null +++ b/Pods/SwiftJWT/Sources/SwiftJWT/SignerAlgorithm.swift @@ -0,0 +1,20 @@ +/** + * Copyright IBM Corporation 2018 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +protocol SignerAlgorithm { + /// A function to sign the header and claims of a JSON web token and return a signed JWT string. + func sign(header: String, claims: String) throws -> String +} diff --git a/Pods/SwiftJWT/Sources/SwiftJWT/ValidateClaimsResult.swift b/Pods/SwiftJWT/Sources/SwiftJWT/ValidateClaimsResult.swift new file mode 100644 index 0000000..91e9d54 --- /dev/null +++ b/Pods/SwiftJWT/Sources/SwiftJWT/ValidateClaimsResult.swift @@ -0,0 +1,52 @@ +/** + * Copyright IBM Corporation 2017 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +// MARK: ValidateClaimsResult + +/// ValidateClaimsResult list the possible results of a call to JWT.validateClaims method. +/// In case of successful validation, .success is returned, all other cases list various +/// problems that may occur during claims validation and indicate that the validation failed. +public struct ValidateClaimsResult: CustomStringConvertible, Equatable { + + /// The human readable description of the ValidateClaimsResult + public let description: String + + /// Successful validation. + public static let success = ValidateClaimsResult(description: "Success") + + /// Invalid Expiration claim. + public static let invalidExpiration = ValidateClaimsResult(description: "Invalid Expiration claim") + + /// Expired token: expiration time claim is in the past. + public static let expired = ValidateClaimsResult(description: "Expired token") + + /// Invalid Not Before claim. + public static let invalidNotBefore = ValidateClaimsResult(description: "Invalid Not Before claim") + + /// Not Before claim is in the future. + public static let notBefore = ValidateClaimsResult(description: "Token is not valid yet, Not Before claim is greater than the current time") + + /// Invalid Issued At claim. + public static let invalidIssuedAt = ValidateClaimsResult(description: "Invalid Issued At claim") + + /// Issued At claim is in the future. + public static let issuedAt = ValidateClaimsResult(description: "Issued At claim is greater than the current time") + + /// Check if two ValidateClaimsResults are equal. Required for the Equatable protocol + public static func == (lhs: ValidateClaimsResult, rhs: ValidateClaimsResult) -> Bool { + return lhs.description == rhs.description + } +} diff --git a/Pods/SwiftJWT/Sources/SwiftJWT/VerifierAlgorithm.swift b/Pods/SwiftJWT/Sources/SwiftJWT/VerifierAlgorithm.swift new file mode 100644 index 0000000..e7edd1d --- /dev/null +++ b/Pods/SwiftJWT/Sources/SwiftJWT/VerifierAlgorithm.swift @@ -0,0 +1,21 @@ +/** + * Copyright IBM Corporation 2018 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +protocol VerifierAlgorithm { + /// A function to verify the signature of a JSON web token string is correct for the header and claims. + func verify(jwt: String) -> Bool + +} diff --git a/Pods/SwiftyBeaver/LICENSE b/Pods/SwiftyBeaver/LICENSE new file mode 100644 index 0000000..5c4c4ad --- /dev/null +++ b/Pods/SwiftyBeaver/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Sebastian Kreutzberger + +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. diff --git a/Pods/SwiftyBeaver/PrivacyInfo.xcprivacy b/Pods/SwiftyBeaver/PrivacyInfo.xcprivacy new file mode 100644 index 0000000..b9fa038 --- /dev/null +++ b/Pods/SwiftyBeaver/PrivacyInfo.xcprivacy @@ -0,0 +1,27 @@ + + + + + NSPrivacyTracking + + NSPrivacyTrackingDomains + + NSPrivacyAccessedAPITypes + + NSPrivacyCollectedDataTypes + + + NSPrivacyCollectedDataType + NSPrivacyCollectedDataTypeOtherDiagnosticData + NSPrivacyCollectedDataTypeLinked + + NSPrivacyCollectedDataTypeTracking + + NSPrivacyCollectedDataTypePurposes + + NSPrivacyCollectedDataTypePurposeAppFunctionality + + + + + diff --git a/Pods/SwiftyBeaver/README.md b/Pods/SwiftyBeaver/README.md new file mode 100644 index 0000000..d526858 --- /dev/null +++ b/Pods/SwiftyBeaver/README.md @@ -0,0 +1,249 @@ +

Colorful, flexible, lightweight logging for Swift 3, Swift 4 & Swift 5.
Great for development & release with support for Console, file & cloud destinations for server-side Swift.

+ +

Language Swift 2, 3, 4 & 5 CircleCI

+ +--- + +
+ +### During Development: Colored Logging to Xcode Console via OSLog API or Print + +image + + +#### In Xcode 15 +```Swift + +// use Apple's fancy OSLog API: +let console = ConsoleDestination() +console.logPrintWay = .logger(subsystem: "Main", category: "UI") + +// or use good ol' "print" (which is the default): +let console = ConsoleDestination() +console.logPrintWay = .print +``` +#### In Xcode 8 +[Learn more](http://docs.swiftybeaver.com/article/9-log-to-xcode-console) about colored logging to Xcode 8 Console with Swift 3, 4 & 5. For Swift 2.3 [use this Gist](https://gist.github.com/skreutzberger/7c396573796473ed1be2c6d15cafed34). **No need to hack Xcode 8 anymore** to get color. You can even customize the log level word (ATTENTION instead of ERROR maybe?), the general amount of displayed data and if you want to use the 💜s or replace them with something else 😉 + +
+ +### During Development: Colored Logging to File + + + +[Learn more](http://docs.swiftybeaver.com/article/10-log-to-file) about logging to file which is great for Terminal.app fans or to store logs on disk. + +
+ +### Google Cloud & More + +You can fully customize your log format, turn it into JSON, or create your own destinations. For example, our [Google Cloud Destination](https://github.com/SwiftyBeaver/SwiftyBeaver/blob/master/Sources/GoogleCloudDestination.swift) is just another customized logging format that adds the powerful functionality of automatic server-side Swift logging when hosted on Google Cloud Platform. + +
+ +--- + +
+
+ +## Installation + +- For **Swift 4 & 5** install the latest SwiftyBeaver version +- For **Swift 3** install SwiftyBeaver 1.8.4 +- For **Swift 2** install SwiftyBeaver 0.7.0 + +
+ +### Carthage + +You can use [Carthage](https://github.com/Carthage/Carthage) to install SwiftyBeaver by adding that to your Cartfile: + +Swift 4 & 5: + +```Swift +github "SwiftyBeaver/SwiftyBeaver" +``` + +Swift 3: + +```Swift +github "SwiftyBeaver/SwiftyBeaver" ~> 1.8.4 +``` + +Swift 2: + +```Swift +github "SwiftyBeaver/SwiftyBeaver" ~> 0.7 +``` + +
+ +### Swift Package Manager + +For [Swift Package Manager](https://swift.org/package-manager/) add the following package to your Package.swift file. Just Swift 4 & 5 are supported: + +```Swift +.package(url: "https://github.com/SwiftyBeaver/SwiftyBeaver.git", .upToNextMajor(from: "2.0.0")), +``` + +
+ +### CocoaPods + +To use [CocoaPods](https://cocoapods.org) just add this to your Podfile: + +Swift 4 & 5: + +```Swift +pod 'SwiftyBeaver' +``` + +Swift 3: + +```Ruby +target 'MyProject' do + use_frameworks! + + # Pods for MyProject + pod 'SwiftyBeaver', '~> 1.8.4' +end +``` + +Swift 2: + +```Ruby +target 'MyProject' do + use_frameworks! + + # Pods for MyProject + pod 'SwiftyBeaver', '~> 0.7' +end + +post_install do |installer| + installer.pods_project.build_configurations.each do |config| + # Configure Pod targets for Xcode 8 with Swift 2.3 + config.build_settings['SWIFT_VERSION'] = '2.3' + end +end +``` + +
+
+ +## Usage + +Add that near the top of your `AppDelegate.swift` to be able to use SwiftyBeaver in your whole project. + +```Swift +import SwiftyBeaver +let log = SwiftyBeaver.self + +``` + +At the beginning of your `AppDelegate:didFinishLaunchingWithOptions()` add the SwiftyBeaver log destinations (console, file, etc.), optionally adjust the [log format](http://docs.swiftybeaver.com/article/20-custom-format) and then you can already do the following log level calls globally: + +```Swift +// add log destinations. at least one is needed! +let console = ConsoleDestination() // log to Xcode Console +let file = FileDestination() // log to default swiftybeaver.log file + +// use custom format and set console output to short time, log level & message +console.format = "$DHH:mm:ss$d $L $M" +// or use this for JSON output: console.format = "$J" + +// In Xcode 15, specifying the logging method as .logger to display color, subsystem, and category information in the console.(Relies on the OSLog API) +console.logPrintWay = .logger(subsystem: "Main", category: "UI") +// If you prefer not to use the OSLog API, you can use print instead. +// console.logPrintWay = .print + +// add the destinations to SwiftyBeaver +log.addDestination(console) +log.addDestination(file) + +// Now let’s log! +log.verbose("not so important") // prio 1, VERBOSE in silver +log.debug("something to debug") // prio 2, DEBUG in green +log.info("a nice information") // prio 3, INFO in blue +log.warning("oh no, that won’t be good") // prio 4, WARNING in yellow +log.error("ouch, an error did occur!") // prio 5, ERROR in red + +// log anything! +log.verbose(123) +log.info(-123.45678) +log.warning(Date()) +log.error(["I", "like", "logs!"]) +log.error(["name": "Mr Beaver", "address": "7 Beaver Lodge"]) + +// optionally add context to a log message +console.format = "$L: $M $X" +log.debug("age", context: 123) // "DEBUG: age 123" +log.info("my data", context: [1, "a", 2]) // "INFO: my data [1, \"a\", 2]" + +``` + +Alternatively, if you are using SwiftUI, consider using the following setup: + +```swift +import SwiftyBeaver +let logger = SwiftyBeaver.self + +@main +struct yourApp: App { + + init() { + let console = ConsoleDestination() + logger.addDestination(console) + // etc... + } + + var body: some Scene { + WindowGroup { + } + } +} +``` + +
+
+ +## Server-side Swift + +We ❤️ server-side Swift 4 & 5 and SwiftyBeaver support it **out-of-the-box**! Try for yourself and run SwiftyBeaver inside a Ubuntu Docker container. Just install Docker and then go to your project folder on macOS or Ubuntu and type: + +```shell +# create docker image, build SwiftyBeaver and run unit tests +docker run --rm -it -v $PWD:/app swiftybeaver /bin/bash -c "cd /app ; swift build ; swift test" + +# optionally log into container to run Swift CLI and do more stuff +docker run --rm -it --privileged=true -v $PWD:/app swiftybeaver +``` + +Best: for the popular server-side Swift web framework [Vapor](https://github.com/vapor/vapor) you can use **[our Vapor logging provider](https://github.com/SwiftyBeaver/SwiftyBeaver-Vapor)** which makes server logging awesome again 🙌 + +
+
+ +## Documentation + +**Getting Started:** + +- [Features](http://docs.swiftybeaver.com/article/7-introduction) +- [Installation](http://docs.swiftybeaver.com/article/5-installation) +- [Basic Setup](http://docs.swiftybeaver.com/article/6-basic-setup) + +**Logging Destinations:** + +- [Colored Logging to Xcode Console](http://docs.swiftybeaver.com/article/9-log-to-xcode-console) +- [Colored Logging to File](http://docs.swiftybeaver.com/article/10-log-to-file) + +**Advanced Topics:** + +- [Custom Format & Context](http://docs.swiftybeaver.com/article/20-custom-format) +- [Filters](http://docs.swiftybeaver.com/article/21-filters) + +
+
+ +## License + +SwiftyBeaver Framework is released under the [MIT License](https://github.com/SwiftyBeaver/SwiftyBeaver/blob/master/LICENSE). diff --git a/Pods/SwiftyBeaver/Sources/Base64.swift b/Pods/SwiftyBeaver/Sources/Base64.swift new file mode 100644 index 0000000..2d850da --- /dev/null +++ b/Pods/SwiftyBeaver/Sources/Base64.swift @@ -0,0 +1,165 @@ +// +// Base64.swift +// SwiftyBeaver (macOS) +// +// Copyright © 2017 Sebastian Kreutzberger. All rights reserved. +// +#if os(Linux) +import Foundation + +struct InvalidBase64: Error {} + +struct Base64 { + static func decode(_ string: String) throws -> [UInt8] { + return try decode([UInt8](string.utf8)) + } + + /// Decodes a Base64 encoded String into Data + /// + /// - throws: If the string isn't base64 encoded + static func decode(_ string: [UInt8]) throws -> [UInt8] { + let lookupTable: [UInt8] = [ + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 62, 64, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64, + 64, 00, 01, 02, 03, 04, 05, 06, 07, 08, 09, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 63, + 64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64 + ] + + let remainder = string.count % 4 + let length = (string.count - remainder) / 4 + + var decoded = [UInt8]() + decoded.reserveCapacity(length) + + var index = 0 + var i0: UInt8 = 0 + var i1: UInt8 = 0 + var i2: UInt8 = 0 + var i3: UInt8 = 0 + + while index &+ 4 < string.count { + i0 = lookupTable[numericCast(string[index])] + i1 = lookupTable[numericCast(string[index &+ 1])] + i2 = lookupTable[numericCast(string[index &+ 2])] + i3 = lookupTable[numericCast(string[index &+ 3])] + + if i0 > 63 || i1 > 63 || i2 > 63 || i3 > 63 { + throw InvalidBase64() + } + + decoded.append(i0 << 2 | i1 >> 4) + decoded.append(i1 << 4 | i2 >> 2) + decoded.append(i2 << 6 | i3) + index += 4 + } + + if string.count &- index > 1 { + i0 = lookupTable[numericCast(string[index])] + i1 = lookupTable[numericCast(string[index &+ 1])] + + if i1 > 63 { + guard string[index] == 61 else { + throw InvalidBase64() + } + + return decoded + } + + if i2 > 63 { + guard string[index &+ 2] == 61 else { + throw InvalidBase64() + } + + return decoded + } + + decoded.append(i0 << 2 | i1 >> 4) + + if string.count &- index > 2 { + i2 = lookupTable[numericCast(string[index &+ 2])] + + if i2 > 63 { + guard string[index &+ 2] == 61 else { + throw InvalidBase64() + } + + return decoded + } + + decoded.append(i1 << 4 | i2 >> 2) + + if string.count &- index > 3 { + i3 = lookupTable[numericCast(string[index &+ 3])] + + if i3 > 63 { + guard string[index &+ 3] == 61 else { + throw InvalidBase64() + } + + return decoded + } + + decoded.append(i2 << 6 | i3) + } + } + } + + return decoded + } + + static func encode(_ data: [UInt8]) -> String { + let base64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" + var encoded: String = "" + + func appendCharacterFromBase(_ character: Int) { + encoded.append(base64[base64.index(base64.startIndex, offsetBy: character)]) + } + + func byte(_ index: Int) -> Int { + return Int(data[index]) + } + + let decodedBytes = data.map { Int($0) } + + var i = 0 + + while i < decodedBytes.count - 2 { + appendCharacterFromBase((byte(i) >> 2) & 0x3F) + appendCharacterFromBase(((byte(i) & 0x3) << 4) | ((byte(i + 1) & 0xF0) >> 4)) + appendCharacterFromBase(((byte(i + 1) & 0xF) << 2) | ((byte(i + 2) & 0xC0) >> 6)) + appendCharacterFromBase(byte(i + 2) & 0x3F) + i += 3 + } + + if i < decodedBytes.count { + appendCharacterFromBase((byte(i) >> 2) & 0x3F) + + if i == decodedBytes.count - 1 { + appendCharacterFromBase(((byte(i) & 0x3) << 4)) + encoded.append("=") + } else { + appendCharacterFromBase(((byte(i) & 0x3) << 4) | ((byte(i + 1) & 0xF0) >> 4)) + appendCharacterFromBase(((byte(i + 1) & 0xF) << 2)) + } + + encoded.append("=") + } + + return encoded + } +} + +#endif diff --git a/Pods/SwiftyBeaver/Sources/BaseDestination.swift b/Pods/SwiftyBeaver/Sources/BaseDestination.swift new file mode 100644 index 0000000..c494e1e --- /dev/null +++ b/Pods/SwiftyBeaver/Sources/BaseDestination.swift @@ -0,0 +1,529 @@ +// +// BaseDestination.swift +// SwiftyBeaver +// +// Created by Sebastian Kreutzberger (Twitter @skreutzb) on 05.12.15. +// Copyright © 2015 Sebastian Kreutzberger +// Some rights reserved: http://opensource.org/licenses/MIT +// + +import Foundation +import Dispatch + +// store operating system / platform +#if os(iOS) +let OS = "iOS" +#elseif os(OSX) +let OS = "OSX" +#elseif os(watchOS) +let OS = "watchOS" +#elseif os(tvOS) +let OS = "tvOS" +#elseif os(Linux) +let OS = "Linux" +#elseif os(FreeBSD) +let OS = "FreeBSD" +#elseif os(Windows) +let OS = "Windows" +#elseif os(Android) +let OS = "Android" +#else +let OS = "Unknown" +#endif + +/// destination which all others inherit from. do not directly use +open class BaseDestination: Hashable, Equatable { + + /// output format pattern, see documentation for syntax + open var format = "$DHH:mm:ss.SSS$d $C$L$c $N.$F:$l - $M" + + /// runs in own serial background thread for better performance + open var asynchronously = true + + /// do not log any message which has a lower level than this one + open var minLevel = SwiftyBeaver.Level.verbose + + /// set custom log level words for each level + open var levelString = LevelString() + + /// set custom log level colors for each level + open var levelColor = LevelColor() + + /// set custom calendar for dateFormatter + open var calendar = Calendar.current + + public struct LevelString { + public var verbose = "VERBOSE" + public var debug = "DEBUG" + public var info = "INFO" + public var warning = "WARNING" + public var error = "ERROR" + public var critical = "CRITICAL" + public var fault = "FAULT" + } + + // For a colored log level word in a logged line + // empty on default + public struct LevelColor { + public var verbose = "" // silver + public var debug = "" // green + public var info = "" // blue + public var warning = "" // yellow + public var error = "" // red + public var critical = "" // red + public var fault = "" // red + } + + var reset = "" + var escape = "" + + var filters = [FilterType]() + let formatter = DateFormatter() + let startDate = Date() + + // each destination class must have an own hashValue Int + #if swift(>=4.2) + public func hash(into hasher: inout Hasher) { + hasher.combine(defaultHashValue) + } + #else + lazy public var hashValue: Int = self.defaultHashValue + #endif + + open var defaultHashValue: Int {return 0} + + // each destination instance must have an own serial queue to ensure serial output + // GCD gives it a prioritization between User Initiated and Utility + var queue: DispatchQueue? //dispatch_queue_t? + var debugPrint = false // set to true to debug the internal filter logic of the class + + public init() { + let uuid = NSUUID().uuidString + let queueLabel = "swiftybeaver-queue-" + uuid + queue = DispatchQueue(label: queueLabel, target: queue) + } + + /// send / store the formatted log message to the destination + /// returns the formatted log message for processing by inheriting method + /// and for unit tests (nil if error) + open func send(_ level: SwiftyBeaver.Level, msg: String, thread: String, file: String, + function: String, line: Int, context: Any? = nil) -> String? { + + if format.hasPrefix("$J") { + return messageToJSON(level, msg: msg, thread: thread, + file: file, function: function, line: line, context: context) + + } else { + return formatMessage(format, level: level, msg: msg, thread: thread, + file: file, function: function, line: line, context: context) + } + } + + public func execute(synchronously: Bool, block: @escaping () -> Void) { + guard let queue = queue else { + fatalError("Queue not set") + } + if synchronously { + queue.sync(execute: block) + } else { + queue.async(execute: block) + } + } + + public func executeSynchronously(block: @escaping () throws -> T) rethrows -> T { + guard let queue = queue else { + fatalError("Queue not set") + } + return try queue.sync(execute: block) + } + + //////////////////////////////// + // MARK: Format + //////////////////////////////// + + /// returns (padding length value, offset in string after padding info) + private func parsePadding(_ text: String) -> (Int, Int) { + // look for digits followed by a alpha character + var s: String! + var sign: Int = 1 + if text.firstChar == "-" { + sign = -1 + s = String(text.suffix(from: text.index(text.startIndex, offsetBy: 1))) + } else { + s = text + } + let numStr = String(s.prefix { $0 >= "0" && $0 <= "9" }) + if let num = Int(numStr) { + return (sign * num, (sign == -1 ? 1 : 0) + numStr.count) + } else { + return (0, 0) + } + } + + private func paddedString(_ text: String, _ toLength: Int, truncating: Bool = false) -> String { + if toLength > 0 { + // Pad to the left of the string + if text.count > toLength { + // Hm... better to use suffix or prefix? + return truncating ? String(text.suffix(toLength)) : text + } else { + return "".padding(toLength: toLength - text.count, withPad: " ", startingAt: 0) + text + } + } else if toLength < 0 { + // Pad to the right of the string + let maxLength = truncating ? -toLength : max(-toLength, text.count) + return text.padding(toLength: maxLength, withPad: " ", startingAt: 0) + } else { + return text + } + } + + /// returns the log message based on the format pattern + func formatMessage(_ format: String, level: SwiftyBeaver.Level, msg: String, thread: String, + file: String, function: String, line: Int, context: Any? = nil) -> String { + + var text = "" + // Prepend a $I for 'ignore' or else the first character is interpreted as a format character + // even if the format string did not start with a $. + let phrases: [String] = ("$I" + format).components(separatedBy: "$") + + for phrase in phrases where !phrase.isEmpty { + let (padding, offset) = parsePadding(phrase) + let formatCharIndex = phrase.index(phrase.startIndex, offsetBy: offset) + let formatChar = phrase[formatCharIndex] + let rangeAfterFormatChar = phrase.index(formatCharIndex, offsetBy: 1)..=3.2) + text += paddedString(formatDate(String(remainingPhrase)), padding) + #else + text += paddedString(formatDate(remainingPhrase), padding) + #endif + case "d": + text += remainingPhrase + case "U": + text += paddedString(uptime(), padding) + remainingPhrase + case "Z": + // start of datetime format in UTC timezone + #if swift(>=3.2) + text += paddedString(formatDate(String(remainingPhrase), timeZone: "UTC"), padding) + #else + text += paddedString(formatDate(remainingPhrase, timeZone: "UTC"), padding) + #endif + case "z": + text += remainingPhrase + case "C": + // color code ("" on default) + text += escape + colorForLevel(level) + remainingPhrase + case "c": + text += reset + remainingPhrase + case "X": + // add the context + if let cx = context { + text += paddedString(String(describing: cx).trimmingCharacters(in: .whitespacesAndNewlines), padding) + remainingPhrase + } else { + text += paddedString("", padding) + remainingPhrase + } + default: + text += phrase + } + } + // right trim only + return text.replacingOccurrences(of: "\\s+$", with: "", options: .regularExpression) + } + + /// returns the log payload as optional JSON string + func messageToJSON(_ level: SwiftyBeaver.Level, msg: String, + thread: String, file: String, function: String, line: Int, context: Any? = nil) -> String? { + var dict: [String: Any] = [ + "timestamp": Date().timeIntervalSince1970, + "level": level.rawValue, + "message": msg, + "thread": thread, + "file": file, + "function": function, + "line": line + ] + if let cx = context { + dict["context"] = cx + } + return jsonStringFromDict(dict) + } + + /// returns the string of a level + func levelWord(_ level: SwiftyBeaver.Level) -> String { + + var str = "" + + switch level { + case .verbose: + str = levelString.verbose + + case .debug: + str = levelString.debug + + case .info: + str = levelString.info + + case .warning: + str = levelString.warning + + case .error: + str = levelString.error + + case .critical: + str = levelString.critical + + case .fault: + str = levelString.fault + } + return str + } + + /// returns color string for level + func colorForLevel(_ level: SwiftyBeaver.Level) -> String { + var color = "" + + switch level { + case .verbose: + color = levelColor.verbose + + case .debug: + color = levelColor.debug + + case .info: + color = levelColor.info + + case .warning: + color = levelColor.warning + + case .error: + color = levelColor.error + + case .critical: + color = levelColor.critical + + case .fault: + color = levelColor.fault + } + return color + } + + /// returns the filename of a path + func fileNameOfFile(_ file: String) -> String { + let fileParts = file.components(separatedBy: "/") + if let lastPart = fileParts.last { + return lastPart + } + return "" + } + + /// returns the filename without suffix (= file ending) of a path + func fileNameWithoutSuffix(_ file: String) -> String { + let fileName = fileNameOfFile(file) + + if !fileName.isEmpty { + let fileNameParts = fileName.components(separatedBy: ".") + if let firstPart = fileNameParts.first { + return firstPart + } + } + return "" + } + + /// returns a formatted date string + /// optionally in a given abbreviated timezone like "UTC" + func formatDate(_ dateFormat: String, timeZone: String = "") -> String { + if !timeZone.isEmpty { + formatter.timeZone = TimeZone(abbreviation: timeZone) + } + formatter.calendar = calendar + formatter.dateFormat = dateFormat + //let dateStr = formatter.string(from: NSDate() as Date) + let dateStr = formatter.string(from: Date()) + return dateStr + } + + /// returns a uptime string + func uptime() -> String { + let interval = Date().timeIntervalSince(startDate) + + let hours = Int(interval) / 3600 + let minutes = Int(interval / 60) - Int(hours * 60) + let seconds = Int(interval) - (Int(interval / 60) * 60) + let milliseconds = Int(interval.truncatingRemainder(dividingBy: 1) * 1000) + + return String(format: "%0.2d:%0.2d:%0.2d.%03d", arguments: [hours, minutes, seconds, milliseconds]) + } + + /// returns the json-encoded string value + /// after it was encoded by jsonStringFromDict + func jsonStringValue(_ jsonString: String?, key: String) -> String { + guard let str = jsonString else { + return "" + } + + // remove the leading {"key":" from the json string and the final } + let offset = key.length + 5 + let endIndex = str.index(str.startIndex, + offsetBy: str.length - 2) + let range = str.index(str.startIndex, offsetBy: offset)..=3.2) + return String(str[range]) + #else + return str[range] + #endif + } + + /// turns dict into JSON-encoded string + func jsonStringFromDict(_ dict: [String: Any]) -> String? { + var jsonString: String? + + // try to create JSON string + do { + let jsonData = try JSONSerialization.data(withJSONObject: dict, options: []) + jsonString = String(data: jsonData, encoding: .utf8) + } catch { + print("SwiftyBeaver could not create JSON from dict.") + } + return jsonString + } + + //////////////////////////////// + // MARK: Filters + //////////////////////////////// + + /// Add a filter that determines whether or not a particular message will be logged to this destination + public func addFilter(_ filter: FilterType) { + filters.append(filter) + } + + /// Remove a filter from the list of filters + public func removeFilter(_ filter: FilterType) { + #if swift(>=5) + let index = filters.firstIndex { + return ObjectIdentifier($0) == ObjectIdentifier(filter) + } + #else + let index = filters.index { + return ObjectIdentifier($0) == ObjectIdentifier(filter) + } + #endif + + guard let filterIndex = index else { + return + } + + filters.remove(at: filterIndex) + } + + /// Answer whether the destination has any message filters + /// returns boolean and is used to decide whether to resolve + /// the message before invoking shouldLevelBeLogged + func hasMessageFilters() -> Bool { + return !getFiltersTargeting(Filter.TargetType.Message(.Equals([], true)), + fromFilters: self.filters).isEmpty + } + + /// checks if level is at least minLevel or if a minLevel filter for that path does exist + /// returns boolean and can be used to decide if a message should be logged or not + func shouldLevelBeLogged(_ level: SwiftyBeaver.Level, path: String, + function: String, message: String? = nil) -> Bool { + + if filters.isEmpty { + if level.rawValue >= minLevel.rawValue { + if debugPrint { + print("filters are empty and level >= minLevel") + } + return true + } else { + if debugPrint { + print("filters are empty and level < minLevel") + } + return false + } + } + + let filterCheckResult = FilterValidator.validate(input: .init(filters: self.filters, level: level, path: path, function: function, message: message)) + + // Exclusion filters match if they do NOT meet the filter condition (see Filter.apply(_:) method) + switch filterCheckResult[.excluded] { + case .some(.someFiltersMatch): + // Exclusion filters are present and at least one of them matches the log entry + if debugPrint { + print("filters are not empty and message was excluded") + } + return false + case .some(.allFiltersMatch), .some(.noFiltersMatchingType), .none: break + } + + // If required filters exist, we should validate or invalidate the log if all of them pass or not + switch filterCheckResult[.required] { + case .some(.allFiltersMatch): return true + case .some(.someFiltersMatch): return false + case .some(.noFiltersMatchingType), .none: break + } + + let checkLogLevel: () -> Bool = { + // Check if the log message's level matches or exceeds the minLevel of the destination + return level.rawValue >= self.minLevel.rawValue + } + + // Non-required filters should only be applied if the log entry matches the filter condition (e.g. path) + switch filterCheckResult[.nonRequired] { + case .some(.allFiltersMatch): return true + case .some(.noFiltersMatchingType), .none: return checkLogLevel() + case .some(.someFiltersMatch(let partialMatchData)): + if partialMatchData.fullMatchCount > 0 { + // The log entry matches at least one filter condition and the destination's log level + return true + } else if partialMatchData.conditionMatchCount > 0 { + // The log entry matches at least one filter condition, but does not match or exceed the destination's log level + return false + } else { + // There is no filter with a matching filter condition. Check the destination's log level + return checkLogLevel() + } + } + } + + func getFiltersTargeting(_ target: Filter.TargetType, fromFilters: [FilterType]) -> [FilterType] { + return fromFilters.filter { filter in + return filter.getTarget() == target + } + } + + /** + Triggered by main flush() method on each destination. Runs in background thread. + Use for destinations that buffer log items, implement this function to flush those + buffers to their final destination (web server...) + */ + func flush() { + // no implementation in base destination needed + } +} + +public func == (lhs: BaseDestination, rhs: BaseDestination) -> Bool { + return ObjectIdentifier(lhs) == ObjectIdentifier(rhs) +} diff --git a/Pods/SwiftyBeaver/Sources/ConsoleDestination.swift b/Pods/SwiftyBeaver/Sources/ConsoleDestination.swift new file mode 100644 index 0000000..ec17d44 --- /dev/null +++ b/Pods/SwiftyBeaver/Sources/ConsoleDestination.swift @@ -0,0 +1,130 @@ +// +// ConsoleDestination.swift +// SwiftyBeaver +// +// Created by Sebastian Kreutzberger on 05.12.15. +// Copyright © 2015 Sebastian Kreutzberger +// Some rights reserved: http://opensource.org/licenses/MIT +// + +import Foundation +#if canImport(OSLog) +import OSLog +#endif + +open class ConsoleDestination: BaseDestination { + public enum LogPrintWay { + case logger(subsystem: String, category: String) + case nslog + case print + } + + /// Use this to change the logging method to the console. By default, it is set to .print. You can switch to .logger(subsystem:category:) to utilize the OSLog API. + public var logPrintWay: LogPrintWay = .print + /// use NSLog instead of print, default is false + public var useNSLog = false { + didSet { + if useNSLog { + logPrintWay = .nslog + } + } + } + /// uses colors compatible to Terminal instead of Xcode, default is false + public var useTerminalColors: Bool = false { + didSet { + if useTerminalColors { + // use Terminal colors + reset = "\u{001b}[0m" + escape = "\u{001b}[38;5;" + levelColor.verbose = "251m" // silver + levelColor.debug = "35m" // green + levelColor.info = "38m" // blue + levelColor.warning = "178m" // yellow + levelColor.error = "197m" // red + levelColor.critical = "197m" // red + levelColor.fault = "197m" // red + } else { + // use colored Emojis for better visual distinction + // of log level for Xcode 8 + levelColor.verbose = "💜 " // purple + levelColor.debug = "💚 " // green + levelColor.info = "💙 " // blue + levelColor.warning = "💛 " // yellow + levelColor.error = "❤️ " // red + levelColor.critical = "❤️ " // red + levelColor.fault = "❤️ " // red + } + } + } + + override public var defaultHashValue: Int { return 1 } + + public override init() { + super.init() + levelColor.verbose = "💜 " // purple + levelColor.debug = "💚 " // green + levelColor.info = "💙 " // blue + levelColor.warning = "💛 " // yellow + levelColor.error = "❤️ " // red + levelColor.critical = "❤️ " // red + levelColor.fault = "❤️ " // red + } + + // print to Xcode Console. uses full base class functionality + override open func send(_ level: SwiftyBeaver.Level, msg: String, thread: String, + file: String, function: String, line: Int, context: Any? = nil) -> String? { + let formattedString = super.send(level, msg: msg, thread: thread, file: file, function: function, line: line, context: context) + + if let message = formattedString { +#if os(Linux) + print(message) +#else + switch logPrintWay { + case let .logger(subsystem, category): + _logger(message: message, level: level, subsystem: subsystem, category: category) + case .nslog: + _nslog(message: message) + case .print: + _print(message: message) + } +#endif + } + return formattedString + } + + private func _logger(message: String, level: SwiftyBeaver.Level, subsystem: String, category: String) { +#if canImport(OSLog) + if #available(macOS 11.0, iOS 14.0, watchOS 7.0, tvOS 14.0, *) { + let logger = Logger(subsystem: subsystem, category: category) + switch level { + case .verbose: + logger.trace("\(message)") + case .debug: + logger.debug("\(message)") + case .info: + logger.info("\(message)") + case .warning: + logger.warning("\(message)") + case .error: + logger.error("\(message)") + case .critical: + logger.critical("\(message)") + case .fault: + logger.fault("\(message)") + } + } else { + _print(message: message) + } +#else + _print(message: message) +#endif + } + + private func _nslog(message: String) { + NSLog("%@", message) + } + + private func _print(message: String) { + print(message) + } +} diff --git a/Pods/SwiftyBeaver/Sources/Extensions.swift b/Pods/SwiftyBeaver/Sources/Extensions.swift new file mode 100644 index 0000000..490e625 --- /dev/null +++ b/Pods/SwiftyBeaver/Sources/Extensions.swift @@ -0,0 +1,35 @@ +// +// Extensions.swift +// SwiftyBeaver +// +// Created by Sebastian Kreutzberger on 13.12.17. +// Copyright © 2017 Sebastian Kreutzberger. All rights reserved. +// + +import Foundation + +extension String { + /// cross-Swift compatible characters count + var length: Int { + return self.count + } + + /// cross-Swift-compatible first character + var firstChar: Character? { + return self.first + } + + /// cross-Swift-compatible last character + var lastChar: Character? { + return self.last + } + + /// cross-Swift-compatible index + func find(_ char: Character) -> Index? { + #if swift(>=5) + return self.firstIndex(of: char) + #else + return self.index(of: char) + #endif + } +} diff --git a/Pods/SwiftyBeaver/Sources/FileDestination.swift b/Pods/SwiftyBeaver/Sources/FileDestination.swift new file mode 100644 index 0000000..6513c59 --- /dev/null +++ b/Pods/SwiftyBeaver/Sources/FileDestination.swift @@ -0,0 +1,240 @@ +// +// FileDestination.swift +// SwiftyBeaver +// +// Created by Sebastian Kreutzberger on 05.12.15. +// Copyright © 2015 Sebastian Kreutzberger +// Some rights reserved: http://opensource.org/licenses/MIT +// + +import Foundation + +open class FileDestination: BaseDestination { + + public var logFileURL: URL? + public var syncAfterEachWrite: Bool = false + public var colored: Bool = false { + didSet { + if colored { + // bash font color, first value is intensity, second is color + // see http://bit.ly/1Otu3Zr & for syntax http://bit.ly/1Tp6Fw9 + // uses the 256-color table from http://bit.ly/1W1qJuH + reset = "\u{001b}[0m" + escape = "\u{001b}[38;5;" + levelColor.verbose = "251m" // silver + levelColor.debug = "35m" // green + levelColor.info = "38m" // blue + levelColor.warning = "178m" // yellow + levelColor.error = "197m" // red + } else { + reset = "" + escape = "" + levelColor.verbose = "" + levelColor.debug = "" + levelColor.info = "" + levelColor.warning = "" + levelColor.error = "" + } + } + } + + // LOGFILE ROTATION + // ho many bytes should a logfile have until it is rotated? + // default is 5 MB. Just is used if logFileAmount > 1 + public var logFileMaxSize = (5 * 1024 * 1024) + // Number of log files used in rotation, default is 1 which deactivates file rotation + public var logFileAmount = 1 + + override public var defaultHashValue: Int {return 2} + let fileManager = FileManager.default + + + public init(logFileURL: URL? = nil) { + if let logFileURL = logFileURL { + self.logFileURL = logFileURL + super.init() + return + } + + // platform-dependent logfile directory default + var baseURL: URL? + #if os(OSX) + if let url = fileManager.urls(for: .cachesDirectory, in: .userDomainMask).first { + baseURL = url + // try to use ~/Library/Caches/APP NAME instead of ~/Library/Caches + if let appName = Bundle.main.object(forInfoDictionaryKey: "CFBundleExecutable") as? String { + do { + if let appURL = baseURL?.appendingPathComponent(appName, isDirectory: true) { + try fileManager.createDirectory(at: appURL, + withIntermediateDirectories: true, attributes: nil) + baseURL = appURL + } + } catch { + print("Warning! Could not create folder /Library/Caches/\(appName)") + } + } + } + #else + #if os(Linux) + baseURL = URL(fileURLWithPath: "/var/cache") + #else + // iOS, watchOS, etc. are using the caches directory + if let url = fileManager.urls(for: .cachesDirectory, in: .userDomainMask).first { + baseURL = url + } + #endif + #endif + + if let baseURL = baseURL { + self.logFileURL = baseURL.appendingPathComponent("swiftybeaver.log", isDirectory: false) + } + super.init() + } + + // append to file. uses full base class functionality + override open func send(_ level: SwiftyBeaver.Level, msg: String, thread: String, + file: String, function: String, line: Int, context: Any? = nil) -> String? { + let formattedString = super.send(level, msg: msg, thread: thread, file: file, function: function, line: line, context: context) + + if let str = formattedString { + _ = validateSaveFile(str: str) + } + return formattedString + } + + // check if filesize is bigger than wanted and if yes then rotate them + func validateSaveFile(str: String) -> Bool { + if self.logFileAmount > 1 { + guard let url = logFileURL else { return false } + let filePath = url.path + if FileManager.default.fileExists(atPath: filePath) == true { + do { + // Get file size + let attr = try FileManager.default.attributesOfItem(atPath: filePath) + let fileSize = attr[FileAttributeKey.size] as! UInt64 + // Do file rotation + if fileSize > logFileMaxSize { + rotateFile(url) + } + } catch { + print("validateSaveFile error: \(error)") + } + } + } + return saveToFile(str: str) + } + + private func rotateFile(_ fileUrl: URL) { + let filePath = fileUrl.path + let lastIndex = (logFileAmount-1) + let firstIndex = 1 + do { + for index in stride(from: lastIndex, through: firstIndex, by: -1) { + let oldFile = makeRotatedFileUrl(fileUrl, index: index).path + + if FileManager.default.fileExists(atPath: oldFile) { + if index == lastIndex { + // Delete the last file + try FileManager.default.removeItem(atPath: oldFile) + } else { + // Move the current file to next index + let newFile = makeRotatedFileUrl(fileUrl, index: index + 1).path + try FileManager.default.moveItem(atPath: oldFile, toPath: newFile) + } + } + } + + // Finally, move the current file + let newFile = makeRotatedFileUrl(fileUrl, index: firstIndex).path + try FileManager.default.moveItem(atPath: filePath, toPath: newFile) + } catch { + print("rotateFile error: \(error)") + } + } + + private func makeRotatedFileUrl(_ fileUrl: URL, index: Int) -> URL { + // The index is appended to the file name, to preserve the original extension. + fileUrl.deletingPathExtension() + .appendingPathExtension("\(index).\(fileUrl.pathExtension)") + } + + /// appends a string as line to a file. + /// returns boolean about success + func saveToFile(str: String) -> Bool { + guard let url = logFileURL else { return false } + + let line = str + "\n" + guard let data = line.data(using: String.Encoding.utf8) else { return false } + + return write(data: data, to: url) + } + + private func write(data: Data, to url: URL) -> Bool { + + #if os(Linux) + return true + #else + var success = false + let coordinator = NSFileCoordinator(filePresenter: nil) + var error: NSError? + coordinator.coordinate(writingItemAt: url, error: &error) { url in + do { + if fileManager.fileExists(atPath: url.path) == false { + + let directoryURL = url.deletingLastPathComponent() + if fileManager.fileExists(atPath: directoryURL.path) == false { + try fileManager.createDirectory( + at: directoryURL, + withIntermediateDirectories: true + ) + } + fileManager.createFile(atPath: url.path, contents: nil) + + #if os(iOS) || os(watchOS) + if #available(iOS 10.0, watchOS 3.0, *) { + var attributes = try fileManager.attributesOfItem(atPath: url.path) + attributes[FileAttributeKey.protectionKey] = FileProtectionType.none + try fileManager.setAttributes(attributes, ofItemAtPath: url.path) + } + #endif + } + + let fileHandle = try FileHandle(forWritingTo: url) + fileHandle.seekToEndOfFile() + if #available(iOS 13.4, watchOS 6.2, tvOS 13.4, macOS 10.15.4, *) { + try fileHandle.write(contentsOf: data) + } else { + fileHandle.write(data) + } + if syncAfterEachWrite { + fileHandle.synchronizeFile() + } + fileHandle.closeFile() + success = true + } catch { + print("SwiftyBeaver File Destination could not write to file \(url).") + } + } + + if let error = error { + print("Failed writing file with error: \(String(describing: error))") + return false + } + + return success + #endif + } + + /// deletes log file. + /// returns true if file was removed or does not exist, false otherwise + public func deleteLogFile() -> Bool { + guard let url = logFileURL, fileManager.fileExists(atPath: url.path) == true else { return true } + do { + try fileManager.removeItem(at: url) + return true + } catch { + print("SwiftyBeaver File Destination could not remove file \(url).") + return false + } + } +} diff --git a/Pods/SwiftyBeaver/Sources/Filter.swift b/Pods/SwiftyBeaver/Sources/Filter.swift new file mode 100644 index 0000000..2ef0a2f --- /dev/null +++ b/Pods/SwiftyBeaver/Sources/Filter.swift @@ -0,0 +1,283 @@ +// +// Filter.swift +// SwiftyBeaver +// +// Created by Jeff Roberts on 5/31/16. +// Copyright © 2015 Sebastian Kreutzberger +// Some rights reserved: http://opensource.org/licenses/MIT +// + +import Foundation + +/// FilterType is a protocol that describes something that determines +/// whether or not a message gets logged. A filter answers a Bool when it +/// is applied to a value. If the filter passes, it shall return true, +/// false otherwise. +/// +/// A filter must contain a target, which identifies what it filters against +/// A filter can be required meaning that all required filters against a specific +/// target must pass in order for the message to be logged. +public protocol FilterType : AnyObject { + func apply(_ value: String?) -> Bool + func getTarget() -> Filter.TargetType + func isRequired() -> Bool + func isExcluded() -> Bool + func reachedMinLevel(_ level: SwiftyBeaver.Level) -> Bool +} + +/// Filters is syntactic sugar used to easily construct filters +public class Filters { + public static let Path = PathFilterFactory.self + public static let Function = FunctionFilterFactory.self + public static let Message = MessageFilterFactory.self +} + +/// Filter is an abstract base class for other filters +public class Filter { + public enum TargetType { + case Path(Filter.ComparisonType) + case Function(Filter.ComparisonType) + case Message(Filter.ComparisonType) + } + + public enum ComparisonType { + case StartsWith([String], Bool) + case Contains([String], Bool) + case Excludes([String], Bool) + case EndsWith([String], Bool) + case Equals([String], Bool) + case Custom((String) -> Bool) + } + + let targetType: Filter.TargetType + let required: Bool + let minLevel: SwiftyBeaver.Level + + public init(_ target: Filter.TargetType, required: Bool, minLevel: SwiftyBeaver.Level) { + self.targetType = target + self.required = required + self.minLevel = minLevel + } + + public func getTarget() -> Filter.TargetType { + return self.targetType + } + + public func isRequired() -> Bool { + return self.required + } + + public func isExcluded() -> Bool { + return false + } + + /// returns true of set minLevel is >= as given level + public func reachedMinLevel(_ level: SwiftyBeaver.Level) -> Bool { + //print("checking if given level \(level) >= \(minLevel)") + return level.rawValue >= minLevel.rawValue + } +} + +/// CompareFilter is a FilterType that can filter based upon whether a target +/// starts with, contains or ends with a specific string. CompareFilters can be +/// case sensitive. +public class CompareFilter: Filter, FilterType { + + private var filterComparisonType: Filter.ComparisonType? + + override public init(_ target: Filter.TargetType, required: Bool, minLevel: SwiftyBeaver.Level) { + super.init(target, required: required, minLevel: minLevel) + + let comparisonType: Filter.ComparisonType? + switch self.getTarget() { + case let .Function(comparison): + comparisonType = comparison + + case let .Path(comparison): + comparisonType = comparison + + case let .Message(comparison): + comparisonType = comparison + + /*default: + comparisonType = nil*/ + } + self.filterComparisonType = comparisonType + } + + public func apply(_ value: String?) -> Bool { + guard let value = value else { + return false + } + + guard let filterComparisonType = self.filterComparisonType else { + return false + } + + let matches: Bool + switch filterComparisonType { + case let .Contains(strings, caseSensitive): + matches = !strings.filter { string in + return caseSensitive ? value.contains(string) : + value.lowercased().contains(string.lowercased()) + }.isEmpty + + case let .Excludes(strings, caseSensitive): + matches = !strings.filter { string in + return caseSensitive ? !value.contains(string) : + !value.lowercased().contains(string.lowercased()) + }.isEmpty + + case let .StartsWith(strings, caseSensitive): + matches = !strings.filter { string in + return caseSensitive ? value.hasPrefix(string) : + value.lowercased().hasPrefix(string.lowercased()) + }.isEmpty + + case let .EndsWith(strings, caseSensitive): + matches = !strings.filter { string in + return caseSensitive ? value.hasSuffix(string) : + value.lowercased().hasSuffix(string.lowercased()) + }.isEmpty + + case let .Equals(strings, caseSensitive): + matches = !strings.filter { string in + return caseSensitive ? value == string : + value.lowercased() == string.lowercased() + }.isEmpty + case let .Custom(predicate): + matches = predicate(value) + } + + return matches + } + + override public func isExcluded() -> Bool { + guard let filterComparisonType = self.filterComparisonType else { return false } + + switch filterComparisonType { + case .Excludes: + return true + default: + return false + } + } +} + +// Syntactic sugar for creating a function comparison filter +public class FunctionFilterFactory { + public static func startsWith(_ prefixes: String..., caseSensitive: Bool = false, + required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType { + return CompareFilter(.Function(.StartsWith(prefixes, caseSensitive)), required: required, minLevel: minLevel) + } + + public static func contains(_ strings: String..., caseSensitive: Bool = false, + required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType { + return CompareFilter(.Function(.Contains(strings, caseSensitive)), required: required, minLevel: minLevel) + } + + public static func excludes(_ strings: String..., caseSensitive: Bool = false, + required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType { + return CompareFilter(.Function(.Excludes(strings, caseSensitive)), required: required, minLevel: minLevel) + } + + public static func endsWith(_ suffixes: String..., caseSensitive: Bool = false, + required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType { + return CompareFilter(.Function(.EndsWith(suffixes, caseSensitive)), required: required, minLevel: minLevel) + } + + public static func equals(_ strings: String..., caseSensitive: Bool = false, + required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType { + return CompareFilter(.Function(.Equals(strings, caseSensitive)), required: required, minLevel: minLevel) + } + + public static func custom(required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose, filterPredicate: @escaping (String) -> Bool) -> FilterType { + return CompareFilter(.Function(.Custom(filterPredicate)), required: required, minLevel: minLevel) + } +} + +// Syntactic sugar for creating a message comparison filter +public class MessageFilterFactory { + public static func startsWith(_ prefixes: String..., caseSensitive: Bool = false, + required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType { + return CompareFilter(.Message(.StartsWith(prefixes, caseSensitive)), required: required, minLevel: minLevel) + } + + public static func contains(_ strings: String..., caseSensitive: Bool = false, + required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType { + return CompareFilter(.Message(.Contains(strings, caseSensitive)), required: required, minLevel: minLevel) + } + + public static func excludes(_ strings: String..., caseSensitive: Bool = false, + required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType { + return CompareFilter(.Message(.Excludes(strings, caseSensitive)), required: required, minLevel: minLevel) + } + + public static func endsWith(_ suffixes: String..., caseSensitive: Bool = false, + required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType { + return CompareFilter(.Message(.EndsWith(suffixes, caseSensitive)), required: required, minLevel: minLevel) + } + + public static func equals(_ strings: String..., caseSensitive: Bool = false, + required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType { + return CompareFilter(.Message(.Equals(strings, caseSensitive)), required: required, minLevel: minLevel) + } + + public static func custom(required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose, filterPredicate: @escaping (String) -> Bool) -> FilterType { + return CompareFilter(.Message(.Custom(filterPredicate)), required: required, minLevel: minLevel) + } +} + +// Syntactic sugar for creating a path comparison filter +public class PathFilterFactory { + public static func startsWith(_ prefixes: String..., caseSensitive: Bool = false, + required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType { + return CompareFilter(.Path(.StartsWith(prefixes, caseSensitive)), required: required, minLevel: minLevel) + } + + public static func contains(_ strings: String..., caseSensitive: Bool = false, + required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType { + return CompareFilter(.Path(.Contains(strings, caseSensitive)), required: required, minLevel: minLevel) + } + + public static func excludes(_ strings: String..., caseSensitive: Bool = false, + required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType { + return CompareFilter(.Path(.Excludes(strings, caseSensitive)), required: required, minLevel: minLevel) + } + + public static func endsWith(_ suffixes: String..., caseSensitive: Bool = false, + required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType { + return CompareFilter(.Path(.EndsWith(suffixes, caseSensitive)), required: required, minLevel: minLevel) + } + + public static func equals(_ strings: String..., caseSensitive: Bool = false, + required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType { + return CompareFilter(.Path(.Equals(strings, caseSensitive)), required: required, minLevel: minLevel) + } + + public static func custom(required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose, filterPredicate: @escaping (String) -> Bool) -> FilterType { + return CompareFilter(.Path(.Custom(filterPredicate)), required: required, minLevel: minLevel) + } +} + +extension Filter.TargetType: Equatable { +} + +// The == does not compare associated values for each enum. Instead == evaluates to true +// if both enums are the same "types", ignoring the associated values of each enum +public func == (lhs: Filter.TargetType, rhs: Filter.TargetType) -> Bool { + switch (lhs, rhs) { + + case (.Path, .Path): + return true + + case (.Function, .Function): + return true + + case (.Message, .Message): + return true + + default: + return false + } +} diff --git a/Pods/SwiftyBeaver/Sources/FilterValidator.swift b/Pods/SwiftyBeaver/Sources/FilterValidator.swift new file mode 100644 index 0000000..1a9ddc5 --- /dev/null +++ b/Pods/SwiftyBeaver/Sources/FilterValidator.swift @@ -0,0 +1,129 @@ +// +// FilterValidator.swift +// SwiftyBeaver (iOS) +// +// Created by Felix Lisczyk on 07.07.19. +// Copyright © 2019 Sebastian Kreutzberger. All rights reserved. +// + +import Foundation + +/// FilterValidator is a utility class used by BaseDestination. +/// It encapsulates the filtering logic for excluded, required +/// and non-required filters. +/// +/// FilterValidator evaluates a set of filters for a single log +/// entry. It determines if these filters apply to the log entry +/// based on their condition (path, function, message) and their +/// minimum log level. + +struct FilterValidator { + + // These are the different filter types that the user can set + enum ValidationType: CaseIterable { + case excluded + case required + case nonRequired + + func apply(to filters: [FilterType]) -> [FilterType] { + switch self { + case .excluded: + return filters.filter { $0.isExcluded() } + case .required: + return filters.filter { $0.isRequired() && !$0.isExcluded() } + case .nonRequired: + return filters.filter { !$0.isRequired() && !$0.isExcluded() } + } + } + } + + // Wrapper object for input parameters + struct Input { + let filters: [FilterType] + let level: SwiftyBeaver.Level + let path: String + let function: String + let message: String? + } + + // Result wrapper object + enum Result { + case allFiltersMatch // All filters fully match the log entry (condition + minimum log level) + case someFiltersMatch(PartialMatchData) // Only some filters fully match the log entry (condition + minimum log level) + case noFiltersMatchingType // There are no filters set for a particular type (excluded, required, nonRequired) + + struct PartialMatchData { + let fullMatchCount: Int // Number of filters that match both the condition and the minimum log level of the log entry + let conditionMatchCount: Int // Number of filters that match ONLY the condition of the log entry (path, function, message) + let logLevelMatchCount: Int // Number of filters that match ONLY the minimum log level of the log entry + } + } + + static func validate(input: Input, for types: [ValidationType] = ValidationType.allCases) -> [ValidationType: Result] { + var results = [ValidationType: Result]() + for type in types { + let filtersToValidate = type.apply(to: input.filters) + + if filtersToValidate.isEmpty { + // There are no filters set for this particular type + results[type] = .noFiltersMatchingType + } else { + var fullMatchCount: Int = 0 + var conditionMatchCount: Int = 0 + var logLevelMatchCount: Int = 0 + + for filter in filtersToValidate { + let filterMatchesCondition = self.filterMatchesCondition(filter, level: input.level, path: input.path, function: input.function, message: input.message) + let filterMatchesMinLogLevel = self.filterMatchesMinLogLevel(filter, level: input.level) + + switch (filterMatchesCondition, filterMatchesMinLogLevel) { + // Filter matches both the condition and the minimum log level + case (true, true): fullMatchCount += 1 + // Filter matches only the condition (path, function, message) + case (true, false): conditionMatchCount += 1 + // Filter matches only the minimum log level + case (false, true): logLevelMatchCount += 1 + // Filter does not match the condition nor the minimum log level + case (false, false): break + } + } + + if filtersToValidate.count == fullMatchCount { + // All filters fully match the log entry + results[type] = .allFiltersMatch + } else { + // Only some filters match the log entry + results[type] = .someFiltersMatch(.init(fullMatchCount: fullMatchCount, conditionMatchCount: conditionMatchCount, logLevelMatchCount: logLevelMatchCount)) + } + } + } + + return results + } + + private static func filterMatchesCondition(_ filter: FilterType, level: SwiftyBeaver.Level, + path: String, function: String, message: String?) -> Bool { + let passes: Bool + + switch filter.getTarget() { + case .Path(_): + passes = filter.apply(path) + + case .Function(_): + passes = filter.apply(function) + + case .Message(_): + guard let message = message else { + return false + } + + passes = filter.apply(message) + } + + return passes + } + + private static func filterMatchesMinLogLevel(_ filter: FilterType, level: SwiftyBeaver.Level) -> Bool { + return filter.reachedMinLevel(level) + } +} diff --git a/Pods/SwiftyBeaver/Sources/GoogleCloudDestination.swift b/Pods/SwiftyBeaver/Sources/GoogleCloudDestination.swift new file mode 100644 index 0000000..0cad18e --- /dev/null +++ b/Pods/SwiftyBeaver/Sources/GoogleCloudDestination.swift @@ -0,0 +1,99 @@ +// +// GoogleCloudDestination.swift +// SwiftyBeaver +// +// Copyright © 2017 Sebastian Kreutzberger. All rights reserved. +// + +import Foundation + +public final class GoogleCloudDestination: BaseDestination { + + private let serviceName: String + + public init(serviceName: String) { + self.serviceName = serviceName + super.init() + } + + override public var asynchronously: Bool { + get { + return false + } + set { + return + } + } + + override public func send(_ level: SwiftyBeaver.Level, msg: String, thread: String, + file: String, function: String, line: Int, context: Any? = nil) -> String? { + + let reportLocation: [String: Any] = ["filePath": file, "lineNumber": line, "functionName": function] + var gcpContext: [String: Any] = ["reportLocation": reportLocation] + if let context = context as? [String: Any] { + if let httpRequestContext = context["httpRequest"] as? [String: Any] { + gcpContext["httpRequest"] = httpRequestContext + } + + if let user = context["user"] as? String { + gcpContext["user"] = user + } + } + + let gcpJSON: [String: Any] = [ + "serviceContext": [ + "service": serviceName + ], + "message": msg, + "severity": level.severity, + "context": gcpContext + ] + + let finalLogString: String + + do { + finalLogString = try jsonString(obj: gcpJSON) + } catch { + let uncrashableLogString = "{\"context\":{\"reportLocation\":{\"filePath\": \"\(file)\"" + + ",\"functionName\":\"\(function)\"" + + ",\"lineNumber\":\(line)},\"severity\"" + + ":\"CRITICAL\",\"message\":\"Error encoding " + + "JSON log entry. You may be losing log messages!\"}" + finalLogString = uncrashableLogString.description + } + print(finalLogString) + return finalLogString + } + + private func jsonString(obj: Dictionary) throws -> String { + let json = try JSONSerialization.data(withJSONObject: obj, options: []) + guard let string = String(data: json, encoding: .utf8) else { + throw GCPError.serialization + } + return string + } +} + +/// +/// https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry#LogSeverity +extension SwiftyBeaver.Level { + + /// Verbose is reported as Debug to GCP. + /// Recommend you don't bother using it. + var severity: String { + switch self { + // There is only one level below "Debug": "Default", which becomes "Any" and is considered as a potential error as well + case .verbose: return "DEBUG" + case .debug: return "DEBUG" + case .info: return "INFO" + case .warning: return "WARNING" + case .error: return "ERROR" + case .critical: return "CRITICAL" + case .fault: return "FAULT" + } + } +} + +private enum GCPError: Error { + case serialization +} diff --git a/Pods/SwiftyBeaver/Sources/SwiftyBeaver.swift b/Pods/SwiftyBeaver/Sources/SwiftyBeaver.swift new file mode 100644 index 0000000..36cafea --- /dev/null +++ b/Pods/SwiftyBeaver/Sources/SwiftyBeaver.swift @@ -0,0 +1,246 @@ +// +// SwiftyBeaver.swift +// SwiftyBeaver +// +// Created by Sebastian Kreutzberger (Twitter @skreutzb) on 28.11.15. +// Copyright © 2015 Sebastian Kreutzberger +// Some rights reserved: http://opensource.org/licenses/MIT +// + +import Foundation + +open class SwiftyBeaver { + + /// version string of framework + public static let version = "2.1.1" // UPDATE ON RELEASE! + /// build number of framework + public static let build = 2110 // version 1.6.2 -> 1620, UPDATE ON RELEASE! + + public enum Level: Int { + case verbose = 0 + case debug = 1 + case info = 2 + case warning = 3 + case error = 4 + case critical = 5 + case fault = 6 + } + + // a set of active destinations + public private(set) static var destinations = Set() + + /// A private queue for synchronizing access to `destinations`. + /// Read accesses are done concurrently. + /// Write accesses are done with a barrier, ensuring only 1 operation is ran at that time. + private static let queue = DispatchQueue(label: "destination queue", attributes: .concurrent) + + // MARK: Destination Handling + + /// returns boolean about success + @discardableResult + open class func addDestination(_ destination: BaseDestination) -> Bool { + return queue.sync(flags: DispatchWorkItemFlags.barrier) { + if destinations.contains(destination) { + return false + } + destinations.insert(destination) + return true + } + } + + /// returns boolean about success + @discardableResult + open class func removeDestination(_ destination: BaseDestination) -> Bool { + return queue.sync(flags: DispatchWorkItemFlags.barrier) { + if destinations.contains(destination) == false { + return false + } + destinations.remove(destination) + return true + } + } + + /// if you need to start fresh + open class func removeAllDestinations() { + queue.sync(flags: DispatchWorkItemFlags.barrier) { + destinations.removeAll() + } + } + + /// returns the amount of destinations + open class func countDestinations() -> Int { + return queue.sync { destinations.count } + } + + /// returns the current thread name + open class func threadName() -> String { + + #if os(Linux) + // on 9/30/2016 not yet implemented in server-side Swift: + // > import Foundation + // > Thread.isMainThread + return "" + #else + if Thread.isMainThread { + return "" + } else { + let name = __dispatch_queue_get_label(nil) + return String(cString: name, encoding: .utf8) ?? Thread.current.description + } + #endif + } + + // MARK: Levels + + /// log something generally unimportant (lowest priority) + open class func verbose(_ message: @autoclosure () -> Any, + file: String = #file, function: String = #function, line: Int = #line, context: Any? = nil) { + #if swift(>=5) + custom(level: .verbose, message: message(), file: file, function: function, line: line, context: context) + #else + custom(level: .verbose, message: message, file: file, function: function, line: line, context: context) + #endif + } + + /// log something which help during debugging (low priority) + open class func debug(_ message: @autoclosure () -> Any, + file: String = #file, function: String = #function, line: Int = #line, context: Any? = nil) { + #if swift(>=5) + custom(level: .debug, message: message(), file: file, function: function, line: line, context: context) + #else + custom(level: .debug, message: message, file: file, function: function, line: line, context: context) + #endif + } + + /// log something which you are really interested but which is not an issue or error (normal priority) + open class func info(_ message: @autoclosure () -> Any, + file: String = #file, function: String = #function, line: Int = #line, context: Any? = nil) { + #if swift(>=5) + custom(level: .info, message: message(), file: file, function: function, line: line, context: context) + #else + custom(level: .info, message: message, file: file, function: function, line: line, context: context) + #endif + } + + /// log something which may cause big trouble soon (high priority) + open class func warning(_ message: @autoclosure () -> Any, + file: String = #file, function: String = #function, line: Int = #line, context: Any? = nil) { + #if swift(>=5) + custom(level: .warning, message: message(), file: file, function: function, line: line, context: context) + #else + custom(level: .warning, message: message, file: file, function: function, line: line, context: context) + #endif + } + + /// log something which will keep you awake at night (highest priority) + open class func error(_ message: @autoclosure () -> Any, + file: String = #file, function: String = #function, line: Int = #line, context: Any? = nil) { + #if swift(>=5) + custom(level: .error, message: message(), file: file, function: function, line: line, context: context) + #else + custom(level: .error, message: message, file: file, function: function, line: line, context: context) + #endif + } + + /// log something which will keep you awake at night (highest priority) + open class func critical(_ message: @autoclosure () -> Any, + file: String = #file, function: String = #function, line: Int = #line, context: Any? = nil) { + #if swift(>=5) + custom(level: .critical, message: message(), file: file, function: function, line: line, context: context) + #else + custom(level: .critical, message: message, file: file, function: function, line: line, context: context) + #endif + } + + /// log something which will keep you awake at night (highest priority) + open class func fault(_ message: @autoclosure () -> Any, + file: String = #file, function: String = #function, line: Int = #line, context: Any? = nil) { + #if swift(>=5) + custom(level: .fault, message: message(), file: file, function: function, line: line, context: context) + #else + custom(level: .fault, message: message, file: file, function: function, line: line, context: context) + #endif + } + + /// custom logging to manually adjust values, should just be used by other frameworks + open class func custom(level: SwiftyBeaver.Level, message: @autoclosure () -> Any, + file: String = #file, function: String = #function, line: Int = #line, context: Any? = nil) { + #if swift(>=5) + dispatch_send(level: level, message: message(), thread: threadName(), + file: file, function: function, line: line, context: context) + #else + dispatch_send(level: level, message: message, thread: threadName(), + file: file, function: function, line: line, context: context) + #endif + } + + /// internal helper which dispatches send to dedicated queue if minLevel is ok + class func dispatch_send(level: SwiftyBeaver.Level, message: @autoclosure () -> Any, + thread: String, file: String, function: String, line: Int, context: Any?) { + var resolvedMessage: String? + let destinations = queue.sync { self.destinations } + for dest in destinations { + + guard let queue = dest.queue else { + continue + } + + resolvedMessage = resolvedMessage == nil && dest.hasMessageFilters() ? "\(message())" : resolvedMessage + if dest.shouldLevelBeLogged(level, path: file, function: function, message: resolvedMessage) { + // try to convert msg object to String and put it on queue + let msgStr = resolvedMessage == nil ? "\(message())" : resolvedMessage! + let f = stripParams(function: function) + + if dest.asynchronously { + queue.async { + _ = dest.send(level, msg: msgStr, thread: thread, file: file, function: f, line: line, context: context) + } + } else { + queue.sync { + _ = dest.send(level, msg: msgStr, thread: thread, file: file, function: f, line: line, context: context) + } + } + } + } + } + + /// flush all destinations to make sure all logging messages have been written out + /// returns after all messages flushed or timeout seconds + /// returns: true if all messages flushed, false if timeout or error occurred + public class func flush(secondTimeout: Int64) -> Bool { + let grp = DispatchGroup() + let destinations = queue.sync { self.destinations } + for dest in destinations { + guard let queue = dest.queue else { + continue + } + grp.enter() + if dest.asynchronously { + queue.async { + dest.flush() + grp.leave() + } + } else { + queue.sync { + dest.flush() + grp.leave() + } + } + } + return grp.wait(timeout: .now() + .seconds(Int(secondTimeout))) == .success + } + + /// removes the parameters from a function because it looks weird with a single param + class func stripParams(function: String) -> String { + var f = function + if let indexOfBrace = f.find("(") { + #if swift(>=4.0) + f = String(f[.. + + + + CFBundleDevelopmentRegion + ${PODS_DEVELOPMENT_LANGUAGE} + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 5.9.1 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/Pods/Target Support Files/Alamofire/Alamofire-dummy.m b/Pods/Target Support Files/Alamofire/Alamofire-dummy.m new file mode 100644 index 0000000..a6c4594 --- /dev/null +++ b/Pods/Target Support Files/Alamofire/Alamofire-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_Alamofire : NSObject +@end +@implementation PodsDummy_Alamofire +@end diff --git a/Pods/Target Support Files/Alamofire/Alamofire-prefix.pch b/Pods/Target Support Files/Alamofire/Alamofire-prefix.pch new file mode 100644 index 0000000..beb2a24 --- /dev/null +++ b/Pods/Target Support Files/Alamofire/Alamofire-prefix.pch @@ -0,0 +1,12 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + diff --git a/Pods/Target Support Files/Alamofire/Alamofire-umbrella.h b/Pods/Target Support Files/Alamofire/Alamofire-umbrella.h new file mode 100644 index 0000000..00014e3 --- /dev/null +++ b/Pods/Target Support Files/Alamofire/Alamofire-umbrella.h @@ -0,0 +1,16 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + + +FOUNDATION_EXPORT double AlamofireVersionNumber; +FOUNDATION_EXPORT const unsigned char AlamofireVersionString[]; + diff --git a/Pods/Target Support Files/Alamofire/Alamofire.debug.xcconfig b/Pods/Target Support Files/Alamofire/Alamofire.debug.xcconfig new file mode 100644 index 0000000..dbfd4d1 --- /dev/null +++ b/Pods/Target Support Files/Alamofire/Alamofire.debug.xcconfig @@ -0,0 +1,15 @@ +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Alamofire +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift +OTHER_LDFLAGS = $(inherited) -framework "CFNetwork" +OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE} +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/Alamofire +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Pods/Target Support Files/Alamofire/Alamofire.modulemap b/Pods/Target Support Files/Alamofire/Alamofire.modulemap new file mode 100644 index 0000000..d1f125f --- /dev/null +++ b/Pods/Target Support Files/Alamofire/Alamofire.modulemap @@ -0,0 +1,6 @@ +framework module Alamofire { + umbrella header "Alamofire-umbrella.h" + + export * + module * { export * } +} diff --git a/Pods/Target Support Files/Alamofire/Alamofire.release.xcconfig b/Pods/Target Support Files/Alamofire/Alamofire.release.xcconfig new file mode 100644 index 0000000..dbfd4d1 --- /dev/null +++ b/Pods/Target Support Files/Alamofire/Alamofire.release.xcconfig @@ -0,0 +1,15 @@ +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Alamofire +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift +OTHER_LDFLAGS = $(inherited) -framework "CFNetwork" +OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE} +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/Alamofire +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Pods/Target Support Files/Alamofire/ResourceBundle-Alamofire-Alamofire-Info.plist b/Pods/Target Support Files/Alamofire/ResourceBundle-Alamofire-Alamofire-Info.plist new file mode 100644 index 0000000..e4cc543 --- /dev/null +++ b/Pods/Target Support Files/Alamofire/ResourceBundle-Alamofire-Alamofire-Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + ${PODS_DEVELOPMENT_LANGUAGE} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + BNDL + CFBundleShortVersionString + 5.9.1 + CFBundleSignature + ???? + CFBundleVersion + 1 + NSPrincipalClass + + + diff --git a/Pods/Target Support Files/BlueCryptor/BlueCryptor-Info.plist b/Pods/Target Support Files/BlueCryptor/BlueCryptor-Info.plist new file mode 100644 index 0000000..6909162 --- /dev/null +++ b/Pods/Target Support Files/BlueCryptor/BlueCryptor-Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + ${PODS_DEVELOPMENT_LANGUAGE} + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0.32 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/Pods/Target Support Files/BlueCryptor/BlueCryptor-dummy.m b/Pods/Target Support Files/BlueCryptor/BlueCryptor-dummy.m new file mode 100644 index 0000000..03b00d8 --- /dev/null +++ b/Pods/Target Support Files/BlueCryptor/BlueCryptor-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_BlueCryptor : NSObject +@end +@implementation PodsDummy_BlueCryptor +@end diff --git a/Pods/Target Support Files/BlueCryptor/BlueCryptor-prefix.pch b/Pods/Target Support Files/BlueCryptor/BlueCryptor-prefix.pch new file mode 100644 index 0000000..beb2a24 --- /dev/null +++ b/Pods/Target Support Files/BlueCryptor/BlueCryptor-prefix.pch @@ -0,0 +1,12 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + diff --git a/Pods/Target Support Files/BlueCryptor/BlueCryptor-umbrella.h b/Pods/Target Support Files/BlueCryptor/BlueCryptor-umbrella.h new file mode 100644 index 0000000..72f0569 --- /dev/null +++ b/Pods/Target Support Files/BlueCryptor/BlueCryptor-umbrella.h @@ -0,0 +1,16 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + + +FOUNDATION_EXPORT double CryptorVersionNumber; +FOUNDATION_EXPORT const unsigned char CryptorVersionString[]; + diff --git a/Pods/Target Support Files/BlueCryptor/BlueCryptor.debug.xcconfig b/Pods/Target Support Files/BlueCryptor/BlueCryptor.debug.xcconfig new file mode 100644 index 0000000..e031c57 --- /dev/null +++ b/Pods/Target Support Files/BlueCryptor/BlueCryptor.debug.xcconfig @@ -0,0 +1,15 @@ +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/BlueCryptor +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift +OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE} +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/BlueCryptor +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES +SWIFT_VERSION = 5.0 +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Pods/Target Support Files/BlueCryptor/BlueCryptor.modulemap b/Pods/Target Support Files/BlueCryptor/BlueCryptor.modulemap new file mode 100644 index 0000000..104be9d --- /dev/null +++ b/Pods/Target Support Files/BlueCryptor/BlueCryptor.modulemap @@ -0,0 +1,6 @@ +framework module Cryptor { + umbrella header "BlueCryptor-umbrella.h" + + export * + module * { export * } +} diff --git a/Pods/Target Support Files/BlueCryptor/BlueCryptor.release.xcconfig b/Pods/Target Support Files/BlueCryptor/BlueCryptor.release.xcconfig new file mode 100644 index 0000000..e031c57 --- /dev/null +++ b/Pods/Target Support Files/BlueCryptor/BlueCryptor.release.xcconfig @@ -0,0 +1,15 @@ +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/BlueCryptor +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift +OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE} +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/BlueCryptor +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES +SWIFT_VERSION = 5.0 +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Pods/Target Support Files/BlueECC/BlueECC-Info.plist b/Pods/Target Support Files/BlueECC/BlueECC-Info.plist new file mode 100644 index 0000000..5fb3183 --- /dev/null +++ b/Pods/Target Support Files/BlueECC/BlueECC-Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + ${PODS_DEVELOPMENT_LANGUAGE} + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.2.5 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/Pods/Target Support Files/BlueECC/BlueECC-dummy.m b/Pods/Target Support Files/BlueECC/BlueECC-dummy.m new file mode 100644 index 0000000..2134f52 --- /dev/null +++ b/Pods/Target Support Files/BlueECC/BlueECC-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_BlueECC : NSObject +@end +@implementation PodsDummy_BlueECC +@end diff --git a/Pods/Target Support Files/BlueECC/BlueECC-prefix.pch b/Pods/Target Support Files/BlueECC/BlueECC-prefix.pch new file mode 100644 index 0000000..beb2a24 --- /dev/null +++ b/Pods/Target Support Files/BlueECC/BlueECC-prefix.pch @@ -0,0 +1,12 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + diff --git a/Pods/Target Support Files/BlueECC/BlueECC-umbrella.h b/Pods/Target Support Files/BlueECC/BlueECC-umbrella.h new file mode 100644 index 0000000..8b3edd1 --- /dev/null +++ b/Pods/Target Support Files/BlueECC/BlueECC-umbrella.h @@ -0,0 +1,16 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + + +FOUNDATION_EXPORT double CryptorECCVersionNumber; +FOUNDATION_EXPORT const unsigned char CryptorECCVersionString[]; + diff --git a/Pods/Target Support Files/BlueECC/BlueECC.debug.xcconfig b/Pods/Target Support Files/BlueECC/BlueECC.debug.xcconfig new file mode 100644 index 0000000..53b0c04 --- /dev/null +++ b/Pods/Target Support Files/BlueECC/BlueECC.debug.xcconfig @@ -0,0 +1,14 @@ +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/BlueECC +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift +OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE} +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/BlueECC +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Pods/Target Support Files/BlueECC/BlueECC.modulemap b/Pods/Target Support Files/BlueECC/BlueECC.modulemap new file mode 100644 index 0000000..951da32 --- /dev/null +++ b/Pods/Target Support Files/BlueECC/BlueECC.modulemap @@ -0,0 +1,6 @@ +framework module CryptorECC { + umbrella header "BlueECC-umbrella.h" + + export * + module * { export * } +} diff --git a/Pods/Target Support Files/BlueECC/BlueECC.release.xcconfig b/Pods/Target Support Files/BlueECC/BlueECC.release.xcconfig new file mode 100644 index 0000000..53b0c04 --- /dev/null +++ b/Pods/Target Support Files/BlueECC/BlueECC.release.xcconfig @@ -0,0 +1,14 @@ +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/BlueECC +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift +OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE} +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/BlueECC +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Pods/Target Support Files/BlueRSA/BlueRSA-Info.plist b/Pods/Target Support Files/BlueRSA/BlueRSA-Info.plist new file mode 100644 index 0000000..a3112ae --- /dev/null +++ b/Pods/Target Support Files/BlueRSA/BlueRSA-Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + ${PODS_DEVELOPMENT_LANGUAGE} + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0.200 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/Pods/Target Support Files/BlueRSA/BlueRSA-dummy.m b/Pods/Target Support Files/BlueRSA/BlueRSA-dummy.m new file mode 100644 index 0000000..1eb0c43 --- /dev/null +++ b/Pods/Target Support Files/BlueRSA/BlueRSA-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_BlueRSA : NSObject +@end +@implementation PodsDummy_BlueRSA +@end diff --git a/Pods/Target Support Files/BlueRSA/BlueRSA-prefix.pch b/Pods/Target Support Files/BlueRSA/BlueRSA-prefix.pch new file mode 100644 index 0000000..beb2a24 --- /dev/null +++ b/Pods/Target Support Files/BlueRSA/BlueRSA-prefix.pch @@ -0,0 +1,12 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + diff --git a/Pods/Target Support Files/BlueRSA/BlueRSA-umbrella.h b/Pods/Target Support Files/BlueRSA/BlueRSA-umbrella.h new file mode 100644 index 0000000..b858b9a --- /dev/null +++ b/Pods/Target Support Files/BlueRSA/BlueRSA-umbrella.h @@ -0,0 +1,16 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + + +FOUNDATION_EXPORT double CryptorRSAVersionNumber; +FOUNDATION_EXPORT const unsigned char CryptorRSAVersionString[]; + diff --git a/Pods/Target Support Files/BlueRSA/BlueRSA.debug.xcconfig b/Pods/Target Support Files/BlueRSA/BlueRSA.debug.xcconfig new file mode 100644 index 0000000..b5610c5 --- /dev/null +++ b/Pods/Target Support Files/BlueRSA/BlueRSA.debug.xcconfig @@ -0,0 +1,15 @@ +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/BlueRSA +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift +OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE} +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/BlueRSA +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES +SWIFT_VERSION = 5.0 +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Pods/Target Support Files/BlueRSA/BlueRSA.modulemap b/Pods/Target Support Files/BlueRSA/BlueRSA.modulemap new file mode 100644 index 0000000..a7ea9e7 --- /dev/null +++ b/Pods/Target Support Files/BlueRSA/BlueRSA.modulemap @@ -0,0 +1,6 @@ +framework module CryptorRSA { + umbrella header "BlueRSA-umbrella.h" + + export * + module * { export * } +} diff --git a/Pods/Target Support Files/BlueRSA/BlueRSA.release.xcconfig b/Pods/Target Support Files/BlueRSA/BlueRSA.release.xcconfig new file mode 100644 index 0000000..b5610c5 --- /dev/null +++ b/Pods/Target Support Files/BlueRSA/BlueRSA.release.xcconfig @@ -0,0 +1,15 @@ +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/BlueRSA +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift +OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE} +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/BlueRSA +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES +SWIFT_VERSION = 5.0 +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Pods/Target Support Files/KituraContracts/KituraContracts-Info.plist b/Pods/Target Support Files/KituraContracts/KituraContracts-Info.plist new file mode 100644 index 0000000..c4afd99 --- /dev/null +++ b/Pods/Target Support Files/KituraContracts/KituraContracts-Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + ${PODS_DEVELOPMENT_LANGUAGE} + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.2.1 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/Pods/Target Support Files/KituraContracts/KituraContracts-dummy.m b/Pods/Target Support Files/KituraContracts/KituraContracts-dummy.m new file mode 100644 index 0000000..b4b7806 --- /dev/null +++ b/Pods/Target Support Files/KituraContracts/KituraContracts-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_KituraContracts : NSObject +@end +@implementation PodsDummy_KituraContracts +@end diff --git a/Pods/Target Support Files/KituraContracts/KituraContracts-prefix.pch b/Pods/Target Support Files/KituraContracts/KituraContracts-prefix.pch new file mode 100644 index 0000000..beb2a24 --- /dev/null +++ b/Pods/Target Support Files/KituraContracts/KituraContracts-prefix.pch @@ -0,0 +1,12 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + diff --git a/Pods/Target Support Files/KituraContracts/KituraContracts-umbrella.h b/Pods/Target Support Files/KituraContracts/KituraContracts-umbrella.h new file mode 100644 index 0000000..4723488 --- /dev/null +++ b/Pods/Target Support Files/KituraContracts/KituraContracts-umbrella.h @@ -0,0 +1,16 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + + +FOUNDATION_EXPORT double KituraContractsVersionNumber; +FOUNDATION_EXPORT const unsigned char KituraContractsVersionString[]; + diff --git a/Pods/Target Support Files/KituraContracts/KituraContracts.debug.xcconfig b/Pods/Target Support Files/KituraContracts/KituraContracts.debug.xcconfig new file mode 100644 index 0000000..fd6361b --- /dev/null +++ b/Pods/Target Support Files/KituraContracts/KituraContracts.debug.xcconfig @@ -0,0 +1,16 @@ +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/KituraContracts +FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/LoggerAPI" "${PODS_CONFIGURATION_BUILD_DIR}/Logging" +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift +OTHER_LDFLAGS = $(inherited) -framework "LoggerAPI" +OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE} +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/KituraContracts +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Pods/Target Support Files/KituraContracts/KituraContracts.modulemap b/Pods/Target Support Files/KituraContracts/KituraContracts.modulemap new file mode 100644 index 0000000..ab47519 --- /dev/null +++ b/Pods/Target Support Files/KituraContracts/KituraContracts.modulemap @@ -0,0 +1,6 @@ +framework module KituraContracts { + umbrella header "KituraContracts-umbrella.h" + + export * + module * { export * } +} diff --git a/Pods/Target Support Files/KituraContracts/KituraContracts.release.xcconfig b/Pods/Target Support Files/KituraContracts/KituraContracts.release.xcconfig new file mode 100644 index 0000000..fd6361b --- /dev/null +++ b/Pods/Target Support Files/KituraContracts/KituraContracts.release.xcconfig @@ -0,0 +1,16 @@ +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/KituraContracts +FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/LoggerAPI" "${PODS_CONFIGURATION_BUILD_DIR}/Logging" +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift +OTHER_LDFLAGS = $(inherited) -framework "LoggerAPI" +OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE} +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/KituraContracts +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Pods/Target Support Files/LoggerAPI/LoggerAPI-Info.plist b/Pods/Target Support Files/LoggerAPI/LoggerAPI-Info.plist new file mode 100644 index 0000000..e2ff1aa --- /dev/null +++ b/Pods/Target Support Files/LoggerAPI/LoggerAPI-Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + ${PODS_DEVELOPMENT_LANGUAGE} + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.9.200 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/Pods/Target Support Files/LoggerAPI/LoggerAPI-dummy.m b/Pods/Target Support Files/LoggerAPI/LoggerAPI-dummy.m new file mode 100644 index 0000000..af6f671 --- /dev/null +++ b/Pods/Target Support Files/LoggerAPI/LoggerAPI-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_LoggerAPI : NSObject +@end +@implementation PodsDummy_LoggerAPI +@end diff --git a/Pods/Target Support Files/LoggerAPI/LoggerAPI-prefix.pch b/Pods/Target Support Files/LoggerAPI/LoggerAPI-prefix.pch new file mode 100644 index 0000000..beb2a24 --- /dev/null +++ b/Pods/Target Support Files/LoggerAPI/LoggerAPI-prefix.pch @@ -0,0 +1,12 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + diff --git a/Pods/Target Support Files/LoggerAPI/LoggerAPI-umbrella.h b/Pods/Target Support Files/LoggerAPI/LoggerAPI-umbrella.h new file mode 100644 index 0000000..46a3407 --- /dev/null +++ b/Pods/Target Support Files/LoggerAPI/LoggerAPI-umbrella.h @@ -0,0 +1,16 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + + +FOUNDATION_EXPORT double LoggerAPIVersionNumber; +FOUNDATION_EXPORT const unsigned char LoggerAPIVersionString[]; + diff --git a/Pods/Target Support Files/LoggerAPI/LoggerAPI.debug.xcconfig b/Pods/Target Support Files/LoggerAPI/LoggerAPI.debug.xcconfig new file mode 100644 index 0000000..6d192f2 --- /dev/null +++ b/Pods/Target Support Files/LoggerAPI/LoggerAPI.debug.xcconfig @@ -0,0 +1,16 @@ +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/LoggerAPI +FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Logging" +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift +OTHER_LDFLAGS = $(inherited) -framework "Logging" +OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE} +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/LoggerAPI +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Pods/Target Support Files/LoggerAPI/LoggerAPI.modulemap b/Pods/Target Support Files/LoggerAPI/LoggerAPI.modulemap new file mode 100644 index 0000000..63593f7 --- /dev/null +++ b/Pods/Target Support Files/LoggerAPI/LoggerAPI.modulemap @@ -0,0 +1,6 @@ +framework module LoggerAPI { + umbrella header "LoggerAPI-umbrella.h" + + export * + module * { export * } +} diff --git a/Pods/Target Support Files/LoggerAPI/LoggerAPI.release.xcconfig b/Pods/Target Support Files/LoggerAPI/LoggerAPI.release.xcconfig new file mode 100644 index 0000000..6d192f2 --- /dev/null +++ b/Pods/Target Support Files/LoggerAPI/LoggerAPI.release.xcconfig @@ -0,0 +1,16 @@ +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/LoggerAPI +FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Logging" +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift +OTHER_LDFLAGS = $(inherited) -framework "Logging" +OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE} +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/LoggerAPI +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Pods/Target Support Files/Logging/Logging-Info.plist b/Pods/Target Support Files/Logging/Logging-Info.plist new file mode 100644 index 0000000..e3a8af0 --- /dev/null +++ b/Pods/Target Support Files/Logging/Logging-Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + ${PODS_DEVELOPMENT_LANGUAGE} + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.4.0 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/Pods/Target Support Files/Logging/Logging-dummy.m b/Pods/Target Support Files/Logging/Logging-dummy.m new file mode 100644 index 0000000..40d643a --- /dev/null +++ b/Pods/Target Support Files/Logging/Logging-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_Logging : NSObject +@end +@implementation PodsDummy_Logging +@end diff --git a/Pods/Target Support Files/Logging/Logging-prefix.pch b/Pods/Target Support Files/Logging/Logging-prefix.pch new file mode 100644 index 0000000..beb2a24 --- /dev/null +++ b/Pods/Target Support Files/Logging/Logging-prefix.pch @@ -0,0 +1,12 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + diff --git a/Pods/Target Support Files/Logging/Logging-umbrella.h b/Pods/Target Support Files/Logging/Logging-umbrella.h new file mode 100644 index 0000000..276b50e --- /dev/null +++ b/Pods/Target Support Files/Logging/Logging-umbrella.h @@ -0,0 +1,16 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + + +FOUNDATION_EXPORT double LoggingVersionNumber; +FOUNDATION_EXPORT const unsigned char LoggingVersionString[]; + diff --git a/Pods/Target Support Files/Logging/Logging.debug.xcconfig b/Pods/Target Support Files/Logging/Logging.debug.xcconfig new file mode 100644 index 0000000..69bff2c --- /dev/null +++ b/Pods/Target Support Files/Logging/Logging.debug.xcconfig @@ -0,0 +1,14 @@ +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Logging +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift +OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE} +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/Logging +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Pods/Target Support Files/Logging/Logging.modulemap b/Pods/Target Support Files/Logging/Logging.modulemap new file mode 100644 index 0000000..ba5c55f --- /dev/null +++ b/Pods/Target Support Files/Logging/Logging.modulemap @@ -0,0 +1,6 @@ +framework module Logging { + umbrella header "Logging-umbrella.h" + + export * + module * { export * } +} diff --git a/Pods/Target Support Files/Logging/Logging.release.xcconfig b/Pods/Target Support Files/Logging/Logging.release.xcconfig new file mode 100644 index 0000000..69bff2c --- /dev/null +++ b/Pods/Target Support Files/Logging/Logging.release.xcconfig @@ -0,0 +1,14 @@ +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Logging +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift +OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE} +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/Logging +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Pods/Target Support Files/Pods-AIGrammar-AIGrammarUITests/Pods-AIGrammar-AIGrammarUITests-Info.plist b/Pods/Target Support Files/Pods-AIGrammar-AIGrammarUITests/Pods-AIGrammar-AIGrammarUITests-Info.plist new file mode 100644 index 0000000..19cf209 --- /dev/null +++ b/Pods/Target Support Files/Pods-AIGrammar-AIGrammarUITests/Pods-AIGrammar-AIGrammarUITests-Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + ${PODS_DEVELOPMENT_LANGUAGE} + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0.0 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/Pods/Target Support Files/Pods-AIGrammar-AIGrammarUITests/Pods-AIGrammar-AIGrammarUITests-acknowledgements.markdown b/Pods/Target Support Files/Pods-AIGrammar-AIGrammarUITests/Pods-AIGrammar-AIGrammarUITests-acknowledgements.markdown new file mode 100644 index 0000000..1af4720 --- /dev/null +++ b/Pods/Target Support Files/Pods-AIGrammar-AIGrammarUITests/Pods-AIGrammar-AIGrammarUITests-acknowledgements.markdown @@ -0,0 +1,1389 @@ +# Acknowledgements +This application makes use of the following third party libraries: + +## Alamofire + +Copyright (c) 2014-2022 Alamofire Software Foundation (http://alamofire.org/) + +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. + + +## BlueCryptor + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + +## BlueECC + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + +## BlueRSA + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + +## KituraContracts + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +## LoggerAPI + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + +## Logging + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +## SwiftJWT + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + +## SwiftyBeaver + +The MIT License (MIT) + +Copyright (c) 2015 Sebastian Kreutzberger + +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. + + +## TrustDecision + +MIT License + +Copyright (c) 2022 trustdecision + +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. + +Generated by CocoaPods - https://cocoapods.org diff --git a/Pods/Target Support Files/Pods-AIGrammar-AIGrammarUITests/Pods-AIGrammar-AIGrammarUITests-acknowledgements.plist b/Pods/Target Support Files/Pods-AIGrammar-AIGrammarUITests/Pods-AIGrammar-AIGrammarUITests-acknowledgements.plist new file mode 100644 index 0000000..83cc389 --- /dev/null +++ b/Pods/Target Support Files/Pods-AIGrammar-AIGrammarUITests/Pods-AIGrammar-AIGrammarUITests-acknowledgements.plist @@ -0,0 +1,1475 @@ + + + + + PreferenceSpecifiers + + + FooterText + This application makes use of the following third party libraries: + Title + Acknowledgements + Type + PSGroupSpecifier + + + FooterText + Copyright (c) 2014-2022 Alamofire Software Foundation (http://alamofire.org/) + +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. + + License + MIT + Title + Alamofire + Type + PSGroupSpecifier + + + FooterText + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + License + Apache License, Version 2.0 + Title + BlueCryptor + Type + PSGroupSpecifier + + + FooterText + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + License + Apache License, Version 2.0 + Title + BlueECC + Type + PSGroupSpecifier + + + FooterText + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + License + Apache License, Version 2.0 + Title + BlueRSA + Type + PSGroupSpecifier + + + FooterText + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + License + Apache License, Version 2.0 + Title + KituraContracts + Type + PSGroupSpecifier + + + FooterText + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + License + Apache License, Version 2.0 + Title + LoggerAPI + Type + PSGroupSpecifier + + + FooterText + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + License + Apache 2.0 + Title + Logging + Type + PSGroupSpecifier + + + FooterText + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + License + Apache License, Version 2.0 + Title + SwiftJWT + Type + PSGroupSpecifier + + + FooterText + The MIT License (MIT) + +Copyright (c) 2015 Sebastian Kreutzberger + +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. + + License + MIT + Title + SwiftyBeaver + Type + PSGroupSpecifier + + + FooterText + MIT License + +Copyright (c) 2022 trustdecision + +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. + + License + MIT + Title + TrustDecision + Type + PSGroupSpecifier + + + FooterText + Generated by CocoaPods - https://cocoapods.org + Title + + Type + PSGroupSpecifier + + + StringsTable + Acknowledgements + Title + Acknowledgements + + diff --git a/Pods/Target Support Files/Pods-AIGrammar-AIGrammarUITests/Pods-AIGrammar-AIGrammarUITests-dummy.m b/Pods/Target Support Files/Pods-AIGrammar-AIGrammarUITests/Pods-AIGrammar-AIGrammarUITests-dummy.m new file mode 100644 index 0000000..e1b8ad2 --- /dev/null +++ b/Pods/Target Support Files/Pods-AIGrammar-AIGrammarUITests/Pods-AIGrammar-AIGrammarUITests-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_Pods_AIGrammar_AIGrammarUITests : NSObject +@end +@implementation PodsDummy_Pods_AIGrammar_AIGrammarUITests +@end diff --git a/Pods/Target Support Files/Pods-AIGrammar-AIGrammarUITests/Pods-AIGrammar-AIGrammarUITests-frameworks-Debug-input-files.xcfilelist b/Pods/Target Support Files/Pods-AIGrammar-AIGrammarUITests/Pods-AIGrammar-AIGrammarUITests-frameworks-Debug-input-files.xcfilelist new file mode 100644 index 0000000..0cb20b1 --- /dev/null +++ b/Pods/Target Support Files/Pods-AIGrammar-AIGrammarUITests/Pods-AIGrammar-AIGrammarUITests-frameworks-Debug-input-files.xcfilelist @@ -0,0 +1,10 @@ +${PODS_ROOT}/Target Support Files/Pods-AIGrammar-AIGrammarUITests/Pods-AIGrammar-AIGrammarUITests-frameworks.sh +${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework +${BUILT_PRODUCTS_DIR}/BlueCryptor/Cryptor.framework +${BUILT_PRODUCTS_DIR}/BlueECC/CryptorECC.framework +${BUILT_PRODUCTS_DIR}/BlueRSA/CryptorRSA.framework +${BUILT_PRODUCTS_DIR}/KituraContracts/KituraContracts.framework +${BUILT_PRODUCTS_DIR}/LoggerAPI/LoggerAPI.framework +${BUILT_PRODUCTS_DIR}/Logging/Logging.framework +${BUILT_PRODUCTS_DIR}/SwiftJWT/SwiftJWT.framework +${BUILT_PRODUCTS_DIR}/SwiftyBeaver/SwiftyBeaver.framework \ No newline at end of file diff --git a/Pods/Target Support Files/Pods-AIGrammar-AIGrammarUITests/Pods-AIGrammar-AIGrammarUITests-frameworks-Debug-output-files.xcfilelist b/Pods/Target Support Files/Pods-AIGrammar-AIGrammarUITests/Pods-AIGrammar-AIGrammarUITests-frameworks-Debug-output-files.xcfilelist new file mode 100644 index 0000000..0568650 --- /dev/null +++ b/Pods/Target Support Files/Pods-AIGrammar-AIGrammarUITests/Pods-AIGrammar-AIGrammarUITests-frameworks-Debug-output-files.xcfilelist @@ -0,0 +1,9 @@ +${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Alamofire.framework +${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Cryptor.framework +${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/CryptorECC.framework +${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/CryptorRSA.framework +${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/KituraContracts.framework +${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/LoggerAPI.framework +${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Logging.framework +${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftJWT.framework +${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftyBeaver.framework \ No newline at end of file diff --git a/Pods/Target Support Files/Pods-AIGrammar-AIGrammarUITests/Pods-AIGrammar-AIGrammarUITests-frameworks-Release-input-files.xcfilelist b/Pods/Target Support Files/Pods-AIGrammar-AIGrammarUITests/Pods-AIGrammar-AIGrammarUITests-frameworks-Release-input-files.xcfilelist new file mode 100644 index 0000000..0cb20b1 --- /dev/null +++ b/Pods/Target Support Files/Pods-AIGrammar-AIGrammarUITests/Pods-AIGrammar-AIGrammarUITests-frameworks-Release-input-files.xcfilelist @@ -0,0 +1,10 @@ +${PODS_ROOT}/Target Support Files/Pods-AIGrammar-AIGrammarUITests/Pods-AIGrammar-AIGrammarUITests-frameworks.sh +${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework +${BUILT_PRODUCTS_DIR}/BlueCryptor/Cryptor.framework +${BUILT_PRODUCTS_DIR}/BlueECC/CryptorECC.framework +${BUILT_PRODUCTS_DIR}/BlueRSA/CryptorRSA.framework +${BUILT_PRODUCTS_DIR}/KituraContracts/KituraContracts.framework +${BUILT_PRODUCTS_DIR}/LoggerAPI/LoggerAPI.framework +${BUILT_PRODUCTS_DIR}/Logging/Logging.framework +${BUILT_PRODUCTS_DIR}/SwiftJWT/SwiftJWT.framework +${BUILT_PRODUCTS_DIR}/SwiftyBeaver/SwiftyBeaver.framework \ No newline at end of file diff --git a/Pods/Target Support Files/Pods-AIGrammar-AIGrammarUITests/Pods-AIGrammar-AIGrammarUITests-frameworks-Release-output-files.xcfilelist b/Pods/Target Support Files/Pods-AIGrammar-AIGrammarUITests/Pods-AIGrammar-AIGrammarUITests-frameworks-Release-output-files.xcfilelist new file mode 100644 index 0000000..0568650 --- /dev/null +++ b/Pods/Target Support Files/Pods-AIGrammar-AIGrammarUITests/Pods-AIGrammar-AIGrammarUITests-frameworks-Release-output-files.xcfilelist @@ -0,0 +1,9 @@ +${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Alamofire.framework +${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Cryptor.framework +${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/CryptorECC.framework +${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/CryptorRSA.framework +${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/KituraContracts.framework +${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/LoggerAPI.framework +${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Logging.framework +${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftJWT.framework +${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftyBeaver.framework \ No newline at end of file diff --git a/Pods/Target Support Files/Pods-AIGrammar-AIGrammarUITests/Pods-AIGrammar-AIGrammarUITests-frameworks.sh b/Pods/Target Support Files/Pods-AIGrammar-AIGrammarUITests/Pods-AIGrammar-AIGrammarUITests-frameworks.sh new file mode 100755 index 0000000..6bcabee --- /dev/null +++ b/Pods/Target Support Files/Pods-AIGrammar-AIGrammarUITests/Pods-AIGrammar-AIGrammarUITests-frameworks.sh @@ -0,0 +1,202 @@ +#!/bin/sh +set -e +set -u +set -o pipefail + +function on_error { + echo "$(realpath -mq "${0}"):$1: error: Unexpected failure" +} +trap 'on_error $LINENO' ERR + +if [ -z ${FRAMEWORKS_FOLDER_PATH+x} ]; then + # If FRAMEWORKS_FOLDER_PATH is not set, then there's nowhere for us to copy + # frameworks to, so exit 0 (signalling the script phase was successful). + exit 0 +fi + +echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" +mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + +COCOAPODS_PARALLEL_CODE_SIGN="${COCOAPODS_PARALLEL_CODE_SIGN:-false}" +SWIFT_STDLIB_PATH="${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" +BCSYMBOLMAP_DIR="BCSymbolMaps" + + +# This protects against multiple targets copying the same framework dependency at the same time. The solution +# was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html +RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") + +# Copies and strips a vendored framework +install_framework() +{ + if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then + local source="${BUILT_PRODUCTS_DIR}/$1" + elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then + local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" + elif [ -r "$1" ]; then + local source="$1" + fi + + local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + + if [ -L "${source}" ]; then + echo "Symlinked..." + source="$(readlink -f "${source}")" + fi + + if [ -d "${source}/${BCSYMBOLMAP_DIR}" ]; then + # Locate and install any .bcsymbolmaps if present, and remove them from the .framework before the framework is copied + find "${source}/${BCSYMBOLMAP_DIR}" -name "*.bcsymbolmap"|while read f; do + echo "Installing $f" + install_bcsymbolmap "$f" "$destination" + rm "$f" + done + rmdir "${source}/${BCSYMBOLMAP_DIR}" + fi + + # Use filter instead of exclude so missing patterns don't throw errors. + echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" + rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" + + local basename + basename="$(basename -s .framework "$1")" + binary="${destination}/${basename}.framework/${basename}" + + if ! [ -r "$binary" ]; then + binary="${destination}/${basename}" + elif [ -L "${binary}" ]; then + echo "Destination binary is symlinked..." + dirname="$(dirname "${binary}")" + binary="${dirname}/$(readlink "${binary}")" + fi + + # Strip invalid architectures so "fat" simulator / device frameworks work on device + if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then + strip_invalid_archs "$binary" + fi + + # Resign the code if required by the build settings to avoid unstable apps + code_sign_if_enabled "${destination}/$(basename "$1")" + + # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. + if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then + local swift_runtime_libs + swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u) + for lib in $swift_runtime_libs; do + echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" + rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" + code_sign_if_enabled "${destination}/${lib}" + done + fi +} +# Copies and strips a vendored dSYM +install_dsym() { + local source="$1" + warn_missing_arch=${2:-true} + if [ -r "$source" ]; then + # Copy the dSYM into the targets temp dir. + echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\"" + rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}" + + local basename + basename="$(basename -s .dSYM "$source")" + binary_name="$(ls "$source/Contents/Resources/DWARF")" + binary="${DERIVED_FILES_DIR}/${basename}.dSYM/Contents/Resources/DWARF/${binary_name}" + + # Strip invalid architectures from the dSYM. + if [[ "$(file "$binary")" == *"Mach-O "*"dSYM companion"* ]]; then + strip_invalid_archs "$binary" "$warn_missing_arch" + fi + if [[ $STRIP_BINARY_RETVAL == 0 ]]; then + # Move the stripped file into its final destination. + echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\"" + rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.dSYM" "${DWARF_DSYM_FOLDER_PATH}" + else + # The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing. + mkdir -p "${DWARF_DSYM_FOLDER_PATH}" + touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.dSYM" + fi + fi +} + +# Used as a return value for each invocation of `strip_invalid_archs` function. +STRIP_BINARY_RETVAL=0 + +# Strip invalid architectures +strip_invalid_archs() { + binary="$1" + warn_missing_arch=${2:-true} + # Get architectures for current target binary + binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)" + # Intersect them with the architectures we are building for + intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)" + # If there are no archs supported by this binary then warn the user + if [[ -z "$intersected_archs" ]]; then + if [[ "$warn_missing_arch" == "true" ]]; then + echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)." + fi + STRIP_BINARY_RETVAL=1 + return + fi + stripped="" + for arch in $binary_archs; do + if ! [[ "${ARCHS}" == *"$arch"* ]]; then + # Strip non-valid architectures in-place + lipo -remove "$arch" -output "$binary" "$binary" + stripped="$stripped $arch" + fi + done + if [[ "$stripped" ]]; then + echo "Stripped $binary of architectures:$stripped" + fi + STRIP_BINARY_RETVAL=0 +} + +# Copies the bcsymbolmap files of a vendored framework +install_bcsymbolmap() { + local bcsymbolmap_path="$1" + local destination="${BUILT_PRODUCTS_DIR}" + echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}"" + rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}" +} + +# Signs a framework with the provided identity +code_sign_if_enabled() { + if [ -n "${EXPANDED_CODE_SIGN_IDENTITY:-}" -a "${CODE_SIGNING_REQUIRED:-}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then + # Use the current code_sign_identity + echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" + local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS:-} --preserve-metadata=identifier,entitlements '$1'" + + if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then + code_sign_cmd="$code_sign_cmd &" + fi + echo "$code_sign_cmd" + eval "$code_sign_cmd" + fi +} + +if [[ "$CONFIGURATION" == "Debug" ]]; then + install_framework "${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework" + install_framework "${BUILT_PRODUCTS_DIR}/BlueCryptor/Cryptor.framework" + install_framework "${BUILT_PRODUCTS_DIR}/BlueECC/CryptorECC.framework" + install_framework "${BUILT_PRODUCTS_DIR}/BlueRSA/CryptorRSA.framework" + install_framework "${BUILT_PRODUCTS_DIR}/KituraContracts/KituraContracts.framework" + install_framework "${BUILT_PRODUCTS_DIR}/LoggerAPI/LoggerAPI.framework" + install_framework "${BUILT_PRODUCTS_DIR}/Logging/Logging.framework" + install_framework "${BUILT_PRODUCTS_DIR}/SwiftJWT/SwiftJWT.framework" + install_framework "${BUILT_PRODUCTS_DIR}/SwiftyBeaver/SwiftyBeaver.framework" +fi +if [[ "$CONFIGURATION" == "Release" ]]; then + install_framework "${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework" + install_framework "${BUILT_PRODUCTS_DIR}/BlueCryptor/Cryptor.framework" + install_framework "${BUILT_PRODUCTS_DIR}/BlueECC/CryptorECC.framework" + install_framework "${BUILT_PRODUCTS_DIR}/BlueRSA/CryptorRSA.framework" + install_framework "${BUILT_PRODUCTS_DIR}/KituraContracts/KituraContracts.framework" + install_framework "${BUILT_PRODUCTS_DIR}/LoggerAPI/LoggerAPI.framework" + install_framework "${BUILT_PRODUCTS_DIR}/Logging/Logging.framework" + install_framework "${BUILT_PRODUCTS_DIR}/SwiftJWT/SwiftJWT.framework" + install_framework "${BUILT_PRODUCTS_DIR}/SwiftyBeaver/SwiftyBeaver.framework" +fi +if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then + wait +fi diff --git a/Pods/Target Support Files/Pods-AIGrammar-AIGrammarUITests/Pods-AIGrammar-AIGrammarUITests-umbrella.h b/Pods/Target Support Files/Pods-AIGrammar-AIGrammarUITests/Pods-AIGrammar-AIGrammarUITests-umbrella.h new file mode 100644 index 0000000..614da6f --- /dev/null +++ b/Pods/Target Support Files/Pods-AIGrammar-AIGrammarUITests/Pods-AIGrammar-AIGrammarUITests-umbrella.h @@ -0,0 +1,16 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + + +FOUNDATION_EXPORT double Pods_AIGrammar_AIGrammarUITestsVersionNumber; +FOUNDATION_EXPORT const unsigned char Pods_AIGrammar_AIGrammarUITestsVersionString[]; + diff --git a/Pods/Target Support Files/Pods-AIGrammar-AIGrammarUITests/Pods-AIGrammar-AIGrammarUITests.debug.xcconfig b/Pods/Target Support Files/Pods-AIGrammar-AIGrammarUITests/Pods-AIGrammar-AIGrammarUITests.debug.xcconfig new file mode 100644 index 0000000..0c09f6b --- /dev/null +++ b/Pods/Target Support Files/Pods-AIGrammar-AIGrammarUITests/Pods-AIGrammar-AIGrammarUITests.debug.xcconfig @@ -0,0 +1,15 @@ +ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" "${PODS_CONFIGURATION_BUILD_DIR}/BlueCryptor" "${PODS_CONFIGURATION_BUILD_DIR}/BlueECC" "${PODS_CONFIGURATION_BUILD_DIR}/BlueRSA" "${PODS_CONFIGURATION_BUILD_DIR}/KituraContracts" "${PODS_CONFIGURATION_BUILD_DIR}/LoggerAPI" "${PODS_CONFIGURATION_BUILD_DIR}/Logging" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftJWT" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyBeaver" "${PODS_CONFIGURATION_BUILD_DIR}/TrustDecision" +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/BlueCryptor/Cryptor.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/BlueECC/CryptorECC.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/BlueRSA/CryptorRSA.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/KituraContracts/KituraContracts.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/LoggerAPI/LoggerAPI.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Logging/Logging.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftJWT/SwiftJWT.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyBeaver/SwiftyBeaver.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/TrustDecision/TrustDecision.framework/Headers" +LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift "$(PLATFORM_DIR)/Developer/Library/Frameworks" '@executable_path/Frameworks' '@loader_path/Frameworks' +LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift +OTHER_LDFLAGS = $(inherited) -ObjC -framework "Alamofire" -framework "CFNetwork" -framework "Cryptor" -framework "CryptorECC" -framework "CryptorRSA" -framework "KituraContracts" -framework "LoggerAPI" -framework "Logging" -framework "SwiftJWT" -framework "SwiftyBeaver" -framework "TrustDecision" +OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_PODFILE_DIR_PATH = ${SRCROOT}/. +PODS_ROOT = ${SRCROOT}/Pods +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Pods/Target Support Files/Pods-AIGrammar-AIGrammarUITests/Pods-AIGrammar-AIGrammarUITests.modulemap b/Pods/Target Support Files/Pods-AIGrammar-AIGrammarUITests/Pods-AIGrammar-AIGrammarUITests.modulemap new file mode 100644 index 0000000..576c1d8 --- /dev/null +++ b/Pods/Target Support Files/Pods-AIGrammar-AIGrammarUITests/Pods-AIGrammar-AIGrammarUITests.modulemap @@ -0,0 +1,6 @@ +framework module Pods_AIGrammar_AIGrammarUITests { + umbrella header "Pods-AIGrammar-AIGrammarUITests-umbrella.h" + + export * + module * { export * } +} diff --git a/Pods/Target Support Files/Pods-AIGrammar-AIGrammarUITests/Pods-AIGrammar-AIGrammarUITests.release.xcconfig b/Pods/Target Support Files/Pods-AIGrammar-AIGrammarUITests/Pods-AIGrammar-AIGrammarUITests.release.xcconfig new file mode 100644 index 0000000..0c09f6b --- /dev/null +++ b/Pods/Target Support Files/Pods-AIGrammar-AIGrammarUITests/Pods-AIGrammar-AIGrammarUITests.release.xcconfig @@ -0,0 +1,15 @@ +ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" "${PODS_CONFIGURATION_BUILD_DIR}/BlueCryptor" "${PODS_CONFIGURATION_BUILD_DIR}/BlueECC" "${PODS_CONFIGURATION_BUILD_DIR}/BlueRSA" "${PODS_CONFIGURATION_BUILD_DIR}/KituraContracts" "${PODS_CONFIGURATION_BUILD_DIR}/LoggerAPI" "${PODS_CONFIGURATION_BUILD_DIR}/Logging" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftJWT" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyBeaver" "${PODS_CONFIGURATION_BUILD_DIR}/TrustDecision" +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/BlueCryptor/Cryptor.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/BlueECC/CryptorECC.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/BlueRSA/CryptorRSA.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/KituraContracts/KituraContracts.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/LoggerAPI/LoggerAPI.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Logging/Logging.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftJWT/SwiftJWT.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyBeaver/SwiftyBeaver.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/TrustDecision/TrustDecision.framework/Headers" +LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift "$(PLATFORM_DIR)/Developer/Library/Frameworks" '@executable_path/Frameworks' '@loader_path/Frameworks' +LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift +OTHER_LDFLAGS = $(inherited) -ObjC -framework "Alamofire" -framework "CFNetwork" -framework "Cryptor" -framework "CryptorECC" -framework "CryptorRSA" -framework "KituraContracts" -framework "LoggerAPI" -framework "Logging" -framework "SwiftJWT" -framework "SwiftyBeaver" -framework "TrustDecision" +OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_PODFILE_DIR_PATH = ${SRCROOT}/. +PODS_ROOT = ${SRCROOT}/Pods +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Pods/Target Support Files/Pods-AIGrammar/Pods-AIGrammar-Info.plist b/Pods/Target Support Files/Pods-AIGrammar/Pods-AIGrammar-Info.plist new file mode 100644 index 0000000..19cf209 --- /dev/null +++ b/Pods/Target Support Files/Pods-AIGrammar/Pods-AIGrammar-Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + ${PODS_DEVELOPMENT_LANGUAGE} + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0.0 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/Pods/Target Support Files/Pods-AIGrammar/Pods-AIGrammar-acknowledgements.markdown b/Pods/Target Support Files/Pods-AIGrammar/Pods-AIGrammar-acknowledgements.markdown new file mode 100644 index 0000000..1af4720 --- /dev/null +++ b/Pods/Target Support Files/Pods-AIGrammar/Pods-AIGrammar-acknowledgements.markdown @@ -0,0 +1,1389 @@ +# Acknowledgements +This application makes use of the following third party libraries: + +## Alamofire + +Copyright (c) 2014-2022 Alamofire Software Foundation (http://alamofire.org/) + +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. + + +## BlueCryptor + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + +## BlueECC + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + +## BlueRSA + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + +## KituraContracts + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +## LoggerAPI + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + +## Logging + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +## SwiftJWT + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + +## SwiftyBeaver + +The MIT License (MIT) + +Copyright (c) 2015 Sebastian Kreutzberger + +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. + + +## TrustDecision + +MIT License + +Copyright (c) 2022 trustdecision + +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. + +Generated by CocoaPods - https://cocoapods.org diff --git a/Pods/Target Support Files/Pods-AIGrammar/Pods-AIGrammar-acknowledgements.plist b/Pods/Target Support Files/Pods-AIGrammar/Pods-AIGrammar-acknowledgements.plist new file mode 100644 index 0000000..83cc389 --- /dev/null +++ b/Pods/Target Support Files/Pods-AIGrammar/Pods-AIGrammar-acknowledgements.plist @@ -0,0 +1,1475 @@ + + + + + PreferenceSpecifiers + + + FooterText + This application makes use of the following third party libraries: + Title + Acknowledgements + Type + PSGroupSpecifier + + + FooterText + Copyright (c) 2014-2022 Alamofire Software Foundation (http://alamofire.org/) + +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. + + License + MIT + Title + Alamofire + Type + PSGroupSpecifier + + + FooterText + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + License + Apache License, Version 2.0 + Title + BlueCryptor + Type + PSGroupSpecifier + + + FooterText + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + License + Apache License, Version 2.0 + Title + BlueECC + Type + PSGroupSpecifier + + + FooterText + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + License + Apache License, Version 2.0 + Title + BlueRSA + Type + PSGroupSpecifier + + + FooterText + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + License + Apache License, Version 2.0 + Title + KituraContracts + Type + PSGroupSpecifier + + + FooterText + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + License + Apache License, Version 2.0 + Title + LoggerAPI + Type + PSGroupSpecifier + + + FooterText + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + License + Apache 2.0 + Title + Logging + Type + PSGroupSpecifier + + + FooterText + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + License + Apache License, Version 2.0 + Title + SwiftJWT + Type + PSGroupSpecifier + + + FooterText + The MIT License (MIT) + +Copyright (c) 2015 Sebastian Kreutzberger + +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. + + License + MIT + Title + SwiftyBeaver + Type + PSGroupSpecifier + + + FooterText + MIT License + +Copyright (c) 2022 trustdecision + +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. + + License + MIT + Title + TrustDecision + Type + PSGroupSpecifier + + + FooterText + Generated by CocoaPods - https://cocoapods.org + Title + + Type + PSGroupSpecifier + + + StringsTable + Acknowledgements + Title + Acknowledgements + + diff --git a/Pods/Target Support Files/Pods-AIGrammar/Pods-AIGrammar-dummy.m b/Pods/Target Support Files/Pods-AIGrammar/Pods-AIGrammar-dummy.m new file mode 100644 index 0000000..f3f0df2 --- /dev/null +++ b/Pods/Target Support Files/Pods-AIGrammar/Pods-AIGrammar-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_Pods_AIGrammar : NSObject +@end +@implementation PodsDummy_Pods_AIGrammar +@end diff --git a/Pods/Target Support Files/Pods-AIGrammar/Pods-AIGrammar-frameworks-Debug-input-files.xcfilelist b/Pods/Target Support Files/Pods-AIGrammar/Pods-AIGrammar-frameworks-Debug-input-files.xcfilelist new file mode 100644 index 0000000..7239e66 --- /dev/null +++ b/Pods/Target Support Files/Pods-AIGrammar/Pods-AIGrammar-frameworks-Debug-input-files.xcfilelist @@ -0,0 +1,10 @@ +${PODS_ROOT}/Target Support Files/Pods-AIGrammar/Pods-AIGrammar-frameworks.sh +${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework +${BUILT_PRODUCTS_DIR}/BlueCryptor/Cryptor.framework +${BUILT_PRODUCTS_DIR}/BlueECC/CryptorECC.framework +${BUILT_PRODUCTS_DIR}/BlueRSA/CryptorRSA.framework +${BUILT_PRODUCTS_DIR}/KituraContracts/KituraContracts.framework +${BUILT_PRODUCTS_DIR}/LoggerAPI/LoggerAPI.framework +${BUILT_PRODUCTS_DIR}/Logging/Logging.framework +${BUILT_PRODUCTS_DIR}/SwiftJWT/SwiftJWT.framework +${BUILT_PRODUCTS_DIR}/SwiftyBeaver/SwiftyBeaver.framework \ No newline at end of file diff --git a/Pods/Target Support Files/Pods-AIGrammar/Pods-AIGrammar-frameworks-Debug-output-files.xcfilelist b/Pods/Target Support Files/Pods-AIGrammar/Pods-AIGrammar-frameworks-Debug-output-files.xcfilelist new file mode 100644 index 0000000..0568650 --- /dev/null +++ b/Pods/Target Support Files/Pods-AIGrammar/Pods-AIGrammar-frameworks-Debug-output-files.xcfilelist @@ -0,0 +1,9 @@ +${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Alamofire.framework +${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Cryptor.framework +${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/CryptorECC.framework +${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/CryptorRSA.framework +${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/KituraContracts.framework +${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/LoggerAPI.framework +${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Logging.framework +${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftJWT.framework +${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftyBeaver.framework \ No newline at end of file diff --git a/Pods/Target Support Files/Pods-AIGrammar/Pods-AIGrammar-frameworks-Release-input-files.xcfilelist b/Pods/Target Support Files/Pods-AIGrammar/Pods-AIGrammar-frameworks-Release-input-files.xcfilelist new file mode 100644 index 0000000..7239e66 --- /dev/null +++ b/Pods/Target Support Files/Pods-AIGrammar/Pods-AIGrammar-frameworks-Release-input-files.xcfilelist @@ -0,0 +1,10 @@ +${PODS_ROOT}/Target Support Files/Pods-AIGrammar/Pods-AIGrammar-frameworks.sh +${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework +${BUILT_PRODUCTS_DIR}/BlueCryptor/Cryptor.framework +${BUILT_PRODUCTS_DIR}/BlueECC/CryptorECC.framework +${BUILT_PRODUCTS_DIR}/BlueRSA/CryptorRSA.framework +${BUILT_PRODUCTS_DIR}/KituraContracts/KituraContracts.framework +${BUILT_PRODUCTS_DIR}/LoggerAPI/LoggerAPI.framework +${BUILT_PRODUCTS_DIR}/Logging/Logging.framework +${BUILT_PRODUCTS_DIR}/SwiftJWT/SwiftJWT.framework +${BUILT_PRODUCTS_DIR}/SwiftyBeaver/SwiftyBeaver.framework \ No newline at end of file diff --git a/Pods/Target Support Files/Pods-AIGrammar/Pods-AIGrammar-frameworks-Release-output-files.xcfilelist b/Pods/Target Support Files/Pods-AIGrammar/Pods-AIGrammar-frameworks-Release-output-files.xcfilelist new file mode 100644 index 0000000..0568650 --- /dev/null +++ b/Pods/Target Support Files/Pods-AIGrammar/Pods-AIGrammar-frameworks-Release-output-files.xcfilelist @@ -0,0 +1,9 @@ +${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Alamofire.framework +${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Cryptor.framework +${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/CryptorECC.framework +${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/CryptorRSA.framework +${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/KituraContracts.framework +${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/LoggerAPI.framework +${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Logging.framework +${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftJWT.framework +${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftyBeaver.framework \ No newline at end of file diff --git a/Pods/Target Support Files/Pods-AIGrammar/Pods-AIGrammar-frameworks.sh b/Pods/Target Support Files/Pods-AIGrammar/Pods-AIGrammar-frameworks.sh new file mode 100755 index 0000000..6bcabee --- /dev/null +++ b/Pods/Target Support Files/Pods-AIGrammar/Pods-AIGrammar-frameworks.sh @@ -0,0 +1,202 @@ +#!/bin/sh +set -e +set -u +set -o pipefail + +function on_error { + echo "$(realpath -mq "${0}"):$1: error: Unexpected failure" +} +trap 'on_error $LINENO' ERR + +if [ -z ${FRAMEWORKS_FOLDER_PATH+x} ]; then + # If FRAMEWORKS_FOLDER_PATH is not set, then there's nowhere for us to copy + # frameworks to, so exit 0 (signalling the script phase was successful). + exit 0 +fi + +echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" +mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + +COCOAPODS_PARALLEL_CODE_SIGN="${COCOAPODS_PARALLEL_CODE_SIGN:-false}" +SWIFT_STDLIB_PATH="${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" +BCSYMBOLMAP_DIR="BCSymbolMaps" + + +# This protects against multiple targets copying the same framework dependency at the same time. The solution +# was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html +RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") + +# Copies and strips a vendored framework +install_framework() +{ + if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then + local source="${BUILT_PRODUCTS_DIR}/$1" + elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then + local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" + elif [ -r "$1" ]; then + local source="$1" + fi + + local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + + if [ -L "${source}" ]; then + echo "Symlinked..." + source="$(readlink -f "${source}")" + fi + + if [ -d "${source}/${BCSYMBOLMAP_DIR}" ]; then + # Locate and install any .bcsymbolmaps if present, and remove them from the .framework before the framework is copied + find "${source}/${BCSYMBOLMAP_DIR}" -name "*.bcsymbolmap"|while read f; do + echo "Installing $f" + install_bcsymbolmap "$f" "$destination" + rm "$f" + done + rmdir "${source}/${BCSYMBOLMAP_DIR}" + fi + + # Use filter instead of exclude so missing patterns don't throw errors. + echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" + rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" + + local basename + basename="$(basename -s .framework "$1")" + binary="${destination}/${basename}.framework/${basename}" + + if ! [ -r "$binary" ]; then + binary="${destination}/${basename}" + elif [ -L "${binary}" ]; then + echo "Destination binary is symlinked..." + dirname="$(dirname "${binary}")" + binary="${dirname}/$(readlink "${binary}")" + fi + + # Strip invalid architectures so "fat" simulator / device frameworks work on device + if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then + strip_invalid_archs "$binary" + fi + + # Resign the code if required by the build settings to avoid unstable apps + code_sign_if_enabled "${destination}/$(basename "$1")" + + # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. + if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then + local swift_runtime_libs + swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u) + for lib in $swift_runtime_libs; do + echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" + rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" + code_sign_if_enabled "${destination}/${lib}" + done + fi +} +# Copies and strips a vendored dSYM +install_dsym() { + local source="$1" + warn_missing_arch=${2:-true} + if [ -r "$source" ]; then + # Copy the dSYM into the targets temp dir. + echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\"" + rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}" + + local basename + basename="$(basename -s .dSYM "$source")" + binary_name="$(ls "$source/Contents/Resources/DWARF")" + binary="${DERIVED_FILES_DIR}/${basename}.dSYM/Contents/Resources/DWARF/${binary_name}" + + # Strip invalid architectures from the dSYM. + if [[ "$(file "$binary")" == *"Mach-O "*"dSYM companion"* ]]; then + strip_invalid_archs "$binary" "$warn_missing_arch" + fi + if [[ $STRIP_BINARY_RETVAL == 0 ]]; then + # Move the stripped file into its final destination. + echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\"" + rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.dSYM" "${DWARF_DSYM_FOLDER_PATH}" + else + # The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing. + mkdir -p "${DWARF_DSYM_FOLDER_PATH}" + touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.dSYM" + fi + fi +} + +# Used as a return value for each invocation of `strip_invalid_archs` function. +STRIP_BINARY_RETVAL=0 + +# Strip invalid architectures +strip_invalid_archs() { + binary="$1" + warn_missing_arch=${2:-true} + # Get architectures for current target binary + binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)" + # Intersect them with the architectures we are building for + intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)" + # If there are no archs supported by this binary then warn the user + if [[ -z "$intersected_archs" ]]; then + if [[ "$warn_missing_arch" == "true" ]]; then + echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)." + fi + STRIP_BINARY_RETVAL=1 + return + fi + stripped="" + for arch in $binary_archs; do + if ! [[ "${ARCHS}" == *"$arch"* ]]; then + # Strip non-valid architectures in-place + lipo -remove "$arch" -output "$binary" "$binary" + stripped="$stripped $arch" + fi + done + if [[ "$stripped" ]]; then + echo "Stripped $binary of architectures:$stripped" + fi + STRIP_BINARY_RETVAL=0 +} + +# Copies the bcsymbolmap files of a vendored framework +install_bcsymbolmap() { + local bcsymbolmap_path="$1" + local destination="${BUILT_PRODUCTS_DIR}" + echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}"" + rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}" +} + +# Signs a framework with the provided identity +code_sign_if_enabled() { + if [ -n "${EXPANDED_CODE_SIGN_IDENTITY:-}" -a "${CODE_SIGNING_REQUIRED:-}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then + # Use the current code_sign_identity + echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" + local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS:-} --preserve-metadata=identifier,entitlements '$1'" + + if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then + code_sign_cmd="$code_sign_cmd &" + fi + echo "$code_sign_cmd" + eval "$code_sign_cmd" + fi +} + +if [[ "$CONFIGURATION" == "Debug" ]]; then + install_framework "${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework" + install_framework "${BUILT_PRODUCTS_DIR}/BlueCryptor/Cryptor.framework" + install_framework "${BUILT_PRODUCTS_DIR}/BlueECC/CryptorECC.framework" + install_framework "${BUILT_PRODUCTS_DIR}/BlueRSA/CryptorRSA.framework" + install_framework "${BUILT_PRODUCTS_DIR}/KituraContracts/KituraContracts.framework" + install_framework "${BUILT_PRODUCTS_DIR}/LoggerAPI/LoggerAPI.framework" + install_framework "${BUILT_PRODUCTS_DIR}/Logging/Logging.framework" + install_framework "${BUILT_PRODUCTS_DIR}/SwiftJWT/SwiftJWT.framework" + install_framework "${BUILT_PRODUCTS_DIR}/SwiftyBeaver/SwiftyBeaver.framework" +fi +if [[ "$CONFIGURATION" == "Release" ]]; then + install_framework "${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework" + install_framework "${BUILT_PRODUCTS_DIR}/BlueCryptor/Cryptor.framework" + install_framework "${BUILT_PRODUCTS_DIR}/BlueECC/CryptorECC.framework" + install_framework "${BUILT_PRODUCTS_DIR}/BlueRSA/CryptorRSA.framework" + install_framework "${BUILT_PRODUCTS_DIR}/KituraContracts/KituraContracts.framework" + install_framework "${BUILT_PRODUCTS_DIR}/LoggerAPI/LoggerAPI.framework" + install_framework "${BUILT_PRODUCTS_DIR}/Logging/Logging.framework" + install_framework "${BUILT_PRODUCTS_DIR}/SwiftJWT/SwiftJWT.framework" + install_framework "${BUILT_PRODUCTS_DIR}/SwiftyBeaver/SwiftyBeaver.framework" +fi +if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then + wait +fi diff --git a/Pods/Target Support Files/Pods-AIGrammar/Pods-AIGrammar-umbrella.h b/Pods/Target Support Files/Pods-AIGrammar/Pods-AIGrammar-umbrella.h new file mode 100644 index 0000000..316de2e --- /dev/null +++ b/Pods/Target Support Files/Pods-AIGrammar/Pods-AIGrammar-umbrella.h @@ -0,0 +1,16 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + + +FOUNDATION_EXPORT double Pods_AIGrammarVersionNumber; +FOUNDATION_EXPORT const unsigned char Pods_AIGrammarVersionString[]; + diff --git a/Pods/Target Support Files/Pods-AIGrammar/Pods-AIGrammar.debug.xcconfig b/Pods/Target Support Files/Pods-AIGrammar/Pods-AIGrammar.debug.xcconfig new file mode 100644 index 0000000..7e05423 --- /dev/null +++ b/Pods/Target Support Files/Pods-AIGrammar/Pods-AIGrammar.debug.xcconfig @@ -0,0 +1,15 @@ +ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" "${PODS_CONFIGURATION_BUILD_DIR}/BlueCryptor" "${PODS_CONFIGURATION_BUILD_DIR}/BlueECC" "${PODS_CONFIGURATION_BUILD_DIR}/BlueRSA" "${PODS_CONFIGURATION_BUILD_DIR}/KituraContracts" "${PODS_CONFIGURATION_BUILD_DIR}/LoggerAPI" "${PODS_CONFIGURATION_BUILD_DIR}/Logging" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftJWT" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyBeaver" "${PODS_CONFIGURATION_BUILD_DIR}/TrustDecision" +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/BlueCryptor/Cryptor.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/BlueECC/CryptorECC.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/BlueRSA/CryptorRSA.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/KituraContracts/KituraContracts.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/LoggerAPI/LoggerAPI.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Logging/Logging.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftJWT/SwiftJWT.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyBeaver/SwiftyBeaver.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/TrustDecision/TrustDecision.framework/Headers" +LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift '@executable_path/Frameworks' '@loader_path/Frameworks' +LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift +OTHER_LDFLAGS = $(inherited) -ObjC -framework "Alamofire" -framework "CFNetwork" -framework "Cryptor" -framework "CryptorECC" -framework "CryptorRSA" -framework "KituraContracts" -framework "LoggerAPI" -framework "Logging" -framework "SwiftJWT" -framework "SwiftyBeaver" -framework "TrustDecision" +OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_PODFILE_DIR_PATH = ${SRCROOT}/. +PODS_ROOT = ${SRCROOT}/Pods +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Pods/Target Support Files/Pods-AIGrammar/Pods-AIGrammar.modulemap b/Pods/Target Support Files/Pods-AIGrammar/Pods-AIGrammar.modulemap new file mode 100644 index 0000000..9551951 --- /dev/null +++ b/Pods/Target Support Files/Pods-AIGrammar/Pods-AIGrammar.modulemap @@ -0,0 +1,6 @@ +framework module Pods_AIGrammar { + umbrella header "Pods-AIGrammar-umbrella.h" + + export * + module * { export * } +} diff --git a/Pods/Target Support Files/Pods-AIGrammar/Pods-AIGrammar.release.xcconfig b/Pods/Target Support Files/Pods-AIGrammar/Pods-AIGrammar.release.xcconfig new file mode 100644 index 0000000..7e05423 --- /dev/null +++ b/Pods/Target Support Files/Pods-AIGrammar/Pods-AIGrammar.release.xcconfig @@ -0,0 +1,15 @@ +ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" "${PODS_CONFIGURATION_BUILD_DIR}/BlueCryptor" "${PODS_CONFIGURATION_BUILD_DIR}/BlueECC" "${PODS_CONFIGURATION_BUILD_DIR}/BlueRSA" "${PODS_CONFIGURATION_BUILD_DIR}/KituraContracts" "${PODS_CONFIGURATION_BUILD_DIR}/LoggerAPI" "${PODS_CONFIGURATION_BUILD_DIR}/Logging" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftJWT" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyBeaver" "${PODS_CONFIGURATION_BUILD_DIR}/TrustDecision" +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/BlueCryptor/Cryptor.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/BlueECC/CryptorECC.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/BlueRSA/CryptorRSA.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/KituraContracts/KituraContracts.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/LoggerAPI/LoggerAPI.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Logging/Logging.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftJWT/SwiftJWT.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyBeaver/SwiftyBeaver.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/TrustDecision/TrustDecision.framework/Headers" +LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift '@executable_path/Frameworks' '@loader_path/Frameworks' +LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift +OTHER_LDFLAGS = $(inherited) -ObjC -framework "Alamofire" -framework "CFNetwork" -framework "Cryptor" -framework "CryptorECC" -framework "CryptorRSA" -framework "KituraContracts" -framework "LoggerAPI" -framework "Logging" -framework "SwiftJWT" -framework "SwiftyBeaver" -framework "TrustDecision" +OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_PODFILE_DIR_PATH = ${SRCROOT}/. +PODS_ROOT = ${SRCROOT}/Pods +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Pods/Target Support Files/Pods-AIGrammarTests/Pods-AIGrammarTests-Info.plist b/Pods/Target Support Files/Pods-AIGrammarTests/Pods-AIGrammarTests-Info.plist new file mode 100644 index 0000000..19cf209 --- /dev/null +++ b/Pods/Target Support Files/Pods-AIGrammarTests/Pods-AIGrammarTests-Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + ${PODS_DEVELOPMENT_LANGUAGE} + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0.0 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/Pods/Target Support Files/Pods-AIGrammarTests/Pods-AIGrammarTests-acknowledgements.markdown b/Pods/Target Support Files/Pods-AIGrammarTests/Pods-AIGrammarTests-acknowledgements.markdown new file mode 100644 index 0000000..102af75 --- /dev/null +++ b/Pods/Target Support Files/Pods-AIGrammarTests/Pods-AIGrammarTests-acknowledgements.markdown @@ -0,0 +1,3 @@ +# Acknowledgements +This application makes use of the following third party libraries: +Generated by CocoaPods - https://cocoapods.org diff --git a/Pods/Target Support Files/Pods-AIGrammarTests/Pods-AIGrammarTests-acknowledgements.plist b/Pods/Target Support Files/Pods-AIGrammarTests/Pods-AIGrammarTests-acknowledgements.plist new file mode 100644 index 0000000..7acbad1 --- /dev/null +++ b/Pods/Target Support Files/Pods-AIGrammarTests/Pods-AIGrammarTests-acknowledgements.plist @@ -0,0 +1,29 @@ + + + + + PreferenceSpecifiers + + + FooterText + This application makes use of the following third party libraries: + Title + Acknowledgements + Type + PSGroupSpecifier + + + FooterText + Generated by CocoaPods - https://cocoapods.org + Title + + Type + PSGroupSpecifier + + + StringsTable + Acknowledgements + Title + Acknowledgements + + diff --git a/Pods/Target Support Files/Pods-AIGrammarTests/Pods-AIGrammarTests-dummy.m b/Pods/Target Support Files/Pods-AIGrammarTests/Pods-AIGrammarTests-dummy.m new file mode 100644 index 0000000..8e84e48 --- /dev/null +++ b/Pods/Target Support Files/Pods-AIGrammarTests/Pods-AIGrammarTests-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_Pods_AIGrammarTests : NSObject +@end +@implementation PodsDummy_Pods_AIGrammarTests +@end diff --git a/Pods/Target Support Files/Pods-AIGrammarTests/Pods-AIGrammarTests-umbrella.h b/Pods/Target Support Files/Pods-AIGrammarTests/Pods-AIGrammarTests-umbrella.h new file mode 100644 index 0000000..ff476e0 --- /dev/null +++ b/Pods/Target Support Files/Pods-AIGrammarTests/Pods-AIGrammarTests-umbrella.h @@ -0,0 +1,16 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + + +FOUNDATION_EXPORT double Pods_AIGrammarTestsVersionNumber; +FOUNDATION_EXPORT const unsigned char Pods_AIGrammarTestsVersionString[]; + diff --git a/Pods/Target Support Files/Pods-AIGrammarTests/Pods-AIGrammarTests.debug.xcconfig b/Pods/Target Support Files/Pods-AIGrammarTests/Pods-AIGrammarTests.debug.xcconfig new file mode 100644 index 0000000..05a5621 --- /dev/null +++ b/Pods/Target Support Files/Pods-AIGrammarTests/Pods-AIGrammarTests.debug.xcconfig @@ -0,0 +1,11 @@ +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" "${PODS_CONFIGURATION_BUILD_DIR}/BlueCryptor" "${PODS_CONFIGURATION_BUILD_DIR}/BlueECC" "${PODS_CONFIGURATION_BUILD_DIR}/BlueRSA" "${PODS_CONFIGURATION_BUILD_DIR}/KituraContracts" "${PODS_CONFIGURATION_BUILD_DIR}/LoggerAPI" "${PODS_CONFIGURATION_BUILD_DIR}/Logging" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftJWT" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyBeaver" "${PODS_CONFIGURATION_BUILD_DIR}/TrustDecision" +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/BlueCryptor/Cryptor.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/BlueECC/CryptorECC.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/BlueRSA/CryptorRSA.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/KituraContracts/KituraContracts.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/LoggerAPI/LoggerAPI.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Logging/Logging.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftJWT/SwiftJWT.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyBeaver/SwiftyBeaver.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/TrustDecision/TrustDecision.framework/Headers" +OTHER_LDFLAGS = $(inherited) -framework "Alamofire" -framework "CFNetwork" -framework "Cryptor" -framework "CryptorECC" -framework "CryptorRSA" -framework "KituraContracts" -framework "LoggerAPI" -framework "Logging" -framework "SwiftJWT" -framework "SwiftyBeaver" +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_PODFILE_DIR_PATH = ${SRCROOT}/. +PODS_ROOT = ${SRCROOT}/Pods +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Pods/Target Support Files/Pods-AIGrammarTests/Pods-AIGrammarTests.modulemap b/Pods/Target Support Files/Pods-AIGrammarTests/Pods-AIGrammarTests.modulemap new file mode 100644 index 0000000..b0b72bc --- /dev/null +++ b/Pods/Target Support Files/Pods-AIGrammarTests/Pods-AIGrammarTests.modulemap @@ -0,0 +1,6 @@ +framework module Pods_AIGrammarTests { + umbrella header "Pods-AIGrammarTests-umbrella.h" + + export * + module * { export * } +} diff --git a/Pods/Target Support Files/Pods-AIGrammarTests/Pods-AIGrammarTests.release.xcconfig b/Pods/Target Support Files/Pods-AIGrammarTests/Pods-AIGrammarTests.release.xcconfig new file mode 100644 index 0000000..05a5621 --- /dev/null +++ b/Pods/Target Support Files/Pods-AIGrammarTests/Pods-AIGrammarTests.release.xcconfig @@ -0,0 +1,11 @@ +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" "${PODS_CONFIGURATION_BUILD_DIR}/BlueCryptor" "${PODS_CONFIGURATION_BUILD_DIR}/BlueECC" "${PODS_CONFIGURATION_BUILD_DIR}/BlueRSA" "${PODS_CONFIGURATION_BUILD_DIR}/KituraContracts" "${PODS_CONFIGURATION_BUILD_DIR}/LoggerAPI" "${PODS_CONFIGURATION_BUILD_DIR}/Logging" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftJWT" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyBeaver" "${PODS_CONFIGURATION_BUILD_DIR}/TrustDecision" +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/BlueCryptor/Cryptor.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/BlueECC/CryptorECC.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/BlueRSA/CryptorRSA.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/KituraContracts/KituraContracts.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/LoggerAPI/LoggerAPI.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Logging/Logging.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftJWT/SwiftJWT.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyBeaver/SwiftyBeaver.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/TrustDecision/TrustDecision.framework/Headers" +OTHER_LDFLAGS = $(inherited) -framework "Alamofire" -framework "CFNetwork" -framework "Cryptor" -framework "CryptorECC" -framework "CryptorRSA" -framework "KituraContracts" -framework "LoggerAPI" -framework "Logging" -framework "SwiftJWT" -framework "SwiftyBeaver" +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_PODFILE_DIR_PATH = ${SRCROOT}/. +PODS_ROOT = ${SRCROOT}/Pods +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Pods/Target Support Files/SwiftJWT/SwiftJWT-Info.plist b/Pods/Target Support Files/SwiftJWT/SwiftJWT-Info.plist new file mode 100644 index 0000000..c11ce59 --- /dev/null +++ b/Pods/Target Support Files/SwiftJWT/SwiftJWT-Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + ${PODS_DEVELOPMENT_LANGUAGE} + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 3.6.200 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/Pods/Target Support Files/SwiftJWT/SwiftJWT-dummy.m b/Pods/Target Support Files/SwiftJWT/SwiftJWT-dummy.m new file mode 100644 index 0000000..5055c34 --- /dev/null +++ b/Pods/Target Support Files/SwiftJWT/SwiftJWT-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_SwiftJWT : NSObject +@end +@implementation PodsDummy_SwiftJWT +@end diff --git a/Pods/Target Support Files/SwiftJWT/SwiftJWT-prefix.pch b/Pods/Target Support Files/SwiftJWT/SwiftJWT-prefix.pch new file mode 100644 index 0000000..beb2a24 --- /dev/null +++ b/Pods/Target Support Files/SwiftJWT/SwiftJWT-prefix.pch @@ -0,0 +1,12 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + diff --git a/Pods/Target Support Files/SwiftJWT/SwiftJWT-umbrella.h b/Pods/Target Support Files/SwiftJWT/SwiftJWT-umbrella.h new file mode 100644 index 0000000..7964b27 --- /dev/null +++ b/Pods/Target Support Files/SwiftJWT/SwiftJWT-umbrella.h @@ -0,0 +1,16 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + + +FOUNDATION_EXPORT double SwiftJWTVersionNumber; +FOUNDATION_EXPORT const unsigned char SwiftJWTVersionString[]; + diff --git a/Pods/Target Support Files/SwiftJWT/SwiftJWT.debug.xcconfig b/Pods/Target Support Files/SwiftJWT/SwiftJWT.debug.xcconfig new file mode 100644 index 0000000..cd25b73 --- /dev/null +++ b/Pods/Target Support Files/SwiftJWT/SwiftJWT.debug.xcconfig @@ -0,0 +1,16 @@ +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/SwiftJWT +FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/BlueCryptor" "${PODS_CONFIGURATION_BUILD_DIR}/BlueECC" "${PODS_CONFIGURATION_BUILD_DIR}/BlueRSA" "${PODS_CONFIGURATION_BUILD_DIR}/KituraContracts" "${PODS_CONFIGURATION_BUILD_DIR}/LoggerAPI" "${PODS_CONFIGURATION_BUILD_DIR}/Logging" +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift +OTHER_LDFLAGS = $(inherited) -framework "Cryptor" -framework "CryptorECC" -framework "CryptorRSA" -framework "KituraContracts" -framework "LoggerAPI" +OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE} +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/SwiftJWT +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Pods/Target Support Files/SwiftJWT/SwiftJWT.modulemap b/Pods/Target Support Files/SwiftJWT/SwiftJWT.modulemap new file mode 100644 index 0000000..7828b6a --- /dev/null +++ b/Pods/Target Support Files/SwiftJWT/SwiftJWT.modulemap @@ -0,0 +1,6 @@ +framework module SwiftJWT { + umbrella header "SwiftJWT-umbrella.h" + + export * + module * { export * } +} diff --git a/Pods/Target Support Files/SwiftJWT/SwiftJWT.release.xcconfig b/Pods/Target Support Files/SwiftJWT/SwiftJWT.release.xcconfig new file mode 100644 index 0000000..cd25b73 --- /dev/null +++ b/Pods/Target Support Files/SwiftJWT/SwiftJWT.release.xcconfig @@ -0,0 +1,16 @@ +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/SwiftJWT +FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/BlueCryptor" "${PODS_CONFIGURATION_BUILD_DIR}/BlueECC" "${PODS_CONFIGURATION_BUILD_DIR}/BlueRSA" "${PODS_CONFIGURATION_BUILD_DIR}/KituraContracts" "${PODS_CONFIGURATION_BUILD_DIR}/LoggerAPI" "${PODS_CONFIGURATION_BUILD_DIR}/Logging" +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift +OTHER_LDFLAGS = $(inherited) -framework "Cryptor" -framework "CryptorECC" -framework "CryptorRSA" -framework "KituraContracts" -framework "LoggerAPI" +OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE} +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/SwiftJWT +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Pods/Target Support Files/SwiftyBeaver/ResourceBundle-SwiftyBeaver-SwiftyBeaver-Info.plist b/Pods/Target Support Files/SwiftyBeaver/ResourceBundle-SwiftyBeaver-SwiftyBeaver-Info.plist new file mode 100644 index 0000000..7201446 --- /dev/null +++ b/Pods/Target Support Files/SwiftyBeaver/ResourceBundle-SwiftyBeaver-SwiftyBeaver-Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + ${PODS_DEVELOPMENT_LANGUAGE} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + BNDL + CFBundleShortVersionString + 2.1.1 + CFBundleSignature + ???? + CFBundleVersion + 1 + NSPrincipalClass + + + diff --git a/Pods/Target Support Files/SwiftyBeaver/SwiftyBeaver-Info.plist b/Pods/Target Support Files/SwiftyBeaver/SwiftyBeaver-Info.plist new file mode 100644 index 0000000..8c82683 --- /dev/null +++ b/Pods/Target Support Files/SwiftyBeaver/SwiftyBeaver-Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + ${PODS_DEVELOPMENT_LANGUAGE} + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 2.1.1 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/Pods/Target Support Files/SwiftyBeaver/SwiftyBeaver-dummy.m b/Pods/Target Support Files/SwiftyBeaver/SwiftyBeaver-dummy.m new file mode 100644 index 0000000..b00afab --- /dev/null +++ b/Pods/Target Support Files/SwiftyBeaver/SwiftyBeaver-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_SwiftyBeaver : NSObject +@end +@implementation PodsDummy_SwiftyBeaver +@end diff --git a/Pods/Target Support Files/SwiftyBeaver/SwiftyBeaver-prefix.pch b/Pods/Target Support Files/SwiftyBeaver/SwiftyBeaver-prefix.pch new file mode 100644 index 0000000..beb2a24 --- /dev/null +++ b/Pods/Target Support Files/SwiftyBeaver/SwiftyBeaver-prefix.pch @@ -0,0 +1,12 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + diff --git a/Pods/Target Support Files/SwiftyBeaver/SwiftyBeaver-umbrella.h b/Pods/Target Support Files/SwiftyBeaver/SwiftyBeaver-umbrella.h new file mode 100644 index 0000000..5bb3bbb --- /dev/null +++ b/Pods/Target Support Files/SwiftyBeaver/SwiftyBeaver-umbrella.h @@ -0,0 +1,16 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + + +FOUNDATION_EXPORT double SwiftyBeaverVersionNumber; +FOUNDATION_EXPORT const unsigned char SwiftyBeaverVersionString[]; + diff --git a/Pods/Target Support Files/SwiftyBeaver/SwiftyBeaver.debug.xcconfig b/Pods/Target Support Files/SwiftyBeaver/SwiftyBeaver.debug.xcconfig new file mode 100644 index 0000000..b61fa29 --- /dev/null +++ b/Pods/Target Support Files/SwiftyBeaver/SwiftyBeaver.debug.xcconfig @@ -0,0 +1,14 @@ +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/SwiftyBeaver +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift +OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE} +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/SwiftyBeaver +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Pods/Target Support Files/SwiftyBeaver/SwiftyBeaver.modulemap b/Pods/Target Support Files/SwiftyBeaver/SwiftyBeaver.modulemap new file mode 100644 index 0000000..c217e28 --- /dev/null +++ b/Pods/Target Support Files/SwiftyBeaver/SwiftyBeaver.modulemap @@ -0,0 +1,6 @@ +framework module SwiftyBeaver { + umbrella header "SwiftyBeaver-umbrella.h" + + export * + module * { export * } +} diff --git a/Pods/Target Support Files/SwiftyBeaver/SwiftyBeaver.release.xcconfig b/Pods/Target Support Files/SwiftyBeaver/SwiftyBeaver.release.xcconfig new file mode 100644 index 0000000..b61fa29 --- /dev/null +++ b/Pods/Target Support Files/SwiftyBeaver/SwiftyBeaver.release.xcconfig @@ -0,0 +1,14 @@ +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/SwiftyBeaver +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift +OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE} +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/SwiftyBeaver +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Pods/Target Support Files/TrustDecision/TrustDecision-Info.plist b/Pods/Target Support Files/TrustDecision/TrustDecision-Info.plist new file mode 100644 index 0000000..e3a8af0 --- /dev/null +++ b/Pods/Target Support Files/TrustDecision/TrustDecision-Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + ${PODS_DEVELOPMENT_LANGUAGE} + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.4.0 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/Pods/Target Support Files/TrustDecision/TrustDecision-dummy.m b/Pods/Target Support Files/TrustDecision/TrustDecision-dummy.m new file mode 100644 index 0000000..9a7bc0c --- /dev/null +++ b/Pods/Target Support Files/TrustDecision/TrustDecision-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_TrustDecision : NSObject +@end +@implementation PodsDummy_TrustDecision +@end diff --git a/Pods/Target Support Files/TrustDecision/TrustDecision-prefix.pch b/Pods/Target Support Files/TrustDecision/TrustDecision-prefix.pch new file mode 100644 index 0000000..beb2a24 --- /dev/null +++ b/Pods/Target Support Files/TrustDecision/TrustDecision-prefix.pch @@ -0,0 +1,12 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + diff --git a/Pods/Target Support Files/TrustDecision/TrustDecision-umbrella.h b/Pods/Target Support Files/TrustDecision/TrustDecision-umbrella.h new file mode 100644 index 0000000..025de91 --- /dev/null +++ b/Pods/Target Support Files/TrustDecision/TrustDecision-umbrella.h @@ -0,0 +1,18 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + +#import "TDMobRisk.h" +#import "TDMobRiskManager.h" + +FOUNDATION_EXPORT double TrustDecisionVersionNumber; +FOUNDATION_EXPORT const unsigned char TrustDecisionVersionString[]; + diff --git a/Pods/Target Support Files/TrustDecision/TrustDecision.debug.xcconfig b/Pods/Target Support Files/TrustDecision/TrustDecision.debug.xcconfig new file mode 100644 index 0000000..ca85393 --- /dev/null +++ b/Pods/Target Support Files/TrustDecision/TrustDecision.debug.xcconfig @@ -0,0 +1,12 @@ +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/TrustDecision +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE} +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/TrustDecision +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Pods/Target Support Files/TrustDecision/TrustDecision.modulemap b/Pods/Target Support Files/TrustDecision/TrustDecision.modulemap new file mode 100644 index 0000000..c4bc86d --- /dev/null +++ b/Pods/Target Support Files/TrustDecision/TrustDecision.modulemap @@ -0,0 +1,6 @@ +framework module TrustDecision { + umbrella header "TrustDecision-umbrella.h" + + export * + module * { export * } +} diff --git a/Pods/Target Support Files/TrustDecision/TrustDecision.release.xcconfig b/Pods/Target Support Files/TrustDecision/TrustDecision.release.xcconfig new file mode 100644 index 0000000..ca85393 --- /dev/null +++ b/Pods/Target Support Files/TrustDecision/TrustDecision.release.xcconfig @@ -0,0 +1,12 @@ +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/TrustDecision +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE} +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/TrustDecision +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Pods/TrustDecision/LICENSE b/Pods/TrustDecision/LICENSE new file mode 100644 index 0000000..f849b3c --- /dev/null +++ b/Pods/TrustDecision/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 trustdecision + +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. diff --git a/Pods/TrustDecision/README.md b/Pods/TrustDecision/README.md new file mode 100644 index 0000000..f7d002e --- /dev/null +++ b/Pods/TrustDecision/README.md @@ -0,0 +1,268 @@ +

+ + + + + trustdevice logo + +

+ +

+ +

+ + +# TrustDevice-iOS +A lightweight library for determining device uniqueness and risk identification. + +Create a device identifier based on basic device information. + +Will remain the same after uninstalling and reinstalling or clearing app data. + +## Quick Start + +### 1. Add Dependcy + +TrustDecision is available through [CocoaPods](https://cocoapods.org). To install it, simply add the following line to your Podfile: + +```ruby +# Podfile +pod 'TrustDecision', '1.4' +``` + +### 2. Get DeviceInfo +DeviceInfo contains device id, risk information and device details. + +#### Objective-C + +```objective-c +#import + +TDMobRiskManager_t *manager = [TDMobRiskManager sharedManager]; +NSMutableDictionary *options = [[NSMutableDictionary alloc] init]; +[options setObject:^(NSDictionary *response) { + // Response in sub-thread, do something with the response + // Get DeviceId + NSString *deviceId = response[@"device_id"]; + // Get DeviceRiskLabel + NSDictionary *deviceRisk = response[@"device_risk_label"]; + // Get DeviceDetail + NSDictionary *deviceDetail = response[@"device_detail"]; +} forKey:@"callback"]; +manager->initWithOptions(options); +``` + +#### Swift + +```swift +import TrustDecision + +var options = [String : NSObject]() +let responseCallback: ([String : Any])-> Void = { response in + // Response in sub-thread, do something with the response + // Get DeviceId + let deviceId = response["device_id"] + // Get DeviceRiskLabel + let deviceRisk = response["device_risk_label"] + // Get DeviceDetail + let deviceDetail = response["device_detail"] +} +options["callback"] = unsafeBitCast(responseCallback as @convention(block) ([String : Any]) -> Void, to: AnyObject.self) as? NSObject +let manager = TDMobRiskManager.sharedManager() +manager?.pointee.initWithOptions(options) +``` + +## Data Sample +```json +{ + "device_id":"4b3491c6bb6a27c6c58a038939a10d4cad97555e517574e7bd0291db96243859", + "device_risk_label":{ + "jailbreak":false, + "simulator":false, + "debug":true + }, + "device_detail":{ + "displayResolution":"828.0x1792.0", + "deviceModel":"N104AP", + "kernelVersion":"Darwin Kernel Version 22.6.0: Wed Jun 28 20:51:23 PDT 2023; root:xnu-8796.142.1~1\/RELEASE_ARM64_T8030", + "debug":true, + "sandboxPath":"3CDAE89E-1D51-4DD2-941C-02B2780021FF", + "mcc":"460", + "batteryState":2, + "teamName":"Hangzhou BoDunXiYan Technology Co., Ltd.", + "cloudid":"316A8157-B4E5-4F40-BE37-8115588CA734", + "bundleId":"com.trustdevice.fingerprint", + "appVersion":"1.0", + "timeZone":"Asia\/Shanghai", + "applicationId":"R3F7G5M76J.com.trustdevice.fingerprint", + "freeDiskSpace":4820676608, + "physicalMemory":"8333852672", + "currentTime":1693451898456090, + "totalDiskSpace":127933894656, + "mnc":"02", + "appInstallTime":1679884827203788.8, + "osVersion":"20G75", + "hostName":"ssd", + "osType":"Darwin", + "deviceType":"iPhone", + "displayScale":2, + "idfv":"E5082421-F447-4B45-B453-22C656B56314", + "osRelease":"22.6.0", + "memorySize":"4038885376", + "deviceName":"iOS", + "cpuCount":"9023060809483288582", + "isiOSAppOnMac":0, + "simulator":false, + "jailbreak":false, + "bootTime":1692954854089697, + "secureKernelStatus":true + } +} +``` +## Open Source Features + ++ Basic device ID, consistent when uninstalling applications and reinstalling ++ Basic equipment information, which can be used for simple data analysis ++ Basic risk identification ability + +| RiskLabel | Risk Description | +| --------- | ------------------------------------------------------------ | +| jailbreak | Attackers will have higher privileges and can install many cheating software to affect the normal development of application business. | +| simulator | The simulator provides many simulation functions, which will affect the normal operation of applications, such as virtual positioning. | +| debug | Applications can be modified by attackers at will, and the program will return unexpected values. | + + +## Open Source VS Pro +| Ability | Open Source | Pro | +| :---------------------------: | :---------: | :----------------------------------------------------------: | +| 100% open source | Yes | No | +| Device ID | Basic | Extremely stable, even if the device is restored to factory settings, it can still be recognized as the same | +| Device Risk Label | Basic | Extremely rich | +| Device Details | Basic | Extremely rich | +| IP Location | - | ✓ | +| Device Risk Score | - | ✓ | +| Environment Risk Evaluation | - | ✓ | +| Fraud Tools Detection | - | ✓ | +| Behavioral Activity Capturing | - | ✓ | + +## Pro Introduction + +TrustDecision TrustDevice has the leading device fingerprint technology, which has been integrated by more than 10000 global leading brands, protecting the entire customer journey. + +**There are 6 leading core features about TrustDevice Pro:** + +### 1. Wide Coverage +Comprehensive coverage of Android, iOS, Web, H5, applets and other device types. + +### 2. Stable and Reliable +TrustDevice served more than 10,000 clients, 200 million+ daily active users , and 6 billion+ devices , with excellent product functions and stability. +The fingerprint accuracy of different terminal devices exceeded 99.9%, and the output of risk labels exceeded 70 items. + +### 3. Unparalleled Safety +TrustDevice's code virtualization & obfuscation technology make the malware fraudsters suffer from painful cost and imprecision when performing reverse-engineering. + +### 4. Core Intellectual Property +Fully independent intellectual property rights, with a number of patented technology. + +### 5. Security Compliance +TrustDevice is committed to the highest standards in security and compliance to keep your data safe. +GDPR/CCA/PCI DSS/ISO 27701/ISO 9001 Compliant. + +### 6. Easy to Deploy +SaaS(Software as a Service)deployment supported, reducing massively your integration cost and enabling rapid access to device fingerprint service. + +## Where to Get Support +We are happy to provide technical support for our open-source trustdevice-ios library. We recommend using GitHub Issues to submit bugs or Discussions to ask questions. Using [Issues](https://github.com/trustdecision/trustdevice-ios/issues) and [Discussions](https://github.com/trustdecision/trustdevice-ios/discussions) publicly will help the open-source community and other users with similar issues. + +In addition, any idea or interest in using TrustDevice Pro can be found on the [www.trustdecision.com](https://www.trustdecision.com/trustDevice), registered account for a free trial; or via email trustdevice@trustdecision.com contact us directly and quickly open the service. + +## Compatibility +| Items | Description | +| :-----------------------: | :------------------: | +| Supported System Versions | iOS9.0+ | +| Supported Architecture | armv7, arm64, x86_64 | + +## TrustDevice Pro VS Others + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeSceneResultTrusDevice ProFingerprintSeonCredoLabSiftShieldPerimeterX
Device Fingerprint CompatibilityiOS 9.0 and above, including iOS 16, etc.Able to collect device info and generate device ID✅(ios12 and above)✅(ios9 and above)✅(ios11 and above)✅(ios9.2 and above)✅(ios9 and above)✅(ios11 and above)
Device fingerprint uniquenessDifferent apps (with different package names) on the same deviceDevice fingerprints/ID matches
The same app on two unique devices (including the case of the same device model and the same system version)Device fingerprint/ID should not match. Each device to have its own unique device fingeprint/ID
Device Fingerprint StabilityUninstall and reinstallDevice fingerprints/ID are consistent before and after reinstallation
Clear all app dataDevice fingerprints/ID are consistent before and after clearing all app data
Disable all app permissions except network permissions and clear app dataDevice fingerprints/ID are consistent before and after disabling all permissions
Modify the common information of the device system (brand, model, IDFA, IDFV, etc.) through the machine modification toolDevice fingerprint/ID still matches with before modification
Device system upgradeDevice fingerprints/ID are consistent before and after system upgrade
Factory reset (including iOS 14 and above, etc.)The device fingerprints/ID are the same before and after the device is restored to factory settings
Device Fingerprint Risk IdentificationSecondary packagingAbility to identify secondary packaged unofficial apps
Replay attackAbility to identify replay attacks
Device location information has been tampered withAbility to identify tampering of location information-
No SIM card insertedCan identify whether the device is inserted with a SIM card
The device uses an HTTP proxyCan identify HTTP proxy risks
The device uses a VPN proxyCan identify VPN proxy risks
JailbreakAbility to identify jailbreak risks
The device is an emulatorAbility to recognize emulators
The device uses the machine modification tool to modify the device parameter informationAbility to identify mainstream modification tools
The device has scripts and group control tools installed (tools that are used to control multiple devices from single terminal - device farm/device group)Can identify mainstream scripting/group control tools
The device is equipped with an M chipDevices capable of recognizing M-chips-
The device is not logged into the iCloud accountAble to identify login iCloud account
Security and StabilityCode ProtectionThe device fingerprint SDK and JS have code protection mechanisms (such as VMP), which effectively resist black hat cracking attempts to manipulate code logic and falsify data✅ (OLLVM, VMP)
DowngradeIntercepting and sending collection requests in the iOS environment can still generate device fingerprints normally
Anti packet captureiOS has the ability to prevent packet capture
+ + +## License + +This library is MIT licensed. Copyright trustdecision, Inc. 2022. diff --git a/Pods/TrustDecision/TrustDecision/Classes/Collect/Info/Base/TDMobRiskBaseInfo.h b/Pods/TrustDecision/TrustDecision/Classes/Collect/Info/Base/TDMobRiskBaseInfo.h new file mode 100644 index 0000000..9a18c88 --- /dev/null +++ b/Pods/TrustDecision/TrustDecision/Classes/Collect/Info/Base/TDMobRiskBaseInfo.h @@ -0,0 +1,13 @@ +// +// TDMobRiskBaseInfo.h +// TDMobRisk +// +// Created by zeinber on 2022/12/7. +// + +#import + +@interface TDMobRiskBaseInfo : NSObject +/// Get current json dictionary of object +- (NSDictionary *)getJsonDict; +@end diff --git a/Pods/TrustDecision/TrustDecision/Classes/Collect/Info/Base/TDMobRiskBaseInfo.m b/Pods/TrustDecision/TrustDecision/Classes/Collect/Info/Base/TDMobRiskBaseInfo.m new file mode 100644 index 0000000..b023496 --- /dev/null +++ b/Pods/TrustDecision/TrustDecision/Classes/Collect/Info/Base/TDMobRiskBaseInfo.m @@ -0,0 +1,46 @@ +// +// TDMobRiskBaseInfo.m +// TDMobRisk +// +// Created by zeinber on 2022/12/7. +// + +#import "TDMobRiskBaseInfo.h" +#import + +@implementation TDMobRiskBaseInfo +#pragma mark - Initialization Methods +- (instancetype)init { + if (self = [super init]) { + [self getInfo]; + } + return self; +} + +#pragma mark - Base Methods +- (void)getInfo { + +} + +- (NSDictionary *)getJsonDict { + NSMutableDictionary *jsonDict = [[NSMutableDictionary alloc] init]; + unsigned int propertyCount = 0; + objc_property_t *propertyList = class_copyPropertyList([self class], &propertyCount); + for (int i = 0; i < propertyCount; i++) { + objc_property_t property = propertyList[i]; + const char *propertyCName = property_getName(property); + NSString *propertyName = [NSString stringWithUTF8String:propertyCName]; + id propertyValue = [self valueForKey:propertyName]; + if (propertyName && propertyValue) { + [jsonDict setObject:propertyValue forKey:propertyName]; + }else { +#ifdef DEBUG + NSLog(@"[%s] - [Error] - propertyName[%@] or propertyValue[%@] is nil",__func__,propertyName,propertyValue); +#endif + } + } + free(propertyList); + return jsonDict; +} + +@end diff --git a/Pods/TrustDecision/TrustDecision/Classes/Collect/Info/TDMobRiskAppInfo.h b/Pods/TrustDecision/TrustDecision/Classes/Collect/Info/TDMobRiskAppInfo.h new file mode 100644 index 0000000..bff8349 --- /dev/null +++ b/Pods/TrustDecision/TrustDecision/Classes/Collect/Info/TDMobRiskAppInfo.h @@ -0,0 +1,24 @@ +// +// TDMobRiskAppInfo.h +// TDMobRisk +// +// Created by zeinber on 2022/12/7. +// + +#import "TDMobRiskBaseInfo.h" + +@interface TDMobRiskAppInfo : TDMobRiskBaseInfo +/** App Info **/ +/// bundleId +@property (nonatomic, copy) NSString *bundleId; +/// appVersion +@property (nonatomic, copy) NSString *appVersion; +/// applicationId +@property (nonatomic, copy) NSString *applicationId; +/// teamName +@property (nonatomic, copy) NSString *teamName; +/// sandboxPath +@property (nonatomic, copy) NSString *sandboxPath; +/// appInstallTime +@property (nonatomic, assign) NSTimeInterval appInstallTime; +@end diff --git a/Pods/TrustDecision/TrustDecision/Classes/Collect/Info/TDMobRiskAppInfo.m b/Pods/TrustDecision/TrustDecision/Classes/Collect/Info/TDMobRiskAppInfo.m new file mode 100644 index 0000000..55e4eb2 --- /dev/null +++ b/Pods/TrustDecision/TrustDecision/Classes/Collect/Info/TDMobRiskAppInfo.m @@ -0,0 +1,126 @@ +// +// TDMobRiskAppInfo.m +// TDMobRisk +// +// Created by zeinber on 2022/12/7. +// + +#import "TDMobRiskAppInfo.h" +#import "TDMobRiskAPIHelper.h" +#import + +@implementation TDMobRiskAppInfo +#pragma mark - Initialization Methods +- (instancetype)init { + if (self = [super init]) { + [self getInfo]; + } + return self; +} +#pragma mark - Collect Methods +- (void)getInfo { + // bundleId + { + NSString *bundleId = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleIdentifier"]; + _bundleId = bundleId; + } + + // appVersion + { + NSString *appVersion = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"]; + _appVersion = appVersion; + } + + // teamName, applicationId + { + NSString *applicationId = nil; + NSString *teamName = nil; + NSString *embeddedPath = [[NSBundle mainBundle] pathForResource:@"embedded" ofType:@"mobileprovision"]; + const char *embeddedPath_c = [embeddedPath UTF8String]; + FILE *fp = fopen(embeddedPath_c, "r"); + if (fp) { + fseek(fp, 0L, SEEK_END); + uint64_t sz = ftell(fp); + rewind(fp); + char *mobileProvisionContent = malloc(sz); + memset(mobileProvisionContent, 0, sz); + fread(mobileProvisionContent,sz,1, fp); + + char *plistBeginTag = " plistBeginTagPos) { + uint64_t plistContent_len = plistEndTagPos - plistBeginTagPos; + NSData *data = [NSData dataWithBytes:plistContent length:plistContent_len]; + NSError *error; + NSDictionary * plistDic = [NSPropertyListSerialization propertyListWithData:data options:NSPropertyListWriteStreamError format:nil error:&error]; + + if ([plistDic isKindOfClass:[NSDictionary class]]) { + teamName = plistDic[@"TeamName"]; + NSDictionary *entitlementsDic = plistDic[@"Entitlements"]; + if ([entitlementsDic isKindOfClass:[NSDictionary class]]) { + applicationId = entitlementsDic[@"application-identifier"]; + } + } + } + if (mobileProvisionContent) { + free(mobileProvisionContent); + } + if (fp) { + fclose(fp); + } + } + + _applicationId = applicationId; + _teamName = teamName; + } + + // sandboxPath + { + NSString *sandboxPath = [NSHomeDirectory() lastPathComponent]; + _sandboxPath = sandboxPath; + } + + // appInstallTime + { + NSString *appDocumentsPath = [[[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject] path]; + NSDictionary *appDocumentsDict = [[NSFileManager defaultManager] attributesOfItemAtPath:appDocumentsPath error:nil]; + NSDate *appDocumentsCreationDate = appDocumentsDict[NSFileCreationDate]; + NSTimeInterval appInstallTime = [appDocumentsCreationDate timeIntervalSince1970] * 1000000; + _appInstallTime = appInstallTime; + } +} +@end diff --git a/Pods/TrustDecision/TrustDecision/Classes/Collect/Info/TDMobRiskCpuInfo.h b/Pods/TrustDecision/TrustDecision/Classes/Collect/Info/TDMobRiskCpuInfo.h new file mode 100644 index 0000000..6231142 --- /dev/null +++ b/Pods/TrustDecision/TrustDecision/Classes/Collect/Info/TDMobRiskCpuInfo.h @@ -0,0 +1,14 @@ +// +// TDMobRiskCpuInfo.h +// TDMobRisk +// +// Created by zeinber on 2022/12/7. +// + +#import "TDMobRiskBaseInfo.h" + +@interface TDMobRiskCpuInfo : TDMobRiskBaseInfo +/** Cpu Info **/ +/// cpuCount +@property (nonatomic, copy) NSString *cpuCount; +@end diff --git a/Pods/TrustDecision/TrustDecision/Classes/Collect/Info/TDMobRiskCpuInfo.m b/Pods/TrustDecision/TrustDecision/Classes/Collect/Info/TDMobRiskCpuInfo.m new file mode 100644 index 0000000..c04e935 --- /dev/null +++ b/Pods/TrustDecision/TrustDecision/Classes/Collect/Info/TDMobRiskCpuInfo.m @@ -0,0 +1,20 @@ +// +// TDMobRiskCpuInfo.m +// TDMobRisk +// +// Created by zeinber on 2022/12/7. +// + +#import "TDMobRiskCpuInfo.h" +#import "TDMobRiskAPIHelper.h" + +@implementation TDMobRiskCpuInfo +#pragma mark - Collect Methods +- (void)getInfo { + // cpuCount + { + NSString *cpuCount = [TDMobRiskAPIHelper sysctlByName:"hw.ncpu" isObject:NO]; + _cpuCount = cpuCount; + } +} +@end diff --git a/Pods/TrustDecision/TrustDecision/Classes/Collect/Info/TDMobRiskDeviceInfo.h b/Pods/TrustDecision/TrustDecision/Classes/Collect/Info/TDMobRiskDeviceInfo.h new file mode 100644 index 0000000..3828c54 --- /dev/null +++ b/Pods/TrustDecision/TrustDecision/Classes/Collect/Info/TDMobRiskDeviceInfo.h @@ -0,0 +1,32 @@ +// +// TDMobRiskDeviceInfo.h +// TDMobRisk +// +// Created by zeinber on 2022/12/7. +// + +#import "TDMobRiskBaseInfo.h" + +@interface TDMobRiskDeviceInfo : TDMobRiskBaseInfo +/** deviceInfo */ +/// deviceType +@property (nonatomic, copy) NSString *deviceType; +/// displayResolution +@property (nonatomic, copy) NSString *displayResolution; +/// displayScale +@property (nonatomic, assign) double displayScale; +/// deviceModel +@property (nonatomic, copy) NSString *deviceModel; +/// deviceName +@property (nonatomic, copy) NSString *deviceName; +/// hostName +@property (nonatomic, copy) NSString *hostName; +/// timeZone +@property (nonatomic, copy) NSString *timeZone; +/// batteryState +@property (nonatomic, assign) NSInteger batteryState; +/// Mobile Network Code +@property (nonatomic, copy) NSString *mnc; +/// Mobile Country Code +@property (nonatomic, copy) NSString *mcc; +@end diff --git a/Pods/TrustDecision/TrustDecision/Classes/Collect/Info/TDMobRiskDeviceInfo.m b/Pods/TrustDecision/TrustDecision/Classes/Collect/Info/TDMobRiskDeviceInfo.m new file mode 100644 index 0000000..dc9c765 --- /dev/null +++ b/Pods/TrustDecision/TrustDecision/Classes/Collect/Info/TDMobRiskDeviceInfo.m @@ -0,0 +1,95 @@ +// +// TDMobRiskDeviceInfo.m +// TDMobRisk +// +// Created by zeinber on 2022/12/7. +// + +#import "TDMobRiskDeviceInfo.h" +#import "TDMobRiskAPIHelper.h" +#import +#import +#import +#import + +@implementation TDMobRiskDeviceInfo +#pragma mark - Collect Methods +- (void)getInfo { + // deviceType + { + UIDevice *device = [UIDevice currentDevice]; + NSString *deviceType = device.model; + _deviceType = deviceType; + } + + // displayResolution + { + UIScreen *screen = [UIScreen mainScreen]; + CGRect nativeBounds = [screen nativeBounds]; + NSString *displayResolution = [NSString stringWithFormat:@"%.1lfx%.1lf",nativeBounds.size.width,nativeBounds.size.height]; + _displayResolution = displayResolution; + } + + // displayScale + { + UIScreen *screen = [UIScreen mainScreen]; + double displayScale = [screen nativeScale]; + _displayScale = displayScale; + } + + // deviceModel + { + NSString *deviceModel = [TDMobRiskAPIHelper sysctlByName:"hw.model" isObject:YES]; + _deviceModel = deviceModel; + } + + // deviceName + { + NSString *deviceName = [[UIDevice currentDevice] systemName]; + _deviceName = deviceName; + } + + // hostName + { + size_t size = 0; + const char *name = "kern.hostname"; + sysctlbyname(name , 0LL, &size, 0, 0); + void *buf = malloc(size); + sysctlbyname(name, buf, &size, 0, 0); + NSString *hostName; + if(buf) { + hostName = [NSString stringWithCString:buf encoding:NSUTF8StringEncoding]; + free(buf); + } + _hostName = hostName; + } + + // timeZone + { + NSString *timeZone = [[NSTimeZone localTimeZone] name]; + _timeZone = timeZone; + } + + // batteryState + { + UIDevice *currentDevice = [UIDevice currentDevice]; + if (!currentDevice.isBatteryMonitoringEnabled) { + currentDevice.batteryMonitoringEnabled = true; + } + _batteryState = [currentDevice batteryState]; + } + + // mnc + { + CTTelephonyNetworkInfo *networkInfo = [[CTTelephonyNetworkInfo alloc] init]; + _mnc = [[networkInfo subscriberCellularProvider] mobileNetworkCode]; + } + + // mcc + { + CTTelephonyNetworkInfo *networkInfo = [[CTTelephonyNetworkInfo alloc] init]; + _mcc = [[networkInfo subscriberCellularProvider] mobileCountryCode]; + } +} + +@end diff --git a/Pods/TrustDecision/TrustDecision/Classes/Collect/Info/TDMobRiskDeviceStatusInfo.h b/Pods/TrustDecision/TrustDecision/Classes/Collect/Info/TDMobRiskDeviceStatusInfo.h new file mode 100644 index 0000000..3d8a4b1 --- /dev/null +++ b/Pods/TrustDecision/TrustDecision/Classes/Collect/Info/TDMobRiskDeviceStatusInfo.h @@ -0,0 +1,22 @@ +// +// TDMobRiskDeviceStatusInfo.h +// TDMobRisk +// +// Created by zeinber on 2022/12/7. +// + +#import "TDMobRiskBaseInfo.h" + +@interface TDMobRiskDeviceStatusInfo : TDMobRiskBaseInfo +/** Device Status Info **/ +/// jailbreak +@property (nonatomic, assign) BOOL jailbreak; +/// debug +@property (nonatomic, assign) BOOL debug; +/// simulator +@property (nonatomic, assign) BOOL simulator; +/// secureKernelStatus +@property (nonatomic, assign) BOOL secureKernelStatus; +/// isiOSAppOnMac +@property (nonatomic, assign) int isiOSAppOnMac; +@end diff --git a/Pods/TrustDecision/TrustDecision/Classes/Collect/Info/TDMobRiskDeviceStatusInfo.m b/Pods/TrustDecision/TrustDecision/Classes/Collect/Info/TDMobRiskDeviceStatusInfo.m new file mode 100644 index 0000000..ff6e34f --- /dev/null +++ b/Pods/TrustDecision/TrustDecision/Classes/Collect/Info/TDMobRiskDeviceStatusInfo.m @@ -0,0 +1,160 @@ +// +// TDMobRiskDeviceStatusInfo.m +// TDMobRisk +// +// Created by zeinber on 2022/12/7. +// + +#import "TDMobRiskDeviceStatusInfo.h" +#import +#import +#import +#import +#import +#import +#import +#import + +@implementation TDMobRiskDeviceStatusInfo +#pragma mark - Collect Methods +- (void)getInfo { + // jailbreak + { + BOOL jailbreak = [self isJailbreak]; + _jailbreak = jailbreak; + } + + // debug + { + int debug = [self checkdebug]; + _debug = debug; + } + + // simulator + { + int simulator = [self isSimulator]; + _simulator = simulator; + } + + // secureKernelStatus + { + int value = 0; + size_t length = sizeof(value); + if (sysctlbyname("kern.secure_kernel", &value, &length, NULL, 0) == 0) { + _secureKernelStatus = (value != 0); + } + } + + // isiOSAppOnMac + { + int isiOSAppOnMac = -1; + if (@available(iOS 14.0, *)) { + isiOSAppOnMac = [[NSProcessInfo processInfo] isiOSAppOnMac]; + } + _isiOSAppOnMac =isiOSAppOnMac; + } +} + +#pragma mark - Private Collect Methods +- (int)isJailbreak { + NSArray *jailBreakFileList = @[ + @"/Applications/Cydia.app", + @"/private/var/lib/apt/", + @"/User/Applications/", + @"/Library/MobileSubstrate/MobileSubstrate.dylib", + @"/bin/bash", + @"/usr/sbin/sshd", + @"/etc/apt", + @"/var/jb" + ]; + for (NSString *filePath in jailBreakFileList) { + if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) { + return YES; + } + } + char *env = getenv("DYLD_INSERT_LIBRARIES"); + if (env != NULL) { + return YES; + } + if (!access("/Library/MobileSubstrate/DynamicLibraries/",R_OK)) { + return YES; + } + //symlink verification check + NSArray *symlinkList = @[ + @"/Applications", + @"/var/stash/Library/Ringtones", + @"/var/stash/Library/Wallpaper", + @"/var/stash/usr/include", + @"/var/stash/usr/libexec", + @"/var/stash/usr/share", + @"/var/stash/usr/arm-apple-darwin9", + @"/bin/bash" + ]; + for (NSString *symlinkPath in symlinkList) { + struct stat sym; + int f = lstat(symlinkPath.UTF8String, &sym); + if (f == 0 && (sym.st_mode & S_IFLNK)) { + return YES; + } + } + return NO; +} + +- (int)checkdebug { + int debug = -1; + // check sysctl antidebug + struct kinfo_proc info; + size_t size; + info.kp_proc.p_flag = 0; + int mib[4]; + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PID; + mib[3] = getpid(); + size = sizeof(info); + int status = sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0); + if (status == 0) { + debug = (info.kp_proc.p_flag & P_TRACED) != 0; + } + // check isatty antidebug + if (debug < 1) { + debug = isatty(1); + } + return debug; +} + +- (int)isSimulator { + int simulator = -1; + { +#if TARGET_IPHONE_SIMULATOR + simulator = 1; +#else + simulator = 0; +#endif + } + if (!simulator) { + uint64_t dyld_simulator = 0; + uint32_t dyld_count = _dyld_image_count(); + NSString* dyld_sim_str = @"usr/lib/dyld_sim"; + NSString* CoreSimulator_str = @"Library/Developer/CoreSimulator"; + char* cEmptyString = ""; + for (uint32_t i = 0; i < dyld_count; i++) { + @autoreleasepool { + if(i > 10000) + break; + NSString *dyld_image_name = [NSString stringWithCString:_dyld_get_image_name(i) ?: cEmptyString encoding:NSUTF8StringEncoding]; + //dyld_simulator + if ([dyld_image_name hasSuffix:dyld_sim_str]) { + dyld_simulator |= 1; + } + if ([dyld_image_name containsString:CoreSimulator_str]) { + dyld_simulator |= 1<<1; + } + } + } + simulator = (dyld_simulator ? 1 : 0); + } + return simulator; + +} +@end diff --git a/Pods/TrustDecision/TrustDecision/Classes/Collect/Info/TDMobRiskIdentifierInfo.h b/Pods/TrustDecision/TrustDecision/Classes/Collect/Info/TDMobRiskIdentifierInfo.h new file mode 100644 index 0000000..23ad248 --- /dev/null +++ b/Pods/TrustDecision/TrustDecision/Classes/Collect/Info/TDMobRiskIdentifierInfo.h @@ -0,0 +1,17 @@ +// +// TDMobRiskIdentifierInfo.h +// TDMobRisk +// +// Created by zeinber on 2022/12/6. +// + +#import "TDMobRiskBaseInfo.h" + +@interface TDMobRiskIdentifierInfo : TDMobRiskBaseInfo +/** Identifier */ +/// idfv +@property (nonatomic, copy) NSString *idfv; + +/// cloudid +@property (nonatomic, copy) NSString *cloudid; +@end diff --git a/Pods/TrustDecision/TrustDecision/Classes/Collect/Info/TDMobRiskIdentifierInfo.m b/Pods/TrustDecision/TrustDecision/Classes/Collect/Info/TDMobRiskIdentifierInfo.m new file mode 100644 index 0000000..afa8732 --- /dev/null +++ b/Pods/TrustDecision/TrustDecision/Classes/Collect/Info/TDMobRiskIdentifierInfo.m @@ -0,0 +1,37 @@ +// +// TDMobRiskIdentifierInfo.m +// TDMobRisk +// +// Created by zeinber on 2022/12/6. +// + +#import "TDMobRiskIdentifierInfo.h" +#import "TDMobRiskKeychainsHelper.h" +#import +#import + +@implementation TDMobRiskIdentifierInfo +#pragma mark - Collect Methods +- (void)getInfo { + // idfv + { + NSString *idfvCache = [TDMobRiskKeychainsHelper loadValueForKey:@"idfv"]; + // Read the keychains cache first. If the cache does not exist, regenerate the IDFV and write it to the keychains cache + if (!idfvCache || idfvCache.length == 0) { + UIDevice *device = [UIDevice currentDevice]; + idfvCache = [[device identifierForVendor] UUIDString]; + } + _idfv = idfvCache; + } + + //cloudid need to iCloud Key-Value storage Capability + NSString *cloudid = [[NSUbiquitousKeyValueStore defaultStore] stringForKey:@"td_cloudid"]; + if(!cloudid) { + cloudid = [NSUUID UUID].UUIDString; + [[NSUbiquitousKeyValueStore defaultStore] setString:cloudid forKey:@"td_cloudid"]; + [[NSUbiquitousKeyValueStore defaultStore] synchronize]; + } + _cloudid = cloudid; + +} +@end diff --git a/Pods/TrustDecision/TrustDecision/Classes/Collect/Info/TDMobRiskOSInfo.h b/Pods/TrustDecision/TrustDecision/Classes/Collect/Info/TDMobRiskOSInfo.h new file mode 100644 index 0000000..4b73196 --- /dev/null +++ b/Pods/TrustDecision/TrustDecision/Classes/Collect/Info/TDMobRiskOSInfo.h @@ -0,0 +1,20 @@ +// +// TDMobRiskOSInfo.h +// TDMobRisk +// +// Created by zeinber on 2022/12/7. +// + +#import "TDMobRiskBaseInfo.h" + +@interface TDMobRiskOSInfo : TDMobRiskBaseInfo +/** OS Info **/ +/// osVersion +@property (nonatomic, copy) NSString *osVersion; +/// osType +@property (nonatomic, copy) NSString *osType; +/// osRelease +@property (nonatomic, copy) NSString *osRelease; +/// kernelVersion +@property (nonatomic, copy) NSString *kernelVersion; +@end diff --git a/Pods/TrustDecision/TrustDecision/Classes/Collect/Info/TDMobRiskOSInfo.m b/Pods/TrustDecision/TrustDecision/Classes/Collect/Info/TDMobRiskOSInfo.m new file mode 100644 index 0000000..e4b65e5 --- /dev/null +++ b/Pods/TrustDecision/TrustDecision/Classes/Collect/Info/TDMobRiskOSInfo.m @@ -0,0 +1,40 @@ +// +// TDMobRiskOSInfo.m +// TDMobRisk +// +// Created by zeinber on 2022/12/7. +// + +#import "TDMobRiskOSInfo.h" +#import "TDMobRiskAPIHelper.h" +#import + + +@implementation TDMobRiskOSInfo +#pragma mark - Collect Methods +- (void)getInfo { + // osVersion + { + NSString *osVersion = [TDMobRiskAPIHelper sysctlByName:"kern.osversion" isObject:YES]; + _osVersion = osVersion; + } + + // osType + { + NSString *osType = [TDMobRiskAPIHelper sysctlByName:"kern.ostype" isObject:YES]; + _osType = osType; + } + + // osRelease + { + NSString *osRelease = [TDMobRiskAPIHelper sysctlByName:"kern.osrelease" isObject:YES]; + _osRelease = osRelease; + } + + // kernelVersion + { + NSString *kernelVersion = [TDMobRiskAPIHelper sysctlByName:"kern.version" isObject:YES]; + _kernelVersion = kernelVersion; + } +} +@end diff --git a/Pods/TrustDecision/TrustDecision/Classes/Collect/Info/TDMobRiskSpaceInfo.h b/Pods/TrustDecision/TrustDecision/Classes/Collect/Info/TDMobRiskSpaceInfo.h new file mode 100644 index 0000000..8954ab5 --- /dev/null +++ b/Pods/TrustDecision/TrustDecision/Classes/Collect/Info/TDMobRiskSpaceInfo.h @@ -0,0 +1,22 @@ +// +// TDMobRiskSpaceInfo.h +// TDMobRisk +// +// Created by zeinber on 2022/12/6. +// + +#import "TDMobRiskBaseInfo.h" + +@interface TDMobRiskSpaceInfo : TDMobRiskBaseInfo + +/** spaceInfo */ +/// freeDiskSpace +@property (nonatomic, assign) long long freeDiskSpace; +/// totalDiskSpace +@property (nonatomic, assign) long long totalDiskSpace; +/// memorySize(ram size) +@property (nonatomic, copy) NSString *memorySize; +/// physicalMemory +@property (nonatomic, copy) NSString *physicalMemory; + +@end diff --git a/Pods/TrustDecision/TrustDecision/Classes/Collect/Info/TDMobRiskSpaceInfo.m b/Pods/TrustDecision/TrustDecision/Classes/Collect/Info/TDMobRiskSpaceInfo.m new file mode 100644 index 0000000..14edf9a --- /dev/null +++ b/Pods/TrustDecision/TrustDecision/Classes/Collect/Info/TDMobRiskSpaceInfo.m @@ -0,0 +1,62 @@ +// +// TDMobRiskSpaceInfo.m +// TDMobRisk +// +// Created by zeinber on 2022/12/6. +// + +#import "TDMobRiskSpaceInfo.h" +#import "TDMobRiskAPIHelper.h" +#import + +@implementation TDMobRiskSpaceInfo +#pragma mark - Collect Methods +- (void)getInfo { + // totalDiskSpace + { + long long totalDiskSpace = -1; + NSError *error = nil; + NSArray *docPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); + NSDictionary *docDict = [[NSFileManager defaultManager] attributesOfFileSystemForPath:[docPaths lastObject] error:&error]; + if (docDict) { + NSNumber *systemSize = [docDict objectForKey:NSFileSystemSize]; + totalDiskSpace = [systemSize unsignedLongLongValue]; + }else { +#ifdef DEBUG + NSLog(@"[%s] - [Error] - %@ ",__func__, error); +#endif + } + _totalDiskSpace = totalDiskSpace; + } + + // freeDiskSpace + { + long long freeDiskSpace = -1; + NSError *error = nil; + NSArray *docPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); + NSDictionary *docDict = [[NSFileManager defaultManager] attributesOfFileSystemForPath:[docPaths lastObject] error:&error]; + if (docDict) { + NSNumber *systemFreeSize = [docDict objectForKey:NSFileSystemFreeSize]; + freeDiskSpace = [systemFreeSize unsignedLongLongValue]; + }else { +#ifdef DEBUG + NSLog(@"[%s] - [Error] - %@ ",__func__, error); +#endif + } + _freeDiskSpace = freeDiskSpace; + } + + // memorySize + { + NSString *memorySize = [TDMobRiskAPIHelper sysctlByName:"hw.memsize" isObject:NO]; + _memorySize = memorySize; + } + + // physicalMemory + { + NSString *physicalMemory = [TDMobRiskAPIHelper sysctlByName:"hw.physmem" isObject:NO]; + _physicalMemory = physicalMemory; + } +} + +@end diff --git a/Pods/TrustDecision/TrustDecision/Classes/Collect/Info/TDMobRiskTimeInfo.h b/Pods/TrustDecision/TrustDecision/Classes/Collect/Info/TDMobRiskTimeInfo.h new file mode 100644 index 0000000..683e6b3 --- /dev/null +++ b/Pods/TrustDecision/TrustDecision/Classes/Collect/Info/TDMobRiskTimeInfo.h @@ -0,0 +1,16 @@ +// +// TDMobRiskTimeInfo.h +// TDMobRisk +// +// Created by zeinber on 2022/12/7. +// + +#import "TDMobRiskBaseInfo.h" + +@interface TDMobRiskTimeInfo : TDMobRiskBaseInfo +/** timeInfo */ +/// currentTime, unit is microsecond +@property (nonatomic, assign) NSTimeInterval currentTime; +/// bootTime, unit is microsecond +@property (nonatomic, assign) NSTimeInterval bootTime; +@end diff --git a/Pods/TrustDecision/TrustDecision/Classes/Collect/Info/TDMobRiskTimeInfo.m b/Pods/TrustDecision/TrustDecision/Classes/Collect/Info/TDMobRiskTimeInfo.m new file mode 100644 index 0000000..b01cd70 --- /dev/null +++ b/Pods/TrustDecision/TrustDecision/Classes/Collect/Info/TDMobRiskTimeInfo.m @@ -0,0 +1,34 @@ +// +// TDMobRiskTimeInfo.m +// TDMobRisk +// +// Created by zeinber on 2022/12/7. +// + +#import "TDMobRiskTimeInfo.h" +#import "TDMobRiskAPIHelper.h" +#import + +@implementation TDMobRiskTimeInfo +#pragma mark - Collect Methods +- (void)getInfo { + // currentTime + { + NSDate *date = [NSDate date]; + NSTimeInterval currentTime = [date timeIntervalSince1970] * 1000000; + _currentTime = currentTime; + } + + // bootTime + { + struct timeval bootTime_t; + size_t size = sizeof(bootTime_t); + int result = sysctlbyname("kern.boottime", &bootTime_t, &size, NULL, 0); + NSTimeInterval bootTime = 0; + if (result == 0) { + bootTime = bootTime_t.tv_sec *(long long)1000000 + bootTime_t.tv_usec; + } + _bootTime = bootTime; + } +} +@end diff --git a/Pods/TrustDecision/TrustDecision/Classes/Collect/TDMobRiskCollector.h b/Pods/TrustDecision/TrustDecision/Classes/Collect/TDMobRiskCollector.h new file mode 100644 index 0000000..813877d --- /dev/null +++ b/Pods/TrustDecision/TrustDecision/Classes/Collect/TDMobRiskCollector.h @@ -0,0 +1,14 @@ +// +// TDMobRiskCollector.h +// TDMobRisk +// +// Created by zeinber on 2022/12/6. +// + +#import + +@interface TDMobRiskCollector : NSObject +/// Get CollectData ++ (NSDictionary *)getCollectInfo; +@end + diff --git a/Pods/TrustDecision/TrustDecision/Classes/Collect/TDMobRiskCollector.m b/Pods/TrustDecision/TrustDecision/Classes/Collect/TDMobRiskCollector.m new file mode 100644 index 0000000..70439e3 --- /dev/null +++ b/Pods/TrustDecision/TrustDecision/Classes/Collect/TDMobRiskCollector.m @@ -0,0 +1,62 @@ +// +// TDMobRiskCollector.m +// TDMobRisk +// +// Created by zeinber on 2022/12/6. +// + +#import "TDMobRiskCollector.h" +#import "TDMobRiskIdentifierInfo.h" +#import "TDMobRiskOSInfo.h" +#import "TDMobRiskCpuInfo.h" +#import "TDMobRiskAppInfo.h" +#import "TDMobRiskDeviceStatusInfo.h" +#import "TDMobRiskSpaceInfo.h" +#import "TDMobRiskDeviceInfo.h" +#import "TDMobRiskTimeInfo.h" +#import "TDMobRiskIdCalculator.h" +#import "TDMobRiskHeader.h" + +@implementation TDMobRiskCollector +#pragma mark - Output Methods +/// Get CollectData ++ (NSDictionary *)getCollectInfo { + /** Collect Data */ + // IdentifierInfo + TDMobRiskIdentifierInfo *identifierInfo = [[TDMobRiskIdentifierInfo alloc] init]; + // OSInfo + TDMobRiskOSInfo *osInfo = [[TDMobRiskOSInfo alloc] init]; + // CpuInfo + TDMobRiskCpuInfo *cpuInfo = [[TDMobRiskCpuInfo alloc] init]; + // AppInfo + TDMobRiskAppInfo *appInfo = [[TDMobRiskAppInfo alloc] init]; + // DeviceStatusInfo + TDMobRiskDeviceStatusInfo *deviceStatusInfo = [[TDMobRiskDeviceStatusInfo alloc] init]; + // SpaceInfo + TDMobRiskSpaceInfo *spaceInfo = [[TDMobRiskSpaceInfo alloc] init]; + // DeviceInfo + TDMobRiskDeviceInfo *deviceInfo = [[TDMobRiskDeviceInfo alloc] init]; + // TimeInfo + TDMobRiskTimeInfo *timeInfo = [[TDMobRiskTimeInfo alloc] init]; + + /* Transfer the collected data to json **/ + NSArray *collectTypeObjArray = @[ + identifierInfo, + osInfo, + cpuInfo, + appInfo, + deviceStatusInfo, + spaceInfo, + deviceInfo, + timeInfo + ]; + NSMutableDictionary *collectDataDict = [[NSMutableDictionary alloc] init]; + for (TDMobRiskBaseInfo *info in collectTypeObjArray) { + NSDictionary *infoDict = [info getJsonDict]; + if (infoDict && [infoDict isKindOfClass:[NSDictionary class]]) { + [collectDataDict setValuesForKeysWithDictionary:infoDict]; + } + } + return collectDataDict; +} +@end diff --git a/Pods/TrustDecision/TrustDecision/Classes/Containers/TDMobRiskSafeDictionary.h b/Pods/TrustDecision/TrustDecision/Classes/Containers/TDMobRiskSafeDictionary.h new file mode 100644 index 0000000..2b1bedc --- /dev/null +++ b/Pods/TrustDecision/TrustDecision/Classes/Containers/TDMobRiskSafeDictionary.h @@ -0,0 +1,12 @@ +// +// TDMobRiskSafeDictionary.h +// TDMobRisk +// +// Created by zeinber on 2022/12/7. +// + +#import + +@interface TDMobRiskSafeDictionary : NSMutableDictionary + +@end diff --git a/Pods/TrustDecision/TrustDecision/Classes/Containers/TDMobRiskSafeDictionary.m b/Pods/TrustDecision/TrustDecision/Classes/Containers/TDMobRiskSafeDictionary.m new file mode 100644 index 0000000..3f10840 --- /dev/null +++ b/Pods/TrustDecision/TrustDecision/Classes/Containers/TDMobRiskSafeDictionary.m @@ -0,0 +1,225 @@ +// +// TDMobRiskSafeDictionary.m +// TDMobRisk +// +// Created by zeinber on 2022/12/7. +// + +#import "TDMobRiskSafeDictionary.h" + +#define INIT(...) self = super.init; \ +if (!self) return nil; \ +__VA_ARGS__; \ +if (!_dic) return nil; \ +_lock = dispatch_semaphore_create(1); \ +return self; + + +#define LOCK(...) dispatch_semaphore_wait(_lock, DISPATCH_TIME_FOREVER); \ +__VA_ARGS__; \ +dispatch_semaphore_signal(_lock); + +@implementation TDMobRiskSafeDictionary { + NSMutableDictionary *_dic; //Subclass a class cluster... + dispatch_semaphore_t _lock; +} + +#pragma mark - init + +- (instancetype)init { + INIT(_dic = [[NSMutableDictionary alloc] init]); +} + +- (instancetype)initWithObjects:(NSArray *)objects forKeys:(NSArray *)keys { + INIT(_dic = [[NSMutableDictionary alloc] initWithObjects:objects forKeys:keys]); +} + +- (instancetype)initWithCapacity:(NSUInteger)capacity { + INIT(_dic = [[NSMutableDictionary alloc] initWithCapacity:capacity]); +} + +- (instancetype)initWithObjects:(const id[])objects forKeys:(const id [])keys count:(NSUInteger)cnt { + INIT(_dic = [[NSMutableDictionary alloc] initWithObjects:objects forKeys:keys count:cnt]); +} + +- (instancetype)initWithDictionary:(NSDictionary *)otherDictionary { + INIT(_dic = [[NSMutableDictionary alloc] initWithDictionary:otherDictionary]); +} + +- (instancetype)initWithDictionary:(NSDictionary *)otherDictionary copyItems:(BOOL)flag { + INIT(_dic = [[NSMutableDictionary alloc] initWithDictionary:otherDictionary copyItems:flag]); +} + + +#pragma mark - method + +- (NSUInteger)count { + LOCK(NSUInteger c = _dic.count); return c; +} + +- (id)objectForKey:(id)aKey { + LOCK(id o = [_dic objectForKey:aKey]); return o; +} + +- (NSEnumerator *)keyEnumerator { + LOCK(NSEnumerator * e = [_dic keyEnumerator]); return e; +} + +- (NSArray *)allKeys { + LOCK(NSArray * a = [_dic allKeys]); return a; +} + +- (NSArray *)allKeysForObject:(id)anObject { + LOCK(NSArray * a = [_dic allKeysForObject:anObject]); return a; +} + +- (NSArray *)allValues { + LOCK(NSArray * a = [_dic allValues]); return a; +} + +- (NSString *)description { + LOCK(NSString * d = [_dic description]); return d; +} + +- (NSString *)descriptionInStringsFileFormat { + LOCK(NSString * d = [_dic descriptionInStringsFileFormat]); return d; +} + +- (NSString *)descriptionWithLocale:(id)locale { + LOCK(NSString * d = [_dic descriptionWithLocale:locale]); return d; +} + +- (NSString *)descriptionWithLocale:(id)locale indent:(NSUInteger)level { + LOCK(NSString * d = [_dic descriptionWithLocale:locale indent:level]); return d; +} + +- (BOOL)isEqualToDictionary:(NSDictionary *)otherDictionary { + if (otherDictionary == self) return YES; + + if ([otherDictionary isKindOfClass:TDMobRiskSafeDictionary.class]) { + TDMobRiskSafeDictionary *other = (id)otherDictionary; + BOOL isEqual; + dispatch_semaphore_wait(_lock, DISPATCH_TIME_FOREVER); + dispatch_semaphore_wait(other->_lock, DISPATCH_TIME_FOREVER); + isEqual = [_dic isEqual:other->_dic]; + dispatch_semaphore_signal(other->_lock); + dispatch_semaphore_signal(_lock); + return isEqual; + } + return NO; +} + +- (NSEnumerator *)objectEnumerator { + LOCK(NSEnumerator * e = [_dic objectEnumerator]); return e; +} + +- (NSArray *)objectsForKeys:(NSArray *)keys notFoundMarker:(id)marker { + LOCK(NSArray * a = [_dic objectsForKeys:keys notFoundMarker:marker]); return a; +} + +- (NSArray *)keysSortedByValueUsingSelector:(SEL)comparator { + LOCK(NSArray * a = [_dic keysSortedByValueUsingSelector:comparator]); return a; +} + +- (void)getObjects:(id __unsafe_unretained[])objects andKeys:(id __unsafe_unretained[])keys { + LOCK([_dic getObjects:objects andKeys:keys]); +} + +- (id)objectForKeyedSubscript:(id)key { + LOCK(id o = [_dic objectForKeyedSubscript:key]); return o; +} + +- (void)enumerateKeysAndObjectsUsingBlock:(void (NS_NOESCAPE ^)(id key, id obj, BOOL *stop))block { + LOCK([_dic enumerateKeysAndObjectsUsingBlock:block]); +} + +- (void)enumerateKeysAndObjectsWithOptions:(NSEnumerationOptions)opts usingBlock:(void (NS_NOESCAPE ^)(id key, id obj, BOOL *stop))block { + LOCK([_dic enumerateKeysAndObjectsWithOptions:opts usingBlock:block]); +} + +- (NSArray *)keysSortedByValueUsingComparator:(NSComparator NS_NOESCAPE)cmptr { + LOCK(NSArray * a = [_dic keysSortedByValueUsingComparator:cmptr]); return a; +} + +- (NSArray *)keysSortedByValueWithOptions:(NSSortOptions)opts usingComparator:(NSComparator NS_NOESCAPE)cmptr { + LOCK(NSArray * a = [_dic keysSortedByValueWithOptions:opts usingComparator:cmptr]); return a; +} + +- (NSSet *)keysOfEntriesPassingTest:(BOOL (NS_NOESCAPE ^)(id key, id obj, BOOL *stop))predicate { + LOCK(NSSet * a = [_dic keysOfEntriesPassingTest:predicate]); return a; +} + +- (NSSet *)keysOfEntriesWithOptions:(NSEnumerationOptions)opts passingTest:(BOOL (NS_NOESCAPE ^)(id key, id obj, BOOL *stop))predicate { + LOCK(NSSet * a = [_dic keysOfEntriesWithOptions:opts passingTest:predicate]); return a; +} + +#pragma mark - mutable + +- (void)removeObjectForKey:(id)aKey { + LOCK([_dic removeObjectForKey:aKey]); +} + +- (void)setObject:(id)anObject forKey:(id )aKey { + LOCK([_dic setObject:anObject forKey:aKey]); +} + +- (void)addEntriesFromDictionary:(NSDictionary *)otherDictionary { + LOCK([_dic addEntriesFromDictionary:otherDictionary]); +} + +- (void)removeAllObjects { + LOCK([_dic removeAllObjects]); +} + +- (void)removeObjectsForKeys:(NSArray *)keyArray { + LOCK([_dic removeObjectsForKeys:keyArray]); +} + +- (void)setDictionary:(NSDictionary *)otherDictionary { + LOCK([_dic setDictionary:otherDictionary]); +} + +- (void)setObject:(id)obj forKeyedSubscript:(id )key { + LOCK([_dic setObject:obj forKeyedSubscript:key]); +} + +#pragma mark - protocol + +- (id)copyWithZone:(NSZone *)zone { + return [self mutableCopyWithZone:zone]; +} + +- (id)mutableCopyWithZone:(NSZone *)zone { + LOCK(id copiedDictionary = [[self.class allocWithZone:zone] initWithDictionary:_dic]); + return copiedDictionary; +} + +- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state + objects:(id __unsafe_unretained[])stackbuf + count:(NSUInteger)len { + LOCK(NSUInteger count = [_dic countByEnumeratingWithState:state objects:stackbuf count:len]); + return count; +} + +- (BOOL)isEqual:(id)object { + if (object == self) return YES; + + if ([object isKindOfClass:TDMobRiskSafeDictionary.class]) { + TDMobRiskSafeDictionary *other = object; + BOOL isEqual; + dispatch_semaphore_wait(_lock, DISPATCH_TIME_FOREVER); + dispatch_semaphore_wait(other->_lock, DISPATCH_TIME_FOREVER); + isEqual = [_dic isEqual:other->_dic]; + dispatch_semaphore_signal(other->_lock); + dispatch_semaphore_signal(_lock); + return isEqual; + } + return NO; +} + +- (NSUInteger)hash { + LOCK(NSUInteger hash = [_dic hash]); + return hash; +} + +@end diff --git a/Pods/TrustDecision/TrustDecision/Classes/Core/TDMobRiskCalculator.h b/Pods/TrustDecision/TrustDecision/Classes/Core/TDMobRiskCalculator.h new file mode 100644 index 0000000..c4eaca6 --- /dev/null +++ b/Pods/TrustDecision/TrustDecision/Classes/Core/TDMobRiskCalculator.h @@ -0,0 +1,13 @@ +// +// TDMobRiskCalculator.h +// TDMobRisk +// +// Created by zeinber on 2022/12/12. +// + +#import + +@interface TDMobRiskCalculator : NSObject +/// Generate risk label according to the collected information ++ (NSDictionary *)generateRiskLabelByInfo:(NSDictionary *)info; +@end diff --git a/Pods/TrustDecision/TrustDecision/Classes/Core/TDMobRiskCalculator.m b/Pods/TrustDecision/TrustDecision/Classes/Core/TDMobRiskCalculator.m new file mode 100644 index 0000000..2518d7a --- /dev/null +++ b/Pods/TrustDecision/TrustDecision/Classes/Core/TDMobRiskCalculator.m @@ -0,0 +1,24 @@ +// +// TDMobRiskCalculator.m +// TDMobRisk +// +// Created by zeinber on 2022/12/12. +// + +#import "TDMobRiskCalculator.h" + +@implementation TDMobRiskCalculator ++ (NSDictionary *)generateRiskLabelByInfo:(NSDictionary *)info { + NSMutableDictionary *riskDict = [[NSMutableDictionary alloc] init]; + NSArray *riskArray = @[ + @"jailbreak",@"simulator",@"debug" + ]; + for (id riskLabel in riskArray) { + id riskInfo = info[riskLabel]; + if (riskInfo) { + [riskDict setObject:riskInfo forKey:riskLabel]; + } + } + return [riskDict copy]; +} +@end diff --git a/Pods/TrustDecision/TrustDecision/Classes/Core/TDMobRiskIdCalculator.h b/Pods/TrustDecision/TrustDecision/Classes/Core/TDMobRiskIdCalculator.h new file mode 100644 index 0000000..051de5f --- /dev/null +++ b/Pods/TrustDecision/TrustDecision/Classes/Core/TDMobRiskIdCalculator.h @@ -0,0 +1,13 @@ +// +// TDMobRiskIdCalculator.h +// TDMobRisk +// +// Created by zeinber on 2022/12/7. +// + +#import + +@interface TDMobRiskIdCalculator : NSObject +/// Generate ID according to the collected information ++ (NSString *)generateIdByInfo:(NSDictionary *)info; +@end diff --git a/Pods/TrustDecision/TrustDecision/Classes/Core/TDMobRiskIdCalculator.m b/Pods/TrustDecision/TrustDecision/Classes/Core/TDMobRiskIdCalculator.m new file mode 100644 index 0000000..27154c3 --- /dev/null +++ b/Pods/TrustDecision/TrustDecision/Classes/Core/TDMobRiskIdCalculator.m @@ -0,0 +1,21 @@ +// +// TDMobRiskIdCalculator.m +// TDMobRisk +// +// Created by zeinber on 2022/12/7. +// + +#import "TDMobRiskIdCalculator.h" +#import "TDMobRiskEncodeHelper.h" + +@implementation TDMobRiskIdCalculator ++ (NSString *)generateIdByInfo:(NSDictionary *)info { + // Calculate DeviceId + NSString *idfv = info[@"idfv"]; + NSString *deviceId = nil; + if (idfv && [idfv isKindOfClass:[NSString class]]) { + deviceId = [TDMobRiskEncodeHelper sha256WithSrc:idfv]; + } + return deviceId; +} +@end diff --git a/Pods/TrustDecision/TrustDecision/Classes/Helper/TDMobRiskAPIHelper.h b/Pods/TrustDecision/TrustDecision/Classes/Helper/TDMobRiskAPIHelper.h new file mode 100644 index 0000000..bd3baf3 --- /dev/null +++ b/Pods/TrustDecision/TrustDecision/Classes/Helper/TDMobRiskAPIHelper.h @@ -0,0 +1,13 @@ +// +// TDMobRiskAPIHelper.h +// TDMobRisk +// +// Created by zeinber on 2022/12/6. +// + +#import + +@interface TDMobRiskAPIHelper : NSObject +/// get SystemInfo by Name. ++ (NSString *)sysctlByName:(const char *)name isObject:(BOOL)isObject; +@end diff --git a/Pods/TrustDecision/TrustDecision/Classes/Helper/TDMobRiskAPIHelper.m b/Pods/TrustDecision/TrustDecision/Classes/Helper/TDMobRiskAPIHelper.m new file mode 100644 index 0000000..39c011e --- /dev/null +++ b/Pods/TrustDecision/TrustDecision/Classes/Helper/TDMobRiskAPIHelper.m @@ -0,0 +1,40 @@ +// +// TDMobRiskAPIHelper.m +// TDMobRisk +// +// Created by zeinber on 2022/12/6. +// + +#import "TDMobRiskAPIHelper.h" +#import + +@implementation TDMobRiskAPIHelper + ++ (NSString *)sysctlByName:(const char *)name isObject:(BOOL)isObject { + NSString *cString = nil; + NSString *unknowDesc = @"unknow"; + if (isObject) { + size_t size = 0; + sysctlbyname(name, NULL, &size, NULL, 0); + void *cstr = malloc(size); + memset(cstr, 0, size); + int sysctl_flag = sysctlbyname(name, cstr, &size, NULL, 0); + if (sysctl_flag == 0) { + cString = [[NSString alloc] initWithCString:cstr ?: "" encoding:NSUTF8StringEncoding]; + }else { + cString = unknowDesc; + } + free(cstr); + }else { + long long num; + size_t numLen = sizeof(num); + int sysctl_flag = sysctlbyname(name, &num, &numLen, NULL, 0); + if (sysctl_flag == 0) { + cString = [NSString stringWithFormat:@"%lld",num]; + }else { + cString = unknowDesc; + } + } + return cString; +} +@end diff --git a/Pods/TrustDecision/TrustDecision/Classes/Helper/TDMobRiskEncodeHelper.h b/Pods/TrustDecision/TrustDecision/Classes/Helper/TDMobRiskEncodeHelper.h new file mode 100644 index 0000000..436743a --- /dev/null +++ b/Pods/TrustDecision/TrustDecision/Classes/Helper/TDMobRiskEncodeHelper.h @@ -0,0 +1,13 @@ +// +// TDMobRiskEncodeHelper.h +// TDMobRisk +// +// Created by zeinber on 2022/12/6. +// + +#import + +@interface TDMobRiskEncodeHelper : NSObject +/// sha256 Encode String ++ (NSString *)sha256WithSrc:(NSString *)src; +@end diff --git a/Pods/TrustDecision/TrustDecision/Classes/Helper/TDMobRiskEncodeHelper.m b/Pods/TrustDecision/TrustDecision/Classes/Helper/TDMobRiskEncodeHelper.m new file mode 100644 index 0000000..a74c4bd --- /dev/null +++ b/Pods/TrustDecision/TrustDecision/Classes/Helper/TDMobRiskEncodeHelper.m @@ -0,0 +1,30 @@ +// +// TDMobRiskEncodeHelper.m +// TDMobRisk +// +// Created by zeinber on 2022/12/6. +// + +#import "TDMobRiskEncodeHelper.h" +#import + +@implementation TDMobRiskEncodeHelper +#pragma mark - SHA256 ++ (NSString *)sha256WithSrc:(NSString *)src { + const char *c = [src UTF8String]; + if (!c) { +#ifdef DEBUG + NSLog(@"[%s] - [Error] - src[%@] is nil",__func__,src); +#endif + return nil; + } + unsigned char result[CC_SHA256_DIGEST_LENGTH]; + CC_SHA256(c, (CC_LONG)strlen(c), result); + NSMutableString *hash = [NSMutableString stringWithCapacity:CC_SHA256_DIGEST_LENGTH * 2]; + for (NSInteger i = 0; i < CC_SHA256_DIGEST_LENGTH; i++) { + [hash appendFormat:@"%02x", result[i]]; + } + return [hash lowercaseString]; + +} +@end diff --git a/Pods/TrustDecision/TrustDecision/Classes/Helper/TDMobRiskKeychainsHelper.h b/Pods/TrustDecision/TrustDecision/Classes/Helper/TDMobRiskKeychainsHelper.h new file mode 100644 index 0000000..0accc64 --- /dev/null +++ b/Pods/TrustDecision/TrustDecision/Classes/Helper/TDMobRiskKeychainsHelper.h @@ -0,0 +1,14 @@ +// +// TDMobRiskKeychainsHelper.h +// TDMobRisk +// +// Created by zeinber on 2022/12/7. +// + +#import + +@interface TDMobRiskKeychainsHelper : NSObject ++ (OSStatus)deleteValueForKey:(NSString *)key; ++ (OSStatus)saveValueForKey:(NSString *)key value:(id)value; ++ (id)loadValueForKey:(NSString *)key; +@end diff --git a/Pods/TrustDecision/TrustDecision/Classes/Helper/TDMobRiskKeychainsHelper.m b/Pods/TrustDecision/TrustDecision/Classes/Helper/TDMobRiskKeychainsHelper.m new file mode 100644 index 0000000..8aa04e1 --- /dev/null +++ b/Pods/TrustDecision/TrustDecision/Classes/Helper/TDMobRiskKeychainsHelper.m @@ -0,0 +1,78 @@ +// +// TDMobRiskKeychainsHelper.m +// TDMobRisk +// +// Created by zeinber on 2022/12/7. +// + +#import "TDMobRiskKeychainsHelper.h" +#import + +static NSString *keychainAccount = @"com.td.mobrisk"; +@implementation TDMobRiskKeychainsHelper +#pragma mark - function ++ (OSStatus)deleteValueForKey:(NSString *)key { + if (!key) { + return -1; + } + NSDictionary *keychainQuery = @{ + (__bridge id)kSecClass : (__bridge id)kSecClassGenericPassword, + (__bridge id)kSecAttrService : key, + (__bridge id)kSecAttrAccount : keychainAccount + }; + OSStatus result = SecItemDelete((__bridge CFDictionaryRef)keychainQuery); + return result; +} + ++ (OSStatus)saveValueForKey:(NSString *)key value:(id)value { + if (!key) { + return -1; + } + if (!value) { + return -2; + } + NSMutableDictionary *keychainQuery = [[NSMutableDictionary alloc] initWithDictionary:@{ + (__bridge id)kSecClass : (__bridge id)kSecClassGenericPassword, + (__bridge id)kSecAttrService : key, + (__bridge id)kSecAttrAccount : keychainAccount, + (__bridge id)kSecAttrAccessible : (__bridge id)kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly + }]; + [self deleteValueForKey:key]; + NSData *archiverData = [NSKeyedArchiver archivedDataWithRootObject:value]; + if (archiverData) { + [keychainQuery setObject:archiverData forKey:(__bridge id)kSecValueData]; + } + OSStatus result = SecItemAdd((__bridge CFDictionaryRef)keychainQuery, NULL); + return result; +} + ++ (id)loadValueForKey:(NSString *)key { + id value = nil; + if (!key) { + return value; + } + NSMutableDictionary *keychainQuery = [[NSMutableDictionary alloc] initWithDictionary:@{ + (__bridge id)kSecClass : (__bridge id)kSecClassGenericPassword, + (__bridge id)kSecAttrService : key, + (__bridge id)kSecAttrAccount : keychainAccount, + (__bridge id)kSecReturnData : (__bridge id)kCFBooleanTrue, + (__bridge id)kSecMatchLimit : (__bridge id)kSecMatchLimitOne + }]; + CFDataRef keyData = NULL; + OSStatus result = SecItemCopyMatching((__bridge CFDictionaryRef)keychainQuery, (CFTypeRef *)&keyData); + if (result == noErr) { + @try { + value = [NSKeyedUnarchiver unarchiveObjectWithData:(__bridge NSData *)keyData]; + } + @catch (NSException *e) { + value = nil; + } + @finally {} + } + if (keyData) { + CFRelease(keyData); + } + return value; +} + +@end diff --git a/Pods/TrustDecision/TrustDecision/Classes/TDMobRisk.h b/Pods/TrustDecision/TrustDecision/Classes/TDMobRisk.h new file mode 100644 index 0000000..d2b9925 --- /dev/null +++ b/Pods/TrustDecision/TrustDecision/Classes/TDMobRisk.h @@ -0,0 +1,17 @@ +// +// TDMobRisk.h +// TDMobRisk +// +// Created by zeinber on 2022/12/6. +// + +#import + +//! Project version number for TDMobRisk. +FOUNDATION_EXPORT double TDMobRiskVersionNumber; + +//! Project version string for TDMobRisk. +FOUNDATION_EXPORT const unsigned char TDMobRiskVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import +#import diff --git a/Pods/TrustDecision/TrustDecision/Classes/TDMobRiskHeader.h b/Pods/TrustDecision/TrustDecision/Classes/TDMobRiskHeader.h new file mode 100644 index 0000000..869754a --- /dev/null +++ b/Pods/TrustDecision/TrustDecision/Classes/TDMobRiskHeader.h @@ -0,0 +1,15 @@ +// +// TDMobRiskHeader.h +// TDMobRisk +// +// Created by zeinber on 2022/12/7. +// + +#ifndef TDMobRiskHeader_h +#define TDMobRiskHeader_h + +#pragma mark - Defination List +#define DeviceIdKey @"device_id" +#define DeviceRiskLabelKey @"device_risk_label" +#define DeviceDetailKey @"device_detail" +#endif /* TDMobRiskHeader_h */ diff --git a/Pods/TrustDecision/TrustDecision/Classes/TDMobRiskManager.h b/Pods/TrustDecision/TrustDecision/Classes/TDMobRiskManager.h new file mode 100755 index 0000000..890dbd7 --- /dev/null +++ b/Pods/TrustDecision/TrustDecision/Classes/TDMobRiskManager.h @@ -0,0 +1,31 @@ +// +// TDMobRiskManager.h +// TDMobRisk +// +// Created by zeinber on 2022/12/5. +// + +#import + +typedef struct _TDMobRiskVoid { +/** + * Initialization method + * @param options Initialization parameters,please see the document for details. + */ +void (*initWithOptions)(NSDictionary *); +/** + * get device info, you should make sure that method calls after initWithOptions. + */ +NSDictionary *(*getBlackBox)(void); +/** + * get sdkVersion,currentVersion is 1.1 + */ +NSString *(*getSDKVersion)(void); +} TDMobRiskManager_t; + +@interface TDMobRiskManager : NSObject +/** + * get a singleton object + */ ++ (TDMobRiskManager_t *)sharedManager; +@end diff --git a/Pods/TrustDecision/TrustDecision/Classes/TDMobRiskManager.m b/Pods/TrustDecision/TrustDecision/Classes/TDMobRiskManager.m new file mode 100755 index 0000000..5d1c58c --- /dev/null +++ b/Pods/TrustDecision/TrustDecision/Classes/TDMobRiskManager.m @@ -0,0 +1,102 @@ +// +// TDMobRiskManager.m +// TDMobRisk +// +// Created by zeinber on 2022/12/5. +// + +#import "TDMobRiskManager.h" +#import "TDMobRiskSafeDictionary.h" +#import "TDMobRiskCollector.h" +#import "TDMobRiskIdCalculator.h" +#import "TDMobRiskCalculator.h" +#import "TDMobRiskHeader.h" + +#pragma mark - Static Param +/// SDK Object +static TDMobRiskManager_t *riskManager; +/// SDKVersion +static NSString *sdkVersion = @"1.3"; +/// CollectData Dictionary +static TDMobRiskSafeDictionary *infoDict; +/// Global Collect Queue +static dispatch_queue_t collectQueue; + +#pragma mark - Output Function Define List +/// Initialization Function +void initWithOptions(NSDictionary *options); +/// Get BlackBox +NSDictionary *getBlackBox(void); +/// Get SDKVersion +NSString *getSDKVersion(void); + +#pragma mark - Private Function Define List +/// Assert +static void TDMobRisk_assert(BOOL condition, const char *assertDescription); + +@implementation TDMobRiskManager +#pragma mark - public methods ++ (TDMobRiskManager_t *)sharedManager { + static dispatch_once_t deviceManager_once_token; + dispatch_once(&deviceManager_once_token, ^{ + riskManager = malloc(sizeof(TDMobRiskManager_t)); + riskManager->initWithOptions = initWithOptions; + riskManager->getBlackBox = getBlackBox; + riskManager->getSDKVersion = getSDKVersion; + collectQueue = dispatch_queue_create("com.td.mobrisk.collector", DISPATCH_QUEUE_CONCURRENT); + }); + return riskManager; +} + +void initWithOptions(NSDictionary *options) { + // protect judge + /// Please use Dictionary!!! + TDMobRisk_assert([options isKindOfClass:[NSDictionary class]], ("[TDMobRiskManager] - [INIT_ERROR] - options is not dictonary type")); + // Async Collect + dispatch_barrier_async(collectQueue, ^{ + // Response data + NSMutableDictionary *dataDict = [[NSMutableDictionary alloc] init]; + // Collect data + NSDictionary *collectInfo = [TDMobRiskCollector getCollectInfo]; + // Calculate and set deviceId + NSString *deviceId = [TDMobRiskIdCalculator generateIdByInfo:collectInfo]; + if (deviceId) { + [dataDict setObject:deviceId forKey:DeviceIdKey]; + } + // Calculate and set deviceRisk + NSDictionary *deviceRiskLabel = [TDMobRiskCalculator generateRiskLabelByInfo:collectInfo]; + if (deviceRiskLabel) { + [dataDict setObject:deviceRiskLabel forKey:DeviceRiskLabelKey]; + } + // Calculate and set deviceDetail + if (collectInfo) { + [dataDict setObject:collectInfo forKey:DeviceDetailKey]; + } + infoDict = [[TDMobRiskSafeDictionary alloc] initWithDictionary:dataDict]; + // Copy Dictionary of infoDict + NSDictionary *responseDict = [[NSDictionary alloc] initWithDictionary:infoDict]; + // Set parameters of response + void (^responseCallback)(NSDictionary *blackBox) = [options objectForKey:@"callback"]; + // Response callback + if (responseCallback) { + responseCallback(responseDict); + } + }); +} + +NSDictionary *getBlackBox() { + return [infoDict copy]; +} + +NSString *getSDKVersion() { + return sdkVersion; +} + +#pragma mark - Private Function List +/// Asset Error +static void TDMobRisk_assert(BOOL condition, const char *assertDescription) { + if (!condition) { + __assert_rtn(__func__, ("unknown"), 0, assertDescription); + } +} +@end