/* flags.c * * Copyright (C) 1997 Robey Pointer * Copyright (C) 1999 - 2006 Eggheads Development Team * * 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. * * $Id: flags.c,v 1.11 2006-11-20 13:53:33 tothwolf Exp $ */ #include "main.h" #include "chanprog.h" /* findchan_by_dname */ #include "dcc.h" /* struct dcc_t */ #include "dccutil.h" /* shareout */ #include "mem.h" /* nfree */ #include "misc.h" /* strncpyz */ #include "rfc1459.h" /* rfc_casecmp */ #include "userent.h" /* list_type_kill */ extern int raw_log, noshare, allow_dk_cmds; extern struct dcc_t *dcc; /* Some flags are mutually exclusive -- this roots them out */ int sanity_check(int atr) { if ((atr & USER_BOT) && (atr & (USER_PARTY | USER_MASTER | USER_COMMON | USER_OWNER))) atr &= ~(USER_PARTY | USER_MASTER | USER_COMMON | USER_OWNER); if ((atr & USER_OP) && (atr & USER_DEOP)) atr &= ~(USER_OP | USER_DEOP); if ((atr & USER_HALFOP) && (atr & USER_DEHALFOP)) atr &= ~(USER_HALFOP | USER_DEHALFOP); if ((atr & USER_AUTOOP) && (atr & USER_DEOP)) atr &= ~(USER_AUTOOP | USER_DEOP); if ((atr & USER_AUTOHALFOP) && (atr & USER_DEHALFOP)) atr &= ~(USER_AUTOHALFOP | USER_DEHALFOP); if ((atr & USER_VOICE) && (atr & USER_QUIET)) atr &= ~(USER_VOICE | USER_QUIET); if ((atr & USER_GVOICE) && (atr & USER_QUIET)) atr &= ~(USER_GVOICE | USER_QUIET); /* Can't be owner without also being master */ if (atr & USER_OWNER) atr |= USER_MASTER; /* Master implies botmaster, op and janitor */ if (atr & USER_MASTER) atr |= USER_BOTMAST | USER_OP | USER_JANITOR; /* Can't be botnet master without party-line access */ if (atr & USER_BOTMAST) atr |= USER_PARTY; /* Janitors can use the file area */ if (atr & USER_JANITOR) atr |= USER_XFER; /* Ops should be halfops */ if (atr & USER_OP) atr |= USER_HALFOP; return atr; } /* Sanity check on channel attributes */ int chan_sanity_check(int chatr, int atr) { if ((chatr & USER_OP) && (chatr & USER_DEOP)) chatr &= ~(USER_OP | USER_DEOP); if ((chatr & USER_HALFOP) && (chatr & USER_DEHALFOP)) chatr &= ~(USER_HALFOP | USER_DEHALFOP); if ((chatr & USER_AUTOOP) && (chatr & USER_DEOP)) chatr &= ~(USER_AUTOOP | USER_DEOP); if ((chatr & USER_AUTOHALFOP) && (chatr & USER_DEHALFOP)) chatr &= ~(USER_AUTOHALFOP | USER_DEHALFOP); if ((chatr & USER_VOICE) && (chatr & USER_QUIET)) chatr &= ~(USER_VOICE | USER_QUIET); if ((chatr & USER_GVOICE) && (chatr & USER_QUIET)) chatr &= ~(USER_GVOICE | 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; /* Op implies halfop */ if (chatr & USER_OP) chatr |= USER_HALFOP; /* Can't be +s on chan unless you're a bot */ if (!(atr & USER_BOT)) chatr &= ~BOT_SHARE; 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 * (^) halfop on any channel * (-) other */ char geticon(int idx) { struct flag_record fr = { FR_GLOBAL | FR_CHAN | FR_ANYWH, 0, 0, 0, 0, 0 }; if (!dcc[idx].user) return '-'; get_user_flagrec(dcc[idx].user, &fr, 0); if (glob_owner(fr) || chan_owner(fr)) return '*'; if (glob_master(fr) || chan_master(fr)) return '+'; if (glob_botmast(fr)) return '%'; if (glob_op(fr) || chan_op(fr)) return '@'; if (glob_halfop(fr) || chan_halfop(fr)) return '^'; return '-'; } void break_down_flags(const char *string, struct flag_record *plus, struct flag_record *minus) { struct flag_record *which = plus; int mode = 0; /* 0 = glob, 1 = chan, 2 = bot */ int flags = plus->match; if (!(flags & FR_GLOBAL)) { if (flags & FR_BOT) mode = 2; else if (flags & FR_CHAN) mode = 1; else return; /* We dont actually want any..huh? */ } egg_bzero(plus, sizeof(struct flag_record)); if (minus) egg_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 (!mode) { if (*string == '|') plus->match = FR_OR; else plus->match = FR_AND; } which = plus; mode++; if ((mode == 2) && !(flags & (FR_CHAN | FR_BOT))) string = ""; else if (mode == 3) mode = 1; break; default: if ((*string >= 'a') && (*string <= 'z')) { switch (mode) { case 0: which->global |=1 << (*string - 'a'); break; case 1: which->chan |= 1 << (*string - 'a'); break; case 2: which->bot |= 1 << (*string - 'a'); } } else if ((*string >= 'A') && (*string <= 'Z')) { switch (mode) { case 0: which->udef_global |= 1 << (*string - 'A'); break; case 1: which->udef_chan |= 1 << (*string - 'A'); break; } } else if ((*string >= '0') && (*string <= '9')) { switch (mode) { /* Map 0->9 to A->K for glob/chan so they are not lost */ case 0: which->udef_global |= 1 << (*string - '0'); break; case 1: which->udef_chan |= 1 << (*string - '0'); break; case 2: which->bot |= BOT_FLAG0 << (*string - '0'); break; } } } string++; } for (which = plus; which; which = (which == plus ? minus : 0)) { which->global &=USER_VALID; which->udef_global &= 0x03ffffff; which->chan &= CHAN_VALID; which->udef_chan &= 0x03ffffff; which->bot &= BOT_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, int bot, int udef) { char x = 'a', *old = string; while (bot && (x <= 'z')) { if (bot & 1) *string++ = x; x++; bot = bot >> 1; } x = 'A'; while (udef && (x <= 'Z')) { if (udef & 1) *string++ = x; udef = udef >> 1; x++; } if (string == old) *string++ = '-'; return string - old; } static int bot2str(char *string, int bot) { char x = 'a', *old = string; while (x < 'v') { if (bot & 1) *string++ = x; x++; bot >>= 1; } x = '0'; while (x <= '9') { if (bot & 1) *string++ = x; x++; bot >>= 1; } 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 ||plus->udef_global)) *string++ = '+'; string += flag2str(string, plus->global, plus->udef_global); if (minus && (minus->global ||minus->udef_global)) { *string++ = '-'; string += flag2str(string, minus->global, minus->udef_global); } } else if (plus->match & FR_BOT) { if (minus && plus->bot) *string++ = '+'; string += bot2str(string, plus->bot); if (minus && minus->bot) { *string++ = '-'; string += bot2str(string, minus->bot); } } if (plus->match & FR_CHAN) { if (plus->match & (FR_GLOBAL | FR_BOT)) *string++ = (plus->match & FR_AND) ? '&' : '|'; if (minus && (plus->chan || plus->udef_chan)) *string++ = '+'; string += flag2str(string, plus->chan, plus->udef_chan); if (minus && (minus->chan || minus->udef_chan)) { *string++ = '-'; string += flag2str(string, minus->global, minus->udef_chan); } } if ((plus->match & (FR_BOT | FR_CHAN)) == (FR_BOT | FR_CHAN)) { *string++ = (plus->match & FR_AND) ? '&' : '|'; if (minus && plus->bot) *string++ = '+'; string += bot2str(string, plus->bot); if (minus && minus->bot) { *string++ = '-'; string += bot2str(string, minus->bot); } } if (string == old) { *string++ = '-'; *string = 0; return 0; } *string = 0; return string - old; } int flagrec_ok(struct flag_record *req, struct flag_record *have) { /* FIXME: flag masks with '&' in them won't be subject to * further tests below. Example: 'o&j' */ if (req->match & FR_AND) return flagrec_eq(req, have); else if (req->match & FR_OR) { int hav = have->global; /* Exception 1 - global +d/+k cant use -|-, unless they are +p */ if (!req->chan && !req->global &&!req->udef_global && !req->udef_chan) { if (!allow_dk_cmds) { if (glob_party(*have)) return 1; if (glob_kick(*have) || chan_kick(*have)) return 0; /* +k cant use -|- commands */ if (glob_deop(*have) || chan_deop(*have)) return 0; /* neither can +d's */ } return 1; } /* The +n/+m checks aren't needed anymore because +n/+m * automatically adds lower flags */ if (hav & req->global) return 1; if (have->chan & req->chan) return 1; if (have->udef_global & req->udef_global) return 1; if (have->udef_chan & req->udef_chan) return 1; return 0; } return 0; /* fr0k3 binding, dont pass it */ } 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->udef_global & have->udef_global) != req->udef_global) return 0; } if (req->match & FR_BOT) if ((req->bot & have->bot) != req->bot) return 0; if (req->match & FR_CHAN) { if ((req->chan & have->chan) != req->chan) return 0; if ((req->udef_chan & have->udef_chan) != req->udef_chan) return 0; } return 1; } else if (req->match & FR_OR) { if (!req->chan && !req->global && !req->udef_chan && !req->udef_global && !req->bot) return 1; if (req->match & FR_GLOBAL) { if (have->global & req->global) return 1; if (have->udef_global & req->udef_global) return 1; } if (req->match & FR_BOT) if (have->bot & req->bot) return 1; if (req->match & FR_CHAN) { if (have->chan & req->chan) return 1; if (have->udef_chan & req->udef_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) { struct chanuserrec *cr = NULL; int oldflags = fr->match; char buffer[100]; struct chanset_t *ch; if (!u) return; if (oldflags & FR_GLOBAL) { u->flags = fr->global; u->flags_udef = fr->udef_global; if (!noshare && !(u->flags & USER_UNSHARED)) { fr->match = FR_GLOBAL; build_flags(buffer, fr, NULL); shareout(NULL, "a %s %s\n", u->handle, buffer); } } if ((oldflags & FR_BOT) && (u->flags & USER_BOT)) set_user(&USERENTRY_BOTFL, u, (void *) fr->bot); /* Don't share bot attrs */ 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 = user_malloc(sizeof(struct chanuserrec)); egg_bzero(cr, sizeof(struct chanuserrec)); cr->next = u->chanrec; u->chanrec = cr; strncpyz(cr->channel, chname, sizeof cr->channel); } if (cr && ch) { cr->flags = fr->chan; cr->flags_udef = fr->udef_chan; if (!noshare && !(u->flags & USER_UNSHARED) && channel_shared(ch)) { fr->match = FR_CHAN; build_flags(buffer, fr, NULL); shareout(ch, "a %s %s %s\n", u->handle, buffer, chname); } } } fr->match = oldflags; } /* Always pass the dname (display name) to this function for chname */ void get_user_flagrec(struct userrec *u, struct flag_record *fr, const char *chname) { struct chanuserrec *cr = NULL; if (!u) { fr->global = fr->udef_global = fr->chan = fr->udef_chan = fr->bot = 0; return; } if (fr->match & FR_GLOBAL) { fr->global = u->flags; fr->udef_global = u->flags_udef; } else { fr->global = 0; fr->udef_global = 0; } if (fr->match & FR_BOT) { fr->bot = (long) get_user(&USERENTRY_BOTFL, u); } else fr->bot = 0; if (fr->match & FR_CHAN) { if (fr->match & FR_ANYWH) { fr->chan = u->flags; fr->udef_chan = u->flags_udef; for (cr = u->chanrec; cr; cr = cr->next) if (findchan_by_dname(cr->channel)) { fr->chan |= cr->flags; fr->udef_chan |= cr->flags_udef; } } else { if (chname) for (cr = u->chanrec; cr; cr = cr->next) if (!rfc_casecmp(chname, cr->channel)) break; if (cr) { fr->chan = cr->flags; fr->udef_chan = cr->flags_udef; } else { fr->chan = 0; fr->udef_chan = 0; } } } } static int botfl_unpack(struct userrec *u, struct user_entry *e) { struct flag_record fr = { FR_BOT, 0, 0, 0, 0, 0 }; break_down_flags(e->u.list->extra, &fr, NULL); list_type_kill(e->u.list); e->u.ulong = fr.bot; return 1; } static int botfl_pack(struct userrec *u, struct user_entry *e) { char x[100]; struct flag_record fr = { FR_BOT, 0, 0, 0, 0, 0 }; fr.bot = e->u.ulong; e->u.list = user_malloc(sizeof(struct list_type)); e->u.list->next = NULL; e->u.list->extra = user_malloc(build_flags(x, &fr, NULL) + 1); strcpy(e->u.list->extra, x); return 1; } static int botfl_kill(struct user_entry *e) { nfree(e); return 1; } static int botfl_write_userfile(FILE *f, struct userrec *u, struct user_entry *e) { char x[100]; struct flag_record fr = { FR_BOT, 0, 0, 0, 0, 0 }; fr.bot = e->u.ulong; build_flags(x, &fr, NULL); if (fprintf(f, "--%s %s\n", e->type->name, x) == EOF) return 0; return 1; } static int botfl_set(struct userrec *u, struct user_entry *e, void *buf) { register long atr = ((long) buf & BOT_VALID); if (!(u->flags & USER_BOT)) return 1; /* Don't even bother trying to set the * flags for a non-bot */ if ((atr & BOT_HUB) && (atr & BOT_ALT)) atr &= ~BOT_ALT; if (atr & BOT_REJECT) { if (atr & BOT_SHARE) atr &= ~(BOT_SHARE | BOT_REJECT); if (atr & BOT_HUB) atr &= ~(BOT_HUB | BOT_REJECT); if (atr & BOT_ALT) atr &= ~(BOT_ALT | BOT_REJECT); } if (!(atr & BOT_SHARE)) atr &= ~BOT_GLOBAL; e->u.ulong = atr; return 1; } static int botfl_tcl_get(Tcl_Interp *interp, struct userrec *u, struct user_entry *e, int argc, char **argv) { char x[100]; struct flag_record fr = { FR_BOT, 0, 0, 0, 0, 0 }; fr.bot = e->u.ulong; build_flags(x, &fr, NULL); Tcl_AppendResult(interp, x, NULL); return TCL_OK; } static int botfl_tcl_set(Tcl_Interp *irp, struct userrec *u, struct user_entry *e, int argc, char **argv) { struct flag_record fr = { FR_BOT, 0, 0, 0, 0, 0 }; BADARGS(4, 4, " handle BOTFL flags"); if (u->flags & USER_BOT) { /* Silently ignore for users */ break_down_flags(argv[3], &fr, NULL); botfl_set(u, e, (void *) fr.bot); } return TCL_OK; } static int botfl_expmem(struct user_entry *e) { return 0; } static void botfl_display(int idx, struct user_entry *e) { struct flag_record fr = { FR_BOT, 0, 0, 0, 0, 0 }; char x[100]; fr.bot = e->u.ulong; build_flags(x, &fr, NULL); dprintf(idx, " BOT FLAGS: %s\n", x); } struct user_entry_type USERENTRY_BOTFL = { 0, /* always 0 ;) */ 0, def_dupuser, botfl_unpack, botfl_pack, botfl_write_userfile, botfl_kill, def_get, botfl_set, botfl_tcl_get, botfl_tcl_set, botfl_expmem, botfl_display, "BOTFL" };