Skip to content

Instantly share code, notes, and snippets.

@matheusfillipe
Created March 13, 2025 19:08
Show Gist options
  • Save matheusfillipe/b02a8e26bae360930315f05654a8994c to your computer and use it in GitHub Desktop.
Save matheusfillipe/b02a8e26bae360930315f05654a8994c to your computer and use it in GitHub Desktop.
Unrealircd 6.x relaymsg module
/*
* draft/relaymsg module for UnrealIRCd
*
* This module implements a minimal IRCv3 draft/relaymsg handler that relays
* PRIVMSG/NOTICE messages with an overridden source to channels.
*
* The RELAYMSG command format is:
* RELAYMSG <target> <relayprefix> <command> <parameters...>
*
* Only channel targets (beginning with '#' or '&') are supported.
*
* The module creates a minimal fake Client whose nick is derived from
* <relayprefix> (expected in "nick!user@host" format) and uses sendto_channel()
* to deliver the message.
*
* NOTE: This is a minimal example for demonstration purposes.
*/
#include "unrealircd.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
/* Module header */
ModuleHeader MOD_HEADER = {
"third/relaymsg", /* Module name */
"0.0.1",
"Provides minimal IRCv3 draft/relaymsg support for relaying PRIVMSG/NOTICE with an overridden source to channels",
"Matheus Fillipe", /* author */
"unrealircd-6", /* do not change this, it indicates module API version */
};
/*
* Declaration for find_channel(), assumed to be exported.
*/
extern Channel *find_channel(const char *chan);
/*
* Minimal fake client creation.
*
* Allocates a Client and copies the nick portion (up to the first '!')
* from the relay prefix into client->name.
*
* Adjust this code if your Client structure differs.
*/
static Client *create_fake_client(const char *prefix) {
Client *client = calloc(1, sizeof(Client));
if (!client)
return NULL;
{
const char *bang = strchr(prefix, '!');
if (bang) {
size_t nicklen = bang - prefix;
strlcpy(client->name, prefix, nicklen + 1);
} else {
strlcpy(client->name, prefix, sizeof(client->name));
}
}
/* Mark as local by setting client->local to a non-NULL dummy pointer */
client->local = (LocalClient *)1;
return client;
}
/*
* Free the fake client.
*
* Since the string fields are stored in fixed arrays inside Client,
* simply free the client.
*/
static void destroy_fake_client(Client *client) {
if (client)
free(client);
}
/*
* relaymsg_cmd - handle the RELAYMSG command.
*
* Expected parameters:
* [0] - "RELAYMSG"
* [1] - <target> : the channel (must begin with '#' or '&').
* [2] - <relayprefix> : full prefix to use as source ("nick!user@host").
* [3] - <command> : the command to relay (typically PRIVMSG or NOTICE).
* [4...] - <parameters...> : message text.
*/
CMD_FUNC(relaymsg_cmd) {
char message[1024];
int i;
if (parc < 4) {
sendto_server(client, 0UL, 0UL, NULL, "ERROR :Not enough parameters for RELAYMSG");
return;
}
const char *target = parv[1];
const char *relayprefix = parv[2];
const char *relaycmd = parv[3];
/* Concatenate remaining parameters into the message */
message[0] = '\0';
for (i = 4; i < parc; i++) {
if (i > 4)
strlcat(message, " ", sizeof(message));
strlcat(message, parv[i], sizeof(message));
}
/* Only support channel targets */
if (target[0] != '#' && target[0] != '&') {
sendto_server(client, 0UL, 0UL, NULL, "ERROR :Target must be a channel");
return;
}
Channel *chan = find_channel(target);
if (!chan) {
sendto_server(client, 0UL, 0UL, NULL, "ERROR :No such channel");
return;
}
Client *fake = create_fake_client(relayprefix);
if (!fake) {
sendto_server(client, 0UL, 0UL, NULL, "ERROR :Unable to create fake client for RELAYMSG");
return;
}
/* Relay the message to the channel.
*
* sendto_channel() signature (example):
* void sendto_channel(Channel *channel, Client *from, Client *skip,
* char *member_modes, long clicap, int sendflags,
* const char *cmd, const char *format, ...);
*
* Here:
* - from is our fake client,
* - skip is NULL,
* - member_modes is NULL,
* - clicap is 0,
* - sendflags is 0.
*/
sendto_channel(chan, fake, NULL, NULL, 0, SEND_ALL, relaycmd, ":%s", message);
destroy_fake_client(fake);
}
/*
* Module initialization.
*
* Register the "RELAYMSG" command using CommandAdd().
*/
MOD_INIT() {
if (!CommandAdd(modinfo->handle, "RELAYMSG", relaymsg_cmd, 4, CMD_OPER))
return MOD_FAILED;
return MOD_SUCCESS;
}
/*
* Module unload function.
*/
MOD_DESTROY() {
return MOD_SUCCESS;
}
MOD_LOAD()
{
/* Do necessary initialization for when module is loaded */
/* For example: CommandOverrideAdd() */
return MOD_SUCCESS; /* returning anything else is not really supported here */
}
MOD_UNLOAD()
{
// Perform any cleanup for unload
return MOD_SUCCESS;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment