diff --git a/Privitty.framework/Privitty b/Privitty.framework/Privitty index e73904752..c96320030 100644 Binary files a/Privitty.framework/Privitty and b/Privitty.framework/Privitty differ diff --git a/Privitty.framework/_CodeSignature/CodeDirectory b/Privitty.framework/_CodeSignature/CodeDirectory index ee1433bfd..136c429bc 100644 Binary files a/Privitty.framework/_CodeSignature/CodeDirectory and b/Privitty.framework/_CodeSignature/CodeDirectory differ diff --git a/Privitty.framework/_CodeSignature/CodeRequirements-1 b/Privitty.framework/_CodeSignature/CodeRequirements-1 index dec6abb8a..c23cfb830 100644 Binary files a/Privitty.framework/_CodeSignature/CodeRequirements-1 and b/Privitty.framework/_CodeSignature/CodeRequirements-1 differ diff --git a/Privitty.framework/_CodeSignature/CodeSignature b/Privitty.framework/_CodeSignature/CodeSignature index 1907fcfe2..9a241270a 100644 Binary files a/Privitty.framework/_CodeSignature/CodeSignature and b/Privitty.framework/_CodeSignature/CodeSignature differ diff --git a/deltachat-ios/AppDelegate.swift b/deltachat-ios/AppDelegate.swift index b5ae80891..2c9e1f1ab 100644 --- a/deltachat-ios/AppDelegate.swift +++ b/deltachat-ios/AppDelegate.swift @@ -76,10 +76,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD self.launchOptions = launchOptions continueDidFinishLaunchingWithOptions() - if !PrvContext.shared.initialize() { - logger.error("APP DELEGATE: Failed to initialize Privitty Core") - } - // Verify Poppins fonts are loaded #if DEBUG logger.info("🔤 Verifying custom fonts...") @@ -189,13 +185,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD registerForNotifications() prepopulateWidget() - // Ensure Privitty user is selected after account is configured + // Initialize Privitty for existing accounts (e.g., after backup restore) let dcContext = dcAccounts.getSelected() - if PrvContext.shared.switchProfile(for: dcContext) { - logger.info("Privitty profile aligned with selected account on app launch") - } else { - logger.error("Failed to align Privitty profile on app launch") - } + PrvContext.shared.initializePrivittyForSelectedAccount(dcContext: dcContext) } launchOptions = nil @@ -603,8 +595,13 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD if dcAccounts.getSelected().isConfigured() { appCoordinator.resetTabBarRootViewControllers() - // Ensure Privitty user is selected after account reload + // Initialize Privitty if not already initialized (e.g., after backup restore) let dcContext = dcAccounts.getSelected() + if !PrvContext.shared.isInitialized() { + PrvContext.shared.initializePrivittyForSelectedAccount(dcContext: dcContext) + } + + // Ensure Privitty user is selected after account reload if PrvContext.shared.switchProfile(for: dcContext) { logger.info("Privitty profile aligned with selected account on context reload") } else { diff --git a/deltachat-ios/Chat/ChatViewController.swift b/deltachat-ios/Chat/ChatViewController.swift index d993feb4b..f6075ec71 100644 --- a/deltachat-ios/Chat/ChatViewController.swift +++ b/deltachat-ios/Chat/ChatViewController.swift @@ -1473,12 +1473,18 @@ class ChatViewController: UITableViewController, UITableViewDropDelegate { } private func handleAddAttachment() { - // Check if Privitty is initialized + // Try to initialize Privitty if not already initialized + if !PrvContext.shared.isInitialized() { + logger.info("Privitty not initialized, attempting to initialize for current account") + PrvContext.shared.initializePrivittyForSelectedAccount(dcContext: dcContext) + } + + // Check if Privitty is initialized after attempting initialization guard PrvContext.shared.isInitialized() else { - logger.error("Privitty not initialized") + logger.error("Privitty not initialized and initialization failed") let alert = UIAlertController( title: "Error", - message: "Privitty is not initialized", + message: "Privitty is not initialized. Please restart the app.", preferredStyle: .alert ) alert.addAction(UIAlertAction(title: "OK", style: .default)) diff --git a/deltachat-ios/Controller/AccountSetup/Instand Onboarding/InstantOnboardingViewController.swift b/deltachat-ios/Controller/AccountSetup/Instand Onboarding/InstantOnboardingViewController.swift index ba6f978af..9d0ed46c0 100644 --- a/deltachat-ios/Controller/AccountSetup/Instand Onboarding/InstantOnboardingViewController.swift +++ b/deltachat-ios/Controller/AccountSetup/Instand Onboarding/InstantOnboardingViewController.swift @@ -301,27 +301,15 @@ class InstantOnboardingViewController: UIViewController { appDelegate.registerForNotifications() appDelegate.reloadDcContext() appDelegate.prepopulateWidget() + + // Initialize Privitty for the newly created account + let dcContext = self.dcAccounts.getSelected() + PrvContext.shared.initializePrivittyForSelectedAccount(dcContext: dcContext) } } private func storeImageAndName() { dcContext.displayname = contentView?.nameTextField.text - - // Create/switch Privitty user profile. - if let username = contentView?.nameTextField.text, !username.isEmpty { - // Get user email from Delta Chat config (like Android Java code) - let selfEmail = dcContext.getConfig("configured_addr") ?? "" - - logger.info("Creating Privitty user: \(username)") - logger.debug("Email: \(selfEmail)") - - let success = PrvContext.shared.createOrSwitchUser(username: username, useremail: selfEmail, userid: "") - if success { - logger.info("Privitty user created/switched successfully: \(username)") - } else { - logger.error("Failed to create/switch Privitty user: \(username)") - } - } } } diff --git a/deltachat-ios/Controller/AccountSetup/WelcomeViewController.swift b/deltachat-ios/Controller/AccountSetup/WelcomeViewController.swift index 661855068..59d8e2b49 100644 --- a/deltachat-ios/Controller/AccountSetup/WelcomeViewController.swift +++ b/deltachat-ios/Controller/AccountSetup/WelcomeViewController.swift @@ -145,6 +145,12 @@ class WelcomeViewController: UIViewController { private func handleBackupRestoreSuccess() { guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return } + + // Initialize Privitty for the restored account FIRST (before reloadDcContext) + // This ensures Privitty is initialized before any code tries to use it + let dcContext = dcAccounts.getSelected() + PrvContext.shared.initializePrivittyForSelectedAccount(dcContext: dcContext) + appDelegate.registerForNotifications() appDelegate.reloadDcContext() appDelegate.prepopulateWidget() diff --git a/deltachat-ios/Controller/BackupTransferViewController.swift b/deltachat-ios/Controller/BackupTransferViewController.swift index 07e6455b2..a06c3ccde 100644 --- a/deltachat-ios/Controller/BackupTransferViewController.swift +++ b/deltachat-ios/Controller/BackupTransferViewController.swift @@ -165,6 +165,11 @@ class BackupTransferViewController: UIViewController { self.navigationItem.leftBarButtonItem = nil // "Cancel" no longer fits as things are done statusLineText = String.localized("done") + " 😀" hideQrCode = true + + // Initialize Privitty for the restored account (no app restart needed) + // This handles the case when BackupTransferViewController is used for receiving + let dcContext = self.dcAccounts.getSelected() + PrvContext.shared.initializePrivittyForSelectedAccount(dcContext: dcContext) } if let statusLineText = statusLineText { diff --git a/deltachat-ios/DC/DcContext.swift b/deltachat-ios/DC/DcContext.swift index f78e271d4..343eb6c3d 100644 --- a/deltachat-ios/DC/DcContext.swift +++ b/deltachat-ios/DC/DcContext.swift @@ -791,4 +791,12 @@ public class DcContext { setConfig("proxy_url", allProxies) } + + /// Get the blob directory path for this account + public func getBlobdir() -> String? { + guard let cString = dc_get_blobdir(contextPointer) else { return nil } + let swiftString = String(cString: cString) + dc_str_unref(cString) + return swiftString.isEmpty ? nil : swiftString + } } diff --git a/deltachat-ios/DC/PrvContext.swift b/deltachat-ios/DC/PrvContext.swift index 124bc3f74..4413c814c 100644 --- a/deltachat-ios/DC/PrvContext.swift +++ b/deltachat-ios/DC/PrvContext.swift @@ -111,6 +111,106 @@ public class PrvContext { core = nil } + /// Initialize Privitty for the selected account if a Privitty database exists. + /// This is called during app startup to initialize Privitty for existing accounts + /// (e.g., after backup restore). + /// Can also be called after backup restore to initialize Privitty without app restart. + public func initializePrivittyForSelectedAccount(dcContext: DcContext) { + guard dcContext.isOpen() else { + logger.debug("No selected account available for Privitty initialization") + return + } + + // Check if account is configured + guard dcContext.isConfigured() else { + logger.debug("Selected account is not configured, skipping Privitty initialization") + return + } + + // Get account directory path (parent of blobdir) + guard let blobdirPath = dcContext.getBlobdir(), !blobdirPath.isEmpty else { + logger.warning("Blobdir path is null or empty, cannot initialize Privitty") + return + } + + let blobdirURL = URL(fileURLWithPath: blobdirPath) + guard let accountDir = blobdirURL.deletingLastPathComponent().pathComponents.isEmpty ? nil : blobdirURL.deletingLastPathComponent() else { + logger.warning("Account directory does not exist: \(blobdirPath)") + return + } + + let accountDirPath = accountDir.path + let fileManager = FileManager.default + + guard fileManager.fileExists(atPath: accountDirPath) else { + logger.warning("Account directory does not exist: \(accountDirPath)") + return + } + + // Check if .privitty/dbase/ directory exists and has database files + let privittyDbaseDir = accountDir.appendingPathComponent(".privitty").appendingPathComponent("dbase") + let privittyDbasePath = privittyDbaseDir.path + + var hasExistingDatabase = false + if fileManager.fileExists(atPath: privittyDbasePath), + let contents = try? fileManager.contentsOfDirectory(atPath: privittyDbasePath) { + // Look for any prv*.db file + let dbFiles = contents.filter { $0.hasPrefix("prv") && $0.hasSuffix(".db") } + hasExistingDatabase = !dbFiles.isEmpty + } + + if hasExistingDatabase { + logger.info("Found Privitty database, initializing with account path: \(accountDirPath)") + } else { + logger.info("No existing Privitty database found, initializing Privitty for new account at: \(accountDirPath)") + } + + // Shutdown existing core if initialized + if isInitialized() { + shutdown() + } + + guard let newCore = PrivittyCore(baseDirectory: accountDirPath) else { + logger.error("Failed to create PrivittyCore object") + return + } + + let isInit = newCore.isInitialized() + guard isInit else { + logger.error("Core object created but not initialized") + return + } + + core = newCore + + if let versionResponse = newCore.getVersion(), + let success = versionResponse["success"] as? Int, success == 1, + let data = versionResponse["data"] as? [String: Any] { + if let coreVersion = data["core_version"] as? String { + logger.info("Privitty initialized successfully - Version: \(coreVersion)") + } + } else { + logger.warning("Failed to get or parse version response") + } + + // Switch to the account's profile + let userName = dcContext.displayname ?? dcContext.getConfig("configured_addr") ?? "" + let selfEmail = dcContext.getConfig("configured_addr") ?? "" + let selfContact = dcContext.getContact(id: Int(DC_CONTACT_ID_SELF)) + let userId = String(selfContact.id) + + if !userName.isEmpty { + let switched = createOrSwitchUser(username: userName, useremail: selfEmail, userid: userId) + if switched { + logger.info("Privitty profile switched to: \(userName) (email: \(selfEmail))") + } else { + logger.warning("Privitty profile switch failed for: \(userName)") + } + } else { + logger.warning("Username is null or empty, cannot switch Privitty profile") + } + } + // MARK: - Chat Room Management /// Delete a chat room from Privitty database public func deleteChatRoom(chatId: String) -> (success: Bool, message: String?, error: String?) { @@ -166,7 +266,7 @@ public class PrvContext { /// Get all available users public func getAvailableUsers() -> [String] { guard let core = getCore() else { return [] } - guard let users = core.getAvailableUsers() as? [String] else { return [] } + guard let users = core.getAvailableUsers() else { return [] } return users }