otkl.c 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676
  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/otkl";
  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/otkl\";";
  15. "Then /rehash the IRCd.";
  16. "For usage information, refer to the module's documentation found at: https://gottem.nl/unreal/man/otkl";
  17. }
  18. }
  19. *** <<<MODULE MANAGER END>>>
  20. */
  21. // One include for all cross-platform compatibility thangs
  22. #include "unrealircd.h"
  23. // Command strings
  24. #define MSG_OGLINE "OGLINE" // Regular G-Lines
  25. #define MSG_OZLINE "OZLINE" // Actually a GZ-Line ;]
  26. #define DEF_NORAISIN "no reason" // Default message y0
  27. // Dem macros yo
  28. CMD_FUNC(cmd_ogline); // Register command function
  29. CMD_FUNC(cmd_ozline); // Register command function
  30. #define CheckAPIError(apistr, apiobj) \
  31. do { \
  32. if(!(apiobj)) { \
  33. config_error("A critical error occurred on %s for %s: %s", (apistr), MOD_HEADER.name, ModuleGetErrorStr(modinfo->handle)); \
  34. return MOD_FAILED; \
  35. } \
  36. } while(0)
  37. // Big hecks go here
  38. typedef struct t_otkline OTKLine;
  39. struct t_otkline {
  40. char flag; // G or Z really
  41. char *user; // Ident
  42. char *host; // Hostname or IP
  43. char *raisin; // Raisin lel
  44. char *setby; // Set by who
  45. time_t setat; // UNIX time of when it's set
  46. OTKLine *next; // Quality linked list
  47. };
  48. // Quality fowod declarations
  49. static void dumpit(Client *client, char **p);
  50. void otkl_identmd_free(ModData *md);
  51. void otkl_moddata_free(ModData *md);
  52. OTKLine *get_otklines(void);
  53. OTKLine *find_otkline(char *user, char *host, char flag);
  54. void add_otkline(OTKLine *newotkl);
  55. void del_otkline(OTKLine *muhotkl);
  56. void free_all_otklines(OTKLine *OTKList);
  57. int match_otkline(Client *client, OTKLine *otkline, char flag);
  58. void free_otkline(OTKLine *otkline);
  59. void otkl_main(Client *client, int parc, char *parv[], char flag);
  60. int otkl_hook_serverconnect(Client *client);
  61. int otkl_hook_pre_localconnect(Client *client);
  62. int ban_too_broad(char *usermask, char *hostmask);
  63. // Muh globals
  64. ModDataInfo *otklMDI; // To store the OTKLines with &me lol (hack so we don't have to use a .db file or some shit)
  65. ModDataInfo *otklIdentMDI; // Moddata for the original ident =]
  66. int OTKCount; // Count em
  67. // Help string in case someone does just /OGLINE
  68. static char *muhHalp[] = {
  69. /* Special characters:
  70. ** \002 = bold -- \x02
  71. ** \037 = underlined -- \x1F
  72. */
  73. "*** \002Help on /OGLINE and /OZLINE\002 ***",
  74. "Allows privileged opers to set one-time G/GZ-Lines.",
  75. "Local Z-Lines are not supported because they seem fairly useless.",
  76. "The rules for hostmasks etc are the same as for regular G/GZ-Lines.",
  77. "These \"OTKLines\" persist through a rehash, plus servers will",
  78. "sync everything they know at link-time too. If any online users",
  79. "match a newly added OTKLine, they will be killed first. Then once",
  80. "they reconnect, they'll get killed once more before the OTKLine is",
  81. "removed from all servers.",
  82. " ",
  83. "Syntax:",
  84. " \002/OGLINE\002 or \002/OZLINE\002",
  85. " List the currently known OG/OZ-Lines (two different lists)",
  86. " \002/OGLINE\002 [-]\037identmask@hostmask\037 {\037reason\037}",
  87. " Set/remove an OG-Line (reason is required for adding)",
  88. " \002/OZLINE\002 [-]\037identmask@ipmask\037 {\037reason\037}",
  89. " Set/remove an OZ-Line (reason is required for adding)",
  90. " \002/OGLINE\002 \037help\037",
  91. " \002/OZLINE\002 \037help\037",
  92. " View this built-in help",
  93. " ",
  94. "Examples:",
  95. " \002/OGLINE *@some.domain.* not gonna happen\002",
  96. " Sets a host-based ban",
  97. " \002/OZLINE *@123.123.* nope\002",
  98. " Sets an IP-based ban",
  99. " \002/OZLINE -*@123.123.* nope\002",
  100. " Removes an IP-based ban",
  101. NULL
  102. };
  103. // Dat dere module header
  104. ModuleHeader MOD_HEADER = {
  105. "third/otkl", // Module name
  106. "2.0", // Version
  107. "Implements one-time TKLines", // Description
  108. "Gottem", // Author
  109. "unrealircd-5", // Modversion
  110. };
  111. // Initialisation routine (register hooks, commands and modes or create structs etc)
  112. MOD_INIT() {
  113. OTKLine *OTKList, *cur; // To initialise the OTKL counter imo tbh fam
  114. CheckAPIError("CommandAdd(OGLINE)", CommandAdd(modinfo->handle, MSG_OGLINE, cmd_ogline, MAXPARA, CMD_USER | CMD_SERVER));
  115. CheckAPIError("CommandAdd(OZLINE)", CommandAdd(modinfo->handle, MSG_OZLINE, cmd_ozline, MAXPARA, CMD_USER | CMD_SERVER));
  116. // Request moddata for storing the original nick
  117. ModDataInfo mreq2;
  118. memset(&mreq2, 0, sizeof(mreq2));
  119. mreq2.type = MODDATATYPE_LOCAL_CLIENT;
  120. mreq2.name = "otkl_lastidents"; // Name it
  121. mreq2.free = otkl_identmd_free; // Function to free 'em
  122. otklIdentMDI = ModDataAdd(modinfo->handle, mreq2);
  123. CheckAPIError("ModDataAdd(otkl_lastidents)", otklIdentMDI);
  124. OTKCount = 0;
  125. if(!(otklMDI = findmoddata_byname("otkl", MODDATATYPE_LOCAL_VARIABLE))) { // Attempt to find active moddata (like in case of a rehash)
  126. ModDataInfo mreq; // No moddata, let's request that shit
  127. memset(&mreq, 0, sizeof(mreq)); // Set 'em lol
  128. mreq.type = MODDATATYPE_LOCAL_VARIABLE;
  129. mreq.name = "otkl_list"; // Name it
  130. mreq.free = otkl_moddata_free; // Function to free 'em
  131. mreq.serialize = NULL;
  132. mreq.unserialize = NULL;
  133. mreq.sync = 0;
  134. otklMDI = ModDataAdd(modinfo->handle, mreq); // Add 'em yo
  135. CheckAPIError("ModDataAdd(otkl_list)", otklMDI);
  136. }
  137. else { // We did get moddata
  138. if((OTKList = get_otklines())) { // So load 'em
  139. for(cur = OTKList; cur; cur = cur->next) // and iter8 m8
  140. OTKCount++; // Ayyy premium countur
  141. }
  142. }
  143. MARK_AS_GLOBAL_MODULE(modinfo);
  144. // Add 'em hewks
  145. HookAdd(modinfo->handle, HOOKTYPE_SERVER_CONNECT, 0, otkl_hook_serverconnect);
  146. HookAdd(modinfo->handle, HOOKTYPE_PRE_LOCAL_CONNECT, 0, otkl_hook_pre_localconnect);
  147. return MOD_SUCCESS;
  148. }
  149. // Actually load the module here (also command overrides as they may not exist in MOD_INIT yet)
  150. MOD_LOAD() {
  151. return MOD_SUCCESS; // We good
  152. }
  153. // Called on unload/rehash obv
  154. MOD_UNLOAD() {
  155. // Not clearing the moddata structs here so we can re-use them easily ;];]
  156. return MOD_SUCCESS; // We good
  157. }
  158. // Dump a NULL-terminated array of strings to the user (taken from DarkFire IRCd)
  159. static void dumpit(Client *client, char **p) {
  160. if(IsServer(client)) // Bail out early and silently if it's a server =]
  161. return;
  162. // Using sendto_one() instead of sendnumericfmt() because the latter strips indentation and stuff ;]
  163. for(; *p != NULL; p++)
  164. sendto_one(client, NULL, ":%s %03d %s :%s", me.name, RPL_TEXT, client->name, *p);
  165. // Let user take 8 seconds to read it
  166. client->local->since += 8;
  167. }
  168. void otkl_identmd_free(ModData *md) {
  169. safe_free(md->ptr);
  170. }
  171. // Probably never called but it's a required function
  172. // The free shit here normally only happens when the client attached to the moddata quits (afaik), but that's us =]
  173. void otkl_moddata_free(ModData *md) {
  174. if(md->ptr) { // r u insaiyan?
  175. free_all_otklines(md->ptr); // Free that shit
  176. md->ptr = NULL; // Just in case lol
  177. }
  178. }
  179. CMD_FUNC(cmd_ogline) {
  180. otkl_main(client, parc, parv, 'G');
  181. }
  182. CMD_FUNC(cmd_ozline) {
  183. otkl_main(client, parc, parv, 'Z');
  184. }
  185. OTKLine *get_otklines(void) {
  186. OTKLine *OTKList = moddata_local_variable(otklMDI).ptr; // Get mod data
  187. if(OTKList && OTKList->user) // Sanity check lol
  188. return OTKList;
  189. return NULL;
  190. }
  191. OTKLine *find_otkline(char *user, char *host, char flag) {
  192. OTKLine *OTKList, *otkline; // Head and iter8or fam
  193. if((OTKList = get_otklines())) { // Check if the list even has entries kek
  194. for(otkline = OTKList; otkline; otkline = otkline->next) { // Iter8 em
  195. // Let's do strcasecmp() just in case =]
  196. if(otkline->flag == flag && !strcasecmp(otkline->user, user) && !strcasecmp(otkline->host, host))
  197. return otkline;
  198. }
  199. }
  200. return NULL; // Not found m8
  201. }
  202. void add_otkline(OTKLine *newotkl) {
  203. OTKLine *OTKList, *otkline; // Head + iter8or imo tbh
  204. newotkl->next = NULL; // Cuz inb4rip linked list
  205. OTKCount++; // Always increment count
  206. if(!(OTKList = get_otklines())) { // If OTKList is NULL...
  207. OTKList = newotkl; // ...simply have it point to the newly alloc8ed entry
  208. moddata_local_variable(otklMDI).ptr = OTKList; // And st0re em
  209. return;
  210. }
  211. for(otkline = OTKList; otkline && otkline->next; otkline = otkline->next) { } // Get teh last entray
  212. otkline->next = newotkl; // Append lol
  213. }
  214. void del_otkline(OTKLine *muhotkl) {
  215. OTKLine *OTKList, *last, **otkline;
  216. if(!(OTKList = get_otklines())) // Ayyy no OTKLines known
  217. return;
  218. otkline = &OTKList; // Hecks so the ->next chain stays intact
  219. if(*otkline == muhotkl) { // If it's the first entry, need to take special precautions ;]
  220. last = *otkline; // Get the entry pointur
  221. *otkline = last->next; // Set the iterat0r to the next one
  222. free_otkline(last); // Free that shit
  223. moddata_local_variable(otklMDI).ptr = *otkline; // Cuz shit rips if we don't do dis
  224. OTKCount--;
  225. return;
  226. }
  227. while(*otkline) { // Loop while we have entries obv
  228. if(*otkline == muhotkl) { // Do we need to delete em?
  229. last = *otkline; // Get the entry pointur
  230. *otkline = last->next; // Set the iterat0r to the next one
  231. free_otkline(last); // Free that shit
  232. OTKCount--;
  233. break;
  234. }
  235. else {
  236. otkline = &(*otkline)->next; // No need, go to the next one
  237. }
  238. }
  239. if(OTKCount <= 0) // Cuz shit rips if we don't do dis
  240. moddata_local_variable(otklMDI).ptr = NULL;
  241. }
  242. void free_all_otklines(OTKLine *OTKList) {
  243. OTKLine *last, **otkline;
  244. if(!OTKList)
  245. return;
  246. otkline = &OTKList; // Hecks so the ->next chain stays intact
  247. while(*otkline) { // Loop while we have entries obv
  248. last = *otkline; // Get the entry pointur
  249. *otkline = last->next; // Set the iterat0r to the next one
  250. free_otkline(last); // Free that shit
  251. OTKCount--;
  252. }
  253. }
  254. int match_otkline(Client *client, OTKLine *otkline, char flag) {
  255. int gottem = 0; // Equals 2 when we got a solid match
  256. char *orig = moddata_local_client(client, otklIdentMDI).ptr;
  257. // Check current and stored idents ;]
  258. if(match_simple(otkline->user, client->user->username))
  259. gottem++;
  260. else if(orig && match_simple(otkline->user, orig))
  261. gottem++;
  262. // Check IP or host based shit ;]
  263. if(flag == 'Z' && match_simple(otkline->host, GetIP(client)))
  264. gottem++;
  265. else if(flag == 'G' && match_simple(otkline->host, client->user->realhost))
  266. gottem++;
  267. return (gottem == 2 ? 1 : 0);
  268. }
  269. void free_otkline(OTKLine *otkline) {
  270. if(otkline) {
  271. safe_free(otkline->user); // Gotta
  272. safe_free(otkline->host); // free
  273. safe_free(otkline->raisin); // em
  274. safe_free(otkline->setby); // all
  275. safe_free(otkline); // lol
  276. otkline = NULL; // Just in case imo
  277. }
  278. }
  279. void otkl_main(Client *client, int parc, char *parv[], char flag) {
  280. char *otxt; // Get command name =]
  281. char *mask, *usermask, *hostmask; // Some mask pointers yo
  282. char prevmask[USERLEN + HOSTLEN + 1]; // When replacing, show the old one in the notice lel
  283. char *p; // For checking mask validity
  284. char *lineby; // When deleted by someone else =]
  285. char *setby; // Set by who (just a nick imo tbh)
  286. char *raisin; // Cleaned reason (like no colours and shit)
  287. time_t setat; // UNIX time
  288. char gmt[128]; // For a pretty timestamp instead of UNIX time lol
  289. int del; // In case it's -id@host shit
  290. int repl; // Or replacing one
  291. OTKLine *otkline; // To check for existing OTKLines yo
  292. OTKLine *newotkl; // For adding one =]
  293. OTKLine *OTKList; // For listing that shit
  294. Client *acptr; // The mask may also be a nick, so let's store the user pointer for it =]
  295. Client *victim, *vnext; // Iterate online users to see if we need to kill any ;];]
  296. int i; // Iter8or for reason concaten8ion m8 =]
  297. int c; // To send a notice bout not having ne OTKLines
  298. int locuser; // Is local user (to store result of MyUser(client) lol)
  299. if(IsServer(client) && parc < 6)
  300. return;
  301. otxt = (flag == 'Z' ? MSG_OZLINE : MSG_OGLINE);
  302. locuser = (MyUser(client));
  303. // Servers can always use it (in order to sync that shit lol)
  304. if(IsUser(client) && (!IsOper(client) || !ValidatePermissionsForPath("otkl", client, NULL, NULL, NULL))) {
  305. if(locuser) // We allow remote clients as part of a hack, so only print errors for l0qal users for een bit ;]
  306. sendnumeric(client, ERR_NOPRIVILEGES); // Check ur privilege fam
  307. return;
  308. }
  309. if(!strchr("GZ", flag)) { // Should't happen tho tbh
  310. if(locuser) // A bunch of shit is silent for servers and rem0te users =]
  311. sendnotice(client, "*** Invalid OTKL type '%c'", flag);
  312. return;
  313. }
  314. c = 0;
  315. if(BadPtr(parv[1])) { // If first argument is a bad pointer, dump known OTKLines of this type instead
  316. if(locuser) { // Only shit out the list if we're talking to a local user here
  317. if((OTKList = get_otklines())) { // We gottem list?
  318. for(otkline = OTKList; otkline; otkline = otkline->next) { // Checkem
  319. if(otkline->flag != flag) // Can only one of the types we carry
  320. continue;
  321. // Make pwetti~~ timestamp imo
  322. *gmt = '\0';
  323. short_date(otkline->setat, gmt);
  324. sendnotice(client, "*** %s by %s (set at %s GMT) for %s@%s [reason: %s]", otxt, otkline->setby, gmt, otkline->user, otkline->host, otkline->raisin);
  325. c++; // For the if below, required for if OGLINE list is empty but OZLINE isn't etc
  326. }
  327. }
  328. if(!c)
  329. sendnotice(client, "*** No %sS found", otxt);
  330. }
  331. return;
  332. }
  333. if(!strcasecmp(parv[1], "help") || !strcasecmp(parv[1], "halp")) { // If first argument is "halp" or "help"
  334. if(!locuser || IsServer(client)) // El silenci0
  335. return;
  336. dumpit(client, muhHalp); // Return halp string for l0cal users only rly =]
  337. return;
  338. }
  339. // Initialise some shit here =]
  340. mask = parv[1];
  341. usermask = NULL;
  342. hostmask = NULL;
  343. p = NULL;
  344. lineby = client->name;
  345. setby = client->name;
  346. raisin = NULL;
  347. setat = 0;
  348. repl = 0;
  349. acptr = NULL;
  350. if(IsServer(client)) {
  351. setat = atol(parv[2]);
  352. setby = parv[3];
  353. lineby = parv[4];
  354. raisin = parv[5];
  355. }
  356. if((del = (*mask == '-')) || *mask == '+') // Check for +hue@hue and -hue@hue etc
  357. mask++; // Skip that shit
  358. // Check for the sanity of the passed mask
  359. // Ripped from src/modules/tkl.c with some edits =]
  360. if(strchr(mask, '!')) {
  361. if(locuser)
  362. sendnotice(client, "*** The mask should be either a nick or of the format ident@host");
  363. return;
  364. }
  365. if(*mask == ':') {
  366. if(locuser)
  367. sendnotice(client, "*** Masks cannot start with a ':'");
  368. return;
  369. }
  370. if(strchr(mask, ' ')) // Found a space in the mask, that should be impossibru ;];]
  371. return;
  372. // Check if it's a hostmask and legal
  373. p = strchr(mask, '@');
  374. if(p) {
  375. if((p == mask) || !p[1]) {
  376. if(locuser)
  377. sendnotice(client, "*** No (valid) user@host mask specified");
  378. return;
  379. }
  380. usermask = strtok(mask, "@");
  381. hostmask = strtok(NULL, "");
  382. if(BadPtr(hostmask)) {
  383. if(BadPtr(usermask))
  384. return;
  385. hostmask = usermask;
  386. usermask = "*";
  387. }
  388. if(*hostmask == ':') {
  389. if(locuser)
  390. sendnotice(client, "*** For (weird) technical reasons you cannot start the host with a ':'");
  391. return;
  392. }
  393. else if(strchr(hostmask, '/')) {
  394. if(locuser)
  395. sendnotice(client, "*** CIDR notation for hostmasks is not supported"); // Cbf
  396. return;
  397. }
  398. if(flag == 'Z' && !del) {
  399. for(p = hostmask; *p; p++) {
  400. if(isalpha(*p) && !isxdigit(*p)) {
  401. if(locuser)
  402. sendnotice(client, "*** O(G)Z-Lines must be placed at ident@\037IPMASK\037, not ident@\037HOSTMASK\037");
  403. return;
  404. }
  405. }
  406. }
  407. p = hostmask - 1;
  408. }
  409. else { // Might be a nick
  410. if((acptr = find_person(mask, NULL))) { // Try to find the user
  411. if(!(usermask = moddata_local_client(acptr, otklIdentMDI).ptr))
  412. usermask = (acptr->user->username ? acptr->user->username : "*"); // Let's set an ident@host/ip ban lel
  413. if(flag == 'Z') { // We got OZLINE
  414. hostmask = GetIP(acptr); // So gettem IP lol
  415. if(!hostmask) {
  416. if(locuser)
  417. sendnotice(client, "*** Could not get IP for user '%s'", acptr->name);
  418. return;
  419. }
  420. }
  421. else
  422. hostmask = acptr->user->realhost; // Get _real_ hostname (not masked/cloaked)
  423. p = hostmask - 1;
  424. }
  425. else { // No such nick found lel
  426. if(locuser)
  427. sendnumeric(client, ERR_NOSUCHNICK, mask);
  428. return;
  429. }
  430. }
  431. if(!del && ban_too_broad(usermask, hostmask)) { // If we adding but the ban is too broad
  432. if(locuser)
  433. sendnotice(client, "*** Too broad mask");
  434. return;
  435. }
  436. if(IsUser(client) && !del) { // If a user is adding an OTKLine, we need to concat the reason params =]
  437. setat = TStime(); // Get the "setat" time only here for accuracy etc
  438. if(!BadPtr(parv[2])) {
  439. char rbuf[256]; // Reason buffer, 256 shud b enough xd
  440. memset(rbuf, '\0', sizeof(rbuf));
  441. for(i = 2; i < parc && parv[i]; i++) { // Start at the "third" arg
  442. if(i == 2) // Only for the first time around
  443. strlcpy(rbuf, parv[i], sizeof(rbuf)); // cpy() that shit
  444. else { // cat() the rest =]
  445. strlcat(rbuf, " ", sizeof(rbuf));
  446. strlcat(rbuf, parv[i], sizeof(rbuf));
  447. }
  448. }
  449. raisin = (char *)StripControlCodes(rbuf); // No markup pls
  450. }
  451. }
  452. if(!raisin)
  453. raisin = DEF_NORAISIN;
  454. // Mite need2fow0d the OTKLine to another server (like if the id@ho mask is an online remote nickname instead)
  455. if(acptr && !MyUser(acptr)) {
  456. // Forward as the user lol (the receiving server will re-parse and rebroadcast em proper OTKLine)
  457. sendto_one(acptr->direction, NULL, ":%s %s %s%s :%s", client->name, otxt, (del ? "-" : ""), acptr->name, raisin); // Muh raw command
  458. return; // We done for nao
  459. }
  460. // After all the sanity checks, see if we have an OTKLine alredy
  461. otkline = find_otkline(usermask, hostmask, flag); // Attempt to find that shit
  462. if(del && !otkline) { // See if the OTKLine even exists when deleting
  463. if(IsUser(client))
  464. sendnotice(client, "*** %s for %s@%s was not found", otxt, usermask, hostmask);
  465. return; // We done
  466. }
  467. else if(!del && otkline) { // Adding, but already exists kek
  468. ircsnprintf(prevmask, sizeof(prevmask), "%s@%s", otkline->user, otkline->host);
  469. if(strcasecmp(otkline->user, usermask)) {
  470. safe_strdup(otkline->user, usermask);
  471. repl = 1;
  472. }
  473. if(strcasecmp(otkline->host, hostmask)) {
  474. safe_strdup(otkline->host, hostmask);
  475. repl = 1;
  476. }
  477. if(strcasecmp(otkline->raisin, raisin)) {
  478. safe_strdup(otkline->raisin, raisin);
  479. repl = 1;
  480. }
  481. if(IsUser(client))
  482. sendnotice(client, "*** Matching %s for %s@%s already exists%s", otxt, usermask, hostmask, (repl ? ", replacing it" : " (no change detected)"));
  483. if(!repl)
  484. return; // We done
  485. if(strcasecmp(otkline->setby, setby)) {
  486. free(otkline->setby);
  487. safe_strdup(otkline->setby, setby);
  488. }
  489. otkline->setat = setat;
  490. }
  491. else if(!del && !otkline) { // Adding a new one
  492. // Es fer b0th servers and users ;]
  493. // Allocate/initialise mem0ry for the new entry
  494. newotkl = safe_alloc(sizeof(OTKLine));
  495. newotkl->flag = flag; // Set flag ('G', 'Z')
  496. safe_strdup(newotkl->user, usermask); // Gotta
  497. safe_strdup(newotkl->host, hostmask); // dup
  498. safe_strdup(newotkl->raisin, raisin); // 'em
  499. safe_strdup(newotkl->setby, setby); // all
  500. newotkl->setat = setat; // Set timestampus
  501. add_otkline(newotkl); // Add to the linked list
  502. otkline = newotkl; // Set the main iteration pointer to the new entry ;]
  503. }
  504. // Propagate the M-Line to other local servers fam (excluding the direction it came from ;])
  505. sendto_server(client, 0, 0, NULL, ":%s %s %s%s@%s %ld %s %s :%s", me.id, otxt, (del ? "-" : ""), otkline->user, otkline->host, otkline->setat, otkline->setby, lineby, otkline->raisin);
  506. // Also send snomask notices to all local opers =]
  507. // Make pretty setat timestamp first tho
  508. *gmt = '\0';
  509. short_date(otkline->setat, gmt);
  510. if(del && *lineby == '*' && IsServer(client)) // This condition is true if someone reconnects and hits an OTKLine on a diff server
  511. sendto_snomask(SNO_TKL, "*** %s by %s (set at %s GMT) for %s@%s was matched, so removing it now [reason: %s]", otxt, otkline->setby, gmt, otkline->user, otkline->host, otkline->raisin);
  512. else if(repl)
  513. sendto_snomask(SNO_TKL, "*** %s replaced %s for %s with %s@%s [reason: %s]", (*lineby == '*' ? otkline->setby : lineby), otxt, prevmask, otkline->user, otkline->host, otkline->raisin);
  514. else
  515. sendto_snomask(SNO_TKL, "*** %s %sed by %s (set at %s GMT) for %s@%s [reason: %s]", otxt, (del ? "delet" : "add"), (*lineby == '*' ? otkline->setby : lineby), gmt, otkline->user, otkline->host, otkline->raisin);
  516. if(del) // Delete em famamlamlamlmal
  517. del_otkline(otkline);
  518. else {
  519. // Let's kill online (local) users that match this OTKLine =]
  520. list_for_each_entry_safe(victim, vnext, &lclient_list, lclient_node) { // Iterate over all local users
  521. if(!victim || !MyUser(victim) || !IsUser(victim) || IsULine(victim)) // Just some sanity checks imo
  522. continue;
  523. if(match_otkline(victim, otkline, flag)) { // Check if victim's user@host matches the OTKLine
  524. char banmsg[BUFSIZE]; // Let's construct an exit message similar to G-Lines etc =]
  525. ircsnprintf(banmsg, sizeof(banmsg), "User has been permanently banned from %s (%s)", ircnetwork, otkline->raisin);
  526. exit_client(victim, NULL, banmsg); // Exit the client y0 =]
  527. }
  528. }
  529. }
  530. }
  531. int otkl_hook_serverconnect(Client *client) {
  532. // Sync OTKLines fam =]
  533. OTKLine *OTKList, *otkline; // Head and iter8or ;];]
  534. char *otxt; // Get text OGLINE for 'G' etc
  535. if((OTKList = get_otklines())) { // Gettem list
  536. for(otkline = OTKList; otkline; otkline = otkline->next) {
  537. if(!otkline || !otkline->user) // Sanity check imo ;]
  538. continue;
  539. otxt = (otkline->flag == 'Z' ? MSG_OZLINE : MSG_OGLINE);
  540. // Syntax for servers is a bit different (namely the setat/by args and the : before reason (makes the entire string after be considered one arg ;];])
  541. sendto_one(client, NULL, ":%s %s %s@%s %ld %s * :%s", me.id, otxt, otkline->user, otkline->host, otkline->setat, otkline->setby, otkline->raisin); // Muh raw command
  542. }
  543. }
  544. return HOOK_CONTINUE;
  545. }
  546. int otkl_hook_pre_localconnect(Client *client) {
  547. OTKLine *OTKList, *otkline; // Head and iter8or fam
  548. int banned = 0;
  549. char *otxt; // Get text OGLINE from 'G' etc
  550. char gmt[128];
  551. if((OTKList = get_otklines())) { // Check if the list even has entries kek
  552. for(otkline = OTKList; otkline; otkline = otkline->next) { // Iter8 em
  553. if(match_otkline(client, otkline, otkline->flag)) { // If connecting user matches the OTKLine
  554. char banmsg[BUFSIZE]; // Construct the same ban message
  555. ircsnprintf(banmsg, sizeof(banmsg), "User has been permanently banned from %s (%s)", ircnetwork, otkline->raisin);
  556. otxt = (otkline->flag == 'Z' ? MSG_OZLINE : MSG_OGLINE);
  557. exit_client(client, NULL, banmsg); // And kill them =]
  558. banned = 1;
  559. break; // No need to check any further
  560. }
  561. }
  562. }
  563. if(banned) { // We gottem match =]
  564. // Make pretty timestamp yet again =3
  565. *gmt = '\0';
  566. short_date(otkline->setat, gmt);
  567. // Notify other servers to delete their copy of the OTKLine, who in turn will also notify their local opers
  568. sendto_server(NULL, 0, 0, NULL, ":%s %s -%s@%s %ld %s * :%s", me.id, otxt, otkline->user, otkline->host, otkline->setat, otkline->setby, otkline->raisin); // Muh raw command
  569. // And notify online (local) opers about the hit
  570. sendto_snomask(SNO_TKL, "*** %s by %s (set at %s GMT) for %s@%s was matched, so removing it now [reason: %s]", otxt, otkline->setby, gmt, otkline->user, otkline->host, otkline->raisin);
  571. del_otkline(otkline); // Then actually remove it from the list
  572. }
  573. else // No match, git dat ident fam ;];]
  574. moddata_local_client(client, otklIdentMDI).ptr = strdup(client->user->username);
  575. return (banned ? HOOK_DENY : HOOK_CONTINUE);
  576. }
  577. // Ripped from src/modules/tkl.c with some edits =]
  578. int ban_too_broad(char *usermask, char *hostmask) {
  579. char *p;
  580. int cnt = 0;
  581. if(ALLOW_INSANE_BANS)
  582. return 0;
  583. // Allow things like clone@*, dsfsf@*, etc
  584. if(!strchr(usermask, '*') && !strchr(usermask, '?'))
  585. return 0;
  586. // Need at least 4 non-wildcard/dot chars kek
  587. for(p = hostmask; *p; p++) {
  588. if(*p != '*' && *p != '.' && *p != '?')
  589. cnt++;
  590. }
  591. if(cnt >= 4)
  592. return 0; // We good, so return "not too broad"
  593. return 1; // Too broad babe
  594. }