userchan.c 30 KB

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