1
0
mirror of https://github.com/unrealircd/unrealircd.git synced 2026-07-03 06:33:12 +02:00
Files
unrealircd/src/modules/m_svsmode.c
T
William Pitcock 3563c26a93 - Add support for account-notify client capability (#4098). This capability
can be used to request passive notifications for accountname changes.
2012-04-09 10:48:51 +02:00

680 lines
17 KiB
C

/*
* IRC - Internet Relay Chat, src/modules/m_svsmode.c
* (C) 2001 The UnrealIRCd Team
*
* SVSMODE and SVS2MODE commands
*
* See file AUTHORS in IRC package for additional names of
* the programmers.
*
* 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 1, 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.
*/
#include "config.h"
#include "struct.h"
#include "common.h"
#include "sys.h"
#include "numeric.h"
#include "msg.h"
#include "channel.h"
#include <time.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef _WIN32
#include <io.h>
#endif
#include <fcntl.h>
#include "h.h"
#include "proto.h"
#ifdef STRIPBADWORDS
#include "badwords.h"
#endif
#ifdef _WIN32
#include "version.h"
#endif
void add_send_mode_param(aChannel *chptr, aClient *from, char what, char mode, char *param);
DLLFUNC int m_svsmode(aClient *cptr, aClient *sptr, int parc, char *parv[]);
DLLFUNC int m_svs2mode(aClient *cptr, aClient *sptr, int parc, char *parv[]);
#define MSG_SVSMODE "SVSMODE"
#define TOK_SVSMODE "n"
#define MSG_SVS2MODE "SVS2MODE"
#define TOK_SVS2MODE "v"
ModuleHeader MOD_HEADER(m_svsmode)
= {
"m_svsmode",
"$Id$",
"command /svsmode and svs2mode",
"3.2-b8-1",
NULL
};
DLLFUNC int MOD_INIT(m_svsmode)(ModuleInfo *modinfo)
{
add_Command(MSG_SVSMODE, TOK_SVSMODE, m_svsmode, MAXPARA);
add_Command(MSG_SVS2MODE, TOK_SVS2MODE, m_svs2mode, MAXPARA);
MARK_AS_OFFICIAL_MODULE(modinfo);
return MOD_SUCCESS;
}
DLLFUNC int MOD_LOAD(m_svsmode)(int module_load)
{
return MOD_SUCCESS;
}
DLLFUNC int MOD_UNLOAD(m_svsmode)(int module_unload)
{
if (del_Command(MSG_SVSMODE, TOK_SVSMODE, m_svsmode) < 0 || del_Command(MSG_SVS2MODE, TOK_SVS2MODE, m_svs2mode) < 0)
{
sendto_realops("Failed to delete commands when unloading %s",
MOD_HEADER(m_svsmode).name);
}
return MOD_SUCCESS;
}
void unban_user(aClient *sptr, aChannel *chptr, aClient *acptr, char chmode)
{
Extban *extban;
Ban *ban, *bnext;
Ban **banlist;
char uhost[NICKLEN+USERLEN+HOSTLEN+6], vhost[NICKLEN+USERLEN+HOSTLEN+6];
char ihost[NICKLEN+USERLEN+HOSTLEN+6], chost[NICKLEN+USERLEN+HOSTLEN+6];
/* BUILD HOSTS */
*uhost = *vhost = *ihost = *chost = '\0';
strlcpy(uhost, make_nick_user_host(acptr->name,
acptr->user->username, acptr->user->realhost),
sizeof uhost);
if (GetIP(acptr)) /* only if we actually have an IP */
strlcpy(ihost, make_nick_user_host(acptr->name,
acptr->user->username, GetIP(acptr)),
sizeof ihost);
/* The next could have been an IsSetHost(), but I'm playing it safe with regards to backward compat. */
if (IsHidden(acptr) &&
!(*acptr->user->cloakedhost && !strcasecmp(acptr->user->virthost, acptr->user->cloakedhost)))
{
strlcpy(vhost, make_nick_user_host(acptr->name,
acptr->user->username, acptr->user->virthost),
sizeof vhost);
}
if (*acptr->user->cloakedhost) /* only if we know the cloaked host */
strlcpy(chost, make_nick_user_host(acptr->name,
acptr->user->username, acptr->user->cloakedhost),
sizeof chost);
/* SELECT BANLIST */
switch (chmode)
{
case 'b':
banlist = &chptr->banlist;
break;
case 'e':
banlist = &chptr->exlist;
break;
case 'I':
banlist = &chptr->invexlist;
break;
default:
abort();
}
/* DO THE ACTUAL WORK */
for (ban = *banlist; ban; ban = bnext)
{
bnext = ban->next;
if (!match(ban->banstr, uhost) ||
(*vhost && !match(ban->banstr, vhost)) ||
(*ihost && !match(ban->banstr, ihost)) ||
(*chost && !match(ban->banstr, chost)))
{
add_send_mode_param(chptr, sptr, '-', chmode,
ban->banstr);
del_listmode(banlist, chptr, ban->banstr);
}
else if (chmode != 'I' && *ban->banstr == '~' && (extban = findmod_by_bantype(ban->banstr[1])))
{
if (extban->options & EXTBOPT_CHSVSMODE)
{
if (extban->is_banned(acptr, chptr, ban->banstr, BANCHK_JOIN))
{
add_send_mode_param(chptr, acptr, '-', chmode, ban->banstr);
del_listmode(banlist, chptr, ban->banstr);
}
}
}
}
}
void clear_bans(aClient *sptr, aChannel *chptr, char chmode)
{
Extban *extban;
Ban *ban, *bnext;
Ban **banlist;
switch (chmode)
{
case 'b':
banlist = &chptr->banlist;
break;
case 'e':
banlist = &chptr->exlist;
break;
case 'I':
banlist = &chptr->invexlist;
break;
default:
abort();
}
for (ban = *banlist; ban; ban = bnext)
{
bnext = ban->next;
if (chmode != 'I' && (*ban->banstr == '~') && (extban = findmod_by_bantype(ban->banstr[1])))
{
if (!(extban->options & EXTBOPT_CHSVSMODE))
continue;
}
add_send_mode_param(chptr, sptr, '-', chmode, ban->banstr);
del_listmode(banlist, chptr, ban->banstr);
}
}
int channel_svsmode(aClient *cptr, aClient *sptr, int parc, char *parv[])
{
aChannel *chptr;
time_t ts;
aClient *acptr;
char *m;
int what;
int i = 4;
*parabuf = '\0';
modebuf[0] = 0;
if(!(chptr = find_channel(parv[1], NULL)))
return 0;
/* if (parc >= 4) {
return 0;
if (parc > 4) {
ts = TS2ts(parv[4]);
if (acptr->since != ts)
return 0;
}
}*/
ts = TS2ts(parv[parc-1]);
for(m = parv[2]; *m; m++) {
switch (*m) {
case '+':
what = MODE_ADD;
break;
case '-':
what = MODE_DEL;
break;
case 'q': {
Member *cm;
for (cm = chptr->members; cm; cm = cm->next) {
if (cm->flags & CHFL_CHANOWNER) {
Membership *mb;
mb = find_membership_link(cm->cptr->user->channel,
chptr);
add_send_mode_param(chptr, sptr, '-', 'q', cm->cptr->name);
cm->flags &= ~CHFL_CHANOWNER;
if (mb)
mb->flags = cm->flags;
}
}
}
break;
case 'a': {
Member *cm;
for (cm = chptr->members; cm; cm = cm->next) {
if (cm->flags & CHFL_CHANPROT) {
Membership *mb;
mb = find_membership_link(cm->cptr->user->channel,
chptr);
add_send_mode_param(chptr, sptr, '-', 'a', cm->cptr->name);
cm->flags &= ~CHFL_CHANPROT;
if (mb)
mb->flags = cm->flags;
}
}
}
break;
case 'o': {
Member *cm;
for (cm = chptr->members; cm; cm = cm->next) {
if (cm->flags & CHFL_CHANOP) {
Membership *mb;
mb = find_membership_link(cm->cptr->user->channel,
chptr);
add_send_mode_param(chptr, sptr, '-', 'o', cm->cptr->name);
cm->flags &= ~CHFL_CHANOP;
if (mb)
mb->flags = cm->flags;
}
}
}
break;
case 'h': {
Member *cm;
for (cm = chptr->members; cm; cm = cm->next) {
if (cm->flags & CHFL_HALFOP) {
Membership *mb;
mb = find_membership_link(cm->cptr->user->channel,
chptr);
add_send_mode_param(chptr, sptr, '-', 'h', cm->cptr->name);
cm->flags &= ~CHFL_HALFOP;
if (mb)
mb->flags = cm->flags;
}
}
}
break;
case 'v': {
Member *cm;
for (cm = chptr->members; cm; cm = cm->next) {
if (cm->flags & CHFL_VOICE) {
Membership *mb;
mb = find_membership_link(cm->cptr->user->channel,
chptr);
add_send_mode_param(chptr, sptr, '-', 'v', cm->cptr->name);
cm->flags &= ~CHFL_VOICE;
if (mb)
mb->flags = cm->flags;
}
}
}
break;
case 'b': {
Extban *extban;
Ban *ban, *bnext;
if (parc >= i) {
if (!(acptr = find_person(parv[i-1], NULL))) {
i++;
break;
}
if (ts && ts != acptr->since) {
i++;
break;
}
i++;
unban_user(sptr, chptr, acptr, 'b');
}
else {
clear_bans(sptr, chptr, 'b');
}
}
break;
case 'e': {
Extban *extban;
Ban *ban, *bnext;
if (parc >= i) {
if (!(acptr = find_person(parv[i-1], NULL))) {
i++;
break;
}
if (ts && ts != acptr->since) {
i++;
break;
}
i++;
unban_user(sptr, chptr, acptr, 'e');
}
else {
clear_bans(sptr, chptr, 'e');
}
}
break;
case 'I': {
Ban *ban, *bnext;
if (parc >= i) {
if (!(acptr = find_person(parv[i-1], NULL))) {
i++;
break;
}
if (ts && ts != acptr->since) {
i++;
break;
}
i++;
unban_user(sptr, chptr, acptr, 'I');
}
else {
clear_bans(sptr, chptr, 'I');
}
}
break;
#ifdef DEBUGMODE
default:
sendto_realops("Warning! Invalid mode `%c' used with 'SVSMODE %s %s %s' (from %s %s)",
*m, chptr->chname, parv[2], parv[3] ? parv[3] : "",
cptr->name, sptr->name);
break;
#endif
}
}
/* only send message if modes have changed */
if (*parabuf) {
sendto_channel_butserv(chptr, sptr, ":%s MODE %s %s %s", sptr->name, chptr->chname,
modebuf, parabuf);
sendto_serv_butone(NULL, ":%s MODE %s %s %s", sptr->name, chptr->chname, modebuf, parabuf);
/* Activate this hook just like m_mode.c */
RunHook7(HOOKTYPE_REMOTE_CHANMODE, cptr, sptr, chptr, modebuf, parabuf, ts, 0);
*parabuf = 0;
}
return 0;
}
/*
* do_svsmode() [merge from svsmode/svs2mode]
* parv[0] - sender
* parv[1] - username to change mode for
* parv[2] - modes to change
* parv[3] - Service Stamp (if mode == d)
*
* show_change can be 0 (for svsmode) or 1 (for svs2mode).
*/
int do_svsmode(aClient *cptr, aClient *sptr, int parc, char *parv[], int show_change)
{
int i;
char *m;
aClient *acptr;
int what, setflags;
char *xmsg = show_change ? MSG_SVS2MODE : MSG_SVSMODE;
char *xtok = show_change ? TOK_SVS2MODE : TOK_SVSMODE;
if (!IsULine(sptr))
return 0;
what = MODE_ADD;
if (parc < 3)
return 0;
if (parv[1][0] == '#')
return channel_svsmode(cptr, sptr, parc, parv);
if (!(acptr = find_person(parv[1], NULL)))
return 0;
/* initialize setflag to be the user's pre-SVSMODE flags */
setflags = 0;
for (i = 0; i <= Usermode_highest; i++)
if (Usermode_Table[i].flag && (acptr->umodes & Usermode_Table[i].mode))
setflags |= Usermode_Table[i].mode;
/* parse mode change string(s) */
for (m = parv[2]; *m; m++)
switch (*m)
{
case '+':
what = MODE_ADD;
break;
case '-':
what = MODE_DEL;
break;
/* we may not get these, but they shouldnt be in default */
case ' ':
case '\n':
case '\r':
case '\t':
break;
case 'i':
if ((what == MODE_ADD) && !(acptr->umodes & UMODE_INVISIBLE))
IRCstats.invisible++;
if ((what == MODE_DEL) && (acptr->umodes & UMODE_INVISIBLE))
IRCstats.invisible--;
goto setmodex;
case 'O': /* Locops are opers too! */
if (what == MODE_ADD)
{
#ifndef NO_FDLIST
if (!IsAnOper(acptr) && MyClient(acptr))
addto_fdlist(acptr->slot, &oper_fdlist);
#endif
acptr->umodes &= ~UMODE_OPER;
}
#ifndef NO_FDLIST
if (what == MODE_DEL && (acptr->umodes & UMODE_LOCOP) && MyClient(acptr))
delfrom_fdlist(acptr->slot, &oper_fdlist);
#endif
goto setmodex;
case 'o':
if ((what == MODE_ADD) && !(acptr->umodes & UMODE_OPER))
{
#ifndef NO_FDLIST
if (MyClient(acptr) && !IsLocOp(acptr))
addto_fdlist(acptr->slot, &oper_fdlist);
#endif
acptr->umodes &= ~UMODE_LOCOP; /* can't be both local and global */
IRCstats.operators++;
}
if ((what == MODE_DEL) && (acptr->umodes & UMODE_OPER))
{
if (acptr->umodes & UMODE_HIDEOPER)
{
/* clear 'H' too, and opercount stays the same.. */
acptr->umodes &= ~UMODE_HIDEOPER;
} else {
IRCstats.operators--;
}
#ifndef NO_FDLIST
if (MyClient(acptr))
delfrom_fdlist(acptr->slot, &oper_fdlist);
#endif
}
goto setmodex;
case 'H':
if (what == MODE_ADD && !(acptr->umodes & UMODE_HIDEOPER))
{
if (!IsAnOper(acptr) && !strchr(parv[2], 'o')) /* (ofcoz this strchr() is flawed) */
{
/* isn't an oper, and would not become one either.. abort! */
sendto_realops(
"[BUG] server %s tried to set +H while user not an oper, para=%s/%s, "
"umodes=%ld, please fix your services or if you think it's our fault, "
"report at http://bugs.unrealircd.org/", sptr->name, parv[1], parv[2], acptr->umodes);
break; /* abort! */
}
if (!IsLocOp(acptr))
IRCstats.operators--;
}
if (what == MODE_DEL && (acptr->umodes & UMODE_HIDEOPER) && !IsLocOp(acptr))
IRCstats.operators++;
goto setmodex;
case 'd':
if (parv[3])
{
strlcpy(acptr->user->svid, parv[3], sizeof(acptr->user->svid));
sendto_common_channels_local_butone(acptr, PROTO_ACCOUNT_NOTIFY, ":%s!%s@%s ACCOUNT %s",
acptr->name, acptr->user->username, GetHost(acptr),
!isdigit(*acptr->user->svid) ? acptr->user->svid : "*");
}
break;
case 'x':
if (what == MODE_DEL)
{
/* -x */
if (acptr->user->virthost)
{
/* Removing mode +x and virthost set... recalculate host then (but don't activate it!) */
MyFree(acptr->user->virthost);
acptr->user->virthost = strdup(acptr->user->cloakedhost);
}
} else
{
/* +x */
if (!acptr->user->virthost)
{
/* Hmm... +x but no virthost set, that's bad... use cloakedhost.
* Not sure if this could ever happen, but just in case... -- Syzop
*/
acptr->user->virthost = strdup(acptr->user->cloakedhost);
}
/* Announce the new host to VHP servers if we're setting the virthost to the cloakedhost.
* In other cases, we can assume that the host has been broadcasted already (after all,
* how else could it have been changed...?).
* NOTES: we're doing a strcasecmp here instead of simply checking if it's a "+x but
* not -t"-case. The reason for this is that the 't' might follow ("+xt" instead of "+tx"),
* in which case we would have needlessly announced it. Ok I didn't test it but that's
* the idea behind it :P. -- Syzop
*/
if (MyClient(acptr) && !strcasecmp(acptr->user->virthost, acptr->user->cloakedhost))
sendto_serv_butone_token_opt(NULL, OPT_VHP, acptr->name,
MSG_SETHOST, TOK_SETHOST, "%s", acptr->user->virthost);
}
goto setmodex;
case 'z':
/* Setting and unsetting user mode 'z' remotely is not supported */
break;
default:
setmodex:
for (i = 0; i <= Usermode_highest; i++)
{
if (!Usermode_Table[i].flag)
continue;
if (*m == Usermode_Table[i].flag)
{
if (what == MODE_ADD)
acptr->umodes |= Usermode_Table[i].mode;
else
acptr->umodes &= ~Usermode_Table[i].mode;
break;
}
}
break;
} /*switch*/
if (parc > 3)
sendto_serv_butone_token(cptr, parv[0], xmsg, xtok,
"%s %s %s", parv[1], parv[2], parv[3]);
else
sendto_serv_butone_token(cptr, parv[0], xmsg, xtok,
"%s %s", parv[1], parv[2]);
/* Here we trigger the same hooks that m_mode does and, likewise,
only if the old flags (setflags) are different than the newly-
set ones */
if (setflags != acptr->umodes)
RunHook3(HOOKTYPE_UMODE_CHANGE, sptr, setflags, acptr->umodes);
if (show_change)
{
char buf[BUFSIZE];
send_umode(NULL, acptr, setflags, ALL_UMODES, buf);
if (MyClient(acptr) && buf[0] && buf[1])
sendto_one(acptr, ":%s MODE %s :%s", parv[0], parv[1], buf);
}
VERIFY_OPERCOUNT(acptr, "svsmodeX");
return 0;
}
/*
* m_svsmode() added by taz
* parv[0] - sender
* parv[1] - username to change mode for
* parv[2] - modes to change
* parv[3] - Service Stamp (if mode == d)
*/
int m_svsmode(aClient *cptr, aClient *sptr, int parc, char *parv[])
{
return do_svsmode(cptr, sptr, parc, parv, 0);
}
/*
* m_svs2mode() added by Potvin
* parv[0] - sender
* parv[1] - username to change mode for
* parv[2] - modes to change
* parv[3] - Service Stamp (if mode == d)
*/
int m_svs2mode(aClient *cptr, aClient *sptr, int parc, char *parv[])
{
return do_svsmode(cptr, sptr, parc, parv, 1);
}
void add_send_mode_param(aChannel *chptr, aClient *from, char what, char mode, char *param) {
static char *modes = NULL, lastwhat;
static short count = 0;
short send = 0;
if (!modes) modes = modebuf;
if (!modebuf[0]) {
modes = modebuf;
*modes++ = what;
*modes = 0;
lastwhat = what;
*parabuf = 0;
count = 0;
}
if (lastwhat != what) {
*modes++ = what;
*modes = 0;
lastwhat = what;
}
if (strlen(parabuf) + strlen(param) + 11 < MODEBUFLEN) {
if (*parabuf)
strcat(parabuf, " ");
strcat(parabuf, param);
*modes++ = mode;
*modes = 0;
count++;
}
else if (*parabuf)
send = 1;
if (count == MAXMODEPARAMS)
send = 1;
if (send) {
sendto_channel_butserv(chptr, from, ":%s MODE %s %s %s",
from->name, chptr->chname, modebuf, parabuf);
sendto_serv_butone(NULL, ":%s MODE %s %s %s", from->name, chptr->chname, modebuf, parabuf);
send = 0;
*parabuf = 0;
modes = modebuf;
*modes++ = what;
lastwhat = what;
if (count != MAXMODEPARAMS) {
strcpy(parabuf, param);
*modes++ = mode;
count = 1;
}
else
count = 0;
*modes = 0;
}
}