mirror of
https://github.com/SideStore/SideStore.git
synced 2026-02-09 06:43:25 +01:00
fix: Account exporting/importing
This commit is contained in:
@@ -10,6 +10,8 @@ import UIKit
|
|||||||
import Roxas
|
import Roxas
|
||||||
import minimuxer
|
import minimuxer
|
||||||
import WidgetKit
|
import WidgetKit
|
||||||
|
|
||||||
|
import AltSign
|
||||||
import AltStoreCore
|
import AltStoreCore
|
||||||
import UniformTypeIdentifiers
|
import UniformTypeIdentifiers
|
||||||
|
|
||||||
@@ -70,7 +72,9 @@ final class LaunchViewController: UIViewController, UIDocumentPickerDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if !targetEnvironment(simulator)
|
#if !targetEnvironment(simulator)
|
||||||
guard let pf = PairingFileManager.shared.fetchPairingFile(presentingVC: self) else {
|
|
||||||
|
detectAndImportAccountFile()
|
||||||
|
guard let pf = fetchPairingFile() else {
|
||||||
displayError("Device pairing file not found.")
|
displayError("Device pairing file not found.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -102,6 +106,11 @@ final class LaunchViewController: UIViewController, UIDocumentPickerDelegate {
|
|||||||
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
|
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
|
||||||
let url = urls[0]
|
let url = urls[0]
|
||||||
let isSecuredURL = url.startAccessingSecurityScopedResource() == true
|
let isSecuredURL = url.startAccessingSecurityScopedResource() == true
|
||||||
|
defer {
|
||||||
|
if (isSecuredURL) {
|
||||||
|
url.stopAccessingSecurityScopedResource()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
do {
|
do {
|
||||||
let data = try Data(contentsOf: url)
|
let data = try Data(contentsOf: url)
|
||||||
@@ -114,14 +123,68 @@ final class LaunchViewController: UIViewController, UIDocumentPickerDelegate {
|
|||||||
} catch {
|
} catch {
|
||||||
displayError("Unable to read pairing file")
|
displayError("Unable to read pairing file")
|
||||||
}
|
}
|
||||||
|
|
||||||
if isSecuredURL { url.stopAccessingSecurityScopedResource() }
|
controller.dismiss(animated: true, completion: nil)
|
||||||
controller.dismiss(animated: true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) {
|
func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) {
|
||||||
displayError("Choosing a pairing file was cancelled. Please re-open the app and try again.")
|
displayError("Choosing a pairing file was cancelled. Please re-open the app and try again.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func start_minimuxer_threads(_ pairing_file: String) {
|
||||||
|
target_minimuxer_address()
|
||||||
|
let documentsDirectory = FileManager.default.documentsDirectory.absoluteString
|
||||||
|
do {
|
||||||
|
// enable minimuxer console logging only if enabled in settings
|
||||||
|
let isMinimuxerConsoleLoggingEnabled = UserDefaults.standard.isMinimuxerConsoleLoggingEnabled
|
||||||
|
try minimuxer.startWithLogger(pairing_file, documentsDirectory, isMinimuxerConsoleLoggingEnabled)
|
||||||
|
} catch {
|
||||||
|
try! FileManager.default.removeItem(at: FileManager.default.documentsDirectory.appendingPathComponent("\(pairingFileName)"))
|
||||||
|
displayError("minimuxer failed to start, please restart SideStore. \((error as? LocalizedError)?.failureReason ?? "UNKNOWN ERROR!!!!!! REPORT TO GITHUB ISSUES!")")
|
||||||
|
}
|
||||||
|
start_auto_mounter(documentsDirectory)
|
||||||
|
// Create destinationViewController now so view controllers can register for receiving Notifications.
|
||||||
|
self.destinationViewController = self.storyboard!.instantiateViewController(withIdentifier: "tabBarController") as? TabBarController
|
||||||
|
}
|
||||||
|
|
||||||
|
func importAccountAtFile(_ file: URL, remove: Bool = false) {
|
||||||
|
_ = file.startAccessingSecurityScopedResource()
|
||||||
|
defer { file.stopAccessingSecurityScopedResource() }
|
||||||
|
guard let accountD = try? Data(contentsOf: file) else {
|
||||||
|
let toastView = ToastView(text: NSLocalizedString("Could not read data from file!", comment: ""), detailText: "\(file)")
|
||||||
|
return toastView.show(in: self)
|
||||||
|
}
|
||||||
|
guard let account = try? Foundation.JSONDecoder().decode(ImportedAccount.self, from: accountD) else {
|
||||||
|
let toastView = ToastView(text: NSLocalizedString("Could not parse data from file!", comment: ""), detailText: "\(file)")
|
||||||
|
return toastView.show(in: self)
|
||||||
|
}
|
||||||
|
print("We want to import this account probably: \(account)")
|
||||||
|
if remove {
|
||||||
|
try? FileManager.default.removeItem(at: file)
|
||||||
|
}
|
||||||
|
Keychain.shared.appleIDEmailAddress = account.email
|
||||||
|
Keychain.shared.appleIDPassword = account.password
|
||||||
|
Keychain.shared.adiPb = account.adiPB
|
||||||
|
Keychain.shared.identifier = account.local_user
|
||||||
|
if let altCert = ALTCertificate(p12Data: account.cert, password: account.certpass) {
|
||||||
|
Keychain.shared.signingCertificate = altCert.encryptedP12Data(withPassword: "")!
|
||||||
|
Keychain.shared.signingCertificatePassword = account.certpass
|
||||||
|
let toastView = ToastView(text: NSLocalizedString("Successfully imported '\(account.email)'!", comment: ""), detailText: "SideStore should be fully operational!")
|
||||||
|
return toastView.show(in: self)
|
||||||
|
} else {
|
||||||
|
let toastView = ToastView(text: NSLocalizedString("Failed to import account certificate!", comment: ""), detailText: "Failed to create ALTCertificate. Check if the password is correct. Still imported account/adi.pb details!")
|
||||||
|
return toastView.show(in: self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func detectAndImportAccountFile() {
|
||||||
|
let accountFileURL = FileManager.default.documentsDirectory.appendingPathComponent("Account.sideconf")
|
||||||
|
#if !DEBUG
|
||||||
|
importAccountAtFile(accountFileURL, remove: true)
|
||||||
|
#else
|
||||||
|
importAccountAtFile(accountFileURL)
|
||||||
|
#endif
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension LaunchViewController {
|
extension LaunchViewController {
|
||||||
|
|||||||
@@ -261,28 +261,34 @@ final class SettingsViewController: UITableViewController
|
|||||||
}
|
}
|
||||||
|
|
||||||
func importAccountAtFile(_ file: URL, remove: Bool = false) {
|
func importAccountAtFile(_ file: URL, remove: Bool = false) {
|
||||||
if let accountData = try? Data(contentsOf: file),
|
_ = file.startAccessingSecurityScopedResource()
|
||||||
let account = try? Foundation.JSONDecoder().decode(ImportedAccount.self, from: accountData) {
|
defer { file.stopAccessingSecurityScopedResource() }
|
||||||
print("We want to import this account probably: \(account)")
|
guard let accountD = try? Data(contentsOf: file) else {
|
||||||
if remove {
|
let toastView = ToastView(text: NSLocalizedString("Could not read data from file!", comment: ""), detailText: "\(file)")
|
||||||
try? FileManager.default.removeItem(at: file)
|
return toastView.show(in: self)
|
||||||
}
|
}
|
||||||
Keychain.shared.appleIDEmailAddress = account.email
|
guard let account = try? Foundation.JSONDecoder().decode(ImportedAccount.self, from: accountD) else {
|
||||||
Keychain.shared.appleIDPassword = account.password
|
let toastView = ToastView(text: NSLocalizedString("Could not parse data from file!", comment: ""), detailText: "\(file)")
|
||||||
Keychain.shared.adiPb = account.adiPB
|
return toastView.show(in: self)
|
||||||
Keychain.shared.adiSerial = account.serial
|
}
|
||||||
Keychain.shared.identifier = account.local_user
|
print("We want to import this account probably: \(account)")
|
||||||
signIn()
|
if remove {
|
||||||
update()
|
try? FileManager.default.removeItem(at: file)
|
||||||
if let altCert = ALTCertificate(p12Data: account.cert, password: account.certpass) {
|
}
|
||||||
Keychain.shared.signingCertificate = altCert.encryptedP12Data(withPassword: "")!
|
Keychain.shared.appleIDEmailAddress = account.email
|
||||||
Keychain.shared.signingCertificatePassword = account.certpass
|
Keychain.shared.appleIDPassword = account.password
|
||||||
let toastView = ToastView(text: NSLocalizedString("Successfully imported '\(account.email)'!", comment: ""), detailText: "SideStore should be fully operational!")
|
Keychain.shared.adiPb = account.adiPB
|
||||||
return toastView.show(in: self)
|
Keychain.shared.identifier = account.local_user
|
||||||
} else {
|
signIn()
|
||||||
let toastView = ToastView(text: NSLocalizedString("Failed to import account certificate!", comment: ""), detailText: "Failed to create ALTCertificate. Check if the password is correct. Still imported account/adi.pb details!")
|
update()
|
||||||
return toastView.show(in: self)
|
if let altCert = ALTCertificate(p12Data: account.cert, password: account.certpass) {
|
||||||
}
|
Keychain.shared.signingCertificate = altCert.encryptedP12Data(withPassword: "")!
|
||||||
|
Keychain.shared.signingCertificatePassword = account.certpass
|
||||||
|
let toastView = ToastView(text: NSLocalizedString("Successfully imported '\(account.email)'!", comment: ""), detailText: "SideStore should be fully operational!")
|
||||||
|
return toastView.show(in: self)
|
||||||
|
} else {
|
||||||
|
let toastView = ToastView(text: NSLocalizedString("Failed to import account certificate!", comment: ""), detailText: "Failed to create ALTCertificate. Check if the password is correct. Still imported account/adi.pb details!")
|
||||||
|
return toastView.show(in: self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -300,7 +306,6 @@ final class SettingsViewController: UITableViewController
|
|||||||
let password = Keychain.shared.appleIDPassword,
|
let password = Keychain.shared.appleIDPassword,
|
||||||
let cert = Keychain.shared.signingCertificate,
|
let cert = Keychain.shared.signingCertificate,
|
||||||
let identifier = Keychain.shared.identifier,
|
let identifier = Keychain.shared.identifier,
|
||||||
let adiSerial = Keychain.shared.adiSerial,
|
|
||||||
let adiPB = Keychain.shared.adiPb else {
|
let adiPB = Keychain.shared.adiPb else {
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
print(Keychain.shared.appleIDEmailAddress ?? "Empty email")
|
print(Keychain.shared.appleIDEmailAddress ?? "Empty email")
|
||||||
@@ -308,17 +313,16 @@ final class SettingsViewController: UITableViewController
|
|||||||
print(Keychain.shared.signingCertificate ?? "Empty cert")
|
print(Keychain.shared.signingCertificate ?? "Empty cert")
|
||||||
print(Keychain.shared.identifier ?? "Empty identifier")
|
print(Keychain.shared.identifier ?? "Empty identifier")
|
||||||
print(Keychain.shared.adiPb ?? "Empty adiPb")
|
print(Keychain.shared.adiPb ?? "Empty adiPb")
|
||||||
print(Keychain.shared.adiSerial ?? "Empty adiSerial")
|
|
||||||
#endif
|
#endif
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return ImportedAccount(email: email, password: password, cert: cert, certpass: certpass, local_user: identifier, serial: adiSerial, adiPB: adiPB)
|
return ImportedAccount(email: email, password: password, cert: cert, certpass: certpass, local_user: identifier, adiPB: adiPB)
|
||||||
}
|
}
|
||||||
|
|
||||||
func showExportAccount() {
|
func showExportAccount() {
|
||||||
|
|
||||||
Task {
|
Task {
|
||||||
let password = await withUnsafeContinuation { (c: UnsafeContinuation<String?,Never>) in
|
guard let password = await withUnsafeContinuation({ (c: UnsafeContinuation<String?,Never>) in
|
||||||
let alertController = UIAlertController(title: NSLocalizedString("Please enter the password for the certificate.", comment: ""), message: nil, preferredStyle: .alert)
|
let alertController = UIAlertController(title: NSLocalizedString("Please enter the password for the certificate.", comment: ""), message: nil, preferredStyle: .alert)
|
||||||
|
|
||||||
alertController.addTextField { (textField) in
|
alertController.addTextField { (textField) in
|
||||||
@@ -338,19 +342,16 @@ final class SettingsViewController: UITableViewController
|
|||||||
})
|
})
|
||||||
|
|
||||||
self.present(alertController, animated: true)
|
self.present(alertController, animated: true)
|
||||||
}
|
}) else {
|
||||||
|
|
||||||
guard let password else {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let account = exportAccount(password) else {
|
guard let account = exportAccount(password) else {
|
||||||
let toastView = ToastView(text: NSLocalizedString("Failed to export account!", comment: ""), detailText: "Account not found.")
|
let toastView = ToastView(text: NSLocalizedString("Failed to export account!", comment: ""), detailText: "Account not found.")
|
||||||
toastView.show(in: self)
|
return toastView.show(in: self)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let accountData = try? Foundation.JSONEncoder().encode(account).base64EncodedData() else {
|
guard let accountData = try? Foundation.JSONEncoder().encode(account) else {
|
||||||
let toastView = ToastView(text: NSLocalizedString("Failed to export account data!", comment: ""), detailText: "Account malformed.")
|
let toastView = ToastView(text: NSLocalizedString("Failed to export account data!", comment: ""), detailText: "Account malformed.")
|
||||||
toastView.show(in: self)
|
toastView.show(in: self)
|
||||||
return
|
return
|
||||||
@@ -1364,15 +1365,25 @@ extension SettingsViewController
|
|||||||
guard let confUrl else {
|
guard let confUrl else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
_ = confUrl.startAccessingSecurityScopedResource()
|
|
||||||
defer { confUrl.stopAccessingSecurityScopedResource() }
|
|
||||||
importAccountAtFile(confUrl)
|
importAccountAtFile(confUrl)
|
||||||
}
|
}
|
||||||
case .importCert:
|
case .importCert:
|
||||||
|
let importVc = UIDocumentPickerViewController(forOpeningContentTypes: [UTType(filenameExtension: "p12")!], asCopy: false)
|
||||||
|
ImportExport.documentPickerHandler = DocumentPickerHandler { url in
|
||||||
|
guard let url else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
importVc.delegate = ImportExport.documentPickerHandler
|
||||||
|
self.present(importVc, animated: true)
|
||||||
|
_ = url.startAccessingSecurityScopedResource()
|
||||||
|
defer { url.stopAccessingSecurityScopedResource() }
|
||||||
|
}
|
||||||
Task {
|
Task {
|
||||||
let certUrl = await withUnsafeContinuation { c in
|
let certUrl = await withUnsafeContinuation { c in
|
||||||
let importVc = UIDocumentPickerViewController(forOpeningContentTypes: [UTType(filenameExtension: "p12")!], asCopy: false)
|
let importVc = UIDocumentPickerViewController(forOpeningContentTypes: [UTType(filenameExtension: "p12")!], asCopy: false)
|
||||||
ImportExport.documentPickerHandler = DocumentPickerHandler { url in
|
ImportExport.documentPickerHandler = DocumentPickerHandler { url in
|
||||||
|
_ = url?.startAccessingSecurityScopedResource()
|
||||||
|
defer { url?.stopAccessingSecurityScopedResource() }
|
||||||
c.resume(returning: url)
|
c.resume(returning: url)
|
||||||
}
|
}
|
||||||
importVc.delegate = ImportExport.documentPickerHandler
|
importVc.delegate = ImportExport.documentPickerHandler
|
||||||
|
|||||||
@@ -14,6 +14,5 @@ struct ImportedAccount: Codable {
|
|||||||
let cert: Data
|
let cert: Data
|
||||||
let certpass: String
|
let certpass: String
|
||||||
let local_user: String
|
let local_user: String
|
||||||
let serial: String
|
|
||||||
let adiPB: String
|
let adiPB: String
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,9 +80,6 @@ public class Keychain
|
|||||||
@KeychainItem(key: "identifier")
|
@KeychainItem(key: "identifier")
|
||||||
public var identifier: String?
|
public var identifier: String?
|
||||||
|
|
||||||
@KeychainItem(key: "adiSerial")
|
|
||||||
public var adiSerial: String?
|
|
||||||
|
|
||||||
@KeychainItem(key: "adiPb")
|
@KeychainItem(key: "adiPb")
|
||||||
public var adiPb: String?
|
public var adiPb: String?
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user