From: Kevin L. Mitchell Date: Sun, 30 Apr 2000 01:33:28 +0000 (+0000) Subject: Author: Kev X-Git-Url: http://git.pk910.de/?a=commitdiff_plain;h=31aa8c5e863e6eaf9b23d172fabc857691122748;p=ircu2.10.12-pk.git Author: Kev Log message: Rewrote JOIN/CREATE/PART routines, fixing a bad JOIN 0 bug in the process... git-svn-id: file:///home/klmitch/undernet-ircu/undernet-ircu-svn/ircu2/trunk@217 c9e4aea6-c8fd-4c43-8297-357d70d61c8c --- diff --git a/ChangeLog b/ChangeLog index 89ed9d0..b232630 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,20 @@ +2000-04-29 Kevin L. Mitchell + + * ircd/m_join.c: reimplement JOIN in terms of struct JoinBuf + + * ircd/channel.c (clean_channelname): make clean_channelname also + truncate long channel names + +2000-04-28 Kevin L. Mitchell + + * ircd/m_create.c: reimplement CREATE in terms of struct JoinBuf + + * ircd/channel.c: implemented joinbuf_init, joinbuf_join, + joinbuf_flush + + * include/channel.h: definitions and declarations for the struct + JoinBuf abstraction + 2000-04-29 Perry Lorier * include/s_bsd.c: Ok, so I thought I compiled and tested this... @@ -975,7 +992,7 @@ # # ChangeLog for ircu2.10.11 # -# $Id: ChangeLog,v 1.114 2000-04-29 05:30:05 isomer Exp $ +# $Id: ChangeLog,v 1.115 2000-04-30 01:33:27 kev Exp $ # # Insert new changes at beginning of the change list. # diff --git a/include/channel.h b/include/channel.h index 8bd0d02..7294cb4 100644 --- a/include/channel.h +++ b/include/channel.h @@ -47,6 +47,10 @@ struct Client; #define MAXBANS 30 #define MAXBANLENGTH 1024 +#define MAXJOINARGS 15 /* number of slots for join buffer */ +#define STARTJOINLEN 10 /* fuzzy numbers */ +#define STARTCREATELEN 20 + /* * Macro's */ @@ -269,6 +273,23 @@ struct ModeBuf { #define MB_STRING(mb, i) ((mb)->mb_modeargs[(i)].mbm_arg.mbma_string) #define MB_CLIENT(mb, i) ((mb)->mb_modeargs[(i)].mbm_arg.mbma_client) +struct JoinBuf { + struct Client *jb_source; /* Source of joins (ie, joiner) */ + struct Client *jb_connect; /* Connection of joiner */ + unsigned int jb_type; /* Type of join (JOIN or CREATE) */ + char *jb_comment; /* part comment */ + time_t jb_create; /* Creation timestamp */ + unsigned int jb_count; /* Number of channels */ + unsigned int jb_strlen; /* length so far */ + struct Channel *jb_channels[MAXJOINARGS]; + /* channels joined or whatever */ +}; + +#define JOINBUF_TYPE_JOIN 0 /* send JOINs */ +#define JOINBUF_TYPE_CREATE 1 /* send CREATEs */ +#define JOINBUF_TYPE_PART 2 /* send PARTs */ +#define JOINBUF_TYPE_PARTALL 3 /* send local PARTs, but not remote */ + extern struct Channel* GlobalChannelList; extern int LocalChanOperMode; @@ -351,4 +372,11 @@ extern int mode_parse(struct ModeBuf *mbuf, struct Client *cptr, #define MODE_PARSE_NOTOPER 0x10 /* send "not chanop" to user */ #define MODE_PARSE_NOTMEMBER 0x20 /* send "not member" to user */ +extern void joinbuf_init(struct JoinBuf *jbuf, struct Client *source, + struct Client *connect, unsigned int type, + char *comment, time_t create); +extern void joinbuf_join(struct JoinBuf *jbuf, struct Channel *chan, + unsigned int flags); +extern int joinbuf_flush(struct JoinBuf *jbuf); + #endif /* INCLUDED_channel_h */ diff --git a/ircd/channel.c b/ircd/channel.c index 04ce375..4927d07 100644 --- a/ircd/channel.c +++ b/ircd/channel.c @@ -2140,20 +2140,22 @@ int can_join(struct Client *sptr, struct Channel *chptr, char *key) */ void clean_channelname(char *cn) { - for (; *cn; ++cn) { - if (!IsChannelChar(*cn)) { - *cn = '\0'; + int i; + + for (i = 0; cn[i]; i++) { + if (i >= CHANNELLEN || !IsChannelChar(cn[i])) { + cn[i] = '\0'; return; } - if (IsChannelLower(*cn)) { - *cn = ToLower(*cn); + if (IsChannelLower(cn[i])) { + cn[i] = ToLower(cn[i]); #ifndef FIXME /* * Remove for .08+ * toupper(0xd0) */ - if ((unsigned char)(*cn) == 0xd0) - *cn = (char) 0xf0; + if ((unsigned char)(cn[i]) == 0xd0) + cn[i] = (char) 0xf0; #endif } } @@ -2659,9 +2661,10 @@ void send_hack_notice(struct Client *cptr, struct Client *sptr, int parc, * of course, str2 is not NULL) */ static void -build_string(char *strptr, int *strptr_i, char *str1, char *str2) +build_string(char *strptr, int *strptr_i, char *str1, char *str2, char c) { - strptr[(*strptr_i)++] = ' '; + if (c) + strptr[(*strptr_i)++] = c; while (*str1) strptr[(*strptr_i)++] = *(str1++); @@ -2819,11 +2822,11 @@ modebuf_flush_int(struct ModeBuf *mbuf, int all) /* deal with clients... */ if (MB_TYPE(mbuf, i) & (MODE_CHANOP | MODE_VOICE)) - build_string(strptr, strptr_i, MB_CLIENT(mbuf, i)->name, 0); + build_string(strptr, strptr_i, MB_CLIENT(mbuf, i)->name, 0, ' '); /* deal with strings... */ else if (MB_TYPE(mbuf, i) & (MODE_KEY | MODE_BAN)) - build_string(strptr, strptr_i, MB_STRING(mbuf, i), 0); + build_string(strptr, strptr_i, MB_STRING(mbuf, i), 0, ' '); /* * deal with limit; note we cannot include the limit parameter if we're @@ -2831,7 +2834,7 @@ modebuf_flush_int(struct ModeBuf *mbuf, int all) */ else if ((MB_TYPE(mbuf, i) & (MODE_ADD | MODE_LIMIT)) == (MODE_ADD | MODE_LIMIT)) - build_string(strptr, strptr_i, limitbuf, 0); + build_string(strptr, strptr_i, limitbuf, 0, ' '); } /* send the messages off to their destination */ @@ -2906,11 +2909,11 @@ modebuf_flush_int(struct ModeBuf *mbuf, int all) /* deal with modes that take clients */ if (MB_TYPE(mbuf, i) & (MODE_CHANOP | MODE_VOICE)) - build_string(strptr, strptr_i, NumNick(MB_CLIENT(mbuf, i))); + build_string(strptr, strptr_i, NumNick(MB_CLIENT(mbuf, i)), ' '); /* deal with modes that take strings */ else if (MB_TYPE(mbuf, i) & (MODE_KEY | MODE_BAN)) - build_string(strptr, strptr_i, MB_STRING(mbuf, i), 0); + build_string(strptr, strptr_i, MB_STRING(mbuf, i), 0, ' '); /* * deal with the limit. Logic here is complicated; if HACK2 is set, @@ -2918,14 +2921,14 @@ modebuf_flush_int(struct ModeBuf *mbuf, int all) * include the original limit if it looks like it's being removed */ else if ((MB_TYPE(mbuf, i) & limitdel) == limitdel) - build_string(strptr, strptr_i, limitbuf, 0); + build_string(strptr, strptr_i, limitbuf, 0, ' '); } /* we were told to deop the source */ if (mbuf->mb_dest & MODEBUF_DEST_DEOP) { addbuf[addbuf_i++] = 'o'; /* remember, sense is reversed */ addbuf[addbuf_i] = '\0'; /* terminate the string... */ - build_string(addstr, &addstr_i, NumNick(mbuf->mb_source)); /* add user */ + build_string(addstr, &addstr_i, NumNick(mbuf->mb_source), ' '); /* mark that we've done this, so we don't do it again */ mbuf->mb_dest &= ~MODEBUF_DEST_DEOP; @@ -3849,3 +3852,139 @@ mode_parse(struct ModeBuf *mbuf, struct Client *cptr, struct Client *sptr, return state.args_used; /* tell our parent how many args we gobbled */ } + +/* + * Initialize a join buffer + */ +void +joinbuf_init(struct JoinBuf *jbuf, struct Client *source, + struct Client *connect, unsigned int type, char *comment, + time_t create) +{ + int i; + + assert(0 != jbuf); + assert(0 != source); + assert(0 != connect); + + jbuf->jb_source = source; /* just initialize struct JoinBuf */ + jbuf->jb_connect = connect; + jbuf->jb_type = type; + jbuf->jb_comment = comment; + jbuf->jb_create = create; + jbuf->jb_count = 0; + jbuf->jb_strlen = (((type == JOINBUF_TYPE_JOIN || + type == JOINBUF_TYPE_PART || + type == JOINBUF_TYPE_PARTALL) ? + STARTJOINLEN : STARTCREATELEN) + + (comment ? strlen(comment) + 2 : 0)); + + for (i = 0; i < MAXJOINARGS; i++) + jbuf->jb_channels[i] = 0; +} + +/* + * Add a channel to the join buffer + */ +void +joinbuf_join(struct JoinBuf *jbuf, struct Channel *chan, unsigned int flags) +{ + unsigned int len; + + assert(0 != jbuf); + + if (chan) { + if (jbuf->jb_type == JOINBUF_TYPE_PART || + jbuf->jb_type == JOINBUF_TYPE_PARTALL) { + /* Send notification to channel */ + if (!(flags & CHFL_ZOMBIE)) + sendcmdto_channel_butserv(jbuf->jb_source, CMD_PART, chan, + (flags & CHFL_BANNED || !jbuf->jb_comment) ? + ":%H" : "%H :%s", chan, jbuf->jb_comment); + else if (MyUser(jbuf->jb_source)) + sendcmdto_one(jbuf->jb_source, CMD_PART, jbuf->jb_source, + (flags & CHFL_BANNED || !jbuf->jb_comment) ? + ":%H" : "%H :%s", chan, jbuf->jb_comment); + + /* Remove user from channel */ + remove_user_from_channel(jbuf->jb_source, chan); + } else { + /* Add user to channel */ + add_user_to_channel(chan, jbuf->jb_source, flags); + + /* Send the notification to the channel */ + sendcmdto_channel_butserv(jbuf->jb_source, CMD_JOIN, chan, ":%H", chan); + + /* send an op, too, if needed */ + if (jbuf->jb_type == JOINBUF_TYPE_CREATE && + !IsModelessChannel(chan->chname)) + sendcmdto_channel_butserv(jbuf->jb_source, CMD_MODE, chan, "%H +o %C", + chan, jbuf->jb_source); + } + + if (jbuf->jb_type == JOINBUF_TYPE_PARTALL || IsLocalChannel(chan->chname)) + return; /* don't send to remote */ + } + + /* figure out if channel name will cause buffer to be overflowed */ + len = chan ? strlen(chan->chname) + 1 : 2; + if (jbuf->jb_strlen + len > IRC_BUFSIZE) + joinbuf_flush(jbuf); + + /* add channel to list of channels to send and update counts */ + jbuf->jb_channels[jbuf->jb_count++] = chan; + jbuf->jb_strlen += len; + + /* if we've used up all slots, flush */ + if (jbuf->jb_count >= MAXJOINARGS) + joinbuf_flush(jbuf); +} + +/* + * Flush the channel list to remote servers + */ +int +joinbuf_flush(struct JoinBuf *jbuf) +{ + char chanlist[IRC_BUFSIZE]; + int chanlist_i = 0; + int i; + + if (!jbuf->jb_count || jbuf->jb_type == JOINBUF_TYPE_PARTALL) + return 0; /* no joins to process */ + + for (i = 0; i < jbuf->jb_count; i++) { /* build channel list */ + build_string(chanlist, &chanlist_i, + jbuf->jb_channels[i] ? jbuf->jb_channels[i]->chname : "0", 0, + i == 0 ? '\0' : ','); + + jbuf->jb_channels[i] = 0; /* mark slot empty */ + } + + jbuf->jb_count = 0; /* reset base counters */ + jbuf->jb_strlen = ((jbuf->jb_type == JOINBUF_TYPE_JOIN || + jbuf->jb_type == JOINBUF_TYPE_PART ? + STARTJOINLEN : STARTCREATELEN) + + (jbuf->jb_comment ? strlen(jbuf->jb_comment) + 2 : 0)); + + /* and send the appropriate command */ + switch (jbuf->jb_type) { + case JOINBUF_TYPE_JOIN: + sendcmdto_serv_butone(jbuf->jb_source, CMD_JOIN, jbuf->jb_connect, + "%s", chanlist); + break; + + case JOINBUF_TYPE_CREATE: + sendcmdto_serv_butone(jbuf->jb_source, CMD_CREATE, jbuf->jb_connect, + "%s %Tu", chanlist, jbuf->jb_create); + break; + + case JOINBUF_TYPE_PART: + sendcmdto_serv_butone(jbuf->jb_source, CMD_PART, jbuf->jb_connect, + jbuf->jb_comment ? "%s :%s" : "%s", chanlist, + jbuf->jb_comment); + break; + } + + return 0; +} diff --git a/ircd/m_create.c b/ircd/m_create.c index 05bacc8..c3d537b 100644 --- a/ircd/m_create.c +++ b/ircd/m_create.c @@ -96,224 +96,87 @@ #include "msg.h" #include "numeric.h" #include "numnicks.h" +#include "s_debug.h" #include "send.h" #include #include #include -/* - * m_create - generic message handler - */ -int m_create(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) -{ - return 0; -} - /* * ms_create - server message handler */ int ms_create(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) { - char cbuf[BUFSIZE]; /* Buffer for list with channels - that `sptr' really creates */ - time_t chanTS; /* Creation time for all channels - in the comma seperated list */ - char* p; - char* name; - struct Channel* chptr; - int badop; + time_t chanTS; /* channel creation time */ + char *p; /* strtok state */ + char *name; /* channel name */ + struct Channel *chptr; /* channel */ + struct JoinBuf join; /* join and create buffers */ + struct JoinBuf create; + struct ModeBuf mbuf; /* a mode buffer */ + int badop; /* a flag */ if (IsServer(sptr)) { - /* PROTOCOL WARNING */ - /* bail, don't core */ + Debug((DEBUG_ERROR, "%s tried to CREATE a channel", sptr->name)); return 0; } + /* sanity checks: Only accept CREATE messages from servers */ if (!IsServer(cptr) || parc < 3 || *parv[2] == '\0') return 0; chanTS = atoi(parv[2]); + joinbuf_init(&join, sptr, cptr, JOINBUF_TYPE_JOIN, 0, 0); + joinbuf_init(&create, sptr, cptr, JOINBUF_TYPE_CREATE, 0, chanTS); + /* A create that didn't appear during a burst has that servers idea of * the current time. Use it for lag calculations. */ - if (!IsBurstOrBurstAck(sptr) && 0 != chanTS && MAGIC_REMOTE_JOIN_TS != chanTS) + if (!IsBurstOrBurstAck(sptr) && 0 != chanTS && + MAGIC_REMOTE_JOIN_TS != chanTS) sptr->user->server->serv->lag = TStime() - chanTS; - *cbuf = '\0'; /* Start with empty buffer */ - /* For each channel in the comma seperated list: */ - for (name = ircd_strtok(&p, parv[1], ","); name; name = ircd_strtok(&p, 0, ",")) - { - badop = 0; /* Default is to accept the op */ - if ((chptr = FindChannel(name))) - { - name = chptr->chname; - if (TStime() - chanTS > TS_LAG_TIME) - { - /* A bounce would not be accepted anyway - if we get here something - is wrong with the TS clock syncing (or we have more then - TS_LAG_TIME lag, or an admin is hacking */ - badop = 2; - /* This causes a HACK notice on all upstream servers: */ - sendto_one(cptr, "%s " TOK_MODE " %s -o %s%s 0", NumServ(&me), name, NumNick(sptr)); - /* This causes a WALLOPS on all downstream servers and a notice to our - own opers: */ - parv[1] = name; /* Corrupt parv[1], it is not used anymore anyway */ - send_hack_notice(cptr, sptr, parc, parv, badop, 2); - } - else if (chptr->creationtime && chanTS > chptr->creationtime && - chptr->creationtime != MAGIC_REMOTE_JOIN_TS) - { - /* We (try) to bounce the mode, because the CREATE is used on an older - channel, probably a net.ride */ - badop = 1; - /* Send a deop upstream: */ - sendto_one(cptr, "%s " TOK_MODE " %s -o %s%s " TIME_T_FMT, NumServ(&me), - name, NumNick(sptr), chptr->creationtime); - } - } - else /* Channel doesn't exist: create it */ - chptr = get_channel(sptr, name, CGT_CREATE); - - /* Add and mark ops */ - add_user_to_channel(chptr, sptr, - (badop || IsModelessChannel(name)) ? CHFL_DEOPPED : CHFL_CHANOP); - - /* Send user join to the local clients (if any) */ - sendto_channel_butserv(chptr, sptr, ":%s " MSG_JOIN " :%s", parv[0], name); - - if (badop) /* handle badop: convert CREATE into JOIN */ - sendto_serv_butone(cptr, "%s%s " TOK_JOIN " %s " TIME_T_FMT, - NumNick(sptr), name, chptr->creationtime); - else - { - /* Send the op to local clients: - (if any; extremely unlikely, but it CAN happen) */ - if (!IsModelessChannel(name)) - sendto_channel_butserv(chptr, sptr, ":%s MODE %s +o %s", - sptr->user->server->name, name, parv[0]); - - /* Set/correct TS and add the channel to the - buffer for accepted channels: */ - chptr->creationtime = chanTS; - if (*cbuf) - strcat(cbuf, ","); - strcat(cbuf, name); - } - } - - if (*cbuf) /* Any channel accepted with ops ? */ - { - sendto_serv_butone(cptr, "%s%s " TOK_CREATE " %s " TIME_T_FMT, - NumNick(sptr), cbuf, chanTS); - } - - return 0; -} + for (name = ircd_strtok(&p, parv[1], ","); name; + name = ircd_strtok(&p, 0, ",")) { + badop = 0; -#if 0 -/* - * m_create - * - * parv[0] = sender prefix - * parv[1] = channel names - * parv[2] = channel time stamp - */ -int m_create(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) -{ - char cbuf[BUFSIZE]; /* Buffer for list with channels - that `sptr' really creates */ - time_t chanTS; /* Creation time for all channels - in the comma seperated list */ - char* p; - char* name; - struct Channel* chptr; - int badop; + if (IsLocalChannel(name)) + continue; - /* sanity checks: Only accept CREATE messages from servers */ - if (!IsServer(cptr) || parc < 3 || *parv[2] == '\0') - return 0; + if ((chptr = FindChannel(name))) { + name = chptr->chname; - chanTS = atoi(parv[2]); + /* Check if we need to bounce a mode */ + if (TStime() - chanTS > TS_LAG_TIME || + (chptr->creationtime && chanTS > chptr->creationtime && + chptr->creationtime != MAGIC_REMOTE_JOIN_TS)) { + modebuf_init(&mbuf, sptr, cptr, chptr, + (MODEBUF_DEST_SERVER | /* Send mode to server */ + MODEBUF_DEST_HACK2 | /* Send a HACK(2) message */ + MODEBUF_DEST_BOUNCE)); /* And bounce the mode */ - /* A create that didn't appear during a burst has that servers idea of - * the current time. Use it for lag calculations. - */ - if (!IsBurstOrBurstAck(sptr) && 0 != chanTS && MAGIC_REMOTE_JOIN_TS != chanTS) - sptr->user->server->serv->lag = TStime() - chanTS; + modebuf_mode_client(&mbuf, MODE_ADD | MODE_CHANOP, sptr); - *cbuf = '\0'; /* Start with empty buffer */ + modebuf_flush(&mbuf); - /* For each channel in the comma seperated list: */ - for (name = ircd_strtok(&p, parv[1], ","); name; name = ircd_strtok(&p, 0, ",")) - { - badop = 0; /* Default is to accept the op */ - if ((chptr = FindChannel(name))) - { - name = chptr->chname; - if (TStime() - chanTS > TS_LAG_TIME) - { - /* A bounce would not be accepted anyway - if we get here something - is wrong with the TS clock syncing (or we have more then - TS_LAG_TIME lag, or an admin is hacking */ - badop = 2; - /* This causes a HACK notice on all upstream servers: */ - sendto_one(cptr, "%s " TOK_MODE " %s -o %s%s 0", NumServ(&me), name, NumNick(sptr)); - /* This causes a WALLOPS on all downstream servers and a notice to our - own opers: */ - parv[1] = name; /* Corrupt parv[1], it is not used anymore anyway */ - send_hack_notice(cptr, sptr, parc, parv, badop, 2); - } - else if (chptr->creationtime && chanTS > chptr->creationtime && - chptr->creationtime != MAGIC_REMOTE_JOIN_TS) - { - /* We (try) to bounce the mode, because the CREATE is used on an older - channel, probably a net.ride */ - badop = 1; - /* Send a deop upstream: */ - sendto_one(cptr, "%s " TOK_MODE " %s -o %s%s " TIME_T_FMT, NumServ(&me), - name, NumNick(sptr), chptr->creationtime); + badop = 1; } - } - else /* Channel doesn't exist: create it */ + } else /* Channel doesn't exist: create it */ chptr = get_channel(sptr, name, CGT_CREATE); - /* Add and mark ops */ - add_user_to_channel(chptr, sptr, - (badop || IsModelessChannel(name)) ? CHFL_DEOPPED : CHFL_CHANOP); - - /* Send user join to the local clients (if any) */ - sendto_channel_butserv(chptr, sptr, ":%s " MSG_JOIN " :%s", parv[0], name); - - if (badop) /* handle badop: convert CREATE into JOIN */ - sendto_serv_butone(cptr, "%s%s " TOK_JOIN " %s " TIME_T_FMT, - NumNick(sptr), name, chptr->creationtime); - else - { - /* Send the op to local clients: - (if any; extremely unlikely, but it CAN happen) */ - if (!IsModelessChannel(name)) - sendto_channel_butserv(chptr, sptr, ":%s MODE %s +o %s", - sptr->user->server->name, name, parv[0]); - - /* Set/correct TS and add the channel to the - buffer for accepted channels: */ + if (!badop) /* Set/correct TS */ chptr->creationtime = chanTS; - if (*cbuf) - strcat(cbuf, ","); - strcat(cbuf, name); - } - } - if (*cbuf) /* Any channel accepted with ops ? */ - { - sendto_serv_butone(cptr, "%s%s " TOK_CREATE " %s " TIME_T_FMT, - NumNick(sptr), cbuf, chanTS); + joinbuf_join(badop ? &join : &create, chptr, + (badop || IsModelessChannel(name)) ? + CHFL_DEOPPED : CHFL_CHANOP); } + joinbuf_flush(&join); /* flush out the joins and creates */ + joinbuf_flush(&create); + return 0; } -#endif /* 0 */ - diff --git a/ircd/m_join.c b/ircd/m_join.c index d52a34e..b09778a 100644 --- a/ircd/m_join.c +++ b/ircd/m_join.c @@ -98,6 +98,7 @@ #include "msg.h" #include "numeric.h" #include "numnicks.h" +#include "s_debug.h" #include "s_user.h" #include "send.h" @@ -110,712 +111,267 @@ #endif /* - * m_join - generic message handler + * Helper function to find last 0 in a comma-separated list of + * channel names. */ -int m_join(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) +static char * +last0(char *chanlist) { - static char jbuf[BUFSIZE]; - static char mbuf[BUFSIZE]; - struct Membership* member; - struct Channel* chptr; - char* name; - char* keysOrTS = NULL; - int i = 0; - int zombie = 0; - int sendcreate = 0; - unsigned int flags = 0; - size_t jlen = 0; - size_t mlen = 0; - size_t* buflen; - char* p = NULL; - char* bufptr; + char *p; + for (p = chanlist; p[0]; p++) /* find last "JOIN 0" */ + if (p[0] == '0' && (p[1] == ',' || p[1] == '\0' || !IsChannelChar(p[1]))) { + chanlist = p; /* we'll start parsing here */ - if (parc < 2 || *parv[1] == '\0') - return need_more_params(sptr, "JOIN"); + if (!p[1]) /* hit the end */ + break; - for (p = parv[1]; *p; p++) /* find the last "JOIN 0" in the line -Kev */ - if (*p == '0' - && (*(p + 1) == ',' || *(p + 1) == '\0' || !IsChannelChar(*(p + 1)))) - { - /* If it's a single "0", remember the place; we will start parsing - the channels after the last 0 in the line -Kev */ - parv[1] = p; - if (!*(p + 1)) - break; p++; - } - else - { /* Step through to the next comma or until the - end of the line, in an attempt to save CPU - -Kev */ - while (*p != ',' && *p != '\0') - p++; - if (!*p) - break; - } + } else { + while (p[0] != ',' && p[0] != '\0') /* skip past channel name */ + p++; - keysOrTS = parv[2]; /* Remember where our keys are or the TS is; - parv[2] needs to be NULL for the call to - m_names below -Kev */ - parv[2] = p = NULL; - - *jbuf = *mbuf = '\0'; /* clear both join and mode buffers -Kev */ - /* - * Rebuild list of channels joined to be the actual result of the - * JOIN. Note that "JOIN 0" is the destructive problem. - */ - for (name = ircd_strtok(&p, parv[1], ","); name; name = ircd_strtok(&p, NULL, ",")) - { - size_t len; - if (MyConnect(sptr)) - clean_channelname(name); - else if (IsLocalChannel(name)) - continue; - if (*name == '0' && *(name + 1) == '\0') - { - /* Remove the user from all his channels -Kev */ - while ((member = sptr->user->channel)) - { - chptr = member->channel; - if (!IsZombie(member)) - sendto_channel_butserv(chptr, sptr, PartFmt2, - parv[0], chptr->chname, "Left all channels"); - remove_user_from_channel(sptr, chptr); - } - /* Just in case */ - *mbuf = *jbuf = '\0'; - mlen = jlen = 0; + if (!p[0]) /* hit the end */ + break; } - else - { /* not a /join 0, so treat it as - a /join #channel -Kev */ - if (!IsChannelName(name)) - { - if (MyUser(sptr)) - sendto_one(sptr, err_str(ERR_NOSUCHCHANNEL), me.name, parv[0], name); - continue; - } - - if (MyConnect(sptr)) - { -#ifdef BADCHAN - struct Gline *gline; - - if ((gline = gline_find(name, GLINE_BADCHAN | GLINE_EXACT)) && - GlineIsActive(gline) && !IsAnOper(sptr)) - { - sendto_one(sptr, err_str(ERR_BADCHANNAME), me.name, parv[0], name); - continue; - } -#endif - /* - * Local client is first to enter previously nonexistant - * channel so make them (rightfully) the Channel Operator. - * This looks kind of ugly because we try to avoid calling the strlen() - */ - if (ChannelExists(name)) - { - flags = CHFL_DEOPPED; - sendcreate = 0; - } - else if (strlen(name) > CHANNELLEN) - { - *(name + CHANNELLEN) = '\0'; - if (ChannelExists(name)) - { - flags = CHFL_DEOPPED; - sendcreate = 0; - } - else - { - flags = IsModelessChannel(name) ? CHFL_DEOPPED : CHFL_CHANOP; - sendcreate = 1; - } - } - else - { - flags = IsModelessChannel(name) ? CHFL_DEOPPED : CHFL_CHANOP; - sendcreate = 1; - } -#ifdef OPER_NO_CHAN_LIMIT - /* - * Opers are allowed to join any number of channels - */ - if (sptr->user->joined >= MAXCHANNELSPERUSER && !IsAnOper(sptr)) -#else - if (sptr->user->joined >= MAXCHANNELSPERUSER) -#endif - { - chptr = get_channel(sptr, name, CGT_NO_CREATE); - sendto_one(sptr, err_str(ERR_TOOMANYCHANNELS), - me.name, parv[0], chptr ? chptr->chname : name); - /* - * Can't return, else he won't get on ANY channels! - * Break out of the for loop instead. -Kev - */ - break; - } - } - chptr = get_channel(sptr, name, CGT_CREATE); - if (chptr && (member = find_member_link(chptr, sptr))) - { - if (IsZombie(member)) - { - zombie = 1; - flags = member->status & (CHFL_DEOPPED | CHFL_SERVOPOK); - remove_user_from_channel(sptr, chptr); - chptr = get_channel(sptr, name, CGT_CREATE); - } - else - continue; - } - name = chptr->chname; - if (!chptr->creationtime) /* A remote JOIN created this channel ? */ - chptr->creationtime = MAGIC_REMOTE_JOIN_TS; - if (parc > 2) - { - if (chptr->creationtime == MAGIC_REMOTE_JOIN_TS) - chptr->creationtime = atoi(keysOrTS); - else - parc = 2; /* Don't pass it on */ - } - if (!zombie) - { - if (!MyConnect(sptr)) - flags = CHFL_DEOPPED; - if (sptr->flags & FLAGS_TS8) - flags |= CHFL_SERVOPOK; - } - if (MyConnect(sptr)) - { - int created = chptr->users == 0; - if (check_target_limit(sptr, chptr, chptr->chname, created)) - { - if (created) /* Did we create the channel? */ - sub1_from_channel(chptr); /* Remove it again! */ - continue; - } - if ((i = can_join(sptr, chptr, keysOrTS))) - { -#ifdef OPER_WALK_THROUGH_LMODES - if (i > MAGIC_OPER_OVERRIDE) - { - switch(i - MAGIC_OPER_OVERRIDE) - { - case ERR_CHANNELISFULL: i = 'l'; break; - case ERR_INVITEONLYCHAN: i = 'i'; break; - case ERR_BANNEDFROMCHAN: i = 'b'; break; - case ERR_BADCHANNELKEY: i = 'k'; break; - } - sendto_op_mask(SNO_HACK4,"OPER JOIN: %s JOIN %s (overriding +%c)",sptr->name,chptr->chname,i); - } - else - { - sendto_one(sptr, err_str(i), me.name, parv[0], chptr->chname); - continue; - } -#else - sendto_one(sptr, err_str(i), me.name, parv[0], chptr->chname); - continue; -#endif - } - } - /* - * Complete user entry to the new channel (if any) - */ - add_user_to_channel(chptr, sptr, flags); - - /* - * Notify all other users on the new channel - */ - sendto_channel_butserv(chptr, sptr, ":%s JOIN :%s", parv[0], name); - - if (MyUser(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; - m_names(cptr, sptr, 2, parv); - } - } + return chanlist; +} - /* Select proper buffer; mbuf for creation, jbuf otherwise */ - - if (*name == '&') - continue; /* Head off local channels at the pass */ - - bufptr = (sendcreate == 0) ? jbuf : mbuf; - buflen = (sendcreate == 0) ? &jlen : &mlen; - len = strlen(name); - if (*buflen < BUFSIZE - len - 2) - { - if (*bufptr) - { - strcat(bufptr, ","); /* Add to join buf */ - *buflen += 1; - } - strncat(bufptr, name, BUFSIZE - *buflen - 1); - *buflen += len; - } - sendcreate = 0; /* Reset sendcreate */ - } +/* + * Helper function to perform a JOIN 0 if needed; returns 0 if channel + * name is not 0, else removes user from all channels and returns 1. + */ +static int +join0(struct JoinBuf *join, struct Client *cptr, struct Client *sptr, + char *chan) +{ + struct Membership *member; + struct JoinBuf part; - if (*jbuf) /* Propgate joins to P10 servers */ - sendto_serv_butone(cptr, - parc > 2 ? "%s%s " TOK_JOIN " %s %s" : "%s%s " TOK_JOIN " %s", NumNick(sptr), jbuf, keysOrTS); - if (*mbuf) /* and now creation events */ - sendto_serv_butone(cptr, "%s%s " TOK_CREATE " %s " TIME_T_FMT, - NumNick(sptr), mbuf, TStime()); - - if (MyUser(sptr)) - { /* shouldn't ever set TS for remote JOIN's */ - if (*jbuf) - { /* check for channels that need TS's */ - p = NULL; - for (name = ircd_strtok(&p, jbuf, ","); name; name = ircd_strtok(&p, NULL, ",")) - { - chptr = get_channel(sptr, name, CGT_NO_CREATE); - if (chptr && chptr->mode.mode & MODE_SENDTS) - { /* send a TS? */ - sendto_serv_butone(cptr, "%s " TOK_MODE " %s + " TIME_T_FMT, NumServ(&me), - chptr->chname, chptr->creationtime); /* ok, send TS */ - chptr->mode.mode &= ~MODE_SENDTS; /* reset flag */ - } - } - } - } - return 0; + /* is it a JOIN 0? */ + if (chan[0] != '0' || chan[1] != '\0') + return 0; + + joinbuf_join(join, 0, 0); /* join special channel 0 */ + + /* leave all channels */ + joinbuf_init(&part, sptr, cptr, JOINBUF_TYPE_PARTALL, + "Left all channels", 0); + + while ((member = sptr->user->channel)) + joinbuf_join(&part, member->channel, + IsZombie(member) ? CHFL_ZOMBIE : 0); + + joinbuf_flush(&part); + + return 1; } /* - * ms_join - server message handler + * m_join - generic message handler */ -int ms_join(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) +int m_join(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) { - static char jbuf[BUFSIZE]; - static char mbuf[BUFSIZE]; - struct Membership* member; - struct Channel* chptr; - char* name; - char* keysOrTS = NULL; - int zombie = 0; - int sendcreate = 0; - unsigned int flags = 0; - size_t jlen = 0; - size_t mlen = 0; - size_t* buflen; - char* p = NULL; - char* bufptr; - - /* - * Doesn't make sense having a server join a channel, and besides - * the server cores. - */ - if (IsServer(sptr)) - return 0; + struct Channel *chptr; + struct JoinBuf join; + struct JoinBuf create; +#ifdef BADCHAN + struct Gline *gline; +#endif + unsigned int flags = 0; + int i; + char *p = 0; + char *chanlist; + char *name; + char *keys; if (parc < 2 || *parv[1] == '\0') return need_more_params(sptr, "JOIN"); - keysOrTS = parv[2]; /* Remember where our keys are or the TS is; - parv[2] needs to be NULL for the call to - m_names below -Kev */ - parv[2] = p = NULL; - - *jbuf = *mbuf = '\0'; /* clear both join and mode buffers -Kev */ - /* - * Rebuild list of channels joined to be the actual result of the - * JOIN. Note that "JOIN 0" is the destructive problem. - */ - for (name = ircd_strtok(&p, parv[1], ","); name; name = ircd_strtok(&p, NULL, ",")) - { - size_t len; - if (IsLocalChannel(name)) + joinbuf_init(&join, sptr, cptr, JOINBUF_TYPE_JOIN, 0, 0); + joinbuf_init(&create, sptr, cptr, JOINBUF_TYPE_CREATE, 0, TStime()); + + chanlist = last0(parv[1]); /* find last "JOIN 0" */ + + keys = parv[2]; /* remember where keys are */ + + parv[2] = 0; /* for call to m_names below */ + + for (name = ircd_strtok(&p, chanlist, ","); name; + name = ircd_strtok(&p, 0, ",")) { + clean_channelname(name); + + if (join0(&join, cptr, sptr, name)) /* did client do a JOIN 0? */ continue; - if (!IsChannelName(name)) - { - sendto_one(sptr, err_str(ERR_NOSUCHCHANNEL), me.name, parv[0], name); + if (!IsChannelName(name)) { /* bad channel name */ + send_reply(sptr, ERR_NOSUCHCHANNEL, name); continue; } - chptr = get_channel(sptr, name, CGT_CREATE); - if (chptr && (member = find_member_link(chptr, sptr))) - { - if (!IsZombie(member)) - continue; - - /* If they are a zombie, make them really part - * and rejoin - */ - zombie = 1; - flags = member->status & (CHFL_DEOPPED | CHFL_SERVOPOK); - remove_user_from_channel(sptr, chptr); - chptr = get_channel(sptr, name, CGT_CREATE); - - } - - name = chptr->chname; - - if (!chptr->creationtime) /* A remote JOIN created this channel ? */ - chptr->creationtime = MAGIC_REMOTE_JOIN_TS; - - if (parc > 2) - { - if (chptr->creationtime == MAGIC_REMOTE_JOIN_TS) - chptr->creationtime = atoi(keysOrTS); - else - parc = 2; /* Don't pass it on */ +#ifdef BADCHAN + /* BADCHANed channel */ + if ((gline = gline_find(name, GLINE_BADCHAN | GLINE_EXACT)) && + GlineIsActive(gline) && !IsAnOper(sptr)) { + send_reply(sptr, ERR_BADCHANNAME, name); + continue; } - - if (!zombie) - { - if (sptr->flags & FLAGS_TS8) - flags |= CHFL_SERVOPOK; +#endif + + if ((chptr = FindChannel(name))) { + if (find_member_link(chptr, sptr)) + continue; /* already on channel */ + + flags = CHFL_DEOPPED; + } else + flags = IsModelessChannel(name) ? CHFL_DEOPPED : CHFL_CHANOP; + + if (sptr->user->joined >= MAXCHANNELSPERUSER +#ifdef OPER_NO_CHAN_LIMIT + /* Opers are allowed to join any number of channels */ + && !IsAnOper(sptr) +#endif + ) { + send_reply(sptr, ERR_TOOMANYCHANNELS, chptr ? chptr->chname : name); + break; /* no point processing the other channels */ } - - /* - * Complete user entry to the new channel (if any) - */ - add_user_to_channel(chptr, sptr, flags); - /* - * Notify all other users on the new channel - */ - sendto_channel_butserv(chptr, sptr, ":%s JOIN :%s", parv[0], name); - - /* Select proper buffer; mbuf for creation, jbuf otherwise */ - - if (*name == '&') - continue; /* Head off local channels at the pass */ - - bufptr = (sendcreate == 0) ? jbuf : mbuf; - buflen = (sendcreate == 0) ? &jlen : &mlen; - len = strlen(name); - - if (*buflen < BUFSIZE - len - 2) - { - if (*bufptr) - { - strcat(bufptr, ","); /* Add to join buf */ - *buflen += 1; - } - strncat(bufptr, name, BUFSIZE - *buflen - 1); - *buflen += len; + if (chptr) { + if (check_target_limit(sptr, chptr, chptr->chname, 0)) + continue; /* exceeded target limit */ + else if ((i = can_join(sptr, chptr, keys))) { +#ifdef OPER_WALK_THROUGH_LMODES + if (i > MAGIC_OPER_OVERRIDE) { /* oper overrode mode */ + switch (i - MAGIC_OPER_OVERRIDE) { + case ERR_CHANNELISFULL: /* figure out which mode */ + i = 'l'; + break; + + case ERR_INVITEONLYCHAN: + i = 'i'; + break; + + case ERR_BANNEDFROMCHAN: + i = 'b'; + break; + + case ERR_BADCHANNELKEY: + i = 'k'; + break; + } + + /* send accountability notice */ + sendto_opmask_butone(0, SNO_HACK4, "OPER JOIN: %C JOIN %H " + "(overriding +%c)", sptr, chptr, i); + } else { + send_reply(sptr, i, chptr->chname); + continue; + } +#else + send_reply(sptr, i, chptr->chname); + continue; +#endif + } /* else if ((i = can_join(sptr, chptr, keys))) { */ + + joinbuf_join(&join, chptr, flags); + } else if (!(chptr = get_channel(sptr, name, CGT_CREATE))) + continue; /* couldn't get channel */ + else if (check_target_limit(sptr, chptr, chptr->chname, 1)) { + /* Note: check_target_limit will only ever return 0 here */ + sub1_from_channel(chptr); /* created it... */ + continue; + } else + joinbuf_join(&create, chptr, flags); + + del_invite(sptr, chptr); + + if (chptr->topic[0]) { + send_reply(sptr, RPL_TOPIC, chptr->chname, chptr->topic); + send_reply(sptr, RPL_TOPICWHOTIME, chptr->chname, chptr->topic_nick, + chptr->topic_time); } - sendcreate = 0; /* Reset sendcreate */ + + parv[1] = name; + m_names(cptr, sptr, 2, parv); /* XXX find a better way */ } - if (*jbuf) /* Propgate joins to P10 servers */ - sendto_serv_butone(cptr, - parc > 2 ? "%s%s " TOK_JOIN " %s %s" : "%s%s " TOK_JOIN " %s", NumNick(sptr), jbuf, keysOrTS); - if (*mbuf) /* and now creation events */ - sendto_serv_butone(cptr, "%s%s " TOK_CREATE " %s " TIME_T_FMT, - NumNick(sptr), mbuf, TStime()); + joinbuf_flush(&join); /* must be first, if there's a JOIN 0 */ + joinbuf_flush(&create); return 0; } - -#if 0 /* - * m_join - * - * parv[0] = sender prefix - * parv[1] = channel - * parv[2] = channel keys (client), or channel TS (server) + * ms_join - server message handler */ -int m_join(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) +int ms_join(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) { - static char jbuf[BUFSIZE]; - static char mbuf[BUFSIZE]; - struct Membership* member; - struct Channel* chptr; - char* name; - char* keysOrTS = NULL; - int i = 0; - int zombie = 0; - int sendcreate = 0; - unsigned int flags = 0; - size_t jlen = 0; - size_t mlen = 0; - size_t* buflen; - char* p = NULL; - char* bufptr; - - /* - * Doesn't make sense having a server join a channel, and besides - * the server cores. - */ - if (IsServer(sptr)) + struct Membership *member; + struct Channel *chptr; + struct JoinBuf join; + struct JoinBuf create; + unsigned int flags = 0; + char *p = 0; + char *chanlist; + char *name; + + if (IsServer(sptr)) { + Debug((DEBUG_ERROR, "%s tried to JOIN a channel", sptr->name)); return 0; + } if (parc < 2 || *parv[1] == '\0') return need_more_params(sptr, "JOIN"); - for (p = parv[1]; *p; p++) /* find the last "JOIN 0" in the line -Kev */ - if (*p == '0' - && (*(p + 1) == ',' || *(p + 1) == '\0' || !IsChannelChar(*(p + 1)))) - { - /* If it's a single "0", remember the place; we will start parsing - the channels after the last 0 in the line -Kev */ - parv[1] = p; - if (!*(p + 1)) - break; - p++; - } - else - { /* Step through to the next comma or until the - end of the line, in an attempt to save CPU - -Kev */ - while (*p != ',' && *p != '\0') - p++; - if (!*p) - break; - } + joinbuf_init(&join, sptr, cptr, JOINBUF_TYPE_JOIN, 0, 0); + joinbuf_init(&create, sptr, cptr, JOINBUF_TYPE_CREATE, 0, TStime()); + + chanlist = last0(parv[1]); /* find last "JOIN 0" */ + + for (name = ircd_strtok(&p, chanlist, ","); name; + name = ircd_strtok(&p, 0, ",")) { + clean_channelname(name); - keysOrTS = parv[2]; /* Remember where our keys are or the TS is; - parv[2] needs to be NULL for the call to - m_names below -Kev */ - parv[2] = p = NULL; - - *jbuf = *mbuf = '\0'; /* clear both join and mode buffers -Kev */ - /* - * Rebuild list of channels joined to be the actual result of the - * JOIN. Note that "JOIN 0" is the destructive problem. - */ - for (name = ircd_strtok(&p, parv[1], ","); name; name = ircd_strtok(&p, NULL, ",")) - { - size_t len; - if (MyConnect(sptr)) - clean_channelname(name); - else if (IsLocalChannel(name)) + if (join0(&join, cptr, sptr, name)) /* did client do a JOIN 0? */ continue; - if (*name == '0' && *(name + 1) == '\0') - { - /* Remove the user from all his channels -Kev */ - while ((member = sptr->user->channel)) - { - chptr = member->channel; - if (!IsZombie(member)) - sendto_channel_butserv(chptr, sptr, PartFmt2, - parv[0], chptr->chname, "Left all channels"); - remove_user_from_channel(sptr, chptr); - } - /* Just in case */ - *mbuf = *jbuf = '\0'; - mlen = jlen = 0; - } - else - { /* not a /join 0, so treat it as - a /join #channel -Kev */ - if (!IsChannelName(name)) - { - if (MyUser(sptr)) - sendto_one(sptr, err_str(ERR_NOSUCHCHANNEL), me.name, parv[0], name); - continue; - } - - if (MyConnect(sptr)) - { -#ifdef BADCHAN - struct Gline *gline; - - if ((gline = gline_find(name, GLINE_BADCHAN | GLINE_EXACT)) && - GlineIsActive(gline) && !IsAnOper(sptr)) - { - sendto_one(sptr, err_str(ERR_BADCHANNAME), me.name, parv[0], name); - continue; - } -#endif - /* - * Local client is first to enter previously nonexistant - * channel so make them (rightfully) the Channel Operator. - * This looks kind of ugly because we try to avoid calling the strlen() - */ - if (ChannelExists(name)) - { - flags = CHFL_DEOPPED; - sendcreate = 0; - } - else if (strlen(name) > CHANNELLEN) - { - *(name + CHANNELLEN) = '\0'; - if (ChannelExists(name)) - { - flags = CHFL_DEOPPED; - sendcreate = 0; - } - else - { - flags = IsModelessChannel(name) ? CHFL_DEOPPED : CHFL_CHANOP; - sendcreate = 1; - } - } - else - { - flags = IsModelessChannel(name) ? CHFL_DEOPPED : CHFL_CHANOP; - sendcreate = 1; - } -#ifdef OPER_NO_CHAN_LIMIT - /* - * Opers are allowed to join any number of channels - */ - if (sptr->user->joined >= MAXCHANNELSPERUSER && !IsAnOper(sptr)) -#else - if (sptr->user->joined >= MAXCHANNELSPERUSER) -#endif - { - chptr = get_channel(sptr, name, CGT_NO_CREATE); - sendto_one(sptr, err_str(ERR_TOOMANYCHANNELS), - me.name, parv[0], chptr ? chptr->chname : name); - /* - * Can't return, else he won't get on ANY channels! - * Break out of the for loop instead. -Kev - */ - break; - } - } - chptr = get_channel(sptr, name, CGT_CREATE); - if (chptr && (member = find_member_link(chptr, sptr))) - { - if (IsZombie(member)) - { - zombie = 1; - flags = member->status & (CHFL_DEOPPED | CHFL_SERVOPOK); - remove_user_from_channel(sptr, chptr); - chptr = get_channel(sptr, name, CGT_CREATE); - } - else - continue; - } - name = chptr->chname; - if (!chptr->creationtime) /* A remote JOIN created this channel ? */ - chptr->creationtime = MAGIC_REMOTE_JOIN_TS; - if (parc > 2) - { - if (chptr->creationtime == MAGIC_REMOTE_JOIN_TS) - chptr->creationtime = atoi(keysOrTS); - else - parc = 2; /* Don't pass it on */ - } - if (!zombie) - { - if (!MyConnect(sptr)) - flags = CHFL_DEOPPED; - if (sptr->flags & FLAGS_TS8) - flags |= CHFL_SERVOPOK; - } - if (MyConnect(sptr)) - { - int created = chptr->users == 0; - if (check_target_limit(sptr, chptr, chptr->chname, created)) - { - if (created) /* Did we create the channel? */ - sub1_from_channel(chptr); /* Remove it again! */ - continue; - } - if ((i = can_join(sptr, chptr, keysOrTS))) - { - sendto_one(sptr, err_str(i), me.name, parv[0], chptr->chname); -#ifdef OPER_WALK_THROUGH_LMODES - if (i > MAGIC_OPER_OVERRIDE) - { - switch(i - MAGIC_OPER_OVERRIDE) - { - case ERR_CHANNELISFULL: i = 'l'; break; - case ERR_INVITEONLYCHAN: i = 'i'; break; - case ERR_BANNEDFROMCHAN: i = 'b'; break; - case ERR_BADCHANNELKEY: i = 'k'; break; - } - sendto_op_mask(SNO_HACK4,"OPER JOIN: %s JOIN %s (overriding +%c)",sptr->name,chptr->chname,i); - } - else - continue; -#else - continue; -#endif - } - } - /* - * Complete user entry to the new channel (if any) - */ - add_user_to_channel(chptr, sptr, flags); - - /* - * Notify all other users on the new channel - */ - sendto_channel_butserv(chptr, sptr, ":%s JOIN :%s", parv[0], name); - - if (MyUser(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; - m_names(cptr, sptr, 2, parv); - } - } + if (IsLocalChannel(name) || !IsChannelName(name)) + continue; - /* Select proper buffer; mbuf for creation, jbuf otherwise */ - - if (*name == '&') - continue; /* Head off local channels at the pass */ - - bufptr = (sendcreate == 0) ? jbuf : mbuf; - buflen = (sendcreate == 0) ? &jlen : &mlen; - len = strlen(name); - if (*buflen < BUFSIZE - len - 2) - { - if (*bufptr) - { - strcat(bufptr, ","); /* Add to join buf */ - *buflen += 1; - } - strncat(bufptr, name, BUFSIZE - *buflen - 1); - *buflen += len; + if ((chptr = FindChannel(name))) { + if ((member = find_member_link(chptr, sptr))) { + if (!IsZombie(member)) /* already on channel */ + continue; + + flags = member->status & (CHFL_DEOPPED | CHFL_SERVOPOK); + remove_user_from_channel(sptr, chptr); + chptr = FindChannel(name); + } else + flags = CHFL_DEOPPED | ((sptr->flags & FLAGS_TS8) ? CHFL_SERVOPOK : 0); + } else + flags = IsModelessChannel(name) ? CHFL_DEOPPED : CHFL_CHANOP; + + if (chptr) + joinbuf_join(&join, chptr, flags); + /* Strange that a channel should get created by a JOIN, but as long + * as there are desynchs, we have to assume it's possible. Previous + * solutions involved sending a TS with the JOIN, but that seems + * unworkable to me; my solution is to transmute the JOIN into a + * CREATE, and let the next BURST try to fix the problem. Another + * solution would be to simply issue an upstream KICK, but that + * won't currently be accepted. Is this what DESTRUCT is for? + */ + else if (!(chptr = get_channel(sptr, name, CGT_CREATE))) + continue; /* couldn't get channel */ + else { + chptr->creationtime = TStime(); /* have to set the creation TS */ + joinbuf_join(&create, chptr, flags); } - sendcreate = 0; /* Reset sendcreate */ } - if (*jbuf) /* Propgate joins to P10 servers */ - sendto_serv_butone(cptr, - parc > 2 ? "%s%s " TOK_JOIN " %s %s" : "%s%s " TOK_JOIN " %s", NumNick(sptr), jbuf, keysOrTS); - if (*mbuf) /* and now creation events */ - sendto_serv_butone(cptr, "%s%s " TOK_CREATE " %s " TIME_T_FMT, - NumNick(sptr), mbuf, TStime()); - - if (MyUser(sptr)) - { /* shouldn't ever set TS for remote JOIN's */ - if (*jbuf) - { /* check for channels that need TS's */ - p = NULL; - for (name = ircd_strtok(&p, jbuf, ","); name; name = ircd_strtok(&p, NULL, ",")) - { - chptr = get_channel(sptr, name, CGT_NO_CREATE); - if (chptr && chptr->mode.mode & MODE_SENDTS) - { /* send a TS? */ - sendto_serv_butone(cptr, "%s " TOK_MODE " %s + " TIME_T_FMT, NumServ(&me), - chptr->chname, chptr->creationtime); /* ok, send TS */ - chptr->mode.mode &= ~MODE_SENDTS; /* reset flag */ - } - } - } - } + joinbuf_flush(&join); /* must be first, if there's a JOIN 0 */ + joinbuf_flush(&create); + return 0; } -#endif /* 0 */ diff --git a/ircd/m_part.c b/ircd/m_part.c index d4de9b3..4cf098a 100644 --- a/ircd/m_part.c +++ b/ircd/m_part.c @@ -109,80 +109,46 @@ */ int m_part(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) { - struct Channel* chptr; - struct Membership* member; - char* p = 0; - char* name; - char pbuf[BUFSIZE]; - char* comment = (parc > 2 && !EmptyString(parv[parc - 1])) ? parv[parc - 1] : 0; - - *pbuf = '\0'; /* Initialize the part buffer... -Kev */ + struct Channel *chptr; + struct Membership *member; + struct JoinBuf parts; + char *p = 0; + char *name; sptr->flags &= ~FLAGS_TS8; + /* check number of arguments */ if (parc < 2 || parv[1][0] == '\0') return need_more_params(sptr, "PART"); - for (; (name = ircd_strtok(&p, parv[1], ",")); parv[1] = 0) - { - chptr = get_channel(sptr, name, CGT_NO_CREATE); - if (!chptr) { - sendto_one(sptr, err_str(ERR_NOSUCHCHANNEL), me.name, parv[0], name); + /* init join/part buffer */ + joinbuf_init(&parts, sptr, cptr, JOINBUF_TYPE_PART, + (parc > 2 && !EmptyString(parv[parc - 1])) ? parv[parc - 1] : 0, + 0); + + /* scan through channel list */ + for (name = ircd_strtok(&p, parv[1], ","); name; + name = ircd_strtok(&p, 0, ",")) { + + chptr = get_channel(sptr, name, CGT_NO_CREATE); /* look up channel */ + + if (!chptr) { /* complain if there isn't such a channel */ + send_reply(sptr, ERR_NOSUCHCHANNEL, name); continue; } - if (*name == '&' && !MyUser(sptr)) - continue; - /* - * Do not use find_channel_member here: zombies must be able to part too - */ - if (!(member = find_member_link(chptr, sptr))) - { - /* Normal to get when our client did a kick - * for a remote client (who sends back a PART), - * so check for remote client or not --Run - */ - if (MyUser(sptr)) - sendto_one(sptr, err_str(ERR_NOTONCHANNEL), me.name, parv[0], - chptr->chname); + + if (!(member = find_member_link(chptr, sptr))) { /* complain if not on */ + send_reply(sptr, ERR_NOTONCHANNEL, chptr->chname); continue; } - /* Recreate the /part list for sending to servers */ - if (*name != '&') - { - if (*pbuf) - strcat(pbuf, ","); - strcat(pbuf, name); - } - if (IsZombie(member) - || !member_can_send_to_channel(member)) /* Returns 1 if we CAN send */ - comment = 0; - /* Send part to all clients */ - if (!IsZombie(member)) - { - if (comment) - sendto_channel_butserv(chptr, sptr, PartFmt2, parv[0], chptr->chname, - comment); - else - sendto_channel_butserv(chptr, sptr, PartFmt1, parv[0], chptr->chname); - } - else if (MyUser(sptr)) - { - if (comment) - sendto_one(sptr, PartFmt2, parv[0], chptr->chname, comment); - else - sendto_one(sptr, PartFmt1, parv[0], chptr->chname); - } - remove_user_from_channel(sptr, chptr); - } - /* Send out the parts to all servers... -Kev */ - if (*pbuf) - { - if (comment) - sendto_serv_butone(cptr, PartFmt2serv, NumNick(sptr), pbuf, comment); - else - sendto_serv_butone(cptr, PartFmt1serv, NumNick(sptr), pbuf); + + assert(!IsZombie(member)); /* Local users should never zombie */ + + joinbuf_join(&parts, chptr, /* part client from channel */ + member_can_send_to_channel(member) ? 0 : CHFL_BANNED); } - return 0; + + return joinbuf_flush(&parts); /* flush channel parts */ } /* @@ -194,165 +160,49 @@ int m_part(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) */ int ms_part(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) { - struct Channel* chptr; - struct Membership* member; - char* p = 0; - char* name; - char pbuf[BUFSIZE]; - char* comment = (parc > 2 && !EmptyString(parv[parc - 1])) ? parv[parc - 1] : 0; - - *pbuf = '\0'; /* Initialize the part buffer... -Kev */ + struct Channel *chptr; + struct Membership *member; + struct JoinBuf parts; + unsigned int flags; + char *p = 0; + char *name; sptr->flags &= ~FLAGS_TS8; + /* check number of arguments */ if (parc < 2 || parv[1][0] == '\0') return need_more_params(sptr, "PART"); - for (; (name = ircd_strtok(&p, parv[1], ",")); parv[1] = 0) - { - chptr = get_channel(sptr, name, CGT_NO_CREATE); - if (!chptr) { - sendto_one(sptr, err_str(ERR_NOSUCHCHANNEL), me.name, parv[0], name); - continue; - } - if (*name == '&' && !MyUser(sptr)) - continue; - /* - * Do not use find_channel_member here: zombies must be able to part too - */ - if (!(member = find_member_link(chptr, sptr))) - { - /* Normal to get when our client did a kick - * for a remote client (who sends back a PART), - * so check for remote client or not --Run - */ - if (MyUser(sptr)) - sendto_one(sptr, err_str(ERR_NOTONCHANNEL), me.name, parv[0], - chptr->chname); - continue; - } - /* Recreate the /part list for sending to servers */ - if (*name != '&') - { - if (*pbuf) - strcat(pbuf, ","); - strcat(pbuf, name); - } - if (IsZombie(member) - || !member_can_send_to_channel(member)) /* Returns 1 if we CAN send */ - comment = 0; - /* Send part to all clients */ - if (!IsZombie(member)) - { - if (comment) - sendto_channel_butserv(chptr, sptr, PartFmt2, parv[0], chptr->chname, - comment); - else - sendto_channel_butserv(chptr, sptr, PartFmt1, parv[0], chptr->chname); - } - else if (MyUser(sptr)) - { - if (comment) - sendto_one(sptr, PartFmt2, parv[0], chptr->chname, comment); - else - sendto_one(sptr, PartFmt1, parv[0], chptr->chname); - } - remove_user_from_channel(sptr, chptr); - } - /* Send out the parts to all servers... -Kev */ - if (*pbuf) - { - if (comment) - sendto_serv_butone(cptr, PartFmt2serv, NumNick(sptr), pbuf, comment); - else - sendto_serv_butone(cptr, PartFmt1serv, NumNick(sptr), pbuf); - } - return 0; -} + /* init join/part buffer */ + joinbuf_init(&parts, sptr, cptr, JOINBUF_TYPE_PART, + (parc > 2 && !EmptyString(parv[parc - 1])) ? parv[parc - 1] : 0, + 0); -#if 0 -/* - * m_part - * - * parv[0] = sender prefix - * parv[1] = channel - * parv[parc - 1] = comment - */ -int m_part(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) -{ - struct Channel* chptr; - struct Membership* member; - char* p = 0; - char* name; - char pbuf[BUFSIZE]; - char* comment = (parc > 2 && !EmptyString(parv[parc - 1])) ? parv[parc - 1] : 0; + /* scan through channel list */ + for (name = ircd_strtok(&p, parv[1], ","); name; + name = ircd_strtok(&p, 0, ",")) { - *pbuf = '\0'; /* Initialize the part buffer... -Kev */ + flags = 0; - sptr->flags &= ~FLAGS_TS8; + chptr = get_channel(sptr, name, CGT_NO_CREATE); /* look up channel */ - if (parc < 2 || parv[1][0] == '\0') - return need_more_params(sptr, "PART"); + if (!chptr || IsLocalChannel(name) || + !(member = find_member_link(chptr, sptr))) + continue; /* ignore from remote clients */ - for (; (name = ircd_strtok(&p, parv[1], ",")); parv[1] = 0) - { - chptr = get_channel(sptr, name, CGT_NO_CREATE); - if (!chptr) { - sendto_one(sptr, err_str(ERR_NOSUCHCHANNEL), me.name, parv[0], name); - continue; - } - if (*name == '&' && !MyUser(sptr)) - continue; + if (IsZombie(member)) /* figure out special flags... */ + flags |= CHFL_ZOMBIE; /* - * Do not use find_channel_member here: zombies must be able to part too + * XXX BUG: If a client /part's with a part notice, on channels where + * he's banned, local clients will not see the part notice, but remote + * clients will. */ - if (!(member = find_member_link(chptr, sptr))) - { - /* Normal to get when our client did a kick - * for a remote client (who sends back a PART), - * so check for remote client or not --Run - */ - if (MyUser(sptr)) - sendto_one(sptr, err_str(ERR_NOTONCHANNEL), me.name, parv[0], - chptr->chname); - continue; - } - /* Recreate the /part list for sending to servers */ - if (*name != '&') - { - if (*pbuf) - strcat(pbuf, ","); - strcat(pbuf, name); - } - if (IsZombie(member) - || !member_can_send_to_channel(member)) /* Returns 1 if we CAN send */ - comment = 0; - /* Send part to all clients */ - if (!IsZombie(member)) - { - if (comment) - sendto_channel_butserv(chptr, sptr, PartFmt2, parv[0], chptr->chname, - comment); - else - sendto_channel_butserv(chptr, sptr, PartFmt1, parv[0], chptr->chname); - } - else if (MyUser(sptr)) - { - if (comment) - sendto_one(sptr, PartFmt2, parv[0], chptr->chname, comment); - else - sendto_one(sptr, PartFmt1, parv[0], chptr->chname); - } - remove_user_from_channel(sptr, chptr); - } - /* Send out the parts to all servers... -Kev */ - if (*pbuf) - { - if (comment) - sendto_serv_butone(cptr, PartFmt2serv, NumNick(sptr), pbuf, comment); - else - sendto_serv_butone(cptr, PartFmt1serv, NumNick(sptr), pbuf); + if (!member_can_send_to_channel(member)) + flags |= CHFL_BANNED; + + /* part user from channel */ + joinbuf_join(&parts, chptr, flags); } - return 0; + + return joinbuf_flush(&parts); /* flush channel parts */ } -#endif /* 0 */