Skip to content

Commit 3d4e0b0

Browse files
authored
servers: blocking records for content moderation (#1430)
* servers: blocking records for content moderation * update * encode BLOCKED as AUTH in old versions * update * unblock queue command * test, status command
1 parent 9d9ec8c commit 3d4e0b0

File tree

20 files changed

+415
-94
lines changed

20 files changed

+415
-94
lines changed

src/Simplex/FileTransfer/Protocol.hs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import Data.List.NonEmpty (NonEmpty (..))
2525
import Data.Maybe (isNothing)
2626
import Data.Type.Equality
2727
import Data.Word (Word32)
28-
import Simplex.FileTransfer.Transport (XFTPErrorType (..), XFTPVersion, xftpClientHandshakeStub)
28+
import Simplex.FileTransfer.Transport (XFTPErrorType (..), XFTPVersion, blockedFilesXFTPVersion, xftpClientHandshakeStub)
2929
import Simplex.Messaging.Client (authTransmission)
3030
import qualified Simplex.Messaging.Crypto as C
3131
import Simplex.Messaging.Encoding
@@ -276,12 +276,14 @@ data FileResponse
276276

277277
instance ProtocolEncoding XFTPVersion XFTPErrorType FileResponse where
278278
type Tag FileResponse = FileResponseTag
279-
encodeProtocol _v = \case
279+
encodeProtocol v = \case
280280
FRSndIds fId rIds -> e (FRSndIds_, ' ', fId, rIds)
281281
FRRcvIds rIds -> e (FRRcvIds_, ' ', rIds)
282282
FRFile rDhKey nonce -> e (FRFile_, ' ', rDhKey, nonce)
283283
FROk -> e FROk_
284-
FRErr err -> e (FRErr_, ' ', err)
284+
FRErr err -> case err of
285+
BLOCKED _ | v < blockedFilesXFTPVersion -> e (FRErr_, ' ', AUTH)
286+
_ -> e (FRErr_, ' ', err)
285287
FRPong -> e FRPong_
286288
where
287289
e :: Encoding a => a -> ByteString

src/Simplex/FileTransfer/Server.hs

Lines changed: 35 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,11 @@ import qualified Simplex.Messaging.Crypto as C
5353
import qualified Simplex.Messaging.Crypto.Lazy as LC
5454
import Simplex.Messaging.Encoding
5555
import Simplex.Messaging.Encoding.String
56-
import Simplex.Messaging.Protocol (CorrId (..), EntityId (..), RcvPublicAuthKey, RcvPublicDhKey, RecipientId, TransmissionAuth, pattern NoEntity)
56+
import Simplex.Messaging.Protocol (CorrId (..), BlockingInfo, EntityId (..), RcvPublicAuthKey, RcvPublicDhKey, RecipientId, TransmissionAuth, pattern NoEntity)
5757
import Simplex.Messaging.Server (dummyVerifyCmd, verifyCmdAuthorization)
5858
import Simplex.Messaging.Server.Control (CPClientRole (..))
5959
import Simplex.Messaging.Server.Expiration
60-
import Simplex.Messaging.Server.QueueStore (RoundedSystemTime, getRoundedSystemTime)
60+
import Simplex.Messaging.Server.QueueStore (RoundedSystemTime, ServerEntityStatus (..), getRoundedSystemTime)
6161
import Simplex.Messaging.Server.Stats
6262
import Simplex.Messaging.TMap (TMap)
6363
import qualified Simplex.Messaging.TMap as TM
@@ -287,11 +287,15 @@ xftpServer cfg@XFTPServerConfig {xftpPort, transportConfig, inactiveClientExpira
287287
CPDelete fileId -> withUserRole $ unliftIO u $ do
288288
fs <- asks store
289289
r <- runExceptT $ do
290-
let asSender = ExceptT . atomically $ getFile fs SFSender fileId
291-
let asRecipient = ExceptT . atomically $ getFile fs SFRecipient fileId
292-
(fr, _) <- asSender `catchError` const asRecipient
290+
(fr, _) <- ExceptT $ atomically $ getFile fs SFSender fileId
293291
ExceptT $ deleteServerFile_ fr
294292
liftIO . hPutStrLn h $ either (\e -> "error: " <> show e) (\() -> "ok") r
293+
CPBlock fileId info -> withUserRole $ unliftIO u $ do
294+
fs <- asks store
295+
r <- runExceptT $ do
296+
(fr, _) <- ExceptT $ atomically $ getFile fs SFSender fileId
297+
ExceptT $ blockServerFile fr info
298+
liftIO . hPutStrLn h $ either (\e -> "error: " <> show e) (\() -> "ok") r
295299
CPHelp -> hPutStrLn h "commands: stats-rts, delete, help, quit"
296300
CPQuit -> pure ()
297301
CPSkip -> pure ()
@@ -321,7 +325,7 @@ processRequest XFTPTransportRequest {thParams, reqBody = body@HTTP2Body {bodyHea
321325
let THandleParams {thAuth} = thParams
322326
verifyXFTPTransmission ((,C.cbNonce (bs corrId)) <$> thAuth) sig_ signed fId cmd >>= \case
323327
VRVerified req -> uncurry send =<< processXFTPRequest body req
324-
VRFailed -> send (FRErr AUTH) Nothing
328+
VRFailed e -> send (FRErr e) Nothing
325329
Left e -> send (FRErr e) Nothing
326330
where
327331
send resp = sendXFTPResponse (corrId, fId, resp)
@@ -355,7 +359,7 @@ randomDelay = do
355359
threadDelay $ (d * (1000 + pc)) `div` 1000
356360
#endif
357361

358-
data VerificationResult = VRVerified XFTPRequest | VRFailed
362+
data VerificationResult = VRVerified XFTPRequest | VRFailed XFTPErrorType
359363

360364
verifyXFTPTransmission :: Maybe (THandleAuth 'TServer, C.CbNonce) -> Maybe TransmissionAuth -> ByteString -> XFTPFileId -> FileCmd -> M VerificationResult
361365
verifyXFTPTransmission auth_ tAuth authorized fId cmd =
@@ -367,13 +371,19 @@ verifyXFTPTransmission auth_ tAuth authorized fId cmd =
367371
verifyCmd :: SFileParty p -> M VerificationResult
368372
verifyCmd party = do
369373
st <- asks store
370-
atomically $ verify <$> getFile st party fId
374+
atomically $ verify =<< getFile st party fId
371375
where
372376
verify = \case
373-
Right (fr, k) -> XFTPReqCmd fId fr cmd `verifyWith` k
374-
_ -> maybe False (dummyVerifyCmd Nothing authorized) tAuth `seq` VRFailed
377+
Right (fr, k) -> result <$> readTVar (fileStatus fr)
378+
where
379+
result = \case
380+
EntityActive -> XFTPReqCmd fId fr cmd `verifyWith` k
381+
EntityBlocked info -> VRFailed $ BLOCKED info
382+
EntityOff -> noFileAuth
383+
Left _ -> pure noFileAuth
384+
noFileAuth = maybe False (dummyVerifyCmd Nothing authorized) tAuth `seq` VRFailed AUTH
375385
-- TODO verify with DH authorization
376-
req `verifyWith` k = if verifyCmdAuthorization auth_ tAuth authorized k then VRVerified req else VRFailed
386+
req `verifyWith` k = if verifyCmdAuthorization auth_ tAuth authorized k then VRVerified req else VRFailed AUTH
377387

378388
processXFTPRequest :: HTTP2Body -> XFTPRequest -> M (FileResponse, Maybe ServerFile)
379389
processXFTPRequest HTTP2Body {bodyPart} = \case
@@ -390,7 +400,7 @@ processXFTPRequest HTTP2Body {bodyPart} = \case
390400
FACK -> noFile =<< ackFileReception fId fr
391401
-- it should never get to the commands below, they are passed in other constructors of XFTPRequest
392402
FNEW {} -> noFile $ FRErr INTERNAL
393-
PING -> noFile FRPong
403+
PING -> noFile $ FRErr INTERNAL
394404
XFTPReqPing -> noFile FRPong
395405
where
396406
noFile resp = pure (resp, Nothing)
@@ -518,15 +528,24 @@ processXFTPRequest HTTP2Body {bodyPart} = \case
518528
pure FROk
519529

520530
deleteServerFile_ :: FileRec -> M (Either XFTPErrorType ())
521-
deleteServerFile_ FileRec {senderId, fileInfo, filePath} = do
531+
deleteServerFile_ fr@FileRec {senderId} = do
522532
withFileLog (`logDeleteFile` senderId)
523-
runExceptT $ do
533+
deleteOrBlockServerFile_ fr filesDeleted (`deleteFile` senderId)
534+
535+
-- this also deletes the file from storage, but doesn't include it in delete statistics
536+
blockServerFile :: FileRec -> BlockingInfo -> M (Either XFTPErrorType ())
537+
blockServerFile fr@FileRec {senderId} info = do
538+
withFileLog $ \sl -> logBlockFile sl senderId info
539+
deleteOrBlockServerFile_ fr filesBlocked $ \st -> blockFile st senderId info True
540+
541+
deleteOrBlockServerFile_ :: FileRec -> (FileServerStats -> IORef Int) -> (FileStore -> STM (Either XFTPErrorType ())) -> M (Either XFTPErrorType ())
542+
deleteOrBlockServerFile_ FileRec {filePath, fileInfo} stat storeAction = runExceptT $ do
524543
path <- readTVarIO filePath
525544
stats <- asks serverStats
526545
ExceptT $ first (\(_ :: SomeException) -> FILE_IO) <$> try (forM_ path $ \p -> whenM (doesFileExist p) (removeFile p >> deletedStats stats))
527546
st <- asks store
528-
void $ atomically $ deleteFile st senderId
529-
lift $ incFileStat filesDeleted
547+
void $ atomically $ storeAction st
548+
lift $ incFileStat stat
530549
where
531550
deletedStats stats = do
532551
liftIO $ atomicModifyIORef'_ (filesCount stats) (subtract 1)

src/Simplex/FileTransfer/Server/Control.hs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,13 @@ module Simplex.FileTransfer.Server.Control where
66
import qualified Data.Attoparsec.ByteString.Char8 as A
77
import Simplex.FileTransfer.Protocol (XFTPFileId)
88
import Simplex.Messaging.Encoding.String
9-
import Simplex.Messaging.Protocol (BasicAuth)
9+
import Simplex.Messaging.Protocol (BasicAuth, BlockingInfo)
1010

1111
data ControlProtocol
1212
= CPAuth BasicAuth
1313
| CPStatsRTS
1414
| CPDelete XFTPFileId
15+
| CPBlock XFTPFileId BlockingInfo
1516
| CPHelp
1617
| CPQuit
1718
| CPSkip
@@ -21,6 +22,7 @@ instance StrEncoding ControlProtocol where
2122
CPAuth tok -> "auth " <> strEncode tok
2223
CPStatsRTS -> "stats-rts"
2324
CPDelete fId -> strEncode (Str "delete", fId)
25+
CPBlock fId info -> strEncode (Str "block", fId, info)
2426
CPHelp -> "help"
2527
CPQuit -> "quit"
2628
CPSkip -> ""
@@ -29,6 +31,7 @@ instance StrEncoding ControlProtocol where
2931
"auth" -> CPAuth <$> _strP
3032
"stats-rts" -> pure CPStatsRTS
3133
"delete" -> CPDelete <$> _strP
34+
"block" -> CPBlock <$> _strP <*> _strP
3235
"help" -> pure CPHelp
3336
"quit" -> pure CPQuit
3437
"" -> pure CPSkip

src/Simplex/FileTransfer/Server/Stats.hs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ data FileServerStats = FileServerStats
2020
filesUploaded :: IORef Int,
2121
filesExpired :: IORef Int,
2222
filesDeleted :: IORef Int,
23+
filesBlocked :: IORef Int,
2324
filesDownloaded :: PeriodStats,
2425
fileDownloads :: IORef Int,
2526
fileDownloadAcks :: IORef Int,
@@ -34,6 +35,7 @@ data FileServerStatsData = FileServerStatsData
3435
_filesUploaded :: Int,
3536
_filesExpired :: Int,
3637
_filesDeleted :: Int,
38+
_filesBlocked :: Int,
3739
_filesDownloaded :: PeriodStatsData,
3840
_fileDownloads :: Int,
3941
_fileDownloadAcks :: Int,
@@ -50,12 +52,13 @@ newFileServerStats ts = do
5052
filesUploaded <- newIORef 0
5153
filesExpired <- newIORef 0
5254
filesDeleted <- newIORef 0
55+
filesBlocked <- newIORef 0
5356
filesDownloaded <- newPeriodStats
5457
fileDownloads <- newIORef 0
5558
fileDownloadAcks <- newIORef 0
5659
filesCount <- newIORef 0
5760
filesSize <- newIORef 0
58-
pure FileServerStats {fromTime, filesCreated, fileRecipients, filesUploaded, filesExpired, filesDeleted, filesDownloaded, fileDownloads, fileDownloadAcks, filesCount, filesSize}
61+
pure FileServerStats {fromTime, filesCreated, fileRecipients, filesUploaded, filesExpired, filesDeleted, filesBlocked, filesDownloaded, fileDownloads, fileDownloadAcks, filesCount, filesSize}
5962

6063
getFileServerStatsData :: FileServerStats -> IO FileServerStatsData
6164
getFileServerStatsData s = do
@@ -65,12 +68,13 @@ getFileServerStatsData s = do
6568
_filesUploaded <- readIORef $ filesUploaded s
6669
_filesExpired <- readIORef $ filesExpired s
6770
_filesDeleted <- readIORef $ filesDeleted s
71+
_filesBlocked <- readIORef $ filesBlocked s
6872
_filesDownloaded <- getPeriodStatsData $ filesDownloaded s
6973
_fileDownloads <- readIORef $ fileDownloads s
7074
_fileDownloadAcks <- readIORef $ fileDownloadAcks s
7175
_filesCount <- readIORef $ filesCount s
7276
_filesSize <- readIORef $ filesSize s
73-
pure FileServerStatsData {_fromTime, _filesCreated, _fileRecipients, _filesUploaded, _filesExpired, _filesDeleted, _filesDownloaded, _fileDownloads, _fileDownloadAcks, _filesCount, _filesSize}
77+
pure FileServerStatsData {_fromTime, _filesCreated, _fileRecipients, _filesUploaded, _filesExpired, _filesDeleted, _filesBlocked, _filesDownloaded, _fileDownloads, _fileDownloadAcks, _filesCount, _filesSize}
7478

7579
-- this function is not thread safe, it is used on server start only
7680
setFileServerStats :: FileServerStats -> FileServerStatsData -> IO ()
@@ -81,21 +85,23 @@ setFileServerStats s d = do
8185
writeIORef (filesUploaded s) $! _filesUploaded d
8286
writeIORef (filesExpired s) $! _filesExpired d
8387
writeIORef (filesDeleted s) $! _filesDeleted d
88+
writeIORef (filesBlocked s) $! _filesBlocked d
8489
setPeriodStats (filesDownloaded s) $! _filesDownloaded d
8590
writeIORef (fileDownloads s) $! _fileDownloads d
8691
writeIORef (fileDownloadAcks s) $! _fileDownloadAcks d
8792
writeIORef (filesCount s) $! _filesCount d
8893
writeIORef (filesSize s) $! _filesSize d
8994

9095
instance StrEncoding FileServerStatsData where
91-
strEncode FileServerStatsData {_fromTime, _filesCreated, _fileRecipients, _filesUploaded, _filesExpired, _filesDeleted, _filesDownloaded, _fileDownloads, _fileDownloadAcks, _filesCount, _filesSize} =
96+
strEncode FileServerStatsData {_fromTime, _filesCreated, _fileRecipients, _filesUploaded, _filesExpired, _filesDeleted, _filesBlocked, _filesDownloaded, _fileDownloads, _fileDownloadAcks, _filesCount, _filesSize} =
9297
B.unlines
9398
[ "fromTime=" <> strEncode _fromTime,
9499
"filesCreated=" <> strEncode _filesCreated,
95100
"fileRecipients=" <> strEncode _fileRecipients,
96101
"filesUploaded=" <> strEncode _filesUploaded,
97102
"filesExpired=" <> strEncode _filesExpired,
98103
"filesDeleted=" <> strEncode _filesDeleted,
104+
"filesBlocked=" <> strEncode _filesBlocked,
99105
"filesCount=" <> strEncode _filesCount,
100106
"filesSize=" <> strEncode _filesSize,
101107
"filesDownloaded:",
@@ -110,9 +116,10 @@ instance StrEncoding FileServerStatsData where
110116
_filesUploaded <- "filesUploaded=" *> strP <* A.endOfLine
111117
_filesExpired <- "filesExpired=" *> strP <* A.endOfLine <|> pure 0
112118
_filesDeleted <- "filesDeleted=" *> strP <* A.endOfLine
119+
_filesBlocked <- "filesBlocked=" *> strP <* A.endOfLine
113120
_filesCount <- "filesCount=" *> strP <* A.endOfLine <|> pure 0
114121
_filesSize <- "filesSize=" *> strP <* A.endOfLine <|> pure 0
115122
_filesDownloaded <- "filesDownloaded:" *> A.endOfLine *> strP <* A.endOfLine
116123
_fileDownloads <- "fileDownloads=" *> strP <* A.endOfLine
117124
_fileDownloadAcks <- "fileDownloadAcks=" *> strP <* A.endOfLine
118-
pure FileServerStatsData {_fromTime, _filesCreated, _fileRecipients, _filesUploaded, _filesExpired, _filesDeleted, _filesDownloaded, _fileDownloads, _fileDownloadAcks, _filesCount, _filesSize}
125+
pure FileServerStatsData {_fromTime, _filesCreated, _fileRecipients, _filesUploaded, _filesExpired, _filesDeleted, _filesBlocked, _filesDownloaded, _fileDownloads, _fileDownloadAcks, _filesCount, _filesSize}

src/Simplex/FileTransfer/Server/Store.hs

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ module Simplex.FileTransfer.Server.Store
1313
setFilePath,
1414
addRecipient,
1515
deleteFile,
16+
blockFile,
1617
deleteRecipient,
1718
expiredFilePath,
1819
getFile,
@@ -22,6 +23,7 @@ module Simplex.FileTransfer.Server.Store
2223
where
2324

2425
import Control.Concurrent.STM
26+
import Control.Monad
2527
import qualified Data.Attoparsec.ByteString.Char8 as A
2628
import Data.Int (Int64)
2729
import Data.Set (Set)
@@ -30,8 +32,8 @@ import Simplex.FileTransfer.Protocol (FileInfo (..), SFileParty (..), XFTPFileId
3032
import Simplex.FileTransfer.Transport (XFTPErrorType (..))
3133
import qualified Simplex.Messaging.Crypto as C
3234
import Simplex.Messaging.Encoding.String
33-
import Simplex.Messaging.Protocol (RcvPublicAuthKey, RecipientId, SenderId)
34-
import Simplex.Messaging.Server.QueueStore (RoundedSystemTime (..))
35+
import Simplex.Messaging.Protocol (BlockingInfo, RcvPublicAuthKey, RecipientId, SenderId)
36+
import Simplex.Messaging.Server.QueueStore (RoundedSystemTime (..), ServerEntityStatus (..))
3537
import Simplex.Messaging.TMap (TMap)
3638
import qualified Simplex.Messaging.TMap as TM
3739
import Simplex.Messaging.Util (ifM, ($>>=))
@@ -47,7 +49,8 @@ data FileRec = FileRec
4749
fileInfo :: FileInfo,
4850
filePath :: TVar (Maybe FilePath),
4951
recipientIds :: TVar (Set RecipientId),
50-
createdAt :: RoundedSystemTime
52+
createdAt :: RoundedSystemTime,
53+
fileStatus :: TVar ServerEntityStatus
5154
}
5255

5356
fileTimePrecision :: Int64
@@ -78,7 +81,8 @@ newFileRec :: SenderId -> FileInfo -> RoundedSystemTime -> STM FileRec
7881
newFileRec senderId fileInfo createdAt = do
7982
recipientIds <- newTVar S.empty
8083
filePath <- newTVar Nothing
81-
pure FileRec {senderId, fileInfo, filePath, recipientIds, createdAt}
84+
fileStatus <- newTVar EntityActive
85+
pure FileRec {senderId, fileInfo, filePath, recipientIds, createdAt, fileStatus}
8286

8387
setFilePath :: FileStore -> SenderId -> FilePath -> STM (Either XFTPErrorType ())
8488
setFilePath st sId fPath =
@@ -109,6 +113,14 @@ deleteFile FileStore {files, recipients, usedStorage} senderId = do
109113
pure $ Right ()
110114
_ -> pure $ Left AUTH
111115

116+
-- this function must be called after the file is deleted from the file system
117+
blockFile :: FileStore -> SenderId -> BlockingInfo -> Bool -> STM (Either XFTPErrorType ())
118+
blockFile st@FileStore {usedStorage} senderId info deleted =
119+
withFile st senderId $ \FileRec {fileInfo, fileStatus} -> do
120+
when deleted $ modifyTVar' usedStorage $ subtract (fromIntegral $ size fileInfo)
121+
writeTVar fileStatus $! EntityBlocked info
122+
pure $ Right ()
123+
112124
deleteRecipient :: FileStore -> RecipientId -> FileRec -> STM ()
113125
deleteRecipient FileStore {recipients} rId FileRec {recipientIds} = do
114126
TM.delete rId recipients

src/Simplex/FileTransfer/Server/StoreLog.hs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ module Simplex.FileTransfer.Server.StoreLog
1414
logPutFile,
1515
logAddRecipients,
1616
logDeleteFile,
17+
logBlockFile,
1718
logAckFile,
1819
)
1920
where
@@ -31,7 +32,7 @@ import qualified Data.Map.Strict as M
3132
import Simplex.FileTransfer.Protocol (FileInfo (..))
3233
import Simplex.FileTransfer.Server.Store
3334
import Simplex.Messaging.Encoding.String
34-
import Simplex.Messaging.Protocol (RcvPublicAuthKey, RecipientId, SenderId)
35+
import Simplex.Messaging.Protocol (BlockingInfo, RcvPublicAuthKey, RecipientId, SenderId)
3536
import Simplex.Messaging.Server.QueueStore (RoundedSystemTime)
3637
import Simplex.Messaging.Server.StoreLog
3738
import Simplex.Messaging.Util (bshow)
@@ -42,7 +43,8 @@ data FileStoreLogRecord
4243
| PutFile SenderId FilePath
4344
| AddRecipients SenderId (NonEmpty FileRecipient)
4445
| DeleteFile SenderId
45-
| AckFile RecipientId
46+
| BlockFile SenderId BlockingInfo
47+
| AckFile RecipientId -- TODO add senderId as well?
4648
deriving (Show)
4749

4850
instance StrEncoding FileStoreLogRecord where
@@ -51,13 +53,15 @@ instance StrEncoding FileStoreLogRecord where
5153
PutFile sId path -> strEncode (Str "FPUT", sId, path)
5254
AddRecipients sId rcps -> strEncode (Str "FADD", sId, rcps)
5355
DeleteFile sId -> strEncode (Str "FDEL", sId)
56+
BlockFile sId info -> strEncode (Str "FBLK", sId, info)
5457
AckFile rId -> strEncode (Str "FACK", rId)
5558
strP =
5659
A.choice
5760
[ "FNEW " *> (AddFile <$> strP_ <*> strP_ <*> strP),
5861
"FPUT " *> (PutFile <$> strP_ <*> strP),
5962
"FADD " *> (AddRecipients <$> strP_ <*> strP),
6063
"FDEL " *> (DeleteFile <$> strP),
64+
"FBLK " *> (BlockFile <$> strP_ <*> strP),
6165
"FACK " *> (AckFile <$> strP)
6266
]
6367

@@ -76,6 +80,9 @@ logAddRecipients s = logFileStoreRecord s .: AddRecipients
7680
logDeleteFile :: StoreLog 'WriteMode -> SenderId -> IO ()
7781
logDeleteFile s = logFileStoreRecord s . DeleteFile
7882

83+
logBlockFile :: StoreLog 'WriteMode -> SenderId -> BlockingInfo -> IO ()
84+
logBlockFile s fId = logFileStoreRecord s . BlockFile fId
85+
7986
logAckFile :: StoreLog 'WriteMode -> RecipientId -> IO ()
8087
logAckFile s = logFileStoreRecord s . AckFile
8188

@@ -96,6 +103,7 @@ readFileStore f st = mapM_ (addFileLogRecord . LB.toStrict) . LB.lines =<< LB.re
96103
PutFile qId path -> setFilePath st qId path
97104
AddRecipients sId rcps -> runExceptT $ addRecipients sId rcps
98105
DeleteFile sId -> deleteFile st sId
106+
BlockFile sId info -> blockFile st sId info True
99107
AckFile rId -> ackFile st rId
100108
addRecipients sId rcps = mapM_ (ExceptT . addRecipient st sId) rcps
101109

0 commit comments

Comments
 (0)