m_timedbans.c 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  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. #define TIMEDBANS_EXTCHAR 't' // Extended ban character (e.g. +b ~t: etc)
  9. #define BACKPORT (UNREAL_VERSION_GENERATION == 4 && UNREAL_VERSION_MAJOR == 0 && UNREAL_VERSION_MINOR <= 12)
  10. // Quality fowod declarations
  11. #if BACKPORT
  12. extern char *extban_conv_param_nuh(char *para); // Trivial bug where this function isn't marked extern in include/h.h (bug ID: 4975)
  13. #endif
  14. Ban *find_timedban(aChannel *chptr, char *mask);
  15. int timedbans_extban_isok(aClient *sptr, aChannel *chptr, char *param, int checkt, int what, int what2);
  16. int timedbans_isbanned(aClient *sptr, aChannel *chptr, char *ban, int chktype);
  17. EVENT(timedbans_event); // For expiring that shit fam
  18. void expirem(int count, char *modes[], aChannel *chptr);
  19. // Muh globals
  20. static ModuleInfo *timedbansMI = NULL; // Store ModuleInfo so we can use it to check for errors in MOD_LOAD
  21. Extban *timedExtban;
  22. // Dat dere module header
  23. ModuleHeader MOD_HEADER(m_timedbans) = {
  24. "m_timedbans", // Module name
  25. "$Id: v1.02 2017/11/26 Gottem$", // Version
  26. "Adds extban ~t for auto-expiring bans", // Description
  27. "3.2-b8-1", // Modversion, not sure wat do
  28. NULL
  29. };
  30. // Initialisation routine (register hooks, commands and modes or create structs etc)
  31. MOD_INIT(m_timedbans) {
  32. timedbansMI = modinfo;
  33. // Request that extban y0
  34. ExtbanInfo req;
  35. memset(&req, 0, sizeof(ExtbanInfo));
  36. req.flag = TIMEDBANS_EXTCHAR; // Required ;]
  37. req.is_ok = timedbans_extban_isok; // Optional, but we need to check the expiration field more closely ;];]
  38. req.conv_param = extban_conv_param_nuh; // Required, simply use built-in function to turn ~t:60:ham into ~t:60:ham!*@* etc ;];];]
  39. req.is_banned = timedbans_isbanned; // Required, checks if user matches een bannerino ;];];];]
  40. if(!(timedExtban = ExtbanAdd(modinfo->handle, req))) { // Error while adding (flag exists?)
  41. config_error("[%s] Adding extban ~%c failed", MOD_HEADER(m_timedbans).name, TIMEDBANS_EXTCHAR);
  42. return MOD_FAILED;
  43. }
  44. return MOD_SUCCESS; // Let MOD_LOAD handle errors
  45. }
  46. // Actually load the module here (also command overrides as they may not exist in MOD_INIT yet)
  47. MOD_LOAD(m_timedbans) {
  48. EventAddEx(timedbansMI->handle, "timedbans_event", 15, 0, timedbans_event, NULL); // Run event every 15 seconds, indefinitely and without any additional data (void *NULL etc)
  49. // Did the module throw an error during initialisation?
  50. if(ModuleGetError(timedbansMI->handle) != MODERR_NOERROR) {
  51. // Display error string kek
  52. config_error("A critical error occurred when loading module %s: %s", MOD_HEADER(m_timedbans).name, ModuleGetErrorStr(timedbansMI->handle));
  53. return MOD_FAILED; // No good
  54. }
  55. return MOD_SUCCESS; // We good
  56. }
  57. // Called on unload/rehash obv
  58. MOD_UNLOAD(m_timedbans) {
  59. return MOD_SUCCESS; // We good
  60. }
  61. Ban *find_timedban(aChannel *chptr, char *mask) {
  62. Ban *ban; // Pointer to ban entry lol
  63. char *bmask; //
  64. for(ban = chptr->banlist; ban; ban=ban->next) {
  65. if((ban->banstr[0] == '~') && (ban->banstr[1] == TIMEDBANS_EXTCHAR) && (ban->banstr[2] == ':')) { // Must start with ~t: etc ;];]
  66. if(strlen(ban->banstr) < 5) // Sanity check ("~t:" plus ":" and at least one char in expiration (so minimum of 5 lol)
  67. continue;
  68. bmask = strchr(ban->banstr + 3, ':') + 1;
  69. if(bmask && !stricmp(bmask, mask)) // Compare stored mask to new one (w/o taking into account the expiration) ;]
  70. return ban;
  71. }
  72. }
  73. return NULL;
  74. }
  75. // Validate param =]
  76. int timedbans_extban_isok(aClient *sptr, aChannel *chptr, char *param, int checkt, int what, int what2) {
  77. char *mask, *ep; // Pointers to store the mask bit and expiration field
  78. long expiry = 0; // Expiration as a long for checkin' em durations =]
  79. Ban *ban; // Ban entry y0
  80. if((what == MODE_ADD) && (what2 != EXBTYPE_BAN) && MyClient(sptr)) // Only works w/ +b ;];]
  81. return EX_DENY;
  82. if((what == MODE_ADD) && (checkt == EXBCHK_PARAM) && MyClient(sptr)) { // Check parameter sanity bruh
  83. if(match("~?:*:*!*@*", param) || strchr(param, ' ') != NULL) { // Cannot contain spaces imo tbh famlamla (should never happen but let's checkem anywaysz)
  84. sendto_one(sptr, ":%s NOTICE %s :[timedbans] Ban format is ~%c:\037expiration\037:\002banmask\002 (example: \002~%c:5m:*!*@*.some.isp\002)", me.name, chptr->chname, TIMEDBANS_EXTCHAR, TIMEDBANS_EXTCHAR);
  85. return EX_DENY; // D E N I E D
  86. }
  87. char buf[BUFSIZE]; // Cuz strtok() fucks ur shit up fam
  88. snprintf(buf, sizeof(buf), "%s", param + 3); // Copy that shit, get rid of ~t: first
  89. // Second token is the expiration fielderino
  90. ep = strtok(buf, ":");
  91. if(ep) // Sanity checc
  92. expiry = config_checkval(ep, CFG_TIME); // Converts that shit to a long yo
  93. // The rest is the actual banmask =]
  94. mask = strtok(NULL, "");
  95. if(!ep || !mask) // Should be handled by the match() at the top of muh parent if, but just in case yo
  96. return EX_DENY; // N O P E
  97. if(!expiry || expiry % 60 != 0) { // So the EVENT doesn't have to check every fucking second
  98. sendto_one(sptr, ":%s NOTICE %s :[timedbans] The resolution for the expiration time is 1 minute, valid examples are: \037120\037 (2 minutes written in seconds), \0375m\037 and \0372h30m\037", me.name, chptr->chname);
  99. return EX_DENY; // R E K T
  100. }
  101. if((ban = find_timedban(chptr, mask))) { // Gottem alredy?
  102. sendto_one(sptr, ":%s NOTICE %s :[timedbans] A similar timed ban was already found: %s", me.name, chptr->chname, ban->banstr);
  103. return EX_DENY; // R I P
  104. }
  105. }
  106. return EX_ALLOW; // Ayy we good FAM
  107. }
  108. // Check if a user should be banned
  109. int timedbans_isbanned(aClient *sptr, aChannel *chptr, char *ban, int chktype) {
  110. char *nuhost, *nurhost, *nuip;
  111. char *mask; // Only need the mask bit here imo tbh
  112. int ret; // St0re return value cuz we need to free some shit =]
  113. if(strlen(ban) < 5) // Sanity check ("~t:" plus ":" and at least one char in expiration (so minimum of 5 lol)
  114. return 0;
  115. mask = strchr(ban + 3, ':') + 1;
  116. if(!mask) // r u insaiyan?
  117. return 0; // y-yes
  118. nuhost = strdup(make_nick_user_host(sptr->name, sptr->user->username, GetHost(sptr)));
  119. nurhost = strdup(make_nick_user_host(sptr->name, sptr->user->username, sptr->user->realhost));
  120. nuip = strdup(make_nick_user_host(sptr->name, sptr->user->username, GetIP(sptr)));
  121. ret = 0; // 0 means we g00d
  122. if(!match(mask, nuhost) || !match(mask, nurhost) || !match(mask, nuip)) // Checkem
  123. ret = 1; // b& and v&
  124. free(nuhost);
  125. free(nurhost);
  126. free(nuip);
  127. return ret;
  128. }
  129. EVENT(timedbans_event) {
  130. // Gotta check all channels for ~t: extbans fam
  131. int bcount = 0; // We might do -bbbb instead of one -b for every fucking mask ;]
  132. size_t blen = 0;
  133. char *multi[8] = { 0 }; // Buffer 4 dat ^
  134. char *ep; // To grab expiration from the banstr
  135. long expiry = 0; // Need it as a long tho ;]
  136. TS tiem = TStime(); // Get current time
  137. aChannel *chptr; // Channel iter8or m8
  138. Ban *ban, *nban; // Ban iter8ors m8
  139. for(chptr = channel; chptr; chptr = chptr->nextch) { // Loop over all channels...
  140. for(ban = chptr->banlist; ban; ban = nban) { // ...and their banlists
  141. nban = ban->next; // So our loop doesn't go ripperoni
  142. if((ban->banstr[0] == '~') && (ban->banstr[1] == TIMEDBANS_EXTCHAR) && (ban->banstr[2] == ':')) { // Must start with ~t: etc ;];]
  143. size_t elen = strlen(ban->banstr + 3) - strlen(strchr(ban->banstr + 3, ':')); // Only interested in the expiration field hur ;]
  144. if(!elen) // Sanity cheqq lol
  145. continue; // Gtfo
  146. ep = malloc(elen + 1); // + nullbyet ofc
  147. memcpy(ep, &ban->banstr[3], elen); // Copy that shit =]
  148. ep[elen] = '\0'; // Ayy
  149. if(strlen(ep)) // Sanity checc
  150. expiry = config_checkval(ep, CFG_TIME); // Converts that shit to a long yo
  151. free(ep);
  152. if(tiem >= ban->when + expiry) { // Should expire =]]
  153. bcount++;
  154. blen += strlen(ban->banstr);
  155. multi[bcount - 1] = ban->banstr;
  156. }
  157. if(bcount >= 8 || blen >= 200) { // Some masks may be lengthy yo, so let's keep it to 8 per line or 200 chars max =]
  158. expirem(bcount, multi, chptr); // Actually expire them
  159. blen = bcount = 0; // Reset counters imo
  160. }
  161. }
  162. }
  163. if(bcount && multi[0]) { // The loop may not have hit the per-line limit, so "flush" that shit here lol
  164. expirem(bcount, multi, chptr);
  165. blen = bcount = 0;
  166. }
  167. }
  168. }
  169. void expirem(int count, char *modes[], aChannel *chptr) {
  170. char flags[MAXMODEPARAMS + 1]; // Includes nullbyet lel
  171. int i; // Iter8or lol
  172. int parc = 3 + count; // 3 "base" params plus the dynamic counter = total params =]
  173. char *parv[MAXPARA + 1]; // Also dat array fam (need to end w/ NULL etc, so + 1)
  174. flags[0] = '-'; // Flags shud begin with '-' obv
  175. for(i = 1; i <= count; i++) { // Now use the counter to loop that shit
  176. flags[i] = 'b';
  177. if(modes[i - 1])
  178. parv[2 + i] = modes[i - 1]; // e.g. i = 1, so parv[3] = modes[0]
  179. }
  180. flags[i] = '\0'; // May be wise to termin8 em yo
  181. parv[0] = NULL; // First "base" param is always NULL
  182. parv[1] = chptr->chname; // Followed by chanul
  183. parv[2] = flags; // And flags
  184. parv[parc] = NULL; // Also finish w/ NULL
  185. do_mode(chptr, &me, &me, parc - 2, parv + 2, TStime(), 0); // Call internal command so it clears that shit from memory too (also refresh timestamp here) ;];]
  186. for(i = 0; i < count; i++) // Clearasil fam
  187. modes[i] = NULL;
  188. }