Browse Source

m_mafiareturns.c: Requested by IzzyCreamcheese, improves MafiaReturns chat functionality =]

Wazakindjes 3 years ago
parent
commit
2d9c8c1d1b
2 changed files with 353 additions and 0 deletions
  1. 15 0
      spec/README.md
  2. 338 0
      spec/m_mafiareturns.c

+ 15 - 0
spec/README.md

@@ -5,10 +5,25 @@ __The same important notes from the main README (at the top) also apply here__
 # Index lol
 | Module														| Description																														|
 | ------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------|
+| [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_mafiareturns
+Requested by `IzzyCreamcheese`, this module improves upon his website's chat functionality.
+
+__Config block:__
+    mafiareturns {
+    	channels "#chan1, #chan2";
+    	tag "[MR]";
+    	position back;
+    };
+
+The `position` can be either `back` or `front`, the meaning of which should be pretty clear. =] If a user's nick matches the `tag` at `position`, the module will prevent them from changing their nick or parting any of the channels listed in `channels` and notices them about it (shit like `/part #somechan,#chan1` will be rewritten to `/part #somechan` too). Servers, U:Lines and opers etc are exempt for obvious raisins. ;]
+
+The directives `channels` and `tag` are required, `position` will fall back to `back` if it's omitted or contains an invalid value.
+
 # m_signore
 Requested by `Jellis` and `Stanley`. This module allows privileged opers to set a server-side ignore of sorts, making 2 users' messages invisible to each other (in private only tho, just muteban them if they shitpost in-channel or something). Servers, U:Lines and opers are exempt for obvious raisins.
 

+ 338 - 0
spec/m_mafiareturns.c

@@ -0,0 +1,338 @@
+// One include for all cross-platform compatibility thangs
+#include "unrealircd.h"
+
+// Config block
+#define MYCONF "mafiareturns"
+#define MCHAN_DELIMS ", \t"
+
+// Commands to override
+#define OVR_NICK "NICK"
+#define OVR_PART "PART"
+
+// Quality fowod declarations
+unsigned short int mafiareturns_matchnick(aClient *sptr);
+int mafiareturns_configtest(ConfigFile *cf, ConfigEntry *ce, int type, int *errs);
+int mafiareturns_configposttest(int *errs); // You may not need this
+int mafiareturns_configrun(ConfigFile *cf, ConfigEntry *ce, int type);
+static int mafiareturns_nick_override(Cmdoverride *ovr, aClient *cptr, aClient *sptr, int parc, char *parv[]);
+static int mafiareturns_part_override(Cmdoverride *ovr, aClient *cptr, aClient *sptr, int parc, char *parv[]);
+
+// Muh globals
+static ModuleInfo *mafiaMI = NULL; // Store ModuleInfo so we can use it to check for errors in MOD_LOAD
+Cmdoverride *mafiaNickOVR, *mafiaPartOVR; // Pointers to the overrides we're gonna add
+
+// Big hecks go here
+typedef struct t_mafiachan MChan;
+struct t_mafiachan {
+	char *name;
+	MChan *next;
+};
+
+struct {
+	MChan *channels; // List of channels to "protect"
+	char *tag; // If the nick ends/starts with this, we gotta do some shit
+	char position; // 'f'ront and 'b'ack lol
+
+	unsigned int num_chans; // How many chans we got
+
+	// These are just for setting to 0 or 1 to see if we got em config directives ;]
+	unsigned short int got_channels;
+	unsigned short int got_tag;
+	unsigned short int got_position;
+} muhcfg;
+
+// Dat dere module header
+ModuleHeader MOD_HEADER(m_mafiareturns) = {
+	"m_mafiareturns", // Module name
+	"$Id: v1.0 2018/01/11 Gottem$", // Version
+	"Improves MafiaReturns chat functionality", // Description
+	"3.2-b8-1", // Modversion, not sure wat do
+	NULL
+};
+
+// Configuration testing-related hewks go in testing phase obv
+MOD_TEST(m_mafiareturns) {
+	// 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, mafiareturns_configtest);
+	HookAdd(modinfo->handle, HOOKTYPE_CONFIGPOSTTEST, 0, mafiareturns_configposttest);
+	return MOD_SUCCESS;
+}
+
+// Initialisation routine (register hooks, commands and modes or create structs etc)
+MOD_INIT(m_mafiareturns) {
+	mafiaMI = modinfo; // Store module info yo
+	HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, mafiareturns_configrun);
+	return MOD_SUCCESS; // Let MOD_LOAD handle errors and registering of overrides
+}
+
+MOD_LOAD(m_mafiareturns) {
+	// Attempt to add command overrides
+	mafiaNickOVR = CmdoverrideAdd(mafiaMI->handle, OVR_NICK, mafiareturns_nick_override);
+	mafiaPartOVR = CmdoverrideAdd(mafiaMI->handle, OVR_PART, mafiareturns_part_override);
+
+	// Check if module handle is available, also check for commands/hooks/overrides that weren't added for some raisin
+	if(ModuleGetError(mafiaMI->handle) != MODERR_NOERROR || !mafiaNickOVR || !mafiaPartOVR) {
+		// Display error string kek
+		config_error("A critical error occurred when loading module %s: %s", MOD_HEADER(m_mafiareturns).name, ModuleGetErrorStr(mafiaMI->handle));
+		return MOD_FAILED; // No good
+	}
+	return MOD_SUCCESS; // We good
+}
+
+// Called on unload/rehash obv
+MOD_UNLOAD(m_mafiareturns) {
+	MChan *chan; // Iterat0r
+	if(muhcfg.num_chans) { // inb4rip
+		// This shit is a bit convoluted to prevent memory issues obv famalmalmalmlmalm
+		while((chan = muhcfg.channels) != NULL) {
+			muhcfg.channels = muhcfg.channels->next;
+			free(chan);
+		}
+		muhcfg.channels = NULL; // Let's be explicit lol
+	}
+	muhcfg.num_chans = 0; // Just to maek shur
+	if(muhcfg.tag) // inb4rip
+		free(muhcfg.tag); // Let's free this lol
+	muhcfg.position = 0;
+	return MOD_SUCCESS; // We good
+}
+
+int mafiareturns_configtest(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) {
+	int errors = 0; // Error count
+	ConfigEntry *cep; // To store the current variable/value pair etc
+	char *chanlist, *p, *chname; // Pointurs for checking em chanlist
+
+	// 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 || !strlen(cep->ce_vardata)) {
+			config_error("%s:%i: blank value for %s::%s", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, MYCONF, cep->ce_varname); // Rep0t error
+			errors++; // Increment err0r count fam
+			continue; // Next iteration imo tbh
+		}
+
+		if(!strcmp(cep->ce_varname, "channels")) {
+			chanlist = strdup(cep->ce_vardata); // Clone em
+			// And czech em
+			for(chname = strtoken(&p, chanlist, MCHAN_DELIMS); chname; chname = strtoken(&p, NULL, MCHAN_DELIMS)) {
+				if(*chname != '#') {
+					config_warn("%s:%i: invalid value '%s' for %s::%s (must start with #)", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, chname, MYCONF, cep->ce_varname); // Rep0t error
+					errors++;
+					continue;
+				}
+				if(strlen(chname) > CHANNELLEN) {
+					config_warn("%s:%i: invalid value '%s' for %s::%s (channel names may be up to %d characters long)", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, chname, MYCONF, cep->ce_varname, CHANNELLEN);
+					errors++;
+					continue;
+				}
+			}
+			free(chanlist);
+			if(!errors)
+				muhcfg.got_channels = 1;
+			continue;
+		}
+
+		if(!strcmp(cep->ce_varname, "tag")) {
+			muhcfg.got_tag = 1;
+			continue;
+		}
+
+		if(!strcmp(cep->ce_varname, "position")) {
+			if(strcmp(cep->ce_vardata, "front") && strcmp(cep->ce_vardata, "back")) {
+				config_warn("%s:%i: value for %s::%s must be either 'front' or 'back'", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, MYCONF, cep->ce_varname); // Rep0t error
+				errors++;
+				continue;
+			}
+			muhcfg.got_position = 1;
+			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 mafiareturns_configposttest(int *errs) {
+	int errors = 0;
+
+	if(!muhcfg.got_channels) {
+		config_error("%s: %s::channels is empty/invalid, aborting module load", MOD_HEADER(m_mafiareturns).name, MYCONF);
+		errors++;
+	}
+
+	if(!muhcfg.got_tag) {
+		config_error("%s: %s::tag is empty/invalid, aborting module load", MOD_HEADER(m_mafiareturns).name, MYCONF);
+		errors++;
+	}
+
+	if(!muhcfg.got_position) {
+		config_warn("%s: %s::position is empty/invalid, defaulting to 'back'", MOD_HEADER(m_mafiareturns).name, MYCONF);
+		muhcfg.position = 'b';
+	}
+
+	*errs = errors;
+	return errors ? -1 : 1;
+}
+
+// "Run" the config (everything should be valid at this point)
+int mafiareturns_configrun(ConfigFile *cf, ConfigEntry *ce, int type) {
+	ConfigEntry *cep; // To store the current variable/value pair etc
+	char *chanlist, *p, *chname; // Pointurs for checking em chanlist
+
+	MChan *last = NULL; // Initialise to NULL so the loop requires minimal l0gic
+	MChan **chan = &muhcfg.channels; // 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(!ce || !ce->ce_varname)
+		return 0;
+
+	// If it isn't mafiareturns, 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 val00 l0l?
+		if(!cep->ce_varname || !cep->ce_vardata || !strlen(cep->ce_vardata))
+			continue; // Next iteration imo tbh
+
+		if(!strcmp(cep->ce_varname, "channels")) {
+			chanlist = strdup(cep->ce_vardata); // Clone em
+			for(chname = strtoken(&p, chanlist, MCHAN_DELIMS); chname; chname = strtoken(&p, NULL, MCHAN_DELIMS)) {
+				size_t chanlen = sizeof(char) * (strlen(chname) + 1);
+
+				// Allocate mem0ry for the current entry
+				*chan = malloc(sizeof(MChan));
+
+				// Allocate/initialise shit here
+				(*chan)->name = malloc(chanlen);
+				(*chan)->next = NULL;
+
+				// Copy that shit fam
+				strncpy((*chan)->name, chname, chanlen);
+
+				// Premium linked list fam
+				if(last)
+					last->next = *chan;
+
+				last = *chan;
+				chan = &(*chan)->next;
+			}
+			free(chanlist);
+			continue;
+		}
+
+		if(!strcmp(cep->ce_varname, "tag")) {
+			muhcfg.tag = strdup(cep->ce_vardata);
+			continue;
+		}
+
+		if(!strcmp(cep->ce_varname, "position")) {
+			muhcfg.position = cep->ce_vardata[0];
+			continue;
+		}
+
+	}
+
+	return 1; // We good
+}
+
+unsigned short int mafiareturns_matchnick(aClient *sptr) {
+	size_t taglen; // Length of el taggerino y0
+
+	if(!sptr || !MyClient(sptr) || !IsPerson(sptr) || IsULine(sptr) || IsOper(sptr)) // Sanity cheqq and exclusions lol
+		return 0;
+
+	taglen = strlen(muhcfg.tag);
+
+	if(muhcfg.position == 'f') {
+		if(!strncmp(sptr->name, muhcfg.tag, taglen))
+			return 1; // got em
+	}
+
+	else if(muhcfg.position == 'b') {
+		if(!strncmp(sptr->name + strlen(sptr->name) - taglen, muhcfg.tag, taglen))
+			return 1; // got em
+	}
+
+	return 0; // No match phambly
+}
+
+// Now for the actual overrides
+static int mafiareturns_nick_override(Cmdoverride *ovr, aClient *cptr, aClient *sptr, int parc, char *parv[]) {
+	/* Gets args: Cmdoverride *ovr, aClient *cptr, aClient *sptr, int parc, char *parv[]
+	**
+	** ovr: Pointer to the override we're attached to
+	** cptr: Pointer to directly attached client -- if remote user this is the remote server instead
+	** sptr: Pointer to user executing command
+	** parc: Amount of arguments (also includes the command in the count)
+	** parv: Contains the actual args, first one starts at parv[1]
+	**
+	** So "NICK test" would result in parc = 2 and parv[1] = "test"
+	** Also, parv[0] seems to always be NULL, so better not rely on it fam
+	*/
+	if(!BadPtr(parv[1]) && mafiareturns_matchnick(sptr)) {
+		sendnotice(sptr, "*** [mafiareturns] Official accounts may not change their names.");
+		return 0; // Stop processing yo
+	}
+
+	return CallCmdoverride(ovr, cptr, sptr, parc, parv); // Run original function yo
+}
+
+static int mafiareturns_part_override(Cmdoverride *ovr, aClient *cptr, aClient *sptr, int parc, char *parv[]) {
+	MChan *chan; // Iterat0r
+	char *chanlist, *p, *chname; // Pointurs for checking em chanlist
+	char partstr[BUFSIZE]; // To rebuild the argument with allowed chans =]
+
+	if(BadPtr(parv[1]) || !mafiareturns_matchnick(sptr)) // No need to do anything lol
+		return CallCmdoverride(ovr, cptr, sptr, parc, parv); // Run original function yo
+
+	// Check if tryna part one or more "protected" chans imo
+	memset(partstr, '\0', sizeof(partstr));
+	chanlist = strdup(parv[1]); // Clone em arg
+	for(chname = strtoken(&p, chanlist, MCHAN_DELIMS); chname; chname = strtoken(&p, NULL, MCHAN_DELIMS)) {
+		for(chan = muhcfg.channels; chan; chan = chan->next) {
+			if(!stricmp(chan->name, chname)) { // Case insensitive match pls
+				sendnotice(sptr, "*** [mafiareturns] Official accounts may not part game channels (%s).", chname);
+				break;
+			}
+		}
+		if(!chan)
+			ircsnprintf(partstr, sizeof(partstr), "%s%s,", partstr, chname); // Allowed to part, always appending comma for maximum laziness ;];]
+	}
+	free(chanlist); // Free pls
+
+	// Got nething left?
+	if(!partstr[0])
+		return 0; // Stop processing yo
+
+	parv[1] = partstr; // Replace w/ new argument
+	return CallCmdoverride(ovr, cptr, sptr, parc, parv); // Run original function yo
+}