@@ -442,7 +442,7 @@ id = \x -> x
442442filter = \xs pred -> do x <- xs; where pred x; yield x
443443removeWhere = \xs pred -> filter xs (\x -> not (pred x))
444444pluck = \xs f -> do x <- xs; yield (f x)
445−mapWhere = \xs pred f -> pluck (filter xs pred) f
445+mapWhere = \xs pred -> pluck (filter xs pred)
446446countWhere = \xs pred -> count (filter xs pred)
447447any : [a] -> (a -> Bool) -> Bool
448448any = \xs pred -> countWhere xs pred > 0
@@ -550,8 +550,7 @@ tooLong = \s maxLen -> count (chars s) > maxLen
550550
551551isValidHex : Text -> Bool
552552isValidHex = \s ->
553− let cs = chars s in
554− count cs > 0 && mod (count cs) 2 == 0 && all cs (\c -> contains c "0123456789abcdef")
553+ count (chars s) > 0 && mod (count (chars s)) 2 == 0 && all (chars s) (\c -> contains c "0123456789abcdef")
555554
556555normalizeHex : Text -> Text
557556normalizeHex = \s -> bytesToHex (bytesFromHex s)
@@ -708,8 +707,8 @@ checkWindowRate = \windowRef countRef windowMs maxRate t -> atomic do
708707 set *countRef = c + 1
709708 yield False
710709
711−checkGlobalRate = \t -> checkWindowRate globalRateWindowStart globalRateCount globalRateWindowMs maxGlobalRequestRate t
712−checkAuthGlobalRate = \t -> checkWindowRate authGlobalRateWindowStart authGlobalRateCount authGlobalRateWindowMs maxAuthGlobalRate t
710+checkGlobalRate = checkWindowRate globalRateWindowStart globalRateCount globalRateWindowMs maxGlobalRequestRate
711+checkAuthGlobalRate = checkWindowRate authGlobalRateWindowStart authGlobalRateCount authGlobalRateWindowMs maxAuthGlobalRate
713712
714713checkIpRateGate = \ip t ->
715714 if ip == "" then yield False
@@ -828,8 +827,7 @@ pruneExpired = \t -> atomic do
828827 -- presence: remember locally-expired keys for offline gossip
829828 pres <- *presence
830829 pruneRef presence (\p -> p.expiresAt > t)
831− newPres <- *presence
832− yield (filter pres (\p -> p.expiresAt <= t && isLocalServer p.server && none newPres (\k -> isLocalServer k.server && k.pubkey == p.pubkey)))
830+ yield (filter pres (\p -> p.expiresAt <= t && isLocalServer p.server && none pres (\k -> isLocalServer k.server && k.pubkey == p.pubkey && k.expiresAt > t)))
833831
834832-- ============================================
835833-- Federation gossip
@@ -875,9 +873,8 @@ sendBatchedGossip = \peers events hops -> forEach peers id (\server -> do
875873
876874broadcastOfflineGossip = \t expiredLocal -> do
877875 presence <- *presence
878− let peers = knownPeerServersFromPresence presence
879876 when (federationEnabled && count expiredLocal > 0)
880− (sendBatchedGossip peers (pluck expiredLocal (\p -> mkGossip (GossipOffline {}) p.pubkey t offlineGossipMinTtl)) gossipOriginHops)
877+ (sendBatchedGossip (knownPeerServersFromPresence presence) (pluck expiredLocal (\p -> mkGossip (GossipOffline {}) p.pubkey t offlineGossipMinTtl)) gossipOriginHops)
881878
882879-- ============================================
883880-- Forward retries and acks
@@ -1026,24 +1023,21 @@ handleRevoke = \targetToken authorization clientIp respond -> do
10261023
10271024handleLogout = \authorization clientIp respond -> do
10281025 t <- now
1029− case extractToken authorization of
1030− Nothing {} -> yield (respond {ok: False {}, authorized: False {}})
1031− Just {value: token} -> do
1032− removed <- atomic (withSessionAuth authorization t clientIp
1033− (yield {ok: False {}, pubkey: "", peers: []})
1034− (\pubkey -> do
1035− ses <- *sessions
1036− set *sessions = removeWhere ses (\s -> s.token == token)
1037− after <- *sessions
1038− peers <- if any after (\s -> s.pubkey == pubkey) then yield []
1039− else do
1040− pres <- *presence
1041− set *presence = removeWhere pres (\p -> p.pubkey == pubkey && isLocalServer p.server)
1042− yield (knownPeerServersFromPresence pres)
1043− yield {ok: True {}, pubkey: pubkey, peers: peers}))
1044− when (removed.ok && federationEnabled)
1045− (fork (sendBatchedGossip removed.peers [mkGossip (GossipOffline {}) removed.pubkey t offlineGossipMinTtl] gossipOriginHops))
1046− yield (respond {ok: removed.ok, authorized: removed.ok})
1026+ removed <- atomic (withSessionAuth authorization t clientIp
1027+ (yield {ok: False {}, pubkey: "", peers: []})
1028+ (\pubkey -> do
1029+ ses <- *sessions
1030+ set *sessions = removeWhere ses (\s -> Just {value: s.token} == extractToken authorization)
1031+ after <- *sessions
1032+ peers <- if any after (\s -> s.pubkey == pubkey) then yield []
1033+ else do
1034+ pres <- *presence
1035+ set *presence = removeWhere pres (\p -> p.pubkey == pubkey && isLocalServer p.server)
1036+ yield (knownPeerServersFromPresence pres)
1037+ yield {ok: True {}, pubkey: pubkey, peers: peers}))
1038+ when (removed.ok && federationEnabled)
1039+ (fork (sendBatchedGossip removed.peers [mkGossip (GossipOffline {}) removed.pubkey t offlineGossipMinTtl] gossipOriginHops))
1040+ yield (respond {ok: removed.ok, authorized: removed.ok})
10471041
10481042-- ============================================
10491043-- Handler: poll
@@ -1057,7 +1051,7 @@ handlePoll = \cursor generation watchKeys authorization clientIp respond -> do
10571051 let cursorWasReset = generation != serverGen && cursor > 0
10581052 if dp >= 2 then yield (respond (emptyPollResp 0 (True {}) (False {}) (True {}) serverGen 0 cursorWasReset))
10591053 else if g then yield (respond (emptyPollResp 0 (False {}) (True {}) (False {}) serverGen globalRateWindowMs cursorWasReset))
1060− else pollOnce authorization (if generation != serverGen then 0 else cursor) cursorWasReset (takeRelation maxWatchKeys watchKeys) clientIp serverGen respond t
1054+ else pollOnce authorization (if cursorWasReset then 0 else cursor) cursorWasReset (takeRelation maxWatchKeys watchKeys) clientIp serverGen respond t
10611055
10621056pollOnce = \authorization cursor cursorWasReset watchKeys clientIp serverGen respond t -> do
10631057 setup <- atomic (withSessionAuth authorization t clientIp
@@ -1330,10 +1324,9 @@ handleSendBatch = \messages authorization clientIp respond -> do
13301324 results <- mapEach (takeRelation maxBatchSize messages) id (\m -> do
13311325 effMsgId <- if m.messageId != "" then yield m.messageId else genToken
13321326 let clampedTs = clampTimestamp m.timestamp t
1333− let clampedPri = clamp 0 maxMessagePriority m.priority
13341327 r <- if t - m.timestamp > messageMaxAge
13351328 then yield {status: MsRejected {}, remoteServers: []}
1336− else atomic do sendMessageCore senderKey m.to m.encryptedBlob clampedTs effMsgId m.expiresInMs clampedPri t
1329+ else atomic do sendMessageCore senderKey m.to m.encryptedBlob clampedTs effMsgId m.expiresInMs (clamp 0 maxMessagePriority m.priority) t
13371330 when federationEnabled
13381331 (fork (forkForwardsTo effMsgId m.to m.encryptedBlob clampedTs r.remoteServers))
13391332 yield {messageId: effMsgId, status: r.status})
@@ -1373,7 +1366,7 @@ handleRecvGossip = \events hopsRemaining fromServer requestTimestamp correlation
13731366 let online = mapWhere validEvents (\v -> case v.eventType of GossipOnline {} -> True {}; _ -> False {}) (\v -> {pubkey: v.pubkey, server: v.server, expiresAt: v.expiresAt, timestamp: v.timestamp})
13741367 let offline = mapWhere validEvents (\v -> case v.eventType of GossipOffline {} -> True {}; _ -> False {}) (\v -> {pubkey: v.pubkey, server: v.server, timestamp: v.timestamp})
13751368 set *presence = capList (mergePresence (filter pres (\p -> none offline (\o -> o.pubkey == p.pubkey && o.server == p.server && o.timestamp > p.timestamp))) online) maxPresenceEntries (\p -> p.expiresAt)
1376− yield (mapWhere online (\e -> not (isLocalServer e.server) && none pres (\p -> p.pubkey == e.pubkey && p.server == e.server && p.expiresAt >= e.expiresAt)) (\e -> {pubkey: e.pubkey, server: e.server}))
1369+ yield (mapWhere online (\e -> none pres (\p -> p.pubkey == e.pubkey && p.server == e.server && p.expiresAt >= e.expiresAt)) (\e -> {pubkey: e.pubkey, server: e.server}))
13771370 fork (forEach newlyOnline (\tgt -> tgt.pubkey ++ "|" ++ tgt.server) (\tgt -> forwardQueuedToServer tgt.pubkey tgt.server t))
13781371 yield (respond {ok: True {}, error: "", retryAfterMs: 0}))
13791372
@@ -1496,7 +1489,7 @@ handleFederationDeliveryReceipt = \messageIds toKeys fromServer requestTimestamp
14961489 yield (respond {ok: True {}, error: "", receivedCount: count pairs, retryAfterMs: 0}))
14971490
14981491-- Index a list (1-based by virtue of `count` of the partial fold).
1499−withIndex = \xs -> fold (\acc x -> union acc [{i: count acc, v: x}]) [] xs
1492+withIndex = fold (\acc x -> union acc [{i: count acc, v: x}]) []
15001493
15011494-- ============================================
15021495-- Maintenance handlers
@@ -1740,8 +1733,7 @@ handleTopRecipients = \limit authorization respond -> withMaintAuth authorizatio
17401733 (do
17411734 msgs <- *messages
17421735 let counts = pluck (unique (pluck msgs (\m -> m.toKey))) (\pk -> {pubkey: pk, count: countWhere msgs (\m -> m.toKey == pk)})
1743− let sorted = sortBy (\c -> 0 - c.count) counts
1744− yield (respond {recipients: takeRelation (if limit <= 0 then 20 else clamp 1 100 limit) sorted, ok: True {}, total: count counts}))
1736+ yield (respond {recipients: takeRelation (if limit <= 0 then 20 else clamp 1 100 limit) (sortBy (\c -> 0 - c.count) counts), ok: True {}, total: count counts}))
17451737
17461738handleStatsForwards = \authorization respond -> withMaintAuth authorization
17471739 (yield (respond {servers: [], ok: False {}, total: 0}))