m_anticaps.c 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  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. // Command to override
  9. #define OVR_PRIVMSG "PRIVMSG"
  10. #define OVR_NOTICE "NOTICE"
  11. // Config block
  12. #define MYCONF "anticaps"
  13. // Quality fowod declarations
  14. int anticaps_configtest(ConfigFile *cf, ConfigEntry *ce, int type, int *errs);
  15. int anticaps_configrun(ConfigFile *cf, ConfigEntry *ce, int type);
  16. int anticaps_rehash(void);
  17. static int anticaps_override(Cmdoverride *ovr, aClient *cptr, aClient *sptr, int parc, char *parv[]);
  18. // Muh globals
  19. static ModuleInfo *anticapsMI = NULL; // Store ModuleInfo so we can use it to check for errors in MOD_LOAD
  20. Cmdoverride *anticapsOVR, *anticapsOVR2; // Pointers to the overrides we're gonna add
  21. int capsLimit = 50; // Default to blocking >= 50% caps
  22. int minLength = 30; // Minimum length of 30 before we checkem
  23. int lcIt = 0; // Lowercase 'em instead
  24. // Dat dere module header
  25. ModuleHeader MOD_HEADER(m_anticaps) = {
  26. "m_anticaps", // Module name
  27. "$Id: v1.08 2018/09/25 Gottem$", // Version
  28. "Block/lowercase messages that contain a configurable amount of capital letters", // Description
  29. "3.2-b8-1", // Modversion, not sure wat do
  30. NULL
  31. };
  32. // Configuration testing-related hewks go in testing phase obv
  33. MOD_TEST(m_anticaps) {
  34. // We have our own config block so we need to checkem config obv m9
  35. // Priorities don't really matter here
  36. HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, anticaps_configtest);
  37. return MOD_SUCCESS;
  38. }
  39. // Initialisation routine (register hooks, commands and modes or create structs etc)
  40. MOD_INIT(m_anticaps) {
  41. HookAdd(modinfo->handle, HOOKTYPE_REHASH, 0, anticaps_rehash);
  42. HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, anticaps_configrun);
  43. anticapsMI = modinfo; // Store module info yo
  44. return MOD_SUCCESS; // Let MOD_LOAD handle errors and registering of overrides
  45. }
  46. // Actually load the module here (also command overrides as they may not exist in MOD_INIT yet)
  47. MOD_LOAD(m_anticaps) {
  48. anticapsOVR = CmdoverrideAdd(anticapsMI->handle, OVR_PRIVMSG, anticaps_override); // Attempt to add command override for PRIVMSG
  49. anticapsOVR2 = CmdoverrideAdd(anticapsMI->handle, OVR_NOTICE, anticaps_override); // Attempt to add command override for NOTICE
  50. // Did the module throw an error when adding override(s), or is anticapsOVR(2) null even?
  51. if(ModuleGetError(anticapsMI->handle) != MODERR_NOERROR || !anticapsOVR || !anticapsOVR2) {
  52. // Display error string kek
  53. config_error("A critical error occurred when loading module %s: %s", MOD_HEADER(m_anticaps).name, ModuleGetErrorStr(anticapsMI->handle));
  54. return MOD_FAILED; // No good
  55. }
  56. return MOD_SUCCESS; // We good
  57. }
  58. // Called on unload/rehash obv
  59. MOD_UNLOAD(m_anticaps) {
  60. return MOD_SUCCESS; // We good
  61. }
  62. int anticaps_configtest(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) {
  63. int errors = 0; // Error count
  64. int i, limit; // Iterat0r
  65. ConfigEntry *cep; // To store the current variable/value pair etc
  66. // Since we'll add a top-level block to unrealircd.conf, need to filter on CONFIG_MAIN lmao
  67. if(type != CONFIG_MAIN)
  68. return 0; // Returning 0 means idgaf bout dis
  69. // Check for valid config entries first
  70. if(!ce || !ce->ce_varname)
  71. return 0;
  72. // If it isn't our block, idc
  73. if(strcmp(ce->ce_varname, MYCONF))
  74. return 0;
  75. // Loop dat shyte fam
  76. for(cep = ce->ce_entries; cep; cep = cep->ce_next) {
  77. // Do we even have a valid name l0l?
  78. if(!cep->ce_varname || !cep->ce_vardata) {
  79. config_error("%s:%i: blank %s item", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, MYCONF); // Rep0t error
  80. errors++; // Increment err0r count fam
  81. continue; // Next iteration imo tbh
  82. }
  83. if(!strcmp(cep->ce_varname, "capslimit")) {
  84. limit = atoi(cep->ce_vardata);
  85. if(limit <= 0 || limit > 100) {
  86. config_error("%s:%i: %s::capslimit must be an integer from 1 thru 99 (represents a percentage)", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, MYCONF);
  87. errors++;
  88. }
  89. continue;
  90. }
  91. if(!strcmp(cep->ce_varname, "minlength")) {
  92. for(i = 0; cep->ce_vardata[i]; i++) {
  93. if(!isdigit(cep->ce_vardata[i])) {
  94. config_error("%s:%i: %s::minlength must be an integer of 0 or larger m8", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, MYCONF);
  95. errors++; // Increment err0r count fam
  96. break;
  97. }
  98. }
  99. continue;
  100. }
  101. if(!strcmp(cep->ce_varname, "lowercase_it")) {
  102. if(!cep->ce_vardata || (strcmp(cep->ce_vardata, "0") && strcmp(cep->ce_vardata, "1"))) {
  103. config_error("%s:%i: %s::lowercase_it must be either 0 or 1 fam", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, MYCONF);
  104. errors++; // Increment err0r count fam
  105. }
  106. continue;
  107. }
  108. }
  109. *errs = errors;
  110. // Returning 1 means "all good", -1 means we shat our panties
  111. return errors ? -1 : 1;
  112. }
  113. // "Run" the config (everything should be valid at this point)
  114. int anticaps_configrun(ConfigFile *cf, ConfigEntry *ce, int type) {
  115. ConfigEntry *cep; // To store the current variable/value pair etc, nested
  116. // Since we'll add a top-level block to unrealircd.conf, need to filter on CONFIG_MAIN lmao
  117. if(type != CONFIG_MAIN)
  118. return 0; // Returning 0 means idgaf bout dis
  119. // Check for valid config entries first
  120. if(!ce || !ce->ce_varname)
  121. return 0;
  122. // If it isn't anticaps, idc
  123. if(strcmp(ce->ce_varname, MYCONF))
  124. return 0;
  125. for(cep = ce->ce_entries; cep; cep = cep->ce_next) {
  126. // Do we even have a valid name l0l?
  127. if(!cep->ce_varname || !cep->ce_vardata)
  128. continue; // Next iteration imo tbh
  129. if(!strcmp(cep->ce_varname, "capslimit")) {
  130. capsLimit = atoi(cep->ce_vardata);
  131. continue;
  132. }
  133. if(!strcmp(cep->ce_varname, "minlength")) {
  134. minLength = atoi(cep->ce_vardata);
  135. continue;
  136. }
  137. if(!strcmp(cep->ce_varname, "lowercase_it")) {
  138. lcIt = atoi(cep->ce_vardata);
  139. continue;
  140. }
  141. }
  142. return 1; // We good
  143. }
  144. int anticaps_rehash(void) {
  145. // Reset config defaults
  146. capsLimit = 50;
  147. minLength = 30;
  148. lcIt = 0;
  149. return HOOK_CONTINUE;
  150. }
  151. // Now for the actual override
  152. static int anticaps_override(Cmdoverride *ovr, aClient *cptr, aClient *sptr, int parc, char *parv[]) {
  153. /* Gets args: Cmdoverride *ovr, aClient *cptr, aClient *sptr, int parc, char *parv[]
  154. **
  155. ** ovr: Pointer to the override we're attached to
  156. ** cptr: Pointer to directly attached client -- if remote user this is the remote server instead
  157. ** sptr: Pointer to user executing command
  158. ** parc: Amount of arguments (also includes the command in the count)
  159. ** parv: Contains the actual args, first one starts at parv[1]
  160. **
  161. ** So "PRIVMSG test" would result in parc = 2 and parv[1] = "test"
  162. ** Also, parv[0] seems to always be NULL, so better not rely on it fam
  163. */
  164. char plaintext[BUFSIZE]; // Let's not modify parv[2] directly =]
  165. char *tmpp; // We gonna fix up da string fam
  166. int perc; // Store percentage etc
  167. int i, len, rlen, caps; // To count full length as well as caps
  168. if(BadPtr(parv[1]) || BadPtr(parv[2]) || !sptr || IsULine(sptr) || IsServer(sptr) || IsMe(sptr) || IsOper(sptr) || strlen(parv[2]) < minLength)
  169. return CallCmdoverride(ovr, cptr, sptr, parc, parv); // Run original function yo
  170. // Some shitty ass scripts may use different colours/markup across chans, so fuck that
  171. if(!(tmpp = (char *)StripControlCodes(parv[2])))
  172. return CallCmdoverride(ovr, cptr, sptr, parc, parv);
  173. memset(plaintext, '\0', sizeof(plaintext));
  174. strlcpy(plaintext, tmpp, sizeof(plaintext));
  175. perc = len = rlen = caps = 0;
  176. for(i = 0; plaintext[i]; i++, rlen++) {
  177. if(plaintext[i] == 32) // Let's skip spaces too
  178. continue;
  179. if(plaintext[i] >= 65 && plaintext[i] <= 90) // Premium ASCII yo
  180. caps++;
  181. len++;
  182. }
  183. if(!caps || !len) // Inb4division by zero lmao
  184. return CallCmdoverride(ovr, cptr, sptr, parc, parv); // Run original function yo
  185. i = 0;
  186. if(*plaintext == '\001') { // Might be an ACTION or CTCP
  187. if(rlen > 7 && !strncmp(&plaintext[1], "ACTION ", 7) && plaintext[rlen - 1] == '\001') {
  188. caps -= 6; // Reduce caps count by 6 chars y0
  189. len -= 8; // Also reduce the total length (including both \001's) so we're still good percent-wise
  190. i = 8; // Let's not lowercase the ACTION bit later ;];];]];]
  191. }
  192. else if(plaintext[rlen - 1] == '\001') // Not an ACTION so maybe a CTCP, ignore it all if so
  193. caps = 0;
  194. if(caps <= 0 || len <= 0) // Correction may have reduced it to zero (never really _below_ zero but let's just anyways lel)
  195. return CallCmdoverride(ovr, cptr, sptr, parc, parv);
  196. }
  197. perc = (int)(((float)caps / (float)len) * 100);
  198. if(perc >= capsLimit) {
  199. if(!lcIt) { // If not configured to lowercase em, deny/drop the message
  200. sendnotice(sptr, "*** Cannot send to %s: your message contains too many capital letters (%d%% >= %d%%)", parv[1], perc, capsLimit);
  201. return 0; // Stop processing yo
  202. }
  203. // Lowercase it all lol
  204. for(; plaintext[i]; i++) {
  205. if(plaintext[i] < 65 || plaintext[i] > 122) // Premium ASCII yo, lazy mode
  206. continue;
  207. plaintext[i] = tolower(plaintext[i]);
  208. }
  209. parv[2] = plaintext;
  210. }
  211. return CallCmdoverride(ovr, cptr, sptr, parc, parv); // Run original function yo
  212. }