@@ -46,8 +46,8 @@ route AuthApi where
4646 = VerifyAuth
4747
4848route ClientApi where
49− GET /poll headers {authorization: Text}
50− -> {events: [PollEvent], cursor: Text}
49+ GET {cursor: Int} /poll headers {authorization: Text}
50+ -> {events: [PollEvent], cursor: Int}
5151 = Poll
5252 /messages
5353 POST {to: Text, encryptedBlob: Text, timestamp: Int} / headers {authorization: Text}
@@ -169,7 +169,7 @@ serve = \req -> case req of
169169 t <- now
170170 token <- genToken
171171 let expiry = t + 3600000
172− sigValid <- atomic do
172+ result <- atomic do
173173 pruneExpired t
174174 challenges <- *challenges
175175 sessions <- *sessions
@@ -195,16 +195,17 @@ serve = \req -> case req of
195195 where s.expiresAt > t
196196 yield s
197197 let oldestExpiry = fold (\acc s -> if s.expiresAt < acc then s.expiresAt else acc) expiry existingForKey
198+ let cappedFold = fold (\acc s ->
199+ if not acc.removed && s.pubkey == pubkey && s.expiresAt == oldestExpiry
200+ then {removed: True {}, sessions: acc.sessions}
201+ else {removed: acc.removed, sessions: union acc.sessions [s]}) {removed: False {}, sessions: []} sessions
198202 let sessionsCapped = if count existingForKey >= 10
199− then do
200− s <- sessions
201− where not (s.pubkey == pubkey && s.expiresAt == oldestExpiry)
202− yield s
203+ then cappedFold.sessions
203204 else sessions
204205 set *sessions = if sigValid
205206 then union sessionsCapped [{token: token, pubkey: pubkey, expiresAt: expiry}]
206207 else sessions
207− set *presence = if sigValid
208+ let updatedPresence = if sigValid
208209 then union
209210 (do
210211 p <- presence
@@ -212,25 +213,25 @@ serve = \req -> case req of
212213 yield p)
213214 [{pubkey: pubkey, server: serverHost, expiresAt: expiry}]
214215 else presence
215− yield sigValid
216− if sigValid
216+ set *presence = updatedPresence
217+ let peerServers = if sigValid && not (federationSecret == "")
218+ then fold (\acc p ->
219+ if p.server == serverHost then acc
220+ else if count (do a <- acc; where a == p.server; yield a) > 0 then acc
221+ else union acc [p.server]) [] updatedPresence
222+ else []
223+ yield {sigValid: sigValid, peerServers: peerServers}
224+ if result.sigValid
217225 then respond {token: token, expiresAt: expiry}
218226 else respond {token: "", expiresAt: 0}
219− if sigValid && not (federationSecret == "")
220− then do
221− peerServers <- atomic do
222− presence <- *presence
223− yield (fold (\acc p ->
224− if p.server == serverHost then acc
225− else if count (do a <- acc; where a == p.server; yield a) > 0 then acc
226− else union acc [p.server]) [] presence)
227− sendToEach peerServers (\server ->
228− httpPost (federationScheme ++ server ++ "/federation/gossip")
229− {authorization: "Bearer " ++ federationSecret}
230− {events: [{eventType: "online", pubkey: pubkey, server: serverHost, timestamp: t, ttl: 3600}], hopsRemaining: 2})
227+ if result.sigValid && not (federationSecret == "")
228+ then sendToEach result.peerServers (\server ->
229+ httpPost (federationScheme ++ server ++ "/federation/gossip")
230+ {authorization: "Bearer " ++ federationSecret}
231+ {events: [{eventType: "online", pubkey: pubkey, server: serverHost, timestamp: t, ttl: 3600}], hopsRemaining: 2})
231232 else yield {}
232233
233− Poll {authorization, respond} -> do
234+ Poll {cursor, authorization, respond} -> do
234235 t <- now
235236 let token = extractToken authorization
236237 result <- atomic do
@@ -244,20 +245,22 @@ serve = \req -> case req of
244245 m <- messages
245246 where m.toKey == s.pubkey
246247 where m.timestamp >= t - messageMaxAge
248+ where m.timestamp >= cursor
247249 yield {id: m.messageId, eventType: "message",
248250 payload: toJson {toKey: m.toKey, fromKey: m.fromKey,
249251 encryptedBlob: m.encryptedBlob,
250252 timestamp: m.timestamp}}
251− let cursor = case session of
252− Nothing {} -> "unauthorized"
253− _ -> show t
254− yield {events: evts, cursor: cursor}
253+ let newCursor = case session of
254+ Nothing {} -> 0
255+ _ -> t
256+ yield {events: evts, cursor: newCursor}
255257 respond result
256258
257259 SendMessage {to, encryptedBlob, timestamp, authorization, respond} -> do
258260 t <- now
259261 msgId <- genToken
260262 let clampedTs = if timestamp > t then t else if timestamp < t - messageMaxAge then t else timestamp
263+ let validInput = not (to == "") && not (encryptedBlob == "")
261264 result <- atomic do
262265 pruneExpired t
263266 sessions <- *sessions
@@ -266,9 +269,11 @@ serve = \req -> case req of
266269 let session = lookupSession sessions (extractToken authorization) t
267270 set *messages = case session of
268271 Nothing {} -> messages
269− Just {value: s} -> union messages
270− [{messageId: msgId, toKey: to, fromKey: s.pubkey,
271− encryptedBlob: encryptedBlob, timestamp: clampedTs, originServer: ""}]
272+ Just {value: s} -> if validInput
273+ then union messages
274+ [{messageId: msgId, toKey: to, fromKey: s.pubkey,
275+ encryptedBlob: encryptedBlob, timestamp: clampedTs, originServer: ""}]
276+ else messages
272277 let localOnline = case session of
273278 Nothing {} -> False {}
274279 Just {value: _} -> count (do
@@ -290,7 +295,8 @@ serve = \req -> case req of
290295 Just {value: s} -> s.pubkey
291296 let status = case session of
292297 Nothing {} -> "unauthorized"
293− _ -> if localOnline then "delivered"
298+ _ -> if not validInput then "invalid"
299+ else if localOnline then "delivered"
294300 else if count remoteServers > 0 then "federated"
295301 else "queued"
296302 yield {status: status, remoteServers: remoteServers, fromKey: fromKey}
@@ -302,7 +308,7 @@ serve = \req -> case req of
302308 timestamp: clampedTs, originServer: serverHost, messageId: msgId}
303309 yield (if anyOk then "federated" else "queued")
304310 else yield result.status
305− respond {status: finalStatus, messageId: (if result.status == "unauthorized" then "" else msgId)}
311+ respond {status: finalStatus, messageId: (if result.status == "unauthorized" then "" else if result.status == "invalid" then "" else msgId)}
306312
307313 AckMessages {messageIds, authorization, respond} -> do
308314 t <- now
@@ -477,6 +483,7 @@ serve = \req -> case req of
477483 t <- now
478484 let authorized = verifyFederationAuth authorization
479485 let validOrigin = not (originServer == "") && not (originServer == serverHost)
486+ let validFields = not (toKey == "") && not (fromKey == "") && not (encryptedBlob == "") && not (messageId == "")
480487 let clampedTs = if timestamp > t then t else if timestamp < t - messageMaxAge then t else timestamp
481488 atomic do
482489 pruneExpired t
@@ -486,13 +493,13 @@ serve = \req -> case req of
486493 where m.messageId == messageId
487494 where m.timestamp >= t - messageMaxAge
488495 yield m) > 0
489− let shouldInsert = authorized && validOrigin && not exists
496+ let shouldInsert = authorized && validOrigin && validFields && not exists
490497 set *messages = if shouldInsert
491498 then union messages
492499 [{messageId: messageId, toKey: toKey, fromKey: fromKey,
493500 encryptedBlob: encryptedBlob, timestamp: clampedTs, originServer: originServer}]
494501 else messages
495− respond {ok: authorized && validOrigin}
502+ respond {ok: authorized && validOrigin && validFields}
496503
497504 FederationAck {messageIds, originServer, fromServer, authorization, respond} -> do
498505 t <- now
@@ -513,12 +520,6 @@ serve = \req -> case req of
513520 where p.server == serverHost
514521 where p.expiresAt > t
515522 yield p) == 0
516− where count (do
517− p <- presence
518− where p.pubkey == m.toKey
519− where p.server == fromServer
520− where p.expiresAt > t
521− yield p) > 0
522523 yield m)
523524 else messages
524525 respond {ok: validAck}