flags.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670
  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. * flags.c -- handles:
  22. * all the flag matching/conversion functions in one neat package :)
  23. *
  24. */
  25. #include "common.h"
  26. #include "crypt.h"
  27. #include "src/mod/share.mod/share.h"
  28. #include "rfc1459.h"
  29. #include "userrec.h"
  30. #include "misc.h"
  31. #include "dccutil.h"
  32. #include "userent.h"
  33. #include "users.h"
  34. #include "chanprog.h"
  35. flag_t FLAG[128];
  36. void
  37. init_flags()
  38. {
  39. unsigned char i;
  40. for (i = 0; i < 'A'; i++)
  41. FLAG[(int) i] = 0;
  42. for (; i <= 'Z'; i++)
  43. FLAG[(int) i] = (flag_t) 1 << (26 + (i - 'A'));
  44. for (; i < 'a'; i++)
  45. FLAG[(int) i] = 0;
  46. for (; i <= 'z'; i++)
  47. FLAG[(int) i] = (flag_t) 1 << (i - 'a');
  48. for (; i < 128; i++)
  49. FLAG[(int) i] = 0;
  50. }
  51. /* Some flags are mutually exclusive -- this roots them out
  52. */
  53. flag_t
  54. sanity_check(flag_t atr, int bot)
  55. {
  56. if (bot && (atr & (USER_PARTY | USER_MASTER | USER_OWNER | USER_ADMIN | USER_HUBA | USER_CHUBA)))
  57. atr &= ~(USER_PARTY | USER_MASTER | USER_OWNER | USER_ADMIN | USER_HUBA | USER_CHUBA);
  58. /* only bots should be there: */
  59. if (!bot && (atr & (BOT_DOLIMIT | BOT_DOVOICE | BOT_UPDATEHUB | BOT_CHANHUB | BOT_FLOODBOT )))
  60. atr &= ~(BOT_DOLIMIT | BOT_DOVOICE | BOT_UPDATEHUB | BOT_CHANHUB | BOT_FLOODBOT );
  61. if (atr & USER_AUTOOP)
  62. atr |= USER_OP;
  63. if ((atr & USER_OP) && (atr & USER_DEOP))
  64. atr &= ~(USER_OP | USER_DEOP);
  65. if ((atr & USER_AUTOOP) && (atr & USER_DEOP))
  66. atr &= ~(USER_AUTOOP | USER_DEOP);
  67. if ((atr & USER_VOICE) && (atr & USER_QUIET))
  68. atr &= ~(USER_VOICE | USER_QUIET);
  69. /* Can't be admin without also being owner and having hub access */
  70. if (atr & USER_ADMIN)
  71. atr |= USER_OWNER | USER_HUBA | USER_PARTY;
  72. /* Hub access gets chanhub access */
  73. if (atr & USER_HUBA)
  74. atr |= USER_CHUBA;
  75. if (atr & USER_OWNER) {
  76. atr |= USER_MASTER;
  77. }
  78. /* Master implies botmaster, op */
  79. if (atr & USER_MASTER)
  80. atr |= USER_OP;
  81. /* Can't be botnet master without party-line access */
  82. /* if (atr & USER_BOTMAST)
  83. atr |= USER_PARTY;
  84. */
  85. return atr;
  86. }
  87. /* Sanity check on channel attributes
  88. */
  89. flag_t
  90. chan_sanity_check(flag_t chatr, int bot)
  91. {
  92. /* these should only be global */
  93. if (chatr & (USER_PARTY | USER_ADMIN | USER_HUBA | USER_CHUBA | BOT_UPDATEHUB))
  94. chatr &= ~(USER_PARTY | USER_ADMIN | USER_HUBA | USER_CHUBA | BOT_UPDATEHUB);
  95. /* these should only be set on bots */
  96. if (!bot && (chatr & (BOT_DOLIMIT | BOT_DOVOICE | BOT_CHANHUB | BOT_FLOODBOT )))
  97. chatr &= ~(BOT_DOLIMIT | BOT_DOVOICE | BOT_CHANHUB | BOT_FLOODBOT );
  98. if ((chatr & USER_OP) && (chatr & USER_DEOP))
  99. chatr &= ~(USER_OP | USER_DEOP);
  100. if ((chatr & USER_AUTOOP) && (chatr & USER_DEOP))
  101. chatr &= ~(USER_AUTOOP | USER_DEOP);
  102. if ((chatr & USER_VOICE) && (chatr & USER_QUIET))
  103. chatr &= ~(USER_VOICE | USER_QUIET);
  104. /* Can't be channel owner without also being channel master */
  105. if (chatr & USER_OWNER)
  106. chatr |= USER_MASTER;
  107. /* Master implies op */
  108. if (chatr & USER_MASTER)
  109. chatr |= USER_OP;
  110. return chatr;
  111. }
  112. /* Get icon symbol for a user (depending on access level)
  113. *
  114. * (*) owner on any channel
  115. * (+) master on any channel
  116. * (%) botnet master
  117. * (@) op on any channel
  118. * (-) other
  119. */
  120. char
  121. geticon(int idx)
  122. {
  123. if (!dcc[idx].user)
  124. return '-';
  125. struct flag_record fr = { FR_GLOBAL | FR_CHAN | FR_ANYWH, 0, 0, 0 };
  126. get_user_flagrec(dcc[idx].user, &fr, NULL);
  127. if (glob_admin(fr))
  128. return '^';
  129. if (chan_owner(fr))
  130. return '*';
  131. if (chan_master(fr))
  132. return '+';
  133. if (chan_op(fr))
  134. return '@';
  135. return '-';
  136. }
  137. void
  138. break_down_flags(const char *string, struct flag_record *plus, struct flag_record *minus)
  139. {
  140. struct flag_record *which = plus;
  141. int chan = 0; /* 0 = glob, 1 = chan */
  142. int flags = plus->match;
  143. if (!(flags & FR_GLOBAL)) {
  144. if (flags & FR_CHAN)
  145. chan = 1;
  146. else
  147. return; /* We dont actually want any..huh? */
  148. }
  149. bzero(plus, sizeof(struct flag_record));
  150. if (minus)
  151. bzero(minus, sizeof(struct flag_record));
  152. plus->match = FR_OR; /* Default binding type OR */
  153. while (*string) {
  154. switch (*string) {
  155. case '+':
  156. which = plus;
  157. break;
  158. case '-':
  159. which = minus ? minus : plus;
  160. break;
  161. case '|':
  162. case '&':
  163. if (!chan) {
  164. if (*string == '|')
  165. plus->match = FR_OR;
  166. else
  167. plus->match = FR_AND;
  168. }
  169. which = plus;
  170. chan++;
  171. if (chan == 2)
  172. string = "";
  173. else if (chan == 3)
  174. chan = 1;
  175. break; /* switch() */
  176. default:
  177. {
  178. flag_t flagbit = FLAG[(int) *string];
  179. if (flagbit) {
  180. switch (chan) {
  181. case 0:
  182. /* which->global |= (flag_t) 1 << (*string - 'a'); */
  183. which->global |=flagbit;
  184. break;
  185. case 1:
  186. /* which->chan |= (flag_t) 1 << (*string - 'a'); */
  187. which->chan |= flagbit;
  188. break;
  189. }
  190. }
  191. }
  192. }
  193. string++;
  194. }
  195. for (which = plus; which; which = (which == plus ? minus : 0)) {
  196. if (flags & FR_BOT) {
  197. which->global &= BOT_VALID;
  198. which->chan &= BOT_CHAN_VALID;
  199. } else {
  200. which->global &= USER_VALID;
  201. which->chan &= USER_CHAN_VALID;
  202. }
  203. }
  204. plus->match |= flags;
  205. if (minus) {
  206. minus->match |= flags;
  207. if (!(plus->match & (FR_AND | FR_OR)))
  208. plus->match |= FR_OR;
  209. }
  210. }
  211. static int
  212. flag2str(char *string, flag_t flag)
  213. {
  214. unsigned char c;
  215. char *old = string;
  216. for (c = 0; c < 128; c++)
  217. if (flag & FLAG[(int) c])
  218. *string++ = c;
  219. if (string == old)
  220. *string++ = '-';
  221. return string - old;
  222. }
  223. int
  224. build_flags(char *string, struct flag_record *plus, struct flag_record *minus)
  225. {
  226. char *old = string;
  227. if (plus->match & FR_GLOBAL) {
  228. if (minus && plus->global)
  229. *string++ = '+';
  230. string += flag2str(string, plus->global);
  231. if (minus && minus->global) {
  232. *string++ = '-';
  233. string += flag2str(string, minus->global);
  234. }
  235. }
  236. if (plus->match & FR_CHAN) {
  237. if (plus->match & FR_GLOBAL)
  238. *string++ = (plus->match & FR_AND) ? '&' : '|';
  239. if (minus && plus->chan)
  240. *string++ = '+';
  241. string += flag2str(string, plus->chan);
  242. if (minus && minus->chan) {
  243. *string++ = '-';
  244. string += flag2str(string, minus->global);
  245. }
  246. }
  247. if (string == old) {
  248. *string++ = '-';
  249. *string = 0;
  250. return 0;
  251. }
  252. *string = 0;
  253. return string - old;
  254. }
  255. /* Returns 1 if flags match, 0 if they don't. */
  256. int
  257. flagrec_ok(struct flag_record *req, struct flag_record *have)
  258. {
  259. if (req->match & FR_AND) {
  260. return flagrec_eq(req, have);
  261. } else if (req->match & FR_OR) {
  262. int hav = have->global;
  263. /* no flags, is a match */
  264. if (!req->chan && !req->global)
  265. return 1;
  266. if (hav & req->global)
  267. return 1;
  268. if (have->chan & req->chan)
  269. return 1;
  270. return 0;
  271. }
  272. return 0;
  273. }
  274. /* Returns 1 if flags match, 0 if they don't. */
  275. int
  276. flagrec_eq(struct flag_record *req, struct flag_record *have)
  277. {
  278. if (req->match & FR_AND) {
  279. if (req->match & FR_GLOBAL) {
  280. if ((req->global &have->global) !=req->global)
  281. return 0;
  282. }
  283. if (req->match & FR_CHAN) {
  284. if ((req->chan & have->chan) != req->chan)
  285. return 0;
  286. }
  287. return 1;
  288. } else if (req->match & FR_OR) {
  289. if (!req->chan && !req->global)
  290. return 1;
  291. if (req->match & FR_GLOBAL) {
  292. if (have->global &req->global)
  293. return 1;
  294. }
  295. if (req->match & FR_CHAN) {
  296. if (have->chan & req->chan)
  297. return 1;
  298. }
  299. return 0;
  300. }
  301. return 0; /* fr0k3 binding, dont pass it */
  302. }
  303. void
  304. set_user_flagrec(struct userrec *u, struct flag_record *fr, const char *chname)
  305. {
  306. if (!u)
  307. return;
  308. struct chanuserrec *cr = NULL;
  309. flag_t oldflags = fr->match;
  310. char buffer[100] = "";
  311. struct chanset_t *ch;
  312. if (oldflags & FR_GLOBAL) {
  313. u->flags = fr->global;
  314. if (!noshare) {
  315. fr->match = FR_GLOBAL;
  316. build_flags(buffer, fr, NULL);
  317. shareout("a %s %s\n", u->handle, buffer);
  318. }
  319. }
  320. if ((oldflags & FR_CHAN) && chname) {
  321. for (cr = u->chanrec; cr; cr = cr->next)
  322. if (!rfc_casecmp(chname, cr->channel))
  323. break;
  324. ch = findchan_by_dname(chname);
  325. if (!cr && ch) {
  326. cr = (struct chanuserrec *) my_calloc(1, sizeof(struct chanuserrec));
  327. cr->next = u->chanrec;
  328. u->chanrec = cr;
  329. strlcpy(cr->channel, chname, sizeof cr->channel);
  330. }
  331. if (cr && ch) {
  332. cr->flags = fr->chan;
  333. if (!noshare) {
  334. fr->match = FR_CHAN;
  335. build_flags(buffer, fr, NULL);
  336. shareout("a %s %s %s\n", u->handle, buffer, chname);
  337. }
  338. }
  339. }
  340. fr->match = oldflags;
  341. }
  342. /* Always pass the dname (display name) to this function for chname <cybah>
  343. */
  344. void
  345. get_user_flagrec(const struct userrec *u, struct flag_record *fr, const char *chname, const struct chanset_t* chan)
  346. {
  347. fr->bot = 0;
  348. if (!u) {
  349. fr->global = fr->chan = 0;
  350. return;
  351. }
  352. if (u->bot)
  353. fr->bot = 1;
  354. fr->global = (fr->match & FR_GLOBAL) ? u->flags : 0;
  355. if (fr->match & FR_CHAN) {
  356. struct chanuserrec *cr = NULL;
  357. if ((fr->match & FR_ANYWH) || (fr->match & FR_ANYCH)) {
  358. if (fr->match & FR_ANYWH)
  359. fr->chan = u->flags;
  360. for (cr = u->chanrec; cr; cr = cr->next) {
  361. if (chan || findchan_by_dname(cr->channel)) {
  362. fr->chan |= cr->flags;
  363. }
  364. }
  365. } else {
  366. if (chname) {
  367. for (cr = u->chanrec; cr; cr = cr->next) {
  368. if (!rfc_casecmp(chname, cr->channel))
  369. break;
  370. }
  371. }
  372. if (cr) {
  373. fr->chan = cr->flags;
  374. } else {
  375. fr->chan = 0;
  376. }
  377. }
  378. }
  379. }
  380. /* private returns 0 if user has access, and 1 if they dont because of +private
  381. * This function does not check if the user has "op" access, it only checks if the user is
  382. * restricted by +private for the channel
  383. */
  384. int
  385. privchan(const struct flag_record fr, const struct chanset_t *chan, int type)
  386. {
  387. if (!chan || !channel_privchan(chan) || glob_bot(fr) || glob_owner(fr))
  388. return 0; /* user is implicitly not restricted by +private, they may however be lacking other flags */
  389. if (type == PRIV_OP) {
  390. /* |o implies all flags above. n| has access to all +private. Bots are exempt. */
  391. if (chan_op(fr))
  392. return 0;
  393. } else if (type == PRIV_VOICE) {
  394. if (chan_voice(fr) || chan_op(fr))
  395. return 0;
  396. }
  397. return 1; /* user is restricted by +private */
  398. }
  399. int
  400. real_chk_op(const struct flag_record fr, const struct chanset_t *chan, bool botbitch)
  401. {
  402. if (!chan && glob_op(fr))
  403. return 1;
  404. else if (!chan)
  405. return 0;
  406. if (!privchan(fr, chan, PRIV_OP) && !real_chk_deop(fr, chan, botbitch)) {
  407. if (chan_op(fr) || (glob_op(fr) && !chan_deop(fr)))
  408. return 1;
  409. }
  410. return 0;
  411. }
  412. int
  413. chk_autoop(const struct flag_record fr, const struct chanset_t *chan)
  414. {
  415. if (glob_bot(fr))
  416. return 0;
  417. if (!chan || (!channel_take(chan) && !privchan(fr, chan, PRIV_OP) && chk_op(fr, chan) && !chk_deop(fr, chan))) {
  418. if (channel_autoop(chan) || chan_autoop(fr) || glob_autoop(fr))
  419. return 1;
  420. }
  421. return 0;
  422. }
  423. int
  424. real_chk_deop(const struct flag_record fr, const struct chanset_t *chan, bool botbitch)
  425. {
  426. if (chan && botbitch && channel_botbitch(chan) && !glob_bot(fr))
  427. return 1;
  428. if (chan_deop(fr) || (glob_deop(fr) && !chan_op(fr)))
  429. return 1;
  430. else
  431. return 0;
  432. }
  433. int
  434. doresolv(const struct chanset_t *chan)
  435. {
  436. if (!chan)
  437. return 0;
  438. struct flag_record fr = { FR_GLOBAL | FR_CHAN | FR_BOT, 0, 0, 0 };
  439. get_user_flagrec(conf.bot->u, &fr, chan->dname);
  440. if (glob_doresolv(fr) || chan_doresolv(fr))
  441. return 1;
  442. return 0;
  443. }
  444. int
  445. dovoice(const struct chanset_t *chan)
  446. {
  447. if (!chan)
  448. return 0;
  449. struct flag_record fr = { FR_GLOBAL | FR_CHAN | FR_BOT, 0, 0, 0 };
  450. get_user_flagrec(conf.bot->u, &fr, chan->dname);
  451. if (glob_dovoice(fr) || chan_dovoice(fr))
  452. return 1;
  453. return 0;
  454. }
  455. int
  456. doflood(const struct chanset_t *chan)
  457. {
  458. struct flag_record fr = { FR_GLOBAL | FR_CHAN | FR_BOT, 0, 0, 0 };
  459. if (!chan)
  460. fr.match |= FR_ANYWH;
  461. get_user_flagrec(conf.bot->u, &fr, chan ? chan->dname : NULL);
  462. if (glob_doflood(fr) || chan_doflood(fr))
  463. return 1;
  464. return 0;
  465. }
  466. int
  467. dolimit(const struct chanset_t *chan)
  468. {
  469. if (!chan)
  470. return 0;
  471. struct flag_record fr = { FR_GLOBAL | FR_CHAN | FR_BOT, 0, 0, 0 };
  472. get_user_flagrec(conf.bot->u, &fr, chan->dname);
  473. if (glob_dolimit(fr) || chan_dolimit(fr))
  474. return 1;
  475. return 0;
  476. }
  477. int
  478. whois_access(struct userrec *user, struct userrec *whois_user)
  479. {
  480. if (user == whois_user)
  481. return 1;
  482. struct flag_record fr = { FR_GLOBAL | FR_CHAN, 0, 0, 0 }, whois = {
  483. FR_GLOBAL | FR_CHAN, 0, 0, 0};
  484. get_user_flagrec(user, &fr, NULL);
  485. get_user_flagrec(whois_user, &whois, NULL);
  486. /* Don't show hub bots from leaf bots. */
  487. if (!conf.bot->hub && whois_user->bot && bot_hublevel(whois_user) < 999)
  488. return 0;
  489. if (
  490. (isowner(whois_user->handle) && !isowner(user->handle)) ||
  491. (glob_admin(whois) && !glob_admin(fr)) ||
  492. (glob_owner(whois) && !glob_owner(fr)) ||
  493. (glob_master(whois) && !glob_master(fr)) ||
  494. (glob_bot(whois) && !glob_master(fr))
  495. )
  496. return 0;
  497. return 1;
  498. }
  499. deflag_t deflag_translate(const char *buf)
  500. {
  501. if (str_isdigit(buf))
  502. return (static_cast<deflag_t>(atoi(buf)));
  503. if (!strcasecmp(buf, "ignore"))
  504. return DEFLAG_IGNORE;
  505. else if (!strcasecmp(buf, "deop"))
  506. return DEFLAG_DEOP;
  507. else if (!strcasecmp(buf, "kick"))
  508. return DEFLAG_KICK;
  509. else if (!strcasecmp(buf, "delete") || !strcasecmp(buf, "remove"))
  510. return DEFLAG_DELETE;
  511. else if (!strcasecmp(buf, "react"))
  512. return DEFLAG_REACT;
  513. return DEFLAG_IGNORE;
  514. }
  515. void deflag_user(struct userrec *u, deflag_event_t why, const char *msg, const struct chanset_t *chan)
  516. {
  517. char tmp[50] = "", tmp2[1024] = "";
  518. struct flag_record fr = {FR_GLOBAL | FR_CHAN, 0, 0, 0 };
  519. int which = 0;
  520. switch (why) {
  521. case DEFLAG_EVENT_BADCOOKIE:
  522. strlcpy(tmp, "Bad op cookie", sizeof(tmp));
  523. which = chan->bad_cookie;
  524. break;
  525. case DEFLAG_EVENT_MANUALOP:
  526. strlcpy(tmp, STR("Manual op in -manop channel"), sizeof(tmp));
  527. which = chan->manop;
  528. break;
  529. case DEFLAG_EVENT_REVENGE_DEOP:
  530. strlcpy(tmp, STR("Deopped bot in +revenge channel"), sizeof(tmp));
  531. which = chan->revenge;
  532. break;
  533. case DEFLAG_EVENT_REVENGE_KICK:
  534. strlcpy(tmp, STR("Kicked bot in +revenge channel"), sizeof(tmp));
  535. which = chan->revenge;
  536. break;
  537. case DEFLAG_EVENT_REVENGE_BAN:
  538. strlcpy(tmp, STR("Banned bot in +revenge channel"), sizeof(tmp));
  539. which = chan->revenge;
  540. break;
  541. case DEFLAG_EVENT_MDOP:
  542. strlcpy(tmp, "Mass deop", sizeof(tmp));
  543. which = chan->mdop;
  544. break;
  545. case DEFLAG_EVENT_MOP:
  546. strlcpy(tmp, "Mass op", sizeof(tmp));
  547. which = chan->mop;
  548. break;
  549. default:
  550. simple_snprintf(tmp, sizeof(tmp), "Reason #%i", why);
  551. }
  552. if (which == DEFLAG_DEOP) {
  553. putlog(LOG_WARN, "*", "Setting %s +d (%s): %s", u->handle, tmp, msg);
  554. simple_snprintf(tmp2, sizeof(tmp2), "+d: %s (%s)", tmp, msg);
  555. set_user(&USERENTRY_COMMENT, u, tmp2);
  556. get_user_flagrec(u, &fr, chan->dname);
  557. if (fr.global == USER_DEOP)
  558. fr.match &= ~FR_GLOBAL;
  559. else
  560. fr.global = USER_DEOP;
  561. if (fr.chan == USER_DEOP)
  562. fr.match &= ~FR_CHAN;
  563. else
  564. fr.chan = USER_DEOP;
  565. /* did anything change? */
  566. if (fr.match)
  567. set_user_flagrec(u, &fr, chan->dname);
  568. } else if (which == DEFLAG_KICK) {
  569. putlog(LOG_WARN, "*", "Setting %s +dk (%s): %s", u->handle, tmp, msg);
  570. simple_snprintf(tmp2, sizeof(tmp2), "+dk: %s (%s)", tmp, msg);
  571. set_user(&USERENTRY_COMMENT, u, tmp2);
  572. get_user_flagrec(u, &fr, chan->dname);
  573. if (fr.global == (USER_DEOP|USER_KICK))
  574. fr.match &= ~FR_GLOBAL;
  575. else
  576. fr.global = USER_DEOP | USER_KICK;
  577. if (fr.chan == (USER_DEOP|USER_KICK))
  578. fr.match &= ~FR_CHAN;
  579. else
  580. fr.chan = USER_DEOP | USER_KICK;
  581. /* did anything change */
  582. if (fr.match)
  583. set_user_flagrec(u, &fr, chan->dname);
  584. } else if (which == DEFLAG_DELETE) {
  585. putlog(LOG_WARN, "*", "Deleting %s (%s): %s", u->handle, tmp, msg);
  586. deluser(u->handle);
  587. } else {
  588. putlog(LOG_WARN, "*", "No user flag effects for %s (%s): %s", u->handle, tmp, msg);
  589. simple_snprintf(tmp2, sizeof(tmp2), "Warning: %s (%s)", tmp, msg);
  590. set_user(&USERENTRY_COMMENT, u, tmp2);
  591. }
  592. }