From 7905d0ff257f29f430e0f47d8f45be2ddbed7809 Mon Sep 17 00:00:00 2001 From: Selim Mustafaev Date: Wed, 13 May 2020 14:39:19 +0300 Subject: [PATCH] Settings update --- AutoCat.xcodeproj/project.pbxproj | 37 +++ .../xcshareddata/swiftpm/Package.resolved | 9 + .../xcdebugger/Breakpoints_v2.xcbkptlist | 64 +++++ AutoCat/AppDelegate.swift | 2 +- AutoCat/Base.lproj/Main.storyboard | 77 +++-- .../Controllers/GoogleSignInController.swift | 98 +++++++ AutoCat/Controllers/SettingsController.swift | 47 +++- AutoCat/Models/User.swift | 2 + AutoCat/ThirdParty/Base64FS.swift | 262 ++++++++++++++++++ AutoCat/Utils/Constants.swift | 10 + AutoCat/Utils/GoogleAuth.swift | 2 + 11 files changed, 582 insertions(+), 28 deletions(-) create mode 100644 AutoCat/Controllers/GoogleSignInController.swift create mode 100644 AutoCat/ThirdParty/Base64FS.swift create mode 100644 AutoCat/Utils/Constants.swift create mode 100644 AutoCat/Utils/GoogleAuth.swift diff --git a/AutoCat.xcodeproj/project.pbxproj b/AutoCat.xcodeproj/project.pbxproj index d438843..cad3904 100644 --- a/AutoCat.xcodeproj/project.pbxproj +++ b/AutoCat.xcodeproj/project.pbxproj @@ -48,6 +48,7 @@ 7A64AE812469E16100ABE48E /* ProgressAnimatedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A64AE7B2469E16100ABE48E /* ProgressAnimatedView.swift */; }; 7A64AE822469E16100ABE48E /* IHProgressHUD.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A64AE7C2469E16100ABE48E /* IHProgressHUD.swift */; }; 7A64AE832469E16100ABE48E /* IHProgressHUD.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 7A64AE7D2469E16100ABE48E /* IHProgressHUD.bundle */; }; + 7A64AE85246AD12E00ABE48E /* GoogleAuth.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A64AE84246AD12E00ABE48E /* GoogleAuth.swift */; }; 7A6DD903242BF4A5009DE740 /* PlateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A6DD902242BF4A5009DE740 /* PlateView.swift */; }; 7A6DD90824329144009DE740 /* CenterTextLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A6DD90724329144009DE740 /* CenterTextLayer.swift */; }; 7A6DD90A24329541009DE740 /* RoadNumbers2.0.otf in Resources */ = {isa = PBXBuildFile; fileRef = 7A6DD90924329541009DE740 /* RoadNumbers2.0.otf */; }; @@ -56,6 +57,11 @@ 7A7547DD2403180A004E8406 /* SectionHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A7547DB2403180A004E8406 /* SectionHeader.swift */; }; 7A7547DE2403180A004E8406 /* SectionHeader.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7A7547DC2403180A004E8406 /* SectionHeader.xib */; }; 7A7547E024032CB6004E8406 /* VehiclePhotoCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A7547DF24032CB6004E8406 /* VehiclePhotoCell.swift */; }; + 7A96AE2A246AFD6200297C33 /* Eureka in Frameworks */ = {isa = PBXBuildFile; productRef = 7A96AE29246AFD6200297C33 /* Eureka */; }; + 7A96AE2D246B2B7400297C33 /* GoogleSignInController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A96AE2C246B2B7400297C33 /* GoogleSignInController.swift */; }; + 7A96AE2F246B2BCD00297C33 /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7A96AE2E246B2BCD00297C33 /* WebKit.framework */; }; + 7A96AE31246B2FE400297C33 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A96AE30246B2FE400297C33 /* Constants.swift */; }; + 7A96AE33246C095700297C33 /* Base64FS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A96AE32246C095700297C33 /* Base64FS.swift */; }; 7AB67E8C2435C38700258F61 /* CustomTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB67E8B2435C38700258F61 /* CustomTextField.swift */; }; 7AB67E8E2435D1A000258F61 /* CustomButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB67E8D2435D1A000258F61 /* CustomButton.swift */; }; 7AEFE728240455E200910EB7 /* SettingsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AEFE727240455E200910EB7 /* SettingsController.swift */; }; @@ -99,6 +105,7 @@ 7A64AE7B2469E16100ABE48E /* ProgressAnimatedView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProgressAnimatedView.swift; sourceTree = ""; }; 7A64AE7C2469E16100ABE48E /* IHProgressHUD.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IHProgressHUD.swift; sourceTree = ""; }; 7A64AE7D2469E16100ABE48E /* IHProgressHUD.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = IHProgressHUD.bundle; sourceTree = ""; }; + 7A64AE84246AD12E00ABE48E /* GoogleAuth.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GoogleAuth.swift; sourceTree = ""; }; 7A6DD902242BF4A5009DE740 /* PlateView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlateView.swift; sourceTree = ""; }; 7A6DD90724329144009DE740 /* CenterTextLayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CenterTextLayer.swift; sourceTree = ""; }; 7A6DD90924329541009DE740 /* RoadNumbers2.0.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = RoadNumbers2.0.otf; sourceTree = ""; }; @@ -108,6 +115,10 @@ 7A7547DC2403180A004E8406 /* SectionHeader.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = SectionHeader.xib; sourceTree = ""; }; 7A7547DF24032CB6004E8406 /* VehiclePhotoCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VehiclePhotoCell.swift; sourceTree = ""; }; 7A92D0AB240425B100EF3B77 /* ATGMediaBrowser.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ATGMediaBrowser.framework; path = Carthage/Build/iOS/ATGMediaBrowser.framework; sourceTree = ""; }; + 7A96AE2C246B2B7400297C33 /* GoogleSignInController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GoogleSignInController.swift; sourceTree = ""; }; + 7A96AE2E246B2BCD00297C33 /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/System/Library/Frameworks/WebKit.framework; sourceTree = DEVELOPER_DIR; }; + 7A96AE30246B2FE400297C33 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = ""; }; + 7A96AE32246C095700297C33 /* Base64FS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Base64FS.swift; sourceTree = ""; }; 7AB67E8B2435C38700258F61 /* CustomTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomTextField.swift; sourceTree = ""; }; 7AB67E8D2435D1A000258F61 /* CustomButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomButton.swift; sourceTree = ""; }; 7AEFE727240455E200910EB7 /* SettingsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsController.swift; sourceTree = ""; }; @@ -122,9 +133,11 @@ files = ( 7AF58D342402A91C00CE01A0 /* Kingfisher in Frameworks */, 7A0516162414EC1200FC55AC /* Differentiator in Frameworks */, + 7A96AE2F246B2BCD00297C33 /* WebKit.framework in Frameworks */, 7A11472823FEA1F400B424AF /* RealmSwift in Frameworks */, 7A11472123FEA18700B424AF /* RxCocoa in Frameworks */, 7A0516182414EC1200FC55AC /* RxDataSources in Frameworks */, + 7A96AE2A246AFD6200297C33 /* Eureka in Frameworks */, 7A051611241412CA00FC55AC /* SwiftDate in Frameworks */, 7A11472323FEA18700B424AF /* RxBlocking in Frameworks */, 7A530B8B240181F500CBFE6E /* RxRealm in Frameworks */, @@ -188,6 +201,7 @@ 7A530B7924001D3300CBFE6E /* CheckController.swift */, 7AEFE727240455E200910EB7 /* SettingsController.swift */, 7A3F07AC2436350B00E59687 /* SearchController.swift */, + 7A96AE2C246B2B7400297C33 /* GoogleSignInController.swift */, ); path = Controllers; sourceTree = ""; @@ -198,6 +212,7 @@ 7A64AE772469E16100ABE48E /* IHProgressHUD */, 7A64AE6E2469DFB600ABE48E /* ATGMediaBrowser */, 7A6DD90724329144009DE740 /* CenterTextLayer.swift */, + 7A96AE32246C095700297C33 /* Base64FS.swift */, ); path = ThirdParty; sourceTree = ""; @@ -206,6 +221,8 @@ isa = PBXGroup; children = ( 7A11474323FF06CA00B424AF /* Api.swift */, + 7A64AE84246AD12E00ABE48E /* GoogleAuth.swift */, + 7A96AE30246B2FE400297C33 /* Constants.swift */, ); path = Utils; sourceTree = ""; @@ -227,6 +244,7 @@ 7A11474C23FFEE8700B424AF /* Frameworks */ = { isa = PBXGroup; children = ( + 7A96AE2E246B2BCD00297C33 /* WebKit.framework */, 7A92D0AB240425B100EF3B77 /* ATGMediaBrowser.framework */, 7A11474D23FFEE8800B424AF /* SVProgressHUD.framework */, ); @@ -328,6 +346,7 @@ 7A051610241412CA00FC55AC /* SwiftDate */, 7A0516152414EC1200FC55AC /* Differentiator */, 7A0516172414EC1200FC55AC /* RxDataSources */, + 7A96AE29246AFD6200297C33 /* Eureka */, ); productName = AutoCat; productReference = 7A1146FD23FDE7E500B424AF /* AutoCat.app */; @@ -367,6 +386,7 @@ 7AF58D322402A91C00CE01A0 /* XCRemoteSwiftPackageReference "Kingfisher" */, 7A05160F241412CA00FC55AC /* XCRemoteSwiftPackageReference "SwiftDate" */, 7A0516142414EC1200FC55AC /* XCRemoteSwiftPackageReference "RxDataSources" */, + 7A96AE28246AFD6200297C33 /* XCRemoteSwiftPackageReference "Eureka" */, ); productRefGroup = 7A1146FE23FDE7E500B424AF /* Products */; projectDirPath = ""; @@ -399,7 +419,9 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 7A96AE33246C095700297C33 /* Base64FS.swift in Sources */, 7A530B802401803A00CBFE6E /* Vehicle.swift in Sources */, + 7A96AE31246B2FE400297C33 /* Constants.swift in Sources */, 7A64AE822469E16100ABE48E /* IHProgressHUD.swift in Sources */, 7A11470123FDE7E500B424AF /* AppDelegate.swift in Sources */, 7A6DD90824329144009DE740 /* CenterTextLayer.swift in Sources */, @@ -413,12 +435,14 @@ 7A64AE762469DFB600ABE48E /* ContentTransformers.swift in Sources */, 7A11471823FDEBFA00B424AF /* ReportController.swift in Sources */, 7A64AE7E2469E16100ABE48E /* RadialGradientLayer.swift in Sources */, + 7A64AE85246AD12E00ABE48E /* GoogleAuth.swift in Sources */, 7A64AE7F2469E16100ABE48E /* IndefiniteAnimatedView.swift in Sources */, 7A11471A23FE839000B424AF /* AuthController.swift in Sources */, 7A530B7A24001D3300CBFE6E /* CheckController.swift in Sources */, 7A64AE742469DFB600ABE48E /* MediaContentView.swift in Sources */, 7A7547DD2403180A004E8406 /* SectionHeader.swift in Sources */, 7AF58D58240309CA00CE01A0 /* VehicleTextParamCell.swift in Sources */, + 7A96AE2D246B2B7400297C33 /* GoogleSignInController.swift in Sources */, 7A11474723FF2AA500B424AF /* User.swift in Sources */, 7A11471623FDEB2A00B424AF /* MainSplitController.swift in Sources */, 7AF58D3124029E1000CE01A0 /* VehicleHeaderCell.swift in Sources */, @@ -697,6 +721,14 @@ minimumVersion = 2.0.0; }; }; + 7A96AE28246AFD6200297C33 /* XCRemoteSwiftPackageReference "Eureka" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/xmartlabs/Eureka"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 5.2.1; + }; + }; 7AF58D2D24029C5200CE01A0 /* XCRemoteSwiftPackageReference "MagazineLayout" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/airbnb/MagazineLayout"; @@ -776,6 +808,11 @@ package = 7A530B89240181F500CBFE6E /* XCRemoteSwiftPackageReference "RxRealm" */; productName = RxRealm; }; + 7A96AE29246AFD6200297C33 /* Eureka */ = { + isa = XCSwiftPackageProductDependency; + package = 7A96AE28246AFD6200297C33 /* XCRemoteSwiftPackageReference "Eureka" */; + productName = Eureka; + }; 7AF58D2E24029C5200CE01A0 /* MagazineLayout */ = { isa = XCSwiftPackageProductDependency; package = 7AF58D2D24029C5200CE01A0 /* XCRemoteSwiftPackageReference "MagazineLayout" */; diff --git a/AutoCat.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/AutoCat.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index c135394..d19adf4 100644 --- a/AutoCat.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/AutoCat.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -10,6 +10,15 @@ "version": "4.1.0" } }, + { + "package": "Eureka", + "repositoryURL": "https://github.com/xmartlabs/Eureka", + "state": { + "branch": null, + "revision": "9a30ae436ec9e9fcb8b3eb1bdfd95e95989d6ca5", + "version": "5.2.1" + } + }, { "package": "InputMask", "repositoryURL": "https://github.com/RedMadRobot/input-mask-ios", diff --git a/AutoCat.xcodeproj/xcuserdata/selim.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/AutoCat.xcodeproj/xcuserdata/selim.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist index 07e3a91..af111e5 100644 --- a/AutoCat.xcodeproj/xcuserdata/selim.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ b/AutoCat.xcodeproj/xcuserdata/selim.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -24,5 +24,69 @@ stopOnStyle = "0"> + + + + + + + + + + + + + + + + diff --git a/AutoCat/AppDelegate.swift b/AutoCat/AppDelegate.swift index 6f55e48..8ce7358 100644 --- a/AutoCat/AppDelegate.swift +++ b/AutoCat/AppDelegate.swift @@ -19,7 +19,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { let config = Realm.Configuration( - schemaVersion: 2, + schemaVersion: 3, migrationBlock: { migration, oldSchemaVersion in }) diff --git a/AutoCat/Base.lproj/Main.storyboard b/AutoCat/Base.lproj/Main.storyboard index d400fb0..3f83ecb 100644 --- a/AutoCat/Base.lproj/Main.storyboard +++ b/AutoCat/Base.lproj/Main.storyboard @@ -193,14 +193,14 @@ - + - + - + @@ -266,27 +266,7 @@ - - - - - - - - @@ -405,6 +385,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/AutoCat/Controllers/GoogleSignInController.swift b/AutoCat/Controllers/GoogleSignInController.swift new file mode 100644 index 0000000..87fbf25 --- /dev/null +++ b/AutoCat/Controllers/GoogleSignInController.swift @@ -0,0 +1,98 @@ +import UIKit +import WebKit +import CommonCrypto + +class GoogleSignInController: UIViewController, WKNavigationDelegate { + @IBOutlet weak var webView: WKWebView! + + private var codeVerifier: String = "" + + override func viewDidLoad() { + super.viewDidLoad() + self.webView.navigationDelegate = self + +#if targetEnvironment(macCatalyst) + self.webView.customUserAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1 Safari/605.1.15" +#else + self.webView.customUserAgent = "Mozilla/5.0 (iPhone; CPU iPhone OS 13_4_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1 Mobile/15E148 Safari/604.1" +#endif + + self.codeVerifier = UUID().uuidString + let codeChallenge = self.sha256(string: self.codeVerifier) ?? "" + print("++++++++++++++++++") + print(codeVerifier) + print(codeChallenge) + + let authUrlString = Constants.googleAuthURL + + "?response_type=code" + + "&code_challenge_method=S256" + + "&scope=email%20profile" + + "&redirect_uri=" + Constants.googleRedirectURL + + "&client_id=" + Constants.googleClientId + + "&code_challenge=" + codeChallenge + + if let url = URL(string: authUrlString) { + let request = URLRequest(url: url) + self.webView.load(request) + } + } + + func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) { + if let url = navigationAction.request.url { + if let components = URLComponents(url: url, resolvingAgainstBaseURL: false) { + if let queryItems = components.queryItems { + if let code = queryItems.first(where: { $0.name == "code" })?.value { + decisionHandler(.cancel) + self.getToken(code: code) { error, idToken in + print("ID Token: \(idToken)") + } + return + } + } + } + } + + decisionHandler(.allow) + } + + @IBAction func close(_ sender: UIBarButtonItem) { + self.dismiss(animated: true, completion: nil) + } + + func sha256(string: String) -> String? { + guard let data = string.data(using: .utf8) else { return nil } + + var hash = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH)) + data.withUnsafeBytes { + _ = CC_SHA256($0.baseAddress, CC_LONG(data.count), &hash) + } + + return String(data: Data(Base64FS.encode(data: hash)), encoding: .utf8)?.trimmingCharacters(in: CharacterSet(charactersIn: "=")) + } + + func getToken(code: String, completion: @escaping (Error?, String?) -> Void) { + let tokenUrlString = Constants.googleTokenURL + + "?grant_type=authorization_code" + + "&code=" + code + + "&redirect_uri=" + Constants.googleRedirectURL + + "&client_id=" + Constants.googleClientId + + "&code_verifier=" + self.codeVerifier + + if let url = URL(string: tokenUrlString) { + var request = URLRequest(url: url) + request.httpMethod = "POST" + URLSession.shared.dataTask(with: request) { data, response, error in + if let data = data { + if let str = String(data: data, encoding: .utf8) { + print(str) + } + } else { + completion(error, nil) + } + } + } else { + let error = NSError(domain: "", code: 0, userInfo: [NSLocalizedDescriptionKey: "Bad URL"]) + completion(error, nil) + } + } +} diff --git a/AutoCat/Controllers/SettingsController.swift b/AutoCat/Controllers/SettingsController.swift index 1db1b9d..9cfddaa 100644 --- a/AutoCat/Controllers/SettingsController.swift +++ b/AutoCat/Controllers/SettingsController.swift @@ -1,16 +1,57 @@ import UIKit +import Eureka -class SettingsController: UIViewController { +class SettingsController: FormViewController { override func viewDidLoad() { super.viewDidLoad() - // Do any additional setup after loading the view. + form +++ Section("Profile") + <<< LabelRow("AutoCatAccount") { row in + row.title = "AutoCat Account" + row.value = Settings.shared.user.login + } + <<< LabelRow("GoogleAccount") { row in + row.title = "Google Account" + row.value = "Log In" + row.cellUpdate { cell, _ in + cell.accessoryType = .disclosureIndicator + } + }.onCellSelection { cell, row in + if Settings.shared.user.googleIdToken != nil { + self.displayLogoutSheet() + } else { + self.loginToGoogle() + } + } + +++ Section() + <<< ButtonRow("SignOut") { $0.title = "Sign Out" }.onCellSelection { cell, row in + self.logout() + } } - @IBAction func logout(_ sender: UIButton) { + func logout() { Settings.shared.user.token = "" let storyboard = UIStoryboard(name: "Main", bundle: nil) self.view.window?.rootViewController = storyboard.instantiateViewController(identifier: "AuthController") } + + func displayLogoutSheet() { + let sheet = UIAlertController(title: "Google Account", message: "You are currently signed in with email example@gmail.com. It allows us to find more data about vehicles.", preferredStyle: .actionSheet) + + let cancel = UIAlertAction(title: "Cancel", style: .cancel) { _ in sheet.dismiss(animated: true, completion: nil) } + let logout = UIAlertAction(title: "Sign Out", style: .destructive) { _ in + Settings.shared.user.googleIdToken = nil + } + + sheet.addAction(logout) + sheet.addAction(cancel) + self.present(sheet, animated: true, completion: nil) + } + + func loginToGoogle() { + let storyboard = UIStoryboard(name: "Main", bundle: nil) + let vc = storyboard.instantiateViewController(identifier: "GoogleSignInController") + self.present(vc, animated: true) + } } diff --git a/AutoCat/Models/User.swift b/AutoCat/Models/User.swift index 3238c54..ef6c6eb 100644 --- a/AutoCat/Models/User.swift +++ b/AutoCat/Models/User.swift @@ -3,9 +3,11 @@ import Foundation struct User: Codable { let login: String var token: String + var googleIdToken: String? init() { self.login = "" self.token = "" + self.googleIdToken = "" } } diff --git a/AutoCat/ThirdParty/Base64FS.swift b/AutoCat/ThirdParty/Base64FS.swift new file mode 100644 index 0000000..f33a990 --- /dev/null +++ b/AutoCat/ThirdParty/Base64FS.swift @@ -0,0 +1,262 @@ +// +// Base64FS.swift +// +// Created by Jack Chorley on 27/06/2017. +// + +import Foundation + +public class Base64FS { + + private static let padding: UInt8 = 61 // Padding = "=" + + private static let filenameSafeAlphabet: [UInt8] = [ + 65, // 0 = "A" + 66, // 1 = "B" + 67, // 2 = "C" + 68, // 3 = "D" + 69, // 4 = "E" + 70, // 5 = "F" + 71, // 6 = "G" + 72, // 7 = "H" + 73, // 8 = "I" + 74, // 9 = "J" + 75, // 10 = "K" + 76, // 11 = "L" + 77, // 12 = "M" + 78, // 13 = "N" + 79, // 14 = "O" + 80, // 15 = "P" + 81, // 16 = "Q" + 82, // 17 = "R" + 83, // 18 = "S" + 84, // 19 = "T" + 85, // 20 = "U" + 86, // 21 = "V" + 87, // 22 = "W" + 88, // 23 = "X" + 89, // 24 = "Y" + 90, // 25 = "Z" + 97, // 26 = "a" + 98, // 27 = "b" + 99, // 28 = "c" + 100, // 29 = "d" + 101, // 30 = "e" + 102, // 31 = "f" + 103, // 32 = "g" + 104, // 33 = "h" + 105, // 34 = "i" + 106, // 35 = "j" + 107, // 36 = "k" + 108, // 37 = "l" + 109, // 38 = "m" + 110, // 39 = "n" + 111, // 40 = "o" + 112, // 41 = "p" + 113, // 42 = "q" + 114, // 43 = "r" + 115, // 44 = "s" + 116, // 45 = "t" + 117, // 46 = "u" + 118, // 47 = "v" + 119, // 48 = "w" + 120, // 49 = "x" + 121, // 50 = "y" + 122, // 51 = "z" + 48, // 52 = "0" + 49, // 53 = "1" + 50, // 54 = "2" + 51, // 55 = "3" + 52, // 56 = "4" + 53, // 57 = "5" + 54, // 58 = "6" + 55, // 59 = "7" + 56, // 60 = "8" + 57, // 61 = "9" + 45, // 62 = "-" + 95, // 63 = "_" + ] + + private static let safeAlphabetToIndex: [UInt8 : UInt8] = [ + 61 : 0, // Padding = 0 + 65 : 0, // 0 = "A" + 66 : 1, // 1 = "B" + 67 : 2, // 2 = "C" + 68 : 3, // 3 = "D" + 69 : 4, // 4 = "E" + 70 : 5, // 5 = "F" + 71 : 6, // 6 = "G" + 72 : 7, // 7 = "H" + 73 : 8, // 8 = "I" + 74 : 9, // 9 = "J" + 75 : 10, // 10 = "K" + 76 : 11, // 11 = "L" + 77 : 12, // 12 = "M" + 78 : 13, // 13 = "N" + 79 : 14, // 14 = "O" + 80 : 15, // 15 = "P" + 81 : 16, // 16 = "Q" + 82 : 17, // 17 = "R" + 83 : 18, // 18 = "S" + 84 : 19, // 19 = "T" + 85 : 20, // 20 = "U" + 86 : 21, // 21 = "V" + 87 : 22, // 22 = "W" + 88 : 23, // 23 = "X" + 89 : 24, // 24 = "Y" + 90 : 25, // 25 = "Z" + 97 : 26, // 26 = "a" + 98 : 27, // 27 = "b" + 99 : 28, // 28 = "c" + 100 : 29, // 29 = "d" + 101 : 30, // 30 = "e" + 102 : 31, // 31 = "f" + 103 : 32, // 32 = "g" + 104 : 33, // 33 = "h" + 105 : 34, // 34 = "i" + 106 : 35, // 35 = "j" + 107 : 36, // 36 = "k" + 108 : 37, // 37 = "l" + 109 : 38, // 38 = "m" + 110 : 39, // 39 = "n" + 111 : 40, // 40 = "o" + 112 : 41, // 41 = "p" + 113 : 42, // 42 = "q" + 114 : 43, // 43 = "r" + 115 : 44, // 44 = "s" + 116 : 45, // 45 = "t" + 117 : 46, // 46 = "u" + 118 : 47, // 47 = "v" + 119 : 48, // 48 = "w" + 120 : 49, // 49 = "x" + 121 : 50, // 50 = "y" + 122 : 51, // 51 = "z" + 48 : 52, // 52 = "0" + 49 : 53, // 53 = "1" + 50 : 54, // 54 = "2" + 51 : 55, // 55 = "3" + 52 : 56, // 56 = "4" + 53 : 57, // 57 = "5" + 54 : 58, // 58 = "6" + 55 : 59, // 59 = "7" + 56 : 60, // 60 = "8" + 57 : 61, // 61 = "9" + 45 : 62, // 62 = "-" + 95 : 63, // 63 = "_" + ] + + + public static func encodeString(str: String) -> String { + + // Get the ascii representation and return + let data = str.data(using: .ascii)! + + let encData = encode(data: [UInt8](data)) + + let retStr = String(data: Data(encData), encoding: .ascii)! + + return retStr + } + + public static func encode(data: [UInt8]) -> [UInt8] { + + var result: [UInt8] = [] + + let size = data.count + + // Step through 3 bytes at a time + for i in stride(from: 0, to: size, by: 3) { + + + // Get the first 6 bits, and add the Base64 letter + let first = data[i] >> 2 + result.append(filenameSafeAlphabet[Int(first)]) + + // Get the remaining 2 bits from the previous byte + var second = (data[i] & 0b11) << 4 + + + // If there is more of the array, add the next 4 bits from byte 2, or return with padding for the 3rd and 4th characters + if i + 1 < size { + second |= (data[i + 1] & 0b11110000) >> 4 + result.append(filenameSafeAlphabet[Int(second)]) + } else { + result.append(filenameSafeAlphabet[Int(second)]) + result.append(padding) + result.append(padding) + return result + } + + + // Get the remaining 4 bits from the previous byte + var third = (data[i + 1] & 0b1111) << 2 + + // If there is more of the array, add the next 2 bits from byte 3, or return with padding for the 4th character + if i + 2 < size { + third |= (data[i + 2] & 0b11000000) >> 6 + result.append(filenameSafeAlphabet[Int(third)]) + } else { + result.append(filenameSafeAlphabet[Int(third)]) + result.append(padding) + return result + } + + + // Get the remaining 6 bits from the previous byte, add to the result + let forth = data[i + 2] & 0b00111111 + result.append(filenameSafeAlphabet[Int(forth)]) + } + + return result + } + + public static func decode(data: [UInt8]) -> [UInt8] { + + var result: [UInt8] = [] + + let size = data.count + // We loop over the 4 letters at a time + // We know it is padded, so we dont need to check for size + for i in stride(from: 0, to: size, by: 4) { + + // Get the 4 letters, then get back to their non-index values + let first = safeAlphabetToIndex[data[i]]! + let second = safeAlphabetToIndex[data[i + 1]]! + let third = safeAlphabetToIndex[data[i + 2]]! + let forth = safeAlphabetToIndex[data[i + 3]]! + + // Get the 3 binary letters from the four 6-bit ones + let l1 = first << 2 | ((second & 0b110000) >> 4) + let l2 = ((second & 0b1111) << 4) | ((third & 0b111100) >> 2) + let l3 = ((third & 0b11) << 6) | forth + + // Return the letters if they arent empty + result.append(l1) + + if l3 != 0 { + result.append(l2) + result.append(l3) + } else if l2 != 0 { + result.append(l2) + } + } + + return result + } + + public static func decodeString(str: String) -> String { + + // Get the ascii representation and return + let data = str.data(using: .ascii)! + + let decData = decode(data: [UInt8](data)) + + let retStr = String(data: Data(decData), encoding: .ascii)! + + return retStr + } +} + + + + diff --git a/AutoCat/Utils/Constants.swift b/AutoCat/Utils/Constants.swift new file mode 100644 index 0000000..5672f45 --- /dev/null +++ b/AutoCat/Utils/Constants.swift @@ -0,0 +1,10 @@ +import Foundation + +enum Constants { + static let googleAuthURL = "https://accounts.google.com/o/oauth2/v2/auth" + static let googleTokenURL = "https://oauth2.googleapis.com/token" + static let googleRedirectURL = "com.googleusercontent.apps.994679674451-k7clunkk4nicl6iuajdtc5u7hvustbdb:/oauth2callback" + + static let googleClientId = "994679674451-k7clunkk4nicl6iuajdtc5u7hvustbdb.apps.googleusercontent.com" + static let googleApiKey = "AIzaSyDVlrQj_05y6AeZNf8enpSWFIiHhgwfnGI" +} diff --git a/AutoCat/Utils/GoogleAuth.swift b/AutoCat/Utils/GoogleAuth.swift new file mode 100644 index 0000000..fbf2875 --- /dev/null +++ b/AutoCat/Utils/GoogleAuth.swift @@ -0,0 +1,2 @@ +import Foundation +