/*
* IRC - Internet Relay Chat, 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.
*/
/* $Id: channel.c,v 1.19 2006/06/25 22:53:01 sheik Exp $ */
#include "struct.h"
#include "common.h"
#include "sys.h"
#include "numeric.h"
#include "channel.h"
#include "h.h"
#include "userban.h"
#include "memcount.h"
int server_was_split = YES;
aChannel *channel = NullChn;
#ifdef INVITE_LISTS
/* +I list functions */
int add_invite_id(aClient*, aChannel*, char*);
int del_invite_id(aChannel*, char*);
anInvite* is_invited(aClient*, aChannel*);
#endif
#ifdef EXEMPT_LISTS
/* +e list functions */
int add_exempt_id(aClient*, aChannel*, char*);
int del_exempt_id(aChannel*, char*);
#endif
static int add_banid(aClient *, aChannel *, char *);
static int can_join(aClient *, aChannel *, char *);
static void channel_modes(aClient *, char *, char *, aChannel *);
static int del_banid(aChannel *, char *);
static int is_banned(aClient *, aChannel *, chanMember *);
static int set_mode(aClient *, aClient *, aChannel *, int,
int, char **, char *, char *);
static void sub1_from_channel(aChannel *);
int check_channelname(aClient *, unsigned char *);
void clean_channelname(unsigned char *);
static void add_invite(aClient *, aChannel *);
void del_invite(aClient *, aChannel *);
#ifdef ORATIMING
struct timeval tsdnow, tsdthen;
unsigned long tsdms;
#endif
/* number of seconds to add to all readings of time() when making TS's */
static char *PartFmt = ":%s PART %s";
static char *PartFmt2 = ":%s PART %s :%s";
/* server <-> server SJOIN format */
static char *SJOINFmt = ":%s SJOIN %ld %s %s %s :%s";
/* NP means no paramaters, don't send the extra space there */
static char *SJOINFmtNP = ":%s SJOIN %ld %s %s :%s";
/* client SJOIN format, for no channel creation */
static char *CliSJOINFmt = ":%s SJOIN %ld %s";
/* some buffers for rebuilding channel/nick lists with ,'s */
static char nickbuf[BUFSIZE], buf[BUFSIZE];
static char modebuf[REALMODEBUFLEN], parabuf[REALMODEBUFLEN];
/* externally defined function */
extern Link *find_channel_link(Link *, aChannel *); /* defined in list.c */
#ifdef ANTI_SPAMBOT
extern int spam_num; /* defined in s_serv.c */
extern int spam_time; /* defined in s_serv.c */
#endif
/* 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;
}
/* check to see if the message has any control chars in it. */
static int
msg_has_ctrls(char *msg)
{
unsigned char *c;
if (msg == NULL)
return 0;
for (c = (unsigned char *) msg; *c; c++)
{
/* not a control code */
if (*c > 31)
continue;
/* ctcp */
if (*c == 1)
continue;
/* escape */
if (*c == 27)
{
/* ISO 2022 charset shift sequence */
if (c[1] == '$' || c[1] == '(')
{
c++;
continue;
}
}
/* control code */
break;
}
if(*c)
return 1;
return 0;
}
/*
* 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)
return who;
if (!(who = get_history(user, (long) KILLCHASETIMELIMIT)))
{
sendto_one(sptr, err_str(ERR_NOSUCHNICK),
me.name, sptr->name, user);
return ((aClient *) NULL);
}
if (chasing)
*chasing = 1;
return who;
}
/*
* Fixes a string so that the first white space found becomes an end of
* string marker (`\-`). returns the 'fixed' string or "*" if the
* string was NULL length or a NULL pointer.
*/
static char * check_string(char *s)
{
static char star[2] = "*";
char *str = s;
if (BadPtr(s))
return star;
for (; *s; s++)
if (IsSpace(*s))
{
*s = '\0';
break;
}
return (BadPtr(str)) ? star : str;
}
/*
* create a string of form "foo!bar@fubar" given foo, bar and fubar as
* the parameters. If NULL, they become "*".
*/
static char *make_nick_user_host(char *nick, char *name, char *host)
{
static char namebuf[NICKLEN + USERLEN + HOSTLEN + 6];
int n;
char *ptr1, *ptr2;
ptr1 = namebuf;
for (ptr2 = check_string(nick), n = NICKLEN; *ptr2 && n--;)
*ptr1++ = *ptr2++;
*ptr1++ = '!';
for (ptr2 = check_string(name), n = USERLEN; *ptr2 && n--;)
*ptr1++ = *ptr2++;
*ptr1++ = '@';
for (ptr2 = check_string(host), n = HOSTLEN; *ptr2 && n--;)
*ptr1++ = *ptr2++;
*ptr1 = '\0';
return (namebuf);
}
#ifdef EXEMPT_LISTS
/* Exempt list functions (+e) */
int add_exempt_id(aClient* cptr, aChannel* chptr, char* exempt_id)
{
aBanExempt* exempt = NULL;
int cnt = 0;
for (exempt = chptr->banexempt_list; exempt; exempt = exempt->next)
{
if (MyClient(cptr))
{
if (++cnt >= MAXEXEMPTLIST)
{
sendto_one(cptr, getreply(ERR_BANLISTFULL), me.name, cptr->name,
chptr->chname, exempt_id, "exempt");
return -1;
}
if (!match(exempt->banstr, exempt_id))
return -1;
}
else if (!mycmp(exempt->banstr, exempt_id))
return -1;
}
exempt = (aBanExempt*)MyMalloc(sizeof(aBanExempt));
exempt->banstr = (char*)MyMalloc(strlen(exempt_id)+1);
strcpy(exempt->banstr, exempt_id);
exempt->when = timeofday;
exempt->next = chptr->banexempt_list;
chptr->banexempt_list = exempt;
chptr->banserial++;
if (IsPerson(cptr))
{
exempt->who = (char *) MyMalloc(strlen(cptr->name) +
strlen(cptr->user->username) +
strlen(cptr->user->host) + 3);
(void) ircsprintf(exempt->who, "%s!%s@%s",
cptr->name, cptr->user->username, cptr->user->host);
}
else
{
exempt->who = (char *) MyMalloc(strlen(cptr->name) + 1);
(void) strcpy(exempt->who, cptr->name);
}
/* determine type for less matching later */
if(exempt_id[0] == '*' && exempt_id[1] == '!')
{
if(exempt_id[2] == '*' && exempt_id[3] == '@')
exempt->type = MTYP_HOST;
else
exempt->type = MTYP_USERHOST;
}
else
exempt->type = MTYP_FULL;
return 0;
}
int del_exempt_id(aChannel* chptr, char* exempt_id)
{
aBanExempt** exempt;
aBanExempt* tmp;
if (!exempt_id)
return -1;
for (exempt = &chptr->banexempt_list; *exempt; exempt = &((*exempt)->next))
{
if (mycmp(exempt_id, (*exempt)->banstr) == 0)
{
tmp = *exempt;
*exempt = tmp->next;
chptr->banserial++;
MyFree(tmp->banstr);
MyFree(tmp->who);
MyFree(tmp);
break;
}
}
return 0;
}
#endif
#ifdef INVITE_LISTS
/* Invite list functions (+I) */
int add_invite_id(aClient* cptr, aChannel* chptr, char* invite_id)
{
anInvite* invite;
int cnt = 0;
for (invite = chptr->invite_list; invite; invite = invite->next)
{
if (MyClient(cptr))
{
if (++cnt >= MAXINVITELIST)
{
sendto_one(cptr, getreply(ERR_BANLISTFULL), me.name, cptr->name,
chptr->chname, invite_id, "invite");
return -1;
}
if (!match(invite->invstr, invite_id))
return -1;
}
else if (!mycmp(invite->invstr, invite_id))
return -1;
}
invite = (anInvite*)MyMalloc(sizeof(anInvite));
invite->invstr = (char*)MyMalloc(strlen(invite_id)+1);
strcpy(invite->invstr, invite_id);
invite->when = timeofday;
invite->next = chptr->invite_list;
chptr->invite_list = invite;
if (IsPerson(cptr))
{
invite->who = (char *) MyMalloc(strlen(cptr->name) +
strlen(cptr->user->username) +
strlen(cptr->user->host) + 3);
(void) ircsprintf(invite->who, "%s!%s@%s",
cptr->name, cptr->user->username, cptr->user->host);
}
else
{
invite->who = (char *) MyMalloc(strlen(cptr->name) + 1);
(void) strcpy(invite->who, cptr->name);
}
return 0;
}
int del_invite_id(aChannel* chptr, char* invite_id)
{
anInvite** invite;
anInvite* tmp;
if (!invite_id)
return -1;
for (invite = &chptr->invite_list; *invite; invite = &((*invite)->next))
{
if (mycmp(invite_id, (*invite)->invstr) == 0)
{
tmp = *invite;
*invite = tmp->next;
MyFree(tmp->invstr);
MyFree(tmp->who);
MyFree(tmp);
break;
}
}
return 0;
}
anInvite* is_invited(aClient* cptr, aChannel* chptr)
{
char s[NICKLEN + USERLEN + HOSTLEN + 6];
char *s2;
anInvite* invite;
strcpy(s, make_nick_user_host(cptr->name, cptr->user->username,
cptr->user->host));
s2 = make_nick_user_host(cptr->name, cptr->user->username,
cptr->hostip);
for (invite = chptr->invite_list; invite; invite = invite->next)
{
if (!match(invite->invstr, s) || !match(invite->invstr, s2))
break;
}
return invite;
}
#endif
/* Ban functions to work with mode +b */
/* add_banid - add an id to be banned to the channel (belongs to cptr) */
static int add_banid(aClient *cptr, aChannel *chptr, char *banid)
{
aBan *ban;
int cnt = 0;
for (ban = chptr->banlist; ban; ban = ban->next)
{
/* Begin unbreaking redundant ban checking. First step is to allow
* ALL non-duplicates from remote servers. Local clients are still
* subject to the flawed redundancy check for compatibility with
* older servers. This check can be corrected later. -Quension */
if (MyClient(cptr))
{
if (++cnt >= MAXBANS)
{
sendto_one(cptr, getreply(ERR_BANLISTFULL), me.name, cptr->name,
chptr->chname, banid, "ban");
return -1;
}
if (!match(ban->banstr, banid))
return -1;
}
else if (!mycmp(ban->banstr, banid))
return -1;
}
ban = (aBan *) MyMalloc(sizeof(aBan));
ban->banstr = (char *) MyMalloc(strlen(banid) + 1);
(void) strcpy(ban->banstr, banid);
ban->next = chptr->banlist;
if (IsPerson(cptr))
{
ban->who = (char *) MyMalloc(strlen(cptr->name) +
strlen(cptr->user->username) +
strlen(cptr->user->host) + 3);
(void) ircsprintf(ban->who, "%s!%s@%s",
cptr->name, cptr->user->username, cptr->user->host);
}
else
{
ban->who = (char *) MyMalloc(strlen(cptr->name) + 1);
(void) strcpy(ban->who, cptr->name);
}
/* determine what 'type' of mask this is, for less matching later */
if(banid[0] == '*' && banid[1] == '!')
{
if(banid[2] == '*' && banid[3] == '@')
ban->type = MTYP_HOST;
else
ban->type = MTYP_USERHOST;
}
else
ban->type = MTYP_FULL;
ban->when = timeofday;
chptr->banlist = ban;
chptr->banserial++;
return 0;
}
/*
* del_banid - delete an id belonging to cptr if banid is null,
* deleteall banids belonging to cptr.
*/
static int del_banid(aChannel *chptr, char *banid)
{
aBan **ban;
aBan *tmp;
if (!banid)
return -1;
for (ban = &(chptr->banlist); *ban; ban = &((*ban)->next))
if (mycmp(banid, (*ban)->banstr) == 0)
{
tmp = *ban;
*ban = tmp->next;
chptr->banserial++;
MyFree(tmp->banstr);
MyFree(tmp->who);
MyFree(tmp);
break;
}
return 0;
}
/*
* is_banned - returns CHFL_BANNED if banned else 0
*
* caches banned status in chanMember for can_send()
* -Quension [Jun 2004]
*/
static int is_banned(aClient *cptr, aChannel *chptr, chanMember *cm)
{
aBan *ban;
#ifdef EXEMPT_LISTS
aBanExempt *exempt;
#endif
char s[NICKLEN + USERLEN + HOSTLEN + 6];
char *s2;
if (!IsPerson(cptr))
return 0;
/* if cache is valid, use it */
if (cm)
{
if (cm->banserial == chptr->banserial)
return (cm->flags & CHFL_BANNED);
cm->banserial = chptr->banserial;
cm->flags &= ~CHFL_BANNED;
}
strcpy(s, make_nick_user_host(cptr->name, cptr->user->username,
cptr->user->host));
s2 = make_nick_user_host(cptr->name, cptr->user->username,
cptr->hostip);
#ifdef EXEMPT_LISTS
for (exempt = chptr->banexempt_list; exempt; exempt = exempt->next)
if (!match(exempt->banstr, s) || !match(exempt->banstr, s2))
return 0;
#endif
for (ban = chptr->banlist; ban; ban = ban->next)
if ((match(ban->banstr, s) == 0) ||
(match(ban->banstr, s2) == 0))
break;
if (ban)
{
if (cm)
cm->flags |= CHFL_BANNED;
return CHFL_BANNED;
}
return 0;
}
aBan *nick_is_banned(aChannel *chptr, char *nick, aClient *cptr)
{
aBan *ban;
#ifdef EXEMPT_LISTS
aBanExempt *exempt;
#endif
char *s, s2[NICKLEN+USERLEN+HOSTLEN+6];
if (!IsPerson(cptr)) return NULL;
strcpy(s2, make_nick_user_host(nick, cptr->user->username,
cptr->user->host));
s = make_nick_user_host(nick, cptr->user->username, cptr->hostip);
#ifdef EXEMPT_LISTS
for (exempt = chptr->banexempt_list; exempt; exempt = exempt->next)
if (exempt->type == MTYP_FULL &&
((match(exempt->banstr, s2) == 0) ||
(match(exempt->banstr, s) == 0)))
return NULL;
#endif
for (ban = chptr->banlist; ban; ban = ban->next)
if (ban->type == MTYP_FULL && /* only check applicable bans */
((match(ban->banstr, s2) == 0) || /* check host before IP */
(match(ban->banstr, s) == 0)))
break;
return (ban);
}
void remove_matching_bans(aChannel *chptr, aClient *cptr, aClient *from)
{
aBan *ban, *bnext;
char targhost[NICKLEN+USERLEN+HOSTLEN+6];
char targip[NICKLEN+USERLEN+HOSTLEN+6];
char *m;
int count = 0, send = 0;
if (!IsPerson(cptr)) return;
strcpy(targhost, make_nick_user_host(cptr->name, cptr->user->username,
cptr->user->host));
strcpy(targip, make_nick_user_host(cptr->name, cptr->user->username,
cptr->hostip));
m = modebuf;
*m++ = '-';
*m = '\0';
*parabuf = '\0';
ban = chptr->banlist;
while(ban)
{
bnext = ban->next;
if((match(ban->banstr, targhost) == 0) ||
(match(ban->banstr, targip) == 0))
{
if (strlen(parabuf) + strlen(ban->banstr) + 10 < (size_t) MODEBUFLEN)
{
if(*parabuf)
strcat(parabuf, " ");
strcat(parabuf, ban->banstr);
count++;
*m++ = 'b';
*m = '\0';
}
else
if(*parabuf)
send = 1;
if(count == MAXTSMODEPARAMS)
send = 1;
if(send)
{
sendto_channel_butserv_me(chptr, from, ":%s MODE %s %s %s",
from->name, chptr->chname, modebuf,
parabuf);
sendto_serv_butone(from, ":%s MODE %s %ld %s %s", from->name,
chptr->chname, chptr->channelts, modebuf,
parabuf);
send = 0;
*parabuf = '\0';
m = modebuf;
*m++ = '-';
if(count != MAXTSMODEPARAMS)
{
strcpy(parabuf, ban->banstr);
*m++ = 'b';
count = 1;
}
else
count = 0;
*m = '\0';
}
del_banid(chptr, ban->banstr);
}
ban = bnext;
}
if(*parabuf)
{
sendto_channel_butserv_me(chptr, from, ":%s MODE %s %s %s", from->name,
chptr->chname, modebuf, parabuf);
sendto_serv_butone(from, ":%s MODE %s %ld %s %s", from->name,
chptr->chname, chptr->channelts, modebuf, parabuf);
}
return;
}
#ifdef EXEMPT_LISTS
void remove_matching_exempts(aChannel *chptr, aClient *cptr, aClient *from)
{
aBanExempt *ex, *enext;
char targhost[NICKLEN+USERLEN+HOSTLEN+6];
char targip[NICKLEN+USERLEN+HOSTLEN+6];
char *m;
int count = 0, send = 0;
if (!IsPerson(cptr)) return;
strcpy(targhost, make_nick_user_host(cptr->name, cptr->user->username,
cptr->user->host));
strcpy(targip, make_nick_user_host(cptr->name, cptr->user->username,
cptr->hostip));
m = modebuf;
*m++ = '-';
*m = '\0';
*parabuf = '\0';
ex = chptr->banexempt_list;
while(ex)
{
enext = ex->next;
if((match(ex->banstr, targhost) == 0) ||
(match(ex->banstr, targip) == 0))
{
if (strlen(parabuf) + strlen(ex->banstr) + 10 < (size_t) MODEBUFLEN)
{
if(*parabuf)
strcat(parabuf, " ");
strcat(parabuf, ex->banstr);
count++;
*m++ = 'e';
*m = '\0';
}
else
if(*parabuf)
send = 1;
if(count == MAXTSMODEPARAMS)
send = 1;
if(send)
{
sendto_channel_butserv_me(chptr, from, ":%s MODE %s %s %s",
from->name, chptr->chname, modebuf,
parabuf);
sendto_serv_butone(from, ":%s MODE %s %ld %s %s", from->name,
chptr->chname, chptr->channelts, modebuf,
parabuf);
send = 0;
*parabuf = '\0';
m = modebuf;
*m++ = '-';
if(count != MAXTSMODEPARAMS)
{
strcpy(parabuf, ex->banstr);
*m++ = 'e';
count = 1;
}
else
count = 0;
*m = '\0';
}
del_exempt_id(chptr, ex->banstr);
}
ex = enext;
}
if(*parabuf)
{
sendto_channel_butserv_me(chptr, from, ":%s MODE %s %s %s", from->name,
chptr->chname, modebuf, parabuf);
sendto_serv_butone(from, ":%s MODE %s %ld %s %s", from->name,
chptr->chname, chptr->channelts, modebuf, parabuf);
}
return;
}
#endif
#ifdef INVITE_LISTS
void remove_matching_invites(aChannel *chptr, aClient *cptr, aClient *from)
{
anInvite *inv, *inext;
char targhost[NICKLEN+USERLEN+HOSTLEN+6];
char targip[NICKLEN+USERLEN+HOSTLEN+6];
char *m;
int count = 0, send = 0;
if (!IsPerson(cptr)) return;
strcpy(targhost, make_nick_user_host(cptr->name, cptr->user->username,
cptr->user->host));
strcpy(targip, make_nick_user_host(cptr->name, cptr->user->username,
cptr->hostip));
m = modebuf;
*m++ = '-';
*m = '\0';
*parabuf = '\0';
inv = chptr->invite_list;
while(inv)
{
inext = inv->next;
if((match(inv->invstr, targhost) == 0) ||
(match(inv->invstr, targip) == 0))
{
if (strlen(parabuf) + strlen(inv->invstr) + 10 < (size_t) MODEBUFLEN)
{
if(*parabuf)
strcat(parabuf, " ");
strcat(parabuf, inv->invstr);
count++;
*m++ = 'I';
*m = '\0';
}
else
if(*parabuf)
send = 1;
if(count == MAXTSMODEPARAMS)
send = 1;
if(send)
{
sendto_channel_butserv_me(chptr, from, ":%s MODE %s %s %s",
from->name, chptr->chname, modebuf,
parabuf);
sendto_serv_butone(from, ":%s MODE %s %ld %s %s", from->name,
chptr->chname, chptr->channelts, modebuf,
parabuf);
send = 0;
*parabuf = '\0';
m = modebuf;
*m++ = '-';
if(count != MAXTSMODEPARAMS)
{
strcpy(parabuf, inv->invstr);
*m++ = 'I';
count = 1;
}
else
count = 0;
*m = '\0';
}
del_invite_id(chptr, inv->invstr);
}
inv = inext;
}
if(*parabuf)
{
sendto_channel_butserv_me(chptr, from, ":%s MODE %s %s %s", from->name,
chptr->chname, modebuf, parabuf);
sendto_serv_butone(from, ":%s MODE %s %ld %s %s", from->name,
chptr->chname, chptr->channelts, modebuf, parabuf);
}
return;
}
#endif
int check_joinrate(aChannel *chptr, time_t ts, int local, aClient *cptr)
{
int join_num = DEFAULT_JOIN_NUM;
int join_time = DEFAULT_JOIN_TIME;
if(!local && (NOW - ts) > 60)
return 1; /* attempt to compensate for lag */
/* This first section checks the defaults, the second
* checks the channels +j settings */
/* Has the join_time period elapsed? */
if((NOW - chptr->default_join_start) > join_time)
{
chptr->default_join_start = NOW;
chptr->default_join_count = 0;
}
/* update the count here for attempts, in case a lower throttle blocks */
chptr->default_join_count++;
/* If it's local and we've filled the join count, complain
* to ops so they can take the appropriate measures */
if(local && chptr->default_join_count > join_num)
sendto_realops_lev(DEBUG_LEV, "Join rate warning on %s for %s!%s@%s"
" (%d in %d)",
chptr->chname, cptr->name, cptr->user->username,
cptr->hostip, chptr->default_join_count,
NOW - chptr->default_join_start);
/* Has the channel set their own custom settings? */
if(chptr->mode.mode & MODE_JOINRATE)
{
if(chptr->mode.join_num == 0 || chptr->mode.join_time == 0)
return 1; /* channel has turned this off */
join_time = chptr->mode.join_time;
join_num = chptr->mode.join_num;
}
/* Has the join_time period elapsed? */
if((NOW - chptr->join_start) > join_time)
{
chptr->join_start = NOW;
chptr->join_count = 0;
return 1;
}
/* If it's local and we've filled the join count, say no */
if(local && chptr->join_count >= join_num)
{
sendto_realops_lev(DEBUG_LEV, "Join rate throttling on %s for %s!%s@%s"
" (%d/%d in %d/%d)",
chptr->chname, cptr->name, cptr->user->username,
cptr->hostip, chptr->join_count, join_num,
NOW - chptr->join_start, join_time);
return 0;
}
return 1;
}
/*
* adds a user to a channel by adding another link to the channels
* member chain.
*/
static void add_user_to_channel(aChannel *chptr, aClient *who, int flags)
{
Link *ptr;
chanMember *cm;
#ifdef DUMP_DEBUG
fprintf(dumpfp,"Add to channel %s: %p:%s\n",chptr->chname,who,who->name);
#endif
if (who->user)
{
cm = make_chanmember();
cm->flags = flags;
cm->cptr = who;
cm->next = chptr->members;
cm->banserial = chptr->banserial;
chptr->members = cm;
chptr->users++;
ptr = make_link();
ptr->value.chptr = chptr;
ptr->next = who->user->channel;
who->user->channel = ptr;
who->user->joined++;
}
}
void remove_user_from_channel(aClient *sptr, aChannel *chptr)
{
chanMember **curr, *tmp;
Link **lcurr, *ltmp;
for (curr = &chptr->members; (tmp = *curr); curr = &tmp->next)
if (tmp->cptr == sptr)
{
*curr = tmp->next;
free_chanmember(tmp);
break;
}
for (lcurr = &sptr->user->channel; (ltmp = *lcurr); lcurr = <mp->next)
if (ltmp->value.chptr == chptr)
{
*lcurr = ltmp->next;
free_link(ltmp);
break;
}
sptr->user->joined--;
sub1_from_channel(chptr);
}
int is_chan_op(aClient *cptr, aChannel *chptr)
{
chanMember *cm;
if (chptr)
if ((cm = find_user_member(chptr->members, cptr)))
return (cm->flags & CHFL_CHANOP);
return 0;
}
int is_deopped(aClient *cptr, aChannel *chptr)
{
chanMember *cm;
if (chptr)
if ((cm = find_user_member(chptr->members, cptr)))
return (cm->flags & CHFL_DEOPPED);
return 0;
}
int is_only_halfop(aClient *cptr, aChannel *chptr)
{
chanMember *cm;
if (chptr)
if ((cm = find_user_member(chptr->members, cptr)))
if (!(cm->flags & CHFL_CHANOP))
return (cm->flags & CHFL_HALFOP);
return 0;
}
int is_halfop(aClient *cptr, aChannel *chptr)
{
chanMember *cm;
if (chptr)
if ((cm = find_user_member(chptr->members, cptr)))
return (cm->flags & CHFL_HALFOP);
return 0;
}
int has_voice(aClient *cptr, aChannel *chptr)
{
chanMember *cm;
if (chptr)
if ((cm = find_user_member(chptr->members, cptr)))
return (cm->flags & CHFL_VOICE);
return 0;
}
int can_send(aClient *cptr, aChannel *chptr, char *msg)
{
chanMember *cm;
if (IsServer(cptr) || IsULine(cptr))
return 0;
cm = find_user_member(chptr->members, cptr);
if(!cm)
{
if (chptr->mode.mode & MODE_MODERATED)
return (MODE_MODERATED);
if(chptr->mode.mode & MODE_NOPRIVMSGS)
return (MODE_NOPRIVMSGS);
if ((chptr->mode.mode & MODE_MODREG) && !IsRegNick(cptr))
return (ERR_NEEDREGGEDNICK);
if ((chptr->mode.mode & MODE_NOCTRL) && msg_has_ctrls(msg))
return (ERR_NOCTRLSONCHAN);
if (MyClient(cptr) && is_banned(cptr, chptr, NULL))
return (MODE_BAN); /*
* channel is -n and user is not there;
* we need to bquiet them if we can
*/
}
else
{
/* ops and voices can talk through everything except NOCTRL */
if (!(cm->flags & (CHFL_CHANOP | CHFL_HALFOP | CHFL_VOICE)))
{
if (chptr->mode.mode & MODE_MODERATED)
return (MODE_MODERATED);
if (is_banned(cptr, chptr, cm))
return (MODE_BAN);
if ((chptr->mode.mode & MODE_MODREG) && !IsRegNick(cptr))
return (ERR_NEEDREGGEDNICK);
}
if ((chptr->mode.mode & MODE_NOCTRL) && msg_has_ctrls(msg))
return (ERR_NOCTRLSONCHAN);
}
return 0;
}
/* Can a user change his nick on this channel? - TheSphere 08/01/04*/
int can_changenick(aClient *cptr, aChannel *chptr)
{
chanMember *cm;
if (IsServer(cptr) || IsULine(cptr))
return 0;
if ((cm = find_user_member(chptr->members, cptr)))
{
if (!(cm->flags & (CHFL_CHANOP | CHFL_HALFOP))) /* Let halfops change their name aswell - Tux */
{
if (chptr->mode.mode & MODE_NONICK)
return (MODE_MODERATED);
}
}
return 0;
}
/*
* write the "simple" list of channel modes for channel chptr onto
* buffer mbuf with the parameters in pbuf.
*/
static void channel_modes(aClient *cptr, char *mbuf, char *pbuf,
aChannel *chptr)
{
pbuf[0] = '\0';
*mbuf++ = '+';
if (chptr->mode.mode & MODE_SECRET)
*mbuf++ = 's';
if (chptr->mode.mode & MODE_PRIVATE)
*mbuf++ = 'p';
if (chptr->mode.mode & MODE_MODERATED)
*mbuf++ = 'm';
if (chptr->mode.mode & MODE_TOPICLIMIT)
*mbuf++ = 't';
if (chptr->mode.mode & MODE_INVITEONLY)
*mbuf++ = 'i';
if (chptr->mode.mode & MODE_NOPRIVMSGS)
*mbuf++ = 'n';
if (chptr->mode.mode & MODE_REGISTERED)
*mbuf++ = 'r';
if (chptr->mode.mode & MODE_REGONLY)
*mbuf++ = 'R';
if (chptr->mode.mode & MODE_NOCTRL)
*mbuf++ = 'c';
if (chptr->mode.mode & MODE_OPERONLY)
*mbuf++ = 'O';
#ifdef HAVE_SSL
if (chptr->mode.mode & MODE_SSL)
*mbuf++ = 'S';
#endif
#ifdef ENABLE_CHANNEL_MODE_D
if (chptr->mode.mode & MODE_RSL)
*mbuf++ = 'D';
#endif
if (chptr->mode.mode & MODE_NONICK)
*mbuf++ = 'N';
if (chptr->mode.mode & MODE_MODREG)
*mbuf++ = 'M';
#ifdef USE_CHANMODE_L
if (chptr->mode.mode & MODE_LISTED)
*mbuf++ = 'L';
#endif
if (chptr->mode.limit)
{
*mbuf++ = 'l';
if (IsMember(cptr, chptr) || IsServer(cptr) || IsULine(cptr))
ircsprintf(pbuf, "%d", chptr->mode.limit);
}
if (*chptr->mode.key)
{
*mbuf++ = 'k';
if (IsMember(cptr, chptr) || IsServer(cptr) || IsULine(cptr))
{
if(pbuf[0] != '\0')
strcat(pbuf, " ");
strcat(pbuf, chptr->mode.key);
}
}
if (chptr->mode.mode & MODE_JOINRATE)
{
*mbuf++ = 'j';
if (IsMember(cptr, chptr) || IsServer(cptr) || IsULine(cptr))
{
char tmp[128];
if(pbuf[0] != '\0')
strcat(pbuf, " ");
if(chptr->mode.join_num == 0 || chptr->mode.join_time == 0)
ircsprintf(tmp, "0");
else
ircsprintf(tmp, "%d:%d", chptr->mode.join_num,
chptr->mode.join_time);
strcat(pbuf, tmp);
}
}
*mbuf++ = '\0';
return;
}
static void send_channel_lists(aClient *cptr, aChannel *chptr)
{
aBan *bp;
#ifdef EXEMPT_LISTS
aBanExempt *exempt;
#endif
#ifdef INVITE_LISTS
anInvite *inv;
#endif
char *cp;
int count = 0, send = 0;
cp = modebuf + strlen(modebuf);
if (*parabuf) /* mode +l or +k xx */
count = 1;
for (bp = chptr->banlist; bp; bp = bp->next)
{
if (strlen(parabuf) + strlen(bp->banstr) + 20 < (size_t) MODEBUFLEN)
{
if(*parabuf)
strcat(parabuf, " ");
strcat(parabuf, bp->banstr);
count++;
*cp++ = 'b';
*cp = '\0';
}
else if (*parabuf)
send = 1;
if (count == MAXTSMODEPARAMS)
send = 1;
if (send)
{
sendto_one(cptr, ":%s MODE %s %ld %s %s", me.name, chptr->chname,
chptr->channelts, modebuf, parabuf);
send = 0;
*parabuf = '\0';
cp = modebuf;
*cp++ = '+';
if (count != MAXTSMODEPARAMS)
{
strcpy(parabuf, bp->banstr);
*cp++ = 'b';
count = 1;
}
else
count = 0;
*cp = '\0';
}
}
#ifdef EXEMPT_LISTS
for (exempt = chptr->banexempt_list; exempt; exempt = exempt->next)
{
if (strlen(parabuf) + strlen(exempt->banstr) + 20 < (size_t)MODEBUFLEN)
{
if (*parabuf) strcat(parabuf, " ");
strcat(parabuf, exempt->banstr);
count++;
*cp++ = 'e';
*cp = 0;
}
else if (*parabuf)
send = 1;
if (count == MAXTSMODEPARAMS)
send = 1;
if (send)
{
sendto_one(cptr, ":%s MODE %s %ld %s %s", me.name, chptr->chname,
chptr->channelts, modebuf, parabuf);
send = 0;
*parabuf = 0;
cp = modebuf;
*cp++ = '+';
if (count != MAXTSMODEPARAMS)
{
strcpy(parabuf, exempt->banstr);
*cp++ = 'e';
count = 1;
}
else count = 0;
*cp = 0;
}
}
#endif
#ifdef INVITE_LISTS
for (inv = chptr->invite_list; inv; inv = inv->next)
{
if (strlen(parabuf) + strlen(inv->invstr) + 20 < (size_t)MODEBUFLEN)
{
if (*parabuf) strcat(parabuf, " ");
strcat(parabuf, inv->invstr);
count++;
*cp++ = 'I';
*cp = 0;
}
else if (*parabuf)
send = 1;
if (count == MAXTSMODEPARAMS)
send = 1;
if (send)
{
sendto_one(cptr, ":%s MODE %s %ld %s %s", me.name, chptr->chname,
chptr->channelts, modebuf, parabuf);
send = 0;
*parabuf = 0;
cp = modebuf;
*cp++ = '+';
if (count != MAXTSMODEPARAMS)
{
strcpy(parabuf, inv->invstr);
*cp++ = 'I';
count = 1;
}
else count = 0;
*cp = 0;
}
}
#endif
}
/* send "cptr" a full list of the modes for channel chptr. */
void send_channel_modes(aClient *cptr, aChannel *chptr)
{
chanMember *l, *anop = NULL, *skip = NULL;
int n = 0;
char *t;
if (*chptr->chname != '#')
return;
*modebuf = *parabuf = '\0';
channel_modes(cptr, modebuf, parabuf, chptr);
ircsprintf(buf, ":%s SJOIN %ld %s %s %s :", me.name,
chptr->channelts, chptr->chname, modebuf, parabuf);
t = buf + strlen(buf);
for (l = chptr->members; l; l = l->next)
if (l->flags & (MODE_CHANOP|MODE_HALFOP))
{
anop = l;
break;
}
/*
* follow the channel, but doing anop first if it's defined *
* -orabidoo
*/
l = NULL;
for (;;)
{
if (anop)
{
l = skip = anop;
anop = NULL;
}
else
{
if (l == NULL || l == skip)
l = chptr->members;
else
l = l->next;
if (l && l == skip)
l = l->next;
if (l == NULL)
break;
}
if (l->flags & MODE_CHANOP)
*t++ = '@';
if (l->flags & MODE_HALFOP)
*t++ = '%';
if (l->flags & MODE_VOICE)
*t++ = '+';
strcpy(t, l->cptr->name);
t += strlen(t);
*t++ = ' ';
n++;
if (t - buf > BUFSIZE - 80)
{
*t++ = '\0';
if (t[-1] == ' ')
t[-1] = '\0';
sendto_one(cptr, "%s", buf);
sprintf(buf, ":%s SJOIN %ld %s 0 :", me.name,
chptr->channelts, chptr->chname);
t = buf + strlen(buf);
n = 0;
}
}
if (n)
{
*t++ = '\0';
if (t[-1] == ' ')
t[-1] = '\0';
sendto_one(cptr, "%s", buf);
}
*parabuf = '\0';
*modebuf = '+';
modebuf[1] = '\0';
send_channel_lists(cptr, chptr);
if (modebuf[1] || *parabuf)
sendto_one(cptr, ":%s MODE %s %ld %s %s",
me.name, chptr->chname, chptr->channelts, modebuf, parabuf);
}
/* This opermode function needs a rewrite -Sheik 17/05/2005 */
static void do_opermode(aChannel *chptr, aClient *sptr, int i)
{
static int sent = 0;
if (!i) {
if (sent==0) {
sendto_ops("OperMode on Channel (%s), by %s. Modes: %s%s%s",
chptr->chname, sptr->name, modebuf, (*parabuf!=0 ? " " : ""), parabuf);
sent=1;
}
}
else
sent = 0;
}
int check_level(int level, int rlevel, aChannel *chptr, aClient *sptr)
{
if (level < rlevel && !IsSAdmin(sptr))
return 1;
else if (MyClient(sptr) && (level < rlevel) && IsSAdmin(sptr))
do_opermode(chptr,sptr,0);
return 0;
}
/* m_mode parv[0] - sender parv[1] - channel */
int m_mode(aClient *cptr, aClient *sptr, int parc, char *parv[])
{
int mcount = 0, chanop=0;
aChannel *chptr;
int subparc = 2;
/* Now, try to find the channel in question */
if (parc > 1)
{
chptr = find_channel(parv[1], NullChn);
if (chptr == NullChn)
return m_umode(cptr, sptr, parc, parv);
}
else
{
sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS),
me.name, parv[0], "MODE");
return 0;
}
if (!check_channelname(sptr, (unsigned char *) parv[1]))
return 0;
if (IsULine(sptr) && !MyClient(sptr))
chanop=4; /* extra speshul access */
else if (IsSAdmin(sptr) && !is_chan_op(sptr, chptr) && !is_halfop(sptr,chptr) && !IsULine(sptr))
chanop=3;
else if (is_chan_op(sptr, chptr) || (IsServer(sptr) && chptr->channelts!=0))
chanop=2;
else if (is_only_halfop(sptr,chptr))
chanop=1;
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->channelts);
return 0;
}
if(IsServer(cptr) && IsDigit(parv[2][0]))
{
ts_val modets = atol(parv[2]);
if(modets != 0 && (modets > chptr->channelts))
return 0;
subparc++;
}
mcount = set_mode(cptr, sptr, chptr, chanop, parc - subparc, parv + subparc,
modebuf, parabuf);
if (strlen(modebuf) > (size_t) 1)
switch (mcount)
{
case 0:
break;
case -1:
if (MyClient(sptr))
sendto_one(sptr,
err_str(ERR_CHANOPRIVSNEEDED),
me.name, parv[0], chptr->chname);
else
ircstp->is_fake++;
break;
default:
/* Les give this oper mode another try using the old code.. 17/05/2005 -Sheik
* This old format seems to be 95% corect -Sheik June 05 2005
*/
if (chanop == 3 && MyClient(sptr))
sendto_ops("OperMode on Channel (%s), by %s. Modes: %s%s%s",
chptr->chname, parv[0], modebuf, (*parabuf!=0 ? " " : ""), parabuf);
sendto_channel_butserv_me(chptr, sptr, ":%s MODE %s %s %s", parv[0],
chptr->chname, modebuf, parabuf);
sendto_serv_butone(cptr, ":%s MODE %s %ld %s %s", parv[0],
chptr->chname, chptr->channelts, modebuf, parabuf);
}
return 0;
}
/* the old set_mode was pissing me off with it's disgusting
* hackery, so I rewrote it. Hope this works. }:> --wd
* Corrected a 4-year-old mistake: the max modes limit applies to
* the number of parameters, not mode changes. -Quension [Apr 2004]
*/
static int set_mode(aClient *cptr, aClient *sptr, aChannel *chptr,
int level, int parc, char *parv[], char *mbuf, char *pbuf)
{
#define SM_ERR_NOPRIVS 0x0001 /* is not an op */
#define SM_ERR_MOREPARMS 0x0002 /* needs more parameters */
#define SM_ERR_RESTRICTED 0x0004 /* not allowed to op others or be op'd */
#define SM_ERR_NOTOPER 0x0008 /* not an irc op */
#ifdef HAVE_SSL
#define SM_ERR_SSL 0x0010 /* SSL Only*/
#endif
#ifdef ENABLE_CHANNEL_MODE_D
#define SM_ERR_RSL 0x0020 /* RSL(Resolvoled Clients) Only*/
#endif
#define SM_MAXMODES MAXMODEPARAMSUSER
/* this macro appends to pbuf */
#define ADD_PARA(p) pptr = p; if(pidx) pbuf[pidx++] = ' '; while(*pptr) \
pbuf[pidx++] = *pptr++;
static int flags[] =
{
MODE_PRIVATE, 'p', MODE_SECRET, 's',
MODE_MODERATED, 'm', MODE_NOPRIVMSGS, 'n',
MODE_TOPICLIMIT, 't', MODE_REGONLY, 'R',
MODE_INVITEONLY, 'i', MODE_NOCTRL, 'c', MODE_OPERONLY, 'O',
MODE_MODREG, 'M', MODE_NONICK, 'N',
#ifdef ENABLE_CHANNEL_MODE_D
MODE_RSL, 'D',
#endif
#ifdef HAVE_SSL
MODE_SSL, 'S',
#endif
#ifdef USE_CHANMODE_L
MODE_LISTED, 'L',
#endif
0x0, 0x0
};
Link *lp; /* for walking lists */
chanMember *cm; /* for walking channel member lists */
aBan *bp; /* for walking banlists */
char *modes=parv[0]; /* user's idea of mode changes */
int args; /* counter for what argument we're on */
int anylistsent = IsServer(sptr) ? 1 : 0; /* Only send 1 list and not to servers */
char change='+'; /* by default we + things... */
int errors=0; /*
* errors returned, set with bitflags
* so we only return them once
*/
/* from remote servers, ungodly numbers of modes can be sent, but
* from local users only SM_MAXMODES are allowed */
int maxparams=((IsServer(sptr) || IsULine(sptr)) ? 512 : SM_MAXMODES);
int nmodes=0; /* how many modes we've set so far */
int nparams=0; /* how many modes with parameters we've set so far */
aClient *who = NULL; /* who we're doing a mode for */
int chasing = 0;
int i=0;
char moreparmsstr[]="MODE ";
char nuhbuf[NICKLEN + USERLEN + HOSTLEN + 6]; /* for bans */
char tmp[128]; /* temporary buffer */
int pidx = 0; /* index into pbuf */
char *pptr; /* temporary paramater pointer */
char *morig = mbuf; /* beginning of mbuf */
/* :cptr-name MODE chptr->chname [MBUF] [PBUF] (buflen - 3 max and NULL) */
/* added another 11 bytes to this, for TSMODE -epi */
int prelen = strlen(cptr->name) + strlen(chptr->chname) + 27;
/* drop duplicates in the same mode change -- yeah, this is cheap, but real
duplicate checking will have to wait for a protocol change to kill
desyncs */
int seenalready = 0;
args=1;
if(parc<1)
return 0;
*mbuf++='+'; /* add the plus, even if they don't */
/* go through once to clean the user's mode string so we can
* have a simple parser run through it...*/
while(*modes)
{
switch(*modes)
{
case '+':
if(*(mbuf-1)=='-')
{
*(mbuf-1)='+'; /* change it around now */
change='+';
break;
}
else if(change=='+') /* we're still doing a +, we don't care */
break;
change=*modes;
*mbuf++='+';
break;
case '-':
if(*(mbuf-1)=='+')
{
*(mbuf-1)='-'; /* change it around now */
change='-';
break;
}
else if(change=='-')
break; /* we're still doing a -, we don't care */
change=*modes;
*mbuf++='-';
break;
#ifdef HAVE_SSL
case 'S':
if (!IsULine(sptr) && (check_level(level,1,chptr,sptr) || !IsUmodez(sptr)))
{
errors |= SM_ERR_SSL;
break;
}
else
{
if (change=='+')
chptr->mode.mode|=MODE_SSL;
else
chptr->mode.mode&=~MODE_SSL;
*mbuf++ = *modes;
nmodes++;
}
break;
#endif
/* Added by Gabriel Baez On May 23 2005.
* +D will prevent users who have been marke as unresolved clients, from joining the channel.
* This is one of my favorite modes. -Sheik June 05 2005.
*/
#ifdef ENABLE_CHANNEL_MODE_D
case 'D':
if (!IsULine(sptr) && (check_level(level,2,chptr,sptr) && IsURSL(sptr) && !IsOper(sptr)))
{
errors |= SM_ERR_RSL;
break;
}
else if (MyClient(sptr) && IsURSL(sptr) && !IsOper(sptr)) /* If you're not a oper, then stay out! -Sheik */
{
errors |= SM_ERR_RSL;
break;
}
else
{
if (change=='+')
chptr->mode.mode|=MODE_RSL;
else
chptr->mode.mode&=~MODE_RSL;
*mbuf++ = *modes;
nmodes++;
}
break;
#endif
case 'O':
if (!IsULine(sptr) && (level<2 || !IsOper(sptr)))
{
errors |= SM_ERR_NOTOPER;
break;
}
else if (MyClient(sptr) && !IsOper(sptr))
{
errors |= SM_ERR_NOTOPER;
break;
}
else
{
if (change=='+')
chptr->mode.mode|=MODE_OPERONLY;
else
chptr->mode.mode&=~MODE_OPERONLY;
*mbuf++ = *modes;
nmodes++;
}
break;
case 'o':
if (check_level(level,2,chptr,sptr))
{
errors |= SM_ERR_NOPRIVS;
break;
}
if(parv[args]==NULL)
{
/* silently drop the spare +o/v's */
break;
}
if(++nparams > maxparams)
{
/* too many modes with params, eat this one */
args++;
break;
}
who = find_chasing(sptr, parv[args], &chasing);
cm = find_user_member(chptr->members, who);
if(cm == NULL)
{
sendto_one(sptr, err_str(ERR_USERNOTINCHANNEL),
me.name, cptr->name, parv[args], chptr->chname);
/* swallow the arg */
args++;
break;
}
/* if we're going to overflow our mode buffer,
* drop the change instead */
if((prelen + (mbuf - morig) + pidx + NICKLEN + 1) >
REALMODEBUFLEN)
{
args++;
break;
}
/* if we have the user, set them +/-[vo] */
if(change=='+')
cm->flags|=CHFL_CHANOP;
else
cm->flags&=~CHFL_CHANOP;
/* we've decided their mode was okay, cool */
*mbuf++ = *modes;
ADD_PARA(cm->cptr->name)
args++;
nmodes++;
if (IsServer(sptr) && change=='+')
{
chptr->channelts = 0;
sendto_ops("Server %s setting +o and blasting TS on %s",
sptr->name, chptr->chname);
}
break;
case 'v':
if (check_level(level,1,chptr,sptr))
{
errors |= SM_ERR_NOPRIVS;
break;
}
if(parv[args]==NULL)
{
/* silently drop the spare +o/v's */
break;
}
if(++nparams > maxparams)
{
/* too many modes with params, eat this one */
args++;
break;
}
who = find_chasing(sptr, parv[args], &chasing);
cm = find_user_member(chptr->members, who);
if(cm == NULL)
{
sendto_one(sptr, err_str(ERR_USERNOTINCHANNEL),
me.name, cptr->name, parv[args], chptr->chname);
/* swallow the arg */
args++;
break;
}
/* if we're going to overflow our mode buffer,
* drop the change instead */
if((prelen + (mbuf - morig) + pidx + NICKLEN + 1) >
REALMODEBUFLEN)
{
args++;
break;
}
/* if we have the user, set them +/-[vo] */
if(change=='+')
cm->flags|=CHFL_VOICE;
else
cm->flags&=~CHFL_VOICE;
/* we've decided their mode was okay, cool */
*mbuf++ = *modes;
ADD_PARA(cm->cptr->name)
args++;
nmodes++;
break;
case 'h':
if(check_level(level,2,chptr,sptr))
{
errors |= SM_ERR_NOPRIVS;
break;
}
if(parv[args]==NULL)
{
/* silently drop the spare +o/v's */
break;
}
if(++nparams > maxparams)
{
/* too many modes with params, eat this one */
args++;
break;
}
who = find_chasing(sptr, parv[args], &chasing);
cm = find_user_member(chptr->members, who);
if(cm == NULL)
{
sendto_one(sptr, err_str(ERR_USERNOTINCHANNEL),
me.name, cptr->name, parv[args], chptr->chname);
/* swallow the arg */
args++;
break;
}
/* if we're going to overflow our mode buffer,
* drop the change instead */
if((prelen + (mbuf - morig) + pidx + NICKLEN + 1) >
REALMODEBUFLEN)
{
args++;
break;
}
/* if we have the user, set them +/-[vo] */
if(change=='+')
cm->flags |= (CHFL_HALFOP);
else
cm->flags &=~(CHFL_HALFOP);
/* we've decided their mode was okay, cool */
*mbuf++ = *modes;
ADD_PARA(cm->cptr->name)
args++;
nmodes++;
if (IsServer(sptr) && *modes == 'h' && change=='+')
{
chptr->channelts = 0;
sendto_ops("Server %s setting +h and blasting TS on %s",
sptr->name, chptr->chname);
}
break;
#ifdef INVITE_LISTS
case 'I':
if (check_level(level,1,chptr,sptr) && parv[args] != NULL)
{
errors |= SM_ERR_NOPRIVS;
break;
}
else if (parv[args] == NULL)
{
anInvite *invite;
if (anylistsent) /* don't send the list if they have received one */
break;
for (invite = chptr->invite_list; invite; invite = invite->next)
sendto_one(sptr, rpl_str(RPL_INVITELIST), me.name, cptr->name,
chptr->chname, invite->invstr, invite->who, invite->when);
sendto_one(cptr, rpl_str(RPL_ENDOFINVITELIST), me.name,
cptr->name, chptr->chname);
anylistsent = 1;
break;
}
if(++nparams > maxparams)
{
/* too many modes with params, eat this one */
args++;
break;
}
if (*parv[args] == ':' || *parv[args] == '\0')
{
args++;
break;
}
strcpy(nuhbuf, collapse(pretty_mask(parv[args])));
parv[args] = nuhbuf;
/* if we're going to overflow our mode buffer,
* drop the change instead */
if((prelen + (mbuf - morig) + pidx + strlen(nuhbuf) + 1) >
REALMODEBUFLEN)
{
args++;
break;
}
/* if we can't add or delete (depending) the ban, change is
* worthless anyhow */
if(!(change=='+' && !add_invite_id(sptr, chptr, parv[args])) &&
!(change=='-' && !del_invite_id(chptr, parv[args])))
{
args++;
break;
}
*mbuf++ = 'I';
ADD_PARA(parv[args])
args++;
nmodes++;
break;
#endif
#ifdef EXEMPT_LISTS
case 'e':
if (check_level(level,1,chptr,sptr) && parv[args] != NULL)
{
errors |= SM_ERR_NOPRIVS;
break;
}
else if (parv[args] == NULL)
{
aBanExempt* exempt;
if (anylistsent) /* don't send the list if they have received one */
break;
for (exempt = chptr->banexempt_list; exempt; exempt = exempt->next)
sendto_one(sptr, rpl_str(RPL_EXEMPTLIST), me.name, cptr->name,
chptr->chname, exempt->banstr, exempt->who, exempt->when);
sendto_one(cptr, rpl_str(RPL_ENDOFEXEMPTLIST), me.name,
cptr->name, chptr->chname);
anylistsent = 1;
break;
}
if(++nparams > maxparams)
{
/* too many modes with params, eat this one */
args++;
break;
}
if (*parv[args] == ':' || *parv[args] == '\0')
{
args++;
break;
}
strcpy(nuhbuf, collapse(pretty_mask(parv[args])));
parv[args] = nuhbuf;
/* if we're going to overflow our mode buffer,
* drop the change instead */
if((prelen + (mbuf - morig) + pidx + strlen(nuhbuf) + 1) >
REALMODEBUFLEN)
{
args++;
break;
}
/* if we can't add or delete (depending) the exempt, change is
* worthless anyhow */
if(!(change=='+' && !add_exempt_id(sptr, chptr, parv[args])) &&
!(change=='-' && !del_exempt_id(chptr, parv[args])))
{
args++;
break;
}
*mbuf++ = 'e';
ADD_PARA(parv[args])
args++;
nmodes++;
break;
#endif
case 'b':
/* if the user has no more arguments, then they just want
* to see the bans, okay, cool. */
if(check_level(level,1,chptr,sptr) && parv[args] != NULL)
{
errors |= SM_ERR_NOPRIVS;
break;
}
/* show them the bans, woowoo */
if(parv[args]==NULL)
{
if (anylistsent)
break;
for(bp=chptr->banlist;bp;bp=bp->next)
sendto_one(sptr, rpl_str(RPL_BANLIST), me.name, cptr->name,
chptr->chname, bp->banstr, bp->who, bp->when);
sendto_one(cptr, rpl_str(RPL_ENDOFBANLIST), me.name,
cptr->name, chptr->chname);
anylistsent = 1;
break; /* we don't pass this along, either.. */
}
if(++nparams > maxparams)
{
/* too many modes with params, eat this one */
args++;
break;
}
/* do not allow : in bans, or a null ban */
if(*parv[args]==':' || *parv[args] == '\0')
{
args++;
break;
}
/* make a 'pretty' ban mask here, then try and set it */
/* okay kids, let's do this again.
* the buffer returned by pretty_mask is from
* make_nick_user_host. This buffer is eaten by add/del banid.
* Thus, some poor schmuck gets himself on the banlist.
* Fixed. - lucas */
strcpy(nuhbuf, collapse(pretty_mask(parv[args])));
parv[args] = nuhbuf;
/* if we're going to overflow our mode buffer,
* drop the change instead */
if((prelen + (mbuf - morig) + pidx + strlen(nuhbuf) + 1) >
REALMODEBUFLEN)
{
args++;
break;
}
/* if we can't add or delete (depending) the ban, change is
* worthless anyhow */
if(!(change=='+' && !add_banid(sptr, chptr, parv[args])) &&
!(change=='-' && !del_banid(chptr, parv[args])))
{
args++;
break;
}
*mbuf++ = 'b';
ADD_PARA(parv[args])
args++;
nmodes++;
break;
case 'j':
#ifdef JOINRATE_SERVER_ONLY
if (MyClient(sptr))
{
sendto_one(sptr, err_str(ERR_ONLYSERVERSCANCHANGE),
me.name, cptr->name, chptr->chname);
break;
}
#endif
if(check_level(level,1,chptr,sptr))
{
errors |= SM_ERR_NOPRIVS;
break;
}
/* if it's a -, just change the flag, we have no arguments */
if(change=='-')
{
if (MyClient(sptr) && (seenalready & MODE_JOINRATE))
break;
seenalready |= MODE_JOINRATE;
if((prelen + (mbuf - morig) + pidx + 1) > REALMODEBUFLEN)
break;
*mbuf++ = 'j';
chptr->mode.mode &= ~MODE_JOINRATE;
chptr->mode.join_num = 0;
chptr->mode.join_time = 0;
chptr->join_start = 0;
chptr->join_count = 0;
nmodes++;
break;
}
else
{
char *tmpa, *tmperr;
int j_num, j_time, tval;
if(parv[args] == NULL)
{
errors|=SM_ERR_MOREPARMS;
break;
}
if(++nparams > maxparams)
{
/* too many modes with params, eat this one */
args++;
break;
}
if (MyClient(sptr) && (seenalready & MODE_JOINRATE))
{
args++;
break;
}
seenalready |= MODE_JOINRATE;
tmpa = strchr(parv[args], ':');
if(tmpa)
{
*tmpa = '\0';
tmpa++;
j_time = strtol(tmpa, &tmperr, 10);
if(*tmperr != '\0' || j_time < 0)
{
/* error, user specified something
* invalid, just bail. */
args++;
break;
}
}
else
j_time = 0;
j_num = strtol(parv[args], &tmperr, 10);
if(*tmperr != '\0' || j_num < 0)
{
args++;
break;
}
/* range limit for local non-samodes */
if (MyClient(sptr) && level < 2)
{
/* static limits: time <= 60, 4 <= num <= 60 */
if (j_time > 60)
j_time = 60;
if (j_num > 60)
j_num = 60;
if (j_num < 4)
j_num = 4;
/* adjust number to time using min rate 1/8 */
tval = (j_time-1)/8+1;
if (j_num < tval)
j_num = tval;
/* adjust time to number using max rate 2/1 */
tval = j_num/2;
if (j_time < tval)
j_time = tval;
}
if(j_num == 0 || j_time == 0)
{
j_num = j_time = 0;
ircsprintf(tmp, "0");
}
else
ircsprintf(tmp, "%d:%d", j_num, j_time);
/* if we're going to overflow our mode buffer,
* drop the change instead */
if((prelen + (mbuf - morig) + pidx + strlen(tmp)) > REALMODEBUFLEN)
{
args++;
break;
}
chptr->mode.mode |= MODE_JOINRATE;
chptr->mode.join_num = j_num;
chptr->mode.join_time = j_time;
chptr->join_start = 0;
chptr->join_count = 0;
*mbuf++ = 'j';
ADD_PARA(tmp);
args++;
nmodes++;
break;
}
case 'l':
if(check_level(level,1,chptr,sptr))
{
errors |= SM_ERR_NOPRIVS;
break;
}
/* if it's a -, just change the flag, we have no arguments */
if(change=='-')
{
if (MyClient(sptr) && (seenalready & MODE_LIMIT))
break;
seenalready |= MODE_LIMIT;
if((prelen + (mbuf - morig) + pidx + 1) > REALMODEBUFLEN)
break;
*mbuf++ = 'l';
chptr->mode.mode &= ~MODE_LIMIT;
chptr->mode.limit = 0;
nmodes++;
break;
}
else
{
if(parv[args] == NULL)
{
errors|=SM_ERR_MOREPARMS;
break;
}
if(++nparams > maxparams)
{
/* too many modes with params, eat this one */
args++;
break;
}
if (MyClient(sptr) && (seenalready & MODE_LIMIT))
{
args++;
break;
}
seenalready |= MODE_LIMIT;
/* if we're going to overflow our mode buffer,
* drop the change instead */
if((prelen + (mbuf - morig) + pidx + 16) > REALMODEBUFLEN)
{
args++;
break;
}
i = atoi(parv[args]);
/* toss out invalid modes */
if(i < 1)
{
args++;
break;
}
ircsprintf(tmp, "%d", i);
chptr->mode.limit = i;
chptr->mode.mode |= MODE_LIMIT;
*mbuf++ = 'l';
ADD_PARA(tmp);
args++;
nmodes++;
break;
}
case 'k':
if(check_level(level,1,chptr,sptr))
{
errors |= SM_ERR_NOPRIVS;
break;
}
if(parv[args]==NULL)
break;
if(++nparams > maxparams)
{
/* too many modes with params, eat this one */
args++;
break;
}
if (MyClient(sptr) && (seenalready & MODE_KEY))
{
args++;
break;
}
seenalready |= MODE_KEY;
/* do not allow keys to start with :! ack! - lucas */
/* another ack: don't let people set null keys! */
/* and yet a third ack: no spaces in keys -epi */
if(*parv[args]==':' || *parv[args] == '\0' ||
strchr(parv[args], ' '))
{
args++;
break;
}
/* Do not let *'s in keys in preperation for key hiding - Raist
* Also take out ",", which makes a channel unjoinable - lucas
*/
if (strchr(parv[args], '*') != NULL ||
strchr(parv[args], ',') != NULL)
{
args++;
break;
}
/* if we're going to overflow our mode buffer,
* drop the change instead */
if((prelen + (mbuf - morig) + pidx + KEYLEN+2) > REALMODEBUFLEN)
{
args++;
break;
}
/* if they're an op, they can futz with the key in
* any manner they like, we're not picky */
if(change=='+')
{
strncpy(chptr->mode.key,parv[args],KEYLEN);
ADD_PARA(chptr->mode.key)
}
else
{
char *sendkey = chptr->mode.key;
if (!*sendkey)
sendkey = parv[args];
ADD_PARA(sendkey)
*chptr->mode.key = '\0';
}
*mbuf++='k';
args++;
nmodes++;
break;
case 'r':
if (MyClient(sptr) && (seenalready & MODE_REGISTERED))
break;
seenalready |= MODE_REGISTERED;
if (!IsServer(sptr) && !IsULine(sptr))
{
sendto_one(sptr, err_str(ERR_ONLYSERVERSCANCHANGE),
me.name, cptr->name, chptr->chname);
break;
}
else
{
if((prelen + (mbuf - morig) + pidx + 1) > REALMODEBUFLEN)
break;
if(change=='+')
chptr->mode.mode|=MODE_REGISTERED;
else
chptr->mode.mode&=~MODE_REGISTERED;
}
*mbuf++='r';
nmodes++;
break;
case 'L':
if (MyClient(sptr) && (seenalready & MODE_LISTED))
break;
seenalready |= MODE_LISTED;
if (MyClient(sptr))
{
sendto_one(sptr, err_str(ERR_ONLYSERVERSCANCHANGE),
me.name, cptr->name, chptr->chname);
break;
}
else
{
if((prelen + (mbuf - morig) + pidx + 1) > REALMODEBUFLEN)
break;
if(change=='+')
chptr->mode.mode|=MODE_LISTED;
else
chptr->mode.mode&=~MODE_LISTED;
}
*mbuf++='L';
nmodes++;
break;
case 'i':
if(check_level(level,2,chptr,sptr))
{
errors |= SM_ERR_NOPRIVS;
break;
}
if(change=='-')
while ((lp=chptr->invites))
del_invite(lp->value.cptr, chptr);
/* fall through to default case */
default:
/* phew, no more tough modes. }:>, the rest are all
* covered in one step
* with the above array */
if(check_level(level,1,chptr,sptr))
{
errors |= SM_ERR_NOPRIVS;
break;
}
for(i=1;flags[i]!=0x0;i+=2)
{
if((prelen + (mbuf - morig) + pidx + 1) > REALMODEBUFLEN)
break;
if(*modes==(char)flags[i])
{
if (MyClient(sptr) && (seenalready & flags[i-1]))
break;
seenalready |= flags[i-1];
if(change=='+')
chptr->mode.mode |= flags[i-1];
else
chptr->mode.mode &= ~flags[i-1];
*mbuf++=*modes;
nmodes++;
break;
}
}
/* unknown mode.. */
if(flags[i]==0x0)
{
/* we still spew lots of unknown mode bits...*/
/* but only to our own clients, silently ignore bogosity
* from other servers... */
if(MyClient(sptr))
sendto_one(sptr, err_str(ERR_UNKNOWNMODE), me.name,
sptr->name, *modes);
}
break;
}
/* spit out more parameters error here */
if(errors & SM_ERR_MOREPARMS && MyClient(sptr))
{
moreparmsstr[5]=change;
moreparmsstr[6]=*modes;
sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name,
sptr->name, moreparmsstr);
errors &= ~SM_ERR_MOREPARMS; /* oops, kill it in this case */
}
modes++;
}
/* clean up the end of the string... */
if(*(mbuf-1) == '+' || *(mbuf-1) == '-')
*(mbuf-1) = '\0';
else
*mbuf = '\0';
pbuf[pidx] = '\0';
if(MyClient(sptr))
{
if(errors & SM_ERR_NOPRIVS)
sendto_one(sptr, err_str(ERR_CHANOPRIVSNEEDED), me.name,
sptr->name, chptr->chname);
if(errors & SM_ERR_NOTOPER)
sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, sptr->name);
if(errors & SM_ERR_RESTRICTED)
sendto_one(sptr,":%s NOTICE %s :*** Notice -- You are "
"restricted and cannot chanop others",
me.name, sptr->name);
#ifdef HAVE_SSL
if(errors & SM_ERR_SSL)
sendto_one(sptr,":%s NOTICE %s :*** Notice -- This channel has (+S) set, which means that is"
"restricted to SSL Connections only.",
me.name, sptr->name);
#endif
#ifdef ENABLE_CHANNEL_MODE_D
if(errors & SM_ERR_RSL)
sendto_one(sptr,":%s NOTICE %s :*** Notice -- Permission denied you may not set (+D) or join a (+D)"
"channel since you have been marked has an unresolved client.",
me.name, sptr->name);
#endif
}
/* all done! */
return nmodes;
#undef ADD_PARA
}
/* This function allows Admins & IRCops to walk bans etc... -Sheik 04/14/03 */
static int can_walk(aClient *sptr, aChannel *chptr) {
if (IsSAdmin(sptr)) {
*modebuf = *parabuf = '\0';
modebuf[1] = '\0';
channel_modes(sptr, modebuf, parabuf, chptr);
sendto_ops("OperWalk on Channel: (%s), by %s. Modes: (%s%s%s).",
chptr->chname, sptr->name, modebuf, (*parabuf!=0 ? " " : ""), parabuf);
return 1;
}
return 0;
}
static int can_join(aClient *sptr, aChannel *chptr, char *key)
{
Link *lp;
int invited = 0;
int error = 0;
char *r = NULL;
for(lp = sptr->user->invited; lp; lp = lp->next)
{
if(lp->value.chptr == chptr)
{
invited = 1;
break;
}
}
if (invited || IsULine(sptr))
return 1;
if (check_joinrate(chptr, NOW, 1, sptr) == 0)
{
r = "+j";
error = ERR_CHANNELISFULL;
}
else if (chptr->mode.mode & MODE_INVITEONLY)
{
r = "+i";
error = ERR_INVITEONLYCHAN;
}
else if (chptr->mode.mode & MODE_OPERONLY && !IsOper(sptr))
{
r = "+O";
error = ERR_INVITEONLYCHAN;
}
else if (chptr->mode.limit && chptr->users >= chptr->mode.limit)
{
r = "+l";
error = ERR_CHANNELISFULL;
}
else if (chptr->mode.mode & MODE_REGONLY && !IsRegNick(sptr))
error = ERR_NEEDREGGEDNICK;
else if (*chptr->mode.key && (BadPtr(key) || mycmp(chptr->mode.key, key)))
error = ERR_BADCHANNELKEY;
#ifdef HAVE_SSL
if (chptr->mode.mode & MODE_SSL && !IsUmodez(sptr))
error = ERR_SSL;
#endif
#ifdef ENABLE_CHANNEL_MODE_D
if (chptr->mode.mode & MODE_RSL && IsURSL(sptr))
error = ERR_RSL;
#endif
#ifdef INVITE_LISTS
if (error && is_invited(sptr, chptr))
error = 0;
#endif
if (!error && is_banned(sptr, chptr, NULL))
error = ERR_BANNEDFROMCHAN;
if (error && can_walk(sptr, chptr))
error = 0;
if (error)
{
if (error==ERR_NEEDREGGEDNICK)
sendto_one(sptr, getreply(ERR_NEEDREGGEDNICK), me.name, sptr->name,
chptr->chname, "join", aliastab[AII_NS].nick,
aliastab[AII_NS].server, NS_Register_URL);
else
sendto_one(sptr, getreply(error), me.name, sptr->name,
chptr->chname, r);
return 0;
}
return 1;
}
/*
* can_join_whynot:
* puts a list of the modes preventing us from joining in reasonbuf
* ret is number of matched modes
*/
static int
can_join_whynot(aClient *sptr, aChannel *chptr, char *key, char *reasonbuf)
{
Link *lp;
int invited = 0;
int rbufpos = 0;
for(lp = sptr->user->invited; lp; lp = lp->next)
{
if(lp->value.chptr == chptr)
{
invited = 1;
break;
}
}
if (invited || IsULine(sptr))
return 0;
if (chptr->mode.mode & MODE_INVITEONLY)
reasonbuf[rbufpos++] = 'i';
if (chptr->mode.mode & MODE_REGONLY && !IsRegNick(sptr))
reasonbuf[rbufpos++] = 'R';
if (chptr->mode.mode & MODE_OPERONLY && !IsOper(sptr))
reasonbuf[rbufpos++] = 'O';
if (*chptr->mode.key && (BadPtr(key) || mycmp(chptr->mode.key, key)))
reasonbuf[rbufpos++] = 'k';
if (chptr->mode.limit && chptr->users >= chptr->mode.limit)
reasonbuf[rbufpos++] = 'l';
if (check_joinrate(chptr, NOW, 1, sptr) == 0)
reasonbuf[rbufpos++] = 'j';
#ifdef INVITE_LISTS
if (rbufpos && is_invited(sptr, chptr))
rbufpos = 0;
#endif
if (is_banned(sptr, chptr, NULL))
reasonbuf[rbufpos++] = 'b';
reasonbuf[rbufpos] = '\0';
return rbufpos;
}
/*
* Remove bells and commas from channel name
*/
void clean_channelname(unsigned char *cn)
{
for (; *cn; cn++)
/*
* All characters >33 are allowed, except commas, and the weird
* fake-space character mIRCers whine about -wd
*/
if (*cn < 33 || *cn == ',' || (*cn == 160))
{
*cn = '\0';
return;
}
return;
}
/* we also tell the client if the channel is invalid. */
int check_channelname(aClient *cptr, unsigned char *cn)
{
if(!MyClient(cptr))
return 1;
for(;*cn;cn++)
{
if(*cn<33 || *cn == ',' || *cn==160)
{
sendto_one(cptr, getreply(ERR_BADCHANNAME), me.name, cptr->name,
cn);
return 0;
}
}
return 1;
}
/*
* * Get Channel block for chname (and allocate a new channel *
* block, if it didn't exist before).
*/
static aChannel *
get_channel(aClient *cptr, char *chname, int flag, int *created)
{
aChannel *chptr;
int len;
if(created)
*created = 0;
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 = make_channel();
if(created)
*created = 1;
strncpyzt(chptr->chname, chname, len + 1);
if (channel)
channel->prevch = chptr;
chptr->prevch = NULL;
chptr->nextch = channel;
channel = chptr;
chptr->channelts = timeofday;
(void) add_to_channel_hash_table(chname, chptr);
Count.chan++;
}
return chptr;
}
static 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);
*/
del_invite(cptr, cptr->user->invited->value.chptr);
}
/*
* add client to channel invite list
*/
inv = make_link();
inv->value.cptr = cptr;
inv->next = chptr->invites;
chptr->invites = inv;
/*
* add channel to the end of the client invite list
*/
for (tmp = &(cptr->user->invited); *tmp; tmp = &((*tmp)->next));
inv = make_link();
inv->value.chptr = chptr;
inv->next = NULL;
(*tmp) = 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).
*/
static void sub1_from_channel(aChannel *chptr)
{
Link *tmp;
aBan *bp, *bprem;
#ifdef INVITE_LISTS
anInvite *invite, *invrem;
#endif
#ifdef EXEMPT_LISTS
aBanExempt *exempt, *exrem;
#endif
if (--chptr->users <= 0)
{
/*
* Now, find all invite links from channel structure
*/
while ((tmp = chptr->invites))
del_invite(tmp->value.cptr, chptr);
bp = chptr->banlist;
while (bp)
{
bprem = bp;
bp = bp->next;
MyFree(bprem->banstr);
MyFree(bprem->who);
MyFree(bprem);
}
#ifdef INVITE_LISTS
invite = chptr->invite_list;
while (invite)
{
invrem = invite;
invite = invite->next;
MyFree(invrem->invstr);
MyFree(invrem->who);
MyFree(invrem);
}
#endif
#ifdef EXEMPT_LISTS
exempt = chptr->banexempt_list;
while (exempt)
{
exrem = exempt;
exempt = exempt->next;
MyFree(exrem->banstr);
MyFree(exrem->who);
MyFree(exrem);
}
#endif
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);
#ifdef FLUD
free_fluders(NULL, chptr);
#endif
free_channel(chptr);
Count.chan--;
}
}
/*
* m_join
* parv[0] = sender prefix
* parv[1] = channel
* parv[2] = channel password (key)
*/
int m_join(aClient *cptr, aClient *sptr, int parc, char *parv[])
{
static char jbuf[BUFSIZE];
Link *lp;
struct simBan *ban;
aChannel *chptr;
char *name, *key = NULL;
int i, flags = 0, chanlen=0;
int allow_op = YES;
char *p = NULL, *p2 = NULL;
#ifdef ANTI_SPAMBOT
int successful_join_count = 0;
/* Number of channels successfully joined */
#endif
/* Stop freezed and shunned users from joining channels 19/03/2006 -Sheik */
if (cptr->user && cptr->user->special_mode) {
switch (cptr->user->special_mode) {
case 3:
#ifdef ERROR_FREEZE_NOTICE
sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS),
me.name, parv[0], "JOIN");
#endif
return 0;
}
}
if IsShunned(sptr) {
#ifdef ERROR_FREEZE_NOTICE
sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS),
me.name, parv[0], "JOIN");
#endif
return 0;
}
if (!(sptr->user))
{
/* something is *fucked* - bail */
return 0;
}
if (parc < 2 || *parv[1] == '\0')
{
sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS),
me.name, parv[0], "JOIN");
return 0;
}
if (MyClient(sptr))
parv[1] = canonize(parv[1]);
*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, (char *) 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
*/
if(!check_channelname(sptr, (unsigned char *) name))
continue;
chanlen=strlen(name);
if (chanlen > CHANNELLEN) /* same thing is done in get_channel() */
{
name[CHANNELLEN] = '\0';
chanlen=CHANNELLEN;
}
if (*name == '0' && !atoi(name))
*jbuf = '\0';
else if (!IsChannelName(name))
{
if (MyClient(sptr))
sendto_one(sptr, err_str(ERR_NOSUCHCHANNEL),
me.name, parv[0], name);
continue;
}
if (*jbuf)
(void) strcat(jbuf, ",");
(void) strncat(jbuf, name, sizeof(jbuf) - i - 1);
i += chanlen + 1;
}
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))
{
if (sptr->user->channel == NULL)
continue;
while ((lp = sptr->user->channel))
{
chptr = lp->value.chptr;
sendto_channel_butserv(chptr, sptr, PartFmt,
parv[0], chptr->chname);
remove_user_from_channel(sptr, chptr);
}
/*
* Added /quote set for SPAMBOT
*
* int spam_time = MIN_JOIN_LEAVE_TIME; int spam_num =
* MAX_JOIN_LEAVE_COUNT;
*/
#ifdef ANTI_SPAMBOT /* Dianora */
if (MyConnect(sptr) && !IsAnOper(sptr))
{
if (sptr->join_leave_count >= spam_num)
{
sendto_realops_lev(SPAM_LEV, "User %s (%s@%s) is a "
"possible spambot", sptr->name,
sptr->user->username, sptr->user->host);
sptr->oper_warn_count_down = OPER_SPAM_COUNTDOWN;
}
else
{
int t_delta;
if ((t_delta = (NOW - sptr->last_leave_time)) >
JOIN_LEAVE_COUNT_EXPIRE_TIME)
{
int decrement_count;
decrement_count = (t_delta /
JOIN_LEAVE_COUNT_EXPIRE_TIME);
if (decrement_count > sptr->join_leave_count)
sptr->join_leave_count = 0;
else
sptr->join_leave_count -= decrement_count;
}
else
{
if ((NOW - (sptr->last_join_time)) < spam_time)
{
/* oh, its a possible spambot */
sptr->join_leave_count++;
}
}
sptr->last_leave_time = NOW;
}
}
#endif
sendto_serv_butone(cptr, ":%s JOIN 0", parv[0]);
continue;
}
if (MyConnect(sptr))
{
/* have we quarantined this channel? */
if(!IsOper(sptr) && (ban = check_mask_simbanned(name, SBAN_CHAN)))
{
sendto_one(sptr, getreply(ERR_CHANBANREASON), me.name, parv[0], name,
BadPtr(ban->reason) ? "Reserved channel" :
ban->reason);
sendto_realops_lev(REJ_LEV,
"Forbidding restricted channel %s from %s.",
name, get_client_name(cptr, TRUE));
continue;
}
/*
* local client is first to enter previously nonexistent *
* channel so make them (rightfully) the Channel * Operator.
*/
flags = (ChannelExists(name)) ? 0 : CHFL_CHANOP;
if (!IsAnOper(sptr) && server_was_split
&& !(confopts & FLAGS_SPLITOPOK))
allow_op = NO;
if ((sptr->user->joined >= maxchannelsperuser) &&
(!IsAnOper(sptr) || (sptr->user->joined >=
maxchannelsperuser * 3)))
{
sendto_one(sptr, err_str(ERR_TOOMANYCHANNELS),
me.name, parv[0], name);
#ifdef ANTI_SPAMBOT
if (successful_join_count)
sptr->last_join_time = NOW;
#endif
return 0;
}
#ifdef ANTI_SPAMBOT /*
* Dianora
*/
if (flags == 0) /* if channel doesn't exist, don't penalize */
successful_join_count++;
if (sptr->join_leave_count >= spam_num)
{
/* Its already known as a possible spambot */
if (sptr->oper_warn_count_down > 0) /* my general paranoia */
sptr->oper_warn_count_down--;
else
sptr->oper_warn_count_down = 0;
if (sptr->oper_warn_count_down == 0)
{
sendto_realops_lev(SPAM_LEV, "User %s (%s@%s) trying to "
"join %s is a possible spambot",
sptr->name,
sptr->user->username,
sptr->user->host,
name);
sptr->oper_warn_count_down = OPER_SPAM_COUNTDOWN;
}
# ifndef ANTI_SPAMBOT_WARN_ONLY
return 0; /* Don't actually JOIN anything, but
* don't let spambot know that */
# endif
}
#endif
}
else
{
/*
* complain for remote JOINs to existing channels * (they
* should be SJOINs) -orabidoo
*/
if (!ChannelExists(name))
ts_warn("User on %s remotely JOINing new channel",
sptr->user->server);
}
chptr = get_channel(sptr, name, CREATE, NULL);
if (chptr && IsMember(sptr, chptr))
continue;
if (!chptr || (MyConnect(sptr) && !can_join(sptr, chptr, key)))
{
#ifdef ANTI_SPAMBOT
if (successful_join_count > 0)
successful_join_count--;
#endif
continue;
}
/* only complain when the user can join the channel, the channel is
* being created by this user, and this user is not allowed to be an op.
* - lucas
*/
if (flags && !allow_op)
sendto_one(sptr, ":%s NOTICE %s :*** Notice -- Due to a network "
"split, you can not obtain channel operator status in "
"a new channel at this time.", me.name, sptr->name);
/* Complete user entry to the new channel (if any) */
if (allow_op)
add_user_to_channel(chptr, sptr, flags);
else
add_user_to_channel(chptr, sptr, 0);
chptr->join_count++;
/* Set timestamp if appropriate, and propagate */
if (MyClient(sptr) && flags == CHFL_CHANOP)
{
chptr->channelts = timeofday;
/* we keep channel "creations" to the server sjoin format,
so we can bounce modes and stuff if our ts is older. */
if (allow_op)
sendto_serv_butone(cptr, ":%s SJOIN %ld %s + :@%s", me.name,
chptr->channelts, name, parv[0]);
else
sendto_serv_butone(cptr, ":%s SJOIN %ld %s + :%s", me.name,
chptr->channelts, name, parv[0]);
}
else if (MyClient(sptr))
sendto_serv_butone(cptr, CliSJOINFmt, parv[0], chptr->channelts,
name);
else
sendto_serv_butone(cptr, ":%s JOIN :%s", parv[0], name);
/* notify all other users on the new channel */
sendto_channel_butserv(chptr, sptr, ":%s JOIN :%s", parv[0], name);
if (MyClient(sptr))
{
del_invite(sptr, chptr);
if (chptr->topic[0] != '\0')
{
sendto_one(sptr, rpl_str(RPL_TOPIC), me.name,
parv[0], name, chptr->topic);
sendto_one(sptr, rpl_str(RPL_TOPICWHOTIME),
me.name, parv[0], name,
chptr->topic_nick,
chptr->topic_time);
}
parv[1] = name;
(void) m_names(cptr, sptr, 2, parv);
}
}
#ifdef ANTI_SPAMBOT
if (MyConnect(sptr) && successful_join_count)
sptr->last_join_time = NOW;
#endif
return 0;
}
/* m_sajoin
* join a channel regardless of modes.
*/
int m_sajoin(aClient *cptr, aClient *sptr, int parc, char *parv[])
{
aChannel *chptr;
char *name;
int i;
char errmodebuf[128];
/* Remote sajoin? nope. */
if(!MyClient(sptr))
return 0;
if(!IsSAdmin(sptr))
{
sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
return 0;
}
if (parc < 2 || *parv[1] == '\0')
{
sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS),
me.name, parv[0], "SAJOIN");
return 0;
}
name = parv[1];
chptr = find_channel(name, NULL);
if(!chptr)
{
sendto_one(sptr, err_str(ERR_NOSUCHCHANNEL),
me.name, parv[0], name);
return 0;
}
/* bail if they're already in the channel */
if(IsMember(sptr, chptr))
return 0;
if((i = can_join_whynot(sptr, chptr, NULL, errmodebuf)))
{
send_globops("from %s: %s used SAJOIN (%s +%s)",
me.name, sptr->name, chptr->chname, errmodebuf);
sendto_serv_butone(NULL, ":%s GLOBOPS :%s used SAJOIN (%s +%s)",
me.name, sptr->name, chptr->chname, errmodebuf);
}
else
sendto_one(sptr, ":%s NOTICE %s :You didn't need to use"
" /SAJOIN for %s", me.name, parv[0], chptr->chname);
add_user_to_channel(chptr, sptr, 0);
sendto_serv_butone(cptr, CliSJOINFmt, parv[0], chptr->channelts, name);
sendto_channel_butserv(chptr, sptr, ":%s JOIN :%s", parv[0], name);
if(MyClient(sptr))
{
if(chptr->topic[0] != '\0')
{
sendto_one(sptr, rpl_str(RPL_TOPIC), me.name, parv[0],
name, chptr->topic);
sendto_one(sptr, rpl_str(RPL_TOPICWHOTIME), me.name, parv[0],
name, chptr->topic_nick, chptr->topic_time);
}
parv[1] = name;
parv[2] = NULL;
m_names(cptr, sptr, 2, parv);
}
return 0;
}
/*
* m_part
* parv[0] = sender prefix
* parv[1] = channel
* parv[2] = Optional part reason
*/
int m_part(aClient *cptr, aClient *sptr, int parc, char *parv[])
{
aChannel *chptr;
char *p, *name;
char *reason = (parc > 2 && parv[2]) ? parv[2] : NULL;
if (parc < 2 || parv[1][0] == '\0')
{
sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS),
me.name, parv[0], "PART");
return 0;
}
name = strtoken(&p, parv[1], ",");
/* Stop freezed & Shunned users from parting channels. 19/03/2006 -Sheik */
if (cptr->user && cptr->user->special_mode) {
switch (cptr->user->special_mode) {
case 3:
#ifdef ERROR_FREEZE_NOTICE
sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS),
me.name, parv[0], "PART");
#endif
return 0;
}
}
if IsShunned(sptr) {
#ifdef ERROR_FREEZE_NOTICE
sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS),
me.name, parv[0], "PART");
#endif
return 0;
}
#ifdef ANTI_SPAMBOT /* Dianora */
/* if its my client, and isn't an oper */
if (name && MyConnect(sptr) && !IsAnOper(sptr))
{
if (sptr->join_leave_count >= spam_num)
{
sendto_realops_lev(SPAM_LEV, "User %s (%s@%s) is a possible"
" spambot", sptr->name, sptr->user->username,
sptr->user->host);
sptr->oper_warn_count_down = OPER_SPAM_COUNTDOWN;
}
else
{
int t_delta;
if ((t_delta = (NOW - sptr->last_leave_time)) >
JOIN_LEAVE_COUNT_EXPIRE_TIME)
{
int decrement_count;
decrement_count = (t_delta / JOIN_LEAVE_COUNT_EXPIRE_TIME);
if (decrement_count > sptr->join_leave_count)
sptr->join_leave_count = 0;
else
sptr->join_leave_count -= decrement_count;
}
else
{
if ((NOW - (sptr->last_join_time)) < spam_time)
{
/* oh, its a possible spambot */
sptr->join_leave_count++;
}
}
sptr->last_leave_time = NOW;
}
}
#endif
while (name)
{
chptr = get_channel(sptr, name, 0, NULL);
if (!chptr)
{
sendto_one(sptr, err_str(ERR_NOSUCHCHANNEL),
me.name, parv[0], name);
name = strtoken(&p, (char *) NULL, ",");
continue;
}
if (!IsMember(sptr, chptr))
{
sendto_one(sptr, err_str(ERR_NOTONCHANNEL),
me.name, parv[0], name);
name = strtoken(&p, (char *) NULL, ",");
continue;
}
/* Remove user from the old channel (if any) */
if (parc < 3 || can_send(sptr,chptr,reason))
sendto_serv_butone(cptr, PartFmt, parv[0], name);
else
sendto_serv_butone(cptr, PartFmt2, parv[0], name, reason);
if (parc < 3 || can_send(sptr,chptr,reason))
sendto_channel_butserv(chptr, sptr, PartFmt, parv[0], name);
else
sendto_channel_butserv(chptr, sptr, PartFmt2, parv[0], name,
reason);
remove_user_from_channel(sptr, chptr);
name = strtoken(&p, (char *) NULL, ",");
}
return 0;
}
/*
* m_kick
* parv[0] = sender prefix
* parv[1] = channel
* parv[2] = client to kick
* parv[3] = kick comment
*/
int m_kick(aClient *cptr, aClient *sptr, int parc, char *parv[])
{
aClient *who;
aChannel *chptr;
int chasing = 0;
int user_count; /* count nicks being kicked, only allow 4 */
char *comment, *name, *p = NULL, *user, *p2 = NULL;
if (parc < 3 || *parv[1] == '\0')
{
sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS),
me.name, parv[0], "KICK");
return 0;
}
if (IsServer(sptr) && !IsULine(sptr))
sendto_ops("KICK from %s for %s %s",
parv[0], parv[1], parv[2]);
comment = (BadPtr(parv[3])) ? parv[0] : parv[3];
if (strlen(comment) > (size_t) TOPICLEN)
comment[TOPICLEN] = '\0';
*nickbuf = *buf = '\0';
name = strtoken(&p, parv[1], ",");
while (name)
{
chptr = get_channel(sptr, name, !CREATE, NULL);
if (!chptr)
{
sendto_one(sptr, err_str(ERR_NOSUCHCHANNEL),
me.name, parv[0], name);
name = strtoken(&p, (char *) NULL, ",");
continue;
}
/*
* You either have chan op privs, or you don't -Dianora
*
* orabidoo and I discussed this one for a while... I hope he
* approves of this code, users can get quite confused...
* -Dianora
*/
if (!IsServer(sptr) && !is_chan_op(sptr, chptr) && !is_halfop(sptr, chptr) && !IsULine(sptr))
{
/* was a user, not a server and user isn't seen as a chanop here */
if (MyConnect(sptr))
{
/* user on _my_ server, with no chanops.. so go away */
sendto_one(sptr, err_str(ERR_CHANOPRIVSNEEDED),
me.name, parv[0], chptr->chname);
name = strtoken(&p, (char *) NULL, ",");
continue;
}
if (chptr->channelts == 0)
{
/* If its a TS 0 channel, do it the old way */
sendto_one(sptr, err_str(ERR_CHANOPRIVSNEEDED),
me.name, parv[0], chptr->chname);
name = strtoken(&p, (char *) NULL, ",");
continue;
}
/*
* Its a user doing a kick, but is not showing as chanop
* locally its also not a user ON -my- server, and the channel
* has a TS. There are two cases we can get to this point
* then...
*
* 1) connect burst is happening, and for some reason a legit op
* has sent a KICK, but the SJOIN hasn't happened yet or been
* seen. (who knows.. due to lag...)
*
* 2) The channel is desynced. That can STILL happen with TS
*
* Now, the old code roger wrote, would allow the KICK to go
* through. Thats quite legit, but lets weird things like
* KICKS by users who appear not to be chanopped happen, or
* even neater, they appear not to be on the channel. This
* fits every definition of a desync, doesn't it? ;-) So I
* will allow the KICK, otherwise, things are MUCH worse. But
* I will warn it as a possible desync.
*
* -Dianora
*
* sendto_one(sptr, err_str(ERR_DESYNC), me.name, parv[0],
* chptr->chname);
*
* After more discussion with orabidoo...
*
* The code was sound, however, what happens if we have +h (TS4)
* and some servers don't understand it yet? we will be seeing
* servers with users who appear to have no chanops at all,
* merrily kicking users.... -Dianora
*
*/
}
user = strtoken(&p2, parv[2], ",");
user_count = 4;
while (user && user_count)
{
user_count--;
if (!(who = find_chasing(sptr, user, &chasing)))
{
user = strtoken(&p2, (char *) NULL, ",");
continue; /* No such user left! */
}
if (IsMember(who, chptr))
{
if (is_only_halfop(sptr, chptr) && (is_chan_op(who,chptr) || is_halfop(who,chptr)))
{
sendto_one (who,
":%s NOTICE %s :*** Notice -- %s attempted to kick you, however you are a channel operator",
me.name, who->name, sptr->name); // this will help out paranoid channel operators -Sheik.
sendto_one(sptr, ":%s NOTICE %s :*** Notice -- You cant kick %s, due to insufficient privileges",
me.name, parv[0], who->name);
}
else
{
sendto_channel_butserv(chptr, sptr,
":%s KICK %s %s :%s", parv[0],
name, who->name, comment);
sendto_serv_butone(cptr, ":%s KICK %s %s :%s", parv[0], name,
who->name, comment);
remove_user_from_channel(who, chptr);
}
}
else
sendto_one(sptr, err_str(ERR_USERNOTINCHANNEL),
me.name, parv[0], user, name);
user = strtoken(&p2, (char *) NULL, ",");
} /* loop on parv[2] */
name = strtoken(&p, (char *) NULL, ",");
} /* loop on parv[1] */
return (0);
}
int count_channels(aClient *sptr)
{
aChannel *chptr;
int count = 0;
for (chptr = channel; chptr; chptr = chptr->nextch)
count++;
return (count);
}
void send_topic_burst(aClient *cptr)
{
aChannel *chptr;
aClient *acptr;
if (!(confopts & FLAGS_SERVHUB) || !(cptr->serv->uflags & ULF_NOBTOPIC))
for (chptr = channel; chptr; chptr = chptr->nextch)
{
if(chptr->topic[0] != '\0')
sendto_one(cptr, ":%s TOPIC %s %s %ld :%s", me.name, chptr->chname,
chptr->topic_nick, chptr->topic_time, chptr->topic);
}
if (!(confopts & FLAGS_SERVHUB) || !(cptr->serv->uflags & ULF_NOAWAY))
for (acptr = client; acptr; acptr = acptr->next)
{
if(!IsPerson(acptr) || acptr->from == cptr)
continue;
if(acptr->user->away)
sendto_one(cptr, ":%s AWAY :%s", acptr->name, acptr->user->away);
}
}
/*
* m_topic
* parv[0] = sender prefix
* parv[1] = topic text
*/
int m_topic(aClient *cptr, aClient *sptr, int parc, char *parv[])
{
aChannel *chptr = NullChn;
char *topic = NULL, *name, *tnick = sptr->name;
time_t ts = timeofday;
int member;
if (parc < 2)
{
sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0],
"TOPIC");
return 0;
}
name = parv[1];
chptr = find_channel(name, NullChn);
if(!chptr)
{
sendto_one(sptr, err_str(ERR_NOSUCHCHANNEL), me.name, parv[0], name);
return 0;
}
member = IsMember(sptr, chptr);
if (parc == 2) /* user is requesting a topic */
{
char *namep = chptr->chname;
char tempchname[CHANNELLEN + 2];
if(!member && !(ShowChannel(sptr, chptr)))
{
if(IsAdmin(sptr))
{
tempchname[0] = '%';
strcpy(&tempchname[1], chptr->chname);
namep = tempchname;
}
else
{
sendto_one(sptr, err_str(ERR_NOTONCHANNEL), me.name, parv[0],
name);
return 0;
}
}
if (chptr->topic[0] == '\0')
sendto_one(sptr, rpl_str(RPL_NOTOPIC), me.name, parv[0], namep);
else
{
sendto_one(sptr, rpl_str(RPL_TOPIC), me.name, parv[0], namep,
chptr->topic);
sendto_one(sptr, rpl_str(RPL_TOPICWHOTIME), me.name, parv[0],
namep, chptr->topic_nick, chptr->topic_time);
}
return 0;
}
topic = parv[2];
if (MyClient(sptr))
{
if (!member)
{
sendto_one(sptr, err_str(ERR_NOTONCHANNEL), me.name, parv[0],name);
return 0;
}
if ((chptr->mode.mode & MODE_TOPICLIMIT) && !is_chan_op(sptr, chptr))
{
sendto_one(sptr, err_str(ERR_CHANOPRIVSNEEDED), me.name, parv[0],
chptr->chname);
return 0;
}
}
else
{
/* extended info */
if (parc > 3)
{
topic = (parc > 4 ? parv[4] : "");
tnick = parv[2];
ts = atoi(parv[3]);
}
/* ignore old topics during burst/race */
if (!IsULine(sptr) && chptr->topic[0] && chptr->topic_time >= ts)
return 0;
}
strncpyzt(chptr->topic, topic, TOPICLEN + 1);
strcpy(chptr->topic_nick, tnick);
chptr->topic_time = ts;
/* in this case I think it's better that we send all the info that df
* sends with the topic, so I changed everything to work like that.
* -wd */
sendto_serv_butone(cptr, ":%s TOPIC %s %s %lu :%s", parv[0],
chptr->chname, chptr->topic_nick, chptr->topic_time,
chptr->topic);
sendto_channel_butserv_me(chptr, sptr, ":%s TOPIC %s :%s", parv[0],
chptr->chname, chptr->topic);
return 0;
}
/*
* m_invite
* parv[0] - sender prefix
* parv[1] - user to invite
* parv[2] - channel name
*/
int m_invite(aClient *cptr, aClient *sptr, int parc, char *parv[])
{
aClient *acptr;
aChannel *chptr = NULL;
if (parc < 3 || *parv[1] == 0)
{
sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0],
"INVITE");
return -1;
}
if (!(acptr = find_person(parv[1], NULL)))
{
sendto_one(sptr, err_str(ERR_NOSUCHNICK), me.name, parv[0], parv[1]);
return 0;
}
if (MyClient(sptr))
{
if (!(chptr = find_channel(parv[2], NULL)))
{
sendto_one(sptr, err_str(ERR_NOSUCHCHANNEL), me.name, parv[0],
parv[2]);
return 0;
}
if (!IsMember(sptr, chptr))
{
sendto_one(sptr, err_str(ERR_NOTONCHANNEL), me.name, parv[0],
parv[2]);
return 0;
}
if (IsMember(acptr, chptr))
{
sendto_one(sptr, err_str(ERR_USERONCHANNEL), me.name, parv[0],
parv[1], chptr->chname);
return 0;
}
if (!is_chan_op(sptr, chptr))
{
sendto_one(sptr, err_str(ERR_CHANOPRIVSNEEDED), me.name, parv[0],
chptr->chname);
return 0;
}
sendto_one(sptr, rpl_str(RPL_INVITING), me.name, parv[0], acptr->name,
chptr->chname);
if (acptr->user->away)
sendto_one(sptr, rpl_str(RPL_AWAY), me.name, parv[0], acptr->name,
acptr->user->away);
}
if (MyClient(acptr))
{
/* stuff already done above */
if (!MyClient(sptr))
{
if (!(chptr = find_channel(parv[2], NullChn)))
return 0;
if (IsMember(acptr, chptr))
return 0;
}
add_invite(acptr, chptr);
sendto_prefix_one(acptr, sptr, ":%s INVITE %s :%s", parv[0],
acptr->name, chptr->chname);
sendto_channelflags_butone(NULL, &me, chptr, CHFL_CHANOP,
":%s NOTICE @%s :%s invited %s into "
"channel %s", me.name, chptr->chname,
parv[0], acptr->name, chptr->chname);
return 0;
}
sendto_one(acptr, ":%s INVITE %s :%s", parv[0], parv[1], parv[2]);
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
*/
void send_list(aClient *cptr, int numsend)
{
aChannel *chptr;
LOpts *lopt = cptr->user->lopt;
int hashnum;
char tempbuff[KEYLEN+8+3+1+3+6];
char modestuff[TOPICLEN+3+KEYLEN+8+3+1+6];
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) && !IsAnOper(cptr)
&& !IsMember(cptr, chptr))
continue;
#ifdef USE_CHANMODE_L
if (lopt->only_listed && !(chptr->mode.mode & MODE_LISTED))
continue;
#endif
if ((!lopt->showall) && ((chptr->users < lopt->usermin) ||
((lopt->usermax >= 0) &&
(chptr->users > lopt->usermax)) ||
((chptr->channelts) <
lopt->chantimemin) ||
(chptr->topic_time <
lopt->topictimemin) ||
(chptr->channelts >
lopt->chantimemax) ||
(chptr->topic_time >
lopt->topictimemax) ||
(lopt->nolist &&
find_str_link(lopt->nolist,
chptr->chname)) ||
(lopt->yeslist &&
!find_str_link(lopt->yeslist,
chptr->chname))))
continue;
/* Seem'd more efficent to seperate into two commands
* then adding an or to the inline. -- Doc.
*/
if (IsAnOper(cptr))
{
*modebuf = *parabuf = '\0';
modebuf[1] = '\0';
channel_modes(cptr, modebuf, parabuf, chptr);
if (SecretChannel(chptr) || HiddenChannel(chptr))
ircsprintf(tempbuff, "\002[%s %s]\002", modebuf, parabuf);
else
ircsprintf(tempbuff, "[%s %s]", modebuf, parabuf);
ircsprintf(modestuff, "%-20s %s", tempbuff, chptr->topic);
sendto_one(cptr, rpl_str(RPL_LIST), me.name, cptr->name,
chptr->chname, chptr->users, modestuff);
}
else
{
sendto_one(cptr, rpl_str(RPL_LIST), me.name, cptr->name,
ShowChannel(cptr, chptr) ? chptr->chname : "*",
chptr->users,
ShowChannel(cptr, chptr) ? chptr->topic : "");
}
numsend--;
}
}
else
break;
}
/* All done */
if (hashnum == CH_MAX)
{
Link *lp, *next;
sendto_one(cptr, rpl_str(RPL_LISTEND), me.name, cptr->name);
for (lp = lopt->yeslist; lp; lp = next)
{
next = lp->next;
MyFree(lp->value.cp);
free_link(lp);
}
for (lp = lopt->nolist; lp; lp = next)
{
next = lp->next;
MyFree(lp->value.cp);
free_link(lp);
}
MyFree(cptr->user->lopt);
cptr->user->lopt = NULL;
remove_from_list(&listing_clients, cptr, NULL);
return;
}
/*
* We've exceeded the limit on the number of channels to send back
* at once.
*/
lopt->starthash = hashnum;
return;
}
/* This code is by the fqircd team ( http://code.freequest.net/) credit goes to them.
* It has been modified slightly by Sheik 4/16/05
* m_dolist
* This function works as a wrapper for /LIST
* Previous versions of this code had two functions, one for /QLIST and one for /LIST
* Now we just call m_list directly from MSG_QLIST and MSG_LIST gets thrown into this wrapper
*/
#ifdef STRICT_LIST
int m_dolist(aClient *cptr, aClient *sptr, int parc, char *parv[])
{
/* If you're not in the bots class or using a registered nick, you'll get a message to use /QLIST instead */
char *class;
if(sptr->class)
class = sptr->class->name;
else
class = "NONE";
if(!bot_class)
bot_class = "unconfigured";
if(!StrEq(class, bot_class) && !IsOper(sptr) && !IsRegNick(sptr)) {
sendto_one(sptr, ":%s NOTICE %s :Usage of /LIST is restricted, identify for your nickname or use /QLIST instead.", me.name, parv[0]);
sendto_one(sptr, rpl_str(RPL_LISTEND), me.name, parv[0]);
return 0;
}
m_list(cptr,sptr,parc,parv);
return 0;
}
#endif
/*
* m_list
* parv[0] = sender prefix
* parv[1] = channel
*/
int m_list(aClient *cptr, aClient *sptr, int parc, char *parv[])
{
aChannel *chptr;
time_t currenttime = time(NULL);
char *name, *p = NULL;
LOpts *lopt = NULL;
Link *lp, *next;
int usermax, usermin, error = 0, doall = 0, only_listed = 1;
int x;
time_t chantimemin, chantimemax;
ts_val topictimemin, topictimemax;
Link *yeslist = NULL, *nolist = NULL;
static char *usage[] = {
"Usage: /raw LIST options (on mirc) or /quote LIST options (ircII)",
"",
"If you don't include any options, the default is to send you the",
"entire unfiltered list of channels. Below are the options you can",
"use, and what channels LIST will return when you use them.",
">number List channels with more than <number> people.",
"<number List channels with less than <number> people.",
"C>number List channels created between now and <number> minutes ago.",
"C<number List channels created earlier than <number> minutes ago.",
"T>number List channels whose topics are older than <number> minutes",
" (Ie, they have not changed in the last <number> minutes.",
"T<number List channels whose topics are not older than <number> "
"minutes.",
"*mask* List channels that match *mask*",
"!*mask* List channels that do not match *mask*",
NULL
};
/* Some starting san checks -- No interserver lists allowed. */
if (cptr != sptr || !sptr->user) return 0;
if (IsSquelch(sptr))
{
sendto_one(sptr, rpl_str(RPL_LISTEND), me.name, parv[0]);
return 0;
}
/* If a /list is in progress, then another one will cancel it */
if ((lopt = sptr->user->lopt)!=NULL)
{
sendto_one(sptr, rpl_str(RPL_LISTEND), me.name, parv[0]);
for (lp = lopt->yeslist; lp; lp = next)
{
next = lp->next;
free_link(lp);
}
for (lp = lopt->nolist; lp; lp = next)
{
next = lp->next;
free_link(lp);
}
MyFree(sptr->user->lopt);
sptr->user->lopt = NULL;
remove_from_list(&listing_clients, sptr, NULL);
return 0;
}
if (parc < 2 || BadPtr(parv[1]))
{
sendto_one(sptr, rpl_str(RPL_LISTSTART), me.name, parv[0]);
lopt = sptr->user->lopt = (LOpts *) MyMalloc(sizeof(LOpts));
memset(lopt, '\0', sizeof(LOpts));
lopt->showall = 1;
#ifdef USE_CHANMODE_L
lopt->only_listed = 1;
#endif
add_to_list(&listing_clients, sptr);
if (SBufLength(&cptr->sendQ) < 2048)
send_list(cptr, 64);
return 0;
}
if ((parc == 2) && (parv[1][0] == '?') && (parv[1][1] == '\0'))
{
char **ptr = usage;
for (; *ptr; ptr++)
sendto_one(sptr, rpl_str(RPL_COMMANDSYNTAX), me.name,
cptr->name, *ptr);
return 0;
}
sendto_one(sptr, rpl_str(RPL_LISTSTART), me.name, parv[0]);
chantimemax = topictimemax = currenttime + 86400;
chantimemin = topictimemin = 0;
usermin = 0;
usermax = -1; /* No maximum */
for (name = strtoken(&p, parv[1], ","); name && !error;
name = strtoken(&p, (char *) NULL, ","))
{
switch (*name)
{
case '<':
usermax = atoi(name+1) - 1;
doall = 1;
break;
case '>':
usermin = atoi(name+1) + 1;
doall = 1;
break;
#ifdef USE_CHANMODE_L
case '-':
if(!strcasecmp(++name,"all"))
{
only_listed = 0;
doall = 1;
}
break;
#endif
case 'C':
case 'c': /* Channel TS time -- creation time? */
++name;
switch (*name++)
{
case '<':
chantimemax = currenttime - 60 * atoi(name);
doall = 1;
break;
case '>':
chantimemin = currenttime - 60 * atoi(name);
doall = 1;
break;
default:
sendto_one(sptr, err_str(ERR_LISTSYNTAX), me.name,
cptr->name);
error = 1;
}
break;
case 'T':
case 't':
++name;
switch (*name++)
{
case '<':
topictimemax = currenttime - 60 * atoi(name);
doall = 1;
break;
case '>':
topictimemin = currenttime - 60 * atoi(name);
doall = 1;
break;
default:
sendto_one(sptr, err_str(ERR_LISTSYNTAX), me.name,
cptr->name);
error = 1;
}
break;
default: /* A channel, possibly with wildcards.
* Thought for the future: Consider turning wildcard
* processing on the fly.
* new syntax: !channelmask will tell ircd to ignore
* any channels matching that mask, and then
* channelmask will tell ircd to send us a list of
* channels only masking channelmask. Note: Specifying
* a channel without wildcards will return that
* channel even if any of the !channelmask masks
* matches it.
*/
if (*name == '!')
{
doall = 1;
lp = make_link();
lp->next = nolist;
nolist = lp;
DupString(lp->value.cp, name+1);
}
else if (strchr(name, '*') || strchr(name, '?'))
{
doall = 1;
lp = make_link();
lp->next = yeslist;
yeslist = lp;
DupString(lp->value.cp, name);
}
else /* Just a normal channel */
{
chptr = find_channel(name, NullChn);
if (chptr && ((x = ShowChannel(sptr, chptr)) ||
IsAdmin(sptr)))
{
char *nameptr = name;
char channame[CHANNELLEN + 2];
if(!x && IsAdmin(sptr))
{
channame[0] = '%';
strcpy(&channame[1], chptr->chname);
nameptr = channame;
}
sendto_one(sptr, rpl_str(RPL_LIST), me.name, parv[0],
nameptr, chptr->users, chptr->topic);
}
}
} /* switch */
} /* while */
if (doall)
{
lopt = sptr->user->lopt = (LOpts *) MyMalloc(sizeof(LOpts));
memset(lopt, '\0', sizeof(LOpts));
lopt->usermin = usermin;
lopt->usermax = usermax;
lopt->topictimemax = topictimemax;
lopt->topictimemin = topictimemin;
lopt->chantimemax = chantimemax;
lopt->chantimemin = chantimemin;
lopt->nolist = nolist;
lopt->yeslist = yeslist;
lopt->only_listed = only_listed;
add_to_list(&listing_clients, sptr);
if (SBufLength(&cptr->sendQ) < 2048)
send_list(cptr, 64);
return 0;
}
sendto_one(sptr, rpl_str(RPL_LISTEND), me.name, parv[0]);
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
*/
/* maximum names para to show to opers when abuse occurs */
#define TRUNCATED_NAMES 64
int m_names(aClient *cptr, aClient *sptr, int parc, char *parv[])
{
int mlen = strlen(me.name) + NICKLEN + 7;
aChannel *chptr;
aClient *acptr;
int member;
chanMember *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;
}
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;
}
}
if(!check_channelname(sptr, (unsigned char *)para))
return 0;
chptr = find_channel(para, (aChannel *) NULL);
if (!chptr || !ShowChannel(sptr, chptr))
{
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)
continue;
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)
{
Link *lp;
aChannel *chptr;
int cnt = 0, len = 0, clen;
char *mask;
*buf = ':';
(void) strcpy(buf + 1, user->name);
(void) strcat(buf, " JOIN ");
len = strlen(user->name) + 7;
for (lp = user->user->channel; lp; lp = lp->next)
{
chptr = lp->value.chptr;
if (*chptr->chname == '&')
continue;
if ((mask = strchr(chptr->chname, ':')))
if (match(++mask, cptr->name))
continue;
clen = strlen(chptr->chname);
if (clen > (size_t) BUFSIZE - 7 - len)
{
if (cnt)
sendto_one(cptr, "%s", buf);
*buf = ':';
(void) strcpy(buf + 1, user->name);
(void) strcat(buf, " JOIN ");
len = strlen(user->name) + 7;
cnt = 0;
}
(void) strcpy(buf + len, chptr->chname);
cnt++;
len += clen;
if (lp->next)
{
len++;
(void) strcat(buf, ",");
}
}
if (*buf && cnt)
sendto_one(cptr, "%s", buf);
return;
}
void kill_ban_list(aClient *cptr, aChannel *chptr)
{
void *pnx;
aBan *bp;
#ifdef EXEMPT_LISTS
aBanExempt *ep;
#endif
#ifdef INVITE_LISTS
anInvite *ip;
#endif
char *cp;
int count = 0, send = 0;
cp = modebuf;
*cp++ = '-';
*cp = '\0';
*parabuf = '\0';
for (bp = chptr->banlist; bp; bp = bp->next)
{
if (strlen(parabuf) + strlen(bp->banstr) + 10 < (size_t) MODEBUFLEN)
{
if(*parabuf)
strcat(parabuf, " ");
strcat(parabuf, bp->banstr);
count++;
*cp++ = 'b';
*cp = '\0';
}
else if (*parabuf)
send = 1;
if (count == MAXMODEPARAMS)
send = 1;
if (send) {
sendto_channel_butserv_me(chptr, cptr, ":%s MODE %s %s %s",
cptr->name, chptr->chname, modebuf, parabuf);
send = 0;
*parabuf = '\0';
cp = modebuf;
*cp++ = '-';
if (count != MAXMODEPARAMS)
{
strcpy(parabuf, bp->banstr);
*cp++ = 'b';
count = 1;
}
else
count = 0;
*cp = '\0';
}
}
#ifdef EXEMPT_LISTS
for (ep = chptr->banexempt_list; ep; ep = ep->next)
{
if (strlen(parabuf) + strlen(ep->banstr) + 10 < (size_t) MODEBUFLEN)
{
if(*parabuf)
strcat(parabuf, " ");
strcat(parabuf, ep->banstr);
count++;
*cp++ = 'e';
*cp = '\0';
}
else if (*parabuf)
send = 1;
if (count == MAXMODEPARAMS)
send = 1;
if (send) {
sendto_channel_butserv_me(chptr, cptr, ":%s MODE %s %s %s",
cptr->name, chptr->chname, modebuf, parabuf);
send = 0;
*parabuf = '\0';
cp = modebuf;
*cp++ = '-';
if (count != MAXMODEPARAMS)
{
strcpy(parabuf, ep->banstr);
*cp++ = 'e';
count = 1;
}
else
count = 0;
*cp = '\0';
}
}
#endif
#ifdef INVITE_LISTS
for (ip = chptr->invite_list; ip; ip = ip->next)
{
if (strlen(parabuf) + strlen(ip->invstr) + 10 < (size_t) MODEBUFLEN)
{
if(*parabuf)
strcat(parabuf, " ");
strcat(parabuf, ip->invstr);
count++;
*cp++ = 'I';
*cp = '\0';
}
else if (*parabuf)
send = 1;
if (count == MAXMODEPARAMS)
send = 1;
if (send) {
sendto_channel_butserv_me(chptr, cptr, ":%s MODE %s %s %s",
cptr->name, chptr->chname, modebuf, parabuf);
send = 0;
*parabuf = '\0';
cp = modebuf;
*cp++ = '-';
if (count != MAXMODEPARAMS)
{
strcpy(parabuf, ip->invstr);
*cp++ = 'I';
count = 1;
}
else
count = 0;
*cp = '\0';
}
}
#endif
if(*parabuf)
{
sendto_channel_butserv_me(chptr, cptr, ":%s MODE %s %s %s", cptr->name,
chptr->chname, modebuf, parabuf);
}
/* physically destroy channel ban list */
bp = chptr->banlist;
while(bp)
{
pnx = bp->next;
MyFree(bp->banstr);
MyFree(bp->who);
MyFree(bp);
bp = pnx;
}
chptr->banlist = NULL;
#ifdef EXEMPT_LISTS
ep = chptr->banexempt_list;
while(ep)
{
pnx = ep->next;
MyFree(ep->banstr);
MyFree(ep->who);
MyFree(ep);
ep = pnx;
}
chptr->banexempt_list = NULL;
#endif
#ifdef INVITE_LISTS
ip = chptr->invite_list;
while(ip)
{
pnx = ip->next;
MyFree(ip->invstr);
MyFree(ip->who);
MyFree(ip);
ip = pnx;
}
chptr->invite_list = NULL;
#endif
/* reset bquiet cache */
chptr->banserial++;
}
static inline void sjoin_sendit(aClient *cptr, aClient *sptr,
aChannel *chptr, char *from)
{
sendto_channel_butserv_me(chptr, sptr, ":%s MODE %s %s %s", from,
chptr->chname, modebuf, parabuf);
}
/* m_resynch
*
* parv[0] = sender
* parv[1] = #channel
*
* Sent from a server I am directly connected to that is requesting I resend
* EVERYTHING I know about #channel.
*/
int m_resynch(aClient *cptr, aClient *sptr, int parc, char *parv[])
{
aChannel *chptr;
if(!MyConnect(sptr) || !IsServer(sptr) || parc < 2)
return 0;
chptr = find_channel(parv[1], NullChn);
sendto_realops_lev(DEBUG_LEV, "%s is requesting a resynch of %s%s",
parv[0], parv[1], (chptr == NullChn) ? " [failed]" : "");
if (chptr != NullChn)
send_channel_modes(sptr, chptr);
return 0;
}
/*
* m_sjoin
* parv[0] - sender
* parv[1] - TS
* parv[2] - channel
* parv[3] - modes + n arguments (key and/or limit)
* parv[4+n] - flags+nick list (all in one parameter)
*
* process a SJOIN, taking the TS's into account to either ignore the
* incoming modes or undo the existing ones or merge them, and JOIN all
* the specified users while sending JOIN/MODEs to non-TS servers and
* to clients
*/
#define INSERTSIGN(x,y) \
if (what != x) { \
*mbuf++=y; \
what = x; \
}
#define SJ_MODEPLUS(x, y) \
if(((y) & mode.mode) && !((y) & oldmode->mode)) \
{ \
INSERTSIGN(1, '+') \
*mbuf++ = (x); \
}
#define SJ_MODEMINUS(x, y) \
if(((y) & oldmode->mode) && !((y) & mode.mode)) \
{ \
INSERTSIGN(-1, '-') \
*mbuf++ = (x); \
}
#define SJ_MODEADD(x, y) case (x): mode.mode |= (y); break
#define ADD_PARA(p) para = p; if(pbpos) parabuf[pbpos++] = ' '; \
while(*para) parabuf[pbpos++] = *para++;
#define ADD_SJBUF(p) para = p; if(sjbufpos) sjbuf[sjbufpos++] = ' '; \
while(*para) sjbuf[sjbufpos++] = *para++;
int m_sjoin(aClient *cptr, aClient *sptr, int parc, char *parv[])
{
aChannel *chptr;
aClient *acptr;
ts_val newts, oldts, tstosend;
static Mode mode, *oldmode;
chanMember *cm;
int args = 0, haveops = 0, keepourmodes = 1, keepnewmodes = 1,
doesop = 0, what = 0, pargs = 0, fl, people = 0,
isnew, clientjoin = 0, pbpos, sjbufpos, created = 0;
char *s, *s0, *para;
static char numeric[16], sjbuf[BUFSIZE];
char keep_modebuf[REALMODEBUFLEN], keep_parabuf[REALMODEBUFLEN];
char *mbuf = modebuf, *p;
/* if my client is SJOINing, it's just a local user being a dufus.
* Ignore him.
* parc >= 5 (new serv<->serv SJOIN format)
* parc >= 6 (old serv<->serv SJOIN format)
* parc == 3 (new serv<->serv cliSJOIN format)
*/
if (MyClient(sptr) || (parc < 5 && IsServer(sptr)) ||
(parc < 3 && IsPerson(sptr)))
return 0;
if(parc == 3 && IsPerson(sptr))
clientjoin = 1;
else
if(IsDigit(parv[2][0]))
{
int i;
if(parc < 6)
return 0;
for(i = 2; i < (parc - 1); i++)
parv[i] = parv[i+1];
parc--;
}
if (!IsChannelName(parv[2]))
return 0;
newts = atol(parv[1]);
isnew = ChannelExists(parv[2]) ? 0 : 1;
chptr = get_channel(sptr, parv[2], CREATE, &created);
oldts = chptr->channelts;
for (cm = chptr->members; cm; cm = cm->next)
if (cm->flags & MODE_CHANOP)
{
haveops++;
break;
}
if(clientjoin) /* we have a good old client sjoin, with timestamp */
{
if (isnew)
chptr->channelts = tstosend = newts;
else if (newts == 0 || oldts == 0)
chptr->channelts = tstosend = 0;
else if (newts == oldts)
tstosend = oldts;
else if (newts < oldts)
{
#ifdef OLD_WEIRD_CHANOP_NEGOTIATION
if (haveops)
tstosend = oldts;
else
chptr->channelts = tstosend = newts;
#else
chptr->channelts = tstosend = newts;
if (!IsULine(sptr))
sendto_realops_lev(DEBUG_LEV, "Changing TS for %s from %d to %d on"
" client SJOIN", chptr->chname, oldts, newts);
#endif
}
else
tstosend = oldts;
/* parv[0] is the client that is joining. parv[0] == sptr->name */
if (!IsMember(sptr, chptr))
{
add_user_to_channel(chptr, sptr, 0);
chptr->join_count++;
chptr->default_join_count++;
sendto_channel_butserv(chptr, sptr, ":%s JOIN :%s", parv[0],
parv[2]);
}
sendto_serv_butone(cptr, CliSJOINFmt, parv[0], tstosend, parv[2]);
/* if the channel is created in client sjoin,
* we lost some channel modes. */
if(created)
{
sendto_realops_lev(DEBUG_LEV, "Requesting resynch of %s from "
"%s (%s!%s@%s[%s] created)", chptr->chname,
cptr->name, sptr->name, sptr->user->username,
sptr->user->host, sptr->hostip);
sendto_one(cptr, "RESYNCH %s", chptr->chname);
}
return 0;
}
memset((char *) &mode, '\0', sizeof(mode));
s = parv[3];
while (*s)
{
switch (*(s++))
{
SJ_MODEADD('i', MODE_INVITEONLY);
SJ_MODEADD('n', MODE_NOPRIVMSGS);
SJ_MODEADD('p', MODE_PRIVATE);
SJ_MODEADD('s', MODE_SECRET);
SJ_MODEADD('m', MODE_MODERATED);
SJ_MODEADD('t', MODE_TOPICLIMIT);
SJ_MODEADD('r', MODE_REGISTERED);
SJ_MODEADD('R', MODE_REGONLY);
SJ_MODEADD('M', MODE_MODREG);
SJ_MODEADD('c', MODE_NOCTRL);
SJ_MODEADD('O', MODE_OPERONLY);
#ifdef HAVE_SSL
SJ_MODEADD('S', MODE_SSL);
#endif
#ifdef ENABLE_CHANNEL_MODE_D
SJ_MODEADD('D', MODE_RSL);
#endif
SJ_MODEADD('N', MODE_NONICK);
#ifdef USE_CHANMODE_L
SJ_MODEADD('L', MODE_LISTED);
#endif
case 'k':
strncpyzt(mode.key, parv[4 + args], KEYLEN + 1);
args++;
if (parc < 5 + args)
return 0;
break;
case 'j':
{
char *tmpa, *tmpb;
mode.mode |= MODE_JOINRATE;
tmpa = parv[4 + args];
tmpb = strchr(tmpa, ':');
if(tmpb)
{
*tmpb = '\0';
tmpb++;
mode.join_time = atoi(tmpb);
}
else
mode.join_time = 0;
mode.join_num = atoi(tmpa);
args++;
if (parc < 5 + args)
return 0;
}
break;
case 'l':
mode.limit = atoi(parv[4 + args]);
args++;
if (parc < 5 + args)
return 0;
break;
}
}
doesop = (parv[4 + args][0] == '@' || parv[4 + args][1] == '@');
oldmode = &chptr->mode;
/* newts is the ts the remote server is providing */
/* oldts is our channel TS */
/* whichever TS is smaller wins. */
if (isnew)
chptr->channelts = tstosend = newts;
else if (newts == 0 || oldts == 0)
chptr->channelts = tstosend = 0;
else if (newts == oldts)
tstosend = oldts;
#ifdef OLD_WEIRD_CHANOP_NEGOTIATION
else if (newts < oldts)
{
/* if remote ts is older, and they have ops, don't keep our modes. */
if (doesop)
{
kill_ban_list(sptr, chptr);
keepourmodes = 0;
}
if (haveops && !doesop)
tstosend = oldts;
else
chptr->channelts = tstosend = newts;
}
else /* if our TS is older, and we have ops, don't keep their modes */
{
if (haveops)
keepnewmodes = 0;
if (doesop && !haveops)
{
chptr->channelts = tstosend = newts;
if (MyConnect(sptr) && !IsULine(sptr))
ts_warn("Hacked ops on opless channel: %s", chptr->chname);
}
else
tstosend = oldts;
}
#else
else if (newts < oldts)
{
/* if remote ts is older, don't keep our modes. */
kill_ban_list(sptr, chptr);
keepourmodes = 0;
chptr->channelts = tstosend = newts;
}
else /* if our TS is older, don't keep their modes */
{
keepnewmodes = 0;
tstosend = oldts;
}
#endif
if (!keepnewmodes)
mode = *oldmode;
else if (keepourmodes)
{
/* check overriding modes first */
if (oldmode->limit > mode.limit)
mode.limit = oldmode->limit;
if(*oldmode->key && *mode.key && strcmp(mode.key, oldmode->key) > 0)
strcpy(mode.key, oldmode->key);
else if(*oldmode->key && *mode.key == '\0')
strcpy(mode.key, oldmode->key);
if (oldmode->mode & MODE_JOINRATE)
{
if ((mode.mode & MODE_JOINRATE) && !mode.join_num)
/* 0 wins */ ;
else if (oldmode->join_num && mode.join_num > oldmode->join_num)
/* more joins wins */ ;
else if (mode.join_num == oldmode->join_num &&
mode.join_time < oldmode->join_time)
/* same joins in less time wins */ ;
else
{
/* our settings win */
mode.join_num = oldmode->join_num;
mode.join_time = oldmode->join_time;
}
}
/* now merge */
mode.mode |= oldmode->mode;
}
pbpos = 0;
/*
* since the most common case is that the modes are exactly the same,
* this if will skip over the most common case... :)
*
* this would look prettier in a for loop, but it's unrolled here
* so it's a bit faster. - lucas
*
* pass +: go through and add new modes that are in mode and not oldmode
* pass -: go through and delete old modes that are in oldmode and not mode
*/
if(mode.mode != oldmode->mode)
{
SJ_MODEPLUS('p', MODE_PRIVATE);
SJ_MODEPLUS('s', MODE_SECRET);
SJ_MODEPLUS('m', MODE_MODERATED);
SJ_MODEPLUS('n', MODE_NOPRIVMSGS);
SJ_MODEPLUS('t', MODE_TOPICLIMIT);
SJ_MODEPLUS('i', MODE_INVITEONLY);
SJ_MODEPLUS('r', MODE_REGISTERED);
SJ_MODEPLUS('R', MODE_REGONLY);
SJ_MODEPLUS('M', MODE_MODREG);
SJ_MODEPLUS('c', MODE_NOCTRL);
SJ_MODEPLUS('O', MODE_OPERONLY);
#ifdef HAVE_SSL
SJ_MODEPLUS('S', MODE_SSL);
#endif
#ifdef ENABLE_CHANNEL_MODE_D
SJ_MODEPLUS('D', MODE_RSL);
#endif
SJ_MODEPLUS('N', MODE_NONICK);
#ifdef USE_CHANMODE_L
SJ_MODEPLUS('L', MODE_LISTED);
#endif
SJ_MODEMINUS('p', MODE_PRIVATE);
SJ_MODEMINUS('s', MODE_SECRET);
SJ_MODEMINUS('m', MODE_MODERATED);
SJ_MODEMINUS('n', MODE_NOPRIVMSGS);
SJ_MODEMINUS('t', MODE_TOPICLIMIT);
SJ_MODEMINUS('i', MODE_INVITEONLY);
SJ_MODEMINUS('r', MODE_REGISTERED);
SJ_MODEMINUS('R', MODE_REGONLY);
SJ_MODEMINUS('M', MODE_MODREG);
SJ_MODEMINUS('c', MODE_NOCTRL);
SJ_MODEMINUS('O', MODE_OPERONLY);
#ifdef HAVE_SSL
SJ_MODEMINUS('S', MODE_SSL);
#endif
#ifdef ENABLE_CHANNEL_MODE_D
SJ_MODEMINUS('D', MODE_RSL);
#endif
SJ_MODEMINUS('N', MODE_NONICK);
#ifdef USE_CHANMODE_L
SJ_MODEMINUS('L', MODE_LISTED);
#endif
}
if ((oldmode->mode & MODE_JOINRATE) && !(mode.mode & MODE_JOINRATE))
{
INSERTSIGN(-1,'-')
*mbuf++ = 'j';
}
if ((mode.mode & MODE_JOINRATE) && (!(oldmode->mode & MODE_JOINRATE) ||
(oldmode->join_num != mode.join_num ||
oldmode->join_time != mode.join_time)))
{
char tmp[128];
INSERTSIGN(1,'+')
*mbuf++ = 'j';
if(mode.join_num == 0 || mode.join_time == 0)
ircsprintf(tmp, "0");
else
ircsprintf(tmp, "%d:%d", mode.join_num, mode.join_time);
ADD_PARA(tmp)
pargs++;
}
if (oldmode->limit && !mode.limit)
{
INSERTSIGN(-1,'-')
*mbuf++ = 'l';
}
if (mode.limit && oldmode->limit != mode.limit)
{
INSERTSIGN(1,'+')
*mbuf++ = 'l';
sprintf(numeric, "%-15d", mode.limit);
if ((s = strchr(numeric, ' ')))
*s = '\0';
ADD_PARA(numeric);
pargs++;
}
if (oldmode->key[0] && !mode.key[0])
{
INSERTSIGN(-1,'-')
*mbuf++ = 'k';
ADD_PARA(oldmode->key)
pargs++;
}
if (mode.key[0] && strcmp(oldmode->key, mode.key))
{
INSERTSIGN(1,'+')
*mbuf++ = 'k';
ADD_PARA(mode.key)
pargs++;
}
chptr->mode = mode;
if (!keepourmodes) /* deop and devoice everyone! */
{
what = 0;
for (cm = chptr->members; cm; cm = cm->next)
{
if (cm->flags & MODE_CHANOP)
{
INSERTSIGN(-1,'-')
*mbuf++ = 'o';
ADD_PARA(cm->cptr->name)
pargs++;
if (pargs >= MAXMODEPARAMS)
{
*mbuf = '\0';
parabuf[pbpos] = '\0';
sjoin_sendit(cptr, sptr, chptr, parv[0]);
mbuf = modebuf;
*mbuf = '\0';
pargs = pbpos = what = 0;
}
cm->flags &= ~MODE_CHANOP;
}
if (cm->flags & MODE_HALFOP)
{
INSERTSIGN(-1,'-')
*mbuf++ = 'h';
ADD_PARA(cm->cptr->name)
pargs++;
if (pargs >= MAXMODEPARAMS)
{
*mbuf = '\0';
parabuf[pbpos] = '\0';
sjoin_sendit(cptr, sptr, chptr, parv[0]);
mbuf = modebuf;
*mbuf = '\0';
pargs = pbpos = what = 0;
}
cm->flags &= ~MODE_HALFOP;
}
if (cm->flags & MODE_VOICE)
{
INSERTSIGN(-1,'-')
*mbuf++ = 'v';
ADD_PARA(cm->cptr->name)
pargs++;
if (pargs >= MAXMODEPARAMS)
{
*mbuf = '\0';
parabuf[pbpos] = '\0';
sjoin_sendit(cptr, sptr, chptr, parv[0]);
mbuf = modebuf;
*mbuf = '\0';
pargs = pbpos = what = 0;
}
cm->flags &= ~MODE_VOICE;
}
}
/*
* We know if we get here, and we're in a sync, we haven't sent our topic
* to sptr yet. (since topic burst is sent after sjoin burst finishes)
*/
if(chptr->topic[0])
{
chptr->topic[0] = '\0';
sendto_channel_butserv_me(chptr, sptr, ":%s TOPIC %s :%s",
sptr->name, chptr->chname, chptr->topic);
}
sendto_channel_butserv(chptr, &me,
":%s NOTICE %s :*** Notice -- TS for %s "
"changed from %ld to %ld",
me.name, chptr->chname, chptr->chname,
oldts, newts);
}
if (mbuf != modebuf)
{
*mbuf = '\0';
parabuf[pbpos] = '\0';
sjoin_sendit(cptr, sptr, chptr, parv[0]);
}
*modebuf = '\0';
parabuf[0] = '\0';
if (parv[3][0] != '0' && keepnewmodes)
channel_modes(sptr, modebuf, parabuf, chptr);
else
{
modebuf[0] = '0';
modebuf[1] = '\0';
}
/* We do this down below now, so we can send out for two sjoin formats.
* sprintf(t, ":%s SJOIN %ld %ld %s %s %s :", parv[0], tstosend, tstosend,
* parv[2], modebuf, parabuf);
* t += strlen(t);
* the pointer "t" has been removed and is now replaced with an
* index into sjbuf for faster appending
*/
strcpy(keep_modebuf, modebuf);
strcpy(keep_parabuf, parabuf);
sjbufpos = 0;
mbuf = modebuf;
pbpos = 0;
pargs = 0;
*mbuf++ = '+';
for (s = s0 = strtoken(&p, parv[args + 4], " "); s;
s = s0 = strtoken(&p, (char *) NULL, " "))
{
fl = 0;
if (*s == '@' || s[1] == '@')
fl |= MODE_CHANOP;
if (*s == '%' || s[1] == '%')
fl |= MODE_HALFOP;
if (*s == '+' || s[1] == '+')
fl |= MODE_VOICE;
if (!keepnewmodes)
{
if (fl & MODE_CHANOP)
fl = MODE_DEOPPED;
else
fl = 0;
}
while (*s == '@' || *s == '%' || *s == '+')
s++;
if (!(acptr = find_chasing(sptr, s, NULL)))
continue;
if (acptr->from != cptr)
continue;
people++;
if (!IsMember(acptr, chptr))
{
add_user_to_channel(chptr, acptr, fl);
sendto_channel_butserv(chptr, acptr, ":%s JOIN :%s", s, parv[2]);
}
if (keepnewmodes)
{
ADD_SJBUF(s0)
}
else
{
ADD_SJBUF(s)
}
if (fl & MODE_CHANOP)
{
*mbuf++ = 'o';
ADD_PARA(s)
pargs++;
if (pargs >= MAXMODEPARAMS)
{
*mbuf = '\0';
parabuf[pbpos] = '\0';
sjoin_sendit(cptr, sptr, chptr, parv[0]);
mbuf = modebuf;
*mbuf++ = '+';
pargs = pbpos = 0;
}
}
if (fl & MODE_HALFOP)
{
*mbuf++ = 'h';
ADD_PARA(s)
pargs++;
if (pargs >= MAXMODEPARAMS)
{
*mbuf = '\0';
parabuf[pbpos] = '\0';
sjoin_sendit(cptr, sptr, chptr, parv[0]);
mbuf = modebuf;
*mbuf++ = '+';
pargs = pbpos = 0;
}
}
if (fl & MODE_VOICE)
{
*mbuf++ = 'v';
ADD_PARA(s)
pargs++;
if (pargs >= MAXMODEPARAMS)
{
*mbuf = '\0';
parabuf[pbpos] = '\0';
sjoin_sendit(cptr, sptr, chptr, parv[0]);
mbuf = modebuf;
*mbuf++ = '+';
pargs = pbpos = 0;
}
}
}
parabuf[pbpos] = '\0';
*mbuf = '\0';
if (pargs)
sjoin_sendit(cptr, sptr, chptr, parv[0]);
if (people)
{
sjbuf[sjbufpos] = '\0';
if(keep_parabuf[0] != '\0')
sendto_serv_butone(cptr, SJOINFmt, parv[0], tstosend, parv[2],
keep_modebuf, keep_parabuf, sjbuf);
else
sendto_serv_butone(cptr, SJOINFmtNP, parv[0], tstosend, parv[2],
keep_modebuf, sjbuf);
}
else if(created && chptr->users == 0)
sub1_from_channel(chptr);
return 0;
}
#undef INSERTSIGN
#undef ADD_PARA
#undef ADD_SJBUF
/* m_samode - Just bout the same as df
* - Raistlin
* parv[0] = sender
* parv[1] = channel
* parv[2] = modes
*/
int m_samode(aClient *cptr, aClient *sptr, int parc, char *parv[])
{
aChannel *chptr;
if (!MyClient(sptr))
return 0;
if (!IsAnOper(sptr) || !IsSAdmin(sptr))
{
sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
return 0;
}
if(parc < 3)
{
sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS),
me.name, parv[0], "SAMODE");
return 0;
}
if((chptr = find_channel(parv[1], NullChn)) == NullChn)
return 0;
if(!check_channelname(sptr, (unsigned char *)parv[1]))
return 0;
set_mode(cptr, sptr, chptr, 2, parc - 2, parv + 2, modebuf, parabuf);
if (strlen(modebuf) > (size_t)1)
{
sendto_channel_butserv(chptr, sptr, ":%s MODE %s %s %s",
parv[0], chptr->chname, modebuf, parabuf);
sendto_serv_butone(cptr, ":%s MODE %s 0 %s %s", parv[0], chptr->chname,
modebuf, parabuf);
if(MyClient(sptr))
{
sendto_serv_butone(NULL, ":%s GLOBOPS :%s used SAMODE (%s %s%s%s)",
me.name, sptr->name, chptr->chname, modebuf,
(*parabuf!=0 ? " " : ""), parabuf);
send_globops("from %s: %s used SAMODE (%s %s%s%s)",
me.name, sptr->name, chptr->chname, modebuf,
(*parabuf!=0 ? " " : ""), parabuf);
}
}
return 0;
}
char *pretty_mask(char *mask)
{
char *cp, *user, *host;
if ((user = strchr((cp = mask), '!')))
*user++ = '\0';
if ((host = strrchr(user ? user : cp, '@')))
{
*host++ = '\0';
if (!user)
return make_nick_user_host(NULL, cp, host);
}
else if (!user && strchr(cp, '.'))
return make_nick_user_host(NULL, NULL, cp);
return make_nick_user_host(cp, user, host);
}
u_long
memcount_channel(MCchannel *mc)
{
aChannel *chptr;
aBan *ban;
#ifdef EXEMPT_LISTS
aBanExempt *exempt;
#endif
#ifdef INVITE_LISTS
anInvite *invite;
#endif
DLink *lp;
Link *lp2;
chanMember *cm;
#ifdef FLUD
struct fludbot *fb;
#endif
mc->file = __FILE__;
for (chptr = channel; chptr; chptr = chptr->nextch)
{
mc->e_channels++;
for (ban = chptr->banlist; ban; ban = ban->next)
{
mc->bans.c++;
mc->bans.m += sizeof(*ban);
mc->bans.m += strlen(ban->banstr) + 1;
mc->bans.m += strlen(ban->who) + 1;
}
#ifdef EXEMPT_LISTS
for (exempt = chptr->banexempt_list; exempt; exempt = exempt->next)
{
mc->exempts.c++;
mc->exempts.m += sizeof(*exempt);
mc->exempts.m += strlen(exempt->banstr) + 1;
mc->exempts.m += strlen(exempt->who) + 1;
}
#endif
#ifdef INVITE_LISTS
for (invite = chptr->invite_list; invite; invite = invite->next)
{
mc->invites.c++;
mc->invites.m += sizeof(*invite);
mc->invites.m += strlen(invite->invstr) + 1;
mc->invites.m += strlen(invite->who) + 1;
}
#endif
for (cm = chptr->members; cm; cm = cm->next)
mc->e_chanmembers++;
#ifdef FLUD
for (fb = chptr->fluders; fb; fb = fb->next)
mc->e_fludbots++;
#endif
mc->e_inv_links += mc_links(chptr->invites);
}
for (lp = listing_clients; lp; lp = lp->next)
{
mc->lopts.c++;
mc->lopts.m += sizeof(LOpts);
mc->e_dlinks++;
for (lp2 = lp->value.cptr->user->lopt->yeslist; lp2; lp2 = lp2->next)
{
mc->lopts.m += strlen(lp2->value.cp) + 1;
mc->e_lopt_links++;
}
for (lp2 = lp->value.cptr->user->lopt->nolist; lp2; lp2 = lp2->next)
{
mc->lopts.m += strlen(lp2->value.cp) + 1;
mc->e_lopt_links++;
}
}
mc->total.c = mc->bans.c;
mc->total.m = mc->bans.m;
#ifdef EXEMPT_LISTS
mc->total.c += mc->exempts.c;
mc->total.m += mc->exempts.m;
#endif
#ifdef INVITE_LISTS
mc->total.c += mc->invites.c;
mc->total.m += mc->invites.m;
#endif
mc->total.c += mc->lopts.c;
mc->total.m += mc->lopts.m;
mc->s_scratch.c++;
mc->s_scratch.m += sizeof(nickbuf);
mc->s_scratch.c++;
mc->s_scratch.m += sizeof(buf);
mc->s_scratch.c++;
mc->s_scratch.m += sizeof(modebuf);
mc->s_scratch.c++;
mc->s_scratch.m += sizeof(parabuf);
return mc->total.m;
}
syntax highlighted by Code2HTML, v. 0.9.1