1
0
mirror of https://github.com/unrealircd/unrealircd.git synced 2026-07-05 18:53:12 +02:00
Files
unrealircd/src/channel.c
T

5219 lines
129 KiB
C

/* Unreal Internet Relay Chat Daemon, src/channel.c
* Copyright (C) 1990 Jarkko Oikarinen and
* University of Oulu, Co Center
*
* 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.
*/
/*
* 23 Jun 1999
* Changing unsigned int modes to long
* --- Sts - 28 May 1999
Incorporated twilight mode system
*/
/* -- Jto -- 09 Jul 1990
* Bug fix
*/
/* -- Jto -- 03 Jun 1990
* Moved m_channel() and related functions from s_msg.c to here
* Many changes to start changing into string channels...
*/
/* -- Jto -- 24 May 1990
* Moved is_full() from list.c
*/
#include "struct.h"
#include "common.h"
#include "sys.h"
#include "numeric.h"
#include "channel.h"
#include "msg.h" /* For TOK_*** and MSG_*** strings */
#include "hash.h" /* For CHANNELHASHSIZE */
#include "h.h"
#include "proto.h"
#include <string.h>
ID_Copyright
("(C) 1990 University of Oulu, Computing Center and Jarkko Oikarinen");
#define MAXBOUNCE 5 /** Most sensible */
typedef struct ChLink CHLINK;
struct CHLink {
aChannel *chan;
};
static int bouncedtimes = 0;
struct MODVAR CHLink chlbounce[MAXBOUNCE];
int MODVAR chbounce = 0;
long opermode = 0;
aChannel *channel = NullChn;
extern char backupbuf[];
extern ircstats IRCstats;
extern int spamf_ugly_vchanoverride;
#ifndef NO_FDLIST
extern int lifesux;
#endif
/* Some forward declarations */
CMD_FUNC(do_join);
void add_invite(aClient *, aChannel *);
char *clean_ban_mask(char *, int, aClient *);
int add_banid(aClient *, aChannel *, char *);
int can_join(aClient *, aClient *, aChannel *, char *, char *,
char **);
void channel_modes(aClient *, char *, char *, aChannel *);
int check_channelmask(aClient *, aClient *, char *);
int del_banid(aChannel *, char *);
void set_mode(aChannel *, aClient *, int, char **, u_int *,
char[MAXMODEPARAMS][MODEBUFLEN + 3], int);
#ifdef EXTCMODE
void make_mode_str(aChannel *, long, Cmode_t, long, int,
char[MAXMODEPARAMS][MODEBUFLEN + 3], char *, char *, char);
#else
void make_mode_str(aChannel *, long, long, int,
char[MAXMODEPARAMS][MODEBUFLEN + 3], char *, char *, char);
#endif
int do_mode_char(aChannel *, long, char, char *,
u_int, aClient *,
u_int *, char[MAXMODEPARAMS][MODEBUFLEN + 3], char);
void do_mode(aChannel *, aClient *, aClient *, int, char **, time_t,
int);
void bounce_mode(aChannel *, aClient *, int, char **);
void sub1_from_channel(aChannel *);
void clean_channelname(char *);
void del_invite(aClient *, aChannel *);
#ifdef NEWCHFLOODPROT
void chanfloodtimer_del(aChannel *chptr, char mflag, long mbit);
void chanfloodtimer_stopchantimers(aChannel *chptr);
#endif
static char *PartFmt = ":%s PART %s";
static char *PartFmt2 = ":%s PART %s :%s";
/*
* some buffers for rebuilding channel/nick lists with ,'s
*/
static char nickbuf[BUFSIZE], buf[BUFSIZE];
char modebuf[BUFSIZE], parabuf[BUFSIZE];
#include "sjoin.h"
#define MODESYS_LINKOK /* We do this for a TEST */
aCtab cFlagTab[] = {
{MODE_LIMIT, 'l', 0, 1},
{MODE_VOICE, 'v', 1, 1},
{MODE_HALFOP, 'h', 0, 1},
{MODE_CHANOP, 'o', 0, 1},
{MODE_PRIVATE, 'p', 0, 0},
{MODE_SECRET, 's', 0, 0},
{MODE_MODERATED, 'm', 1, 0},
{MODE_NOPRIVMSGS, 'n', 1, 0},
{MODE_TOPICLIMIT, 't', 1, 0},
{MODE_INVITEONLY, 'i', 1, 0},
{MODE_KEY, 'k', 1, 1},
{MODE_RGSTR, 'r', 0, 0},
{MODE_RGSTRONLY, 'R', 0, 0},
{MODE_NOCOLOR, 'c', 0, 0},
{MODE_CHANPROT, 'a', 0, 1},
{MODE_CHANOWNER, 'q', 0, 1},
{MODE_OPERONLY, 'O', 0, 0},
{MODE_ADMONLY, 'A', 0, 0},
{MODE_LINK, 'L', 0, 1},
{MODE_NOKICKS, 'Q', 0, 0},
{MODE_BAN, 'b', 1, 1},
{MODE_STRIP, 'S', 0, 0}, /* works? */
{MODE_EXCEPT, 'e', 1, 0}, /* exception ban */
{MODE_NOKNOCK, 'K', 0, 0}, /* knock knock (no way!) */
{MODE_NOINVITE, 'V', 0, 0}, /* no invites */
{MODE_FLOODLIMIT, 'f', 0, 1}, /* flood limiter */
{MODE_MODREG, 'M', 0, 0}, /* Need umode +r to talk */
#ifdef STRIPBADWORDS
{MODE_STRIPBADWORDS, 'G', 0, 0}, /* no badwords */
#endif
{MODE_NOCTCP, 'C', 0, 0}, /* no CTCPs */
{MODE_AUDITORIUM, 'u', 0, 0},
{MODE_ONLYSECURE, 'z', 0, 0},
{MODE_NONICKCHANGE, 'N', 0, 0},
{0x0, 0x0, 0x0}
};
#define BADOP_BOUNCE 1
#define BADOP_USER 2
#define BADOP_SERVER 3
#define BADOP_OVERRIDE 4
/* is some kind of admin */
#define IsSkoAdmin(sptr) (IsAdmin(sptr) || IsNetAdmin(sptr) || IsSAdmin(sptr))
char cmodestring[512];
inline int op_can_override(aClient *sptr)
{
#ifndef NO_OPEROVERRIDE
if (!IsOper(sptr))
return 0;
if (MyClient(sptr) && !OPCanOverride(sptr))
return 0;
return 1;
#else
return 0;
#endif
}
void make_cmodestr(void)
{
char *p = &cmodestring[0];
aCtab *tab = &cFlagTab[0];
#ifdef EXTCMODE
int i;
#endif
while (tab->mode != 0x0)
{
*p = tab->flag;
p++;
tab++;
}
#ifdef EXTCMODE
for (i=0; i <= Channelmode_highest; i++)
if (Channelmode_Table[i].flag)
*p++ = Channelmode_Table[i].flag;
#endif
*p = '\0';
}
int Halfop_mode(long mode)
{
aCtab *tab = &cFlagTab[0];
while (tab->mode != 0x0)
{
if (tab->mode == mode)
return (tab->halfop == 1 ? TRUE : FALSE);
tab++;
}
return TRUE;
}
/*
* return the length (>=0) of a chain of links.
*/
static int list_length(Link *lp)
{
int count = 0;
for (; lp; lp = lp->next)
count++;
return count;
}
Member *find_member_link(Member *lp, aClient *ptr)
{
if (ptr)
while (lp)
{
if (lp->cptr == ptr)
return (lp);
lp = lp->next;
}
return NULL;
}
Membership *find_membership_link(Membership *lp, aChannel *ptr)
{
if (ptr)
while (lp)
{
if (lp->chptr == ptr)
return (lp);
lp = lp->next;
}
return NULL;
}
/*
* Member functions
*/
Member *make_member(void)
{
Member *lp;
unsigned int i;
if (freemember == NULL)
{
for (i = 1; i <= (4072/sizeof(Member)); ++i)
{
lp = (Member *)MyMalloc(sizeof(Member));
lp->cptr = NULL;
lp->flags = 0;
lp->next = freemember;
freemember = lp;
}
}
lp = freemember;
freemember = freemember->next;
lp->next = NULL;
return lp;
}
void free_member(Member *lp)
{
if (lp)
{
lp->next = freemember;
lp->cptr = NULL;
lp->flags = 0;
freemember = lp;
}
}
/*
* Membership functions
*/
Membership *make_membership(int local)
{
Membership *lp = NULL;
MembershipL *lp2 = NULL;
unsigned int i;
if (!local)
{
if (freemembership == NULL)
{
for (i = 1; i <= (4072/sizeof(Membership)); i++)
{
lp = (Membership *)MyMalloc(sizeof(Membership));
lp->next = freemembership;
freemembership = lp;
}
lp = freemembership;
freemembership = lp->next;
}
else
{
lp = freemembership;
freemembership = freemembership->next;
}
bzero(lp, sizeof(Membership));
}
else
{
if (freemembershipL == NULL)
{
for (i = 1; i <= (4072/sizeof(MembershipL)); i++)
{
lp2 = (MembershipL *)MyMalloc(sizeof(MembershipL));
lp2->next = (Membership *) freemembershipL;
freemembershipL = lp2;
}
lp2 = freemembershipL;
freemembershipL = (MembershipL *) lp2->next;
Debug((DEBUG_ERROR, "floodmode::alloc gotone"));
}
else
{
lp2 = freemembershipL;
freemembershipL = (MembershipL *) freemembershipL->next;
Debug((DEBUG_ERROR, "floodmode::freelist gotone"));
}
Debug((DEBUG_ERROR, "floodmode:: bzeroing"));
bzero(lp2, sizeof(MembershipL));
}
if (local)
{
return ((Membership *) lp2);
}
return lp;
}
void free_membership(Membership *lp, int local)
{
if (lp)
{
if (!local)
{
lp->next = freemembership;
freemembership = lp;
}
else
{
lp->next = (Membership *) freemembershipL;
freemembershipL = (MembershipL *) lp;
}
}
}
/*
** find_chasing
** Find the client structure for a nick name (user) using history
** mechanism if necessary. If the client is not found, an error
** message (NO SUCH NICK) is generated. If the client was found
** through the history, chasing will be 1 and otherwise 0.
*/
aClient *find_chasing(aClient *sptr, char *user, int *chasing)
{
aClient *who = find_client(user, (aClient *)NULL);
if (chasing)
*chasing = 0;
if (who)
{
if (!IsServer(who))
return who;
else
return NULL;
}
if (!(who = get_history(user, (long)KILLCHASETIMELIMIT)))
{
sendto_one(sptr, err_str(ERR_NOSUCHNICK),
me.name, sptr->name, user);
return NULL;
}
if (chasing)
*chasing = 1;
if (!IsServer(who))
return who;
else return NULL;
}
/*
Exception functions to work with mode +e
-sts
*/
/* add_exbanid - add an id to be excepted to the channel bans (belongs to cptr) */
int add_exbanid(aClient *cptr, aChannel *chptr, char *banid)
{
Ban *ban;
int cnt = 0, len = 0;
if (MyClient(cptr))
(void)collapse(banid);
for (ban = chptr->exlist; ban; ban = ban->next)
{
len += strlen(ban->banstr);
if (MyClient(cptr))
if ((len > MAXBANLENGTH) || (++cnt >= MAXBANS))
{
sendto_one(cptr, err_str(ERR_BANLISTFULL),
me.name, cptr->name, chptr->chname, banid);
return -1;
}
else
{
/* Temp workaround added in b19. -- Syzop */
if (!mycmp(ban->banstr, banid) || (!strchr(banid, '\\') && !strchr(ban->banstr, '\\')))
if (!match(ban->banstr, banid))
return -1;
}
else if (!mycmp(ban->banstr, banid))
return -1;
}
ban = make_ban();
bzero((char *)ban, sizeof(Ban));
/* ban->flags = CHFL_BAN; They're all bans!! */
ban->next = chptr->exlist;
ban->banstr = (char *)MyMalloc(strlen(banid) + 1);
(void)strcpy(ban->banstr, banid);
ban->who = (char *)MyMalloc(strlen(cptr->name) + 1);
(void)strcpy(ban->who, cptr->name);
ban->when = TStime();
chptr->exlist = ban;
return 0;
}
/*
* del_exbanid - delete an id belonging to cptr
*/
int del_exbanid(aChannel *chptr, char *banid)
{
Ban **ban;
Ban *tmp;
if (!banid)
return -1;
for (ban = &(chptr->exlist); *ban; ban = &((*ban)->next))
if (mycmp(banid, (*ban)->banstr) == 0)
{
tmp = *ban;
*ban = tmp->next;
MyFree(tmp->banstr);
MyFree(tmp->who);
free_ban(tmp);
return 0;
}
return -1;
}
/*
* Ban functions to work with mode +b
*/
/* add_banid - add an id to be banned to the channel (belongs to cptr) */
int add_banid(aClient *cptr, aChannel *chptr, char *banid)
{
Ban *ban;
int cnt = 0, len = 0;
if (MyClient(cptr))
(void)collapse(banid);
for (ban = chptr->banlist; ban; ban = ban->next)
{
len += strlen(ban->banstr);
if (MyClient(cptr))
if ((len > MAXBANLENGTH) || (++cnt >= MAXBANS))
{
sendto_one(cptr, err_str(ERR_BANLISTFULL),
me.name, cptr->name, chptr->chname, banid);
return -1;
}
else
{
/* Temp workaround added in b19. -- Syzop */
if (!mycmp(ban->banstr, banid) || (!strchr(banid, '\\') && !strchr(ban->banstr, '\\')))
#ifdef NAZIISH_CHBAN_HANDLING /* why does it do this?? */
if (!match(ban->banstr, banid) ||
!match(banid, ban->banstr))
#else
if (!match(ban->banstr, banid))
#endif
return -1;
}
else if (!mycmp(ban->banstr, banid))
return -1;
}
ban = make_ban();
bzero((char *)ban, sizeof(Ban));
/* ban->flags = CHFL_BAN; They're all bans!! */
ban->next = chptr->banlist;
ban->banstr = (char *)MyMalloc(strlen(banid) + 1);
(void)strcpy(ban->banstr, banid);
ban->who = (char *)MyMalloc(strlen(cptr->name) + 1);
(void)strcpy(ban->who, cptr->name);
ban->when = TStime();
chptr->banlist = ban;
return 0;
}
/*
* del_banid - delete an id belonging to cptr
*/
int del_banid(aChannel *chptr, char *banid)
{
Ban **ban;
Ban *tmp;
if (!banid)
return -1;
for (ban = &(chptr->banlist); *ban; ban = &((*ban)->next))
if (mycmp(banid, (*ban)->banstr) == 0)
{
tmp = *ban;
*ban = tmp->next;
MyFree(tmp->banstr);
MyFree(tmp->who);
free_ban(tmp);
return 0;
}
return -1;
}
/*
* IsMember - returns 1 if a person is joined
* Moved to struct.h
*/
/* Those 3 pointers can be used by extended ban modules so they
* don't have to do 3 make_nick_user_host()'s all the time:
*/
char *ban_realhost = NULL, *ban_virthost = NULL, *ban_ip = NULL;
/** is_banned - checks for bans.
* PARAMETERS:
* sptr: the client to check (can be remote client)
* chptr: the channel to check
* type: one of BANCHK_*
* RETURNS:
* a pointer to the ban structure if banned, else NULL.
*/
Ban *is_banned(aClient *sptr, aChannel *chptr, int type)
{
Ban *tmp, *tmp2;
char *s;
static char realhost[NICKLEN + USERLEN + HOSTLEN + 6];
static char virthost[NICKLEN + USERLEN + HOSTLEN + 6];
static char nuip[NICKLEN + USERLEN + HOSTLEN + 6];
int dovirt = 0, mine = 0;
Extban *extban;
if (!IsPerson(sptr))
return NULL;
ban_realhost = realhost;
ban_ip = ban_virthost = NULL;
if (MyConnect(sptr)) {
mine = 1;
s = make_nick_user_host(sptr->name, sptr->user->username, GetIP(sptr));
strlcpy(nuip, s, sizeof nuip);
ban_ip = nuip;
}
if (sptr->user->virthost)
if (strcmp(sptr->user->realhost, sptr->user->virthost))
{
dovirt = 1;
}
s = make_nick_user_host(sptr->name, sptr->user->username,
sptr->user->realhost);
strlcpy(realhost, s, sizeof realhost);
if (dovirt)
{
s = make_nick_user_host(sptr->name, sptr->user->username,
sptr->user->virthost);
strlcpy(virthost, s, sizeof virthost);
ban_virthost = virthost;
}
/* We now check +b first, if a +b is found we then see if there is a +e.
* If a +e was found we return NULL, if not, we return the ban.
*/
for (tmp = chptr->banlist; tmp; tmp = tmp->next)
{
if (*tmp->banstr == '~')
{
extban = findmod_by_bantype(tmp->banstr[1]);
if (!extban)
continue;
if (!extban->is_banned(sptr, chptr, tmp->banstr, type))
continue;
} else {
if ((match(tmp->banstr, realhost) == 0) ||
(dovirt && (match(tmp->banstr, virthost) == 0)) ||
(mine && (match(tmp->banstr, nuip) == 0)))
{
/* matches.. do nothing */
} else
continue;
}
/* Ban found, now check for +e */
for (tmp2 = chptr->exlist; tmp2; tmp2 = tmp2->next)
{
if (*tmp2->banstr == '~')
{
extban = findmod_by_bantype(tmp2->banstr[1]);
if (!extban)
continue;
if (extban->is_banned(sptr, chptr, tmp2->banstr, type))
return NULL;
} else {
if ((match(tmp2->banstr, realhost) == 0) ||
(dovirt && (match(tmp2->banstr, virthost) == 0)) ||
(mine && (match(tmp2->banstr, nuip) == 0)) )
return NULL;
}
}
break; /* ban found and not on except */
}
return (tmp);
}
/*
* Checks if the "user" IRC is banned, used by +mu.
*/
static int is_irc_banned(aChannel *chptr)
{
Ban *tmp;
/* Check for this user, ident/host are "illegal" on purpose */
char *check = "IRC!\001@\001";
for (tmp = chptr->banlist; tmp; tmp = tmp->next)
if (match(tmp->banstr, check) == 0)
{
/* Ban found, now check for +e */
for (tmp = chptr->exlist; tmp; tmp = tmp->next)
if (match(tmp->banstr, check) == 0)
return 0; /* In exception list */
return 1;
}
return 0;
}
/*
* adds a user to a channel by adding another link to the channels member
* chain.
*/
void add_user_to_channel(aChannel *chptr, aClient *who, int flags)
{
Member *ptr;
Membership *ptr2;
if (who->user)
{
ptr = make_member();
ptr->cptr = who;
ptr->flags = flags;
ptr->next = chptr->members;
chptr->members = ptr;
chptr->users++;
ptr2 = make_membership(MyClient(who));
/* we should make this more efficient --stskeeps
is now, as we only use it in membership */
ptr2->chptr = chptr;
ptr2->next = who->user->channel;
ptr2->flags = flags;
who->user->channel = ptr2;
who->user->joined++;
}
}
void remove_user_from_channel(aClient *sptr, aChannel *chptr)
{
Member **curr; Membership **curr2;
Member *tmp; Membership *tmp2;
Member *lp = chptr->members;
/* find 1st entry in list that is not user */
for (; lp && (lp->cptr == sptr); lp = lp->next);
for (;;)
{
for (curr = &chptr->members; (tmp = *curr); curr = &tmp->next)
if (tmp->cptr == sptr)
{
*curr = tmp->next;
free_member(tmp);
break;
}
for (curr2 = &sptr->user->channel; (tmp2 = *curr2); curr2 = &tmp2->next)
if (tmp2->chptr == chptr)
{
*curr2 = tmp2->next;
free_membership(tmp2, MyClient(sptr));
break;
}
sptr->user->joined--;
if (lp)
break;
if (chptr->members)
sptr = chptr->members->cptr;
else
break;
sub1_from_channel(chptr);
}
sub1_from_channel(chptr);
}
long get_access(aClient *cptr, aChannel *chptr)
{
Membership *lp;
if (chptr)
if ((lp = find_membership_link(cptr->user->channel, chptr)))
return lp->flags;
return 0;
}
int is_chan_op(aClient *cptr, aChannel *chptr)
{
Membership *lp;
/* chanop/halfop ? */
if (IsServer(cptr))
return 1;
if (chptr)
if ((lp = find_membership_link(cptr->user->channel, chptr)))
#ifdef PREFIX_AQ
return ((lp->flags & (CHFL_CHANOP|CHFL_CHANPROT|CHFL_CHANOWNER)));
#else
return ((lp->flags & CHFL_CHANOP));
#endif
return 0;
}
int has_voice(aClient *cptr, aChannel *chptr)
{
Membership *lp;
if (IsServer(cptr))
return 1;
if (chptr)
if ((lp = find_membership_link(cptr->user->channel, chptr)))
return (lp->flags & CHFL_VOICE);
return 0;
}
int is_halfop(aClient *cptr, aChannel *chptr)
{
Membership *lp;
if (IsServer(cptr))
return 1;
if (chptr)
if ((lp = find_membership_link(cptr->user->channel, chptr)))
if (!(lp->flags & CHFL_CHANOP))
return (lp->flags & CHFL_HALFOP);
return 0;
}
int is_chanowner(aClient *cptr, aChannel *chptr)
{
Membership *lp;
if (IsServer(cptr))
return 1;
if (chptr)
if ((lp = find_membership_link(cptr->user->channel, chptr)))
return (lp->flags & CHFL_CHANOWNER);
return 0;
}
int is_chanownprotop(aClient *cptr, aChannel *chptr) {
Membership *lp;
if (IsServer(cptr))
return 1;
if (chptr)
if ((lp = find_membership_link(cptr->user->channel, chptr)))
if (lp->flags & (CHFL_CHANOWNER|CHFL_CHANPROT|CHFL_CHANOP))
return 1;
return 0;
}
int is_skochanop(aClient *cptr, aChannel *chptr) {
Membership *lp;
if (IsServer(cptr))
return 1;
if (chptr)
if ((lp = find_membership_link(cptr->user->channel, chptr)))
if (lp->flags & (CHFL_CHANOWNER|CHFL_CHANPROT|CHFL_CHANOP|CHFL_HALFOP))
return 1;
return 0;
}
int is_chanprot(aClient *cptr, aChannel *chptr)
{
Membership *lp;
if (chptr)
if ((lp = find_membership_link(cptr->user->channel, chptr)))
return (lp->flags & CHFL_CHANPROT);
return 0;
}
#define CANNOT_SEND_MODERATED 1
#define CANNOT_SEND_NOPRIVMSGS 2
#define CANNOT_SEND_NOCOLOR 3
#define CANNOT_SEND_BAN 4
#define CANNOT_SEND_NOCTCP 5
#define CANNOT_SEND_MODREG 6
#define CANNOT_SEND_SWEAR 7 /* This isn't actually used here */
#define CANNOT_SEND_NOTICE 8
int can_send(aClient *cptr, aChannel *chptr, char *msgtext, int notice)
{
Membership *lp;
int member;
/*
* #0000053 by |savage|, speedup
*/
if (!MyClient(cptr))
{
if (IsClient(cptr))
{
/* channelmode +mu is a special case.. sux!. -- Syzop */
lp = find_membership_link(cptr->user->channel, chptr);
if ((chptr->mode.mode & MODE_MODERATED) && (chptr->mode.mode & MODE_AUDITORIUM) &&
!IsOper(cptr) &&
(!lp || !(lp->flags & (CHFL_CHANOP|CHFL_VOICE|CHFL_CHANOWNER|CHFL_HALFOP|CHFL_CHANPROT))) &&
!is_irc_banned(chptr))
{
sendto_chmodemucrap(cptr, chptr, msgtext);
return (CANNOT_SEND_MODERATED);
}
}
return 0;
}
if (chptr->mode.mode & MODE_NOCOLOR)
if (strchr((char *)msgtext, 3) || strchr((char *)msgtext, 27))
return (CANNOT_SEND_NOCOLOR);
member = IsMember(cptr, chptr);
if (chptr->mode.mode & MODE_NOPRIVMSGS && !member)
return (CANNOT_SEND_NOPRIVMSGS);
lp = find_membership_link(cptr->user->channel, chptr);
if ((chptr->mode.mode & MODE_MODREG) && !op_can_override(cptr) && !IsRegNick(cptr) &&
(!lp
|| !(lp->flags & (CHFL_CHANOP | CHFL_VOICE | CHFL_CHANOWNER |
CHFL_HALFOP | CHFL_CHANPROT))))
return CANNOT_SEND_MODREG;
if (chptr->mode.mode & MODE_MODERATED && !op_can_override(cptr) &&
(!lp
|| !(lp->flags & (CHFL_CHANOP | CHFL_VOICE | CHFL_CHANOWNER |
CHFL_HALFOP | CHFL_CHANPROT))))
{
if ((chptr->mode.mode & MODE_AUDITORIUM) && !is_irc_banned(chptr))
sendto_chmodemucrap(cptr, chptr, msgtext);
return (CANNOT_SEND_MODERATED);
}
if (chptr->mode.mode & MODE_NOCTCP &&
(!lp
|| !(lp->flags & (CHFL_CHANOP | CHFL_CHANOWNER | CHFL_CHANPROT))))
if (msgtext[0] == 1 && strncmp(&msgtext[1], "ACTION ", 7))
return (CANNOT_SEND_NOCTCP);
#ifdef EXTCMODE
if (notice && (chptr->mode.extmode & EXTMODE_NONOTICE) &&
(!lp || !(lp->flags & (CHFL_CHANOP | CHFL_CHANOWNER | CHFL_CHANPROT))))
return (CANNOT_SEND_NOTICE);
#endif
/* Makes opers able to talk thru bans -Stskeeps suggested by The_Cat */
if (IsOper(cptr))
return 0;
if ((!lp
|| !(lp->flags & (CHFL_CHANOP | CHFL_VOICE | CHFL_CHANOWNER |
CHFL_HALFOP | CHFL_CHANPROT))) && MyClient(cptr)
&& is_banned(cptr, chptr, BANCHK_MSG))
return (CANNOT_SEND_BAN);
return 0;
}
/* [just a helper for channel_modef_string()] */
static inline char *chmodefstrhelper(char *buf, char t, char tdef, unsigned short l, unsigned char a, unsigned char r)
{
char *p;
char tmpbuf[16], *p2 = tmpbuf;
ircsprintf(buf, "%hd", l);
p = buf + strlen(buf);
*p++ = t;
if (a && ((a != tdef) || r))
{
*p++ = '#';
*p++ = a;
if (r)
{
sprintf(tmpbuf, "%hd", (short)r);
while ((*p = *p2++))
p++;
}
}
*p++ = ',';
return p;
}
/** returns the channelmode +f string (ie: '[5k,40j]:10') */
char *channel_modef_string(ChanFloodProt *x)
{
static char retbuf[512]; /* overkill :p */
char *p = retbuf;
*p++ = '[';
/* (alphabetized) */
if (x->l[FLD_CTCP])
p = chmodefstrhelper(p, 'c', 'C', x->l[FLD_CTCP], x->a[FLD_CTCP], x->r[FLD_CTCP]);
if (x->l[FLD_JOIN])
p = chmodefstrhelper(p, 'j', 'i', x->l[FLD_JOIN], x->a[FLD_JOIN], x->r[FLD_JOIN]);
if (x->l[FLD_KNOCK])
p = chmodefstrhelper(p, 'k', 'K', x->l[FLD_KNOCK], x->a[FLD_KNOCK], x->r[FLD_KNOCK]);
if (x->l[FLD_MSG])
p = chmodefstrhelper(p, 'm', 'm', x->l[FLD_MSG], x->a[FLD_MSG], x->r[FLD_MSG]);
if (x->l[FLD_NICK])
p = chmodefstrhelper(p, 'n', 'N', x->l[FLD_NICK], x->a[FLD_NICK], x->r[FLD_NICK]);
if (x->l[FLD_TEXT])
p = chmodefstrhelper(p, 't', '\0', x->l[FLD_TEXT], x->a[FLD_TEXT], x->r[FLD_TEXT]);
if (*(p - 1) == ',')
p--;
*p++ = ']';
ircsprintf(p, ":%hd", x->per);
return retbuf;
}
/*
* write the "simple" list of channel modes for channel chptr onto buffer mbuf
* with the parameters in pbuf.
*/
void channel_modes(aClient *cptr, char *mbuf, char *pbuf, aChannel *chptr)
{
aCtab *tab = &cFlagTab[0];
char bcbuf[1024];
#ifdef EXTCMODE
int i;
#endif
*mbuf++ = '+';
/* Paramless first */
while (tab->mode != 0x0)
{
if ((chptr->mode.mode & tab->mode))
if (!tab->parameters)
*mbuf++ = tab->flag;
tab++;
}
#ifdef EXTCMODE
for (i=0; i <= Channelmode_highest; i++)
{
if (Channelmode_Table[i].flag && !Channelmode_Table[i].paracount &&
(chptr->mode.extmode & Channelmode_Table[i].mode))
*mbuf++ = Channelmode_Table[i].flag;
}
#endif
if (chptr->mode.limit)
{
*mbuf++ = 'l';
if (IsMember(cptr, chptr) || IsServer(cptr)
|| IsULine(cptr))
(void)ircsprintf(pbuf, "%d ", chptr->mode.limit);
}
if (*chptr->mode.key)
{
*mbuf++ = 'k';
if (IsMember(cptr, chptr) || IsServer(cptr)
|| IsULine(cptr))
{
/* FIXME: hope pbuf is long enough */
(void)snprintf(bcbuf, sizeof bcbuf, "%s ", chptr->mode.key);
(void)strcat(pbuf, bcbuf);
}
}
if (*chptr->mode.link)
{
*mbuf++ = 'L';
if (IsMember(cptr, chptr) || IsServer(cptr)
|| IsULine(cptr))
{
/* FIXME: is pbuf long enough? */
(void)snprintf(bcbuf, sizeof bcbuf, "%s ", chptr->mode.link);
(void)strcat(pbuf, bcbuf);
}
}
/* if we add more parameter modes, add a space to the strings here --Stskeeps */
#ifdef NEWCHFLOODPROT
if (chptr->mode.floodprot)
#else
if (chptr->mode.per)
#endif
{
*mbuf++ = 'f';
if (IsMember(cptr, chptr) || IsServer(cptr)
|| IsULine(cptr))
{
#ifdef NEWCHFLOODPROT
ircsprintf(bcbuf, "%s ", channel_modef_string(chptr->mode.floodprot));
#else
if (chptr->mode.kmode == 1)
ircsprintf(bcbuf, "*%i:%i ", chptr->mode.msgs, chptr->mode.per);
else
ircsprintf(bcbuf, "%i:%i ", chptr->mode.msgs, chptr->mode.per);
#endif
(void)strcat(pbuf, bcbuf);
}
}
#ifdef EXTCMODE
for (i=0; i <= Channelmode_highest; i++)
{
if (Channelmode_Table[i].flag && Channelmode_Table[i].paracount &&
(chptr->mode.extmode & Channelmode_Table[i].mode))
{
*mbuf++ = Channelmode_Table[i].flag;
strcat(pbuf, Channelmode_Table[i].get_param(extcmode_get_struct(chptr->mode.extmodeparam, Channelmode_Table[i].flag)));
strcat(pbuf, " ");
}
}
#endif
/* Remove the trailing space from the parameters -- codemastr */
if (*pbuf)
pbuf[strlen(pbuf)-1]=0;
*mbuf++ = '\0';
return;
}
static int send_mode_list(aClient *cptr, char *chname, TS creationtime, Member *top, int mask, char flag)
{
Member *lp;
char *cp, *name;
int count = 0, send = 0, sent = 0;
cp = modebuf + strlen(modebuf);
if (*parabuf) /* mode +l or +k xx */
count = 1;
for (lp = top; lp; lp = lp->next)
{
/*
* Okay, since ban's are stored in their own linked
* list, we won't even bother to check if CHFL_BAN
* is set in the flags. This should work as long
* as only ban-lists are feed in with CHFL_BAN mask.
* However, we still need to typecast... -Donwulff
*/
if ((mask == CHFL_BAN) || (mask == CHFL_EXCEPT))
{
/* if (!(((Ban *)lp)->flags & mask)) continue; */
name = ((Ban *) lp)->banstr;
}
else
{
if (!(lp->flags & mask))
continue;
name = lp->cptr->name;
}
if (strlen(parabuf) + strlen(name) + 11 < (size_t)MODEBUFLEN)
{
if (*parabuf)
(void)strlcat(parabuf, " ", sizeof parabuf);
(void)strlcat(parabuf, name, sizeof parabuf);
count++;
*cp++ = flag;
*cp = '\0';
}
else if (*parabuf)
send = 1;
if (count == RESYNCMODES)
send = 1;
if (send)
{
/* cptr is always a server! So we send creationtimes */
sendmodeto_one(cptr, me.name, chname, modebuf,
parabuf, creationtime);
sent = 1;
send = 0;
*parabuf = '\0';
cp = modebuf;
*cp++ = '+';
if (count != RESYNCMODES)
{
(void)strlcpy(parabuf, name, sizeof parabuf);
*cp++ = flag;
}
count = 0;
*cp = '\0';
}
}
return sent;
}
/* A little kludge to prevent sending double spaces -- codemastr */
static inline void send_channel_mode(aClient *cptr, char *from, aChannel *chptr)
{
if (*parabuf)
sendto_one(cptr, ":%s %s %s %s %s %lu", from,
(IsToken(cptr) ? TOK_MODE : MSG_MODE), chptr->chname,
modebuf, parabuf, chptr->creationtime);
else
sendto_one(cptr, ":%s %s %s %s %lu", from,
(IsToken(cptr) ? TOK_MODE : MSG_MODE), chptr->chname,
modebuf, chptr->creationtime);
}
/*
* send "cptr" a full list of the modes for channel chptr.
*/
void send_channel_modes(aClient *cptr, aChannel *chptr)
{
int sent;
/* fixed a bit .. to fit halfops --sts */
if (*chptr->chname != '#')
return;
*parabuf = '\0';
*modebuf = '\0';
channel_modes(cptr, modebuf, parabuf, chptr);
sent = send_mode_list(cptr, chptr->chname, chptr->creationtime,
chptr->members, CHFL_CHANOP, 'o');
if (!sent && chptr->creationtime)
send_channel_mode(cptr, me.name, chptr);
else if (modebuf[1] || *parabuf)
sendmodeto_one(cptr, me.name,
chptr->chname, modebuf, parabuf, chptr->creationtime);
*parabuf = '\0';
*modebuf = '+';
modebuf[1] = '\0';
sent = send_mode_list(cptr, chptr->chname, chptr->creationtime,
chptr->members, CHFL_HALFOP, 'h');
if (!sent && chptr->creationtime)
send_channel_mode(cptr, me.name, chptr);
else if (modebuf[1] || *parabuf)
sendmodeto_one(cptr, me.name,
chptr->chname, modebuf, parabuf, chptr->creationtime);
*parabuf = '\0';
*modebuf = '+';
modebuf[1] = '\0';
(void)send_mode_list(cptr, chptr->chname, chptr->creationtime,
(Member *)chptr->banlist, CHFL_BAN, 'b');
if (modebuf[1] || *parabuf)
sendmodeto_one(cptr, me.name, chptr->chname, modebuf,
parabuf, chptr->creationtime);
*parabuf = '\0';
*modebuf = '+';
modebuf[1] = '\0';
(void)send_mode_list(cptr, chptr->chname, chptr->creationtime,
(Member *)chptr->exlist, CHFL_EXCEPT, 'e');
if (modebuf[1] || *parabuf)
sendmodeto_one(cptr, me.name, chptr->chname, modebuf,
parabuf, chptr->creationtime);
*parabuf = '\0';
*modebuf = '+';
modebuf[1] = '\0';
(void)send_mode_list(cptr, chptr->chname, chptr->creationtime,
chptr->members, CHFL_VOICE, 'v');
if (modebuf[1] || *parabuf)
sendmodeto_one(cptr, me.name, chptr->chname, modebuf,
parabuf, chptr->creationtime);
*parabuf = '\0';
*modebuf = '+';
modebuf[1] = '\0';
(void)send_mode_list(cptr, chptr->chname, chptr->creationtime,
chptr->members, CHFL_CHANOWNER, 'q');
if (modebuf[1] || *parabuf)
sendmodeto_one(cptr, me.name, chptr->chname, modebuf,
parabuf, chptr->creationtime);
*parabuf = '\0';
*modebuf = '+';
modebuf[1] = '\0';
(void)send_mode_list(cptr, chptr->chname, chptr->creationtime,
chptr->members, CHFL_CHANPROT, 'a');
if (modebuf[1] || *parabuf)
sendmodeto_one(cptr, me.name, chptr->chname, modebuf,
parabuf, chptr->creationtime);
}
/*
* m_mode -- written by binary (garryb@binary.islesfan.net)
* Completely rewrote it. The old mode command was 820 lines of ICKY
* coding, which is a complete waste, because I wrote it in 570 lines of
* *decent* coding. This is also easier to read, change, and fine-tune. Plus,
* everything isn't scattered; everything's grouped where it should be.
*
* parv[0] - sender
* parv[1] - channel
*/
CMD_FUNC(m_mode)
{
long unsigned sendts = 0;
Ban *ban;
aChannel *chptr;
/* Now, try to find the channel in question */
if (parc > 1)
{
if (*parv[1] == '#')
{
chptr = find_channel(parv[1], NullChn);
if (chptr == NullChn)
{
return m_umode(cptr, sptr, parc, parv);
}
}
else
return m_umode(cptr, sptr, parc, parv);
}
else
{
sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS),
me.name, parv[0], "MODE");
return 0;
}
if (MyConnect(sptr))
clean_channelname(parv[1]);
if (check_channelmask(sptr, cptr, parv[1]))
return 0;
if (parc < 3)
{
*modebuf = *parabuf = '\0';
modebuf[1] = '\0';
channel_modes(sptr, modebuf, parabuf, chptr);
sendto_one(sptr, rpl_str(RPL_CHANNELMODEIS), me.name, parv[0],
chptr->chname, modebuf, parabuf);
sendto_one(sptr, rpl_str(RPL_CREATIONTIME), me.name, parv[0],
chptr->chname, chptr->creationtime);
return 0;
}
if (IsPerson(sptr) && parc < 4 && ((*parv[2] == 'b'
&& parv[2][1] == '\0') || (parv[2][1] == 'b' && parv[2][2] == '\0'
&& (*parv[2] == '+' || *parv[2] == '-'))))
{
if (!IsMember(sptr, chptr))
return 0;
/* send ban list */
for (ban = chptr->banlist; ban; ban = ban->next)
sendto_one(sptr, rpl_str(RPL_BANLIST), me.name,
sptr->name, chptr->chname, ban->banstr,
ban->who, ban->when);
sendto_one(cptr,
rpl_str(RPL_ENDOFBANLIST), me.name, sptr->name,
chptr->chname);
return 0;
}
if (IsPerson(sptr) && parc < 4 && ((*parv[2] == 'e'
&& parv[2][1] == '\0') || (parv[2][1] == 'e' && parv[2][2] == '\0'
&& (*parv[2] == '+' || *parv[2] == '-'))))
{
if (!IsMember(sptr, chptr))
return 0;
/* send exban list */
for (ban = chptr->exlist; ban; ban = ban->next)
sendto_one(sptr, rpl_str(RPL_EXLIST), me.name,
sptr->name, chptr->chname, ban->banstr,
ban->who, ban->when);
sendto_one(cptr,
rpl_str(RPL_ENDOFEXLIST), me.name, sptr->name,
chptr->chname);
return 0;
}
if (IsPerson(sptr) && parc < 4 && ((*parv[2] == 'q'
&& parv[2][1] == '\0') || (parv[2][1] == 'q' && parv[2][2] == '\0'
&& (*parv[2] == '+' || *parv[2] == '-'))))
{
if (!IsMember(sptr, chptr))
return 0;
{
Member *member;
/* send chanowner list */
/* [Whole story about bad loops removed, sorry ;)]
* Now rewritten so it works (was: bad logic) -- Syzop
*/
for (member = chptr->members; member; member = member->next)
{
if (is_chanowner(member->cptr, chptr))
sendto_one(sptr, rpl_str(RPL_QLIST),
me.name, sptr->name, chptr->chname,
member->cptr->name);
}
sendto_one(cptr,
rpl_str(RPL_ENDOFQLIST), me.name, sptr->name,
chptr->chname);
return 0;
}
}
if (IsPerson(sptr) && parc < 4 && ((*parv[2] == 'a'
&& parv[2][1] == '\0') || (parv[2][1] == 'a' && parv[2][2] == '\0'
&& (*parv[2] == '+' || *parv[2] == '-'))))
{
if (!IsMember(sptr, chptr))
return 0;
{
Member *member;
/* send chanowner list */
/* [Whole story about bad loops removed, sorry ;)]
* Now rewritten so it works (was: bad logic) -- Syzop
*/
for (member = chptr->members; member; member = member->next)
{
if (is_chanprot(member->cptr, chptr))
sendto_one(sptr, rpl_str(RPL_ALIST),
me.name, sptr->name, chptr->chname,
member->cptr->name);
}
sendto_one(cptr,
rpl_str(RPL_ENDOFALIST), me.name, sptr->name,
chptr->chname);
return 0;
}
}
if (IsPerson(sptr) && parc < 4 && ((*parv[2] == 'I'
&& parv[2][1] == '\0') || (parv[2][1] == 'I' && parv[2][2] == '\0'
&& (*parv[2] == '+' || *parv[2] == '-'))))
{
if (!IsMember(sptr, chptr))
return 0;
sendto_one(sptr, rpl_str(RPL_ENDOFINVEXLIST), me.name,
sptr->name, chptr->chname);
return 0;
}
opermode = 0;
#ifndef NO_OPEROVERRIDE
if (IsPerson(sptr) && !IsULine(sptr) && !is_chan_op(sptr, chptr)
&& !is_half_op(sptr, chptr) && (MyClient(sptr) ? (IsOper(sptr) &&
OPCanOverride(sptr)) : IsOper(sptr)))
{
sendts = 0;
opermode = 1;
goto aftercheck;
}
if (IsPerson(sptr) && !IsULine(sptr) && !is_chan_op(sptr, chptr)
&& is_half_op(sptr, chptr) && (MyClient(sptr) ? (IsOper(sptr) &&
OPCanOverride(sptr)) : IsOper(sptr)))
{
opermode = 2;
goto aftercheck;
}
#endif
if (IsPerson(sptr) && !IsULine(sptr) && !is_chan_op(sptr, chptr)
&& !is_half_op(sptr, chptr)
&& (cptr == sptr || !IsSAdmin(sptr) || !IsOper(sptr)))
{
if (cptr == sptr)
{
sendto_one(sptr, err_str(ERR_CHANOPRIVSNEEDED),
me.name, parv[0], chptr->chname);
return 0;
}
sendto_one(cptr, ":%s MODE %s -oh %s %s 0",
me.name, chptr->chname, parv[0], parv[0]);
/* Tell the other server that the user is
* de-opped. Fix op desyncs. */
bounce_mode(chptr, cptr, parc - 2, parv + 2);
return 0;
}
if (IsServer(sptr) && (sendts = TS2ts(parv[parc - 1]))
&& !IsULine(sptr) && chptr->creationtime
&& sendts > chptr->creationtime)
{
if (!(*parv[2] == '&')) /* & denotes a bounce */
{
/* !!! */
sendto_snomask(SNO_EYES,
"*** TS bounce for %s - %lu(ours) %lu(theirs)",
chptr->chname, chptr->creationtime, sendts);
bounce_mode(chptr, cptr, parc - 2, parv + 2);
return 0;
}
/* other server will resync soon enough... */
}
if (IsServer(sptr) && !sendts && *parv[parc - 1] != '0')
sendts = -1;
if (IsServer(sptr) && sendts != -1)
parc--; /* server supplied a time stamp, remove it now */
aftercheck:
/* if (IsPerson(sptr) && IsOper(sptr)) {
if (!is_chan_op(sptr, chptr)) {
if (MyClient(sptr) && !IsULine(cptr) && mode_buf[1])
sendto_snomask(SNO_EYES, "*** OperMode [IRCop: %s] - [Channel: %s] - [Mode: %s %s]",
sptr->name, chptr->chname, mode_buf, parabuf);
sendts = 0;
}
}
*/
/* Filter out the unprivileged FIRST. *
* Now, we can actually do the mode. */
(void)do_mode(chptr, cptr, sptr, parc - 2, parv + 2, sendts, 0);
opermode = 0; /* Important since sometimes forgotten. -- Syzop */
return 0;
}
/* bounce_mode -- written by binary
* User or server is NOT authorized to change the mode. This takes care
* of making the bounce string and bounce it. Because of the 1 for the bounce
* param (last param) of the calls to set_mode and make_mode_str, it will not
* set the mode, but create the bounce string.
*/
void bounce_mode(aChannel *chptr, aClient *cptr, int parc, char *parv[])
{
char pvar[MAXMODEPARAMS][MODEBUFLEN + 3];
int pcount;
set_mode(chptr, cptr, parc, parv, &pcount, pvar, 1);
if (chptr->creationtime)
sendto_one(cptr, ":%s MODE %s &%s %s %lu", me.name,
chptr->chname, modebuf, parabuf, chptr->creationtime);
else
sendto_one(cptr, ":%s MODE %s &%s %s", me.name, chptr->chname,
modebuf, parabuf);
/* the '&' denotes a bounce so servers won't bounce a bounce */
}
/* do_mode -- written by binary
* User or server is authorized to do the mode. This takes care of
* setting the mode and relaying it to other users and servers.
*/
void do_mode(aChannel *chptr, aClient *cptr, aClient *sptr, int parc, char *parv[], time_t sendts, int samode)
{
char pvar[MAXMODEPARAMS][MODEBUFLEN + 3];
int pcount;
char tschange = 0, isbounce = 0; /* fwd'ing bounce */
if (**parv == '&')
isbounce = 1;
set_mode(chptr, sptr, parc, parv, &pcount, pvar, 0);
if (IsServer(sptr))
{
if (sendts > 0)
{
if (!chptr->creationtime
|| sendts < chptr->creationtime)
{
tschange = 1;
/*
if (chptr->creationtime != 0)
sendto_snomask(SNO_EYES, "*** TS fix for %s - %lu(ours) %lu(theirs)",
chptr->chname, chptr->creationtime, sendts);
*/
chptr->
creationtime = sendts;
#if 0
if (sendts < 750000)
sendto_realops(
"Warning! Possible desynch: MODE for channel %s ('%s %s') has fishy timestamp (%ld) (from %s/%s)"
chptr->chname, modebuf, parabuf, sendts, cptr->name, sptr->name);
#endif
/* new chan or our timestamp is wrong */
/* now works for double-bounce prevention */
}
if (sendts > chptr->creationtime && chptr->creationtime)
{
/* theirs is wrong but we let it pass anyway */
sendts = chptr->creationtime;
sendto_one(cptr, ":%s MODE %s + %lu", me.name,
chptr->chname, chptr->creationtime);
}
}
if (sendts == -1 && chptr->creationtime)
sendts = chptr->creationtime;
}
if (*modebuf == '\0' || (*(modebuf + 1) == '\0' && (*modebuf == '+'
|| *modebuf == '-')))
{
if (tschange || isbounce) { /* relay bounce time changes */
if (chptr->creationtime)
sendto_serv_butone_token(cptr, me.name,
MSG_MODE, TOK_MODE, "%s %s+ %lu",
chptr->chname, isbounce ? "&" : "",
chptr->creationtime);
else
sendto_serv_butone_token(cptr, me.name,
MSG_MODE, TOK_MODE, "%s %s+ ",
chptr->chname, isbounce ? "&" : "");
return; /* nothing to send */
}
}
/* opermode for twimodesystem --sts */
#ifndef NO_OPEROVERRIDE
if (opermode == 1)
{
if (modebuf[1])
sendto_snomask(SNO_EYES,
"*** OperOverride -- %s (%s@%s) MODE %s %s %s",
sptr->name, sptr->user->username, sptr->user->realhost,
chptr->chname, modebuf, parabuf);
/* Logging Implementation added by XeRXeS */
ircd_log(LOG_OVERRIDE,"OVERRIDE: %s (%s@%s) MODE %s %s %s",
sptr->name, sptr->user->username, sptr->user->realhost,
chptr->chname, modebuf, parabuf);
sendts = 0;
}
#endif
/* Should stop null modes */
if (*(modebuf + 1) == '\0')
return;
if (IsPerson(sptr) && samode && MyClient(sptr))
{
sendto_serv_butone_token(NULL, me.name, MSG_GLOBOPS,
TOK_GLOBOPS, ":%s used SAMODE %s (%s%s%s)", sptr->name,
chptr->chname, modebuf, *parabuf ? " " : "", parabuf);
sendto_failops_whoare_opers
("from %s: %s used SAMODE %s (%s%s%s)", me.name, sptr->name,
chptr->chname, modebuf, *parabuf ? " " : "", parabuf);
sptr = &me;
sendts = 0;
}
sendto_channel_butserv(chptr, sptr, ":%s MODE %s %s %s",
sptr->name, chptr->chname, modebuf, parabuf);
if (IsServer(sptr) && sendts != -1)
sendto_serv_butone_token(cptr, sptr->name, MSG_MODE, TOK_MODE,
"%s %s%s %s %lu", chptr->chname, isbounce ? "&" : "",
modebuf, parabuf, sendts);
else
sendto_serv_butone_token(cptr, sptr->name, MSG_MODE, TOK_MODE,
"%s %s%s %s", chptr->chname, isbounce ? "&" : "",
modebuf, parabuf);
/* tell them it's not a timestamp, in case the last param
** is a number. */
if (MyConnect(sptr))
RunHook7(HOOKTYPE_LOCAL_CHANMODE, cptr, sptr, chptr, modebuf, parabuf, sendts, samode);
else
RunHook7(HOOKTYPE_REMOTE_CHANMODE, cptr, sptr, chptr, modebuf, parabuf, sendts, samode);
}
/* make_mode_str -- written by binary
* Reconstructs the mode string, to make it look clean. mode_buf will
* contain the +x-y stuff, and the parabuf will contain the parameters.
* If bounce is set to 1, it will make the string it needs for a bounce.
*/
#ifdef EXTCMODE
void make_mode_str(aChannel *chptr, long oldm, Cmode_t oldem, long oldl, int pcount,
char pvar[MAXMODEPARAMS][MODEBUFLEN + 3], char *mode_buf, char *para_buf, char bounce)
#else
void make_mode_str(aChannel *chptr, long oldm, long oldl, int pcount,
char pvar[MAXMODEPARAMS][MODEBUFLEN + 3], char *mode_buf, char *para_buf, char bounce)
#endif
{
char tmpbuf[MODEBUFLEN+3], *tmpstr;
aCtab *tab = &cFlagTab[0];
char *x = mode_buf;
int what, cnt, z;
#ifdef EXTCMODE
int i;
#endif
char *m;
what = 0;
*tmpbuf = '\0';
*mode_buf = '\0';
*para_buf = '\0';
what = 0;
/* + param-less modes */
tab = &cFlagTab[0];
while (tab->mode != 0x0)
{
if (chptr->mode.mode & tab->mode)
{
if (!(oldm & tab->mode))
{
if (what != MODE_ADD)
{
*x++ = bounce ? '-' : '+';
what = MODE_ADD;
}
*x++ = tab->flag;
}
}
tab++;
}
#ifdef EXTCMODE
/* + paramless extmodes... */
for (i=0; i <= Channelmode_highest; i++)
{
if (!Channelmode_Table[i].flag || Channelmode_Table[i].paracount)
continue;
/* have it now and didn't have it before? */
if ((chptr->mode.extmode & Channelmode_Table[i].mode) &&
!(oldem & Channelmode_Table[i].mode))
{
if (what != MODE_ADD)
{
*x++ = bounce ? '-' : '+';
what = MODE_ADD;
}
*x++ = Channelmode_Table[i].flag;
}
}
#endif
*x = '\0';
/* - param-less modes */
tab = &cFlagTab[0];
while (tab->mode != 0x0)
{
if (!(chptr->mode.mode & tab->mode))
{
if (oldm & tab->mode)
{
if (what != MODE_DEL)
{
*x++ = bounce ? '+' : '-';
what = MODE_DEL;
}
*x++ = tab->flag;
}
}
tab++;
}
#ifdef EXTCMODE
/* - extmodes (both "param modes" and paramless don't have
* any params when unsetting...
*/
for (i=0; i <= Channelmode_highest; i++)
{
if (!Channelmode_Table[i].flag /* || Channelmode_Table[i].paracount */)
continue;
/* don't have it now and did have it before */
if (!(chptr->mode.extmode & Channelmode_Table[i].mode) &&
(oldem & Channelmode_Table[i].mode))
{
if (what != MODE_DEL)
{
*x++ = bounce ? '+' : '-';
what = MODE_DEL;
}
*x++ = Channelmode_Table[i].flag;
}
}
#endif
*x = '\0';
/* user limit */
if (chptr->mode.limit != oldl)
{
if ((!bounce && chptr->mode.limit == 0) ||
(bounce && chptr->mode.limit != 0))
{
if (what != MODE_DEL)
{
*x++ = '-';
what = MODE_DEL;
}
if (bounce)
chptr->mode.limit = 0; /* set it back */
*x++ = 'l';
}
else
{
if (what != MODE_ADD)
{
*x++ = '+';
what = MODE_ADD;
}
*x++ = 'l';
if (bounce)
chptr->mode.limit = oldl; /* set it back */
ircsprintf(para_buf, "%s%d ", para_buf, chptr->mode.limit);
}
}
/* reconstruct bkov chain */
for (cnt = 0; cnt < pcount; cnt++)
{
if ((*(pvar[cnt]) == '+') && what != MODE_ADD)
{
*x++ = bounce ? '-' : '+';
what = MODE_ADD;
}
if ((*(pvar[cnt]) == '-') && what != MODE_DEL)
{
*x++ = bounce ? '+' : '-';
what = MODE_DEL;
}
*x++ = *(pvar[cnt] + 1);
tmpstr = &pvar[cnt][2];
z = (MODEBUFLEN * MAXMODEPARAMS);
m = para_buf;
while ((*m)) { m++; }
while ((*tmpstr) && ((m-para_buf) < z))
{
*m = *tmpstr;
m++;
tmpstr++;
}
*m++ = ' ';
*m = '\0';
}
if (bounce)
{
chptr->mode.mode = oldm;
#ifdef EXTCMODE
chptr->mode.extmode = oldem;
#endif
}
z = strlen(para_buf);
if (para_buf[z - 1] == ' ')
para_buf[z - 1] = '\0';
*x = '\0';
if (*mode_buf == '\0')
{
*mode_buf = '+';
mode_buf++;
*mode_buf = '\0';
/* Don't send empty lines. */
}
return;
}
/* do_mode_char
* processes one mode character
* returns 1 if it ate up a param, otherwise 0
* written by binary
* modified for Unreal by stskeeps..
*/
int do_mode_char(aChannel *chptr, long modetype, char modechar, char *param,
u_int what, aClient *cptr,
u_int *pcount, char pvar[MAXMODEPARAMS][MODEBUFLEN + 3], char bounce)
{
aCtab *tab = &cFlagTab[0];
int retval = 0;
Member *member = NULL;
Membership *membership = NULL;
aClient *who;
unsigned int tmp = 0;
char tmpbuf[512], *tmpstr;
char tc = ' '; /* */
int chasing, x;
int xxi, xyi, xzi, hascolon;
char *xp;
int notsecure;
chasing = 0;
if (is_half_op(cptr, chptr) && !is_chan_op(cptr, chptr) && !IsULine(cptr)
&& !op_can_override(cptr))
{
/* Ugly halfop hack --sts
- this allows halfops to do +b +e +v and so on */
/* (Syzop/20040413: Allow remote halfop modes */
if ((Halfop_mode(modetype) == FALSE) && MyClient(cptr))
{
int eaten = 0;
while (tab->mode != 0x0)
{
if (tab->mode == modetype)
{
sendto_one(cptr,
err_str(ERR_NOTFORHALFOPS), me.name,
cptr->name, tab->flag);
eaten = tab->parameters;
break;
}
tab++;
}
return eaten;
}
}
switch (modetype)
{
case MODE_AUDITORIUM:
if (IsULine(cptr) || IsServer(cptr))
goto auditorium_ok;
if (!IsNetAdmin(cptr) && !is_chanowner(cptr, chptr))
{
sendto_one(cptr, err_str(ERR_CHANOWNPRIVNEEDED), me.name, cptr->name,
chptr->chname);
break;
}
auditorium_ok:
goto setthephuckingmode;
case MODE_OPERONLY:
if (!IsAnOper(cptr) && !IsServer(cptr)
&& !IsULine(cptr))
{
sendto_one(cptr, err_str(ERR_NOPRIVILEGES), me.name, cptr->name);
break;
}
goto setthephuckingmode;
case MODE_ADMONLY:
if (!IsSkoAdmin(cptr) && !IsServer(cptr)
&& !IsULine(cptr))
{
sendto_one(cptr, err_str(ERR_NOPRIVILEGES), me.name, cptr->name);
break;
}
goto setthephuckingmode;
case MODE_RGSTR:
if (!IsServer(cptr) && !IsULine(cptr))
{
sendto_one(cptr, err_str(ERR_ONLYSERVERSCANCHANGE), me.name, cptr->name,
chptr->chname);
break;
}
goto setthephuckingmode;
case MODE_SECRET:
case MODE_PRIVATE:
case MODE_MODERATED:
case MODE_TOPICLIMIT:
case MODE_NOPRIVMSGS:
case MODE_RGSTRONLY:
case MODE_MODREG:
case MODE_NOCOLOR:
case MODE_NOKICKS:
case MODE_STRIP:
goto setthephuckingmode;
case MODE_INVITEONLY:
if (what == MODE_DEL && modetype == MODE_INVITEONLY && (chptr->mode.mode & MODE_NOKNOCK))
chptr->mode.mode &= ~MODE_NOKNOCK;
goto setthephuckingmode;
case MODE_NOKNOCK:
if (what == MODE_ADD && modetype == MODE_NOKNOCK && !(chptr->mode.mode & MODE_INVITEONLY))
{
sendto_one(cptr, err_str(ERR_CANNOTCHANGECHANMODE),
me.name, cptr->name, 'K', "+i must be set");
break;
}
goto setthephuckingmode;
case MODE_ONLYSECURE:
notsecure = 0;
if (what == MODE_ADD && modetype == MODE_ONLYSECURE && !(IsServer(cptr) || IsULine(cptr)))
{
for (member = chptr->members; member; member = member->next)
{
if (!IsSecureConnect(member->cptr) && !IsULine(member->cptr))
{
sendto_one(cptr, err_str(ERR_CANNOTCHANGECHANMODE),
me.name, cptr->name, 'z',
"all members must be connected via SSL");
notsecure = 1;
break;
}
}
member = NULL;
/* first break nailed the for loop, this one nails switch() */
if (notsecure == 1) break;
}
goto setthephuckingmode;
#ifdef STRIPBADWORDS
case MODE_STRIPBADWORDS:
#endif
case MODE_NOCTCP:
case MODE_NONICKCHANGE:
case MODE_NOINVITE:
setthephuckingmode:
retval = 0;
if (what == MODE_ADD) {
/* +sp bugfix.. (by JK/Luke)*/
if (modetype == MODE_SECRET
&& (chptr->mode.mode & MODE_PRIVATE))
chptr->mode.mode &= ~MODE_PRIVATE;
if (modetype == MODE_PRIVATE
&& (chptr->mode.mode & MODE_SECRET))
chptr->mode.mode &= ~MODE_SECRET;
if (modetype == MODE_NOCOLOR
&& (chptr->mode.mode & MODE_STRIP))
chptr->mode.mode &= ~MODE_STRIP;
if (modetype == MODE_STRIP
&& (chptr->mode.mode & MODE_NOCOLOR))
chptr->mode.mode &= ~MODE_NOCOLOR;
chptr->mode.mode |= modetype;
}
else
{
chptr->mode.mode &= ~modetype;
#ifdef NEWCHFLOODPROT
/* reset joinflood on -i, reset msgflood on -m, etc.. */
if (chptr->mode.floodprot)
{
switch(modetype)
{
case MODE_NOCTCP:
chptr->mode.floodprot->c[FLD_CTCP] = 0;
chanfloodtimer_del(chptr, 'C', MODE_NOCTCP);
break;
case MODE_NONICKCHANGE:
chptr->mode.floodprot->c[FLD_NICK] = 0;
chanfloodtimer_del(chptr, 'N', MODE_NONICKCHANGE);
break;
case MODE_MODERATED:
chptr->mode.floodprot->c[FLD_MSG] = 0;
chptr->mode.floodprot->c[FLD_CTCP] = 0;
chanfloodtimer_del(chptr, 'm', MODE_MODERATED);
break;
case MODE_NOKNOCK:
chptr->mode.floodprot->c[FLD_KNOCK] = 0;
chanfloodtimer_del(chptr, 'K', MODE_NOKNOCK);
break;
case MODE_INVITEONLY:
chptr->mode.floodprot->c[FLD_JOIN] = 0;
chanfloodtimer_del(chptr, 'i', MODE_INVITEONLY);
break;
case MODE_MODREG:
chptr->mode.floodprot->c[FLD_MSG] = 0;
chptr->mode.floodprot->c[FLD_CTCP] = 0;
chanfloodtimer_del(chptr, 'M', MODE_MODREG);
break;
case MODE_RGSTRONLY:
chptr->mode.floodprot->c[FLD_JOIN] = 0;
chanfloodtimer_del(chptr, 'R', MODE_RGSTRONLY);
break;
default:
break;
}
}
#endif
}
break;
/* do pro-opping here (popping) */
case MODE_CHANOWNER:
if (!IsULine(cptr) && !IsServer(cptr)
&& !is_chanowner(cptr, chptr))
{
if (IsNetAdmin(cptr))
{
if (!is_halfop(cptr, chptr)) /* htrig will take care of halfop override notices */
opermode = 1;
}
else if (MyClient(cptr))
{
sendto_one(cptr, err_str(ERR_CHANOWNPRIVNEEDED),
me.name, cptr->name, chptr->chname);
break;
}
}
case MODE_CHANPROT:
if (!IsULine(cptr) && !IsServer(cptr)
&& !is_chanowner(cptr, chptr))
{
if (IsNetAdmin(cptr))
{
if (!is_halfop(cptr, chptr)) /* htrig will take care of halfop override notices */
opermode = 1;
}
else if (MyClient(cptr))
{
sendto_one(cptr, err_str(ERR_CHANOWNPRIVNEEDED), me.name, cptr->name,
chptr->chname);
break;
}
}
case MODE_HALFOP:
case MODE_CHANOP:
case MODE_VOICE:
if (!param || *pcount >= MAXMODEPARAMS)
{
retval = 0;
break;
}
retval = 1;
if (!(who = find_chasing(cptr, param, &chasing)))
break;
if (!who->user)
break;
/* codemastr: your patch is a good idea here, but look at the
member->flags stuff longer down. this caused segfaults */
if (!(membership = find_membership_link(who->user->channel, chptr)))
{
sendto_one(cptr, err_str(ERR_USERNOTINCHANNEL),
me.name, cptr->name, who->name, chptr->chname);
break;
}
member = find_member_link(chptr->members, who);
if (!member)
{
/* should never happen */
sendto_realops("crap! find_membership_link && !find_member_link !!. Report to unreal team");
break;
}
/* we make the rules, we bend the rules */
if (IsServer(cptr) || IsULine(cptr))
goto breaktherules;
/* Services are special! */
if (IsServices(member->cptr) && MyClient(cptr) && !IsNetAdmin(cptr) && (what == MODE_DEL))
{
char errbuf[NICKLEN+50];
ircsprintf(errbuf, "%s is a network service", member->cptr->name);
sendto_one(cptr, err_str(ERR_CANNOTCHANGECHANMODE), me.name, cptr->name,
modechar, errbuf);
break;
}
if (is_chanowner(member->cptr, chptr)
&& member->cptr != cptr
&& !is_chanowner(cptr, chptr) && !IsServer(cptr)
&& !IsULine(cptr) && !opermode && (what == MODE_DEL))
{
if (MyClient(cptr))
{
char errbuf[NICKLEN+30];
ircsprintf(errbuf, "%s is a channel owner", member->cptr->name);
sendto_one(cptr, err_str(ERR_CANNOTCHANGECHANMODE), me.name, cptr->name,
modechar, errbuf);
break;
} else
if (IsOper(cptr))
opermode = 1;
}
if (is_chanprot(member->cptr, chptr)
&& member->cptr != cptr
&& !is_chanowner(cptr, chptr) && !IsServer(cptr) && !opermode
&& modetype != MODE_CHANOWNER && (what == MODE_DEL))
{
if (MyClient(cptr))
{
char errbuf[NICKLEN+30];
ircsprintf(errbuf, "%s is a channel admin", member->cptr->name);
sendto_one(cptr, err_str(ERR_CANNOTCHANGECHANMODE), me.name, cptr->name,
modechar, errbuf);
break;
} else
if (IsOper(cptr))
opermode = 1;
}
breaktherules:
tmp = member->flags;
if (what == MODE_ADD)
member->flags |= modetype;
else
member->flags &= ~modetype;
if ((tmp == member->flags) && (bounce || !IsULine(cptr)))
break;
/* It's easier to undo the mode here instead of later
* when you call make_mode_str for a bounce string.
* Why set it if it will be instantly removed?
* Besides, pvar keeps a log of it. */
if (bounce)
member->flags = tmp;
if (modetype == MODE_CHANOWNER)
tc = 'q';
if (modetype == MODE_CHANPROT)
tc = 'a';
if (modetype == MODE_CHANOP)
tc = 'o';
if (modetype == MODE_HALFOP)
tc = 'h';
if (modetype == MODE_VOICE)
tc = 'v';
/* Make sure membership->flags and member->flags is the same */
membership->flags = member->flags;
(void)ircsprintf(pvar[*pcount], "%c%c%s",
what == MODE_ADD ? '+' : '-', tc, who->name);
(*pcount)++;
break;
case MODE_LIMIT:
if (what == MODE_ADD)
{
if (!param)
{
retval = 0;
break;
}
retval = 1;
tmp = atoi(param);
if (chptr->mode.limit == tmp)
break;
chptr->mode.limit = tmp;
}
else
{
retval = 0;
if (!chptr->mode.limit)
break;
chptr->mode.limit = 0;
}
break;
case MODE_KEY:
if (!param || *pcount >= MAXMODEPARAMS)
{
retval = 0;
break;
}
retval = 1;
for (x = 0; x < *pcount; x++)
{
if (pvar[x][1] == 'k')
{ /* don't allow user to change key
* more than once per command. */
retval = 0;
break;
}
}
if (retval == 0) /* you can't break a case from loop */
break;
if (what == MODE_ADD)
{
if (!bounce) { /* don't do the mode at all. */
char *tmp;
if ((tmp = strchr(param, ' ')))
*tmp = '\0';
if ((tmp = strchr(param, ':')))
*tmp = '\0';
if ((tmp = strchr(param, ',')))
*tmp = '\0';
if (*param == '\0')
break;
if (strlen(param) > KEYLEN)
param[KEYLEN] = '\0';
if (!strcmp(chptr->mode.key, param))
break;
strncpyzt(chptr->mode.key, param,
sizeof(chptr->mode.key));
}
tmpstr = param;
}
else
{
if (!*chptr->mode.key)
break; /* no change */
strncpyzt(tmpbuf, chptr->mode.key, sizeof(tmpbuf));
tmpstr = tmpbuf;
if (!bounce)
strcpy(chptr->mode.key, "");
}
retval = 1;
(void)ircsprintf(pvar[*pcount], "%ck%s",
what == MODE_ADD ? '+' : '-', tmpstr);
(*pcount)++;
break;
case MODE_BAN:
if (!param || *pcount >= MAXMODEPARAMS)
{
retval = 0;
break;
}
retval = 1;
tmpstr = clean_ban_mask(param, what, cptr);
if (BadPtr(tmpstr))
break; /* ignore ban, but eat param */
if ((tmpstr[0] == '~') && MyClient(cptr) && !bounce)
{
/* extban: check access if needed */
Extban *p = findmod_by_bantype(tmpstr[1]);
if (p && p->is_ok)
{
if (!p->is_ok(cptr, chptr, tmpstr, EXBCHK_ACCESS, what, EXBTYPE_BAN))
{
if (IsAnOper(cptr))
{
/* TODO: send operoverride notice */
} else {
p->is_ok(cptr, chptr, tmpstr, EXBCHK_ACCESS_ERR, what, EXBTYPE_BAN);
break;
}
}
if (!p->is_ok(cptr, chptr, tmpstr, EXBCHK_PARAM, what, EXBTYPE_BAN))
break;
}
}
/* For bounce, we don't really need to worry whether
* or not it exists on our server. We'll just always
* bounce it. */
if (!bounce &&
((what == MODE_ADD && add_banid(cptr, chptr, tmpstr))
|| (what == MODE_DEL && del_banid(chptr, tmpstr))))
break; /* already exists */
(void)ircsprintf(pvar[*pcount], "%cb%s",
what == MODE_ADD ? '+' : '-', tmpstr);
(*pcount)++;
break;
case MODE_EXCEPT:
if (!param || *pcount >= MAXMODEPARAMS)
{
retval = 0;
break;
}
retval = 1;
tmpstr = clean_ban_mask(param, what, cptr);
if (BadPtr(tmpstr))
break; /* ignore except, but eat param */
if ((tmpstr[0] == '~') && MyClient(cptr) && !bounce)
{
/* extban: check access if needed */
Extban *p = findmod_by_bantype(tmpstr[1]);
if (p && p->is_ok)
{
if (!p->is_ok(cptr, chptr, tmpstr, EXBCHK_ACCESS, what, EXBTYPE_EXCEPT))
{
if (IsAnOper(cptr))
{
/* TODO: send operoverride notice */
} else {
p->is_ok(cptr, chptr, tmpstr, EXBCHK_ACCESS_ERR, what, EXBTYPE_EXCEPT);
break;
}
}
if (!p->is_ok(cptr, chptr, tmpstr, EXBCHK_PARAM, what, EXBTYPE_EXCEPT))
break;
}
}
/* For bounce, we don't really need to worry whether
* or not it exists on our server. We'll just always
* bounce it. */
if (!bounce &&
((what == MODE_ADD && add_exbanid(cptr, chptr, tmpstr))
|| (what == MODE_DEL && del_exbanid(chptr, tmpstr))))
break; /* already exists */
(void)ircsprintf(pvar[*pcount], "%ce%s",
what == MODE_ADD ? '+' : '-', tmpstr);
(*pcount)++;
break;
case MODE_LINK:
if (IsULine(cptr) || IsServer(cptr))
{
goto linkok;
}
if (!IsNetAdmin(cptr) && !is_chanowner(cptr, chptr))
{
sendto_one(cptr, err_str(ERR_CHANOWNPRIVNEEDED), me.name, cptr->name,
chptr->chname);
break;
}
if (!chptr->mode.limit && what == MODE_ADD)
{
sendto_one(cptr, err_str(ERR_CANNOTCHANGECHANMODE),
me.name, cptr->name, 'L', "+l must be set");
break;
}
linkok:
retval = 1;
for (x = 0; x < *pcount; x++)
{
if (pvar[x][1] == 'L')
{ /* don't allow user to change link
* more than once per command. */
retval = 0;
break;
}
}
if (retval == 0) /* you can't break a case from loop */
break;
if (what == MODE_ADD)
{
char *tmp;
if (!param || *pcount >= MAXMODEPARAMS)
{
retval = 0;
break;
}
if (strchr(param, ','))
break;
if (!IsChannelName(param))
{
if (MyClient(cptr))
sendto_one(cptr,
err_str(ERR_NOSUCHCHANNEL),
me.name, cptr->name, param);
break;
}
/* Now make it a clean channelname.. This has to be done before all checking
* because it could have been changed later to something disallowed (like
* self-linking). -- Syzop
*/
strlcpy(tmpbuf, param, CHANNELLEN+1);
clean_channelname(tmpbuf);
/* don't allow linking to local chans either.. */
if ((tmp = strchr(tmpbuf, ':')))
*tmp = '\0';
if (!stricmp(tmpbuf, chptr->mode.link))
break;
if (!stricmp(tmpbuf, chptr->chname))
{
if (MyClient(cptr))
sendto_one(cptr, err_str(ERR_CANNOTCHANGECHANMODE),
me.name, cptr->name, 'L',
"a channel cannot be linked to itself");
break;
}
if (!bounce) /* don't do the mode at all. */
{
strncpyzt(chptr->mode.link, tmpbuf,
sizeof(chptr->mode.link));
tmpstr = tmpbuf;
} else
tmpstr = param; /* Use the original value if bounce?? -- Syzop */
}
else
{
if (!*chptr->mode.link)
break; /* no change */
strncpyzt(tmpbuf, chptr->mode.link, sizeof(tmpbuf));
tmpstr = tmpbuf;
if (!bounce)
{
strcpy(chptr->mode.link, "");
}
}
retval = 1;
(void)ircsprintf(pvar[*pcount], "%cL%s",
what == MODE_ADD ? '+' : '-', tmpstr);
(*pcount)++;
break;
case MODE_FLOODLIMIT:
retval = 1;
for (x = 0; x < *pcount; x++)
{
if (pvar[x][1] == 'f')
{ /* don't allow user to change flood
* more than once per command. */
retval = 0;
break;
}
}
if (retval == 0) /* you can't break a case from loop */
break;
#ifndef NEWCHFLOODPROT
if (what == MODE_ADD)
{
if (!bounce) /* don't do the mode at all. */
{
if (!param || *pcount >= MAXMODEPARAMS)
{
retval = 0;
break;
}
/* like 1:1 and if its less than 3 chars then ahem.. */
if (strlen(param) < 3)
{
break;
}
/* may not contain other chars
than 0123456789: & NULL */
hascolon = 0;
for (xp = param; *xp; xp++)
{
if (*xp == ':')
hascolon++;
/* fast alpha check */
if (((*xp < '0') || (*xp > '9'))
&& (*xp != ':')
&& (*xp != '*'))
goto break_flood;
/* uh oh, not the first char */
if (*xp == '*' && (xp != param))
goto break_flood;
}
/* We can avoid 2 strchr() and a strrchr() like this
* it should be much faster. -- codemastr
*/
if (hascolon != 1)
break;
if (*param == '*')
{
xzi = 1;
// chptr->mode.kmode = 1;
}
else
{
xzi = 0;
// chptr->mode.kmode = 0;
}
xp = index(param, ':');
*xp = '\0';
xxi =
atoi((*param ==
'*' ? (param + 1) : param));
xp++;
xyi = atoi(xp);
if (xxi > 500 || xyi > 500)
break;
xp--;
*xp = ':';
if ((xxi == 0) || (xyi == 0))
break;
if ((chptr->mode.msgs == xxi)
&& (chptr->mode.per == xyi)
&& (chptr->mode.kmode == xzi))
break;
chptr->mode.msgs = xxi;
chptr->mode.per = xyi;
chptr->mode.kmode = xzi;
}
tmpstr = param;
retval = 1;
}
else
{
if (!chptr->mode.msgs || !chptr->mode.per)
break; /* no change */
ircsprintf(tmpbuf,
(chptr->mode.kmode > 0 ? "*%i:%i" : "%i:%i"),
chptr->mode.msgs, chptr->mode.per);
tmpstr = tmpbuf;
if (!bounce)
{
chptr->mode.msgs = chptr->mode.per =
chptr->mode.kmode = 0;
}
retval = 0;
}
#else
/* NEW */
if (what == MODE_ADD)
{
if (!bounce) /* don't do the mode at all. */
{
ChanFloodProt newf;
memset(&newf, 0, sizeof(newf));
if (!param || *pcount >= MAXMODEPARAMS)
{
retval = 0;
break;
}
/* old +f was like +f 10:5 or +f *10:5
* new is +f [5c,30j,10t#b]:15
* +f 10:5 --> +f [10t]:5
* +f *10:5 --> +f [10t#b]:5
*/
if (param[0] != '[')
{
/* <<OLD +f>> */
/* like 1:1 and if its less than 3 chars then ahem.. */
if (strlen(param) < 3)
{
break;
}
/* may not contain other chars
than 0123456789: & NULL */
hascolon = 0;
for (xp = param; *xp; xp++)
{
if (*xp == ':')
hascolon++;
/* fast alpha check */
if (((*xp < '0') || (*xp > '9'))
&& (*xp != ':')
&& (*xp != '*'))
goto break_flood;
/* uh oh, not the first char */
if (*xp == '*' && (xp != param))
goto break_flood;
}
/* We can avoid 2 strchr() and a strrchr() like this
* it should be much faster. -- codemastr
*/
if (hascolon != 1)
break;
if (*param == '*')
{
xzi = 1;
// chptr->mode.kmode = 1;
}
else
{
xzi = 0;
// chptr->mode.kmode = 0;
}
xp = index(param, ':');
*xp = '\0';
xxi =
atoi((*param ==
'*' ? (param + 1) : param));
xp++;
xyi = atoi(xp);
if (xxi > 500 || xyi > 500)
break;
xp--;
*xp = ':';
if ((xxi == 0) || (xyi == 0))
break;
/* ok, we passed */
newf.l[FLD_TEXT] = xxi;
newf.per = xyi;
if (xzi == 1)
newf.a[FLD_TEXT] = 'b';
} else {
/* NEW +F */
char xbuf[256], c, a, *p, *p2, *x = xbuf+1;
int v;
unsigned short warnings = 0, breakit;
unsigned char r;
/* '['<number><1 letter>[optional: '#'+1 letter],[next..]']'':'<number> */
strlcpy(xbuf, param, sizeof(xbuf));
p2 = strchr(xbuf+1, ']');
if (!p2)
break;
*p2 = '\0';
if (*(p2+1) != ':')
break;
breakit = 0;
for (x = strtok(xbuf+1, ","); x; x = strtok(NULL, ","))
{
/* <number><1 letter>[optional: '#'+1 letter] */
p = x;
while(isdigit(*p)) { p++; }
if ((*p == '\0') ||
!((*p == 'c') || (*p == 'j') || (*p == 'k') ||
(*p == 'm') || (*p == 'n') || (*p == 't')))
{
if (MyClient(cptr) && *p && (warnings++ < 3))
sendto_one(cptr, ":%s NOTICE %s :warning: channelmode +f: floodtype '%c' unknown, ignored.",
me.name, cptr->name, *p);
continue; /* continue instead of break for forward compatability. */
}
c = *p;
*p = '\0';
v = atoi(x);
if ((v < 1) || (v > 999)) /* out of range... */
{
if (MyClient(cptr))
{
sendto_one(cptr, err_str(ERR_CANNOTCHANGECHANMODE),
me.name, cptr->name,
'f', "value should be from 1-999");
breakit = 1;
break;
} else
continue; /* just ignore for remote servers */
}
p++;
a = '\0';
r = MyClient(cptr) ? MODEF_DEFAULT_UNSETTIME : 0;
if (*p != '\0')
{
if (*p == '#')
{
p++;
a = *p;
p++;
if (*p != '\0')
{
int tv;
tv = atoi(p);
if (tv <= 0)
tv = 0; /* (ignored) */
if (tv > (MyClient(cptr) ? MODEF_MAX_UNSETTIME : 255))
tv = (MyClient(cptr) ? MODEF_MAX_UNSETTIME : 255); /* set to max */
r = (unsigned char)tv;
}
}
}
switch(c)
{
case 'c':
newf.l[FLD_CTCP] = v;
if ((a == 'm') || (a == 'M'))
newf.a[FLD_CTCP] = a;
else
newf.a[FLD_CTCP] = 'C';
newf.r[FLD_CTCP] = r;
break;
case 'j':
newf.l[FLD_JOIN] = v;
if (a == 'R')
newf.a[FLD_JOIN] = a;
else
newf.a[FLD_JOIN] = 'i';
newf.r[FLD_JOIN] = r;
break;
case 'k':
newf.l[FLD_KNOCK] = v;
newf.a[FLD_KNOCK] = 'K';
newf.r[FLD_KNOCK] = r;
break;
case 'm':
newf.l[FLD_MSG] = v;
if (a == 'M')
newf.a[FLD_MSG] = a;
else
newf.a[FLD_MSG] = 'm';
newf.r[FLD_MSG] = r;
break;
case 'n':
newf.l[FLD_NICK] = v;
newf.a[FLD_NICK] = 'N';
newf.r[FLD_NICK] = r;
break;
case 't':
newf.l[FLD_TEXT] = v;
if (a == 'b')
newf.a[FLD_TEXT] = a;
/** newf.r[FLD_TEXT] ** not supported */
break;
default:
breakit=1;
break;
}
if (breakit)
break;
} /* for */
if (breakit)
break;
/* parse 'per' */
p2++;
if (*p2 != ':')
break;
p2++;
if (!*p2)
break;
v = atoi(p2);
if ((v < 1) || (v > 999)) /* 'per' out of range */
{
if (MyClient(cptr))
sendto_one(cptr, err_str(ERR_CANNOTCHANGECHANMODE),
me.name, cptr->name, 'f',
"time range should be 1-999");
break;
}
newf.per = v;
/* Is anything turned on? (to stop things like '+f []:15' */
breakit = 1;
for (v=0; v < NUMFLD; v++)
if (newf.l[v])
breakit=0;
if (breakit)
break;
} /* if param[0] == '[' */
if (chptr->mode.floodprot &&
!memcmp(chptr->mode.floodprot, &newf, sizeof(ChanFloodProt)))
break; /* They are identical */
/* Good.. store the mode (and alloc if needed) */
if (!chptr->mode.floodprot)
chptr->mode.floodprot = MyMalloc(sizeof(ChanFloodProt));
memcpy(chptr->mode.floodprot, &newf, sizeof(ChanFloodProt));
strcpy(tmpbuf, channel_modef_string(chptr->mode.floodprot));
tmpstr = tmpbuf;
} else {
/* bounce... */
tmpstr = param;
}
retval = 1;
} else
{ /* MODE_DEL */
if (!chptr->mode.floodprot)
break; /* no change */
if (!bounce)
{
strcpy(tmpbuf, channel_modef_string(chptr->mode.floodprot));
tmpstr = tmpbuf;
free(chptr->mode.floodprot);
chptr->mode.floodprot = NULL;
chanfloodtimer_stopchantimers(chptr);
} else {
/* bounce.. */
tmpstr = param;
}
retval = 0; /* ??? copied from previous +f code. */
}
#endif
(void)ircsprintf(pvar[*pcount], "%cf%s",
what == MODE_ADD ? '+' : '-', tmpstr);
(*pcount)++;
break_flood:
break;
}
return retval;
}
#ifdef EXTCMODE
/** Check access and if granted, set the extended chanmode to the requested value in memory.
* note: if bounce is requested then the mode will not be set.
* @returns amount of params eaten (0 or 1)
*/
int do_extmode_char(aChannel *chptr, int modeindex, char *param, u_int what,
aClient *cptr, u_int *pcount, char pvar[MAXMODEPARAMS][MODEBUFLEN + 3],
char bounce)
{
int paracnt = (what == MODE_ADD) ? Channelmode_Table[modeindex].paracount : 0;
int x;
/* Expected a param and it isn't there? */
if (paracnt && (!param || (*pcount >= MAXMODEPARAMS)))
return 0;
if (MyClient(cptr))
{
x = Channelmode_Table[modeindex].is_ok(cptr, chptr, param, EXCHK_ACCESS, what);
if ((x == EX_ALWAYS_DENY) ||
((x == EX_DENY) && !op_can_override(cptr)))
{
Channelmode_Table[modeindex].is_ok(cptr, chptr, param, EXCHK_ACCESS_ERR, what);
return paracnt; /* Denied & error msg sent */
}
if (x == EX_DENY)
opermode = 1; /* override in progress... */
} else {
/* remote user: we only need to check if we need to generate an operoverride msg */
if (!IsULine(cptr) && IsPerson(cptr) && op_can_override(cptr) &&
(Channelmode_Table[modeindex].is_ok(cptr, chptr, param, EXCHK_ACCESS, what) != EX_ALLOW))
opermode = 1; /* override in progress... */
}
/* Check for multiple changes in 1 command (like +y-y+y 1 2, or +yy 1 2). */
for (x = 0; x < *pcount; x++)
{
if (pvar[x][1] == Channelmode_Table[modeindex].flag)
{
/* this is different than the old chanmode system, coz:
* "mode #chan +kkL #a #b #c" will get "+kL #a #b" which is wrong :p.
* we do eat the parameter. -- Syzop
*/
return paracnt;
}
}
/* w00t... a parameter mode */
if (Channelmode_Table[modeindex].paracount)
{
if (what == MODE_DEL)
{
if (!(chptr->mode.extmode & Channelmode_Table[modeindex].mode))
return paracnt; /* There's nothing to remove! */
/* del means any parameter is ok, the one-who-is-set will be used */
ircsprintf(pvar[*pcount], "-%c", Channelmode_Table[modeindex].flag);
} else {
/* add: is the parameter ok? */
if (Channelmode_Table[modeindex].is_ok(cptr, chptr, param, EXCHK_PARAM, what) == FALSE)
return paracnt;
/* is it already set at the same value? if so, ignore it. */
if (chptr->mode.extmode & Channelmode_Table[modeindex].mode)
{
char *p, *p2;
p = Channelmode_Table[modeindex].get_param(extcmode_get_struct(chptr->mode.extmodeparam,Channelmode_Table[modeindex].flag));
p2 = Channelmode_Table[modeindex].conv_param(param);
if (p && p2 && !strcmp(p, p2))
return paracnt; /* ignore... */
}
ircsprintf(pvar[*pcount], "+%c%s",
Channelmode_Table[modeindex].flag, Channelmode_Table[modeindex].conv_param(param));
(*pcount)++;
}
}
if (bounce) /* bounce here means: only check access and return return value */
return paracnt;
if (what == MODE_ADD)
{ /* + */
chptr->mode.extmode |= Channelmode_Table[modeindex].mode;
if (Channelmode_Table[modeindex].paracount)
{
CmodeParam *p = extcmode_get_struct(chptr->mode.extmodeparam, Channelmode_Table[modeindex].flag);
CmodeParam *r;
r = Channelmode_Table[modeindex].put_param(p, param);
if (r != p)
AddListItem(r, chptr->mode.extmodeparam);
}
} else
{ /* - */
chptr->mode.extmode &= ~(Channelmode_Table[modeindex].mode);
if (Channelmode_Table[modeindex].paracount)
{
CmodeParam *p = extcmode_get_struct(chptr->mode.extmodeparam, Channelmode_Table[modeindex].flag);
if (p)
{
DelListItem(p, chptr->mode.extmodeparam);
Channelmode_Table[modeindex].free_param(p);
}
}
}
return paracnt;
}
#endif /* EXTCMODE */
/*
* ListBits(bitvalue, bitlength);
* written by Stskeeps
*/
#ifdef DEVELOP
char *ListBits(long bits, long length)
{
char *bitstr, *p;
long l, y;
y = 1;
bitstr = (char *)MyMalloc(length + 1);
p = bitstr;
for (l = 1; l <= length; l++)
{
if (bits & y)
*p = '1';
else
*p = '0';
p++;
y = y + y;
}
*p = '\0';
return (bitstr);
}
#endif
/* set_mode
* written by binary
*/
void set_mode(aChannel *chptr, aClient *cptr, int parc, char *parv[], u_int *pcount,
char pvar[MAXMODEPARAMS][MODEBUFLEN + 3], int bounce)
{
char *curchr;
u_int what = MODE_ADD;
long modetype = 0;
int paracount = 1;
#ifdef DEVELOP
char *tmpo = NULL;
#endif
aCtab *tab = &cFlagTab[0];
aCtab foundat;
int found = 0;
unsigned int htrig = 0;
long oldm, oldl;
int checkrestr = 0, warnrestr = 1;
#ifdef EXTCMODE
int extm = 1000000; /* (default value not used but stops gcc from complaining) */
Cmode_t oldem;
#endif
paracount = 1;
*pcount = 0;
oldm = chptr->mode.mode;
oldl = chptr->mode.limit;
#ifdef EXTCMODE
oldem = chptr->mode.extmode;
#endif
if (RESTRICT_CHANNELMODES && MyClient(cptr) && !IsAnOper(cptr) && !IsServer(cptr)) /* "cache" this */
checkrestr = 1;
for (curchr = parv[0]; *curchr; curchr++)
{
switch (*curchr)
{
case '+':
what = MODE_ADD;
break;
case '-':
what = MODE_DEL;
break;
#ifdef DEVELOP
case '^':
tmpo = (char *)ListBits(chptr->mode.mode, 64);
sendto_one(cptr,
":%s NOTICE %s :*** %s mode is %li (0x%lx) [%s]",
me.name, cptr->name, chptr->chname,
chptr->mode.mode, chptr->mode.mode, tmpo);
MyFree(tmpo);
break;
#endif
default:
found = 0;
tab = &cFlagTab[0];
while ((tab->mode != 0x0) && found == 0)
{
if (tab->flag == *curchr)
{
found = 1;
foundat = *tab;
}
tab++;
}
if (found == 1)
{
modetype = foundat.mode;
} else {
#ifdef EXTCMODE
/* Maybe in extmodes */
for (extm=0; extm <= Channelmode_highest; extm++)
{
if (Channelmode_Table[extm].flag == *curchr)
{
found = 2;
break;
}
}
#endif
}
if (found == 0) /* Mode char unknown */
{
/* temporary hack: eat parameters of certain future chanmodes.. */
if (*curchr == 'I')
paracount++;
if ((*curchr == 'j') && (what == MODE_ADD))
paracount++;
if (MyClient(cptr))
sendto_one(cptr, err_str(ERR_UNKNOWNMODE),
me.name, cptr->name, *curchr);
break;
}
if (checkrestr && strchr(RESTRICT_CHANNELMODES, *curchr))
{
if (warnrestr)
{
sendto_one(cptr, ":%s %s %s :Setting/removing of channelmode(s) '%s' has been disabled.",
me.name, IsWebTV(cptr) ? "PRIVMSG" : "NOTICE", cptr->name, RESTRICT_CHANNELMODES);
warnrestr = 0;
}
paracount += foundat.parameters;
break;
}
#ifndef NO_OPEROVERRIDE
if (found == 1)
{
if ((Halfop_mode(modetype) == FALSE) && opermode == 2 && htrig != 1)
{
opermode = 0;
htrig = 1;
}
}
#ifdef EXTCMODE
else if (found == 2) {
/* Extended mode: all override stuff is in do_extmode_char which will set
* opermode if appropriate. -- Syzop
*/
}
#endif /* EXTCMODE */
#endif /* !NO_OPEROVERRIDE */
/* We can afford to send off a param */
if (parc <= paracount)
parv[paracount] = NULL;
if (parv[paracount] &&
strlen(parv[paracount]) >= MODEBUFLEN)
parv[paracount][MODEBUFLEN-1] = '\0';
if (found == 1)
{
paracount +=
do_mode_char(chptr, modetype, *curchr,
parv[paracount], what, cptr, pcount, pvar,
bounce);
}
#ifdef EXTCMODE
else if (found == 2)
{
paracount += do_extmode_char(chptr, extm, parv[paracount],
what, cptr, pcount, pvar, bounce);
}
#endif /* EXTCMODE */
break;
}
}
#ifdef EXTCMODE
make_mode_str(chptr, oldm, oldem, oldl, *pcount, pvar, modebuf, parabuf, bounce);
#else
make_mode_str(chptr, oldm, oldl, *pcount, pvar, modebuf, parabuf, bounce);
#endif
#ifndef NO_OPEROVERRIDE
if (htrig == 1)
{
/* This is horrible. Just horrible. */
if (!((modebuf[0] == '+' || modebuf[0] == '-') && modebuf[1] == '\0'))
sendto_snomask(SNO_EYES, "*** OperOverride -- %s (%s@%s) MODE %s %s %s",
cptr->name, cptr->user->username, cptr->user->realhost,
chptr->chname, modebuf, parabuf);
/* Logging Implementation added by XeRXeS */
ircd_log(LOG_OVERRIDE,"OVERRIDE: %s (%s@%s) MODE %s %s %s",
cptr->name, cptr->user->username, cptr->user->realhost,
chptr->chname, modebuf, parabuf);
htrig = 0;
opermode = 0; /* stop double override notices... but is this ok??? -- Syzop */
}
#endif
}
int DoesOp(char *modebuf)
{
modebuf--; /* Is it possible that a mode starts with o and not +o ? */
while (*++modebuf)
if (*modebuf == 'h' || *modebuf == 'o'
|| *modebuf == 'v' || *modebuf == 'q')
return (1);
return 0;
}
int sendmodeto_one(aClient *cptr, char *from, char *name, char *mode, char *param, TS creationtime)
{
if ((IsServer(cptr) && DoesOp(mode) && creationtime) ||
IsULine(cptr))
sendto_one(cptr, ":%s %s %s %s %s %lu", from,
(IsToken(cptr) ? TOK_MODE : MSG_MODE), name, mode,
param, creationtime);
else
sendto_one(cptr, ":%s %s %s %s %s", from,
(IsToken(cptr) ? TOK_MODE : MSG_MODE), name, mode, param);
return 0;
}
char *pretty_mask(char *mask)
{
char *cp;
char *user;
char *host;
if ((user = index((cp = mask), '!')))
*user++ = '\0';
if ((host = rindex(user ? user : cp, '@')))
{
*host++ = '\0';
if (!user)
return make_nick_user_host(NULL, cp, host);
}
else if (!user && index(cp, '.'))
return make_nick_user_host(NULL, NULL, cp);
return make_nick_user_host(cp, user, host);
}
char *trim_str(char *str, int len)
{
int l;
if (!str)
return NULL;
if ((l = strlen(str)) > len)
{
str += l - len;
*str = '*';
}
return str;
}
/* clean_ban_mask: makes a proper banmask
* RETURNS: pointer to correct banmask or NULL in case of error
* NOTES:
* - A pointer is returned to a static buffer, which is overwritten
* on next clean_ban_mask or make_nick_user_host call.
* - mask is fragged in some cases, this could be bad.
*/
char *clean_ban_mask(char *mask, int what, aClient *cptr)
{
char *cp;
char *user;
char *host;
Extban *p;
cp = index(mask, ' ');
if (cp)
*cp = '\0';
/* Strip any ':' at beginning coz that desynchs clients/banlists */
for (; (*mask && (*mask == ':')); mask++);
if (!*mask)
return NULL;
/* Extended ban? */
if ((*mask == '~') && mask[1] && (mask[2] == ':'))
{
if (RESTRICT_EXTENDEDBANS && MyClient(cptr) && !IsAnOper(cptr))
{
if (!strcmp(RESTRICT_EXTENDEDBANS, "*"))
{
sendnotice(cptr, "Setting/removing of extended bans has been disabled");
return NULL;
}
if (strchr(RESTRICT_EXTENDEDBANS, mask[1]))
{
sendnotice(cptr, "Setting/removing of extended bantypes '%s' has been disabled",
RESTRICT_EXTENDEDBANS);
return NULL;
}
}
p = findmod_by_bantype(mask[1]);
if (!p)
{
/* extended bantype not supported, what to do?
* Here are the rules:
* - if from a remote client/server: allow it (easy upgrading,
* no desynch)
* - if from a local client trying to REMOVE the extban,
* allow it too (so you don't get "unremovable" extbans).
*/
if (!MyClient(cptr) || (what == MODE_DEL))
return mask; /* allow it */
return NULL; /* reject */
}
if (p->conv_param)
return p->conv_param(mask);
/* else, do some basic sanity checks and cut it off at 80 bytes */
if ((cp[1] != ':') || (cp[2] == '\0'))
return NULL; /* require a ":<char>" after extban type */
if (strlen(mask) > 80)
mask[80] = '\0';
return mask;
}
if ((*mask == '~') && !strchr(mask, '@'))
return NULL; /* not and extended ban and not a ~user@host ban either. */
if ((user = index((cp = mask), '!')))
*user++ = '\0';
if ((host = rindex(user ? user : cp, '@')))
{
*host++ = '\0';
if (!user)
return make_nick_user_host(NULL, trim_str(cp,USERLEN),
trim_str(host,HOSTLEN));
}
else if (!user && index(cp, '.'))
return make_nick_user_host(NULL, NULL, trim_str(cp,HOSTLEN));
return make_nick_user_host(trim_str(cp,NICKLEN), trim_str(user,USERLEN),
trim_str(host,HOSTLEN));
}
/* This function adds as an extra (weird) operoverride.
* Currently it's only used if you try to operoverride for a +z channel,
* if you then do '/join #chan override' it will put the channel -z and allow you directly in.
* This is to avoid attackers from using 'race conditions' to prevent you from joining.
* PARAMETERS: sptr = the client, chptr = the channel, mval = mode value (eg MODE_ONLYSECURE),
* mchar = mode char (eg 'z')
* RETURNS: 1 if operoverride, 0 if not.
*/
int extended_operoverride(aClient *sptr, aChannel *chptr, char *key, int mval, char mchar)
{
unsigned char invited = 0;
Link *lp;
if (!IsAnOper(sptr) || !OPCanOverride(sptr))
return 0;
for (lp = sptr->user->invited; lp; lp = lp->next)
if (lp->value.chptr == chptr)
{
invited = 1;
break;
}
if (invited)
{
if (key && !strcasecmp(key, "override"))
{
sendto_channelprefix_butone(NULL, &me, chptr, PREFIX_OP|PREFIX_ADMIN|PREFIX_OWNER,
":%s NOTICE @%s :setting channel -%c due to OperOverride request from %s",
me.name, chptr->chname, mchar, sptr->name);
sendto_serv_butone(&me, ":%s MODE %s -%c 0", me.name, chptr->chname, mchar);
sendto_channel_butserv(chptr, &me, ":%s MODE %s -%c", me.name, chptr->chname, mchar);
chptr->mode.mode &= ~mval;
return 1;
}
}
return 0;
}
/* Now let _invited_ people join thru bans, +i and +l.
* Checking if an invite exist could be done only if a block exists,
* but I'm not too fancy of the complicated structure that'd cause,
* when optimization will hopefully take care of it. Most of the time
* a user won't have invites on him anyway. -Donwulff
*/
int can_join(aClient *cptr, aClient *sptr, aChannel *chptr, char *key, char *link, char *parv[])
{
Link *lp;
Ban *banned;
if ((chptr->mode.mode & MODE_ONLYSECURE) && !(sptr->umodes & UMODE_SECURE))
{
if (!extended_operoverride(sptr, chptr, key, MODE_ONLYSECURE, 'z'))
return (ERR_SECUREONLYCHAN);
else
return 0;
}
if ((chptr->mode.mode & MODE_OPERONLY) && !IsAnOper(sptr))
return (ERR_OPERONLY);
if ((chptr->mode.mode & MODE_ADMONLY) && !IsSkoAdmin(sptr))
return (ERR_ADMONLY);
/* Admin, Coadmin, Netadmin, and SAdmin can still walk +b in +O */
banned = is_banned(sptr, chptr, BANCHK_JOIN);
if (banned && (chptr->mode.mode & MODE_OPERONLY) &&
IsAnOper(sptr) && !IsSkoAdmin(sptr) && !IsCoAdmin(sptr))
return (ERR_BANNEDFROMCHAN);
/* Only NetAdmin/SAdmin can walk +b in +A */
if (banned && (chptr->mode.mode & MODE_ADMONLY) &&
IsAnOper(sptr) && !IsNetAdmin(sptr) && !IsSAdmin(sptr))
return (ERR_BANNEDFROMCHAN);
for (lp = sptr->user->invited; lp; lp = lp->next)
if (lp->value.chptr == chptr)
return 0;
if ((chptr->mode.limit && chptr->users >= chptr->mode.limit))
{
if (chptr->mode.link)
{
if (*chptr->mode.link != '\0')
{
/* We are linked. */
sendto_one(sptr,
err_str(ERR_LINKCHANNEL), me.name,
sptr->name, chptr->chname,
chptr->mode.link);
parv[0] = sptr->name;
parv[1] = (chptr->mode.link);
do_join(cptr, sptr, 2, parv);
return -1;
}
}
/* We check this later return (ERR_CHANNELISFULL); */
}
if ((chptr->mode.mode & MODE_RGSTRONLY) && !IsARegNick(sptr))
return (ERR_NEEDREGGEDNICK);
if (*chptr->mode.key && (BadPtr(key) || strcmp(chptr->mode.key, key)))
return (ERR_BADCHANNELKEY);
if ((chptr->mode.mode & MODE_INVITEONLY))
return (ERR_INVITEONLYCHAN);
if ((chptr->mode.limit && chptr->users >= chptr->mode.limit))
return (ERR_CHANNELISFULL);
if (banned)
return (ERR_BANNEDFROMCHAN);
#ifndef NO_OPEROVERRIDE
#ifdef OPEROVERRIDE_VERIFY
if (IsOper(sptr) && (chptr->mode.mode & MODE_SECRET ||
chptr->mode.mode & MODE_PRIVATE))
return (ERR_OPERSPVERIFY);
#endif
#endif
return 0;
}
/*
** Remove bells and commas from channel name
*/
void clean_channelname(char *cn)
{
u_char *ch = (u_char *)cn;
for (; *ch; ch++)
/* Don't allow any control chars, the space, the comma,
* or the "non-breaking space" in channel names.
* Might later be changed to a system where the list of
* allowed/non-allowed chars for channels was a define
* or some such.
* --Wizzu
*/
if (*ch < 33 || *ch == ',' || *ch == 160)
{
*ch = '\0';
return;
}
}
/*
** Return -1 if mask is present and doesnt match our server name.
*/
int check_channelmask(aClient *sptr, aClient *cptr, char *chname)
{
char *s;
s = rindex(chname, ':');
if (!s)
return 0;
s++;
if (match(s, me.name) || (IsServer(cptr) && match(s, cptr->name)))
{
if (MyClient(sptr))
sendto_one(sptr, err_str(ERR_BADCHANMASK),
me.name, sptr->name, chname);
return -1;
}
return 0;
}
/*
** Get Channel block for i (and allocate a new channel
** block, if it didn't exists before).
*/
aChannel *get_channel(aClient *cptr, char *chname, int flag)
{
aChannel *chptr;
int len;
if (BadPtr(chname))
return NULL;
len = strlen(chname);
if (MyClient(cptr) && len > CHANNELLEN)
{
len = CHANNELLEN;
*(chname + CHANNELLEN) = '\0';
}
if ((chptr = find_channel(chname, (aChannel *)NULL)))
return (chptr);
if (flag == CREATE)
{
chptr = (aChannel *)MyMalloc(sizeof(aChannel) + len);
bzero((char *)chptr, sizeof(aChannel));
strncpyzt(chptr->chname, chname, len + 1);
if (channel)
channel->prevch = chptr;
chptr->topic = NULL;
chptr->topic_nick = NULL;
chptr->prevch = NULL;
chptr->nextch = channel;
chptr->creationtime = MyClient(cptr) ? TStime() : (TS)0;
channel = chptr;
(void)add_to_channel_hash_table(chname, chptr);
IRCstats.channels++;
RunHook2(HOOKTYPE_CHANNEL_CREATE, cptr, chptr);
}
return chptr;
}
/*
* Slight changes in routine, now working somewhat symmetrical:
* First try to remove the client & channel pair to avoid duplicates
* Second check client & channel invite-list lengths and remove tail
* Finally add new invite-links to both client and channel
* Should U-lined clients have higher limits? -Donwulff
*/
void add_invite(aClient *cptr, aChannel *chptr)
{
Link *inv, *tmp;
del_invite(cptr, chptr);
/*
* delete last link in chain if the list is max length
*/
if (list_length(cptr->user->invited) >= MAXCHANNELSPERUSER)
{
/* This forgets the channel side of invitation -Vesa
inv = cptr->user->invited;
cptr->user->invited = inv->next;
free_link(inv);
*/
for (tmp = cptr->user->invited; tmp->next; tmp = tmp->next)
;
del_invite(cptr, tmp->value.chptr);
}
/* We get pissy over too many invites per channel as well now,
* since otherwise mass-inviters could take up some major
* resources -Donwulff
*/
if (list_length(chptr->invites) >= MAXCHANNELSPERUSER)
{
for (tmp = chptr->invites; tmp->next; tmp = tmp->next)
;
del_invite(tmp->value.cptr, chptr);
}
/*
* add client to the beginning of the channel invite list
*/
inv = make_link();
inv->value.cptr = cptr;
inv->next = chptr->invites;
chptr->invites = inv;
/*
* add channel to the beginning of the client invite list
*/
inv = make_link();
inv->value.chptr = chptr;
inv->next = cptr->user->invited;
cptr->user->invited = inv;
}
/*
* Delete Invite block from channel invite list and client invite list
*/
void del_invite(aClient *cptr, aChannel *chptr)
{
Link **inv, *tmp;
for (inv = &(chptr->invites); (tmp = *inv); inv = &tmp->next)
if (tmp->value.cptr == cptr)
{
*inv = tmp->next;
free_link(tmp);
break;
}
for (inv = &(cptr->user->invited); (tmp = *inv); inv = &tmp->next)
if (tmp->value.chptr == chptr)
{
*inv = tmp->next;
free_link(tmp);
break;
}
}
/*
** Subtract one user from channel i (and free channel
** block, if channel became empty).
*/
void sub1_from_channel(aChannel *chptr)
{
Ban *ban;
Link *lp;
/* if (--chptr->users <= 0) */
if (chptr->users == 0 || --chptr->users == 0)
{
/*
* Now, find all invite links from channel structure
*/
RunHook(HOOKTYPE_CHANNEL_DESTROY, chptr);
while ((lp = chptr->invites))
del_invite(lp->value.cptr, chptr);
while (chptr->banlist)
{
ban = chptr->banlist;
chptr->banlist = ban->next;
MyFree(ban->banstr);
MyFree(ban->who);
free_ban(ban);
}
while (chptr->exlist)
{
ban = chptr->exlist;
chptr->exlist = ban->next;
MyFree(ban->banstr);
MyFree(ban->who);
free_ban(ban);
}
#ifdef EXTCMODE
/* free extcmode params */
extcmode_free_paramlist(chptr->mode.extmodeparam);
chptr->mode.extmodeparam = NULL;
#endif
#ifdef NEWCHFLOODPROT
chanfloodtimer_stopchantimers(chptr);
if (chptr->mode.floodprot)
MyFree(chptr->mode.floodprot);
#endif
if (chptr->topic)
MyFree(chptr->topic);
if (chptr->topic_nick)
MyFree(chptr->topic_nick);
if (chptr->prevch)
chptr->prevch->nextch = chptr->nextch;
else
channel = chptr->nextch;
if (chptr->nextch)
chptr->nextch->prevch = chptr->prevch;
(void)del_from_channel_hash_table(chptr->chname, chptr);
IRCstats.channels--;
MyFree((char *)chptr);
}
}
/*
** m_join
** parv[0] = sender prefix
** parv[1] = channel
** parv[2] = channel password (key)
*/
CMD_FUNC(m_join)
{
int r;
if (bouncedtimes)
sendto_realops("m_join: bouncedtimes=%d??? [please report at http://bugs.unrealircd.org/]", bouncedtimes);
bouncedtimes = 0;
if (IsServer(sptr))
return 0;
r = do_join(cptr, sptr, parc, parv);
bouncedtimes = 0;
return r;
}
/* Routine that actually makes a user join the channel
* this does no actual checking (banned, etc.) it just adds the user
*/
void join_channel(aChannel *chptr, aClient *cptr, aClient *sptr, int flags)
{
char *parv[] = { 0, 0 };
/*
** Complete user entry to the new channel (if any)
*/
add_user_to_channel(chptr, sptr, flags);
/*
** notify all other users on the new channel
*/
if (chptr->mode.mode & MODE_AUDITORIUM)
{
if (MyClient(sptr))
sendto_one(sptr, ":%s!%s@%s JOIN :%s",
sptr->name, sptr->user->username,
GetHost(sptr), chptr->chname);
sendto_chanops_butone(NULL, chptr, ":%s!%s@%s JOIN :%s",
sptr->name, sptr->user->username,
GetHost(sptr), chptr->chname);
}
else
sendto_channel_butserv(chptr, sptr,
":%s JOIN :%s", sptr->name, chptr->chname);
sendto_serv_butone_token_opt(cptr, OPT_NOT_SJ3, sptr->name, MSG_JOIN,
TOK_JOIN, "%s", chptr->chname);
#ifdef JOIN_INSTEAD_OF_SJOIN_ON_REMOTEJOIN
if ((MyClient(sptr) && !(flags & CHFL_CHANOP)) || !MyClient(sptr))
sendto_serv_butone_token_opt(cptr, OPT_SJ3, sptr->name, MSG_JOIN,
TOK_JOIN, "%s", chptr->chname);
if (flags & CHFL_CHANOP)
{
#endif
/* I _know_ that the "@%s " look a bit wierd
with the space and all .. but its to get around
a SJOIN bug --stskeeps */
sendto_serv_butone_token_opt(cptr, OPT_SJ3|OPT_SJB64,
me.name, MSG_SJOIN, TOK_SJOIN,
"%B %s :%s%s ", chptr->creationtime,
chptr->chname, flags & CHFL_CHANOP ? "@" : "", sptr->name);
sendto_serv_butone_token_opt(cptr, OPT_SJ3|OPT_NOT_SJB64,
me.name, MSG_SJOIN, TOK_SJOIN,
"%li %s :%s%s ", chptr->creationtime,
chptr->chname, flags & CHFL_CHANOP ? "@" : "", sptr->name);
#ifdef JOIN_INSTEAD_OF_SJOIN_ON_REMOTEJOIN
}
#endif
if (MyClient(sptr))
{
/*
** Make a (temporal) creationtime, if someone joins
** during a net.reconnect : between remote join and
** the mode with TS. --Run
*/
if (chptr->creationtime == 0)
{
chptr->creationtime = TStime();
sendto_serv_butone_token(cptr, me.name,
MSG_MODE, TOK_MODE, "%s + %lu",
chptr->chname, chptr->creationtime);
}
del_invite(sptr, chptr);
if (flags & CHFL_CHANOP)
sendto_serv_butone_token_opt(cptr, OPT_NOT_SJ3,
me.name,
MSG_MODE, TOK_MODE, "%s +o %s %lu",
chptr->chname, sptr->name,
chptr->creationtime);
if (chptr->topic)
{
sendto_one(sptr, rpl_str(RPL_TOPIC),
me.name, sptr->name, chptr->chname, chptr->topic);
sendto_one(sptr,
rpl_str(RPL_TOPICWHOTIME), me.name,
sptr->name, chptr->chname, chptr->topic_nick,
chptr->topic_time);
}
if (chptr->users == 1 && (MODES_ON_JOIN
#ifdef EXTCMODE
|| iConf.modes_on_join.extmodes)
#endif
)
{
#ifdef EXTCMODE
int i;
chptr->mode.extmode = iConf.modes_on_join.extmodes;
/* Param fun */
for (i = 0; i <= Channelmode_highest; i++)
{
if (!Channelmode_Table[i].flag || !Channelmode_Table[i].paracount)
continue;
if (chptr->mode.extmode & Channelmode_Table[i].mode)
{
CmodeParam *p;
p = Channelmode_Table[i].put_param(NULL, iConf.modes_on_join.extparams[i]);
AddListItem(p, chptr->mode.extmodeparam);
}
}
#endif
chptr->mode.mode = MODES_ON_JOIN;
#ifdef NEWCHFLOODPROT
if (iConf.modes_on_join.floodprot.per)
{
chptr->mode.floodprot = MyMalloc(sizeof(ChanFloodProt));
memcpy(chptr->mode.floodprot, &iConf.modes_on_join.floodprot, sizeof(ChanFloodProt));
}
#else
chptr->mode.kmode = iConf.modes_on_join.kmode;
chptr->mode.per = iConf.modes_on_join.per;
chptr->mode.msgs = iConf.modes_on_join.msgs;
#endif
*modebuf = *parabuf = 0;
channel_modes(sptr, modebuf, parabuf, chptr);
/* This should probably be in the SJOIN stuff */
sendto_serv_butone_token(&me, me.name, MSG_MODE, TOK_MODE,
"%s %s %s %lu", chptr->chname, modebuf, parabuf,
chptr->creationtime);
sendto_one(sptr, ":%s MODE %s %s %s", me.name, chptr->chname, modebuf, parabuf);
}
parv[0] = sptr->name;
parv[1] = chptr->chname;
(void)m_names(cptr, sptr, 2, parv);
RunHook4(HOOKTYPE_LOCAL_JOIN, cptr, sptr,chptr,parv);
} else {
RunHook4(HOOKTYPE_REMOTE_JOIN, cptr, sptr, chptr, parv); /* (rarely used) */
}
#ifdef NEWCHFLOODPROT
/* I'll explain this only once:
* 1. if channel is +f
* 2. local client OR synced server
* 3. then, increase floodcounter
* 4. if we reached the limit AND only if source was a local client.. do the action (+i).
* Nr 4 is done because otherwise you would have a noticeflood with 'joinflood detected'
* from all servers.
*/
if (chptr->mode.floodprot && (MyClient(sptr) || sptr->srvptr->serv->flags.synced) &&
!IsULine(sptr) && do_chanflood(chptr->mode.floodprot, FLD_JOIN) && MyClient(sptr))
{
do_chanflood_action(chptr, FLD_JOIN, "join");
}
#endif
}
/** User request to join a channel.
* This routine can be called from both m_join or via do_join->can_join->do_join
* if the channel is 'linked' (chmode +L). We use a counter 'bouncedtimes' which
* is set to 0 in m_join, increased every time we enter this loop and decreased
* anytime we leave the loop. So be carefull ;p.
*/
CMD_FUNC(do_join)
{
char jbuf[BUFSIZE];
Membership *lp;
aChannel *chptr;
char *name, *key = NULL, *link = NULL;
int i, flags = 0;
char *p = NULL, *p2 = NULL;
#define RET(x) { bouncedtimes--; return x; }
if (parc < 2 || *parv[1] == '\0')
{
sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS),
me.name, parv[0], "JOIN");
return 0;
}
bouncedtimes++;
/* don't use 'return x;' but 'RET(x)' from here ;p */
if (bouncedtimes > MAXBOUNCE)
{
/* bounced too many times */
sendto_one(sptr,
":%s %s %s :*** Couldn't join %s ! - Link setting was too bouncy",
me.name, IsWebTV(sptr) ? "PRIVMSG" : "NOTICE", sptr->name, parv[1]);
RET(0)
}
*jbuf = '\0';
/*
** Rebuild list of channels joined to be the actual result of the
** JOIN. Note that "JOIN 0" is the destructive problem.
*/
for (i = 0, name = strtoken(&p, parv[1], ","); name;
name = strtoken(&p, NULL, ","))
{
/* pathological case only on longest channel name.
** If not dealt with here, causes desynced channel ops
** since ChannelExists() doesn't see the same channel
** as one being joined. cute bug. Oct 11 1997, Dianora/comstud
** Copied from Dianora's "hybrid 5" ircd.
*/
if (strlen(name) > CHANNELLEN) /* same thing is done in get_channel() */
name[CHANNELLEN] = '\0';
if (MyConnect(sptr))
clean_channelname(name);
if (check_channelmask(sptr, cptr, name) == -1)
continue;
if (*name == '0' && !atoi(name))
{
(void)strcpy(jbuf, "0");
i = 1;
continue;
}
else if (!IsChannelName(name))
{
if (MyClient(sptr))
sendto_one(sptr,
err_str(ERR_NOSUCHCHANNEL), me.name,
parv[0], name);
continue;
}
if (*jbuf)
(void)strlcat(jbuf, ",", sizeof jbuf);
(void)strlncat(jbuf, name, sizeof jbuf, sizeof(jbuf) - i - 1);
i += strlen(name) + 1;
}
/* This strcpy should be safe since jbuf contains the "filtered"
* result of parv[1] which should never be larger than the source.
*/
(void)strcpy(parv[1], jbuf);
p = NULL;
if (parv[2])
key = strtoken(&p2, parv[2], ",");
parv[2] = NULL; /* for m_names call later, parv[parc] must == NULL */
for (name = strtoken(&p, jbuf, ","); name;
key = (key) ? strtoken(&p2, NULL, ",") : NULL,
name = strtoken(&p, NULL, ","))
{
/*
** JOIN 0 sends out a part for all channels a user
** has joined.
*/
if (*name == '0' && !atoi(name))
{
while ((lp = sptr->user->channel))
{
chptr = lp->chptr;
sendto_channel_butserv(chptr, sptr,
PartFmt2, parv[0], chptr->chname,
"Left all channels");
if (MyConnect(sptr))
RunHook4(HOOKTYPE_LOCAL_PART, cptr, sptr, chptr, "Left all channels");
remove_user_from_channel(sptr, chptr);
}
sendto_serv_butone_token(cptr, parv[0],
MSG_JOIN, TOK_JOIN, "0");
continue;
}
if (MyConnect(sptr))
{
/*
** local client is first to enter previously nonexistant
** channel so make them (rightfully) the Channel
** Operator.
*/
/* Where did this come from? Potvin ? --Stskeeps
flags = (ChannelExists(name)) ? CHFL_DEOPPED :
CHFL_CHANOWNER;
*/
flags =
(ChannelExists(name)) ? CHFL_DEOPPED : CHFL_CHANOP;
if (!IsAnOper(sptr)) /* opers can join unlimited chans */
if (sptr->user->joined >= MAXCHANNELSPERUSER)
{
sendto_one(sptr,
err_str
(ERR_TOOMANYCHANNELS),
me.name, parv[0], name);
RET(0)
}
/* RESTRICTCHAN */
if (conf_deny_channel)
{
if (!IsOper(sptr) && !IsULine(sptr))
{
ConfigItem_deny_channel *d;
if ((d = Find_channel_allowed(name)))
{
if (d->warn)
{
sendto_snomask(SNO_EYES, "*** %s tried to join forbidden channel %s",
get_client_name(sptr, 1), name);
}
if (d->reason)
sendto_one(sptr,
":%s %s %s :*** Can not join %s: %s",
me.name, IsWebTV(sptr) ? "PRIVMSG" : "NOTICE", sptr->name, name, d->reason);
if (d->redirect)
{
sendto_one(sptr,
":%s %s %s :*** Redirecting you to %s",
me.name, IsWebTV(sptr) ? "PRIVMSG" : "NOTICE", sptr->name, d->redirect);
parv[0] = sptr->name;
parv[1] = d->redirect;
do_join(cptr, sptr, 2, parv);
}
continue;
}
}
}
/* ugly set::spamfilter::virus-help-channel-deny hack.. */
if (SPAMFILTER_VIRUSCHANDENY && SPAMFILTER_VIRUSCHAN &&
!strcasecmp(name, SPAMFILTER_VIRUSCHAN) &&
!IsAnOper(sptr) && !spamf_ugly_vchanoverride)
{
int invited = 0;
Link *lp;
aChannel *chptr = find_channel(name, NULL);
if (chptr)
{
for (lp = sptr->user->invited; lp; lp = lp->next)
if (lp->value.chptr == chptr)
invited = 1;
}
if (!invited)
{
sendnotice(sptr, "*** Cannot join '%s' because it's the virus-help-channel which is "
"reserved for infected users only", name);
continue;
}
}
}
chptr = get_channel(sptr, name, CREATE);
if (chptr && (lp = find_membership_link(sptr->user->channel, chptr)))
continue;
if (!chptr)
continue;
i = HOOK_CONTINUE;
if (!MyConnect(sptr))
flags = CHFL_DEOPPED;
else
{
Hook *h;
for (h = Hooks[HOOKTYPE_PRE_LOCAL_JOIN]; h; h = h->next)
{
i = (*(h->func.intfunc))(sptr,chptr,parv);
if (i == HOOK_DENY || i == HOOK_ALLOW)
break;
}
/* Denied, get out now! */
if (i == HOOK_DENY)
{
/* Rejected... if we just created a new chan we should destroy it too. -- Syzop */
if (!chptr->users)
sub1_from_channel(chptr);
continue;
}
/* If they are allowed, don't check can_join */
if (i != HOOK_ALLOW &&
(i = can_join(cptr, sptr, chptr, key, link, parv)))
{
if (i != -1)
sendto_one(sptr, err_str(i),
me.name, parv[0], name);
continue;
}
}
join_channel(chptr, cptr, sptr, flags);
}
RET(0)
#undef RET
}
/*
** m_part
** parv[0] = sender prefix
** parv[1] = channel
** parv[2] = comment (added by Lefler)
*/
CMD_FUNC(m_part)
{
aChannel *chptr;
Membership *lp;
char *p = NULL, *name;
char *commentx = (parc > 2 && parv[2]) ? parv[2] : NULL;
char *comment;
int n;
if (parc < 2 || parv[1][0] == '\0')
{
sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS),
me.name, parv[0], "PART");
return 0;
}
if (MyClient(sptr))
{
if (IsShunned(sptr))
commentx = NULL;
if (STATIC_PART)
{
if (!strcasecmp(STATIC_PART, "yes") || !strcmp(STATIC_PART, "1"))
commentx = NULL;
else
commentx = STATIC_PART;
}
if (commentx)
{
n = dospamfilter(sptr, commentx, SPAMF_PART, parv[1]);
if (n == FLUSH_BUFFER)
return n;
if (n < 0)
commentx = NULL;
}
}
for (; (name = strtoken(&p, parv[1], ",")); parv[1] = NULL)
{
chptr = get_channel(sptr, name, 0);
if (!chptr)
{
sendto_one(sptr, err_str(ERR_NOSUCHCHANNEL),
me.name, parv[0], name);
continue;
}
if (check_channelmask(sptr, cptr, name))
continue;
/* 'commentx' is the general part msg, but it can be changed
* per-channel (eg some chans block badwords, strip colors, etc)
* so we copy it to 'comment' and use that in this for loop :)
*/
comment = commentx;
if (!(lp = find_membership_link(sptr->user->channel, chptr)))
{
/* Normal to get get when our client did a kick
** for a remote client (who sends back a PART),
** so check for remote client or not --Run
*/
if (MyClient(sptr))
sendto_one(sptr,
err_str(ERR_NOTONCHANNEL), me.name,
parv[0], name);
continue;
}
if (!IsAnOper(sptr) && !is_chanownprotop(sptr, chptr)) {
#ifdef STRIPBADWORDS
int blocked = 0;
#endif
if ((chptr->mode.mode & MODE_NOCOLOR) && comment) {
if (strchr((char *)comment, 3) || strchr((char *)comment, 27)) {
comment = NULL;
}
}
if ((chptr->mode.mode & MODE_MODERATED) && comment &&
!has_voice(sptr, chptr) && !is_halfop(sptr, chptr))
{
comment = NULL;
}
if ((chptr->mode.mode & MODE_STRIP) && comment) {
comment = (char *)StripColors(comment);
}
#ifdef STRIPBADWORDS
#ifdef STRIPBADWORDS_CHAN_ALWAYS
if (comment)
{
comment = (char *)stripbadwords_channel(comment, &blocked);
}
#else
if ((chptr->mode.mode & MODE_STRIPBADWORDS) && comment) {
comment = (char *)stripbadwords_channel(comment, &blocked);
}
#endif
#endif
}
/* +M and not +r? */
if ((chptr->mode.mode & MODE_MODREG) && !IsRegNick(sptr) && !IsAnOper(sptr))
comment = NULL;
if (MyConnect(sptr))
{
Hook *tmphook;
for (tmphook = Hooks[HOOKTYPE_PRE_LOCAL_PART]; tmphook; tmphook = tmphook->next) {
comment = (*(tmphook->func.pcharfunc))(sptr, chptr, comment);
if (!comment)
break;
}
}
/* Send to other servers... */
if (!comment)
sendto_serv_butone_token(cptr, parv[0],
MSG_PART, TOK_PART, "%s", chptr->chname);
else
sendto_serv_butone_token(cptr, parv[0],
MSG_PART, TOK_PART, "%s :%s", chptr->chname,
comment);
if (1)
{
if ((chptr->mode.mode & MODE_AUDITORIUM) && !is_chanownprotop(sptr, chptr))
{
if (!comment)
{
sendto_chanops_butone(NULL,
chptr, ":%s!%s@%s PART %s",
sptr->name, sptr->user->username, GetHost(sptr),
chptr->chname);
if (!is_chan_op(sptr, chptr) && MyClient(sptr))
sendto_one(sptr, ":%s!%s@%s PART %s",
sptr->name, sptr->user->username, GetHost(sptr), chptr->chname);
}
else
{
sendto_chanops_butone(NULL,
chptr,
":%s!%s@%s PART %s %s",
sptr->name,
sptr->user->username,
GetHost(sptr),
chptr->chname, comment);
if (!is_chan_op(cptr, chptr) && MyClient(sptr))
sendto_one(sptr,
":%s!%s@%s PART %s %s",
sptr->name, sptr->user->username, GetHost(sptr),
chptr->chname, comment);
}
}
else
{
if (!comment)
sendto_channel_butserv(chptr,
sptr, PartFmt, parv[0],
chptr->chname);
else
sendto_channel_butserv(chptr,
sptr, PartFmt2, parv[0],
chptr->chname, comment);
}
if (MyClient(sptr))
RunHook4(HOOKTYPE_LOCAL_PART, cptr, sptr, chptr, comment);
else
RunHook4(HOOKTYPE_REMOTE_PART, cptr, sptr, chptr, comment);
remove_user_from_channel(sptr, chptr);
}
}
return 0;
}
/*
* The function which sends the actual channel list back to the user.
* Operates by stepping through the hashtable, sending the entries back if
* they match the criteria.
* cptr = Local client to send the output back to.
* numsend = Number (roughly) of lines to send back. Once this number has
* been exceeded, send_list will finish with the current hash bucket,
* and record that number as the number to start next time send_list
* is called for this user. So, this function will almost always send
* back more lines than specified by numsend (though not by much,
* assuming CH_MAX is was well picked). So be conservative in your choice
* of numsend. -Rak
*/
/* Taken from bahamut, modified for Unreal by codemastr */
void send_list(aClient *cptr, int numsend)
{
aChannel *chptr;
LOpts *lopt = cptr->user->lopt;
unsigned int hashnum;
/* Begin of /list? then send official channels. */
if ((lopt->starthash == 0) && conf_offchans)
{
ConfigItem_offchans *x;
for (x = conf_offchans; x; x = (ConfigItem_offchans *)x->next)
{
if (find_channel(x->chname, (aChannel *)NULL))
continue; /* exists, >0 users.. will be sent later */
sendto_one(cptr,
rpl_str(RPL_LIST), me.name,
cptr->name, x->chname,
0,
#ifdef LIST_SHOW_MODES
"",
#endif
x->topic ? x->topic : "");
}
}
for (hashnum = lopt->starthash; hashnum < CH_MAX; hashnum++)
{
if (numsend > 0)
for (chptr =
(aChannel *)hash_get_chan_bucket(hashnum);
chptr; chptr = chptr->hnextch)
{
if (SecretChannel(chptr)
&& !IsMember(cptr, chptr)
&& !IsAnOper(cptr))
continue;
/* Much more readable like this -- codemastr */
if ((!lopt->showall))
{
/* User count must be in range */
if ((chptr->users < lopt->usermin) ||
((lopt->usermax >= 0) && (chptr->users >
lopt->usermax)))
continue;
/* Creation time must be in range */
if ((chptr->creationtime && (chptr->creationtime <
lopt->chantimemin)) || (chptr->creationtime >
lopt->chantimemax))
continue;
/* Topic time must be in range */
if ((chptr->topic_time < lopt->topictimemin) ||
(chptr->topic_time > lopt->topictimemax))
continue;
/* Must not be on nolist (if it exists) */
if (lopt->nolist && find_str_match_link(lopt->nolist,
chptr->chname))
continue;
/* Must be on yeslist (if it exists) */
if (lopt->yeslist && !find_str_match_link(lopt->yeslist,
chptr->chname))
continue;
}
#ifdef LIST_SHOW_MODES
modebuf[0] = '[';
channel_modes(cptr, &modebuf[1], parabuf, chptr);
if (modebuf[2] == '\0')
modebuf[0] = '\0';
else
strlcat(modebuf, "]", sizeof modebuf);
#endif
if (!IsAnOper(cptr))
sendto_one(cptr,
rpl_str(RPL_LIST), me.name,
cptr->name,
ShowChannel(cptr,
chptr) ? chptr->chname :
"*", chptr->users,
#ifdef LIST_SHOW_MODES
ShowChannel(cptr, chptr) ?
modebuf : "",
#endif
ShowChannel(cptr,
chptr) ? (chptr->topic ?
chptr->topic : "") : "");
else
sendto_one(cptr,
rpl_str(RPL_LIST), me.name,
cptr->name, chptr->chname,
chptr->users,
#ifdef LIST_SHOW_MODES
modebuf,
#endif
(chptr->topic ? chptr->topic : ""));
numsend--;
}
else
break;
}
/* All done */
if (hashnum == CH_MAX)
{
sendto_one(cptr, rpl_str(RPL_LISTEND), me.name, cptr->name);
free_str_list(cptr->user->lopt->yeslist);
free_str_list(cptr->user->lopt->nolist);
MyFree(cptr->user->lopt);
cptr->user->lopt = NULL;
return;
}
/*
* We've exceeded the limit on the number of channels to send back
* at once.
*/
lopt->starthash = hashnum;
return;
}
int check_for_chan_flood(aClient *cptr, aClient *sptr, aChannel *chptr)
{
Membership *lp;
MembershipL *lp2;
int c_limit, t_limit, banthem;
if (!MyClient(sptr))
return 0;
if (IsOper(sptr) || IsULine(sptr))
return 0;
if (is_chan_op(sptr, chptr))
return 0;
if (!(lp = find_membership_link(sptr->user->channel, chptr)))
return 0;
lp2 = (MembershipL *) lp;
#ifdef NEWCHFLOODPROT
if (!chptr->mode.floodprot || !chptr->mode.floodprot->l[FLD_TEXT])
return 0;
c_limit = chptr->mode.floodprot->l[FLD_TEXT];
t_limit = chptr->mode.floodprot->per;
banthem = (chptr->mode.floodprot->a[FLD_TEXT] == 'b') ? 1 : 0;
#else
if ((chptr->mode.msgs < 1) || (chptr->mode.per < 1))
return 0;
c_limit = chptr->mode.msgs;
t_limit = chptr->mode.per;
banthem = chptr->mode.kmode;
#endif
/* if current - firstmsgtime >= mode.per, then reset,
* if nummsg > mode.msgs then kick/ban
*/
Debug((DEBUG_ERROR, "Checking for flood +f: firstmsg=%d (%ds ago), new nmsgs: %d, limit is: %d:%d",
lp2->flood.firstmsg, TStime() - lp2->flood.firstmsg, lp2->flood.nmsg + 1,
c_limit, t_limit));
if ((TStime() - lp2->flood.firstmsg) >= t_limit)
{
/* reset */
lp2->flood.firstmsg = TStime();
lp2->flood.nmsg = 1;
return 0; /* forget about it.. */
}
/* increase msgs */
lp2->flood.nmsg++;
if ((lp2->flood.nmsg) > c_limit)
{
char comment[1024], mask[1024];
ircsprintf(comment,
"Flooding (Limit is %i lines per %i seconds)",
c_limit, t_limit);
if (banthem)
{ /* ban. */
ircsprintf(mask, "*!*@%s", GetHost(sptr));
add_banid(&me, chptr, mask);
sendto_serv_butone(&me, ":%s MODE %s +b %s 0",
me.name, chptr->chname, mask);
sendto_channel_butserv(chptr, &me,
":%s MODE %s +b %s", me.name, chptr->chname, mask);
}
sendto_channel_butserv(chptr, &me,
":%s KICK %s %s :%s", me.name,
chptr->chname, sptr->name, comment);
sendto_serv_butone_token(cptr, me.name,
MSG_KICK, TOK_KICK,
"%s %s :%s",
chptr->chname, sptr->name, comment);
remove_user_from_channel(sptr, chptr);
return 1;
}
return 0;
}
/************************************************************************
* m_names() - Added by Jto 27 Apr 1989
* 12 Feb 2000 - geesh, time for a rewrite -lucas
************************************************************************/
/*
** m_names
** parv[0] = sender prefix
** parv[1] = channel
*/
#define TRUNCATED_NAMES 64
CMD_FUNC(m_names)
{
int mlen = strlen(me.name) + NICKLEN + 7;
aChannel *chptr;
aClient *acptr;
int member;
Member *cm;
int idx, flag = 1, spos;
char *s, *para = parv[1];
if (parc < 2 || !MyConnect(sptr))
{
sendto_one(sptr, rpl_str(RPL_ENDOFNAMES), me.name,
parv[0], "*");
return 0;
}
if (parc > 1 &&
hunt_server_token(cptr, sptr, MSG_NAMES, TOK_NAMES, "%s %s", 2, parc, parv))
return 0;
for (s = para; *s; s++)
{
if (*s == ',')
{
if (strlen(para) > TRUNCATED_NAMES)
para[TRUNCATED_NAMES] = '\0';
sendto_realops("names abuser %s %s",
get_client_name(sptr, FALSE), para);
sendto_one(sptr, err_str(ERR_TOOMANYTARGETS),
me.name, sptr->name, "NAMES");
return 0;
}
}
chptr = find_channel(para, (aChannel *)NULL);
if (!chptr || (!ShowChannel(sptr, chptr) && !IsAnOper(sptr)))
{
sendto_one(sptr, rpl_str(RPL_ENDOFNAMES), me.name,
parv[0], para);
return 0;
}
/* cache whether this user is a member of this channel or not */
member = IsMember(sptr, chptr);
if (PubChannel(chptr))
buf[0] = '=';
else if (SecretChannel(chptr))
buf[0] = '@';
else
buf[0] = '*';
idx = 1;
buf[idx++] = ' ';
for (s = chptr->chname; *s; s++)
buf[idx++] = *s;
buf[idx++] = ' ';
buf[idx++] = ':';
/* If we go through the following loop and never add anything,
we need this to be empty, otherwise spurious things from the
LAST /names call get stuck in there.. - lucas */
buf[idx] = '\0';
spos = idx; /* starting point in buffer for names! */
for (cm = chptr->members; cm; cm = cm->next)
{
acptr = cm->cptr;
if (IsInvisible(acptr) && !member && !IsNetAdmin(sptr))
continue;
if (chptr->mode.mode & MODE_AUDITORIUM)
if (!is_chan_op(sptr, chptr)
&& !is_chanprot(sptr, chptr)
&& !is_chanowner(sptr, chptr))
if (!(cm->
flags & (CHFL_CHANOP | CHFL_CHANPROT |
CHFL_CHANOWNER)) && acptr != sptr)
continue;
#ifdef PREFIX_AQ
if (cm->flags & CHFL_CHANOWNER)
buf[idx++] = '~';
else if (cm->flags & CHFL_CHANPROT)
buf[idx++] = '&';
else
#endif
if (cm->flags & CHFL_CHANOP)
buf[idx++] = '@';
else if (cm->flags & CHFL_HALFOP)
buf[idx++] = '%';
else if (cm->flags & CHFL_VOICE)
buf[idx++] = '+';
for (s = acptr->name; *s; s++)
buf[idx++] = *s;
buf[idx++] = ' ';
buf[idx] = '\0';
flag = 1;
if (mlen + idx + NICKLEN > BUFSIZE - 3)
{
sendto_one(sptr, rpl_str(RPL_NAMREPLY), me.name,
parv[0], buf);
idx = spos;
flag = 0;
}
}
if (flag)
sendto_one(sptr, rpl_str(RPL_NAMREPLY), me.name, parv[0], buf);
sendto_one(sptr, rpl_str(RPL_ENDOFNAMES), me.name, parv[0], para);
return 0;
}
void send_user_joins(aClient *cptr, aClient *user)
{
Membership *lp;
aChannel *chptr;
int cnt = 0, len = 0, clen;
char *mask;
snprintf(buf, sizeof buf, ":%s %s ", user->name,
(IsToken(cptr) ? TOK_JOIN : MSG_JOIN));
len = strlen(buf);
for (lp = user->user->channel; lp; lp = lp->next)
{
chptr = lp->chptr;
if ((mask = index(chptr->chname, ':')))
if (match(++mask, cptr->name))
continue;
if (*chptr->chname == '&')
continue;
clen = strlen(chptr->chname);
if (clen + 1 + len > BUFSIZE - 3)
{
if (cnt)
{
buf[len - 1] = '\0';
sendto_one(cptr, "%s", buf);
}
snprintf(buf, sizeof buf, ":%s %s ", user->name,
(IsToken(cptr) ? TOK_JOIN : MSG_JOIN));
len = strlen(buf);
cnt = 0;
}
(void)strlcpy(buf + len, chptr->chname, sizeof buf-len);
cnt++;
len += clen;
if (lp->next)
{
len++;
(void)strlcat(buf, ",", sizeof buf);
}
}
if (*buf && cnt)
sendto_one(cptr, "%s", buf);
return;
}
static int send_ban_list(aClient *cptr, char *chname, TS creationtime, aChannel *channel)
{
Ban *top;
Ban *lp;
char *cp, *name;
int count = 0, send = 0, sent = 0;
cp = modebuf + strlen(modebuf);
if (*parabuf) /* mode +l or +k xx */
count = 1;
top = channel->banlist;
for (lp = top; lp; lp = lp->next)
{
name = ((Ban *) lp)->banstr;
if (strlen(parabuf) + strlen(name) + 11 < (size_t)MODEBUFLEN)
{
if (*parabuf)
(void)strcat(parabuf, " ");
(void)strcat(parabuf, name);
count++;
*cp++ = 'b';
*cp = '\0';
}
else if (*parabuf)
send = 1;
if (count == MODEPARAMS)
send = 1;
if (send)
{
/* cptr is always a server! So we send creationtimes */
sendto_one(cptr, "%s %s %s %s %lu",
(IsToken(cptr) ? TOK_MODE : MSG_MODE),
chname, modebuf, parabuf, creationtime);
sent = 1;
send = 0;
*parabuf = '\0';
cp = modebuf;
*cp++ = '+';
if (count != MODEPARAMS)
{
(void)strlcpy(parabuf, name, sizeof parabuf);
*cp++ = 'b';
}
count = 0;
*cp = '\0';
}
}
top = channel->exlist;
for (lp = top; lp; lp = lp->next)
{
name = ((Ban *) lp)->banstr;
if (strlen(parabuf) + strlen(name) + 11 < (size_t)MODEBUFLEN)
{
if (*parabuf)
(void)strcat(parabuf, " ");
(void)strcat(parabuf, name);
count++;
*cp++ = 'e';
*cp = '\0';
}
else if (*parabuf)
send = 1;
if (count == MODEPARAMS)
send = 1;
if (send)
{
/* cptr is always a server! So we send creationtimes */
sendto_one(cptr, "%s %s %s %s %lu",
(IsToken(cptr) ? TOK_MODE : MSG_MODE),
chname, modebuf, parabuf, creationtime);
sent = 1;
send = 0;
*parabuf = '\0';
cp = modebuf;
*cp++ = '+';
if (count != MODEPARAMS)
{
(void)strlcpy(parabuf, name, sizeof parabuf);
*cp++ = 'e';
}
count = 0;
*cp = '\0';
}
}
return sent;
}
/*
* This will send "cptr" a full list of the modes for channel chptr,
*/
void send_channel_modes_sjoin(aClient *cptr, aChannel *chptr)
{
Member *members;
Member *lp;
char *name;
char *bufptr;
int n = 0;
if (*chptr->chname != '#')
return;
members = chptr->members;
/* First we'll send channel, channel modes and members and status */
*modebuf = *parabuf = '\0';
channel_modes(cptr, modebuf, parabuf, chptr);
if (*parabuf)
{
}
else
{
if (!SupportSJOIN2(cptr))
strlcpy(parabuf, "<none>", sizeof parabuf);
else
strlcpy(parabuf, "<->", sizeof parabuf);
}
ircsprintf(buf, "%s %ld %s %s %s :",
(IsToken(cptr) ? TOK_SJOIN : MSG_SJOIN),
chptr->creationtime, chptr->chname, modebuf, parabuf);
bufptr = buf + strlen(buf);
for (lp = members; lp; lp = lp->next)
{
if (lp->flags & MODE_CHANOP)
*bufptr++ = '@';
if (lp->flags & MODE_VOICE)
*bufptr++ = '+';
if (lp->flags & MODE_HALFOP)
*bufptr++ = '%';
if (lp->flags & MODE_CHANOWNER)
*bufptr++ = '*';
if (lp->flags & MODE_CHANPROT)
*bufptr++ = '~';
name = lp->cptr->name;
strcpy(bufptr, name);
bufptr += strlen(bufptr);
*bufptr++ = ' ';
n++;
if (bufptr - buf > BUFSIZE - 80)
{
*bufptr++ = '\0';
if (bufptr[-1] == ' ')
bufptr[-1] = '\0';
sendto_one(cptr, "%s", buf);
ircsprintf(buf, "%s %ld %s %s %s :",
(IsToken(cptr) ? TOK_SJOIN : MSG_SJOIN),
chptr->creationtime, chptr->chname, modebuf,
parabuf);
n = 0;
bufptr = buf + strlen(buf);
}
}
if (n)
{
*bufptr++ = '\0';
if (bufptr[-1] == ' ')
bufptr[-1] = '\0';
sendto_one(cptr, "%s", buf);
}
/* Then we'll send the ban-list */
*parabuf = '\0';
*modebuf = '+';
modebuf[1] = '\0';
send_ban_list(cptr, chptr->chname, chptr->creationtime, chptr);
if (modebuf[1] || *parabuf)
sendto_one(cptr, "%s %s %s %s %lu",
(IsToken(cptr) ? TOK_MODE : MSG_MODE),
chptr->chname, modebuf, parabuf, chptr->creationtime);
return;
}
/*
* This will send "cptr" a full list of the modes for channel chptr,
*/
void send_channel_modes_sjoin3(aClient *cptr, aChannel *chptr)
{
Member *members;
Member *lp;
Ban *ban;
char *name;
char *bufptr;
short nomode, nopara;
char bbuf[1024];
int n = 0;
if (*chptr->chname != '#')
return;
nomode = 0;
nopara = 0;
members = chptr->members;
/* First we'll send channel, channel modes and members and status */
*modebuf = *parabuf = '\0';
channel_modes(cptr, modebuf, parabuf, chptr);
if (!modebuf[1])
nomode = 1;
if (!(*parabuf))
nopara = 1;
if (nomode && nopara)
{
ircsprintf(buf,
(cptr->proto & PROTO_SJB64 ? "%s %B %s :" : "%s %ld %s :"),
(IsToken(cptr) ? TOK_SJOIN : MSG_SJOIN),
chptr->creationtime, chptr->chname);
}
if (nopara && !nomode)
{
ircsprintf(buf,
(cptr->proto & PROTO_SJB64 ? "%s %B %s %s :" : "%s %ld %s %s :"),
(IsToken(cptr) ? TOK_SJOIN : MSG_SJOIN),
chptr->creationtime, chptr->chname, modebuf);
}
if (!nopara && !nomode)
{
ircsprintf(buf,
(cptr->proto & PROTO_SJB64 ? "%s %B %s %s %s :" : "%s %ld %s %s %s :"),
(IsToken(cptr) ? TOK_SJOIN : MSG_SJOIN),
chptr->creationtime, chptr->chname, modebuf, parabuf);
}
strcpy(bbuf, buf);
bufptr = buf + strlen(buf);
for (lp = members; lp; lp = lp->next)
{
if (lp->flags & MODE_CHANOP)
*bufptr++ = '@';
if (lp->flags & MODE_VOICE)
*bufptr++ = '+';
if (lp->flags & MODE_HALFOP)
*bufptr++ = '%';
if (lp->flags & MODE_CHANOWNER)
*bufptr++ = '*';
if (lp->flags & MODE_CHANPROT)
*bufptr++ = '~';
name = lp->cptr->name;
strcpy(bufptr, name);
bufptr += strlen(bufptr);
*bufptr++ = ' ';
n++;
if (bufptr - buf > BUFSIZE - 80)
{
*bufptr++ = '\0';
if (bufptr[-1] == ' ')
bufptr[-1] = '\0';
sendto_one(cptr, "%s", buf);
strcpy(buf, bbuf);
n = 0;
bufptr = buf + strlen(buf);
}
}
for (ban = chptr->banlist; ban; ban = ban->next)
{
*bufptr++ = '&';
strcpy(bufptr, ban->banstr);
bufptr += strlen(bufptr);
*bufptr++ = ' ';
n++;
if (bufptr - buf > BUFSIZE - 80)
{
*bufptr++ = '\0';
if (bufptr[-1] == ' ')
bufptr[-1] = '\0';
sendto_one(cptr, "%s", buf);
strcpy(buf, bbuf);
n = 0;
bufptr = buf + strlen(buf);
}
}
for (ban = chptr->exlist; ban; ban = ban->next)
{
*bufptr++ = '"';
strcpy(bufptr, ban->banstr);
bufptr += strlen(bufptr);
*bufptr++ = ' ';
n++;
if (bufptr - buf > BUFSIZE - 80)
{
*bufptr++ = '\0';
if (bufptr[-1] == ' ')
bufptr[-1] = '\0';
sendto_one(cptr, "%s", buf);
strcpy(buf, bbuf);
n = 0;
bufptr = buf + strlen(buf);
}
}
if (n)
{
*bufptr++ = '\0';
if (bufptr[-1] == ' ')
bufptr[-1] = '\0';
sendto_one(cptr, "%s", buf);
}
}
void add_send_mode_param(aChannel *chptr, aClient *from, char what, char mode, char *param) {
static char *modes = modebuf, lastwhat;
static short count = 0;
short send = 0;
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;
}
}
/*
* rejoin_doparts:
* sends a PART to all channels (to local users only)
*/
void rejoin_doparts(aClient *sptr)
{
Membership *tmp;
aChannel *chptr;
char *comment = "Rejoining because of user@host change";
for (tmp = sptr->user->channel; tmp; tmp = tmp->next)
{
chptr = tmp->chptr;
if (!chptr)
continue; /* Possible? */
if ((chptr->mode.mode & MODE_AUDITORIUM) &&
!(tmp->flags & (CHFL_CHANOWNER|CHFL_CHANPROT|CHFL_CHANOP)))
{
sendto_chanops_butone(sptr, chptr, ":%s!%s@%s PART %s :%s", sptr->name, sptr->user->username, GetHost(sptr), chptr->chname, comment);
} else
sendto_channel_butserv_butone(chptr, sptr, sptr, ":%s PART %s :%s", sptr->name, chptr->chname, comment);
}
}
/*
* rejoin_dojoinandmode:
* sends a JOIN and a MODE (if needed) to restore qaohv modes (to local users only)
*/
void rejoin_dojoinandmode(aClient *sptr)
{
Membership *tmp;
aChannel *chptr;
int i, n, flags;
char flagbuf[8]; /* For holding "qohva" and "*~@%+" */
for (tmp = sptr->user->channel; tmp; tmp = tmp->next)
{
flags = tmp->flags;
chptr = tmp->chptr;
if (!chptr)
continue; /* Is it possible? */
if ((chptr->mode.mode & MODE_AUDITORIUM) &&
!(flags & (CHFL_CHANOWNER|CHFL_CHANPROT|CHFL_CHANOP)))
{
sendto_chanops_butone(sptr, chptr, ":%s!%s@%s JOIN :%s", sptr->name, sptr->user->username, GetHost(sptr), chptr->chname);
} else
sendto_channel_butserv_butone(chptr, sptr, sptr, ":%s JOIN :%s", sptr->name, chptr->chname);
/* Set the modes (if any) */
if (flags)
{
char *p = flagbuf;
if (flags & MODE_CHANOP)
*p++ = 'o';
if (flags & MODE_VOICE)
*p++ = 'v';
if (flags & MODE_HALFOP)
*p++ = 'h';
if (flags & MODE_CHANOWNER)
*p++ = 'q';
if (flags & MODE_CHANPROT)
*p++ = 'a';
*p = '\0';
parabuf[0] = '\0';
n = strlen(flagbuf);
if (n)
{
for (i=0; i < n; i++)
{
strcat(parabuf, sptr->name);
if (i < n - 1)
strcat(parabuf, " ");
}
sendto_channel_butserv_butone(chptr, &me, sptr, ":%s MODE %s +%s %s",
me.name, chptr->chname, flagbuf, parabuf);
}
}
}
}
#ifdef NEWCHFLOODPROT
MODVAR RemoveFld *removefld_list = NULL;
RemoveFld *chanfloodtimer_find(aChannel *chptr, char mflag)
{
RemoveFld *e;
for (e=removefld_list; e; e=e->next)
{
if ((e->chptr == chptr) && (e->m == mflag))
return e;
}
return NULL;
}
/*
* Adds a "remove channelmode set by +f" timer.
* chptr Channel
* mflag Mode flag, eg 'C'
* mbit Mode bitflag, eg MODE_NOCTCP
* when when it should be removed
* NOTES:
* - This function takes care of overwriting of any previous timer
* for the same modechar.
* - The function takes care of chptr->mode.floodprot->timer_flags,
* do not modify it yourself.
* - chptr->mode.floodprot is asumed to be non-NULL.
*/
void chanfloodtimer_add(aChannel *chptr, char mflag, long mbit, time_t when)
{
RemoveFld *e = NULL;
unsigned char add=1;
if (chptr->mode.floodprot->timer_flags & mbit)
{
/* Already exists... */
e = chanfloodtimer_find(chptr, mflag);
if (e)
add = 0;
}
if (add)
e = MyMallocEx(sizeof(RemoveFld));
e->chptr = chptr;
e->m = mflag;
e->when = when;
if (add)
AddListItem(e, removefld_list);
chptr->mode.floodprot->timer_flags |= mbit;
}
void chanfloodtimer_del(aChannel *chptr, char mflag, long mbit)
{
RemoveFld *e;
if (chptr->mode.floodprot && !(chptr->mode.floodprot->timer_flags & mbit))
return; /* nothing to remove.. */
e = chanfloodtimer_find(chptr, mflag);
if (!e)
return;
DelListItem(e, removefld_list);
if (chptr->mode.floodprot)
chptr->mode.floodprot->timer_flags &= ~mbit;
}
long get_chanbitbychar(char m)
{
aCtab *tab = &cFlagTab[0];
while(tab->mode != 0x0)
{
if (tab->flag == m)
return tab->mode;
tab++;;
}
return 0;
}
EVENT(modef_event)
{
RemoveFld *e = removefld_list;
time_t now;
long mode;
now = TStime();
while(e)
{
if (e->when <= now)
{
/* Remove chanmode... */
#ifdef NEWFLDDBG
sendto_realops("modef_event: chan %s mode -%c EXPIRED", e->chptr->chname, e->m);
#endif
mode = get_chanbitbychar(e->m);
if (e->chptr->mode.mode & mode)
{
sendto_serv_butone(&me, ":%s MODE %s -%c 0", me.name, e->chptr->chname, e->m);
sendto_channel_butserv(e->chptr, &me, ":%s MODE %s -%c", me.name, e->chptr->chname, e->m);
e->chptr->mode.mode &= ~mode;
}
/* And delete... */
e = (RemoveFld *)DelListItem(e, removefld_list);
} else {
#ifdef NEWFLDDBG
sendto_realops("modef_event: chan %s mode -%c about %d seconds",
e->chptr->chname, e->m, e->when - now);
#endif
e = e->next;
}
}
}
void init_modef()
{
EventAddEx(NULL, "modef_event", 10, 0, modef_event, NULL);
}
void chanfloodtimer_stopchantimers(aChannel *chptr)
{
RemoveFld *e = removefld_list;
while(e)
{
if (e->chptr == chptr)
e = (RemoveFld *)DelListItem(e, removefld_list);
else
e = e->next;
}
}
int do_chanflood(ChanFloodProt *chp, int what)
{
if (!chp || !chp->l[what]) /* no +f or not restricted */
return 0;
if (TStime() - chp->t[what] >= chp->per)
{
chp->t[what] = TStime();
chp->c[what] = 1;
} else
{
chp->c[what]++;
if ((chp->c[what] > chp->l[what]) &&
(TStime() - chp->t[what] < chp->per))
{
/* reset it too (makes it easier for chanops to handle the situation) */
/*
*XXchp->t[what] = TStime();
*XXchp->c[what] = 1;
*
* BAD.. there are some situations where we might 'miss' a flood
* because of this. The reset has been moved to -i,-m,-N,-C,etc.
*/
return 1; /* flood detected! */
}
}
return 0;
}
void do_chanflood_action(aChannel *chptr, int what, char *text)
{
long modeflag = 0;
aCtab *tab = &cFlagTab[0];
char m;
m = chptr->mode.floodprot->a[what];
if (!m)
return;
/* [TODO: add extended channel mode support] */
while(tab->mode != 0x0)
{
if (tab->flag == m)
{
modeflag = tab->mode;
break;
}
tab++;
}
if (!modeflag)
return;
if (!(chptr->mode.mode & modeflag))
{
char comment[1024], target[CHANNELLEN + 8];
ircsprintf(comment, "*** Channel %sflood detected (limit is %d per %d seconds), setting mode +%c",
text, chptr->mode.floodprot->l[what], chptr->mode.floodprot->per, m);
ircsprintf(target, "@%%%s", chptr->chname);
sendto_channelprefix_butone_tok(NULL, &me, chptr,
PREFIX_HALFOP|PREFIX_OP|PREFIX_ADMIN|PREFIX_OWNER,
MSG_NOTICE, TOK_NOTICE, target, comment, 0);
sendto_serv_butone(&me, ":%s MODE %s +%c 0", me.name, chptr->chname, m);
sendto_channel_butserv(chptr, &me, ":%s MODE %s +%c", me.name, chptr->chname, m);
chptr->mode.mode |= modeflag;
if (chptr->mode.floodprot->r[what]) /* Add remove-chanmode timer... */
{
chanfloodtimer_add(chptr, m, modeflag, TStime() + ((long)chptr->mode.floodprot->r[what] * 60) - 5);
/* (since the chanflood timer event is called every 10s, we do -5 here so the accurancy will
* be -5..+5, without it it would be 0..+10.)
*/
}
}
}
#endif