mirror of
https://github.com/passepartoutvpn/passepartout-apple.git
synced 2025-01-31 13:02:11 +00:00
Merge branch 'handle-malicious-refunds'
This commit is contained in:
commit
1ed4d32d5d
@ -27,6 +27,9 @@ import UIKit
|
|||||||
import TunnelKit
|
import TunnelKit
|
||||||
import PassepartoutCore
|
import PassepartoutCore
|
||||||
import Convenience
|
import Convenience
|
||||||
|
import SwiftyBeaver
|
||||||
|
|
||||||
|
private let log = SwiftyBeaver.self
|
||||||
|
|
||||||
@UIApplicationMain
|
@UIApplicationMain
|
||||||
class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDelegate {
|
class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDelegate {
|
||||||
@ -79,6 +82,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDele
|
|||||||
|
|
||||||
func applicationDidBecomeActive(_ application: UIApplication) {
|
func applicationDidBecomeActive(_ application: UIApplication) {
|
||||||
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
|
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
|
||||||
|
ProductManager.shared.reviewPurchases()
|
||||||
}
|
}
|
||||||
|
|
||||||
func applicationWillTerminate(_ application: UIApplication) {
|
func applicationWillTerminate(_ application: UIApplication) {
|
||||||
@ -161,6 +165,9 @@ extension UISplitViewController {
|
|||||||
|
|
||||||
extension AppDelegate {
|
extension AppDelegate {
|
||||||
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
|
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
|
||||||
|
guard ProductManager.shared.isEligible(forFeature: .siriShortcuts) else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
guard let interaction = userActivity.interaction else {
|
guard let interaction = userActivity.interaction else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -35,6 +35,8 @@ private let log = SwiftyBeaver.self
|
|||||||
class ProductManager: NSObject {
|
class ProductManager: NSObject {
|
||||||
static let didReloadReceipt = Notification.Name("ProductManagerDidReloadReceipt")
|
static let didReloadReceipt = Notification.Name("ProductManagerDidReloadReceipt")
|
||||||
|
|
||||||
|
static let didReviewPurchases = Notification.Name("ProductManagerDidReviewPurchases")
|
||||||
|
|
||||||
private static let lastFullVersionBuild = 2016 // 1.8.1
|
private static let lastFullVersionBuild = 2016 // 1.8.1
|
||||||
|
|
||||||
static let shared = ProductManager()
|
static let shared = ProductManager()
|
||||||
@ -96,7 +98,7 @@ class ProductManager: NSObject {
|
|||||||
|
|
||||||
// MARK: In-app eligibility
|
// MARK: In-app eligibility
|
||||||
|
|
||||||
private func reloadReceipt() {
|
private func reloadReceipt(andNotify: Bool = true) {
|
||||||
guard let url = Bundle.main.appStoreReceiptURL else {
|
guard let url = Bundle.main.appStoreReceiptURL else {
|
||||||
log.warning("No App Store receipt found!")
|
log.warning("No App Store receipt found!")
|
||||||
return
|
return
|
||||||
@ -122,21 +124,27 @@ class ProductManager: NSObject {
|
|||||||
if let iapReceipts = receipt.inAppPurchaseReceipts {
|
if let iapReceipts = receipt.inAppPurchaseReceipts {
|
||||||
log.debug("In-app receipts:")
|
log.debug("In-app receipts:")
|
||||||
iapReceipts.forEach {
|
iapReceipts.forEach {
|
||||||
guard let pid = $0.productIdentifier, let date = $0.originalPurchaseDate else {
|
guard let pid = $0.productIdentifier, let purchaseDate = $0.originalPurchaseDate else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
log.debug("\t\(pid) [\(date)]")
|
log.debug("\t\(pid) [purchased on: \(purchaseDate)]")
|
||||||
}
|
}
|
||||||
for r in iapReceipts {
|
for r in iapReceipts {
|
||||||
guard let pid = r.productIdentifier, let product = Product(rawValue: pid) else {
|
guard let pid = r.productIdentifier, let product = Product(rawValue: pid) else {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if let cancellationDate = r.cancellationDate {
|
||||||
|
log.debug("\t\(pid) [cancelled on: \(cancellationDate)]")
|
||||||
|
continue
|
||||||
|
}
|
||||||
purchasedFeatures.insert(product)
|
purchasedFeatures.insert(product)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log.info("Purchased features: \(purchasedFeatures)")
|
log.info("Purchased features: \(purchasedFeatures)")
|
||||||
|
|
||||||
NotificationCenter.default.post(name: ProductManager.didReloadReceipt, object: nil)
|
if andNotify {
|
||||||
|
NotificationCenter.default.post(name: ProductManager.didReloadReceipt, object: nil)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func isFullVersion() -> Bool {
|
func isFullVersion() -> Bool {
|
||||||
@ -165,6 +173,60 @@ class ProductManager: NSObject {
|
|||||||
func isEligibleForFeedback() -> Bool {
|
func isEligibleForFeedback() -> Bool {
|
||||||
return AppConstants.Flags.isBeta || !purchasedFeatures.isEmpty
|
return AppConstants.Flags.isBeta || !purchasedFeatures.isEmpty
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: Review
|
||||||
|
|
||||||
|
func reviewPurchases() {
|
||||||
|
let service = TransientStore.shared.service
|
||||||
|
reloadReceipt(andNotify: false)
|
||||||
|
var shouldReinstall = false
|
||||||
|
|
||||||
|
// review features and potentially revert them if they were used (Siri is handled in AppDelegate)
|
||||||
|
|
||||||
|
log.debug("Checking 'Trusted networks'")
|
||||||
|
if !isEligible(forFeature: .trustedNetworks) {
|
||||||
|
if service.preferences.trustsMobileNetwork || !service.preferences.trustedWifis.isEmpty {
|
||||||
|
service.preferences.trustsMobileNetwork = false
|
||||||
|
service.preferences.trustedWifis.removeAll()
|
||||||
|
log.debug("\tRefunded")
|
||||||
|
shouldReinstall = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.debug("Checking 'Unlimited hosts'")
|
||||||
|
if !isEligible(forFeature: .unlimitedHosts) {
|
||||||
|
let ids = service.ids(forContext: .host)
|
||||||
|
if ids.count > AppConstants.InApp.limitedNumberOfHosts {
|
||||||
|
for id in ids {
|
||||||
|
service.removeProfile(ProfileKey(.host, id))
|
||||||
|
}
|
||||||
|
log.debug("\tRefunded")
|
||||||
|
shouldReinstall = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.debug("Checking providers")
|
||||||
|
for name in service.currentProviderNames() {
|
||||||
|
if !isEligible(forProvider: name) {
|
||||||
|
service.removeProfile(ProfileKey(name))
|
||||||
|
log.debug("\tRefunded provider: \(name)")
|
||||||
|
shouldReinstall = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// no refunds
|
||||||
|
guard shouldReinstall else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
// save reverts and remove fraud VPN profile
|
||||||
|
TransientStore.shared.serialize(withProfiles: true)
|
||||||
|
VPN.shared.uninstall(completionHandler: nil)
|
||||||
|
|
||||||
|
NotificationCenter.default.post(name: ProductManager.didReviewPurchases, object: nil)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension ConnectionService {
|
extension ConnectionService {
|
||||||
|
@ -120,6 +120,7 @@ class ServiceViewController: UIViewController, StrongTableHost {
|
|||||||
}
|
}
|
||||||
nc.addObserver(self, selector: #selector(serviceDidUpdateDataCount(_:)), name: ConnectionService.didUpdateDataCount, object: nil)
|
nc.addObserver(self, selector: #selector(serviceDidUpdateDataCount(_:)), name: ConnectionService.didUpdateDataCount, object: nil)
|
||||||
nc.addObserver(self, selector: #selector(productManagerDidReloadReceipt), name: ProductManager.didReloadReceipt, object: nil)
|
nc.addObserver(self, selector: #selector(productManagerDidReloadReceipt), name: ProductManager.didReloadReceipt, object: nil)
|
||||||
|
nc.addObserver(self, selector: #selector(productManagerDidReviewPurchases), name: ProductManager.didReviewPurchases, object: nil)
|
||||||
|
|
||||||
// run this no matter what
|
// run this no matter what
|
||||||
// XXX: convenient here vs AppDelegate for updating table
|
// XXX: convenient here vs AppDelegate for updating table
|
||||||
@ -141,6 +142,7 @@ class ServiceViewController: UIViewController, StrongTableHost {
|
|||||||
super.viewDidAppear(animated)
|
super.viewDidAppear(animated)
|
||||||
|
|
||||||
clearSelection()
|
clearSelection()
|
||||||
|
hideProfileIfDeleted()
|
||||||
}
|
}
|
||||||
|
|
||||||
override func didReceiveMemoryWarning() {
|
override func didReceiveMemoryWarning() {
|
||||||
@ -674,6 +676,10 @@ class ServiceViewController: UIViewController, StrongTableHost {
|
|||||||
reloadModel()
|
reloadModel()
|
||||||
tableView.reloadData()
|
tableView.reloadData()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@objc private func productManagerDidReviewPurchases() {
|
||||||
|
hideProfileIfDeleted()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: -
|
// MARK: -
|
||||||
|
@ -1 +1 @@
|
|||||||
Subproject commit 7476e0751d1a64efca0180a1e31f0e24f04fce84
|
Subproject commit dad47f3581ab25d966d204de93634d82b9fe808b
|
Loading…
Reference in New Issue
Block a user