| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671 |
- /*
- * Copyright (C) 1997 Robey Pointer
- * Copyright (C) 1999 - 2002 Eggheads Development Team
- * Copyright (C) 2002 - 2014 Bryan Drewery
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
- /*
- * flags.c -- handles:
- * all the flag matching/conversion functions in one neat package :)
- *
- */
- #include "common.h"
- #include "crypt.h"
- #include "src/mod/share.mod/share.h"
- #include "rfc1459.h"
- #include "userrec.h"
- #include "misc.h"
- #include "dccutil.h"
- #include "userent.h"
- #include "users.h"
- #include "chanprog.h"
- flag_t FLAG[128];
- void
- init_flags()
- {
- unsigned char i;
- for (i = 0; i < 'A'; i++)
- FLAG[(int) i] = 0;
- for (; i <= 'Z'; i++)
- FLAG[(int) i] = (flag_t) 1 << (26 + (i - 'A'));
- for (; i < 'a'; i++)
- FLAG[(int) i] = 0;
- for (; i <= 'z'; i++)
- FLAG[(int) i] = (flag_t) 1 << (i - 'a');
- for (; i < 128; i++)
- FLAG[(int) i] = 0;
- }
- /* Some flags are mutually exclusive -- this roots them out
- */
- flag_t
- sanity_check(flag_t atr, int bot)
- {
- if (bot && (atr & (USER_PARTY | USER_MASTER | USER_OWNER | USER_ADMIN | USER_HUBA | USER_CHUBA)))
- atr &= ~(USER_PARTY | USER_MASTER | USER_OWNER | USER_ADMIN | USER_HUBA | USER_CHUBA);
- /* only bots should be there: */
- if (!bot && (atr & (BOT_DOLIMIT | BOT_DOVOICE | BOT_UPDATEHUB | BOT_CHANHUB | BOT_FLOODBOT )))
- atr &= ~(BOT_DOLIMIT | BOT_DOVOICE | BOT_UPDATEHUB | BOT_CHANHUB | BOT_FLOODBOT );
- if (atr & USER_AUTOOP)
- atr |= USER_OP;
- if ((atr & USER_OP) && (atr & USER_DEOP))
- atr &= ~(USER_OP | USER_DEOP);
- if ((atr & USER_AUTOOP) && (atr & USER_DEOP))
- atr &= ~(USER_AUTOOP | USER_DEOP);
- if ((atr & USER_VOICE) && (atr & USER_QUIET))
- atr &= ~(USER_VOICE | USER_QUIET);
- /* Can't be admin without also being owner and having hub access */
- if (atr & USER_ADMIN)
- atr |= USER_OWNER | USER_HUBA | USER_PARTY;
- /* Hub access gets chanhub access */
- if (atr & USER_HUBA)
- atr |= USER_CHUBA;
- if (atr & USER_OWNER) {
- atr |= USER_MASTER;
- }
- /* Master implies botmaster, op */
- if (atr & USER_MASTER)
- atr |= USER_OP;
- /* Can't be botnet master without party-line access */
- /* if (atr & USER_BOTMAST)
- atr |= USER_PARTY;
- */
- return atr;
- }
- /* Sanity check on channel attributes
- */
- flag_t
- chan_sanity_check(flag_t chatr, int bot)
- {
- /* these should only be global */
- if (chatr & (USER_PARTY | USER_ADMIN | USER_HUBA | USER_CHUBA | BOT_UPDATEHUB))
- chatr &= ~(USER_PARTY | USER_ADMIN | USER_HUBA | USER_CHUBA | BOT_UPDATEHUB);
- /* these should only be set on bots */
- if (!bot && (chatr & (BOT_DOLIMIT | BOT_DOVOICE | BOT_CHANHUB | BOT_FLOODBOT )))
- chatr &= ~(BOT_DOLIMIT | BOT_DOVOICE | BOT_CHANHUB | BOT_FLOODBOT );
- if ((chatr & USER_OP) && (chatr & USER_DEOP))
- chatr &= ~(USER_OP | USER_DEOP);
- if ((chatr & USER_AUTOOP) && (chatr & USER_DEOP))
- chatr &= ~(USER_AUTOOP | USER_DEOP);
- if ((chatr & USER_VOICE) && (chatr & USER_QUIET))
- chatr &= ~(USER_VOICE | USER_QUIET);
- /* Can't be channel owner without also being channel master */
- if (chatr & USER_OWNER)
- chatr |= USER_MASTER;
- /* Master implies op */
- if (chatr & USER_MASTER)
- chatr |= USER_OP;
- return chatr;
- }
- /* Get icon symbol for a user (depending on access level)
- *
- * (*) owner on any channel
- * (+) master on any channel
- * (%) botnet master
- * (@) op on any channel
- * (-) other
- */
- char
- geticon(int idx)
- {
- if (!dcc[idx].user)
- return '-';
- struct flag_record fr = { FR_GLOBAL | FR_CHAN | FR_ANYWH, 0, 0, 0 };
- get_user_flagrec(dcc[idx].user, &fr, NULL);
- if (glob_admin(fr))
- return '^';
- if (chan_owner(fr))
- return '*';
- if (chan_master(fr))
- return '+';
- if (chan_op(fr))
- return '@';
- return '-';
- }
- void
- break_down_flags(const char *string, struct flag_record *plus, struct flag_record *minus)
- {
- struct flag_record *which = plus;
- int chan = 0; /* 0 = glob, 1 = chan */
- int flags = plus->match;
- if (!(flags & FR_GLOBAL)) {
- if (flags & FR_CHAN)
- chan = 1;
- else
- return; /* We dont actually want any..huh? */
- }
- bzero(plus, sizeof(struct flag_record));
- if (minus)
- bzero(minus, sizeof(struct flag_record));
- plus->match = FR_OR; /* Default binding type OR */
- while (*string) {
- switch (*string) {
- case '+':
- which = plus;
- break;
- case '-':
- which = minus ? minus : plus;
- break;
- case '|':
- case '&':
- if (!chan) {
- if (*string == '|')
- plus->match = FR_OR;
- else
- plus->match = FR_AND;
- }
- which = plus;
- chan++;
- if (chan == 2)
- string = "";
- else if (chan == 3)
- chan = 1;
- break; /* switch() */
- default:
- {
- flag_t flagbit = FLAG[(int) *string];
- if (flagbit) {
- switch (chan) {
- case 0:
- /* which->global |= (flag_t) 1 << (*string - 'a'); */
- which->global |=flagbit;
- break;
- case 1:
- /* which->chan |= (flag_t) 1 << (*string - 'a'); */
- which->chan |= flagbit;
- break;
- }
- }
- }
- }
- string++;
- }
- for (which = plus; which; which = (which == plus ? minus : 0)) {
- if (flags & FR_BOT) {
- which->global &= BOT_VALID;
- which->chan &= BOT_CHAN_VALID;
- } else {
- which->global &= USER_VALID;
- which->chan &= USER_CHAN_VALID;
- }
- }
- plus->match |= flags;
- if (minus) {
- minus->match |= flags;
- if (!(plus->match & (FR_AND | FR_OR)))
- plus->match |= FR_OR;
- }
- }
- static int
- flag2str(char *string, flag_t flag)
- {
- unsigned char c;
- char *old = string;
- for (c = 0; c < 128; c++)
- if (flag & FLAG[(int) c])
- *string++ = c;
- if (string == old)
- *string++ = '-';
- return string - old;
- }
- int
- build_flags(char *string, struct flag_record *plus, struct flag_record *minus)
- {
- char *old = string;
- if (plus->match & FR_GLOBAL) {
- if (minus && plus->global)
- *string++ = '+';
- string += flag2str(string, plus->global);
- if (minus && minus->global) {
- *string++ = '-';
- string += flag2str(string, minus->global);
- }
- }
- if (plus->match & FR_CHAN) {
- if (plus->match & FR_GLOBAL)
- *string++ = (plus->match & FR_AND) ? '&' : '|';
- if (minus && plus->chan)
- *string++ = '+';
- string += flag2str(string, plus->chan);
- if (minus && minus->chan) {
- *string++ = '-';
- string += flag2str(string, minus->global);
- }
- }
- if (string == old) {
- *string++ = '-';
- *string = 0;
- return 0;
- }
- *string = 0;
- return string - old;
- }
- /* Returns 1 if flags match, 0 if they don't. */
- int
- flagrec_ok(struct flag_record *req, struct flag_record *have)
- {
- if (req->match & FR_AND) {
- return flagrec_eq(req, have);
- } else if (req->match & FR_OR) {
- int hav = have->global;
- /* no flags, is a match */
- if (!req->chan && !req->global)
- return 1;
- if (hav & req->global)
- return 1;
- if (have->chan & req->chan)
- return 1;
- return 0;
- }
- return 0;
- }
- /* Returns 1 if flags match, 0 if they don't. */
- int
- flagrec_eq(struct flag_record *req, struct flag_record *have)
- {
- if (req->match & FR_AND) {
- if (req->match & FR_GLOBAL) {
- if ((req->global &have->global) !=req->global)
- return 0;
- }
- if (req->match & FR_CHAN) {
- if ((req->chan & have->chan) != req->chan)
- return 0;
- }
- return 1;
- } else if (req->match & FR_OR) {
- if (!req->chan && !req->global)
- return 1;
- if (req->match & FR_GLOBAL) {
- if (have->global &req->global)
- return 1;
- }
- if (req->match & FR_CHAN) {
- if (have->chan & req->chan)
- return 1;
- }
- return 0;
- }
- return 0; /* fr0k3 binding, dont pass it */
- }
- void
- set_user_flagrec(struct userrec *u, struct flag_record *fr, const char *chname)
- {
- if (!u)
- return;
- struct chanuserrec *cr = NULL;
- flag_t oldflags = fr->match;
- char buffer[100] = "";
- struct chanset_t *ch;
- if (oldflags & FR_GLOBAL) {
- u->flags = fr->global;
- if (!noshare) {
- fr->match = FR_GLOBAL;
- build_flags(buffer, fr, NULL);
- shareout("a %s %s\n", u->handle, buffer);
- }
- }
- if ((oldflags & FR_CHAN) && chname) {
- for (cr = u->chanrec; cr; cr = cr->next)
- if (!rfc_casecmp(chname, cr->channel))
- break;
- ch = findchan_by_dname(chname);
- if (!cr && ch) {
- cr = (struct chanuserrec *) my_calloc(1, sizeof(struct chanuserrec));
- cr->next = u->chanrec;
- u->chanrec = cr;
- strlcpy(cr->channel, chname, sizeof cr->channel);
- }
- if (cr && ch) {
- cr->flags = fr->chan;
- if (!noshare) {
- fr->match = FR_CHAN;
- build_flags(buffer, fr, NULL);
- shareout("a %s %s %s\n", u->handle, buffer, chname);
- }
- }
- }
- fr->match = oldflags;
- }
- /* Always pass the dname (display name) to this function for chname <cybah>
- */
- void
- get_user_flagrec(const struct userrec *u, struct flag_record *fr, const char *chname, const struct chanset_t* chan)
- {
- fr->bot = 0;
- if (!u) {
- fr->global = fr->chan = 0;
- return;
- }
- if (u->bot)
- fr->bot = 1;
- fr->global = (fr->match & FR_GLOBAL) ? u->flags : 0;
- if (fr->match & FR_CHAN) {
- struct chanuserrec *cr = NULL;
- if ((fr->match & FR_ANYWH) || (fr->match & FR_ANYCH)) {
- if (fr->match & FR_ANYWH)
- fr->chan = u->flags;
- for (cr = u->chanrec; cr; cr = cr->next) {
- if (chan || findchan_by_dname(cr->channel)) {
- fr->chan |= cr->flags;
- }
- }
- } else {
- if (chname) {
- for (cr = u->chanrec; cr; cr = cr->next) {
- if (!rfc_casecmp(chname, cr->channel))
- break;
- }
- }
- if (cr) {
- fr->chan = cr->flags;
- } else {
- fr->chan = 0;
- }
- }
- }
- }
- /* private returns 0 if user has access, and 1 if they dont because of +private
- * This function does not check if the user has "op" access, it only checks if the user is
- * restricted by +private for the channel
- */
- int
- privchan(const struct flag_record fr, const struct chanset_t *chan, int type)
- {
- if (!chan || !channel_privchan(chan) || glob_bot(fr) || glob_owner(fr))
- return 0; /* user is implicitly not restricted by +private, they may however be lacking other flags */
- if (type == PRIV_OP) {
- /* |o implies all flags above. n| has access to all +private. Bots are exempt. */
- if (chan_op(fr))
- return 0;
- } else if (type == PRIV_VOICE) {
- if (chan_voice(fr) || chan_op(fr))
- return 0;
- }
- return 1; /* user is restricted by +private */
- }
- int
- real_chk_op(const struct flag_record fr, const struct chanset_t *chan, bool botbitch)
- {
- if (!chan && glob_op(fr))
- return 1;
- else if (!chan)
- return 0;
- if (!privchan(fr, chan, PRIV_OP) && !real_chk_deop(fr, chan, botbitch)) {
- if (chan_op(fr) || (glob_op(fr) && !chan_deop(fr)))
- return 1;
- }
- return 0;
- }
- int
- chk_autoop(const struct flag_record fr, const struct chanset_t *chan)
- {
- if (glob_bot(fr))
- return 0;
- if (!chan || (!channel_take(chan) && !privchan(fr, chan, PRIV_OP) && chk_op(fr, chan) && !chk_deop(fr, chan))) {
- if (channel_autoop(chan) || chan_autoop(fr) || glob_autoop(fr))
- return 1;
- }
- return 0;
- }
- int
- real_chk_deop(const struct flag_record fr, const struct chanset_t *chan, bool botbitch)
- {
- if (chan && botbitch && channel_botbitch(chan) && !glob_bot(fr))
- return 1;
- if (chan_deop(fr) || (glob_deop(fr) && !chan_op(fr)))
- return 1;
- else
- return 0;
- }
- int
- doresolv(const struct chanset_t *chan)
- {
- if (!chan)
- return 0;
- struct flag_record fr = { FR_GLOBAL | FR_CHAN | FR_BOT, 0, 0, 0 };
- get_user_flagrec(conf.bot->u, &fr, chan->dname);
- if (glob_doresolv(fr) || chan_doresolv(fr))
- return 1;
- return 0;
- }
- int
- dovoice(const struct chanset_t *chan)
- {
- if (!chan)
- return 0;
- struct flag_record fr = { FR_GLOBAL | FR_CHAN | FR_BOT, 0, 0, 0 };
- get_user_flagrec(conf.bot->u, &fr, chan->dname);
- if (glob_dovoice(fr) || chan_dovoice(fr))
- return 1;
- return 0;
- }
- int
- doflood(const struct chanset_t *chan)
- {
- struct flag_record fr = { FR_GLOBAL | FR_CHAN | FR_BOT, 0, 0, 0 };
- if (!chan)
- fr.match |= FR_ANYWH;
- get_user_flagrec(conf.bot->u, &fr, chan ? chan->dname : NULL);
- if (glob_doflood(fr) || chan_doflood(fr))
- return 1;
- return 0;
- }
- int
- dolimit(const struct chanset_t *chan)
- {
- if (!chan)
- return 0;
- struct flag_record fr = { FR_GLOBAL | FR_CHAN | FR_BOT, 0, 0, 0 };
- get_user_flagrec(conf.bot->u, &fr, chan->dname);
- if (glob_dolimit(fr) || chan_dolimit(fr))
- return 1;
- return 0;
- }
- int
- whois_access(struct userrec *user, struct userrec *whois_user)
- {
- if (user == whois_user)
- return 1;
- struct flag_record fr = { FR_GLOBAL | FR_CHAN, 0, 0, 0 }, whois = {
- FR_GLOBAL | FR_CHAN, 0, 0, 0};
- get_user_flagrec(user, &fr, NULL);
- get_user_flagrec(whois_user, &whois, NULL);
- /* Don't show hub bots from leaf bots. */
- if (!conf.bot->hub && whois_user->bot && bot_hublevel(whois_user) < 999)
- return 0;
- if (
- (isowner(whois_user->handle) && !isowner(user->handle)) ||
- (glob_admin(whois) && !glob_admin(fr)) ||
- (glob_owner(whois) && !glob_owner(fr)) ||
- (glob_master(whois) && !glob_master(fr)) ||
- (glob_bot(whois) && !glob_master(fr))
- )
- return 0;
- return 1;
- }
- deflag_t deflag_translate(const char *buf)
- {
- if (str_isdigit(buf))
- return (static_cast<deflag_t>(atoi(buf)));
- if (!strcasecmp(buf, "ignore"))
- return DEFLAG_IGNORE;
- else if (!strcasecmp(buf, "deop"))
- return DEFLAG_DEOP;
- else if (!strcasecmp(buf, "kick"))
- return DEFLAG_KICK;
- else if (!strcasecmp(buf, "delete") || !strcasecmp(buf, "remove"))
- return DEFLAG_DELETE;
- else if (!strcasecmp(buf, "react"))
- return DEFLAG_REACT;
- return DEFLAG_IGNORE;
- }
- void deflag_user(struct userrec *u, deflag_event_t why, const char *msg, const struct chanset_t *chan)
- {
- char tmp[50] = "", tmp2[1024] = "";
- struct flag_record fr = {FR_GLOBAL | FR_CHAN, 0, 0, 0 };
- int which = 0;
- switch (why) {
- case DEFLAG_EVENT_BADCOOKIE:
- strlcpy(tmp, "Bad op cookie", sizeof(tmp));
- which = chan->bad_cookie;
- break;
- case DEFLAG_EVENT_MANUALOP:
- strlcpy(tmp, STR("Manual op in -manop channel"), sizeof(tmp));
- which = chan->manop;
- break;
- case DEFLAG_EVENT_REVENGE_DEOP:
- strlcpy(tmp, STR("Deopped bot in +revenge channel"), sizeof(tmp));
- which = chan->revenge;
- break;
- case DEFLAG_EVENT_REVENGE_KICK:
- strlcpy(tmp, STR("Kicked bot in +revenge channel"), sizeof(tmp));
- which = chan->revenge;
- break;
- case DEFLAG_EVENT_REVENGE_BAN:
- strlcpy(tmp, STR("Banned bot in +revenge channel"), sizeof(tmp));
- which = chan->revenge;
- break;
- case DEFLAG_EVENT_MDOP:
- strlcpy(tmp, "Mass deop", sizeof(tmp));
- which = chan->mdop;
- break;
- case DEFLAG_EVENT_MOP:
- strlcpy(tmp, "Mass op", sizeof(tmp));
- which = chan->mop;
- break;
- default:
- simple_snprintf(tmp, sizeof(tmp), "Reason #%i", why);
- }
- if (which == DEFLAG_DEOP) {
- putlog(LOG_WARN, "*", "Setting %s +d (%s): %s", u->handle, tmp, msg);
- simple_snprintf(tmp2, sizeof(tmp2), "+d: %s (%s)", tmp, msg);
- set_user(&USERENTRY_COMMENT, u, tmp2);
- get_user_flagrec(u, &fr, chan->dname);
- if (fr.global == USER_DEOP)
- fr.match &= ~FR_GLOBAL;
- else
- fr.global = USER_DEOP;
- if (fr.chan == USER_DEOP)
- fr.match &= ~FR_CHAN;
- else
- fr.chan = USER_DEOP;
-
- /* did anything change? */
- if (fr.match)
- set_user_flagrec(u, &fr, chan->dname);
- } else if (which == DEFLAG_KICK) {
- putlog(LOG_WARN, "*", "Setting %s +dk (%s): %s", u->handle, tmp, msg);
- simple_snprintf(tmp2, sizeof(tmp2), "+dk: %s (%s)", tmp, msg);
- set_user(&USERENTRY_COMMENT, u, tmp2);
- get_user_flagrec(u, &fr, chan->dname);
- if (fr.global == (USER_DEOP|USER_KICK))
- fr.match &= ~FR_GLOBAL;
- else
- fr.global = USER_DEOP | USER_KICK;
-
- if (fr.chan == (USER_DEOP|USER_KICK))
- fr.match &= ~FR_CHAN;
- else
- fr.chan = USER_DEOP | USER_KICK;
- /* did anything change */
- if (fr.match)
- set_user_flagrec(u, &fr, chan->dname);
- } else if (which == DEFLAG_DELETE) {
- putlog(LOG_WARN, "*", "Deleting %s (%s): %s", u->handle, tmp, msg);
- deluser(u->handle);
- } else {
- putlog(LOG_WARN, "*", "No user flag effects for %s (%s): %s", u->handle, tmp, msg);
- simple_snprintf(tmp2, sizeof(tmp2), "Warning: %s (%s)", tmp, msg);
- set_user(&USERENTRY_COMMENT, u, tmp2);
- }
- }
- /* vim: set sts=2 sw=2 ts=8 et: */
|