/*
* flags.c -- handles:
* all the flag matching/conversion functions in one neat package :)
*
* $Id: flags.c,v 1.32 2006-03-28 02:35:50 wcc Exp $
*/
/*
* 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.
*/
#include "main.h"
extern int raw_log, require_p, noshare, allow_dk_cmds;
extern struct dcc_t *dcc;
int logmodes(char *s)
{
int i;
int res = 0;
for (i = 0; i < strlen(s); i++)
switch (s[i]) {
case 'm':
case 'M':
res |= LOG_MSGS;
break;
case 'p':
case 'P':
res |= LOG_PUBLIC;
break;
case 'j':
case 'J':
res |= LOG_JOIN;
break;
case 'k':
case 'K':
res |= LOG_MODES;
break;
case 'c':
case 'C':
res |= LOG_CMDS;
break;
case 'o':
case 'O':
res |= LOG_MISC;
break;
case 'b':
case 'B':
res |= LOG_BOTS;
break;
case 'r':
case 'R':
res |= raw_log ? LOG_RAW : 0;
break;
case 'w':
case 'W':
res |= LOG_WALL;
break;
case 'x':
case 'X':
res |= LOG_FILES;
break;
case 's':
case 'S':
res |= LOG_SERV;
break;
case 'd':
case 'D':
res |= LOG_DEBUG;
break;
case 'v':
case 'V':
res |= raw_log ? LOG_SRVOUT : 0;
break;
case 't':
case 'T':
res |= raw_log ? LOG_BOTNET : 0;
break;
case 'h':
case 'H':
res |= raw_log ? LOG_BOTSHARE : 0;
break;
case '1':
res |= LOG_LEV1;
break;
case '2':
res |= LOG_LEV2;
break;
case '3':
res |= LOG_LEV3;
break;
case '4':
res |= LOG_LEV4;
break;
case '5':
res |= LOG_LEV5;
break;
case '6':
res |= LOG_LEV6;
break;
case '7':
res |= LOG_LEV7;
break;
case '8':
res |= LOG_LEV8;
break;
case '*':
res |= LOG_ALL;
break;
}
return res;
}
char *masktype(int x)
{
static char s[24]; /* Change this if you change the levels */
char *p = s;
if (x & LOG_MSGS)
*p++ = 'm';
if (x & LOG_PUBLIC)
*p++ = 'p';
if (x & LOG_JOIN)
*p++ = 'j';
if (x & LOG_MODES)
*p++ = 'k';
if (x & LOG_CMDS)
*p++ = 'c';
if (x & LOG_MISC)
*p++ = 'o';
if (x & LOG_BOTS)
*p++ = 'b';
if ((x & LOG_RAW) && raw_log)
*p++ = 'r';
if (x & LOG_FILES)
*p++ = 'x';
if (x & LOG_SERV)
*p++ = 's';
if (x & LOG_DEBUG)
*p++ = 'd';
if (x & LOG_WALL)
*p++ = 'w';
if ((x & LOG_SRVOUT) && raw_log)
*p++ = 'v';
if ((x & LOG_BOTNET) && raw_log)
*p++ = 't';
if ((x & LOG_BOTSHARE) && raw_log)
*p++ = 'h';
if (x & LOG_LEV1)
*p++ = '1';
if (x & LOG_LEV2)
*p++ = '2';
if (x & LOG_LEV3)
*p++ = '3';
if (x & LOG_LEV4)
*p++ = '4';
if (x & LOG_LEV5)
*p++ = '5';
if (x & LOG_LEV6)
*p++ = '6';
if (x & LOG_LEV7)
*p++ = '7';
if (x & LOG_LEV8)
*p++ = '8';
if (p == s)
*p++ = '-';
*p = 0;
return s;
}
char *maskname(int x)
{
static char s[207]; /* Change this if you change the levels */
int i = 0;
s[0] = 0;
if (x & LOG_MSGS)
i += my_strcpy(s, "msgs, ");
if (x & LOG_PUBLIC)
i += my_strcpy(s + i, "public, ");
if (x & LOG_JOIN)
i += my_strcpy(s + i, "joins, ");
if (x & LOG_MODES)
i += my_strcpy(s + i, "kicks/modes, ");
if (x & LOG_CMDS)
i += my_strcpy(s + i, "cmds, ");
if (x & LOG_MISC)
i += my_strcpy(s + i, "misc, ");
if (x & LOG_BOTS)
i += my_strcpy(s + i, "bots, ");
if ((x & LOG_RAW) && raw_log)
i += my_strcpy(s + i, "raw, ");
if (x & LOG_FILES)
i += my_strcpy(s + i, "files, ");
if (x & LOG_SERV)
i += my_strcpy(s + i, "server, ");
if (x & LOG_DEBUG)
i += my_strcpy(s + i, "debug, ");
if (x & LOG_WALL)
i += my_strcpy(s + i, "wallops, ");
if ((x & LOG_SRVOUT) && raw_log)
i += my_strcpy(s + i, "server output, ");
if ((x & LOG_BOTNET) && raw_log)
i += my_strcpy(s + i, "botnet traffic, ");
if ((x & LOG_BOTSHARE) && raw_log)
i += my_strcpy(s + i, "share traffic, ");
if (x & LOG_LEV1)
i += my_strcpy(s + i, "level 1, ");
if (x & LOG_LEV2)
i += my_strcpy(s + i, "level 2, ");
if (x & LOG_LEV3)
i += my_strcpy(s + i, "level 3, ");
if (x & LOG_LEV4)
i += my_strcpy(s + i, "level 4, ");
if (x & LOG_LEV5)
i += my_strcpy(s + i, "level 5, ");
if (x & LOG_LEV6)
i += my_strcpy(s + i, "level 6, ");
if (x & LOG_LEV7)
i += my_strcpy(s + i, "level 7, ");
if (x & LOG_LEV8)
i += my_strcpy(s + i, "level 8, ");
if (i)
s[i - 2] = 0;
else
strcpy(s, "none");
return s;
}
/* 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 (!require_p && ((hav & USER_OP) || (have->chan & USER_OWNER)))
hav |= USER_PARTY;
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 <cybah>
*/
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"
};