cyberta vor 4 Jahren
Ursprung
Commit
4599e43a49

+ 6 - 1
DcCore/DcCore/DC/Wrapper.swift

@@ -78,7 +78,8 @@ public class DcAccounts {
             version += " " + appVersion
         }
 
-        if let sharedDbLocation = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: applicationGroupIdentifier) {
+        if var sharedDbLocation = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: applicationGroupIdentifier) {
+            sharedDbLocation.appendPathComponent("accounts", isDirectory: true)
             accountsPointer = dc_accounts_new("iOS\(version)", sharedDbLocation.path)
         }
     }
@@ -110,6 +111,10 @@ public class DcContext {
         contextPointer = nil
     }
 
+    public var id: Int {
+        return Int(dc_get_id(contextPointer))
+    }
+
     // viewType: one of DC_MSG_*
     public func newMessage(viewType: Int32) -> DcMsg {
         let messagePointer = dc_msg_new(contextPointer, viewType)

+ 30 - 5
DcCore/DcCore/DC/events.swift

@@ -13,16 +13,17 @@ public let dcEphemeralTimerModified =  Notification.Name(rawValue: "dcEphemeralT
 public let dcMsgsNoticed = Notification.Name(rawValue: "dcMsgsNoticed")
 
 public class DcEventHandler {
-    let dcContext: DcContext
+    let dcAccounts: DcAccounts
 
-    public init(dcContext: DcContext) {
-        self.dcContext = dcContext
+    public init(dcAccounts: DcAccounts) {
+        self.dcAccounts = dcAccounts
     }
 
     public func handleEvent(event: DcEvent) {
         let id = event.id
         let data1 = event.data1Int
         let data2 = event.data2Int
+        let dcContext = dcAccounts.getAccount(id: event.accountId)
 
         if id >= DC_EVENT_ERROR && id <= 499 {
             let s = event.data2String
@@ -68,7 +69,7 @@ public class DcEventHandler {
 
         case DC_EVENT_IMEX_PROGRESS:
             let nc = NotificationCenter.default
-            DispatchQueue.main.async { [weak self] in
+            DispatchQueue.main.async {
                 nc.post(
                     name: dcNotificationImexProgress,
                     object: nil,
@@ -76,7 +77,7 @@ public class DcEventHandler {
                         "progress": Int(data1),
                         "error": Int(data1) == 0,
                         "done": Int(data1) == 1000,
-                        "errorMessage": self?.dcContext.lastErrorString as Any,
+                        "errorMessage": dcContext.lastErrorString as Any,
                     ]
                 )
             }
@@ -85,6 +86,9 @@ public class DcEventHandler {
             dcContext.logger?.warning("network: \(event.data2String)")
 
         case DC_EVENT_MSGS_CHANGED, DC_EVENT_MSG_READ, DC_EVENT_MSG_DELIVERED, DC_EVENT_MSG_FAILED:
+            if dcContext.id != dcAccounts.get().id {
+                return
+            }
             dcContext.logger?.info("change: \(id)")
 
             let nc = NotificationCenter.default
@@ -102,6 +106,9 @@ public class DcEventHandler {
             }
 
         case DC_EVENT_MSGS_NOTICED:
+            if dcContext.id != dcAccounts.get().id {
+                return
+            }
             let nc = NotificationCenter.default
             DispatchQueue.main.async {
                 nc.post(
@@ -114,6 +121,9 @@ public class DcEventHandler {
             }
 
         case DC_EVENT_CHAT_MODIFIED:
+            if dcContext.id != dcAccounts.get().id {
+                return
+            }
             dcContext.logger?.info("chat modified: \(id)")
             let nc = NotificationCenter.default
             DispatchQueue.main.async {
@@ -126,6 +136,9 @@ public class DcEventHandler {
                 )
             }
         case DC_EVENT_CHAT_EPHEMERAL_TIMER_MODIFIED:
+            if dcContext.id != dcAccounts.get().id {
+                return
+            }
             dcContext.logger?.info("chat ephemeral timer modified: \(id)")
             let nc = NotificationCenter.default
             DispatchQueue.main.async {
@@ -137,6 +150,9 @@ public class DcEventHandler {
             }
 
         case DC_EVENT_INCOMING_MSG:
+            if dcContext.id != dcAccounts.get().id {
+                return
+            }
             let nc = NotificationCenter.default
             let userInfo = [
                 "message_id": Int(data2),
@@ -157,6 +173,9 @@ public class DcEventHandler {
             dcContext.logger?.info("message delivered: \(data1)-\(data2)")
 
         case DC_EVENT_SECUREJOIN_INVITER_PROGRESS:
+            if dcContext.id != dcAccounts.get().id {
+                return
+            }
             dcContext.logger?.info("securejoin inviter progress \(data1)")
 
             let nc = NotificationCenter.default
@@ -173,6 +192,9 @@ public class DcEventHandler {
             }
 
         case DC_EVENT_SECUREJOIN_JOINER_PROGRESS:
+            if dcContext.id != dcAccounts.get().id {
+                return
+            }
             dcContext.logger?.info("securejoin joiner progress \(data1)")
             let nc = NotificationCenter.default
             DispatchQueue.main.async {
@@ -188,6 +210,9 @@ public class DcEventHandler {
                 )
             }
         case DC_EVENT_CONTACTS_CHANGED:
+            if dcContext.id != dcAccounts.get().id {
+                return
+            }
             dcContext.logger?.info("contact changed: \(data1)")
             let nc = NotificationCenter.default
             DispatchQueue.main.async {

+ 8 - 8
DcCore/DcCore/Helper/DatabaseHelper.swift

@@ -49,10 +49,10 @@ public class DatabaseHelper {
         return sharedDbBlobsDir
     }
 
-    var dcContext: DcContext
+    var dcLogger: Logger?
 
-    public init(dcContext: DcContext) {
-        self.dcContext = dcContext
+    public init(dcLogger: Logger?) {
+        self.dcLogger = dcLogger
     }
 
     func clearDbBlobsDir(at path: String) {
@@ -67,7 +67,7 @@ public class DatabaseHelper {
                 try fileManager.removeItem(atPath: path)
             }
         } catch {
-            dcContext.logger?.error("Could not clean shared blobs dir, it might be it didn't exist")
+            dcLogger?.error("Could not clean shared blobs dir, it might be it didn't exist")
         }
     }
 
@@ -77,12 +77,12 @@ public class DatabaseHelper {
             do {
                 try filemanager.removeItem(atPath: path)
             } catch {
-                dcContext.logger?.error("Failed to delete db: \(error)")
+                dcLogger?.error("Failed to delete db: \(error)")
             }
         }
     }
 
-    public func clearAccountData() {
+    public func clearUnmanagedAccountData() {
         clearDb(at: currentDatabaseLocation)
         clearDbBlobsDir(at: currentBlobsDirLocation)
     }
@@ -94,7 +94,7 @@ public class DatabaseHelper {
                 clearDbBlobsDir(at: sharedDbBlobsDir)
                 try filemanager.moveItem(at: URL(fileURLWithPath: localDbBlobsDir), to: URL(fileURLWithPath: sharedDbBlobsDir))
             } catch let error {
-                dcContext.logger?.error("Could not move db blobs directory to shared space: \(error.localizedDescription)")
+                dcLogger?.error("Could not move db blobs directory to shared space: \(error.localizedDescription)")
             }
         }
     }
@@ -107,7 +107,7 @@ public class DatabaseHelper {
               try filemanager.moveItem(at: URL(fileURLWithPath: localDbFile), to: URL(fileURLWithPath: sharedDbFile))
               moveBlobsFolder()
           } catch let error {
-              dcContext.logger?.error("Could not update DB location. Share extension will probably not work. \n\(error.localizedDescription)")
+              dcLogger?.error("Could not update DB location. Share extension will probably not work. \n\(error.localizedDescription)")
               return localDbFile
           }
       }

+ 47 - 23
deltachat-ios/AppDelegate.swift

@@ -11,7 +11,7 @@ let logger = SwiftyBeaver.self
 
 @UIApplicationMain
 class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {
-    private let dcContext = DcContext()
+    private let dcAccounts = DcAccounts()
     var appCoordinator: AppCoordinator!
     var relayHelper: RelayHelper!
     var locationManager: LocationManager!
@@ -55,7 +55,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
         let console = ConsoleDestination()
         console.format = "$DHH:mm:ss.SSS$d $C$L$c $M" // see https://docs.swiftybeaver.com/article/20-custom-format
         logger.addDestination(console)
-        dcContext.logger = DcLogger()
+
+        dcAccounts.openDatabase()
+        migrateToDcAccounts()
+        dcAccounts.get().logger = DcLogger()
         logger.info("➡️ didFinishLaunchingWithOptions")
 
         window = UIWindow(frame: UIScreen.main.bounds)
@@ -68,14 +71,13 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
             window.backgroundColor = UIColor.white
         }
 
-        openDatabase()
         installEventHandler()
-        RelayHelper.setup(dcContext)
-        appCoordinator = AppCoordinator(window: window, dcContext: dcContext)
-        locationManager = LocationManager(context: dcContext)
+        RelayHelper.setup(dcAccounts)
+        appCoordinator = AppCoordinator(window: window, dcAccounts: dcAccounts)
+        locationManager = LocationManager(dcAccounts: dcAccounts)
         UIApplication.shared.setMinimumBackgroundFetchInterval(UIApplication.backgroundFetchIntervalMinimum)
-        notificationManager = NotificationManager(dcContext: dcContext)
-        dcContext.maybeStartIo()
+        notificationManager = NotificationManager(dcAccounts: dcAccounts)
+        dcAccounts.maybeStartIo()
         setStockTranslations()
 
         reachability.whenReachable = { reachability in
@@ -83,7 +85,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
             // Reachability::reachabilityChanged uses DispatchQueue.main.async only
             logger.info("network: reachable", reachability.connection.description)
             DispatchQueue.global(qos: .background).async { [weak self] in
-                self?.dcContext.maybeNetwork()
+                self?.dcAccounts.maybeNetwork()
             }
         }
 
@@ -102,7 +104,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
             increaseDebugCounter("notify-remote-launch")
         }
 
-        if dcContext.isConfigured() && !UserDefaults.standard.bool(forKey: "notifications_disabled") {
+        if dcAccounts.get().isConfigured() && !UserDefaults.standard.bool(forKey: "notifications_disabled") {
             registerForNotifications()
         }
 
@@ -138,12 +140,12 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
 
     func applicationWillEnterForeground(_: UIApplication) {
         logger.info("➡️ applicationWillEnterForeground")
-        dcContext.maybeStartIo()
+        dcAccounts.maybeStartIo()
 
         DispatchQueue.global(qos: .background).async { [weak self] in
             guard let self = self else { return }
             if self.reachability.connection != .none {
-                self.dcContext.maybeNetwork()
+                self.dcAccounts.maybeNetwork()
             }
 
             if let userDefaults = UserDefaults.shared, userDefaults.bool(forKey: UserDefaults.hasExtensionAttemptedToSend) {
@@ -206,7 +208,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
                 self.unregisterBackgroundTask()
             } else if app.backgroundTimeRemaining < 10 {
                 logger.info("⬅️ few background time, \(app.backgroundTimeRemaining), stopping")
-                self.dcContext.stopIo()
+                self.dcAccounts.stopIo()
 
                 // to avoid 0xdead10cc exceptions, scheduled jobs need to be done before we get suspended;
                 // we increase the probabilty that this happens by waiting a moment before calling unregisterBackgroundTask()
@@ -381,7 +383,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
         backgroundTask = UIApplication.shared.beginBackgroundTask { [weak self] in
             // usually, this handler is not used as we are taking care of timings below.
             logger.info("⬅️ finishing fetch by system urgency requests")
-            self?.dcContext.stopIo()
+            self?.dcAccounts.get().stopIo()
             completionHandler(.newData)
             if backgroundTask != .invalid {
                 UIApplication.shared.endBackgroundTask(backgroundTask)
@@ -390,8 +392,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
         }
 
         // we're in background, run IO for a little time
-        dcContext.maybeStartIo()
-        dcContext.maybeNetwork()
+        dcAccounts.maybeStartIo()
+        dcAccounts.maybeNetwork()
 
         DispatchQueue.main.asyncAfter(deadline: .now() + 10) { [weak self] in
             logger.info("⬅️ finishing fetch")
@@ -401,7 +403,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
                 return
             }
             if !self.appIsInForeground() {
-                self.dcContext.stopIo()
+                self.dcAccounts.stopIo()
             }
 
             // to avoid 0xdead10cc exceptions, scheduled jobs need to be done before we get suspended;
@@ -445,24 +447,45 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
 
     // MARK: - misc.
 
-    func openDatabase() {
-        guard let databaseLocation = DatabaseHelper(dcContext: dcContext).updateDatabaseLocation() else {
+    func migrateToDcAccounts() {
+        let dcContext = DcContext()
+
+        // first database migration to shared container, can be removed at some point
+        // implemented in April 2020 (https://github.com/deltachat/deltachat-ios/pull/612)
+        let databaseHelper = DatabaseHelper(dcLogger: dcContext.logger)
+        guard let databaseLocation = databaseHelper.updateDatabaseLocation() else {
             fatalError("Database could not be opened")
         }
-        logger.info("open: \(databaseLocation)")
         dcContext.openDatabase(dbFile: databaseLocation)
+        if dcContext.isConfigured() {
+            dcContext.closeDatabase()
+            if dcAccounts.migrateAccount(dbLocation: databaseLocation) == 0 {
+                fatalError("Account could not be migrated")
+                // TODO: show error message in UI
+            }
+            databaseHelper.clearUnmanagedAccountData()
+        }
     }
 
+
+    func openDatabase() {
+        dcAccounts.openDatabase()
+    }
+    
     func closeDatabase() {
-        dcContext.closeDatabase()
+        dcAccounts.closeDatabase()
+    }
+
+    func deleteCurrentAccount() {
+        let _ = dcAccounts.removeAccount(id: dcAccounts.get().id)
     }
 
     func installEventHandler() {
 
         DispatchQueue.global(qos: .background).async { [weak self] in
             guard let self = self else { return }
-            let eventHandler = DcEventHandler(dcContext: self.dcContext)
-            let eventEmitter = self.dcContext.getEventEmitter()
+            let eventHandler = DcEventHandler(dcAccounts: self.dcAccounts)
+            let eventEmitter = self.dcAccounts.getEventEmitter()
             while true {
                 guard let event = eventEmitter.getNextEvent() else { break }
                 eventHandler.handleEvent(event: event)
@@ -488,6 +511,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
     }
 
     private func setStockTranslations() {
+        let dcContext = dcAccounts.get()
         dcContext.setStockTranslation(id: DC_STR_NOMESSAGES, localizationKey: "chat_no_messages")
         dcContext.setStockTranslation(id: DC_STR_SELF, localizationKey: "self")
         dcContext.setStockTranslation(id: DC_STR_DRAFT, localizationKey: "draft")

+ 2 - 1
deltachat-ios/Controller/AccountSetupController.swift

@@ -773,9 +773,10 @@ class AccountSetupController: UITableViewController, ProgressAlertHandler {
 
         alert.addAction(UIAlertAction(title: String.localized("delete_account"), style: .destructive, handler: { _ in
             self.dcContext.stopIo()
+            appDelegate.deleteCurrentAccount()
             appDelegate.closeDatabase()
-            DatabaseHelper(dcContext: self.dcContext).clearAccountData()
             appDelegate.openDatabase()
+            // FIXME: reinitialize UIs with new DcContext
             appDelegate.installEventHandler()
             self.dcContext.maybeStartIo()
             appDelegate.appCoordinator.presentWelcomeController()

+ 13 - 9
deltachat-ios/Coordinator/AppCoordinator.swift

@@ -7,7 +7,7 @@ import DcCore
 class AppCoordinator {
 
     private let window: UIWindow
-    private let dcContext: DcContext
+    private let dcAccounts: DcAccounts
     private let qrTab = 0
     public  let chatsTab = 1
     private let settingsTab = 2
@@ -30,7 +30,7 @@ class AppCoordinator {
     }()
 
     private lazy var qrNavController: UINavigationController = {
-        let root = QrPageController(dcContext: dcContext)
+        let root = QrPageController(dcContext: dcAccounts.get())
         let nav = UINavigationController(rootViewController: root)
         let settingsImage = UIImage(named: "qr_code")
         nav.tabBarItem = UITabBarItem(title: String.localized("qr_code"), image: settingsImage, tag: qrTab)
@@ -38,8 +38,8 @@ class AppCoordinator {
     }()
 
     private lazy var chatsNavController: UINavigationController = {
-        let viewModel = ChatListViewModel(dcContext: dcContext, isArchive: false)
-        let root = ChatListController(dcContext: dcContext, viewModel: viewModel)
+        let viewModel = ChatListViewModel(dcContext: dcAccounts.get(), isArchive: false)
+        let root = ChatListController(dcContext: dcAccounts.get(), viewModel: viewModel)
         let nav = UINavigationController(rootViewController: root)
         let settingsImage = UIImage(named: "ic_chat")
         nav.tabBarItem = UITabBarItem(title: String.localized("pref_chats"), image: settingsImage, tag: chatsTab)
@@ -47,7 +47,7 @@ class AppCoordinator {
     }()
 
     private lazy var settingsNavController: UINavigationController = {
-        let root = SettingsViewController(dcContext: dcContext)
+        let root = SettingsViewController(dcContext: dcAccounts.get())
         let nav = UINavigationController(rootViewController: root)
         let settingsImage = UIImage(named: "settings")
         nav.tabBarItem = UITabBarItem(title: String.localized("menu_settings"), image: settingsImage, tag: settingsTab)
@@ -55,9 +55,10 @@ class AppCoordinator {
     }()
 
     // MARK: - misc
-    init(window: UIWindow, dcContext: DcContext) {
+    init(window: UIWindow, dcAccounts: DcAccounts) {
         self.window = window
-        self.dcContext = dcContext
+        self.dcAccounts = dcAccounts
+        let dcContext = dcAccounts.get()
 
         if dcContext.isConfigured() {
             presentTabBarController()
@@ -104,14 +105,17 @@ class AppCoordinator {
     }
 
     func presentWelcomeController() {
-        loginNavController.setViewControllers([WelcomeViewController(dcContext: dcContext)], animated: true)
+        if dcAccounts.get().isConfigured() {
+            let _ = dcAccounts.addAccount()
+        }
+        loginNavController.setViewControllers([WelcomeViewController(dcContext: dcAccounts.get())], animated: true)
         window.rootViewController = loginNavController
         window.makeKeyAndVisible()
 
         // the applicationIconBadgeNumber is remembered by the system even on reinstalls (just tested on ios 13.3.1),
         // to avoid appearing an old number of a previous installation, we reset the counter manually.
         // but even when this changes in ios, we need the reset as we allow account-deletion also in-app.
-        NotificationManager.updateApplicationIconBadge(dcContext: dcContext, reset: true)
+        NotificationManager.updateApplicationIconBadge(dcContext: dcAccounts.get(), reset: true)
     }
 
     func presentTabBarController() {

+ 5 - 3
deltachat-ios/Helper/LocationManager.swift

@@ -5,13 +5,15 @@ import DcCore
 class LocationManager: NSObject, CLLocationManagerDelegate {
 
     let locationManager: CLLocationManager
-    let dcContext: DcContext
+    let dcAccounts: DcAccounts
+    var dcContext: DcContext
     var lastLocation: CLLocation?
     var chatIdLocationRequest: Int?
     var durationLocationRequest: Int?
 
-    init(context: DcContext) {
-        dcContext = context
+    init(dcAccounts: DcAccounts) {
+        self.dcAccounts = dcAccounts
+        self.dcContext = dcAccounts.get()
         locationManager = CLLocationManager()
         locationManager.distanceFilter = 25
         locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters

+ 9 - 2
deltachat-ios/Helper/NotificationManager.swift

@@ -8,14 +8,21 @@ public class NotificationManager {
     var incomingMsgObserver: NSObjectProtocol?
     var msgsNoticedObserver: NSObjectProtocol?
 
+    private let dcAccounts: DcAccounts
     private var dcContext: DcContext
 
-    init(dcContext: DcContext) {
-        self.dcContext = dcContext
+    init(dcAccounts: DcAccounts) {
+        self.dcAccounts = dcAccounts
+        self.dcContext = dcAccounts.get()
         initIncomingMsgsObserver()
         initMsgsNoticedObserver()
     }
 
+    public func switchContext() {
+        NotificationManager.removeAllNotifications()
+        dcContext = dcAccounts.get()
+    }
+
     public static func updateApplicationIconBadge(dcContext: DcContext, reset: Bool) {
         var unreadMessages = 0
         if !reset {

+ 5 - 5
deltachat-ios/Helper/RelayHelper.swift

@@ -3,17 +3,17 @@ import DcCore
 
 class RelayHelper {
     static var sharedInstance: RelayHelper = RelayHelper()
-    private static var dcContext: DcContext?
+    private static var dcAccounts: DcAccounts?
     var messageIds: [Int]?
 
     private init() {
-        guard RelayHelper.dcContext != nil else {
+        guard RelayHelper.dcAccounts != nil else {
             fatalError("Error - you must call RelayHelper.setup() before accessing RelayHelper.shared")
         }
     }
 
-    class func setup(_ dcContext: DcContext) {
-        RelayHelper.dcContext = dcContext
+    class func setup(_ dcAccounts: DcAccounts) {
+        RelayHelper.dcAccounts = dcAccounts
     }
 
     func setForwardMessage(messageId: Int) {
@@ -30,7 +30,7 @@ class RelayHelper {
 
     func forward(to chat: Int) {
         if let messageIds = self.messageIds {
-            RelayHelper.dcContext?.forwardMessages(with: messageIds, to: chat)
+            RelayHelper.dcAccounts?.get().forwardMessages(with: messageIds, to: chat)
         }
         self.messageIds = nil
     }