mshun.c 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607
  1. /* Copyright (C) All Rights Reserved
  2. ** Written by Gottem <support@gottem.nl>
  3. ** Website: https://gottem.nl/unreal
  4. ** License: https://gottem.nl/unreal/license
  5. */
  6. /*** <<<MODULE MANAGER START>>>
  7. module {
  8. documentation "https://gottem.nl/unreal/man/mshun";
  9. troubleshooting "In case of problems, check the FAQ at https://gottem.nl/unreal/halp or e-mail me at support@gottem.nl";
  10. min-unrealircd-version "5.*";
  11. //max-unrealircd-version "5.*";
  12. post-install-text {
  13. "The module is installed, now all you need to do is add a 'loadmodule' line to your config file:";
  14. "loadmodule \"third/mshun\";";
  15. "Then /rehash the IRCd.";
  16. "For usage information, refer to the module's documentation found at: https://gottem.nl/unreal/man/mshun";
  17. }
  18. }
  19. *** <<<MODULE MANAGER END>>>
  20. */
  21. // One include for all cross-platform compatibility thangs
  22. #include "unrealircd.h"
  23. // Since v5.0.5 some hooks now include a SendType
  24. #define BACKPORT_HOOK_SENDTYPE (UNREAL_VERSION_GENERATION == 5 && UNREAL_VERSION_MAJOR == 0 && UNREAL_VERSION_MINOR < 5)
  25. // Command strings
  26. #define MSG_MSHUN "MSHUN"
  27. #define MSG_MSHUN_ALT "MLINE"
  28. // Big hecks go here
  29. typedef struct t_mshun MShun;
  30. struct t_mshun {
  31. char *mask;
  32. time_t set;
  33. time_t expire;
  34. char *raisin;
  35. char *setby;
  36. MShun *next;
  37. };
  38. // Dem macros yo
  39. CMD_FUNC(mshun); // Register command function
  40. #define CheckAPIError(apistr, apiobj) \
  41. do { \
  42. if(!(apiobj)) { \
  43. config_error("A critical error occurred on %s for %s: %s", (apistr), MOD_HEADER.name, ModuleGetErrorStr(modinfo->handle)); \
  44. return MOD_FAILED; \
  45. } \
  46. } while(0)
  47. // Quality fowod declarations
  48. static void dumpit(Client *client, char **p);
  49. EVENT(mshun_event); // For expiring that shit fam
  50. void mshun_moddata_free(ModData *md);
  51. void check_mlines(void);
  52. void add_mline(MShun *newms);
  53. void del_mline(MShun *muhms);
  54. void free_all_mlines(MShun *MShunList);
  55. MShun *get_mlines(void);
  56. MShun *find_mline(char *mask);
  57. MShun *match_mline(Client *client);
  58. int mshun_hook_serverconnect(Client *client);
  59. int _check_premsg_isshunned(Client *client, int from_cansend, char **text, char **errmsg);
  60. int mshun_hook_preknock(Client *client, Channel *channel);
  61. int mshun_hook_preinvite(Client *client, Client *target, Channel *channel, int *override);
  62. #if BACKPORT_HOOK_SENDTYPE
  63. int mshun_hook_cansend_chan(Client *client, Channel *channel, Membership *lp, char **text, char **errmsg, int notice);
  64. int mshun_hook_cansend_user(Client *client, Client *to, char **text, char **errmsg, int notice);
  65. #else
  66. int mshun_hook_cansend_chan(Client *client, Channel *channel, Membership *lp, char **text, char **errmsg, SendType sendtype);
  67. int mshun_hook_cansend_user(Client *client, Client *to, char **text, char **errmsg, SendType sendtype);
  68. #endif
  69. // Muh globals
  70. ModDataInfo *mshunMDI; // To store the M-Lines with &me lol (hack so we don't have to use a .db file or some shit)
  71. int mshunCount; // A counter for M-Lines so we can change the moddata back to NULL
  72. // Help string in case someone does just /MSHUN
  73. static char *muhhalp[] = {
  74. /* Special characters:
  75. ** \002 = bold -- \x02
  76. ** \037 = underlined -- \x1F
  77. */
  78. "*** \002Help on /MSHUN\002 ***",
  79. "Enables opers to set shuns that only affect \002PRIVMSG\002/\002NOTICE\002/\002KNOCK\002/\002INVITE\002.",
  80. "This means affected users can still do nick changes and join/part channels etc,",
  81. "but Unreal normally has flood control in place to prevent them from spamming",
  82. "that shit. It requires an \037ident@host\037 mask (as is usual with *-Lines).",
  83. "The wildcards * and ? are also supported.",
  84. " ",
  85. "Syntax:",
  86. " \002/MSHUN\002 [-]\037mask\037 [\037expiration\037] \037reason\037",
  87. " ",
  88. "Examples:",
  89. " \002/mshun guest*@* 0 nope\002",
  90. " \002/mline -guest*@*\002",
  91. " Adds/deletes the same M-Line, with no expiration",
  92. " \002/mshun guest*@* 3600 ain't gonna happen\002",
  93. " \002/mline guest*@* 1h ain't gonna happen\002",
  94. " Add an M-Line that expires in an hour",
  95. " \002/mshun fegget 1h kek\002",
  96. " Add an M-Line for the user \037fegget\037 (resolves their \002id@ho\002 mask if they're online)",
  97. " \002/mshun\002",
  98. " Show all M-Lines",
  99. NULL
  100. };
  101. // Dat dere module header
  102. ModuleHeader MOD_HEADER = {
  103. "third/mshun", // Module name
  104. "2.0.1", // Version
  105. "Implements an M-Line for special shuns", // Description
  106. "Gottem", // Author
  107. "unrealircd-5", // Modversion
  108. };
  109. // Initialisation routine (register hooks, commands and modes or create structs etc)
  110. MOD_INIT() {
  111. MShun *MShunList, *msEntry; // To initialise the mshunCount counter imo tbh fam
  112. // Dem commands fam
  113. CheckAPIError("CommandAdd(MSHUN)", CommandAdd(modinfo->handle, MSG_MSHUN, mshun, MAXPARA, CMD_SERVER | CMD_USER));
  114. CheckAPIError("CommandAdd(MLINE)", CommandAdd(modinfo->handle, MSG_MSHUN_ALT, mshun, MAXPARA, CMD_SERVER | CMD_USER));
  115. // Run event every 10 seconds, indefinitely and without any additional data (void *NULL etc)
  116. CheckAPIError("EventAdd(mshun_event)", EventAdd(modinfo->handle, "mshun_event", mshun_event, NULL, 10000, 0));
  117. mshunCount = 0; // Start with 0 obv lmao
  118. if(!(mshunMDI = findmoddata_byname("mshun_list", MODDATATYPE_LOCAL_VARIABLE))) { // Attempt to find active moddata (like in case of a rehash)
  119. ModDataInfo mreq; // No moddata, let's request that shit
  120. memset(&mreq, 0, sizeof(mreq)); // Set 'em lol
  121. mreq.type = MODDATATYPE_LOCAL_VARIABLE;
  122. mreq.name = "mshun_list"; // Name it
  123. mreq.free = mshun_moddata_free; // Function to free 'em
  124. mreq.serialize = NULL;
  125. mreq.unserialize = NULL;
  126. mreq.sync = 0;
  127. mshunMDI = ModDataAdd(modinfo->handle, mreq); // Add 'em yo
  128. CheckAPIError("ModDataAdd(mshun_list)", mshunMDI);
  129. }
  130. else { // We did get moddata
  131. if((MShunList = get_mlines())) { // So load 'em
  132. for(msEntry = MShunList; msEntry; msEntry = msEntry->next) // and iter8 m8
  133. mshunCount++; // Ayyy premium countur
  134. }
  135. }
  136. MARK_AS_GLOBAL_MODULE(modinfo);
  137. // Add muh hooks with (mostly) high prio lol
  138. HookAdd(modinfo->handle, HOOKTYPE_SERVER_CONNECT, 0, mshun_hook_serverconnect);
  139. HookAdd(modinfo->handle, HOOKTYPE_CAN_SEND_TO_CHANNEL, -100, mshun_hook_cansend_chan);
  140. HookAdd(modinfo->handle, HOOKTYPE_CAN_SEND_TO_USER, -100, mshun_hook_cansend_user);
  141. HookAdd(modinfo->handle, HOOKTYPE_PRE_KNOCK, -100, mshun_hook_preknock);
  142. HookAdd(modinfo->handle, HOOKTYPE_PRE_INVITE, -100, mshun_hook_preinvite);
  143. return MOD_SUCCESS;
  144. }
  145. // Actually load the module here (also command overrides as they may not exist in MOD_INIT yet)
  146. MOD_LOAD() {
  147. return MOD_SUCCESS; // We good
  148. }
  149. // Called on unload/rehash obv
  150. MOD_UNLOAD() {
  151. // Not clearing the moddata structs here so we can re-use them easily ;];]
  152. return MOD_SUCCESS; // We good
  153. }
  154. // Dump a NULL-terminated array of strings to the user (taken from DarkFire IRCd)
  155. static void dumpit(Client *client, char **p) {
  156. if(IsServer(client)) // Bail out early and silently if it's a server =]
  157. return;
  158. // Using sendto_one() instead of sendnumericfmt() because the latter strips indentation and stuff ;]
  159. for(; *p != NULL; p++)
  160. sendto_one(client, NULL, ":%s %03d %s :%s", me.name, RPL_TEXT, client->name, *p);
  161. // Let user take 8 seconds to read it
  162. client->local->since += 8;
  163. }
  164. EVENT(mshun_event) {
  165. check_mlines(); // Checkem and expirem
  166. }
  167. // Probably never called but it's a required function
  168. // The free shit here normally only happens when the client attached to the moddata quits (afaik), but that's us =]
  169. void mshun_moddata_free(ModData *md) {
  170. if(md->ptr) { // r u insaiyan?
  171. free_all_mlines(md->ptr);
  172. md->ptr = NULL; // Prolly a good idea too =]
  173. }
  174. }
  175. // Check for expiring M-Lines
  176. void check_mlines(void) {
  177. MShun *MShunList, *head, *last, **msEntry;
  178. char gmt[128]; // For a pretty timestamp instead of UNIX time lol
  179. if(!(MShunList = get_mlines())) // Ayyy no M-Lines known
  180. return;
  181. msEntry = &MShunList; // Hecks so the ->next chain stays intact
  182. head = MShunList;
  183. while(*msEntry) { // Loop while we have entries obv
  184. if((*msEntry)->expire > 0 && TStime() > ((*msEntry)->set + (*msEntry)->expire)) { // Do we need to expire it?
  185. last = *msEntry; // Get the entry pointur
  186. *msEntry = last->next; // Set the iterat0r to the next one
  187. if(last == head) { // If it's the first entry, need to take special precautions ;]
  188. moddata_local_variable(mshunMDI).ptr = *msEntry; // Cuz shit rips if we don't do dis
  189. head = *msEntry; // Move head up
  190. }
  191. // Get pretty timestamp =]
  192. *gmt = '\0';
  193. short_date(last->set, gmt);
  194. // Send expiration notice to all _local_ opers lol (every server checks expirations itself newaysz y0)
  195. sendto_snomask(SNO_TKL, "*** Expiring M-Line set by %s (set at %s GMT) for %s [reason: %s]", last->setby, gmt, last->mask, last->raisin);
  196. safe_free(last->mask); // Gotta
  197. safe_free(last->raisin); // free
  198. safe_free(last->setby); // 'em
  199. safe_free(last); // all
  200. mshunCount--;
  201. }
  202. else {
  203. msEntry = &(*msEntry)->next; // No need for expiration, go to the next one
  204. }
  205. }
  206. if(mshunCount <= 0) // Cuz shit rips if we don't do dis
  207. moddata_local_variable(mshunMDI).ptr = NULL;
  208. }
  209. // Add new M-Line obv fam
  210. void add_mline(MShun *newms) {
  211. MShun *MShunList, *msEntry; // Head + iter8or imo tbh
  212. mshunCount++; // Always increment count
  213. if(!(MShunList = get_mlines())) { // If MShunList is NULL...
  214. MShunList = newms; // ...simply have it point to the newly alloc8ed entry
  215. moddata_local_variable(mshunMDI).ptr = MShunList; // And st0re em
  216. return;
  217. }
  218. for(msEntry = MShunList; msEntry && msEntry->next; msEntry = msEntry->next) { } // Get teh last entray
  219. msEntry->next = newms; // Append lol
  220. }
  221. // Delete em fam
  222. void del_mline(MShun *muhms) {
  223. MShun *MShunList, *last, **msEntry;
  224. if(!(MShunList = get_mlines())) // Ayyy no M-Lines known
  225. return;
  226. msEntry = &MShunList; // Hecks so the ->next chain stays intact
  227. if(*msEntry == muhms) { // If it's the first entry, need to take special precautions ;]
  228. last = *msEntry; // Get the entry pointur
  229. *msEntry = last->next; // Set the iterat0r to the next one
  230. safe_free(last->mask); // Gotta
  231. safe_free(last->raisin); // free
  232. safe_free(last->setby); // 'em
  233. safe_free(last); // all
  234. moddata_local_variable(mshunMDI).ptr = *msEntry; // Cuz shit rips if we don't do dis
  235. mshunCount--;
  236. return;
  237. }
  238. while(*msEntry) { // Loop while we have entries obv
  239. if(*msEntry == muhms) { // Do we need to delete em?
  240. last = *msEntry; // Get the entry pointur
  241. *msEntry = last->next; // Set the iterat0r to the next one
  242. safe_free(last->mask); // Gotta
  243. safe_free(last->raisin); // free
  244. safe_free(last->setby); // 'em
  245. safe_free(last); // all
  246. mshunCount--;
  247. break;
  248. }
  249. else {
  250. msEntry = &(*msEntry)->next; // No need, go to the next one
  251. }
  252. }
  253. if(mshunCount <= 0) // Cuz shit rips if we don't do dis
  254. moddata_local_variable(mshunMDI).ptr = NULL;
  255. }
  256. void free_all_mlines(MShun *MShunList) {
  257. MShun *last, **msEntry;
  258. if(!MShunList)
  259. return;
  260. msEntry = &MShunList; // Hecks so the ->next chain stays intact
  261. while(*msEntry) { // Loop while we have entries obv
  262. last = *msEntry; // Get the entry pointur
  263. *msEntry = last->next; // Set the iterat0r to the next one
  264. safe_free(last->mask); // Gotta
  265. safe_free(last->raisin); // free
  266. safe_free(last->setby); // 'em
  267. safe_free(last); // all
  268. mshunCount--;
  269. }
  270. }
  271. // Get (head of) the M-Line list
  272. MShun *get_mlines(void) {
  273. MShun *MShunList = moddata_local_variable(mshunMDI).ptr; // Get mod data
  274. // Sanity check lol
  275. if(MShunList && MShunList->mask)
  276. return MShunList;
  277. return NULL;
  278. }
  279. // Find a specific M-Line by mask
  280. MShun *find_mline(char *mask) {
  281. MShun *MShunList, *msEntry; // Head and iter8or fam
  282. if((MShunList = get_mlines())) { // Check if the list even has entries kek
  283. for(msEntry = MShunList; msEntry; msEntry = msEntry->next) { // Iter8 em
  284. if(match_simple(msEntry->mask, mask)) // Checkemmmm
  285. return msEntry;
  286. }
  287. }
  288. return NULL; // Not found m8
  289. }
  290. // For matching a user to an M-Line
  291. MShun *match_mline(Client *client) {
  292. char *mask = make_user_host(client->user->username, client->user->realhost); // Get user@host with the real hostnaem
  293. if(!mask) // r u insaiyan lol?
  294. return NULL;
  295. return find_mline(mask); // Returns NULL if n0, gg ez
  296. }
  297. // Internal function called by the CAN_SEND_TO_CHANNEL/USER hooks ;];]
  298. int _check_premsg_isshunned(Client *client, int from_cansend, char **text, char **errmsg) {
  299. if(MyUser(client) && !IsServer(client) && !IsMe(client) && !IsULine(client) && !IsOper(client) && match_mline(client)) { // Servers, U-Lines and opers are exempt for obv raisins
  300. // This is a CAN_SEND type thing, so set text to NULL to drop em ;];];];]
  301. if(from_cansend) {
  302. *text = NULL;
  303. return HOOK_CONTINUE; // Can't return HOOK_DENY here cuz Unreal will abort() in that case :D
  304. }
  305. return HOOK_DENY;
  306. }
  307. return HOOK_CONTINUE;
  308. }
  309. // Server connect hewk familia
  310. int mshun_hook_serverconnect(Client *client) {
  311. // Sync M-Lines fam
  312. MShun *MShunList, *msEntry; // Head and iter8or ;];]
  313. if((MShunList = get_mlines())) { // Gettem list
  314. for(msEntry = MShunList; msEntry; msEntry = msEntry->next) {
  315. if(!msEntry || !msEntry->mask) // Sanity check imo ;]
  316. continue;
  317. // Syntax for servers is a bit different (namely the setby arg and the : before reason (makes the entire string after be considered one arg ;];])
  318. sendto_one(client, NULL, ":%s %s ADD %s %ld %ld %s :%s", me.id, MSG_MSHUN, msEntry->mask, msEntry->set, msEntry->expire, msEntry->setby, msEntry->raisin);
  319. }
  320. }
  321. return HOOK_CONTINUE;
  322. }
  323. // Pre message hewks lol
  324. #if BACKPORT_HOOK_SENDTYPE
  325. int mshun_hook_cansend_chan(Client *client, Channel *channel, Membership *lp, char **text, char **errmsg, int notice) {
  326. return _check_premsg_isshunned(client, 1, text, errmsg);
  327. }
  328. int mshun_hook_cansend_user(Client *client, Client *to, char **text, char **errmsg, int notice) {
  329. if(IsULine(to)) // Allow sending to U-Lines (NickServ etc famiglia) ;]
  330. return HOOK_CONTINUE;
  331. return _check_premsg_isshunned(client, 1, text, errmsg);
  332. }
  333. #else
  334. int mshun_hook_cansend_chan(Client *client, Channel *channel, Membership *lp, char **text, char **errmsg, SendType sendtype) {
  335. if(sendtype != SEND_TYPE_PRIVMSG && sendtype != SEND_TYPE_NOTICE)
  336. return HOOK_CONTINUE;
  337. return _check_premsg_isshunned(client, 1, text, errmsg);
  338. }
  339. int mshun_hook_cansend_user(Client *client, Client *to, char **text, char **errmsg, SendType sendtype) {
  340. if(sendtype != SEND_TYPE_PRIVMSG && sendtype != SEND_TYPE_NOTICE)
  341. return HOOK_CONTINUE;
  342. if(IsULine(to)) // Allow sending to U-Lines (NickServ etc famiglia) ;]
  343. return HOOK_CONTINUE;
  344. return _check_premsg_isshunned(client, 1, text, errmsg);
  345. }
  346. #endif
  347. // Also /knock
  348. int mshun_hook_preknock(Client *client, Channel *channel) {
  349. return _check_premsg_isshunned(client, 0, NULL, NULL);
  350. }
  351. // And /invite lol
  352. int mshun_hook_preinvite(Client *client, Client *target, Channel *channel, int *override) {
  353. return _check_premsg_isshunned(client, 0, NULL, NULL);
  354. }
  355. // Function for /MSHUN etc
  356. CMD_FUNC(mshun) {
  357. // Gets args: Client *client, MessageTag *recv_mtags, int parc, char *parv[]
  358. MShun *MShunList, *newms, *msEntry; // Quality struct pointers
  359. char *mask, *exptmp, *setby; // Muh args
  360. char raisin[BUFSIZE]; // Reasons may or may not be pretty long
  361. char gmt[128], gmt2[128]; // For a pretty timestamp instead of UNIX time lol
  362. char cur, prev, prev2; // For checking time strings
  363. long setat, expire; // After how many seconds the M-Line should expire
  364. int i, adoffset, rindex, del; // Iterat0rs, indices and "booleans" =]
  365. Client *acptr; // To check if the mask is a nick instead =]
  366. // Gotta be at least a server, U-Line or oper with correct privs lol
  367. if((!IsServer(client) && !IsULine(client) && !IsOper(client)) || !ValidatePermissionsForPath("mshun", client, NULL, NULL, NULL)) {
  368. sendnumeric(client, ERR_NOPRIVILEGES); // Check ur privilege fam
  369. return; // Ain't gonna happen lol
  370. }
  371. if(IsServer(client) && parc < 7)
  372. return;
  373. // If no args given (or we got /mshun list)
  374. if(BadPtr(parv[1]) || !strcasecmp(parv[1], "list")) {
  375. if(IsServer(client)) // No need to list shit for servers =]
  376. return;
  377. if(!(MShunList = get_mlines())) // Attempt to get list
  378. sendnotice(client, "*** No M-Lines found");
  379. else {
  380. for(msEntry = MShunList; msEntry; msEntry = msEntry->next) {
  381. *gmt2 = '\0';
  382. short_date(msEntry->set, gmt2);
  383. if(msEntry->expire == 0) // Let's show "permanent" for permanent M-Lines, n0? =]
  384. sendnotice(client, "*** Permanent M-Line by %s (set at %s GMT) for %s [reason: %s]", msEntry->setby, gmt2, msEntry->mask, msEntry->raisin);
  385. else {
  386. // Get pretty timestamp for expiring lines =]
  387. *gmt = '\0';
  388. short_date(msEntry->set + msEntry->expire, gmt);
  389. sendnotice(client, "*** M-Line by %s (set at %s GMT) for %s, expiring at %s GMT [reason: %s]", msEntry->setby, gmt2, msEntry->mask, gmt, msEntry->raisin);
  390. }
  391. }
  392. }
  393. return;
  394. }
  395. if(!strcasecmp(parv[1], "help") || !strcasecmp(parv[1], "halp")) { // Or first arg is halp
  396. dumpit(client, muhhalp); // Return help string instead
  397. return;
  398. }
  399. // Need to offset parv to the left if we got a shorthand like /mshun -dick@butt
  400. adoffset = 0;
  401. del = 0;
  402. if(IsUser(client)) { // Regular clients always use a shorter form =]
  403. mask = parv[1]; // First arg is the mask hur
  404. if(strchr("+-", *mask)) { // Check if it starts with either + or - fam
  405. del = (*mask == '-' ? 1 : 0); // We deleting shyte?
  406. mask++; // Skip past the sign lol
  407. }
  408. adoffset++; // Need to shift by one rn
  409. if((acptr = find_person(mask, NULL)))
  410. mask = make_user_host(acptr->user->username, acptr->user->realhost); // Get user@host with the real hostnaem
  411. }
  412. // Servers always use the full/long form
  413. else {
  414. if(strcasecmp(parv[1], "add") && strcasecmp(parv[1], "del")) // If first arg is neither add nor del, fuck off
  415. return;
  416. del = (!strcasecmp(parv[1], "del") ? 1 : 0); // Are we deleting?
  417. mask = parv[2];
  418. }
  419. if((!del && (parc + adoffset) < 4) || (del && (parc + adoffset) < 3)) { // Delete doesn't require the expire and reason fields
  420. sendnumeric(client, ERR_NEEDMOREPARAMS, MSG_MSHUN); // Need m0ar lol
  421. return;
  422. }
  423. // Check for the sanity of the passed mask
  424. if(match_simple("*!*@*", mask) || !match_simple("*@*", mask) || strlen(mask) < 3) {
  425. sendnotice(client, "*** The mask should be of the format ident@host");
  426. return; // Let's bail lol
  427. }
  428. // Initialise a bunch of shit
  429. memset(raisin, '\0', sizeof(raisin));
  430. exptmp = NULL;
  431. expire = DEFAULT_BANTIME;
  432. setat = TStime();
  433. rindex = 0;
  434. setby = client->name;
  435. msEntry = find_mline(mask); // Attempt to find existing M-Line
  436. // Most shit is silent for servers
  437. if(IsServer(client)) {
  438. if(!del && msEntry) // Adding an M-Line but it already exists
  439. return; // Return silently
  440. else if(del && !msEntry) // Deleting but doesn't exist
  441. return; // Return silently
  442. strlcpy(raisin, parv[6], sizeof(raisin)); // Copy the reason field imo tbh
  443. setat = atol(parv[3]); // Extra arg yo
  444. expire = atol(parv[4]); // Set expiration
  445. setby = parv[5]; // Extra arg yo
  446. if(setat <= 0) // Some error occured lol
  447. return; // Gtfo silently
  448. }
  449. // Command came from a user
  450. else {
  451. if(!del && msEntry) { // Adding an M-Line but it already exists
  452. sendnotice(client, "*** M-Line for mask %s already exists", mask);
  453. return; // Lolnope
  454. }
  455. else if(del && !msEntry) { // Deleting but doesn't exist
  456. sendnotice(client, "*** M-Line for mask %s doesn't exist", mask);
  457. return; // Lolnope
  458. }
  459. // If adding, check for expiration and reason fields
  460. if(!del) {
  461. exptmp = parv[2];
  462. // Let's check for a time string (3600, 1h, 2w3d, etc)
  463. for(i = 0; exptmp[i] != 0; i++) {
  464. cur = exptmp[i];
  465. if(!isdigit(cur)) { // No digit, check for the 'h' in '1h' etc
  466. prev = (i >= 1 ? exptmp[i - 1] : 0);
  467. prev2 = (i >= 2 ? exptmp[i - 2] : 0);
  468. if((prev && prev2 && isdigit(prev2) && prev == 'm' && cur == 'o') || (prev && isdigit(prev) && strchr("smhdw", cur))) // Check for allowed combos
  469. continue;
  470. exptmp = NULL; // Fuck off
  471. rindex = 2; // Reason index for parv[] is now 2 for normal clients
  472. break; // Only one mismatch is enough
  473. }
  474. }
  475. if(exptmp) { // If the for() loop didn't enter the inner if(), expire field is sane
  476. expire = config_checkval(exptmp, CFG_TIME); // So get a long from the (possible) time string
  477. rindex = 3; // And set reason index for parv[] to 3
  478. }
  479. if(!rindex || BadPtr(parv[rindex]) || !strlen(parv[rindex])) { // If rindex is 0 it means the arg is missing
  480. sendnotice(client, "*** The reason field is required");
  481. return; // No good fam
  482. }
  483. // Now start from rindex and copy dem remaining args
  484. for(i = rindex; parv[i] != NULL; i++) {
  485. if(i == rindex)
  486. strlcpy(raisin, parv[i], sizeof(raisin));
  487. else {
  488. strlcat(raisin, " ", sizeof(raisin));
  489. strlcat(raisin, parv[i], sizeof(raisin));
  490. }
  491. }
  492. }
  493. }
  494. // For both servers and users ;]
  495. if(!del) {
  496. // Allocate/initialise mem0ry for the new entry
  497. newms = safe_alloc(sizeof(MShun));
  498. safe_strdup(newms->mask, mask);
  499. newms->set = setat;
  500. newms->expire = expire;
  501. safe_strdup(newms->raisin, raisin);
  502. safe_strdup(newms->setby, setby);
  503. msEntry = newms;
  504. add_mline(newms); // Add em
  505. }
  506. // Propagate the M-Line to other local servers fam (excluding the direction it came from ;])
  507. sendto_server(client, 0, 0, NULL, ":%s %s %s %s %ld %ld %s :%s", me.id, MSG_MSHUN, (del ? "DEL" : "ADD"), mask, msEntry->set, msEntry->expire, setby, msEntry->raisin); // Muh raw command
  508. // Also send snomask notices to all local opers =]
  509. // Make pretty set timestamp first tho
  510. *gmt2 = '\0';
  511. short_date(msEntry->set, gmt2);
  512. // Also send snomask notices to all local opers =]
  513. if(msEntry->expire == 0) { // Permanent lol
  514. if(IsServer(client)) // Show "set at" during sync phases ;]
  515. sendto_snomask(SNO_TKL, "*** Permanent M-Line %sed by %s (set at %s GMT) for %s [reason: %s]", (del ? "delet" : "add"), setby, gmt2, mask, msEntry->raisin);
  516. else
  517. sendto_snomask(SNO_TKL, "*** Permanent M-Line %sed by %s for %s [reason: %s]", (del ? "delet" : "add"), setby, mask, msEntry->raisin);
  518. }
  519. else {
  520. // Make pretty expiration timestamp if not a permanent M-Line
  521. *gmt = '\0';
  522. short_date(msEntry->set + msEntry->expire, gmt);
  523. if(IsServer(client)) // Show "set at" during sync phases ;]
  524. sendto_snomask(SNO_TKL, "*** M-Line %sed by %s (set at %s GMT) for %s, expiring at %s GMT [reason: %s]", (del ? "delet" : "add"), setby, gmt2, mask, gmt, msEntry->raisin);
  525. else
  526. sendto_snomask(SNO_TKL, "*** M-Line %sed by %s for %s, expiring at %s GMT [reason: %s]", (del ? "delet" : "add"), setby, mask, gmt, msEntry->raisin);
  527. }
  528. // Delete em famamlamlamlmal
  529. if(del)
  530. del_mline(msEntry);
  531. }