/* Channel-handling routines.
*
* (C) 2003 Anope Team
* Contact us at info@anope.org
*
* Please read COPYING and README for further details.
*
* Based on the original code of Epona by Lara.
* Based on the original code of Services by Andy Church.
*
* $Id$
*
*/
#include "services.h"
#include "language.h"
Channel *chanlist[1024];
#define HASH(chan) ((chan)[1] ? ((chan)[1]&31)<<5 | ((chan)[2]&31) : 0)
void add_ban(Channel * chan, char *mask);
void add_exception(Channel * chan, char *mask);
void add_invite(Channel * chan, char *mask);
void chan_adduser2(User * user, Channel * c);
Channel *chan_create(char *chan);
void chan_delete(Channel * c);
void del_ban(Channel * chan, char *mask);
void del_exception(Channel * chan, char *mask);
void del_invite(Channel * chan, char *mask);
char *get_flood(Channel * chan);
char *get_key(Channel * chan);
char *get_limit(Channel * chan);
char *get_redirect(Channel * chan);
Channel *join_user_update(User * user, Channel * chan, char *name);
void set_flood(Channel * chan, char *value);
void set_key(Channel * chan, char *value);
void set_limit(Channel * chan, char *value);
void set_redirect(Channel * chan, char *value);
void do_mass_mode(char *modes);
/**************************** External Calls *****************************/
/*************************************************************************/
void chan_deluser(User * user, Channel * c)
{
struct c_userlist *u;
if (c->ci)
update_cs_lastseen(user, c->ci);
for (u = c->users; u && u->user != user; u = u->next);
if (!u)
return;
if (u->ud) {
if (u->ud->lastline)
free(u->ud->lastline);
free(u->ud);
}
if (u->next)
u->next->prev = u->prev;
if (u->prev)
u->prev->next = u->next;
else
c->users = u->next;
free(u);
c->usercount--;
if (s_BotServ && c->ci && c->ci->bi && c->usercount == BSMinUsers - 1) {
anope_cmd_part(c->ci->bi->nick, c->name, NULL);
}
if (!c->users)
chan_delete(c);
}
/*************************************************************************/
/* Returns a fully featured binary modes string. If complete is 0, the
* eventual parameters won't be added to the string.
*/
char *chan_get_modes(Channel * chan, int complete, int plus)
{
static char res[BUFSIZE];
char *end = res;
if (chan->mode) {
int n = 0;
CBModeInfo *cbmi = cbmodeinfos;
do {
if (chan->mode & cbmi->flag)
*end++ = cbmi->mode;
} while ((++cbmi)->flag != 0 && ++n < sizeof(res) - 1);
if (complete) {
cbmi = cbmodeinfos;
do {
if (cbmi->getvalue && (chan->mode & cbmi->flag) &&
(plus || !(cbmi->flags & CBM_MINUS_NO_ARG))) {
char *value = cbmi->getvalue(chan);
if (value) {
*end++ = ' ';
while (*value)
*end++ = *value++;
}
}
} while ((++cbmi)->flag != 0 && ++n < sizeof(res) - 1);
}
}
*end = 0;
return res;
}
/*************************************************************************/
/* Retrieves the status of an user on a channel */
int chan_get_user_status(Channel * chan, User * user)
{
struct u_chanlist *uc;
for (uc = user->chans; uc; uc = uc->next)
if (uc->chan == chan)
return uc->status;
return 0;
}
/*************************************************************************/
/* Has the given user the given status on the given channel? :p */
int chan_has_user_status(Channel * chan, User * user, int16 status)
{
struct u_chanlist *uc;
for (uc = user->chans; uc; uc = uc->next) {
if (uc->chan == chan) {
if (debug) {
alog("debug: chan_has_user_status wanted %d the user is %d", status, uc->status);
}
return (uc->status & status);
}
}
return 0;
}
/*************************************************************************/
/* Remove the status of an user on a channel */
void chan_remove_user_status(Channel * chan, User * user, int16 status)
{
struct u_chanlist *uc;
for (uc = user->chans; uc; uc = uc->next) {
if (uc->chan == chan) {
uc->status &= ~status;
break;
}
}
}
/*************************************************************************/
void chan_set_modes(const char *source, Channel * chan, int ac, char **av,
int check)
{
int add = 1;
int servermode = !!strchr(source, '.');
char *modes = av[0], mode;
CBMode *cbm;
CMMode *cmm;
CUMode *cum;
if (debug)
alog("debug: Changing modes for %s to %s", chan->name,
merge_args(ac, av));
ac--;
while ((mode = *modes++)) {
switch (mode) {
case '+':
add = 1;
continue;
case '-':
add = 0;
continue;
}
if (((int) mode) < 0) {
if (debug)
alog("Debug: Malformed mode detected on %s.", chan->name);
continue;
}
if ((cum = &cumodes[(int) mode])->status != 0) {
User *user;
if (ac == 0) {
alog("channel: mode %c%c with no parameter (?) for channel %s", add ? '+' : '-', mode, chan->name);
continue;
}
ac--;
av++;
if ((cum->flags & CUF_PROTECT_BOTSERV) && !add) {
BotInfo *bi;
if ((bi = findbot(*av))) {
anope_cmd_mode(bi->nick, chan->name, "+%c %s", mode,
bi->nick);
continue;
}
}
if (!(user = finduser(*av))) {
alog("channel: MODE %s %c%c for nonexistent user %s",
chan->name, (add ? '+' : '-'), mode, *av);
continue;
}
if (debug)
alog("debug: Setting %c%c on %s for %s", (add ? '+' : '-'),
mode, chan->name, user->nick);
if (add) {
/*
Okay everyones biggest complaint is that NeoStats or any other
services clients are flagged as services but we still strip their
channel modes when strict is enabled. This lets them keep the mode and
we update our internal user/channel struct - TSL
*/
if (ircd->servicesmode) {
if (user->mode & ircd->servicesmode) {
chan_remove_user_status(chan, user, cum->status);
continue;
}
}
/* Fixes bug #68
- might be a bit ugly but it works, the idea is that since the
is_valid function strips out all of the modes there is no point
in sending it over and over again
*/
if (check) {
if (check == 2 && cum->is_valid) {
if (debug) {
alog("debug: Modes already removed, calling remove_user_status() to clean up");
}
chan_remove_user_status(chan, user, cum->status);
continue;
} else if (cum->is_valid
&& !cum->is_valid(user, chan, servermode)) {
if (debug) {
alog("debug: Modes already sent calling remove_user_status() to clean up");
}
chan_remove_user_status(chan, user, cum->status);
check = 2;
continue;
} else {
chan_set_user_status(chan, user, cum->status);
}
} else {
chan_set_user_status(chan, user, cum->status);
}
} else {
chan_remove_user_status(chan, user, cum->status);
}
} else if ((cbm = &cbmodes[(int) mode])->flag != 0) {
if (add)
chan->mode |= cbm->flag;
else
chan->mode &= ~cbm->flag;
if (cbm->setvalue) {
if (add || !(cbm->flags & CBM_MINUS_NO_ARG)) {
if (ac == 0) {
alog("channel: mode %c%c with no parameter (?) for channel %s", add ? '+' : '-', mode, chan->name);
continue;
}
ac--;
av++;
}
cbm->setvalue(chan, add ? *av : NULL);
}
} else if ((cmm = &cmmodes[(int) mode])->addmask) {
if (ac == 0) {
alog("channel: mode %c%c with no parameter (?) for channel %s", add ? '+' : '-', mode, chan->name);
continue;
}
ac--;
av++;
add ? cmm->addmask(chan, *av) : cmm->delmask(chan, *av);
}
}
if (check)
check_modes(chan);
}
/*************************************************************************/
/* Set the status of an user on a channel */
void chan_set_user_status(Channel * chan, User * user, int16 status)
{
struct u_chanlist *uc;
if (HelpChannel
&& (status == CUS_OP || status == (CUS_PROTECT | CUS_OP)
|| status == (CUS_OWNER | CUS_OP))
&& !stricmp(chan->name, HelpChannel)) {
if (debug) {
alog("debug: %s being given +h for having %d status in %s",
user->nick, status, chan->name);
}
common_svsmode(user, "+h", NULL);
}
for (uc = user->chans; uc; uc = uc->next) {
if (uc->chan == chan) {
uc->status
|