m_listrestrict.c 23 KB

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