userchan.c 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026
  1. /*
  2. * userchan.c -- part of channels.mod
  3. *
  4. */
  5. extern struct cmd_pass *cmdpass;
  6. struct chanuserrec *get_chanrec(struct userrec *u, char *chname)
  7. {
  8. for (register struct chanuserrec *ch = u->chanrec; ch; ch = ch->next)
  9. if (!rfc_casecmp(ch->channel, chname))
  10. return ch;
  11. return NULL;
  12. }
  13. struct chanuserrec *add_chanrec(struct userrec *u, char *chname)
  14. {
  15. if (findchan_by_dname(chname)) {
  16. struct chanuserrec *ch = (struct chanuserrec *) my_calloc(1, sizeof(struct chanuserrec));
  17. ch->next = u->chanrec;
  18. u->chanrec = ch;
  19. ch->info = NULL;
  20. ch->flags = 0;
  21. ch->laston = 0;
  22. strncpy(ch->channel, chname, 81);
  23. ch->channel[80] = 0;
  24. if (!noshare)
  25. shareout("+cr %s %s\n", u->handle, chname);
  26. return ch;
  27. }
  28. return NULL;
  29. }
  30. void add_chanrec_by_handle(struct userrec *bu, char *hand, char *chname)
  31. {
  32. struct userrec *u = get_user_by_handle(bu, hand);
  33. if (!u)
  34. return;
  35. if (!get_chanrec(u, chname))
  36. add_chanrec(u, chname);
  37. }
  38. void get_handle_chaninfo(char *handle, char *chname, char *s)
  39. {
  40. struct userrec *u = get_user_by_handle(userlist, handle);
  41. if (u == NULL) {
  42. s[0] = 0;
  43. return;
  44. }
  45. struct chanuserrec *ch = get_chanrec(u, chname);
  46. if (ch == NULL) {
  47. s[0] = 0;
  48. return;
  49. }
  50. if (ch->info == NULL) {
  51. s[0] = 0;
  52. return;
  53. }
  54. strcpy(s, ch->info);
  55. return;
  56. }
  57. void set_handle_chaninfo(struct userrec *bu, char *handle, char *chname, char *info)
  58. {
  59. struct userrec *u = get_user_by_handle(bu, handle);
  60. if (!u)
  61. return;
  62. struct chanuserrec *ch = get_chanrec(u, chname);
  63. if (!ch) {
  64. add_chanrec_by_handle(bu, handle, chname);
  65. ch = get_chanrec(u, chname);
  66. }
  67. if (info) {
  68. if (strlen(info) > 80)
  69. info[80] = 0;
  70. }
  71. if (ch->info != NULL)
  72. free(ch->info);
  73. if (info && info[0]) {
  74. ch->info = strdup(info);
  75. } else
  76. ch->info = NULL;
  77. if ((!noshare) && (bu == userlist)) {
  78. shareout("chchinfo %s %s %s\n", handle, chname, info ? info : "");
  79. }
  80. }
  81. void del_chanrec(struct userrec *u, char *chname)
  82. {
  83. struct chanuserrec *ch = u->chanrec, *lst = NULL;
  84. while (ch) {
  85. if (!rfc_casecmp(chname, ch->channel)) {
  86. if (lst == NULL)
  87. u->chanrec = ch->next;
  88. else
  89. lst->next = ch->next;
  90. if (ch->info != NULL)
  91. free(ch->info);
  92. free(ch);
  93. if (!noshare)
  94. shareout("-cr %s %s\n", u->handle, chname);
  95. return;
  96. }
  97. lst = ch;
  98. ch = ch->next;
  99. }
  100. }
  101. void set_handle_laston(char *chan, struct userrec *u, time_t n)
  102. {
  103. if (!u)
  104. return;
  105. touch_laston(u, chan, n);
  106. struct chanuserrec *ch = get_chanrec(u, chan);
  107. if (!ch)
  108. return;
  109. ch->laston = n;
  110. }
  111. /* Is this mask sticky?
  112. */
  113. int u_sticky_mask(maskrec *u, char *uhost)
  114. {
  115. for (; u; u = u->next)
  116. if (!rfc_casecmp(u->mask, uhost))
  117. return (u->flags & MASKREC_STICKY);
  118. return 0;
  119. }
  120. /* Set sticky attribute for a mask.
  121. */
  122. int u_setsticky_mask(struct chanset_t *chan, maskrec *u, char *uhost, bool sticky, const char *botcmd)
  123. {
  124. int j;
  125. if (str_isdigit(uhost))
  126. j = atoi(uhost);
  127. else
  128. j = (-1);
  129. while(u) {
  130. if (j >= 0)
  131. j--;
  132. if (!j || ((j < 0) && !rfc_casecmp(u->mask, uhost))) {
  133. if (sticky > 0)
  134. u->flags |= MASKREC_STICKY;
  135. else if (!sticky)
  136. u->flags &= ~MASKREC_STICKY;
  137. else /* We don't actually want to change, just skip over */
  138. return 0;
  139. if (!j)
  140. strcpy(uhost, u->mask);
  141. if (!noshare)
  142. shareout("%s %s %d %s\n", botcmd, uhost, sticky, (chan) ? chan->dname : "");
  143. return 1;
  144. }
  145. u = u->next;
  146. }
  147. if (j >= 0)
  148. return -j;
  149. return 0;
  150. }
  151. /* Merge of u_equals_ban(), u_equals_exempt() and u_equals_invite().
  152. *
  153. * Returns:
  154. * 0 not a ban
  155. * 1 temporary ban
  156. * 2 perm ban
  157. */
  158. int u_equals_mask(maskrec *u, char *mask)
  159. {
  160. for (; u; u = u->next)
  161. if (!rfc_casecmp(u->mask, mask)) {
  162. if (u->flags & MASKREC_PERM)
  163. return 2;
  164. else
  165. return 1;
  166. }
  167. return 0;
  168. }
  169. bool u_match_mask(maskrec *rec, char *mask)
  170. {
  171. for (; rec; rec = rec->next)
  172. if (wild_match(rec->mask, mask))
  173. return 1;
  174. return 0;
  175. }
  176. int u_delmask(char type, struct chanset_t *c, char *who, int doit)
  177. {
  178. int j, i = 0;
  179. char temp[256] = "";
  180. maskrec **u = NULL, *t;
  181. if (type == 'b')
  182. u = c ? &c->bans : &global_bans;
  183. if (type == 'e')
  184. u = c ? &c->exempts : &global_exempts;
  185. if (type == 'I')
  186. u = c ? &c->invites : &global_invites;
  187. if (!strchr(who, '!') && str_isdigit(who)) {
  188. j = atoi(who);
  189. j--;
  190. for (; (*u) && j; u = &((*u)->next), j--);
  191. if (*u) {
  192. strlcpy(temp, (*u)->mask, sizeof temp);
  193. i = 1;
  194. } else
  195. return -j - 1;
  196. } else {
  197. /* Find matching host, if there is one */
  198. for (; *u && !i; u = &((*u)->next))
  199. if (!rfc_casecmp((*u)->mask, who)) {
  200. strlcpy(temp, who, sizeof temp);
  201. i = 1;
  202. break;
  203. }
  204. if (!*u)
  205. return 0;
  206. }
  207. if (i && doit) {
  208. if (!noshare) {
  209. char *mask = str_escape(temp, ':', '\\');
  210. if (mask) {
  211. /* Distribute chan bans differently */
  212. if (c)
  213. shareout("-mc %c %s %s\n", type, c->dname, mask);
  214. else
  215. shareout("-m %c %s\n", type, mask);
  216. free(mask);
  217. }
  218. }
  219. if (lastdeletedmask)
  220. free(lastdeletedmask);
  221. lastdeletedmask = (*u)->mask;
  222. if ((*u)->desc)
  223. free((*u)->desc);
  224. if ((*u)->user)
  225. free((*u)->user);
  226. t = *u;
  227. *u = (*u)->next;
  228. free(t);
  229. }
  230. return i;
  231. }
  232. /* Note: If first char of note is '*' it's a sticky mask.
  233. */
  234. bool u_addmask(char type, struct chanset_t *chan, char *who, char *from, char *note, time_t expire_time, int flags)
  235. {
  236. char host[1024] = "", s[1024] = "";
  237. maskrec *p = NULL, *l = NULL, **u = NULL;
  238. if (type == 'b')
  239. u = chan ? &chan->bans : &global_bans;
  240. if (type == 'e')
  241. u = chan ? &chan->exempts : &global_exempts;
  242. if (type == 'I')
  243. u = chan ? &chan->invites : &global_invites;
  244. strcpy(host, who);
  245. /* Choke check: fix broken bans (must have '!' and '@') */
  246. if ((strchr(host, '!') == NULL) && (strchr(host, '@') == NULL))
  247. strcat(host, "!*@*");
  248. else if (strchr(host, '@') == NULL)
  249. strcat(host, "@*");
  250. else if (strchr(host, '!') == NULL) {
  251. char *i = strchr(host, '@');
  252. strcpy(s, i);
  253. *i = 0;
  254. strcat(host, "!*");
  255. strcat(host, s);
  256. }
  257. if (conf.bot->hub)
  258. simple_sprintf(s, "%s!%s@%s", origbotname, botuser, conf.bot->net.host);
  259. else
  260. simple_sprintf(s, "%s!%s", botname, botuserhost);
  261. if (s[0] && type == 'b' && wild_match(host, s)) {
  262. putlog(LOG_MISC, "*", IRC_IBANNEDME);
  263. return 0;
  264. }
  265. if (expire_time == now)
  266. return 1;
  267. for (l = *u; l; l = l->next)
  268. if (!rfc_casecmp(l->mask, host)) {
  269. p = l;
  270. break;
  271. }
  272. /* It shouldn't expire and be sticky also */
  273. if (note[0] == '*') {
  274. flags |= MASKREC_STICKY;
  275. note++;
  276. }
  277. if ((expire_time == 0L) || (flags & MASKREC_PERM)) {
  278. flags |= MASKREC_PERM;
  279. expire_time = 0L;
  280. }
  281. if (p == NULL) {
  282. p = (maskrec *) my_calloc(1, sizeof(maskrec));
  283. p->next = *u;
  284. *u = p;
  285. }
  286. else {
  287. free( p->mask );
  288. free( p->user );
  289. free( p->desc );
  290. }
  291. p->expire = expire_time;
  292. p->added = now;
  293. p->lastactive = 0;
  294. p->flags = flags;
  295. p->mask = strdup(host);
  296. p->user = strdup(from);
  297. p->desc = strdup(note);
  298. if (!noshare) {
  299. char *mask = str_escape(host, ':', '\\');
  300. if (mask) {
  301. if (!chan)
  302. shareout("+m %c %s %lu %s%s %s %s\n",
  303. type, mask, expire_time - now,
  304. (flags & MASKREC_STICKY) ? "s" : "",
  305. (flags & MASKREC_PERM) ? "p" : "-", from, note);
  306. else
  307. shareout("+mc %c %s %lu %s %s%s %s %s\n",
  308. type, mask, expire_time - now, chan->dname,
  309. (flags & MASKREC_STICKY) ? "s" : "",
  310. (flags & MASKREC_PERM) ? "p" : "-", from, note);
  311. free(mask);
  312. }
  313. }
  314. return 1;
  315. }
  316. /* Take host entry from ban list and display it ban-style.
  317. */
  318. static void display_mask(const char type, int idx, int number, maskrec *mask, struct chanset_t *chan, bool show_inact)
  319. {
  320. char dates[81] = "", s[41] = "";
  321. const char *str_type = (type == 'b' ? "BAN" : type == 'e' ? "EXEMPT" : "INVITE");
  322. if (mask->added) {
  323. daysago(now, mask->added, s);
  324. simple_sprintf(dates, "%s %s", MODES_CREATED, s);
  325. if (mask->added < mask->lastactive) {
  326. strcat(dates, ", ");
  327. strcat(dates, MODES_LASTUSED);
  328. strcat(dates, " ");
  329. daysago(now, mask->lastactive, s);
  330. strcat(dates, s);
  331. }
  332. } else
  333. dates[0] = 0;
  334. if (mask->flags & MASKREC_PERM)
  335. strcpy(s, "(perm)");
  336. else {
  337. char s1[41] = "";
  338. days(mask->expire, now, s1);
  339. simple_sprintf(s, "(expires %s)", s1);
  340. }
  341. if (mask->flags & MASKREC_STICKY)
  342. strcat(s, " (sticky)");
  343. /* always show mask on hubs */
  344. if (!chan || ischanmask(type, chan, mask->mask) || conf.bot->hub) {
  345. if (number >= 0) {
  346. dprintf(idx, " [%3d] %s %s\n", number, mask->mask, s);
  347. } else {
  348. dprintf(idx, "%s: %s %s\n", str_type, mask->mask, s);
  349. }
  350. } else if (show_inact) {
  351. if (number >= 0) {
  352. dprintf(idx, "! [%3d] %s %s\n", number, mask->mask, s);
  353. } else {
  354. dprintf(idx, "%s (inactive): %s %s\n", str_type, mask->mask, s);
  355. }
  356. } else
  357. return;
  358. dprintf(idx, " %s: %s\n", mask->user, mask->desc);
  359. if (dates[0])
  360. dprintf(idx, " %s\n", dates);
  361. }
  362. static void tell_masks(const char type, int idx, bool show_inact, char *match)
  363. {
  364. int k = 1;
  365. char *chname = NULL;
  366. const char *str_type = (type == 'b' ? "ban" : type == 'e' ? "exempt" : "invite");
  367. maskrec *global_masks = (type == 'b' ? global_bans : type == 'e' ? global_exempts : global_invites);
  368. struct chanset_t *chan = NULL;
  369. maskrec *mr = NULL;
  370. /* Was a channel given? */
  371. if (match[0]) {
  372. chname = newsplit(&match);
  373. if (chname[0] && (strchr(CHANMETA, chname[0]))) {
  374. chan = findchan_by_dname(chname);
  375. if (!chan) {
  376. dprintf(idx, "%s.\n", CHAN_NOSUCH);
  377. return;
  378. }
  379. } else
  380. match = chname;
  381. }
  382. /* don't return here, we want to show global masks even if no chan */
  383. if (!chan && !(chan = findchan_by_dname(dcc[idx].u.chat->con_chan)) && !(chan = chanset))
  384. chan = NULL;
  385. get_user_flagrec(dcc[idx].user, &user, chan->dname);
  386. if (privchan(user, chan, PRIV_OP)) {
  387. dprintf(idx, "%s.\n", CHAN_NOSUCH);
  388. return;
  389. }
  390. if (chan && show_inact)
  391. dprintf(idx, "Global %ss: (! = not active on %s)\n", str_type, chan->dname);
  392. else
  393. dprintf(idx, "Global %ss:\n", str_type);
  394. for (mr = global_masks; mr; mr = mr->next) {
  395. if (match[0]) {
  396. if ((wild_match(match, mr->mask)) ||
  397. (wild_match(match, mr->desc)) ||
  398. (wild_match(match, mr->user)))
  399. display_mask(type, idx, k, mr, chan, 1);
  400. k++;
  401. } else
  402. display_mask(type, idx, k++, mr, chan, show_inact);
  403. }
  404. if (chan) {
  405. maskrec *chan_masks = (type == 'b' ? chan->bans : type == 'e' ? chan->exempts : chan->invites);
  406. if (show_inact)
  407. dprintf(idx, "Channel %ss for %s: (! = not active on, * = not placed by bot)\n", str_type, chan->dname);
  408. else
  409. dprintf(idx, "Channel %ss for %s: (* = not placed by bot)\n", str_type, chan->dname);
  410. for (mr = chan_masks; mr; mr = mr->next) {
  411. if (match[0]) {
  412. if ((wild_match(match, mr->mask)) ||
  413. (wild_match(match, mr->desc)) ||
  414. (wild_match(match, mr->user)))
  415. display_mask(type, idx, k, mr, chan, 1);
  416. k++;
  417. } else
  418. display_mask(type, idx, k++, mr, chan, show_inact);
  419. }
  420. if (chan->status & CHAN_ACTIVE) {
  421. masklist *ml = NULL;
  422. masklist *channel_list = (type == 'b' ? chan->channel.ban : type == 'e' ? chan->channel.exempt : chan->channel.invite);
  423. char s[UHOSTLEN] = "", *s1 = NULL, *s2 = NULL, fill[256] = "";
  424. int min, sec;
  425. for (ml = channel_list; ml && ml->mask[0]; ml = ml->next) {
  426. if ((!u_equals_mask(global_masks, ml->mask)) &&
  427. (!u_equals_mask(chan_masks, ml->mask))) {
  428. strcpy(s, ml->who);
  429. s2 = s;
  430. s1 = splitnick(&s2);
  431. if (s1[0])
  432. simple_sprintf(fill, "%s (%s!%s)", ml->mask, s1, s2);
  433. else
  434. simple_sprintf(fill, "%s (server %s)", ml->mask, s2);
  435. if (ml->timer != 0) {
  436. min = (now - ml->timer) / 60;
  437. sec = (now - ml->timer) - (min * 60);
  438. sprintf(s, " (active %02d:%02d)", min, sec);
  439. strcat(fill, s);
  440. }
  441. if ((!match[0]) || (wild_match(match, ml->mask)))
  442. dprintf(idx, "* [%3d] %s\n", k, fill);
  443. k++;
  444. }
  445. }
  446. }
  447. }
  448. if (k == 1)
  449. dprintf(idx, "(There are no %ss, permanent or otherwise.)\n", str_type);
  450. if ((!show_inact) && (!match[0]))
  451. dprintf(idx, "Use '%ss all' to see the total list.\n", str_type);
  452. }
  453. bool write_config(FILE *f, int idx)
  454. {
  455. putlog(LOG_DEBUG, "@", "Writing config entries...");
  456. if (lfprintf(f, CONFIG_NAME " - -\n") == EOF) /* Daemus */
  457. return 0;
  458. for (int i = 0; i < cfg_count; i++) {
  459. if ((cfg[i]->flags & CFGF_GLOBAL) && (cfg[i]->gdata)) {
  460. if (lfprintf(f, "@ %s %s\n", cfg[i]->name, cfg[i]->gdata ? cfg[i]->gdata : "") == EOF)
  461. return 0;
  462. }
  463. }
  464. for (struct cmd_pass *cp = cmdpass; cp; cp = cp->next)
  465. if (lfprintf(f, "- %s %s\n", cp->name, cp->pass) == EOF)
  466. return 0;
  467. return 1;
  468. }
  469. /* Write the ban lists and the ignore list to a file.
  470. */
  471. bool write_bans(FILE *f, int idx)
  472. {
  473. if (global_ign)
  474. if (lfprintf(f, IGNORE_NAME " - -\n") == EOF) /* Daemus */
  475. return 0;
  476. char *mask = NULL;
  477. for (struct igrec *i = global_ign; i; i = i->next) {
  478. mask = str_escape(i->igmask, ':', '\\');
  479. if (!mask ||
  480. lfprintf(f, "- %s:%s%lu:%s:%lu:%s\n", mask,
  481. (i->flags & IGREC_PERM) ? "+" : "", i->expire,
  482. i->user ? i->user : conf.bot->nick, i->added,
  483. i->msg ? i->msg : "") == EOF) {
  484. if (mask)
  485. free(mask);
  486. return 0;
  487. }
  488. free(mask);
  489. }
  490. if (global_bans)
  491. if (lfprintf(f, BAN_NAME " - -\n") == EOF) /* Daemus */
  492. return 0;
  493. maskrec *b = NULL;
  494. for (b = global_bans; b; b = b->next) {
  495. mask = str_escape(b->mask, ':', '\\');
  496. if (!mask ||
  497. lfprintf(f, "- %s:%s%lu%s:+%lu:%lu:%s:%s\n", mask,
  498. (b->flags & MASKREC_PERM) ? "+" : "", b->expire,
  499. (b->flags & MASKREC_STICKY) ? "*" : "", b->added,
  500. b->lastactive, b->user ? b->user : conf.bot->nick,
  501. b->desc ? b->desc : "requested") == EOF) {
  502. if (mask)
  503. free(mask);
  504. return 0;
  505. }
  506. free(mask);
  507. }
  508. for (struct chanset_t *chan = chanset; chan; chan = chan->next) {
  509. if (lfprintf(f, "::%s bans\n", chan->dname) == EOF)
  510. return 0;
  511. for (b = chan->bans; b; b = b->next) {
  512. mask = str_escape(b->mask, ':', '\\');
  513. if (!mask ||
  514. lfprintf(f, "- %s:%s%lu%s:+%lu:%lu:%s:%s\n", mask,
  515. (b->flags & MASKREC_PERM) ? "+" : "", b->expire,
  516. (b->flags & MASKREC_STICKY) ? "*" : "", b->added,
  517. b->lastactive, b->user ? b->user : conf.bot->nick,
  518. b->desc ? b->desc : "requested") == EOF) {
  519. if (mask)
  520. free(mask);
  521. return 0;
  522. }
  523. free(mask);
  524. }
  525. }
  526. return 1;
  527. }
  528. /* Write the exemptlists to a file.
  529. */
  530. bool write_exempts(FILE *f, int idx)
  531. {
  532. if (global_exempts)
  533. if (lfprintf(f, EXEMPT_NAME " - -\n") == EOF) /* Daemus */
  534. return 0;
  535. maskrec *e = NULL;
  536. char *mask = NULL;
  537. for (e = global_exempts; e; e = e->next) {
  538. mask = str_escape(e->mask, ':', '\\');
  539. if (!mask ||
  540. lfprintf(f, "%s %s:%s%lu%s:+%lu:%lu:%s:%s\n", "%", mask,
  541. (e->flags & MASKREC_PERM) ? "+" : "", e->expire,
  542. (e->flags & MASKREC_STICKY) ? "*" : "", e->added,
  543. e->lastactive, e->user ? e->user : conf.bot->nick,
  544. e->desc ? e->desc : "requested") == EOF) {
  545. if (mask)
  546. free(mask);
  547. return 0;
  548. }
  549. free(mask);
  550. }
  551. for (struct chanset_t *chan = chanset;chan ;chan = chan->next) {
  552. if (lfprintf(f, "&&%s exempts\n", chan->dname) == EOF)
  553. return 0;
  554. for (e = chan->exempts; e; e = e->next) {
  555. mask = str_escape(e->mask, ':', '\\');
  556. if (!mask ||
  557. lfprintf(f,"%s %s:%s%lu%s:+%lu:%lu:%s:%s\n","%", mask,
  558. (e->flags & MASKREC_PERM) ? "+" : "", e->expire,
  559. (e->flags & MASKREC_STICKY) ? "*" : "", e->added,
  560. e->lastactive, e->user ? e->user : conf.bot->nick,
  561. e->desc ? e->desc : "requested") == EOF) {
  562. if (mask)
  563. free(mask);
  564. return 0;
  565. }
  566. free(mask);
  567. }
  568. }
  569. return 1;
  570. }
  571. /* Write the invitelists to a file.
  572. */
  573. bool write_invites(FILE *f, int idx)
  574. {
  575. if (global_invites)
  576. if (lfprintf(f, INVITE_NAME " - -\n") == EOF) /* Daemus */
  577. return 0;
  578. maskrec *ir = NULL;
  579. char *mask = NULL;
  580. for (ir = global_invites; ir; ir = ir->next) {
  581. mask = str_escape(ir->mask, ':', '\\');
  582. if (!mask ||
  583. lfprintf(f,"@ %s:%s%lu%s:+%lu:%lu:%s:%s\n", mask,
  584. (ir->flags & MASKREC_PERM) ? "+" : "", ir->expire,
  585. (ir->flags & MASKREC_STICKY) ? "*" : "", ir->added,
  586. ir->lastactive, ir->user ? ir->user : conf.bot->nick,
  587. ir->desc ? ir->desc : "requested") == EOF) {
  588. if (mask)
  589. free(mask);
  590. return 0;
  591. }
  592. free(mask);
  593. }
  594. for (struct chanset_t *chan = chanset; chan; chan = chan->next) {
  595. if (lfprintf(f, "$$%s invites\n", chan->dname) == EOF)
  596. return 0;
  597. for (ir = chan->invites; ir; ir = ir->next) {
  598. mask = str_escape(ir->mask, ':', '\\');
  599. if (!mask ||
  600. lfprintf(f,"@ %s:%s%lu%s:+%lu:%lu:%s:%s\n", mask,
  601. (ir->flags & MASKREC_PERM) ? "+" : "", ir->expire,
  602. (ir->flags & MASKREC_STICKY) ? "*" : "", ir->added,
  603. ir->lastactive, ir->user ? ir->user : conf.bot->nick,
  604. ir->desc ? ir->desc : "requested") == EOF) {
  605. if (mask)
  606. free(mask);
  607. return 0;
  608. }
  609. free(mask);
  610. }
  611. }
  612. return 1;
  613. }
  614. /* Write the channels to the userfile
  615. */
  616. bool write_chans(FILE *f, int idx)
  617. {
  618. putlog(LOG_DEBUG, "*", "Writing channels..");
  619. if (lfprintf(f, CHANS_NAME " - -\n") == EOF) /* Daemus */
  620. return 0;
  621. char w[1024] = "";
  622. for (struct chanset_t *chan = chanset; chan; chan = chan->next) {
  623. char inactive = 0;
  624. putlog(LOG_DEBUG, "*", "writing channel %s to userfile..", chan->dname);
  625. get_mode_protect(chan, w);
  626. /* if a bot should explicitly NOT join, just set it +inactive ... */
  627. if (idx >= 0 && !botshouldjoin(dcc[idx].user, chan))
  628. inactive = '+';
  629. /* ... otherwise give the bot the *actual* setting */
  630. else
  631. inactive = PLSMNS(channel_inactive(chan));
  632. if (lfprintf(f, "\
  633. + channel add %s { chanmode { %s } addedby %s addedts %lu idle-kick %d \
  634. bad-cookie %d manop %d mdop %d mop %d \
  635. limit %d stopnethack-mode %d revenge-mode %d flood-chan %d:%lu \
  636. flood-ctcp %d:%lu flood-join %d:%lu flood-kick %d:%lu flood-deop %d:%lu \
  637. flood-nick %d:%lu closed-ban %d closed-invite %d closed-private %d ban-time %lu \
  638. exempt-time %lu invite-time %lu \
  639. %cenforcebans %cdynamicbans %cuserban %cbitch %cprotectops %crevenge \
  640. %crevengebot %cprivate %ccycle %cinactive %cdynamicexempts %cuserexempts \
  641. %cdynamicinvites %cuserinvites %cnodesynch %cclosed %cvoice \
  642. %cfastop %cautoop %cbotbitch %c%s}\n",
  643. chan->dname,
  644. w,
  645. chan->added_by,
  646. chan->added_ts,
  647. /* Chanchar template
  648. * temp,
  649. * also include temp %s in dprintf.
  650. */
  651. chan->idle_kick, /* idle-kick 0 is same as dont-idle-kick (lcode)*/
  652. chan->bad_cookie,
  653. chan->manop,
  654. chan->mdop,
  655. chan->mop,
  656. chan->limitraise,
  657. chan->stopnethack_mode,
  658. chan->revenge_mode,
  659. chan->flood_pub_thr, chan->flood_pub_time,
  660. chan->flood_ctcp_thr, chan->flood_ctcp_time,
  661. chan->flood_join_thr, chan->flood_join_time,
  662. chan->flood_kick_thr, chan->flood_kick_time,
  663. chan->flood_deop_thr, chan->flood_deop_time,
  664. chan->flood_nick_thr, chan->flood_nick_time,
  665. chan->closed_ban,
  666. /* Chanint template
  667. * chan->temp,
  668. * also include temp %d in dprintf
  669. */
  670. chan->closed_invite,
  671. chan->closed_private,
  672. chan->ban_time,
  673. chan->exempt_time,
  674. chan->invite_time,
  675. PLSMNS(channel_enforcebans(chan)),
  676. PLSMNS(channel_dynamicbans(chan)),
  677. PLSMNS(!channel_nouserbans(chan)),
  678. PLSMNS(channel_bitch(chan)),
  679. PLSMNS(channel_protectops(chan)),
  680. PLSMNS(channel_revenge(chan)),
  681. PLSMNS(channel_revengebot(chan)),
  682. PLSMNS(channel_privchan(chan)),
  683. PLSMNS(channel_cycle(chan)),
  684. inactive,
  685. PLSMNS(channel_dynamicexempts(chan)),
  686. PLSMNS(!channel_nouserexempts(chan)),
  687. PLSMNS(channel_dynamicinvites(chan)),
  688. PLSMNS(!channel_nouserinvites(chan)),
  689. PLSMNS(channel_nodesynch(chan)),
  690. PLSMNS(channel_closed(chan)),
  691. PLSMNS(channel_voice(chan)),
  692. PLSMNS(channel_fastop(chan)),
  693. PLSMNS(channel_autoop(chan)),
  694. PLSMNS(channel_botbitch(chan)),
  695. have_take ? PLSMNS(channel_take(chan)) : 0,
  696. have_take ? "take " : " "
  697. /* Chanflag template
  698. * also include a %ctemp above.
  699. * PLSMNS(channel_temp(chan)),
  700. */
  701. ) == EOF)
  702. return 0;
  703. }
  704. return 1;
  705. }
  706. void channels_writeuserfile()
  707. {
  708. char s[1024] = "";
  709. FILE *f = NULL;
  710. int ret = 0;
  711. putlog(LOG_DEBUG, "@", "Writing channel/ban/exempt/invite entries.");
  712. simple_sprintf(s, "%s~new", userfile);
  713. f = fopen(s, "a");
  714. if (f) {
  715. ret = write_chans(f, -1);
  716. ret += write_config(f, -1);
  717. ret += write_bans(f, -1);
  718. ret += write_exempts(f, -1);
  719. ret += write_invites(f, -1);
  720. fclose(f);
  721. }
  722. if (ret < 5)
  723. putlog(LOG_MISC, "*", USERF_ERRWRITE);
  724. }
  725. /* Expire mask originally set by `who' on `chan'?
  726. *
  727. * We might not want to expire masks in all cases, as other bots
  728. * often tend to immediately reset masks they've listed in their
  729. * internal ban list, making it quite senseless for us to remove
  730. * them in the first place.
  731. *
  732. * Returns 1 if a mask on `chan' by `who' may be expired and 0 if
  733. * not.
  734. */
  735. bool expired_mask(struct chanset_t *chan, char *who)
  736. {
  737. memberlist *m = NULL, *m2 = NULL;
  738. char buf[UHOSTLEN] = "", *snick = NULL, *sfrom = NULL;
  739. struct userrec *u = NULL;
  740. strcpy(buf, who);
  741. sfrom = buf;
  742. snick = splitnick(&sfrom);
  743. if (!snick[0])
  744. return 1;
  745. m = ismember(chan, snick);
  746. if (!m)
  747. for (m2 = chan->channel.member; m2 && m2->nick[0]; m2 = m2->next)
  748. if (!egg_strcasecmp(sfrom, m2->userhost)) {
  749. m = m2;
  750. break;
  751. }
  752. if (!m || !chan_hasop(m) || !rfc_casecmp(m->nick, botname))
  753. return 1;
  754. /* At this point we know the person/bot who set the mask is currently
  755. * present in the channel and has op.
  756. */
  757. if (m->user)
  758. u = m->user;
  759. else {
  760. simple_sprintf(buf, "%s!%s", m->nick, m->userhost);
  761. u = get_user_by_host(buf);
  762. }
  763. /* Do not expire masks set by bots. */
  764. if (u && u->bot)
  765. return 0;
  766. else
  767. return 1;
  768. }
  769. /* Check for expired timed-bans.
  770. */
  771. static void check_expired_bans(void)
  772. {
  773. maskrec *u = NULL, *u2 = NULL;
  774. struct chanset_t *chan = NULL;
  775. masklist *b = NULL;
  776. for (u = global_bans; u; u = u2) {
  777. u2 = u->next;
  778. if (!(u->flags & MASKREC_PERM) && (now >= u->expire)) {
  779. putlog(LOG_MISC, "*", "%s %s (%s)", BANS_NOLONGER, u->mask, MISC_EXPIRED);
  780. for (chan = chanset; chan; chan = chan->next)
  781. for (b = chan->channel.ban; b->mask[0]; b = b->next)
  782. if (!rfc_casecmp(b->mask, u->mask) && expired_mask(chan, b->who) && b->timer != now) {
  783. if (!conf.bot->hub)
  784. add_mode(chan, '-', 'b', u->mask);
  785. b->timer = now;
  786. }
  787. u_delmask('b', NULL, u->mask, 1);
  788. }
  789. }
  790. /* Check for specific channel-domain bans expiring */
  791. for (chan = chanset; chan; chan = chan->next) {
  792. for (u = chan->bans; u; u = u2) {
  793. u2 = u->next;
  794. if (!(u->flags & MASKREC_PERM) && (now >= u->expire)) {
  795. putlog(LOG_MISC, "*", "%s %s %s %s (%s)", BANS_NOLONGER,
  796. u->mask, MISC_ONLOCALE, chan->dname, MISC_EXPIRED);
  797. for (b = chan->channel.ban; b->mask[0]; b = b->next)
  798. if (!rfc_casecmp(b->mask, u->mask) && expired_mask(chan, b->who) && b->timer != now) {
  799. if (!conf.bot->hub)
  800. add_mode(chan, '-', 'b', u->mask);
  801. b->timer = now;
  802. }
  803. u_delmask('b', chan, u->mask, 1);
  804. }
  805. }
  806. }
  807. }
  808. /* Check for expired timed-exemptions
  809. */
  810. static void check_expired_exempts(void)
  811. {
  812. if (!use_exempts)
  813. return;
  814. maskrec *u = NULL, *u2 = NULL;
  815. struct chanset_t *chan = NULL;
  816. masklist *b = NULL, *e = NULL;
  817. int match;
  818. for (u = global_exempts; u; u = u2) {
  819. u2 = u->next;
  820. if (!(u->flags & MASKREC_PERM) && (now >= u->expire)) {
  821. putlog(LOG_MISC, "*", "%s %s (%s)", EXEMPTS_NOLONGER,
  822. u->mask, MISC_EXPIRED);
  823. for (chan = chanset; chan; chan = chan->next) {
  824. match = 0;
  825. b = chan->channel.ban;
  826. while (b->mask[0] && !match) {
  827. if (wild_match(b->mask, u->mask) ||
  828. wild_match(u->mask, b->mask))
  829. match = 1;
  830. else
  831. b = b->next;
  832. }
  833. if (match)
  834. putlog(LOG_MISC, chan->dname,
  835. "Exempt not expired on channel %s. Ban still set!",
  836. chan->dname);
  837. else
  838. for (e = chan->channel.exempt; e->mask[0]; e = e->next)
  839. if (!rfc_casecmp(e->mask, u->mask) && expired_mask(chan, e->who) && e->timer != now) {
  840. if (!conf.bot->hub)
  841. add_mode(chan, '-', 'e', u->mask);
  842. e->timer = now;
  843. }
  844. }
  845. u_delmask('e', NULL, u->mask,1);
  846. }
  847. }
  848. /* Check for specific channel-domain exempts expiring */
  849. for (chan = chanset; chan; chan = chan->next) {
  850. for (u = chan->exempts; u; u = u2) {
  851. u2 = u->next;
  852. if (!(u->flags & MASKREC_PERM) && (now >= u->expire)) {
  853. match=0;
  854. b = chan->channel.ban;
  855. while (b->mask[0] && !match) {
  856. if (wild_match(b->mask, u->mask) ||
  857. wild_match(u->mask, b->mask))
  858. match=1;
  859. else
  860. b = b->next;
  861. }
  862. if (match)
  863. putlog(LOG_MISC, chan->dname,
  864. "Exempt not expired on channel %s. Ban still set!",
  865. chan->dname);
  866. else {
  867. putlog(LOG_MISC, "*", "%s %s %s %s (%s)", EXEMPTS_NOLONGER,
  868. u->mask, MISC_ONLOCALE, chan->dname, MISC_EXPIRED);
  869. for (e = chan->channel.exempt; e->mask[0]; e = e->next)
  870. if (!rfc_casecmp(e->mask, u->mask) && expired_mask(chan, e->who) && e->timer != now) {
  871. if (!conf.bot->hub)
  872. add_mode(chan, '-', 'e', u->mask);
  873. e->timer = now;
  874. }
  875. u_delmask('e', chan, u->mask, 1);
  876. }
  877. }
  878. }
  879. }
  880. }
  881. /* Check for expired timed-invites.
  882. */
  883. static void check_expired_invites(void)
  884. {
  885. if (!use_invites)
  886. return;
  887. maskrec *u = NULL, *u2 = NULL;
  888. struct chanset_t *chan = NULL;
  889. masklist *b = NULL;
  890. for (u = global_invites; u; u = u2) {
  891. u2 = u->next;
  892. if (!(u->flags & MASKREC_PERM) && (now >= u->expire)) {
  893. putlog(LOG_MISC, "*", "%s %s (%s)", INVITES_NOLONGER,
  894. u->mask, MISC_EXPIRED);
  895. for (chan = chanset; chan; chan = chan->next)
  896. if (!(chan->channel.mode & CHANINV))
  897. for (b = chan->channel.invite; b->mask[0]; b = b->next)
  898. if (!rfc_casecmp(b->mask, u->mask) && expired_mask(chan, b->who) && b->timer != now) {
  899. if (!conf.bot->hub)
  900. add_mode(chan, '-', 'I', u->mask);
  901. b->timer = now;
  902. }
  903. u_delmask('I', NULL, u->mask,1);
  904. }
  905. }
  906. /* Check for specific channel-domain invites expiring */
  907. for (chan = chanset; chan; chan = chan->next) {
  908. for (u = chan->invites; u; u = u2) {
  909. u2 = u->next;
  910. if (!(u->flags & MASKREC_PERM) && (now >= u->expire)) {
  911. putlog(LOG_MISC, "*", "%s %s %s %s (%s)", INVITES_NOLONGER,
  912. u->mask, MISC_ONLOCALE, chan->dname, MISC_EXPIRED);
  913. if (!(chan->channel.mode & CHANINV))
  914. for (b = chan->channel.invite; b->mask[0]; b = b->next)
  915. if (!rfc_casecmp(b->mask, u->mask) && expired_mask(chan, b->who) && b->timer != now) {
  916. if (!conf.bot->hub)
  917. add_mode(chan, '-', 'I', u->mask);
  918. b->timer = now;
  919. }
  920. u_delmask('I', chan, u->mask, 1);
  921. }
  922. }
  923. }
  924. }
  925. void check_expired_masks()
  926. {
  927. check_expired_bans();
  928. check_expired_exempts();
  929. check_expired_invites();
  930. }