on master help sign in
Fix server gossip race, DoS, validation ordering, and status reporting
964dd22iko
client/ApiClient.fs+6−1
@@ -107,7 +107,12 @@ module ApiClient =
107 if e.ValueKind = JsonValueKind.Object then
107 if e.ValueKind = JsonValueKind.Object then
108 let id = e.GetProperty("id").GetString()
108 let id = e.GetProperty("id").GetString()
109 let evtType = e.GetProperty("eventType").GetString()
109 let evtType = e.GetProperty("eventType").GetString()
110− let payload = e.GetProperty("payload").GetString()
110+ let payloadEl = e.GetProperty("payload")
111+ let payload =
112+ if payloadEl.ValueKind = JsonValueKind.String then
113+ payloadEl.GetString()
114+ else
115+ payloadEl.GetRawText()
111 yield (id, evtType, payload) ]
116 yield (id, evtType, payload) ]
112
117
113 return (events, cursor)
118 return (events, cursor)
client/App.fs+14−7
@@ -47,7 +47,8 @@ module App =
47 NewPubkey: string
47 NewPubkey: string
48 NewNickname: string
48 NewNickname: string
49 Error: string option
49 Error: string option
50− PollCursor: int64 }
50+ PollCursor: int64
51+ LastSendStatus: string }
51
52
52 // ── Msg ──
53 // ── Msg ──
53
54
@@ -114,7 +115,8 @@ module App =
114 NewPubkey = ""
115 NewPubkey = ""
115 NewNickname = ""
116 NewNickname = ""
116 Error = None
117 Error = None
117− PollCursor = 0L },
118+ PollCursor = 0L
119+ LastSendStatus = "" },
118 []
120 []
119
121
120 // ── Update ──
122 // ── Update ──
@@ -170,7 +172,7 @@ module App =
170 [ CmdSend(model.ServerUrl, token, pk, priv, model.PubKeyHex, pk, text) ]
172 [ CmdSend(model.ServerUrl, token, pk, priv, model.PubKeyHex, pk, text) ]
171 | _ -> model, []
173 | _ -> model, []
172
174
173− | Sent _ -> model, []
175+ | Sent(_, status) -> { model with LastSendStatus = status }, []
174
176
175 | PollResult(incoming, status, newCursor) ->
177 | PollResult(incoming, status, newCursor) ->
176 let model' =
178 let model' =
@@ -290,8 +292,8 @@ module App =
290
292
291 let blob = Crypto.encrypt privKey pubKey recipPub payload
293 let blob = Crypto.encrypt privKey pubKey recipPub payload
292 let blobHex = Crypto.toHex blob
294 let blobHex = Crypto.toHex blob
293− let! (_, msgId) = ApiClient.sendMessage url token toHex blobHex ts
295+ let! (status, msgId) = ApiClient.sendMessage url token toHex blobHex ts
294− return Sent(toHex, msgId)
296+ return Sent(toHex, $"{status}:{msgId}")
295 with ex ->
297 with ex ->
296 return DismissError // TODO: show send errors properly
298 return DismissError // TODO: show send errors properly
297 })
299 })
@@ -309,7 +311,12 @@ module App =
309 try
311 try
310 let doc = JsonDocument.Parse(payload)
312 let doc = JsonDocument.Parse(payload)
311 let blobHex = doc.RootElement.GetProperty("encryptedBlob").GetString()
313 let blobHex = doc.RootElement.GetProperty("encryptedBlob").GetString()
312− let ts = doc.RootElement.GetProperty("timestamp").GetInt64()
314+ let tsEl = doc.RootElement.GetProperty("timestamp")
315+ let ts =
316+ if tsEl.ValueKind = JsonValueKind.String then
317+ Int64.Parse(tsEl.GetString())
318+ else
319+ tsEl.GetInt64()
313 let blob = Crypto.fromHex blobHex
320 let blob = Crypto.fromHex blobHex
314
321
315 match Crypto.decrypt privKey blob with
322 match Crypto.decrypt privKey blob with
@@ -490,7 +497,7 @@ module App =
490 .textColor(Colors.Red)
497 .textColor(Colors.Red)
491 .font(size = 11.)
498 .font(size = 11.)
492
499
493− Label($"[{msgs.Length} message(s), compose: \"{model.ComposeText}\"]")
500+ Label($"[{msgs.Length} message(s), compose: \"{model.ComposeText}\", send: \"{model.LastSendStatus}\"]")
494 .textColor(Colors.Gray)
501 .textColor(Colors.Gray)
495 .font(size = 10.)
502 .font(size = 10.)
496
503
client/bin/Debug/net10.0-ios/iossimulator-arm64/Skrepka.app/Skrepka.dll

Binary file not shown.

client/bin/Debug/net10.0-ios/iossimulator-arm64/Skrepka.app/Skrepka.pdb

Binary file not shown.

client/bin/Debug/net10.0-ios/iossimulator-arm64/Skrepka.app/_CodeSignature/CodeResources+4−4
@@ -162,11 +162,11 @@
162 </data>
162 </data>
163 <key>Skrepka.dll</key>
163 <key>Skrepka.dll</key>
164 <data>
164 <data>
165− ihxkdJmILCX2rvNfx8IAKfrjiRw=
165+ 27gfq5dZ4/eTxH0f4bw8bUDiS2M=
166 </data>
166 </data>
167 <key>Skrepka.pdb</key>
167 <key>Skrepka.pdb</key>
168 <data>
168 <data>
169− 5Ef95J0Y0SPaPbiv94B4OI5dvt4=
169+ D3LomwjioLVqeXPIjplXlbfcC4g=
170 </data>
170 </data>
171 <key>Sodium.Core.dll</key>
171 <key>Sodium.Core.dll</key>
172 <data>
172 <data>
@@ -1382,14 +1382,14 @@
1382 <dict>
1382 <dict>
1383 <key>hash2</key>
1383 <key>hash2</key>
1384 <data>
1384 <data>
1385− jA699ETKlhsSyP0dr9yKgsVa246KniFBNn6h+H093lI=
1385+ 8/yNqmUXv1KVEP6mJx7cV8riXCRkhMvDIW09gUJw6wk=
1386 </data>
1386 </data>
1387 </dict>
1387 </dict>
1388 <key>Skrepka.pdb</key>
1388 <key>Skrepka.pdb</key>
1389 <dict>
1389 <dict>
1390 <key>hash2</key>
1390 <key>hash2</key>
1391 <data>
1391 <data>
1392− 9xCu8W4xbM0EJu+r0fyBhBs95Lz7i0LEqYeCvuPHiYQ=
1392+ uF/IJAfKHG3onHFR8ScB160+btmHZaJqGiJNzR07nu0=
1393 </data>
1393 </data>
1394 </dict>
1394 </dict>
1395 <key>Sodium.Core.dll</key>
1395 <key>Sodium.Core.dll</key>
client/bin/Debug/net10.0-ios/iossimulator-arm64/Skrepka.dll

Binary file not shown.

client/bin/Debug/net10.0-ios/iossimulator-arm64/Skrepka.pdb

Binary file not shown.

client/bin/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.app/Contents/MonoBundle/Skrepka.dll

Binary file not shown.

client/bin/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.app/Contents/MonoBundle/Skrepka.pdb

Binary file not shown.

client/bin/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.app/Contents/_CodeSignature/CodeResources+2−2
@@ -240,14 +240,14 @@
240 <dict>
240 <dict>
241 <key>hash2</key>
241 <key>hash2</key>
242 <data>
242 <data>
243− SkrTLt6NpdxkCOrg3x+0va15w21CQqSaHXDAj+klfQY=
243+ vUKSK+cGYTR7qkuQmbqO8GnXw8MSCSGfEnat9WPM61k=
244 </data>
244 </data>
245 </dict>
245 </dict>
246 <key>MonoBundle/Skrepka.pdb</key>
246 <key>MonoBundle/Skrepka.pdb</key>
247 <dict>
247 <dict>
248 <key>hash2</key>
248 <key>hash2</key>
249 <data>
249 <data>
250− 1pt18PVRQANYqIOx8PEoowFdG5ubKia23BbX/3qPn2Q=
250+ G384EqZj+qubQ4QYr5jq66KSfqzCb5lMDtIbdeDt3Es=
251 </data>
251 </data>
252 </dict>
252 </dict>
253 <key>MonoBundle/Sodium.Core.dll</key>
253 <key>MonoBundle/Sodium.Core.dll</key>
client/bin/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.dll

Binary file not shown.

client/bin/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.pdb

Binary file not shown.

client/obj/Debug/net10.0-ios/iossimulator-arm64/Skrepka.AssemblyInfo.fs+1−1
@@ -10,7 +10,7 @@ open System.Reflection
10[<assembly: System.Reflection.AssemblyCompanyAttribute("Skrepka")>]
10[<assembly: System.Reflection.AssemblyCompanyAttribute("Skrepka")>]
11[<assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")>]
11[<assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")>]
12[<assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")>]
12[<assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")>]
13−[<assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0+1025ec42bdc63a36e82c2b05547b3ddfc72a0761")>]
13+[<assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0+fa9d38a3fad244670e1576c664bf6ba789d0168e")>]
14[<assembly: System.Reflection.AssemblyProductAttribute("Skrepka")>]
14[<assembly: System.Reflection.AssemblyProductAttribute("Skrepka")>]
15[<assembly: System.Reflection.AssemblyTitleAttribute("Skrepka")>]
15[<assembly: System.Reflection.AssemblyTitleAttribute("Skrepka")>]
16[<assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")>]
16[<assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")>]
client/obj/Debug/net10.0-ios/iossimulator-arm64/Skrepka.AssemblyInfoInputs.cache+1−1
@@ -1 +1 @@
1−608be3485c5b02f0859bc57c944a7d90f027c871f4620f1b2625c376aa40ab39
1+5f2895c26967c1b79537d5958be98264c62028600e0beb408e514bf6fedba7a8
client/obj/Debug/net10.0-ios/iossimulator-arm64/Skrepka.dll

Binary file not shown.

client/obj/Debug/net10.0-ios/iossimulator-arm64/Skrepka.fsproj.FileListAbsolute.txt−22
@@ -333,25 +333,3 @@
333/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/Skrepka.pdb
333/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/Skrepka.pdb
334/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/Skrepka.genruntimeconfig.cache
334/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/Skrepka.genruntimeconfig.cache
335/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/ref/Skrepka.dll
335/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/ref/Skrepka.dll
336−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Fabulous.stamp
337−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Fabulous.MauiControls.stamp
338−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/FSharp.Core.stamp
339−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Extensions.Configuration.Abstractions.stamp
340−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Extensions.Configuration.stamp
341−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Extensions.DependencyInjection.Abstractions.stamp
342−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Extensions.DependencyInjection.stamp
343−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Extensions.Diagnostics.Abstractions.stamp
344−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Extensions.FileProviders.Abstractions.stamp
345−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Extensions.Hosting.Abstractions.stamp
346−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Extensions.Logging.Abstractions.stamp
347−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Extensions.Logging.stamp
348−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Extensions.Options.stamp
349−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Extensions.Primitives.stamp
350−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Maui.Controls.Compatibility.stamp
351−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Maui.Controls.stamp
352−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Maui.Controls.Xaml.stamp
353−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Maui.stamp
354−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Maui.Essentials.stamp
355−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Maui.Graphics.stamp
356−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Sodium.Core.stamp
357−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/unpack/Microsoft.Maui.Essentials/BundleResource/xml/microsoft_maui_essentials_fileprovider_file_paths.xml
client/obj/Debug/net10.0-ios/iossimulator-arm64/Skrepka.pdb

Binary file not shown.

client/obj/Debug/net10.0-ios/iossimulator-arm64/Skrepka.sourcelink.json+1−1
@@ -1 +1 @@
1−{"documents":{"/Users/iko/Developer/skrepka/*":"https://raw.githubusercontent.com/ilyakooo0/skrepka/1025ec42bdc63a36e82c2b05547b3ddfc72a0761/*"}}
1+{"documents":{"/Users/iko/Developer/skrepka/*":"https://raw.githubusercontent.com/ilyakooo0/skrepka/fa9d38a3fad244670e1576c664bf6ba789d0168e/*"}}
client/obj/Debug/net10.0-ios/iossimulator-arm64/linked/Skrepka.dll

Binary file not shown.

client/obj/Debug/net10.0-ios/iossimulator-arm64/linked/Skrepka.pdb

Binary file not shown.

client/obj/Debug/net10.0-ios/iossimulator-arm64/ref/Skrepka.dll

Binary file not shown.

client/obj/Debug/net10.0-ios/iossimulator-arm64/refint/Skrepka.dll

Binary file not shown.

client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/FSharp.Core.stampdeleted

No changes to the content.

client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Fabulous.MauiControls.stampdeleted

No changes to the content.

client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Fabulous.stampdeleted

No changes to the content.

client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Extensions.Configuration.Abstractions.stampdeleted

No changes to the content.

client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Extensions.Configuration.stampdeleted

No changes to the content.

client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Extensions.DependencyInjection.Abstractions.stampdeleted

No changes to the content.

client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Extensions.DependencyInjection.stampdeleted

No changes to the content.

client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Extensions.Diagnostics.Abstractions.stampdeleted

No changes to the content.

client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Extensions.FileProviders.Abstractions.stampdeleted

No changes to the content.

client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Extensions.Hosting.Abstractions.stampdeleted

No changes to the content.

client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Extensions.Logging.Abstractions.stampdeleted

No changes to the content.

client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Extensions.Logging.stampdeleted

No changes to the content.

client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Extensions.Options.stampdeleted

No changes to the content.

client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Extensions.Primitives.stampdeleted

No changes to the content.

client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Maui.Controls.Compatibility.stampdeleted

No changes to the content.

client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Maui.Controls.Xaml.stampdeleted

No changes to the content.

client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Maui.Controls.stampdeleted

No changes to the content.

client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Maui.Essentials.stampdeleted

No changes to the content.

client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Maui.Graphics.stampdeleted

No changes to the content.

client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Maui.stampdeleted

No changes to the content.

client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Sodium.Core.stampdeleted

No changes to the content.

client/obj/Debug/net10.0-ios/iossimulator-arm64/unpack/Microsoft.Maui.Essentials/BundleResource/xml/microsoft_maui_essentials_fileprovider_file_paths.xmldeleted−6
@@ -1,6 +0,0 @@
1−<?xml version="1.0" encoding="UTF-8" ?>
2−<paths>
3− <external-path name="external_files" path="." />
4− <cache-path name="internal_cache" path="." />
5− <external-cache-path name="external_cache" path="." />
6−</paths>
client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.AssemblyInfo.fs+1−1
@@ -10,7 +10,7 @@ open System.Reflection
10[<assembly: System.Reflection.AssemblyCompanyAttribute("Skrepka")>]
10[<assembly: System.Reflection.AssemblyCompanyAttribute("Skrepka")>]
11[<assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")>]
11[<assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")>]
12[<assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")>]
12[<assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")>]
13−[<assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0+1025ec42bdc63a36e82c2b05547b3ddfc72a0761")>]
13+[<assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0+fa9d38a3fad244670e1576c664bf6ba789d0168e")>]
14[<assembly: System.Reflection.AssemblyProductAttribute("Skrepka")>]
14[<assembly: System.Reflection.AssemblyProductAttribute("Skrepka")>]
15[<assembly: System.Reflection.AssemblyTitleAttribute("Skrepka")>]
15[<assembly: System.Reflection.AssemblyTitleAttribute("Skrepka")>]
16[<assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")>]
16[<assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")>]
client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.AssemblyInfoInputs.cache+1−1
@@ -1 +1 @@
1−6ce084ea2001e68a95078f1ad7ce56a873907d46e9df803c23a3c8e674e8ede8
1+284a452a2358d44d43d128fc8940476c0b2aae618df88f49906b03be966d2822
client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.dll

Binary file not shown.

client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.pdb

Binary file not shown.

client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.sourcelink.json+1−1
@@ -1 +1 @@
1−{"documents":{"/Users/iko/Developer/skrepka/*":"https://raw.githubusercontent.com/ilyakooo0/skrepka/1025ec42bdc63a36e82c2b05547b3ddfc72a0761/*"}}
1+{"documents":{"/Users/iko/Developer/skrepka/*":"https://raw.githubusercontent.com/ilyakooo0/skrepka/fa9d38a3fad244670e1576c664bf6ba789d0168e/*"}}
client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/linked/Skrepka.dll

Binary file not shown.

client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/linked/Skrepka.pdb

Binary file not shown.

client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/ref/Skrepka.dll

Binary file not shown.

client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/refint/Skrepka.dll

Binary file not shown.

server.knot+17−18
@@ -114,10 +114,6 @@ verifyFederationAuth = \authorization ->
114
114
115clampTtl = \ttl -> if ttl < 1 then 1 else if ttl > maxPresenceTtl then maxPresenceTtl else ttl
115clampTtl = \ttl -> if ttl < 1 then 1 else if ttl > maxPresenceTtl then maxPresenceTtl else ttl
116
116
117−forwardToAll = \servers action -> do
118− sendToEach servers (\server -> action server)
119− yield (count servers > 0)
120−
121sendToEach = \servers action ->
117sendToEach = \servers action ->
122 if count servers == 0
118 if count servers == 0
123 then yield {}
119 then yield {}
@@ -270,7 +266,10 @@ serve = \req -> case req of
270 authorization: "Bearer " ++ federationSecret,
266 authorization: "Bearer " ++ federationSecret,
271 events: [{eventType: "online", pubkey: pubkey, server: serverHost, timestamp: t, ttl: 3600}], hopsRemaining: 2, fromServer: serverHost}))
267 events: [{eventType: "online", pubkey: pubkey, server: serverHost, timestamp: t, ttl: 3600}], hopsRemaining: 2, fromServer: serverHost}))
272 else yield {}
268 else yield {}
273− broadcastOfflineGossip t result.offlinePeers result.expiredLocal
269+ broadcastOfflineGossip t result.offlinePeers (do
270+ p <- result.expiredLocal
271+ where not (p.pubkey == pubkey)
272+ yield p)
274 if result.sigValid
273 if result.sigValid
275 then respond {token: token, expiresAt: expiry}
274 then respond {token: token, expiresAt: expiry}
276 else respond {token: "", expiresAt: 0}
275 else respond {token: "", expiresAt: 0}
@@ -309,7 +308,7 @@ serve = \req -> case req of
309 t <- now
308 t <- now
310 msgId <- genToken
309 msgId <- genToken
311 let clampedTs = if timestamp > t then t else if timestamp < t - messageMaxAge then t else timestamp
310 let clampedTs = if timestamp > t then t else if timestamp < t - messageMaxAge then t else timestamp
312− let validInput = not (to == "") && not (encryptedBlob == "") && isValidHex to && isValidHex encryptedBlob && not (tooLong to maxPubkeyLen) && not (tooLong encryptedBlob maxBlobLen)
311+ let validInput = not (to == "") && not (encryptedBlob == "") && not (tooLong to maxPubkeyLen) && not (tooLong encryptedBlob maxBlobLen) && isValidHex to && isValidHex encryptedBlob
313 result <- atomic do
312 result <- atomic do
314 pr <- pruneExpired t
313 pr <- pruneExpired t
315 sessions <- *sessions
314 sessions <- *sessions
@@ -357,13 +356,13 @@ serve = \req -> case req of
357 yield {status: status, remoteServers: remoteServers, fromKey: fromKey, expiredLocal: pr.expiredLocal, peerServers: pr.peerServers}
356 yield {status: status, remoteServers: remoteServers, fromKey: fromKey, expiredLocal: pr.expiredLocal, peerServers: pr.peerServers}
358 finalStatus <- if result.status == "federated" && not (federationSecret == "")
357 finalStatus <- if result.status == "federated" && not (federationSecret == "")
359 then do
358 then do
360− anyOk <- forwardToAll result.remoteServers (\server ->
359+ sendToEach result.remoteServers (\server ->
361 fetch (federationScheme ++ server) (ForwardMessage {
360 fetch (federationScheme ++ server) (ForwardMessage {
362 toKey: to, fromKey: result.fromKey, encryptedBlob: encryptedBlob,
361 toKey: to, fromKey: result.fromKey, encryptedBlob: encryptedBlob,
363 timestamp: clampedTs, originServer: serverHost, messageId: msgId,
362 timestamp: clampedTs, originServer: serverHost, messageId: msgId,
364 authorization: "Bearer " ++ federationSecret}))
363 authorization: "Bearer " ++ federationSecret}))
365− yield (if anyOk then "federated" else "queued")
364+ yield "federated"
366− else yield result.status
365+ else yield (if result.status == "federated" then "queued" else result.status)
367 broadcastOfflineGossip t result.peerServers result.expiredLocal
366 broadcastOfflineGossip t result.peerServers result.expiredLocal
368 respond {status: finalStatus, messageId: (if result.status == "unauthorized" then "" else if result.status == "invalid" then "" else msgId)}
367 respond {status: finalStatus, messageId: (if result.status == "unauthorized" then "" else if result.status == "invalid" then "" else msgId)}
369
368
@@ -371,9 +370,9 @@ serve = \req -> case req of
371 t <- now
370 t <- now
372 let dedupedIds = fold (\acc mid ->
371 let dedupedIds = fold (\acc mid ->
373 if mid == "" then acc
372 if mid == "" then acc
374− else if not (isValidHex mid) then acc
375− else if tooLong mid maxPubkeyLen then acc
376 else if count acc >= maxMessageIds then acc
373 else if count acc >= maxMessageIds then acc
374+ else if tooLong mid maxPubkeyLen then acc
375+ else if not (isValidHex mid) then acc
377 else if count (do a <- acc; where a == mid; yield a) > 0 then acc
376 else if count (do a <- acc; where a == mid; yield a) > 0 then acc
378 else union acc [mid]) [] messageIds
377 else union acc [mid]) [] messageIds
379 result <- atomic do
378 result <- atomic do
@@ -439,7 +438,7 @@ serve = \req -> case req of
439 where p.server == serverHost
438 where p.server == serverHost
440 yield p) of
439 yield p) of
441 Just {value: p} -> p.server
440 Just {value: p} -> p.server
442− Nothing {} -> fold (\_ p -> p.server) "" valid
441+ Nothing {} -> (fold (\acc p -> if p.expiresAt > acc.best then {server: p.server, best: p.expiresAt} else acc) {server: "", best: 0} valid).server
443 yield {foundServer: foundServer, authorized: authorized, expiredLocal: pr.expiredLocal, peerServers: pr.peerServers}
442 yield {foundServer: foundServer, authorized: authorized, expiredLocal: pr.expiredLocal, peerServers: pr.peerServers}
444 broadcastOfflineGossip t result.peerServers result.expiredLocal
443 broadcastOfflineGossip t result.peerServers result.expiredLocal
445 respond {online: not (result.foundServer == ""), server: result.foundServer, authorized: result.authorized}
444 respond {online: not (result.foundServer == ""), server: result.foundServer, authorized: result.authorized}
@@ -455,9 +454,9 @@ serve = \req -> case req of
455 where e.eventType == "online"
454 where e.eventType == "online"
456 where not (e.server == "")
455 where not (e.server == "")
457 where not (e.pubkey == "")
456 where not (e.pubkey == "")
458− where isValidHex e.pubkey
459 where not (tooLong e.pubkey maxPubkeyLen)
457 where not (tooLong e.pubkey maxPubkeyLen)
460 where not (tooLong e.server maxPubkeyLen)
458 where not (tooLong e.server maxPubkeyLen)
459+ where isValidHex e.pubkey
461 where not (e.server == serverHost)
460 where not (e.server == serverHost)
462 where e.timestamp <= t + clockSkewTolerance
461 where e.timestamp <= t + clockSkewTolerance
463 where e.timestamp + (clampTtl e.ttl) * 1000 > t
462 where e.timestamp + (clampTtl e.ttl) * 1000 > t
@@ -468,9 +467,9 @@ serve = \req -> case req of
468 where e.eventType == "offline"
467 where e.eventType == "offline"
469 where not (e.server == "")
468 where not (e.server == "")
470 where not (e.pubkey == "")
469 where not (e.pubkey == "")
471− where isValidHex e.pubkey
472 where not (tooLong e.pubkey maxPubkeyLen)
470 where not (tooLong e.pubkey maxPubkeyLen)
473 where not (tooLong e.server maxPubkeyLen)
471 where not (tooLong e.server maxPubkeyLen)
472+ where isValidHex e.pubkey
474 where not (e.server == serverHost)
473 where not (e.server == serverHost)
475 where e.timestamp <= t + clockSkewTolerance
474 where e.timestamp <= t + clockSkewTolerance
476 where t - e.timestamp < (clampTtl e.ttl) * 1000
475 where t - e.timestamp < (clampTtl e.ttl) * 1000
@@ -570,9 +569,9 @@ serve = \req -> case req of
570 where validRequest
569 where validRequest
571 where not (e.server == "")
570 where not (e.server == "")
572 where not (e.pubkey == "")
571 where not (e.pubkey == "")
573− where isValidHex e.pubkey
574 where not (tooLong e.pubkey maxPubkeyLen)
572 where not (tooLong e.pubkey maxPubkeyLen)
575 where not (tooLong e.server maxPubkeyLen)
573 where not (tooLong e.server maxPubkeyLen)
574+ where isValidHex e.pubkey
576 where not (e.server == serverHost)
575 where not (e.server == serverHost)
577 where e.timestamp <= t + clockSkewTolerance
576 where e.timestamp <= t + clockSkewTolerance
578 where e.timestamp + (clampTtl e.ttl) * 1000 > t
577 where e.timestamp + (clampTtl e.ttl) * 1000 > t
@@ -614,7 +613,7 @@ serve = \req -> case req of
614 t <- now
613 t <- now
615 let authorized = verifyFederationAuth authorization
614 let authorized = verifyFederationAuth authorization
616 let validOrigin = not (originServer == "") && not (originServer == serverHost) && not (tooLong originServer maxPubkeyLen)
615 let validOrigin = not (originServer == "") && not (originServer == serverHost) && not (tooLong originServer maxPubkeyLen)
617− let validFields = not (toKey == "") && not (fromKey == "") && not (encryptedBlob == "") && not (messageId == "") && isValidHex toKey && isValidHex fromKey && isValidHex encryptedBlob && isValidHex messageId && not (tooLong toKey maxPubkeyLen) && not (tooLong fromKey maxPubkeyLen) && not (tooLong encryptedBlob maxBlobLen) && not (tooLong messageId maxPubkeyLen)
616+ let validFields = not (toKey == "") && not (fromKey == "") && not (encryptedBlob == "") && not (messageId == "") && not (tooLong toKey maxPubkeyLen) && not (tooLong fromKey maxPubkeyLen) && not (tooLong encryptedBlob maxBlobLen) && not (tooLong messageId maxPubkeyLen) && isValidHex toKey && isValidHex fromKey && isValidHex encryptedBlob && isValidHex messageId
618 let clampedTs = if timestamp > t then t else if timestamp < t - messageMaxAge then t else timestamp
617 let clampedTs = if timestamp > t then t else if timestamp < t - messageMaxAge then t else timestamp
619 pruneResult <- atomic do
618 pruneResult <- atomic do
620 pr <- pruneExpired t
619 pr <- pruneExpired t
@@ -643,9 +642,9 @@ serve = \req -> case req of
643 let authorized = verifyFederationAuth authorization
642 let authorized = verifyFederationAuth authorization
644 let dedupedIds = fold (\acc mid ->
643 let dedupedIds = fold (\acc mid ->
645 if mid == "" then acc
644 if mid == "" then acc
646− else if not (isValidHex mid) then acc
647− else if tooLong mid maxPubkeyLen then acc
648 else if count acc >= maxMessageIds then acc
645 else if count acc >= maxMessageIds then acc
646+ else if tooLong mid maxPubkeyLen then acc
647+ else if not (isValidHex mid) then acc
649 else if count (do a <- acc; where a == mid; yield a) > 0 then acc
648 else if count (do a <- acc; where a == mid; yield a) > 0 then acc
650 else union acc [mid]) [] messageIds
649 else union acc [mid]) [] messageIds
651 let validAck = authorized && originServer == serverHost && not (fromServer == "") && not (fromServer == serverHost) && not (tooLong fromServer maxPubkeyLen)
650 let validAck = authorized && originServer == serverHost && not (fromServer == "") && not (fromServer == serverHost) && not (tooLong fromServer maxPubkeyLen)