mirror of
https://github.com/EnergyMech/energymech.git
synced 2025-12-17 15:36:50 +00:00
1766 lines
33 KiB
C
1766 lines
33 KiB
C
/*
|
|
|
|
EnergyMech, IRC bot software
|
|
Parts Copyright (c) 1997-2018 proton
|
|
|
|
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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
*/
|
|
#define USERLIST_C
|
|
#include "config.h"
|
|
|
|
#include "defines.h"
|
|
#include "structs.h"
|
|
#include "global.h"
|
|
#include "h.h"
|
|
#include "text.h"
|
|
#include "mcmd.h"
|
|
#include "usercombo.h"
|
|
|
|
/*
|
|
*
|
|
* reading and writing userlists
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* functions that handle userlist stuff (userlist_cmds[])
|
|
*/
|
|
|
|
void cfg_user(char *rest)
|
|
{
|
|
set_mallocdoer(cfg_user);
|
|
cfgUser = Calloc(sizeof(User) + strlen(rest));
|
|
stringcpy(cfgUser->name,rest);
|
|
}
|
|
|
|
#ifdef BOTNET
|
|
|
|
void cfg_modcount(char *rest)
|
|
{
|
|
int i;
|
|
|
|
i = asc2int(rest);
|
|
if (errno || i < 1)
|
|
return;
|
|
cfgUser->modcount = i;
|
|
}
|
|
|
|
#endif /* BOTNET */
|
|
|
|
void cfg_pass(char *rest)
|
|
{
|
|
Free((char**)&cfgUser->pass);
|
|
set_mallocdoer(cfg_pass);
|
|
cfgUser->pass = stringdup(rest);
|
|
}
|
|
|
|
void cfg_mask(char *rest)
|
|
{
|
|
addtouser(&cfgUser->mask,rest,TRUE);
|
|
}
|
|
|
|
void cfg_chan(char *rest)
|
|
{
|
|
addtouser(&cfgUser->chan,rest,TRUE);
|
|
}
|
|
|
|
struct
|
|
{
|
|
char modechar;
|
|
int modeflag;
|
|
|
|
} cfg_opt_flags[] =
|
|
/* dont gatekeep these flags with ifdefs,
|
|
make userfiles compatible between different compiles */
|
|
{
|
|
{ 'a', COMBO_AOP },
|
|
{ 'b', COMBO_BOUNCE },
|
|
{ 'e', COMBO_ECHO },
|
|
{ 'L', COMBO_NOSHARE },
|
|
{ 'R', COMBO_READONLY },
|
|
{ 'g', COMBO_GREETFILE },
|
|
{ 'r', COMBO_RANDLINE },
|
|
{ 'v', COMBO_AVOICE },
|
|
{ 0, 0, }};
|
|
|
|
void cfg_opt(char *rest)
|
|
{
|
|
int i;
|
|
|
|
/*
|
|
* `OPT abeLRgrp0vu100'
|
|
*/
|
|
while(*rest)
|
|
{
|
|
for(i=0;cfg_opt_flags[i].modechar;i++)
|
|
{
|
|
if (cfg_opt_flags[i].modechar == *rest)
|
|
cfgUser->x.comboflags |= cfg_opt_flags[i].modeflag;
|
|
}
|
|
if (*rest == 'p' && rest[1] >= '0' && rest[1] <= '4')
|
|
cfgUser->x.x.prot = rest[1] - '0';
|
|
if (*rest == 'u')
|
|
{
|
|
cfgUser->x.x.access = asc2int(rest+1);
|
|
return;
|
|
}
|
|
rest++;
|
|
}
|
|
}
|
|
|
|
void cfg_shit(char *rest)
|
|
{
|
|
char *channel,*mask,*from;
|
|
time_t backup_now,expire,when;
|
|
int shitlevel;
|
|
|
|
channel = chop(&rest);
|
|
mask = chop(&rest);
|
|
from = chop(&rest);
|
|
|
|
/*
|
|
* quick way of getting the shitlevel
|
|
* also, if channel, mask or from is NULL, this will fail (cuz *rest == 0)
|
|
*/
|
|
if (*rest < '0' || *rest > MAXSHITLEVELCHAR)
|
|
return;
|
|
shitlevel = *rest - '0';
|
|
chop(&rest); /* discard shitlevel argument */
|
|
|
|
/*
|
|
* convert the expiry time
|
|
*/
|
|
expire = asc2int(chop(&rest)); /* asc2int() can handle NULLs */
|
|
if (errno || expire < now)
|
|
return;
|
|
|
|
/*
|
|
* convert time when the shit was added
|
|
*/
|
|
when = asc2int(chop(&rest));
|
|
if (errno || *rest == 0) /* if *rest == 0, the reason is missing */
|
|
return;
|
|
|
|
/*
|
|
* finally, add the sucker
|
|
*/
|
|
backup_now = now;
|
|
now = when;
|
|
add_shit(from,channel,mask,rest,shitlevel,expire);
|
|
now = backup_now;
|
|
}
|
|
|
|
void cfg_kicksay(char *rest)
|
|
{
|
|
Client *backup;
|
|
|
|
backup = CurrentDCC;
|
|
CurrentDCC = (Client*)&CoreClient;
|
|
do_kicksay((char*)CoreUser.name,NULL,rest,0);
|
|
CurrentDCC = backup;
|
|
}
|
|
|
|
#ifdef GREET
|
|
|
|
void cfg_greet(char *rest)
|
|
{
|
|
Free((char**)&cfgUser->greet);
|
|
set_mallocdoer(cfg_greet);
|
|
cfgUser->greet = stringdup(rest);
|
|
}
|
|
|
|
#endif /* GREET */
|
|
|
|
#ifdef NOTE
|
|
|
|
void cfg_note(char *rest)
|
|
{
|
|
Strp *sp,**np;
|
|
|
|
np = &cfgUser->note;
|
|
while(*np)
|
|
np = &(*np)->next;
|
|
*np = sp = Calloc(sizeof(Strp) + strlen(rest));
|
|
/* Calloc sets to zero sp->next = NULL; */
|
|
stringcpy(sp->p,rest);
|
|
}
|
|
|
|
#endif /* NOTE */
|
|
|
|
void user_sync(void)
|
|
{
|
|
User *user;
|
|
|
|
user = add_user(cfgUser->name,cfgUser->pass,0);
|
|
/* memcpy copies all access and userflags */
|
|
memcpy(&user->mask,&cfgUser->mask,((char*)&user->pass - (char*)&user->mask));
|
|
|
|
#ifdef BOTNET
|
|
user->tick = global_tick++;
|
|
#endif /* BOTNET */
|
|
|
|
Free((char**)&cfgUser->pass);
|
|
Free((char**)&cfgUser);
|
|
}
|
|
|
|
#define FL_ONEARG 1
|
|
#define FL_NEEDUSER 2 /* userfile */
|
|
#define FL_NEWUSER 4 /* userfile */
|
|
|
|
typedef struct CommandStruct
|
|
{
|
|
char *name;
|
|
void (*function)(char *);
|
|
int flags;
|
|
|
|
} ConfCommand;
|
|
|
|
const ConfCommand userlist_cmds[] =
|
|
{
|
|
/*
|
|
* users
|
|
*/
|
|
{ "USER", cfg_user, FL_NEWUSER | FL_ONEARG },
|
|
{ "PASS", cfg_pass, FL_NEEDUSER | FL_ONEARG },
|
|
{ "MASK", cfg_mask, FL_NEEDUSER | FL_ONEARG },
|
|
{ "CHAN", cfg_chan, FL_NEEDUSER | FL_ONEARG },
|
|
{ "OPT", cfg_opt, FL_NEEDUSER },
|
|
{ "SHIT", cfg_shit, 0 },
|
|
{ "KICKSAY", cfg_kicksay, 0 },
|
|
#ifdef GREET
|
|
{ "GREET", cfg_greet, FL_NEEDUSER },
|
|
#endif /* GREET */
|
|
#ifdef NOTE
|
|
{ "NOTE", cfg_note, FL_NEEDUSER },
|
|
#endif /* NOTE */
|
|
#ifdef BOTNET
|
|
{ "MODCOUNT", cfg_modcount, FL_NEEDUSER },
|
|
#endif /* BOTNET */
|
|
{ NULL, }};
|
|
|
|
int read_userlist_callback(char *line)
|
|
{
|
|
char *command;
|
|
int i;
|
|
|
|
fix_config_line(line);
|
|
command = chop(&line);
|
|
for(i=0;userlist_cmds[i].name;i++)
|
|
{
|
|
if (!stringcasecmp(command,userlist_cmds[i].name))
|
|
break;
|
|
}
|
|
if (userlist_cmds[i].name)
|
|
{
|
|
if (!cfgUser && (userlist_cmds[i].flags & FL_NEEDUSER))
|
|
{
|
|
#ifdef DEBUG
|
|
debug("[RUC] cfgUser is NULL for command that requires it to be set (%s)\n",command);
|
|
#endif /* DEBUG */
|
|
return(FALSE);
|
|
}
|
|
if ((userlist_cmds[i].flags & FL_NEWUSER) && cfgUser)
|
|
user_sync();
|
|
if (userlist_cmds[i].flags & FL_ONEARG)
|
|
{
|
|
if ((line = chop(&line)) == NULL)
|
|
return(FALSE);
|
|
}
|
|
userlist_cmds[i].function(line);
|
|
}
|
|
return(FALSE);
|
|
}
|
|
|
|
int read_userlist(char *filename)
|
|
{
|
|
User *user,*u2;
|
|
User *olduserlist;
|
|
User *newuserlist;
|
|
int in;
|
|
#ifdef DEBUG
|
|
int r;
|
|
|
|
if (!filename)
|
|
{
|
|
#ifdef NEWBIE
|
|
if (startup)
|
|
to_file(1,"read_userlist: USERFILE filename empty\n");
|
|
#endif /* NEWBIE */
|
|
debug("(read_userlist) filename is NULL\n");
|
|
return(FALSE);
|
|
}
|
|
if (*filename == '<') /* read only userfile */
|
|
filename++;
|
|
if ((r = is_safepath(filename,FILE_MAY_EXIST)) != FILE_IS_SAFE)
|
|
{
|
|
#ifdef NEWBIE
|
|
if (startup)
|
|
to_file(1,"read_userlist: USERFILE filename is unsafe\n");
|
|
#endif /* NEWBIE */
|
|
debug("(read_userlist) %s: unsafe filename (%i)...\n",filename,r);
|
|
return(FALSE);
|
|
}
|
|
if ((in = open(filename,O_RDONLY)) < 0)
|
|
{
|
|
#ifdef NEWBIE
|
|
if (startup)
|
|
to_file(1,"read_userlist: USERFILE \"%s\": %s\n",filename,strerror(errno));
|
|
#endif /* NEWBIE */
|
|
debug("(read_userlist) failed to open \"%s\": %s\n",filename,strerror(errno));
|
|
return(FALSE);
|
|
}
|
|
#else /* ifdef DEBUG */
|
|
if (!filename)
|
|
{
|
|
#ifdef NEWBIE
|
|
if (startup)
|
|
to_file(1,"read_userlist: USERFILE filename empty\n");
|
|
#endif /* NEWBIE */
|
|
return(FALSE);
|
|
}
|
|
if (*filename == '<') /* read only userfile */
|
|
filename++;
|
|
if (is_safepath(filename,FILE_MAY_EXIST) != FILE_IS_SAFE)
|
|
{
|
|
#ifdef NEWBIE
|
|
if (startup)
|
|
to_file(1,"read_userlist: USERFILE filename is unsafe\n");
|
|
#endif /* NEWBIE */
|
|
return(FALSE);
|
|
}
|
|
if ((in = open(filename,O_RDONLY)) < 0)
|
|
{
|
|
#ifdef NEWBIE
|
|
if (startup)
|
|
to_file(1,"read_userlist: USERFILE \"%s\": %s\n",filename,strerror(errno));
|
|
#endif /* NEWBIE */
|
|
return(FALSE);
|
|
}
|
|
#endif /* ifdef DEBUG */
|
|
|
|
olduserlist = current->userlist;
|
|
cfgUser = current->userlist = NULL;
|
|
|
|
readline(in,&read_userlist_callback); /* readline closes in */
|
|
|
|
/*
|
|
* save the last user
|
|
*/
|
|
if (cfgUser)
|
|
user_sync();
|
|
|
|
newuserlist = current->userlist;
|
|
current->userlist = olduserlist;
|
|
|
|
for(user=newuserlist;user;user=user->next)
|
|
{
|
|
u2 = find_handle(user->name); /* find user in old userlist, may be NULL */
|
|
reset_userlink(u2,user);
|
|
}
|
|
|
|
/*
|
|
* remove the old userlist
|
|
*/
|
|
while(current->userlist)
|
|
remove_user(current->userlist);
|
|
|
|
/*
|
|
* re-apply shitlist that we just loaded
|
|
*/
|
|
check_shit();
|
|
|
|
current->userlist = newuserlist;
|
|
current->ul_save = 0;
|
|
return(TRUE);
|
|
}
|
|
|
|
int write_userlist(char *filename)
|
|
{
|
|
KickSay *ks;
|
|
Shit *shit;
|
|
Strp *ump;
|
|
User *user;
|
|
char *p,flags[7];
|
|
int i,f;
|
|
#ifdef DEBUG
|
|
int dodeb,r;
|
|
#endif /* DEBUG */
|
|
|
|
if (!filename)
|
|
return(FALSE);
|
|
|
|
if (!current->ul_save)
|
|
return(TRUE);
|
|
|
|
#ifdef DEBUG
|
|
if (*filename == '<') /* we dont write to read only userfiles */
|
|
{
|
|
debug("(write_userlist) %s: writing to read only userfile is prohibited...\n",filename);
|
|
return(FALSE);
|
|
}
|
|
if ((r = is_safepath(filename,FILE_MAY_EXIST)) != FILE_IS_SAFE)
|
|
{
|
|
debug("(write_userlist) %s: unsafe filename (%i)...\n",filename,r);
|
|
return(FALSE);
|
|
}
|
|
#else
|
|
if (*filename == '<') /* we dont write to read only userfiles */
|
|
return(FALSE);
|
|
if (is_safepath(filename,FILE_MAY_EXIST) != FILE_IS_SAFE)
|
|
return(FALSE);
|
|
#endif
|
|
|
|
if ((f = open(filename,O_WRONLY|O_CREAT|O_TRUNC,NEWFILEMODE)) < 0)
|
|
return(FALSE);
|
|
|
|
/*
|
|
* reset the change-counter
|
|
*/
|
|
current->ul_save = 0;
|
|
|
|
#ifdef DEBUG
|
|
dodeb = dodebug;
|
|
dodebug = FALSE;
|
|
#endif /* DEBUG */
|
|
|
|
for(user=current->userlist;user;user=user->next)
|
|
{
|
|
to_file(f,"\nuser\t\t%s\n",user->name);
|
|
for(ump=user->mask;ump;ump=ump->next)
|
|
to_file(f,"mask\t\t%s\n",ump->p);
|
|
for(ump=user->chan;ump;ump=ump->next)
|
|
to_file(f,"chan\t\t%s\n",ump->p);
|
|
/*
|
|
* `OPT aegrp0vu100'
|
|
*/
|
|
p = flags;
|
|
for(i=0;cfg_opt_flags[i].modeflag;i++)
|
|
{
|
|
if (user->x.comboflags & cfg_opt_flags[i].modeflag)
|
|
*(p++) = cfg_opt_flags[i].modechar;
|
|
}
|
|
*p = 0;
|
|
to_file(f,"opt\t\t%sp%iu%i\n",flags,user->x.x.prot,user->x.x.access);
|
|
/*
|
|
* `PASS <password>'
|
|
*/
|
|
if (user->pass)
|
|
to_file(f,"pass\t\t%s\n",user->pass);
|
|
#ifdef GREET
|
|
if (user->greet)
|
|
to_file(f,"greet\t\t%s\n",user->greet);
|
|
#endif /* GREET */
|
|
#ifdef NOTE
|
|
for(ump=user->note;ump;ump=ump->next)
|
|
to_file(f,"note\t\t%s\n",ump->p);
|
|
#endif /* NOTE */
|
|
#ifdef BOTNET
|
|
to_file(f,"modcount\t%i\n",user->modcount);
|
|
#endif /* BOTNET */
|
|
}
|
|
|
|
to_file(f,"\n");
|
|
|
|
for(shit=current->shitlist;shit;shit=shit->next)
|
|
{
|
|
to_file(f,"shit\t\t%s %s %s %i %lu %lu %s\n",
|
|
shit->chan,shit->mask,shit->from,shit->action,
|
|
shit->expire,shit->time,shit->reason);
|
|
}
|
|
|
|
to_file(f,"\n");
|
|
|
|
for(ks=current->kicklist;ks;ks=ks->next)
|
|
{
|
|
to_file(f,"kicksay\t\t%s %i \"%s\" %s\n",ks->chan,ks->action,ks->mask,ks->reason);
|
|
}
|
|
|
|
close(f);
|
|
|
|
#ifdef DEBUG
|
|
dodebug = dodeb;
|
|
#endif /* DEBUG */
|
|
return(TRUE);
|
|
}
|
|
|
|
/*
|
|
* adding and removing masks from user records
|
|
* rehash_chanusers() executed if mask is added or removed
|
|
* addtouser() add channel or mask to a user
|
|
* remfromuser() remove a channel or mask from a user
|
|
*/
|
|
|
|
void rehash_chanusers(void)
|
|
{
|
|
Chan *chan;
|
|
ChanUser *cu;
|
|
|
|
for(chan=current->chanlist;chan;chan=chan->next)
|
|
{
|
|
for(cu=chan->users;cu;cu=cu->next)
|
|
cu->user = get_user(get_nuh(cu),chan->name);
|
|
}
|
|
}
|
|
|
|
void addtouser(Strp **pp, const char *string, int rehash)
|
|
{
|
|
Strp *um;
|
|
|
|
while(*pp)
|
|
{
|
|
um = *pp;
|
|
if (!stringcasecmp(um->p,string))
|
|
return;
|
|
pp = &um->next;
|
|
}
|
|
|
|
set_mallocdoer(addtouser);
|
|
*pp = um = (Strp*)Calloc(sizeof(Strp) + strlen(string));
|
|
stringcpy(um->p,string);
|
|
if (rehash)
|
|
rehash_chanusers();
|
|
}
|
|
|
|
int remfromuser(Strp **pp, const char *string)
|
|
{
|
|
Strp *um;
|
|
|
|
while(*pp)
|
|
{
|
|
um = *pp;
|
|
if (!stringcasecmp(um->p,string))
|
|
{
|
|
*pp = um->next;
|
|
Free((char**)&um);
|
|
rehash_chanusers();
|
|
return(TRUE);
|
|
}
|
|
pp = &um->next;
|
|
}
|
|
return(FALSE);
|
|
}
|
|
|
|
/*
|
|
* make duplicates of a user on other local bots
|
|
*/
|
|
void mirror_user(User *user)
|
|
{
|
|
Mech *backup,*anybot;
|
|
User *newuser,*olduser;
|
|
#ifdef NOTE
|
|
Strp *notes;
|
|
#endif /* NOTE */
|
|
|
|
#ifdef BOTNET
|
|
/* dont mirror noshare users */
|
|
if (user->x.x.noshare)
|
|
return;
|
|
#endif /* BOTNET */
|
|
backup = current;
|
|
for(anybot=botlist;anybot;anybot=anybot->next)
|
|
{
|
|
if (anybot == backup) /* dont try to copy to myself, bad things will happen */
|
|
continue;
|
|
for(olduser=anybot->userlist;olduser;olduser=olduser->next)
|
|
{
|
|
if (!stringcasecmp(user->name,olduser->name))
|
|
{
|
|
#ifdef BOTNET
|
|
/* dont overwrite "better" users */
|
|
if (olduser->modcount >= user->modcount)
|
|
return;
|
|
/* dont overwrite read only users */
|
|
if (olduser->x.x.readonly)
|
|
return;
|
|
#endif /* BOTNET */
|
|
break;
|
|
}
|
|
}
|
|
#ifdef DEBUG
|
|
debug("(mirror_user) mirroring user %s[%i] to local bot %s(%i)\n",
|
|
user->name,user->x.x.access,getbotnick(anybot),anybot->guid);
|
|
#endif /* DEBUG */
|
|
|
|
current = anybot;
|
|
if (olduser)
|
|
{
|
|
#ifdef NOTE
|
|
/* hang on to notes */
|
|
notes = olduser->note;
|
|
olduser->note = NULL;
|
|
#endif /* NOTE */
|
|
remove_user(olduser); /* uses current->userlist */
|
|
/* authlist/chanuserlist/dcclist is now a minefield */
|
|
}
|
|
newuser = add_user(user->name,user->pass,user->x.x.access); /* uses current->userlist */
|
|
if (olduser)
|
|
{
|
|
#ifdef NOTE
|
|
newuser->note = notes;
|
|
#endif /* NOTE */
|
|
#ifdef DEBUG
|
|
debug("(1)\n");
|
|
#endif /* DEBUG */
|
|
reset_userlink(olduser,newuser); /* uses current->userlist */
|
|
/* authlist/chanuserlist/dcclist should now be safe again. */
|
|
}
|
|
#ifdef DEBUG
|
|
debug("(2)\n");
|
|
#endif /* DEBUG */
|
|
dupe_strp(user->mask,&newuser->mask); /* copy masks */
|
|
dupe_strp(user->chan,&newuser->chan); /* copy channels */
|
|
/* do not copy notes (creates spam) */
|
|
#ifdef DEBUG
|
|
debug("(3)\n");
|
|
#endif /* DEBUG */
|
|
newuser->x.comboflags = user->x.comboflags;
|
|
#ifdef BOTNET
|
|
newuser->x.x.readonly = 0; /* dont copy the RO flag */
|
|
newuser->modcount = user->modcount;
|
|
newuser->tick = user->tick; /* is this proper??? */
|
|
#endif /* BOTNET */
|
|
}
|
|
current = backup; /* assume my old identity */
|
|
#ifdef DEBUG
|
|
debug("(mirror_user) %s[%i] finished\n",user->name,user->x.x.access);
|
|
#endif /* DEBUG */
|
|
}
|
|
|
|
void mirror_userlist(void)
|
|
{
|
|
User *user;
|
|
|
|
#ifdef DEBUG
|
|
debug("(mirror_userlist) mirroring userlist of %s(%i)\n",getbotnick(current),current->guid);
|
|
#endif /* DEBUG */
|
|
|
|
for(user=current->userlist;user;user=user->next)
|
|
{
|
|
#ifdef BOTNET
|
|
if (!user->x.x.noshare)
|
|
#endif /* BOTNET */
|
|
mirror_user(user);
|
|
}
|
|
}
|
|
/*
|
|
* adding, removing, matching and searching for user records
|
|
*/
|
|
|
|
void reset_userlink(User *old, User *new)
|
|
{
|
|
Auth *auth,*nx_auth;
|
|
Chan *chan;
|
|
ChanUser *cu;
|
|
Client *client,*nx_client;
|
|
Spy *spy;
|
|
|
|
/*
|
|
* auth list
|
|
*/
|
|
for(auth=current->authlist;auth;)
|
|
{
|
|
nx_auth = auth->next;
|
|
if (auth->user == old)
|
|
{
|
|
if (new)
|
|
auth->user = new;
|
|
else
|
|
remove_auth(auth);
|
|
}
|
|
auth = nx_auth;
|
|
}
|
|
|
|
/*
|
|
* client list
|
|
*/
|
|
for(client=current->clientlist;client;)
|
|
{
|
|
nx_client = client->next;
|
|
if (client->user == old)
|
|
{
|
|
if (new)
|
|
client->user = new;
|
|
else
|
|
delete_client(client);
|
|
}
|
|
client = nx_client;
|
|
}
|
|
|
|
/*
|
|
* spy list
|
|
*/
|
|
if (new)
|
|
{
|
|
for(spy=current->spylist;spy;spy=spy->next)
|
|
{
|
|
if (spy->dest == old->name)
|
|
spy->dest = new->name;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* channel userlists
|
|
*/
|
|
for(chan=current->chanlist;chan;chan=chan->next)
|
|
{
|
|
for(cu=chan->users;cu;cu=cu->next)
|
|
{
|
|
if (cu->user == old)
|
|
cu->user = new;
|
|
}
|
|
}
|
|
}
|
|
|
|
void remove_user(User *user)
|
|
{
|
|
User **pp;
|
|
Strp *ump,*nxt;
|
|
|
|
pp = ¤t->userlist;
|
|
for(;(*pp);pp=&(*pp)->next)
|
|
{
|
|
if (*pp == user)
|
|
{
|
|
*pp = user->next;
|
|
#ifdef GREET
|
|
Free((char**)&user->greet);
|
|
#endif /* GREET */
|
|
for(ump=user->mask;ump;)
|
|
{
|
|
nxt = ump->next;
|
|
Free((char**)&ump);
|
|
ump = nxt;
|
|
}
|
|
#ifdef NOTE
|
|
for(ump=user->note;ump;)
|
|
{
|
|
nxt = ump->next;
|
|
Free((char**)&ump);
|
|
ump = nxt;
|
|
}
|
|
#endif /* NOTE */
|
|
Free((char**)&user);
|
|
current->ul_save++;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
User *add_user(char *handle, char *pass, int axs)
|
|
{
|
|
User *user;
|
|
char *p;
|
|
|
|
#ifdef DEBUG
|
|
debug("(add_user) handle = %s; pass = %s; axs = %i\n",
|
|
nullstr(handle),nullstr(pass),axs);
|
|
#endif /* DEBUG */
|
|
|
|
set_mallocdoer(add_user);
|
|
user = (User*)Calloc(sizeof(User) + StrlenX(handle,pass,NULL)); /* StrlenX() tolerates pass being NULL, Strlen2() does not. */
|
|
user->x.x.access = axs;
|
|
user->next = current->userlist;
|
|
current->userlist = user;
|
|
|
|
/*
|
|
* "name\0pass\0"
|
|
*/
|
|
p = stringcpy(user->name,handle) + 1;
|
|
if (pass)
|
|
{
|
|
user->pass = p;
|
|
stringcpy(user->pass,pass);
|
|
}
|
|
current->ul_save++;
|
|
return(user);
|
|
}
|
|
|
|
/*
|
|
* find the user record for a named handle
|
|
*/
|
|
User *find_handle(const char *handle)
|
|
{
|
|
User *user;
|
|
|
|
for(user=current->userlist;user;user=user->next)
|
|
{
|
|
if (!stringcasecmp(handle,user->name))
|
|
return(user);
|
|
}
|
|
return(NULL);
|
|
}
|
|
|
|
int userhaschannel(const User *user, const char *channel)
|
|
{
|
|
Strp *ump;
|
|
|
|
if (!channel)
|
|
return(TRUE);
|
|
for(ump=user->chan;ump;ump=ump->next)
|
|
{
|
|
if (*ump->p == '*' || !stringcasecmp(ump->p,channel))
|
|
return(TRUE);
|
|
}
|
|
return(FALSE);
|
|
}
|
|
|
|
/*
|
|
* Find the user that best matches the userhost
|
|
*/
|
|
User *get_user(const char *userhost, const char *channel)
|
|
{
|
|
Strp *ump;
|
|
User *user,*save;
|
|
int num,best;
|
|
|
|
if (CurrentDCC && CurrentDCC->user->name == userhost)
|
|
{
|
|
if (userhaschannel(CurrentDCC->user,channel))
|
|
return(CurrentDCC->user);
|
|
return(NULL);
|
|
}
|
|
|
|
save = NULL;
|
|
best = 0;
|
|
for(user=current->userlist;user;user=user->next)
|
|
{
|
|
if (userhaschannel(user,channel))
|
|
{
|
|
for(ump=user->mask;ump;ump=ump->next)
|
|
{
|
|
num = num_matches(ump->p,userhost);
|
|
if (num > best)
|
|
{
|
|
best = num;
|
|
save = user;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return(save);
|
|
}
|
|
|
|
/*
|
|
* highest channel on a given channel
|
|
*/
|
|
int get_useraccess(const char *userhost, const char *channel)
|
|
{
|
|
User *user;
|
|
|
|
if (is_bot(userhost))
|
|
return(BOTLEVEL);
|
|
|
|
if ((user = get_user(userhost,channel)) == NULL)
|
|
return(0);
|
|
return(user->x.x.access);
|
|
}
|
|
|
|
/*
|
|
* highest access regardless of channel
|
|
*/
|
|
int get_maxaccess(const char *userhost)
|
|
{
|
|
Strp *ump;
|
|
User *user;
|
|
int uaccess;
|
|
|
|
if (CurrentDCC && CurrentDCC->user->name == userhost)
|
|
return(CurrentDCC->user->x.x.access);
|
|
|
|
if (is_bot(userhost))
|
|
return(BOTLEVEL);
|
|
|
|
uaccess = 0;
|
|
for(user=current->userlist;user;user=user->next)
|
|
{
|
|
for(ump=user->mask;ump;ump=ump->next)
|
|
{
|
|
if (!matches(ump->p,userhost))
|
|
uaccess = (user->x.x.access > uaccess) ? user->x.x.access : uaccess;
|
|
}
|
|
}
|
|
return(uaccess);
|
|
}
|
|
|
|
int is_bot(const char *userhost)
|
|
{
|
|
Mech *bot;
|
|
|
|
for(bot=botlist;bot;bot=bot->next)
|
|
{
|
|
if (!nickcmp(userhost,getbotnick(bot)))
|
|
return(TRUE);
|
|
}
|
|
return(FALSE);
|
|
}
|
|
|
|
/*
|
|
* FIXME: does this apply to local bots?
|
|
*/
|
|
int get_protaction(Chan *chan, char *userhost)
|
|
{
|
|
Strp *ump;
|
|
User *user;
|
|
int prot;
|
|
|
|
prot = 0;
|
|
for(user=current->userlist;user;user=user->next)
|
|
{
|
|
if (userhaschannel(user,chan->name))
|
|
{
|
|
for(ump=user->mask;ump;ump=ump->next)
|
|
{
|
|
if (!matches(userhost,ump->p))
|
|
{
|
|
if (user->x.x.prot > prot)
|
|
prot = user->x.x.prot;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return(prot);
|
|
}
|
|
|
|
int usercanmodify(const char *from, const User *user)
|
|
{
|
|
User *u;
|
|
Strp *fmp,*ump;
|
|
int ua;
|
|
|
|
ua = (user->x.x.access >= 100) ? 100 : user->x.x.access;
|
|
|
|
for(u=current->userlist;u;u=u->next)
|
|
{
|
|
if (!CurrentDCC || CurrentDCC->user != u)
|
|
{
|
|
for(fmp=u->mask;fmp;fmp=fmp->next)
|
|
{
|
|
if (!matches(fmp->p,from))
|
|
break;
|
|
}
|
|
if (!fmp)
|
|
continue;
|
|
}
|
|
for(fmp=u->chan;fmp;fmp=fmp->next)
|
|
{
|
|
if (*fmp->p == '*' && u->x.x.access >= ua)
|
|
return(TRUE);
|
|
for(ump=user->chan;ump;ump=ump->next)
|
|
{
|
|
if (!stringcasecmp(ump->p,fmp->p))
|
|
{
|
|
if (u->x.x.access >= ua)
|
|
return(TRUE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return(FALSE);
|
|
}
|
|
|
|
/*
|
|
*
|
|
* user commands related to userlist
|
|
*
|
|
*/
|
|
|
|
void do_access(COMMAND_ARGS)
|
|
{
|
|
const char *chan;
|
|
char *nuh;
|
|
int level;
|
|
|
|
chan = get_channel(to,&rest);
|
|
if (rest && ((nuh = chop(&rest))))
|
|
{
|
|
if (*nuh == current->setting[CHR_CMDCHAR].char_var)
|
|
{
|
|
nuh++;
|
|
level = access_needed(nuh);
|
|
if (level < 0)
|
|
return;
|
|
else
|
|
if (level > 200)
|
|
to_user(from,"The command \"%s\" has been disabled",nuh);
|
|
else
|
|
to_user(from,"The access level needed for \"%s\" is: %i",nuh,level);
|
|
return;
|
|
}
|
|
if ((nuh = nick2uh(from,nuh)) == NULL)
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
nuh = from;
|
|
}
|
|
to_user(from,"Immortality Level for %s",nuh);
|
|
to_user(from,"Channel: %s Access: %i",chan,get_useraccess(nuh,chan));
|
|
}
|
|
|
|
#ifdef BOTNET
|
|
#define BOTNET_FMT "%s/%s/"
|
|
#else
|
|
#define BOTNET_FMT /* nothing */
|
|
#endif /* BOTNET */
|
|
|
|
#ifdef BOUNCE
|
|
#define BOUNCE_FMT "%s/"
|
|
#else
|
|
#define BOUNCE_FMT /* nothing */
|
|
#endif /* BOUNCE */
|
|
|
|
char *userlist_outputfmt[4] =
|
|
{
|
|
"Chan%s\t: %s",
|
|
"%s\t %s",
|
|
"Mask%s\t: %s",
|
|
"%s\t %s"
|
|
};
|
|
|
|
void do_userlist(COMMAND_ARGS)
|
|
{
|
|
Strp *ump;
|
|
User *user;
|
|
char *channel,*mask,*extra;
|
|
int minlevel = 0;
|
|
int maxlevel = BOTLEVEL;
|
|
int chanonly = FALSE;
|
|
int count;
|
|
int n;
|
|
#ifdef NOTE
|
|
int sz;
|
|
#endif /* NOTE */
|
|
|
|
mask = channel = NULL;
|
|
count = 0;
|
|
|
|
if (*rest)
|
|
{
|
|
if (*rest == '+' && rest[1] >= '0' && rest[1] <= '9')
|
|
{
|
|
minlevel = asc2int(rest+1);
|
|
}
|
|
else
|
|
if (*rest == '-' && rest[1] >= '0' && rest[1] <= '9')
|
|
{
|
|
maxlevel = asc2int(rest+1);
|
|
}
|
|
else
|
|
if (*rest == '-' && (*rest|32) == 'b')
|
|
{
|
|
minlevel = BOTLEVEL;
|
|
}
|
|
else
|
|
if (*rest == '-' && (*rest|32) == 'c')
|
|
{
|
|
chanonly = TRUE;
|
|
}
|
|
else
|
|
if (ischannel(rest))
|
|
{
|
|
channel = rest;
|
|
}
|
|
else
|
|
if (stringchr(rest,'*') != NULL)
|
|
{
|
|
mask = rest;
|
|
}
|
|
else
|
|
{
|
|
usage(from); /* usage for CurrentCmd->name */
|
|
return;
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
debug("(do_userlist) mask=%s minlevel=%i maxlevel=%i chanonly=%s\n",
|
|
(mask) ? mask : "NOMASK",minlevel,maxlevel,(chanonly) ? "Yes" : "No");
|
|
#endif /* DEBUG */
|
|
|
|
for(user=current->userlist;user;user=user->next)
|
|
{
|
|
if (user->x.x.access < minlevel)
|
|
continue;
|
|
if (user->x.x.access > maxlevel)
|
|
continue;
|
|
if (channel && (userhaschannel(user,channel) == FALSE))
|
|
continue;
|
|
if (chanonly && userhaschannel(user,MATCH_ALL) == TRUE)
|
|
continue;
|
|
ump = NULL;
|
|
if (mask)
|
|
{
|
|
for(ump=user->mask;ump;ump=ump->next)
|
|
{
|
|
if (matches(mask,ump->p))
|
|
break;
|
|
}
|
|
}
|
|
if (ump)
|
|
continue;
|
|
|
|
table_buffer("User\t: %-11s [%3i/%s/%s/" BOTNET_FMT BOUNCE_FMT "%s/P%i]",
|
|
user->name,user->x.x.access,
|
|
(user->x.x.aop) ? "AO" : "--",
|
|
(user->x.x.avoice) ? "AV" : "--",
|
|
#ifdef BOTNET
|
|
(user->x.x.readonly) ? "RO" : "--",
|
|
(user->x.x.noshare) ? "NS" : "--",
|
|
#endif /* BOTNET */
|
|
#ifdef BOUNCE
|
|
(user->x.x.bounce) ? "BNC" : "---",
|
|
#endif /* BOUNCE */
|
|
(user->pass) ? "PW" : "--",
|
|
user->x.x.prot);
|
|
|
|
n = 0;
|
|
ump = user->chan;
|
|
extra = NULL;
|
|
while(ump)
|
|
{
|
|
if (!extra)
|
|
extra = (ump->next) ? "s" : "";
|
|
table_buffer(userlist_outputfmt[n],extra,ump->p);
|
|
extra = "";
|
|
n |= 1;
|
|
ump = ump->next;
|
|
if (!ump && n < 2)
|
|
{
|
|
n = 2;
|
|
extra = NULL;
|
|
ump = user->mask;
|
|
}
|
|
}
|
|
#ifdef GREET
|
|
if (user->greet)
|
|
{
|
|
table_buffer("Greet\t: %s%s",user->greet,
|
|
(user->x.x.greetfile) ? " (greetfile)" :
|
|
((user->x.x.randline) ? " (random line from file)" : ""));
|
|
}
|
|
#endif /* GREET */
|
|
#ifdef NOTE
|
|
if ((ump = user->note))
|
|
{
|
|
sz = n = 0;
|
|
for(;ump;ump=ump->next)
|
|
{
|
|
if (*ump->p == 1)
|
|
n++;
|
|
else
|
|
sz += strlen(ump->p);
|
|
}
|
|
table_buffer("Message\t: %i message%s (%i bytes)",n,(n == 1) ? "" : "s",sz);
|
|
}
|
|
#endif /* NOTE */
|
|
table_buffer(" ");
|
|
count++;
|
|
}
|
|
table_send(from,0);
|
|
to_user(from,"Total of %i users",count);
|
|
}
|
|
|
|
#define _ADD '+'
|
|
#define _SUB '-'
|
|
|
|
void do_user(COMMAND_ARGS)
|
|
{
|
|
/*
|
|
* on_msg checks: CARGS
|
|
*/
|
|
User *user;
|
|
Strp *ump;
|
|
char *handle,*pt,*mask,*nick,*chan,*anum,*pass,*encpass;
|
|
char mode;
|
|
char tmpmask[NUHLEN];
|
|
int change;
|
|
int newaccess,uaccess;
|
|
union usercombo combo;
|
|
int ch,ho;
|
|
|
|
mode = 0;
|
|
mask = NULL;
|
|
|
|
/*
|
|
* chop wont return NULL (on_msg checks CARGS)
|
|
*/
|
|
handle = chop(&rest);
|
|
if (*handle == '-' || *handle == '+')
|
|
{
|
|
if (handle[1] == 0)
|
|
{
|
|
mode = *handle;
|
|
handle = chop(&rest);
|
|
}
|
|
else
|
|
{
|
|
mode = *handle;
|
|
handle++;
|
|
}
|
|
}
|
|
|
|
if (!handle)
|
|
goto usage;
|
|
user = find_handle(handle);
|
|
|
|
if (mode == '+')
|
|
{
|
|
/*
|
|
* Usage: USER + <handle> <channel|*> <nick|mask> <level> [password]
|
|
*/
|
|
/*
|
|
* dont create duplicate handles
|
|
*/
|
|
if (user)
|
|
{
|
|
to_user(from,"User \"%s\" already exists",handle);
|
|
return;
|
|
}
|
|
chan = chop(&rest);
|
|
nick = chop(&rest);
|
|
anum = chop(&rest);
|
|
pass = chop(&rest);
|
|
|
|
newaccess = asc2int(anum);
|
|
if (errno)
|
|
goto usage;
|
|
|
|
if (*chan == '*')
|
|
chan = MATCH_ALL;
|
|
else
|
|
if (!ischannel(chan))
|
|
goto usage;
|
|
|
|
if ((uaccess = get_useraccess(from,chan)) < cmdaccess)
|
|
return;
|
|
/*
|
|
* check access level
|
|
*/
|
|
if ((newaccess != BOTLEVEL) && ((newaccess < 0) || (newaccess > OWNERLEVEL)))
|
|
{
|
|
to_user(from,"access must be in the range 0 - %i",OWNERLEVEL);
|
|
return;
|
|
}
|
|
if ((uaccess != OWNERLEVEL) && (newaccess > uaccess))
|
|
{
|
|
to_user(from,"access denied on %s",chan);
|
|
return;
|
|
}
|
|
/*
|
|
* convert and check nick/mask
|
|
*/
|
|
if ((mask = nick2uh(from,nick)) == NULL) /* nick2uh uses nuh_buf */
|
|
return;
|
|
stringcpy(tmpmask,mask);
|
|
#ifdef DEBUG
|
|
debug("(do_user) nick2uh(from \"%s\", nick \"%s\") = mask \"%s\"\n",from,nick,tmpmask);
|
|
#endif /* DEBUG */
|
|
#ifdef NEWBIE
|
|
if (!matches(tmpmask,"!@"))
|
|
{
|
|
to_user(from,"Problem adding %s (global mask)",tmpmask);
|
|
return;
|
|
}
|
|
#endif /* NEWBIE */
|
|
format_uh(tmpmask,FUH_USERHOST); /* format_uh uses local temporary buffer but copies result back into tmpmask */
|
|
/*
|
|
* dont duplicate users
|
|
*/
|
|
if (get_useraccess(tmpmask,chan))
|
|
{
|
|
to_user(from,"%s (%s) on %s is already a user",nick,tmpmask,chan);
|
|
return;
|
|
}
|
|
/*
|
|
* encrypt password
|
|
*/
|
|
encpass = (pass) ? makepass(pass) : NULL;
|
|
/*
|
|
* passwords for bots are never used
|
|
*/
|
|
if (newaccess == BOTLEVEL)
|
|
encpass = NULL;
|
|
|
|
/*
|
|
* add_user() touches current->ul_save for us
|
|
*/
|
|
user = add_user(handle,encpass,newaccess);
|
|
addtouser(&user->mask,tmpmask,FALSE); /* does not run rehash_chanusers(), does not clobber nuh_buf */
|
|
addtouser(&user->chan,chan,TRUE); /* clobbers nuh_buf */
|
|
#ifdef DEBUG
|
|
debug("(do_user) from %s, handle %s,\n\tmask %s, chan %s\n",from,handle,tmpmask,chan);
|
|
#endif /* DEBUG */
|
|
to_user(from,"%s has been added as %s on %s",handle,tmpmask,chan);
|
|
to_user(from,"Access level: %i%s%s",newaccess,(pass) ? " Password: " : "",(pass) ? pass : "");
|
|
#ifdef NEWUSER_SPAM
|
|
if ((newaccess != BOTLEVEL) && find_nuh(nick))
|
|
{
|
|
/*
|
|
* yay! its a nick! we can spam them!
|
|
*/
|
|
char cmdchar = current->setting[CHR_CMDCHAR].char_var;
|
|
|
|
to_server("NOTICE %s :%s has blessed you with %i levels of immortality\n",
|
|
nick,CurrentNick,newaccess);
|
|
to_server("NOTICE %s :My command character is %c\n",nick,cmdchar);
|
|
to_server("NOTICE %s :Use \026%c%s\026 for command help\n",nick,cmdchar,C_HELP);
|
|
if (encpass)
|
|
{
|
|
to_server("NOTICE %s :Password necessary for doing commands: %s\n",nick,pass);
|
|
to_server("NOTICE %s :If you do not like your password, use \026%c%s\026 to change it\n",
|
|
nick,cmdchar,C_PASSWD);
|
|
}
|
|
}
|
|
#endif /* NEWUSER_SPAM */
|
|
return;
|
|
}
|
|
|
|
if (!user)
|
|
{
|
|
to_user(from,TEXT_UNKNOWNUSER,handle);
|
|
return;
|
|
}
|
|
if (!usercanmodify(from,user))
|
|
{
|
|
to_user(from,TEXT_USEROWNSYOU,user->name);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* ".user - handle" passed access tests, remove it.
|
|
*/
|
|
if (mode == '-')
|
|
{
|
|
to_user(from,"User %s has been purged",handle);
|
|
#ifdef BOTNET
|
|
botnet_relay(NULL,"UD%i %s\n",user->modcount,user->name);
|
|
#endif /* BOTNET */
|
|
/*
|
|
* delete all references to the user record
|
|
*/
|
|
reset_userlink(user,NULL);
|
|
/*
|
|
* remove_user() touches current->ul_save for us
|
|
*/
|
|
remove_user(user);
|
|
return;
|
|
}
|
|
|
|
combo.comboflags = user->x.comboflags;
|
|
change = 0;
|
|
ch = ho = 0;
|
|
|
|
while((pt = chop(&rest)))
|
|
{
|
|
switch(*pt++)
|
|
{
|
|
case '+':
|
|
mode = TRUE;
|
|
break;
|
|
case '-':
|
|
mode = FALSE;
|
|
break;
|
|
default:
|
|
goto usage;
|
|
}
|
|
|
|
#ifdef BOTNET
|
|
if (!stringcasecmp(pt,"NS"))
|
|
combo.x.noshare = mode;
|
|
else
|
|
if (!stringcasecmp(pt,"RO"))
|
|
combo.x.readonly = mode;
|
|
else
|
|
#endif /* BOTNET */
|
|
#ifdef BOUNCE
|
|
if (!stringcasecmp(pt,"BNC"))
|
|
combo.x.bounce = mode;
|
|
else
|
|
#endif /* BOUNCE */
|
|
if (!stringcasecmp(pt,"AV"))
|
|
combo.x.avoice = mode;
|
|
else
|
|
if (!stringcasecmp(pt,"AO"))
|
|
combo.x.aop = mode;
|
|
else
|
|
if (!stringcasecmp(pt,"ECHO"))
|
|
combo.x.echo = mode;
|
|
else
|
|
if (!stringcasecmp(pt,"CHAN"))
|
|
{
|
|
ch = (mode) ? _ADD : _SUB;
|
|
mask = chop(&rest);
|
|
if (!mask || *rest) /* mask needs to be the last option */
|
|
goto usage;
|
|
}
|
|
else
|
|
if (!stringcasecmp(pt,"HOST"))
|
|
{
|
|
ho = (mode) ? _ADD : _SUB;
|
|
mask = chop(&rest);
|
|
if (!mask || *rest) /* mask needs to be the last option */
|
|
goto usage;
|
|
}
|
|
else
|
|
if (*pt == 'p' || *pt == 'P')
|
|
{
|
|
pt++;
|
|
if ((*pt >= '0') && (*pt <= '4') && (pt[1] == 0))
|
|
{
|
|
combo.x.prot = *pt - '0';
|
|
}
|
|
else
|
|
if ((*pt == 0) && (mode == _SUB))
|
|
{
|
|
combo.x.prot = 0;
|
|
}
|
|
else
|
|
goto usage;
|
|
}
|
|
else
|
|
if ((uaccess = asc2int(pt)) >= 0 && uaccess <= BOTLEVEL && errno == 0)
|
|
{
|
|
combo.x.access = uaccess;
|
|
}
|
|
else
|
|
goto usage;
|
|
|
|
change++;
|
|
}
|
|
if (!change)
|
|
{
|
|
usage:
|
|
usage(from); /* usage for CurrentCmd->name */
|
|
return;
|
|
}
|
|
change = 0;
|
|
|
|
/*
|
|
* make the actual changes
|
|
*/
|
|
if (ch == _SUB && remfromuser(&user->chan,mask))
|
|
{
|
|
change++;
|
|
}
|
|
else
|
|
if (ch == _ADD)
|
|
{
|
|
for(ump=user->chan;ump;ump=ump->next)
|
|
{
|
|
if (!stringcasecmp(ump->p,mask))
|
|
{
|
|
to_user(from,"Channel %s already exists for %s",mask,user->name);
|
|
return;
|
|
}
|
|
}
|
|
#ifdef NEWBIE
|
|
if (!ischannel(mask) && *mask != '*')
|
|
{
|
|
to_user(from,TEXT_CHANINVALID);
|
|
return;
|
|
}
|
|
#endif /* NEWBIE */
|
|
addtouser(&user->chan,mask,TRUE);
|
|
change++;
|
|
}
|
|
else
|
|
if (ho == _SUB && remfromuser(&user->mask,mask))
|
|
{
|
|
change++;
|
|
}
|
|
else
|
|
if (ho == _ADD)
|
|
{
|
|
for(ump=user->mask;ump;ump=ump->next)
|
|
{
|
|
if (!stringcasecmp(ump->p,mask))
|
|
{
|
|
to_user(from,"Mask %s already exists for %s",mask,user->name);
|
|
return;
|
|
}
|
|
}
|
|
#ifdef NEWBIE
|
|
/*
|
|
* newbies dont know what they're doing
|
|
*/
|
|
if (!matches(mask,"!@"))
|
|
{
|
|
to_user(from,"Problem adding %s (global mask)",mask);
|
|
return;
|
|
}
|
|
/* With ipv6 and other funky crap, this is no longer suitable */
|
|
/*
|
|
if (matches("*@?*.?*",mask))
|
|
{
|
|
to_user(from,"Problem adding %s (invalid mask)",mask);
|
|
return;
|
|
}
|
|
*/
|
|
#endif /* NEWBIE */
|
|
addtouser(&user->mask,mask,TRUE);
|
|
change++;
|
|
}
|
|
|
|
if (combo.comboflags != user->x.comboflags)
|
|
{
|
|
user->x.comboflags = combo.comboflags;
|
|
change++;
|
|
}
|
|
if (change)
|
|
{
|
|
to_user(from,TEXT_USERCHANGED,user->name);
|
|
current->ul_save++;
|
|
#ifdef BOTNET
|
|
user->modcount++;
|
|
user->tick = global_tick++;
|
|
#endif /* BOTNET */
|
|
mirror_user(user);
|
|
}
|
|
else
|
|
{
|
|
to_user(from,TEXT_USERNOTCHANGED,user->name);
|
|
}
|
|
}
|
|
|
|
void do_echo(COMMAND_ARGS)
|
|
{
|
|
/*
|
|
* on_msg checks: CARGS
|
|
*/
|
|
char *tmp;
|
|
|
|
if ((tmp = chop(&rest)))
|
|
{
|
|
if (!stringcasecmp(tmp,__STR_ON))
|
|
{
|
|
if (CurrentUser->x.x.echo == FALSE)
|
|
{
|
|
current->ul_save++;
|
|
CurrentUser->x.x.echo = TRUE;
|
|
mirror_user(CurrentUser);
|
|
}
|
|
to_user(from,TEXT_PARTYECHOON);
|
|
return;
|
|
}
|
|
if (!stringcasecmp(tmp,__STR_OFF))
|
|
{
|
|
if (CurrentUser->x.x.echo == TRUE)
|
|
{
|
|
current->ul_save++;
|
|
CurrentUser->x.x.echo = FALSE;
|
|
mirror_user(CurrentUser);
|
|
}
|
|
to_user(from,TEXT_PARTYECHOOFF);
|
|
return;
|
|
}
|
|
}
|
|
usage(from); /* usage for CurrentCmd->name */
|
|
}
|
|
|
|
|
|
void change_pass(User *user, char *pass)
|
|
{
|
|
User *new,**uptr;
|
|
char *enc;
|
|
|
|
enc = makepass(pass);
|
|
if (user->pass && strlen(user->pass) <= strlen(enc))
|
|
{
|
|
stringcpy(user->pass,enc);
|
|
#ifdef BOTNET
|
|
user->modcount++;
|
|
#endif /* BOTNET */
|
|
}
|
|
/*
|
|
* password is stuck in a solid malloc in a linked list
|
|
* add_user() touches current->ul_save for us
|
|
*/
|
|
new = add_user(user->name,makepass(pass),0);
|
|
memcpy(&new->mask,&user->mask,((char*)&user->pass - (char*)&user->mask));
|
|
#ifdef BOTNET
|
|
new->modcount++;
|
|
new->tick = global_tick++;
|
|
#endif /* BOTNET */
|
|
|
|
/*
|
|
* unlink the old user record
|
|
*/
|
|
uptr = &new->next;
|
|
while(*uptr)
|
|
{
|
|
if (*uptr == user)
|
|
{
|
|
*uptr = user->next;
|
|
break;
|
|
}
|
|
uptr = &(*uptr)->next;
|
|
}
|
|
reset_userlink(user,new);
|
|
Free((char**)&user);
|
|
mirror_user(new);
|
|
}
|
|
|
|
void do_passwd(COMMAND_ARGS)
|
|
{
|
|
/*
|
|
* on_msg checks: CARGS
|
|
*/
|
|
User *user;
|
|
char *pass,*savedpass;
|
|
|
|
if ((user = get_user(from,NULL)) == NULL)
|
|
return;
|
|
|
|
savedpass = pass = chop(&rest);
|
|
if (user->pass)
|
|
{
|
|
pass = chop(&rest);
|
|
if (!pass || !*pass)
|
|
{
|
|
usage(from); /* usage for CurrentCmd->name */
|
|
return;
|
|
}
|
|
}
|
|
if (strlen(pass) < MINPASSCHARS)
|
|
{
|
|
to_user(from,TEXT_PASS_SHORT);
|
|
return;
|
|
}
|
|
if (strlen(pass) > MAXPASSCHARS)
|
|
{
|
|
to_user(from,TEXT_PASS_LONG);
|
|
return;
|
|
}
|
|
if (user->pass && !passmatch(savedpass,user->pass))
|
|
{
|
|
to_user(from,TEXT_PASS_INCORRECT);
|
|
return;
|
|
}
|
|
to_user(from,TEXT_PASS_NEWSET);
|
|
/*
|
|
* all is well
|
|
* change_pass() -> add_user() and current->ul_save is touched
|
|
*/
|
|
change_pass(user,pass);
|
|
}
|
|
|
|
void do_setpass(COMMAND_ARGS)
|
|
{
|
|
/*
|
|
* on_msg checks: CARGS
|
|
*/
|
|
User *user;
|
|
char *nick,*pass;
|
|
|
|
nick = chop(&rest);
|
|
pass = chop(&rest);
|
|
|
|
if (!nick || !pass)
|
|
{
|
|
usage(from); /* usage for CurrentCmd->name */
|
|
return;
|
|
}
|
|
if (strlen(pass) < MINPASSCHARS)
|
|
{
|
|
to_user(from,"password must be at least %i characters long",MINPASSCHARS);
|
|
return;
|
|
}
|
|
if (strlen(pass) >= MAXPASSCHARS)
|
|
pass[MAXPASSCHARS] = 0;
|
|
if ((user = find_handle(nick)) == NULL)
|
|
{
|
|
to_user(from,TEXT_UNKNOWNUSER,nick);
|
|
return;
|
|
}
|
|
if (!usercanmodify(from,user))
|
|
{
|
|
to_user(from,TEXT_USEROWNSYOU,user->name);
|
|
return;
|
|
}
|
|
if (!stringcasecmp(pass,"none"))
|
|
{
|
|
user->pass = NULL;
|
|
to_user(from,"password for %s has been removed",user->name);
|
|
return;
|
|
}
|
|
to_user(from,"new password for %s has been set",user->name);
|
|
/*
|
|
* all is well
|
|
* change_pass() -> add_user() and current->ul_save is groped
|
|
*/
|
|
change_pass(user,pass);
|
|
}
|
|
|
|
void do_save(COMMAND_ARGS)
|
|
{
|
|
char *fname;
|
|
|
|
fname = current->setting[STR_USERFILE].str_var;
|
|
current->ul_save = 1;
|
|
if (!write_userlist(fname))
|
|
{
|
|
to_user(from,(fname) ? ERR_NOSAVE : ERR_NOUSERFILENAME,fname);
|
|
}
|
|
else
|
|
{
|
|
to_user(from,TEXT_LISTSAVED,fname);
|
|
}
|
|
#ifdef SEEN
|
|
if (seenfile && !write_seenlist())
|
|
{
|
|
to_user(from,TEXT_SEENNOSAVE,seenfile);
|
|
}
|
|
#endif /* SEEN */
|
|
#ifdef NOTIFY
|
|
if (current->notifylist)
|
|
write_notifylog();
|
|
#endif /* NOTIFY */
|
|
#ifdef TRIVIA
|
|
write_triviascore();
|
|
#endif /* TRIVIA */
|
|
}
|
|
|
|
void do_load(COMMAND_ARGS)
|
|
{
|
|
/*
|
|
* on_msg checks: GAXS
|
|
*/
|
|
char *fname;
|
|
|
|
fname = current->setting[STR_USERFILE].str_var;
|
|
if (!read_userlist(fname))
|
|
{
|
|
purge_shitlist();
|
|
purge_kicklist();
|
|
to_user(from,(fname) ? ERR_NOREAD : ERR_NOUSERFILENAME,fname);
|
|
}
|
|
else
|
|
{
|
|
to_user(from,TEXT_LISTREAD,fname);
|
|
}
|
|
#ifdef SEEN
|
|
if (seenfile && !read_seenlist())
|
|
{
|
|
to_user(TEXT_SEENNOLOAD,seenfile);
|
|
}
|
|
#endif /* SEEN */
|
|
#ifdef TRIVIA
|
|
read_triviascore();
|
|
#endif /* TRIVIA */
|
|
mirror_userlist();
|
|
}
|