flags.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679
  1. /*
  2. * flags.c -- handles:
  3. * all the flag matching/conversion functions in one neat package :)
  4. *
  5. */
  6. #include "common.h"
  7. #include "crypt.h"
  8. #include "src/mod/share.mod/share.h"
  9. #include "rfc1459.h"
  10. #include "userrec.h"
  11. #include "misc.h"
  12. #include "dccutil.h"
  13. #include "userent.h"
  14. #include "users.h"
  15. static int allow_dk_cmds = 1;
  16. /* Some flags are mutually exclusive -- this roots them out
  17. */
  18. int sanity_check(int atr)
  19. {
  20. /* bots shouldnt have +pmcnaijlys */
  21. if ((atr & USER_BOT) &&
  22. (atr & (USER_PARTY | USER_MASTER | USER_OWNER | USER_ADMIN | USER_HUBA | USER_CHUBA)))
  23. atr &= ~(USER_PARTY | USER_MASTER | USER_OWNER | USER_ADMIN | USER_HUBA | USER_CHUBA);
  24. /* only bots should be there: */
  25. if (!(atr & USER_BOT) &&
  26. (atr & (USER_DOLIMIT | USER_DOVOICE | USER_UPDATEHUB | USER_CHANHUB)))
  27. atr &= ~(USER_DOLIMIT | USER_DOVOICE | USER_UPDATEHUB | USER_CHANHUB);
  28. if ((atr & USER_OP) && (atr & USER_DEOP))
  29. atr &= ~(USER_OP | USER_DEOP);
  30. if ((atr & USER_VOICE) && (atr & USER_QUIET))
  31. atr &= ~(USER_VOICE | USER_QUIET);
  32. /* Can't be admin without also being owner and having hub access */
  33. if (atr & USER_ADMIN)
  34. atr |= USER_OWNER | USER_HUBA | USER_PARTY;
  35. /* Hub access gets chanhub access */
  36. if (atr & USER_HUBA)
  37. atr |= USER_CHUBA;
  38. if (atr & USER_OWNER) {
  39. atr |= USER_MASTER;
  40. }
  41. /* Master implies botmaster, op */
  42. if (atr & USER_MASTER)
  43. atr |= USER_OP | USER_CHUBA;
  44. /* Can't be botnet master without party-line access */
  45. /* if (atr & USER_BOTMAST)
  46. atr |= USER_PARTY;
  47. */
  48. return atr;
  49. }
  50. /* Sanity check on channel attributes
  51. */
  52. int chan_sanity_check(int chatr, int atr)
  53. {
  54. /* admin for chan does shit.. */
  55. if (chatr & USER_ADMIN)
  56. chatr &= ~(USER_ADMIN);
  57. if ((chatr & USER_OP) && (chatr & USER_DEOP))
  58. chatr &= ~(USER_OP | USER_DEOP);
  59. if ((chatr & USER_VOICE) && (chatr & USER_QUIET))
  60. chatr &= ~(USER_VOICE | USER_QUIET);
  61. /* Can't be channel owner without also being channel master */
  62. if (chatr & USER_OWNER)
  63. chatr |= USER_MASTER;
  64. /* Master implies op */
  65. if (chatr & USER_MASTER)
  66. chatr |= USER_OP ;
  67. /* Can't be +s on chan unless you're a bot */
  68. if (!(atr & USER_BOT))
  69. chatr &= ~BOT_SHARE;
  70. return chatr;
  71. }
  72. /* Get icon symbol for a user (depending on access level)
  73. *
  74. * (*) owner on any channel
  75. * (+) master on any channel
  76. * (%) botnet master
  77. * (@) op on any channel
  78. * (-) other
  79. */
  80. char geticon(int idx)
  81. {
  82. struct flag_record fr = {FR_GLOBAL | FR_CHAN | FR_ANYWH, 0, 0, 0, 0, 0};
  83. if (!dcc[idx].user)
  84. return '-';
  85. get_user_flagrec(dcc[idx].user, &fr, 0);
  86. if (glob_admin(fr))
  87. return '^';
  88. if (chan_owner(fr))
  89. return '*';
  90. if (chan_master(fr))
  91. return '+';
  92. if (chan_op(fr))
  93. return '@';
  94. return '-';
  95. }
  96. void break_down_flags(const char *string, struct flag_record *plus,
  97. struct flag_record *minus)
  98. {
  99. struct flag_record *which = plus;
  100. int mode = 0; /* 0 = glob, 1 = chan, 2 = bot */
  101. int flags = plus->match;
  102. if (!(flags & FR_GLOBAL)) {
  103. if (flags & FR_BOT)
  104. mode = 2;
  105. else if (flags & FR_CHAN)
  106. mode = 1;
  107. else
  108. return; /* We dont actually want any..huh? */
  109. }
  110. egg_bzero(plus, sizeof(struct flag_record));
  111. if (minus)
  112. egg_bzero(minus, sizeof(struct flag_record));
  113. plus->match = FR_OR; /* Default binding type OR */
  114. while (*string) {
  115. switch (*string) {
  116. case '+':
  117. which = plus;
  118. break;
  119. case '-':
  120. which = minus ? minus : plus;
  121. break;
  122. case '|':
  123. case '&':
  124. if (!mode) {
  125. if (*string == '|')
  126. plus->match = FR_OR;
  127. else
  128. plus->match = FR_AND;
  129. }
  130. which = plus;
  131. mode++;
  132. if ((mode == 2) && !(flags & (FR_CHAN | FR_BOT)))
  133. string = "";
  134. else if (mode == 3)
  135. mode = 1;
  136. break;
  137. default:
  138. if ((*string >= 'a') && (*string <= 'z')) {
  139. switch (mode) {
  140. case 0:
  141. which->global |=1 << (*string - 'a');
  142. break;
  143. case 1:
  144. which->chan |= 1 << (*string - 'a');
  145. break;
  146. case 2:
  147. which->bot |= 1 << (*string - 'a');
  148. }
  149. } else if ((*string >= 'A') && (*string <= 'Z')) {
  150. switch (mode) {
  151. case 0:
  152. which->udef_global |= 1 << (*string - 'A');
  153. break;
  154. case 1:
  155. which->udef_chan |= 1 << (*string - 'A');
  156. break;
  157. }
  158. } else if ((*string >= '0') && (*string <= '9')) {
  159. switch (mode) {
  160. /* Map 0->9 to A->K for glob/chan so they are not lost */
  161. case 0:
  162. which->udef_global |= 1 << (*string - '0');
  163. break;
  164. case 1:
  165. which->udef_chan |= 1 << (*string - '0');
  166. break;
  167. case 2:
  168. which->bot |= BOT_FLAG0 << (*string - '0');
  169. break;
  170. }
  171. }
  172. }
  173. string++;
  174. }
  175. for (which = plus; which; which = (which == plus ? minus : 0)) {
  176. which->global &=USER_VALID;
  177. which->udef_global &= 0x03ffffff;
  178. which->chan &= CHAN_VALID;
  179. which->udef_chan &= 0x03ffffff;
  180. which->bot &= BOT_VALID;
  181. }
  182. plus->match |= flags;
  183. if (minus) {
  184. minus->match |= flags;
  185. if (!(plus->match & (FR_AND | FR_OR)))
  186. plus->match |= FR_OR;
  187. }
  188. }
  189. static int flag2str(char *string, int bot, int udef)
  190. {
  191. char x = 'a', *old = string;
  192. while (bot && (x <= 'z')) {
  193. if (bot & 1)
  194. *string++ = x;
  195. x++;
  196. bot = bot >> 1;
  197. }
  198. x = 'A';
  199. while (udef && (x <= 'Z')) {
  200. if (udef & 1)
  201. *string++ = x;
  202. udef = udef >> 1;
  203. x++;
  204. }
  205. if (string == old)
  206. *string++ = '-';
  207. return string - old;
  208. }
  209. static int bot2str(char *string, int bot)
  210. {
  211. char x = 'a', *old = string;
  212. while (x < 'v') {
  213. if (bot & 1)
  214. *string++ = x;
  215. x++;
  216. bot >>= 1;
  217. }
  218. x = '0';
  219. while (x <= '9') {
  220. if (bot & 1)
  221. *string++ = x;
  222. x++;
  223. bot >>= 1;
  224. }
  225. return string - old;
  226. }
  227. int build_flags(char *string, struct flag_record *plus, struct flag_record *minus)
  228. {
  229. char *old = string;
  230. if (plus->match & FR_GLOBAL) {
  231. if (minus && (plus->global || plus->udef_global))
  232. *string++ = '+';
  233. string += flag2str(string, plus->global, plus->udef_global);
  234. if (minus && (minus->global || minus->udef_global)) {
  235. *string++ = '-';
  236. string += flag2str(string, minus->global, minus->udef_global);
  237. }
  238. } else if (plus->match & FR_BOT) {
  239. if (minus && plus->bot)
  240. *string++ = '+';
  241. string += bot2str(string, plus->bot);
  242. if (minus && minus->bot) {
  243. *string++ = '-';
  244. string += bot2str(string, minus->bot);
  245. }
  246. }
  247. if (plus->match & FR_CHAN) {
  248. if (plus->match & (FR_GLOBAL | FR_BOT))
  249. *string++ = (plus->match & FR_AND) ? '&' : '|';
  250. if (minus && (plus->chan || plus->udef_chan))
  251. *string++ = '+';
  252. string += flag2str(string, plus->chan, plus->udef_chan);
  253. if (minus && (minus->chan || minus->udef_chan)) {
  254. *string++ = '-';
  255. string += flag2str(string, minus->global, minus->udef_chan);
  256. }
  257. }
  258. if ((plus->match & (FR_BOT | FR_CHAN)) == (FR_BOT | FR_CHAN)) {
  259. *string++ = (plus->match & FR_AND) ? '&' : '|';
  260. if (minus && plus->bot)
  261. *string++ = '+';
  262. string += bot2str(string, plus->bot);
  263. if (minus && minus->bot) {
  264. *string++ = '-';
  265. string += bot2str(string, minus->bot);
  266. }
  267. }
  268. if (string == old) {
  269. *string++ = '-';
  270. *string = 0;
  271. return 0;
  272. }
  273. *string = 0;
  274. return string - old;
  275. }
  276. /* Returns 1 if flags match, 0 if they don't. */
  277. int flagrec_ok(struct flag_record *req, struct flag_record *have)
  278. {
  279. if (req->match & FR_AND) {
  280. return flagrec_eq(req, have);
  281. } else if (req->match & FR_OR) {
  282. int hav = have->global;
  283. /* Exception 1 - global +d/+k cant use -|-, unless they are +p */
  284. if (!req->chan && !req->global && !req->udef_global &&
  285. !req->udef_chan) {
  286. if (!allow_dk_cmds) {
  287. if (glob_party(*have))
  288. return 1;
  289. if (glob_kick(*have) || chan_kick(*have))
  290. return 0; /* +k cant use -|- commands */
  291. if (glob_deop(*have) || chan_deop(*have))
  292. return 0; /* neither can +d's */
  293. }
  294. return 1;
  295. }
  296. /* The +n/+m checks arent needed anymore since +n/+m
  297. * automatically add lower flags
  298. */
  299. /* if (!1 && ((hav & USER_OP) || (have->chan & USER_OWNER)))
  300. hav |= USER_PARTY;*/
  301. if (hav & req->global)
  302. return 1;
  303. if (have->chan & req->chan)
  304. return 1;
  305. if (have->udef_global & req->udef_global)
  306. return 1;
  307. if (have->udef_chan & req->udef_chan)
  308. return 1;
  309. return 0;
  310. }
  311. return 0; /* fr0k3 binding, dont pass it */
  312. }
  313. /* Returns 1 if flags match, 0 if they don't. */
  314. int flagrec_eq(struct flag_record *req, struct flag_record *have)
  315. {
  316. if (req->match & FR_AND) {
  317. if (req->match & FR_GLOBAL) {
  318. if ((req->global &have->global) !=req->global)
  319. return 0;
  320. if ((req->udef_global & have->udef_global) != req->udef_global)
  321. return 0;
  322. }
  323. if (req->match & FR_BOT)
  324. if ((req->bot & have->bot) != req->bot)
  325. return 0;
  326. if (req->match & FR_CHAN) {
  327. if ((req->chan & have->chan) != req->chan)
  328. return 0;
  329. if ((req->udef_chan & have->udef_chan) != req->udef_chan)
  330. return 0;
  331. }
  332. return 1;
  333. } else if (req->match & FR_OR) {
  334. if (!req->chan && !req->global && !req->udef_chan &&
  335. !req->udef_global && !req->bot)
  336. return 1;
  337. if (req->match & FR_GLOBAL) {
  338. if (have->global &req->global)
  339. return 1;
  340. if (have->udef_global & req->udef_global)
  341. return 1;
  342. }
  343. if (req->match & FR_BOT)
  344. if (have->bot & req->bot)
  345. return 1;
  346. if (req->match & FR_CHAN) {
  347. if (have->chan & req->chan)
  348. return 1;
  349. if (have->udef_chan & req->udef_chan)
  350. return 1;
  351. }
  352. return 0;
  353. }
  354. return 0; /* fr0k3 binding, dont pass it */
  355. }
  356. void set_user_flagrec(struct userrec *u, struct flag_record *fr, const char *chname)
  357. {
  358. struct chanuserrec *cr = NULL;
  359. int oldflags = fr->match;
  360. char buffer[100] = "";
  361. struct chanset_t *ch;
  362. if (!u)
  363. return;
  364. if (oldflags & FR_GLOBAL) {
  365. u->flags = fr->global;
  366. u->flags_udef = fr->udef_global;
  367. if (!noshare && !(u->flags & USER_UNSHARED)) {
  368. fr->match = FR_GLOBAL;
  369. build_flags(buffer, fr, NULL);
  370. shareout(NULL, "a %s %s\n", u->handle, buffer);
  371. }
  372. }
  373. if ((oldflags & FR_BOT) && (u->flags & USER_BOT))
  374. set_user(&USERENTRY_BOTFL, u, (void *) fr->bot);
  375. /* Don't share bot attrs */
  376. if ((oldflags & FR_CHAN) && chname) {
  377. for (cr = u->chanrec; cr; cr = cr->next)
  378. if (!rfc_casecmp(chname, cr->channel))
  379. break;
  380. ch = findchan_by_dname(chname);
  381. if (!cr && ch) {
  382. cr = calloc(1, sizeof(struct chanuserrec));
  383. cr->next = u->chanrec;
  384. u->chanrec = cr;
  385. strncpyz(cr->channel, chname, sizeof cr->channel);
  386. }
  387. if (cr && ch) {
  388. cr->flags = fr->chan;
  389. cr->flags_udef = fr->udef_chan;
  390. if (!noshare && !(u->flags & USER_UNSHARED) && channel_shared(ch)) {
  391. fr->match = FR_CHAN;
  392. build_flags(buffer, fr, NULL);
  393. shareout(ch, "a %s %s %s\n", u->handle, buffer, chname);
  394. }
  395. }
  396. }
  397. fr->match = oldflags;
  398. }
  399. /* Always pass the dname (display name) to this function for chname <cybah>
  400. */
  401. void get_user_flagrec(struct userrec *u, struct flag_record *fr, const char *chname)
  402. {
  403. struct chanuserrec *cr = NULL;
  404. if (!u) {
  405. fr->global = fr->udef_global = fr->chan = fr->udef_chan = fr->bot = 0;
  406. return;
  407. }
  408. if (fr->match & FR_GLOBAL) {
  409. fr->global = u->flags;
  410. fr->udef_global = u->flags_udef;
  411. } else {
  412. fr->global = 0;
  413. fr->udef_global = 0;
  414. }
  415. if (fr->match & FR_BOT) {
  416. fr->bot = (long) get_user(&USERENTRY_BOTFL, u);
  417. } else
  418. fr->bot = 0;
  419. if (fr->match & FR_CHAN) {
  420. if (fr->match & FR_ANYWH) {
  421. fr->chan = u->flags;
  422. fr->udef_chan = u->flags_udef;
  423. for (cr = u->chanrec; cr; cr = cr->next)
  424. if (findchan_by_dname(cr->channel)) {
  425. fr->chan |= cr->flags;
  426. fr->udef_chan |= cr->flags_udef;
  427. }
  428. } else {
  429. if (chname)
  430. for (cr = u->chanrec; cr; cr = cr->next)
  431. if (!rfc_casecmp(chname, cr->channel))
  432. break;
  433. if (cr) {
  434. fr->chan = cr->flags;
  435. fr->udef_chan = cr->flags_udef;
  436. } else {
  437. fr->chan = 0;
  438. fr->udef_chan = 0;
  439. }
  440. }
  441. }
  442. }
  443. static int botfl_unpack(struct userrec *u, struct user_entry *e)
  444. {
  445. struct flag_record fr = {FR_BOT, 0, 0, 0, 0, 0};
  446. break_down_flags(e->u.list->extra, &fr, NULL);
  447. list_type_kill(e->u.list);
  448. e->u.ulong = fr.bot;
  449. return 1;
  450. }
  451. static int botfl_pack(struct userrec *u, struct user_entry *e)
  452. {
  453. char x[100] = "";
  454. struct flag_record fr = {FR_BOT, 0, 0, 0, 0, 0};
  455. fr.bot = e->u.ulong;
  456. e->u.list = calloc(1, sizeof(struct list_type));
  457. e->u.list->next = NULL;
  458. e->u.list->extra = calloc(1, build_flags (x, &fr, NULL) + 1);
  459. strcpy(e->u.list->extra, x);
  460. return 1;
  461. }
  462. static int botfl_kill(struct user_entry *e)
  463. {
  464. free(e);
  465. return 1;
  466. }
  467. static int botfl_write_userfile(FILE *f, struct userrec *u, struct user_entry *e)
  468. {
  469. char x[100] = "";
  470. struct flag_record fr = {FR_BOT, 0, 0, 0, 0, 0};
  471. fr.bot = e->u.ulong;
  472. build_flags(x, &fr, NULL);
  473. if (lfprintf(f, "--%s %s\n", e->type->name, x) == EOF)
  474. return 0;
  475. return 1;
  476. }
  477. static int botfl_set(struct userrec *u, struct user_entry *e, void *buf)
  478. {
  479. register long atr = ((long) buf & BOT_VALID);
  480. if (!(u->flags & USER_BOT))
  481. return 1; /* Don't even bother trying to set the
  482. flags for a non-bot */
  483. /* if ((atr & BOT_HUB) && (atr & BOT_ALT))
  484. atr &= ~BOT_ALT;*/
  485. if (atr & BOT_REJECT) {
  486. if (atr & BOT_SHARE)
  487. atr &= ~(BOT_SHARE | BOT_REJECT);
  488. if (atr & BOT_HUB)
  489. atr &= ~(BOT_HUB | BOT_REJECT);
  490. /* if (atr & BOT_ALT)
  491. atr &= ~(BOT_ALT | BOT_REJECT);*/
  492. }
  493. if (!(atr & BOT_SHARE))
  494. atr &= ~BOT_GLOBAL;
  495. e->u.ulong = atr;
  496. return 1;
  497. }
  498. static void botfl_display(int idx, struct user_entry *e, struct userrec *u)
  499. {
  500. struct flag_record fr = {FR_BOT, 0, 0, 0, 0, 0};
  501. char x[100] = "";
  502. fr.bot = e->u.ulong;
  503. build_flags(x, &fr, NULL);
  504. dprintf(idx, " BOT FLAGS: %s\n", x);
  505. }
  506. struct user_entry_type USERENTRY_BOTFL =
  507. {
  508. 0, /* always 0 ;) */
  509. 0,
  510. def_dupuser,
  511. botfl_unpack,
  512. botfl_pack,
  513. botfl_write_userfile,
  514. botfl_kill,
  515. def_get,
  516. botfl_set,
  517. botfl_display,
  518. "BOTFL"
  519. };
  520. /* private returns 0 if user has access, and 1 if they dont because of +private
  521. * This function does not check if the user has "op" access, it only checks if the user is
  522. * restricted by +private for the channel
  523. */
  524. int private(struct flag_record fr, struct chanset_t *chan, int type)
  525. {
  526. if (!channel_private(chan) || glob_bot(fr) || glob_owner(fr))
  527. return 0; /* user is implicitly not restricted by +private, they may however be lacking other flags */
  528. if (type == PRIV_OP) {
  529. /* |o implies all flags above. n| has access to all +private. Bots are exempt. */
  530. if (chan_op(fr))
  531. return 0;
  532. } else if (type == PRIV_VOICE) {
  533. if (chan_voice(fr))
  534. return 0;
  535. }
  536. return 1; /* user is restricted by +private */
  537. }
  538. int chk_op(struct flag_record fr, struct chanset_t *chan)
  539. {
  540. if (!chan || (!private(fr, chan, PRIV_OP) && !chk_deop(fr, chan))) {
  541. if (chan_op(fr) || (glob_op(fr) && !chan_deop(fr)))
  542. return 1;
  543. }
  544. return 0;
  545. }
  546. int chk_deop(struct flag_record fr, struct chanset_t *chan)
  547. {
  548. if (chan_deop(fr) || (glob_deop(fr) && !chan_op(fr)))
  549. return 1;
  550. else
  551. return 0;
  552. }
  553. int chk_voice(struct flag_record fr, struct chanset_t *chan)
  554. {
  555. if (!chan || (!private(fr, chan, PRIV_VOICE) && !chk_devoice(fr, chan))) {
  556. if (chan_voice(fr) || (glob_voice(fr) && !chan_quiet(fr)))
  557. return 1;
  558. }
  559. return 0;
  560. }
  561. int chk_devoice(struct flag_record fr, struct chanset_t *chan)
  562. {
  563. if (chan_quiet(fr) || (glob_quiet(fr) && !chan_voice(fr)))
  564. return 1;
  565. else
  566. return 0;
  567. }
  568. int chk_noflood(struct flag_record fr, struct chanset_t *chan)
  569. {
  570. return (chan_noflood(fr) || glob_noflood(fr));
  571. }
  572. int isupdatehub()
  573. {
  574. #ifdef HUB
  575. if (conf.bot->u && (conf.bot->u->flags & USER_UPDATEHUB))
  576. return 1;
  577. else
  578. #endif /* HUB */
  579. return 0;
  580. }
  581. int ischanhub()
  582. {
  583. if (conf.bot->u && (conf.bot->u->flags & USER_CHANHUB))
  584. return 1;
  585. else
  586. return 0;
  587. }
  588. int dovoice(struct chanset_t *chan)
  589. {
  590. struct flag_record fr = { FR_GLOBAL | FR_CHAN, 0, 0 };
  591. if (!chan) return 0;
  592. get_user_flagrec(conf.bot->u, &fr, chan->dname);
  593. if (glob_dovoice(fr) || chan_dovoice(fr))
  594. return 1;
  595. return 0;
  596. }
  597. int dolimit(struct chanset_t *chan)
  598. {
  599. struct flag_record fr = { FR_GLOBAL | FR_CHAN, 0, 0 };
  600. if (!chan) return 0;
  601. get_user_flagrec(conf.bot->u, &fr, chan->dname);
  602. if (glob_dolimit(fr) || chan_dolimit(fr))
  603. return 1;
  604. return 0;
  605. }
  606. int whois_access(struct userrec *user, struct userrec *whois_user)
  607. {
  608. struct flag_record fr = {FR_GLOBAL | FR_CHAN, 0, 0, 0, 0, 0};
  609. struct flag_record whois = {FR_GLOBAL | FR_CHAN, 0, 0, 0, 0, 0};
  610. get_user_flagrec(user, &fr, NULL);
  611. get_user_flagrec(whois_user, &whois, NULL);
  612. if ((glob_master(whois) && !glob_master(fr)) ||
  613. (glob_owner(whois) && !glob_owner(fr)) ||
  614. (glob_admin(whois) && !glob_admin(fr)) ||
  615. (glob_bot(whois) && !glob_master(fr)))
  616. return 0;
  617. return 1;
  618. }