m_noghosts.c 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. /* Copyright (C) All Rights Reserved
  2. ** Written by Gottem <support@gottem.nl>
  3. ** Website: https://gitgud.malvager.net/Wazakindjes/unrealircd_mods
  4. ** License: https://gitgud.malvager.net/Wazakindjes/unrealircd_mods/raw/master/LICENSE
  5. */
  6. // One include for all cross-platform compatibility thangs
  7. #include "unrealircd.h"
  8. // Config block
  9. #define MYCONF "noghosts"
  10. // Hewkerino
  11. #define MYHEWK HOOKTYPE_UMODE_CHANGE
  12. // Big hecks go here
  13. typedef struct t_chanstrukk muhchan;
  14. struct t_chanstrukk {
  15. char *name;
  16. muhchan *next;
  17. };
  18. // Quality fowod declarations;
  19. int noghosts_configtest(ConfigFile *cf, ConfigEntry *ce, int type, int *errs);
  20. int noghosts_configposttest(int *errs);
  21. int noghosts_configrun(ConfigFile *cf, ConfigEntry *ce, int type);
  22. unsigned short int is_chan_monitored(char *chname);
  23. int noghosts_hook_chumode(aClient *sptr, long oldflags, long newflags);
  24. // Muh globals
  25. static ModuleInfo *noghostsMI = NULL; // Store ModuleInfo so we can use it to check for errors in MOD_LOAD
  26. Hook *noghostsHook = NULL;
  27. // Muh config shit y0
  28. struct {
  29. char *message;
  30. char *flags;
  31. muhchan *channels;
  32. int chancount;
  33. unsigned short int got_message;
  34. unsigned short int got_flags;
  35. } muhcfg;
  36. // Dat dere module header
  37. ModuleHeader MOD_HEADER(m_noghosts) = {
  38. "m_noghosts", // Module name
  39. "$Id: v1.01 2018/03/02 Gottem$", // Version
  40. "Keep channels clear of \"ghosts\" of opers", // Description
  41. "3.2-b8-1", // Modversion, not sure wat do
  42. NULL
  43. };
  44. // Configuration testing-related hewks go in testing phase obv
  45. MOD_TEST(m_noghosts) {
  46. // We have our own config block so we need to checkem config obv m9
  47. // Priorities don't really matter here
  48. HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, noghosts_configtest);
  49. HookAdd(modinfo->handle, HOOKTYPE_CONFIGPOSTTEST, 0, noghosts_configposttest);
  50. return MOD_SUCCESS;
  51. }
  52. // Initialisation routine (register hooks, commands and modes or create structs etc)
  53. MOD_INIT(m_noghosts) {
  54. noghostsMI = modinfo; // Store module info yo
  55. HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, noghosts_configrun);
  56. // Add a hook with priority 0 (i.e. normal) that returns an int
  57. noghostsHook = HookAdd(modinfo->handle, MYHEWK, 0, noghosts_hook_chumode);
  58. return MOD_SUCCESS; // Let MOD_LOAD handle errors and registering of overrides
  59. }
  60. MOD_LOAD(m_noghosts) {
  61. // Check if module handle is available, also check for commands/hooks/overrides that weren't added for some raisin
  62. if(ModuleGetError(noghostsMI->handle) != MODERR_NOERROR || !noghostsHook) {
  63. // Display error string kek
  64. config_error("A critical error occurred when loading module %s: %s", MOD_HEADER(m_noghosts).name, ModuleGetErrorStr(noghostsMI->handle));
  65. return MOD_FAILED; // No good
  66. }
  67. return MOD_SUCCESS; // We good
  68. }
  69. // Called on unload/rehash obv
  70. MOD_UNLOAD(m_noghosts) {
  71. // Clean up any structs and other shit here
  72. if(muhcfg.message) {
  73. free(muhcfg.message);
  74. muhcfg.message = NULL;
  75. }
  76. if(muhcfg.flags) {
  77. free(muhcfg.flags);
  78. muhcfg.flags = NULL;
  79. }
  80. if(muhcfg.channels) {
  81. // This shit is a bit convoluted to prevent memory issues obv famalmalmalmlmalm
  82. muhchan *ch;
  83. while((ch = muhcfg.channels) != NULL) {
  84. muhcfg.channels = muhcfg.channels->next;
  85. if(ch->name) free(ch->name);
  86. free(ch);
  87. }
  88. muhcfg.channels = NULL;
  89. }
  90. muhcfg.chancount = 0;
  91. return MOD_SUCCESS; // We good
  92. }
  93. int noghosts_configtest(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) {
  94. int errors = 0; // Error count
  95. ConfigEntry *cep, *cep2; // To store the current variable/value pair etc, nested
  96. // Since we'll add a top-level block to unrealircd.conf, need to filter on CONFIG_MAIN lmao
  97. if(type != CONFIG_MAIN)
  98. return 0; // Returning 0 means idgaf bout dis
  99. // Check for valid config entries first
  100. if(!ce || !ce->ce_varname)
  101. return 0;
  102. // If it isn't our block, idc
  103. if(strcmp(ce->ce_varname, MYCONF))
  104. return 0;
  105. // Loop dat shyte fam
  106. for(cep = ce->ce_entries; cep; cep = cep->ce_next) {
  107. // Do we even have a valid name l0l?
  108. if(!cep->ce_varname) {
  109. config_error("%s:%i: blank %s item", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, MYCONF); // Rep0t error
  110. errors++; // Increment err0r count fam
  111. continue; // Next iteration imo tbh
  112. }
  113. if(!strcmp(cep->ce_varname, "flags")) {
  114. if(!cep->ce_vardata || !strlen(cep->ce_vardata)) {
  115. config_error("%s:%i: %s::%s must be non-empty fam", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, MYCONF, cep->ce_varname);
  116. errors++; // Increment err0r count fam
  117. continue;
  118. }
  119. if(strcmp(cep->ce_vardata, "O")) {
  120. config_error("%s:%i: %s::%s must be one of: O", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, MYCONF, cep->ce_varname);
  121. errors++; // Increment err0r count fam
  122. continue;
  123. }
  124. muhcfg.got_flags = 1;
  125. continue;
  126. }
  127. if(!strcmp(cep->ce_varname, "message")) {
  128. if(!cep->ce_vardata || !strlen(cep->ce_vardata)) {
  129. config_error("%s:%i: %s::%s must be non-empty fam", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, MYCONF, cep->ce_varname);
  130. errors++; // Increment err0r count fam
  131. continue;
  132. }
  133. muhcfg.got_message = 1;
  134. continue;
  135. }
  136. // Here comes a nested block =]
  137. if(!strcmp(cep->ce_varname, "channels")) {
  138. // Loop 'em again
  139. for(cep2 = cep->ce_entries; cep2; cep2 = cep2->ce_next) {
  140. if(!cep2->ce_varname || !strlen(cep2->ce_varname)) {
  141. config_error("%s:%i: blank %s::%s item", cep2->ce_fileptr->cf_filename, cep2->ce_varlinenum, MYCONF, cep->ce_varname); // Rep0t error
  142. errors++; // Increment err0r count fam
  143. continue; // Next iteration imo tbh
  144. }
  145. if(cep2->ce_varname[0] != '#') {
  146. config_error("%s:%i: invalid channel name '%s': must start with #", cep2->ce_fileptr->cf_filename, cep2->ce_varlinenum, cep2->ce_varname); // Rep0t error
  147. errors++; // Increment err0r count fam
  148. continue; // Next iteration imo tbh
  149. }
  150. if(strlen(cep2->ce_varname) > CHANNELLEN) {
  151. config_error("%s:%i: invalid channel name '%s': must not exceed %d characters in length", cep2->ce_fileptr->cf_filename, cep2->ce_varlinenum, cep2->ce_varname, CHANNELLEN);
  152. errors++; // Increment err0r count fam
  153. continue; // Next iteration imo tbh
  154. }
  155. muhcfg.chancount++;
  156. }
  157. continue;
  158. }
  159. // Anything else is unknown to us =]
  160. 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
  161. }
  162. *errs = errors;
  163. return errors ? -1 : 1; // Returning 1 means "all good", -1 means we shat our panties
  164. }
  165. // Post test, check for missing shit here
  166. int noghosts_configposttest(int *errs) {
  167. int errors = 0;
  168. if(!muhcfg.got_flags)
  169. muhcfg.flags = strdup("O");
  170. if(!muhcfg.got_message) // I may remove/change this when more flags are used ;]
  171. muhcfg.message = strdup("Opered down");
  172. *errs = errors;
  173. return errors ? -1 : 1;
  174. }
  175. // "Run" the config (everything should be valid at this point)
  176. int noghosts_configrun(ConfigFile *cf, ConfigEntry *ce, int type) {
  177. ConfigEntry *cep, *cep2; // To store the current variable/value pair etc, nested
  178. muhchan *last = NULL; // Initialise to NULL so the loop requires minimal l0gic
  179. muhchan **chan = &(muhcfg.channels); // Hecks so the ->next chain stays intact
  180. // Since we'll add a top-level block to unrealircd.conf, need to filter on CONFIG_MAIN lmao
  181. if(type != CONFIG_MAIN)
  182. return 0; // Returning 0 means idgaf bout dis
  183. // Check for valid config entries first
  184. if(!ce || !ce->ce_varname)
  185. return 0;
  186. // If it isn't noghosts, idc
  187. if(strcmp(ce->ce_varname, MYCONF))
  188. return 0;
  189. // Loop dat shyte fam
  190. for(cep = ce->ce_entries; cep; cep = cep->ce_next) {
  191. // Do we even have a valid name l0l?
  192. if(!cep->ce_varname)
  193. continue; // Next iteration imo tbh
  194. if(!strcmp(cep->ce_varname, "flags")) {
  195. if(muhcfg.flags)
  196. free(muhcfg.flags);
  197. muhcfg.flags = strdup(cep->ce_vardata);
  198. continue;
  199. }
  200. if(!strcmp(cep->ce_varname, "message")) {
  201. if(muhcfg.message)
  202. free(muhcfg.message);
  203. muhcfg.message = strdup(cep->ce_vardata);
  204. continue;
  205. }
  206. // Nesting
  207. if(!strcmp(cep->ce_varname, "channels")) {
  208. // Loop 'em
  209. for(cep2 = cep->ce_entries; cep2; cep2 = cep2->ce_next) {
  210. if(!cep2->ce_varname)
  211. continue; // Next iteration imo tbh
  212. // Gotta get em length yo
  213. size_t namelen = sizeof(char) * (strlen(cep2->ce_varname) + 1);
  214. // Allocate mem0ry for the current entry
  215. *chan = malloc(sizeof(muhchan));
  216. // Allocate/initialise shit here
  217. (*chan)->name = malloc(namelen);
  218. (*chan)->next = NULL;
  219. // Copy that shit fam
  220. strncpy((*chan)->name, cep2->ce_varname, namelen);
  221. // Premium linked list fam
  222. if(last)
  223. last->next = *chan;
  224. last = *chan;
  225. chan = &(*chan)->next;
  226. }
  227. continue;
  228. }
  229. }
  230. return 1; // We good
  231. }
  232. unsigned short int is_chan_monitored(char *chname) {
  233. muhchan *chan;
  234. if(!muhcfg.chancount) // No channels specified = default to all ;]
  235. return 1;
  236. // Checkem configured channels nao
  237. for(chan = muhcfg.channels; chan; chan = chan->next) {
  238. if(!stricmp(chan->name, chname))
  239. return 1;
  240. }
  241. return 0;
  242. }
  243. // Actual hewk functions m8
  244. int noghosts_hook_chumode(aClient *sptr, long oldflags, long newflags) {
  245. Membership *lp, *next;
  246. char *parv[4];
  247. // Don't bother with remote clients or if flags r set but don't contain O ;];]
  248. if(!MyConnect(sptr) || (muhcfg.flags && !strchr(muhcfg.flags, 'O')))
  249. return HOOK_CONTINUE;
  250. // CHECK OPER DOWN LOL
  251. if((oldflags & UMODE_OPER) && !(newflags & UMODE_OPER)) {
  252. for(lp = sptr->user->channel; lp; lp = next) {
  253. next = lp->next; // Cuz inb4rip linked list after a PART
  254. // Check for chancount here to save one function call/iteration =]]]
  255. if(has_channel_mode(lp->chptr, 'O') && (!muhcfg.chancount || is_chan_monitored(lp->chptr->chname))) {
  256. // Rebuild all parv cuz we prolly should =]
  257. parv[0] = NULL; // PART
  258. parv[1] = lp->chptr->chname;
  259. parv[2] = muhcfg.message;
  260. parv[3] = NULL; // EOL
  261. int suppresshack = do_cmd(sptr, sptr, "PART", 3, parv);
  262. }
  263. }
  264. }
  265. return HOOK_CONTINUE;
  266. }