diff options
Diffstat (limited to 'channels.c')
-rw-r--r-- | channels.c | 1595 |
1 files changed, 1595 insertions, 0 deletions
diff --git a/channels.c b/channels.c new file mode 100644 index 000000000..485e9a020 --- /dev/null +++ b/channels.c @@ -0,0 +1,1595 @@ +/* Channel-handling routines. + * + * (C) 2003 Anope Team + * Contact us at info@anope.org + * + * Please read COPYING and README for furhter details. + * + * Based on the original code of Epona by Lara. + * Based on the original code of Services by Andy Church. + * + * $Id: channels.c,v 1.36 2004/03/11 16:50:01 rob Exp $ + * + */ + +#include "services.h" +#include "language.h" + +Channel *chanlist[1024]; + +#define HASH(chan) ((chan)[1] ? ((chan)[1]&31)<<5 | ((chan)[2]&31) : 0) + +static void add_ban(Channel * chan, char *mask); +#if defined(IRC_ULTIMATE) || defined(IRC_UNREAL) || defined(IRC_ULTIMATE3) || defined(IRC_HYBRID) +static void add_exception(Channel * chan, char *mask); +#endif +static void chan_adduser2(User * user, Channel * c); +static Channel *chan_create(const char *chan); +static void chan_delete(Channel * c); +static void del_ban(Channel * chan, char *mask); +#if defined(IRC_ULTIMATE) || defined(IRC_UNREAL) || defined(IRC_ULTIMATE3) || defined(IRC_HYBRID) +static void del_exception(Channel * chan, char *mask); +#endif +#ifdef HAS_FMODE +static char *get_flood(Channel * chan); +#endif +static char *get_key(Channel * chan); +static char *get_limit(Channel * chan); +#ifdef HAS_LMODE +static char *get_redirect(Channel * chan); +#endif +static Channel *join_user_update(User * user, Channel * chan, char *name); +#ifdef HAS_FMODE +static void set_flood(Channel * chan, char *value); +#endif +static void set_key(Channel * chan, char *value); +static void set_limit(Channel * chan, char *value); +#ifdef HAS_LMODE +static void set_redirect(Channel * chan, char *value); +#endif +void do_mass_mode(char *modes); + +/*************************************************************************/ +/* *INDENT-OFF* */ + +CBMode cbmodes[128] = { + { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, + { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, + { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, + { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, + { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, + { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, + { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, + { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, + { 0 }, +#if defined(IRC_ULTIMATE) || defined(IRC_UNREAL) || defined(IRC_ULTIMATE3) + { CMODE_A, CBM_NO_USER_MLOCK, NULL, NULL }, +#else + { 0 }, /* A */ +#endif + { 0 }, /* B */ +#ifdef IRC_UNREAL + { CMODE_C, 0, NULL, NULL }, +#else + { 0 }, /* C */ +#endif + { 0 }, /* D */ + { 0 }, /* E */ + { 0 }, /* F */ +#ifdef IRC_UNREAL + { CMODE_G, 0, NULL, NULL }, + { CMODE_H, CBM_NO_USER_MLOCK, NULL, NULL }, +#else + { 0 }, /* G */ + { 0 }, /* H */ +#endif +#ifdef IRC_ULTIMATE + { CMODE_I }, +#else + { 0 }, /* I */ +#endif + { 0 }, /* J */ +#if defined(IRC_ULTIMATE) || defined(IRC_ULTIMATE3) || defined(IRC_UNREAL) || defined(IRC_ULTIMATE3) + { CMODE_K, 0, NULL, NULL }, +#else + { 0 }, /* K */ +#endif +#ifdef HAS_LMODE + { CMODE_L, 0, set_redirect, cs_set_redirect }, +#else + { 0 }, /* L */ +#endif +#ifdef IRC_BAHAMUT + { CMODE_M }, +#else + { 0 }, /* M */ +#endif +#if defined (IRC_UNREAL) || defined (IRC_ULTIMATE3) || defined (IRC_PTLINK) + { CMODE_N, 0, NULL, NULL }, +#else + { 0 }, /* N */ +#endif +#if defined(IRC_BAHAMUT) || defined(IRC_ULTIMATE) || defined(IRC_UNREAL) || defined(IRC_ULTIMATE3) + { CMODE_O, CBM_NO_USER_MLOCK, NULL, NULL }, +#else + { 0 }, /* O */ +#endif + { 0 }, /* P */ +#ifdef IRC_UNREAL + { CMODE_Q, 0, NULL, NULL }, +#else + { 0 }, /* Q */ +#endif +#ifndef IRC_HYBRID + { CMODE_R, 0, NULL, NULL }, /* R */ +#else + { 0 }, +#endif +#if defined(IRC_ULTIMATE) || defined(IRC_UNREAL) || defined (IRC_ULTIMATE3) || defined (IRC_PTLINK) + { CMODE_S, 0, NULL, NULL }, +#else + { 0 }, /* S */ +#endif + { 0 }, /* T */ + { 0 }, /* U */ +#ifdef IRC_UNREAL + { CMODE_V, 0, NULL, NULL }, +#else + { 0 }, /* V */ +#endif + { 0 }, /* W */ + { 0 }, /* X */ + { 0 }, /* Y */ + { 0 }, /* Z */ + { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, +#ifdef IRC_HYBRID + { CMODE_a, 0, NULL, NULL }, +#else + { 0 }, /* a */ +#endif + { 0 }, /* b */ +#if defined(IRC_BAHAMUT) || defined(IRC_UNREAL) || defined (IRC_PTLINK) + { CMODE_c, 0, NULL, NULL }, +#else + { 0 }, /* c */ +#endif +#ifdef IRC_PTLINK + { CMODE_d, 0, NULL, NULL }, +#else + { 0 }, /* d */ +#endif + { 0 }, /* e */ +#ifdef HAS_FMODE + { CMODE_f, 0, set_flood, cs_set_flood }, +#else + { 0 }, /* f */ +#endif + { 0 }, /* g */ + { 0 }, /* h */ + { CMODE_i, 0, NULL, NULL }, + { 0 }, /* j */ + { CMODE_k, 0, set_key, cs_set_key }, + { CMODE_l, CBM_MINUS_NO_ARG, set_limit, cs_set_limit }, + { CMODE_m, 0, NULL, NULL }, + { CMODE_n, 0, NULL, NULL }, + { 0 }, /* o */ + { CMODE_p, 0, NULL, NULL }, +#ifdef IRC_PTLINK + { CMODE_q, 0, NULL, NULL }, +#else + { 0 }, /* q */ +#endif +#ifndef IRC_HYBRID + { CMODE_r, CBM_NO_MLOCK, NULL, NULL }, +#else + { 0 }, +#endif + { CMODE_s, 0, NULL, NULL }, + { CMODE_t, 0, NULL, NULL }, +#ifdef IRC_UNREAL + { CMODE_u, 0, NULL, NULL }, +#else + { 0 }, +#endif + { 0 }, /* v */ + { 0 }, /* w */ +#ifdef IRC_ULTIMATE + { CMODE_x }, +#else + { 0 }, /* x */ +#endif + { 0 }, /* y */ +#ifdef IRC_UNREAL + { CMODE_z, 0, NULL, NULL }, +#else + { 0 }, /* z */ +#endif + { 0 }, { 0 }, { 0 }, { 0 } +}; + +CBModeInfo cbmodeinfos[] = { +#if defined(IRC_HYBRID) + { 'a', CMODE_a, 0, NULL, NULL }, +#endif +#if defined(IRC_BAHAMUT) || defined(IRC_UNREAL) || defined(IRC_PTLINK) + { 'c', CMODE_c, 0, NULL, NULL }, +#endif +#if defined(IRC_PTLINK) + { 'd', CMODE_d, 0, NULL, NULL }, +#endif +#ifdef HAS_FMODE + { 'f', CMODE_f, 0, get_flood, cs_get_flood }, +#endif + { 'i', CMODE_i, 0, NULL, NULL }, + { 'k', CMODE_k, 0, get_key, cs_get_key }, + { 'l', CMODE_l, CBM_MINUS_NO_ARG, get_limit, cs_get_limit }, + { 'm', CMODE_m, 0, NULL, NULL }, + { 'n', CMODE_n, 0, NULL, NULL }, + { 'p', CMODE_p, 0, NULL, NULL }, +#ifdef IRC_PTLINK + { 'q', CMODE_q, 0, NULL, NULL }, +#endif +#ifndef IRC_HYBRID + { 'r', CMODE_r, 0, NULL, NULL }, +#endif + { 's', CMODE_s, 0, NULL, NULL }, + { 't', CMODE_t, 0, NULL, NULL }, +#ifdef IRC_UNREAL + { 'u', CMODE_u, 0, NULL, NULL }, +#endif +#ifdef IRC_ULTIMATE + { 'x', CMODE_x, 0, NULL, NULL }, +#endif +#ifdef IRC_UNREAL + { 'z', CMODE_z, 0, NULL, NULL }, +#endif +#if defined(IRC_ULTIMATE) || defined(IRC_UNREAL) || defined(IRC_ULTIMATE3) || defined(IRC_PTLINK) + { 'A', CMODE_A, 0, NULL, NULL }, +#endif +#ifdef IRC_UNREAL + { 'C', CMODE_C, 0, NULL, NULL }, + { 'G', CMODE_G, 0, NULL, NULL }, + { 'H', CMODE_H, 0, NULL, NULL }, +#endif +#ifdef IRC_ULTIMATE + { 'I', CMODE_I, 0, NULL, NULL }, +#endif +#if defined(IRC_ULTIMATE) || defined(IRC_UNREAL) || defined(IRC_PTLINK) || defined(IRC_ULTIMATE3) + { 'K', CMODE_K, 0, NULL, NULL }, +#endif +#ifdef HAS_LMODE + { 'L', CMODE_L, 0, get_redirect, cs_get_redirect }, +#endif +#ifdef IRC_BAHAMUT +#ifndef IRC_ULTIMATE3 + { 'M', CMODE_M, 0, NULL, NULL }, +#endif +#endif +#if defined(IRC_UNREAL) || defined(IRC_ULTIMATE3) || defined(IRC_PTLINK) + { 'N', CMODE_N, 0, NULL, NULL }, +#endif +#if defined(IRC_BAHAMUT) || defined(IRC_ULTIMATE) || defined(IRC_UNREAL) || defined(IRC_ULTIMATE3) + { 'O', CMODE_O, 0, NULL, NULL }, +#endif +#ifdef IRC_UNREAL + { 'Q', CMODE_Q, 0, NULL, NULL }, +#endif +#ifndef IRC_HYBRID + { 'R', CMODE_R, 0, NULL, NULL }, +#endif +#if defined(IRC_ULTIMATE) || defined(IRC_UNREAL) || defined(IRC_ULTIMATE3) || defined(IRC_PTLINK) + { 'S', CMODE_S, 0, NULL, NULL }, +#endif +#ifdef IRC_UNREAL + { 'V', CMODE_V, 0, NULL, NULL }, +#endif + { 0 } +}; + +static CMMode cmmodes[128] = { + { NULL }, { NULL }, { NULL }, { NULL }, { NULL }, { NULL }, { NULL }, { NULL }, + { NULL }, { NULL }, { NULL }, { NULL }, { NULL }, { NULL }, { NULL }, { NULL }, + { NULL }, { NULL }, { NULL }, { NULL }, { NULL }, { NULL }, { NULL }, { NULL }, + { NULL }, { NULL }, { NULL }, { NULL }, { NULL }, { NULL }, { NULL }, { NULL }, + { NULL }, { NULL }, { NULL }, { NULL }, { NULL }, { NULL }, { NULL }, { NULL }, + { NULL }, { NULL }, { NULL }, { NULL }, { NULL }, { NULL }, { NULL }, { NULL }, + { NULL }, { NULL }, { NULL }, { NULL }, { NULL }, { NULL }, { NULL }, { NULL }, + { NULL }, { NULL }, { NULL }, { NULL }, { NULL }, { NULL }, { NULL }, { NULL }, + { NULL }, { NULL }, { NULL }, { NULL }, { NULL }, { NULL }, { NULL }, { NULL }, + { NULL }, { NULL }, { NULL }, { NULL }, { NULL }, { NULL }, { NULL }, { NULL }, + { NULL }, { NULL }, { NULL }, { NULL }, { NULL }, { NULL }, { NULL }, { NULL }, + { NULL }, { NULL }, { NULL }, { NULL }, { NULL }, { NULL }, { NULL }, { NULL }, + { NULL }, + { NULL }, + { add_ban, del_ban }, + { NULL }, + { NULL }, +#if defined(IRC_ULTIMATE) || defined(IRC_UNREAL) || defined(IRC_ULTIMATE3) || defined(IRC_HYBRID) + { add_exception, del_exception }, +#endif + { NULL }, + { NULL }, + { NULL }, { NULL }, { NULL }, { NULL }, { NULL }, { NULL }, { NULL }, { NULL }, + { NULL }, { NULL }, { NULL }, { NULL }, { NULL }, { NULL }, { NULL }, { NULL }, + { NULL }, { NULL }, { NULL }, { NULL }, { NULL }, { NULL }, { NULL }, { NULL } +}; + + #if defined(IRC_BAHAMUT) || defined(IRC_HYBRID) || defined(IRC_PTLINK) + +static char csmodes[128] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + + 0, + #if defined(IRC_ULTIMATE3) || defined(IRC_HYBRID) + 'a', /* (33) * Channel Admins */ + #else + 0, + #endif + 0, 0, 0, + #ifdef IRC_ULTIMATE3 + 'h', /* (37) % Channel halfops */ + #else + 0, + #endif + 0, 0, 0, 0, 0, + + 'v', 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + + 'o', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +#endif + +static CUMode cumodes[128] = { + { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, + { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, + { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, + { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, + + { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, + { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, + { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, + { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, + + { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, + { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, + { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, + { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, + + { 0 }, + +#if defined(IRC_UNREAL) || defined(IRC_VIAGRA) + { CUS_PROTECT, CUF_PROTECT_BOTSERV, check_valid_op }, +#else +#if defined(IRC_ULTIMATE3) + { CUS_PROTECT, CUF_PROTECT_BOTSERV, check_valid_admin }, +#else + { 0 }, /* a */ +#endif +#endif + { 0 }, /* b */ + { 0 }, /* c */ + { 0 }, /* d */ + { 0 }, /* e */ + { 0 }, /* f */ + { 0 }, /* g */ +#ifdef HAS_HALFOP + { CUS_HALFOP, 0, check_valid_op }, +#else + { 0 }, /* h */ +#endif + { 0 }, /* i */ + { 0 }, /* j */ + { 0 }, /* k */ + { 0 }, /* l */ + { 0 }, /* m */ + { 0 }, /* n */ + { CUS_OP, CUF_PROTECT_BOTSERV, check_valid_op }, + { 0 }, /* p */ +#if defined(IRC_UNREAL) || defined(IRC_VIAGRA) + { CUS_OWNER, 0, check_valid_op }, +#else + { 0 }, /* q */ +#endif + { 0 }, /* r */ + { 0 }, /* s */ + { 0 }, /* t */ + { 0 }, /* u */ + { CUS_VOICE, 0, NULL }, + { 0 }, /* w */ + { 0 }, /* x */ + { 0 }, /* y */ + { 0 }, /* z */ + { 0 }, { 0 }, { 0 }, { 0 }, { 0 } +}; + +/* *INDENT-ON* */ +/*************************************************************************/ +/**************************** 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) { + send_cmd(c->ci->bi->nick, "PART %s", c->name); + } + + 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) + 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))) { + send_cmd(bi->nick, "MODE %s +%c %s", chan->name, 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) { + if (check && cum->is_valid + && !cum->is_valid(user, chan, servermode)) + continue; + 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 + && !stricmp(chan->name, HelpChannel)) + change_user_mode(user, "+h", NULL); + + for (uc = user->chans; uc; uc = uc->next) { + if (uc->chan == chan) { + uc->status |= status; + break; + } + } +} + +/*************************************************************************/ + +/* Return the Channel structure corresponding to the named channel, or NULL + * if the channel was not found. chan is assumed to be non-NULL and valid + * (i.e. pointing to a channel name of 2 or more characters). */ + +Channel *findchan(const char *chan) +{ + Channel *c; + + if (debug >= 3) + alog("debug: findchan(%p)", chan); + c = chanlist[HASH(chan)]; + while (c) { + if (stricmp(c->name, chan) == 0) + return c; + c = c->next; + } + if (debug >= 3) + alog("debug: findchan(%s) -> %p", chan, c); + return NULL; +} + +/*************************************************************************/ + +/* Iterate over all channels in the channel list. Return NULL at end of + * list. + */ + +static Channel *current; +static int next_index; + +Channel *firstchan(void) +{ + next_index = 0; + while (next_index < 1024 && current == NULL) + current = chanlist[next_index++]; + if (debug >= 3) + alog("debug: firstchan() returning %s", + current ? current->name : "NULL (end of list)"); + return current; +} + +Channel *nextchan(void) +{ + if (current) + current = current->next; + if (!current && next_index < 1024) { + while (next_index < 1024 && current == NULL) + current = chanlist[next_index++]; + } + if (debug >= 3) + alog("debug: nextchan() returning %s", + current ? current->name : "NULL (end of list)"); + return current; +} + +/*************************************************************************/ + +/* Return statistics. Pointers are assumed to be valid. */ + +void get_channel_stats(long *nrec, long *memuse) +{ + long count = 0, mem = 0; + Channel *chan; + struct c_userlist *cu; + BanData *bd; + int i, j; + + for (i = 0; i < 1024; i++) { + for (chan = chanlist[i]; chan; chan = chan->next) { + count++; + mem += sizeof(*chan); + if (chan->topic) + mem += strlen(chan->topic) + 1; + if (chan->key) + mem += strlen(chan->key) + 1; +#ifdef HAS_FMODE + if (chan->flood) + mem += strlen(chan->flood) + 1; +#endif +#ifdef HAS_LMODE + if (chan->redirect) + mem += strlen(chan->redirect) + 1; +#endif + mem += sizeof(char *) * chan->bansize; + for (j = 0; j < chan->bancount; j++) { + if (chan->bans[j]) + mem += strlen(chan->bans[j]) + 1; + } +#if defined (IRC_ULTIMATE) || defined (IRC_UNREAL) || defined (IRC_ULTIMATE3) || defined(IRC_HYBRID) + mem += sizeof(char *) * chan->exceptsize; + for (j = 0; j < chan->exceptcount; j++) { + if (chan->excepts[j]) + mem += strlen(chan->excepts[j]) + 1; + } +#endif + for (cu = chan->users; cu; cu = cu->next) { + mem += sizeof(*cu); + if (cu->ud) { + mem += sizeof(*cu->ud); + if (cu->ud->lastline) + mem += strlen(cu->ud->lastline) + 1; + } + } + for (bd = chan->bd; bd; bd = bd->next) { + if (bd->mask) + mem += strlen(bd->mask) + 1; + mem += sizeof(*bd); + } + } + } + *nrec = count; + *memuse = mem; +} + +/*************************************************************************/ + +/* Is the given nick on the given channel? */ + +int is_on_chan(Channel * c, User * u) +{ + struct u_chanlist *uc; + + for (uc = u->chans; uc; uc = uc->next) + if (uc->chan == c) + return 1; + + return 0; +} + +/*************************************************************************/ + +/* Is the given nick on the given channel? + This function supports links. */ + +User *nc_on_chan(Channel * c, NickCore * nc) +{ + struct c_userlist *u; + + if (!c || !nc) + return NULL; + + for (u = c->users; u; u = u->next) { + if (u->user->na && u->user->na->nc == nc + && nick_recognized(u->user)) + return u->user; + } + return NULL; +} + +/*************************************************************************/ +/*************************** Message Handling ****************************/ +/*************************************************************************/ + +/* Handle a JOIN command. + * av[0] = channels to join + */ + +void do_join(const char *source, int ac, char **av) +{ + User *user; + char *s, *t; + struct u_chanlist *c, *nextc; + + user = finduser(source); + if (!user) { + alog("user: JOIN from nonexistent user %s: %s", source, + merge_args(ac, av)); + return; + } + + t = av[0]; + while (*(s = t)) { + t = s + strcspn(s, ","); + if (*t) + *t++ = 0; + if (debug) + alog("debug: %s joins %s", source, s); + + if (*s == '0') { + c = user->chans; + while (c) { + nextc = c->next; + chan_deluser(user, c->chan); + free(c); + c = nextc; + } + user->chans = NULL; + continue; + } + + /* Make sure check_kick comes before chan_adduser, so banned users + * don't get to see things like channel keys. */ + if (check_kick(user, s)) + continue; + +/* chan_adduser(user, s); */ + join_user_update(user, findchan(s), s); + + +/* c = scalloc(sizeof(*c), 1); + c->next = user->chans; + if (user->chans) + user->chans->prev = c; + user->chans = c; + c->chan = findchan(s); */ + } +} + +/*************************************************************************/ + +/* Handle a KICK command. + * av[0] = channel + * av[1] = nick(s) being kicked + * av[2] = reason + */ + +void do_kick(const char *source, int ac, char **av) +{ + BotInfo *bi; + ChannelInfo *ci; + User *user; + char *s, *t; + struct u_chanlist *c; + + t = av[1]; + while (*(s = t)) { + t = s + strcspn(s, ","); + if (*t) + *t++ = 0; + + /* If it is the bot that is being kicked, we make it rejoin the + * channel and stop immediately. + * --lara + */ + if (s_BotServ && (bi = findbot(s)) && (ci = cs_findchan(av[0]))) { + bot_join(ci); + continue; + } + + user = finduser(s); + if (!user) { + alog("user: KICK for nonexistent user %s on %s: %s", s, av[0], + merge_args(ac - 2, av + 2)); + continue; + } + if (debug) + alog("debug: kicking %s from %s", s, av[0]); + for (c = user->chans; c && stricmp(av[0], c->chan->name) != 0; + c = c->next); + if (c) { + chan_deluser(user, c->chan); + if (c->next) + c->next->prev = c->prev; + if (c->prev) + c->prev->next = c->next; + else + user->chans = c->next; + free(c); + } + } +} + +/*************************************************************************/ + +/* Handle a PART command. + * av[0] = channels to leave + * av[1] = reason (optional) + */ + +void do_part(const char *source, int ac, char **av) +{ + User *user; + char *s, *t; + struct u_chanlist *c; + + user = finduser(source); + if (!user) { + alog("user: PART from nonexistent user %s: %s", source, + merge_args(ac, av)); + return; + } + t = av[0]; + while (*(s = t)) { + t = s + strcspn(s, ","); + if (*t) + *t++ = 0; + if (debug) + alog("debug: %s leaves %s", source, s); + for (c = user->chans; c && stricmp(s, c->chan->name) != 0; + c = c->next); + if (c) { + if (!c->chan) { + alog("user: BUG parting %s: channel entry found but c->chan NULL", s); + return; + } + chan_deluser(user, c->chan); + if (c->next) + c->next->prev = c->prev; + if (c->prev) + c->prev->next = c->next; + else + user->chans = c->next; + free(c); + } + } +} + +/*************************************************************************/ + +#if defined(IRC_BAHAMUT) || defined(IRC_HYBRID) || defined(IRC_PTLINK) + +/* Handle a SJOIN command. + + On channel creation, syntax is: + + av[0] = timestamp + av[1] = channel name + av[2|3|4] = modes \ depends of whether the modes k and l + av[3|4|5] = users / are set or not. + + When a single user joins an (existing) channel, it is: + + av[0] = timestamp + av[1] = user + +*/ + +void do_sjoin(const char *source, int ac, char **av) +{ + Channel *c; + User *user; + + int is_sqlined = 0; + + /* Double check to avoid unknown modes that need parameters */ + if (ac >= 4 && ac <= 6) { + char *s, *end, cubuf[CHAN_MAX_SYMBOLS + 2], *end2, + *cumodes[CHAN_MAX_SYMBOLS + 1]; + + c = findchan(av[1]); +#ifndef IRC_HYBRID +#ifndef IRC_PTLINK + if (!c) + is_sqlined = check_chan_sqline(av[1]); +#endif +#endif + + cubuf[0] = '+'; + cumodes[0] = cubuf; + + /* We make all the users join */ + s = av[ac - 1]; /* Users are always the last element */ + + while (*s) { + end = strchr(s, ' '); + if (end) + *end = 0; + + end2 = cubuf + 1; + while (csmodes[(int) *s] != 0) + *end2++ = csmodes[(int) *s++]; + *end2 = 0; + + user = finduser(s); + if (!user) { + alog("user: SJOIN for nonexistent user %s on %s", s, + av[1]); + return; + } + + if (is_sqlined && !is_oper(user)) { + send_cmd(s_OperServ, "KICK %s %s :Q-Lined", av[1], s); + } else { + if (!check_kick(user, av[1])) { + /* Make the user join; if the channel does not exist it + * will be created there. This ensures that the channel + * is not created to be immediately destroyed, and + * that the locked key or topic is not shown to anyone + * who joins the channel when empty. + */ + c = join_user_update(user, c, av[1]); + + /* We update user mode on the channel */ + if (end2 - cubuf > 1) { + int i; + + for (i = 1; i < end2 - cubuf; i++) + cumodes[i] = user->nick; + chan_set_modes(source, c, 1 + (end2 - cubuf - 1), + cumodes, 1); + } + } + } + + if (!end) + break; + s = end + 1; + } + + if (c) { + /* Set the timestamp */ + c->creation_time = strtoul(av[0], NULL, 10); + /* We now update the channel mode. */ + chan_set_modes(source, c, ac - 3, &av[2], 1); + } + } else if (ac == 2) { + user = finduser(source); + if (!user) { + alog("user: SJOIN for nonexistent user %s on %s", source, + av[1]); + return; + } + + if (check_kick(user, av[1])) + return; + + c = findchan(av[1]); +#ifndef IRC_HYBRID +#ifndef IRC_PTLINK + if (!c) + is_sqlined = check_chan_sqline(av[1]); +#endif +#endif + if (is_sqlined && !is_oper(user)) { + send_cmd(s_OperServ, "KICK %s %s :Q-Lined", av[1], user->nick); + } else { + c = join_user_update(user, c, av[1]); + c->creation_time = strtoul(av[0], NULL, 10); + } + } +} + +#endif + +/*************************************************************************/ + +/* Handle a channel MODE command. */ + +void do_cmode(const char *source, int ac, char **av) +{ + Channel *chan; + ChannelInfo *ci = NULL; + + chan = findchan(av[0]); + if (!chan) { + ci = cs_findchan(av[0]); + if (!(ci && (ci->flags & CI_VERBOTEN))) + alog("channel: MODE %s for nonexistent channel %s", + merge_args(ac - 1, av + 1), av[0]); + return; + } + + /* This shouldn't trigger on +o, etc. */ + if (strchr(source, '.') && !av[1][strcspn(av[1], "bovahq")]) { + if (time(NULL) != chan->server_modetime) { + chan->server_modecount = 0; + chan->server_modetime = time(NULL); + } + chan->server_modecount++; + } + + ac--; + av++; + chan_set_modes(source, chan, ac, av, 1); +} + +/*************************************************************************/ + +/* Handle a TOPIC command. */ + +void do_topic(const char *source, int ac, char **av) +{ + Channel *c = findchan(av[0]); + time_t topic_time = strtoul(av[2], NULL, 10); + + if (!c) { + alog("channel: TOPIC %s for nonexistent channel %s", + merge_args(ac - 1, av + 1), av[0]); + return; + } + + if (check_topiclock(c, topic_time)) + return; + + if (c->topic) { + free(c->topic); + c->topic = NULL; + } + if (ac > 3 && *av[3]) + c->topic = sstrdup(av[3]); + + strscpy(c->topic_setter, av[1], sizeof(c->topic_setter)); + c->topic_time = topic_time; + + record_topic(av[0]); +} + +/*************************************************************************/ +/**************************** Internal Calls *****************************/ +/*************************************************************************/ + +static void add_ban(Channel * chan, char *mask) +{ + if (s_BotServ && BSSmartJoin && chan->ci && chan->ci->bi + && chan->usercount >= BSMinUsers) { + char botmask[BUFSIZE]; + BotInfo *bi = chan->ci->bi; + + snprintf(botmask, sizeof(botmask), "%s!%s@%s", bi->nick, bi->user, + bi->host); + if (match_wild_nocase(mask, botmask)) { + send_cmd(bi->nick, "MODE %s -b %s", chan->name, mask); + return; + } + } + + if (chan->bancount >= chan->bansize) { + chan->bansize += 8; + chan->bans = srealloc(chan->bans, sizeof(char *) * chan->bansize); + } + chan->bans[chan->bancount++] = sstrdup(mask); + + if (debug) + alog("debug: Added ban %s to channel %s", mask, chan->name); +} + +/*************************************************************************/ + +#if defined(IRC_ULTIMATE) || defined(IRC_UNREAL) || defined(IRC_ULTIMATE3) || defined(IRC_HYBRID) + +static void add_exception(Channel * chan, char *mask) +{ + if (chan->exceptcount >= chan->exceptsize) { + chan->exceptsize += 8; + chan->excepts = + srealloc(chan->excepts, sizeof(char *) * chan->exceptsize); + } + chan->excepts[chan->exceptcount++] = sstrdup(mask); + + if (debug) + alog("debug: Added except %s to channel %s", mask, chan->name); +} + +#endif + +/*************************************************************************/ + +/* Add/remove a user to/from a channel, creating or deleting the channel as + * necessary. If creating the channel, restore mode lock and topic as + * necessary. Also check for auto-opping and auto-voicing. + * Modified, so ignored users won't get any status via services -certus */ + + +static void chan_adduser2(User * user, Channel * c) +{ + struct c_userlist *u; + char *chan = c->name; + + if (get_ignore(user->nick) == NULL) { + +#if defined(IRC_UNREAL) || defined(IRC_VIAGRA) + if (check_should_owner(user, chan)) { + chan_set_user_status(c, user, CUS_OWNER | CUS_OP); + } else +#endif +#if defined(IRC_UNREAL) || defined(IRC_VIAGRA) || defined(IRC_ULTIMATE3) + if (check_should_protect(user, chan)) { + chan_set_user_status(c, user, CUS_PROTECT | CUS_OP); + } else +#endif + if (check_should_op(user, chan)) { + chan_set_user_status(c, user, CUS_OP); + } else +#ifdef HAS_HALFOP + if (check_should_halfop(user, chan)) { + chan_set_user_status(c, user, CUS_HALFOP); + } else +#endif + if (check_should_voice(user, chan)) { + chan_set_user_status(c, user, CUS_VOICE); + } + } + + u = scalloc(sizeof(struct c_userlist), 1); + u->next = c->users; + if (c->users) + c->users->prev = u; + c->users = u; + u->user = user; + c->usercount++; + + if (get_ignore(user->nick) == NULL) { + if (c->ci && (check_access(user, c->ci, CA_MEMO)) + && (c->ci->memos.memocount > 0)) { + if (c->ci->memos.memocount == 1) { + notice_lang(s_MemoServ, user, MEMO_X_ONE_NOTICE, + c->ci->memos.memocount, c->ci->name); + } else { + notice_lang(s_MemoServ, user, MEMO_X_MANY_NOTICE, + c->ci->memos.memocount, c->ci->name); + } + } + + if (c->ci && c->ci->entry_message) + notice_user(whosends(c->ci), user, "%s", c->ci->entry_message); + + if (s_BotServ && c->ci && c->ci->bi) { + if (c->usercount == BSMinUsers) + bot_join(c->ci); + if (c->usercount >= BSMinUsers && (c->ci->botflags & BS_GREET) + && user->na && user->na->nc->greet + && check_access(user, c->ci, CA_GREET)) { + send_cmd(c->ci->bi->nick, "PRIVMSG %s :[%s] %s", c->name, + user->na->nick, user->na->nc->greet); + c->ci->bi->lastmsg = time(NULL); + } + } + } +} + +/*************************************************************************/ + +/* This creates the channel structure (was originally in + chan_adduser, but splitted to make it more efficient to use for + SJOINs). */ + +static Channel *chan_create(const char *chan) +{ + Channel *c; + Channel **list; + + if (debug) + alog("debug: Creating channel %s", chan); + /* Allocate pre-cleared memory */ + c = scalloc(sizeof(Channel), 1); + strscpy(c->name, chan, sizeof(c->name)); + list = &chanlist[HASH(c->name)]; + c->next = *list; + if (*list) + (*list)->prev = c; + *list = c; + c->creation_time = time(NULL); + /* Store ChannelInfo pointer in channel record */ + c->ci = cs_findchan(chan); + if (c->ci) + c->ci->c = c; + /* Restore locked modes and saved topic */ + if (c->ci) { + check_modes(c); + restore_topic(chan); + stick_all(c->ci); + } + + return c; +} + +/*************************************************************************/ + +/* This destroys the channel structure, freeing everything in it. */ + +static void chan_delete(Channel * c) +{ + BanData *bd, *next; + int i; + + if (debug) + alog("debug: Deleting channel %s", c->name); + + for (bd = c->bd; bd; bd = next) { + if (bd->mask) + free(bd->mask); + next = bd->next; + free(bd); + } + + if (c->ci) + c->ci->c = NULL; + + if (c->topic) + free(c->topic); + + if (c->key) + free(c->key); +#ifdef HAS_FMODE + if (c->flood) + free(c->flood); +#endif +#ifdef HAS_LMODE + if (c->redirect) + free(c->redirect); +#endif + + for (i = 0; i < c->bancount; ++i) { + if (c->bans[i]) + free(c->bans[i]); + else + alog("channel: BUG freeing %s: bans[%d] is NULL!", c->name, i); + } + if (c->bansize) + free(c->bans); + +#if defined (IRC_ULTIMATE) || defined (IRC_UNREAL) || defined (IRC_ULTIMATE3) || defined(IRC_HYBRID) + for (i = 0; i < c->exceptcount; ++i) { + if (c->excepts[i]) + free(c->excepts[i]); + else + alog("channel: BUG freeing %s: exceps[%d] is NULL!", c->name, + i); + } + if (c->exceptsize) + free(c->excepts); +#endif + + if (c->next) + c->next->prev = c->prev; + if (c->prev) + c->prev->next = c->next; + else + chanlist[HASH(c->name)] = c->next; + + free(c); +} + +/*************************************************************************/ + +static void del_ban(Channel * chan, char *mask) +{ + char **s = chan->bans; + int i = 0; + AutoKick *akick; + + while (i < chan->bancount && strcmp(*s, mask) != 0) { + i++; + s++; + } + + if (i < chan->bancount) { + chan->bancount--; + if (i < chan->bancount) + memmove(s, s + 1, sizeof(char *) * (chan->bancount - i)); + + if (debug) + alog("debug: Deleted ban %s from channel %s", mask, + chan->name); + } + + if (chan->ci && (akick = is_stuck(chan->ci, mask))) + stick_mask(chan->ci, akick); +} + +/*************************************************************************/ + +#if defined(IRC_ULTIMATE) || defined(IRC_UNREAL) || defined(IRC_ULTIMATE3) || defined(IRC_HYBRID) + +static void del_exception(Channel * chan, char *mask) +{ + int reset = 0, i; + for (i = 0; i <= chan->exceptcount; i++) { + if (chan->excepts[i] == mask) { + reset = 1; + } + if (reset) { + if (i == chan->exceptcount) + chan->excepts[i] = NULL; + else + chan->excepts[i] = chan->excepts[i + 1]; + } + } + chan->exceptcount--; + if (debug) + alog("debug: Deleted except %s to channel %s", mask, chan->name); +} + +#endif + +/*************************************************************************/ + +#ifdef HAS_FMODE + +static char *get_flood(Channel * chan) +{ + return chan->flood; +} + +#endif + +/*************************************************************************/ + +static char *get_key(Channel * chan) +{ + return chan->key; +} + +/*************************************************************************/ + +static char *get_limit(Channel * chan) +{ + static char limit[16]; + + if (chan->limit == 0) + return NULL; + + snprintf(limit, sizeof(limit), "%lu", chan->limit); + return limit; +} + +/*************************************************************************/ + +#ifdef HAS_LMODE + +static char *get_redirect(Channel * chan) +{ + return chan->redirect; +} + +#endif + +/*************************************************************************/ + +static Channel *join_user_update(User * user, Channel * chan, char *name) +{ + struct u_chanlist *c; + + /* If it's a new channel, so we need to create it first. */ + if (!chan) + chan = chan_create(name); + + if (debug) + alog("debug: %s joins %s", user->nick, chan->name); + + c = scalloc(sizeof(*c), 1); + c->next = user->chans; + if (user->chans) + user->chans->prev = c; + user->chans = c; + c->chan = chan; + + chan_adduser2(user, chan); + + return chan; +} + +/*************************************************************************/ + +#ifdef HAS_FMODE + +static void set_flood(Channel * chan, char *value) +{ + if (chan->flood) + free(chan->flood); + chan->flood = value ? sstrdup(value) : NULL; + + if (debug) + alog("debug: Flood of channel %s set to %s", chan->name, + chan->flood ? chan->flood : "no flood settings"); +} + +#endif + +/*************************************************************************/ + +static void set_key(Channel * chan, char *value) +{ + if (chan->key) + free(chan->key); + chan->key = value ? sstrdup(value) : NULL; + + if (debug) + alog("debug: Key of channel %s set to %s", chan->name, + chan->key ? chan->key : "no key"); +} + +/*************************************************************************/ + +static void set_limit(Channel * chan, char *value) +{ + chan->limit = value ? strtoul(value, NULL, 10) : 0; + + if (debug) + alog("debug: Limit of channel %s set to %u", chan->name, + chan->limit); +} + +/*************************************************************************/ + +#ifdef HAS_LMODE + +static void set_redirect(Channel * chan, char *value) +{ + if (chan->redirect) + free(chan->redirect); + chan->redirect = value ? sstrdup(value) : NULL; + + if (debug) + alog("debug: Redirect of channel %s set to %s", chan->name, + chan->redirect ? chan->redirect : "no redirect"); +} + +#endif + +void do_mass_mode(char *modes) +{ + int ac, i; + char **av; + Channel *c; + char *myModes; + + if (!modes) { + return; + } + + /* Prevent modes being altered by split_buf */ + myModes = sstrdup(modes); + ac = split_buf(myModes, &av, 1); + + for (i = 0; i < 1024; i++) { + for (c = chanlist[i]; c; c = c->next) { + if (c->bouncy_modes) { + return; + } else { + send_cmd(s_OperServ, "MODE %s %s", c->name, modes); + chan_set_modes(s_OperServ, c, ac, av, 1); + } + } + } +} + +/*************************************************************************/ |