/* Copyright (C) All Rights Reserved
** Written by k4be, partially based on src/modules/message.c
** License: GPLv3 https://www.gnu.org/licenses/gpl-3.0.html
** A lot of code is copied from message.c, as MSGTAG spec requires same behavior
** as with PRIVMSG, and functions used inside message.c are not reachable from here.
** UnrealIRCd distribution does not implement MSGTAG by choice.
*/
#include "unrealircd.h"
int clienttags_configtest(ConfigFile *cf, ConfigEntry *ce, int type, int *errs);
int clienttags_configposttest(int *errs);
int clienttags_configrun(ConfigFile *cf, ConfigEntry *ce, int type);
struct ct *find_ct(char *name);
int client_mtag_is_ok(Client *client, char *name, char *value);
void mtag_inherit(Client *sender, MessageTag *recv_mtags, MessageTag **mtag_list, char *signature);
ModuleHeader MOD_HEADER = {
"third/client-tags",
"5.0beta2",
"Selective client tags support",
"k4be@PIRC",
"unrealircd-5",
};
// config file stuff, based on Gottem's module
#define MYCONF "client-tags"
/*
client-tags {
tag-name "+draft/typing";
};
*/
struct ct {
char *name;
struct ct *next;
};
struct {
int tempcount;
struct ct *cts;
} ctdata;
MOD_TEST(){
// We have our own config block so we need to checkem config obv m9
// Priorities don't really matter here
ctdata.tempcount = 0;
HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, clienttags_configtest);
HookAdd(modinfo->handle, HOOKTYPE_CONFIGPOSTTEST, 0, clienttags_configposttest);
return MOD_SUCCESS;
}
int clienttags_configtest(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) {
ConfigEntry *cep; // For looping through our bl0cc
int errors = 0; // Error count
// Since we'll add a new top-level block to unrealircd.conf, need to filter on CONFIG_MAIN lmao
if(type != CONFIG_MAIN)
return 0; // Returning 0 means idgaf bout dis
// Check for valid config entries first
if(!ce || !ce->ce_varname)
return 0;
// If it isn't our bl0ck, idc
if(strcmp(ce
->ce_varname
, MYCONF
))
return 0;
// Loop dat shyte fam
for(cep = ce->ce_entries; cep; cep = cep->ce_next) {
// Do we even have a valid name l0l?
if(!cep->ce_varname) {
config_error("%s:%i: blank %s item", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, MYCONF); // Rep0t error
errors++; // Increment err0r count fam
continue; // Next iteration imo tbh
}
if(!strcmp(cep
->ce_varname
, "tag-name")) {
// Check tag name format
if(!cep
->ce_vardata
|| strlen(cep
->ce_vardata
) < 2 || cep
->ce_vardata
[0] != '+') {
config_error("%s:%i: %s::%s value must not be empty and must start with \"+\" character", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, MYCONF, cep->ce_varname);
errors++; // Increment err0r count fam
break;
}
ctdata.tempcount++;
continue;
}
// Anything else is unknown to us =]
config_warn("%s:%i: unknown item %s::%s", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, MYCONF, cep->ce_varname); // So display just a warning
}
*errs = errors;
return errors ? -1 : 1; // Returning 1 means "all good", -1 means we shat our panties
}
int clienttags_configposttest(int *errs) {
int errors = 0;
if(ctdata.tempcount == 0){
config_error("[%s] No valid %s::tag-name entries found!", MOD_HEADER.name, MYCONF);
errors++;
}
*errs = errors;
return errors ? -1 : 1;
}
// "Run" the config (everything should be valid at this point)
int clienttags_configrun(ConfigFile *cf, ConfigEntry *ce, int type) {
ConfigEntry *cep; // For looping through our bl0cc
struct ct *prev_ct = NULL;
struct ct **ct = &ctdata.cts;
// Since we'll add a new top-level block to unrealircd.conf, need to filter on CONFIG_MAIN lmao
if(type != CONFIG_MAIN)
return 0; // Returning 0 means idgaf bout dis
// Check for valid config entries first
if(!ce || !ce->ce_varname)
return 0;
// If it isn't our bl0cc, idc
if(strcmp(ce
->ce_varname
, MYCONF
))
return 0;
// Loop dat shyte fam
for(cep = ce->ce_entries; cep; cep = cep->ce_next) {
// Do we even have a valid name l0l?
if(!cep->ce_varname)
continue; // Next iteration imo tbh
if(!strcmp(cep
->ce_varname
, "tag-name")) {
if(find_ct(cep->ce_vardata)){
config_warn("%s:%i: duplicate tag name \"%s\"", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_vardata);
continue;
}
*ct = safe_alloc(sizeof(struct ct));
(*ct)->name = strdup(cep->ce_vardata);
if(prev_ct)
prev_ct->next = *ct;
prev_ct = *ct;
ct = &(*ct)->next;
continue;
}
}
return 1; // We good
}
MOD_INIT(){
MARK_AS_GLOBAL_MODULE(modinfo);
HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, clienttags_configrun);
HookAddVoid(modinfo->handle, HOOKTYPE_NEW_MESSAGE, 0, mtag_inherit);
return MOD_SUCCESS;
}
MOD_LOAD(){
MessageTagHandlerInfo mtag;
struct ct *ct;
// now request all the client tags configured
for(ct = ctdata.cts; ct; ct = ct->next){
memset(&mtag
, 0, sizeof(mtag
));
mtag.name = ct->name;
mtag.is_ok = client_mtag_is_ok;
mtag.flags = MTAG_HANDLER_FLAGS_NO_CAP_NEEDED;
if(!MessageTagHandlerAdd(modinfo->handle, &mtag)){
config_warn("[%s] Failed to request client tag \"%s\": %s. This one will not work.", MOD_HEADER.name, ct->name, ModuleGetErrorStr(modinfo->handle));
}
}
return MOD_SUCCESS;
}
MOD_UNLOAD(){
struct ct *ct, *prev_ct;
#ifdef TAGMSG_HACK
RealCommand *c;
c = find_command_simple("TAGMSG");
if(c){
c->flags &= ~CMD_SERVER; // revert to the original
}
#endif
// free the memory
ct = ctdata.cts;
prev_ct = ctdata.cts;
while(ct){
safe_free(ct->name);
ct = ct->next;
safe_free(prev_ct);
prev_ct = ct;
}
ctdata.cts = NULL;
return MOD_SUCCESS;
}
struct ct *find_ct(char *name){
struct ct *ct;
for(ct = ctdata.cts; ct; ct = ct->next){
return ct;
}
return NULL;
}
int client_mtag_is_ok(Client *client, char *name, char *value){ // permit anything
return 1;
}
void mtag_inherit(Client *sender, MessageTag *recv_mtags, MessageTag **mtag_list, char *signature){
struct ct *ct;
for(ct = ctdata.cts; ct; ct = ct->next){
MessageTag *m = find_mtag(recv_mtags, ct->name);
if (m)
m = duplicate_mtag(m);
else
continue;
AddListItem(m, *mtag_list);
}
}