Author: Kev <klmitch@mit.edu>
authorKevin L. Mitchell <klmitch@mit.edu>
Sun, 30 Apr 2000 01:33:28 +0000 (01:33 +0000)
committerKevin L. Mitchell <klmitch@mit.edu>
Sun, 30 Apr 2000 01:33:28 +0000 (01:33 +0000)
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

ChangeLog
include/channel.h
ircd/channel.c
ircd/m_create.c
ircd/m_join.c
ircd/m_part.c

index 89ed9d0ad0c3325013baadd6163da0b833f0e3ae..b232630118a5050be28775f81e40af67ff65df97 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,20 @@
+2000-04-29  Kevin L. Mitchell  <klmitch@mit.edu>
+
+       * 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  <klmitch@mit.edu>
+
+       * 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 <isomer@coders.net>
        * include/s_bsd.c: Ok, so I thought I compiled and tested this...
 
 #
 # 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.
 #
index 8bd0d0287db39cbc2cbcd747e9f7f89b1db0d967..7294cb4513de609f8fe6bf66117c0f6220603d92 100644 (file)
@@ -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 */
index 04ce375fa8155cc3dddf2192645934e782a4c9a3..4927d078b0ed926dd5fcab1cb57e8a419ad724d5 100644 (file)
@@ -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;
+}
index 05bacc8c31b4715dc699ab28579cf81f16f1194f..c3d537bf9a6c8058c36d6b04440fc4901ace6a45 100644 (file)
 #include "msg.h"
 #include "numeric.h"
 #include "numnicks.h"
+#include "s_debug.h"
 #include "send.h"
 
 #include <assert.h>
 #include <stdlib.h>
 #include <string.h>
 
-/*
- * 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 */
-
index d52a34e01ddfdfaadc5ac10a0c1bebb8e5b3d646..b09778a450633b777c8ae543d66f1150dd436f34 100644 (file)
@@ -98,6 +98,7 @@
 #include "msg.h"
 #include "numeric.h"
 #include "numnicks.h"
+#include "s_debug.h"
 #include "s_user.h"
 #include "send.h"
 
 #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 */
index d4de9b3a1c5758464d54c76cce69101d015d7ce7..4cf098adfa75c49f31d5fb6f80a6ccfd79baac75 100644 (file)
  */
 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 */