@@ -87,6 +87,8 @@ impl ToJSON MessageStatus where
87 MsUnauthorized {} -> "unauthorized"
87 MsUnauthorized {} -> "unauthorized"
88 MsRejected {} -> "rejected")
88 MsRejected {} -> "rejected")
90+data DeliveryStatus = DsDelivered {} | DsFederated {} | DsQueued {}
90isValidBoundedHex = \maxLen s -> isValidHex s && not (tooLong s maxLen) && s == normalizeHex s
92isValidBoundedHex = \maxLen s -> isValidHex s && not (tooLong s maxLen) && s == normalizeHex s
91type PubkeyHex = Text where isValidBoundedHex maxPubkeyLen
93type PubkeyHex = Text where isValidBoundedHex maxPubkeyLen
92type BlobHex = Text where isValidBoundedHex maxBlobLen
94type BlobHex = Text where isValidBoundedHex maxBlobLen
@@ -108,10 +110,9 @@ type PresenceEntry = {pubkey: PubkeyHex, server: AnyServerName, expiresAt: Times
108type ForwardEntry = {messageId: MessageIdHex, toServer: ServerName, createdAt: Timestamp, toKey: PubkeyHex, fromKey: PubkeyHex, encryptedBlob: BlobHex, timestamp: Timestamp}
110type ForwardEntry = {messageId: MessageIdHex, toServer: ServerName, createdAt: Timestamp, toKey: PubkeyHex, fromKey: PubkeyHex, encryptedBlob: BlobHex, timestamp: Timestamp}
109type AckedEntry = {messageId: MessageIdHex, toKey: PubkeyHex, ackedAt: Timestamp, originServer: ServerName}
111type AckedEntry = {messageId: MessageIdHex, toKey: PubkeyHex, ackedAt: Timestamp, originServer: ServerName}
110data AuthResult = Authorized {pubkey: PubkeyHex} | Unauthorized {}
112data AuthResult = Authorized {pubkey: PubkeyHex} | Unauthorized {}
111−data SendResult = SendUnauthorized {} | SendRejected {} | SendDone {status: MessageStatus, remoteServers: [ServerName], fromKey: PubkeyHex}
113+data SendResult = SendFailed {status: MessageStatus} | SendDone {delivery: DeliveryStatus, remoteServers: [ServerName], fromKey: PubkeyHex}
112data VerifyResult = VerifyFailed {} | VerifyOk {peerServers: [ServerName]}
114data VerifyResult = VerifyFailed {} | VerifyOk {peerServers: [ServerName]}
113data AckResult = AckUnauthorized {} | AckDone {pendingAcks: [AckedEntry], evictedEntries: [AckedEntry]}
115data AckResult = AckUnauthorized {} | AckDone {pendingAcks: [AckedEntry], evictedEntries: [AckedEntry]}
114−data LookupResult = LookupUnauthorized {} | LookupDone {server: AnyServerName}
115type PollResponse = {events: [PollEvent], cursor: Timestamp, authorized: Bool}
116type PollResponse = {events: [PollEvent], cursor: Timestamp, authorized: Bool}
@@ -375,6 +376,11 @@ clampTtl = clamp 1 maxPresenceTtl
376clampTimestamp = \timestamp t -> clamp (t - messageMaxAge) t timestamp
377clampTimestamp = \timestamp t -> clamp (t - messageMaxAge) t timestamp
379+deliveryToMessageStatus = \ds -> case ds of
380+ DsDelivered {} -> MsDelivered {}
381+ DsFederated {} -> MsFederated {}
382+ DsQueued {} -> MsQueued {}
378nextSeqTs = \t seqCounter -> if t > seqCounter then t else seqCounter + 1
384nextSeqTs = \t seqCounter -> if t > seqCounter then t else seqCounter + 1
380takeN = \items n -> (fold (\acc e -> if acc.n >= n then acc else {n: acc.n + 1, items: union acc.items [e]}) {n: 0, items: []} items).items
386takeN = \items n -> (fold (\acc e -> if acc.n >= n then acc else {n: acc.n + 1, items: union acc.items [e]}) {n: 0, items: []} items).items
@@ -518,10 +524,11 @@ validGossipEvents = \events t ->
518 then [{pubkey: e.pubkey, server: e.server, expiresAt: expiresAt, timestamp: e.timestamp, ttl: ttl}]
524 then [{pubkey: e.pubkey, server: e.server, expiresAt: expiresAt, timestamp: e.timestamp, ttl: ttl}]
521− in {
527+ in fold (\acc e ->
522− online: do e <- events; case e.eventType of GossipOnline {} -> validate e; _ -> [],
528+ case e.eventType of
523− offline: do e <- events; case e.eventType of GossipOffline {} -> validate e; _ -> []
529+ GossipOnline {} -> {online: union acc.online (validate e), offline: acc.offline}
524− }
530+ GossipOffline {} -> {online: acc.online, offline: union acc.offline (validate e)}
531+ _ -> acc) {online: [], offline: []} events
526sendToEach : [ServerName] -> (ServerName -> (a {})) -> a {}
533sendToEach : [ServerName] -> (ServerName -> (a {})) -> a {}
527sendToEach = \servers action ->
534sendToEach = \servers action ->
@@ -831,11 +838,11 @@ serve = \req -> case req of
831 auth <- authenticateRequest authorization t
838 auth <- authenticateRequest authorization t
832 case auth of
839 case auth of
833 Unauthorized {} ->
840 Unauthorized {} ->
834− yield {pr: pr, send: SendUnauthorized {}}
841+ yield {pr: pr, send: SendFailed {status: MsUnauthorized {}}}
835 Authorized {pubkey: fromKey} -> do
842 Authorized {pubkey: fromKey} -> do
836 messages <- *messages
843 messages <- *messages
837 if countMessagesFor messages to t >= maxMessagesPerRecipient
844 if countMessagesFor messages to t >= maxMessagesPerRecipient
838− then yield {pr: pr, send: SendRejected {}}
845+ then yield {pr: pr, send: SendFailed {status: MsRejected {}}}
840 presence <- *presence
847 presence <- *presence
841 seqCounter <- *seqCounter
848 seqCounter <- *seqCounter
@@ -850,15 +857,15 @@ serve = \req -> case req of
850 forwards <- *forwards
857 forwards <- *forwards
851 let forwardServers = takeN remoteServers (maxForwardsEntries - count forwards)
858 let forwardServers = takeN remoteServers (maxForwardsEntries - count forwards)
852 let shouldForward = federationEnabled && count forwardServers > 0
859 let shouldForward = federationEnabled && count forwardServers > 0
853− let status = if localOnline then MsDelivered {}
860+ let delivery = if localOnline then DsDelivered {}
854− else if shouldForward then MsFederated {}
861+ else if shouldForward then DsFederated {}
855− else MsQueued {}
862+ else DsQueued {}
856 set *forwards = if shouldForward
863 set *forwards = if shouldForward
857 then union forwards (do s <- forwardServers; yield {messageId: msgId, toServer: s, createdAt: t, toKey: to, fromKey: fromKey, encryptedBlob: encryptedBlob, timestamp: clampedTs})
864 then union forwards (do s <- forwardServers; yield {messageId: msgId, toServer: s, createdAt: t, toKey: to, fromKey: fromKey, encryptedBlob: encryptedBlob, timestamp: clampedTs})
858 else forwards
865 else forwards
859− yield {pr: pr, send: SendDone {status: status, remoteServers: remoteServers, fromKey: fromKey}}
866+ yield {pr: pr, send: SendDone {delivery: delivery, remoteServers: remoteServers, fromKey: fromKey}}
860 case result.send of
867 case result.send of
861− SendDone {status, remoteServers, fromKey} ->
868+ SendDone {delivery, remoteServers, fromKey} ->
863 when (count remoteServers > 0 && federationEnabled) (do
870 when (count remoteServers > 0 && federationEnabled) (do
864 sendToEach remoteServers (\server -> do
871 sendToEach remoteServers (\server -> do
@@ -867,15 +874,14 @@ serve = \req -> case req of
867 timestamp: clampedTs, originServer: serverHostN, messageId: msgId,
874 timestamp: clampedTs, originServer: serverHostN, messageId: msgId,
868 authorization: federationAuth})
875 authorization: federationAuth})
869 whenOk fwdResult (removeForward msgId server))
876 whenOk fwdResult (removeForward msgId server))
870− case status of
877+ case delivery of
871− MsQueued {} -> println ("sendMessage: forwards at capacity (" ++ show maxForwardsEntries ++ "), skipping forward tracking")
878+ DsQueued {} -> println ("sendMessage: forwards at capacity (" ++ show maxForwardsEntries ++ "), skipping forward tracking")
872 _ -> yield {})
879 _ -> yield {})
873 broadcastOfflineGossip t result.pr.peerServers result.pr.expiredLocal)
880 broadcastOfflineGossip t result.pr.peerServers result.pr.expiredLocal)
874 _ -> forkBroadcastOffline t result.pr
881 _ -> forkBroadcastOffline t result.pr
875 yield (case result.send of
882 yield (case result.send of
876− SendUnauthorized {} -> respond {status: MsUnauthorized {}, messageId: ""}
877− SendRejected {} -> respond {status: MsRejected {}, messageId: ""}
883+ SendFailed {status} -> respond {status: status, messageId: ""}
878− SendDone {status} -> respond {status: status, messageId: msgId})
884+ SendDone {delivery} -> respond {status: deliveryToMessageStatus delivery, messageId: msgId})
880 AckMessages {messageIds, authorization, respond} -> do
886 AckMessages {messageIds, authorization, respond} -> do
@@ -921,7 +927,7 @@ serve = \req -> case req of
921 auth <- authenticateRequest authorization t
927 auth <- authenticateRequest authorization t
922 case auth of
928 case auth of
923 Unauthorized {} ->
929 Unauthorized {} ->
924− yield {pr: pr, lookup: LookupUnauthorized {}}
930+ yield {pr: pr, server: "", authorized: False {}}
925 Authorized _ -> do
931 Authorized _ -> do
926 presence <- *presence
932 presence <- *presence
927 let valid = do
933 let valid = do
@@ -929,16 +935,15 @@ serve = \req -> case req of
929 where p.pubkey == pubkey && p.expiresAt > t
935 where p.pubkey == pubkey && p.expiresAt > t
931 let localOnline = any valid (\p -> isLocalServer p.server)
937 let localOnline = any valid (\p -> isLocalServer p.server)
938+ let remoteValid = do p <- valid; where not (isLocalServer p.server); yield p
932− let bestRemote = fold (\acc p ->
939+ let bestRemote = fold (\acc p -> case acc of
933− let dominated = case acc of Nothing {} -> True {}; Just {value: v} -> p.expiresAt > v.expiresAt in
940+ Nothing {} -> Just {value: p}
934− if not (isLocalServer p.server) && dominated then Just {value: p} else acc) Nothing {} valid
941+ Just {value: v} -> if p.expiresAt > v.expiresAt then Just {value: p} else acc) Nothing {} remoteValid
935 let foundServer = if localOnline then serverHostN
942 let foundServer = if localOnline then serverHostN
936 else case bestRemote of Just {value: p} -> p.server; Nothing {} -> ""
943 else case bestRemote of Just {value: p} -> p.server; Nothing {} -> ""
937− yield {pr: pr, lookup: LookupDone {server: foundServer}}
944+ yield {pr: pr, server: foundServer, authorized: True {}}
938 forkBroadcastOffline t result.pr
945 forkBroadcastOffline t result.pr
939− yield (case result.lookup of
946+ yield (respond {server: result.server, authorized: result.authorized})
940− LookupUnauthorized {} -> respond {server: "", authorized: False {}}
941− LookupDone {server} -> respond {server: server, authorized: True {}})
943 RecvGossip {events, hopsRemaining, fromServer, authorization, respond} -> do
948 RecvGossip {events, hopsRemaining, fromServer, authorization, respond} -> do