userchan.c 30 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054
  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-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. closed-ban %d closed-invite %d closed-private %d ban-time %d \
  639. exempt-time %d invite-time %d voice-non-ident %d auto-delay %d \
  640. flood-exempt %d flood-lock-time %d knock %d fish-key { %s } \
  641. %cmeankicks %cenforcebans %cdynamicbans %cuserbans %cbitch %cfloodban \
  642. %cprivate %ccycle %cinactive %cdynamicexempts %cuserexempts \
  643. %cdynamicinvites %cuserinvites %cnodesynch %cclosed %cvoice \
  644. %cfastop %cautoop %cbotbitch %cbackup %cnomassjoin %crbl %cvoicebitch %cprotect protect-backup %d %c%s",
  645. w,
  646. /* Chanchar template
  647. * temp,
  648. * also include temp %s in dprintf.
  649. */
  650. chan->groups && chan->groups->length() ? static_cast<bd::String>(chan->groups->join(" ")).c_str() : "",
  651. chan->bad_cookie,
  652. chan->manop,
  653. chan->mdop,
  654. chan->mop,
  655. chan->revenge,
  656. chan->limitraise,
  657. chan->ban_type,
  658. chan->flood_pub_thr, chan->flood_pub_time,
  659. chan->flood_ctcp_thr, chan->flood_ctcp_time,
  660. chan->flood_join_thr, chan->flood_join_time,
  661. chan->flood_kick_thr, chan->flood_kick_time,
  662. chan->flood_deop_thr, chan->flood_deop_time,
  663. chan->flood_nick_thr, chan->flood_nick_time,
  664. chan->flood_mjoin_thr, chan->flood_mjoin_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. chan->voice_non_ident,
  676. chan->auto_delay,
  677. chan->flood_exempt_mode,
  678. chan->flood_lock_time,
  679. chan->knock_flags,
  680. chan->fish_key,
  681. PLSMNS(channel_meankicks(chan)),
  682. PLSMNS(channel_enforcebans(chan)),
  683. PLSMNS(channel_dynamicbans(chan)),
  684. PLSMNS(!channel_nouserbans(chan)),
  685. PLSMNS(channel_bitch(chan)),
  686. PLSMNS(channel_floodban(chan)),
  687. PLSMNS(channel_privchan(chan)),
  688. PLSMNS(channel_cycle(chan)),
  689. force_inactive ? '+' : PLSMNS(channel_inactive(chan)),
  690. PLSMNS(channel_dynamicexempts(chan)),
  691. PLSMNS(!channel_nouserexempts(chan)),
  692. PLSMNS(channel_dynamicinvites(chan)),
  693. PLSMNS(!channel_nouserinvites(chan)),
  694. PLSMNS(channel_nodesynch(chan)),
  695. PLSMNS(channel_closed(chan)),
  696. PLSMNS(channel_voice(chan)),
  697. PLSMNS(channel_fastop(chan)),
  698. PLSMNS(channel_autoop(chan)),
  699. PLSMNS(channel_botbitch(chan)),
  700. PLSMNS(channel_backup(chan)),
  701. PLSMNS(channel_nomassjoin(chan)),
  702. PLSMNS(channel_rbl(chan)),
  703. PLSMNS(channel_voicebitch(chan)),
  704. PLSMNS(channel_protect(chan)),
  705. chan->protect_backup,
  706. HAVE_TAKE ? PLSMNS(channel_take(chan)) : ' ',
  707. HAVE_TAKE ? "take " : " "
  708. /* Chanflag template
  709. * also include a %ctemp above.
  710. * PLSMNS(channel_temp(chan)),
  711. */
  712. );
  713. }
  714. /* Write the channels to the userfile
  715. */
  716. void write_chans(bd::Stream& stream, int idx, bool old)
  717. {
  718. putlog(LOG_DEBUG, "*", "Writing channels..");
  719. stream << bd::String::printf(CHANS_NAME " - -\n");
  720. /* FIXME: Remove after 1.2.15 */
  721. if (!old)
  722. write_chan(stream, idx, chanset_default);
  723. for (struct chanset_t *chan = chanset; chan; chan = chan->next)
  724. write_chan(stream, idx, chan);
  725. }
  726. /* FIXME: remove after 1.2.14 */
  727. /* Write the channels to the userfile
  728. */
  729. void write_chans_compat(bd::Stream& stream, int idx)
  730. {
  731. putlog(LOG_DEBUG, "*", "Writing channels..");
  732. stream << bd::String::printf(CHANS_NAME " - -\n");
  733. char w[1024] = "";
  734. for (struct chanset_t *chan = chanset; chan; chan = chan->next) {
  735. char inactive = 0;
  736. putlog(LOG_DEBUG, "*", "writing channel %s to userfile..", chan->dname);
  737. get_mode_protect(chan, w, sizeof(w));
  738. inactive = PLSMNS(channel_inactive(chan));
  739. stream << bd::String::printf("\
  740. + channel add %s { chanmode { %s } addedby %s addedts %li \
  741. bad-cookie %d manop %d mdop %d mop %d limit %d \
  742. flood-chan %d:%d flood-ctcp %d:%d flood-join %d:%d \
  743. flood-kick %d:%d flood-deop %d:%d flood-nick %d:%d \
  744. closed-ban %d closed-invite %d closed-private %d ban-time %d \
  745. exempt-time %d invite-time %d voice-non-ident %d auto-delay %d \
  746. %cenforcebans %cdynamicbans %cuserbans %cbitch \
  747. %cprivate %ccycle %cinactive %cdynamicexempts %cuserexempts \
  748. %cdynamicinvites %cuserinvites %cnodesynch %cclosed %cvoice \
  749. %cfastop %cautoop %cbotbitch %cbackup %cnomassjoin %c%s}\n",
  750. chan->dname,
  751. w,
  752. chan->added_by,
  753. (long)chan->added_ts,
  754. chan->bad_cookie,
  755. chan->manop,
  756. chan->mdop,
  757. chan->mop,
  758. chan->limitraise,
  759. chan->flood_pub_thr, chan->flood_pub_time,
  760. chan->flood_ctcp_thr, chan->flood_ctcp_time,
  761. chan->flood_join_thr, chan->flood_join_time,
  762. chan->flood_kick_thr, chan->flood_kick_time,
  763. chan->flood_deop_thr, chan->flood_deop_time,
  764. chan->flood_nick_thr, chan->flood_nick_time,
  765. chan->closed_ban,
  766. chan->closed_invite,
  767. chan->closed_private,
  768. chan->ban_time,
  769. chan->exempt_time,
  770. chan->invite_time,
  771. chan->voice_non_ident,
  772. chan->auto_delay,
  773. PLSMNS(channel_enforcebans(chan)),
  774. PLSMNS(channel_dynamicbans(chan)),
  775. PLSMNS(!channel_nouserbans(chan)),
  776. PLSMNS(channel_bitch(chan)),
  777. PLSMNS(channel_privchan(chan)),
  778. PLSMNS(channel_cycle(chan)),
  779. inactive,
  780. PLSMNS(channel_dynamicexempts(chan)),
  781. PLSMNS(!channel_nouserexempts(chan)),
  782. PLSMNS(channel_dynamicinvites(chan)),
  783. PLSMNS(!channel_nouserinvites(chan)),
  784. PLSMNS(channel_nodesynch(chan)),
  785. PLSMNS(channel_closed(chan)),
  786. PLSMNS(channel_voice(chan)),
  787. PLSMNS(channel_fastop(chan)),
  788. PLSMNS(channel_autoop(chan)),
  789. PLSMNS(channel_botbitch(chan)),
  790. PLSMNS(channel_backup(chan)),
  791. PLSMNS(channel_nomassjoin(chan)),
  792. HAVE_TAKE ? PLSMNS(channel_take(chan)) : ' ',
  793. HAVE_TAKE ? "take " : " "
  794. );
  795. }
  796. }
  797. void channels_writeuserfile(bd::Stream& stream, int old)
  798. {
  799. putlog(LOG_DEBUG, "@", "Writing channel/ban/exempt/invite entries.");
  800. if (old != 1)
  801. write_chans(stream, -1, old == 0 ? 0 : 1);
  802. else /* flood-* hacks */
  803. write_chans_compat(stream, -1);
  804. write_vars_and_cmdpass(stream, -1);
  805. write_bans(stream, -1);
  806. write_exempts(stream, -1);
  807. write_invites(stream, -1);
  808. }
  809. /* Expire mask originally set by `who' on `chan'?
  810. *
  811. * We might not want to expire masks in all cases, as other bots
  812. * often tend to immediately reset masks they've listed in their
  813. * internal ban list, making it quite senseless for us to remove
  814. * them in the first place.
  815. *
  816. * Returns 1 if a mask on `chan' by `who' may be expired and 0 if
  817. * not.
  818. */
  819. bool expired_mask(struct chanset_t *chan, char *who)
  820. {
  821. memberlist *m = NULL, *m2 = NULL;
  822. char buf[UHOSTLEN] = "", *snick = NULL, *sfrom = NULL;
  823. struct userrec *u = NULL;
  824. strlcpy(buf, who, sizeof(buf));
  825. sfrom = buf;
  826. snick = splitnick(&sfrom);
  827. if (!snick[0])
  828. return 1;
  829. m = ismember(chan, snick);
  830. if (!m)
  831. for (m2 = chan->channel.member; m2 && m2->nick[0]; m2 = m2->next)
  832. if (!strcasecmp(sfrom, m2->userhost)) {
  833. m = m2;
  834. break;
  835. }
  836. if (!m || !chan_hasop(m) || m->is_me)
  837. return 1;
  838. /* At this point we know the person/bot who set the mask is currently
  839. * present in the channel and has op.
  840. */
  841. if (m->user)
  842. u = m->user;
  843. else {
  844. simple_snprintf(buf, sizeof(buf), "%s!%s", m->nick, m->userhost);
  845. u = get_user_by_host(buf);
  846. }
  847. /* Do not expire masks set by bots. */
  848. if (u && u->bot)
  849. return 0;
  850. else
  851. return 1;
  852. }
  853. /* Check for expired timed-bans.
  854. */
  855. static void check_expired_mask(const char type)
  856. {
  857. maskrec *u = NULL, *u2 = NULL, *list = NULL;
  858. struct chanset_t *chan = NULL;
  859. masklist *b = NULL, *chanlist = NULL;
  860. const char *str_typing = (type == 'b' ? "banning" : type == 'e' ? "ban exempting" : "inviting");
  861. bool remove, match;
  862. list = (type == 'b' ? global_bans : type == 'e' ? global_exempts : global_invites);
  863. for (u = list; u; u = u2) {
  864. u2 = u->next;
  865. if (!(u->flags & MASKREC_PERM) && (now >= u->expire)) {
  866. putlog(LOG_MISC, "*", "No longer %s %s (expired)", str_typing, u->mask);
  867. if (!conf.bot->hub) {
  868. for (chan = chanset; chan; chan = chan->next) {
  869. remove = 1; /* hack for 'e' */
  870. if (type == 'e') {
  871. match = 0;
  872. b = chan->channel.ban;
  873. while (b->mask[0] && !match) {
  874. if (wild_match(b->mask, u->mask) || wild_match(u->mask, b->mask))
  875. match = 1;
  876. else
  877. b = b->next;
  878. }
  879. if (match) {
  880. putlog(LOG_MISC, chan->dname, "Exempt not expired on channel %s. Ban still set!", chan->dname);
  881. remove = 0;
  882. }
  883. }
  884. if (remove) {
  885. chanlist = (type == 'b' ? chan->channel.ban : type == 'e' ? chan->channel.exempt : chan->channel.invite);
  886. for (b = chanlist; b->mask[0]; b = b->next) {
  887. if (!rfc_casecmp(b->mask, u->mask) && expired_mask(chan, b->who) && b->timer != now) {
  888. add_mode(chan, '-', type, u->mask);
  889. b->timer = now;
  890. }
  891. }
  892. u_delmask(type, NULL, u->mask, 1);
  893. }
  894. }
  895. } else
  896. u_delmask(type, NULL, u->mask, 1);
  897. }
  898. }
  899. /* Check for specific channel-domain bans expiring */
  900. for (chan = chanset; chan; chan = chan->next) {
  901. list = (type == 'b' ? chan->bans : type == 'e' ? chan->exempts : chan->invites);
  902. for (u = list; u; u = u2) {
  903. u2 = u->next;
  904. if (!(u->flags & MASKREC_PERM) && (now >= u->expire)) {
  905. remove = 1;
  906. if (!conf.bot->hub && type == 'e') {
  907. match = 0;
  908. b = chan->channel.ban;
  909. while (b->mask[0] && !match) {
  910. if (wild_match(b->mask, u->mask) || wild_match(u->mask, b->mask))
  911. match = 1;
  912. else
  913. b = b->next;
  914. }
  915. if (match) {
  916. putlog(LOG_MISC, chan->dname, "Exempt not expired on channel %s. Ban still set!", chan->dname);
  917. remove = 0;
  918. }
  919. }
  920. if (remove) {
  921. putlog(LOG_MISC, "*", "No longer %s %s on %s (expired)", str_typing, u->mask, chan->dname);
  922. if (!conf.bot->hub) {
  923. chanlist = (type == 'b' ? chan->channel.ban : type == 'e' ? chan->channel.exempt : chan->channel.invite);
  924. for (b = chanlist; b->mask[0]; b = b->next) {
  925. if (!rfc_casecmp(b->mask, u->mask) && expired_mask(chan, b->who) && b->timer != now) {
  926. if (!conf.bot->hub)
  927. add_mode(chan, '-', type, u->mask);
  928. b->timer = now;
  929. }
  930. }
  931. }
  932. u_delmask(type, chan, u->mask, 1);
  933. }
  934. }
  935. }
  936. }
  937. }
  938. void check_expired_masks()
  939. {
  940. check_expired_mask('b');
  941. if (use_exempts)
  942. check_expired_mask('e');
  943. if (use_invites)
  944. check_expired_mask('I');
  945. }