Count UTF-8 bytes and graphemes instead Microsoft brand UTF-16 whatevers.

This commit is contained in:
flash 2025-04-14 21:57:24 +00:00
parent 306f8a29d1
commit e7b38dc8e1
Signed by: flash
GPG key ID: 2C9C2C574D47FE3E
9 changed files with 52 additions and 15 deletions

View file

@ -1,5 +1,7 @@
using System; using System;
using System.Globalization;
using System.Text; using System.Text;
using SharpChat.Commands;
namespace SharpChat { namespace SharpChat {
public class ChatUser : IEquatable<ChatUser> { public class ChatUser : IEquatable<ChatUser> {
@ -23,8 +25,15 @@ namespace SharpChat {
get { get {
StringBuilder sb = new(); StringBuilder sb = new();
if(Status == ChatUserStatus.Away) if(Status == ChatUserStatus.Away) {
sb.AppendFormat("&lt;{0}&gt;_", StatusText[..Math.Min(StatusText.Length, 5)].ToUpperInvariant()); string statusText = StatusText.Trim();
StringInfo sti = new StringInfo(statusText);
if(Encoding.UTF8.GetByteCount(statusText) > AFKCommand.MAX_BYTES
|| sti.LengthInTextElements > AFKCommand.MAX_GRAPHEMES)
statusText = sti.SubstringByTextElements(0, AFKCommand.MAX_GRAPHEMES);
sb.AppendFormat("&lt;{0}&gt;_", statusText.ToUpperInvariant());
}
sb.Append(LegacyName); sb.Append(LegacyName);

View file

@ -1,10 +1,13 @@
using SharpChat.Packet; using SharpChat.Packet;
using System.Globalization;
using System.Linq; using System.Linq;
using System.Text;
namespace SharpChat.Commands { namespace SharpChat.Commands {
public class AFKCommand : IChatCommand { public class AFKCommand : IChatCommand {
private const string DEFAULT = "AFK"; private const string DEFAULT = "AFK";
private const int MAX_LENGTH = 5; public const int MAX_GRAPHEMES = 5;
public const int MAX_BYTES = MAX_GRAPHEMES * 10;
public bool IsMatch(ChatCommandContext ctx) { public bool IsMatch(ChatCommandContext ctx) {
return ctx.NameEquals("afk"); return ctx.NameEquals("afk");
@ -16,8 +19,11 @@ namespace SharpChat.Commands {
statusText = DEFAULT; statusText = DEFAULT;
else { else {
statusText = statusText.Trim(); statusText = statusText.Trim();
if(statusText.Length > MAX_LENGTH)
statusText = statusText[..MAX_LENGTH].Trim(); StringInfo sti = new StringInfo(statusText);
if(Encoding.UTF8.GetByteCount(statusText) > MAX_BYTES
|| sti.LengthInTextElements > MAX_GRAPHEMES)
statusText = sti.SubstringByTextElements(0, MAX_GRAPHEMES).Trim();
} }
ctx.Chat.UpdateUser( ctx.Chat.UpdateUser(

View file

@ -1,8 +1,13 @@
using SharpChat.Packet; using SharpChat.Packet;
using System.Globalization;
using System.Linq; using System.Linq;
using System.Text;
namespace SharpChat.Commands { namespace SharpChat.Commands {
public class NickCommand : IChatCommand { public class NickCommand : IChatCommand {
private const int MAX_GRAPHEMES = 16;
private const int MAX_BYTES = MAX_GRAPHEMES * 10;
public bool IsMatch(ChatCommandContext ctx) { public bool IsMatch(ChatCommandContext ctx) {
return ctx.NameEquals("nick"); return ctx.NameEquals("nick");
} }
@ -36,10 +41,14 @@ namespace SharpChat.Commands {
if(nickStr == targetUser.UserName) if(nickStr == targetUser.UserName)
nickStr = string.Empty; nickStr = string.Empty;
else if(nickStr.Length > 15)
nickStr = nickStr[..15];
else if(string.IsNullOrEmpty(nickStr)) else if(string.IsNullOrEmpty(nickStr))
nickStr = string.Empty; nickStr = string.Empty;
else {
StringInfo nsi = new StringInfo(nickStr);
if(Encoding.UTF8.GetByteCount(nickStr) > MAX_BYTES
|| nsi.LengthInTextElements > MAX_GRAPHEMES)
nickStr = nsi.SubstringByTextElements(0, MAX_GRAPHEMES).Trim();
}
if(!string.IsNullOrWhiteSpace(nickStr) && ctx.Chat.Users.Any(u => u.NameEquals(nickStr))) { if(!string.IsNullOrWhiteSpace(nickStr) && ctx.Chat.Users.Any(u => u.NameEquals(nickStr))) {
ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.NAME_IN_USE, true, nickStr)); ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.NAME_IN_USE, true, nickStr));

View file

@ -24,6 +24,7 @@ namespace SharpChat.EventStorage {
SslMode = MySqlSslMode.None, SslMode = MySqlSslMode.None,
ForceSynchronous = true, ForceSynchronous = true,
ConnectionTimeout = 5, ConnectionTimeout = 5,
DefaultCommandTimeout = 900, // fuck it, 15 minutes
}.ToString(); }.ToString();
} }

View file

@ -30,6 +30,14 @@ namespace SharpChat.EventStorage {
DoMigration("create_events_table", CreateEventsTable); DoMigration("create_events_table", CreateEventsTable);
DoMigration("allow_null_target", AllowNullTarget); DoMigration("allow_null_target", AllowNullTarget);
DoMigration("event_data_as_medium_blob", EventDataAsMediumBlob);
}
private void EventDataAsMediumBlob() {
RunCommand(
"ALTER TABLE `sqc_events`"
+ " CHANGE COLUMN `event_data` `event_data` MEDIUMBLOB NULL DEFAULT NULL AFTER `event_flags`;"
);
} }
private void AllowNullTarget() { private void AllowNullTarget() {

View file

@ -5,7 +5,9 @@ using SharpChat.EventStorage;
using SharpChat.Packet; using SharpChat.Packet;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.Linq; using System.Linq;
using System.Text;
namespace SharpChat.PacketHandlers namespace SharpChat.PacketHandlers
{ {
@ -55,8 +57,10 @@ namespace SharpChat.PacketHandlers
ctx.Chat.UpdateUser(user, status: ChatUserStatus.Online); ctx.Chat.UpdateUser(user, status: ChatUserStatus.Online);
int maxMsgLength = MaxMessageLength; int maxMsgLength = MaxMessageLength;
if(messageText.Length > maxMsgLength) StringInfo messageTextInfo = new StringInfo(messageText);
messageText = messageText[..maxMsgLength]; if(Encoding.UTF8.GetByteCount(messageText) > (maxMsgLength * 10)
|| messageTextInfo.LengthInTextElements > maxMsgLength)
messageText = messageTextInfo.SubstringByTextElements(0, maxMsgLength);
messageText = messageText.Trim(); messageText = messageText.Trim();

View file

@ -2,7 +2,7 @@
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net9.0</TargetFramework>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View file

@ -86,10 +86,11 @@ namespace SharpChat {
return; return;
} }
if(EnabledSslProtocols == SslProtocols.None) { // makes dotnet shut up, TLS is handled by NGINX anyway
EnabledSslProtocols = SslProtocols.Tls; // if(EnabledSslProtocols == SslProtocols.None) {
FleckLog.Debug("Using default TLS 1.0 security protocol."); // EnabledSslProtocols = SslProtocols.Tls;
} // FleckLog.Debug("Using default TLS 1.0 security protocol.");
// }
} }
ListenForClients(); ListenForClients();
_config = config; _config = config;

View file

@ -10,5 +10,4 @@ namespace SharpChat {
=> ((DateTimeOffset.Now.ToUnixTimeMilliseconds() - EPOCH) << 8) => ((DateTimeOffset.Now.ToUnixTimeMilliseconds() - EPOCH) << 8)
| (ushort)(Interlocked.Increment(ref Counter) & 0xFFFF); | (ushort)(Interlocked.Increment(ref Counter) & 0xFFFF);
} }
} }