diff --git a/DBModule/.gitignore b/DBModule/.gitignore new file mode 100644 index 000000000..3b2981208 --- /dev/null +++ b/DBModule/.gitignore @@ -0,0 +1,9 @@ +.DS_Store +/.build +/Packages +/*.xcodeproj +xcuserdata/ +DerivedData/ +.swiftpm/config/registries.json +.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata +.netrc diff --git a/DBModule/Makefile b/DBModule/Makefile new file mode 100644 index 000000000..cca7592a0 --- /dev/null +++ b/DBModule/Makefile @@ -0,0 +1,4 @@ +.PHONY: test + +test: + xcodebuild -scheme DBModule -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 14,OS=16.4' test \ No newline at end of file diff --git a/DBModule/Package.resolved b/DBModule/Package.resolved new file mode 100644 index 000000000..cb4b75775 --- /dev/null +++ b/DBModule/Package.resolved @@ -0,0 +1,14 @@ +{ + "pins" : [ + { + "identity" : "sqlite.swift", + "kind" : "remoteSourceControl", + "location" : "https://github.com/stephencelis/SQLite.swift.git", + "state" : { + "revision" : "7a2e3cd27de56f6d396e84f63beefd0267b55ccb", + "version" : "0.14.1" + } + } + ], + "version" : 2 +} diff --git a/DBModule/Package.swift b/DBModule/Package.swift new file mode 100644 index 000000000..b0fa048d9 --- /dev/null +++ b/DBModule/Package.swift @@ -0,0 +1,33 @@ +// swift-tools-version: 5.8 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "DBModule", + products: [ + // Products define the executables and libraries a package produces, making them visible to other packages. + .library( + name: "DBModule", + targets: ["DBModule"]), + ], + dependencies: [ + .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.14.1") + ], + targets: [ + // Targets are the basic building blocks of a package, defining a module or a test suite. + // Targets can depend on other targets in this package and products from dependencies. + .binaryTarget( + name: "Internalsdk", + path: "../ios/internalsdk/Internalsdk.xcframework"), + .target( + name: "DBModule", + dependencies: [ + "Internalsdk", + .product(name: "SQLite", package: "SQLite.swift"), + ]), + .testTarget( + name: "DBModuleTests", + dependencies: ["DBModule"]), + ] +) diff --git a/DBModule/Sources/DBModule/Database.swift b/DBModule/Sources/DBModule/Database.swift new file mode 100644 index 000000000..c4c71f297 --- /dev/null +++ b/DBModule/Sources/DBModule/Database.swift @@ -0,0 +1,231 @@ +// +// DB.swift +// Runner +// +// Created by jigar fumakiya on 28/07/23. +// + +import Foundation +import Internalsdk +import SQLite + +public struct DatabaseFactory { + public static func getDbManager(databasePath: String) throws -> MinisqlDBProtocol { + guard !databasePath.isEmpty else { + throw NSError( + domain: "DatabasePathError", code: 1, + userInfo: [NSLocalizedDescriptionKey: "Database path cannot be blank"]) + } + let connection = try! Connection(databasePath) + connection.trace { print($0) } + return DatabaseManager(database: connection) + } +} + +class DatabaseManager: NSObject, MinisqlDBProtocol, MinisqlTxProtocol { + private let database: Connection + private let transactional: Bool + private var currentTransaction: DatabaseManager? + private var savepointName: String? + + init(database: Connection, transactional: Bool = false) { + self.database = database + self.transactional = transactional + } + + // Static function to get an instance of DatabaseManager + // Expose to Client + static func getDbManager(databasePath: String) throws -> MinisqlDBProtocol { + guard !databasePath.isEmpty else { + throw NSError( + domain: "DatabasePathError", code: 1, + userInfo: [NSLocalizedDescriptionKey: "Database path cannot be blank"]) + } + let connection = try! Connection(databasePath) + return DatabaseManager(database: connection) + } + + public func begin() throws -> MinisqlTxProtocol { + currentTransaction = DatabaseManager(database: database, transactional: true) + return currentTransaction! + } + + func commit() throws { + if let savepointName = savepointName { + try database.run("RELEASE '\(savepointName)'") + } + savepointName = nil + } + + func rollback() throws { + if let savepointName = savepointName { + try database.run("ROLLBACK TO SAVEPOINT '\(savepointName)'") + try database.run("RELEASE '\(savepointName)'") + } + savepointName = nil + } + + public func close() throws { + // Automatically manages the database connections + } + + private func beginTransaction() throws { + savepointName = "Savepoint\(UUID().uuidString)" + if let savepointName = savepointName { + try database.run("SAVEPOINT '\(savepointName)'") + } + } + + func exec(_ query: String?, args: MinisqlValuesProtocol?) throws -> MinisqlResultProtocol { + guard let query = query, let args = args else { + throw NSError( + domain: "ArgumentError", code: 1, + userInfo: [NSLocalizedDescriptionKey: "Query or arguments are nil"]) + } + + let bindings = ValueUtil.toBindingsArray(args) + let statement = try database.prepare(query) + // Start a transaction if none has been started yet + if transactional && savepointName == nil { + try beginTransaction() + } + + try runStatement(statement, bindings) + return QueryResult(changes: database.changes) + } + + func query(_ query: String?, args: MinisqlValuesProtocol?) throws -> MinisqlRowsProtocol { + guard let query = query, let args = args else { + throw NSError( + domain: "ArgumentError", code: 1, + userInfo: [NSLocalizedDescriptionKey: "Query or arguments are nil"]) + } + + let bindings = ValueUtil.toBindingsArray(args) + let statement = try database.prepare(query) + // Start a transaction if none has been started yet + if transactional && savepointName == nil { + try beginTransaction() + } + + var rows: [Statement.Element] = [] + + for row in try runStatement(statement, bindings) { + rows.append(row) + } + + return RowData(rows: rows) + } + + func runStatement(_ statement: Statement, _ bindings: [Binding?]) throws -> Statement { + do { + return try statement.run(bindings) + } catch let SQLite.Result.error(message, code, _) { + throw NSError(domain: message, code: Int(code), userInfo: nil) + } catch let error { + throw NSError(domain: String(describing: error), code: 0, userInfo: nil) + } + } +} + +class QueryResult: NSObject, MinisqlResultProtocol { + let changes: Int + + init(changes: Int) { + self.changes = changes + } + + func lastInsertId(_ ret0_: UnsafeMutablePointer?) throws { + ret0_?.pointee = Int64(changes) + } + + func rowsAffected(_ ret0_: UnsafeMutablePointer?) throws { + ret0_?.pointee = Int64(changes) + } +} + +class RowData: NSObject, MinisqlRowsProtocol { + let rows: [Statement.Element] + var currentIndex: Int = -1 + private let syncQueue = DispatchQueue(label: "com.lantern.RowData.syncQueue") + + init(rows: [Statement.Element]) { + self.rows = rows + } + + func close() throws { + // Not sure what to put here + } + + func next() -> Bool { + currentIndex += 1 + return currentIndex < rows.count + } + + /** + This method scans the current row and converts its values to `MinisqlValue` objects. + This method assumes that `values` is an object that supports setting values by index, and `rows` is an array of arrays where each inner array represents a row from a database and contains values of type `Binding`. + - Parameter values: An object that conforms to `MinisqlValuesProtocol`. This object will be populated with the values from the current row, converted to `MinisqlValue` objects. + - Throws: An `NSError` if `values` is `nil` or if `currentIndex` is outside the bounds of the `rows` array. + - Note: This method updates `currentIndex` to point to the next row. If there are no more rows, `next()` will return `false`. + */ + func scan(_ values: MinisqlValuesProtocol?) throws { + try syncQueue.sync { + if values == nil { + throw NSError( + domain: "Scan method failed", code: 0, + userInfo: [NSLocalizedDescriptionKey: "Values object is nil"]) + } + if currentIndex >= rows.count { + throw NSError( + domain: "Scan method failed", code: 0, + userInfo: [NSLocalizedDescriptionKey: "Current index is out of bounds"]) + } + let currentRow = rows[currentIndex] + for (index, value) in currentRow.enumerated() { + let miniSqlValue = values!.get(index)! + if value != nil { + ValueUtil.setValueFromBinding(binding: value!, value: miniSqlValue) + } + } + } + } +} + +public struct ValueArrayFactory { + public static func createValueArrayHandler(values: [MinisqlValue]) -> MinisqlValuesProtocol { + return ValueArrayHandler(values: values) + } +} + +class ValueArrayHandler: NSObject, MinisqlValuesProtocol { + + var values: [MinisqlValue] + + init(values: [MinisqlValue]) { + self.values = values + } + + public func get(_ index: Int) -> MinisqlValue? { + guard index < values.count else { + return nil + } + return values[index] + } + + func len() -> Int { + return values.count + } + + func set(_ index: Int, value: MinisqlValue?) { + guard index < values.count else { + return + } + + guard let value = value else { + return + } + + values[index] = value + } +} diff --git a/DBModule/Sources/DBModule/ValueUtil.swift b/DBModule/Sources/DBModule/ValueUtil.swift new file mode 100644 index 000000000..830db1b77 --- /dev/null +++ b/DBModule/Sources/DBModule/ValueUtil.swift @@ -0,0 +1,146 @@ +// +// Value.swift +// Runner +// +// Created by jigar fumakiya on 28/07/23. +// + +import Foundation +import Internalsdk +import SQLite + +public class ValueUtil { + // Define the types constants + public static let TYPE_BYTES = Int(MinisqlValueTypeBytes) + public static let TYPE_STRING = Int(MinisqlValueTypeString) + public static let TYPE_INT = Int(MinisqlValueTypeInt) + public static let TYPE_BOOL = Int(MinisqlValueTypeBool) + + public static func makeValue(from anyValue: Any) -> MinisqlValue { + let value: MinisqlValue! + + switch anyValue { + case is String: + value = MinisqlNewValueString(anyValue as! String) + case is Int: + value = MinisqlNewValueInt(anyValue as! Int) + case is Bool: + value = MinisqlNewValueBool(anyValue as! Bool) + case is UInt8: + let blob = anyValue as! SQLite.Blob + let data = Data(blob.bytes) + value = MinisqlNewValueBytes(data) + default: + fatalError("Unsupported type \(type(of: anyValue)) with value: \(anyValue)") + } + return value + } + + public static func convertFromMinisqlValue(from internalsdkValue: MinisqlValue) -> Any? { + switch internalsdkValue.type { + case TYPE_STRING: + return internalsdkValue.string() + case TYPE_INT: + return Int(internalsdkValue.int_() as Int) + case TYPE_BYTES: + return internalsdkValue.bytes()! + case TYPE_BOOL: + return internalsdkValue.bool_() + default: + fatalError("Unsupported type") + } + } + + public static func toBindingsArray(_ args: MinisqlValuesProtocol) -> [Binding?] { + var bindings = [Binding?]() + for i in 0.. MinisqlValue? { + switch anyValue { + case is String: + return MinisqlNewValueString(anyValue as! String) + case is Int: + return MinisqlNewValueInt(anyValue as! Int) + case is Bool: + return MinisqlNewValueBool(anyValue as! Bool) + case is [Any]: // For arrays + if let jsonData = try? JSONSerialization.data(withJSONObject: anyValue, options: []), + let jsonString = String(data: jsonData, encoding: .utf8) + { + return MinisqlNewValueString(jsonString) + } + case is [String: Any]: // For dictionaries + if let jsonData = try? JSONSerialization.data(withJSONObject: anyValue, options: []), + let jsonString = String(data: jsonData, encoding: .utf8) + { + return MinisqlNewValueString(jsonString) + } + default: + return MinisqlNewValueString("\(anyValue)") + } + return nil + } + +} +extension Binding { + var bindingType: String { + switch self { + case is Int64: + return "Int64" + case is Double: + return "Double" + case is String: + return "String" + case is Bool: + return "Bool" + case is Blob: + return "Blob" + case is NSNumber: + return "NSNumber" + default: + return "Unknown" + } + } +} diff --git a/DBModule/Tests/DBModuleTests/DBModuleTests.swift b/DBModule/Tests/DBModuleTests/DBModuleTests.swift new file mode 100644 index 000000000..bd281a2bd --- /dev/null +++ b/DBModule/Tests/DBModuleTests/DBModuleTests.swift @@ -0,0 +1,77 @@ +import Internalsdk +import XCTest + +@testable import DBModule + +final class DBModuleTests: XCTestCase, TestsupportTestingTProtocol { + func errorf(_ text: String?) { + XCTFail(text!) + } + + func failNow() { + XCTFail("failing now!") + } + + override func setUp() { + super.setUp() + continueAfterFailure = false + } + + func testTransactions() throws { + let db = try newDB() + TestsupportTestTransactions(self, db) + } + + func testSubscriptions() throws { + let db = try newDB() + TestsupportTestSubscriptions(self, db) + } + + func testSubscribeToInitialDetails() throws { + let db = try newDB() + TestsupportTestSubscribeToInitialDetails(self, db) + } + + func testDetailSubscriptionModifyDetails() throws { + let db = try newDB() + TestsupportTestDetailSubscriptionModifyDetails(self, db) + } + + func testDetailSubscriptionModifyIndex() throws { + let db = try newDB() + TestsupportTestDetailSubscriptionModifyIndex(self, db) + } + + func testList() throws { + let db = try newDB() + TestsupportTestList(self, db) + } + + func testSearch() throws { + let db = try newDB() + TestsupportTestSearch(self, db) + } + + func testSearchChinese() throws { + let db = try newDB() + TestsupportTestSearchChinese(self, db) + } + + private func newDB() throws -> MinisqlDBProtocol { + return try DatabaseFactory.getDbManager(databasePath: newDBPath()) + } + + private func newDBPath() -> String { + let fileManager = FileManager.default + let directory = fileManager.temporaryDirectory + let subdirectory = UUID().uuidString + let dbDir = directory.appendingPathComponent(subdirectory) + do { + try fileManager.createDirectory(at: dbDir, withIntermediateDirectories: true, attributes: nil) + let dbLocation = dbDir.appendingPathComponent("db").path + return dbLocation + } catch { + return "" // Return an empty string or handle the error accordingly. + } + } +} diff --git a/Makefile b/Makefile index 4adf1e59b..8e12bae93 100644 --- a/Makefile +++ b/Makefile @@ -411,11 +411,11 @@ build-framework: assert-go-version install-gomobile @echo "generating Ios.xcFramework" go env -w 'GOPRIVATE=github.com/getlantern/*' && \ gomobile init && \ - gomobile bind -target=ios \ + gomobile bind -target=ios,iossimulator \ -tags='headless lantern ios' \ - -ldflags="$(LDFLAGS)" \ + -ldflags="$(LDFLAGS)" \ $(GOMOBILE_EXTRA_BUILD_FLAGS) \ - github.com/getlantern/android-lantern/internalsdk github.com/getlantern/pathdb/minisql + github.com/getlantern/android-lantern/internalsdk github.com/getlantern/pathdb/testsupport github.com/getlantern/pathdb/minisql github.com/getlantern/flashlight/v7/ios @echo "moving framework" mkdir -p $(INTERNALSDK_FRAMEWORK_DIR) mv ./$(INTERNALSDK_FRAMEWORK_NAME) $(INTERNALSDK_FRAMEWORK_DIR)/$(INTERNALSDK_FRAMEWORK_NAME) diff --git a/android/app/src/main/java/org/getlantern/mobilesdk/model/SessionManager.kt b/android/app/src/main/java/org/getlantern/mobilesdk/model/SessionManager.kt index 1c9292d3a..13914a314 100644 --- a/android/app/src/main/java/org/getlantern/mobilesdk/model/SessionManager.kt +++ b/android/app/src/main/java/org/getlantern/mobilesdk/model/SessionManager.kt @@ -583,6 +583,8 @@ abstract class SessionManager(application: Application) : Session { // initialize email address to empty string (if it doesn't already exist) if (email().isEmpty()) setEmail("") + // this condition is unnecessary + // Todo remove this soon if (prefs.getInt(ACCEPTED_TERMS_VERSION, 0) == 0) prefs.edit().putInt(ACCEPTED_TERMS_VERSION, 0).apply() Logger.debug(TAG, "prefs.edit() finished at ${System.currentTimeMillis() - start}") diff --git a/go.mod b/go.mod index 436fcf40d..333240d46 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/getlantern/android-lantern go 1.19 // replace github.com/getlantern/flashlight/v7 => ../flashlight -replace github.com/getlantern/pathdb => ../pathDb/pathdb +// replace github.com/getlantern/pathdb => ../pathDb/pathDb // replace github.com/getlantern/fronted => ../fronted @@ -36,17 +36,18 @@ require ( github.com/getlantern/dnsgrab v0.0.0-20211216020425-5d5e155a01a8 github.com/getlantern/errors v1.0.3 github.com/getlantern/eventual/v2 v2.0.2 - github.com/getlantern/flashlight/v7 v7.5.39 + github.com/getlantern/flashlight/v7 v7.6.9 github.com/getlantern/golog v0.0.0-20230503153817-8e72de7e0a65 github.com/getlantern/idletiming v0.0.0-20201229174729-33d04d220c4e github.com/getlantern/ipproxy v0.0.0-20230511223023-ee52513fd782 github.com/getlantern/mtime v0.0.0-20200417132445-23682092d1f7 - github.com/getlantern/pathdb v0.0.0-20230824172245-c389d5ee88da + github.com/getlantern/pathdb v0.0.0-20231002065638-0396c0b749ef github.com/getlantern/replica v0.14.2 github.com/gorilla/mux v1.8.0 github.com/stretchr/testify v1.8.3 - golang.org/x/mobile v0.0.0-20221110043201-43a038452099 - golang.org/x/net v0.12.0 + golang.org/x/mobile v0.0.0-20230922142353-e2f452493d57 + golang.org/x/net v0.15.0 + google.golang.org/protobuf v1.30.0 nhooyr.io/websocket v1.8.7 ) @@ -110,7 +111,7 @@ require ( github.com/felixge/httpsnoop v1.0.3 // indirect github.com/gaukas/godicttls v0.0.3 // indirect github.com/getlantern/borda v0.0.0-20230421223744-4e208135f082 // indirect - github.com/getlantern/broflake v0.0.0-20230822184836-0b9bbcadd5c6 // indirect + github.com/getlantern/broflake v0.0.0-20230926124502-c8269f54a586 // indirect github.com/getlantern/bufconn v0.0.0-20210901195825-fd7c0267b493 // indirect github.com/getlantern/byteexec v0.0.0-20220903142956-e6ed20032cfd // indirect github.com/getlantern/cmux v0.0.0-20230301223233-dac79088a4c0 // indirect @@ -216,7 +217,6 @@ require ( github.com/kr/binarydist v0.1.0 // indirect github.com/libp2p/go-buffer-pool v0.1.0 // indirect github.com/lispad/go-generics-tools v1.1.0 // indirect - github.com/mattn/go-sqlite3 v1.14.11 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/mdlayher/netlink v1.1.0 // indirect github.com/mholt/archiver/v3 v3.5.1 // indirect @@ -297,18 +297,17 @@ require ( go.uber.org/atomic v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.24.0 // indirect - golang.org/x/crypto v0.11.0 // indirect + golang.org/x/crypto v0.13.0 // indirect golang.org/x/exp v0.0.0-20230510235704-dd950f8aeaea // indirect golang.org/x/mod v0.12.0 // indirect golang.org/x/sync v0.3.0 // indirect - golang.org/x/sys v0.10.0 // indirect - golang.org/x/text v0.11.0 // indirect + golang.org/x/sys v0.12.0 // indirect + golang.org/x/text v0.13.0 // indirect golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.11.0 // indirect + golang.org/x/tools v0.13.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect google.golang.org/grpc v1.55.0 // indirect - google.golang.org/protobuf v1.30.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect howett.net/plist v1.0.0 // indirect diff --git a/go.sum b/go.sum index 78e7de71d..bd8f31e22 100644 --- a/go.sum +++ b/go.sum @@ -311,8 +311,8 @@ github.com/getlantern/autoupdate v0.0.0-20211217175350-d0b211f39ba7 h1:/efTOJpxX github.com/getlantern/autoupdate v0.0.0-20211217175350-d0b211f39ba7/go.mod h1:+X8pAviVhThDBjPEqLUB0iO7EPxhpWk7Q9VYxvz6rCY= github.com/getlantern/borda v0.0.0-20230421223744-4e208135f082 h1:Ka9rIAgef8zYhBr/VgLrt5+Qs7zE33g0OButzpIGcSs= github.com/getlantern/borda v0.0.0-20230421223744-4e208135f082/go.mod h1:oCpQojhSaK0F/6rWMrDvN8/QFHQhTC9Gb3uf7GcqPQQ= -github.com/getlantern/broflake v0.0.0-20230822184836-0b9bbcadd5c6 h1:/G1jb7daSg1FkEEFbvRRM7A927uErx5vEMVwrTcVzp0= -github.com/getlantern/broflake v0.0.0-20230822184836-0b9bbcadd5c6/go.mod h1:Ehdl8IASN5rJi9brldVuCjTDcSU25nvaGRlzNprgeQo= +github.com/getlantern/broflake v0.0.0-20230926124502-c8269f54a586 h1:d8z7UmXW6aKEqhM4lwmYE5IBHjNoii6xP4B5ikWEq+s= +github.com/getlantern/broflake v0.0.0-20230926124502-c8269f54a586/go.mod h1:Ehdl8IASN5rJi9brldVuCjTDcSU25nvaGRlzNprgeQo= github.com/getlantern/bufconn v0.0.0-20190625204133-a08544339f8d/go.mod h1:d6O4RY+V87kIt4o9wru4SaNo7C2NAkD3YnmJFXEpODo= github.com/getlantern/bufconn v0.0.0-20210901195825-fd7c0267b493 h1:8WjDNmpDLFVsAfcnHxqF4pfVKkdAQxyJ9iCHB4LxSfc= github.com/getlantern/bufconn v0.0.0-20210901195825-fd7c0267b493/go.mod h1:d6O4RY+V87kIt4o9wru4SaNo7C2NAkD3YnmJFXEpODo= @@ -372,8 +372,8 @@ github.com/getlantern/fdcount v0.0.0-20210503151800-5decd65b3731/go.mod h1:XZwE+ github.com/getlantern/filepersist v0.0.0-20160317154340-c5f0cd24e799/go.mod h1:8DGAx0LNUfXNnEH+fXI0s3OCBA/351kZCiz/8YSK3i8= github.com/getlantern/filepersist v0.0.0-20210901195658-ed29a1cb0b7c h1:mcz27xtAkb1OuOLBct/uFfL1p3XxAIcFct82GbT+UZM= github.com/getlantern/filepersist v0.0.0-20210901195658-ed29a1cb0b7c/go.mod h1:8DGAx0LNUfXNnEH+fXI0s3OCBA/351kZCiz/8YSK3i8= -github.com/getlantern/flashlight/v7 v7.5.39 h1:g/wJR/SVDUdE7lrqonpN3zSJ451IfcpwEKBdmZNSHuI= -github.com/getlantern/flashlight/v7 v7.5.39/go.mod h1:FLZhyLFeSojL4sRjQEaLRtVl4RB1DdliWegTsIqgzlI= +github.com/getlantern/flashlight/v7 v7.6.9 h1:GqGY05jwvCy7sVQ7vOjLN51tT90yC0ZYMIp5MNQ0NSY= +github.com/getlantern/flashlight/v7 v7.6.9/go.mod h1:E81GrKxK8GU5mRzskGF3+4wZKMrEobE+Ff13S8ryPvs= github.com/getlantern/framed v0.0.0-20190601192238-ceb6431eeede h1:yrU6Px3ZkvCsDLPryPGi6FN+2iqFPq+JeCb7EFoDBhw= github.com/getlantern/framed v0.0.0-20190601192238-ceb6431eeede/go.mod h1:nhnoiS6DE6zfe+BaCMU4YI01UpsuiXnDqM5S8jxHuuI= github.com/getlantern/fronted v0.0.0-20230601004823-7fec719639d8 h1:r/Z/SPPIfLXDI3QA7/tE6nOfPncrqeUPDjiFjnNGP50= @@ -484,6 +484,8 @@ github.com/getlantern/osversion v0.0.0-20230401075644-c2a30e73c451 h1:3Nn0AqIlIm github.com/getlantern/osversion v0.0.0-20230401075644-c2a30e73c451/go.mod h1:kaUdXyKE1Y8bwPnlN7ChFXWnkADpL0zZrk8F0XbpKcc= github.com/getlantern/packetforward v0.0.0-20201001150407-c68a447b0360 h1:pijUoofaQcAM/8zbDzZM2LQ90kGVbKfnSAkFnQwLZZU= github.com/getlantern/packetforward v0.0.0-20201001150407-c68a447b0360/go.mod h1:nsJPNYUSY96xB+p7uiDW8O4uiKea+KjeUdS5d6tf9IU= +github.com/getlantern/pathdb v0.0.0-20231002065638-0396c0b749ef h1:V4gLYfc0ABFib0xMV2wS9ylFMSNcz+8jaOEkTSfEbyc= +github.com/getlantern/pathdb v0.0.0-20231002065638-0396c0b749ef/go.mod h1:SFQy+f58IbLpnbq2nVqlq7ccwaUiO7ablKv631WVIuc= github.com/getlantern/preconn v0.0.0-20180328114929-0b5766010efe/go.mod h1:FvIxQD61iYA42UjaJyzGl9DNne8jbowbgicdeNk/7kE= github.com/getlantern/preconn v1.0.0 h1:DsY3l/y/BJUj86WyaxXylbJnCC9QbKcc3D6js6rFL60= github.com/getlantern/preconn v1.0.0/go.mod h1:i/AnXvx715Fq7HgZLlmQlw3sGfEkku8BQT5hLHMK4+k= @@ -825,7 +827,6 @@ github.com/mattn/go-isatty v0.0.2/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNx github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= github.com/mattn/go-sqlite3 v1.14.11 h1:gt+cp9c0XGqe9S/wAHTL3n/7MqY+siPWgWJgqdsFrzQ= -github.com/mattn/go-sqlite3 v1.14.11/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= @@ -1240,8 +1241,8 @@ golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU= golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= -golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA= -golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= +golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1272,8 +1273,8 @@ golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPI golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mobile v0.0.0-20221110043201-43a038452099 h1:aIu0lKmfdgtn2uTj7JI2oN4TUrQvgB+wzTPO23bCKt8= -golang.org/x/mobile v0.0.0-20221110043201-43a038452099/go.mod h1:aAjjkJNdrh3PMckS4B10TGS2nag27cbKR1y2BpUxsiY= +golang.org/x/mobile v0.0.0-20230922142353-e2f452493d57 h1:Q6NT8ckDYNcwmi/bmxe+XbiDMXqMRW1xFBtJ+bIpie4= +golang.org/x/mobile v0.0.0-20230922142353-e2f452493d57/go.mod h1:wEyOn6VvNW7tcf+bW/wBz1sehi2s2BZ4TimyR7qZen4= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= @@ -1341,8 +1342,8 @@ golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50= -golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= +golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/oauth2 v0.0.0-20170912212905-13449ad91cb2/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1437,8 +1438,8 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= -golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -1461,8 +1462,8 @@ golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= -golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.0.0-20170424234030-8be79e1e0910/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1521,8 +1522,8 @@ golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.11.0 h1:EMCa6U9S2LtZXLAMoWiR/R8dAQFRqbAitmbJ2UKhoi8= -golang.org/x/tools v0.11.0/go.mod h1:anzJrxPjNtfgiYQYirP2CPGzGLxrH2u2QBhn6Bf3qY8= +golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/internalsdk/http_client.go b/internalsdk/http_client.go new file mode 100644 index 000000000..0f5add73a --- /dev/null +++ b/internalsdk/http_client.go @@ -0,0 +1 @@ +package internalsdk diff --git a/internalsdk/messaging_model.go b/internalsdk/messaging_model.go index 0933b124e..d975526bb 100644 --- a/internalsdk/messaging_model.go +++ b/internalsdk/messaging_model.go @@ -17,7 +17,7 @@ func NewMessagingModel(schema string, mdb minisql.DB) (*MessagingModel, error) { if err != nil { return nil, err } - // Initialization for SessionModel + // Initialization for Messaging initMessagingModel(base.(*baseModel)) model := &MessagingModel{base.(*baseModel)} return model, nil diff --git a/internalsdk/model.go b/internalsdk/model.go index 666e7c0de..d92d1b042 100644 --- a/internalsdk/model.go +++ b/internalsdk/model.go @@ -54,6 +54,7 @@ type MyValue struct { // Custom JSON serialization method for Value type func (v *MyValue) MarshalJSON() ([]byte, error) { + switch v.Type { case minisql.ValueTypeBytes: return json.Marshal(map[string]interface{}{ @@ -112,7 +113,11 @@ func (m *baseModel) Subscribe(req *SubscriptionRequest) error { log.Debugf("Error extracting raw value:", err) return err } - val := minisql.NewValue(rawValue) + log.Debugf("Got update for value %v - value: %v rawValue: %v", k, rawValue, string(itemWithRaw.Value.Bytes)) + // When serlizeing might get other other type + //make sure to convered to only supported types + convertedValue := convertValueToSupportedTypes(rawValue) + val := minisql.NewValue(convertedValue) // Need wrap to coz we need to send to json myVal := &MyValue{Value: *val} updatesMap[k] = &ItemInterface{ diff --git a/internalsdk/session_model.go b/internalsdk/session_model.go index 138e9e6bd..d393c2f91 100644 --- a/internalsdk/session_model.go +++ b/internalsdk/session_model.go @@ -1,10 +1,18 @@ package internalsdk import ( + "bytes" + "encoding/json" "fmt" + "net/http" + "path/filepath" + "strconv" + "github.com/getlantern/flashlight/v7/common" + "github.com/getlantern/flashlight/v7/logging" "github.com/getlantern/pathdb" "github.com/getlantern/pathdb/minisql" + "google.golang.org/protobuf/proto" ) // Custom Model implemnation @@ -15,6 +23,7 @@ type SessionModel struct { // List of const we are using for Session Model // Might be eaier to move all const at one place +// All keys are expose to front end so we can use same to avoid duplication and reduce error const DEVICE_ID = "deviceid" const PAYMENT_TEST_MODE = "paymentTestMode" const USER_ID = "userid" @@ -35,7 +44,8 @@ const REFERRAL_CODE = "referral" const FORCE_COUNTRY = "forceCountry" const DNS_DETECTOR = "dns_detector" const PROVIDER = "provider" -const EMAIL_ADDRESS = "email_address" +const EMAIL_ADDRESS = "emailAddress" + const CURRENCY_CODE = "currency_Code" const PRO_USER = "prouser" const REPLICA_ADDR = "replicaAddr" @@ -44,6 +54,32 @@ const LANG = "lang" const ACCEPTED_TERMS_VERSION = "accepted_terms_version" const ADS_ENABLED = "adsEnabled" const CAS_ADS_ENABLED = "casAsEnabled" +const CURRENT_TERMS_VERSION = 1 +const IS_PLAY_VERSION = "playVersion" +const SET_SELECTED_TAB = "/selectedTab" + +// All method names +// this expose to client IOS & Andorid +const SESSION_MODEL_METHOD_INIT_MODEL = "initSesssionModel" +const SESSION_MODEL_METHOD_SET_TIMEZONE = "setTimeZone" +const SESSION_MODEL_METHOD_GET_BANDWIDTH = "getBandwidth" +const SESSION_MODEL_METHOD_SET_DEVICEID = "setDeviceId" +const SESSION_MODEL_METHOD_SET_REFERAL_CODE = "setReferalCode" +const SESSION_MODEL_METHOD_SET_FORCE_COUNTRY = "setForceCountry" +const SESSION_MODEL_METHOD_SET_DNS_SERVER = "setDNSServer" +const SESSION_MODEL_METHOD_SET_PROVIDER = "setProvider" +const SESSION_MODEL_METHOD_SET_EMAIL = "setEmail" +const SESSION_MODEL_METHOD_SET_PRO_USER = "setProUser" +const SESSION_MODEL_METHOD_SET_LOCAL = "setLanguage" +const SESSION_MODEL_METHOD_SET_CURRENCY = "setCurrency" +const SESSION_MODEL_METHOD_ACCEPT_TERMS = "acceptTerms" +const SESSION_MODEL_METHOD_SET_STORE_VERSION = "setStoreVersion" +const SESSION_MODEL_METHOD_SET_SELECTED_TAB = "setSelectedTab" +const SESSION_MODEL_METHOD_CREATE_USER = "createUser" + +type TabData struct { + Tab string `json:"tab"` +} // NewSessionModel initializes a new SessionModel instance. func NewSessionModel(schema string, mdb minisql.DB) (*SessionModel, error) { @@ -56,17 +92,20 @@ func NewSessionModel(schema string, mdb minisql.DB) (*SessionModel, error) { return model, nil } +// TO check if session model implemnets all method or not +// var s Session = &SessionModel{} + func (s *SessionModel) InvokeMethod(method string, arguments minisql.Values) (*minisql.Value, error) { switch method { - case "initSesssionModel": + case SESSION_MODEL_METHOD_INIT_MODEL: jsonString := arguments.Get(0) - err := initSessionModel(s.baseModel, jsonString.String()) + err := initSessionModel(s, jsonString.String()) if err != nil { return nil, err } else { return minisql.NewValueBool(true), nil } - case "setTimeZone": + case SESSION_MODEL_METHOD_SET_TIMEZONE: // Get timezone id timezoneId := arguments.Get(0) err := setTimeZone(s.baseModel, timezoneId.String()) @@ -75,7 +114,23 @@ func (s *SessionModel) InvokeMethod(method string, arguments minisql.Values) (*m } else { return minisql.NewValueBool(true), nil } - case "setReferalCode": + case SESSION_MODEL_METHOD_GET_BANDWIDTH: + limit, err := getBandwidthLimit(s.baseModel) + if err != nil { + return nil, err + } else { + return minisql.NewValueString(limit), nil + } + + case SESSION_MODEL_METHOD_SET_DEVICEID: + deviceID := arguments.Get(0) + err := setDeviceId(s.baseModel, deviceID.String()) + if err != nil { + return nil, err + } else { + return minisql.NewValueBool(true), nil + } + case SESSION_MODEL_METHOD_SET_REFERAL_CODE: referralCode := arguments.Get(0) err := setReferalCode(s.baseModel, referralCode.String()) if err != nil { @@ -83,7 +138,7 @@ func (s *SessionModel) InvokeMethod(method string, arguments minisql.Values) (*m } else { return minisql.NewValueBool(true), nil } - case "setForceCountry": + case SESSION_MODEL_METHOD_SET_FORCE_COUNTRY: forceCountry := arguments.Get(0) err := setForceCountry(s.baseModel, forceCountry.String()) if err != nil { @@ -91,15 +146,15 @@ func (s *SessionModel) InvokeMethod(method string, arguments minisql.Values) (*m } else { return minisql.NewValueBool(true), nil } - case "setDNSServer": - // Todo Implement SetDns server - err := setDNSServer(s.baseModel, "Test") + case SESSION_MODEL_METHOD_SET_DNS_SERVER: + dns := arguments.Get(0) + err := setDNSServer(s.baseModel, dns.String()) if err != nil { return nil, err } else { return minisql.NewValueBool(true), nil } - case "setProvider": + case SESSION_MODEL_METHOD_SET_PROVIDER: // Todo Implement setProvider server err := setProvider(s.baseModel, "Test") if err != nil { @@ -108,7 +163,7 @@ func (s *SessionModel) InvokeMethod(method string, arguments minisql.Values) (*m return minisql.NewValueBool(true), nil } - case "setEmail": + case SESSION_MODEL_METHOD_SET_EMAIL: // Todo Implement setEmail server err := setEmail(s.baseModel, "Test") if err != nil { @@ -116,7 +171,7 @@ func (s *SessionModel) InvokeMethod(method string, arguments minisql.Values) (*m } else { return minisql.NewValueBool(true), nil } - case "setCurrency": + case SESSION_MODEL_METHOD_SET_CURRENCY: // Todo Implement setCurrency server err := setCurrency(s.baseModel, "Test") if err != nil { @@ -124,76 +179,195 @@ func (s *SessionModel) InvokeMethod(method string, arguments minisql.Values) (*m } else { return minisql.NewValueBool(true), nil } - case "setProUser": - // Todo Implement setCurrency server + case SESSION_MODEL_METHOD_SET_PRO_USER: err := setProUser(s.baseModel, false) if err != nil { return nil, err } else { return minisql.NewValueBool(true), nil } - case "setLocal": + case SESSION_MODEL_METHOD_SET_LOCAL: local := arguments.Get(0) - err := setLocale(s.baseModel, local.String()) + value, err := extractLangValueFromJSON(local.String()) if err != nil { return nil, err + } + langErr := setLanguage(s.baseModel, value) + if langErr != nil { + return nil, err } else { return minisql.NewValueBool(true), nil } - default: + case SESSION_MODEL_METHOD_ACCEPT_TERMS: + err := acceptTerms(s.baseModel) + if err != nil { + return nil, err + } else { + return minisql.NewValueBool(true), nil + } + + case SESSION_MODEL_METHOD_SET_STORE_VERSION: + IsStoreVersion := arguments.Get(0) + err := setStoreVersion(s.baseModel, IsStoreVersion.Bool()) + if err != nil { + return nil, err + } else { + return minisql.NewValueBool(true), nil + } + case SESSION_MODEL_METHOD_SET_SELECTED_TAB: + jsonString := arguments.Get(0).String() + + var tabData TabData + err := json.Unmarshal([]byte(jsonString), &tabData) + if err != nil { + return nil, err + } + err = setSelectedTab(s.baseModel, tabData.Tab) + if err != nil { + return nil, err + } + return minisql.NewValueBool(true), nil + case SESSION_MODEL_METHOD_CREATE_USER: + local := arguments.Get(0) + err := userCreate(s.baseModel, local.String()) + if err != nil { + return nil, err + } else { + return minisql.NewValueBool(true), nil + } + default: return s.baseModel.InvokeMethod(method, arguments) } } +// Internal functions that manage method +func (s *SessionModel) StartService(configDir string, + locale string, + settings Settings) { + logging.EnableFileLogging(common.DefaultAppName, filepath.Join(configDir, "logs")) + session := &panickingSessionImpl{s} + startOnce.Do(func() { + go run(configDir, locale, settings, session) + }) + +} + // InvokeMethod handles method invocations on the SessionModel. -func initSessionModel(m *baseModel, jsonString string) error { +func initSessionModel(session *SessionModel, jsonString string) error { + //Check if email if emoty + email, err := session.baseModel.db.Get(EMAIL_ADDRESS) + if err != nil { + log.Debugf("Init Session email error value %v", err) + return err + } + emailStr := string(email) + if emailStr == "" { + log.Debugf("Init Session setting email value to an empty string") + setEmail(session.baseModel, "") + } // Init few path for startup - return putFromJson(jsonString, m.db) + err = putFromJson(jsonString, session.baseModel.db) + if err != nil { + return err + } + //Check if user is already registerd or not + userId, err := session.GetUserID() + if err != nil { + return err + } + if userId == 0 { + local, err := session.Locale() + if err != nil { + return err + } + // Create user + err = userCreate(session.baseModel, local) + if err != nil { + return err + } + } + return nil } func (s *SessionModel) GetAppName() string { return "Lantern-IOS" } -func (s *SessionModel) GetDeviceID() string { +func setDeviceId(m *baseModel, deviceID string) error { + // Find better way to do it + err := pathdb.Mutate(m.db, func(tx pathdb.TX) error { + pathdb.Put[string](tx, DEVICE_ID, deviceID, "") + return nil + }) + return err +} + +func (s *SessionModel) GetDeviceID() (string, error) { byte, err := s.baseModel.db.Get(DEVICE_ID) - panicIfNecessary(err) + if err != nil { + return "", err + } //Todo Find better way to deserialize the values // Also fine generic way - return string(byte) + return string(byte), nil } -func (s *SessionModel) GetUserID() string { +// Todo There is some issue with user id changeing it value +// When Coverting from bytes to Float +func (s *SessionModel) GetUserID() (int64, error) { paymentTestMode, err := s.baseModel.db.Get(PAYMENT_TEST_MODE) - panicIfNecessary(err) + if err != nil { + return 0, err + } //Todo find way to deserialize the values paymentTestModeStr := string(paymentTestMode) - if paymentTestModeStr == "true" { + if paymentTestModeStr == "true" && paymentTestModeStr != "" { // When we're testing payments, use a specific test user ID. This is a user in our // production environment but that gets special treatment from the proserver to hit // payment providers' test endpoints. - return "9007199254740992L" + i64, err := strconv.ParseInt("9007199254740992L", 10, 64) + if err != nil { + return 0, err + } + return i64, nil } else { userId, err := s.baseModel.db.Get(USER_ID) - panicIfNecessary(err) - return string(userId) + if err != nil { + return 0, err + } + + //If userid is null or emtpy return zero to avoid crash + if string(userId) == "" { + return 0, nil + } + userId = userId[1:] + newUserId, err := BytesToFloat64LittleEndian(userId) + if err != nil { + return 0, err + } + i64 := int64(newUserId) + return i64, nil } } -func (s *SessionModel) GetToken() string { +func (s *SessionModel) GetToken() (string, error) { paymentTestMode, err := s.baseModel.db.Get(PAYMENT_TEST_MODE) - panicIfNecessary(err) + if err != nil { + return "", err + } //Todo find way to deserialize the values paymentTestModeStr := string(paymentTestMode) if paymentTestModeStr == "true" { // When we're testing payments, use a specific test user ID. This is a user in our // production environment but that gets special treatment from the proserver to hit // payment providers' test endpoints. - return "OyzvkVvXk7OgOQcx-aZpK5uXx6gQl5i8BnOuUkc0fKpEZW6tc8uUvA" + return "OyzvkVvXk7OgOQcx-aZpK5uXx6gQl5i8BnOuUkc0fKpEZW6tc8uUvA", nil } else { userId, err := s.baseModel.db.Get(TOKEN) - panicIfNecessary(err) - return string(userId) + if err != nil { + return "", err + } + return string(userId), nil } } func (s *SessionModel) SetCountry(country string) error { @@ -210,16 +384,36 @@ func (s *SessionModel) UpdateAdSettings(adsetting AdSettings) error { return nil } -func (s *SessionModel) UpdateStats(city string, country string, countryCode string, httpsUpgrades int, adsBlocked int, hasSucceedingProxy bool) error { - err := pathdb.Mutate(s.db, func(tx pathdb.TX) error { - pathdb.Put[string](tx, SERVER_COUNTRY, country, "") - pathdb.Put[string](tx, SERVER_CITY, city, "") - pathdb.Put[string](tx, SERVER_COUNTRY_CODE, countryCode, "") - pathdb.Put[bool](tx, HAS_SUCCEEDING_PROXY, hasSucceedingProxy, "") - // Not using ads blocked any more - return nil - }) - return err +// Keep name as p1,p2,p3..... +// Name become part of Objective c so this is important +func (s *SessionModel) UpdateStats(p0 string, p1 string, p2 string, p3 int, p4 int, p5 bool) error { + if p0 != "" && p1 != "" && p2 != "" { + serverInfo := &ServerInfo{ + City: p0, + Country: p1, + CountryCode: p2, + } + + // Serialize the ServerInfo object to byte slice + serverInfoBytes, serverErr := proto.Marshal(serverInfo) + if serverErr != nil { + return serverErr + } + + log.Debugf("UpdateStats called with city %v and country %v and code %v with proxy %v server info bytes %v", p0, p1, p2, p5, serverInfoBytes) + err := pathdb.Mutate(s.db, func(tx pathdb.TX) error { + pathdb.Put[string](tx, SERVER_COUNTRY, p1, "") + pathdb.Put[string](tx, SERVER_CITY, p0, "") + pathdb.Put[string](tx, SERVER_COUNTRY_CODE, p2, "") + pathdb.Put[bool](tx, HAS_SUCCEEDING_PROXY, p5, "") + pathdb.Put[[]byte](tx, PATH_SERVER_INFO, serverInfoBytes, "") + + // Not using ads blocked any more + return nil + }) + return err + } + return nil } func (s *SessionModel) SetStaging(stageing bool) error { @@ -227,26 +421,35 @@ func (s *SessionModel) SetStaging(stageing bool) error { return nil } -func (s *SessionModel) BandwidthUpdate(percent int, remaining int, allowed int, ttlSeconds int) error { - pathdb.Mutate(s.db, func(tx pathdb.TX) error { - pathdb.Put[int](tx, LATEST_BANDWIDTH, percent, "") +// Keep name as p1,p2,p3..... +// Name become part of Objective c so this is important +func (s *SessionModel) BandwidthUpdate(p1 int, p2 int, p3 int, p4 int) error { + err := pathdb.Mutate(s.db, func(tx pathdb.TX) error { + pathdb.Put[int](tx, LATEST_BANDWIDTH, p1, "") return nil }) + return err +} - //Here we are using eventBus to post or update UI - // Find way do it from go somehow - return nil +func getBandwidthLimit(m *baseModel) (string, error) { + percent, err := m.db.Get(LATEST_BANDWIDTH) + if err != nil { + return "", err + } + return string(percent), nil } func (s *SessionModel) Locale() (string, error) { - // For now just send back english by default - // Once have machisim but to dyanmic locale, err := s.baseModel.db.Get(LANG) - panicIfNecessary(err) + if err != nil { + return "", err + } return string(locale), nil } -func setLocale(m *baseModel, langCode string) error { +func setLanguage(m *baseModel, langCode string) error { + log.Debugf("Lang debugger got value %v", langCode) + pathdb.Mutate(m.db, func(tx pathdb.TX) error { pathdb.Put[string](tx, LANG, langCode, "") return nil @@ -256,9 +459,9 @@ func setLocale(m *baseModel, langCode string) error { func (s *SessionModel) GetTimeZone() (string, error) { timezoneId, err := s.baseModel.db.Get(TIMEZONE_ID) - panicIfNecessary(err) - // For now just send back english by default - // Once have machisim change to dyanmic + if err != nil { + return "", err + } return string(timezoneId), nil } @@ -268,9 +471,6 @@ func setTimeZone(m *baseModel, timezoneId string) error { pathdb.Put[string](tx, TIMEZONE_ID, timezoneId, "") return nil }) - byte, error := m.db.Get(TIMEZONE_ID) - panicIfNecessary(error) - log.Debugf("Successfulyy added timezone%v", string(byte)) return nil } @@ -278,7 +478,9 @@ func setTimeZone(m *baseModel, timezoneId string) error { func (s *SessionModel) Code() (string, error) { //Set the timezeon from swift referralCode, err := s.baseModel.db.Get(REFERRAL_CODE) - panicIfNecessary(err) + if err != nil { + return "", err + } return string(referralCode), nil } @@ -293,20 +495,28 @@ func setReferalCode(m *baseModel, referralCode string) error { // Todo need to make chanegs for Force country setup func (s *SessionModel) GetCountryCode() (string, error) { //Set the timezeon from swift - forceCountry, err := s.db.Get(FORCE_COUNTRY) - panicIfNecessary(err) + forceCountry, forceCountryErr := s.db.Get(FORCE_COUNTRY) + if forceCountryErr != nil { + return "", forceCountryErr + } contryInString := string(forceCountry) if contryInString != "" { return string(forceCountry), nil } countryCode, err := s.baseModel.db.Get(GEO_COUNTRY_CODE) - panicIfNecessary(err) + if err != nil { + return "", err + } return string(countryCode), nil } func (s *SessionModel) GetForcedCountryCode() (string, error) { forceCountry, err := s.baseModel.db.Get(FORCE_COUNTRY) - panicIfNecessary(err) + if err != nil { + log.Debugf("Force country coode error %v", err) + return "", err + } + log.Debugf("Force country %v", forceCountry) return string(forceCountry), nil } @@ -319,46 +529,61 @@ func setForceCountry(m *baseModel, forceCountry string) error { } func (s *SessionModel) GetDNSServer() (string, error) { dns, err := s.db.Get(DNS_DETECTOR) - panicIfNecessary(err) + if err != nil { + return "", err + } return string(dns), nil } -func setDNSServer(m *baseModel, forceCountry string) error { - // Implement this - // Check out kotlin code - return nil +func setDNSServer(m *baseModel, dnsServer string) error { + err := pathdb.Mutate(m.db, func(tx pathdb.TX) error { + pathdb.Put[string](tx, DNS_DETECTOR, dnsServer, "") + return nil + }) + return err } func (s *SessionModel) Provider() (string, error) { provider, err := s.db.Get(PROVIDER) - panicIfNecessary(err) + if err != nil { + return "", err + } return string(provider), nil } func setProvider(m *baseModel, provider string) error { - // Implement this - // Check out kotlin code - return nil + err := pathdb.Mutate(m.db, func(tx pathdb.TX) error { + pathdb.Put[string](tx, PROVIDER, provider, "") + return nil + }) + return err } -// Todo: Change this method name to IsStoreVersion -func (s *SessionModel) IsPlayVersion() (bool, error) { - // For now return static to yes +func (s *SessionModel) IsStoreVersion() (bool, error) { + osStoreVersion, err := s.db.Get(IS_PLAY_VERSION) + if err != nil { + return false, err + } + if string(osStoreVersion) == "true" { + return true, nil + } return false, nil } func (s *SessionModel) Email() (string, error) { email, err := s.db.Get(EMAIL_ADDRESS) - panicIfNecessary(err) + if err != nil { + return "", err + } return string(email), nil } func setEmail(m *baseModel, email string) error { - pathdb.Mutate(m.db, func(tx pathdb.TX) error { + err := pathdb.Mutate(m.db, func(tx pathdb.TX) error { pathdb.Put[string](tx, EMAIL_ADDRESS, email, "") return nil }) - return nil + return err } func (s *SessionModel) Currency() (string, error) { @@ -378,8 +603,11 @@ func (s *SessionModel) DeviceOS() (string, error) { } func (s *SessionModel) IsProUser() (bool, error) { - // return static for now - return false, nil + proUser, err := s.baseModel.db.Get(PRO_USER) + if err != nil { + return false, err + } + return (string(proUser) == "true"), nil } func setProUser(m *baseModel, isPro bool) error { pathdb.Mutate(m.db, func(tx pathdb.TX) error { @@ -389,17 +617,17 @@ func setProUser(m *baseModel, isPro bool) error { return nil } -func (s *SessionModel) SetReplicaAddr(replicaAddr string) error { +func (s *SessionModel) SetReplicaAddr(replicaAddr string) { pathdb.Mutate(s.db, func(tx pathdb.TX) error { - pathdb.Put[string](tx, REPLICA_ADDR, replicaAddr, "") + //For now force replicate to disbale it + pathdb.Put[string](tx, REPLICA_ADDR, "", "") return nil }) - return nil } func (s *SessionModel) ForceReplica() bool { // return static for now - return true + return false } func (s *SessionModel) SetChatEnabled(chatEnable bool) { @@ -433,3 +661,104 @@ func (s *SessionModel) SerializedInternalHeaders() (string, error) { // Todo implement this method return "", nil } + +func acceptTerms(m *baseModel) error { + pathdb.Mutate(m.db, func(tx pathdb.TX) error { + pathdb.Put[int](tx, ACCEPTED_TERMS_VERSION, CURRENT_TERMS_VERSION, "") + return nil + }) + return nil +} + +func setStoreVersion(m *baseModel, isStoreVersion bool) error { + pathdb.Mutate(m.db, func(tx pathdb.TX) error { + pathdb.Put[bool](tx, IS_PLAY_VERSION, isStoreVersion, "") + return nil + }) + return nil +} +func setSelectedTab(m *baseModel, tap string) error { + pathdb.Mutate(m.db, func(tx pathdb.TX) error { + pathdb.Put[string](tx, SET_SELECTED_TAB, tap, "") + return nil + }) + return nil +} + +func setUserIdAndToken(m *baseModel, userId float64, token string) error { + err := pathdb.Mutate(m.db, func(tx pathdb.TX) error { + pathdb.Put[float64](tx, USER_ID, userId, "") + pathdb.Put[string](tx, TOKEN, token, "") + return nil + }) + return err +} + +type UserResponse struct { + UserID float64 `json:"userId"` + Code string `json:"code"` + Token string `json:"token"` + Referral string `json:"referral"` + Locale string `json:"locale"` + Servers []string `json:"servers"` + Inviters []string `json:"inviters"` + Invitees []string `json:"invitees"` + Devices []string `json:"devices"` + YinbiEnabled bool `json:"yinbiEnabled"` +} + +// Create user +// Todo-: Create Sprate http client to manag and reuse client +func userCreate(m *baseModel, local string) error { + deviecId, err := m.db.Get(DEVICE_ID) + if err != nil { + return err + } + + requestBodyMap := map[string]string{ + "locale": local, + } + // Marshal the map to JSON + requestBody, err := json.Marshal(requestBodyMap) + if err != nil { + log.Errorf("Error marshaling request body: %v", err) + return err + } + // Create a new request + req, err := http.NewRequest("POST", "https://api.getiantem.org/user-create", bytes.NewBuffer(requestBody)) + if err != nil { + log.Errorf("Error creating new request: %v", err) + return err + } + + // Add headers + req.Header.Set("X-Lantern-Device-Id", string(deviecId)) + log.Debugf("Headers set") + // Initialize a new http client + client := &http.Client{} + // Send the request + resp, err := client.Do(req) + if err != nil { + log.Errorf("Error sending request: %v", err) + + return err + } + defer resp.Body.Close() + var userResponse UserResponse + // Read and decode the response body + if err := json.NewDecoder(resp.Body).Decode(&userResponse); err != nil { + log.Errorf("Error decoding response body: %v", err) + return err + } + //Save user refferal code + if userResponse.Referral != "" { + err := setReferalCode(m, userResponse.Referral) + if err != nil { + return err + } + } + //Save user id and token + setUserIdAndToken(m, userResponse.UserID, userResponse.Token) + log.Debugf("Created new Lantern user: %+v", userResponse) + return nil +} diff --git a/internalsdk/utils.go b/internalsdk/utils.go index c617842cd..9a4ea086c 100644 --- a/internalsdk/utils.go +++ b/internalsdk/utils.go @@ -1,7 +1,10 @@ package internalsdk import ( + "encoding/binary" "encoding/json" + "fmt" + "math" "github.com/getlantern/pathdb" "github.com/getlantern/pathdb/minisql" @@ -60,7 +63,22 @@ func putFromJson(jsonString string, db pathdb.DB) error { if !ok { return log.Errorf("Invalid value for type string: %v", value) } - pathdb.Put[string](tx, key, actualValue, "") + + if key == "lang" { + // Check if lang is already added or not + lang, err := pathdb.Get[string](tx, LANG) + if err != nil { + return err + } + if lang != "" { + pathdb.Put[string](tx, key, lang, "") + } else { + pathdb.Put[string](tx, key, actualValue, "") + } + } else { + pathdb.Put[string](tx, key, actualValue, "") + } + case minisql.ValueTypeInt: // Convert value to string and put it actualValue, ok := value.(int) @@ -84,3 +102,43 @@ func putFromJson(jsonString string, db pathdb.DB) error { return nil }) } + +// Convered value in type that supprt minisql values +func convertValueToSupportedTypes(rawValue interface{}) interface{} { + switch v := rawValue.(type) { + case int64: + return int(v) + case int32: + return int(v) + case int16: + return int(v) + case int8: + return int(v) + default: + return rawValue + } +} + +func BytesToFloat64LittleEndian(b []byte) (float64, error) { + if len(b) != 8 { + return 0, fmt.Errorf("expected 8 bytes but got %d", len(b)) + } + bits := binary.LittleEndian.Uint64(b) + return math.Float64frombits(bits), nil +} + +type Lang struct { + Lang string `json:"lang"` +} + +func extractLangValueFromJSON(localStr string) (string, error) { + var langObj Lang + err := json.Unmarshal([]byte(localStr), &langObj) + if err != nil { + return "", err + } + if langObj.Lang == "" { + return "", fmt.Errorf("lang value not found") + } + return langObj.Lang, nil +} diff --git a/internalsdk/vpn.pb.go b/internalsdk/vpn.pb.go new file mode 100644 index 000000000..4963a0cbe --- /dev/null +++ b/internalsdk/vpn.pb.go @@ -0,0 +1,786 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.26.0 +// protoc v4.23.4 +// source: vpn.proto + +package internalsdk + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type ServerInfo struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + City string `protobuf:"bytes,1,opt,name=city,proto3" json:"city,omitempty"` + Country string `protobuf:"bytes,2,opt,name=country,proto3" json:"country,omitempty"` + CountryCode string `protobuf:"bytes,3,opt,name=countryCode,proto3" json:"countryCode,omitempty"` +} + +func (x *ServerInfo) Reset() { + *x = ServerInfo{} + if protoimpl.UnsafeEnabled { + mi := &file_vpn_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ServerInfo) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ServerInfo) ProtoMessage() {} + +func (x *ServerInfo) ProtoReflect() protoreflect.Message { + mi := &file_vpn_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ServerInfo.ProtoReflect.Descriptor instead. +func (*ServerInfo) Descriptor() ([]byte, []int) { + return file_vpn_proto_rawDescGZIP(), []int{0} +} + +func (x *ServerInfo) GetCity() string { + if x != nil { + return x.City + } + return "" +} + +func (x *ServerInfo) GetCountry() string { + if x != nil { + return x.Country + } + return "" +} + +func (x *ServerInfo) GetCountryCode() string { + if x != nil { + return x.CountryCode + } + return "" +} + +type Bandwidth struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Percent int64 `protobuf:"varint,1,opt,name=percent,proto3" json:"percent,omitempty"` // [0, 100] + Remaining int64 `protobuf:"varint,2,opt,name=remaining,proto3" json:"remaining,omitempty"` // in MB + Allowed int64 `protobuf:"varint,3,opt,name=allowed,proto3" json:"allowed,omitempty"` // in MB + TtlSeconds int64 `protobuf:"varint,4,opt,name=ttlSeconds,proto3" json:"ttlSeconds,omitempty"` // number of seconds left before data reset +} + +func (x *Bandwidth) Reset() { + *x = Bandwidth{} + if protoimpl.UnsafeEnabled { + mi := &file_vpn_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Bandwidth) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Bandwidth) ProtoMessage() {} + +func (x *Bandwidth) ProtoReflect() protoreflect.Message { + mi := &file_vpn_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Bandwidth.ProtoReflect.Descriptor instead. +func (*Bandwidth) Descriptor() ([]byte, []int) { + return file_vpn_proto_rawDescGZIP(), []int{1} +} + +func (x *Bandwidth) GetPercent() int64 { + if x != nil { + return x.Percent + } + return 0 +} + +func (x *Bandwidth) GetRemaining() int64 { + if x != nil { + return x.Remaining + } + return 0 +} + +func (x *Bandwidth) GetAllowed() int64 { + if x != nil { + return x.Allowed + } + return 0 +} + +func (x *Bandwidth) GetTtlSeconds() int64 { + if x != nil { + return x.TtlSeconds + } + return 0 +} + +type AppData struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + PackageName string `protobuf:"bytes,1,opt,name=packageName,proto3" json:"packageName,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Icon []byte `protobuf:"bytes,3,opt,name=icon,proto3" json:"icon,omitempty"` + AllowedAccess bool `protobuf:"varint,4,opt,name=allowedAccess,proto3" json:"allowedAccess,omitempty"` +} + +func (x *AppData) Reset() { + *x = AppData{} + if protoimpl.UnsafeEnabled { + mi := &file_vpn_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AppData) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AppData) ProtoMessage() {} + +func (x *AppData) ProtoReflect() protoreflect.Message { + mi := &file_vpn_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AppData.ProtoReflect.Descriptor instead. +func (*AppData) Descriptor() ([]byte, []int) { + return file_vpn_proto_rawDescGZIP(), []int{2} +} + +func (x *AppData) GetPackageName() string { + if x != nil { + return x.PackageName + } + return "" +} + +func (x *AppData) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *AppData) GetIcon() []byte { + if x != nil { + return x.Icon + } + return nil +} + +func (x *AppData) GetAllowedAccess() bool { + if x != nil { + return x.AllowedAccess + } + return false +} + +type Device struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Created int64 `protobuf:"varint,3,opt,name=created,proto3" json:"created,omitempty"` +} + +func (x *Device) Reset() { + *x = Device{} + if protoimpl.UnsafeEnabled { + mi := &file_vpn_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Device) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Device) ProtoMessage() {} + +func (x *Device) ProtoReflect() protoreflect.Message { + mi := &file_vpn_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Device.ProtoReflect.Descriptor instead. +func (*Device) Descriptor() ([]byte, []int) { + return file_vpn_proto_rawDescGZIP(), []int{3} +} + +func (x *Device) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *Device) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Device) GetCreated() int64 { + if x != nil { + return x.Created + } + return 0 +} + +type Devices struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Devices []*Device `protobuf:"bytes,1,rep,name=devices,proto3" json:"devices,omitempty"` +} + +func (x *Devices) Reset() { + *x = Devices{} + if protoimpl.UnsafeEnabled { + mi := &file_vpn_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Devices) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Devices) ProtoMessage() {} + +func (x *Devices) ProtoReflect() protoreflect.Message { + mi := &file_vpn_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Devices.ProtoReflect.Descriptor instead. +func (*Devices) Descriptor() ([]byte, []int) { + return file_vpn_proto_rawDescGZIP(), []int{4} +} + +func (x *Devices) GetDevices() []*Device { + if x != nil { + return x.Devices + } + return nil +} + +type Plan struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"` + BestValue bool `protobuf:"varint,3,opt,name=bestValue,proto3" json:"bestValue,omitempty"` + UsdPrice int64 `protobuf:"varint,4,opt,name=usdPrice,proto3" json:"usdPrice,omitempty"` + Price map[string]int64 `protobuf:"bytes,5,rep,name=price,proto3" json:"price,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"` + TotalCostBilledOneTime string `protobuf:"bytes,6,opt,name=totalCostBilledOneTime,proto3" json:"totalCostBilledOneTime,omitempty"` + OneMonthCost string `protobuf:"bytes,7,opt,name=oneMonthCost,proto3" json:"oneMonthCost,omitempty"` + TotalCost string `protobuf:"bytes,8,opt,name=totalCost,proto3" json:"totalCost,omitempty"` + FormattedBonus string `protobuf:"bytes,9,opt,name=formattedBonus,proto3" json:"formattedBonus,omitempty"` + RenewalText string `protobuf:"bytes,10,opt,name=renewalText,proto3" json:"renewalText,omitempty"` +} + +func (x *Plan) Reset() { + *x = Plan{} + if protoimpl.UnsafeEnabled { + mi := &file_vpn_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Plan) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Plan) ProtoMessage() {} + +func (x *Plan) ProtoReflect() protoreflect.Message { + mi := &file_vpn_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Plan.ProtoReflect.Descriptor instead. +func (*Plan) Descriptor() ([]byte, []int) { + return file_vpn_proto_rawDescGZIP(), []int{5} +} + +func (x *Plan) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *Plan) GetDescription() string { + if x != nil { + return x.Description + } + return "" +} + +func (x *Plan) GetBestValue() bool { + if x != nil { + return x.BestValue + } + return false +} + +func (x *Plan) GetUsdPrice() int64 { + if x != nil { + return x.UsdPrice + } + return 0 +} + +func (x *Plan) GetPrice() map[string]int64 { + if x != nil { + return x.Price + } + return nil +} + +func (x *Plan) GetTotalCostBilledOneTime() string { + if x != nil { + return x.TotalCostBilledOneTime + } + return "" +} + +func (x *Plan) GetOneMonthCost() string { + if x != nil { + return x.OneMonthCost + } + return "" +} + +func (x *Plan) GetTotalCost() string { + if x != nil { + return x.TotalCost + } + return "" +} + +func (x *Plan) GetFormattedBonus() string { + if x != nil { + return x.FormattedBonus + } + return "" +} + +func (x *Plan) GetRenewalText() string { + if x != nil { + return x.RenewalText + } + return "" +} + +type PaymentProviders struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` +} + +func (x *PaymentProviders) Reset() { + *x = PaymentProviders{} + if protoimpl.UnsafeEnabled { + mi := &file_vpn_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PaymentProviders) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PaymentProviders) ProtoMessage() {} + +func (x *PaymentProviders) ProtoReflect() protoreflect.Message { + mi := &file_vpn_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PaymentProviders.ProtoReflect.Descriptor instead. +func (*PaymentProviders) Descriptor() ([]byte, []int) { + return file_vpn_proto_rawDescGZIP(), []int{6} +} + +func (x *PaymentProviders) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +type PaymentMethod struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Method string `protobuf:"bytes,1,opt,name=method,proto3" json:"method,omitempty"` + Providers []*PaymentProviders `protobuf:"bytes,2,rep,name=providers,proto3" json:"providers,omitempty"` +} + +func (x *PaymentMethod) Reset() { + *x = PaymentMethod{} + if protoimpl.UnsafeEnabled { + mi := &file_vpn_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PaymentMethod) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PaymentMethod) ProtoMessage() {} + +func (x *PaymentMethod) ProtoReflect() protoreflect.Message { + mi := &file_vpn_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PaymentMethod.ProtoReflect.Descriptor instead. +func (*PaymentMethod) Descriptor() ([]byte, []int) { + return file_vpn_proto_rawDescGZIP(), []int{7} +} + +func (x *PaymentMethod) GetMethod() string { + if x != nil { + return x.Method + } + return "" +} + +func (x *PaymentMethod) GetProviders() []*PaymentProviders { + if x != nil { + return x.Providers + } + return nil +} + +var File_vpn_proto protoreflect.FileDescriptor + +var file_vpn_proto_rawDesc = []byte{ + 0x0a, 0x09, 0x76, 0x70, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x5c, 0x0a, 0x0a, 0x53, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x69, 0x74, + 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x69, 0x74, 0x79, 0x12, 0x18, 0x0a, + 0x07, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x72, 0x79, 0x43, 0x6f, 0x64, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6f, + 0x75, 0x6e, 0x74, 0x72, 0x79, 0x43, 0x6f, 0x64, 0x65, 0x22, 0x7d, 0x0a, 0x09, 0x42, 0x61, 0x6e, + 0x64, 0x77, 0x69, 0x64, 0x74, 0x68, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, + 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, + 0x12, 0x1c, 0x0a, 0x09, 0x72, 0x65, 0x6d, 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x09, 0x72, 0x65, 0x6d, 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x12, 0x18, + 0x0a, 0x07, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x07, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x74, 0x74, 0x6c, 0x53, + 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x74, 0x74, + 0x6c, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x22, 0x79, 0x0a, 0x07, 0x41, 0x70, 0x70, 0x44, + 0x61, 0x74, 0x61, 0x12, 0x20, 0x0a, 0x0b, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x4e, 0x61, + 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, + 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x63, 0x6f, + 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x69, 0x63, 0x6f, 0x6e, 0x12, 0x24, 0x0a, + 0x0d, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x41, 0x63, 0x63, + 0x65, 0x73, 0x73, 0x22, 0x46, 0x0a, 0x06, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x12, 0x0e, 0x0a, + 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x07, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x22, 0x2c, 0x0a, 0x07, 0x44, + 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x21, 0x0a, 0x07, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x07, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, + 0x52, 0x07, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x22, 0x98, 0x03, 0x0a, 0x04, 0x50, 0x6c, + 0x61, 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, + 0x69, 0x64, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, + 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x62, 0x65, 0x73, 0x74, 0x56, 0x61, 0x6c, 0x75, + 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x62, 0x65, 0x73, 0x74, 0x56, 0x61, 0x6c, + 0x75, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x64, 0x50, 0x72, 0x69, 0x63, 0x65, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x75, 0x73, 0x64, 0x50, 0x72, 0x69, 0x63, 0x65, 0x12, 0x26, + 0x0a, 0x05, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, + 0x50, 0x6c, 0x61, 0x6e, 0x2e, 0x50, 0x72, 0x69, 0x63, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, + 0x05, 0x70, 0x72, 0x69, 0x63, 0x65, 0x12, 0x36, 0x0a, 0x16, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x43, + 0x6f, 0x73, 0x74, 0x42, 0x69, 0x6c, 0x6c, 0x65, 0x64, 0x4f, 0x6e, 0x65, 0x54, 0x69, 0x6d, 0x65, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x16, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x73, + 0x74, 0x42, 0x69, 0x6c, 0x6c, 0x65, 0x64, 0x4f, 0x6e, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x22, + 0x0a, 0x0c, 0x6f, 0x6e, 0x65, 0x4d, 0x6f, 0x6e, 0x74, 0x68, 0x43, 0x6f, 0x73, 0x74, 0x18, 0x07, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x6f, 0x6e, 0x65, 0x4d, 0x6f, 0x6e, 0x74, 0x68, 0x43, 0x6f, + 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x73, 0x74, 0x18, + 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x73, 0x74, + 0x12, 0x26, 0x0a, 0x0e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x74, 0x65, 0x64, 0x42, 0x6f, 0x6e, + 0x75, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, + 0x74, 0x65, 0x64, 0x42, 0x6f, 0x6e, 0x75, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x72, 0x65, 0x6e, 0x65, + 0x77, 0x61, 0x6c, 0x54, 0x65, 0x78, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x72, + 0x65, 0x6e, 0x65, 0x77, 0x61, 0x6c, 0x54, 0x65, 0x78, 0x74, 0x1a, 0x38, 0x0a, 0x0a, 0x50, 0x72, + 0x69, 0x63, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x3a, 0x02, 0x38, 0x01, 0x22, 0x26, 0x0a, 0x10, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x50, + 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x58, 0x0a, 0x0d, + 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x16, 0x0a, + 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, + 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x2f, 0x0a, 0x09, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, + 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x50, 0x61, 0x79, 0x6d, 0x65, + 0x6e, 0x74, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x52, 0x09, 0x70, 0x72, 0x6f, + 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x42, 0x20, 0x0a, 0x10, 0x69, 0x6f, 0x2e, 0x6c, 0x61, 0x6e, + 0x74, 0x65, 0x72, 0x6e, 0x2e, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x5a, 0x0c, 0x2f, 0x69, 0x6e, 0x74, + 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x73, 0x64, 0x6b, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_vpn_proto_rawDescOnce sync.Once + file_vpn_proto_rawDescData = file_vpn_proto_rawDesc +) + +func file_vpn_proto_rawDescGZIP() []byte { + file_vpn_proto_rawDescOnce.Do(func() { + file_vpn_proto_rawDescData = protoimpl.X.CompressGZIP(file_vpn_proto_rawDescData) + }) + return file_vpn_proto_rawDescData +} + +var file_vpn_proto_msgTypes = make([]protoimpl.MessageInfo, 9) +var file_vpn_proto_goTypes = []interface{}{ + (*ServerInfo)(nil), // 0: ServerInfo + (*Bandwidth)(nil), // 1: Bandwidth + (*AppData)(nil), // 2: AppData + (*Device)(nil), // 3: Device + (*Devices)(nil), // 4: Devices + (*Plan)(nil), // 5: Plan + (*PaymentProviders)(nil), // 6: PaymentProviders + (*PaymentMethod)(nil), // 7: PaymentMethod + nil, // 8: Plan.PriceEntry +} +var file_vpn_proto_depIdxs = []int32{ + 3, // 0: Devices.devices:type_name -> Device + 8, // 1: Plan.price:type_name -> Plan.PriceEntry + 6, // 2: PaymentMethod.providers:type_name -> PaymentProviders + 3, // [3:3] is the sub-list for method output_type + 3, // [3:3] is the sub-list for method input_type + 3, // [3:3] is the sub-list for extension type_name + 3, // [3:3] is the sub-list for extension extendee + 0, // [0:3] is the sub-list for field type_name +} + +func init() { file_vpn_proto_init() } +func file_vpn_proto_init() { + if File_vpn_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_vpn_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ServerInfo); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_vpn_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Bandwidth); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_vpn_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AppData); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_vpn_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Device); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_vpn_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Devices); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_vpn_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Plan); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_vpn_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PaymentProviders); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_vpn_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PaymentMethod); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_vpn_proto_rawDesc, + NumEnums: 0, + NumMessages: 9, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_vpn_proto_goTypes, + DependencyIndexes: file_vpn_proto_depIdxs, + MessageInfos: file_vpn_proto_msgTypes, + }.Build() + File_vpn_proto = out.File + file_vpn_proto_rawDesc = nil + file_vpn_proto_goTypes = nil + file_vpn_proto_depIdxs = nil +} diff --git a/internalsdk/vpn_model.go b/internalsdk/vpn_model.go new file mode 100644 index 000000000..ad6f474fd --- /dev/null +++ b/internalsdk/vpn_model.go @@ -0,0 +1,90 @@ +package internalsdk + +import ( + "github.com/getlantern/pathdb" + "github.com/getlantern/pathdb/minisql" +) + +// Custom Model implemnation +// VPNModel is a custom model derived from the baseModel. +type VpnModel struct { + *baseModel +} + +const PATH_VPN_STATUS = "/vpn_status" +const PATH_SERVER_INFO = "/server_info" +const PATH_BANDWIDTH = "/bandwidth" + +// NewSessionModel initializes a new SessionModel instance. +func NewVpnModel(schema string, mdb minisql.DB) (*VpnModel, error) { + base, err := newModel(schema, mdb) + if err != nil { + return nil, err + } + initVpnModel(base.(*baseModel)) + model := &VpnModel{base.(*baseModel)} + return model, nil +} + +func (s *VpnModel) InvokeMethod(method string, arguments minisql.Values) (*minisql.Value, error) { + switch method { + case "switchVPN": + jsonString := arguments.Get(0) + err := switchVPN(s.baseModel, jsonString.Bool()) + if err != nil { + return nil, err + } else { + return minisql.NewValueBool(true), nil + } + case "saveVpnStatus": + jsonString := arguments.Get(0) + err := saveVPNStatus(s.baseModel, jsonString.String()) + if err != nil { + return nil, err + } else { + return minisql.NewValueBool(true), nil + } + case "getVpnStatus": + byte, err := getVPNStatus(s.baseModel) + if err != nil { + return nil, err + } else { + return minisql.NewValueString(byte), nil + } + default: + return s.baseModel.InvokeMethod(method, arguments) + } +} + +func initVpnModel(m *baseModel) error { + pathdb.Mutate(m.db, func(tx pathdb.TX) error { + rawStatus, err := tx.Get(PATH_VPN_STATUS) + panicIfNecessary(err) + status := string(rawStatus) + if status != "" { + pathdb.Put[string](tx, PATH_VPN_STATUS, status, "") + } else { + pathdb.Put[string](tx, PATH_VPN_STATUS, "disconnected", "") + } + return nil + }) + return nil +} + +func switchVPN(m *baseModel, status bool) error { + return nil +} + +func saveVPNStatus(m *baseModel, status string) error { + err := pathdb.Mutate(m.db, func(tx pathdb.TX) error { + pathdb.Put[string](tx, PATH_VPN_STATUS, status, "") + return nil + }) + return err +} + +func getVPNStatus(m *baseModel) (string, error) { + byte, err := m.db.Get(PATH_VPN_STATUS) + panicIfNecessary(err) + return string(byte), nil +} diff --git a/ios/.swiftlint.yml b/ios/.swiftlint.yml new file mode 100644 index 000000000..056efef3e --- /dev/null +++ b/ios/.swiftlint.yml @@ -0,0 +1,40 @@ +disabled_rules: +- trailing_whitespace +- identifier_name +opt_in_rules: +- empty_count +- empty_string +included: # paths to include during linting. `--path` is ignored if present. + - Source + - Runner +excluded: +- Pods +- SwiftLint/Common/3rdPartyLib +line_length: + warning: 150 + error: 200 + ignores_function_declarations: true + ignores_comments: true + ignores_urls: true +# configurable rules can be customized from this configuration file +# binary rules can set their severity level +force_cast: warning # implicitly +force_try: + severity: warning # explicitly +function_body_length: + warning: 300 + error: 500 +function_parameter_count: + warning: 6 + error: 8 +type_body_length: + warning: 300 + error: 500 +file_length: + warning: 1000 + error: 1500 + ignore_comment_only_lines: true +cyclomatic_complexity: + warning: 15 + error: 25 +reporter: "xcode" diff --git a/ios/LanternTests/LanternTests.swift b/ios/LanternTests/LanternTests.swift new file mode 100644 index 000000000..c8635f133 --- /dev/null +++ b/ios/LanternTests/LanternTests.swift @@ -0,0 +1,24 @@ +// +// LanternTests.swift +// LanternTests +// +// Created by Ox Cart on 9/21/23. +// + +import Internalsdk +import SQLite +@testable import Runner +import XCTest + + +final class LanternTests: XCTestCase { + + override func setUpWithError() throws { + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDownWithError() throws { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + +} diff --git a/ios/Podfile b/ios/Podfile index 3eb1eab92..89788fb35 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -1,6 +1,6 @@ # Uncomment this line to define a global platform for your project -# platform :ios, '11.0' - +platform :ios, '12.0' +inhibit_all_warnings! # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' @@ -33,10 +33,28 @@ target 'Runner' do flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) pod 'SQLite.swift', '~> 0.12.2' + pod 'Toast-Swift', '~> 5.0.1' +end + +target 'LanternTests' do + use_frameworks! + use_modular_headers! + + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) + pod 'SQLite.swift', '~> 0.12.2' + pod 'Toast-Swift', '~> 5.0.1' end +#post_install do |installer| +# installer.pods_project.targets.each do |target| +# flutter_additional_ios_build_settings(target) +# end +#end post_install do |installer| installer.pods_project.targets.each do |target| flutter_additional_ios_build_settings(target) + target.build_configurations.each do |config| + config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '11.0' + end end end diff --git a/ios/Podfile.lock b/ios/Podfile.lock index d5a34bdd6..57b65fb68 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -67,21 +67,21 @@ PODS: - Flutter - Google-Mobile-Ads-SDK (~> 10.4.0) - webview_flutter_wkwebview - - GoogleAppMeasurement (10.14.0): - - GoogleAppMeasurement/AdIdSupport (= 10.14.0) + - GoogleAppMeasurement (10.15.0): + - GoogleAppMeasurement/AdIdSupport (= 10.15.0) - GoogleUtilities/AppDelegateSwizzler (~> 7.11) - GoogleUtilities/MethodSwizzler (~> 7.11) - GoogleUtilities/Network (~> 7.11) - "GoogleUtilities/NSData+zlib (~> 7.11)" - nanopb (< 2.30910.0, >= 2.30908.0) - - GoogleAppMeasurement/AdIdSupport (10.14.0): - - GoogleAppMeasurement/WithoutAdIdSupport (= 10.14.0) + - GoogleAppMeasurement/AdIdSupport (10.15.0): + - GoogleAppMeasurement/WithoutAdIdSupport (= 10.15.0) - GoogleUtilities/AppDelegateSwizzler (~> 7.11) - GoogleUtilities/MethodSwizzler (~> 7.11) - GoogleUtilities/Network (~> 7.11) - "GoogleUtilities/NSData+zlib (~> 7.11)" - nanopb (< 2.30910.0, >= 2.30908.0) - - GoogleAppMeasurement/WithoutAdIdSupport (10.14.0): + - GoogleAppMeasurement/WithoutAdIdSupport (10.15.0): - GoogleUtilities/AppDelegateSwizzler (~> 7.11) - GoogleUtilities/MethodSwizzler (~> 7.11) - GoogleUtilities/Network (~> 7.11) @@ -107,17 +107,17 @@ PODS: - GoogleUtilities/Logger - integration_test (0.0.1): - Flutter - - libwebp (1.3.1): - - libwebp/demux (= 1.3.1) - - libwebp/mux (= 1.3.1) - - libwebp/sharpyuv (= 1.3.1) - - libwebp/webp (= 1.3.1) - - libwebp/demux (1.3.1): + - libwebp (1.3.2): + - libwebp/demux (= 1.3.2) + - libwebp/mux (= 1.3.2) + - libwebp/sharpyuv (= 1.3.2) + - libwebp/webp (= 1.3.2) + - libwebp/demux (1.3.2): - libwebp/webp - - libwebp/mux (1.3.1): + - libwebp/mux (1.3.2): - libwebp/demux - - libwebp/sharpyuv (1.3.1) - - libwebp/webp (1.3.1): + - libwebp/sharpyuv (1.3.2) + - libwebp/webp (1.3.2): - libwebp/sharpyuv - MTBBarcodeScanner (5.0.11) - nanopb (2.30909.0): @@ -136,9 +136,9 @@ PODS: - qr_code_scanner (0.2.0): - Flutter - MTBBarcodeScanner - - SDWebImage (5.17.0): - - SDWebImage/Core (= 5.17.0) - - SDWebImage/Core (5.17.0) + - SDWebImage (5.18.2): + - SDWebImage/Core (= 5.18.2) + - SDWebImage/Core (5.18.2) - share_plus (0.0.1): - Flutter - shared_preferences_foundation (0.0.1): @@ -152,6 +152,7 @@ PODS: - SQLite.swift/standard (0.12.2) - SwiftyGif (5.4.4) - Toast (4.0.0) + - Toast-Swift (5.0.1) - url_launcher_ios (0.0.1): - Flutter - video_player_avfoundation (0.0.1): @@ -187,6 +188,7 @@ DEPENDENCIES: - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) - sqflite (from `.symlinks/plugins/sqflite/ios`) - SQLite.swift (~> 0.12.2) + - Toast-Swift (~> 5.0.1) - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) - video_player_avfoundation (from `.symlinks/plugins/video_player_avfoundation/ios`) - video_thumbnail (from `.symlinks/plugins/video_thumbnail/ios`) @@ -211,6 +213,7 @@ SPEC REPOS: - SQLite.swift - SwiftyGif - Toast + - Toast-Swift EXTERNAL SOURCES: audioplayers_darwin: @@ -285,11 +288,11 @@ SPEC CHECKSUMS: FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a Google-Mobile-Ads-SDK: 32fe7836431a06a29f7734ae092b600137c8108d google_mobile_ads: 53b1f0d74445963e5810e34ac38dfb27aabe278e - GoogleAppMeasurement: 7fee012a868315d418f365fbc8d394d8e020e749 + GoogleAppMeasurement: 722db6550d1e6d552b08398b69a975ac61039338 GoogleUserMessagingPlatform: dce302b8f1b84d6e945812ee7a15c3f65a102cbf GoogleUtilities: 13e2c67ede716b8741c7989e26893d151b2b2084 integration_test: 13825b8a9334a850581300559b8839134b124670 - libwebp: 33dc822fbbf4503668d09f7885bbfedc76c45e96 + libwebp: 1786c9f4ff8a279e4dac1e8f385004d5fc253009 MTBBarcodeScanner: f453b33c4b7dfe545d8c6484ed744d55671788cb nanopb: b552cce312b6c8484180ef47159bc0f65a1f0431 package_info_plus: fd030dabf36271f146f1f3beacd48f564b0f17f7 @@ -297,19 +300,20 @@ SPEC CHECKSUMS: permission_handler_apple: e76247795d700c14ea09e3a2d8855d41ee80a2e6 PromisesObjC: c50d2056b5253dadbd6c2bea79b0674bd5a52fa4 qr_code_scanner: bb67d64904c3b9658ada8c402e8b4d406d5d796e - SDWebImage: 750adf017a315a280c60fde706ab1e552a3ae4e9 + SDWebImage: c0de394d7cf7f9838aed1fd6bb6037654a4572e4 share_plus: 599aa54e4ea31d4b4c0e9c911bcc26c55e791028 shared_preferences_foundation: e2dae3258e06f44cc55f49d42024fd8dd03c590c sqflite: 31f7eba61e3074736dff8807a9b41581e4f7f15a SQLite.swift: d2b4642190917051ce6bd1d49aab565fe794eea3 SwiftyGif: 93a1cc87bf3a51916001cf8f3d63835fb64c819f Toast: 91b396c56ee72a5790816f40d3a94dd357abc196 + Toast-Swift: 9b6a70f28b3bf0b96c40d46c0c4b9d6639846711 url_launcher_ios: 08a3dfac5fb39e8759aeb0abbd5d9480f30fc8b4 video_player_avfoundation: 81e49bb3d9fb63dccf9fa0f6d877dc3ddbeac126 video_thumbnail: c4e2a3c539e247d4de13cd545344fd2d26ffafd1 wakelock: d0fc7c864128eac40eba1617cb5264d9c940b46f webview_flutter_wkwebview: 2e2d318f21a5e036e2c3f26171342e95908bd60a -PODFILE CHECKSUM: 10709cb59b281aac30469691d9a0ebc2700919da +PODFILE CHECKSUM: 0cfe0e959396ac6aad61f61d0b82005395de9d66 COCOAPODS: 1.11.3 diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index e78618334..097ab2c47 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -7,56 +7,136 @@ objects = { /* Begin PBXBuildFile section */ - 03026EA12A77D654001D5507 /* Database.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03026EA02A77D654001D5507 /* Database.swift */; }; - 03026EA32A77D67A001D5507 /* ValueUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03026EA22A77D67A001D5507 /* ValueUtil.swift */; }; + 0308A1282AAEED7E0086157A /* VpnHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0308A1272AAEED7E0086157A /* VpnHelper.swift */; }; + 0308A12C2AAEEE2C0086157A /* UserNotificationsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0308A12B2AAEEE2C0086157A /* UserNotificationsManager.swift */; }; + 0308A12E2AAEEF410086157A /* Events.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0308A12D2AAEEF410086157A /* Events.swift */; }; + 0308A1302AAEF3AA0086157A /* Event.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0308A12F2AAEF3AA0086157A /* Event.swift */; }; + 0308A1312AAEF3B00086157A /* Event.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0308A12F2AAEF3AA0086157A /* Event.swift */; }; + 0308A1342AAEF5130086157A /* VPNBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03A1AB152AA88BF200FB41B2 /* VPNBase.swift */; }; + 0308A1392AAEF8DE0086157A /* FlashlightManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0321C3F82AA9D8DF00D462C1 /* FlashlightManager.swift */; }; + 0308A13D2AAF39F80086157A /* FlashlightManagerExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0308A13C2AAF39F80086157A /* FlashlightManagerExtension.swift */; }; + 0308A13E2AAF43A10086157A /* UserNotificationsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0308A12B2AAEEE2C0086157A /* UserNotificationsManager.swift */; }; + 0321C3F72AA9D84700D462C1 /* Process.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0321C3F62AA9D84700D462C1 /* Process.swift */; }; + 0321C3F92AA9D8DF00D462C1 /* FlashlightManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0321C3F82AA9D8DF00D462C1 /* FlashlightManager.swift */; }; + 032A43582AA6F8AA00EF442B /* DnsDetector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 032A43572AA6F8AA00EF442B /* DnsDetector.swift */; }; + 0333203B2AC698FA00333DA9 /* MockVPNManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0333203A2AC698FA00333DA9 /* MockVPNManager.swift */; }; + 03567DBB2AA9F15100A233EA /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03A1AB432AA8946400FB41B2 /* Constants.swift */; }; + 03567DBC2AA9F18D00A233EA /* Process.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0321C3F62AA9D84700D462C1 /* Process.swift */; }; + 03567DBE2AA9F31200A233EA /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03F2FE332A6949EF0082B34C /* Logger.swift */; }; + 03567DC02AA9F47200A233EA /* FileUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03567DBF2AA9F47200A233EA /* FileUtils.swift */; }; + 03567DC12AA9F47200A233EA /* FileUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03567DBF2AA9F47200A233EA /* FileUtils.swift */; }; + 03567DC42AA9F77000A233EA /* NEProviderStopReason.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03567DC22AA9F77000A233EA /* NEProviderStopReason.swift */; }; + 03567DC62AA9F82F00A233EA /* UDPConn.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03567DC52AA9F82F00A233EA /* UDPConn.swift */; }; 035BE7052A7122BC0084059A /* LanternModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 035BE7032A7122BC0084059A /* LanternModel.swift */; }; 035BE7062A7122BC0084059A /* SessionModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 035BE7042A7122BC0084059A /* SessionModel.swift */; }; - 035FDDD52A6A547F007B15C8 /* Internalsdk.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 035FDDD42A6A547F007B15C8 /* Internalsdk.xcframework */; }; 036663D92A9E0C0E00595971 /* JsonUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 036663D82A9E0C0E00595971 /* JsonUtils.swift */; }; 0369DF792A93803D000EBE43 /* MessagingModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0369DF782A93803D000EBE43 /* MessagingModel.swift */; }; 03802FA32A98C9B000DACDFC /* SubscriberRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03802FA22A98C9B000DACDFC /* SubscriberRequest.swift */; }; + 03A1AB142AA88BDA00FB41B2 /* VPNManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03A1AB132AA88BDA00FB41B2 /* VPNManager.swift */; }; + 03A1AB322AA890AB00FB41B2 /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 03A1AB312AA890AB00FB41B2 /* NetworkExtension.framework */; }; + 03A1AB352AA890AB00FB41B2 /* PacketTunnelProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03A1AB342AA890AB00FB41B2 /* PacketTunnelProvider.swift */; }; + 03A1AB3A2AA890AB00FB41B2 /* Tunnel.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 03A1AB302AA890AB00FB41B2 /* Tunnel.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + 03A1AB422AA8930A00FB41B2 /* Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03A1AB412AA8930A00FB41B2 /* Result.swift */; }; + 03A1AB442AA8946400FB41B2 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03A1AB432AA8946400FB41B2 /* Constants.swift */; }; + 03AEDBFA2ABAD5B9000FDE52 /* ModelMethods.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03AEDBF92ABAD5B9000FDE52 /* ModelMethods.swift */; }; + 03F00B5D2AB07B3800E991E2 /* NavigationModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03F00B5C2AB07B3800E991E2 /* NavigationModel.swift */; }; + 03F00B5F2AB07C8000E991E2 /* LoadingIndicatorManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03F00B5E2AB07C8000E991E2 /* LoadingIndicatorManager.swift */; }; 03F2FE342A6949EF0082B34C /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03F2FE332A6949EF0082B34C /* Logger.swift */; }; - 03F4CD7E2A77DE8500F7BDD8 /* SessionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03F4CD7D2A77DE8500F7BDD8 /* SessionManager.swift */; }; 03F4CD822A77E43C00F7BDD8 /* BaseModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03F4CD812A77E43C00F7BDD8 /* BaseModel.swift */; }; + 03FAF1B22AA1C7940063580C /* RunningEnv.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03FAF1B12AA1C7940063580C /* RunningEnv.swift */; }; + 03FAF1B42AA1E9F40063580C /* VpnModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03FAF1B32AA1E9F40063580C /* VpnModel.swift */; }; 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; - 9DAC9EDBE28EB779A10E5A26 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 351BB463FD77FC724A8E233C /* Pods_Runner.framework */; }; + EC22F6242AC44C8E0017D36C /* Internalsdk.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 035FDDD42A6A547F007B15C8 /* Internalsdk.xcframework */; }; + EC27DE902AC44FAD00B840F2 /* DBModule in Frameworks */ = {isa = PBXBuildFile; productRef = EC27DE8F2AC44FAD00B840F2 /* DBModule */; }; + ECA871BD2AC59104007C400D /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 351BB463FD77FC724A8E233C /* Pods_Runner.framework */; }; + ECA871BF2AC59109007C400D /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 03A1AB312AA890AB00FB41B2 /* NetworkExtension.framework */; }; + ECCA8C672ABD16D20037BF9B /* LanternTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECCA8C662ABD16D20037BF9B /* LanternTests.swift */; }; + F309D8C575359E6D35A75E9D /* Pods_LanternTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 49496710B5388EA1D58F0B15 /* Pods_LanternTests.framework */; }; /* End PBXBuildFile section */ +/* Begin PBXContainerItemProxy section */ + 03A1AB382AA890AB00FB41B2 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 97C146E61CF9000F007C117D /* Project object */; + proxyType = 1; + remoteGlobalIDString = 03A1AB2F2AA890AB00FB41B2; + remoteInfo = Tunnel; + }; + ECCA8C682ABD16D20037BF9B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 97C146E61CF9000F007C117D /* Project object */; + proxyType = 1; + remoteGlobalIDString = 97C146ED1CF9000F007C117D; + remoteInfo = Runner; + }; +/* End PBXContainerItemProxy section */ + /* Begin PBXCopyFilesBuildPhase section */ - 9705A1C41CF9048500538489 /* Embed Frameworks */ = { + 03A1AB3F2AA890AB00FB41B2 /* Embed Foundation Extensions */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; - dstSubfolderSpec = 10; + dstSubfolderSpec = 13; files = ( + 03A1AB3A2AA890AB00FB41B2 /* Tunnel.appex in Embed Foundation Extensions */, ); - name = "Embed Frameworks"; + name = "Embed Foundation Extensions"; runOnlyForDeploymentPostprocessing = 0; }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 03026EA02A77D654001D5507 /* Database.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Database.swift; sourceTree = ""; }; - 03026EA22A77D67A001D5507 /* ValueUtil.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValueUtil.swift; sourceTree = ""; }; + 0308A1272AAEED7E0086157A /* VpnHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VpnHelper.swift; sourceTree = ""; }; + 0308A12B2AAEEE2C0086157A /* UserNotificationsManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserNotificationsManager.swift; sourceTree = ""; }; + 0308A12D2AAEEF410086157A /* Events.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Events.swift; sourceTree = ""; }; + 0308A12F2AAEF3AA0086157A /* Event.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Event.swift; sourceTree = ""; }; + 0308A13C2AAF39F80086157A /* FlashlightManagerExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FlashlightManagerExtension.swift; sourceTree = ""; }; + 0315F55C2AC1C1A500D49F7B /* DatabaseFramework.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = DatabaseFramework.framework; path = "../../../../../Library/Developer/Xcode/DerivedData/DatabaseFramework-csnjouuwjwleiddbptbupimqigld/Build/Products/Debug-iphoneos/DatabaseFramework.framework"; sourceTree = ""; }; + 0321C3F62AA9D84700D462C1 /* Process.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Process.swift; sourceTree = ""; }; + 0321C3F82AA9D8DF00D462C1 /* FlashlightManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FlashlightManager.swift; sourceTree = ""; }; + 032A43572AA6F8AA00EF442B /* DnsDetector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DnsDetector.swift; sourceTree = ""; }; + 0333203A2AC698FA00333DA9 /* MockVPNManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockVPNManager.swift; sourceTree = ""; }; + 03567DBF2AA9F47200A233EA /* FileUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileUtils.swift; sourceTree = ""; }; + 03567DC22AA9F77000A233EA /* NEProviderStopReason.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NEProviderStopReason.swift; sourceTree = ""; }; + 03567DC52AA9F82F00A233EA /* UDPConn.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UDPConn.swift; sourceTree = ""; }; 035BE7032A7122BC0084059A /* LanternModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = LanternModel.swift; path = Models/LanternModel.swift; sourceTree = ""; }; 035BE7042A7122BC0084059A /* SessionModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SessionModel.swift; path = Models/SessionModel.swift; sourceTree = ""; }; 035FDDD42A6A547F007B15C8 /* Internalsdk.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; path = Internalsdk.xcframework; sourceTree = ""; }; 036663D82A9E0C0E00595971 /* JsonUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JsonUtils.swift; sourceTree = ""; }; 0369DF782A93803D000EBE43 /* MessagingModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = MessagingModel.swift; path = Models/MessagingModel.swift; sourceTree = ""; }; 03802FA22A98C9B000DACDFC /* SubscriberRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriberRequest.swift; sourceTree = ""; }; + 03A1AB102AA8893900FB41B2 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = ""; }; + 03A1AB132AA88BDA00FB41B2 /* VPNManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VPNManager.swift; sourceTree = ""; }; + 03A1AB152AA88BF200FB41B2 /* VPNBase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VPNBase.swift; sourceTree = ""; }; + 03A1AB302AA890AB00FB41B2 /* Tunnel.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = Tunnel.appex; sourceTree = BUILT_PRODUCTS_DIR; }; + 03A1AB312AA890AB00FB41B2 /* NetworkExtension.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = NetworkExtension.framework; path = System/Library/Frameworks/NetworkExtension.framework; sourceTree = SDKROOT; }; + 03A1AB342AA890AB00FB41B2 /* PacketTunnelProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PacketTunnelProvider.swift; sourceTree = ""; }; + 03A1AB362AA890AB00FB41B2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 03A1AB372AA890AB00FB41B2 /* Tunnel.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Tunnel.entitlements; sourceTree = ""; }; + 03A1AB412AA8930A00FB41B2 /* Result.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Result.swift; sourceTree = ""; }; + 03A1AB432AA8946400FB41B2 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = ""; }; + 03AEDBF92ABAD5B9000FDE52 /* ModelMethods.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = ModelMethods.swift; path = Models/ModelMethods.swift; sourceTree = ""; }; + 03C58B082AC18BE7003C9788 /* DatabaseFramework.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = DatabaseFramework.framework; path = ../../../../DatabaseFramework.framework; sourceTree = ""; }; + 03D97B1B2AC1900E0092BFC2 /* Pods_DatabaseFramework.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Pods_DatabaseFramework.framework; path = "../../../../../Library/Developer/Xcode/DerivedData/DatabaseFramework-csnjouuwjwleiddbptbupimqigld/Build/Products/Debug-iphoneos/Pods_DatabaseFramework.framework"; sourceTree = ""; }; + 03F00B5C2AB07B3800E991E2 /* NavigationModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = NavigationModel.swift; path = Models/NavigationModel.swift; sourceTree = ""; }; + 03F00B5E2AB07C8000E991E2 /* LoadingIndicatorManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadingIndicatorManager.swift; sourceTree = ""; }; 03F2FE332A6949EF0082B34C /* Logger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Logger.swift; sourceTree = ""; }; - 03F4CD7D2A77DE8500F7BDD8 /* SessionManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = SessionManager.swift; path = Models/SessionManager.swift; sourceTree = ""; }; 03F4CD812A77E43C00F7BDD8 /* BaseModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = BaseModel.swift; path = Models/BaseModel.swift; sourceTree = ""; }; + 03FAF1B12AA1C7940063580C /* RunningEnv.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunningEnv.swift; sourceTree = ""; }; + 03FAF1B32AA1E9F40063580C /* VpnModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = VpnModel.swift; path = Models/VpnModel.swift; sourceTree = ""; }; 04AF578F28344E5CF0932AA9 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 2F26DCD30807EDF6F82D2EED /* Pods-LanternTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-LanternTests.debug.xcconfig"; path = "Target Support Files/Pods-LanternTests/Pods-LanternTests.debug.xcconfig"; sourceTree = ""; }; 351BB463FD77FC724A8E233C /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 49496710B5388EA1D58F0B15 /* Pods_LanternTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_LanternTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 639C2FD1FB8C2B8BA0BC8FA2 /* Pods-LanternTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-LanternTests.profile.xcconfig"; path = "Target Support Files/Pods-LanternTests/Pods-LanternTests.profile.xcconfig"; sourceTree = ""; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; @@ -68,31 +148,104 @@ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + CA815533D3563C2353745CE2 /* Pods-LanternTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-LanternTests.release.xcconfig"; path = "Target Support Files/Pods-LanternTests/Pods-LanternTests.release.xcconfig"; sourceTree = ""; }; E33BD5976CBD307AC6B17F23 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + EC27DE8E2AC44F4300B840F2 /* DBModule */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = DBModule; path = ../DBModule; sourceTree = ""; }; + ECCA8C642ABD16D20037BF9B /* LanternTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = LanternTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + ECCA8C662ABD16D20037BF9B /* LanternTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LanternTests.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + 03A1AB2D2AA890AB00FB41B2 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 03A1AB322AA890AB00FB41B2 /* NetworkExtension.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 97C146EB1CF9000F007C117D /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 9DAC9EDBE28EB779A10E5A26 /* Pods_Runner.framework in Frameworks */, - 035FDDD52A6A547F007B15C8 /* Internalsdk.xcframework in Frameworks */, + ECA871BD2AC59104007C400D /* Pods_Runner.framework in Frameworks */, + ECA871BF2AC59109007C400D /* NetworkExtension.framework in Frameworks */, + EC27DE902AC44FAD00B840F2 /* DBModule in Frameworks */, + EC22F6242AC44C8E0017D36C /* Internalsdk.xcframework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + ECCA8C612ABD16D20037BF9B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + F309D8C575359E6D35A75E9D /* Pods_LanternTests.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 032A43562AA6F86A00EF442B /* Network */ = { + isa = PBXGroup; + children = ( + 032A43572AA6F8AA00EF442B /* DnsDetector.swift */, + ); + path = Network; + sourceTree = ""; + }; 033CBDCC2A94D86300DEBCBA /* Db */ = { isa = PBXGroup; children = ( - 03026EA02A77D654001D5507 /* Database.swift */, 03802FA22A98C9B000DACDFC /* SubscriberRequest.swift */, ); path = Db; sourceTree = ""; }; + 03A1AB112AA88B8C00FB41B2 /* Core */ = { + isa = PBXGroup; + children = ( + 033CBDCC2A94D86300DEBCBA /* Db */, + 032A43562AA6F86A00EF442B /* Network */, + 03A1AB402AA892F800FB41B2 /* Extension */, + 03A1AB122AA88BB400FB41B2 /* Vpn */, + ); + path = Core; + sourceTree = ""; + }; + 03A1AB122AA88BB400FB41B2 /* Vpn */ = { + isa = PBXGroup; + children = ( + 0308A13C2AAF39F80086157A /* FlashlightManagerExtension.swift */, + 0308A1272AAEED7E0086157A /* VpnHelper.swift */, + 0321C3F82AA9D8DF00D462C1 /* FlashlightManager.swift */, + 03A1AB152AA88BF200FB41B2 /* VPNBase.swift */, + 03A1AB132AA88BDA00FB41B2 /* VPNManager.swift */, + 0333203A2AC698FA00333DA9 /* MockVPNManager.swift */, + ); + path = Vpn; + sourceTree = ""; + }; + 03A1AB332AA890AB00FB41B2 /* Tunnel */ = { + isa = PBXGroup; + children = ( + 03567DC52AA9F82F00A233EA /* UDPConn.swift */, + 03567DC22AA9F77000A233EA /* NEProviderStopReason.swift */, + 03A1AB342AA890AB00FB41B2 /* PacketTunnelProvider.swift */, + 03A1AB362AA890AB00FB41B2 /* Info.plist */, + 03A1AB372AA890AB00FB41B2 /* Tunnel.entitlements */, + ); + path = Tunnel; + sourceTree = ""; + }; + 03A1AB402AA892F800FB41B2 /* Extension */ = { + isa = PBXGroup; + children = ( + 03A1AB412AA8930A00FB41B2 /* Result.swift */, + ); + path = Extension; + sourceTree = ""; + }; 03CD38182A693C050044B004 /* internalsdk */ = { isa = PBXGroup; children = ( @@ -104,7 +257,7 @@ 03D789A42A616D25000D36F9 /* Lantern */ = { isa = PBXGroup; children = ( - 033CBDCC2A94D86300DEBCBA /* Db */, + 03A1AB112AA88B8C00FB41B2 /* Core */, 03F2FE322A6949C60082B34C /* Utils */, 03EE344A2A652B3D00E37100 /* Models */, ); @@ -116,9 +269,11 @@ children = ( 035BE7032A7122BC0084059A /* LanternModel.swift */, 035BE7042A7122BC0084059A /* SessionModel.swift */, - 03F4CD7D2A77DE8500F7BDD8 /* SessionManager.swift */, 03F4CD812A77E43C00F7BDD8 /* BaseModel.swift */, 0369DF782A93803D000EBE43 /* MessagingModel.swift */, + 03FAF1B32AA1E9F40063580C /* VpnModel.swift */, + 03F00B5C2AB07B3800E991E2 /* NavigationModel.swift */, + 03AEDBF92ABAD5B9000FDE52 /* ModelMethods.swift */, ); name = Models; sourceTree = ""; @@ -126,9 +281,16 @@ 03F2FE322A6949C60082B34C /* Utils */ = { isa = PBXGroup; children = ( + 0308A12F2AAEF3AA0086157A /* Event.swift */, + 0308A12D2AAEEF410086157A /* Events.swift */, + 0308A12B2AAEEE2C0086157A /* UserNotificationsManager.swift */, + 0321C3F62AA9D84700D462C1 /* Process.swift */, 03F2FE332A6949EF0082B34C /* Logger.swift */, - 03026EA22A77D67A001D5507 /* ValueUtil.swift */, 036663D82A9E0C0E00595971 /* JsonUtils.swift */, + 03FAF1B12AA1C7940063580C /* RunningEnv.swift */, + 03A1AB432AA8946400FB41B2 /* Constants.swift */, + 03567DBF2AA9F47200A233EA /* FileUtils.swift */, + 03F00B5E2AB07C8000E991E2 /* LoadingIndicatorManager.swift */, ); path = Utils; sourceTree = ""; @@ -147,9 +309,12 @@ 97C146E51CF9000F007C117D = { isa = PBXGroup; children = ( + EC22F61E2AC44B000017D36C /* Packages */, 03CD38182A693C050044B004 /* internalsdk */, 9740EEB11CF90186004384FC /* Flutter */, 97C146F01CF9000F007C117D /* Runner */, + 03A1AB332AA890AB00FB41B2 /* Tunnel */, + ECCA8C652ABD16D20037BF9B /* LanternTests */, 97C146EF1CF9000F007C117D /* Products */, BD7D6E2034FFCA3D48D162CE /* Pods */, FF47084EFEAB686FD7974BE4 /* Frameworks */, @@ -160,6 +325,8 @@ isa = PBXGroup; children = ( 97C146EE1CF9000F007C117D /* Runner.app */, + 03A1AB302AA890AB00FB41B2 /* Tunnel.appex */, + ECCA8C642ABD16D20037BF9B /* LanternTests.xctest */, ); name = Products; sourceTree = ""; @@ -167,6 +334,7 @@ 97C146F01CF9000F007C117D /* Runner */ = { isa = PBXGroup; children = ( + 03A1AB102AA8893900FB41B2 /* Runner.entitlements */, 03D789A42A616D25000D36F9 /* Lantern */, 97C146FA1CF9000F007C117D /* Main.storyboard */, 97C146FD1CF9000F007C117D /* Assets.xcassets */, @@ -186,57 +354,143 @@ 81DE944FD5152F594CC8D7B2 /* Pods-Runner.debug.xcconfig */, E33BD5976CBD307AC6B17F23 /* Pods-Runner.release.xcconfig */, 04AF578F28344E5CF0932AA9 /* Pods-Runner.profile.xcconfig */, + 2F26DCD30807EDF6F82D2EED /* Pods-LanternTests.debug.xcconfig */, + CA815533D3563C2353745CE2 /* Pods-LanternTests.release.xcconfig */, + 639C2FD1FB8C2B8BA0BC8FA2 /* Pods-LanternTests.profile.xcconfig */, ); path = Pods; sourceTree = ""; }; + EC22F61E2AC44B000017D36C /* Packages */ = { + isa = PBXGroup; + children = ( + EC27DE8E2AC44F4300B840F2 /* DBModule */, + ); + name = Packages; + sourceTree = ""; + }; + ECCA8C652ABD16D20037BF9B /* LanternTests */ = { + isa = PBXGroup; + children = ( + ECCA8C662ABD16D20037BF9B /* LanternTests.swift */, + ); + path = LanternTests; + sourceTree = ""; + }; FF47084EFEAB686FD7974BE4 /* Frameworks */ = { isa = PBXGroup; children = ( + 0315F55C2AC1C1A500D49F7B /* DatabaseFramework.framework */, + 03D97B1B2AC1900E0092BFC2 /* Pods_DatabaseFramework.framework */, + 03C58B082AC18BE7003C9788 /* DatabaseFramework.framework */, 351BB463FD77FC724A8E233C /* Pods_Runner.framework */, + 03A1AB312AA890AB00FB41B2 /* NetworkExtension.framework */, + 49496710B5388EA1D58F0B15 /* Pods_LanternTests.framework */, ); name = Frameworks; sourceTree = ""; }; /* End PBXGroup section */ +/* Begin PBXHeadersBuildPhase section */ + 0321C3FA2AA9DC3400D462C1 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + /* Begin PBXNativeTarget section */ + 03A1AB2F2AA890AB00FB41B2 /* Tunnel */ = { + isa = PBXNativeTarget; + buildConfigurationList = 03A1AB3B2AA890AB00FB41B2 /* Build configuration list for PBXNativeTarget "Tunnel" */; + buildPhases = ( + 03A1AB2C2AA890AB00FB41B2 /* Sources */, + 03A1AB2D2AA890AB00FB41B2 /* Frameworks */, + 03A1AB2E2AA890AB00FB41B2 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Tunnel; + productName = Tunnel; + productReference = 03A1AB302AA890AB00FB41B2 /* Tunnel.appex */; + productType = "com.apple.product-type.app-extension"; + }; 97C146ED1CF9000F007C117D /* Runner */ = { isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( 46164C762D3E5DACEA5EB7F0 /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, + 0321C3FA2AA9DC3400D462C1 /* Headers */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, 97C146EC1CF9000F007C117D /* Resources */, - 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 3F72706DAEB45E756D135F84 /* [CP] Embed Pods Frameworks */, 8CFC94838D10599DD335E9EC /* [CP] Copy Pods Resources */, + 03A1AB3F2AA890AB00FB41B2 /* Embed Foundation Extensions */, + 03ACAAB62AC5B7ED00D74DD4 /* ShellScript */, ); buildRules = ( ); dependencies = ( + 03A1AB392AA890AB00FB41B2 /* PBXTargetDependency */, ); name = Runner; + packageProductDependencies = ( + EC27DE8F2AC44FAD00B840F2 /* DBModule */, + ); productName = Runner; productReference = 97C146EE1CF9000F007C117D /* Runner.app */; productType = "com.apple.product-type.application"; }; + ECCA8C632ABD16D20037BF9B /* LanternTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = ECCA8C6D2ABD16D20037BF9B /* Build configuration list for PBXNativeTarget "LanternTests" */; + buildPhases = ( + 4E451C14EEC79D30CB4B7946 /* [CP] Check Pods Manifest.lock */, + ECCA8C602ABD16D20037BF9B /* Sources */, + ECCA8C612ABD16D20037BF9B /* Frameworks */, + ECCA8C622ABD16D20037BF9B /* Resources */, + 6CBDC5B399303E3AF83CFF31 /* [CP] Embed Pods Frameworks */, + BE89F0204C2A5649490D586B /* [CP] Copy Pods Resources */, + ); + buildRules = ( + ); + dependencies = ( + ECCA8C692ABD16D20037BF9B /* PBXTargetDependency */, + ); + name = LanternTests; + productName = LanternTests; + productReference = ECCA8C642ABD16D20037BF9B /* LanternTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { + LastSwiftUpdateCheck = 1430; LastUpgradeCheck = 1300; ORGANIZATIONNAME = ""; TargetAttributes = { + 03A1AB2F2AA890AB00FB41B2 = { + CreatedOnToolsVersion = 14.3.1; + }; 97C146ED1CF9000F007C117D = { CreatedOnToolsVersion = 7.3.1; LastSwiftMigration = 1100; }; + ECCA8C632ABD16D20037BF9B = { + CreatedOnToolsVersion = 14.3.1; + TestTargetID = 97C146ED1CF9000F007C117D; + }; }; }; buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; @@ -253,11 +507,20 @@ projectRoot = ""; targets = ( 97C146ED1CF9000F007C117D /* Runner */, + 03A1AB2F2AA890AB00FB41B2 /* Tunnel */, + ECCA8C632ABD16D20037BF9B /* LanternTests */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ + 03A1AB2E2AA890AB00FB41B2 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 97C146EC1CF9000F007C117D /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -269,9 +532,33 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + ECCA8C622ABD16D20037BF9B /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ + 03ACAAB62AC5B7ED00D74DD4 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "if [[ \"$(uname -m)\" == arm64 ]]; then\n export PATH=\"/opt/homebrew/bin:$PATH\"\nfi\n\nif which swiftlint > /dev/null; then\n swiftlint\nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n"; + }; 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; @@ -327,6 +614,45 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; + 4E451C14EEC79D30CB4B7946 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-LanternTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 6CBDC5B399303E3AF83CFF31 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-LanternTests/Pods-LanternTests-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-LanternTests/Pods-LanternTests-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-LanternTests/Pods-LanternTests-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; 8CFC94838D10599DD335E9EC /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -357,32 +683,103 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build\n"; + }; + BE89F0204C2A5649490D586B /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-LanternTests/Pods-LanternTests-resources-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Copy Pods Resources"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-LanternTests/Pods-LanternTests-resources-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-LanternTests/Pods-LanternTests-resources.sh\"\n"; + showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + 03A1AB2C2AA890AB00FB41B2 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 03567DBE2AA9F31200A233EA /* Logger.swift in Sources */, + 0308A13E2AAF43A10086157A /* UserNotificationsManager.swift in Sources */, + 0308A1312AAEF3B00086157A /* Event.swift in Sources */, + 03A1AB352AA890AB00FB41B2 /* PacketTunnelProvider.swift in Sources */, + 03567DBB2AA9F15100A233EA /* Constants.swift in Sources */, + 03567DC12AA9F47200A233EA /* FileUtils.swift in Sources */, + 03567DBC2AA9F18D00A233EA /* Process.swift in Sources */, + 0308A1392AAEF8DE0086157A /* FlashlightManager.swift in Sources */, + 03567DC42AA9F77000A233EA /* NEProviderStopReason.swift in Sources */, + 03567DC62AA9F82F00A233EA /* UDPConn.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 97C146EA1CF9000F007C117D /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 032A43582AA6F8AA00EF442B /* DnsDetector.swift in Sources */, + 03A1AB442AA8946400FB41B2 /* Constants.swift in Sources */, + 03FAF1B22AA1C7940063580C /* RunningEnv.swift in Sources */, + 0308A1342AAEF5130086157A /* VPNBase.swift in Sources */, 03F2FE342A6949EF0082B34C /* Logger.swift in Sources */, - 03F4CD7E2A77DE8500F7BDD8 /* SessionManager.swift in Sources */, + 0308A12E2AAEEF410086157A /* Events.swift in Sources */, 0369DF792A93803D000EBE43 /* MessagingModel.swift in Sources */, + 0333203B2AC698FA00333DA9 /* MockVPNManager.swift in Sources */, + 0308A12C2AAEEE2C0086157A /* UserNotificationsManager.swift in Sources */, + 03FAF1B42AA1E9F40063580C /* VpnModel.swift in Sources */, 03F4CD822A77E43C00F7BDD8 /* BaseModel.swift in Sources */, + 0321C3F92AA9D8DF00D462C1 /* FlashlightManager.swift in Sources */, 035BE7062A7122BC0084059A /* SessionModel.swift in Sources */, 036663D92A9E0C0E00595971 /* JsonUtils.swift in Sources */, + 03F00B5D2AB07B3800E991E2 /* NavigationModel.swift in Sources */, + 03AEDBFA2ABAD5B9000FDE52 /* ModelMethods.swift in Sources */, + 0308A1302AAEF3AA0086157A /* Event.swift in Sources */, 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, + 03A1AB142AA88BDA00FB41B2 /* VPNManager.swift in Sources */, + 0308A13D2AAF39F80086157A /* FlashlightManagerExtension.swift in Sources */, + 03567DC02AA9F47200A233EA /* FileUtils.swift in Sources */, 03802FA32A98C9B000DACDFC /* SubscriberRequest.swift in Sources */, + 03A1AB422AA8930A00FB41B2 /* Result.swift in Sources */, 035BE7052A7122BC0084059A /* LanternModel.swift in Sources */, - 03026EA32A77D67A001D5507 /* ValueUtil.swift in Sources */, - 03026EA12A77D654001D5507 /* Database.swift in Sources */, + 0308A1282AAEED7E0086157A /* VpnHelper.swift in Sources */, + 03F00B5F2AB07C8000E991E2 /* LoadingIndicatorManager.swift in Sources */, 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + 0321C3F72AA9D84700D462C1 /* Process.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + ECCA8C602ABD16D20037BF9B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ECCA8C672ABD16D20037BF9B /* LanternTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ +/* Begin PBXTargetDependency section */ + 03A1AB392AA890AB00FB41B2 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 03A1AB2F2AA890AB00FB41B2 /* Tunnel */; + targetProxy = 03A1AB382AA890AB00FB41B2 /* PBXContainerItemProxy */; + }; + ECCA8C692ABD16D20037BF9B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 97C146ED1CF9000F007C117D /* Runner */; + targetProxy = ECCA8C682ABD16D20037BF9B /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + /* Begin PBXVariantGroup section */ 97C146FA1CF9000F007C117D /* Main.storyboard */ = { isa = PBXVariantGroup; @@ -403,6 +800,121 @@ /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ + 03A1AB3C2AA890AB00FB41B2 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = Tunnel/Tunnel.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 4FYC28AXA2; + GCC_C_LANGUAGE_STANDARD = gnu11; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = Tunnel/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = Tunnel; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 16.4; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = 1.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + OTHER_LDFLAGS = "-lc++"; + PRODUCT_BUNDLE_IDENTIFIER = org.getlantern.lantern.Tunnel; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 03A1AB3D2AA890AB00FB41B2 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = Tunnel/Tunnel.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 4FYC28AXA2; + GCC_C_LANGUAGE_STANDARD = gnu11; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = Tunnel/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = Tunnel; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 16.4; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = 1.0; + MTL_FAST_MATH = YES; + OTHER_LDFLAGS = "-lc++"; + PRODUCT_BUNDLE_IDENTIFIER = org.getlantern.lantern.Tunnel; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + 03A1AB3E2AA890AB00FB41B2 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = Tunnel/Tunnel.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 4FYC28AXA2; + GCC_C_LANGUAGE_STANDARD = gnu11; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = Tunnel/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = Tunnel; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 16.4; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = 1.0; + MTL_FAST_MATH = YES; + OTHER_LDFLAGS = "-lc++"; + PRODUCT_BUNDLE_IDENTIFIER = org.getlantern.lantern.Tunnel; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Profile; + }; 249021D3217E4FDB00AE95B9 /* Profile */ = { isa = XCBuildConfiguration; buildSettings = { @@ -457,12 +969,20 @@ isa = XCBuildConfiguration; baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = 4FYC28AXA2; ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/InternalFrameworks", + ); INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = Lantern; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -585,12 +1105,20 @@ isa = XCBuildConfiguration; baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = 4FYC28AXA2; ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/InternalFrameworks", + ); INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = Lantern; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -608,12 +1136,20 @@ isa = XCBuildConfiguration; baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = 4FYC28AXA2; ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/InternalFrameworks", + ); INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = Lantern; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -626,9 +1162,119 @@ }; name = Release; }; + ECCA8C6A2ABD16D20037BF9B /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 2F26DCD30807EDF6F82D2EED /* Pods-LanternTests.debug.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 4FYC28AXA2; + GCC_C_LANGUAGE_STANDARD = gnu11; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 16.4; + MACOSX_DEPLOYMENT_TARGET = 13.3; + MARKETING_VERSION = 1.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = org.getlantern.lantern.LanternTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = auto; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Debug; + }; + ECCA8C6B2ABD16D20037BF9B /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = CA815533D3563C2353745CE2 /* Pods-LanternTests.release.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 4FYC28AXA2; + GCC_C_LANGUAGE_STANDARD = gnu11; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 16.4; + MACOSX_DEPLOYMENT_TARGET = 13.3; + MARKETING_VERSION = 1.0; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = org.getlantern.lantern.LanternTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = auto; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Release; + }; + ECCA8C6C2ABD16D20037BF9B /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 639C2FD1FB8C2B8BA0BC8FA2 /* Pods-LanternTests.profile.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 4FYC28AXA2; + GCC_C_LANGUAGE_STANDARD = gnu11; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 16.4; + MACOSX_DEPLOYMENT_TARGET = 13.3; + MARKETING_VERSION = 1.0; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = org.getlantern.lantern.LanternTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = auto; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Profile; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + 03A1AB3B2AA890AB00FB41B2 /* Build configuration list for PBXNativeTarget "Tunnel" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 03A1AB3C2AA890AB00FB41B2 /* Debug */, + 03A1AB3D2AA890AB00FB41B2 /* Release */, + 03A1AB3E2AA890AB00FB41B2 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { isa = XCConfigurationList; buildConfigurations = ( @@ -649,7 +1295,24 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + ECCA8C6D2ABD16D20037BF9B /* Build configuration list for PBXNativeTarget "LanternTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + ECCA8C6A2ABD16D20037BF9B /* Debug */, + ECCA8C6B2ABD16D20037BF9B /* Release */, + ECCA8C6C2ABD16D20037BF9B /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ + +/* Begin XCSwiftPackageProductDependency section */ + EC27DE8F2AC44FAD00B840F2 /* DBModule */ = { + isa = XCSwiftPackageProductDependency; + productName = DBModule; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 97C146E61CF9000F007C117D /* Project object */; } diff --git a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 000000000..cb4b75775 --- /dev/null +++ b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,14 @@ +{ + "pins" : [ + { + "identity" : "sqlite.swift", + "kind" : "remoteSourceControl", + "location" : "https://github.com/stephencelis/SQLite.swift.git", + "state" : { + "revision" : "7a2e3cd27de56f6d396e84f63beefd0267b55ccb", + "version" : "0.14.1" + } + } + ], + "version" : 2 +} diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 3db53b6e1..8e80a21b7 100644 --- a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -27,8 +27,6 @@ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" shouldUseLaunchSchemeArgsEnv = "YES"> - - - - + + + + + + - - Bool { - let controller : FlutterViewController = window?.rootViewController as! FlutterViewController - flutterbinaryMessenger=controller.binaryMessenger - setupModels() - prepareChannel() - setupLocal() + initializeFlutterComponents() + setupAppComponents() GeneratedPluginRegistrant.register(with: self) return super.application(application, didFinishLaunchingWithOptions: launchOptions) } - - private func setupModels(){ + + // Flutter related stuff + private func initializeFlutterComponents() { + flutterViewController = window?.rootViewController as! FlutterViewController + flutterbinaryMessenger = flutterViewController.binaryMessenger} + + // Intlize this GO model and callback + private func setupAppComponents() { + setupModels() + startUpSequency() + setupLoadingBar() + } + + // Init all the models + private func setupModels() { logger.log("setupModels method called") - //Init Session Model + // Init Session Model sessionModel=SessionModel(flutterBinary: flutterbinaryMessenger) - //Init Messaging Model + // Init Messaging Model messagingModel=MessagingModel(flutterBinary: flutterbinaryMessenger) - //Init Lantern Model + // Init Lantern Model lanternModel=LanternModel(flutterBinary: flutterbinaryMessenger) + // Init VPN Model + vpnModel=VpnModel(flutterBinary: flutterbinaryMessenger, vpnBase: VPNManager.appDefault) + // Init Navigation Model + navigationModel=NavigationModel(flutterBinary: flutterbinaryMessenger) } - - - private func prepareChannel (){ - logger.log("prepareChannel method called") - //Navigation Channel - navigationChannel=FlutterMethodChannel(name: NAVIGATION_METHOED_CHANNEL, binaryMessenger: flutterbinaryMessenger) - navigationChannel.setMethodCallHandler(handleNavigationethodCall) + + // Post start up + // Init all method needed for user + func startUpSequency() { + // setupLocal() + // createUser() + askNotificationPermssion() + } - - private func setupLocal(){ - let langStr = Locale.current.languageCode - if langStr != nil{ - sessionModel.setLocal(lang: langStr!) - logger.log("Local value found \(langStr)") - }else{ - logger.log("Local value found nil") + + func askNotificationPermssion() { + UserNotificationsManager.shared.requestNotificationPermission { granted in + if granted { + logger.debug("Notification Permssion is granted") + } else { + logger.debug("Notification Permssion is denied") + } } } - - - func handleNavigationethodCall(_ call: FlutterMethodCall, result: @escaping FlutterResult) { - // Handle your method calls here - // The 'call' contains the method name and arguments - // The 'result' can be used to send back the data to Flutter - - switch call.method { - case "yourMethod": - // handle yourMethod - break - default: - result(FlutterMethodNotImplemented) - } + + func setupLoadingBar() { + loadingManager = LoadingIndicatorManager(parentView: flutterViewController.view) } + } diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index 01cce0efb..4d3ada402 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -2,207 +2,8 @@ - GADApplicationIdentifier - ca-app-pub-2685698271254859~9283700921 - SKAdNetworkItems - - - SKAdNetworkIdentifier - cstr6suwn9.skadnetwork - - - SKAdNetworkIdentifier - 4fzdc2evr5.skadnetwork - - - SKAdNetworkIdentifier - 4pfyvq9l8r.skadnetwork - - - SKAdNetworkIdentifier - 2fnua5tdw4.skadnetwork - - - SKAdNetworkIdentifier - ydx93a7ass.skadnetwork - - - SKAdNetworkIdentifier - 5a6flpkh64.skadnetwork - - - SKAdNetworkIdentifier - p78axxw29g.skadnetwork - - - SKAdNetworkIdentifier - v72qych5uu.skadnetwork - - - SKAdNetworkIdentifier - ludvb6z3bs.skadnetwork - - - SKAdNetworkIdentifier - cp8zw746q7.skadnetwork - - - SKAdNetworkIdentifier - 3sh42y64q3.skadnetwork - - - SKAdNetworkIdentifier - c6k4g5qg8m.skadnetwork - - - SKAdNetworkIdentifier - s39g8k73mm.skadnetwork - - - SKAdNetworkIdentifier - 3qy4746246.skadnetwork - - - SKAdNetworkIdentifier - f38h382jlk.skadnetwork - - - SKAdNetworkIdentifier - hs6bdukanm.skadnetwork - - - SKAdNetworkIdentifier - v4nxqhlyqp.skadnetwork - - - SKAdNetworkIdentifier - wzmmz9fp6w.skadnetwork - - - SKAdNetworkIdentifier - yclnxrl5pm.skadnetwork - - - SKAdNetworkIdentifier - t38b2kh725.skadnetwork - - - SKAdNetworkIdentifier - 7ug5zh24hu.skadnetwork - - - SKAdNetworkIdentifier - gta9lk7p23.skadnetwork - - - SKAdNetworkIdentifier - vutu7akeur.skadnetwork - - - SKAdNetworkIdentifier - y5ghdn5j9k.skadnetwork - - - SKAdNetworkIdentifier - n6fk4nfna4.skadnetwork - - - SKAdNetworkIdentifier - v9wttpbfk9.skadnetwork - - - SKAdNetworkIdentifier - n38lu8286q.skadnetwork - - - SKAdNetworkIdentifier - 47vhws6wlr.skadnetwork - - - SKAdNetworkIdentifier - kbd757ywx3.skadnetwork - - - SKAdNetworkIdentifier - 9t245vhmpl.skadnetwork - - - SKAdNetworkIdentifier - eh6m2bh4zr.skadnetwork - - - SKAdNetworkIdentifier - a2p9lx4jpn.skadnetwork - - - SKAdNetworkIdentifier - 22mmun2rn5.skadnetwork - - - SKAdNetworkIdentifier - 4468km3ulz.skadnetwork - - - SKAdNetworkIdentifier - 2u9pt9hc89.skadnetwork - - - SKAdNetworkIdentifier - 8s468mfl3y.skadnetwork - - - SKAdNetworkIdentifier - klf5c3l5u5.skadnetwork - - - SKAdNetworkIdentifier - ppxm28t8ap.skadnetwork - - - SKAdNetworkIdentifier - ecpz2srf59.skadnetwork - - - SKAdNetworkIdentifier - uw77j35x4d.skadnetwork - - - SKAdNetworkIdentifier - pwa73g5rt2.skadnetwork - - - SKAdNetworkIdentifier - mlmmfzh3r3.skadnetwork - - - SKAdNetworkIdentifier - 578prtvx9j.skadnetwork - - - SKAdNetworkIdentifier - 4dzt52r2t5.skadnetwork - - - SKAdNetworkIdentifier - e5fvkxwrpn.skadnetwork - - - SKAdNetworkIdentifier - 8c4e2ghe7u.skadnetwork - - - SKAdNetworkIdentifier - zq492l623r.skadnetwork - - - SKAdNetworkIdentifier - 3rd42ekr43.skadnetwork - - - SKAdNetworkIdentifier - 3qcr597p9d.skadnetwork - - + CADisableMinimumFrameDurationOnPhone + CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleExecutable @@ -221,30 +22,234 @@ ???? CFBundleVersion $(FLUTTER_BUILD_NUMBER) + GADApplicationIdentifier + ca-app-pub-2685698271254859~9283700921 LSRequiresIPhoneOS + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + + SKAdNetworkItems + + + SKAdNetworkIdentifier + cstr6suwn9.skadnetwork + + + SKAdNetworkIdentifier + 4fzdc2evr5.skadnetwork + + + SKAdNetworkIdentifier + 4pfyvq9l8r.skadnetwork + + + SKAdNetworkIdentifier + 2fnua5tdw4.skadnetwork + + + SKAdNetworkIdentifier + ydx93a7ass.skadnetwork + + + SKAdNetworkIdentifier + 5a6flpkh64.skadnetwork + + + SKAdNetworkIdentifier + p78axxw29g.skadnetwork + + + SKAdNetworkIdentifier + v72qych5uu.skadnetwork + + + SKAdNetworkIdentifier + ludvb6z3bs.skadnetwork + + + SKAdNetworkIdentifier + cp8zw746q7.skadnetwork + + + SKAdNetworkIdentifier + 3sh42y64q3.skadnetwork + + + SKAdNetworkIdentifier + c6k4g5qg8m.skadnetwork + + + SKAdNetworkIdentifier + s39g8k73mm.skadnetwork + + + SKAdNetworkIdentifier + 3qy4746246.skadnetwork + + + SKAdNetworkIdentifier + f38h382jlk.skadnetwork + + + SKAdNetworkIdentifier + hs6bdukanm.skadnetwork + + + SKAdNetworkIdentifier + v4nxqhlyqp.skadnetwork + + + SKAdNetworkIdentifier + wzmmz9fp6w.skadnetwork + + + SKAdNetworkIdentifier + yclnxrl5pm.skadnetwork + + + SKAdNetworkIdentifier + t38b2kh725.skadnetwork + + + SKAdNetworkIdentifier + 7ug5zh24hu.skadnetwork + + + SKAdNetworkIdentifier + gta9lk7p23.skadnetwork + + + SKAdNetworkIdentifier + vutu7akeur.skadnetwork + + + SKAdNetworkIdentifier + y5ghdn5j9k.skadnetwork + + + SKAdNetworkIdentifier + n6fk4nfna4.skadnetwork + + + SKAdNetworkIdentifier + v9wttpbfk9.skadnetwork + + + SKAdNetworkIdentifier + n38lu8286q.skadnetwork + + + SKAdNetworkIdentifier + 47vhws6wlr.skadnetwork + + + SKAdNetworkIdentifier + kbd757ywx3.skadnetwork + + + SKAdNetworkIdentifier + 9t245vhmpl.skadnetwork + + + SKAdNetworkIdentifier + eh6m2bh4zr.skadnetwork + + + SKAdNetworkIdentifier + a2p9lx4jpn.skadnetwork + + + SKAdNetworkIdentifier + 22mmun2rn5.skadnetwork + + + SKAdNetworkIdentifier + 4468km3ulz.skadnetwork + + + SKAdNetworkIdentifier + 2u9pt9hc89.skadnetwork + + + SKAdNetworkIdentifier + 8s468mfl3y.skadnetwork + + + SKAdNetworkIdentifier + klf5c3l5u5.skadnetwork + + + SKAdNetworkIdentifier + ppxm28t8ap.skadnetwork + + + SKAdNetworkIdentifier + ecpz2srf59.skadnetwork + + + SKAdNetworkIdentifier + uw77j35x4d.skadnetwork + + + SKAdNetworkIdentifier + pwa73g5rt2.skadnetwork + + + SKAdNetworkIdentifier + mlmmfzh3r3.skadnetwork + + + SKAdNetworkIdentifier + 578prtvx9j.skadnetwork + + + SKAdNetworkIdentifier + 4dzt52r2t5.skadnetwork + + + SKAdNetworkIdentifier + e5fvkxwrpn.skadnetwork + + + SKAdNetworkIdentifier + 8c4e2ghe7u.skadnetwork + + + SKAdNetworkIdentifier + zq492l623r.skadnetwork + + + SKAdNetworkIdentifier + 3rd42ekr43.skadnetwork + + + SKAdNetworkIdentifier + 3qcr597p9d.skadnetwork + + + UIApplicationSupportsIndirectInputEvents + UILaunchStoryboardName LaunchScreen UIMainStoryboardFile Main UISupportedInterfaceOrientations - UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight + UIInterfaceOrientationPortrait UISupportedInterfaceOrientations~ipad - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown UIViewControllerBasedStatusBarAppearance - CADisableMinimumFrameDurationOnPhone - - UIApplicationSupportsIndirectInputEvents - diff --git a/ios/Runner/Lantern/Db/SubscriberRequest.swift b/ios/Runner/Lantern/Core/Db/SubscriberRequest.swift similarity index 70% rename from ios/Runner/Lantern/Db/SubscriberRequest.swift rename to ios/Runner/Lantern/Core/Db/SubscriberRequest.swift index fdbcce134..9ce6ce504 100644 --- a/ios/Runner/Lantern/Db/SubscriberRequest.swift +++ b/ios/Runner/Lantern/Core/Db/SubscriberRequest.swift @@ -7,15 +7,15 @@ import Foundation import Internalsdk -import Flutter +import DBModule class DetailsSubscriber: InternalsdkSubscriptionRequest { - + var subscriberID: String var path: String var updaterDelegate: DetailsSubscriberUpdater - - init(subscriberID: String, path: String, onChanges: @escaping ([String:Any], [String]) -> Void) { + + init(subscriberID: String, path: String, onChanges: @escaping ([String: Any], [String]) -> Void) { self.subscriberID = subscriberID self.path = path self.updaterDelegate = DetailsSubscriberUpdater() @@ -26,14 +26,13 @@ class DetailsSubscriber: InternalsdkSubscriptionRequest { self.updater = updaterDelegate updaterDelegate.onChangesCallback = onChanges } - -} +} class DetailsSubscriberUpdater: NSObject, InternalsdkUpdaterModelProtocol { var onChangesCallback: (([String: Any], [String]) -> Void)? typealias DynamicKeysData = [String: ItemDetail] - + func onChanges(_ cs: InternalsdkChangeSetInterface?) throws { guard let cs = cs else { throw NSError(domain: "onChangesError", code: 1, userInfo: [NSLocalizedDescriptionKey: "ChangeSet is nil"]) @@ -41,54 +40,51 @@ class DetailsSubscriberUpdater: NSObject, InternalsdkUpdaterModelProtocol { let decoder = JSONDecoder() var updatesDictionary: [String: Any] = [:] var deletesList: [String] = [] - + // Deserialize updates - if let updatesData = cs.updatesSerialized.data(using: .utf8), !updatesData.isEmpty { - do { - let updates = try decoder.decode(DynamicKeysData.self, from: updatesData) - - updatesDictionary = Dictionary(uniqueKeysWithValues: updates.map { (path, detail) -> (String, Any) in - return (path, detail.value.value) - }) - - } catch let jsonError { - logger.log("Error deserializing updates: \(jsonError.localizedDescription)") - throw jsonError - } - } else { - logger.log("No updatesSerialized data to parse or it's empty.") - } - - + if let updatesData = cs.updatesSerialized.data(using: .utf8), !updatesData.isEmpty { + do { + let updates = try decoder.decode(DynamicKeysData.self, from: updatesData) + + updatesDictionary = Dictionary(uniqueKeysWithValues: updates.map { (path, detail) -> (String, Any) in + return (path, detail.value.value) + }) + + } catch let jsonError { + logger.log("Error deserializing updates: \(jsonError.localizedDescription)") + throw jsonError + } + } else { + logger.log("No updatesSerialized data to parse or it's empty.") + } + // Deserialize deletes - if cs.deletesSerialized.lowercased() != "null" { - if let deletesData = cs.deletesSerialized.data(using: .utf8), !deletesData.isEmpty { - do { - - deletesList = try decoder.decode([String].self, from: deletesData) - } catch let jsonError { - logger.log("Error deserializing deletes: \(jsonError.localizedDescription)") - throw jsonError - } - } else { - logger.log("No deletesSerialized data to parse or it's empty.") - } - } - + if cs.deletesSerialized.lowercased() != "null" { + if let deletesData = cs.deletesSerialized.data(using: .utf8), !deletesData.isEmpty { + do { + + deletesList = try decoder.decode([String].self, from: deletesData) + } catch let jsonError { + logger.log("Error deserializing deletes: \(jsonError.localizedDescription)") + throw jsonError + } + } else { + logger.log("No deletesSerialized data to parse or it's empty.") + } + } + onChangesCallback?(updatesDictionary, deletesList) - + } } - - // Json Decode struct ItemDetail: Codable { let path: String let detailPath: String let value: ValueStruct - + enum CodingKeys: String, CodingKey { case path = "Path" case detailPath = "DetailPath" @@ -96,15 +92,14 @@ struct ItemDetail: Codable { } } - struct ValueStruct: Codable { let type: Int var value: Any? - + init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) type = try container.decode(Int.self, forKey: .type) - + switch type { case ValueUtil.TYPE_BYTES: // Assuming this corresponds to bytes // Handle bytes @@ -124,11 +119,11 @@ struct ValueStruct: Codable { ) } } - + func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(type, forKey: .type) - + switch type { case ValueUtil.TYPE_BYTES: // Handle bytes @@ -145,13 +140,9 @@ struct ValueStruct: Codable { throw EncodingError.invalidValue(value as Any, EncodingError.Context(codingPath: [CodingKeys.value], debugDescription: "Invalid type")) } } - + enum CodingKeys: String, CodingKey { case type = "Type" case value = "Value" } } - - - - diff --git a/ios/Runner/Lantern/Core/Extension/Result.swift b/ios/Runner/Lantern/Core/Extension/Result.swift new file mode 100644 index 000000000..bd3668c50 --- /dev/null +++ b/ios/Runner/Lantern/Core/Extension/Result.swift @@ -0,0 +1,15 @@ +// +// File.swift +// Lantern +// + +import Foundation + +extension Result { + var isSuccess: Bool { + switch self { + case .success: return true + case .failure: return false + } + } +} diff --git a/ios/Runner/Lantern/Core/Network/DnsDetector.swift b/ios/Runner/Lantern/Core/Network/DnsDetector.swift new file mode 100644 index 000000000..9fbee34f0 --- /dev/null +++ b/ios/Runner/Lantern/Core/Network/DnsDetector.swift @@ -0,0 +1,18 @@ +// +// DnsDetector.swift +// Runner +// +// Created by jigar fumakiya on 05/09/23. +// + +import Foundation +import NetworkExtension +import Darwin + +class DnsDetector { + + // Since IOS does allow to read dns server from Setting + // So while creating setting we will use this as our Ip so this will become dns server IP + static let DEFAULT_DNS_SERVER = "8.8.8.8" + +} diff --git a/ios/Shared/FlashlightManager.swift b/ios/Runner/Lantern/Core/Vpn/FlashlightManager.swift similarity index 80% rename from ios/Shared/FlashlightManager.swift rename to ios/Runner/Lantern/Core/Vpn/FlashlightManager.swift index 9a86d66ec..02fe940dc 100644 --- a/ios/Shared/FlashlightManager.swift +++ b/ios/Runner/Lantern/Core/Vpn/FlashlightManager.swift @@ -5,15 +5,9 @@ import Foundation import os.log -import Ios +import Internalsdk +import UIKit -// Temporary for log file issue hotfix -enum LanternError: Error { - case unknown -} - -// Any and all interaction with Go will run through FlashlightManager. -// See FlashlightManager+AppSide.swift for app-specific functionality. class FlashlightManager { static let `appDefault` = FlashlightManager(constants: .appDefault) static let `netExDefault` = FlashlightManager(constants: .netExDefault) @@ -34,15 +28,11 @@ class FlashlightManager { var hasCheckedForUpdate = false // MARK: Go Logging - func configureGoLoggerReturningError() -> Error? { var error: NSError? IosConfigureFileLogging(constants.goLogBaseURL.path, constants.targetDirectoryURL.path, &error) return error } - - - // MARK: Testing specific proxies // set the below to a proxies.yaml with whatever proxies you want to test, will override static let hardcodedProxies = "" diff --git a/ios/Runner/Lantern/Core/Vpn/FlashlightManagerExtension.swift b/ios/Runner/Lantern/Core/Vpn/FlashlightManagerExtension.swift new file mode 100644 index 000000000..47683a7e8 --- /dev/null +++ b/ios/Runner/Lantern/Core/Vpn/FlashlightManagerExtension.swift @@ -0,0 +1,45 @@ +// +// FlashlightManager+FetchConfig.swift +// Lantern +// + +import Foundation +import UIKit +import Internalsdk + +extension FlashlightManager { + // MARK: Config + func fetchConfig(userID: Int, proToken: String?, excludedIPsURL: URL? = nil, refreshProxies: Bool, _ completion: ((Result) -> Void)? = nil) { + var configError: NSError? + let configDirectory = constants.configDirectoryURL.path + let deviceID = UIDevice.current.identifierForVendor!.uuidString + + let workItem = DispatchWorkItem { + logger.debug("Calling IosConfigure") + let configResult = IosConfigure(configDirectory, userID, proToken, deviceID, refreshProxies, FlashlightManager.hardcodedProxies, &configError) + logger.debug("Called IosConfigure") + + guard configError == nil else { + assertionFailure("fetch config failed:" + configError!.localizedDescription) + completion?(.failure(configError!)) + return + } + + guard let result = configResult else { + completion?(.failure(VpnHelper.Error.unknown)) + return + } + + if let url = excludedIPsURL { + if result.vpnNeedsReconfiguring { + try! result.ipsToExcludeFromVPN.write(to: url, atomically: false, encoding: .utf8) + } + } + + completion?(.success(result.vpnNeedsReconfiguring)) + } + + // TODO: investigate: does this have to happen on goQueue? + queue.async(execute: workItem) + } +} diff --git a/ios/Runner/Lantern/Core/Vpn/MockVPNManager.swift b/ios/Runner/Lantern/Core/Vpn/MockVPNManager.swift new file mode 100644 index 000000000..76acab0c9 --- /dev/null +++ b/ios/Runner/Lantern/Core/Vpn/MockVPNManager.swift @@ -0,0 +1,47 @@ +// +// MockVPNManager.swift +// Lantern +// + +import Foundation +import NetworkExtension + +// Mostly for testing UI on simulator; +// Calls within actual VPNManager will crash on simulator. +class MockVPNManager: VPNBase { + + var connectionStatus: NEVPNStatus = .disconnected { + didSet { + guard oldValue != connectionStatus else { return } + didUpdateConnectionStatusCallback?(connectionStatus) + } + } + var didUpdateConnectionStatusCallback: ((NEVPNStatus) -> Void)? + + // MARK: Start/Stop Tunnel + + var startTunnelError: VPNManagerError? + + func startTunnel(completion: (Result) -> Void) { + connectionStatus = .connected + completion(startTunnelError == nil ? .success(()) : .failure(startTunnelError!)) + } + + func stopTunnel() { + connectionStatus = .disconnected + } + + // MARK: Message NetEx + + var messageNetExError: VPNManagerError? + var messageNetExResponseData: Data? + + func messageNetEx(messageData: Data, responseHandler: ((Data?) -> Void)?) throws { + if let error = messageNetExError { + throw error + } else { + let data = messageNetExResponseData + DispatchQueue.global().async { responseHandler?(data) } + } + } +} diff --git a/ios/Runner/Lantern/Core/Vpn/VPNBase.swift b/ios/Runner/Lantern/Core/Vpn/VPNBase.swift new file mode 100644 index 000000000..6fa9a76c7 --- /dev/null +++ b/ios/Runner/Lantern/Core/Vpn/VPNBase.swift @@ -0,0 +1,14 @@ +// +// VPNManagerType.swift +// Lantern +// + +import NetworkExtension + +protocol VPNBase: AnyObject { + var connectionStatus: NEVPNStatus { get } + var didUpdateConnectionStatusCallback: ((NEVPNStatus) -> Void)? { get set } + func startTunnel(completion: @escaping (Result) -> Void) + func stopTunnel() + func messageNetEx(messageData: Data, responseHandler: ((Data?) -> Void)?) throws +} diff --git a/ios/Runner/Lantern/Core/Vpn/VPNManager.swift b/ios/Runner/Lantern/Core/Vpn/VPNManager.swift new file mode 100644 index 000000000..42b23ccc4 --- /dev/null +++ b/ios/Runner/Lantern/Core/Vpn/VPNManager.swift @@ -0,0 +1,229 @@ +// +// VPNManager.swift +// Lantern +// + +import NetworkExtension +import Network + +enum VPNManagerError: Swift.Error { + case userDisallowedVPNConfigurations + case loadingProviderFailed + case savingProviderFailed + case unknown +} + +class VPNManager: VPNBase { + + static let appDefault = VPNManager(userDefaults: .standard) + + // This is used to determine if we have successfully set up a VPN configuration + // If user deletes VPN config in device Settings, status becomes "Invalid" and we clear this flag. + private static let expectsSavedProviderDefaultsKey = "VPNManager.savedProvider" + + private let queue: DispatchQueue = .global(qos: .userInitiated) + private var providerManager: NETunnelProviderManager? + + var connectionStatus: NEVPNStatus = .disconnected { + didSet { + guard oldValue != connectionStatus else { return } + didUpdateConnectionStatusCallback?(connectionStatus) + } + } + var didUpdateConnectionStatusCallback: ((NEVPNStatus) -> Void)? + + private let userDefaults: UserDefaults + private var expectsSavedProvider: Bool { + get { return userDefaults.bool(forKey: VPNManager.expectsSavedProviderDefaultsKey) } + set { userDefaults.set(newValue, forKey: VPNManager.expectsSavedProviderDefaultsKey) } + } + + // MARK: Init + private init(userDefaults: UserDefaults) { + self.userDefaults = userDefaults + subscribeToVPNStatusNotifications() + if expectsSavedProvider { + // setUpProvider() will prompt VPN Configuration permission if not granted, + // so only allow this when user has already set up, and we want VPN Status ASAP. + setUpProvider(completion: { _ in }) + } + } + + // MARK: NETunnelProviderManager Set Up + private func setUpProvider(completion: @escaping (Result) -> Void) { + queue.async { + VPNManager.loadSavedProvider { [weak self] result in + switch result { + case .success(let savedProvider): + guard let saved = savedProvider else { + self?.createAndSaveNewProvider(completion) + return + } + self?.providerManager = saved + self?.expectsSavedProvider = true + completion(.success(())) + case .failure(let error): + self?.expectsSavedProvider = false + completion(.failure(error)) + } + } + } + } + + private func createAndSaveNewProvider(_ completion: @escaping (Result) -> Void) { + let newManager = VPNManager.newProvider() + VPNManager.saveThenLoadProvider(newManager) { [weak self] result in + if result.isSuccess { + self?.providerManager = newManager + self?.expectsSavedProvider = true + } + completion(result) + } + } + + // MARK: Start VPN / Tunnel + func startTunnel(completion: @escaping (Result) -> Void) { + guard let provider = self.providerManager else { + setUpProvider { [weak self] result in + switch result { + case .success: // re-call + self?.startTunnel(completion: completion) + case .failure(let error): // complete + completion(.failure(error as! VPNManagerError)) + } + } + return + } + + do { + provider.isEnabled = true // calling start when !isEnabled will crash + let options = [Constants.netExStartReasonKey: NSString("User Initiated")] + try provider.connection.startVPNTunnel(options: options) + + // set onDemand enabled AFTER starting to avoid potential race-condition + provider.isOnDemandEnabled = true + + VPNManager.saveThenLoadProvider(provider, { _ in }) // necessary to persist isOnDemandEnabled + completion(.success(())) + } catch { + // re-mark needs set up + self.providerManager = nil + self.expectsSavedProvider = false + completion(.failure(.userDisallowedVPNConfigurations)) + } + } + + // MARK: Stop VPN / Tunnel + func stopTunnel() { + guard let provider = self.providerManager else { + // this should never happen but _just in case_ + self.expectsSavedProvider = false + return + } + provider.isOnDemandEnabled = false + VPNManager.saveThenLoadProvider(provider) { _ in + provider.connection.stopVPNTunnel() // no need to pass reason, system passes UserInitiated on its own + } + } + + // MARK: VPN Status Update + private func subscribeToVPNStatusNotifications() { + NotificationCenter.default.addObserver( + self, + selector: #selector(VPNManager.handleVPNStatusDidChangeNotification(_:)), + name: NSNotification.Name.NEVPNStatusDidChange, + object: nil + ) + } + + @objc private func handleVPNStatusDidChangeNotification(_ notification: Notification) { + // We can assume this session is the same as ours because: + // 1. Apps only have access to providers/VPNStatus that they have installed + // 2. This app specifically only installs/uses _one_ provider + guard let session = notification.object as? NETunnelProviderSession else { + assertionFailure("VPNStatus notification fired but unable to grab session") + return + } + + connectionStatus = session.status // potentially triggers update callback + + if session.status == .invalid { + // this indicates the user has deleted the VPN config in device Settings + // reset flag and nil provider so we can perform a clean config install + expectsSavedProvider = false + providerManager = nil + } + } + + // MARK: Messaging Net Ex + + func messageNetEx(messageData: Data, responseHandler: ((Data?) -> Void)?) throws { + guard let session = self.providerManager?.connection as? NETunnelProviderSession, session.status == .connected else { + return + } + try session.sendProviderMessage(messageData, responseHandler: responseHandler) + } + + // MARK: Provider Management + + static private func loadSavedProvider(_ completion: @escaping (Result) -> Void) { + // loadAllFromPreferences triggers VPNConfiguration permission prompt if not yet granted + NETunnelProviderManager.loadAllFromPreferences { (savedManagers, error) in + if error == nil { + completion(.success(savedManagers?.first)) // may be nil, but thats ok + } else { + completion(.failure(.loadingProviderFailed)) + } + } + } + + // This function exists because of this thread: https://forums.developer.apple.com/thread/25928 + static private func saveThenLoadProvider(_ provider: NETunnelProviderManager, _ completion: @escaping (Result) -> Void) { + provider.saveToPreferences { saveError in + if let _ = saveError { + completion(.failure(.savingProviderFailed)) + } else { + provider.loadFromPreferences { loadError in + if let _ = loadError { + completion(.failure(.loadingProviderFailed)) + } else { + completion(.success(())) + } + } + } + } + } + + static private func newProvider() -> NETunnelProviderManager { + let provider = NETunnelProviderManager() + let config = NETunnelProviderProtocol() + config.providerBundleIdentifier = Constants.netExBundleId + config.serverAddress = "0.0.0.0" // needs to be set but purely 8.8.8.8 + provider.protocolConfiguration = config + provider.isEnabled = true // calling start when disabled crashes + // Set rules for onDemand... + let alwaysConnectRule = NEOnDemandRuleConnect() + provider.onDemandRules = [alwaysConnectRule] + // BUT set to false for now— set to true RIGHT BEFORE calling start + // otherwise it will continually try to turn itself on BEFORE the user even hits the switch + provider.isOnDemandEnabled = false + + return provider + } + + // For debug use only— allows you to wipe all created providerManagers + static private func clearOldProviderManagers(_ completion: @escaping () -> Void) { + NETunnelProviderManager.loadAllFromPreferences { (savedManagers, _: Swift.Error?) in + guard let saved = savedManagers, !saved.isEmpty else { + completion() + return + } + let group = DispatchGroup() + saved.forEach({ old in + group.enter() + old.removeFromPreferences(completionHandler: { _ in group.leave() }) + }) + group.notify(queue: .main, execute: completion) + } + } +} diff --git a/ios/Runner/Lantern/Core/Vpn/VpnHelper.swift b/ios/Runner/Lantern/Core/Vpn/VpnHelper.swift new file mode 100644 index 000000000..1f523b44c --- /dev/null +++ b/ios/Runner/Lantern/Core/Vpn/VpnHelper.swift @@ -0,0 +1,338 @@ +// +// FlashlightManager+AppEvents.swift +// Lantern +// + +import Foundation +import UIKit +import UserNotifications +import NetworkExtension +import Internalsdk + +class VpnHelper: NSObject { + static let shared = VpnHelper(constants: Constants(process: .app), + fileManager: .default, + userDefaults: Constants.appGroupDefaults, + notificationCenter: .default, + flashlightManager: FlashlightManager.appDefault, + vpnManager: (isSimulator() ? MockVPNManager() : VPNManager.appDefault)) + // MARK: State + static let didUpdateStateNotification = Notification.Name("Lantern.didUpdateState") + enum VPNState: Equatable { + case configuring + case idle(Error?) + case connecting + case connected + case disconnecting + var isIdle: Bool { + if case .idle = self { return true } + return false + } + } + var configuring: Bool { + didSet { + guard oldValue != configuring else { return } + notificationCenter.post(name: VpnHelper.didUpdateStateNotification, object: nil) + } + } + + private let stateLock = NSLock() + private var _state: VPNState + var state: VPNState { + get { + stateLock.lock() + defer { stateLock.unlock() } + if configuring { + return .configuring + } + return _state + } + + set(newState) { + stateLock.lock() + guard _state != newState else { + stateLock.unlock() + return + } + _state = newState + notificationCenter.post(name: VpnHelper.didUpdateStateNotification, object: nil) + stateLock.unlock() + } + } + + static let hasFetchedConfigDefaultsKey = "Lantern.hasConfig" + // Apple + let fileManager: FileManager + let userDefaults: UserDefaults + let notificationCenter: NotificationCenter + // Custom + let constants: Constants + let flashlightManager: FlashlightManager + let vpnManager: VPNBase + var configFetchTimer: Timer! + var hasConfiguredThisSession = false + var hasFetchedConfigOnce: Bool { + return (userDefaults.value(forKey: VpnHelper.hasFetchedConfigDefaultsKey) as? Bool) ?? false + } + + init(constants: Constants, + fileManager: FileManager, + userDefaults: UserDefaults, + notificationCenter: NotificationCenter, + flashlightManager: FlashlightManager, + vpnManager: VPNBase, + userNotificationsManager: UserNotificationsManager? = nil + ) { + self.constants = constants + self.fileManager = fileManager + self.userDefaults = userDefaults + self.notificationCenter = notificationCenter + self.flashlightManager = flashlightManager + self.vpnManager = vpnManager + configuring = true + _state = .idle(nil) + super.init() + if self.hasFetchedConfigOnce { + self.configuring = false + } + performAppSetUp() + } + + // MARK: Set Up + func performAppSetUp() { + // STARTUP OVERVIEW + + // 1. set up files for flashlight + createFilesForAppGoPackage() + // Todo Use new method we are using in Android + // 2. set up data usage monitor + // dataUsageMonitor.startObservingDataUsageChanges(callback: handleDataUsageUpdated) + + // 3. set up VPN manager + vpnManager.didUpdateConnectionStatusCallback = handleVPNStatusUpdated + + logger.debug("Setting Go Log path to:\(constants.goLogBaseURL.path)") + if let error = flashlightManager.configureGoLoggerReturningError() { + logger.error("IosConfigureLogger FAILED: " + error.localizedDescription) + } + // 5 Fetch config + fetchConfigIfNecessary() + } + + private func createFilesForAppGoPackage() { + // where "~" is the shared app group container... + // create process-specific directory @ ~/app + + do { + try fileManager.ensureDirectoryExists(at: constants.targetDirectoryURL) + } catch { + logger.error("Failed to create directory @ \(constants.targetDirectoryURL.path)") + } + // create process-shared directory @ ~/config + do { + try fileManager.ensureDirectoryExists(at: constants.configDirectoryURL) + } catch { + logger.error("Failed to create directory @ \(constants.configDirectoryURL.path)") + } + + // create shared config files (eg- config.yaml, masquerade_cache, etc) @ ~/config/<_> + let configSuccess = fileManager.ensureFilesExist(at: constants.allConfigURLs) + if !configSuccess { + logger.error("Failed to create config files") + } + // create process-specific log files @ ~/app/lantern.log.# + var logURLs = fileManager.generateLogRotationURLs(count: Constants.defaultLogRotationFileCount, from: constants.goLogBaseURL) + logURLs.append(constants.heapProfileURL) + logURLs.append(constants.heapProfileTempURL) + logURLs.append(constants.goroutineProfileURL) + logURLs.append(constants.goroutineProfileTempURL) + let success = fileManager.ensureFilesExist(at: logURLs) + if !success { + logger.error("Failed to create log URLs") + } + } + + func startVPN( + onError: ((Error) -> Void)? = nil, + onSuccess: (() -> Void)? = nil + ) { + guard state.isIdle else { return } + if !hasFetchedConfigOnce { + initiateConfigFetching(onError: onError, onSuccess: onSuccess) + } else { + initiateVPNStart(onError: onError, onSuccess: onSuccess) + } + } + + private func initiateConfigFetching(onError: ((Error) -> Void)? = nil, onSuccess: (() -> Void)? = nil) { + configuring = true + fetchConfig { [weak self] result in + DispatchQueue.main.async { + self?.configuring = false + guard let state = self?.state, state.isIdle else { return } + if result.isSuccess { + self?.startVPN(onError: onError, onSuccess: onSuccess) + } else { + self?.state = .idle(.unableToFetchConfig) + onError?(.unableToFetchConfig) + } + } + } + } + + private func initiateVPNStart(onError: ((Error) -> Void)? = nil, onSuccess: (() -> Void)? = nil) { + vpnManager.startTunnel { result in + switch result { + case .success: + logger.debug("VPN successfully started") + onSuccess?() + case .failure(let error): + logger.error("VPN start failed: \(error.localizedDescription)") + onError?(.userDisallowedVPNConfig) + } + } + } + + func stopVPN() { + vpnManager.stopTunnel() + } + + // Internal method for VPN status + func handleVPNStatusUpdated(_ status: NEVPNStatus) { + let newState = translateVPNStatusToLanternState(status) + logger.debug("VPN status updated while \(state): \(newState)") + guard status != .reasserting && status != .invalid else { + state = .idle(.invalidVPNState) + stopVPN() + return + } + state = newState + } + + func translateVPNStatusToLanternState(_ status: NEVPNStatus) -> VpnHelper.VPNState { + switch status { + case .disconnected, .invalid, .reasserting: + return .idle(nil) + case .connecting: + return .connecting + case .connected: + return .connected + case .disconnecting: + return .disconnecting + @unknown default: + assertionFailure("Unhandled VPN state") + return .idle(.unknown) + } + } + + func fetchConfigIfNecessary() { + logger.debug("Checking if config fetch is needed") + guard !self.hasConfiguredThisSession else { return } + logger.debug("Will fetch config") + self.hasConfiguredThisSession = true + let hasFetchedConfigOnce = self.hasFetchedConfigOnce + fetchConfig { [weak self] result in + self?.setUpConfigFetchTimer() + DispatchQueue.main.async { + self?.configuring = false + if let state = self?.state { + guard state.isIdle else { return } + } + if !result.isSuccess && !hasFetchedConfigOnce { + self?.state = .idle(.unableToFetchConfig) + } + } + } + } + + func fetchConfig(refreshProxies: Bool = true, _ completion: @escaping (Result) -> Void) { + flashlightManager.fetchConfig(userID: self.userID, proToken: self.proToken, excludedIPsURL: constants.excludedIPsURL, refreshProxies: refreshProxies) { [weak self] result in + switch result { + case .success(let vpnNeedsReconfiguring): + if vpnNeedsReconfiguring { + self?.messageNetExToUpdateExcludedIPs() + } + self?.userDefaults.set(refreshProxies, forKey: VpnHelper.hasFetchedConfigDefaultsKey) + logger.debug("Successfully fetched new config with \(result)") + completion(.success(())) + case .failure(let error): + // TODO: convert this error to a Lantern.Error + logger.error("Fetch config failed:" + error.localizedDescription) + completion(.failure(error)) + } + } + } + + func setUpConfigFetchTimer() { + // set up timer on Main queue's runloop + // FlashlightManager will automatically use its designated goQueue when fetching + DispatchQueue.main.async { [weak self] in + let time: Double = 60 + self?.configFetchTimer = Timer.scheduledTimer(withTimeInterval: time, repeats: true, block: { [weak self] _ in + // Only auto-fetch new config when VPN is on + guard self?.state == .connected else { return } + logger.debug("Config Fetch timer fired after \(time), fetching...") + self?.fetchConfig { result in + switch result { + case .success: + logger.debug("Auto-config fetch success") + case .failure(let error): + logger.error("Auto-config fetch failed: \(error.localizedDescription)") + } + } + }) + } + } + + private func messageNetExToUpdateExcludedIPs() { + logger.debug("Notifying network extension of updated config") + do { + let msg = Constants.configUpdatedMessageData + try vpnManager.messageNetEx(messageData: msg) { data in + let success = (data == Constants.configUpdatedACKData) + let logMsg = (success ? "Successfully ACKd config update message" + : "Did not return expected ACK for config update message.") + logger.error("NetEx \(logMsg)") + } + } catch { + logger.error("Failed to message Provider from App— \(error.localizedDescription)") + } + } +} + +extension VpnHelper { + enum Error: Swift.Error { + case unknown + case userDisallowedVPNConfig + case unableToFetchConfig + case invalidVPNState + } +} + +extension VpnHelper { + // MARK: Pro + var userID: Int { + return self.userDefaults.integer(forKey: Constants.userID) + } + + var proToken: String? { + return self.userDefaults.string(forKey: Constants.proToken) + } + + var isPro: Bool { + if !self.userDefaults.bool(forKey: Constants.isPro) { + return false + } + + if self.userID == 0 { + return false + } + + let pt = self.proToken + if pt == nil || pt?.isEmpty == true { + return false + } + + return true + } +} diff --git a/ios/Runner/Lantern/Db/Database.swift b/ios/Runner/Lantern/Db/Database.swift deleted file mode 100644 index 7a0a537e5..000000000 --- a/ios/Runner/Lantern/Db/Database.swift +++ /dev/null @@ -1,238 +0,0 @@ -// -// DB.swift -// Runner -// -// Created by jigar fumakiya on 28/07/23. -// - -import Foundation -import Internalsdk -import FMDB -import SQLite - - -class DatabaseManager: NSObject, MinisqlDBProtocol { - private let db: Connection - private var currentTransaction: TransactionManager? - private let queue = DispatchQueue(label: "com.myapp.DatabaseManager", attributes: .concurrent) - - init(database: Connection) { - self.db = database - } - - func begin() throws -> MinisqlTxProtocol { - currentTransaction = TransactionManager(database: db) - return currentTransaction! - } - - func close()throws { - //Automatically manages the database connections - } - - func exec(_ query: String?, args: MinisqlValuesProtocol?) throws -> MinisqlResultProtocol { - guard let query = query, let args = args else { - throw NSError(domain: "ArgumentError", code: 1, userInfo: [NSLocalizedDescriptionKey: "Query or arguments are nil"]) - } - let bindings = ValueUtil.toBindingsArray(args) - let statement = try db.prepare(query) - - try statement.run(bindings) - return QueryResult(changes: db.totalChanges) - } - - func query(_ query: String?, args: MinisqlValuesProtocol?) throws -> MinisqlRowsProtocol { - - guard let query = query, let args = args else { - throw NSError(domain: "ArgumentError", code: 1, userInfo: [NSLocalizedDescriptionKey: "Query or arguments are nil"]) - } - - let bindings = ValueUtil.toBindingsArray(args) - let statement = try db.prepare(query) - - var rows: [Statement.Element] = [] - - try statement.run(bindings).forEach { row in - rows.append(row) - } - return RowData(rows: rows) - } - -} - -class TransactionManager: NSObject, MinisqlTxProtocol { - let database: Connection - var statements: [Statement] = [] - private var savepointName: String? - - init(database: Connection) { - self.database = database - } - - private func begin() throws { - savepointName = "Savepoint\(Date.currentTimeStamp)" - if let savepointName = savepointName { - try database.run("SAVEPOINT \(savepointName)") - } - } - - func commit() throws { - for statement in statements { - try statement.run() - } - if let savepointName = savepointName { - try database.run("RELEASE \(savepointName)") - } - statements = [] - savepointName = nil - } - - func rollback() throws { - if let savepointName = savepointName { - try database.run("ROLLBACK TO SAVEPOINT \(savepointName)") - try database.run("RELEASE \(savepointName)") - } - statements = [] - savepointName = nil - } - - func exec(_ query: String?, args: MinisqlValuesProtocol?) throws -> MinisqlResultProtocol { - guard let query = query, let args = args else { - throw NSError(domain: "ArgumentError", code: 1, userInfo: [NSLocalizedDescriptionKey: "Query or arguments are nil"]) } - - let bindings = ValueUtil.toBindingsArray(args) - let statement = try database.prepare(query) - statements.append(statement) - // Start a transaction if none has been started yet - if savepointName == nil { - try begin() - } - - try statement.run(bindings) - return QueryResult(changes: database.totalChanges) - } - - func query(_ query: String?, args: MinisqlValuesProtocol?) throws -> MinisqlRowsProtocol { - guard let query = query, let args = args else { - throw NSError(domain: "ArgumentError", code: 1, userInfo: [NSLocalizedDescriptionKey: "Query or arguments are nil"]) } - - let bindings = ValueUtil.toBindingsArray(args) - let statement = try database.prepare(query) - statements.append(statement) - // Start a transaction if none has been started yet - if savepointName == nil { - try begin() - } - - var rows: [Statement.Element] = [] - - for row in try statement.run(bindings) { - rows.append(row) - } - - return RowData(rows: rows) - } -} - -class QueryResult: NSObject, MinisqlResultProtocol { - - let changes: Int - - init(changes: Int) { - self.changes = changes - } - - func lastInsertId(_ ret0_: UnsafeMutablePointer?) throws -> Void { - ret0_?.pointee = Int64(changes) - } - - func rowsAffected(_ ret0_: UnsafeMutablePointer?) throws -> Void { - ret0_?.pointee = Int64(changes) - } -} - -class RowData: NSObject, MinisqlRowsProtocol { - - let rows: [Statement.Element] - var currentIndex: Int = -1 - private let syncQueue = DispatchQueue(label: "com.yourapp.RowData.syncQueue") - - - init(rows: [Statement.Element]) { - self.rows = rows - } - - func close() throws { - // Not sure what to put here - } - - func next() -> Bool { - currentIndex += 1 - return currentIndex < rows.count - } - - /** - This method scans the current row and converts its values to `MinisqlValue` objects. - This method assumes that `values` is an object that supports setting values by index, and `rows` is an array of arrays where each inner array represents a row from a database and contains values of type `Binding`. - - Parameter values: An object that conforms to `MinisqlValuesProtocol`. This object will be populated with the values from the current row, converted to `MinisqlValue` objects. - - Throws: An `NSError` if `values` is `nil` or if `currentIndex` is outside the bounds of the `rows` array. - - Note: This method updates `currentIndex` to point to the next row. If there are no more rows, `next()` will return `false`. - */ - func scan(_ values: MinisqlValuesProtocol?) throws { - try syncQueue.sync { - if values == nil { - logger.log("Error: values is nil") - throw NSError(domain: "Scan method failed", code: 0, userInfo: [NSLocalizedDescriptionKey: "Values object is nil"]) - } - if currentIndex >= rows.count { - logger.log("Error: currentIndex \(currentIndex) is out of bounds") - throw NSError(domain: "Scan method failed", code: 0, userInfo: [NSLocalizedDescriptionKey: "Current index is out of bounds"]) - } - let currentRow = rows[currentIndex] - for (index, value) in currentRow.enumerated() { - let miniSqlValue = values!.get(index)! - ValueUtil.setValueFromBinding(binding: value!, value: miniSqlValue) - } - } - } - -} - -class ValueArrayHandler: NSObject, MinisqlValuesProtocol { - - var values: [MinisqlValue] - - init(values: [MinisqlValue]) { - self.values = values - } - - func get(_ index: Int) -> MinisqlValue? { - guard index < values.count else { - print("Error: Index out of bounds while trying to get value.") - return nil - } - return values[index] - } - - func len() -> Int { - return values.count - } - - func set(_ index: Int, value: MinisqlValue?) { - guard index < values.count else { - print("Error: Index out of bounds while trying to set value.") - return - } - - guard let value = value else { - print("Error: Attempted to set nil value.") - return - } - - values[index] = value - } -} -extension Date { - static var currentTimeStamp: Int64{ - return Int64(Date().timeIntervalSince1970 * 1000) - } -} diff --git a/ios/Runner/Lantern/EventManager.swift b/ios/Runner/Lantern/EventManager.swift deleted file mode 100644 index 8ca59ff9f..000000000 --- a/ios/Runner/Lantern/EventManager.swift +++ /dev/null @@ -1,79 +0,0 @@ -// -// EventManager.swift -// Runner -// -// Created by jigar fumakiya on 14/07/23. -// - -import Foundation -import Flutter - - -enum Event: String, CaseIterable { - case all = "All" - case surveyAvailable = "SurveyAvailable" - case noNetworkAvailable = "NoNetworkAvailable" - case networkAvailable = "NetworkAvailable" - case noProxyAvailable = "NoProxyAvailable" - case proxyAvailable = "ProxyAvailable" -} - - -class EventManager: NSObject, FlutterStreamHandler { - private let name: String - private var activeSubscribers: [Event : Set] = [:] - private var activeSink: FlutterEventSink? - private let mainThreadHandler = DispatchQueue.main - var onListenClosure: (Event) -> Void - - init(name: String, binaryMessenger: FlutterBinaryMessenger ,onListenClosure: @escaping ((Event) -> Void)) { - self.onListenClosure = onListenClosure - self.name = name - super.init() - let eventChannel = FlutterEventChannel(name: name, binaryMessenger: binaryMessenger) - eventChannel.setStreamHandler(self) - - } - - func onNewEvent(event: Event, params: [String : Any] = [:]) { - var params = params - mainThreadHandler.async { - self.activeSubscribers[event]?.forEach { subscriberID in - params["subscriberID"] = subscriberID - self.activeSink?(params) - } - - self.activeSubscribers[.all]?.forEach { subscriberID in - params["subscriberID"] = subscriberID - self.activeSink?(params) - } - } - } - - - // MARK: - FlutterStreamHandler - func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? { - activeSink = events - if let args = arguments as? [String: Any], - let subscriberID = args["subscriberID"] as? Int, - let eventName = args["eventName"] as? String, - let event = Event(rawValue: eventName) { - let subscribers = activeSubscribers[event] ?? [] - activeSubscribers[event] = subscribers.union([subscriberID]) - onListenClosure(event) - - } - return nil - } - - func onCancel(withArguments arguments: Any?) -> FlutterError? { - if let args = arguments as? [String: Any], let subscriberID = args["subscriberID"] as? Int { - activeSubscribers.forEach { event, subscriberSet in - activeSubscribers[event]?.remove(subscriberID) - } - } - return nil - } - - -} diff --git a/ios/Runner/Lantern/Models/BaseModel.swift b/ios/Runner/Lantern/Models/BaseModel.swift index ebb001d7f..b53213d87 100644 --- a/ios/Runner/Lantern/Models/BaseModel.swift +++ b/ios/Runner/Lantern/Models/BaseModel.swift @@ -9,14 +9,15 @@ import Foundation import Internalsdk import SQLite import Flutter +import DBModule enum ModelType { case sessionModel case messagingModel + case vpnModel } - -open class BaseModel: NSObject ,FlutterStreamHandler{ +open class BaseModel: NSObject, FlutterStreamHandler { var model: T! private var schema: String = "LANTERN_DB" private var modelType: ModelType @@ -27,23 +28,22 @@ open class BaseModel: NSObject ,FlutterStreamHandler{ var activeSubscribers: Set = [] private let mainHandler = DispatchQueue.main private let asyncHandler = DispatchQueue(label: "BaseModel-AsyncHandler") - - - init(type: ModelType,flutterBinary:FlutterBinaryMessenger) { + + init(type: ModelType, flutterBinary: FlutterBinaryMessenger) { self.modelType = type self.binaryMessenger = flutterBinary super.init() setupDB() setupFlutterChannels() } - + private func setupDB() { do { let dbPath = getDatabasePath() - let db = try Connection(dbPath) - let swiftDB = DatabaseManager(database: db) + // let db = try DatabaseManager.getDbManager(databasePath: dbPath) + let swiftDB = try DatabaseFactory.getDbManager(databasePath: dbPath) var error: NSError? - + // Depending on the model type, initialize the correct model switch modelType { case .sessionModel: @@ -56,13 +56,18 @@ open class BaseModel: NSObject ,FlutterStreamHandler{ throw error! } self.model = createdModel as! T + case .vpnModel: + guard let createdModel = InternalsdkNewVpnModel(self.schema, swiftDB, &error) else { + throw error! + } + self.model = createdModel as! T } - + } catch { logger.log("Failed to create new model: \(error)") } } - + private func getDatabasePath() -> String { let fileManager = FileManager.default let dbDir = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first!.appendingPathComponent("masterDBv2") @@ -76,7 +81,7 @@ open class BaseModel: NSObject ,FlutterStreamHandler{ return "" // Return an empty string or handle the error accordingly. } } - + private func setupFlutterChannels() { var modelName = "" switch modelType { @@ -84,31 +89,34 @@ open class BaseModel: NSObject ,FlutterStreamHandler{ modelName = "session" case .messagingModel: modelName = "messaging" + case .vpnModel: + modelName = "vpn" } + eventChannel = FlutterEventChannel(name: "\(modelName)_event_channel", binaryMessenger: binaryMessenger) eventChannel.setStreamHandler(self) - + methodChannel = FlutterMethodChannel(name: "\(modelName)_method_channel", binaryMessenger: binaryMessenger) methodChannel.setMethodCallHandler(handleMethodCall) } - + public func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? { -// logger.log("onListen initiated with arguments: \(arguments)") + logger.log("onListen initiated with arguments: \(arguments)") guard let args = arguments as? [String: Any] else { let errorMessage = "Failed to cast arguments \(arguments) to dictionary. Exiting..." - return createFlutterError(code:"INVALID_ARGUMENTS", message: errorMessage) + return createFlutterError(code: "INVALID_ARGUMENTS", message: errorMessage) } activeSinks.set(events) guard let subscriberID = args["subscriberID"] as? String, let path = args["path"] as? String else { let errorMessage = "Required parameters subscriberID or path missing in arguments. Exiting..." - return createFlutterError(code:"MISSING_PARAMETERS", message: errorMessage) + return createFlutterError(code: "MISSING_PARAMETERS", message: errorMessage) } - + let details = args["details"] as? Bool ?? false // Mark the subscriber as active activeSubscribers.insert(subscriberID) - + // Closure to send events back to the Flutter side asynchronously let notifyActiveSink = { (data: [String: Any]) in self.mainHandler.async { @@ -126,42 +134,41 @@ open class BaseModel: NSObject ,FlutterStreamHandler{ notifyActiveSink(data) } } - + do { try handleSubscribe(for: model, subscriber: subscriber) } catch let error { let errorMessage = "An error occurred while subscribing: \(error.localizedDescription)" - return createFlutterError(code:"SUBSCRIBE_ERROR", message: errorMessage) - } + return createFlutterError(code: "SUBSCRIBE_ERROR", message: errorMessage) + } return nil } - + public func onCancel(withArguments arguments: Any?) -> FlutterError? { - if(arguments==nil){ + if arguments==nil { return nil } guard let args = arguments as? [String: Any] else { let errorMessage = "onCancel Failed to cast arguments \(arguments) to dictionary. Exiting..." - return createFlutterError(code:"INVALID_ARGUMENTS", message: errorMessage) + return createFlutterError(code: "INVALID_ARGUMENTS", message: errorMessage) } - + guard let subscriberID = args["subscriberID"] as? String else { let errorMessage = "Required parameters subscriberID missing in arguments. Exiting..." - return createFlutterError(code:"MISSING_PARAMETERS", message: errorMessage) + return createFlutterError(code: "MISSING_PARAMETERS", message: errorMessage) } - + do { try handleUnsubscribe(for: model, subscriberID: subscriberID) activeSubscribers.remove(subscriberID) } catch let error { let errorMessage = "An error occurred while unsubscribing: \(error.localizedDescription)" - return createFlutterError(code:"UNSUBSCRIBE_ERROR", message: errorMessage) + return createFlutterError(code: "UNSUBSCRIBE_ERROR", message: errorMessage) } return nil } - - - //Method channels + + // Method channels func handleMethodCall(_ call: FlutterMethodCall, result: @escaping FlutterResult) { // Handle your method calls here // The 'call' contains the method name and arguments @@ -170,43 +177,44 @@ open class BaseModel: NSObject ,FlutterStreamHandler{ self.doOnMethodCall(call: call, result: result) } } - + open func doOnMethodCall(call: FlutterMethodCall, result: @escaping FlutterResult) { // Default implementation. This will force subclasses to provide their own implementation. fatalError("Subclasses must implement this method.") } - + func handleSubscribe(for model: Any, subscriber: DetailsSubscriber) throws { switch model { case let sessionModelSub as InternalsdkSessionModel: try sessionModelSub.subscribe(subscriber) case let messagingModel as InternalsdkMessagingModel: try messagingModel.subscribe(subscriber) + case let vpnModel as InternalsdkVpnModel: + try vpnModel.subscribe(subscriber) default: throw NSError(domain: "UnsupportedModel", code: 999, userInfo: ["Description": "Unsupported model type."]) } } - + func handleUnsubscribe(for model: Any, subscriberID: String) throws { switch model { case let sessionSub as InternalsdkSessionModel: try sessionSub.unsubscribe(subscriberID) case let messagingModel as InternalsdkMessagingModel: try messagingModel.unsubscribe(subscriberID) + case let vpnModel as InternalsdkVpnModel: + try vpnModel.unsubscribe(subscriberID) default: throw NSError(domain: "UnsupportedModel", code: 999, userInfo: ["Description": "Unsupported model type."]) } } - + private func createFlutterError(code: String, message: String, details: Any? = nil) -> FlutterError { logger.log(message) return FlutterError(code: code, message: message, details: details) } - -} - - +} /// A simple thread-safe wrapper for atomic property access. class AtomicReference { diff --git a/ios/Runner/Lantern/Models/LanternModel.swift b/ios/Runner/Lantern/Models/LanternModel.swift index d44d4d25b..aa9ffe13a 100644 --- a/ios/Runner/Lantern/Models/LanternModel.swift +++ b/ios/Runner/Lantern/Models/LanternModel.swift @@ -9,19 +9,17 @@ import Foundation import Internalsdk import Flutter -class LanternModel:NSObject ,FlutterStreamHandler{ - - - +class LanternModel: NSObject, FlutterStreamHandler { + let LANTERN_EVENT_CHANNEL="lantern_event_channel" let LANTERN_METHOED_CHANNEL="lantern_method_channel" - - var lanternMethodChannel:FlutterMethodChannel! - var lanternEventChannel:FlutterEventChannel! - var flutterbinaryMessenger:FlutterBinaryMessenger - - init(flutterBinary:FlutterBinaryMessenger) { + var lanternMethodChannel: FlutterMethodChannel! + var lanternEventChannel: FlutterEventChannel! + + var flutterbinaryMessenger: FlutterBinaryMessenger + + init(flutterBinary: FlutterBinaryMessenger) { self.flutterbinaryMessenger=flutterBinary super.init() lanternMethodChannel = FlutterMethodChannel(name: LANTERN_METHOED_CHANNEL, binaryMessenger: flutterBinary) @@ -30,13 +28,12 @@ class LanternModel:NSObject ,FlutterStreamHandler{ lanternEventChannel.setStreamHandler(self) } - - + func handleMethodCall(_ call: FlutterMethodCall, result: @escaping FlutterResult) { // Handle your method calls here // The 'call' contains the method name and arguments // The 'result' can be used to send back the data to Flutter - + switch call.method { case "yourMethod": // handle yourMethod @@ -45,15 +42,13 @@ class LanternModel:NSObject ,FlutterStreamHandler{ result(FlutterMethodNotImplemented) } } - - + func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? { return nil } - + func onCancel(withArguments arguments: Any?) -> FlutterError? { return nil } - -} +} diff --git a/ios/Runner/Lantern/Models/MessagingModel.swift b/ios/Runner/Lantern/Models/MessagingModel.swift index 89951d35b..1e152a373 100644 --- a/ios/Runner/Lantern/Models/MessagingModel.swift +++ b/ios/Runner/Lantern/Models/MessagingModel.swift @@ -8,34 +8,35 @@ import Foundation import Internalsdk import Flutter +import DBModule -class MessagingModel:BaseModel{ - var flutterbinaryMessenger:FlutterBinaryMessenger +class MessagingModel: BaseModel { + var flutterbinaryMessenger: FlutterBinaryMessenger - init(flutterBinary:FlutterBinaryMessenger) { + init(flutterBinary: FlutterBinaryMessenger) { self.flutterbinaryMessenger=flutterBinary - super.init(type: .messagingModel , flutterBinary: self.flutterbinaryMessenger) - } - - // Todo Move to base class + super.init(type: .messagingModel, flutterBinary: self.flutterbinaryMessenger) + } + + // Todo Move to base class override func doOnMethodCall(call: FlutterMethodCall, result: @escaping FlutterResult) { - // Convert the entire arguments to a single MinisqlValue + // Convert the entire arguments to a single MinisqlValue guard let minisqlValue = ValueUtil.convertToMinisqlValue(call.arguments) else { - result(FlutterError(code: "ARGUMENTS_ERROR", message: "Failed to convert arguments to MinisqlValue", details: nil)) - return - } - + result(FlutterError(code: "ARGUMENTS_ERROR", message: "Failed to convert arguments to MinisqlValue", details: nil)) + return + } + do { let invocationResult = try invokeMethodOnGo(name: call.method, argument: minisqlValue) - + if let originalValue = ValueUtil.convertFromMinisqlValue(from: invocationResult as! MinisqlValue) { result(originalValue) } else { result(FlutterError(code: "CONVERSION_ERROR", message: "Failed to convert MinisqlValue back to original value", details: nil)) } - + } catch let error as NSError { - + // Check for the specific "method not implemented" error if error.localizedDescription.contains("method not implemented") { result(FlutterMethodNotImplemented) @@ -49,13 +50,12 @@ class MessagingModel:BaseModel{ result(FlutterError(code: "UNKNOWN_ERROR", message: error.localizedDescription, details: nil)) } } - } - + } + func invokeMethodOnGo(name: String, argument: MinisqlValue) throws -> Any { // Convert any argument to Minisql values - let goResult = try model.invokeMethod(name, arguments: ValueArrayHandler(values: [argument])) + let goResult = try model.invokeMethod(name, arguments: ValueArrayFactory.createValueArrayHandler(values: [argument])) return goResult } - - + } diff --git a/ios/Runner/Lantern/Models/ModelMethods.swift b/ios/Runner/Lantern/Models/ModelMethods.swift new file mode 100644 index 000000000..85a891002 --- /dev/null +++ b/ios/Runner/Lantern/Models/ModelMethods.swift @@ -0,0 +1,69 @@ +// +// ModelMethods.swift +// Runner +// +// Created by jigar fumakiya on 20/09/23. +// + +import Foundation +import Internalsdk + +// This extension encapsulates various methods related to different models in the application, providing a type-safe way to reference methods for go (internalsdk). +// +extension SessionModel { + enum Methods { + case acceptTerms + case createUser + case getBandwidth + case initModel + case setCurrency + case setDeviceId + case setDnsServer + case setEmail + case setForceCountry + case setLocal + case setProvider + case setProUser + case setReferralCode + case setSelectedTab + case setStoreVersion + case setTimezone + + var methodName: String { + switch self { + case .acceptTerms: + return InternalsdkSESSION_MODEL_METHOD_ACCEPT_TERMS + case .createUser: + return InternalsdkSESSION_MODEL_METHOD_CREATE_USER + case .getBandwidth: + return InternalsdkSESSION_MODEL_METHOD_GET_BANDWIDTH + case .initModel: + return InternalsdkSESSION_MODEL_METHOD_INIT_MODEL + case .setCurrency: + return InternalsdkSESSION_MODEL_METHOD_SET_CURRENCY + case .setDeviceId: + return InternalsdkSESSION_MODEL_METHOD_SET_DEVICEID + case .setDnsServer: + return InternalsdkSESSION_MODEL_METHOD_SET_DNS_SERVER + case .setEmail: + return InternalsdkSESSION_MODEL_METHOD_SET_EMAIL + case .setForceCountry: + return InternalsdkSESSION_MODEL_METHOD_SET_FORCE_COUNTRY + case .setLocal: + return InternalsdkSESSION_MODEL_METHOD_SET_LOCAL + case .setProvider: + return InternalsdkSESSION_MODEL_METHOD_SET_PROVIDER + case .setProUser: + return InternalsdkSESSION_MODEL_METHOD_SET_PRO_USER + case .setReferralCode: + return InternalsdkSESSION_MODEL_METHOD_SET_REFERAL_CODE + case .setSelectedTab: + return InternalsdkSESSION_MODEL_METHOD_SET_SELECTED_TAB + case .setStoreVersion: + return InternalsdkSESSION_MODEL_METHOD_SET_STORE_VERSION + case .setTimezone: + return InternalsdkSESSION_MODEL_METHOD_SET_TIMEZONE + } + } + } +} diff --git a/ios/Runner/Lantern/Models/NavigationModel.swift b/ios/Runner/Lantern/Models/NavigationModel.swift new file mode 100644 index 000000000..2babc9828 --- /dev/null +++ b/ios/Runner/Lantern/Models/NavigationModel.swift @@ -0,0 +1,41 @@ +// +// NavigationModel.swift +// Runner +// +// Created by jigar fumakiya on 12/09/23. +// + +import Foundation +import Flutter + +class NavigationModel { + let navigationMethodChannel = "lantern_method_channel" + + var flutterbinaryMessenger: FlutterBinaryMessenger + + init(flutterBinary: FlutterBinaryMessenger) { + self.flutterbinaryMessenger=flutterBinary + prepareNavigationChannel() + } + + private func prepareNavigationChannel () { + + // Navigation Channel + let navigationChannel=FlutterMethodChannel(name: navigationMethodChannel, binaryMessenger: flutterbinaryMessenger) + navigationChannel.setMethodCallHandler(handleNavigationethodCall) + } + + func handleNavigationethodCall(_ call: FlutterMethodCall, result: @escaping FlutterResult) { + // Handle your method calls here + // The 'call' contains the method name and arguments + // The 'result' can be used to send back the data to Flutter + switch call.method { + case "yourMethod": + // handle yourMethod + break + default: + result(FlutterMethodNotImplemented) + } + } + +} diff --git a/ios/Runner/Lantern/Models/SessionManager.swift b/ios/Runner/Lantern/Models/SessionManager.swift deleted file mode 100644 index 9590b9b1c..000000000 --- a/ios/Runner/Lantern/Models/SessionManager.swift +++ /dev/null @@ -1,117 +0,0 @@ -// -// SessionManager.swift -// Runner -// Created by jigar fumakiya on 31/07/23. -// - -import Foundation -import Internalsdk -import SQLite -// -//class SessionManager: NSObject, InternalsdkSessionProtocol { -// -// func bandwidthUpdate(_ p0: Int, p1: Int, p2: Int, p3: Int) throws { -// -// } -// -// func code(_ error: NSErrorPointer) -> String { -// -// } -// -// func currency(_ error: NSErrorPointer) -> String { -// -// } -// -// func deviceOS(_ error: NSErrorPointer) -> String { -// -// } -// -// func email(_ error: NSErrorPointer) -> String { -// -// } -// -// func forceReplica() -> Bool { -// -// } -// -// func getAppName() -> String { -// -// } -// -// func getCountryCode(_ error: NSErrorPointer) -> String { -// -// } -// -// func getDNSServer(_ error: NSErrorPointer) -> String { -// -// } -// -// func getDeviceID(_ error: NSErrorPointer) -> String { -// <#code#> -// } -// -// func getForcedCountryCode(_ error: NSErrorPointer) -> String { -// <#code#> -// } -// -// func getTimeZone(_ error: NSErrorPointer) -> String { -// <#code#> -// } -// -// func getToken(_ error: NSErrorPointer) -> String { -// <#code#> -// } -// -// func getUserID(_ ret0_: UnsafeMutablePointer?) throws { -// <#code#> -// } -// -// func isPlayVersion(_ ret0_: UnsafeMutablePointer?) throws { -// <#code#> -// } -// -// func isProUser(_ ret0_: UnsafeMutablePointer?) throws { -// <#code#> -// } -// -// func locale(_ error: NSErrorPointer) -> String { -// <#code#> -// } -// -// func provider(_ error: NSErrorPointer) -> String { -// <#code#> -// } -// -// func serializedInternalHeaders(_ error: NSErrorPointer) -> String { -// <#code#> -// } -// -// func setChatEnabled(_ p0: Bool) { -// <#code#> -// } -// -// func setCountry(_ p0: String?) throws { -// <#code#> -// } -// -// func setReplicaAddr(_ p0: String?) { -// <#code#> -// } -// -// func setStaging(_ p0: Bool) throws { -// <#code#> -// } -// -// func splitTunnelingEnabled(_ ret0_: UnsafeMutablePointer?) throws { -// <#code#> -// } -// -// func update(_ p0: InternalsdkAdSettingsProtocol?) throws { -// <#code#> -// } -// -// func updateStats(_ p0: String?, p1: String?, p2: String?, p3: Int, p4: Int, p5: Bool) throws { -// <#code#> -// } - -//} diff --git a/ios/Runner/Lantern/Models/SessionModel.swift b/ios/Runner/Lantern/Models/SessionModel.swift index 7512dc9dd..51a4e55a4 100644 --- a/ios/Runner/Lantern/Models/SessionModel.swift +++ b/ios/Runner/Lantern/Models/SessionModel.swift @@ -8,40 +8,50 @@ import Foundation import Internalsdk import Flutter +import UIKit +import DBModule -class SessionModel:BaseModel { - var flutterbinaryMessenger:FlutterBinaryMessenger - - init(flutterBinary:FlutterBinaryMessenger) { +class SessionModel: BaseModel { + var flutterbinaryMessenger: FlutterBinaryMessenger + lazy var notificationsManager: UserNotificationsManager = { + return UserNotificationsManager() + }() + + init(flutterBinary: FlutterBinaryMessenger) { self.flutterbinaryMessenger=flutterBinary - super.init(type: .sessionModel , flutterBinary: self.flutterbinaryMessenger) + super.init(type: .sessionModel, flutterBinary: self.flutterbinaryMessenger) initializeAppSettings() } - - func initializeAppSettings(){ - setTimeZone() - setReferalCode() - initializeSessionModel() + + func initializeAppSettings() { + initializeSessionModel() + setTimeZone() + setDNS() + getBandwidth() + } + + func startService() { + let configDir = configDirFor( suffix: "service") + (model as InternalsdkSessionModel).startService(configDir, locale: "en", settings: Settings()) + logger.error("Service Started successfully") } - - + override func doOnMethodCall(call: FlutterMethodCall, result: @escaping FlutterResult) { - // Convert the entire arguments to a single MinisqlValue + // Convert the entire arguments to a single MinisqlValue guard let minisqlValue = ValueUtil.convertToMinisqlValue(call.arguments) else { - result(FlutterError(code: "ARGUMENTS_ERROR", message: "Failed to convert arguments to MinisqlValue", details: nil)) - return - } - + result(FlutterError(code: "ARGUMENTS_ERROR", message: "Failed to convert arguments to MinisqlValue", details: nil)) + return + } do { let invocationResult = try invokeMethodOnGo(name: call.method, argument: minisqlValue) - + if let originalValue = ValueUtil.convertFromMinisqlValue(from: invocationResult as! MinisqlValue) { result(originalValue) } else { result(FlutterError(code: "CONVERSION_ERROR", message: "Failed to convert MinisqlValue back to original value", details: nil)) } } catch let error as NSError { - + // Check for the specific "method not implemented" error if error.localizedDescription.contains("method not implemented") { result(FlutterMethodNotImplemented) @@ -55,91 +65,177 @@ class SessionModel:BaseModel { result(FlutterError(code: "UNKNOWN_ERROR", message: error.localizedDescription, details: nil)) } } - } - - - private func initializeSessionModel(){ - let initData: [String: [String: Any]] = [ - "developmentMode": ["type": ValueUtil.TYPE_BOOL, "value": true], - "playVersion": ["type": ValueUtil.TYPE_BOOL, "value": true] - ] + } + + private func initializeSessionModel() { + // Setup the initly data + let initData = createInitData() + guard let jsonString = JsonUtil.convertToJSONString(initData) else { logger.log("Failed to convert initializationData to JSON") return } let miniSqlValue = ValueUtil.convertToMinisqlValue(jsonString) - if(miniSqlValue != nil){ + if miniSqlValue != nil { do { - let result = try invokeMethodOnGo(name: "initSesssionModel", argument: miniSqlValue!) + let result = try invokeMethodOnGo(name: Methods.initModel.methodName, argument: miniSqlValue!) logger.log("Sucessfully set initData \(jsonString) result") - }catch{ + // Start servce once we add all data + startService() + } catch { logger.log("Error while setting initData") } } } - - - private func setTimeZone(){ + + // Set initly data that needed by flashlight + // later on values change be chaneg by mehtod or by flashlight + private func createInitData() -> [String: [String: Any]] { + let deviceId = UIDevice.current.identifierForVendor!.uuidString + let langStr = Locale.current.identifier + return [ + Constants.developmentMode: ["type": ValueUtil.TYPE_BOOL, "value": true], + Constants.prouser: ["type": ValueUtil.TYPE_BOOL, "value": false], + Constants.deviceid: ["type": ValueUtil.TYPE_STRING, "value": deviceId], + Constants.playVersion: ["type": ValueUtil.TYPE_BOOL, "value": isRunningFromAppStore()], + Constants.lang: ["type": ValueUtil.TYPE_STRING, "value": langStr] + ] + } + + private func setTimeZone() { let timeZoneId = TimeZone.current.identifier let miniSqlValue = ValueUtil.convertToMinisqlValue(timeZoneId) - if(miniSqlValue != nil){ + if miniSqlValue != nil { do { - let result = try invokeMethodOnGo(name: "setTimeZone", argument: miniSqlValue!) - logger.log("Sucessfully set timezone with id \(timeZoneId) result \(result)") - }catch{ + let result = try invokeMethodOnGo(name: Methods.setTimezone.methodName, argument: miniSqlValue!) + logger.log("Sucessfully set timezone with id \(timeZoneId) result \(result)") + } catch { logger.log("Error while setting timezone") } } } - - func setReferalCode(){ - let miniSqlValue = ValueUtil.convertToMinisqlValue("Test007") - if(miniSqlValue != nil){ + + func setDeviceId() { + let deviceId = UIDevice.current.identifierForVendor!.uuidString + let miniSqlValue = ValueUtil.convertToMinisqlValue(deviceId) + if miniSqlValue != nil { do { - let result = try invokeMethodOnGo(name: "setReferalCode", argument: miniSqlValue!) - logger.log("Sucessfully set ReferalCode result \(result)") - }catch{ - logger.log("Error while setting ReferalCode") + let result = try invokeMethodOnGo(name: Methods.setDeviceId.methodName, argument: miniSqlValue!) + logger.log("Sucessfully set device ID \(deviceId) ") + } catch { + logger.log("Error while setting deevice ID") } } } - - func setForceCountry(countryCode:String){ + func getBandwidth() { + let miniSqlValue = ValueUtil.convertToMinisqlValue("") + if miniSqlValue != nil { + do { + let result = try invokeMethodOnGo(name: Methods.getBandwidth.methodName, argument: miniSqlValue!) + let newValue = ValueUtil.convertFromMinisqlValue(from: result) + // If value is not null mean user has alerady start using bandwith + // We will get that value from Go + if (newValue as! String) != "" { + let limit = newValue as! Int + if limit==100 { + // if user has reached limit show the notificaiton + notificationsManager.scheduleDataCapLocalNotification(withDataLimit: limit) + } + } + logger.log("Sucessfully getbandwidth \(newValue)") + } catch { + logger.log("Error while getting bandwidth") + } + } + } + + func storeVersion() { + let miniSqlValue = ValueUtil.convertToMinisqlValue(isRunningFromAppStore()) + if miniSqlValue != nil { + do { + let result = try invokeMethodOnGo(name: Methods.setStoreVersion.methodName, argument: miniSqlValue!) + logger.log("This is app store version \(result)") + } catch { + logger.log("Error while setting storeVersion") + } + } + } + + func setForceCountry(countryCode: String) { let countryMiniSql = ValueUtil.convertToMinisqlValue(countryCode) - if(countryMiniSql != nil){ + if countryMiniSql != nil { do { - let result = try invokeMethodOnGo(name: "setForceCountry", argument: countryMiniSql!) - logger.log("Sucessfully set force country") - }catch{ + let result = try invokeMethodOnGo(name: Methods.setForceCountry.methodName, argument: countryMiniSql!) + logger.log("Sucessfully set force country") + } catch { logger.log("Error while setting up forceCountry") } } } - - - func setLocal(lang:String){ + + func setLocal(lang: String) { let langSqlValue = ValueUtil.convertToMinisqlValue(lang) - if(langSqlValue != nil){ + if langSqlValue != nil { do { - let result = try invokeMethodOnGo(name: "setLocal", argument: langSqlValue!) - logger.log("Sucessfully set Local result \(result)") - }catch{ + let result = try invokeMethodOnGo(name: Methods.setLocal.methodName, argument: langSqlValue!) + logger.log("Sucessfully set Local result \(result)") + } catch { logger.log("Error while setting Local") } } } - - - - - - func invokeMethodOnGo(name: String, argument: MinisqlValue) throws -> Any { + + private func setDNS() { + let timeZoneId = TimeZone.current.identifier + let miniSqlValue = ValueUtil.convertToMinisqlValue(DnsDetector.DEFAULT_DNS_SERVER) + if miniSqlValue != nil { + do { + let result = try invokeMethodOnGo(name: Methods.setTimezone.methodName, argument: miniSqlValue!) + logger.log("Sucessfully set timezone with id \(timeZoneId) result \(result)") + } catch { + logger.log("Error while setting timezone") + } + } + } + + // Todo:- Move this method base model + func invokeMethodOnGo(name: String, argument: MinisqlValue) throws -> MinisqlValue { // Convert any argument to Minisql values - let goResult = try model.invokeMethod(name, arguments: ValueArrayHandler(values: [argument])) + let goResult = try model.invokeMethod(name, arguments: ValueArrayFactory.createValueArrayHandler(values: [argument])) return goResult } - + + public func configDirFor(suffix: String) -> String { + let filesDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! + let fileURL = filesDirectory.appendingPathComponent(".lantern" + suffix) + + if !FileManager.default.fileExists(atPath: fileURL.path) { + do { + try FileManager.default.createDirectory(at: fileURL, withIntermediateDirectories: true, attributes: nil) + } catch { + print(error.localizedDescription) + } + } + return fileURL.path + } } +class Settings: NSObject, InternalsdkSettingsProtocol { + func getHttpProxyHost() -> String { + return "127.0.0.1" + } + + func getHttpProxyPort() -> Int { + return 49125 + } + + func stickyConfig() -> Bool { + return false + } + func timeoutMillis() -> Int { + return 60000 + } + +} diff --git a/ios/Runner/Lantern/Models/VpnModel.swift b/ios/Runner/Lantern/Models/VpnModel.swift new file mode 100644 index 000000000..f3fb780e5 --- /dev/null +++ b/ios/Runner/Lantern/Models/VpnModel.swift @@ -0,0 +1,110 @@ +// VpnModel.swift +// Runner +// Created by jigar fumakiya on 01/09/23. + +import Foundation +import Internalsdk +import Flutter +import DBModule + +class VpnModel: BaseModel { + var flutterbinaryMessenger: FlutterBinaryMessenger + let vpnManager: VPNBase + let vpnHelper = VpnHelper.shared + + init(flutterBinary: FlutterBinaryMessenger, vpnBase: VPNBase) { + self.flutterbinaryMessenger=flutterBinary + self.vpnManager = vpnBase + super.init(type: .vpnModel, flutterBinary: self.flutterbinaryMessenger) + initializeVpnModel() + } + + func initializeVpnModel() { + + } + + override func doOnMethodCall(call: FlutterMethodCall, result: @escaping FlutterResult) { + if call.method=="switchVPN"{ + // Here for we need to opetaion on swif and go both sides + if let args = call.arguments as? [String: Any] { + let status = args["on"] as! Bool + switchVPN(status: status) + } else { + result(FlutterError(code: "BAD_ARGS", + message: "Expected arguments to be of type Dictionary.", + details: nil)) + } + } else { + guard let minisqlValue = ValueUtil.convertToMinisqlValue(call.arguments) else { + result(FlutterError(code: "ARGUMENTS_ERROR", message: "Failed to convert arguments to MinisqlValue", details: nil)) + return + } + do { + let invocationResult = try invokeMethodOnGo(name: call.method, argument: minisqlValue) + if let originalValue = ValueUtil.convertFromMinisqlValue(from: invocationResult as! MinisqlValue) { + result(originalValue) + } else { + result(FlutterError(code: "CONVERSION_ERROR", message: "Failed to convert MinisqlValue back to original value", details: nil)) + } + } catch let error as NSError { + // Check for the specific "method not implemented" error + if error.localizedDescription.contains("method not implemented") { + result(FlutterMethodNotImplemented) + } + // Check for another specific error (e.g., "database error") + else if error.localizedDescription.contains("database error") { + result(FlutterError(code: "DATABASE_ERROR", message: "A database error occurred.", details: nil)) + } + // Handle all other errors + else { + result(FlutterError(code: "UNKNOWN_ERROR", message: error.localizedDescription, details: nil)) + } + } + } + } + + private func saveVPNStatus(status: String) { + let miniSqlValue = ValueUtil.convertToMinisqlValue(status) + if miniSqlValue != nil { + do { + let result = try invokeMethodOnGo(name: "saveVpnStatus", argument: miniSqlValue!) + logger.log("Sucessfully set VPN status with \(status)") + } catch { + logger.log("Error while setting VPN status") + } + } + } + + func switchVPN(status: Bool) { + if status { + startVPN() + } else { + stopVPN() + } + } + + func startVPN() { + self.saveVPNStatus(status: "connected") + vpnHelper.startVPN( onError: { error in + // in case of error, reset switch position + self.saveVPNStatus(status: "disconnected") + logger.debug("VPN not started \(error)") + }, onSuccess: { + logger.debug("VPN started") + self.saveVPNStatus(status: "connected") + }) + } + + func stopVPN() { + vpnHelper.stopVPN() + logger.debug("VPN Successfully stoped") + self.saveVPNStatus(status: "disconnected") + } + + func invokeMethodOnGo(name: String, argument: MinisqlValue) throws -> MinisqlValue { + // Convert any argument to Minisql values + let goResult = try model.invokeMethod(name, arguments: ValueArrayFactory.createValueArrayHandler(values: [argument])) + return goResult + } + +} diff --git a/ios/Shared/Constants.swift b/ios/Runner/Lantern/Utils/Constants.swift similarity index 92% rename from ios/Shared/Constants.swift rename to ios/Runner/Lantern/Utils/Constants.swift index 7f0dbc626..e1dd0464f 100644 --- a/ios/Shared/Constants.swift +++ b/ios/Runner/Lantern/Utils/Constants.swift @@ -1,20 +1,18 @@ // // Constants.swift -// Lantern +// Runner // import Foundation - +import Internalsdk struct Constants { // MARK: Convenience Inits static let appDefault = Constants(process: .app) static let netExDefault = Constants(process: .netEx) - // MARK: Project Constants static let appBundleId = "org.getlantern.lantern" static let netExBundleId = "org.getlantern.lantern.Tunnel" static let appGroupName = "group.getlantern.lantern" - // MARK: App Group static let appGroupDefaults = UserDefaults(suiteName: appGroupName)! static var appGroupContainerURL: URL { @@ -108,4 +106,13 @@ struct Constants { // URL where app stores "excluded IPs" string from `IosConfigure`. // NetEx loads them to generate excluded routes on the TUN device. var excludedIPsURL: URL { return configDirectoryURL.appendingPathComponent("ips") } + + // MARK: Const for database keys + // All values comes from go even if values chaegs it will reflact here + static var developmentMode = InternalsdkDEVELOPMNET_MODE + static var prouser = InternalsdkPRO_USER + static var deviceid = InternalsdkDEVICE_ID + static var playVersion = InternalsdkIS_PLAY_VERSION + static var lang = InternalsdkLANG + } diff --git a/ios/Runner/Lantern/Utils/Event.swift b/ios/Runner/Lantern/Utils/Event.swift new file mode 100644 index 000000000..b8d4c8e42 --- /dev/null +++ b/ios/Runner/Lantern/Utils/Event.swift @@ -0,0 +1,64 @@ +// +// Event.swift +// Lantern +// +// Based on https://blog.scottlogic.com/2015/02/05/swift-events.html +// +// Created by Ox Cart on 9/28/20. +// Copyright © 2020 Innovate Labs. All rights reserved. +// + +import Foundation + +public class Event { + + public typealias EventHandler = (T) -> Void + + fileprivate var eventHandlers = [Invocable]() + + public func raise(_ data: T) { + for handler in self.eventHandlers { + handler.invoke(data) + } + } + + public func addHandler(target: U, + handler: @escaping (U) -> EventHandler) -> Disposable { + let wrapper = EventHandlerWrapper(target: target, + handler: handler, event: self) + eventHandlers.append(wrapper) + return wrapper + } +} + +private protocol Invocable: class { + func invoke(_ data: Any) +} + +private class EventHandlerWrapper +: Invocable, Disposable { + weak var target: T? + let handler: (T) -> (U) -> Void + let event: Event + + init(target: T?, handler: @escaping (T) -> (U) -> Void, event: Event) { + self.target = target + self.handler = handler + self.event = event + } + + func invoke(_ data: Any) { + if let t = target { + handler(t)(data as! U) + } + } + + func dispose() { + event.eventHandlers = + event.eventHandlers.filter { $0 !== self } + } +} + +public protocol Disposable { + func dispose() +} diff --git a/ios/Runner/Lantern/Utils/Events.swift b/ios/Runner/Lantern/Utils/Events.swift new file mode 100644 index 000000000..26cc563c2 --- /dev/null +++ b/ios/Runner/Lantern/Utils/Events.swift @@ -0,0 +1,15 @@ +// +// Events.swift +// Lantern +// +// Created by Ox Cart on 9/28/20. +// Copyright © 2020 Innovate Labs. All rights reserved. +// + +import Foundation + +struct Events { + static let configFetchTakingLongTime = Event() + + static let updateAvailable = Event() +} diff --git a/ios/Shared/FileManager+FileCreation.swift b/ios/Runner/Lantern/Utils/FileUtils.swift similarity index 88% rename from ios/Shared/FileManager+FileCreation.swift rename to ios/Runner/Lantern/Utils/FileUtils.swift index af0023c61..39786139f 100644 --- a/ios/Shared/FileManager+FileCreation.swift +++ b/ios/Runner/Lantern/Utils/FileUtils.swift @@ -15,8 +15,8 @@ extension FileManager { func ensureDirectoryExists(at url: URL) throws { if !fileExists(atPath: url.path, isDirectory: nil) { try createDirectory(at: url, - withIntermediateDirectories: false, - attributes: nil) + withIntermediateDirectories: false, + attributes: nil) } } @@ -27,7 +27,7 @@ extension FileManager { if !fileExists(atPath: path) { // posix permission 666 is `rw-rw-rw` aka read/write for all let rwAllPermission = 0o666 as Int16 - let success = createFile(atPath: path, contents: nil, attributes: [.posixPermissions : rwAllPermission]) + let success = createFile(atPath: path, contents: nil, attributes: [.posixPermissions: rwAllPermission]) if !success { overallSuccess = false } } } diff --git a/ios/Runner/Lantern/Utils/LoadingIndicatorManager.swift b/ios/Runner/Lantern/Utils/LoadingIndicatorManager.swift new file mode 100644 index 000000000..3f8594681 --- /dev/null +++ b/ios/Runner/Lantern/Utils/LoadingIndicatorManager.swift @@ -0,0 +1,39 @@ +// +// LoadingIndicatorManager.swift +// Runner +// +// Created by jigar fumakiya on 12/09/23. +// + +import Foundation +import UIKit + +class LoadingIndicatorManager { + private var loadingIndicator: UIActivityIndicatorView? + private weak var parentView: UIView? + + init(parentView: UIView) { + self.parentView = parentView + setupLoadingIndicator() + } + + private func setupLoadingIndicator() { + loadingIndicator = UIActivityIndicatorView(style: .gray) + guard let loadingIndicator = loadingIndicator, let parentView = parentView else { + return + } + loadingIndicator.center = parentView.center + loadingIndicator.hidesWhenStopped = true + parentView.addSubview(loadingIndicator) + } + + func show() { + loadingIndicator?.startAnimating() + } + + func hide() { + loadingIndicator?.stopAnimating() + loadingIndicator?.removeFromSuperview() + loadingIndicator = nil + } +} diff --git a/ios/Runner/Lantern/Utils/Logger.swift b/ios/Runner/Lantern/Utils/Logger.swift index 2cb39b265..0d6a7eb19 100644 --- a/ios/Runner/Lantern/Utils/Logger.swift +++ b/ios/Runner/Lantern/Utils/Logger.swift @@ -6,19 +6,26 @@ // import os +import Internalsdk -//https://medium.com/@sauvik_dolui/developing-a-tiny-logger-in-swift-7221751628e6 let logger = LanternLogger() class LanternLogger { private let logger = OSLog(subsystem: "", category: "") private let prefix: String = "[LANTERN-IOS]" - + func log(_ message: String) { let dateFormatter = DateFormatter() dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss.SSS" let date = dateFormatter.string(from: Date()) os_log("%{public}@", log: logger, type: .default, "\(prefix) - \(date): \(message)") + } + + func error(_ msg: String) { + IosLogError(msg) + } + func debug(_ msg: String) { + IosLogDebug(msg) } } diff --git a/ios/Shared/Process.swift b/ios/Runner/Lantern/Utils/Process.swift similarity index 100% rename from ios/Shared/Process.swift rename to ios/Runner/Lantern/Utils/Process.swift diff --git a/ios/Runner/Lantern/Utils/RunningEnv.swift b/ios/Runner/Lantern/Utils/RunningEnv.swift new file mode 100644 index 000000000..e3f76456e --- /dev/null +++ b/ios/Runner/Lantern/Utils/RunningEnv.swift @@ -0,0 +1,58 @@ +// +// RunningEnv.swift +// Runner +// +// Created by jigar fumakiya on 01/09/23. + +import Foundation + +func isRunningFromAppStore() -> Bool { + let file = "\(NSHomeDirectory())/iTunesMetadata.plist" + if FileManager.default.fileExists(atPath: file) { + // The app is running from the App Store + return true + } else { + // The app is not running from the App Store + return false + } +} + +func isRunningInTestFlightEnvironment() -> Bool { + if isSimulator() { + return false + } else { + if isAppStoreReceiptSandbox() && !hasEmbeddedMobileProvision() { + return true + } else { + return false + } + } +} + +private func hasEmbeddedMobileProvision() -> Bool { + if let _ = Bundle.main.path(forResource: "embedded", ofType: "mobileprovision") { + return true + } + return false +} + +func isAppStoreReceiptSandbox() -> Bool { + if isSimulator() { + return false + } else { + if let appStoreReceiptURL = Bundle.main.appStoreReceiptURL { + if appStoreReceiptURL.lastPathComponent == "sandboxReceipt" { + return true + } + } + return false + } +} + +func isSimulator() -> Bool { + #if arch(i386) || arch(x86_64) + return true + #else + return false + #endif +} diff --git a/ios/Shared/UserNotificationsManager.swift b/ios/Runner/Lantern/Utils/UserNotificationsManager.swift similarity index 54% rename from ios/Shared/UserNotificationsManager.swift rename to ios/Runner/Lantern/Utils/UserNotificationsManager.swift index 2807edb98..a62645be2 100644 --- a/ios/Shared/UserNotificationsManager.swift +++ b/ios/Runner/Lantern/Utils/UserNotificationsManager.swift @@ -6,23 +6,34 @@ import Foundation import UserNotifications -// TODO: add interface for mockability -class UserNotificationsManager { - static let permissionDefaultsKey = "Lantern.NotificationPermission" - static let lastDataCapNotificationDefaultsKey = "Lantern.NotifyDataCapDate" +class UserNotificationsManager: NSObject, UNUserNotificationCenterDelegate { + static let shared = UserNotificationsManager() + private static let lastDataCapNotificationDefaultsKey = "Lantern.NotifyDataCapDate" + let dataUsageUpdatedNotification = Notification.Name("Lantern.dataUsageUpdated") let center: UNUserNotificationCenter let userDefaults: UserDefaults - + // Check if user has alerady notification permssion or not var notificationsEnabled: Bool { - // this is an internal-only flag; may not accurately reflect system Settings - get { return userDefaults.bool(forKey: UserNotificationsManager.permissionDefaultsKey) } - set { userDefaults.set(newValue, forKey: UserNotificationsManager.permissionDefaultsKey) } + get { + var isAuthorized = false + // This will ensure synchronous access to notification permission + let semaphore = DispatchSemaphore(value: 0) + UNUserNotificationCenter.current().getNotificationSettings { settings in + if settings.authorizationStatus == .authorized || settings.authorizationStatus == .provisional { + isAuthorized = true + } + semaphore.signal() + } + // Waiting for the completion handler of getNotificationSettings to complete + _ = semaphore.wait(timeout: .distantFuture) + return isAuthorized + } } private var notifiedDataCapThisMonth: Bool { guard let value = userDefaults.value(forKey: UserNotificationsManager.lastDataCapNotificationDefaultsKey), - let lastDate = (value as? Date) else { return false } + let lastDate = (value as? Date) else { return false } return Calendar.current.isDate(lastDate, equalTo: Date(), toGranularity: .month) } @@ -34,35 +45,39 @@ class UserNotificationsManager { self.userDefaults = userDefaults } - func setNotificationCenterDelegate(_ delegate: UNUserNotificationCenterDelegate) { - // used by app-side to allow notifications while app is active (see AppDelegate.swift) - center.delegate = delegate - } - - // MARK: Permissions - - func getSystemNotificationAuthorization(completion: @escaping (UNAuthorizationStatus) -> Void) { - center.getNotificationSettings { completion($0.authorizationStatus) } - } - - func requestNotificationsPermission(completion: @escaping (Bool) -> Void) { - center.requestAuthorization(options: [.provisional, .alert]) { [weak self] allowed, error in - self?.notificationsEnabled = allowed // + attempt to reflect system settings - completion(allowed) + func requestNotificationPermission(completion: @escaping (Bool) -> Void) { + center.getNotificationSettings { settings in + switch settings.authorizationStatus { + case .notDetermined: + self.center.requestAuthorization(options: [.alert, .sound, .badge]) { granted, _ in + DispatchQueue.main.async { + completion(granted) + } + } + case .denied: + DispatchQueue.main.async { + completion(false) + } + case .authorized, .provisional: + DispatchQueue.main.async { + completion(true) + } + default: + DispatchQueue.main.async { + completion(false) + } + } } } // MARK: Posting - func scheduleDataCapLocalNotification(withDataLimit limit: Int) { guard notificationsEnabled, !notifiedDataCapThisMonth else { return } - let content = localizedNotificationContent(withDataLimit: limit) let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 1, repeats: false) let request = UNNotificationRequest(identifier: "Lantern.DataCap", content: content, trigger: trigger) - center.add(request) { [weak self] error in if error == nil { let key = UserNotificationsManager.lastDataCapNotificationDefaultsKey @@ -71,6 +86,8 @@ class UserNotificationsManager { } } + // Todo:- Implement generic way to support multiple lang + // Same as Android func localizedNotificationContent(withDataLimit limit: Int) -> UNMutableNotificationContent { let content = UNMutableNotificationContent() diff --git a/ios/Runner/Lantern/Utils/ValueUtil.swift b/ios/Runner/Lantern/Utils/ValueUtil.swift deleted file mode 100644 index 65011ed83..000000000 --- a/ios/Runner/Lantern/Utils/ValueUtil.swift +++ /dev/null @@ -1,147 +0,0 @@ -// -// Value.swift -// Runner -// -// Created by jigar fumakiya on 28/07/23. -// - -import Foundation -import Internalsdk -import SQLite - - -class ValueUtil { - // Define the types constants - static let TYPE_BYTES = Int(MinisqlValueTypeBytes) - static let TYPE_STRING = Int(MinisqlValueTypeString) - static let TYPE_INT = Int(MinisqlValueTypeInt) - static let TYPE_BOOL = Int(MinisqlValueTypeBool) - - static func makeValue(from anyValue: Any) -> MinisqlValue { - let value: MinisqlValue! - - switch anyValue { - case is String: - value = MinisqlNewValueString(anyValue as! String) - case is Int: - value = MinisqlNewValueInt(anyValue as! Int) - case is Bool: - value = MinisqlNewValueBool(anyValue as! Bool) - case is UInt8: - logger.log("Make value UInt8 with value \(anyValue)") - let blob = anyValue as! SQLite.Blob - let data = Data(blob.bytes) - value = MinisqlNewValueBytes(data) - logger.log("Make value UInt8 Completed with \(data.count) bytes \(blob.bytes)") - default: - fatalError("Unsupported type \(type(of: anyValue)) with value: \(anyValue)") - } - return value - } - - static func convertFromMinisqlValue(from internalsdkValue: MinisqlValue) -> Any? { - switch internalsdkValue.type { - case TYPE_STRING: - return internalsdkValue.string() - case TYPE_INT: - return Int(internalsdkValue.int_() as Int) - case TYPE_BYTES: - return internalsdkValue.bytes()! - case TYPE_BOOL: - return internalsdkValue.bool_() - default: - fatalError("Unsupported type") - } - } - - static func toBindingsArray(_ args: MinisqlValuesProtocol) -> [Binding?] { - var bindings = [Binding?]() - for i in 0.. MinisqlValue? { - switch anyValue { - case is String: - return MinisqlNewValueString(anyValue as! String) - case is Int: - return MinisqlNewValueInt(anyValue as! Int) - case is Bool: - return MinisqlNewValueBool(anyValue as! Bool) - case is [Any]: // For arrays - if let jsonData = try? JSONSerialization.data(withJSONObject: anyValue, options: []), - let jsonString = String(data: jsonData, encoding: .utf8) { - return MinisqlNewValueString(jsonString) - } - case is [String: Any]: // For dictionaries - if let jsonData = try? JSONSerialization.data(withJSONObject: anyValue, options: []), - let jsonString = String(data: jsonData, encoding: .utf8) { - return MinisqlNewValueString(jsonString) - } - default: - return MinisqlNewValueString("\(anyValue)") - } - return nil - } - -} -extension Binding { - var bindingType: String { - switch self { - case is Int64: - return "Int64" - case is Double: - return "Double" - case is String: - return "String" - case is Bool: - return "Bool" - case is Blob: - return "Blob" - case is NSNumber: - return "NSNumber" - default: - return "Unknown" - } - } -} diff --git a/ios/Runner/Runner.entitlements b/ios/Runner/Runner.entitlements new file mode 100644 index 000000000..2bb722b8e --- /dev/null +++ b/ios/Runner/Runner.entitlements @@ -0,0 +1,14 @@ + + + + + com.apple.developer.networking.networkextension + + packet-tunnel-provider + + com.apple.security.application-groups + + group.getlantern.lantern + + + diff --git a/ios/Shared/Bundle+AppVersion.swift b/ios/Shared/Bundle+AppVersion.swift deleted file mode 100644 index cb051d7d7..000000000 --- a/ios/Shared/Bundle+AppVersion.swift +++ /dev/null @@ -1,15 +0,0 @@ -// -// Bundle+AppVersion.swift -// Lantern -// - -import Foundation - -extension Bundle { - var appVersion: String { - let dictionary = infoDictionary! - let version = dictionary["CFBundleShortVersionString"] as! String - let build = dictionary["CFBundleVersion"] as! String - return "\(version) (\(build))" - } -} diff --git a/ios/Shared/Event.swift b/ios/Shared/Event.swift deleted file mode 100644 index a15ed4f1b..000000000 --- a/ios/Shared/Event.swift +++ /dev/null @@ -1,64 +0,0 @@ -// -// Event.swift -// Lantern -// -// Based on https://blog.scottlogic.com/2015/02/05/swift-events.html -// -// Created by Ox Cart on 9/28/20. -// Copyright © 2020 Innovate Labs. All rights reserved. -// - -import Foundation - -public class Event { - - public typealias EventHandler = (T) -> () - - fileprivate var eventHandlers = [Invocable]() - - public func raise(_ data: T) { - for handler in self.eventHandlers { - handler.invoke(data) - } - } - - public func addHandler(target: U, - handler: @escaping (U) -> EventHandler) -> Disposable { - let wrapper = EventHandlerWrapper(target: target, - handler: handler, event: self) - eventHandlers.append(wrapper) - return wrapper - } -} - -private protocol Invocable: class { - func invoke(_ data: Any) -} - -private class EventHandlerWrapper - : Invocable, Disposable { - weak var target: T? - let handler: (T) -> (U) -> () - let event: Event - - init(target: T?, handler: @escaping (T) -> (U) -> (), event: Event) { - self.target = target - self.handler = handler - self.event = event; - } - - func invoke(_ data: Any) -> () { - if let t = target { - handler(t)(data as! U) - } - } - - func dispose() { - event.eventHandlers = - event.eventHandlers.filter { $0 !== self } - } -} - -public protocol Disposable { - func dispose() -} diff --git a/ios/Shared/Logger.swift b/ios/Shared/Logger.swift deleted file mode 100644 index 2b2a8829b..000000000 --- a/ios/Shared/Logger.swift +++ /dev/null @@ -1,20 +0,0 @@ -// -// Logger.swift -// Lantern -// - -import Ios - -let logger = Logger() - -class Logger { - // MARK: Logging via Go code (unifies log messages in single file) - - func error(_ msg: String) { - IosLogError(msg) - } - - func debug(_ msg: String) { - IosLogDebug(msg) - } -} diff --git a/ios/Tunnel/Info.plist b/ios/Tunnel/Info.plist index 2c855fc44..3059459e1 100644 --- a/ios/Tunnel/Info.plist +++ b/ios/Tunnel/Info.plist @@ -2,24 +2,6 @@ - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleDisplayName - Tunnel - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - XPC! - CFBundleShortVersionString - 7.3.3 - CFBundleVersion - 3 NSExtension NSExtensionPointIdentifier diff --git a/ios/Tunnel/NEProviderStopReason+Debug.swift b/ios/Tunnel/NEProviderStopReason.swift similarity index 95% rename from ios/Tunnel/NEProviderStopReason+Debug.swift rename to ios/Tunnel/NEProviderStopReason.swift index 231fbfa35..bc843aeb9 100644 --- a/ios/Tunnel/NEProviderStopReason+Debug.swift +++ b/ios/Tunnel/NEProviderStopReason.swift @@ -5,7 +5,7 @@ import NetworkExtension -// Just for logging purposes, logging self or self.rawValue is un-readable. +// Just for logging purposes, Status taken from NEProviderStopReason extension NEProviderStopReason { var debugString: String { switch self { diff --git a/ios/Tunnel/PacketTunnelProvider.swift b/ios/Tunnel/PacketTunnelProvider.swift index fadc497c3..2286adf8e 100644 --- a/ios/Tunnel/PacketTunnelProvider.swift +++ b/ios/Tunnel/PacketTunnelProvider.swift @@ -4,7 +4,7 @@ // import NetworkExtension -import Ios +import Internalsdk import os.log class PacketTunnelProvider: NEPacketTunnelProvider { @@ -20,7 +20,6 @@ class PacketTunnelProvider: NEPacketTunnelProvider { // MARK: PacketFlow let tunIP = "10.66.66.1" let mtu = 1500 - var client: IosClientWriterProtocol? var bytesRead: Int = 0 var bytesWritten: Int = 0 @@ -30,17 +29,14 @@ class PacketTunnelProvider: NEPacketTunnelProvider { override func startTunnel(options: [String : NSObject]?, completionHandler: @escaping (Error?) -> Void) { // this is our first life-cycle event; perform set up logMemoryUsage(tag: "Before starting flashlight") - increaseFileLimit() - createFilesForNetExGoPackage() let logError = flashlightManager.configureGoLoggerReturningError() if let error = logError { logger.error(error.localizedDescription) // crash the tunnel? } - - // we have no way to discern if the User toggled VPN on in Settings :( + // we have no way to discern if the User toggled VPN on in Settings :( let reason = (options?[Constants.netExStartReasonKey] as? NSString) ?? "'On Demand'/Settings" logger.debug("startTunnel with reason: \(reason)") @@ -51,7 +47,6 @@ class PacketTunnelProvider: NEPacketTunnelProvider { completionHandler(error) return } - self.startFlashlight(completionHandler) } } @@ -190,7 +185,6 @@ extension PacketTunnelProvider: IosWriterProtocol { extension PacketTunnelProvider { // MARK: Tunnel Settings - func generateTunnelNetworkSettings() -> NETunnelNetworkSettings { let remoteAddress = "0.0.0.0" // display use only let networkSettings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: remoteAddress) @@ -247,10 +241,8 @@ extension PacketTunnelProvider { } // create process-specific log files @ ~/netEx/lantern.log.# - let logURLs = fileManager.generateLogRotationURLs(count: Constants.defaultLogRotationFileCount, - from: constants.goLogBaseURL) + let logURLs = fileManager.generateLogRotationURLs(count: Constants.defaultLogRotationFileCount, from: constants.goLogBaseURL) let success = fileManager.ensureFilesExist(at: logURLs) - if !success { logger.error("Failed to create log URLs") } diff --git a/ios/Tunnel/UDPConn.swift b/ios/Tunnel/UDPConn.swift index 9b41bc675..5670215f7 100644 --- a/ios/Tunnel/UDPConn.swift +++ b/ios/Tunnel/UDPConn.swift @@ -7,7 +7,7 @@ // import Foundation -import Ios +import Internalsdk import Network /// UDPDialer provides a mechanism for dialing outbound UDP connections that bypass the VPN. diff --git a/lib/account/account.dart b/lib/account/account.dart index 9a1a69450..519e614df 100644 --- a/lib/account/account.dart +++ b/lib/account/account.dart @@ -1,5 +1,6 @@ import 'package:lantern/common/common.dart'; import 'package:lantern/messaging/messaging_model.dart'; + @RoutePage(name: 'Account') class AccountMenu extends StatelessWidget { AccountMenu({Key? key}) : super(key: key); @@ -47,14 +48,15 @@ class AccountMenu extends StatelessWidget { ) : const SizedBox(), ), - ListItemFactory.settingsItem( - key: AppKeys.upgrade_lantern_pro, - icon: ImagePaths.pro_icon_black, - content: 'Upgrade to Lantern Pro'.i18n, - onTap: () { - upgradeToLanternPro(context); - }, - ), + if (Platform.isAndroid) + ListItemFactory.settingsItem( + key: AppKeys.upgrade_lantern_pro, + icon: ImagePaths.pro_icon_black, + content: 'Upgrade to Lantern Pro'.i18n, + onTap: () { + upgradeToLanternPro(context); + }, + ), ListItemFactory.settingsItem( icon: ImagePaths.star, content: 'Invite Friends'.i18n, @@ -62,13 +64,14 @@ class AccountMenu extends StatelessWidget { inviteFriends(context); }, ), - ListItemFactory.settingsItem( - icon: ImagePaths.devices, - content: 'Authorize Device for Pro'.i18n, - onTap: () { - authorizeDeviceForPro(context); - }, - ), + if (Platform.isAndroid) + ListItemFactory.settingsItem( + icon: ImagePaths.devices, + content: 'Authorize Device for Pro'.i18n, + onTap: () { + authorizeDeviceForPro(context); + }, + ), ...commonItems(context) ]; } @@ -79,19 +82,21 @@ class AccountMenu extends StatelessWidget { (context, hasBeenOnboarded, child) => messagingModel.getCopiedRecoveryStatus( (BuildContext context, bool hasCopiedRecoveryKey, Widget? child) => - ListItemFactory.settingsItem( - key: AppKeys.account_management, - icon: ImagePaths.account, - content: 'account_management'.i18n, - onTap: () async => - await context.pushRoute(AccountManagement(isPro: true)), - trailingArray: [ - if (!hasCopiedRecoveryKey && hasBeenOnboarded == true) - const CAssetImage( - path: ImagePaths.badge, - ), - ], - ), + Platform.isAndroid + ? ListItemFactory.settingsItem( + key: AppKeys.account_management, + icon: ImagePaths.account, + content: 'account_management'.i18n, + onTap: () async => await context + .pushRoute(AccountManagement(isPro: true)), + trailingArray: [ + if (!hasCopiedRecoveryKey && hasBeenOnboarded == true) + const CAssetImage( + path: ImagePaths.badge, + ), + ], + ) + : const SizedBox(), ), ), ListItemFactory.settingsItem( @@ -101,11 +106,12 @@ class AccountMenu extends StatelessWidget { inviteFriends(context); }, ), - ListItemFactory.settingsItem( - icon: ImagePaths.devices, - content: 'add_device'.i18n, - onTap: () async => await context.pushRoute(ApproveDevice()), - ), + if (Platform.isAndroid) + ListItemFactory.settingsItem( + icon: ImagePaths.devices, + content: 'add_device'.i18n, + onTap: () async => await context.pushRoute(ApproveDevice()), + ), ...commonItems(context) ]; } diff --git a/lib/account/language.dart b/lib/account/language.dart index 9fe4c194d..5dc1a97bd 100644 --- a/lib/account/language.dart +++ b/lib/account/language.dart @@ -13,21 +13,23 @@ class Language extends StatelessWidget { padVertical: true, body: sessionModel .language((BuildContext context, String currentLang, Widget? child) { - return ListView.builder( + // Splint language by just code + final countryCode= currentLang; + return ListView.builder( itemCount: languages.length, itemBuilder: (BuildContext context, int index) { var lang = languages[index]; return RadioListTile( activeColor: pink4, contentPadding: const EdgeInsetsDirectional.all(0), - tileColor: lang == currentLang ? grey2 : transparent, + tileColor: lang == countryCode ? grey2 : transparent, dense: true, title: CText( toBeginningOfSentenceCase(displayLanguage(lang))!, style: tsBody1, ), value: lang, - groupValue: currentLang, + groupValue: countryCode, onChanged: (String? value) async { await sessionModel.setLanguage(lang); Navigator.pop(context); diff --git a/lib/account/privacy_disclosure.dart b/lib/account/privacy_disclosure.dart index c7fac560c..979ba5ca1 100644 --- a/lib/account/privacy_disclosure.dart +++ b/lib/account/privacy_disclosure.dart @@ -1,11 +1,13 @@ import 'package:lantern/common/common.dart'; class PrivacyDisclosure extends StatelessWidget { + const PrivacyDisclosure({super.key}); + @override Widget build(BuildContext context) { return FullScreenDialog( widget: Padding( - padding: EdgeInsetsDirectional.only( + padding: const EdgeInsetsDirectional.only( start: 33, end: 33), child: Column( mainAxisAlignment: MainAxisAlignment.spaceAround, diff --git a/lib/account/settings.dart b/lib/account/settings.dart index c5c38226e..e8e7a1e08 100644 --- a/lib/account/settings.dart +++ b/lib/account/settings.dart @@ -69,17 +69,6 @@ class Settings extends StatelessWidget { mirrorLTR(context: context, child: const ContinueArrow()) ], ), - //* Report - ListItemFactory.settingsItem( - icon: ImagePaths.alert, - content: 'report_issue'.i18n, - trailingArray: [ - mirrorLTR(context: context, child: const ContinueArrow()) - ], - onTap: () { - reportIssue(context); - }, - ), ListItemFactory.settingsItem( icon: ImagePaths.update, content: 'check_for_updates'.i18n, diff --git a/lib/ad_helper.dart b/lib/ad_helper.dart index 66079b996..73c989c24 100644 --- a/lib/ad_helper.dart +++ b/lib/ad_helper.dart @@ -1,270 +1,270 @@ -import 'dart:io'; - -import 'package:clever_ads_solutions/CAS.dart'; -import 'package:clever_ads_solutions/public/AdCallback.dart'; -import 'package:clever_ads_solutions/public/AdImpression.dart'; -import 'package:clever_ads_solutions/public/AdTypes.dart'; -import 'package:clever_ads_solutions/public/Audience.dart'; -import 'package:clever_ads_solutions/public/ConsentFlow.dart'; -import 'package:clever_ads_solutions/public/InitConfig.dart'; -import 'package:clever_ads_solutions/public/InitializationListener.dart'; -import 'package:clever_ads_solutions/public/MediationManager.dart'; -import 'package:clever_ads_solutions/public/OnDismissListener.dart'; -import 'package:flutter/foundation.dart'; -import 'package:google_mobile_ads/google_mobile_ads.dart'; -import 'package:lantern/replica/common.dart'; - -import 'common/session_model.dart'; - -enum AdType { Google, CAS } - -const privacyPolicy = 'https://lantern.io/privacy'; - -class AdHelper { - static final AdHelper _instance = AdHelper._internal(); - - AdHelper._internal(); - - factory AdHelper() { - return _instance; - } - - AdType? _currentAdType; - MediationManager? casMediationManager; - InterstitialAd? _interstitialAd; - int _failedLoadAttempts = 0; - int _failedCASLoadAttempts = 0; - - //If ads are getting failed to load we want to make lot of calls - // Just try 5 times - final int _maxFailAttempts = 5; - - //Google Test ID if needed to test - // return 'ca-app-pub-3940256099942544/1033173712'; - String get interstitialAdUnitId { - if (Platform.isAndroid) { - return const String.fromEnvironment('INTERSTITIAL_AD_UNIT_ID'); - } else { - throw UnsupportedError('Unsupported platform'); - } - } - - // Private methods to decide whether to load or show Google Ads or CAS ads based on conditions - Future _decideAndLoadAds() async { - final shouldShowGoogleAds = await sessionModel.shouldShowAds(); - final shouldShowCASAds = await sessionModel.shouldCASShowAds(); - - logger.d( - '[Ads Manager] Google Ads enable $shouldShowGoogleAds: CAS Ads $shouldShowCASAds'); - if (shouldShowGoogleAds) { - _currentAdType = AdType.Google; - logger.i('[Ads Manager] Decision: Loading Google Ads.'); - await _loadInterstitialAd(); - } else if (shouldShowCASAds) { - _currentAdType = AdType.CAS; - logger.i('[Ads Manager] Decision: Loading CAS Ads.'); - if (casMediationManager == null) { - await _initializeCAS(); - } - await _loadCASInterstitial(); - } - } - - Future _decideAndShowAds() async { - if (_currentAdType == AdType.Google && _interstitialAd != null) { - await _showInterstitialAd(); - } else if (_currentAdType == AdType.CAS) { - final isCASReady = (await casMediationManager!.isInterstitialReady()); - if (isCASReady) { - await _showCASInterstitial(); - logger.i('[Ads Manager] Request: Showing CAS Ad .'); - } else { - logger.i('[Ads Manager] CAS: Ad is not yet ready to show.'); - } - } - } - - Future _loadInterstitialAd() async { - //To avoid calling multiple ads request repeatedly - if (_interstitialAd == null && _failedLoadAttempts < _maxFailAttempts) { - logger.i('[Ads Manager] Request: Making Google Ad request.'); - await InterstitialAd.load( - adUnitId: interstitialAdUnitId, - request: const AdRequest(), - adLoadCallback: InterstitialAdLoadCallback( - onAdLoaded: (ad) { - ad.fullScreenContentCallback = FullScreenContentCallback( - onAdClicked: (ad) { - logger.i('[Ads Manager] onAdClicked callback'); - }, - onAdShowedFullScreenContent: (ad) { - logger.i('[Ads Manager] Showing Ads'); - }, - onAdFailedToShowFullScreenContent: (ad, error) { - logger.i( - '[Ads Manager] onAdFailedToShowFullScreenContent callback'); - //if ads fail to load let user turn on VPN - _postShowingAds(); - }, - onAdDismissedFullScreenContent: (ad) { - logger.i('[Ads Manager] fullScreenContentCallback callback'); - _postShowingAds(); - }, - ); - _interstitialAd = ad; - logger.i('[Ads Manager] to loaded $ad'); - }, - onAdFailedToLoad: (err) { - _failedLoadAttempts++; // increment the count on failure - logger.i('[Ads Manager] failed to load $err'); - _postShowingAds(); - }, - ), - ); - } - } - - void _postShowingAds() { - if (_currentAdType == AdType.Google) { - _interstitialAd?.dispose(); - _interstitialAd = null; - _failedLoadAttempts = 0; // Reset counter for Google Ads - logger.i( - '[Ads Manager] Post-show: Google Ad displayed. Resetting failed load attempts and requesting a new ad.'); - _loadInterstitialAd(); - } else if (_currentAdType == AdType.CAS) { - _failedCASLoadAttempts = 0; // Reset counter for CAS Ads - logger.i( - '[Ads Manager] Post-show: CAS Ad displayed. Resetting failed load attempts and requesting a new ad.'); - _loadCASInterstitial(); - } - } - - Future _showInterstitialAd() async { - if (_interstitialAd != null) { - await _interstitialAd?.show(); - } - } - - // Public methods - Future loadAds() async { - await _decideAndLoadAds(); - } - - Future showAds() async { - await _decideAndShowAds(); - } - - ///CAS initialization and method and listeners - /// - Future _initializeCAS() async { - await CAS.setDebugMode(kDebugMode); - await CAS.setAnalyticsCollectionEnabled(true); - // CAS.setFlutterVersion("1.20.0"); - // await CAS.validateIntegration(); - - var builder = CAS - .buildManager() - .withCasId('org.getlantern.lantern') - .withAdTypes(AdTypeFlags.Interstitial) - .withInitializationListener(InitializationListenerWrapper()) - .withTestMode(false); - - CAS.buildConsentFlow().withPrivacyPolicy(privacyPolicy); - casMediationManager = builder.initialize(); - // This can be useful when you need to improve application performance by turning off unused formats. - await casMediationManager!.setEnabled(AdTypeFlags.Interstitial, true); - await CAS.setTaggedAudience(Audience.NOT_CHILDREN); - // await CAS.setTestDeviceIds(['D79728264130CE0918737B5A2178D362']); - logger.i('[Ads Manager] Initialization: CAS completed.'); - } - - Future _loadCASInterstitial() async { - if (casMediationManager != null) { - await casMediationManager!.loadInterstitial(); - logger.i('[Ads Manager] Request: Initiating CAS Interstitial loading.'); - } - } - - Future _showCASInterstitial() async { - logger.i('[Ads Manager] Show: Attempting to display CAS Interstitial.'); - await casMediationManager!.showInterstitial(InterstitialListenerWrapper( - onFailed: _onCASAdShowFailed, - onClosedOrComplete: _onCASAdClosedOrComplete)); - } - - void _onCASAdShowFailed() { - logger.e('[Ads Manager] Error: CAS Interstitial failed to display.'); - _failedCASLoadAttempts++; - _postShowingAds(); // Reload or decide the next action - } - - void _onCASAdClosedOrComplete() { - logger.i('[Ads Manager] Completion: CAS Interstitial closed or completed.'); - // Reset the counter when the ad successfully shows and closes/completes - _failedCASLoadAttempts = 0; - _postShowingAds(); - } - - //This method will use used when free user buy Pro version - Future turnOffCASInterstitial() async { - await casMediationManager!.setEnabled(AdTypeFlags.Interstitial, false); - } -} - -class InitializationListenerWrapper extends InitializationListener { - @override - void onCASInitialized(InitConfig initialConfig) { - logger.i('[CASIntegrationHelper] - onCASInitialized $initialConfig'); - } -} - -class InterstitialListenerWrapper extends AdCallback { - final VoidCallback onFailed; - final VoidCallback onClosedOrComplete; - - InterstitialListenerWrapper({ - required this.onFailed, - required this.onClosedOrComplete, - }); - - @override - void onClicked() { - logger.i('[CASIntegrationHelper] - InterstitialListenerWrapper onClicked'); - } - - @override - void onClosed() { - // Called when ad is clicked - onClosedOrComplete(); - logger.i('[CASIntegrationHelper] - InterstitialListenerWrapper onClosed'); - } - - @override - void onComplete() { - // Called when ad is dismissed - onClosedOrComplete(); - logger.i('[CASIntegrationHelper] - InterstitialListenerWrapper onComplete'); - } - - @override - void onImpression(AdImpression? adImpression) { - // Called when ad is paid. - logger.i( - '[CASIntegrationHelper] - InterstitialListenerWrapper onImpression-:$adImpression'); - } - - @override - void onShowFailed(String? message) { - // Called when ad fails to show. - onFailed.call(); - logger.i( - '[CASIntegrationHelper] - InterstitialListenerWrapper onShowFailed-:$message'); - } - - @override - void onShown() { - // Called when ad is shown. - logger.i('[CASIntegrationHelper] - InterstitialListenerWrapper onShown'); - } -} +// import 'dart:io'; + +// import 'package:clever_ads_solutions/CAS.dart'; +// import 'package:clever_ads_solutions/public/AdCallback.dart'; +// import 'package:clever_ads_solutions/public/AdImpression.dart'; +// import 'package:clever_ads_solutions/public/AdTypes.dart'; +// import 'package:clever_ads_solutions/public/Audience.dart'; +// import 'package:clever_ads_solutions/public/ConsentFlow.dart'; +// import 'package:clever_ads_solutions/public/InitConfig.dart'; +// import 'package:clever_ads_solutions/public/InitializationListener.dart'; +// import 'package:clever_ads_solutions/public/MediationManager.dart'; +// import 'package:clever_ads_solutions/public/OnDismissListener.dart'; +// import 'package:flutter/foundation.dart'; +// import 'package:google_mobile_ads/google_mobile_ads.dart'; +// import 'package:lantern/replica/common.dart'; + +// import 'common/session_model.dart'; + +// enum AdType { Google, CAS } + +// const privacyPolicy = 'https://lantern.io/privacy'; + +// class AdHelper { +// static final AdHelper _instance = AdHelper._internal(); + +// AdHelper._internal(); + +// factory AdHelper() { +// return _instance; +// } + +// AdType? _currentAdType; +// MediationManager? casMediationManager; +// InterstitialAd? _interstitialAd; +// int _failedLoadAttempts = 0; +// int _failedCASLoadAttempts = 0; + +// //If ads are getting failed to load we want to make lot of calls +// // Just try 5 times +// final int _maxFailAttempts = 5; + +// //Google Test ID if needed to test +// // return 'ca-app-pub-3940256099942544/1033173712'; +// String get interstitialAdUnitId { +// if (Platform.isAndroid) { +// return const String.fromEnvironment('INTERSTITIAL_AD_UNIT_ID'); +// } else { +// throw UnsupportedError('Unsupported platform'); +// } +// } + +// // Private methods to decide whether to load or show Google Ads or CAS ads based on conditions +// Future _decideAndLoadAds() async { +// final shouldShowGoogleAds = await sessionModel.shouldShowAds(); +// final shouldShowCASAds = await sessionModel.shouldCASShowAds(); + +// logger.d( +// '[Ads Manager] Google Ads enable $shouldShowGoogleAds: CAS Ads $shouldShowCASAds'); +// if (shouldShowGoogleAds) { +// _currentAdType = AdType.Google; +// logger.i('[Ads Manager] Decision: Loading Google Ads.'); +// await _loadInterstitialAd(); +// } else if (shouldShowCASAds) { +// _currentAdType = AdType.CAS; +// logger.i('[Ads Manager] Decision: Loading CAS Ads.'); +// if (casMediationManager == null) { +// await _initializeCAS(); +// } +// await _loadCASInterstitial(); +// } +// } + +// Future _decideAndShowAds() async { +// if (_currentAdType == AdType.Google && _interstitialAd != null) { +// await _showInterstitialAd(); +// } else if (_currentAdType == AdType.CAS) { +// final isCASReady = (await casMediationManager!.isInterstitialReady()); +// if (isCASReady) { +// await _showCASInterstitial(); +// logger.i('[Ads Manager] Request: Showing CAS Ad .'); +// } else { +// logger.i('[Ads Manager] CAS: Ad is not yet ready to show.'); +// } +// } +// } + +// Future _loadInterstitialAd() async { +// //To avoid calling multiple ads request repeatedly +// if (_interstitialAd == null && _failedLoadAttempts < _maxFailAttempts) { +// logger.i('[Ads Manager] Request: Making Google Ad request.'); +// await InterstitialAd.load( +// adUnitId: interstitialAdUnitId, +// request: const AdRequest(), +// adLoadCallback: InterstitialAdLoadCallback( +// onAdLoaded: (ad) { +// ad.fullScreenContentCallback = FullScreenContentCallback( +// onAdClicked: (ad) { +// logger.i('[Ads Manager] onAdClicked callback'); +// }, +// onAdShowedFullScreenContent: (ad) { +// logger.i('[Ads Manager] Showing Ads'); +// }, +// onAdFailedToShowFullScreenContent: (ad, error) { +// logger.i( +// '[Ads Manager] onAdFailedToShowFullScreenContent callback'); +// //if ads fail to load let user turn on VPN +// _postShowingAds(); +// }, +// onAdDismissedFullScreenContent: (ad) { +// logger.i('[Ads Manager] fullScreenContentCallback callback'); +// _postShowingAds(); +// }, +// ); +// _interstitialAd = ad; +// logger.i('[Ads Manager] to loaded $ad'); +// }, +// onAdFailedToLoad: (err) { +// _failedLoadAttempts++; // increment the count on failure +// logger.i('[Ads Manager] failed to load $err'); +// _postShowingAds(); +// }, +// ), +// ); +// } +// } + +// void _postShowingAds() { +// if (_currentAdType == AdType.Google) { +// _interstitialAd?.dispose(); +// _interstitialAd = null; +// _failedLoadAttempts = 0; // Reset counter for Google Ads +// logger.i( +// '[Ads Manager] Post-show: Google Ad displayed. Resetting failed load attempts and requesting a new ad.'); +// _loadInterstitialAd(); +// } else if (_currentAdType == AdType.CAS) { +// _failedCASLoadAttempts = 0; // Reset counter for CAS Ads +// logger.i( +// '[Ads Manager] Post-show: CAS Ad displayed. Resetting failed load attempts and requesting a new ad.'); +// _loadCASInterstitial(); +// } +// } + +// Future _showInterstitialAd() async { +// if (_interstitialAd != null) { +// await _interstitialAd?.show(); +// } +// } + +// // Public methods +// Future loadAds() async { +// await _decideAndLoadAds(); +// } + +// Future showAds() async { +// await _decideAndShowAds(); +// } + +// ///CAS initialization and method and listeners +// /// +// Future _initializeCAS() async { +// await CAS.setDebugMode(kDebugMode); +// await CAS.setAnalyticsCollectionEnabled(true); +// // CAS.setFlutterVersion("1.20.0"); +// // await CAS.validateIntegration(); + +// var builder = CAS +// .buildManager() +// .withCasId('org.getlantern.lantern') +// .withAdTypes(AdTypeFlags.Interstitial) +// .withInitializationListener(InitializationListenerWrapper()) +// .withTestMode(false); + +// CAS.buildConsentFlow().withPrivacyPolicy(privacyPolicy); +// casMediationManager = builder.initialize(); +// // This can be useful when you need to improve application performance by turning off unused formats. +// await casMediationManager!.setEnabled(AdTypeFlags.Interstitial, true); +// await CAS.setTaggedAudience(Audience.NOT_CHILDREN); +// // await CAS.setTestDeviceIds(['D79728264130CE0918737B5A2178D362']); +// logger.i('[Ads Manager] Initialization: CAS completed.'); +// } + +// Future _loadCASInterstitial() async { +// if (casMediationManager != null) { +// await casMediationManager!.loadInterstitial(); +// logger.i('[Ads Manager] Request: Initiating CAS Interstitial loading.'); +// } +// } + +// Future _showCASInterstitial() async { +// logger.i('[Ads Manager] Show: Attempting to display CAS Interstitial.'); +// await casMediationManager!.showInterstitial(InterstitialListenerWrapper( +// onFailed: _onCASAdShowFailed, +// onClosedOrComplete: _onCASAdClosedOrComplete)); +// } + +// void _onCASAdShowFailed() { +// logger.e('[Ads Manager] Error: CAS Interstitial failed to display.'); +// _failedCASLoadAttempts++; +// _postShowingAds(); // Reload or decide the next action +// } + +// void _onCASAdClosedOrComplete() { +// logger.i('[Ads Manager] Completion: CAS Interstitial closed or completed.'); +// // Reset the counter when the ad successfully shows and closes/completes +// _failedCASLoadAttempts = 0; +// _postShowingAds(); +// } + +// //This method will use used when free user buy Pro version +// Future turnOffCASInterstitial() async { +// await casMediationManager!.setEnabled(AdTypeFlags.Interstitial, false); +// } +// } + +// class InitializationListenerWrapper extends InitializationListener { +// @override +// void onCASInitialized(InitConfig initialConfig) { +// logger.i('[CASIntegrationHelper] - onCASInitialized $initialConfig'); +// } +// } + +// class InterstitialListenerWrapper extends AdCallback { +// final VoidCallback onFailed; +// final VoidCallback onClosedOrComplete; + +// InterstitialListenerWrapper({ +// required this.onFailed, +// required this.onClosedOrComplete, +// }); + +// @override +// void onClicked() { +// logger.i('[CASIntegrationHelper] - InterstitialListenerWrapper onClicked'); +// } + +// @override +// void onClosed() { +// // Called when ad is clicked +// onClosedOrComplete(); +// logger.i('[CASIntegrationHelper] - InterstitialListenerWrapper onClosed'); +// } + +// @override +// void onComplete() { +// // Called when ad is dismissed +// onClosedOrComplete(); +// logger.i('[CASIntegrationHelper] - InterstitialListenerWrapper onComplete'); +// } + +// @override +// void onImpression(AdImpression? adImpression) { +// // Called when ad is paid. +// logger.i( +// '[CASIntegrationHelper] - InterstitialListenerWrapper onImpression-:$adImpression'); +// } + +// @override +// void onShowFailed(String? message) { +// // Called when ad fails to show. +// onFailed.call(); +// logger.i( +// '[CASIntegrationHelper] - InterstitialListenerWrapper onShowFailed-:$message'); +// } + +// @override +// void onShown() { +// // Called when ad is shown. +// logger.i('[CASIntegrationHelper] - InterstitialListenerWrapper onShown'); +// } +// } diff --git a/lib/app.dart b/lib/app.dart index a14bb5fb8..758c7c123 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -78,49 +78,59 @@ class LanternApp extends StatelessWidget { if (!snapshot.hasData) { return Container(); } + return sessionModel.language( + (context, lang, child) { + Localization.locale = lang; return GlobalLoaderOverlay( - overlayColor: Colors.black, - overlayOpacity: 0.6, - child: I18n( - initialLocale: const Locale('en', 'US'), - child: MaterialApp.router( - debugShowCheckedModeBanner: false, - theme: ThemeData( - fontFamily: _getLocaleBasedFont(currentLocal), - brightness: Brightness.light, - primarySwatch: Colors.grey, - appBarTheme: const AppBarTheme( - systemOverlayStyle: SystemUiOverlayStyle.light, + overlayColor: Colors.black, + overlayOpacity: 0.6, + child: I18n( + initialLocale: lang == '' || lang.startsWith('en') + ? const Locale('en', 'US') + : Locale(lang), + child: MaterialApp.router( + locale: lang == '' || lang.startsWith('en') + ? const Locale('en', 'US') + : Locale(lang), + debugShowCheckedModeBanner: false, + theme: ThemeData( + fontFamily: _getLocaleBasedFont(currentLocal), + brightness: Brightness.light, + primarySwatch: Colors.grey, + appBarTheme: const AppBarTheme( + systemOverlayStyle: SystemUiOverlayStyle.dark, + ), + colorScheme: ColorScheme.fromSwatch() + .copyWith(secondary: Colors.black), + ), + title: 'app_name'.i18n, + localizationsDelegates: const [ + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + ], + routeInformationParser: globalRouter.defaultRouteParser(), + routerDelegate: globalRouter.delegate(), + supportedLocales: const [ + Locale('ar', 'EG'), + Locale('fr', 'FR'), + Locale('en', 'US'), + Locale('fa', 'IR'), + Locale('th', 'TH'), + Locale('ms', 'MY'), + Locale('ru', 'RU'), + Locale('ur', 'IN'), + Locale('zh', 'CN'), + Locale('zh', 'HK'), + Locale('es', 'ES'), + Locale('tr', 'TR'), + Locale('vi', 'VN'), + Locale('my', 'MM'), + ], ), - colorScheme: - ColorScheme.fromSwatch().copyWith(secondary: Colors.black), ), - title: 'app_name'.i18n, - localizationsDelegates: const [ - GlobalMaterialLocalizations.delegate, - GlobalWidgetsLocalizations.delegate, - GlobalCupertinoLocalizations.delegate, - ], - routeInformationParser: globalRouter.defaultRouteParser(), - routerDelegate: globalRouter.delegate(), - supportedLocales: const [ - Locale('ar', 'EG'), - Locale('fr', 'FR'), - Locale('en', 'US'), - Locale('fa', 'IR'), - Locale('th', 'TH'), - Locale('ms', 'MY'), - Locale('ru', 'RU'), - Locale('ur', 'IN'), - Locale('zh', 'CN'), - Locale('zh', 'HK'), - Locale('es', 'ES'), - Locale('tr', 'TR'), - Locale('vi', 'VN'), - Locale('my', 'MM'), - ], - ), - ), + ); + }, ); }, ); diff --git a/lib/common/session_model.dart b/lib/common/session_model.dart index 776b14cc3..1990670e4 100644 --- a/lib/common/session_model.dart +++ b/lib/common/session_model.dart @@ -32,11 +32,7 @@ class SessionModel extends Model { 'playVersion', false, ); - - proxyAvailable = singleValueNotifier( - 'hasSucceedingProxy', - true, - ); + proxyAvailable = singleValueNotifier('hasSucceedingProxy', false); } ValueNotifier networkAvailable = ValueNotifier(true); @@ -74,10 +70,8 @@ class SessionModel extends Model { } Widget acceptedTermsVersion(ValueWidgetBuilder builder) { - return subscribedSingleValueBuilder( - 'accepted_terms_version', - builder: builder, - ); + return subscribedSingleValueBuilder('accepted_terms_version', + builder: builder, defaultValue: 0); } Widget forceCountry(ValueWidgetBuilder builder) { @@ -304,10 +298,7 @@ class SessionModel extends Model { } Future reportIssue( - String email, - String issue, - String description - ) async { + String email, String issue, String description) async { return methodChannel.invokeMethod('reportIssue', { 'email': email, 'issue': issue, @@ -315,7 +306,6 @@ class SessionModel extends Model { }).then((value) => value as String); } - Widget getUserId(ValueWidgetBuilder builder) { return subscribedSingleValueBuilder( 'userId', diff --git a/lib/common/ui/full_screen_dialog.dart b/lib/common/ui/full_screen_dialog.dart index d8d27430b..af53a1207 100644 --- a/lib/common/ui/full_screen_dialog.dart +++ b/lib/common/ui/full_screen_dialog.dart @@ -10,7 +10,7 @@ class FullScreenDialog extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( - body: widget, + body: SafeArea(child: widget), ); } } diff --git a/lib/home.dart b/lib/home.dart index afffaceae..d8efad026 100644 --- a/lib/home.dart +++ b/lib/home.dart @@ -132,30 +132,25 @@ class _HomePageState extends State { if (isPlayVersion && version == 0) { // show privacy disclosure if it's a Play build and the terms have // not already been accepted - return PrivacyDisclosure(); + return const PrivacyDisclosure(); } - return sessionModel.language( - (BuildContext context, String lang, Widget? child) { - Localization.locale = lang; - return sessionModel.selectedTab( + return sessionModel.selectedTab( (context, selectedTab, child) => messagingModel - .getOnBoardingStatus((_, isOnboarded, child) { - final isTesting = const String.fromEnvironment( - 'driver', - defaultValue: 'false', - ).toLowerCase() == - 'true'; - return Scaffold( - body: buildBody(selectedTab, isOnboarded), - bottomNavigationBar: CustomBottomBar( - selectedTab: selectedTab, - isDevelop: developmentMode, - isTesting: isTesting, - ), - ); - }), + .getOnBoardingStatus((_, isOnboarded, child) { + final isTesting = const String.fromEnvironment( + 'driver', + defaultValue: 'false', + ).toLowerCase() == + 'true'; + return Scaffold( + body: buildBody(selectedTab, isOnboarded), + bottomNavigationBar: CustomBottomBar( + selectedTab: selectedTab, + isDevelop: developmentMode, + isTesting: isTesting, + ), ); - }, + }), ); }, ); @@ -178,7 +173,7 @@ class _HomePageState extends State { ? Chats() : Welcome(); case TAB_VPN: - return VPNTab(); + return const VPNTab(); case TAB_REPLICA: return ReplicaTab(); case TAB_ACCOUNT: diff --git a/lib/vpn/vpn_switch.dart b/lib/vpn/vpn_switch.dart index 59594956e..c715f62ca 100644 --- a/lib/vpn/vpn_switch.dart +++ b/lib/vpn/vpn_switch.dart @@ -7,12 +7,12 @@ class VPNSwitch extends StatefulWidget { } class _VPNSwitchState extends State { - final adHelper = AdHelper(); + // final adHelper = AdHelper(); @override void initState() { super.initState(); - adHelper.loadAds(); + // adHelper.loadAds(); } bool isIdle(String vpnStatus) => @@ -28,7 +28,7 @@ class _VPNSwitchState extends State { Future.delayed( const Duration(seconds: 1), () async { - await adHelper.showAds(); + // await adHelper.showAds(); }, ); } diff --git a/lib/vpn/vpn_tab.dart b/lib/vpn/vpn_tab.dart index b9a874a11..bba9f9f76 100644 --- a/lib/vpn/vpn_tab.dart +++ b/lib/vpn/vpn_tab.dart @@ -1,6 +1,7 @@ -import 'package:lantern/messaging/messaging.dart'; import 'package:lantern/account/split_tunneling.dart'; +import 'package:lantern/messaging/messaging.dart'; import 'package:lantern/vpn/vpn.dart'; + import 'vpn_bandwidth.dart'; import 'vpn_pro_banner.dart'; import 'vpn_server_location.dart'; @@ -8,7 +9,7 @@ import 'vpn_status.dart'; import 'vpn_switch.dart'; class VPNTab extends StatelessWidget { - VPNTab({Key? key}) : super(key: key); + const VPNTab({Key? key}) : super(key: key); @override Widget build(BuildContext context) { @@ -24,7 +25,10 @@ class VPNTab extends StatelessWidget { body: Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - proUser ? Container() : ProBanner(), + if(!proUser && Platform.isAndroid) + ProBanner() + else + const SizedBox(), VPNSwitch(), Container( padding: const EdgeInsetsDirectional.all(16), @@ -38,18 +42,18 @@ class VPNTab extends StatelessWidget { ), ), child: Column( + mainAxisSize: MainAxisSize.min, children: [ VPNStatus(), - Container( - child: const CDivider(height: 32.0), - ), + const CDivider(height: 32.0), ServerLocationWidget(), - Container( - child: const CDivider(height: 32.0), - ), - SplitTunnelingWidget(), - VPNBandwidth(), - ], + if(Platform.isAndroid)...{ + const CDivider(height: 32.0), + SplitTunnelingWidget(), + // Not sure about this + VPNBandwidth(), + } + ], ), ), ], diff --git a/protos_shared/vpn.proto b/protos_shared/vpn.proto index 4ea4ea811..27752eed6 100644 --- a/protos_shared/vpn.proto +++ b/protos_shared/vpn.proto @@ -1,5 +1,6 @@ syntax = "proto3"; option java_package = "io.lantern.model"; +option go_package = "/internalsdk"; message ServerInfo { string city = 1; diff --git a/pubspec.lock b/pubspec.lock index 442ccb02d..6ddbae451 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -242,14 +242,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.3" - clever_ads_solutions: - dependency: "direct main" - description: - name: clever_ads_solutions - sha256: fd9386aeb8874c25f8e6dfc85f22cee31b661a2c7a02a92f2f1569ed5dd75da9 - url: "https://pub.dev" - source: hosted - version: "0.1.0" clock: dependency: transitive description: @@ -338,14 +330,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.3.1" - dartz: - dependency: transitive - description: - name: dartz - sha256: e6acf34ad2e31b1eb00948692468c30ab48ac8250e0f0df661e29f12dd252168 - url: "https://pub.dev" - source: hosted - version: "0.10.1" dbus: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 5a54da8e8..f76d892dc 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -96,7 +96,7 @@ dependencies: flutter_markdown: ^0.6.17+1 # Ads google_mobile_ads: ^3.0.0 - clever_ads_solutions: ^0.1.0 + # clever_ads_solutions: ^0.1.0