1- // SPDX-FileCopyrightText: 2024 Emulsion contributors <https://github.com/codingteam/emulsion>
1+ // SPDX-FileCopyrightText: 2025 Emulsion contributors <https://github.com/codingteam/emulsion>
22//
33// SPDX-License-Identifier: MIT
44
55/// Helper functions to deal with SharpXMPP low-level details (such as XML stuff).
66module Emulsion.Xmpp.SharpXmppHelper
77
88open System
9+ open System.Buffers
10+ open System.Text
911open System.Xml .Linq
1012
13+ open Microsoft.FSharp .NativeInterop
1114open SharpXMPP
1215open SharpXMPP.XMPP
1316open SharpXMPP.XMPP .Client .Elements
@@ -49,6 +52,28 @@ let private bookmark (roomJid: string) (nickname: string) (password: string opti
4952 room.Add( nickElement)
5053 room
5154
55+ #nowarn " 9" // for NativePtr
56+ let SanitizeXmlText ( text : string ): string =
57+ let mutable hasError = false
58+ let mutable span = text.AsSpan()
59+ while not hasError && not span.IsEmpty do
60+ let mutable rune = Rune()
61+ let mutable consumed = 0
62+ if Rune.DecodeFromUtf16( span, & rune, & consumed) = OperationStatus.Done
63+ then span <- span.Slice consumed
64+ else hasError <- true
65+
66+ if hasError then
67+ let builder = StringBuilder()
68+ for r in text.EnumerateRunes() do
69+ let length = r.Utf16SequenceLength
70+ let buf = Span( NativePtr.stackalloc< char> length |> NativePtr.toVoidPtr, length)
71+ r.EncodeToUtf16 buf |> ignore
72+ builder.Append( buf) |> ignore
73+ builder.ToString()
74+ else
75+ text
76+
5277let joinRoom ( client : XmppClient ) ( roomJid : string ) ( nickname : string ) ( password : string option ): unit =
5378 let room = bookmark roomJid nickname password
5479 client.BookmarkManager.Join room
@@ -59,7 +84,7 @@ let message (id: string) (toAddr: string) (text: string): XMPPMessage =
5984 m.SetAttributeValue( Type, " groupchat" )
6085 m.SetAttributeValue( To, toAddr)
6186 let body = XElement( Body)
62- body.Value <- text
87+ body.Value <- SanitizeXmlText text
6388 m.Add( body)
6489 m
6590
0 commit comments