mirror of
https://github.com/RedWizardsLab/EchoHub.git
synced 2026-09-04 08:36:11 +02:00
- Added IMessageEncryptionService and its implementation MessageEncryptionService for handling message encryption. - Updated ChannelsController and ChatService to encrypt messages before storing and sending. - Introduced encryption key retrieval endpoint in ServerController. - Modified EchoHubDbContext to accommodate increased message content and embed JSON lengths for encrypted data. - Created migrations to support encryption-related database changes. - Enhanced FirstRunSetup to ensure encryption key is generated if not present. - Updated appsettings.example.json to include encryption configuration. - Added comprehensive unit tests for encryption service and compatibility tests between client and server encryption.
132 lines
4.7 KiB
C#
132 lines
4.7 KiB
C#
using EchoHub.Core.Contracts;
|
|
using EchoHub.Core.DTOs;
|
|
|
|
namespace EchoHub.Server.Irc;
|
|
|
|
public class IrcBroadcaster : IChatBroadcaster
|
|
{
|
|
private readonly IrcGatewayService _gateway;
|
|
private readonly IMessageEncryptionService _encryption;
|
|
|
|
public IrcBroadcaster(IrcGatewayService gateway, IMessageEncryptionService encryption)
|
|
{
|
|
_gateway = gateway;
|
|
_encryption = encryption;
|
|
}
|
|
|
|
public async Task SendMessageToChannelAsync(string channelName, MessageDto message)
|
|
{
|
|
// Decrypt content for IRC clients (they can't handle app-layer encryption)
|
|
var decryptedMessage = message with { Content = _encryption.Decrypt(message.Content) };
|
|
var lines = IrcMessageFormatter.FormatMessage(decryptedMessage);
|
|
|
|
foreach (var conn in _gateway.GetConnectionsInChannel(channelName))
|
|
{
|
|
// IRC convention: don't echo sender's own message
|
|
if (conn.Nickname == message.SenderUsername)
|
|
continue;
|
|
|
|
foreach (var line in lines)
|
|
await conn.SendAsync(line);
|
|
}
|
|
}
|
|
|
|
public async Task SendUserJoinedAsync(string channelName, string username, string? excludeConnectionId = null)
|
|
{
|
|
foreach (var conn in _gateway.GetConnectionsInChannel(channelName))
|
|
{
|
|
if (conn.ConnectionId == excludeConnectionId) continue;
|
|
await conn.SendAsync($":{username}!{username}@echohub JOIN #{channelName}");
|
|
}
|
|
}
|
|
|
|
public async Task SendUserLeftAsync(string channelName, string username)
|
|
{
|
|
foreach (var conn in _gateway.GetConnectionsInChannel(channelName))
|
|
{
|
|
if (conn.Nickname == username) continue;
|
|
await conn.SendAsync($":{username}!{username}@echohub PART #{channelName}");
|
|
}
|
|
}
|
|
|
|
public async Task SendChannelUpdatedAsync(ChannelDto channel, string? channelName = null)
|
|
{
|
|
var target = channelName ?? channel.Name;
|
|
if (channel.Topic is null) return;
|
|
|
|
foreach (var conn in _gateway.GetConnectionsInChannel(target))
|
|
{
|
|
await conn.SendAsync($":{_gateway.Options.ServerName} TOPIC #{channel.Name} :{channel.Topic}");
|
|
}
|
|
}
|
|
|
|
public Task SendUserStatusChangedAsync(List<string> channelNames, UserPresenceDto presence)
|
|
{
|
|
// IRC has no active status broadcast. Clients discover away via WHOIS/WHO.
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
public async Task SendUserKickedAsync(string channelName, string username, string? reason)
|
|
{
|
|
var reasonText = reason is not null ? $" :{reason}" : "";
|
|
foreach (var conn in _gateway.GetConnectionsInChannel(channelName))
|
|
{
|
|
await conn.SendAsync($":{_gateway.Options.ServerName} KICK #{channelName} {username}{reasonText}");
|
|
}
|
|
}
|
|
|
|
public async Task SendUserBannedAsync(string username, string? reason)
|
|
{
|
|
var reasonText = reason ?? "You have been banned.";
|
|
foreach (var conn in _gateway.GetAllConnections())
|
|
{
|
|
if (conn.Nickname == username)
|
|
await conn.SendAsync($":{_gateway.Options.ServerName} NOTICE {username} :You have been banned: {reasonText}");
|
|
}
|
|
}
|
|
|
|
public async Task SendMessageDeletedAsync(string channelName, Guid messageId)
|
|
{
|
|
foreach (var conn in _gateway.GetConnectionsInChannel(channelName))
|
|
{
|
|
await conn.SendAsync($":{_gateway.Options.ServerName} NOTICE {conn.Nickname ?? "*"} :Message {messageId} was deleted in #{channelName}");
|
|
}
|
|
}
|
|
|
|
public async Task SendChannelNukedAsync(string channelName)
|
|
{
|
|
foreach (var conn in _gateway.GetConnectionsInChannel(channelName))
|
|
{
|
|
await conn.SendAsync($":{_gateway.Options.ServerName} NOTICE {conn.Nickname ?? "*"} :All messages in #{channelName} have been cleared");
|
|
}
|
|
}
|
|
|
|
public async Task SendErrorAsync(string connectionId, string message)
|
|
{
|
|
if (!connectionId.StartsWith("irc-")) return;
|
|
|
|
if (_gateway.Connections.TryGetValue(connectionId, out var conn))
|
|
{
|
|
await conn.SendAsync($":{_gateway.Options.ServerName} NOTICE {conn.Nickname ?? "*"} :{message}");
|
|
}
|
|
}
|
|
|
|
public async Task ForceDisconnectUserAsync(List<string> connectionIds, string reason)
|
|
{
|
|
foreach (var connId in connectionIds)
|
|
{
|
|
if (!connId.StartsWith("irc-")) continue;
|
|
|
|
if (_gateway.Connections.TryGetValue(connId, out var conn))
|
|
{
|
|
try
|
|
{
|
|
await conn.SendAsync($"ERROR :Closing Link: {reason}");
|
|
await conn.DisposeAsync();
|
|
}
|
|
catch { /* connection may already be closed */ }
|
|
}
|
|
}
|
|
}
|
|
}
|