diff --git a/apps/ios/Shared/SimpleXApp.swift b/apps/ios/Shared/SimpleXApp.swift index c023f375d3..f72ffcaaaf 100644 --- a/apps/ios/Shared/SimpleXApp.swift +++ b/apps/ios/Shared/SimpleXApp.swift @@ -21,10 +21,10 @@ struct SimpleXApp: App { @State private var enteredBackgroundAuthenticated: TimeInterval? = nil init() { -// DispatchQueue.global(qos: .background).sync { - haskell_init() + DispatchQueue.global(qos: .background).sync { + haskell_init() // hs_init(0, nil) -// } + } UserDefaults.standard.register(defaults: appDefaults) setGroupDefaults() registerGroupDefaults() diff --git a/apps/ios/SimpleX NSE/NotificationService.swift b/apps/ios/SimpleX NSE/NotificationService.swift index c286ee1c3c..f9b4852e53 100644 --- a/apps/ios/SimpleX NSE/NotificationService.swift +++ b/apps/ios/SimpleX NSE/NotificationService.swift @@ -442,7 +442,7 @@ func startChat() -> DBMigrationResult? { func doStartChat() -> DBMigrationResult? { logger.debug("NotificationService: doStartChat") hs_init(0, nil) - let (_, dbStatus) = chatMigrateInit(confirmMigrations: defaultMigrationConfirmation()) + let (_, dbStatus) = chatMigrateInit(confirmMigrations: defaultMigrationConfirmation(), backgroundMode: true) if dbStatus != .ok { resetChatCtrl() NSEChatState.shared.set(.created) diff --git a/apps/ios/SimpleXChat/API.swift b/apps/ios/SimpleXChat/API.swift index dfa4caf099..8d05a066e8 100644 --- a/apps/ios/SimpleXChat/API.swift +++ b/apps/ios/SimpleXChat/API.swift @@ -17,7 +17,7 @@ public func getChatCtrl(_ useKey: String? = nil) -> chat_ctrl { fatalError("chat controller not initialized") } -public func chatMigrateInit(_ useKey: String? = nil, confirmMigrations: MigrationConfirmation? = nil) -> (Bool, DBMigrationResult) { +public func chatMigrateInit(_ useKey: String? = nil, confirmMigrations: MigrationConfirmation? = nil, backgroundMode: Bool = false) -> (Bool, DBMigrationResult) { if let res = migrationResult { return res } let dbPath = getAppDatabasePath().path var dbKey = "" @@ -41,7 +41,7 @@ public func chatMigrateInit(_ useKey: String? = nil, confirmMigrations: Migratio var cKey = dbKey.cString(using: .utf8)! var cConfirm = confirm.rawValue.cString(using: .utf8)! // the last parameter of chat_migrate_init is used to return the pointer to chat controller - let cjson = chat_migrate_init_key(&cPath, &cKey, 1, &cConfirm, &chatController)! + let cjson = chat_migrate_init_key(&cPath, &cKey, 1, &cConfirm, backgroundMode ? 1 : 0, &chatController)! let dbRes = dbMigrationResult(fromCString(cjson)) let encrypted = dbKey != "" let keychainErr = dbRes == .ok && useKeychain && encrypted && !kcDatabasePassword.set(dbKey) diff --git a/apps/ios/SimpleXChat/SimpleX.h b/apps/ios/SimpleXChat/SimpleX.h index 909d76a76c..c49d104514 100644 --- a/apps/ios/SimpleXChat/SimpleX.h +++ b/apps/ios/SimpleXChat/SimpleX.h @@ -16,7 +16,7 @@ extern void hs_init(int argc, char **argv[]); typedef void* chat_ctrl; // the last parameter is used to return the pointer to chat controller -extern char *chat_migrate_init_key(char *path, char *key, int keepKey, char *confirm, chat_ctrl *ctrl); +extern char *chat_migrate_init_key(char *path, char *key, int keepKey, char *confirm, int backgroundMode, chat_ctrl *ctrl); extern char *chat_close_store(chat_ctrl ctl); extern char *chat_reopen_store(chat_ctrl ctl); extern char *chat_send_cmd(chat_ctrl ctl, char *cmd); diff --git a/cabal.project b/cabal.project index 1ff8aacd77..ca967b458d 100644 --- a/cabal.project +++ b/cabal.project @@ -14,7 +14,7 @@ constraints: zip +disable-bzip2 +disable-zstd source-repository-package type: git location: https://github.com/simplex-chat/simplexmq.git - tag: 13a60d1d3944aa175311563e661161e759b92563 + tag: 9ea9b2c7356a9b42be8ab685c343076ff3c452fe source-repository-package type: git diff --git a/scripts/nix/sha256map.nix b/scripts/nix/sha256map.nix index 595d40c4e7..626d2d8515 100644 --- a/scripts/nix/sha256map.nix +++ b/scripts/nix/sha256map.nix @@ -1,5 +1,5 @@ { - "https://github.com/simplex-chat/simplexmq.git"."13a60d1d3944aa175311563e661161e759b92563" = "08mvqrbjfnq7c6mhkj4hhy4cxn0cj21n49lqzh67ani71g2g1xwa"; + "https://github.com/simplex-chat/simplexmq.git"."9ea9b2c7356a9b42be8ab685c343076ff3c452fe" = "16jgsh5wnf8q56hlsdpa5xf2qhlrv80j8088xys0sbwfa4br2nk8"; "https://github.com/simplex-chat/hs-socks.git"."a30cc7a79a08d8108316094f8f2f82a0c5e1ac51" = "0yasvnr7g91k76mjkamvzab2kvlb1g5pspjyjn2fr6v83swjhj38"; "https://github.com/simplex-chat/direct-sqlcipher.git"."f814ee68b16a9447fbb467ccc8f29bdd3546bfd9" = "1ql13f4kfwkbaq7nygkxgw84213i0zm7c1a8hwvramayxl38dq5d"; "https://github.com/simplex-chat/sqlcipher-simple.git"."a46bd361a19376c5211f1058908fc0ae6bf42446" = "1z0r78d8f0812kxbgsm735qf6xx8lvaz27k1a0b4a2m0sshpd5gl"; diff --git a/src/Simplex/Chat.hs b/src/Simplex/Chat.hs index 8bce204f54..61e32cb3d0 100644 --- a/src/Simplex/Chat.hs +++ b/src/Simplex/Chat.hs @@ -197,79 +197,84 @@ createChatDatabase filePrefix key keepKey confirmMigrations = runExceptT $ do agentStore <- ExceptT $ createAgentStore (agentStoreFile filePrefix) key keepKey confirmMigrations pure ChatDatabase {chatStore, agentStore} -newChatController :: ChatDatabase -> Maybe User -> ChatConfig -> ChatOpts -> IO ChatController -newChatController ChatDatabase {chatStore, agentStore} user cfg@ChatConfig {agentConfig = aCfg, defaultServers, inlineFiles, tempDir, deviceNameForRemote} ChatOpts {coreOptions = CoreChatOpts {smpServers, xftpServers, networkConfig, logLevel, logConnections, logServerHosts, logFile, tbqSize, highlyAvailable}, deviceName, optFilesFolder, showReactions, allowInstantFiles, autoAcceptFileSize} = do - let inlineFiles' = if allowInstantFiles || autoAcceptFileSize > 0 then inlineFiles else inlineFiles {sendChunks = 0, receiveInstant = False} - config = cfg {logLevel, showReactions, tbqSize, subscriptionEvents = logConnections, hostEvents = logServerHosts, defaultServers = configServers, inlineFiles = inlineFiles', autoAcceptFileSize, highlyAvailable} - firstTime = dbNew chatStore - currentUser <- newTVarIO user - currentRemoteHost <- newTVarIO Nothing - servers <- agentServers config - smpAgent <- getSMPAgentClient aCfg {tbqSize} servers agentStore - agentAsync <- newTVarIO Nothing - random <- liftIO C.newRandom - inputQ <- newTBQueueIO tbqSize - outputQ <- newTBQueueIO tbqSize - connNetworkStatuses <- atomically TM.empty - subscriptionMode <- newTVarIO SMSubscribe - chatLock <- newEmptyTMVarIO - sndFiles <- newTVarIO M.empty - rcvFiles <- newTVarIO M.empty - currentCalls <- atomically TM.empty - localDeviceName <- newTVarIO $ fromMaybe deviceNameForRemote deviceName - multicastSubscribers <- newTMVarIO 0 - remoteSessionSeq <- newTVarIO 0 - remoteHostSessions <- atomically TM.empty - remoteHostsFolder <- newTVarIO Nothing - remoteCtrlSession <- newTVarIO Nothing - filesFolder <- newTVarIO optFilesFolder - chatStoreChanged <- newTVarIO False - expireCIThreads <- newTVarIO M.empty - expireCIFlags <- newTVarIO M.empty - cleanupManagerAsync <- newTVarIO Nothing - timedItemThreads <- atomically TM.empty - showLiveItems <- newTVarIO False - encryptLocalFiles <- newTVarIO False - userXFTPFileConfig <- newTVarIO $ xftpFileConfig cfg - tempDirectory <- newTVarIO tempDir - contactMergeEnabled <- newTVarIO True - pure - ChatController - { firstTime, - currentUser, - currentRemoteHost, - smpAgent, - agentAsync, - chatStore, - chatStoreChanged, - random, - inputQ, - outputQ, - connNetworkStatuses, - subscriptionMode, - chatLock, - sndFiles, - rcvFiles, - currentCalls, - localDeviceName, - multicastSubscribers, - remoteSessionSeq, - remoteHostSessions, - remoteHostsFolder, - remoteCtrlSession, - config, - filesFolder, - expireCIThreads, - expireCIFlags, - cleanupManagerAsync, - timedItemThreads, - showLiveItems, - encryptLocalFiles, - userXFTPFileConfig, - tempDirectory, - logFilePath = logFile, - contactMergeEnabled - } +newChatController :: ChatDatabase -> Maybe User -> ChatConfig -> ChatOpts -> Bool -> IO ChatController +newChatController + ChatDatabase {chatStore, agentStore} + user + cfg@ChatConfig {agentConfig = aCfg, defaultServers, inlineFiles, tempDir, deviceNameForRemote} + ChatOpts {coreOptions = CoreChatOpts {smpServers, xftpServers, networkConfig, logLevel, logConnections, logServerHosts, logFile, tbqSize, highlyAvailable}, deviceName, optFilesFolder, showReactions, allowInstantFiles, autoAcceptFileSize} + backgroundMode = do + let inlineFiles' = if allowInstantFiles || autoAcceptFileSize > 0 then inlineFiles else inlineFiles {sendChunks = 0, receiveInstant = False} + config = cfg {logLevel, showReactions, tbqSize, subscriptionEvents = logConnections, hostEvents = logServerHosts, defaultServers = configServers, inlineFiles = inlineFiles', autoAcceptFileSize, highlyAvailable} + firstTime = dbNew chatStore + currentUser <- newTVarIO user + currentRemoteHost <- newTVarIO Nothing + servers <- agentServers config + smpAgent <- getSMPAgentClient aCfg {tbqSize} servers agentStore backgroundMode + agentAsync <- newTVarIO Nothing + random <- liftIO C.newRandom + inputQ <- newTBQueueIO tbqSize + outputQ <- newTBQueueIO tbqSize + connNetworkStatuses <- atomically TM.empty + subscriptionMode <- newTVarIO SMSubscribe + chatLock <- newEmptyTMVarIO + sndFiles <- newTVarIO M.empty + rcvFiles <- newTVarIO M.empty + currentCalls <- atomically TM.empty + localDeviceName <- newTVarIO $ fromMaybe deviceNameForRemote deviceName + multicastSubscribers <- newTMVarIO 0 + remoteSessionSeq <- newTVarIO 0 + remoteHostSessions <- atomically TM.empty + remoteHostsFolder <- newTVarIO Nothing + remoteCtrlSession <- newTVarIO Nothing + filesFolder <- newTVarIO optFilesFolder + chatStoreChanged <- newTVarIO False + expireCIThreads <- newTVarIO M.empty + expireCIFlags <- newTVarIO M.empty + cleanupManagerAsync <- newTVarIO Nothing + timedItemThreads <- atomically TM.empty + showLiveItems <- newTVarIO False + encryptLocalFiles <- newTVarIO False + userXFTPFileConfig <- newTVarIO $ xftpFileConfig cfg + tempDirectory <- newTVarIO tempDir + contactMergeEnabled <- newTVarIO True + pure + ChatController + { firstTime, + currentUser, + currentRemoteHost, + smpAgent, + agentAsync, + chatStore, + chatStoreChanged, + random, + inputQ, + outputQ, + connNetworkStatuses, + subscriptionMode, + chatLock, + sndFiles, + rcvFiles, + currentCalls, + localDeviceName, + multicastSubscribers, + remoteSessionSeq, + remoteHostSessions, + remoteHostsFolder, + remoteCtrlSession, + config, + filesFolder, + expireCIThreads, + expireCIFlags, + cleanupManagerAsync, + timedItemThreads, + showLiveItems, + encryptLocalFiles, + userXFTPFileConfig, + tempDirectory, + logFilePath = logFile, + contactMergeEnabled + } where configServers :: DefaultAgentServers configServers = diff --git a/src/Simplex/Chat/Core.hs b/src/Simplex/Chat/Core.hs index c409526a0b..1d870bf381 100644 --- a/src/Simplex/Chat/Core.hs +++ b/src/Simplex/Chat/Core.hs @@ -28,7 +28,7 @@ simplexChatCore cfg@ChatConfig {confirmMigrations, testView} opts@ChatOpts {core exitFailure run db@ChatDatabase {chatStore} = do u <- getCreateActiveUser chatStore testView - cc <- newChatController db (Just u) cfg opts + cc <- newChatController db (Just u) cfg opts False runSimplexChat opts u cc chat runSimplexChat :: ChatOpts -> User -> ChatController -> (User -> ChatController -> IO ()) -> IO () diff --git a/src/Simplex/Chat/Mobile.hs b/src/Simplex/Chat/Mobile.hs index 6540352a3d..d7f2e5a43c 100644 --- a/src/Simplex/Chat/Mobile.hs +++ b/src/Simplex/Chat/Mobile.hs @@ -72,7 +72,7 @@ $(JQ.deriveToJSON defaultJSON ''APIResponse) foreign export ccall "chat_migrate_init" cChatMigrateInit :: CString -> CString -> CString -> Ptr (StablePtr ChatController) -> IO CJSONString -foreign export ccall "chat_migrate_init_key" cChatMigrateInitKey :: CString -> CString -> CInt -> CString -> Ptr (StablePtr ChatController) -> IO CJSONString +foreign export ccall "chat_migrate_init_key" cChatMigrateInitKey :: CString -> CString -> CInt -> CString -> CInt -> Ptr (StablePtr ChatController) -> IO CJSONString foreign export ccall "chat_close_store" cChatCloseStore :: StablePtr ChatController -> IO CString @@ -108,10 +108,10 @@ foreign export ccall "chat_decrypt_file" cChatDecryptFile :: CString -> CString -- | check / migrate database and initialize chat controller on success cChatMigrateInit :: CString -> CString -> CString -> Ptr (StablePtr ChatController) -> IO CJSONString -cChatMigrateInit fp key = cChatMigrateInitKey fp key 0 +cChatMigrateInit fp key conf = cChatMigrateInitKey fp key 0 conf 0 -cChatMigrateInitKey :: CString -> CString -> CInt -> CString -> Ptr (StablePtr ChatController) -> IO CJSONString -cChatMigrateInitKey fp key keepKey conf ctrl = do +cChatMigrateInitKey :: CString -> CString -> CInt -> CString -> CInt -> Ptr (StablePtr ChatController) -> IO CJSONString +cChatMigrateInitKey fp key keepKey conf background ctrl = do -- ensure we are set to UTF-8; iOS does not have locale, and will default to -- US-ASCII all the time. setLocaleEncoding utf8 @@ -122,7 +122,7 @@ cChatMigrateInitKey fp key keepKey conf ctrl = do dbKey <- BA.convert <$> B.packCString key confirm <- peekCAString conf r <- - chatMigrateInitKey dbPath dbKey (keepKey /= 0) confirm >>= \case + chatMigrateInitKey dbPath dbKey (keepKey /= 0) confirm (background /= 0) >>= \case Right cc -> (newStablePtr cc >>= poke ctrl) $> DBMOk Left e -> pure e newCStringFromLazyBS $ J.encode r @@ -220,10 +220,10 @@ getActiveUser_ :: SQLiteStore -> IO (Maybe User) getActiveUser_ st = find activeUser <$> withTransaction st getUsers chatMigrateInit :: String -> ScrubbedBytes -> String -> IO (Either DBMigrationResult ChatController) -chatMigrateInit dbFilePrefix dbKey = chatMigrateInitKey dbFilePrefix dbKey False +chatMigrateInit dbFilePrefix dbKey confirm = chatMigrateInitKey dbFilePrefix dbKey False confirm False -chatMigrateInitKey :: String -> ScrubbedBytes -> Bool -> String -> IO (Either DBMigrationResult ChatController) -chatMigrateInitKey dbFilePrefix dbKey keepKey confirm = runExceptT $ do +chatMigrateInitKey :: String -> ScrubbedBytes -> Bool -> String -> Bool -> IO (Either DBMigrationResult ChatController) +chatMigrateInitKey dbFilePrefix dbKey keepKey confirm backgroundMode = runExceptT $ do confirmMigrations <- liftEitherWith (const DBMInvalidConfirmation) $ strDecode $ B.pack confirm chatStore <- migrate createChatStore (chatStoreFile dbFilePrefix) confirmMigrations agentStore <- migrate createAgentStore (agentStoreFile dbFilePrefix) confirmMigrations @@ -231,7 +231,7 @@ chatMigrateInitKey dbFilePrefix dbKey keepKey confirm = runExceptT $ do where initialize st db = do user_ <- getActiveUser_ st - newChatController db user_ defaultMobileConfig (mobileChatOpts dbFilePrefix) + newChatController db user_ defaultMobileConfig (mobileChatOpts dbFilePrefix) backgroundMode migrate createStore dbFile confirmMigrations = ExceptT $ (first (DBMErrorMigration dbFile) <$> createStore dbFile dbKey keepKey confirmMigrations) diff --git a/tests/ChatClient.hs b/tests/ChatClient.hs index c32d8002b9..044d95eaaf 100644 --- a/tests/ChatClient.hs +++ b/tests/ChatClient.hs @@ -175,7 +175,7 @@ startTestChat_ :: ChatDatabase -> ChatConfig -> ChatOpts -> User -> IO TestCC startTestChat_ db cfg opts user = do t <- withVirtualTerminal termSettings pure ct <- newChatTerminal t opts - cc <- newChatController db (Just user) cfg opts + cc <- newChatController db (Just user) cfg opts False chatAsync <- async . runSimplexChat opts user cc $ \_u cc' -> runChatTerminal ct cc' opts atomically . unless (maintenance opts) $ readTVar (agentAsync cc) >>= \a -> when (isNothing a) retry termQ <- newTQueueIO