diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt index 619788f6eb..4bb06afd8b 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt @@ -516,8 +516,8 @@ object ChatController { throw Exception("failed to delete the user ${r.responseType} ${r.details}") } - suspend fun apiStartChat(openDBWithKey: String? = null): Boolean { - val r = sendCmd(CC.StartChat(ChatCtrlCfg(subConns = true, enableExpireCIs = true, startXFTPWorkers = true, openDBWithKey = openDBWithKey))) + suspend fun apiStartChat(): Boolean { + val r = sendCmd(CC.StartChat(expire = true)) when (r) { is CR.ChatStarted -> return true is CR.ChatRunning -> return false @@ -525,8 +525,8 @@ object ChatController { } } - suspend fun apiStopChat(closeStore: Boolean): Boolean { - val r = sendCmd(CC.ApiStopChat(closeStore)) + suspend fun apiStopChat(): Boolean { + val r = sendCmd(CC.ApiStopChat()) when (r) { is CR.ChatStopped -> return true else -> throw Error("failed stopping chat: ${r.responseType} ${r.details}") @@ -1834,8 +1834,8 @@ sealed class CC { class ApiMuteUser(val userId: Long): CC() class ApiUnmuteUser(val userId: Long): CC() class ApiDeleteUser(val userId: Long, val delSMPQueues: Boolean, val viewPwd: String?): CC() - class StartChat(val cfg: ChatCtrlCfg): CC() - class ApiStopChat(val closeStore: Boolean): CC() + class StartChat(val expire: Boolean): CC() + class ApiStopChat: CC() class SetTempFolder(val tempFolder: String): CC() class SetFilesFolder(val filesFolder: String): CC() class ApiSetXFTPConfig(val config: XFTPFileConfig?): CC() @@ -1938,9 +1938,8 @@ sealed class CC { is ApiMuteUser -> "/_mute user $userId" is ApiUnmuteUser -> "/_unmute user $userId" is ApiDeleteUser -> "/_delete user $userId del_smp=${onOff(delSMPQueues)}${maybePwd(viewPwd)}" -// is StartChat -> "/_start ${json.encodeToString(cfg)}" // this can be used with the new core - is StartChat -> "/_start subscribe=on expire=${onOff(cfg.enableExpireCIs)} xftp=on" - is ApiStopChat -> if (closeStore) "/_stop close" else "/_stop" + is StartChat -> "/_start subscribe=on expire=${onOff(expire)} xftp=on" + is ApiStopChat -> "/_stop" is SetTempFolder -> "/_temp_folder $tempFolder" is SetFilesFolder -> "/_files_folder $filesFolder" is ApiSetXFTPConfig -> if (config != null) "/_xftp on ${json.encodeToString(config)}" else "/_xftp off" @@ -2157,14 +2156,6 @@ sealed class CC { } } -@Serializable -data class ChatCtrlCfg ( - val subConns: Boolean, - val enableExpireCIs: Boolean, - val startXFTPWorkers: Boolean, - val openDBWithKey: String? -) - @Serializable data class NewUser( val profile: Profile?, diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/database/DatabaseView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/database/DatabaseView.kt index 3eb2e7d734..fa0f8f54d1 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/database/DatabaseView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/database/DatabaseView.kt @@ -419,7 +419,7 @@ private fun stopChat(m: ChatModel) { } suspend fun stopChatAsync(m: ChatModel) { - m.controller.apiStopChat(false) + m.controller.apiStopChat() m.chatRunning.value = false } diff --git a/cabal.project b/cabal.project index d46121a22f..af6f347b9b 100644 --- a/cabal.project +++ b/cabal.project @@ -9,7 +9,7 @@ constraints: zip +disable-bzip2 +disable-zstd source-repository-package type: git location: https://github.com/simplex-chat/simplexmq.git - tag: 459b6d39bf0205e61c8b57f079216b1d8712fead + tag: ee895a14f5cb81ab70726e3a346037c9a45decda source-repository-package type: git diff --git a/scripts/nix/sha256map.nix b/scripts/nix/sha256map.nix index 8284e36817..f7860dc031 100644 --- a/scripts/nix/sha256map.nix +++ b/scripts/nix/sha256map.nix @@ -1,5 +1,5 @@ { - "https://github.com/simplex-chat/simplexmq.git"."459b6d39bf0205e61c8b57f079216b1d8712fead" = "13q7isxcn346qxr50a75fx7plfjzyy78kg8qn29i3s64f09sfc69"; + "https://github.com/simplex-chat/simplexmq.git"."ee895a14f5cb81ab70726e3a346037c9a45decda" = "07w7f4hj7js5w0dhw53gvbsyn6s80gx04qgbyl3bvbfpiprzryh9"; "https://github.com/simplex-chat/hs-socks.git"."a30cc7a79a08d8108316094f8f2f82a0c5e1ac51" = "0yasvnr7g91k76mjkamvzab2kvlb1g5pspjyjn2fr6v83swjhj38"; "https://github.com/kazu-yamamoto/http2.git"."b5a1b7200cf5bc7044af34ba325284271f6dff25" = "0dqb50j57an64nf4qcf5vcz4xkd1vzvghvf8bk529c1k30r9nfzb"; "https://github.com/simplex-chat/direct-sqlcipher.git"."f814ee68b16a9447fbb467ccc8f29bdd3546bfd9" = "0kiwhvml42g9anw4d2v0zd1fpc790pj9syg5x3ik4l97fnkbbwpp"; diff --git a/src/Simplex/Chat.hs b/src/Simplex/Chat.hs index 597f127a19..02ba4ecdb0 100644 --- a/src/Simplex/Chat.hs +++ b/src/Simplex/Chat.hs @@ -34,7 +34,6 @@ import qualified Data.ByteString.Base64 as B64 import Data.ByteString.Char8 (ByteString) import qualified Data.ByteString.Char8 as B import Data.Char (isSpace, toLower) -import Data.Composition ((.:)) import Data.Constraint (Dict (..)) import Data.Either (fromRight, rights) import Data.Fixed (div') @@ -84,7 +83,7 @@ import Simplex.Messaging.Agent.Client (AgentStatsKey (..), SubInfo (..), agentCl import Simplex.Messaging.Agent.Env.SQLite (AgentConfig (..), InitialAgentServers (..), createAgentStore, defaultAgentConfig) import Simplex.Messaging.Agent.Lock import Simplex.Messaging.Agent.Protocol -import Simplex.Messaging.Agent.Store.SQLite (MigrationConfirmation (..), MigrationError, SQLiteStore (dbNew), execSQL, upMigration, withConnection, checkpointSQLiteStore, closeSQLiteStore, openSQLiteStore) +import Simplex.Messaging.Agent.Store.SQLite (MigrationConfirmation (..), MigrationError, SQLiteStore (dbNew), execSQL, upMigration, withConnection, checkpointSQLiteStore) import Simplex.Messaging.Agent.Store.SQLite.DB (SlowQueryStats (..)) import qualified Simplex.Messaging.Agent.Store.SQLite.DB as DB import qualified Simplex.Messaging.Agent.Store.SQLite.Migrations as Migrations @@ -218,8 +217,8 @@ newChatController ChatDatabase {chatStore, agentStore} user cfg@ChatConfig {agen where configServers :: DefaultAgentServers configServers = - let smp' = fromMaybe defaultServers.smp (nonEmpty smpServers) - xftp' = fromMaybe defaultServers.xftp (nonEmpty xftpServers) + let smp' = fromMaybe (defaultServers.smp) (nonEmpty smpServers) + xftp' = fromMaybe (defaultServers.xftp) (nonEmpty xftpServers) in defaultServers {smp = smp', xftp = xftp', netCfg = networkConfig} agentServers :: ChatConfig -> IO InitialAgentServers agentServers config@ChatConfig {defaultServers = defServers@DefaultAgentServers {ntf, netCfg}} = do @@ -252,7 +251,7 @@ cfgServers p s = case p of startChatController :: forall m. ChatMonad' m => Bool -> Bool -> Bool -> m (Async ()) startChatController subConns enableExpireCIs startXFTPWorkers = do - resumeAgentClient =<< asks smpAgent + asks smpAgent >>= resumeAgentClient unless subConns $ chatWriteVar subscriptionMode SMOnlyCreate users <- fromRight [] <$> runExceptT (withStoreCtx' (Just "startChatController, getUsers") getUsers) @@ -324,8 +323,8 @@ restoreCalls = do calls <- asks currentCalls atomically $ writeTVar calls callsMap -stopChatController :: forall m. MonadUnliftIO m => ChatController -> Bool -> m () -stopChatController ChatController {chatStore, smpAgent, agentAsync = s, sndFiles, rcvFiles, expireCIFlags} closeStore = do +stopChatController :: forall m. MonadUnliftIO m => ChatController -> m () +stopChatController ChatController {chatStore, smpAgent, agentAsync = s, sndFiles, rcvFiles, expireCIFlags} = do disconnectAgentClient smpAgent readTVarIO s >>= mapM_ (\(a1, a2) -> uninterruptibleCancel a1 >> mapM_ uninterruptibleCancel a2) closeFiles sndFiles @@ -334,13 +333,9 @@ stopChatController ChatController {chatStore, smpAgent, agentAsync = s, sndFiles keys <- M.keys <$> readTVar expireCIFlags forM_ keys $ \k -> TM.insert k False expireCIFlags writeTVar s Nothing - liftIO $ do - let agentStore = agentClientStore smpAgent - checkpointSQLiteStore chatStore - checkpointSQLiteStore agentStore - when closeStore $ do - closeSQLiteStore chatStore - closeSQLiteStore agentStore + let agentStore = agentClientStore smpAgent + liftIO $ checkpointSQLiteStore chatStore + liftIO $ checkpointSQLiteStore agentStore where closeFiles :: TVar (Map Int64 Handle) -> m () closeFiles files = do @@ -470,19 +465,12 @@ processChatCommand = \case checkDeleteChatUser user' withChatLock "deleteUser" . procCmd $ deleteChatUser user' delSMPQueues DeleteUser uName delSMPQueues viewPwd_ -> withUserName uName $ \userId -> APIDeleteUser userId delSMPQueues viewPwd_ - APIStartChat ChatCtrlCfg {subConns, enableExpireCIs, startXFTPWorkers, openDBWithKey} -> withUser' $ \_ -> + StartChat subConns enableExpireCIs startXFTPWorkers -> withUser' $ \_ -> asks agentAsync >>= readTVarIO >>= \case Just _ -> pure CRChatRunning - _ -> checkStoreNotChanged $ do - forM_ openDBWithKey $ \(DBEncryptionKey dbKey) -> do - ChatController {chatStore, smpAgent} <- ask - open chatStore dbKey - open (agentClientStore smpAgent) dbKey - startChatController subConns enableExpireCIs startXFTPWorkers $> CRChatStarted - where - open = handleDBError DBErrorOpen .: openSQLiteStore - APIStopChat closeStore -> do - ask >>= (`stopChatController` closeStore) + _ -> checkStoreNotChanged $ startChatController subConns enableExpireCIs startXFTPWorkers $> CRChatStarted + APIStopChat -> do + ask >>= stopChatController pure CRChatStopped APIActivateChat -> withUser $ \_ -> do restoreCalls @@ -2574,7 +2562,7 @@ subscribeUserConnections onlyNeeded agentBatchSubscribe user@User {userId} = do getContactConns :: m ([ConnId], Map ConnId Contact) getContactConns = do cts <- withStore_ ("subscribeUserConnections " <> show userId <> ", getUserContacts") getUserContacts - let connIds = map contactConnId cts + let connIds = map contactConnId (filter contactActive cts) pure (connIds, M.fromList $ zip connIds cts) getUserContactLinkConns :: m ([ConnId], Map ConnId UserContact) getUserContactLinkConns = do @@ -2584,7 +2572,7 @@ subscribeUserConnections onlyNeeded agentBatchSubscribe user@User {userId} = do getGroupMemberConns :: m ([Group], [ConnId], Map ConnId GroupMember) getGroupMemberConns = do gs <- withStore_ ("subscribeUserConnections " <> show userId <> ", getUserGroups") getUserGroups - let mPairs = concatMap (\(Group _ ms) -> mapMaybe (\m -> (,m) <$> memberConnId m) ms) gs + let mPairs = concatMap (\(Group _ ms) -> mapMaybe (\m -> (,m) <$> memberConnId m) (filter (not . memberRemoved) ms)) gs pure (gs, map fst mPairs, M.fromList mPairs) getSndFileTransferConns :: m ([ConnId], Map ConnId SndFileTransfer) getSndFileTransferConns = do @@ -5415,9 +5403,9 @@ chatCommandP = "/_delete user " *> (APIDeleteUser <$> A.decimal <* " del_smp=" <*> onOffP <*> optional (A.space *> jsonP)), "/delete user " *> (DeleteUser <$> displayName <*> pure True <*> optional (A.space *> pwdP)), ("/user" <|> "/u") $> ShowActiveUser, - "/_start" *> (APIStartChat <$> ((A.space *> jsonP) <|> chatCtrlCfgP)), - "/_stop close" $> APIStopChat {closeStore = True}, - "/_stop" $> APIStopChat False, + "/_start subscribe=" *> (StartChat <$> onOffP <* " expire=" <*> onOffP <* " xftp=" <*> onOffP), + "/_start" $> StartChat True True True, + "/_stop" $> APIStopChat, "/_app activate" $> APIActivateChat, "/_app suspend " *> (APISuspendChat <$> A.decimal), "/_resubscribe all" $> ResubscribeAllConnections, @@ -5645,12 +5633,6 @@ chatCommandP = ] where choice = A.choice . map (\p -> p <* A.takeWhile (== ' ') <* A.endOfInput) - chatCtrlCfgP = do - subConns <- (" subscribe=" *> onOffP) <|> pure True - enableExpireCIs <- (" expire=" *> onOffP) <|> pure True - startXFTPWorkers <- (" xftp=" *> onOffP) <|> pure True - openDBWithKey <- optional $ " key=" *> dbKeyP - pure ChatCtrlCfg {subConns, enableExpireCIs, startXFTPWorkers, openDBWithKey} incognitoP = (A.space *> ("incognito" <|> "i")) $> True <|> pure False incognitoOnOffP = (A.space *> "incognito=" *> onOffP) <|> pure False imagePrefix = (<>) <$> "data:" <*> ("image/png;base64," <|> "image/jpg;base64,") diff --git a/src/Simplex/Chat/Archive.hs b/src/Simplex/Chat/Archive.hs index d40d3a515e..51d788dc08 100644 --- a/src/Simplex/Chat/Archive.hs +++ b/src/Simplex/Chat/Archive.hs @@ -9,7 +9,6 @@ module Simplex.Chat.Archive importArchive, deleteStorage, sqlCipherExport, - handleDBError, ) where @@ -22,7 +21,7 @@ import qualified Data.Text as T import qualified Database.SQLite3 as SQL import Simplex.Chat.Controller import Simplex.Messaging.Agent.Client (agentClientStore) -import Simplex.Messaging.Agent.Store.SQLite (SQLiteStore (..), setSQLiteModeWAL, sqlString) +import Simplex.Messaging.Agent.Store.SQLite (SQLiteStore (..), closeSQLiteStore, setSQLiteModeWAL, sqlString) import Simplex.Messaging.Util import System.FilePath import UnliftIO.Directory @@ -43,31 +42,27 @@ archiveFilesFolder = "simplex_v1_files" exportArchive :: ChatMonad m => ArchiveConfig -> m () exportArchive cfg@ArchiveConfig {archivePath, disableCompression} = withTempDir cfg "simplex-chat." $ \dir -> do - setMode False - StorageFiles {chatDb, agentDb, filesPath} <- storageFiles - copyFile chatDb $ dir archiveChatDbFile - copyFile agentDb $ dir archiveAgentDbFile - setMode True + fs@StorageFiles {chatStore, agentStore, filesPath} <- storageFiles + setWALMode False `withStores` fs + copyFile (dbFilePath chatStore) $ dir archiveChatDbFile + copyFile (dbFilePath agentStore) $ dir archiveAgentDbFile + setWALMode True `withStores` fs forM_ filesPath $ \fp -> copyDirectoryFiles fp $ dir archiveFilesFolder let method = if disableCompression == Just True then Z.Store else Z.Deflate Z.createArchive archivePath $ Z.packDirRecur method Z.mkEntrySelector dir where - -- TODO this won't work on Windows as we close connection there when stopping chat - setMode walMode = do - ChatController {chatStore, smpAgent} <- ask - liftIO $ setSQLiteModeWAL chatStore walMode - liftIO $ setSQLiteModeWAL (agentClientStore smpAgent) walMode + setWALMode walMode st = liftIO $ setSQLiteModeWAL st walMode importArchive :: ChatMonad m => ArchiveConfig -> m [ArchiveError] importArchive cfg@ArchiveConfig {archivePath} = withTempDir cfg "simplex-chat." $ \dir -> do Z.withArchive archivePath $ Z.unpackInto dir - StorageFiles {chatDb, agentDb, filesPath} <- storageFiles - backup chatDb - backup agentDb - copyFile (dir archiveChatDbFile) chatDb - copyFile (dir archiveAgentDbFile) agentDb + fs@StorageFiles {chatStore, agentStore, filesPath} <- storageFiles + liftIO $ closeSQLiteStore `withStores` fs + backup `withDBs` fs + copyFile (dir archiveChatDbFile) $ dbFilePath chatStore + copyFile (dir archiveAgentDbFile) $ dbFilePath agentStore copyFiles dir filesPath `E.catch` \(e :: E.SomeException) -> pure [AEImport . ChatError . CEException $ show e] where @@ -103,52 +98,69 @@ copyDirectoryFiles fromDir toDir = do deleteStorage :: ChatMonad m => m () deleteStorage = do - StorageFiles {chatDb, agentDb, filesPath} <- storageFiles - removeFile chatDb - removeFile agentDb - mapM_ removePathForcibly filesPath - tmpPath <- readTVarIO =<< asks tempDirectory - mapM_ removePathForcibly tmpPath + fs <- storageFiles + liftIO $ closeSQLiteStore `withStores` fs + remove `withDBs` fs + mapM_ removeDir $ filesPath fs + mapM_ removeDir =<< chatReadVar tempDirectory + where + remove f = whenM (doesFileExist f) $ removeFile f + removeDir d = whenM (doesDirectoryExist d) $ removePathForcibly d data StorageFiles = StorageFiles - { chatDb :: FilePath, - chatEncrypted :: TVar Bool, - agentDb :: FilePath, - agentEncrypted :: TVar Bool, + { chatStore :: SQLiteStore, + agentStore :: SQLiteStore, filesPath :: Maybe FilePath } storageFiles :: ChatMonad m => m StorageFiles storageFiles = do ChatController {chatStore, filesFolder, smpAgent} <- ask - let SQLiteStore {dbFilePath = chatDb, dbEncrypted = chatEncrypted} = chatStore - SQLiteStore {dbFilePath = agentDb, dbEncrypted = agentEncrypted} = agentClientStore smpAgent + let agentStore = agentClientStore smpAgent filesPath <- readTVarIO filesFolder - pure StorageFiles {chatDb, chatEncrypted, agentDb, agentEncrypted, filesPath} + pure StorageFiles {chatStore, agentStore, filesPath} sqlCipherExport :: forall m. ChatMonad m => DBEncryptionConfig -> m () sqlCipherExport DBEncryptionConfig {currentKey = DBEncryptionKey key, newKey = DBEncryptionKey key'} = when (key /= key') $ do - fs@StorageFiles {chatDb, chatEncrypted, agentDb, agentEncrypted} <- storageFiles - checkFile `with` fs - backup `with` fs - (export chatDb chatEncrypted >> export agentDb agentEncrypted) - `catchChatError` \e -> tryChatError (restore `with` fs) >> throwError e + fs <- storageFiles + checkFile `withDBs` fs + backup `withDBs` fs + checkEncryption `withStores` fs + removeExported `withDBs` fs + export `withDBs` fs + -- closing after encryption prevents closing in case wrong encryption key was passed + liftIO $ closeSQLiteStore `withStores` fs + (moveExported `withStores` fs) + `catchChatError` \e -> (restore `withDBs` fs) >> throwError e where - action `with` StorageFiles {chatDb, agentDb} = action chatDb >> action agentDb backup f = copyFile f (f <> ".bak") restore f = copyFile (f <> ".bak") f checkFile f = unlessM (doesFileExist f) $ throwDBError $ DBErrorNoFile f - export f dbEnc = do - enc <- readTVarIO dbEnc + checkEncryption SQLiteStore {dbEncrypted} = do + enc <- readTVarIO dbEncrypted when (enc && null key) $ throwDBError DBErrorEncrypted when (not enc && not (null key)) $ throwDBError DBErrorPlaintext - withDB (`SQL.exec` exportSQL) DBErrorExport - renameFile (f <> ".exported") f - withDB (`SQL.exec` testSQL) DBErrorOpen - atomically $ writeTVar dbEnc $ not (null key') + exported = (<> ".exported") + removeExported f = whenM (doesFileExist $ exported f) $ removeFile (exported f) + moveExported SQLiteStore {dbFilePath = f, dbEncrypted} = do + renameFile (exported f) f + atomically $ writeTVar dbEncrypted $ not (null key') + export f = do + withDB f (`SQL.exec` exportSQL) DBErrorExport + withDB (exported f) (`SQL.exec` testSQL) DBErrorOpen where - withDB a err = handleDBError err $ bracket (SQL.open $ T.pack f) SQL.close a + withDB f' a err = + liftIO (bracket (SQL.open $ T.pack f') SQL.close a $> Nothing) + `catch` checkSQLError + `catch` (\(e :: SomeException) -> sqliteError' e) + >>= mapM_ (throwDBError . err) + where + checkSQLError e = case SQL.sqlError e of + SQL.ErrorNotADatabase -> pure $ Just SQLiteErrorNotADatabase + _ -> sqliteError' e + sqliteError' :: Show e => e -> m (Maybe SQLiteError) + sqliteError' = pure . Just . SQLiteError . show exportSQL = T.unlines $ keySQL key @@ -163,20 +175,12 @@ sqlCipherExport DBEncryptionConfig {currentKey = DBEncryptionKey key, newKey = D keySQL key' <> [ "PRAGMA foreign_keys = ON;", "PRAGMA secure_delete = ON;", - "PRAGMA auto_vacuum = FULL;", "SELECT count(*) FROM sqlite_master;" ] keySQL k = ["PRAGMA key = " <> sqlString k <> ";" | not (null k)] -handleDBError :: forall m. ChatMonad m => (SQLiteError -> DatabaseError) -> IO () -> m () -handleDBError err a = - (liftIO a $> Nothing) - `catch` checkSQLError - `catch` (\(e :: SomeException) -> sqliteError' e) - >>= mapM_ (throwDBError . err) - where - checkSQLError e = case SQL.sqlError e of - SQL.ErrorNotADatabase -> pure $ Just SQLiteErrorNotADatabase - _ -> sqliteError' e - sqliteError' :: Show e => e -> m (Maybe SQLiteError) - sqliteError' = pure . Just . SQLiteError . show +withDBs :: Monad m => (FilePath -> m b) -> StorageFiles -> m b +action `withDBs` StorageFiles {chatStore, agentStore} = action (dbFilePath chatStore) >> action (dbFilePath agentStore) + +withStores :: Monad m => (SQLiteStore -> m b) -> StorageFiles -> m b +action `withStores` StorageFiles {chatStore, agentStore} = action chatStore >> action agentStore diff --git a/src/Simplex/Chat/Controller.hs b/src/Simplex/Chat/Controller.hs index 3f3dae94ff..122a4be3f3 100644 --- a/src/Simplex/Chat/Controller.hs +++ b/src/Simplex/Chat/Controller.hs @@ -221,8 +221,8 @@ data ChatCommand | UnmuteUser | APIDeleteUser UserId Bool (Maybe UserPwd) | DeleteUser UserName Bool (Maybe UserPwd) - | APIStartChat ChatCtrlCfg - | APIStopChat {closeStore :: Bool} + | StartChat {subscribeConnections :: Bool, enableExpireChatItems :: Bool, startXFTPWorkers :: Bool} + | APIStopChat | APIActivateChat | APISuspendChat {suspendTimeout :: Int} | ResubscribeAllConnections @@ -621,14 +621,6 @@ instance ToJSON ChatResponse where toJSON = J.genericToJSON . sumTypeJSON $ dropPrefix "CR" toEncoding = J.genericToEncoding . sumTypeJSON $ dropPrefix "CR" -data ChatCtrlCfg = ChatCtrlCfg - { subConns :: Bool, - enableExpireCIs :: Bool, - startXFTPWorkers :: Bool, - openDBWithKey :: Maybe DBEncryptionKey - } - deriving (Show, Generic, FromJSON) - newtype UserPwd = UserPwd {unUserPwd :: Text} deriving (Eq, Show) diff --git a/src/Simplex/Chat/View.hs b/src/Simplex/Chat/View.hs index 19d729bf7e..01bdfba95a 100644 --- a/src/Simplex/Chat/View.hs +++ b/src/Simplex/Chat/View.hs @@ -1648,7 +1648,7 @@ viewChatError logLevel = \case DBErrorEncrypted -> ["error: chat database is already encrypted"] DBErrorPlaintext -> ["error: chat database is not encrypted"] DBErrorExport e -> ["error encrypting database: " <> sqliteError' e] - DBErrorOpen e -> ["error opening database: " <> sqliteError' e] + DBErrorOpen e -> ["error opening database after encryption: " <> sqliteError' e] e -> ["chat database error: " <> sShow e] ChatErrorAgent err entity_ -> case err of CMD PROHIBITED -> [withConnEntity <> "error: command is prohibited"] diff --git a/stack.yaml b/stack.yaml index ce78db31f9..1d03a4b665 100644 --- a/stack.yaml +++ b/stack.yaml @@ -49,7 +49,7 @@ extra-deps: # - simplexmq-1.0.0@sha256:34b2004728ae396e3ae449cd090ba7410781e2b3cefc59259915f4ca5daa9ea8,8561 # - ../simplexmq - github: simplex-chat/simplexmq - commit: 459b6d39bf0205e61c8b57f079216b1d8712fead + commit: ee895a14f5cb81ab70726e3a346037c9a45decda - github: kazu-yamamoto/http2 commit: b5a1b7200cf5bc7044af34ba325284271f6dff25 # - ../direct-sqlcipher diff --git a/tests/ChatClient.hs b/tests/ChatClient.hs index d947fb63bd..7da5263253 100644 --- a/tests/ChatClient.hs +++ b/tests/ChatClient.hs @@ -171,7 +171,7 @@ startTestChat_ db cfg opts user = do stopTestChat :: TestCC -> IO () stopTestChat TestCC {chatController = cc, chatAsync, termAsync} = do - stopChatController cc False + stopChatController cc uninterruptibleCancel termAsync uninterruptibleCancel chatAsync threadDelay 200000 diff --git a/tests/ChatTests/Direct.hs b/tests/ChatTests/Direct.hs index 445a5ab998..5c4d96cc94 100644 --- a/tests/ChatTests/Direct.hs +++ b/tests/ChatTests/Direct.hs @@ -992,17 +992,10 @@ testDatabaseEncryption tmp = do alice ##> "/_start" alice <## "chat started" testChatWorking alice bob - alice ##> "/_stop close" + alice ##> "/_stop" alice <## "chat stopped" alice ##> "/db key wrongkey nextkey" alice <## "error encrypting database: wrong passphrase or invalid database file" - alice ##> "/_start key=wrongkey" - alice <## "error opening database: wrong passphrase or invalid database file" - alice ##> "/_start key=mykey" - alice <## "chat started" - testChatWorking alice bob - alice ##> "/_stop close" - alice <## "chat stopped" alice ##> "/db key mykey nextkey" alice <## "ok" alice ##> "/_db encryption {\"currentKey\":\"nextkey\",\"newKey\":\"anotherkey\"}"