From 490e8cead8870e7b995dd0b59682527ac3ea2995 Mon Sep 17 00:00:00 2001 From: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com> Date: Wed, 5 Jun 2024 21:02:13 +0400 Subject: [PATCH] core: file errors (#4261) --- apps/ios/SimpleXChat/APITypes.swift | 2 - .../chat/simplex/common/model/SimpleXAPI.kt | 4 -- cabal.project | 2 +- scripts/nix/sha256map.nix | 2 +- src/Simplex/Chat.hs | 53 +++++++++----- src/Simplex/Chat/Controller.hs | 4 +- src/Simplex/Chat/Messages.hs | 71 ++++++++++++++----- src/Simplex/Chat/Options.hs | 1 - src/Simplex/Chat/Types.hs | 3 +- src/Simplex/Chat/View.hs | 12 ++-- tests/ChatClient.hs | 3 +- 11 files changed, 107 insertions(+), 50 deletions(-) diff --git a/apps/ios/SimpleXChat/APITypes.swift b/apps/ios/SimpleXChat/APITypes.swift index 1574d43ac0..87eb21922c 100644 --- a/apps/ios/SimpleXChat/APITypes.swift +++ b/apps/ios/SimpleXChat/APITypes.swift @@ -1771,8 +1771,6 @@ public enum ChatErrorType: Decodable { case fileImageSize(filePath: String) case fileNotReceived(fileId: Int64) case fileNotApproved(fileId: Int64, unknownServers: [String]) - // case xFTPRcvFile - // case xFTPSndFile case fallbackToSMPProhibited(fileId: Int64) case inlineFileProhibited(fileId: Int64) case invalidQuote 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 1f7f55fb53..becacc3932 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 @@ -4982,8 +4982,6 @@ sealed class ChatErrorType { is FileImageSize -> "fileImageSize" is FileNotReceived -> "fileNotReceived" is FileNotApproved -> "fileNotApproved" - // is XFTPRcvFile -> "xftpRcvFile" - // is XFTPSndFile -> "xftpSndFile" is FallbackToSMPProhibited -> "fallbackToSMPProhibited" is InlineFileProhibited -> "inlineFileProhibited" is InvalidQuote -> "invalidQuote" @@ -5061,8 +5059,6 @@ sealed class ChatErrorType { @Serializable @SerialName("fileImageSize") class FileImageSize(val filePath: String): ChatErrorType() @Serializable @SerialName("fileNotReceived") class FileNotReceived(val fileId: Long): ChatErrorType() @Serializable @SerialName("fileNotApproved") class FileNotApproved(val fileId: Long, val unknownServers: List): ChatErrorType() - // @Serializable @SerialName("xFTPRcvFile") object XFTPRcvFile: ChatErrorType() - // @Serializable @SerialName("xFTPSndFile") object XFTPSndFile: ChatErrorType() @Serializable @SerialName("fallbackToSMPProhibited") class FallbackToSMPProhibited(val fileId: Long): ChatErrorType() @Serializable @SerialName("inlineFileProhibited") class InlineFileProhibited(val fileId: Long): ChatErrorType() @Serializable @SerialName("invalidQuote") object InvalidQuote: ChatErrorType() diff --git a/cabal.project b/cabal.project index 4403b2d44f..0aff0b3ce0 100644 --- a/cabal.project +++ b/cabal.project @@ -12,7 +12,7 @@ constraints: zip +disable-bzip2 +disable-zstd source-repository-package type: git location: https://github.com/simplex-chat/simplexmq.git - tag: 3d605310ed1bb4910e4e5f75487277a064e64e66 + tag: 3c0cd7efcc3d3058d940c7a9667faef2dc6de6cc source-repository-package type: git diff --git a/scripts/nix/sha256map.nix b/scripts/nix/sha256map.nix index a55bd91957..21cdb0e660 100644 --- a/scripts/nix/sha256map.nix +++ b/scripts/nix/sha256map.nix @@ -1,5 +1,5 @@ { - "https://github.com/simplex-chat/simplexmq.git"."3d605310ed1bb4910e4e5f75487277a064e64e66" = "0z902yyx6klry4x7fjj9bmwphfn29ff1ilza57pyrdzxjp35if3k"; + "https://github.com/simplex-chat/simplexmq.git"."3c0cd7efcc3d3058d940c7a9667faef2dc6de6cc" = "09fx6bj5f25v6a34lkfggj3a1yqrg1xz9fv0dg9vb87pcajhkrq0"; "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 8167df0f40..82f4ce84cd 100644 --- a/src/Simplex/Chat.hs +++ b/src/Simplex/Chat.hs @@ -93,8 +93,9 @@ import Simplex.FileTransfer.Description (FileDescriptionURI (..), ValidFileDescr import qualified Simplex.FileTransfer.Description as FD import Simplex.FileTransfer.Protocol (FileParty (..), FilePartyI) import qualified Simplex.FileTransfer.Transport as XFTP +import Simplex.FileTransfer.Types (FileErrorType (..), RcvFileId, SndFileId) import Simplex.Messaging.Agent as Agent -import Simplex.Messaging.Agent.Client (AgentStatsKey (..), SubInfo (..), agentClientStore, getAgentQueuesInfo, getAgentWorkersDetails, getAgentWorkersSummary, getNetworkConfig', ipAddressProtected, temporaryAgentError, withLockMap) +import Simplex.Messaging.Agent.Client (AgentStatsKey (..), SubInfo (..), agentClientStore, getAgentQueuesInfo, getAgentWorkersDetails, getAgentWorkersSummary, getNetworkConfig', ipAddressProtected, withLockMap) import Simplex.Messaging.Agent.Env.SQLite (AgentConfig (..), InitialAgentServers (..), createAgentStore, defaultAgentConfig) import Simplex.Messaging.Agent.Lock (withLock) import Simplex.Messaging.Agent.Protocol @@ -3768,11 +3769,11 @@ processAgentMsgSndFile _corrId aFileId msg = do lift $ withAgent' (`xftpDeleteSndFileInternal` aFileId) withStore' $ \db -> createExtraSndFTDescrs db user fileId (map fileDescrText rfds) case rfds of - [] -> sendFileError "no receiver descriptions" vr ft + [] -> sendFileError (FileErrOther "no receiver descriptions") "no receiver descriptions" vr ft rfd : _ -> case [fd | fd@(FD.ValidFileDescription FD.FileDescription {chunks = [_]}) <- rfds] of [] -> case xftpRedirectFor of Nothing -> xftpSndFileRedirect user fileId rfd >>= toView . CRSndFileRedirectStartXFTP user ft - Just _ -> sendFileError "Prohibit chaining redirects" vr ft + Just _ -> sendFileError (FileErrOther "chaining redirects") "Prohibit chaining redirects" vr ft rfds' -> do -- we have 1 chunk - use it as URI whether it is redirect or not ft' <- maybe (pure ft) (\fId -> withStore $ \db -> getFileTransferMeta db user fId) xftpRedirectFor @@ -3817,11 +3818,15 @@ processAgentMsgSndFile _corrId aFileId msg = do pure (sndMsg, msgDeliveryId) _ -> pure () _ -> pure () -- TODO error? - SFERR e - | temporaryAgentError e -> - throwChatError $ CEXFTPSndFile fileId (AgentSndFileId aFileId) e - | otherwise -> - sendFileError (tshow e) vr ft + SFWARN e -> do + let err = tshow e + logWarn $ "Sent file warning: " <> err + ci <- withStore $ \db -> do + liftIO $ updateCIFileStatus db user fileId (CIFSSndWarning $ agentFileError e) + lookupChatItemByFileId db vr user fileId + toView $ CRSndFileWarning user ci ft err + SFERR e -> + sendFileError (agentFileError e) (tshow e) vr ft where fileDescrText :: FilePartyI p => ValidFileDescription p -> T.Text fileDescrText = safeDecodeUtf8 . strEncode @@ -3839,15 +3844,27 @@ processAgentMsgSndFile _corrId aFileId msg = do case L.nonEmpty fds of Just fds' -> loopSend fds' Nothing -> pure msgDeliveryId - sendFileError :: Text -> VersionRangeChat -> FileTransferMeta -> CM () - sendFileError err vr ft = do + sendFileError :: FileError -> Text -> VersionRangeChat -> FileTransferMeta -> CM () + sendFileError ferr err vr ft = do logError $ "Sent file error: " <> err ci <- withStore $ \db -> do - liftIO $ updateFileCancelled db user fileId CIFSSndError + liftIO $ updateFileCancelled db user fileId (CIFSSndError ferr) lookupChatItemByFileId db vr user fileId lift $ withAgent' (`xftpDeleteSndFileInternal` aFileId) toView $ CRSndFileError user ci ft err +agentFileError :: AgentErrorType -> FileError +agentFileError = \case + XFTP _ XFTP.AUTH -> FileErrAuth + FILE NO_FILE -> FileErrNoFile + BROKER _ e -> brokerError FileErrRelay e + e -> FileErrOther $ tshow e + where + brokerError srvErr = \case + HOST -> srvErr SrvErrHost + SMP.TRANSPORT TEVersion -> srvErr SrvErrVersion + e -> srvErr . SrvErrOther $ tshow e + splitFileDescr :: RcvFileDescrText -> CM (NonEmpty FileDescr) splitFileDescr rfdText = do partSize <- asks $ xftpDescrPartSize . config @@ -3900,16 +3917,19 @@ processAgentMsgRcvFile _corrId aFileId msg = do lookupChatItemByFileId db vr user fileId agentXFTPDeleteRcvFile aFileId fileId toView $ maybe (CRRcvStandaloneFileComplete user fsTargetPath ft) (CRRcvFileComplete user) ci_ + RFWARN e -> do + ci <- withStore $ \db -> do + liftIO $ updateCIFileStatus db user fileId (CIFSRcvWarning $ agentFileError e) + lookupChatItemByFileId db vr user fileId + toView $ CRRcvFileWarning user ci e ft RFERR e - | temporaryAgentError e -> - throwChatError $ CEXFTPRcvFile fileId (AgentRcvFileId aFileId) e - | e == XFTP "" XFTP.NOT_APPROVED -> do + | e == FILE NOT_APPROVED -> do aci_ <- resetRcvCIFileStatus user fileId CIFSRcvAborted agentXFTPDeleteRcvFile aFileId fileId forM_ aci_ $ \aci -> toView $ CRChatItemUpdated user aci | otherwise -> do ci <- withStore $ \db -> do - liftIO $ updateFileCancelled db user fileId CIFSRcvError + liftIO $ updateFileCancelled db user fileId (CIFSRcvError $ agentFileError e) lookupChatItemByFileId db vr user fileId agentXFTPDeleteRcvFile aFileId fileId toView $ CRRcvFileError user ci e ft @@ -4825,7 +4845,8 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage = unless (connInactive conn) $ do quotaErrCounter' <- withStore' $ \db -> incQuotaErrCounter db user conn when (quotaErrCounter' >= quotaErrInactiveCount) $ - toView $ CRConnectionInactive connEntity True + toView $ + CRConnectionInactive connEntity True _ -> pure () continueSending :: ConnectionEntity -> Connection -> CM Bool diff --git a/src/Simplex/Chat/Controller.hs b/src/Simplex/Chat/Controller.hs index 77b14b5eac..102d6575e6 100644 --- a/src/Simplex/Chat/Controller.hs +++ b/src/Simplex/Chat/Controller.hs @@ -654,6 +654,7 @@ data ChatResponse | CRRcvFileCancelled {user :: User, chatItem_ :: Maybe AChatItem, rcvFileTransfer :: RcvFileTransfer} | CRRcvFileSndCancelled {user :: User, chatItem :: AChatItem, rcvFileTransfer :: RcvFileTransfer} | CRRcvFileError {user :: User, chatItem_ :: Maybe AChatItem, agentError :: AgentErrorType, rcvFileTransfer :: RcvFileTransfer} + | CRRcvFileWarning {user :: User, chatItem_ :: Maybe AChatItem, agentError :: AgentErrorType, rcvFileTransfer :: RcvFileTransfer} | CRSndFileStart {user :: User, chatItem :: AChatItem, sndFileTransfer :: SndFileTransfer} | CRSndFileComplete {user :: User, chatItem :: AChatItem, sndFileTransfer :: SndFileTransfer} | CRSndFileRcvCancelled {user :: User, chatItem_ :: Maybe AChatItem, sndFileTransfer :: SndFileTransfer} @@ -666,6 +667,7 @@ data ChatResponse | CRSndStandaloneFileComplete {user :: User, fileTransferMeta :: FileTransferMeta, rcvURIs :: [Text]} | CRSndFileCancelledXFTP {user :: User, chatItem_ :: Maybe AChatItem, fileTransferMeta :: FileTransferMeta} | CRSndFileError {user :: User, chatItem_ :: Maybe AChatItem, fileTransferMeta :: FileTransferMeta, errorMessage :: Text} + | CRSndFileWarning {user :: User, chatItem_ :: Maybe AChatItem, fileTransferMeta :: FileTransferMeta, errorMessage :: Text} | CRUserProfileUpdated {user :: User, fromProfile :: Profile, toProfile :: Profile, updateSummary :: UserProfileUpdateSummary} | CRUserProfileImage {user :: User, profile :: Profile} | CRContactAliasUpdated {user :: User, toContact :: Contact} @@ -1157,8 +1159,6 @@ data ChatErrorType | CEFileImageSize {filePath :: FilePath} | CEFileNotReceived {fileId :: FileTransferId} | CEFileNotApproved {fileId :: FileTransferId, unknownServers :: [XFTPServer]} - | CEXFTPRcvFile {fileId :: FileTransferId, agentRcvFileId :: AgentRcvFileId, agentError :: AgentErrorType} - | CEXFTPSndFile {fileId :: FileTransferId, agentSndFileId :: AgentSndFileId, agentError :: AgentErrorType} | CEFallbackToSMPProhibited {fileId :: FileTransferId} | CEInlineFileProhibited {fileId :: FileTransferId} | CEInvalidQuote diff --git a/src/Simplex/Chat/Messages.hs b/src/Simplex/Chat/Messages.hs index ea79161e06..9e0eccbbde 100644 --- a/src/Simplex/Chat/Messages.hs +++ b/src/Simplex/Chat/Messages.hs @@ -536,14 +536,16 @@ data CIFileStatus (d :: MsgDirection) where CIFSSndTransfer :: {sndProgress :: Int64, sndTotal :: Int64} -> CIFileStatus 'MDSnd CIFSSndCancelled :: CIFileStatus 'MDSnd CIFSSndComplete :: CIFileStatus 'MDSnd - CIFSSndError :: CIFileStatus 'MDSnd + CIFSSndError :: {sndFileError :: FileError} -> CIFileStatus 'MDSnd + CIFSSndWarning :: {sndFileError :: FileError} -> CIFileStatus 'MDSnd CIFSRcvInvitation :: CIFileStatus 'MDRcv CIFSRcvAccepted :: CIFileStatus 'MDRcv CIFSRcvTransfer :: {rcvProgress :: Int64, rcvTotal :: Int64} -> CIFileStatus 'MDRcv CIFSRcvAborted :: CIFileStatus 'MDRcv CIFSRcvComplete :: CIFileStatus 'MDRcv CIFSRcvCancelled :: CIFileStatus 'MDRcv - CIFSRcvError :: CIFileStatus 'MDRcv + CIFSRcvError :: {rcvFileError :: FileError} -> CIFileStatus 'MDRcv + CIFSRcvWarning :: {rcvFileError :: FileError} -> CIFileStatus 'MDRcv CIFSInvalid :: {text :: Text} -> CIFileStatus 'MDSnd deriving instance Eq (CIFileStatus d) @@ -556,14 +558,16 @@ ciFileEnded = \case CIFSSndTransfer {} -> False CIFSSndCancelled -> True CIFSSndComplete -> True - CIFSSndError -> True + CIFSSndError {} -> True + CIFSSndWarning {} -> False CIFSRcvInvitation -> False CIFSRcvAccepted -> False CIFSRcvTransfer {} -> False CIFSRcvAborted -> True CIFSRcvCancelled -> True CIFSRcvComplete -> True - CIFSRcvError -> True + CIFSRcvError {} -> True + CIFSRcvWarning {} -> False CIFSInvalid {} -> True ciFileLoaded :: CIFileStatus d -> Bool @@ -572,14 +576,16 @@ ciFileLoaded = \case CIFSSndTransfer {} -> True CIFSSndComplete -> True CIFSSndCancelled -> True - CIFSSndError -> True + CIFSSndError {} -> True + CIFSSndWarning {} -> True CIFSRcvInvitation -> False CIFSRcvAccepted -> False CIFSRcvTransfer {} -> False CIFSRcvAborted -> False CIFSRcvCancelled -> False CIFSRcvComplete -> True - CIFSRcvError -> False + CIFSRcvError {} -> False + CIFSRcvWarning {} -> False CIFSInvalid {} -> False data ACIFileStatus = forall d. MsgDirectionI d => AFS (SMsgDirection d) (CIFileStatus d) @@ -592,14 +598,16 @@ instance MsgDirectionI d => StrEncoding (CIFileStatus d) where CIFSSndTransfer sent total -> strEncode (Str "snd_transfer", sent, total) CIFSSndCancelled -> "snd_cancelled" CIFSSndComplete -> "snd_complete" - CIFSSndError -> "snd_error" + CIFSSndError sndFileErr -> "snd_error " <> strEncode sndFileErr + CIFSSndWarning sndFileErr -> "snd_warning " <> strEncode sndFileErr CIFSRcvInvitation -> "rcv_invitation" CIFSRcvAccepted -> "rcv_accepted" CIFSRcvTransfer rcvd total -> strEncode (Str "rcv_transfer", rcvd, total) CIFSRcvAborted -> "rcv_aborted" CIFSRcvComplete -> "rcv_complete" CIFSRcvCancelled -> "rcv_cancelled" - CIFSRcvError -> "rcv_error" + CIFSRcvError rcvFileErr -> "rcv_error " <> strEncode rcvFileErr + CIFSRcvWarning rcvFileErr -> "rcv_warning " <> strEncode rcvFileErr CIFSInvalid {} -> "invalid" strP = (\(AFS _ st) -> checkDirection st) <$?> strP @@ -615,14 +623,16 @@ instance StrEncoding ACIFileStatus where "snd_transfer" -> AFS SMDSnd <$> progress CIFSSndTransfer "snd_cancelled" -> pure $ AFS SMDSnd CIFSSndCancelled "snd_complete" -> pure $ AFS SMDSnd CIFSSndComplete - "snd_error" -> pure $ AFS SMDSnd CIFSSndError + "snd_error" -> AFS SMDSnd . CIFSSndError <$> ((A.space *> strP) <|> pure (FileErrOther "")) -- alternative for backwards compatibility + "snd_warning" -> AFS SMDSnd . CIFSSndWarning <$> (A.space *> strP) "rcv_invitation" -> pure $ AFS SMDRcv CIFSRcvInvitation "rcv_accepted" -> pure $ AFS SMDRcv CIFSRcvAccepted "rcv_transfer" -> AFS SMDRcv <$> progress CIFSRcvTransfer "rcv_aborted" -> pure $ AFS SMDRcv CIFSRcvAborted "rcv_complete" -> pure $ AFS SMDRcv CIFSRcvComplete "rcv_cancelled" -> pure $ AFS SMDRcv CIFSRcvCancelled - "rcv_error" -> pure $ AFS SMDRcv CIFSRcvError + "rcv_error" -> AFS SMDRcv . CIFSRcvError <$> ((A.space *> strP) <|> pure (FileErrOther "")) -- alternative for backwards compatibility + "rcv_warning" -> AFS SMDRcv . CIFSRcvWarning <$> (A.space *> strP) _ -> fail "bad file status" progress :: (Int64 -> Int64 -> a) -> A.Parser a progress f = f <$> num <*> num <|> pure (f 0 1) @@ -633,14 +643,16 @@ data JSONCIFileStatus | JCIFSSndTransfer {sndProgress :: Int64, sndTotal :: Int64} | JCIFSSndCancelled | JCIFSSndComplete - | JCIFSSndError + | JCIFSSndError {sndFileError :: FileError} + | JCIFSSndWarning {sndFileError :: FileError} | JCIFSRcvInvitation | JCIFSRcvAccepted | JCIFSRcvTransfer {rcvProgress :: Int64, rcvTotal :: Int64} | JCIFSRcvAborted | JCIFSRcvComplete | JCIFSRcvCancelled - | JCIFSRcvError + | JCIFSRcvError {rcvFileError :: FileError} + | JCIFSRcvWarning {rcvFileError :: FileError} | JCIFSInvalid {text :: Text} jsonCIFileStatus :: CIFileStatus d -> JSONCIFileStatus @@ -649,14 +661,16 @@ jsonCIFileStatus = \case CIFSSndTransfer sent total -> JCIFSSndTransfer sent total CIFSSndCancelled -> JCIFSSndCancelled CIFSSndComplete -> JCIFSSndComplete - CIFSSndError -> JCIFSSndError + CIFSSndError sndFileErr -> JCIFSSndError sndFileErr + CIFSSndWarning sndFileErr -> JCIFSSndWarning sndFileErr CIFSRcvInvitation -> JCIFSRcvInvitation CIFSRcvAccepted -> JCIFSRcvAccepted CIFSRcvTransfer rcvd total -> JCIFSRcvTransfer rcvd total CIFSRcvAborted -> JCIFSRcvAborted CIFSRcvComplete -> JCIFSRcvComplete CIFSRcvCancelled -> JCIFSRcvCancelled - CIFSRcvError -> JCIFSRcvError + CIFSRcvError rcvFileErr -> JCIFSRcvError rcvFileErr + CIFSRcvWarning rcvFileErr -> JCIFSRcvWarning rcvFileErr CIFSInvalid text -> JCIFSInvalid text aciFileStatusJSON :: JSONCIFileStatus -> ACIFileStatus @@ -665,16 +679,39 @@ aciFileStatusJSON = \case JCIFSSndTransfer sent total -> AFS SMDSnd $ CIFSSndTransfer sent total JCIFSSndCancelled -> AFS SMDSnd CIFSSndCancelled JCIFSSndComplete -> AFS SMDSnd CIFSSndComplete - JCIFSSndError -> AFS SMDSnd CIFSSndError + JCIFSSndError sndFileErr -> AFS SMDSnd (CIFSSndError sndFileErr) + JCIFSSndWarning sndFileErr -> AFS SMDSnd (CIFSSndWarning sndFileErr) JCIFSRcvInvitation -> AFS SMDRcv CIFSRcvInvitation JCIFSRcvAccepted -> AFS SMDRcv CIFSRcvAccepted JCIFSRcvTransfer rcvd total -> AFS SMDRcv $ CIFSRcvTransfer rcvd total JCIFSRcvAborted -> AFS SMDRcv CIFSRcvAborted JCIFSRcvComplete -> AFS SMDRcv CIFSRcvComplete JCIFSRcvCancelled -> AFS SMDRcv CIFSRcvCancelled - JCIFSRcvError -> AFS SMDRcv CIFSRcvError + JCIFSRcvError rcvFileErr -> AFS SMDRcv (CIFSRcvError rcvFileErr) + JCIFSRcvWarning rcvFileErr -> AFS SMDRcv (CIFSRcvWarning rcvFileErr) JCIFSInvalid text -> AFS SMDSnd $ CIFSInvalid text +data FileError + = FileErrAuth + | FileErrNoFile + | FileErrRelay {srvError :: SrvError} + | FileErrOther {fileError :: Text} + deriving (Eq, Show) + +instance StrEncoding FileError where + strEncode = \case + FileErrAuth -> "auth" + FileErrNoFile -> "no_file" + FileErrRelay srvErr -> "relay " <> strEncode srvErr + FileErrOther e -> "other " <> encodeUtf8 e + strP = + A.takeWhile1 (/= ' ') >>= \case + "auth" -> pure FileErrAuth + "no_file" -> pure FileErrNoFile + "relay" -> FileErrRelay <$> (A.space *> strP) + "other" -> FileErrOther . safeDecodeUtf8 <$> (A.space *> A.takeByteString) + s -> FileErrOther . safeDecodeUtf8 . (s <>) <$> A.takeByteString + -- to conveniently read file data from db data CIFileInfo = CIFileInfo { fileId :: Int64, @@ -1208,6 +1245,8 @@ instance ChatTypeI c => ToJSON (CIMeta c d) where toJSON = $(JQ.mkToJSON defaultJSON ''CIMeta) toEncoding = $(JQ.mkToEncoding defaultJSON ''CIMeta) +$(JQ.deriveJSON (sumTypeJSON $ dropPrefix "FileErr") ''FileError) + $(JQ.deriveJSON (sumTypeJSON $ dropPrefix "JCIFS") ''JSONCIFileStatus) instance MsgDirectionI d => FromJSON (CIFileStatus d) where diff --git a/src/Simplex/Chat/Options.hs b/src/Simplex/Chat/Options.hs index 747414af37..f441afd2b3 100644 --- a/src/Simplex/Chat/Options.hs +++ b/src/Simplex/Chat/Options.hs @@ -27,7 +27,6 @@ import Numeric.Natural (Natural) import Options.Applicative import Simplex.Chat.Controller (ChatLogLevel (..), SimpleNetCfg (..), updateStr, versionNumber, versionString) import Simplex.FileTransfer.Description (mb) -import Simplex.Messaging.Client (SMPProxyMode (..), SMPProxyFallback (..)) import Simplex.Messaging.Encoding.String import Simplex.Messaging.Parsers (parseAll) import Simplex.Messaging.Protocol (ProtoServerWithAuth, ProtocolTypeI, SMPServerWithAuth, XFTPServerWithAuth) diff --git a/src/Simplex/Chat/Types.hs b/src/Simplex/Chat/Types.hs index 5d27b80e06..106c4b3373 100644 --- a/src/Simplex/Chat/Types.hs +++ b/src/Simplex/Chat/Types.hs @@ -47,7 +47,8 @@ import Simplex.Chat.Types.Shared import Simplex.Chat.Types.UITheme import Simplex.Chat.Types.Util import Simplex.FileTransfer.Description (FileDigest) -import Simplex.Messaging.Agent.Protocol (ACorrId, AEventTag (..), AEvtTag (..), ConnId, ConnectionMode (..), ConnectionRequestUri, InvitationId, RcvFileId, SAEntity (..), SndFileId, UserId) +import Simplex.FileTransfer.Types (RcvFileId, SndFileId) +import Simplex.Messaging.Agent.Protocol (ACorrId, AEventTag (..), AEvtTag (..), ConnId, ConnectionMode (..), ConnectionRequestUri, InvitationId, SAEntity (..), UserId) import Simplex.Messaging.Crypto.File (CryptoFileArgs (..)) import Simplex.Messaging.Crypto.Ratchet (PQEncryption (..), PQSupport, pattern PQEncOff) import Simplex.Messaging.Encoding.String diff --git a/src/Simplex/Chat/View.hs b/src/Simplex/Chat/View.hs index e4f1e3925c..ac5f84e5e5 100644 --- a/src/Simplex/Chat/View.hs +++ b/src/Simplex/Chat/View.hs @@ -211,6 +211,8 @@ responseToView hu@(currentRH, user_) ChatConfig {logLevel, showReactions, showRe CRRcvFileSndCancelled u _ ft -> ttyUser u $ viewRcvFileSndCancelled ft CRRcvFileError u (Just ci) e _ -> ttyUser u $ receivingFile_' hu testView "error" ci <> [sShow e] CRRcvFileError u Nothing e ft -> ttyUser u $ receivingFileStandalone "error" ft <> [sShow e] + CRRcvFileWarning u (Just ci) e _ -> ttyUser u $ receivingFile_' hu testView "warning: " ci <> [sShow e] + CRRcvFileWarning u Nothing e ft -> ttyUser u $ receivingFileStandalone "warning: " ft <> [sShow e] CRSndFileStart u _ ft -> ttyUser u $ sendingFile_ "started" ft CRSndFileComplete u _ ft -> ttyUser u $ sendingFile_ "completed" ft CRSndStandaloneFileCreated u ft -> ttyUser u $ uploadingFileStandalone "started" ft @@ -222,6 +224,8 @@ responseToView hu@(currentRH, user_) ChatConfig {logLevel, showReactions, showRe CRSndFileCancelledXFTP {} -> [] CRSndFileError u Nothing ft e -> ttyUser u $ uploadingFileStandalone "error" ft <> [plain e] CRSndFileError u (Just ci) _ e -> ttyUser u $ uploadingFile "error" ci <> [plain e] + CRSndFileWarning u Nothing ft e -> ttyUser u $ uploadingFileStandalone "warning: " ft <> [plain e] + CRSndFileWarning u (Just ci) _ e -> ttyUser u $ uploadingFile "warning: " ci <> [plain e] CRSndFileRcvCancelled u _ ft@SndFileTransfer {recipientDisplayName = c} -> ttyUser u [ttyContact c <> " cancelled receiving " <> sndFile ft] CRStandaloneFileInfo info_ -> maybe ["no file information in URI"] (\j -> [viewJSON j]) info_ @@ -1776,14 +1780,16 @@ viewFileTransferStatusXFTP (AChatItem _ _ _ ChatItem {file = Just CIFile {fileId CIFSSndTransfer progress total -> ["sending " <> fstr <> " in progress " <> fileProgressXFTP progress total fileSize] CIFSSndCancelled -> ["sending " <> fstr <> " cancelled"] CIFSSndComplete -> ["sending " <> fstr <> " complete"] - CIFSSndError -> ["sending " <> fstr <> " error"] + CIFSSndError sndFileErr -> ["sending " <> fstr <> " error: " <> plain (show sndFileErr)] + CIFSSndWarning sndFileErr -> ["sending " <> fstr <> " warning: " <> plain (show sndFileErr)] CIFSRcvInvitation -> ["receiving " <> fstr <> " not accepted yet, use " <> highlight ("/fr " <> show fileId) <> " to receive file"] CIFSRcvAccepted -> ["receiving " <> fstr <> " just started"] CIFSRcvTransfer progress total -> ["receiving " <> fstr <> " progress " <> fileProgressXFTP progress total fileSize] CIFSRcvAborted -> ["receiving " <> fstr <> " aborted, use " <> highlight ("/fr " <> show fileId) <> " to receive file"] CIFSRcvComplete -> ["receiving " <> fstr <> " complete" <> maybe "" (\(CryptoFile fp _) -> ", path: " <> plain fp) fileSource] CIFSRcvCancelled -> ["receiving " <> fstr <> " cancelled"] - CIFSRcvError -> ["receiving " <> fstr <> " error"] + CIFSRcvError rcvFileErr -> ["receiving " <> fstr <> " error: " <> plain (show rcvFileErr)] + CIFSRcvWarning rcvFileErr -> ["receiving " <> fstr <> " warning: " <> plain (show rcvFileErr)] CIFSInvalid text -> [fstr <> " invalid status: " <> plain text] where fstr = fileTransferStr fileId fileName @@ -1984,8 +1990,6 @@ viewChatError isCmd logLevel testView = \case CEFileImageSize _ -> ["max image size: " <> sShow maxImageSize <> " bytes, resize it or send as a file using " <> highlight' "/f"] CEFileNotReceived fileId -> ["file " <> sShow fileId <> " not received"] CEFileNotApproved fileId unknownSrvs -> ["file " <> sShow fileId <> " aborted, unknwon XFTP servers:"] <> map (plain . show) unknownSrvs - CEXFTPRcvFile fileId aFileId e -> ["error receiving XFTP file " <> sShow fileId <> ", agent file id " <> sShow aFileId <> ": " <> sShow e | logLevel == CLLError] - CEXFTPSndFile fileId aFileId e -> ["error sending XFTP file " <> sShow fileId <> ", agent file id " <> sShow aFileId <> ": " <> sShow e | logLevel == CLLError] CEFallbackToSMPProhibited fileId -> ["recipient tried to accept file " <> sShow fileId <> " via old protocol, prohibited"] CEInlineFileProhibited _ -> ["A small file sent without acceptance - you can enable receiving such files with -f option."] CEInvalidQuote -> ["cannot reply to this message"] diff --git a/tests/ChatClient.hs b/tests/ChatClient.hs index 589d880e8f..ceb3988ff9 100644 --- a/tests/ChatClient.hs +++ b/tests/ChatClient.hs @@ -131,8 +131,7 @@ aCfg = (agentConfig defaultChatConfig) {tbqSize = 16} testAgentCfg :: AgentConfig testAgentCfg = aCfg - { reconnectInterval = (reconnectInterval aCfg) {initialInterval = 50000}, - xftpNotifyErrsOnRetry = False + { reconnectInterval = (reconnectInterval aCfg) {initialInterval = 50000} } testCfg :: ChatConfig