diff options
| author | sjaz <sjaz@5417fbe8-f217-4b02-8779-1006273d7864> | 2009-01-01 12:00:20 +0000 |
|---|---|---|
| committer | sjaz <sjaz@5417fbe8-f217-4b02-8779-1006273d7864> | 2009-01-01 12:00:20 +0000 |
| commit | c777c8d9aa7cd5c2e9a399727a7fa9985a77fb1c (patch) | |
| tree | 9e996ae4a1bbb833cec036c5cd4d87a590149e85 /src/channels.c | |
Anope Stable Branch
git-svn-id: http://anope.svn.sourceforge.net/svnroot/anope/stable@1902 5417fbe8-f217-4b02-8779-1006273d7864
Diffstat (limited to 'src/channels.c')
| -rw-r--r-- | src/channels.c | 2322 |
1 files changed, 2322 insertions, 0 deletions
diff --git a/src/channels.c b/src/channels.c new file mode 100644 index 000000000..8ce8a0c51 --- /dev/null +++ b/src/channels.c @@ -0,0 +1,2322 @@ +/* Channel-handling routines. + * + * (C) 2003-2008 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) + +/**************************** 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; + + if (debug >= 2) + alog("debug: removing user status (%d) from %s for %s", status, + user->nick, chan->name); + + 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; + char *modes = av[0], mode; + CBMode *cbm; + CMMode *cmm; + CUMode *cum; + unsigned char botmode = 0; + BotInfo *bi; + User *u, *user; + int i, real_ac = ac; + char **real_av = av; + + if (debug) + alog("debug: Changing modes for %s to %s", chan->name, + merge_args(ac, av)); + + u = finduser(source); + if (u && (chan_get_user_status(chan, u) & CUS_DEOPPED)) { + char *s; + + if (debug) + alog("debug: Removing instead of setting due to DEOPPED flag"); + + /* Swap adding and removing of the modes */ + for (s = av[0]; *s; s++) { + if (*s == '+') + *s = '-'; + else if (*s == '-') + *s = '+'; + } + + /* Set the resulting mode buffer */ + anope_cmd_mode(whosends(chan->ci), chan->name, merge_args(ac, av)); + + return; + } + + 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) { + 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) { + if ((bi = findbot(*av))) { + if (!botmode || botmode != mode) { + anope_cmd_mode(bi->nick, chan->name, "+%c %s", + mode, bi->nick); + botmode = mode; + continue; + } else { + botmode = mode; + continue; + } + } + } else { + if ((bi = findbot(*av))) { + continue; + } + } + + if (!(user = finduser(*av)) + && !(UseTS6 && ircd->ts6 && (user = find_byuid(*av)))) { + if (debug) { + alog("debug: 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) { + chan_set_user_status(chan, user, cum->status); + /* If this does +o, remove any DEOPPED flag */ + if (cum->status & CUS_OP) + chan_remove_user_status(chan, user, CUS_DEOPPED); + } else { + chan_remove_user_status(chan, user, cum->status); + } + + } else if ((cbm = &cbmodes[(int) mode])->flag != 0) { + if (check >= 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); + } + + if (check < 0) { + if (add) + chan->mode |= cbm->flag; + else + chan->mode &= ~cbm->flag; + } + } 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++; + if (add) + cmm->addmask(chan, *av); + else + cmm->delmask(chan, *av); + } + } + + if (check > 0) { + check_modes(chan); + + if (check < 2) { + /* Walk through all users we've set modes for and see if they are + * valid. Invalid modes (like +o with SECUREOPS on) will be removed + */ + real_ac--; + real_av++; + for (i = 0; i < real_ac; i++) { + if ((user = finduser(*real_av)) && is_on_chan(chan, user)) + chan_set_correct_modes(user, chan, 0); + real_av++; + } + } + } +} + +/*************************************************************************/ + +/* 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 (debug >= 2) + alog("debug: setting user status (%d) on %s for %s", status, + user->nick, chan->name); + + if (HelpChannel && ircd->supporthelper && (status & CUS_OP) + && (stricmp(chan->name, HelpChannel) == 0) + && (!chan->ci || check_access(user, chan->ci, CA_AUTOOP))) { + 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 |= 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 (!chan || !*chan) { + if (debug) { + alog("debug: findchan() called with NULL values"); + } + return NULL; + } + + if (debug >= 3) + alog("debug: findchan(%p)", chan); + c = chanlist[HASH(chan)]; + while (c) { + if (stricmp(c->name, chan) == 0) { + if (debug >= 3) + alog("debug: findchan(%s) -> %p", chan, (void *) c); + return c; + } + c = c->next; + } + if (debug >= 3) + alog("debug: findchan(%s) -> %p", chan, (void *) 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; + + 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; + if (ircd->fmode) { + if (chan->flood) + mem += strlen(chan->flood) + 1; + } + if (ircd->Lmode) { + if (chan->redirect) + mem += strlen(chan->redirect) + 1; + } + mem += get_memuse(chan->bans); + if (ircd->except) { + mem += get_memuse(chan->excepts); + } + if (ircd->invitemode) { + mem += get_memuse(chan->invites); + } + 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; + Channel *chan; + char *s, *t; + struct u_chanlist *c, *nextc; + char *channame; + time_t ts = time(NULL); + + if (UseTS6 && ircd->ts6) { + user = find_byuid(source); + if (!user) + user = finduser(source); + } else { + user = finduser(source); + } + if (!user) { + if (debug) { + alog("debug: 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 (*s == '0') { + c = user->chans; + while (c) { + nextc = c->next; + channame = sstrdup(c->chan->name); + send_event(EVENT_PART_CHANNEL, 3, EVENT_START, user->nick, + channame); + chan_deluser(user, c->chan); + send_event(EVENT_PART_CHANNEL, 3, EVENT_STOP, user->nick, + channame); + free(channame); + free(c); + c = nextc; + } + user->chans = NULL; + continue; + } + + /* how about not triggering the JOIN event on an actual /part :) -certus */ + send_event(EVENT_JOIN_CHANNEL, 3, EVENT_START, source, s); + + /* Make sure check_kick comes before chan_adduser, so banned users + * don't get to see things like channel keys. */ + /* If channel already exists, check_kick() will use correct TS. + * Otherwise, we lose. */ + if (check_kick(user, s, ts)) + continue; + + if (ac == 2) { + ts = strtoul(av[1], NULL, 10); + if (debug) { + alog("debug: recieved a new TS for JOIN: %ld", + (long int) ts); + } + } + + chan = findchan(s); + chan = join_user_update(user, chan, s, ts); + chan_set_correct_modes(user, chan, 1); + + send_event(EVENT_JOIN_CHANNEL, 3, EVENT_STOP, source, 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; + } + + if (UseTS6 && ircd->ts6) { + user = find_byuid(s); + if (!user) { + user = finduser(s); + } + } else { + user = finduser(s); + } + if (!user) { + if (debug) { + alog("debug: 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", user->nick, av[0]); + } + for (c = user->chans; c && stricmp(av[0], c->chan->name) != 0; + c = c->next); + if (c) { + send_event(EVENT_CHAN_KICK, 2, user->nick, av[0]); + 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; + char *channame; + + if (UseTS6 && ircd->ts6) { + user = find_byuid(source); + if (!user) + user = finduser(source); + } else { + user = finduser(source); + } + if (!user) { + if (debug) { + alog("debug: 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; + } + channame = sstrdup(c->chan->name); + send_event(EVENT_PART_CHANNEL, (ac >= 2 ? 4 : 3), EVENT_START, + user->nick, channame, (ac >= 2 ? av[1] : "")); + + 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); + + send_event(EVENT_PART_CHANNEL, (ac >= 2 ? 4 : 3), EVENT_STOP, + user->nick, channame, (ac >= 2 ? av[1] : "")); + free(channame); + } + } +} + +/*************************************************************************/ + +/* 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 + + ============================================================ + + Unreal SJOIN + + On Services connect there is + SJOIN !11LkOb #ircops +nt :@Trystan &*!*@*.aol.com "*@*.home.com + + av[0] = time stamp (base64) + av[1] = channel + av[2] = modes + av[3] = users + bans + exceptions + + On Channel Creation or a User joins an existing + Luna.NomadIrc.Net SJOIN !11LkW9 #akill :@Trystan + Luna.NomadIrc.Net SJOIN !11LkW9 #akill :Trystan` + + av[0] = time stamp (base64) + av[1] = channel + av[2] = users + +*/ + +void do_sjoin(const char *source, int ac, char **av) +{ + Channel *c; + User *user; + Server *serv; + struct c_userlist *cu; + char *s = NULL; + char *end, cubuf[7], *end2, *cumodes[6]; + int is_sqlined = 0; + int ts = 0; + int is_created = 0; + int keep_their_modes = 1; + + serv = findserver(servlist, source); + + if (ircd->sjb64) { + ts = base64dects(av[0]); + } else { + ts = strtoul(av[0], NULL, 10); + } + c = findchan(av[1]); + if (c != NULL) { + if (c->creation_time == 0 || ts == 0) + c->creation_time = 0; + else if (c->creation_time > ts) { + c->creation_time = ts; + for (cu = c->users; cu; cu = cu->next) { + /* XXX */ + cumodes[0] = "-ov"; + cumodes[1] = cu->user->nick; + cumodes[2] = cu->user->nick; + chan_set_modes(source, c, 3, cumodes, 2); + } + if (c->ci && c->ci->bi) { + /* This is ugly, but it always works */ + anope_cmd_part(c->ci->bi->nick, c->name, "TS reop"); + bot_join(c->ci); + } + /* XXX simple modes and bans */ + } else if (c->creation_time < ts) + keep_their_modes = 0; + } else + is_created = 1; + + /* Double check to avoid unknown modes that need parameters */ + if (ac >= 4) { + if (ircd->chansqline) { + if (!c) + is_sqlined = check_chan_sqline(av[1]); + } + + 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; + + + if (ircd->sjoinbanchar) { + if (*s == ircd->sjoinbanchar && keep_their_modes) { + add_ban(c, myStrGetToken(s, ircd->sjoinbanchar, 1)); + if (!end) + break; + s = end + 1; + continue; + } + } + if (ircd->sjoinexchar) { + if (*s == ircd->sjoinexchar && keep_their_modes) { + add_exception(c, + myStrGetToken(s, ircd->sjoinexchar, 1)); + if (!end) + break; + s = end + 1; + continue; + } + } + + if (ircd->sjoininvchar) { + if (*s == ircd->sjoininvchar && keep_their_modes) { + add_invite(c, myStrGetToken(s, ircd->sjoininvchar, 1)); + if (!end) + break; + s = end + 1; + continue; + } + } + + while (csmodes[(int) *s] != 0) + *end2++ = csmodes[(int) *s++]; + *end2 = 0; + + + if (UseTS6 && ircd->ts6) { + user = find_byuid(s); + if (!user) + user = finduser(s); + } else { + user = finduser(s); + } + + if (!user) { + if (debug) { + alog("debug: SJOIN for nonexistent user %s on %s", s, + av[1]); + } + return; + } + + if (is_sqlined && !is_oper(user)) { + anope_cmd_kick(s_OperServ, av[1], s, "Q-Lined"); + } else { + if (!check_kick(user, av[1], ts)) { + send_event(EVENT_JOIN_CHANNEL, 3, EVENT_START, + user->nick, 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], ts); + + /* We update user mode on the channel */ + if (end2 - cubuf > 1 && keep_their_modes) { + int i; + + for (i = 1; i < end2 - cubuf; i++) + cumodes[i] = user->nick; + chan_set_modes(source, c, 1 + (end2 - cubuf - 1), + cumodes, 2); + } + + if (c->ci && (!serv || is_sync(serv)) + && !c->topic_sync) + restore_topic(c->name); + chan_set_correct_modes(user, c, 1); + + send_event(EVENT_JOIN_CHANNEL, 3, EVENT_STOP, + user->nick, av[1]); + } + } + + if (!end) + break; + s = end + 1; + } + + if (c && keep_their_modes) { + /* We now update the channel mode. */ + chan_set_modes(source, c, ac - 3, &av[2], 2); + } + + /* Unreal just had to be different */ + } else if (ac == 3 && !ircd->ts6) { + if (ircd->chansqline) { + if (!c) + is_sqlined = check_chan_sqline(av[1]); + } + + cubuf[0] = '+'; + cumodes[0] = cubuf; + + /* We make all the users join */ + s = av[2]; /* 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; + + if (UseTS6 && ircd->ts6) { + user = find_byuid(s); + if (!user) + user = finduser(s); + } else { + user = finduser(s); + } + + if (!user) { + if (debug) { + alog("debug: SJOIN for nonexistent user %s on %s", s, + av[1]); + } + return; + } + + if (is_sqlined && !is_oper(user)) { + anope_cmd_kick(s_OperServ, av[1], s, "Q-Lined"); + } else { + if (!check_kick(user, av[1], ts)) { + send_event(EVENT_JOIN_CHANNEL, 3, EVENT_START, + user->nick, 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], ts); + + /* We update user mode on the channel */ + if (end2 - cubuf > 1 && keep_their_modes) { + int i; + + for (i = 1; i < end2 - cubuf; i++) + cumodes[i] = user->nick; + chan_set_modes(source, c, 1 + (end2 - cubuf - 1), + cumodes, 2); + } + + chan_set_correct_modes(user, c, 1); + + send_event(EVENT_JOIN_CHANNEL, 3, EVENT_STOP, + user->nick, av[1]); + } + } + + if (!end) + break; + s = end + 1; + } + } else if (ac == 3 && ircd->ts6) { + if (ircd->chansqline) { + if (!c) + is_sqlined = check_chan_sqline(av[1]); + } + + cubuf[0] = '+'; + cumodes[0] = cubuf; + + /* We make all the users join */ + s = sstrdup(source); /* 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; + + if (UseTS6 && ircd->ts6) { + user = find_byuid(s); + if (!user) + user = finduser(s); + } else { + user = finduser(s); + } + if (!user) { + if (debug) { + alog("debug: SJOIN for nonexistent user %s on %s", s, + av[1]); + } + free(s); + return; + } + + if (is_sqlined && !is_oper(user)) { + anope_cmd_kick(s_OperServ, av[1], s, "Q-Lined"); + } else { + if (!check_kick(user, av[1], ts)) { + send_event(EVENT_JOIN_CHANNEL, 3, EVENT_START, + user->nick, 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], ts); + + /* We update user mode on the channel */ + if (end2 - cubuf > 1 && keep_their_modes) { + int i; + + for (i = 1; i < end2 - cubuf; i++) + cumodes[i] = user->nick; + chan_set_modes(source, c, 1 + (end2 - cubuf - 1), + cumodes, 2); + } + + chan_set_correct_modes(user, c, 1); + + send_event(EVENT_JOIN_CHANNEL, 3, EVENT_STOP, + user->nick, av[1]); + } + } + + if (!end) + break; + s = end + 1; + } + free(s); + } else if (ac == 2) { + if (UseTS6 && ircd->ts6) { + user = find_byuid(source); + if (!user) + user = finduser(source); + } else { + user = finduser(source); + } + if (!user) { + if (debug) { + alog("debug: SJOIN for nonexistent user %s on %s", source, + av[1]); + } + return; + } + + if (check_kick(user, av[1], ts)) + return; + + if (ircd->chansqline) { + if (!c) + is_sqlined = check_chan_sqline(av[1]); + } + + if (is_sqlined && !is_oper(user)) { + anope_cmd_kick(s_OperServ, av[1], user->nick, "Q-Lined"); + } else { + send_event(EVENT_JOIN_CHANNEL, 3, EVENT_START, user->nick, + av[1]); + + c = join_user_update(user, c, av[1], ts); + if (is_created && c->ci) + restore_topic(c->name); + chan_set_correct_modes(user, c, 1); + + send_event(EVENT_JOIN_CHANNEL, 3, EVENT_STOP, user->nick, + av[1]); + } + } +} + + +/********************************* |
