m_listrestrict.c 23 KB

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