Browse Source

spec/m_nopmchannel.c: Prevents users sharing a channel from privately messaging each other

Wazakindjes 3 years ago
parent
commit
fa6863fbe7
2 changed files with 244 additions and 0 deletions
  1. 15 0
      spec/README.md
  2. 229 0
      spec/m_nopmchannel.c

+ 15 - 0
spec/README.md

@@ -5,12 +5,27 @@ __The same important notes from the main README (at the top) also apply here__
 # Index lol
 | Module														| Description																														|
 | ------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------|
+| [m_nopmchannel](#m_nopmchannel)								| Prevents users sharing a channel from privately messaging each other																|
 | [m_topicgreeting](#m_topicgreeting)							| Greet users who join a channel by changing the topic (channel mode +g)															|
 | [m_mafiareturns](#m_mafiareturns)								| Improves MafiaReturns chat functionality																							|
 | [m_signore](#m_signore)										| Implements an `I:Line` for adding server-side ignores																				|
 | [m_block_nosslident](#m_block_nosslident)						| Restrict certain idents to SSL connections __only__																				|
 | [m_nicksuffix](#m_nicksuffix)									| Restrict `/nick` usage to suffixing your base nick																				|
 
+# m_nopmchannel
+A request by `westor`, this module is p much the opposite of my `m_message_commonchans` in that it __prevents__ users from privately messaging each other if they share a specific channel. There are a few exceptions (as usual ;]):
+* People with any of the `+hoaq` access modes, as well as U:Lined users (e.g. BotServ bots), can send and receive normally from all channel members
+* IRC opers can always send to other members but not receive, unless they have one of above-mentioned access modes
+
+__Config block:__
+    nopmchannel {
+    	name "#somechannel";
+    	name "#another";
+    	// More go here
+    };
+
+I went with a config block instead of a channel mode because the module imposes restrictions on user-to-user communications, which is more of a network thing instead of channel-level. ;]
+
 # m_topicgreeting
 `X-Seti` asked for a module that changes a channel's topic to greet someone who just joined. This module implements channel mode `+g` so you can set that shit on a per-channel basis. =]
 

+ 229 - 0
spec/m_nopmchannel.c

@@ -0,0 +1,229 @@
+/* Copyright (C) All Rights Reserved
+** Written by Gottem <support@gottem.nl>
+** Website: https://gitgud.malvager.net/Wazakindjes/unrealircd_mods
+** License: https://gitgud.malvager.net/Wazakindjes/unrealircd_mods/raw/master/LICENSE
+*/
+
+// One include for all cross-platform compatibility thangs
+#include "unrealircd.h"
+
+#define MYHEWK HOOKTYPE_PRE_USERMSG
+#define MYCONF "nopmchannel"
+
+// Big hecks go here
+typedef struct t_nopmchan noPMChan;
+struct t_nopmchan {
+	char *name;
+	noPMChan *next;
+};
+
+// Quality fowod declarations
+int nopmchannel_configtest(ConfigFile *cf, ConfigEntry *ce, int type, int *errs);
+int nopmchannel_configposttest(int *errs); // You may not need this
+int nopmchannel_configrun(ConfigFile *cf, ConfigEntry *ce, int type);
+char *nopmchannel_preusermsg(aClient *sptr, aClient *to, char *text, int notice);
+
+// Muh globals
+static ModuleInfo *nopmchannelMI = NULL; // Store ModuleInfo so we can use it to check for errors in MOD_LOAD
+int noPMCount = 0;
+noPMChan *noPMList = NULL;
+Hook *noPMHook = NULL;
+
+// Dat dere module header
+ModuleHeader MOD_HEADER(m_nopmchannel) = {
+	"m_nopmchannel", // Module name
+	"$Id: v1.0 2018/12/22 Gottem$", // Version
+	"Prevents users sharing a channel from privately messaging each other", // Description
+	"3.2-b8-1", // Modversion, not sure wat do
+	NULL
+};
+
+// Configuration testing-related hewks go in testing phase obv
+MOD_TEST(m_nopmchannel) {
+	// We have our own config block so we need to checkem config obv m9
+	// Priorities don't really matter here
+	HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, nopmchannel_configtest);
+	HookAdd(modinfo->handle, HOOKTYPE_CONFIGPOSTTEST, 0, nopmchannel_configposttest);
+	return MOD_SUCCESS;
+}
+
+// Initialisation routine (register hooks, commands and modes or create structs etc)
+MOD_INIT(m_nopmchannel) {
+	nopmchannelMI = modinfo; // Store module info yo
+	HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, nopmchannel_configrun);
+	noPMHook = HookAddPChar(modinfo->handle, MYHEWK, -100, nopmchannel_preusermsg); // High priority lol
+	return MOD_SUCCESS; // Let MOD_LOAD handle errors and registering of overrides
+}
+
+MOD_LOAD(m_nopmchannel) {
+	// Check if module handle is available, also check for commands/hooks/overrides that weren't added for some raisin
+	if(ModuleGetError(nopmchannelMI->handle) != MODERR_NOERROR || !noPMHook) {
+		// Display error string kek
+		config_error("A critical error occurred when loading module %s: %s", MOD_HEADER(m_nopmchannel).name, ModuleGetErrorStr(nopmchannelMI->handle));
+		return MOD_FAILED; // No good
+	}
+	return MOD_SUCCESS; // We good
+}
+
+// Called on unload/rehash obv
+MOD_UNLOAD(m_nopmchannel) {
+	// Clean up any structs and other shit here
+	if(noPMList) {
+		// This shit is a bit convoluted to prevent memory issues obv famalmalmalmlmalm
+		noPMChan *npEntry;
+		while((npEntry = noPMList) != NULL) {
+			noPMList = noPMList->next;
+			if(npEntry->name) free(npEntry->name);
+			free(npEntry);
+		}
+		noPMList = NULL;
+	}
+	noPMCount = 0; // Just to maek shur
+	return MOD_SUCCESS; // We good
+}
+
+int nopmchannel_configtest(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) {
+	int errors = 0; // Error count
+	ConfigEntry *cep; // To store the current variable/value pair etc
+
+	// Since we'll add a 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 block, 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(!cep->ce_vardata) {
+			config_error("%s:%i: blank %s value", 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, "name")) {
+			char *chan = cep->ce_vardata;
+			size_t chanlen = strlen(chan);
+			if(!chanlen) {
+				config_error("%s:%i: %s::%s must be non-empty fam", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, MYCONF, cep->ce_varname);
+				errors++; // Increment err0r count fam
+			}
+			else if(chan[0] != '#') {
+				config_error("%s:%i: invalid channel name '%s' for %s (must start with a \002#\002)", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, chan, MYCONF);
+				errors++; // Increment err0r count fam
+			}
+			else if(chan[1] && !isdigit(chan[1]) && !isalpha(chan[1])) {
+				config_error("%s:%i: invalid channel name '%s' for %s (second char must be alphanumeric)", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, chan, MYCONF);
+				errors++; // Increment err0r count fam
+			}
+			else if(chanlen > CHANNELLEN) {
+				config_error("%s:%i: invalid channel name '%s' for %s (name is too long, must be equal to or less than %d chars)", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, chan, MYCONF, CHANNELLEN);
+				errors++; // Increment err0r count fam
+			}
+			else
+				noPMCount++;
+			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
+}
+
+// Post test, check for missing shit here
+int nopmchannel_configposttest(int *errs) {
+	int errors = 0;
+	if(!noPMCount) // Just a warning will suffice imo =]
+		config_warn("Module %s was loaded but there are no (valid) configuration entries (%s {} block)", MOD_HEADER(m_nopmchannel).name, MYCONF);
+	*errs = errors;
+	return errors ? -1 : 1;
+}
+
+noPMChan *find_npchan(char *name) {
+	noPMChan *npEntry = NULL;
+	for(npEntry = noPMList; npEntry; npEntry = npEntry->next) {
+		if(!stricmp(npEntry->name, name))
+			break;
+	}
+	return npEntry;
+}
+
+// "Run" the config (everything should be valid at this point)
+int nopmchannel_configrun(ConfigFile *cf, ConfigEntry *ce, int type) {
+	ConfigEntry *cep; // To store the current variable/value pair etc
+	noPMChan *last = NULL; // Initialise to NULL so the loop requires minimal l0gic
+	noPMChan **npEntry = &noPMList; // Hecks so the ->next chain stays intact
+
+	// Since we'll add a 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(!noPMCount || !ce || !ce->ce_varname)
+		return 0;
+
+	// If it isn't nopmchannel, 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 and value l0l?
+		if(!cep->ce_varname || !cep->ce_vardata)
+			continue; // Next iteration imo tbh
+
+		if(!strcmp(cep->ce_varname, "name")) {
+			size_t namelen = sizeof(char) * (strlen(cep->ce_vardata) + 1);
+			*npEntry = malloc(sizeof(noPMChan));
+			(*npEntry)->name = malloc(namelen);
+			(*npEntry)->next = NULL;
+			strncpy((*npEntry)->name, cep->ce_vardata, namelen);
+
+			// Premium linked list fam
+			if(last)
+				last->next = *npEntry;
+			last = *npEntry;
+			npEntry = &(*npEntry)->next;
+		}
+	}
+
+	return 1; // We good
+}
+
+// Actual hewk function m8
+char *nopmchannel_preusermsg(aClient *sptr, aClient *to, char *text, int notice) {
+	aChannel *chptr;
+	Membership *lp;
+
+	// Let's exclude some shit lol
+	if(!MyConnect(sptr) || !sptr || !to || sptr == to || IsULine(sptr) || IsULine(to) || IsOper(sptr) || !IsPerson(sptr) || !IsPerson(to))
+		return text;
+
+	for(lp = sptr->user->channel; lp; lp = lp->next) {
+		chptr = lp->chptr;
+		if(find_npchan(chptr->chname)) {
+			// Receiver should prolly be a member of the currently checked channel to begin with ;]
+			if(IsMember(to, chptr) && !is_skochanop(sptr, chptr) && !is_skochanop(to, chptr)) {
+				sendnotice(sptr, "*** You have to part from channel '%s' in order to send private messages to %s", chptr->chname, to->name);
+				return NULL;
+			}
+		}
+	}
+
+	return text;
+}