m_listrestrict.c 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661
  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. ** Contains edits by k4be to implement a fake channel list
  7. */
  8. // One include for all cross-platform compatibility thangs
  9. #include "unrealircd.h"
  10. // Unreal >= 4.2.2 doesn't have this define anymore
  11. #ifndef TOPICLEN
  12. #define TOPICLEN (iConf.topic_length > 0 ? iConf.topic_length : tempiConf.topic_length)
  13. #endif
  14. // But Unreal < 4.2.2 doesn't have this one :D
  15. #ifndef MAXTOPICLEN
  16. #define MAXTOPICLEN TOPICLEN
  17. #endif
  18. #define MYCONF "listrestrict"
  19. #define OVR_LIST "LIST"
  20. #define OVR_JOIN "JOIN"
  21. #define FCHAN_DEFUSERS 2 // Let 2 users be the default for a fake channel
  22. #define FCHAN_DEFTOPIC "DO NOT JOIN" // Also topic
  23. #define LR_DELAYFAIL(x) (muhDelay > 0 && (x)->local && TStime() - (x)->local->firsttime < muhDelay)
  24. #define LR_AUTHFAIL(x) (needAuth && !IsLoggedIn((x)))
  25. // Big hecks go here
  26. typedef enum {
  27. LRE_UNKNOWN = -1,
  28. LRE_ALL = 0,
  29. LRE_CONNECT = 1,
  30. LRE_AUTH = 2,
  31. LRE_FAKECHANS = 3,
  32. } exceptType;
  33. typedef struct t_restrictex restrictExcept;
  34. struct t_restrictex {
  35. exceptType type;
  36. char *mask;
  37. restrictExcept *next;
  38. };
  39. typedef struct t_fakechans fakeChannel;
  40. struct t_fakechans {
  41. char *name;
  42. char *topic;
  43. int users;
  44. unsigned short glinem;
  45. fakeChannel *next;
  46. };
  47. // Quality fowod declarations
  48. void checkem_exceptions(aClient *sptr, unsigned short *connect, unsigned short *auth, unsigned short *fakechans);
  49. static int listrestrict_overridelist(Cmdoverride *ovr, aClient *cptr, aClient *sptr, int parc, char *parv[]);
  50. static int listrestrict_overridejoin(Cmdoverride *ovr, aClient *cptr, aClient *sptr, int parc, char *parv[]);
  51. int listrestrict_configtest(ConfigFile *cf, ConfigEntry *ce, int type, int *errs);
  52. int listrestrict_configposttest(int *errs);
  53. int listrestrict_configrun(ConfigFile *cf, ConfigEntry *ce, int type);
  54. int listrestrict_rehash(void);
  55. // Muh globals
  56. static ModuleInfo *lrestrictMI = NULL; // Store ModuleInfo so we can use it to check for errors in MOD_LOAD
  57. Cmdoverride *lrestrictOVRList, *lrestrictOVRJoin; // Pointer to the overrides we're gonna add
  58. restrictExcept *exceptList = NULL; // Stores exceptions yo
  59. fakeChannel *fakechanList = NULL; // Also fake channels
  60. int fakechanCount = 0;
  61. unsigned short conf_fakechans = 0;
  62. // Deez defaults
  63. int muhDelay = 0; // Default to off yo
  64. unsigned short needAuth = 0; // Must be identified w/ NickServ (in addition to passing the delay check)
  65. unsigned short fakeChans = 0; // Send fake channels list
  66. unsigned short authIsEnough = 0; // Only NickServ auth is enough to be exempt
  67. time_t glineTime = 86400; // Default to 1 day
  68. // Dat dere module header
  69. ModuleHeader MOD_HEADER(m_listrestrict) = {
  70. "m_listrestrict", // Module name
  71. "$Id: v1.06 2019/06/17 Gottem/k4be$", // Version
  72. "Impose certain restrictions on /LIST usage", // Description
  73. "3.2-b8-1", // Modversion, not sure wat do
  74. NULL
  75. };
  76. // Configuration testing-related hewks go in testing phase obv
  77. MOD_TEST(m_listrestrict) {
  78. // We have our own config block so we need to checkem config obv m9
  79. // Priorities don't really matter here
  80. HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, listrestrict_configtest);
  81. HookAdd(modinfo->handle, HOOKTYPE_CONFIGPOSTTEST, 0, listrestrict_configposttest);
  82. return MOD_SUCCESS;
  83. }
  84. // Initialisation routine (register hooks, commands and modes or create structs etc)
  85. MOD_INIT(m_listrestrict) {
  86. lrestrictMI = modinfo; // Store module info yo
  87. // Muh config hewks
  88. HookAdd(modinfo->handle, HOOKTYPE_REHASH, 0, listrestrict_rehash);
  89. HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, listrestrict_configrun);
  90. return MOD_SUCCESS; // Let MOD_LOAD handle errors and shyte
  91. }
  92. // Actually load the module here
  93. MOD_LOAD(m_listrestrict) {
  94. // Attempt to add command overrides
  95. lrestrictOVRList = CmdoverrideAdd(lrestrictMI->handle, OVR_LIST, listrestrict_overridelist);
  96. lrestrictOVRJoin = CmdoverrideAdd(lrestrictMI->handle, OVR_JOIN, listrestrict_overridejoin);
  97. // Check if module handle is available, also check for hooks that weren't added for some raisin
  98. if(ModuleGetError(lrestrictMI->handle) != MODERR_NOERROR || !lrestrictOVRList || !lrestrictOVRJoin) {
  99. // Display error string kek
  100. config_error("A critical error occurred when loading module %s: %s", MOD_HEADER(m_listrestrict).name, ModuleGetErrorStr(lrestrictMI->handle));
  101. return MOD_FAILED; // No good
  102. }
  103. return MOD_SUCCESS; // We good
  104. }
  105. // Called on unload/rehash obv
  106. MOD_UNLOAD(m_listrestrict) {
  107. if(exceptList) {
  108. // This shit is a bit convoluted to prevent memory issues obv famalmalmalmlmalm
  109. restrictExcept *exEntry;
  110. while((exEntry = exceptList) != NULL) {
  111. exceptList = exceptList->next;
  112. if(exEntry->mask) free(exEntry->mask);
  113. free(exEntry);
  114. }
  115. exceptList = NULL;
  116. }
  117. if(fakechanList) {
  118. // This shit is a bit convoluted to prevent memory issues obv famalmalmalmlmalm
  119. fakeChannel *fchanEntry;
  120. while((fchanEntry = fakechanList) != NULL) {
  121. fakechanList = fakechanList->next;
  122. if(fchanEntry->name) free(fchanEntry->name);
  123. if(fchanEntry->topic) free(fchanEntry->topic);
  124. free(fchanEntry);
  125. }
  126. fakechanList = NULL;
  127. }
  128. fakechanCount = 0;
  129. return MOD_SUCCESS; // We good
  130. }
  131. void checkem_exceptions(aClient *sptr, unsigned short *connect, unsigned short *auth, unsigned short *fakechans) {
  132. restrictExcept *exEntry; // For iteration yo
  133. for(exEntry = exceptList; exEntry; exEntry = exEntry->next) {
  134. if(!match(exEntry->mask, make_user_host(sptr->user->username, sptr->user->realhost)) || !match(exEntry->mask, make_user_host(sptr->user->username, sptr->ip))) {
  135. switch(exEntry->type) {
  136. case LRE_ALL:
  137. *connect = 1;
  138. *auth = 1;
  139. *fakechans = 1;
  140. break;
  141. case LRE_CONNECT:
  142. *connect = 1;
  143. break;
  144. case LRE_AUTH:
  145. *auth = 1;
  146. break;
  147. case LRE_FAKECHANS:
  148. *fakechans = 1;
  149. break;
  150. default:
  151. break;
  152. }
  153. // Keep checking entries to support just whitelisting 2 instead of 1 or all ;]
  154. }
  155. }
  156. if(authIsEnough && (*auth || IsLoggedIn(sptr)))
  157. *connect = 1;
  158. }
  159. // Now for the actual override
  160. static int listrestrict_overridelist(Cmdoverride *ovr, aClient *cptr, aClient *sptr, int parc, char *parv[]) {
  161. /* Gets args: Cmdoverride *ovr, aClient *cptr, aClient *sptr, int parc, char *parv[]
  162. **
  163. ** ovr: Pointer to the override we're attached to
  164. ** cptr: Pointer to directly attached client -- if remote user this is the remote server instead
  165. ** sptr: Pointer to user executing command
  166. ** parc: Amount of arguments (also includes the command in the count)
  167. ** parv: Contains the actual args, first one starts at parv[1]
  168. **
  169. ** So "LIST test" would result in parc = 2 and parv[1] = "test"
  170. ** Also, parv[0] seems to always be NULL, so better not rely on it fam
  171. */
  172. fakeChannel *fchanEntry; // For iteration yo
  173. unsigned short except_connect; // We gottem exception?
  174. unsigned short except_auth; // Ditt0
  175. unsigned short except_fakechans;
  176. unsigned short delayFail;
  177. unsigned short authFail;
  178. unsigned short fakechanFail;
  179. // Checkem exceptions bro
  180. except_connect = 0;
  181. except_auth = 0;
  182. except_fakechans = 0;
  183. if(!MyConnect(sptr) || !IsPerson(sptr) || IsOper(sptr) || IsULine(sptr)) { // Default set lel
  184. except_connect = 1;
  185. except_auth = 1;
  186. except_fakechans = 1;
  187. }
  188. else // Not an oper/U:Line/server, checkem whitelist (if ne)
  189. checkem_exceptions(sptr, &except_connect, &except_auth, &except_fakechans);
  190. delayFail = (!except_connect && LR_DELAYFAIL(sptr)); // Sanity check + delay check =]
  191. authFail = (!except_auth && LR_AUTHFAIL(sptr)); // Need identified check ;];;]
  192. fakechanFail = (!except_fakechans && fakeChans);
  193. // Send fake list if necessary
  194. if(fakechanFail && (delayFail || authFail)) {
  195. sendto_one(sptr, rpl_str(RPL_LISTSTART), me.name, sptr->name);
  196. for(fchanEntry = fakechanList; fchanEntry; fchanEntry = fchanEntry->next)
  197. sendto_one(sptr, rpl_str(RPL_LIST), me.name, sptr->name, fchanEntry->name, fchanEntry->users, "[+ntr]", fchanEntry->topic);
  198. sendto_one(sptr, rpl_str(RPL_LISTEND), me.name, sptr->name);
  199. }
  200. if(delayFail) {
  201. sendnotice(sptr, "You have to be connected for at least %d seconds before being able to /%s%s", muhDelay, OVR_LIST, (fakechanFail ? ", please ignore the fake output above" : ""));
  202. return 0;
  203. }
  204. if(authFail) {
  205. sendnotice(sptr, "You have to be identified with services before being able to /%s%s", OVR_LIST, (fakechanFail ? ", please ignore the fake output above" : ""));
  206. return 0;
  207. }
  208. return CallCmdoverride(ovr, cptr, sptr, parc, parv); // Run original function yo
  209. }
  210. static int listrestrict_overridejoin(Cmdoverride *ovr, aClient *cptr, aClient *sptr, int parc, char *parv[]) {
  211. // Doing the G:Line thing in an override too so we run _before_ the channel is actually created, plus we need to return FLUSH_BUFFER
  212. // which isn't supported by HOOKTYPE_PRE_LOCAL_JOIN and might crash shit =]
  213. fakeChannel *fchanEntry;
  214. unsigned short except_connect;
  215. unsigned short except_auth;
  216. unsigned short except_fakechans;
  217. unsigned short delayFail;
  218. unsigned short authFail;
  219. unsigned short fakechanFail;
  220. unsigned short glinem;
  221. char *chan, *tmp, *p; // Pointers for getting multiple channel names
  222. // Only act on local joins etc
  223. if(BadPtr(parv[1]) || !MyConnect(sptr) || !IsPerson(sptr) || IsOper(sptr) || IsULine(sptr) || !fakeChans)
  224. return CallCmdoverride(ovr, cptr, sptr, parc, parv); // Run original function yo
  225. glinem = 0;
  226. tmp = strdup(parv[1]);
  227. for(chan = strtoken(&p, tmp, ","); !glinem && chan; chan = strtoken(&p, NULL, ",")) {
  228. for(fchanEntry = fakechanList; fchanEntry; fchanEntry = fchanEntry->next) {
  229. if(chan && !stricmp(chan, fchanEntry->name)) {
  230. // Should only be one channel per unique name, so break regardless of gline flag
  231. if(fchanEntry->glinem)
  232. glinem = 1;
  233. break;
  234. }
  235. }
  236. }
  237. free(tmp);
  238. // Check if we got an entry matching this channel AND the gline flag is enabled
  239. if(!fchanEntry || !glinem)
  240. return CallCmdoverride(ovr, cptr, sptr, parc, parv);
  241. except_connect = 0;
  242. except_auth = 0;
  243. except_fakechans = 0;
  244. checkem_exceptions(sptr, &except_connect, &except_auth, &except_fakechans);
  245. delayFail = (!except_connect && LR_DELAYFAIL(sptr));
  246. authFail = (!except_auth && LR_AUTHFAIL(sptr));
  247. fakechanFail = (!except_fakechans);
  248. // Place ban if necessary =]
  249. if(fakechanFail && (delayFail || authFail))
  250. return place_host_ban(sptr, BAN_ACT_GLINE, "Invalid channel", glineTime);
  251. return CallCmdoverride(ovr, cptr, sptr, parc, parv);
  252. }
  253. int listrestrict_configtest(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) {
  254. ConfigEntry *cep, *cep2; // For looping through our bl0cc, nested
  255. int errors = 0; // Error count
  256. int i; // iter8or m8
  257. int have_fchanname;
  258. // Since we'll add a new top-level block to unrealircd.conf, need to filter on CONFIG_MAIN lmao
  259. if(type != CONFIG_MAIN)
  260. return 0; // Returning 0 means idgaf bout dis
  261. // Check for valid config entries first
  262. if(!ce || !ce->ce_varname)
  263. return 0;
  264. // If it isn't our bl0ck, idc
  265. if(strcmp(ce->ce_varname, MYCONF))
  266. return 0;
  267. // Loop dat shyte fam
  268. for(cep = ce->ce_entries; cep; cep = cep->ce_next) {
  269. // Do we even have a valid name l0l?
  270. if(!cep->ce_varname) {
  271. config_error("%s:%i: blank %s item", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, MYCONF); // Rep0t error
  272. errors++; // Increment err0r count fam
  273. continue; // Next iteration imo tbh
  274. }
  275. if(!strcmp(cep->ce_varname, "connectdelay")) {
  276. if(!cep->ce_vardata) {
  277. config_error("%s:%i: %s::%s must be an integer of 10 or larger m8", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, MYCONF, cep->ce_varname);
  278. errors++; // Increment err0r count fam
  279. continue; // Next iteration imo tbh
  280. }
  281. // Should be an integer yo
  282. for(i = 0; cep->ce_vardata[i]; i++) {
  283. if(!isdigit(cep->ce_vardata[i])) {
  284. config_error("%s:%i: %s::%s must be an integer of 10 or larger m8", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, MYCONF, cep->ce_varname);
  285. errors++; // Increment err0r count fam
  286. break;
  287. }
  288. }
  289. if(!errors && atoi(cep->ce_vardata) < 10) {
  290. config_error("%s:%i: %s::%s must be an integer of 10 or larger m8", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, MYCONF, cep->ce_varname);
  291. errors++; // Increment err0r count fam
  292. }
  293. continue;
  294. }
  295. if(!strcmp(cep->ce_varname, "needauth")) {
  296. if(!cep->ce_vardata || (strcmp(cep->ce_vardata, "0") && strcmp(cep->ce_vardata, "1"))) {
  297. config_error("%s:%i: %s::%s must be either 0 or 1 fam", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, MYCONF, cep->ce_varname);
  298. errors++; // Increment err0r count fam
  299. }
  300. continue;
  301. }
  302. if(!strcmp(cep->ce_varname, "authisenough")) {
  303. if(!cep->ce_vardata || (strcmp(cep->ce_vardata, "0") && strcmp(cep->ce_vardata, "1"))) {
  304. config_error("%s:%i: %s::%s must be either 0 or 1 fam", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, MYCONF, cep->ce_varname);
  305. errors++; // Increment err0r count fam
  306. }
  307. continue;
  308. }
  309. if(!strcmp(cep->ce_varname, "fakechans")) {
  310. if(!cep->ce_vardata || (strcmp(cep->ce_vardata, "0") && strcmp(cep->ce_vardata, "1"))) {
  311. config_error("%s:%i: %s::%s must be either 0 or 1 fam", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, MYCONF, cep->ce_varname);
  312. errors++; // Increment err0r count fam
  313. }
  314. else
  315. conf_fakechans = atoi(cep->ce_vardata);
  316. continue;
  317. }
  318. if(!strcmp(cep->ce_varname, "glinetime")) {
  319. // Should be a time string imo (7d10s etc, or just 20)
  320. if(!cep->ce_vardata || config_checkval(cep->ce_vardata, CFG_TIME) <= 0) {
  321. config_error("%s:%i: %s::%s must be a time string like '7d10m' or simply '20'", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, MYCONF, cep->ce_varname);
  322. errors++; // Increment err0r count fam
  323. }
  324. continue;
  325. }
  326. // Here comes a nested block =]
  327. if(!strcmp(cep->ce_varname, "exceptions")) {
  328. // Loop 'em again
  329. for(cep2 = cep->ce_entries; cep2; cep2 = cep2->ce_next) {
  330. if(!cep2->ce_varname || !cep2->ce_vardata) {
  331. config_error("%s:%i: blank/incomplete %s::exceptions entry", cep2->ce_fileptr->cf_filename, cep2->ce_varlinenum, MYCONF); // Rep0t error
  332. errors++; // Increment err0r count fam
  333. continue; // Next iteration imo tbh
  334. }
  335. if(strcmp(cep2->ce_varname, "all") && strcmp(cep2->ce_varname, "connect") && strcmp(cep2->ce_varname, "auth") && strcmp(cep2->ce_varname, "fakechans")) {
  336. config_error("%s:%i: invalid %s::exceptions type (must be one of: connect, auth, fakechans, all)", cep2->ce_fileptr->cf_filename, cep2->ce_varlinenum, MYCONF); // Rep0t error
  337. errors++; // Increment err0r count fam
  338. continue; // Next iteration imo tbh
  339. }
  340. if(!match("*!*@*", cep2->ce_vardata) || match("*@*", cep2->ce_vardata) || strlen(cep2->ce_vardata) < 3) {
  341. config_error("%s:%i: invalid %s::exceptions mask (must be of the format ident@hostip)", cep2->ce_fileptr->cf_filename, cep2->ce_varlinenum, MYCONF); // Rep0t error
  342. errors++; // Increment err0r count fam
  343. continue; // Next iteration imo tbh
  344. }
  345. }
  346. continue;
  347. }
  348. // Here comes another nested block =]
  349. if(!strcmp(cep->ce_varname, "fakechannel")) {
  350. have_fchanname = 0;
  351. for(cep2 = cep->ce_entries; cep2; cep2 = cep2->ce_next) {
  352. if(!cep2->ce_varname || !cep2->ce_vardata) {
  353. config_error("%s:%i: blank/incomplete %s::fakechannel entry", cep2->ce_fileptr->cf_filename, cep2->ce_varlinenum, MYCONF); // Rep0t error
  354. errors++; // Increment err0r count fam
  355. continue; // Next iteration imo tbh
  356. }
  357. if(strcmp(cep2->ce_varname, "name") && strcmp(cep2->ce_varname, "topic") && strcmp(cep2->ce_varname, "users") && strcmp(cep2->ce_varname, "gline")) {
  358. config_error("%s:%i: invalid %s::fakechannel attribute (must be one of: name, topic, users, gline)", cep2->ce_fileptr->cf_filename, cep2->ce_varlinenum, MYCONF); // Rep0t error
  359. errors++; // Increment err0r count fam
  360. continue; // Next iteration imo tbh
  361. }
  362. if(!strcmp(cep2->ce_varname, "name")) {
  363. have_fchanname = 1;
  364. if(cep2->ce_vardata[0] != '#') {
  365. config_error("%s:%i: invalid %s::fakechannel::%s (channel name must start with a #)", cep2->ce_fileptr->cf_filename, cep2->ce_varlinenum, MYCONF, cep2->ce_varname); // Rep0t error
  366. errors++;
  367. continue;
  368. }
  369. if(strchr(cep2->ce_vardata, ',') || strchr(cep2->ce_vardata, ' ')) {
  370. config_error("%s:%i: invalid %s::fakechannel::%s (contains space or comma)", cep2->ce_fileptr->cf_filename, cep2->ce_varlinenum, MYCONF, cep2->ce_varname); // Rep0t error
  371. errors++;
  372. continue;
  373. }
  374. if(strlen(cep2->ce_vardata) > CHANNELLEN) {
  375. config_error("%s:%i: invalid %s::fakechannel::%s (too long), max length is %i characters", cep2->ce_fileptr->cf_filename, cep2->ce_varlinenum, MYCONF, cep2->ce_varname, CHANNELLEN); // Rep0t error
  376. errors++;
  377. continue;
  378. }
  379. }
  380. if(!strcmp(cep2->ce_varname, "topic")) {
  381. if(strlen(cep2->ce_vardata) > MAXTOPICLEN) {
  382. config_error("%s:%i: invalid %s::fakechannel::%s (too long), absolute max length is %i characters", cep2->ce_fileptr->cf_filename, cep2->ce_varlinenum, MYCONF, cep2->ce_varname, MAXTOPICLEN); // Rep0t error
  383. errors++;
  384. continue;
  385. }
  386. }
  387. if(!strcmp(cep2->ce_varname, "users")) {
  388. if(!cep2->ce_vardata) {
  389. config_error("%s:%i: %s::fakechannel::%s must be an integer of 1 or larger m8", cep2->ce_fileptr->cf_filename, cep2->ce_varlinenum, MYCONF, cep2->ce_varname);
  390. errors++; // Increment err0r count fam
  391. continue;
  392. }
  393. for(i = 0; cep2->ce_vardata[i]; i++) {
  394. if(!isdigit(cep2->ce_vardata[i])) {
  395. config_error("%s:%i: %s::fakechannel::%s must be an integer of 1 or larger m8", cep2->ce_fileptr->cf_filename, cep2->ce_varlinenum, MYCONF, cep2->ce_varname);
  396. errors++; // Increment err0r count fam
  397. break;
  398. }
  399. }
  400. if(!errors && atoi(cep2->ce_vardata) < 1) {
  401. config_error("%s:%i: %s::fakechannel::%s must be an integer of 1 or larger m8", cep2->ce_fileptr->cf_filename, cep2->ce_varlinenum, MYCONF, cep2->ce_varname);
  402. errors++; // Increment err0r count fam
  403. }
  404. continue;
  405. }
  406. if(!strcmp(cep2->ce_varname, "gline")) {
  407. if(!cep2->ce_vardata || (strcmp(cep2->ce_vardata, "0") && strcmp(cep2->ce_vardata, "1"))) {
  408. config_error("%s:%i: %s::fakechannel::%s must be either 0 or 1 fam", cep2->ce_fileptr->cf_filename, cep2->ce_varlinenum, MYCONF, cep2->ce_varname);
  409. errors++; // Increment err0r count fam
  410. }
  411. continue;
  412. }
  413. }
  414. if(!have_fchanname) {
  415. config_error("%s:%i: invalid %s::fakechannel entry (must contain a channel name)", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, MYCONF); // Rep0t error
  416. errors++;
  417. continue;
  418. }
  419. fakechanCount++;
  420. continue;
  421. }
  422. // Anything else is unknown to us =]
  423. 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
  424. }
  425. *errs = errors;
  426. return errors ? -1 : 1; // Returning 1 means "all good", -1 means we shat our panties
  427. }
  428. int listrestrict_configposttest(int *errs) {
  429. int errors = 0;
  430. if(conf_fakechans && !fakechanCount) {
  431. config_error("[%s] %s::fakechans was enabled but there aren't any configured channels (fakechannel {} block)", MOD_HEADER(m_listrestrict).name, MYCONF);
  432. errors++;
  433. }
  434. *errs = errors;
  435. return errors ? -1 : 1;
  436. }
  437. // "Run" the config (everything should be valid at this point)
  438. int listrestrict_configrun(ConfigFile *cf, ConfigEntry *ce, int type) {
  439. ConfigEntry *cep, *cep2; // For looping through our bl0cc, nested
  440. restrictExcept *exLast = NULL; // Initialise to NULL so the loop requires minimal l0gic
  441. fakeChannel *fchanLast = NULL;
  442. restrictExcept **exEntry = &exceptList; // Hecks so the ->next chain stays intact
  443. fakeChannel **fchanEntry = &fakechanList;
  444. exceptType etype = LRE_UNKNOWN; // Just a lil' default =]
  445. char fchanName[BUFSIZE]; // Array instead of pointer for cleaning channel names, cuz we shouldn't modify a strdup()'d pointer directly ;]
  446. char fchanTopic[BUFSIZE];
  447. int fchanUsers;
  448. int fchanGlinem;
  449. size_t tlen;
  450. // Since we'll add a new top-level block to unrealircd.conf, need to filter on CONFIG_MAIN lmao
  451. if(type != CONFIG_MAIN)
  452. return 0; // Returning 0 means idgaf bout dis
  453. // Check for valid config entries first
  454. if(!ce || !ce->ce_varname)
  455. return 0;
  456. // If it isn't our bl0cc, idc
  457. if(strcmp(ce->ce_varname, MYCONF))
  458. return 0;
  459. // Loop dat shyte fam
  460. for(cep = ce->ce_entries; cep; cep = cep->ce_next) {
  461. // Do we even have a valid name l0l?
  462. if(!cep->ce_varname)
  463. continue; // Next iteration imo tbh
  464. if(cep->ce_vardata && !strcmp(cep->ce_varname, "connectdelay")) {
  465. muhDelay = atoi(cep->ce_vardata);
  466. continue;
  467. }
  468. if(cep->ce_vardata && !strcmp(cep->ce_varname, "needauth")) {
  469. needAuth = atoi(cep->ce_vardata);
  470. continue;
  471. }
  472. if(cep->ce_vardata && !strcmp(cep->ce_varname, "authisenough")) {
  473. authIsEnough = atoi(cep->ce_vardata);
  474. continue;
  475. }
  476. if(cep->ce_vardata && !strcmp(cep->ce_varname, "fakechans")) {
  477. fakeChans = atoi(cep->ce_vardata);
  478. continue;
  479. }
  480. if(cep->ce_vardata && !strcmp(cep->ce_varname, "glinetime")) {
  481. glineTime = config_checkval(cep->ce_vardata, CFG_TIME);
  482. continue;
  483. }
  484. if(!strcmp(cep->ce_varname, "exceptions")) {
  485. // Loop 'em
  486. for(cep2 = cep->ce_entries; cep2; cep2 = cep2->ce_next) {
  487. if(!cep2->ce_varname || !cep2->ce_vardata) // Sanity checks imo
  488. continue; // Next iteration imo tbh
  489. if(!strcmp(cep2->ce_varname, "all"))
  490. etype = LRE_ALL;
  491. else if(!strcmp(cep2->ce_varname, "connect"))
  492. etype = LRE_CONNECT;
  493. else if(!strcmp(cep2->ce_varname, "auth"))
  494. etype = LRE_AUTH;
  495. else if(!strcmp(cep2->ce_varname, "fakechans"))
  496. etype = LRE_FAKECHANS;
  497. // Allocate mem0ry for the current entry
  498. *exEntry = malloc(sizeof(restrictExcept));
  499. // Allocate/initialise shit here
  500. (*exEntry)->mask = strdup(cep2->ce_vardata);
  501. (*exEntry)->next = NULL;
  502. // Copy that shit fam
  503. (*exEntry)->type = etype;
  504. // Premium linked list fam
  505. if(exLast)
  506. exLast->next = *exEntry;
  507. exLast = *exEntry;
  508. exEntry = &(*exEntry)->next;
  509. }
  510. continue;
  511. }
  512. if(!strcmp(cep->ce_varname, "fakechannel")) {
  513. // Gotta reset values imo
  514. fchanName[0] = '\0';
  515. fchanTopic[0] = '\0';
  516. fchanUsers = 0;
  517. fchanGlinem = 0;
  518. // Loop through parameters of a single fakechan
  519. for(cep2 = cep->ce_entries; cep2; cep2 = cep2->ce_next) {
  520. if(!cep2->ce_varname || !cep2->ce_vardata) // Sanity checks imo
  521. continue; // Next iteration imo tbh
  522. if(!strcmp(cep2->ce_varname, "name")) {
  523. strlcpy(fchanName, cep2->ce_vardata, sizeof(fchanName));
  524. clean_channelname(fchanName);
  525. continue;
  526. }
  527. if(!strcmp(cep2->ce_varname, "topic")) {
  528. if((tlen = strlen(cep2->ce_vardata)) > 0) {
  529. if(tlen > TOPICLEN)
  530. config_warn("%s:%i: %s::fakechannel::%s exceeds maximum allowed length (%d chars), truncating it", cep2->ce_fileptr->cf_filename, cep2->ce_varlinenum, MYCONF, cep2->ce_varname, TOPICLEN);
  531. strlcpy(fchanTopic, cep2->ce_vardata, TOPICLEN + 1);
  532. }
  533. continue;
  534. }
  535. if(!strcmp(cep2->ce_varname, "users")) {
  536. fchanUsers = atoi(cep2->ce_vardata);
  537. continue;
  538. }
  539. if(!strcmp(cep2->ce_varname, "gline")) {
  540. fchanGlinem = atoi(cep2->ce_vardata);
  541. continue;
  542. }
  543. }
  544. // Make sure we don't overallocate shit (no topic/users specified is all0wed, only name is required)
  545. if(!fchanName[0])
  546. continue;
  547. // Allocate mem0ry for the current entry
  548. *fchanEntry = malloc(sizeof(fakeChannel));
  549. (*fchanEntry)->name = strdup(fchanName);
  550. (*fchanEntry)->topic = (fchanTopic[0] ? strdup(fchanTopic) : strdup(FCHAN_DEFTOPIC));
  551. (*fchanEntry)->users = (fchanUsers <= 0 ? FCHAN_DEFUSERS : fchanUsers);
  552. (*fchanEntry)->glinem = fchanGlinem;
  553. (*fchanEntry)->next = NULL;
  554. if(fchanLast)
  555. fchanLast->next = *fchanEntry;
  556. fchanLast = *fchanEntry;
  557. fchanEntry = &(*fchanEntry)->next;
  558. continue;
  559. }
  560. }
  561. return 1; // We good
  562. }
  563. int listrestrict_rehash(void) {
  564. // Reset config defaults
  565. muhDelay = 0;
  566. needAuth = 0;
  567. fakeChans = 0;
  568. authIsEnough = 0;
  569. glineTime = 86400;
  570. conf_fakechans = 0;
  571. return HOOK_CONTINUE;
  572. }