Binary files ircd-hybrid-6rc4/ChangeLog and ircd-ournet-12302000/ChangeLog differ
diff -uNrd ircd-hybrid-6rc4/include/config.h ircd-ournet-12302000/include/config.h
--- ircd-hybrid-6rc4/include/config.h	Thu Aug 24 02:15:50 2000
+++ ircd-ournet-12302000/include/config.h	Sat Dec 30 02:20:06 2000
@@ -25,6 +25,13 @@
 #define INCLUDED_setup_h
 #endif
 
+
+
+#undef SHADEBUG
+
+#define SPOOF_FREEFORM
+
+
 /* PLEASE READ SECTION:
  *
  * I have commented out WHOIS_NOTICE and STATS_NOTICE
@@ -51,8 +58,8 @@
  * system limits. If you know what you are doing, increase them now
  */
 
-#define HARD_FDLIMIT_   256
-#define INIT_MAXCLIENTS 200
+#define HARD_FDLIMIT_   2048
+#define INIT_MAXCLIENTS 1984
 
 #if defined(__CYGWIN__)
 #undef HARD_FDLIMIT_
@@ -65,7 +72,7 @@
  * This is how many 'buffer connections' we allow... 
  * Remember, MAX_BUFFER + MAX_CLIENTS can't exceed HARD_FDLIMIT :)
  */
-#define MAX_BUFFER      60
+#define MAX_BUFFER      64
 
 /* NICKNAMEHISTORYLENGTH - size of WHOWAS array
  * this defines the length of the nickname history.  each time a user changes
@@ -75,7 +82,7 @@
  *       will be preallocated for the entire whowas array when ircd is started.
  *       You will want to crank this down if you are on a small net.
  */
-#define NICKNAMEHISTORYLENGTH 15000
+#define NICKNAMEHISTORYLENGTH 1500
 
 /* Don't change this... */
 #define HARD_FDLIMIT    (HARD_FDLIMIT_ - 10)
@@ -107,8 +114,8 @@
  *
  */
 
-#define DPATH   "/usr/local/ircd/"
-#define SPATH   "/usr/local/ircd/ircd"
+#define DPATH   "/home/ircd/ircd-ournet/"
+#define SPATH   "/home/ircd/ircd-ournet/ircd"
 #define CPATH   "ircd.conf"
 #define KPATH   "kline.conf"
 #define DLPATH  "kline.conf"
@@ -131,8 +138,8 @@
  * implementation.  Once pre-hybrid5.2 servers are eradicated, we can drop this
  * down to 90 seconds or so. --Rodder
  */
-#define TS_MAX_DELTA 300        /* seconds */
-#define TS_WARN_DELTA 30        /* seconds */
+#define TS_MAX_DELTA 8000        /* seconds */
+#define TS_WARN_DELTA 1        /* seconds */
 
 /* SLAVE_SERVERS - Use this to send LOCOPS and KLINES to servers you define
  * uses U: lines in ircd.conf, each server defined in an U: line
@@ -160,8 +167,8 @@
  *
  * These need to be defined if you want to use SYSLOG logging, too.
  */
-#define FNAME_USERLOG "/usr/local/ircd/users" /* */
-#define FNAME_OPERLOG "/usr/local/ircd/opers" /* */
+#define FNAME_USERLOG "/home/ircd/ircd-ournet/users" /* */
+#define FNAME_OPERLOG "/home/ircd/ircd-ournet/opers" /* */
 
 /* RFC1035_ANAL
  * Defining this causes ircd to reject hostnames with non-compliant chars.
@@ -174,7 +181,7 @@
  * will not be sent.  If MAX_MULTI_MESSAGES is 1, then any PRIVMSG with a ',' in
  * the target will be rejected
  */
-#define MAX_MULTI_MESSAGES 2
+#define MAX_MULTI_MESSAGES 24
 
 /* WARN_NO_NLINE
  * Define this if you want ops to get noticed about "things" trying to
@@ -294,7 +301,7 @@
  * Why do opers need this at all? Its an invasion of privacy. bah.
  * you don't need this. -Dianora
  */
-#undef WHOIS_NOTICE
+#define WHOIS_NOTICE
 
 /* WHOIS_WAIT - minimum seconds between remote use of WHOIS before
  * max use count is reset
@@ -318,7 +325,7 @@
 /* If you are an admin that does not think operwall/wallops
  * should be used instead of a channel, define this.
  */
-#define PACE_WALLOPS
+#undef PACE_WALLOPS
 #define WALLOPS_WAIT 10 
 
 /* SHORT_MOTD
@@ -398,7 +405,7 @@
  * to a leaf which just has 1 server (typically the uplink). Define this
  * correctly for performance reasons.
  */
-#undef  HUB
+#define  HUB
 
 /* CMDLINE_CONFIG - allow conf-file to be specified on command line
  * NOTE: defining CMDLINE_CONFIG and installing ircd SUID or SGID is a MAJOR
@@ -485,8 +492,8 @@
  * define IRC_UID to that UID.  This should only be defined if you are running
  * as root and even then perhaps not.
  */
-#define IRC_UID 1001
-#define IRC_GID 31
+#undef IRC_UID /* 1007 */
+#undef IRC_GID /* 1007 */
 
 /* CLIENT_FLOOD - client excess flood threshold
  * this controls the number of bytes the server will allow a client to
@@ -636,7 +643,7 @@
  * you will still receive a normal trace output.  Defining this will remove
  * "STATS p" funtionality since the two are basically redundant.
  */
-#define LTRACE
+#undef LTRACE
 
 /*
  * LWALLOPS - Like wallops but only local.
@@ -753,7 +760,7 @@
 /* MAXCHANNELSPERUSER -
  * Max number of channels a user is allowed to join.
  */
-#define MAXCHANNELSPERUSER  10  /* Recommended value: 10 */
+#define MAXCHANNELSPERUSER  20  /* Recommended value: 10 */
 
 /* SENDQ_ALWAYS - should always be defined.
  * SendQ-Always causes the server to put all outbound data into the sendq and
@@ -793,7 +800,7 @@
 /* JUPE_CHANNEL - jupes a channel from being joined on this server only
  * if added to Q lines e.g. Q:\#packet_channel:Tired of packets
  */
-#undef JUPE_CHANNEL
+#define JUPE_CHANNEL
 
 /* 
  * ANTI_SPAMBOT
@@ -832,7 +839,7 @@
  * JOIN and PRIVMSG if possible spambot is noticed
  * Depends on your policies.
  */
-#undef ANTI_SPAMBOT_WARN_ONLY
+#define ANTI_SPAMBOT_WARN_ONLY
 
 /* ANTI_SPAM_EXIT_MESSAGE
  *
@@ -902,7 +909,7 @@
  * viconf option, if USE_RCS is defined, viconf will use rcs "ci"
  * to keep the conf file  under RCS control.
  */
-#define USE_RCS
+#undef USE_RCS
 
 /* ----------------- not approved on EFnet section -------------------- */
 /* GLINES - global Kline-like bans
@@ -911,7 +918,7 @@
  * three different servers must do the identical GLINE in order
  * for the G line to take effect.
  */
-#undef GLINES
+#define GLINES
 #define GLINEFILE       "gline.log"
 
 /* GLINE_TIME - local expire time for GLINES
diff -uNrd ircd-hybrid-6rc4/include/patchlevel.h ircd-ournet-12302000/include/patchlevel.h
--- ircd-hybrid-6rc4/include/patchlevel.h	Wed Aug  9 18:50:23 2000
+++ ircd-ournet-12302000/include/patchlevel.h	Sat Dec 30 00:39:58 2000
@@ -17,5 +17,5 @@
  */
 
 #ifndef PATCHLEVEL
-#define PATCHLEVEL    "2.8/hybrid-6rc4"
+#define PATCHLEVEL    "2.8/hybrid-6rc4[ournet!2]"
 #endif
diff -uNrd ircd-hybrid-6rc4/include/serno.h ircd-ournet-12302000/include/serno.h
--- ircd-hybrid-6rc4/include/serno.h	Thu Aug 24 20:29:14 2000
+++ ircd-ournet-12302000/include/serno.h	Sat Dec 30 02:09:23 2000
@@ -1 +1 @@
-#define SERIALNUM "20000824_4"
+#define SERIALNUM "20001230_2"
diff -uNrd ircd-hybrid-6rc4/src/channel.c ircd-ournet-12302000/src/channel.c
--- ircd-hybrid-6rc4/src/channel.c	Thu Aug 24 20:29:11 2000
+++ ircd-ournet-12302000/src/channel.c	Sat Dec 30 00:41:56 2000
@@ -1,4843 +1,4902 @@
-/************************************************************************
- *   IRC - Internet Relay Chat, src/channel.c
- *   Copyright (C) 1990 Jarkko Oikarinen and
- *                      University of Oulu, Co Center
- *
- *   This program is free software; you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation; either version 1, or (at your option)
- *   any later version.
- *
- *   This program is distributed in the hope that it will be useful,
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *   GNU General Public License for more details.
- *
- *   You should have received a copy of the GNU General Public License
- *   along with this program; if not, write to the Free Software
- *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- *
- * a number of behaviours in set_mode() have been rewritten
- * These flags can be set in a define if you wish.
- *
- * OLD_P_S      - restore xor of p vs. s modes per channel
- *                currently p is rather unused, so using +p
- *                to disable "knock" seemed worth while.
- * OLD_MODE_K   - new mode k behaviour means user can set new key
- *                while old one is present, mode * -k of old key is done
- *                on behalf of user, with mode * +k of new key.
- *                /mode * -key results in the sending of a *, which
- *                can be used to resynchronize a channel.
- * OLD_NON_RED  - Current code allows /mode * -s etc. to be applied
- *                even if +s is not set. Old behaviour was not to allow
- *                mode * -p etc. if flag was clear
- *
- *
- * $Id: channel.c,v 1.178 2000/08/25 00:29:11 lusky Exp $
- */
-#include "channel.h"
-#include "client.h"
-#include "common.h"
-#include "hash.h"
-#include "irc_string.h"
-#include "ircd.h"
-#include "list.h"
-#include "numeric.h"
-#include "s_serv.h"       /* captab */
-#include "s_user.h"
-#include "send.h"
-#include "struct.h"
-#include "whowas.h"
-
-#include <assert.h>
-#include <string.h>
-#include <stdlib.h>
-
-#ifdef NEED_SPLITCODE
-
-static void check_still_split();
-int server_was_split=YES;
-int got_server_pong;
-time_t server_split_time;
-
-#if defined(PRESERVE_CHANNEL_ON_SPLIT) || defined(NO_JOIN_ON_SPLIT)
-struct Channel *empty_channel_list=(struct Channel*)NULL;
-void remove_empty_channels();
-#endif
-#endif
-
-struct Channel *channel = NullChn;
-
-static  void    add_invite (struct Client *, struct Channel *);
-static  int     add_banid (struct Client *, struct Channel *, char *);
-static  int     add_exceptid(struct Client *, struct Channel *, char *);
-static  int     add_denyid(struct Client *, struct Channel *, char *);
-static  int     can_join (struct Client *, struct Channel *, char *,int *);
-static  int     del_banid (struct Channel *, char *);
-static  int     del_exceptid (struct Channel *, char *);
-static  int     del_denyid (struct Channel *, char *);
-static  void    clear_bans_exceptions_denies(struct Client *,struct Channel *);
-static  void    free_bans_exceptions_denies(struct Channel *);
-static  int     is_banned (struct Client *, struct Channel *);
-static  void    sub1_from_channel (struct Channel *);
-
-
-/* static functions used in set_mode */
-static char* pretty_mask(char *);
-static char *fix_key(char *);
-static char *fix_key_old(char *);
-static void collapse_signs(char *);
-static int errsent(int,int *);
-static void change_chan_flag(struct Channel *, struct Client *, int );
-static void set_deopped(struct Client *,struct Channel *,int);
-
-static  char    *PartFmt = ":%s PART %s";
-/*
- * some buffers for rebuilding channel/nick lists with ,'s
- */
-
-static  char    buf[BUFSIZE];
-static  char    modebuf[MODEBUFLEN], modebuf2[MODEBUFLEN];
-static  char    parabuf[MODEBUFLEN], parabuf2[MODEBUFLEN];
-
-
-/* 
- * return the length (>=0) of a chain of links.
- */
-static  int     list_length(Link *lp)
-{
-  int   count = 0;
-
-  for (; lp; lp = lp->next)
-    count++;
-  return count;
-}
-
-/*
- *  Fixes a string so that the first white space found becomes an end of
- * string marker (`\0`).  returns the 'fixed' string or "*" if the string
- * was NULL length or a NULL pointer.
- */
-static char* check_string(char* s)
-{
-  static char star[2] = "*";
-  char* str = s;
-
-  if (BadPtr(s))
-    return star;
-
-  for ( ; *s; ++s) {
-    if (IsSpace(*s))
-      {
-        *s = '\0';
-        break;
-      }
-  }
-  return str;
-}
-
-/*
- * create a string of form "foo!bar@fubar" given foo, bar and fubar
- * as the parameters.  If NULL, they become "*".
- */
-static char* make_nick_user_host(const char* nick, 
-                                 const char* name, const char* host)
-{
-  static char namebuf[NICKLEN + USERLEN + HOSTLEN + 6];
-  int   n;
-  char* s;
-  const char* p;
-
-  s = namebuf;
-
-  for (p = nick, n = NICKLEN; *p && n--; )
-    *s++ = *p++;
-  *s++ = '!';
-  for(p = name, n = USERLEN; *p && n--; )
-    *s++ = *p++;
-  *s++ = '@';
-  for(p = host, n = HOSTLEN; *p && n--; )
-    *s++ = *p++;
-  *s = '\0';
-  return namebuf;
-}
-
-/*
- * Ban functions to work with mode +b
- */
-/* add_banid - add an id to be banned to the channel  (belongs to cptr) */
-
-static  int     add_banid(struct Client *cptr, struct Channel *chptr, char *banid)
-{
-  Link  *ban;
-
-  /* dont let local clients overflow the banlist */
-  if ((!IsServer(cptr)) && (chptr->num_bed >= MAXBANS))
-	  if (MyClient(cptr))
-	    {
-	      sendto_one(cptr, form_str(ERR_BANLISTFULL),
-                   me.name, cptr->name,
-                   chptr->chname, banid);
-	      return -1;
-	    }
-  
-  if (MyClient(cptr))
-    collapse(banid);
-
-  for (ban = chptr->banlist; ban; ban = ban->next)
-	  if (match(BANSTR(ban), banid))
-	    return -1;
-
-  ban = make_link();
-  memset(ban, 0, sizeof(Link));
-  ban->flags = CHFL_BAN;
-  ban->next = chptr->banlist;
-
-#ifdef BAN_INFO
-
-  ban->value.banptr = (aBan *)MyMalloc(sizeof(aBan));
-  ban->value.banptr->banstr = (char *)MyMalloc(strlen(banid)+1);
-  (void)strcpy(ban->value.banptr->banstr, banid);
-
-#ifdef USE_UH
-  if (IsPerson(cptr))
-    {
-      ban->value.banptr->who =
-        (char *)MyMalloc(strlen(cptr->name)+
-                         strlen(cptr->username)+
-                         strlen(cptr->host)+3);
-      ircsprintf(ban->value.banptr->who, "%s!%s@%s",
-                 cptr->name, cptr->username, cptr->host);
-    }
-  else
-    {
-#endif
-      ban->value.banptr->who = (char *)MyMalloc(strlen(cptr->name)+1);
-      (void)strcpy(ban->value.banptr->who, cptr->name);
-#ifdef USE_UH
-    }
-#endif
-
-  ban->value.banptr->when = CurrentTime;
-
-#else
-
-  ban->value.cp = (char *)MyMalloc(strlen(banid)+1);
-  (void)strcpy(ban->value.cp, banid);
-
-#endif  /* #ifdef BAN_INFO */
-
-  chptr->banlist = ban;
-  chptr->num_bed++;
-  return 0;
-}
-
-/* add_exceptid - add an id to the exception list for the channel  
- * (belongs to cptr) 
- */
-
-static  int     add_exceptid(struct Client *cptr, struct Channel *chptr, char *eid)
-{
-  Link  *ex, *ban;
-
-  /* dont let local clients overflow the banlist */
-  if ((!IsServer(cptr)) && (chptr->num_bed >= MAXBANS))
-    if (MyClient(cptr))
-      {
-        sendto_one(cptr, form_str(ERR_BANLISTFULL),
-                   me.name, cptr->name,
-                   chptr->chname, eid);
-        return -1;
-      }
-
-  if (MyClient(cptr))
-    (void)collapse(eid);
-
-  for (ban = chptr->exceptlist; ban; ban = ban->next)
-	  if (match(BANSTR(ban), eid))
-	    return -1;
-  
-
-  ex = make_link();
-  memset(ex, 0, sizeof(Link));
-  ex->flags = CHFL_EXCEPTION;
-  ex->next = chptr->exceptlist;
-
-#ifdef BAN_INFO
-
-  ex->value.banptr = (aBan *)MyMalloc(sizeof(aBan));
-  ex->value.banptr->banstr = (char *)MyMalloc(strlen(eid)+1);
-  (void)strcpy(ex->value.banptr->banstr, eid);
-
-#ifdef USE_UH
-  if (IsPerson(cptr))
-    {
-      ex->value.banptr->who =
-        (char *)MyMalloc(strlen(cptr->name)+
-                         strlen(cptr->username)+
-                         strlen(cptr->host)+3);
-      ircsprintf(ex->value.banptr->who, "%s!%s@%s",
-                 cptr->name, cptr->username, cptr->host);
-    }
-  else
-    {
-#endif
-      ex->value.banptr->who = (char *)MyMalloc(strlen(cptr->name)+1);
-      (void)strcpy(ex->value.banptr->who, cptr->name);
-#ifdef USE_UH
-    }
-#endif
-
-  ex->value.banptr->when = CurrentTime;
-
-#else
-
-  ex->value.cp = (char *)MyMalloc(strlen(eid)+1);
-  (void)strcpy(ex->value.cp, eid);
-
-#endif  /* #ifdef BAN_INFO */
-
-  chptr->exceptlist = ex;
-  chptr->num_bed++;
-  return 0;
-}
-
-
-/*
- * add_denyid - add an id to be denied to the channel  (belongs to cptr) 
- * works like an Xline, but for channels.
- * -sean
- */  
-static  int     add_denyid(struct Client *cptr, struct Channel *chptr, char *banid)
-{
-  Link  *ban;
-
-  /* truncate to REALLEN */
-  banid[REALLEN]='\0';
-
-  if ((!IsServer(cptr)) && (chptr->num_bed >= MAXBANS))
-    if (MyClient(cptr))
-      {
-        sendto_one(cptr, form_str(ERR_BANLISTFULL),
-                   me.name, cptr->name,
-                   chptr->chname, banid);
-        return -1;
-      }
-
-  if (MyClient(cptr))
-    (void)collapse(banid);
-
-  for (ban = chptr->denylist; ban; ban = ban->next)
-    if (strcasecmp(BANSTR(ban), banid)==0) 
-      return -1;
-  
-  ban = make_link();
-  memset(ban, 0, sizeof(Link));
-  ban->flags = CHFL_DENY;
-  ban->next = chptr->denylist;
-
-#ifdef BAN_INFO
-
-  ban->value.banptr = (aBan *)MyMalloc(sizeof(aBan));
-  ban->value.banptr->banstr = (char *)MyMalloc(strlen(banid)+1);
-  (void)strcpy(ban->value.banptr->banstr, banid);
-
-#ifdef USE_UH
-  if (IsPerson(cptr))
-    {
-      ban->value.banptr->who =
-        (char *)MyMalloc(strlen(cptr->name)+
-                         strlen(cptr->username)+
-                         strlen(cptr->host)+3);
-      ircsprintf(ban->value.banptr->who, "%s!%s@%s",
-                 cptr->name, cptr->username, cptr->host);
-    }
-   else
-    {
-#endif
-      ban->value.banptr->who = (char *)MyMalloc(strlen(cptr->name)+1);
-      (void)strcpy(ban->value.banptr->who, cptr->name);
-#ifdef USE_UH
-    }
-#endif
-
-  ban->value.banptr->when = CurrentTime;
-
-#else
-
-  ban->value.cp = (char *)MyMalloc(strlen(banid)+1);
-  (void)strcpy(ban->value.cp, banid);
-
-#endif  /* #ifdef BAN_INFO */
-
-  chptr->denylist = ban;
-  chptr->num_bed++;
-  return 0;
-}
-
-
-
-/*
- *
- * "del_banid - delete an id belonging to cptr
- * if banid is null, deleteall banids belonging to cptr."
- *
- * from orabidoo
- */
-static  int     del_banid(struct Channel *chptr, char *banid)
-{
-  register Link **ban;
-  register Link *tmp;
-
-  if (!banid)
-    return -1;
-  for (ban = &(chptr->banlist); *ban; ban = &((*ban)->next))
-#ifdef BAN_INFO
-    if (irccmp(banid, (*ban)->value.banptr->banstr)==0)
-#else
-      if (irccmp(banid, (*ban)->value.cp)==0)
-#endif
-        {
-          tmp = *ban;
-          *ban = tmp->next;
-#ifdef BAN_INFO
-          MyFree(tmp->value.banptr->banstr);
-          MyFree(tmp->value.banptr->who);
-          MyFree(tmp->value.banptr);
-#else
-          MyFree(tmp->value.cp);
-#endif
-          free_link(tmp);
-	  /* num_bed should never be < 0 */
-	  if(chptr->num_bed > 0)
-	    chptr->num_bed--;
-	  else
-	    chptr->num_bed = 0;
-          break;
-        }
-  return 0;
-}
-
-/*
- * del_exceptid - delete an id belonging to cptr
- *
- * from orabidoo
- */
-static  int     del_exceptid(struct Channel *chptr, char *eid)
-{
-  register Link **ex;
-  register Link *tmp;
-
-  if (!eid)
-    return -1;
-  for (ex = &(chptr->exceptlist); *ex; ex = &((*ex)->next))
-    if (irccmp(eid, BANSTR(*ex)) == 0)
-      {
-        tmp = *ex;
-        *ex = tmp->next;
-#ifdef BAN_INFO
-        MyFree(tmp->value.banptr->banstr);
-        MyFree(tmp->value.banptr->who);
-        MyFree(tmp->value.banptr);
-#else
-        MyFree(tmp->value.cp);
-#endif
-        free_link(tmp);
-	/* num_bed should never be < 0 */
-	if(chptr->num_bed > 0)
-	  chptr->num_bed--;
-	else
-	  chptr->num_bed = 0;
-        break;
-      }
-  return 0;
-}
-
-/*
- *
- * "del_denyid - delete an id belonging to cptr
- * if banid is null, deleteall banids belonging to cptr."
- *
- * -sean
- */
-static  int     del_denyid(struct Channel *chptr, char *banid)
-{
-  register Link **ban;
-  register Link *tmp;
-
-  if (!banid)
-    return -1;
-  for (ban = &(chptr->denylist); *ban; ban = &((*ban)->next))
-#ifdef BAN_INFO
-    if (strcasecmp(banid, (*ban)->value.banptr->banstr)==0) 
-#else
-      if (strcasecmp(banid, (*ban)->value.cp)==0)
-#endif
-        {
-          tmp = *ban;
-          *ban = tmp->next;
-#ifdef BAN_INFO
-          MyFree(tmp->value.banptr->banstr);
-          MyFree(tmp->value.banptr->who);
-          MyFree(tmp->value.banptr);
-#else
-          MyFree(tmp->value.cp);
-#endif
-          free_link(tmp);
-
-	  /* num_bed should never be < 0 */
-	  if(chptr->num_bed > 0)
-	    chptr->num_bed--;
-	  else
-	    chptr->num_bed = 0;
-          break;
-        }
-  return 0;
-}
-
-
-
-/*
- * del_matching_exception - delete an exception matching this user
- *
- * The idea is, if a +e client gets kicked for any reason
- * remove the matching exception for this client.
- * This will immediately stop channel "chatter" with scripts
- * that kick on matching ban. It will also stop apparent "desyncs."
- * It's not the long term answer, but it will tide us over.
- *
- * modified from orabidoo - Dianora
- */
-static void del_matching_exception(struct Client *cptr,struct Channel *chptr)
-{
-  register Link **ex;
-  register Link *tmp;
-  char  s[NICKLEN + USERLEN + HOSTLEN+6];
-  char  *s2;
-
-  if (!IsPerson(cptr))
-    return;
-
-  strcpy(s, make_nick_user_host(cptr->name, cptr->username, cptr->host));
-  s2 = make_nick_user_host(cptr->name, cptr->username,
-                           inetntoa((char*) &cptr->ip));
-
-  for (ex = &(chptr->exceptlist); *ex; ex = &((*ex)->next))
-    {
-      tmp = *ex;
-
-      if (match(BANSTR(tmp), s) ||
-          match(BANSTR(tmp), s2) )
-        {
-
-          /* code needed here to send -e to channel.
-           * I will not propogate the removal,
-           * This will lead to desyncs of e modes,
-           * but its not going to be any worse then it is now.
-           *
-           * Kickee gets to see the -e removal by the server
-           * since they have not yet been removed from the channel.
-           * I don't think thats a biggie.
-           *
-           * -Dianora
-           */
-
-          sendto_channel_butserv(chptr,
-                                 &me,
-                                 ":%s MODE %s -e %s", 
-                                 me.name,
-                                 chptr->chname,
-                                 BANSTR(tmp));
-
-          *ex = tmp->next;
-#ifdef BAN_INFO
-          MyFree(tmp->value.banptr->banstr);
-          MyFree(tmp->value.banptr->who);
-          MyFree(tmp->value.banptr);
-#else
-          MyFree(tmp->value.cp);
-#endif
-          free_link(tmp);
-	  /* num_bed should never be < 0 */
-	  if(chptr->num_bed > 0)
-	    chptr->num_bed--;
-	  else
-	    chptr->num_bed = 0;
-          return;
-        }
-    }
-}
-
-/*
- * is_banned -  returns an int 0 if not banned,
- *              CHFL_BAN if banned (or +d'd)
- *              CHFL_EXCEPTION if they have a ban exception
- *
- * IP_BAN_ALL from comstud
- * always on...
- *
- * +e code from orabidoo
- */
-
-static  int is_banned(struct Client *cptr,struct Channel *chptr)
-{
-  register Link *tmp;
-  register Link *t2;
-  char  s[NICKLEN+USERLEN+HOSTLEN+6];
-  char  *s2;
-
-  if (!IsPerson(cptr))
-    return (0);
-
-  strcpy(s, make_nick_user_host(cptr->name, cptr->username, cptr->host));
-  s2 = make_nick_user_host(cptr->name, cptr->username,
-                           inetntoa((char*) &cptr->ip));
-
-  for (tmp = chptr->banlist; tmp; tmp = tmp->next)
-    if (match(BANSTR(tmp), s) ||
-        match(BANSTR(tmp), s2))
-      break;
-
-  if (!tmp) {  /* check +d list */
-    for (tmp = chptr->denylist; tmp; tmp = tmp->next)
-      {
-        if (match(BANSTR(tmp), cptr->info))
-          break;
-      }
-  }
-
-  if (tmp)
-    {
-      for (t2 = chptr->exceptlist; t2; t2 = t2->next)
-        if (match(BANSTR(t2), s) ||
-            match(BANSTR(t2), s2))
-          {
-            return CHFL_EXCEPTION;
-          }
-    }
-
-  /* return CHFL_BAN for +b or +d match, we really dont need to be more
-     specific */
-  return ((tmp?CHFL_BAN:0));
-}
-
-/*
- * adds a user to a channel by adding another link to the channels member
- * chain.
- */
-static  void    add_user_to_channel(struct Channel *chptr, struct Client *who, int flags)
-{
-  Link *ptr;
-
-#if defined(PRESERVE_CHANNEL_ON_SPLIT) || defined(NO_JOIN_ON_SPLIT)
-  if( chptr->mode.mode & MODE_SPLIT )
-    {
-      /* Unmark the split mode */
-      chptr->mode.mode &= ~MODE_SPLIT;
-
-      /* remove from the empty channel double link list */
-      if (chptr->last_empty_channel)
-        chptr->last_empty_channel->next_empty_channel =
-          chptr->next_empty_channel;
-      else
-        empty_channel_list = chptr->next_empty_channel;
-      if (chptr->next_empty_channel)
-        chptr->next_empty_channel->last_empty_channel =
-          chptr->last_empty_channel;
-    }
-#endif
-
-  if (who->user)
-    {
-      ptr = make_link();
-      ptr->flags = flags;
-      ptr->value.cptr = who;
-      ptr->next = chptr->members;
-      chptr->members = ptr;
-
-      chptr->users++;
-
-      ptr = make_link();
-      ptr->value.chptr = chptr;
-      ptr->next = who->user->channel;
-      who->user->channel = ptr;
-      who->user->joined++;
-    }
-}
-
-void    remove_user_from_channel(struct Client *sptr,struct Channel *chptr,int was_kicked)
-{
-  Link  **curr;
-  Link  *tmp;
-
-  for (curr = &chptr->members; (tmp = *curr); curr = &tmp->next)
-    if (tmp->value.cptr == sptr)
-      {
-        /* User was kicked, but had an exception.
-         * so, to reduce chatter I'll remove any
-         * matching exception now.
-         */
-        if(was_kicked && (tmp->flags & CHFL_EXCEPTION))
-          {
-            del_matching_exception(sptr,chptr);
-          }
-        *curr = tmp->next;
-        free_link(tmp);
-        break;
-      }
-  for (curr = &sptr->user->channel; (tmp = *curr); curr = &tmp->next)
-    if (tmp->value.chptr == chptr)
-      {
-        *curr = tmp->next;
-        free_link(tmp);
-        break;
-      }
-  sptr->user->joined--;
-
-  sub1_from_channel(chptr);
-
-}
-
-static  void    change_chan_flag(struct Channel *chptr,struct Client *cptr, int flag)
-{
-  Link *tmp;
-
-  if ((tmp = find_user_link(chptr->members, cptr)))
-   {
-    if (flag & MODE_ADD)
-      {
-        tmp->flags |= flag & MODE_FLAGS;
-        if (flag & MODE_CHANOP)
-          tmp->flags &= ~MODE_DEOPPED;
-      }
-    else
-      {
-        tmp->flags &= ~flag & MODE_FLAGS;
-      }
-   }
-}
-
-static  void    set_deopped(struct Client *cptr, struct Channel *chptr,int flag)
-{
-  Link  *tmp;
-
-  if ((tmp = find_user_link(chptr->members, cptr)))
-    if ((tmp->flags & flag) == 0)
-      tmp->flags |= MODE_DEOPPED;
-}
-
-int     is_chan_op(struct Client *cptr, struct Channel *chptr)
-{
-  Link  *lp;
-
-  if (chptr)
-    if ((lp = find_user_link(chptr->members, cptr)))
-      return (lp->flags & CHFL_CHANOP);
-  
-  return 0;
-}
-
-int     is_deopped(struct Client *cptr, struct Channel *chptr)
-{
-  Link  *lp;
-
-  if (chptr)
-    if ((lp = find_user_link(chptr->members, cptr)))
-      return (lp->flags & CHFL_DEOPPED);
-  
-  return 0;
-}
-
-int     has_voice(struct Client *cptr, struct Channel *chptr)
-{
-  Link  *lp;
-
-  if (chptr)
-    if ((lp = find_user_link(chptr->members, cptr)))
-      return (lp->flags & CHFL_VOICE);
-
-  return 0;
-}
-
-int     can_send(struct Client *cptr, struct Channel *chptr)
-{
-  Link  *lp;
-
-#ifdef JUPE_CHANNEL
-  if (MyClient(cptr) && (chptr->mode.mode & MODE_JUPED))
-    {
-      return (MODE_JUPED);
-    }
-#endif
-
-  lp = find_user_link(chptr->members, cptr);
-
-  if (chptr->mode.mode & MODE_MODERATED &&
-      (!lp || !(lp->flags & (CHFL_CHANOP|CHFL_VOICE))))
-    return (MODE_MODERATED);
-
-  if (chptr->mode.mode & MODE_NOPRIVMSGS && !lp)
-    return (MODE_NOPRIVMSGS);
-
-  return 0;
-}
-
-int     user_channel_mode(struct Client *cptr, struct Channel *chptr)
-{
-  Link  *lp;
-
-  if (chptr)
-    if ((lp = find_user_link(chptr->members, cptr)))
-      return (lp->flags);
-  
-  return 0;
-}
-
-/*
- * write the "simple" list of channel modes for channel chptr onto buffer mbuf
- * with the parameters in pbuf.
- */
-void channel_modes(struct Client *cptr, char *mbuf, char *pbuf, struct Channel *chptr)
-{
-  *mbuf++ = '+';
-  if (chptr->mode.mode & MODE_SECRET)
-    *mbuf++ = 's';
-
-  /* bug found by "is" ejb@debian.org */
-#ifdef OLD_P_S
-  else if (chptr->mode.mode & MODE_PRIVATE)
-    *mbuf++ = 'p';
-#else
-  if (chptr->mode.mode & MODE_PRIVATE)
-    *mbuf++ = 'p';
-#endif
-
-  if (chptr->mode.mode & MODE_MODERATED)
-    *mbuf++ = 'm';
-  if (chptr->mode.mode & MODE_TOPICLIMIT)
-    *mbuf++ = 't';
-  if (chptr->mode.mode & MODE_INVITEONLY)
-    *mbuf++ = 'i';
-  if (chptr->mode.mode & MODE_NOPRIVMSGS)
-    *mbuf++ = 'n';
-  if (chptr->mode.limit)
-    {
-      *mbuf++ = 'l';
-      if (IsMember(cptr, chptr) || IsServer(cptr))
-        ircsprintf(pbuf, "%d ", chptr->mode.limit);
-    }
-  if (*chptr->mode.key)
-    {
-      *mbuf++ = 'k';
-      if (IsMember(cptr, chptr) || IsServer(cptr))
-        (void)strcat(pbuf, chptr->mode.key);
-    }
-  *mbuf++ = '\0';
-  return;
-}
-
-/*
- * only used to send +b and +e now, +d/+a too.
- * 
- */
-
-static  void    send_mode_list(struct Client *cptr,
-                               char *chname,
-                               Link *top,
-                               int mask,
-                               char flag)
-{
-  Link  *lp;
-  char  *cp, *name;
-  int   count = 0, send = 0;
-  
-  cp = modebuf + strlen(modebuf);
-  if (*parabuf) /* mode +l or +k xx */
-    count = 1;
-  for (lp = top; lp; lp = lp->next)
-    {
-      if (!(lp->flags & mask))
-        continue;
-      name = BANSTR(lp);
-        
-      if (strlen(parabuf) + strlen(name) + 10 < (size_t) MODEBUFLEN)
-        {
-          (void)strcat(parabuf, " ");
-          (void)strcat(parabuf, name);
-          count++;
-          *cp++ = flag;
-          *cp = '\0';
-        }
-      else if (*parabuf)
-        send = 1;
-      if (count == 3)
-        send = 1;
-      if (send)
-        {
-          sendto_one(cptr, ":%s MODE %s %s %s",
-                     me.name, chname, modebuf, parabuf);
-          send = 0;
-          *parabuf = '\0';
-          cp = modebuf;
-          *cp++ = '+';
-          if (count != 3)
-            {
-              (void)strcpy(parabuf, name);
-              *cp++ = flag;
-            }
-          count = 0;
-          *cp = '\0';
-        }
-    }
-}
-
-/*
- * send "cptr" a full list of the modes for channel chptr.
- */
-void send_channel_modes(struct Client *cptr, struct Channel *chptr)
-{
-  Link  *l, *anop = NULL, *skip = NULL;
-  int   n = 0;
-  char  *t;
-
-  if (*chptr->chname != '#')
-    return;
-
-  *modebuf = *parabuf = '\0';
-  channel_modes(cptr, modebuf, parabuf, chptr);
-
-  if (*parabuf)
-    strcat(parabuf, " ");
-  ircsprintf(buf, ":%s SJOIN %lu %s %s %s:", me.name,
-          chptr->channelts, chptr->chname, modebuf, parabuf);
-  t = buf + strlen(buf);
-  for (l = chptr->members; l && l->value.cptr; l = l->next)
-    if (l->flags & MODE_CHANOP)
-      {
-        anop = l;
-        break;
-      }
-  /* follow the channel, but doing anop first if it's defined
-  **  -orabidoo
-  */
-  l = NULL;
-  for (;;)
-    {
-      if (anop)
-        {
-          l = skip = anop;
-          anop = NULL;
-        }
-      else 
-        {
-          if (l == NULL || l == skip)
-            l = chptr->members;
-          else
-            l = l->next;
-          if (l && l == skip)
-            l = l->next;
-          if (l == NULL)
-            break;
-        }
-      if (l->flags & MODE_CHANOP)
-        *t++ = '@';
-      if (l->flags & MODE_VOICE)
-        *t++ = '+';
-      strcpy(t, l->value.cptr->name);
-      t += strlen(t);
-      *t++ = ' ';
-      n++;
-      if (t - buf > BUFSIZE - 80)
-        {
-          *t++ = '\0';
-          if (t[-1] == ' ') t[-1] = '\0';
-          sendto_one(cptr, "%s", buf);
-          ircsprintf(buf, ":%s SJOIN %lu %s 0 :",
-                  me.name, chptr->channelts,
-                  chptr->chname);
-          t = buf + strlen(buf);
-          n = 0;
-        }
-    }
-      
-  if (n)
-    {
-      *t++ = '\0';
-      if (t[-1] == ' ') t[-1] = '\0';
-      sendto_one(cptr, "%s", buf);
-    }
-  *parabuf = '\0';
-  *modebuf = '+';
-  modebuf[1] = '\0';
-  send_mode_list(cptr, chptr->chname, chptr->banlist, CHFL_BAN,'b');
-
-  if (modebuf[1] || *parabuf)
-    sendto_one(cptr, ":%s MODE %s %s %s",
-               me.name, chptr->chname, modebuf, parabuf);
-
-  if(!IsCapable(cptr,CAP_EX))
-    return;
-
-  *parabuf = '\0';
-  *modebuf = '+';
-  modebuf[1] = '\0';
-  send_mode_list(cptr, chptr->chname, chptr->exceptlist, CHFL_EXCEPTION,'e');
-
-  if (modebuf[1] || *parabuf)
-    sendto_one(cptr, ":%s MODE %s %s %s",
-               me.name, chptr->chname, modebuf, parabuf);
-
-  if(!IsCapable(cptr,CAP_DE))
-      return;
-  *parabuf = '\0';
-  *modebuf = '+';
-  modebuf[1] = '\0';
-  send_mode_list(cptr, chptr->chname, chptr->denylist, CHFL_DENY,'d');
-  
-  if (modebuf[1] || *parabuf)
-    sendto_one(cptr, ":%s MODE %s %s %s",
-               me.name, chptr->chname, modebuf, parabuf);
-}
-
-/* stolen from Undernet's ircd  -orabidoo
- *
- */
-
-static char* pretty_mask(char* mask)
-{
-  register char* cp = mask;
-  register char* user;
-  register char* host;
-
-  if ((user = strchr(cp, '!')))
-    *user++ = '\0';
-  if ((host = strrchr(user ? user : cp, '@')))
-    {
-      *host++ = '\0';
-      if (!user)
-        return make_nick_user_host("*", check_string(cp), check_string(host));
-    }
-  else if (!user && strchr(cp, '.'))
-    return make_nick_user_host("*", "*", check_string(cp));
-  return make_nick_user_host(check_string(cp), check_string(user), 
-                             check_string(host));
-}
-
-static  char    *fix_key(char *arg)
-{
-  u_char        *s, *t, c;
-
-  for (s = t = (u_char *)arg; (c = *s); s++)
-    {
-      if (c != ':' && c > 0x20)
-      {
-	c &= 0x7f;
-        *t++ = c;
-      }
-    }
-  *t = '\0';
-  return arg;
-}
-
-/*
- * Here we attempt to be compatible with older non-hybrid servers.
- * We can't back down from the ':' issue however.  --Rodder
- */
-static  char    *fix_key_old(char *arg)
-{
-  u_char        *s, *t, c;
-
-  for (s = t = (u_char *)arg; (c = *s); s++)
-    { 
-      c &= 0x7f;
-      if ((c != 0x0a) && (c != ':'))
-        *t++ = c;
-    }
-  *t = '\0';
-  return arg;
-}
-
-/*
- * like the name says...  take out the redundant signs in a modechange list
- */
-static  void    collapse_signs(char *s)
-{
-  char  plus = '\0', *t = s, c;
-  while ((c = *s++))
-    {
-      if (c != plus)
-        *t++ = c;
-      if (c == '+' || c == '-')
-        plus = c;
-    }
-  *t = '\0';
-}
-
-/* little helper function to avoid returning duplicate errors */
-static  int     errsent(int err, int *errs)
-{
-  if (err & *errs)
-    return 1;
-  *errs |= err;
-  return 0;
-}
-
-/* bitmasks for various error returns that set_mode should only return
- * once per call  -orabidoo
- */
-
-#define SM_ERR_NOTS             0x00000001      /* No TS on channel */
-#define SM_ERR_NOOPS            0x00000002      /* No chan ops */
-#define SM_ERR_UNKNOWN          0x00000004
-#define SM_ERR_RPL_C            0x00000008
-#define SM_ERR_RPL_B            0x00000010
-#define SM_ERR_RPL_E            0x00000020
-#define SM_ERR_NOTONCHANNEL     0x00000040      /* Not on channel */
-#define SM_ERR_RESTRICTED       0x00000080      /* Restricted chanop */
-
-/*
-** Apply the mode changes passed in parv to chptr, sending any error
-** messages and MODE commands out.  Rewritten to do the whole thing in
-** one pass, in a desperate attempt to keep the code sane.  -orabidoo
-*/
-/*
- * rewritten to remove +h/+c/z 
- * in spirit with the one pass idea, I've re-written how "imnspt"
- * handling was done
- *
- * I've also left some "remnants" of the +h code in for possible
- * later addition.
- * For example, isok could be replaced witout half ops, with ischop() or
- * chan_op depending.
- *
- * -Dianora
- */
-
-void set_channel_mode(struct Client *cptr,
-                      struct Client *sptr,
-                      struct Channel *chptr,
-                      int parc,
-                      char *parv[])
-{
-  int   errors_sent = 0, opcnt = 0, len = 0, tmp, nusers;
-  int   keychange = 0, limitset = 0;
-  int   whatt = MODE_ADD, the_mode = 0;
-#ifdef OLD_P_S
-  int   done_s_or_p = NO;
-#else
-  int   done_s = NO, done_p = NO;
-#endif
-  int   done_i = NO, done_m = NO, done_n = NO, done_t = NO;
-  struct Client *who;
-  Link  *lp;
-  char  *curr = parv[0], c, *arg, plus = '+', *tmpc;
-  char  numeric[16];
-  /* mbufw gets the param-less mode chars, always with their sign
-   * mbuf2w gets the paramed mode chars, always with their sign
-   * pbufw gets the params, in ID form whenever possible
-   * pbuf2w gets the params, no ID's
-   */
-  /* no ID code at the moment
-   * pbufw gets the params, no ID's
-   * grrrr for now I'll stick the params into pbufw without ID's
-   * -Dianora
-   */
-  /* *sigh* FOR YOU Roger, and ONLY for you ;-)
-   * lets stick mode/params that only the newer servers will understand
-   * into modebuf_new/parabuf_new 
-   * even worse!  nodebuf_newer/parabuf_newer <-- for CAP_DE       
-   */
-
-  char  modebuf_newer[MODEBUFLEN];
-  char  parabuf_newer[MODEBUFLEN];
-
-  char  modebuf_new[MODEBUFLEN];
-  char  parabuf_new[MODEBUFLEN];
-
-  char  *mbufw = modebuf, *mbuf2w = modebuf2;
-  char  *pbufw = parabuf, *pbuf2w = parabuf2;
-
-  char  *mbufw_new = modebuf_new;
-  char  *pbufw_new = parabuf_new;
-
-  char  *mbufw_newer = modebuf_newer;
-  char  *pbufw_newer = parabuf_newer;
-
-  int   ischop;
-  int   isok;
-  int   isdeop;
-  int   chan_op;
-  int   self_lose_ops;
-  int   user_mode;
-
-  self_lose_ops = 0;
-
-  user_mode = user_channel_mode(sptr, chptr);
-  chan_op = (user_mode & CHFL_CHANOP);
-
-  /* has ops or is a server */
-  ischop = IsServer(sptr) || chan_op;
-
-  /* is client marked as deopped */
-  isdeop = !ischop && !IsServer(sptr) && (user_mode & CHFL_DEOPPED);
-
-  /* is an op or server or remote user on a TS channel */
-  isok = ischop || (!isdeop && IsServer(cptr) && chptr->channelts);
-
-  if(isok)
-    chptr->keep_their_modes = YES;
-
-  /* isok_c calculated later, only if needed */
-
-  /* parc is the number of _remaining_ args (where <0 means 0);
-  ** parv points to the first remaining argument
-  */
-  parc--;
-  parv++;
-
-  for ( ; ; )
-    {
-      if (BadPtr(curr))
-        {
-          /*
-           * Deal with mode strings like "+m +o blah +i"
-           */
-          if (parc-- > 0)
-            {
-              curr = *parv++;
-              continue;
-            }
-          break;
-        }
-      c = *curr++;
-
-      switch (c)
-        {
-        case '+' :
-          whatt = MODE_ADD;
-          plus = '+';
-          continue;
-          /* NOT REACHED */
-          break;
-
-        case '-' :
-          whatt = MODE_DEL;
-          plus = '-';
-          continue;
-          /* NOT REACHED */
-          break;
-
-        case '=' :
-          whatt = MODE_QUERY;
-          plus = '=';   
-          continue;
-          /* NOT REACHED */
-          break;
-
-        case 'o' :
-        case 'v' :
-          if (MyClient(sptr))
-            {
-              if(!IsMember(sptr, chptr))
-                {
-                  if(!errsent(SM_ERR_NOTONCHANNEL, &errors_sent))
-                    sendto_one(sptr, form_str(ERR_NOTONCHANNEL),
-                               me.name, sptr->name, chptr->chname);
-                  /* eat the parameter */
-                  parc--;
-                  parv++;
-                  break;
-                }
-#ifdef LITTLE_I_LINES
-              else
-                {
-                  if(IsRestricted(sptr) && (whatt == MODE_ADD))
-                    {
-                      if(!errsent(SM_ERR_RESTRICTED, &errors_sent))
-                        {
-                          sendto_one(sptr,
-            ":%s NOTICE %s :*** Notice -- You are restricted and cannot chanop others",
-                                 me.name,
-                                 sptr->name);
-                        }
-                      /* eat the parameter */
-                      parc--;
-                      parv++;
-                      break;
-                    }
-                }
-#endif
-            }
-          if (whatt == MODE_QUERY)
-            break;
-          if (parc-- <= 0)
-            break;
-          arg = check_string(*parv++);
-
-          if (MyClient(sptr) && opcnt >= MAXMODEPARAMS)
-            break;
-
-          if (!(who = find_chasing(sptr, arg, NULL)))
-            break;
-
-          /* there is always the remote possibility of picking up
-           * a bogus user, be nasty to core for that. -Dianora
-           */
-
-          if (!who->user)
-            break;
-
-          /* no more of that mode bouncing crap */
-          if (!IsMember(who, chptr))
-            {
-              if (MyClient(sptr))
-                sendto_one(sptr, form_str(ERR_USERNOTINCHANNEL), me.name, 
-                           sptr->name, arg, chptr->chname);
-              break;
-            }
-
-          if ((who == sptr) && (c == 'o'))
-            {
-              if(whatt == MODE_ADD)
-                break;
-              
-              if(whatt == MODE_DEL)
-                self_lose_ops = 1;
-              }
-
-          /* ignore server-generated MODE +-ovh */
-          if (IsServer(sptr))
-            {
-              ts_warn( "MODE %c%c on %s for %s from server %s (ignored)", 
-                       (whatt == MODE_ADD ? '+' : '-'), c, chptr->chname, 
-                       who->name,sptr->name);
-              break;
-            }
-
-          if (c == 'o')
-            the_mode = MODE_CHANOP;
-          else if (c == 'v')
-            the_mode = MODE_VOICE;
-
-          if (isdeop && (c == 'o') && whatt == MODE_ADD)
-            set_deopped(who, chptr, the_mode);
-
-          if (!isok)
-            {
-              if (MyClient(sptr) && !errsent(SM_ERR_NOOPS, &errors_sent))
-                sendto_one(sptr, form_str(ERR_CHANOPRIVSNEEDED), me.name, 
-                           sptr->name, chptr->chname);
-              break;
-            }
-        
-          tmp = strlen(arg);
-          if (len + tmp + 2 >= MODEBUFLEN)
-            break;
-
-          *mbufw++ = plus;
-          *mbufw++ = c;
-          strcpy(pbufw, who->name);
-          pbufw += strlen(pbufw);
-          *pbufw++ = ' ';
-          len += tmp + 1;
-          opcnt++;
-
-          change_chan_flag(chptr, who, the_mode|whatt);
-
-          if (self_lose_ops)
-            isok = 0;
-
-          break;
-
-        case 'k':
-          if (whatt == MODE_QUERY)
-            break;
-          if (parc-- <= 0)
-            {
-              /* allow arg-less mode -k */
-              if (whatt == MODE_DEL)
-                arg = "*";
-              else
-                break;
-            }
-          else
-            {
-              if (whatt == MODE_DEL)
-                {
-                  arg = check_string(*parv++);
-                }
-              else
-                {
-                  if MyClient(sptr)
-                    arg = fix_key(check_string(*parv++));
-                  else
-                    arg = fix_key_old(check_string(*parv++));
-                }
-            }
-
-          if (keychange++)
-            break;
-          /*      if (MyClient(sptr) && opcnt >= MAXMODEPARAMS)
-            break;
-            */
-          if (!*arg)
-            break;
-
-          if (!isok)
-            {
-              if (!errsent(SM_ERR_NOOPS, &errors_sent) && MyClient(sptr))
-                sendto_one(sptr, form_str(ERR_CHANOPRIVSNEEDED), me.name, 
-                           sptr->name, chptr->chname);
-              break;
-            }
-
-#ifdef OLD_MODE_K
-          if ((whatt == MODE_ADD) && (*chptr->mode.key))
-            {
-              sendto_one(sptr, form_str(ERR_KEYSET), me.name, 
-                         sptr->name, chptr->chname);
-              break;
-            }
-#endif
-          if ( (tmp = strlen(arg)) > KEYLEN)
-            {
-              arg[KEYLEN] = '\0';
-              tmp = KEYLEN;
-            }
-
-          if (len + tmp + 2 >= MODEBUFLEN)
-            break;
-
-#ifndef OLD_MODE_K
-          /* if there is already a key, and the client is adding one
-           * remove the old one, then add the new one
-           */
-
-          if((whatt == MODE_ADD) && *chptr->mode.key)
-            {
-              /* If the key is the same, don't do anything */
-
-              if(!strcmp(chptr->mode.key,arg))
-                break;
-
-              sendto_channel_butserv(chptr, sptr, ":%s MODE %s -k %s", 
-                                     sptr->name, chptr->chname,
-                                     chptr->mode.key);
-
-              sendto_match_servs(chptr, cptr, ":%s MODE %s -k %s",
-                                 sptr->name, chptr->chname,
-                                 chptr->mode.key);
-            }
-#endif
-          if (whatt == MODE_DEL)
-            {
-              if( (arg[0] == '*') && (arg[1] == '\0'))
-                arg = chptr->mode.key;
-              else
-                {
-                  if(strcmp(arg,chptr->mode.key))
-                    break;
-		}
-	    }
-
-          *mbufw++ = plus;
-          *mbufw++ = 'k';
-          strcpy(pbufw, arg);
-          pbufw += strlen(pbufw);
-          *pbufw++ = ' ';
-          len += tmp + 1;
-          /*      opcnt++; */
-
-          if (whatt == MODE_DEL)
-            {
-              *chptr->mode.key = '\0';
-            }
-          else
-            {
-              /*
-               * chptr was zeroed
-               */
-              strncpy_irc(chptr->mode.key, arg, KEYLEN);
-            }
-
-          break;
-
-          /* There is a nasty here... I'm supposed to have
-           * CAP_EX before I can send exceptions to bans to a server.
-           * But that would mean I'd have to keep two strings
-           * one for local clients, and one for remote servers,
-           * one with the 'e' strings, one without.
-           * I added another parameter buf and mode buf for "new"
-           * capabilities.
-           *
-           * -Dianora
-           */
-
-        case 'e':
-          if (whatt == MODE_QUERY || parc-- <= 0)
-            {
-              if (!MyClient(sptr))
-                break;
-              if (errsent(SM_ERR_RPL_E, &errors_sent))
-                break;
-              /* don't allow a non chanop to see the exception list
-               * suggested by Matt on operlist nov 25 1998
-               */
-              if(isok)
-                {
-#ifdef BAN_INFO
-                  for (lp = chptr->exceptlist; lp; lp = lp->next)
-                    sendto_one(cptr, form_str(RPL_EXCEPTLIST),
-                               me.name, cptr->name,
-                               chptr->chname,
-                               lp->value.banptr->banstr,
-                               lp->value.banptr->who,
-                               lp->value.banptr->when);
-#else 
-                  for (lp = chptr->exceptlist; lp; lp = lp->next)
-                    sendto_one(cptr, form_str(RPL_EXCEPTLIST),
-                               me.name, cptr->name,
-                               chptr->chname,
-                               lp->value.cp);
-#endif
-                  sendto_one(sptr, form_str(RPL_ENDOFEXCEPTLIST),
-                             me.name, sptr->name, 
-                             chptr->chname);
-                }
-              break;
-            }
-          arg = check_string(*parv++);
-
-          if (MyClient(sptr) && opcnt >= MAXMODEPARAMS)
-            break;
-
-          if (!isok)
-            {
-              if (!errsent(SM_ERR_NOOPS, &errors_sent) && MyClient(sptr))
-                sendto_one(sptr, form_str(ERR_CHANOPRIVSNEEDED),
-                           me.name, sptr->name, 
-                           chptr->chname);
-              break;
-            }
-          
-          if(MyClient(sptr))
-            chptr->keep_their_modes = YES;
-          else if(!chptr->keep_their_modes)
-            {
-              parc--;
-              parv++;
-              break;
-            }
-
-          /* user-friendly ban mask generation, taken
-          ** from Undernet's ircd  -orabidoo
-          */
-          if (MyClient(sptr))
-            arg = collapse(pretty_mask(arg));
-
-          if(*arg == ':')
-            {
-              parc--;
-              parv++;
-              break;
-            }
-
-          tmp = strlen(arg);
-          if (len + tmp + 2 >= MODEBUFLEN)
-            break;
-
-          if (!(((whatt & MODE_ADD) && !add_exceptid(sptr, chptr, arg)) ||
-                ((whatt & MODE_DEL) && !del_exceptid(chptr, arg))))
-            break;
-
-          /* This stuff can go back in when all servers understand +e 
-           * with the pbufw_new nonsense removed -Dianora
-           */
-
-          /*
-          *mbufw++ = plus;
-          *mbufw++ = 'e';
-          strcpy(pbufw, arg);
-          pbufw += strlen(pbufw);
-          *pbufw++ = ' ';
-          */
-          len += tmp + 1;
-          opcnt++;
-
-          *mbufw_new++ = plus;
-          *mbufw_new++ = 'e';
-          strcpy(pbufw_new, arg);
-          pbufw_new += strlen(pbufw_new);
-          *pbufw_new++ = ' ';
-
-          break;
-
-
-          /* There is a nasty here... I'm supposed to have
-           * CAP_DE before I can send exceptions to bans to a server.
-           * But that would mean I'd have to keep two strings
-           * one for local clients, and one for remote servers,
-           * one with the 'e' strings, one without.
-           * I added another parameter buf and mode buf for "new"
-           * capabilities.
-           *
-           * -Dianora
-           */
-
-        case 'd':
-          if (whatt == MODE_QUERY || parc-- <= 0)
-            {
-              if (!MyClient(sptr))
-                break;
-              if (errsent(SM_ERR_RPL_E, &errors_sent))
-                break;
-#ifdef BAN_INFO
-                  for (lp = chptr->denylist; lp; lp = lp->next)
-                    sendto_one(cptr, form_str(RPL_BANLIST),
-                               me.name, cptr->name,
-                               chptr->chname,
-                               lp->value.banptr->banstr,
-                               lp->value.banptr->who,
-                               lp->value.banptr->when);
-#else 
-                  for (lp = chptr->denylist; lp; lp = lp->next)
-                    sendto_one(cptr, form_str(RPL_BANLIST),
-                               me.name, cptr->name,
-                               chptr->chname,
-                               lp->value.cp);
-#endif
-                  sendto_one(sptr, form_str(RPL_ENDOFBANLIST),
-                             me.name, sptr->name, 
-                             chptr->chname);
-                  break;
-            }
-          arg = check_string(*parv++);
-
-          if (MyClient(sptr) && opcnt >= MAXMODEPARAMS)
-            break;
-
-          if (!isok)
-            {
-              if (!errsent(SM_ERR_NOOPS, &errors_sent) && MyClient(sptr))
-                sendto_one(sptr, form_str(ERR_CHANOPRIVSNEEDED),
-                           me.name, sptr->name, 
-                           chptr->chname);
-              break;
-            }
-          
-          if(MyClient(sptr))
-            chptr->keep_their_modes = YES;
-          else if(!chptr->keep_their_modes)
-            {
-              parc--;
-              parv++;
-              break;
-            }
-
-          if(*arg == ':')
-            {
-              parc--;
-              parv++;
-              break;
-            }
-
-          tmp = strlen(arg);
-          if (len + tmp + 2 >= MODEBUFLEN)
-            break;
-
-          if (!(((whatt & MODE_ADD) && !add_denyid(sptr, chptr, arg)) ||
-                ((whatt & MODE_DEL) && !del_denyid(chptr, arg))))
-            break;
-
-          /* This stuff can go back in when all servers understand +e 
-           * with the pbufw_new nonsense removed -Dianora
-           */
-
-          /*
-          *mbufw++ = plus;
-          *mbufw++ = 'e';
-          strcpy(pbufw, arg);
-          pbufw += strlen(pbufw);
-          *pbufw++ = ' ';
-          */
-          len += tmp + 1;
-          opcnt++;
-
-          *mbufw_newer++ = plus;
-          *mbufw_newer++ = 'd';
-          strcpy(pbufw_newer, arg);
-          pbufw_newer += strlen(pbufw_newer);
-          *pbufw_newer++ = ' ';
-
-          break;
-
-        case 'b':
-          if (whatt == MODE_QUERY || parc-- <= 0)
-            {
-              if (!MyClient(sptr))
-                break;
-
-              if (errsent(SM_ERR_RPL_B, &errors_sent))
-                break;
-#ifdef BAN_INFO
-              for (lp = chptr->banlist; lp; lp = lp->next)
-                sendto_one(cptr, form_str(RPL_BANLIST),
-                           me.name, cptr->name,
-                           chptr->chname,
-                           lp->value.banptr->banstr,
-                           lp->value.banptr->who,
-                           lp->value.banptr->when);
-#else 
-              for (lp = chptr->banlist; lp; lp = lp->next)
-                sendto_one(cptr, form_str(RPL_BANLIST),
-                           me.name, cptr->name,
-                           chptr->chname,
-                           lp->value.cp);
-#endif
-              sendto_one(sptr, form_str(RPL_ENDOFBANLIST),
-                         me.name, sptr->name, 
-                         chptr->chname);
-              break;
-            }
-
-          if(MyClient(sptr))
-            chptr->keep_their_modes = YES;
-          else if(!chptr->keep_their_modes)
-            {
-              parc--;
-              parv++;
-              break;
-            }
-
-          arg = check_string(*parv++);
-
-          if (MyClient(sptr) && opcnt >= MAXMODEPARAMS)
-            break;
-
-          if (!isok)
-            {
-              if (!errsent(SM_ERR_NOOPS, &errors_sent) && MyClient(sptr))
-                sendto_one(sptr, form_str(ERR_CHANOPRIVSNEEDED),
-                           me.name, sptr->name, 
-                           chptr->chname);
-              break;
-            }
-
-
-          /* Ignore colon at beginning of ban string.
-           * Unfortunately, I can't ignore all such strings,
-           * because otherwise the channel could get desynced.
-           * I can at least, stop local clients from placing a ban
-           * with a leading colon.
-           *
-           * Roger uses check_string() combined with an earlier test
-           * in his TS4 code. The problem is, this means on a mixed net
-           * one can't =remove= a colon prefixed ban if set from
-           * an older server.
-           * His code is more efficient though ;-/ Perhaps
-           * when we've all upgraded this code can be moved up.
-           *
-           * -Dianora
-           */
-
-          /* user-friendly ban mask generation, taken
-          ** from Undernet's ircd  -orabidoo
-          */
-          if (MyClient(sptr))
-            {
-              if( (*arg == ':') && (whatt & MODE_ADD) )
-                {
-                  parc--;
-                  parv++;
-                  break;
-                }
-              arg = collapse(pretty_mask(arg));
-            }
-
-          tmp = strlen(arg);
-          if (len + tmp + 2 >= MODEBUFLEN)
-            break;
-
-          if (!(((whatt & MODE_ADD) && !add_banid(sptr, chptr, arg)) ||
-                ((whatt & MODE_DEL) && !del_banid(chptr, arg))))
-            break;
-
-          *mbufw++ = plus;
-          *mbufw++ = 'b';
-          strcpy(pbufw, arg);
-          pbufw += strlen(pbufw);
-          *pbufw++ = ' ';
-          len += tmp + 1;
-          opcnt++;
-
-          break;
-
-        case 'l':
-          if (whatt == MODE_QUERY)
-            break;
-          if (!isok || limitset++)
-            {
-              if (whatt == MODE_ADD && parc-- > 0)
-                parv++;
-              break;
-            }
-
-          if (whatt == MODE_ADD)
-            {
-              if (parc-- <= 0)
-                {
-                  if (MyClient(sptr))
-                    sendto_one(sptr, form_str(ERR_NEEDMOREPARAMS),
-                               me.name, sptr->name, "MODE +l");
-                  break;
-                }
-              
-              arg = check_string(*parv++);
-              /*              if (MyClient(sptr) && opcnt >= MAXMODEPARAMS)
-                break; */
-              if ((nusers = atoi(arg)) <= 0)
-                break;
-              ircsprintf(numeric, "%d", nusers);
-              if ((tmpc = strchr(numeric, ' ')))
-                *tmpc = '\0';
-              arg = numeric;
-
-              tmp = strlen(arg);
-              if (len + tmp + 2 >= MODEBUFLEN)
-                break;
-
-              chptr->mode.limit = nusers;
-              chptr->mode.mode |= MODE_LIMIT;
-
-              *mbufw++ = '+';
-              *mbufw++ = 'l';
-              strcpy(pbufw, arg);
-              pbufw += strlen(pbufw);
-              *pbufw++ = ' ';
-              len += tmp + 1;
-              /*              opcnt++;*/
-            }
-          else
-            {
-              chptr->mode.limit = 0;
-              chptr->mode.mode &= ~MODE_LIMIT;
-              *mbufw++ = '-';
-              *mbufw++ = 'l';
-            }
-
-          break;
-
-          /* Traditionally, these are handled separately
-           * but I decided to combine them all into this one case
-           * statement keeping it all sane
-           *
-           * The disadvantage is a lot more code duplicated ;-/
-           *
-           * -Dianora
-           */
-
-        case 'i' :
-          if (whatt == MODE_QUERY)      /* shouldn't happen. */
-            break;
-          if (!isok)
-            {
-              if (MyClient(sptr) && !errsent(SM_ERR_NOOPS, &errors_sent))
-                sendto_one(sptr, form_str(ERR_CHANOPRIVSNEEDED), me.name, 
-                           sptr->name, chptr->chname);
-              break;
-            }
-
-          if(MyClient(sptr))
-            {
-              if(done_i)
-                break;
-              else
-                done_i = YES;
-
-              /*              if ( opcnt >= MAXMODEPARAMS)
-                break; */
-            }
-
-          if(whatt == MODE_ADD)
-            {
-              if (len + 2 >= MODEBUFLEN)
-                break;
-#ifdef OLD_NON_RED
-              if(!(chptr->mode.mode & MODE_INVITEONLY))
-#endif
-                {
-                  chptr->mode.mode |= MODE_INVITEONLY;
-                  *mbufw++ = '+';
-                  *mbufw++ = 'i';
-                  len += 2;
-                  /*              opcnt++; */
-                }
-            }
-          else
-            {
-              if (len + 2 >= MODEBUFLEN)
-                break;
-
-              while ( (lp = chptr->invites) )
-                del_invite(lp->value.cptr, chptr);
-
-#ifdef OLD_NON_RED
-              if(chptr->mode.mode & MODE_INVITEONLY)
-#endif
-                {
-                  chptr->mode.mode &= ~MODE_INVITEONLY;
-                  *mbufw++ = '-';
-                  *mbufw++ = 'i';
-                  len += 2;
-                  /*              opcnt++; */
-                }
-            }
-          break;
-
-          /* Un documented for now , I have no idea how this got here ;-) */
-#ifdef JUPE_CHANNEL
-        case 'j':
-          if(MyConnect(sptr) && IsAnOper(sptr))
-            {
-              if (whatt == MODE_ADD)
-                {
-                  chptr->mode.mode |= MODE_JUPED;
-                  sendto_realops("%s!%s@%s juping locally Channel %s",
-                                 sptr->name, sptr->username,
-                                 sptr->host, chptr->chname);
-                }
-              else if(whatt == MODE_DEL)
-                {
-                  chptr->mode.mode &= ~MODE_JUPED;
-                  sendto_realops("%s!%s@%s unjuping locally Channel %s",
-                                 sptr->name, sptr->username,
-                                 sptr->host, chptr->chname);
-                }
-            }
-          break;
-#endif
-
-        case 'm' :
-          if (!isok)
-            {
-              if (MyClient(sptr) && !errsent(SM_ERR_NOOPS, &errors_sent))
-                sendto_one(sptr, form_str(ERR_CHANOPRIVSNEEDED), me.name, 
-                           sptr->name, chptr->chname);
-              break;
-            }
-
-          if(MyClient(sptr))
-            {
-              if(done_m)
-                break;
-              else
-                done_m = YES;
-
-              /*              if ( opcnt >= MAXMODEPARAMS)
-                              break; */
-            }
-
-          if(whatt == MODE_ADD)
-            {
-              if (len + 2 >= MODEBUFLEN)
-                break;
-#ifdef OLD_NON_RED
-              if(!(chptr->mode.mode & MODE_MODERATED))
-#endif
-                {
-                  chptr->mode.mode |= MODE_MODERATED;
-                  *mbufw++ = '+';
-                  *mbufw++ = 'm';
-                  len += 2;
-                  /*              opcnt++; */
-                }
-            }
-          else
-            {
-              if (len + 2 >= MODEBUFLEN)
-                break;
-#ifdef OLD_NON_RED
-              if(chptr->mode.mode & MODE_MODERATED)
-#endif
-                {
-                  chptr->mode.mode &= ~MODE_MODERATED;
-                  *mbufw++ = '-';
-                  *mbufw++ = 'm';
-                  len += 2;
-                  /*              opcnt++; */
-                }
-            }
-          break;
-
-        case 'n' :
-          if (!isok)
-            {
-              if (MyClient(sptr) && !errsent(SM_ERR_NOOPS, &errors_sent))
-                sendto_one(sptr, form_str(ERR_CHANOPRIVSNEEDED), me.name, 
-                           sptr->name, chptr->chname);
-              break;
-            }
-
-          if(MyClient(sptr))
-            {
-              if(done_n)
-                break;
-              else
-                done_n = YES;
-
-              /*              if ( opcnt >= MAXMODEPARAMS)
-                              break; */
-            }
-
-          if(whatt == MODE_ADD)
-            {
-              if (len + 2 >= MODEBUFLEN)
-                break;
-#ifdef OLD_NON_RED
-              if(!(chptr->mode.mode & MODE_NOPRIVMSGS))
-#endif
-                {
-                  chptr->mode.mode |= MODE_NOPRIVMSGS;
-                  *mbufw++ = '+';
-                  *mbufw++ = 'n';
-                  len += 2;
-                  /*              opcnt++; */
-                }
-            }
-          else
-            {
-              if (len + 2 >= MODEBUFLEN)
-                break;
-#ifdef OLD_NON_RED
-              if(chptr->mode.mode & MODE_NOPRIVMSGS)
-#endif
-                {
-                  chptr->mode.mode &= ~MODE_NOPRIVMSGS;
-                  *mbufw++ = '-';
-                  *mbufw++ = 'n';
-                  len += 2;
-                  /*              opcnt++; */
-                }
-            }
-          break;
-
-        case 'p' :
-          if (!isok)
-            {
-              if (MyClient(sptr) && !errsent(SM_ERR_NOOPS, &errors_sent))
-                sendto_one(sptr, form_str(ERR_CHANOPRIVSNEEDED), me.name, 
-                           sptr->name, chptr->chname);
-              break;
-            }
-
-          if(MyClient(sptr))
-            {
-#ifdef OLD_P_S
-              if(done_s_or_p)
-                break;
-              else
-                done_s_or_p = YES;
-#else
-              if(done_p)
-                break;
-              else
-                done_p = YES;
-#endif
-              /*              if ( opcnt >= MAXMODEPARAMS)
-                break; */
-            }
-
-          if(whatt == MODE_ADD)
-            {
-              if (len + 2 >= MODEBUFLEN)
-                break;
-#ifdef OLD_P_S
-              if(chptr->mode.mode & MODE_SECRET)
-                {
-                  if (len + 2 >= MODEBUFLEN)
-                    break;
-                  *mbufw++ = '-';
-                  *mbufw++ = 's';
-                  len += 2;
-                  chptr->mode.mode &= ~MODE_SECRET;
-                }
-#endif
-#ifdef OLD_NON_RED
-              if(!(chptr->mode.mode & MODE_PRIVATE))
-#endif
-                {
-                  chptr->mode.mode |= MODE_PRIVATE;
-                  *mbufw++ = '+';
-                  *mbufw++ = 'p';
-                  len += 2;
-                  /*              opcnt++; */
-                }
-            }
-          else
-            {
-              if (len + 2 >= MODEBUFLEN)
-                break;
-#ifdef OLD_NON_RED
-              if(chptr->mode.mode & MODE_PRIVATE)
-#endif
-                {
-                  chptr->mode.mode &= ~MODE_PRIVATE;
-                  *mbufw++ = '-';
-                  *mbufw++ = 'p';
-                  len += 2;
-                  /*              opcnt++; */
-                }
-            }
-          break;
-
-        case 's' :
-          if (!isok)
-            {
-              if (MyClient(sptr) && !errsent(SM_ERR_NOOPS, &errors_sent))
-                sendto_one(sptr, form_str(ERR_CHANOPRIVSNEEDED), me.name, 
-                           sptr->name, chptr->chname);
-              break;
-            }
-
-          /* ickity poo, traditional +p-s nonsense */
-
-          if(MyClient(sptr))
-            {
-#ifdef OLD_P_S
-              if(done_s_or_p)
-                break;
-              else
-                done_s_or_p = YES;
-#else
-              if(done_s)
-                break;
-              else
-                done_s = YES;
-#endif
-              /*              if ( opcnt >= MAXMODEPARAMS)
-                              break; */
-            }
-
-          if(whatt == MODE_ADD)
-            {
-              if (len + 2 >= MODEBUFLEN)
-                break;
-#ifdef OLD_P_S
-              if(chptr->mode.mode & MODE_PRIVATE)
-                {
-                  if (len + 2 >= MODEBUFLEN)
-                    break;
-                  *mbufw++ = '-';
-                  *mbufw++ = 'p';
-                  len += 2;
-                  chptr->mode.mode &= ~MODE_PRIVATE;
-                }
-#endif
-#ifdef OLD_NON_RED
-              if(!(chptr->mode.mode & MODE_SECRET))
-#endif
-                {
-                  chptr->mode.mode |= MODE_SECRET;
-                  *mbufw++ = '+';
-                  *mbufw++ = 's';
-                  len += 2;
-                  /*              opcnt++; */
-                }
-            }
-          else
-            {
-              if (len + 2 >= MODEBUFLEN)
-                break;
-#ifdef OLD_NON_RED
-              if(chptr->mode.mode & MODE_SECRET)
-#endif
-                {
-                  chptr->mode.mode &= ~MODE_SECRET;
-                  *mbufw++ = '-';
-                  *mbufw++ = 's';
-                  len += 2;
-                  /*              opcnt++; */
-                }
-            }
-          break;
-
-        case 't' :
-          if (!isok)
-            {
-              if (MyClient(sptr) && !errsent(SM_ERR_NOOPS, &errors_sent))
-                sendto_one(sptr, form_str(ERR_CHANOPRIVSNEEDED), me.name, 
-                           sptr->name, chptr->chname);
-              break;
-            }
-
-          if(MyClient(sptr))
-            {
-              if(done_t)
-                break;
-              else
-                done_t = YES;
-
-              /*              if ( opcnt >= MAXMODEPARAMS)
-                              break; */
-            }
-
-          if(whatt == MODE_ADD)
-            {
-              if (len + 2 >= MODEBUFLEN)
-                break;
-#ifdef OLD_NON_RED
-              if(!(chptr->mode.mode & MODE_TOPICLIMIT))
-#endif
-                {
-                  chptr->mode.mode |= MODE_TOPICLIMIT;
-                  *mbufw++ = '+';
-                  *mbufw++ = 't';
-                  len += 2;
-                  /*              opcnt++; */
-                }
-            }
-          else
-            {
-              if (len + 2 >= MODEBUFLEN)
-                break;
-#ifdef OLD_NON_RED
-              if(chptr->mode.mode & MODE_TOPICLIMIT)
-#endif
-                {
-                  chptr->mode.mode &= ~MODE_TOPICLIMIT;
-                  *mbufw++ = '-';
-                  *mbufw++ = 't';
-                  len += 2;
-                  /*              opcnt++; */
-                }
-            }
-          break;
-
-        default:
-          if (whatt == MODE_QUERY)
-            break;
-
-          /* only one "UNKNOWNMODE" per mode... we don't want
-          ** to generate a storm, even if it's just to a 
-          ** local client  -orabidoo
-          */
-          if (MyClient(sptr) && !errsent(SM_ERR_UNKNOWN, &errors_sent))
-            sendto_one(sptr, form_str(ERR_UNKNOWNMODE), me.name, sptr->name, c);
-          break;
-        }
-    }
-
-  /*
-  ** WHEW!!  now all that's left to do is put the various bufs
-  ** together and send it along.
-  */
-
-  *mbufw = *mbuf2w = *pbufw = *pbuf2w = *mbufw_new = *pbufw_new = 
-  *mbufw_newer = *pbufw_newer = '\0';
-
-  collapse_signs(modebuf);
-/*  collapse_signs(modebuf2); */
-  collapse_signs(modebuf_new);
-  collapse_signs(modebuf_newer);
-
-  if(*modebuf)
-    {
-      sendto_channel_butserv(chptr, sptr, ":%s MODE %s %s %s", 
-                           sptr->name, chptr->chname,
-                           modebuf, parabuf);
-
-      sendto_match_servs(chptr, cptr, ":%s MODE %s %s %s",
-                         sptr->name, chptr->chname,
-                         modebuf, parabuf);
-    }
-
-  if(*modebuf_new)
-    {
-      sendto_channel_butserv(chptr, sptr, ":%s MODE %s %s %s", 
-                             sptr->name, chptr->chname,
-                             modebuf_new, parabuf_new);
-
-      sendto_match_cap_servs(chptr, cptr, CAP_EX, ":%s MODE %s %s %s",
-                             sptr->name, chptr->chname,
-                             modebuf_new, parabuf_new);
-    }
-  if(*modebuf_newer)
-    {
-      sendto_channel_butserv(chptr, sptr, ":%s MODE %s %s %s",
-                             sptr->name, chptr->chname,
-                             modebuf_newer, parabuf_newer);
-      sendto_match_cap_servs(chptr, cptr, CAP_DE, ":%s MODE %s %s %s",
-                             sptr->name, chptr->chname,
-                             modebuf_newer, parabuf_newer);
-    }
-
-                     
-  return;
-}
-
-static  int     can_join(struct Client *sptr, struct Channel *chptr, char *key, int *flags)
-{
-  Link  *lp;
-  int ban_or_exception;
-
-#ifdef JUPE_CHANNEL
-  if( chptr->mode.mode & MODE_JUPED )
-    {
-      sendto_ops_flags(FLAGS_SPY,
-             "User %s (%s@%s) is attemping to join locally juped channel %s",
-                     sptr->name,
-                     sptr->username, sptr->host,chptr->chname);
-      return (ERR_BADCHANNAME);
-    }
-#endif
-
-  if ( (ban_or_exception = is_banned(sptr, chptr)) == CHFL_BAN)
-    return (ERR_BANNEDFROMCHAN);
-  else
-    *flags |= ban_or_exception; /* Mark this client as "charmed" */
-
-  if (chptr->mode.mode & MODE_INVITEONLY)
-    {
-      for (lp = sptr->user->invited; lp; lp = lp->next)
-        if (lp->value.chptr == chptr)
-          break;
-      if (!lp)
-        return (ERR_INVITEONLYCHAN);
-    }
-
-  if (*chptr->mode.key && (BadPtr(key) || irccmp(chptr->mode.key, key)))
-    return (ERR_BADCHANNELKEY);
-
-  if (chptr->mode.limit && chptr->users >= chptr->mode.limit)
-    return (ERR_CHANNELISFULL);
-
-  return 0;
-}
-
-/*
- * check_channel_name - check channel name for invalid characters
- * return true (1) if name ok, false (0) otherwise
- */
-int check_channel_name(const char* name)
-{
-  assert(0 != name);
-  
-  for ( ; *name; ++name) {
-    if (!IsChanChar(*name))
-      return 0;
-  }
-  return 1;
-}
-
-/*
-**  Get Channel block for chname (and allocate a new channel
-**  block, if it didn't exist before).
-*/
-static struct Channel* get_channel(struct Client *cptr, char *chname, int flag)
-{
-  struct Channel *chptr;
-  int   len;
-
-  if (BadPtr(chname))
-    return NULL;
-
-  len = strlen(chname);
-  if (MyClient(cptr) && len > CHANNELLEN)
-    {
-      len = CHANNELLEN;
-      *(chname + CHANNELLEN) = '\0';
-    }
-  if ((chptr = hash_find_channel(chname, NULL)))
-    return (chptr);
-
-  /*
-   * If a channel is created during a split make sure its marked
-   * as created locally 
-   * Also make sure a created channel has =some= timestamp
-   * even if it get over-ruled later on. Lets quash the possibility
-   * an ircd coder accidentally blasting TS on channels. (grrrrr -db)
-   *
-   * Actually, it might be fun to make the TS some impossibly huge value (-db)
-   */
-
-  if (flag == CREATE)
-    {
-      chptr = (struct Channel*) MyMalloc(sizeof(struct Channel) + len + 1);
-      memset(chptr, 0, sizeof(struct Channel));
-      /*
-       * NOTE: strcpy ok here, we have allocated strlen + 1
-       */
-      strcpy(chptr->chname, chname);
-      if (channel)
-        channel->prevch = chptr;
-      chptr->prevch = NULL;
-      chptr->nextch = channel;
-      channel = chptr;
-      if (Count.myserver == 0)
-        chptr->locally_created = YES;
-      chptr->keep_their_modes = YES;
-      chptr->channelts = CurrentTime;     /* doesn't hurt to set it here */
-      add_to_channel_hash_table(chname, chptr);
-      Count.chan++;
-    }
-  return chptr;
-}
-
-static  void    add_invite(struct Client *cptr,struct Channel *chptr)
-{
-  Link  *inv, **tmp;
-
-  del_invite(cptr, chptr);
-  /*
-   * delete last link in chain if the list is max length
-   */
-  if (list_length(cptr->user->invited) >= MAXCHANNELSPERUSER)
-    {
-      /*                This forgets the channel side of invitation     -Vesa
-                        inv = cptr->user->invited;
-                        cptr->user->invited = inv->next;
-                        free_link(inv);
-*/
-      del_invite(cptr, cptr->user->invited->value.chptr);
- 
-    }
-  /*
-   * add client to channel invite list
-   */
-  inv = make_link();
-  inv->value.cptr = cptr;
-  inv->next = chptr->invites;
-  chptr->invites = inv;
-  /*
-   * add channel to the end of the client invite list
-   */
-  for (tmp = &(cptr->user->invited); *tmp; tmp = &((*tmp)->next))
-    ;
-  inv = make_link();
-  inv->value.chptr = chptr;
-  inv->next = NULL;
-  (*tmp) = inv;
-}
-
-/*
- * Delete Invite block from channel invite list and client invite list
- */
-void    del_invite(struct Client *cptr,struct Channel *chptr)
-{
-  Link  **inv, *tmp;
-
-  for (inv = &(chptr->invites); (tmp = *inv); inv = &tmp->next)
-    if (tmp->value.cptr == cptr)
-      {
-        *inv = tmp->next;
-        free_link(tmp);
-        break;
-      }
-
-  for (inv = &(cptr->user->invited); (tmp = *inv); inv = &tmp->next)
-    if (tmp->value.chptr == chptr)
-      {
-        *inv = tmp->next;
-        free_link(tmp);
-        break;
-      }
-}
-
-/*
-**  Subtract one user from channel (and free channel
-**  block, if channel became empty).
-*/
-static  void    sub1_from_channel(struct Channel *chptr)
-{
-  Link *tmp;
-
-  if (--chptr->users <= 0)
-    {
-      chptr->users = 0; /* if chptr->users < 0, make sure it sticks at 0
-                         * It should never happen but...
-                         */
-
-#if defined(PRESERVE_CHANNEL_ON_SPLIT) || defined(NO_JOIN_ON_SPLIT)
-      if(server_was_split && (chptr->chname[0] != '&'))
-        {
-          /*
-           * Now, find all invite links from channel structure
-           */
-          /* The idea here is, not to "forget" the channel mode
-           * the ban list, exception lists, and not to release
-           * the channel at this time.
-           * The invite list should be forgotten now, as well
-           * as the flooder lists
-           * -db
-           */
-
-          while ((tmp = chptr->invites))
-            del_invite(tmp->value.cptr, chptr);
-
-#ifdef FLUD
-          free_fluders(NULL, chptr);
-#endif
-          /* flag the channel as split */
-          chptr->mode.mode |= MODE_SPLIT;
-
-          /* Add to double linked empty channel list */
-          if(empty_channel_list)
-            empty_channel_list->last_empty_channel = chptr;
-          chptr->last_empty_channel = (struct Channel *)NULL;
-          chptr->next_empty_channel = empty_channel_list;
-          empty_channel_list = chptr;
-        }
-      else
-#endif
-#ifdef JUPE_CHANNEL
-        if( chptr->mode.mode & MODE_JUPED )
-          {
-            while ((tmp = chptr->invites))
-              del_invite(tmp->value.cptr, chptr);
-            
-#ifdef FLUD
-            free_fluders(NULL, chptr);
-#endif
-          }
-        else
-#endif
-       {
-          /*
-           * Now, find all invite links from channel structure
-           */
-          while ((tmp = chptr->invites))
-            del_invite(tmp->value.cptr, chptr);
-
-	  /* free all bans/exceptions/denies */
-	  free_bans_exceptions_denies( chptr );
-
-          if (chptr->prevch)
-            chptr->prevch->nextch = chptr->nextch;
-          else
-            channel = chptr->nextch;
-          if (chptr->nextch)
-            chptr->nextch->prevch = chptr->prevch;
-
-#ifdef FLUD
-          free_fluders(NULL, chptr);
-#endif
-          del_from_channel_hash_table(chptr->chname, chptr);
-          MyFree((char*) chptr);
-          Count.chan--;
-        }
-    }
-}
-
-/*
- * clear_bans_exceptions_denies
- *
- * I could have re-written del_banid/del_exceptid to do this
- *
- * still need a bit of cleanup on the MODE -b stuff...
- * -Dianora
- */
-
-static void clear_bans_exceptions_denies(struct Client *sptr, struct Channel *chptr)
-{
-  static char modebuf[MODEBUFLEN];
-  register Link *ban;
-  char *b1,*b2,*b3,*b4;
-  char *mp;
-
-  b1="";
-  b2="";
-  b3="";
-  b4="";
-
-  mp= modebuf;
-  *mp = '\0';
-
-  for(ban = chptr->banlist; ban; ban = ban->next)
-    {
-      if(!*b1)
-        {
-          b1 = BANSTR(ban);
-          *mp++ = '-';
-          *mp++ = 'b';
-          *mp = '\0';
-        }
-      else if(!*b2)
-        {
-          b2 = BANSTR(ban);
-          *mp++ = 'b';
-          *mp = '\0';
-        }
-      else if(!*b3)
-        {
-          b3 = BANSTR(ban);
-          *mp++ = 'b';
-          *mp = '\0';
-        }
-      else if(!*b4)
-        {
-          b4 = BANSTR(ban);
-          *mp++ = 'b';
-          *mp = '\0';
-
-          sendto_channel_butserv(chptr, &me,
-                                 ":%s MODE %s %s %s %s %s %s",
-                                 sptr->name,chptr->chname,modebuf,b1,b2,b3,b4);
-          b1="";
-          b2="";
-          b3="";
-          b4="";
-
-          mp = modebuf;
-          *mp = '\0';
-        }
-    }
-
-  if(*modebuf)
-    sendto_channel_butserv(chptr, &me,
-                           ":%s MODE %s %s %s %s %s %s",
-                           sptr->name,chptr->chname,modebuf,b1,b2,b3,b4);
-  b1="";
-  b2="";
-  b3="";
-  b4="";
-
-  mp= modebuf;
-  *mp = '\0';
-
-  for(ban = chptr->exceptlist; ban; ban = ban->next)
-    {
-      if(!*b1)
-        {
-          b1 = BANSTR(ban);
-          *mp++ = '-';
-          *mp++ = 'e';
-          *mp = '\0';
-        }
-      else if(!*b2)
-        {
-          b2 = BANSTR(ban);
-          *mp++ = 'e';
-          *mp = '\0';
-        }
-      else if(!*b3)
-        {
-          b3 = BANSTR(ban);
-          *mp++ = 'e';
-          *mp = '\0';
-        }
-      else if(!*b4)
-        {
-          b4 = BANSTR(ban);
-          *mp++ = 'e';
-          *mp = '\0';
-
-          sendto_channel_butserv(chptr, &me,
-                                 ":%s MODE %s %s %s %s %s %s",
-                                 sptr->name,chptr->chname,modebuf,b1,b2,b3,b4);
-          b1="";
-          b2="";
-          b3="";
-          b4="";
-          mp = modebuf;
-          *mp = '\0';
-        }
-    }
-
-  if(*modebuf)
-    sendto_channel_butserv(chptr, &me,
-                           ":%s MODE %s %s %s %s %s %s",
-                           sptr->name,chptr->chname,modebuf,b1,b2,b3,b4);
-
-  b1="";
-  b2="";
-  b3="";
-  b4="";
-
-  mp= modebuf;
-  *mp = '\0';
-
-  for(ban = chptr->denylist; ban; ban = ban->next)
-    {
-      if(!*b1)
-        {
-          b1 = BANSTR(ban);
-          *mp++ = '-';
-          *mp++ = 'd';
-          *mp = '\0';
-        }
-      else if(!*b2)
-        {
-          b2 = BANSTR(ban);
-          *mp++ = 'd';
-          *mp = '\0';
-        }
-      else if(!*b3)
-        {
-          b3 = BANSTR(ban);
-          *mp++ = 'd';
-          *mp = '\0';
-        }
-      else if(!*b4)
-        {
-          b4 = BANSTR(ban);
-          *mp++ = 'd';
-          *mp = '\0';
-
-          sendto_channel_butserv(chptr, &me,
-                                 ":%s MODE %s %s %s %s %s %s",
-                                 sptr->name,chptr->chname,modebuf,b1,b2,b3,b4);
-          b1="";
-          b2="";
-          b3="";
-          b4="";
-          mp = modebuf;
-          *mp = '\0';
-        }
-    }
-
-  if(*modebuf)
-    sendto_channel_butserv(chptr, &me,
-                           ":%s MODE %s %s %s %s %s %s",
-                           sptr->name,chptr->chname,modebuf,b1,b2,b3,b4);
-
-
-  /* free all bans/exceptions/denies */
-  free_bans_exceptions_denies(chptr);
-}
-
-/*
- * free_bans_exceptions_denies
- *
- * inputs	- pointer to channel structure
- * output	- none
- * side effects	- all bans/exceptions denies are freed for channel
- */
-
-static void free_bans_exceptions_denies(struct Channel *chptr)
-{
-  Link *ban;
-  Link *next_ban;
-
-  for(ban = chptr->banlist; ban; ban = next_ban)
-    {
-      next_ban = ban->next;
-#ifdef BAN_INFO
-      MyFree(ban->value.banptr->banstr);
-      MyFree(ban->value.banptr->who);
-      MyFree(ban->value.banptr);
-#else
-      MyFree(ban->value.cp);
-#endif
-      free_link(ban);
-    }
-
-  for(ban = chptr->exceptlist; ban; ban = next_ban)
-    {
-      next_ban = ban->next;
-#ifdef BAN_INFO
-      MyFree(ban->value.banptr->banstr);
-      MyFree(ban->value.banptr->who);
-      MyFree(ban->value.banptr);
-#else
-      MyFree(ban->value.cp);
-#endif
-      free_link(ban);
-    }
-
-  for(ban = chptr->denylist; ban; ban = next_ban)
-    {
-      next_ban = ban->next;
-#ifdef BAN_INFO
-      MyFree(ban->value.banptr->banstr);
-      MyFree(ban->value.banptr->who);
-      MyFree(ban->value.banptr);
-#else
-      MyFree(ban->value.cp);
-#endif
-      free_link(ban);
-    }
-
-  chptr->banlist = chptr->exceptlist = chptr->denylist = NULL;
-  chptr->num_bed = 0;
-}
-
-
-#ifdef NEED_SPLITCODE
-
-/*
- * check_still_split()
- *
- * inputs       -NONE
- * output       -NONE
- * side effects -
- * Check to see if the server split timer has expired, if so
- * check to see if there are now a decent number of servers connected
- * and users present, so I can consider this split over.
- *
- * -Dianora
- */
-
-static void check_still_split()
-{
-  if((server_split_time + SPLITDELAY) < CurrentTime)
-    {
-      if((Count.server >= SPLITNUM) &&
-#ifdef SPLIT_PONG
-         (got_server_pong == YES) &&
-#endif
-         (Count.total >= SPLITUSERS))
-        {
-          /* server hasn't been split for a while.
-           * -Dianora
-           */
-          server_was_split = NO;
-          sendto_ops("Net Rejoined, split-mode deactivated");
-          cold_start = NO;
-#if defined(PRESERVE_CHANNEL_ON_SPLIT) || defined(NO_JOIN_ON_SPLIT)
-          remove_empty_channels();
-#endif
-        }
-      else
-        {
-          server_split_time = CurrentTime; /* still split */
-          server_was_split = YES;
-        }
-    }
-}
-#endif
-
-#if defined(PRESERVE_CHANNEL_ON_SPLIT) || defined(NO_JOIN_ON_SPLIT)
-/*
- * remove_empty_channels
- *
- * inputs       - none
- * output       - none
- * side effects - remove all channels on empty_channel_list that have
- * 
- * Any channel struct on this link list, is here because it had
- * no members, hence normally would not exist through a split.
- * If after the split is over, there are any channels left in this
- * list, they must be removed. Whenever a channel gains a member
- * whether locally or from a remote SJOIN it is removed from this list.
- */
-
-void remove_empty_channels()
-{
-  Link *tmp;
-  Link  *obtmp;
-  struct Channel *next_empty_channel;
-
-  for(;empty_channel_list;
-      empty_channel_list = next_empty_channel )
-    {
-      next_empty_channel = empty_channel_list->next_empty_channel;
-
-      if(empty_channel_list->users)             /* sanity test */
-        {
-	  /* This is an oddity, rather than an out and out error
-	   * if this happens, a client managed to join the channel
-	   * making it non zero users, and I didn't notice. That means
-	   * strictly speaking its an error. However, if this entry is
-	   * ignored, its a non fatal one.
-	   */
-#if 0
-          sendto_ops("non zero user count in remove_empty_channels");
-          sendto_ops("Please report to the hybrid team! ircd-hybrid@the-project.org");
-#endif
-          empty_channel_list->next_empty_channel = (struct Channel *)NULL;
-          empty_channel_list->last_empty_channel = (struct Channel *)NULL;
-          continue;
-        }
-
-      /*
-       * Now, find all invite links from channel structure
-       */
-      while ((tmp = empty_channel_list->invites))
-        del_invite(tmp->value.cptr, empty_channel_list);
-      
-      tmp = empty_channel_list->banlist;
-      while (tmp)
-        {
-          obtmp = tmp;
-          tmp = tmp->next;
-#ifdef BAN_INFO
-          MyFree(obtmp->value.banptr->banstr);
-          MyFree(obtmp->value.banptr->who);
-          MyFree(obtmp->value.banptr);
-#else
-          MyFree(obtmp->value.cp);
-#endif
-          free_link(obtmp);
-        }
-
-      tmp = empty_channel_list->exceptlist;
-      while (tmp)
-        {
-          obtmp = tmp;
-          tmp = tmp->next;
-#ifdef BAN_INFO
-          MyFree(obtmp->value.banptr->banstr);
-          MyFree(obtmp->value.banptr->who);
-          MyFree(obtmp->value.banptr);
-#else
-          MyFree(obtmp->value.cp);
-#endif
-          free_link(obtmp);
-        }
-      empty_channel_list->banlist = empty_channel_list->exceptlist = NULL;
-      
-      if (empty_channel_list->prevch)
-        empty_channel_list->prevch->nextch = empty_channel_list->nextch;
-      else
-        channel = empty_channel_list->nextch;
-      if (empty_channel_list->nextch)
-        empty_channel_list->nextch->prevch = empty_channel_list->prevch;
-      
-#ifdef FLUD
-      free_fluders(NULL, empty_channel_list);
-#endif
-      del_from_channel_hash_table(empty_channel_list->chname, 
-                                        empty_channel_list);
-      MyFree((char*) empty_channel_list);
-      Count.chan--;
-    }
-}
-#endif
-
-/*
-** m_join
-**      parv[0] = sender prefix
-**      parv[1] = channel
-**      parv[2] = channel password (key)
-*/
-int     m_join(struct Client *cptr,
-               struct Client *sptr,
-               int parc,
-               char *parv[])
-{
-  static char   jbuf[BUFSIZE];
-  Link  *lp;
-  struct Channel *chptr = NULL;
-  char  *name, *key = NULL;
-  int   i, flags = 0;
-#ifdef NO_CHANOPS_WHEN_SPLIT
-  int   allow_op=YES;
-#endif
-  char  *p = NULL, *p2 = NULL;
-#ifdef ANTI_SPAMBOT
-  int   successful_join_count = 0; /* Number of channels successfully joined */
-#endif
-  
-  if (!(sptr->user))
-    {
-      /* something is *fucked* - bail */
-      return 0;
-    }
-
-  if (parc < 2 || *parv[1] == '\0')
-    {
-      sendto_one(sptr, form_str(ERR_NEEDMOREPARAMS),
-                 me.name, parv[0], "JOIN");
-      return 0;
-    }
-
-
-#ifdef NEED_SPLITCODE
-
-  /* Check to see if the timer has timed out, and if so, see if
-   * there are a decent number of servers now connected 
-   * to consider any possible split over.
-   * -Dianora
-   */
-
-  if (server_was_split)
-    check_still_split();
-
-#endif
-
-  *jbuf = '\0';
-  /*
-  ** Rebuild list of channels joined to be the actual result of the
-  ** JOIN.  Note that "JOIN 0" is the destructive problem.
-  */
-  for (i = 0, name = strtoken(&p, parv[1], ","); name;
-       name = strtoken(&p, (char *)NULL, ","))
-    {
-      if (!check_channel_name(name))
-        {
-          sendto_one(sptr, form_str(ERR_BADCHANNAME),
-                       me.name, parv[0], (unsigned char*) name);
-          continue;
-        }
-      if (*name == '&' && !MyConnect(sptr))
-        continue;
-      if (*name == '0' && !atoi(name))
-        *jbuf = '\0';
-      else if (!IsChannelName(name))
-        {
-          if (MyClient(sptr))
-            sendto_one(sptr, form_str(ERR_NOSUCHCHANNEL),
-                       me.name, parv[0], name);
-          continue;
-        }
-
-
-#ifdef NO_JOIN_ON_SPLIT_SIMPLE
-      if (server_was_split && MyClient(sptr) && (*name != '&') &&
-          !IsAnOper(sptr))
-        {
-              sendto_one(sptr, form_str(ERR_UNAVAILRESOURCE),
-                         me.name, parv[0], name);
-              continue;
-        }
-#endif /* NO_JOIN_ON_SPLIT_SIMPLE */
-
-#if defined(PRESERVE_CHANNEL_ON_SPLIT) || defined(NO_JOIN_ON_SPLIT)
-      /* If from a cold start, there were never any channels
-       * joined, hence all of them must be considered off limits
-       * until this server joins the network
-       *
-       * cold_start is set to NO if SPLITDELAY is set to 0 in m_set()
-       */
-
-      if(cold_start && MyClient(sptr) && (*name != '&') &&
-         !IsAnOper(sptr))
-        {
-              sendto_one(sptr, form_str(ERR_UNAVAILRESOURCE),
-                         me.name, parv[0], name);
-              continue;
-        }
-#endif
-      if (*jbuf)
-        (void)strcat(jbuf, ",");
-      (void)strncat(jbuf, name, sizeof(jbuf) - i - 1);
-      i += strlen(name)+1;
-    }
-  /*    (void)strcpy(parv[1], jbuf);*/
-
-  p = NULL;
-  if (parv[2])
-    key = strtoken(&p2, parv[2], ",");
-  parv[2] = NULL;       /* for m_names call later, parv[parc] must == NULL */
-  for (name = strtoken(&p, jbuf, ","); name;
-       key = (key) ? strtoken(&p2, NULL, ",") : NULL,
-         name = strtoken(&p, NULL, ","))
-    {
-      /*
-      ** JOIN 0 sends out a part for all channels a user
-      ** has joined.
-      */
-      if (*name == '0' && !atoi(name))
-        {
-          if (sptr->user->channel == NULL)
-            continue;
-          while ((lp = sptr->user->channel))
-            {
-              chptr = lp->value.chptr;
-              sendto_channel_butserv(chptr, sptr, PartFmt,
-                                     parv[0], chptr->chname);
-              remove_user_from_channel(sptr, chptr, 0);
-            }
-
-#ifdef ANTI_SPAMBOT       /* Dianora */
-
-          if( MyConnect(sptr) && !IsAnOper(sptr) )
-            {
-              if(SPAMNUM && (sptr->join_leave_count >= SPAMNUM))
-                {
-                  sendto_ops_flags(FLAGS_BOTS,
-                                     "User %s (%s@%s) is a possible spambot",
-                                     sptr->name,
-                                     sptr->username, sptr->host);
-                  sptr->oper_warn_count_down = OPER_SPAM_COUNTDOWN;
-                }
-              else
-                {
-                  int t_delta;
-
-                  if( (t_delta = (CurrentTime - sptr->last_leave_time)) >
-                      JOIN_LEAVE_COUNT_EXPIRE_TIME)
-                    {
-                      int decrement_count;
-                      decrement_count = (t_delta/JOIN_LEAVE_COUNT_EXPIRE_TIME);
-
-                      if(decrement_count > sptr->join_leave_count)
-                        sptr->join_leave_count = 0;
-                      else
-                        sptr->join_leave_count -= decrement_count;
-                    }
-                  else
-                    {
-                      if((CurrentTime - (sptr->last_join_time)) < SPAMTIME)
-                        {
-                          /* oh, its a possible spambot */
-                          sptr->join_leave_count++;
-                        }
-                    }
-                  sptr->last_leave_time = CurrentTime;
-                }
-            }
-#endif
-          sendto_match_servs(NULL, cptr, ":%s JOIN 0", parv[0]);
-          continue;
-        }
-      
-      if (MyConnect(sptr))
-        {
-          /*
-          ** local client is first to enter previously nonexistent
-          ** channel so make them (rightfully) the Channel
-          ** Operator.
-          */
-           /*     flags = (ChannelExists(name)) ? 0 : CHFL_CHANOP; */
-
-          /* To save a redundant hash table lookup later on */
-           
-           if((chptr = hash_find_channel(name, NullChn)))
-             flags = 0;
-           else
-             flags = CHFL_CHANOP;
-
-           /* if its not a local channel, or isn't an oper
-            * and server has been split
-            */
-
-#ifdef NO_CHANOPS_WHEN_SPLIT
-          if((*name != '&') && !IsAnOper(sptr) && server_was_split)
-            {
-              allow_op = NO;
-
-              if(!IsRestricted(sptr) && (flags & CHFL_CHANOP))
-                sendto_one(sptr,":%s NOTICE %s :*** Notice -- Due to a network split, you can not obtain channel operator status in a new channel at this time.",
-                       me.name,
-                       sptr->name);
-            }
-#endif
-
-          if ((sptr->user->joined >= MAXCHANNELSPERUSER) &&
-             (!IsAnOper(sptr) || (sptr->user->joined >= MAXCHANNELSPERUSER*3)))
-            {
-              sendto_one(sptr, form_str(ERR_TOOMANYCHANNELS),
-                         me.name, parv[0], name);
-#ifdef ANTI_SPAMBOT
-              if(successful_join_count)
-                sptr->last_join_time = CurrentTime;
-#endif
-              return 0;
-            }
-
-
-#ifdef ANTI_SPAMBOT       /* Dianora */
-          if(flags == 0)        /* if channel doesn't exist, don't penalize */
-            successful_join_count++;
-          if( SPAMNUM && (sptr->join_leave_count >= SPAMNUM))
-            { 
-              /* Its already known as a possible spambot */
- 
-              if(sptr->oper_warn_count_down > 0)  /* my general paranoia */
-                sptr->oper_warn_count_down--;
-              else
-                sptr->oper_warn_count_down = 0;
- 
-              if(sptr->oper_warn_count_down == 0)
-                {
-                  sendto_ops_flags(FLAGS_BOTS,
-                    "User %s (%s@%s) trying to join %s is a possible spambot",
-                             sptr->name,
-                             sptr->username,
-                             sptr->host,
-                             name);     
-                  sptr->oper_warn_count_down = OPER_SPAM_COUNTDOWN;
-                }
-#ifndef ANTI_SPAMBOT_WARN_ONLY
-              return 0; /* Don't actually JOIN anything, but don't let
-                           spambot know that */
-#endif
-            }
-#endif
-        }
-      else
-        {
-          /*
-          ** complain for remote JOINs to existing channels
-          ** (they should be SJOINs) -orabidoo
-          */
-          if (!ChannelExists(name))
-            ts_warn("User on %s remotely JOINing new channel", 
-                    sptr->user->server);
-        }
-
-#if defined(NO_JOIN_ON_SPLIT)
-      if(server_was_split && (*name != '&'))
-        {
-          if( chptr )   /* The channel existed, so I can't join it */
-            {
-              if (IsMember(sptr, chptr)) /* already a member, ignore this */
-                continue;
-
-              /* allow local joins to this channel */
-              if( (chptr->users == 0) && !IsAnOper(sptr) )
-                {
-                  sendto_one(sptr, form_str(ERR_UNAVAILRESOURCE),
-                             me.name, parv[0], name);
-                  continue;
-                }
-            }
-          else
-            chptr = get_channel(sptr, name, CREATE);        
-        }
-      else
-#endif
-      if(!chptr)        /* If I already have a chptr, no point doing this */
-        chptr = get_channel(sptr, name, CREATE);
-
-      if(chptr)
-        {
-          if (IsMember(sptr, chptr))    /* already a member, ignore this */
-            continue;
-        }
-      else
-        {
-           sendto_one(sptr, form_str(ERR_UNAVAILRESOURCE),
-                      me.name, parv[0], name);
-#ifdef ANTI_SPAMBOT
-          if(successful_join_count > 0)
-            successful_join_count--;
-#endif
-          continue;
-        }
-
-      /*
-       * can_join checks for +i key, bans.
-       * If a ban is found but an exception to the ban was found
-       * flags will have CHFL_EXCEPTION set
-       */
-
-      if (MyConnect(sptr) && (i = can_join(sptr, chptr, key, &flags)))
-        {
-          sendto_one(sptr,
-                    form_str(i), me.name, parv[0], name);
-#ifdef ANTI_SPAMBOT
-          if(successful_join_count > 0)
-            successful_join_count--;
-#endif
-          continue;
-        }
-
-      /*
-      **  Complete user entry to the new channel (if any)
-      */
-
-#ifdef NO_CHANOPS_WHEN_SPLIT
-      if(allow_op)
-        {
-          add_user_to_channel(chptr, sptr, flags);
-        }
-      else
-        {
-          add_user_to_channel(chptr, sptr, flags & CHFL_EXCEPTION);
-        }
-#else
-      add_user_to_channel(chptr, sptr, flags);
-#endif
-      /*
-      **  Set timestamp if appropriate, and propagate
-      */
-      if (MyClient(sptr) && (flags & CHFL_CHANOP) )
-        {
-          chptr->channelts = CurrentTime;
-#ifdef NO_CHANOPS_WHEN_SPLIT
-          if(allow_op)
-            {
-              sendto_match_servs(chptr, cptr,
-                                 ":%s SJOIN %lu %s + :@%s", me.name,
-                                 chptr->channelts, name, parv[0]);
-
-            }                                
-          else
-            sendto_match_servs(chptr, cptr,
-                               ":%s SJOIN %lu %s + :%s", me.name,
-                               chptr->channelts, name, parv[0]);
-#else
-          sendto_match_servs(chptr, cptr,
-                             ":%s SJOIN %lu %s + :@%s", me.name,
-                             chptr->channelts, name, parv[0]);
-#endif
-        }
-      else if (MyClient(sptr))
-        {
-          sendto_match_servs(chptr, cptr,
-                             ":%s SJOIN %lu %s + :%s", me.name,
-                             chptr->channelts, name, parv[0]);
-        }
-      else
-        sendto_match_servs(chptr, cptr, ":%s JOIN :%s", parv[0],
-                           name);
-
-      /*
-      ** notify all other users on the new channel
-      */
-      sendto_channel_butserv(chptr, sptr, ":%s JOIN :%s",
-                             parv[0], name);
-
-      if (MyClient(sptr))
-        {
-          if( flags & CHFL_CHANOP )
-            {
-              chptr->mode.mode |= MODE_TOPICLIMIT;
-              chptr->mode.mode |= MODE_NOPRIVMSGS;
-
-              sendto_channel_butserv(chptr, sptr,
-                                 ":%s MODE %s +nt",
-                                 me.name, chptr->chname);
-
-              sendto_match_servs(chptr, sptr, 
-                                 ":%s MODE %s +nt",
-                                 me.name, chptr->chname);
-            }
-
-          del_invite(sptr, chptr);
-
-          if (chptr->topic[0] != '\0')
-            {
-              sendto_one(sptr, form_str(RPL_TOPIC), me.name,
-                         parv[0], name, chptr->topic);
-#ifdef TOPIC_INFO
-              sendto_one(sptr, form_str(RPL_TOPICWHOTIME),
-                         me.name, parv[0], name,
-                         chptr->topic_nick,
-                         chptr->topic_time);
-#endif
-            }
-          parv[1] = name;
-          (void)m_names(cptr, sptr, 2, parv);
-        }
-    }
-
-#ifdef ANTI_SPAMBOT
-  if(MyConnect(sptr) && successful_join_count)
-    sptr->last_join_time = CurrentTime;
-#endif
-  return 0;
-}
-
-/*
-** m_part
-**      parv[0] = sender prefix
-**      parv[1] = channel
-*/
-int     m_part(struct Client *cptr,
-               struct Client *sptr,
-               int parc,
-               char *parv[])
-{
-  struct Channel *chptr;
-  char  *p, *name;
-
-  if (parc < 2 || parv[1][0] == '\0')
-    {
-      sendto_one(sptr, form_str(ERR_NEEDMOREPARAMS),
-                 me.name, parv[0], "PART");
-      return 0;
-    }
-
-  name = strtoken( &p, parv[1], ",");
-
-#ifdef ANTI_SPAMBOT     /* Dianora */
-      /* if its my client, and isn't an oper */
-
-      if (name && MyConnect(sptr) && !IsAnOper(sptr))
-        {
-          if(SPAMNUM && (sptr->join_leave_count >= SPAMNUM))
-            {
-              sendto_ops_flags(FLAGS_BOTS,
-                               "User %s (%s@%s) is a possible spambot",
-                               sptr->name,
-                               sptr->username, sptr->host);
-              sptr->oper_warn_count_down = OPER_SPAM_COUNTDOWN;
-            }
-          else
-            {
-              int t_delta;
-
-              if( (t_delta = (CurrentTime - sptr->last_leave_time)) >
-                  JOIN_LEAVE_COUNT_EXPIRE_TIME)
-                {
-                  int decrement_count;
-                  decrement_count = (t_delta/JOIN_LEAVE_COUNT_EXPIRE_TIME);
-
-                  if(decrement_count > sptr->join_leave_count)
-                    sptr->join_leave_count = 0;
-                  else
-                    sptr->join_leave_count -= decrement_count;
-                }
-              else
-                {
-                  if( (CurrentTime - (sptr->last_join_time)) < SPAMTIME)
-                    {
-                      /* oh, its a possible spambot */
-                      sptr->join_leave_count++;
-                    }
-                }
-              sptr->last_leave_time = CurrentTime;
-            }
-        }
-#endif
-
-  while ( name )
-    {
-      chptr = get_channel(sptr, name, 0);
-      if (!chptr)
-        {
-          sendto_one(sptr, form_str(ERR_NOSUCHCHANNEL),
-                     me.name, parv[0], name);
-          name = strtoken(&p, (char *)NULL, ",");
-          continue;
-        }
-
-      if (!IsMember(sptr, chptr))
-        {
-          sendto_one(sptr, form_str(ERR_NOTONCHANNEL),
-                     me.name, parv[0], name);
-          name = strtoken(&p, (char *)NULL, ",");
-          continue;
-        }
-      /*
-      **  Remove user from the old channel (if any)
-      */
-            
-      sendto_match_servs(chptr, cptr, PartFmt, parv[0], name);
-            
-      sendto_channel_butserv(chptr, sptr, PartFmt, parv[0], name);
-      remove_user_from_channel(sptr, chptr, 0);
-      name = strtoken(&p, (char *)NULL, ",");
-    }
-  return 0;
-}
-
-/*
-** m_kick
-**      parv[0] = sender prefix
-**      parv[1] = channel
-**      parv[2] = client to kick
-**      parv[3] = kick comment
-*/
-/*
- * I've removed the multi channel kick, and the multi user kick
- * though, there are still remnants left ie..
- * "name = strtoken(&p, parv[1], ",");" in a normal kick
- * it will just be "KICK #channel nick"
- * A strchr() is going to be faster than a strtoken(), so rewritten
- * to use a strchr()
- *
- * It appears the original code was supposed to support 
- * "kick #channel1,#channel2 nick1,nick2,nick3." For example, look at
- * the original code for m_topic(), where 
- * "topic #channel1,#channel2,#channel3... topic" was supported.
- *
- * -Dianora
- */
-int     m_kick(struct Client *cptr,
-               struct Client *sptr,
-               int parc,
-               char *parv[])
-{
-  struct Client *who;
-  struct Channel *chptr;
-  int   chasing = 0;
-  char  *comment;
-  char  *name;
-  char  *p = (char *)NULL;
-  char  *user;
-
-  if (parc < 3 || *parv[1] == '\0')
-    {
-      sendto_one(sptr, form_str(ERR_NEEDMOREPARAMS),
-                 me.name, parv[0], "KICK");
-      return 0;
-    }
-  if (IsServer(sptr))
-    sendto_ops("KICK from %s for %s %s",
-               parv[0], parv[1], parv[2]);
-  comment = (BadPtr(parv[3])) ? parv[0] : parv[3];
-  if (strlen(comment) > (size_t) TOPICLEN)
-    comment[TOPICLEN] = '\0';
-
-  *buf = '\0';
-  p = strchr(parv[1],',');
-  if(p)
-    *p = '\0';
-  name = parv[1]; /* strtoken(&p, parv[1], ","); */
-
-  chptr = get_channel(sptr, name, !CREATE);
-  if (!chptr)
-    {
-      sendto_one(sptr, form_str(ERR_NOSUCHCHANNEL),
-                 me.name, parv[0], name);
-      return(0);
-    }
-
-  /* You either have chan op privs, or you don't -Dianora */
-  /* orabidoo and I discussed this one for a while...
-   * I hope he approves of this code, (he did) users can get quite confused...
-   *    -Dianora
-   */
-
-  if (!IsServer(sptr) && !is_chan_op(sptr, chptr) ) 
-    { 
-      /* was a user, not a server, and user isn't seen as a chanop here */
-      
-      if(MyConnect(sptr))
-        {
-          /* user on _my_ server, with no chanops.. so go away */
-          
-          sendto_one(sptr, form_str(ERR_CHANOPRIVSNEEDED),
-                     me.name, parv[0], chptr->chname);
-          return(0);
-        }
-
-      if(chptr->channelts == 0)
-        {
-          /* If its a TS 0 channel, do it the old way */
-          
-          sendto_one(sptr, form_str(ERR_CHANOPRIVSNEEDED),
-                     me.name, parv[0], chptr->chname);
-          return(0);
-        }
-
-      /* Its a user doing a kick, but is not showing as chanop locally
-       * its also not a user ON -my- server, and the channel has a TS.
-       * There are two cases we can get to this point then...
-       *
-       *     1) connect burst is happening, and for some reason a legit
-       *        op has sent a KICK, but the SJOIN hasn't happened yet or 
-       *        been seen. (who knows.. due to lag...)
-       *
-       *     2) The channel is desynced. That can STILL happen with TS
-       *        
-       *     Now, the old code roger wrote, would allow the KICK to 
-       *     go through. Thats quite legit, but lets weird things like
-       *     KICKS by users who appear not to be chanopped happen,
-       *     or even neater, they appear not to be on the channel.
-       *     This fits every definition of a desync, doesn't it? ;-)
-       *     So I will allow the KICK, otherwise, things are MUCH worse.
-       *     But I will warn it as a possible desync.
-       *
-       *     -Dianora
-       */
-
-      /*          sendto_one(sptr, form_str(ERR_DESYNC),
-       *           me.name, parv[0], chptr->chname);
-       */
-
-      /*
-       * After more discussion with orabidoo...
-       *
-       * The code was sound, however, what happens if we have +h (TS4)
-       * and some servers don't understand it yet? 
-       * we will be seeing servers with users who appear to have
-       * no chanops at all, merrily kicking users....
-       * -Dianora
-       */
-    }
-
-  p = strchr(parv[2],',');
-  if(p)
-    *p = '\0';
-  user = parv[2]; /* strtoken(&p2, parv[2], ","); */
-
-  if (!(who = find_chasing(sptr, user, &chasing)))
-    {
-      sendto_one(sptr, form_str(ERR_NOSUCHNICK),
-                 me.name, parv[0], user, name);
-      return(0);
-    }
-
-  if (IsMember(who, chptr))
-    {
-      sendto_channel_butserv(chptr, sptr,
-                             ":%s KICK %s %s :%s", parv[0],
-                             name, who->name, comment);
-      sendto_match_servs(chptr, cptr,
-                         ":%s KICK %s %s :%s",
-                         parv[0], name,
-                         who->name, comment);
-      remove_user_from_channel(who, chptr, 1);
-    }
-  else
-    sendto_one(sptr, form_str(ERR_USERNOTINCHANNEL),
-               me.name, parv[0], user, name);
-
-  return (0);
-}
-
-int     count_channels(struct Client *sptr)
-{
-  struct Channel      *chptr;
-  int   count = 0;
-
-  for (chptr = channel; chptr; chptr = chptr->nextch)
-    count++;
-  return (count);
-}
-
-/* m_knock
-**    parv[0] = sender prefix
-**    parv[1] = channel
-**  The KNOCK command has the following syntax:
-**   :<sender> KNOCK <channel>
-**  If a user is not banned from the channel they can use the KNOCK
-**  command to have the server NOTICE the channel operators notifying
-**  they would like to join.  Helpful if the channel is invite-only, the
-**  key is forgotten, or the channel is full (INVITE can bypass each one
-**  of these conditions.  Concept by Dianora <db@db.net> and written by
-**  <anonymous>
-**
-** Just some flood control added here, five minute delay between each
-** KNOCK -Dianora
-**/
-int     m_knock(struct Client *cptr,
-               struct Client *sptr,
-               int parc,
-               char *parv[])
-{
-  struct Channel      *chptr;
-  char  *p, *name;
-
-  /* anti flooding code,
-   * I did have this in parse.c with a table lookup
-   * but I think this will be less inefficient doing it in each
-   * function that absolutely needs it
-   *
-   * -Dianora
-   */
-  static time_t last_used=0L;
-
-  if (parc < 2)
-    {
-      sendto_one(sptr, form_str(ERR_NEEDMOREPARAMS), me.name, parv[0],
-                 "KNOCK");
-      return 0;
-    }
-
-
-  /* We will cut at the first comma reached, however we will not *
-   * process anything afterwards.                                */
-
-  p = strchr(parv[1],',');
-  if(p)
-    *p = '\0';
-  name = parv[1]; /* strtoken(&p, parv[1], ","); */
-
-  if (!IsChannelName(name) || !(chptr = hash_find_channel(name, NullChn)))
-    {
-      sendto_one(sptr, form_str(ERR_NOSUCHCHANNEL), me.name, parv[0],
-                 name);
-      return 0;
-    }
-
-  if(!((chptr->mode.mode & MODE_INVITEONLY) ||
-       (*chptr->mode.key) ||
-       (chptr->mode.limit && chptr->users >= chptr->mode.limit )
-       ))
-    {
-      sendto_one(sptr,":%s NOTICE %s :*** Notice -- Channel is open!",
-                 me.name,
-                 sptr->name);
-      return 0;
-    }
-
-  /* don't allow a knock if the user is banned, or the channel is secret */
-  if ((chptr->mode.mode & MODE_SECRET) || 
-      (is_banned(sptr, chptr) == CHFL_BAN) )
-    {
-      sendto_one(sptr, form_str(ERR_CANNOTSENDTOCHAN), me.name, parv[0],
-                 name);
-      return 0;
-    }
-
-  /* if the user is already on channel, then a knock is pointless! */
-  if (IsMember(sptr, chptr))
-    {
-      sendto_one(sptr,":%s NOTICE %s :*** Notice -- You are on channel already!",
-                 me.name,
-                 sptr->name);
-      return 0;
-    }
-
-  /* flood control server wide, clients on KNOCK
-   * opers are not flood controlled.
-   */
-
-  if(!IsAnOper(sptr))
-    {
-      if((last_used + PACE_WAIT) > CurrentTime)
-        return 0;
-      else
-        last_used = CurrentTime;
-    }
-
-  /* flood control individual clients on KNOCK
-   * the ugly possibility still exists, 400 clones could all KNOCK
-   * on a channel at once, flooding all the ops. *ugh*
-   * Remember when life was simpler?
-   * -Dianora
-   */
-
-  /* opers are not flow controlled here */
-  if( !IsAnOper(sptr) && (sptr->last_knock + KNOCK_DELAY) > CurrentTime)
-    {
-      sendto_one(sptr,":%s NOTICE %s :*** Notice -- Wait %d seconds before another knock",
-                 me.name,
-                 sptr->name,
-                 KNOCK_DELAY - (CurrentTime - sptr->last_knock));
-      return 0;
-    }
-
-  sptr->last_knock = CurrentTime;
-
-  sendto_one(sptr,":%s NOTICE %s :*** Notice -- Your KNOCK has been delivered",
-                 me.name,
-                 sptr->name);
-
-  /* using &me and me.name won't deliver to clients not on this server
-   * so, the notice will have to appear from the "knocker" ick.
-   *
-   * Ideally, KNOCK would be routable. Also it would be nice to add
-   * a new channel mode. Perhaps some day.
-   * For now, clients that don't want to see KNOCK requests will have
-   * to use client side filtering. 
-   *
-   * -Dianora
-   */
-
-  {
-    char message[NICKLEN*2+CHANNELLEN+USERLEN+HOSTLEN+30];
-
-    /* bit of paranoid, be a shame if it cored for this -Dianora */
-    if(sptr->user)
-      {
-        ircsprintf(message,"KNOCK: %s (%s [%s@%s] has asked for an invite)",
-                   chptr->chname,
-                   sptr->name,
-                   sptr->username,
-                   sptr->host);
-        sendto_channel_type_notice(cptr, chptr, MODE_CHANOP, message);
-      }
-  }
-  return 0;
-}
-
-/*
-** m_topic
-**      parv[0] = sender prefix
-**      parv[1] = topic text
-*/
-int     m_topic(struct Client *cptr,
-                struct Client *sptr,
-                int parc,
-                char *parv[])
-{
-  struct Channel *chptr = NullChn;
-  char  *topic = (char *)NULL, *name, *p = (char *)NULL;
-  
-  if (parc < 2)
-    {
-      sendto_one(sptr, form_str(ERR_NEEDMOREPARAMS),
-                 me.name, parv[0], "TOPIC");
-      return 0;
-    }
-
-  p = strchr(parv[1],',');
-  if(p)
-    *p = '\0';
-  name = parv[1]; /* strtoken(&p, parv[1], ","); */
-
-  /* multi channel topic's are now known to be used by cloners
-   * trying to flood off servers.. so disable it *sigh* - Dianora
-   */
-
-  if (name && IsChannelName(name))
-    {
-      chptr = hash_find_channel(name, NullChn);
-      if (!chptr)
-        {
-          sendto_one(sptr, form_str(ERR_NOSUCHCHANNEL), me.name, parv[0],
-              name);
-          return 0;
-        }
-
-      if (!IsMember(sptr, chptr))
-        {
-          sendto_one(sptr, form_str(ERR_NOTONCHANNEL), me.name, parv[0],
-              name);
-          return 0;
-        }
-
-      if (parc > 2) /* setting topic */
-        topic = parv[2];
-
-      if(topic) /* a little extra paranoia never hurt */
-        {
-          if ((chptr->mode.mode & MODE_TOPICLIMIT) == 0 ||
-               is_chan_op(sptr, chptr))
-            {
-              /* setting a topic */
-              /*
-               * chptr zeroed
-               */
-              strncpy_irc(chptr->topic, topic, TOPICLEN);
-#ifdef TOPIC_INFO
-              /*
-               * XXX - this truncates the topic_nick if
-               * strlen(sptr->name) > NICKLEN
-               */
-              strncpy_irc(chptr->topic_nick, sptr->name, NICKLEN);
-              chptr->topic_time = CurrentTime;
-#endif
-              sendto_match_servs(chptr, cptr,":%s TOPIC %s :%s",
-                                 parv[0], chptr->chname,
-                                 chptr->topic);
-              sendto_channel_butserv(chptr, sptr, ":%s TOPIC %s :%s",
-                                     parv[0],
-                                     chptr->chname, chptr->topic);
-            }
-          else
-            sendto_one(sptr, form_str(ERR_CHANOPRIVSNEEDED),
-                       me.name, parv[0], chptr->chname);
-        }
-      else  /* only asking  for topic  */
-        {
-          if (chptr->topic[0] == '\0')
-            sendto_one(sptr, form_str(RPL_NOTOPIC),
-                       me.name, parv[0], chptr->chname);
-          else
-            {
-              sendto_one(sptr, form_str(RPL_TOPIC),
-                         me.name, parv[0],
-                         chptr->chname, chptr->topic);
-#ifdef TOPIC_INFO
-              sendto_one(sptr, form_str(RPL_TOPICWHOTIME),
-                         me.name, parv[0], chptr->chname,
-                         chptr->topic_nick,
-                         chptr->topic_time);
-#endif
-            }
-        }
-    }
-  else
-    {
-      sendto_one(sptr, form_str(ERR_NOSUCHCHANNEL),
-                 me.name, parv[0], name);
-    }
-
-  return 0;
-}
-
-/*
-** m_invite
-**      parv[0] - sender prefix
-**      parv[1] - user to invite
-**      parv[2] - channel number
-*/
-int     m_invite(struct Client *cptr,
-                 struct Client *sptr,
-                 int parc,
-                 char *parv[])
-{
-  struct Client *acptr;
-  struct Channel *chptr;
-  int need_invite=NO;
-
-  if (parc < 3 || *parv[1] == '\0')
-    {
-      sendto_one(sptr, form_str(ERR_NEEDMOREPARAMS),
-                 me.name, parv[0], "INVITE");
-      return -1;
-    }
-
-  /* A little sanity test here */
-  if(!sptr->user)
-    return 0;
-
-  if (!(acptr = find_person(parv[1], (struct Client *)NULL)))
-    {
-      sendto_one(sptr, form_str(ERR_NOSUCHNICK),
-                 me.name, parv[0], parv[1]);
-      return 0;
-    }
-
-  if (!check_channel_name(parv[2]))
-    { 
-      sendto_one(sptr, form_str(ERR_BADCHANNAME),
-                 me.name, parv[0], (unsigned char *)parv[2]);
-      return 0;
-    }
-
-  if (!IsChannelName(parv[2]))
-    {
-      if (MyClient(sptr))
-        sendto_one(sptr, form_str(ERR_NOSUCHCHANNEL),
-                   me.name, parv[0], parv[2]);
-      return 0;
-    }
-
-  /* Do not send local channel invites to users if they are not on the
-   * same server as the person sending the INVITE message. 
-   */
-  /* Possibly should be an error sent to sptr */
-  if (!MyConnect(acptr) && (parv[2][0] == '&'))
-    return 0;
-
-  if (!(chptr = hash_find_channel(parv[2], NullChn)))
-    {
-      if (MyClient(sptr))
-        sendto_one(sptr, form_str(ERR_NOSUCHCHANNEL),
-                   me.name, parv[0], parv[2]);
-      return 0;
-    }
-
-  /* By this point, chptr is non NULL */  
-
-  if (!IsMember(sptr, chptr))
-    {
-      if (MyClient(sptr))
-        sendto_one(sptr, form_str(ERR_NOTONCHANNEL),
-                   me.name, parv[0], parv[2]);
-      return 0;
-    }
-
-  if (IsMember(acptr, chptr))
-    {
-      if (MyClient(sptr))
-        sendto_one(sptr, form_str(ERR_USERONCHANNEL),
-                   me.name, parv[0], parv[1], parv[2]);
-      return 0;
-    }
-
-  if (chptr && (chptr->mode.mode & MODE_INVITEONLY))
-    {
-      need_invite = YES;
-
-      if (!is_chan_op(sptr, chptr))
-        {
-          if (MyClient(sptr))
-            sendto_one(sptr, form_str(ERR_CHANOPRIVSNEEDED),
-                       me.name, parv[0], parv[2]);
-          return -1;
-        }
-    }
-
-  /*
-   * due to some whining I've taken out the need for the channel
-   * being +i before sending an INVITE. It was intentionally done this
-   * way, it makes no sense (to me at least) letting the server send
-   * an unnecessary invite when a channel isn't +i !
-   * bah. I can't be bothered arguing it
-   * -Dianora
-   */
-  if (MyConnect(sptr) /* && need_invite*/ )
-    {
-      sendto_one(sptr, form_str(RPL_INVITING), me.name, parv[0],
-                 acptr->name, ((chptr) ? (chptr->chname) : parv[2]));
-      if (acptr->user->away)
-        sendto_one(sptr, form_str(RPL_AWAY), me.name, parv[0],
-                   acptr->name, acptr->user->away);
-      
-      if( need_invite )
-        {
-          /* Send a NOTICE to all channel operators concerning chanops who  *
-           * INVITE other users to the channel when it is invite-only (+i). *
-           * The NOTICE is sent from the local server.                      */
-
-          /* Only allow this invite notice if the channel is +p
-           * i.e. "paranoid"
-           * -Dianora
-           */
-
-          if (chptr && (chptr->mode.mode & MODE_PRIVATE))
-            { 
-              char message[NICKLEN*2+CHANNELLEN+USERLEN+HOSTLEN+30];
-
-              /* bit of paranoia, be a shame if it cored for this -Dianora */
-              if(acptr->user)
-                {
-                  ircsprintf(message,
-                             "INVITE: %s (%s invited %s [%s@%s])",
-                             chptr->chname,
-                             sptr->name,
-                             acptr->name,
-                             acptr->username,
-                             acptr->host);
-
-                  sendto_channel_type(cptr, sptr, chptr,
-                                      MODE_CHANOP,
-                                      chptr->chname,
-                                      "PRIVMSG",
-                                      message);
-                }
-            }
-        }
-    }
-
-  if(MyConnect(acptr) && need_invite)
-    add_invite(acptr, chptr);
-
-  sendto_prefix_one(acptr, sptr, ":%s INVITE %s :%s",
-                    parv[0], acptr->name, parv[2]);
-  return 0;
-}
-
-
-/************************************************************************
- * m_names() - Added by Jto 27 Apr 1989
- ************************************************************************/
-
-/*
-** m_names
-**      parv[0] = sender prefix
-**      parv[1] = channel
-*/
-/*
- * Modified to report possible names abuse
- * drastically modified to not show all names, just names
- * on given channel names.
- *
- * -Dianora
- */
-/* maximum names para to show to opers when abuse occurs */
-#define TRUNCATED_NAMES 20
-
-int     m_names( struct Client *cptr,
-                 struct Client *sptr,
-                 int parc,
-                 char *parv[])
-{ 
-  struct Channel *chptr;
-  struct Client *c2ptr;
-  Link  *lp;
-  struct Channel *ch2ptr = NULL;
-  int   idx, flag = 0, len, mlen;
-  char  *s, *para = parc > 1 ? parv[1] : NULL;
-  int comma_count=0;
-  int char_count=0;
-
-  /* Don't route names, no need for it -Dianora */
-  /*
-  if (parc > 1 &&
-      hunt_server(cptr, sptr, ":%s NAMES %s %s", 2, parc, parv))
-    return 0;
-    */
-
-  /* And throw away non local names requests that do get here -Dianora */
-  if(!MyConnect(sptr))
-    return 0;
-
-  /*
-   * names is called by m_join() when client joins a channel,
-   * hence I cannot easily rate limit it.. perhaps that won't
-   * be necessary now that remote names is prohibited.
-   *
-   * -Dianora
-   */
-
-  mlen = strlen(me.name) + NICKLEN + 7;
-
-  if (!BadPtr(para))
-    {
-      /* Here is the lamer detection code
-       * P.S. meta, GROW UP
-       * -Dianora 
-       */
-      for(s = para; *s; s++)
-        {
-          char_count++;
-          if(*s == ',')
-            comma_count++;
-          if(comma_count > 1)
-            {
-              if(char_count > TRUNCATED_NAMES)
-                para[TRUNCATED_NAMES] = '\0';
-              else
-                {
-                  s++;
-                  *s = '\0';
-                }
-              sendto_realops("POSSIBLE /names abuser %s [%s]",
-                             para,
-                             get_client_name(sptr,FALSE));
-              sendto_one(sptr, form_str(ERR_TOOMANYTARGETS),
-                         me.name, sptr->name, "NAMES",1);
-              return 0;
-            }
-        }
-
-      s = strchr(para, ',');
-      if (s)
-        *s = '\0';
-      if (!check_channel_name(para))
-        { 
-          sendto_one(sptr, form_str(ERR_BADCHANNAME),
-                     me.name, parv[0], (unsigned char *)para);
-          return 0;
-        }
-
-      ch2ptr = hash_find_channel(para, NULL);
-    }
-
-  *buf = '\0';
-  
-  /* 
-   *
-   * First, do all visible channels (public and the one user self is)
-   */
-
-  for (chptr = channel; chptr; chptr = chptr->nextch)
-    {
-      if ((chptr != ch2ptr) && !BadPtr(para))
-        continue; /* -- wanted a specific channel */
-      if (!MyConnect(sptr) && BadPtr(para))
-        continue;
-      if (!ShowChannel(sptr, chptr))
-        continue; /* -- users on this are not listed */
-      
-      /* Find users on same channel (defined by chptr) */
-
-      (void)strcpy(buf, "* ");
-      len = strlen(chptr->chname);
-      (void)strcpy(buf + 2, chptr->chname);
-      (void)strcpy(buf + 2 + len, " :");
-
-      if (PubChannel(chptr))
-        *buf = '=';
-      else if (SecretChannel(chptr))
-        *buf = '@';
-      idx = len + 4;
-      flag = 1;
-      for (lp = chptr->members; lp; lp = lp->next)
-        {
-          c2ptr = lp->value.cptr;
-          if (IsInvisible(c2ptr) && !IsMember(sptr,chptr))
-            continue;
-          if (lp->flags & CHFL_CHANOP)
-            {
-              strcat(buf, "@");
-              idx++;
-            }
-          else if (lp->flags & CHFL_VOICE)
-            {
-              strcat(buf, "+");
-              idx++;
-            }
-          strncat(buf, c2ptr->name, NICKLEN);
-          idx += strlen(c2ptr->name) + 1;
-          flag = 1;
-          strcat(buf," ");
-          if (mlen + idx + NICKLEN > BUFSIZE - 3)
-            {
-              sendto_one(sptr, form_str(RPL_NAMREPLY),
-                         me.name, parv[0], buf);
-              strncpy_irc(buf, "* ", 3);
-              strncpy_irc(buf + 2, chptr->chname, len + 1);
-              strcat(buf, " :");
-              if (PubChannel(chptr))
-                *buf = '=';
-              else if (SecretChannel(chptr))
-                *buf = '@';
-              idx = len + 4;
-              flag = 0;
-            }
-        }
-      if (flag)
-        sendto_one(sptr, form_str(RPL_NAMREPLY),
-                   me.name, parv[0], buf);
-    }
-  if (!BadPtr(para))
-    {
-      sendto_one(sptr, form_str(RPL_ENDOFNAMES), me.name, parv[0],
-                 para);
-      return(1);
-    }
-
-  /* Second, do all non-public, non-secret channels in one big sweep */
-
-  strncpy_irc(buf, "* * :", 6);
-  idx = 5;
-  flag = 0;
-  for (c2ptr = GlobalClientList; c2ptr; c2ptr = c2ptr->next)
-    {
-      struct Channel *ch3ptr;
-      int       showflag = 0, secret = 0;
-
-      if (!IsPerson(c2ptr) || IsInvisible(c2ptr))
-        continue;
-      lp = c2ptr->user->channel;
-      /*
-       * dont show a client if they are on a secret channel or
-       * they are on a channel sptr is on since they have already
-       * been show earlier. -avalon
-       */
-      while (lp)
-        {
-          ch3ptr = lp->value.chptr;
-          if (PubChannel(ch3ptr) || IsMember(sptr, ch3ptr))
-            showflag = 1;
-          if (SecretChannel(ch3ptr))
-            secret = 1;
-          lp = lp->next;
-        }
-      if (showflag) /* have we already shown them ? */
-        continue;
-      if (secret) /* on any secret channels ? */
-        continue;
-      (void)strncat(buf, c2ptr->name, NICKLEN);
-      idx += strlen(c2ptr->name) + 1;
-      (void)strcat(buf," ");
-      flag = 1;
-      if (mlen + idx + NICKLEN > BUFSIZE - 3)
-        {
-          sendto_one(sptr, form_str(RPL_NAMREPLY),
-                     me.name, parv[0], buf);
-          strncpy_irc(buf, "* * :", 6);
-          idx = 5;
-          flag = 0;
-        }
-    }
-
-  if (flag)
-    sendto_one(sptr, form_str(RPL_NAMREPLY), me.name, parv[0], buf);
-
-  sendto_one(sptr, form_str(RPL_ENDOFNAMES), me.name, parv[0], "*");
-  return(1);
-}
-
-
-void    send_user_joins(struct Client *cptr, struct Client *user)
-{
-  Link  *lp;
-  struct Channel *chptr;
-  int   cnt = 0, len = 0, clen;
-  char   *mask;
-
-  *buf = ':';
-  (void)strcpy(buf+1, user->name);
-  (void)strcat(buf, " JOIN ");
-  len = strlen(user->name) + 7;
-
-  for (lp = user->user->channel; lp; lp = lp->next)
-    {
-      chptr = lp->value.chptr;
-      if (*chptr->chname == '&')
-        continue;
-      if ((mask = strchr(chptr->chname, ':')))
-        if (!match(++mask, cptr->name))
-          continue;
-      clen = strlen(chptr->chname);
-      if (clen > (size_t) BUFSIZE - 7 - len)
-        {
-          if (cnt)
-            sendto_one(cptr, "%s", buf);
-          *buf = ':';
-          (void)strcpy(buf+1, user->name);
-          (void)strcat(buf, " JOIN ");
-          len = strlen(user->name) + 7;
-          cnt = 0;
-        }
-      (void)strcpy(buf + len, chptr->chname);
-      cnt++;
-      len += clen;
-      if (lp->next)
-        {
-          len++;
-          (void)strcat(buf, ",");
-        }
-    }
-  if (*buf && cnt)
-    sendto_one(cptr, "%s", buf);
-
-  return;
-}
-
-static  void sjoin_sendit(struct Client *cptr,
-                          struct Client *sptr,
-                          struct Channel *chptr,
-                          char *from)
-{
-  sendto_channel_butserv(chptr, sptr, ":%s MODE %s %s %s", from,
-                         chptr->chname, modebuf, parabuf);
-}
-
-/*
- * m_sjoin
- * parv[0] - sender
- * parv[1] - TS
- * parv[2] - channel
- * parv[3] - modes + n arguments (key and/or limit)
- * parv[4+n] - flags+nick list (all in one parameter)
-
- * 
- * process a SJOIN, taking the TS's into account to either ignore the
- * incoming modes or undo the existing ones or merge them, and JOIN
- * all the specified users while sending JOIN/MODEs to non-TS servers
- * and to clients
- */
-
-
-int     m_sjoin(struct Client *cptr,
-                struct Client *sptr,
-                int parc,
-                char *parv[])
-{
-  struct Channel *chptr;
-  struct Client       *acptr;
-  time_t        newts;
-  time_t        oldts;
-  time_t        tstosend;
-  static        Mode mode, *oldmode;
-  Link  *l;
-  int   args = 0, haveops = 0, keep_our_modes = 1, keep_new_modes = 1;
-  int   doesop = 0, what = 0, pargs = 0, fl, people = 0, isnew;
-  /* loop unrolled this is now redundant */
-  /*  int ip; */
-  register      char *s, *s0;
-  static        char numeric[16], sjbuf[BUFSIZE];
-  char  *mbuf = modebuf, *t = sjbuf, *p;
-
-  if (IsClient(sptr) || parc < 5)
-    return 0;
-  if (!IsChannelName(parv[2]))
-    return 0;
-
-  if (!check_channel_name(parv[2]))
-     { 
-       return 0;
-     }
-
-  /* comstud server did this, SJOIN's for
-   * local channels can't happen.
-   */
-
-  if(*parv[2] == '&')
-    return 0;
-
-  newts = atol(parv[1]);
-  memset(&mode, 0, sizeof(mode));
-
-  s = parv[3];
-  while (*s)
-    switch(*(s++))
-      {
-      case 'i':
-        mode.mode |= MODE_INVITEONLY;
-        break;
-      case 'n':
-        mode.mode |= MODE_NOPRIVMSGS;
-        break;
-      case 'p':
-        mode.mode |= MODE_PRIVATE;
-        break;
-      case 's':
-        mode.mode |= MODE_SECRET;
-        break;
-      case 'm':
-        mode.mode |= MODE_MODERATED;
-        break;
-      case 't':
-        mode.mode |= MODE_TOPICLIMIT;
-        break;
-      case 'k':
-        strncpy_irc(mode.key, parv[4 + args], KEYLEN);
-        args++;
-        if (parc < 5+args) return 0;
-        break;
-      case 'l':
-        mode.limit = atoi(parv[4+args]);
-        args++;
-        if (parc < 5+args) return 0;
-        break;
-      }
-
-  *parabuf = '\0';
-
-  isnew = ChannelExists(parv[2]) ? 0 : 1;
-  chptr = get_channel(sptr, parv[2], CREATE);
-
-
-  /*
-   * bogus ban removal code.
-   * If I see that this SJOIN will mean I keep my ops, but lose
-   * the ops from the joining server, I keep track of that in the channel
-   * structure. I set keep_their_modes to NO
-   * since the joining server will not be keeping their ops, I can
-   * ignore any of the bans sent from that server. The moment
-   * I see a chanop MODE being sent, I can set this flag back to YES.
-   *
-   * There is one degenerate case. Two servers connect bursting
-   * at the same time. It might cause a problem, or it might not.
-   * In the case that it becomes an issue, then a short list
-   * of servers having their modes ignored would have to be linked
-   * into the channel structure. This would be only an issue
-   * on hubs.
-   * Hopefully, it will be much of a problem.
-   *
-   * Bogus bans on the server losing its chanops is trivial. All
-   * bans placed on the local server during its split, with bogus chanops
-   * I can just remove.
-   *
-   * -Dianora
-   */
-  
-  chptr->keep_their_modes = YES;
-
-  /* locally created channels do not get created from SJOIN's
-   * any SJOIN destroys the locally_created flag
-   *
-   * -Dianora
-   */
-
-  chptr->locally_created = NO;
-  oldts = chptr->channelts;
-
-  /*
-   * If an SJOIN ever happens on a channel, assume the split is over
-   * for this channel. best I think we can do for now -Dianora
-   */
-
-  /* If the TS goes to 0 for whatever reason, flag it
-   * ya, I know its an invasion of privacy for those channels that
-   * want to keep TS 0 *shrug* sorry
-   * -Dianora
-   */
-
-  if(!isnew && !newts && oldts)
-    {
-      sendto_channel_butserv(chptr, &me,
-             ":%s NOTICE %s :*** Notice -- TS for %s changed from %lu to 0",
-              me.name, chptr->chname, chptr->chname, oldts);
-      sendto_realops("Server %s changing TS on %s from %lu to 0",
-                     sptr->name,parv[2],oldts);
-    }
-
-  doesop = (parv[4+args][0] == '@' || parv[4+args][1] == '@');
-
-  for (l = chptr->members; l && l->value.cptr; l = l->next)
-    if (l->flags & MODE_CHANOP)
-      {
-        haveops++;
-        break;
-      }
-
-  oldmode = &chptr->mode;
-
-  if (isnew)
-    chptr->channelts = tstosend = newts;
-  else if (newts == 0 || oldts == 0)
-    chptr->channelts = tstosend = 0;
-  else if (newts == oldts)
-    tstosend = oldts;
-  else if (newts < oldts)
-    {
-      if (doesop)
-        keep_our_modes = NO;
-
-      clear_bans_exceptions_denies(sptr,chptr);
-
-      if (haveops && !doesop)
-          tstosend = oldts;
-      else
-        chptr->channelts = tstosend = newts;
-    }
-  else
-    {
-      chptr->keep_their_modes = NO;
-
-      if (haveops)
-        keep_new_modes = NO;
-      if (doesop && !haveops)
-        {
-          chptr->channelts = tstosend = newts;
-#if 0
-          /* Only warn of Hacked ops if the channel already
-           * existed on this side.
-           * This should drop the number of warnings down dramatically
-           */
-
-          if (MyConnect(sptr) && !isnew)
-            {
-              sendto_realops("Hacked ops from %s on opless channel: %s",
-                             sptr->name,
-                             chptr->chname);
-            }
-#endif
-        }
-      else
-        tstosend = oldts;
-    }
-
-  if (!keep_new_modes)
-    mode = *oldmode;
-  else if (keep_our_modes)
-    {
-      mode.mode |= oldmode->mode;
-      if (oldmode->limit > mode.limit)
-        mode.limit = oldmode->limit;
-      if (strcmp(mode.key, oldmode->key) < 0)
-        strcpy(mode.key, oldmode->key);
-    }
-
-  /* This loop unrolled below for speed
-   */
-  /*
-  for (ip = 0; flags[ip].mode; ip++)
-    if ((flags[ip].mode & mode.mode) && !(flags[ip].mode & oldmode->mode))
-      {
-        if (what != 1)
-          {
-            *mbuf++ = '+';
-            what = 1;
-          }
-        *mbuf++ = flags[ip].letter;
-      }
-      */
-
-  if((MODE_PRIVATE    & mode.mode) && !(MODE_PRIVATE    & oldmode->mode))
-    {
-      if (what != 1)
-        {
-          *mbuf++ = '+';
-          what = 1;
-        }
-      *mbuf++ = 'p';
-    }
-  if((MODE_SECRET     & mode.mode) && !(MODE_SECRET     & oldmode->mode))
-    {
-      if (what != 1)
-        {
-          *mbuf++ = '+';
-          what = 1;
-        }
-      *mbuf++ = 's';
-    }
-  if((MODE_MODERATED  & mode.mode) && !(MODE_MODERATED  & oldmode->mode))
-    {
-      if (what != 1)
-        {
-          *mbuf++ = '+';
-          what = 1;
-        }
-      *mbuf++ = 'm';
-    }
-  if((MODE_NOPRIVMSGS & mode.mode) && !(MODE_NOPRIVMSGS & oldmode->mode))
-    {
-      if (what != 1)
-        {
-          *mbuf++ = '+';
-          what = 1;
-        }
-      *mbuf++ = 'n';
-    }
-  if((MODE_TOPICLIMIT & mode.mode) && !(MODE_TOPICLIMIT & oldmode->mode))
-    {
-      if (what != 1)
-        {
-          *mbuf++ = '+';
-          what = 1;
-        }
-      *mbuf++ = 't';
-    }
-  if((MODE_INVITEONLY & mode.mode) && !(MODE_INVITEONLY & oldmode->mode))
-    {
-      if (what != 1)
-        {
-          *mbuf++ = '+';
-          what = 1;             /* This one is actually redundant now */
-        }
-      *mbuf++ = 'i';
-    }
-
-  /* This loop unrolled below for speed
-   */
-  /*
-  for (ip = 0; flags[ip].mode; ip++)
-    if ((flags[ip].mode & oldmode->mode) && !(flags[ip].mode & mode.mode))
-      {
-        if (what != -1)
-          {
-            *mbuf++ = '-';
-            what = -1;
-          }
-        *mbuf++ = flags[ip].letter;
-      }
-      */
-  if((MODE_PRIVATE    & oldmode->mode) && !(MODE_PRIVATE    & mode.mode))
-    {
-      if (what != -1)
-        {
-          *mbuf++ = '-';
-          what = -1;
-        }
-      *mbuf++ = 'p';
-    }
-  if((MODE_SECRET     & oldmode->mode) && !(MODE_SECRET     & mode.mode))
-    {
-      if (what != -1)
-        {
-          *mbuf++ = '-';
-          what = -1;
-        }
-      *mbuf++ = 's';
-    }
-  if((MODE_MODERATED  & oldmode->mode) && !(MODE_MODERATED  & mode.mode))
-    {
-      if (what != -1)
-        {
-          *mbuf++ = '-';
-          what = -1;
-        }
-      *mbuf++ = 'm';
-    }
-  if((MODE_NOPRIVMSGS & oldmode->mode) && !(MODE_NOPRIVMSGS & mode.mode))
-    {
-      if (what != -1)
-        {
-          *mbuf++ = '-';
-          what = -1;
-        }
-      *mbuf++ = 'n';
-    }
-  if((MODE_TOPICLIMIT & oldmode->mode) && !(MODE_TOPICLIMIT & mode.mode))
-    {
-      if (what != -1)
-        {
-          *mbuf++ = '-';
-          what = -1;
-        }
-      *mbuf++ = 't';
-    }
-  if((MODE_INVITEONLY & oldmode->mode) && !(MODE_INVITEONLY & mode.mode))
-    {
-      if (what != -1)
-        {
-          *mbuf++ = '-';
-          what = -1;
-        }
-      *mbuf++ = 'i';
-    }
-
-  if (oldmode->limit && !mode.limit)
-    {
-      if (what != -1)
-        {
-          *mbuf++ = '-';
-          what = -1;
-        }
-      *mbuf++ = 'l';
-    }
-  if (oldmode->key[0] && !mode.key[0])
-    {
-      if (what != -1)
-        {
-          *mbuf++ = '-';
-          what = -1;
-        }
-      *mbuf++ = 'k';
-      strcat(parabuf, oldmode->key);
-      strcat(parabuf, " ");
-      pargs++;
-    }
-  if (mode.limit && oldmode->limit != mode.limit)
-    {
-      if (what != 1)
-        {
-          *mbuf++ = '+';
-          what = 1;
-        }
-      *mbuf++ = 'l';
-      ircsprintf(numeric, "%d", mode.limit);
-      if ((s = strchr(numeric, ' ')))
-        *s = '\0';
-      strcat(parabuf, numeric);
-      strcat(parabuf, " ");
-      pargs++;
-    }
-  if (mode.key[0] && strcmp(oldmode->key, mode.key))
-    {
-      if (what != 1)
-        {
-          *mbuf++ = '+';
-          what = 1;
-        }
-      *mbuf++ = 'k';
-      strcat(parabuf, mode.key);
-      strcat(parabuf, " ");
-      pargs++;
-    }
-  
-  chptr->mode = mode;
-
-  if (!keep_our_modes)
-    {
-      what = 0;
-      for (l = chptr->members; l && l->value.cptr; l = l->next)
-        {
-          if (l->flags & MODE_CHANOP)
-            {
-              if (what != -1)
-                {
-                  *mbuf++ = '-';
-                  what = -1;
-                }
-              *mbuf++ = 'o';
-              strcat(parabuf, l->value.cptr->name);
-              strcat(parabuf, " ");
-              pargs++;
-              if (pargs >= MAXMODEPARAMS)
-                {
-                  *mbuf = '\0';
-                  sjoin_sendit(cptr, sptr, chptr,
-                               parv[0]);
-                  mbuf = modebuf;
-                  *mbuf = parabuf[0] = '\0';
-                  pargs = what = 0;
-                }
-              l->flags &= ~MODE_CHANOP;
-            }
-          if (l->flags & MODE_VOICE)
-            {
-              if (what != -1)
-                {
-                  *mbuf++ = '-';
-                  what = -1;
-                }
-              *mbuf++ = 'v';
-              strcat(parabuf, l->value.cptr->name);
-              strcat(parabuf, " ");
-              pargs++;
-              if (pargs >= MAXMODEPARAMS)
-                {
-                  *mbuf = '\0';
-                  sjoin_sendit(cptr, sptr, chptr,
-                               parv[0]);
-                  mbuf = modebuf;
-                  *mbuf = parabuf[0] = '\0';
-                  pargs = what = 0;
-                }
-              l->flags &= ~MODE_VOICE;
-            }
-        }
-        sendto_channel_butserv(chptr, &me,
-            ":%s NOTICE %s :*** Notice -- TS for %s changed from %lu to %lu",
-            me.name, chptr->chname, chptr->chname, oldts, newts);
-    }
-  if (mbuf != modebuf)
-    {
-      *mbuf = '\0';
-      sjoin_sendit(cptr, sptr, chptr, parv[0]);
-    }
-
-  *modebuf = *parabuf = '\0';
-  if (parv[3][0] != '0' && keep_new_modes)
-    channel_modes(sptr, modebuf, parabuf, chptr);
-  else
-    {
-      modebuf[0] = '0';
-      modebuf[1] = '\0';
-    }
-
-  ircsprintf(t, ":%s SJOIN %lu %s %s %s :", parv[0], tstosend, parv[2],
-          modebuf, parabuf);
-  t += strlen(t);
-
-  mbuf = modebuf;
-  parabuf[0] = '\0';
-  pargs = 0;
-  *mbuf++ = '+';
-
-  for (s = s0 = strtoken(&p, parv[args+4], " "); s;
-       s = s0 = strtoken(&p, (char *)NULL, " "))
-    {
-      fl = 0;
-      if (*s == '@' || s[1] == '@')
-        fl |= MODE_CHANOP;
-      if (*s == '+' || s[1] == '+')
-        fl |= MODE_VOICE;
-      if (!keep_new_modes)
-       {
-        if (fl & MODE_CHANOP)
-          {
-            fl = MODE_DEOPPED;
-          }
-        else
-          {
-            fl = 0;
-          }
-       }
-      while (*s == '@' || *s == '+')
-        s++;
-      if (!(acptr = find_chasing(sptr, s, NULL)))
-        continue;
-      if (acptr->from != cptr)
-        continue;
-      people++;
-      if (!IsMember(acptr, chptr))
-        {
-          add_user_to_channel(chptr, acptr, fl);
-          sendto_channel_butserv(chptr, acptr, ":%s JOIN :%s",
-                                 s, parv[2]);
-        }
-      if (keep_new_modes)
-        strcpy(t, s0);
-      else
-        strcpy(t, s);
-      t += strlen(t);
-      *t++ = ' ';
-      if (fl & MODE_CHANOP)
-        {
-          *mbuf++ = 'o';
-          strcat(parabuf, s);
-          strcat(parabuf, " ");
-          pargs++;
-          if (pargs >= MAXMODEPARAMS)
-            {
-              *mbuf = '\0';
-              sjoin_sendit(cptr, sptr, chptr, parv[0]);
-              mbuf = modebuf;
-              *mbuf++ = '+';
-              parabuf[0] = '\0';
-              pargs = 0;
-            }
-        }
-      if (fl & MODE_VOICE)
-        {
-          *mbuf++ = 'v';
-          strcat(parabuf, s);
-          strcat(parabuf, " ");
-          pargs++;
-          if (pargs >= MAXMODEPARAMS)
-            {
-              *mbuf = '\0';
-              sjoin_sendit(cptr, sptr, chptr, parv[0]);
-              mbuf = modebuf;
-              *mbuf++ = '+';
-              parabuf[0] = '\0';
-              pargs = 0;
-            }
-        }
-    }
-  
-  *mbuf = '\0';
-  if (pargs)
-    sjoin_sendit(cptr, sptr, chptr, parv[0]);
-  if (people)
-    {
-      if (t[-1] == ' ')
-        t[-1] = '\0';
-      else
-        *t = '\0';
-      sendto_match_servs(chptr, cptr, "%s", sjbuf);
-    }
-  return 0;
-}
-
-
+/************************************************************************
+ *   IRC - Internet Relay Chat, src/channel.c
+ *   Copyright (C) 1990 Jarkko Oikarinen and
+ *                      University of Oulu, Co Center
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 1, or (at your option)
+ *   any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ * a number of behaviours in set_mode() have been rewritten
+ * These flags can be set in a define if you wish.
+ *
+ * OLD_P_S      - restore xor of p vs. s modes per channel
+ *                currently p is rather unused, so using +p
+ *                to disable "knock" seemed worth while.
+ * OLD_MODE_K   - new mode k behaviour means user can set new key
+ *                while old one is present, mode * -k of old key is done
+ *                on behalf of user, with mode * +k of new key.
+ *                /mode * -key results in the sending of a *, which
+ *                can be used to resynchronize a channel.
+ * OLD_NON_RED  - Current code allows /mode * -s etc. to be applied
+ *                even if +s is not set. Old behaviour was not to allow
+ *                mode * -p etc. if flag was clear
+ *
+ *
+ * $Id: channel.c,v 1.178 2000/08/25 00:29:11 lusky Exp $
+ */
+#include "channel.h"
+#include "client.h"
+#include "common.h"
+#include "hash.h"
+#include "irc_string.h"
+#include "ircd.h"
+#include "list.h"
+#include "numeric.h"
+#include "s_serv.h"       /* captab */
+#include "s_user.h"
+#include "send.h"
+#include "struct.h"
+#include "whowas.h"
+
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+
+#ifdef NEED_SPLITCODE
+
+static void check_still_split();
+int server_was_split=YES;
+int got_server_pong;
+time_t server_split_time;
+
+#if defined(PRESERVE_CHANNEL_ON_SPLIT) || defined(NO_JOIN_ON_SPLIT)
+struct Channel *empty_channel_list=(struct Channel*)NULL;
+void remove_empty_channels();
+#endif
+#endif
+
+struct Channel *channel = NullChn;
+
+static  void    add_invite (struct Client *, struct Channel *);
+static  int     add_banid (struct Client *, struct Channel *, char *);
+static  int     add_exceptid(struct Client *, struct Channel *, char *);
+static  int     add_denyid(struct Client *, struct Channel *, char *);
+static  int     can_join (struct Client *, struct Channel *, char *,int *);
+static  int     del_banid (struct Channel *, char *);
+static  int     del_exceptid (struct Channel *, char *);
+static  int     del_denyid (struct Channel *, char *);
+static  void    clear_bans_exceptions_denies(struct Client *,struct Channel *);
+static  void    free_bans_exceptions_denies(struct Channel *);
+static  int     is_banned (struct Client *, struct Channel *);
+static  void    sub1_from_channel (struct Channel *);
+
+
+/* static functions used in set_mode */
+static char* pretty_mask(char *);
+static char *fix_key(char *);
+static char *fix_key_old(char *);
+static void collapse_signs(char *);
+static int errsent(int,int *);
+static void change_chan_flag(struct Channel *, struct Client *, int );
+static void set_deopped(struct Client *,struct Channel *,int);
+
+static  char    *PartFmt = ":%s PART %s";
+/*
+ * some buffers for rebuilding channel/nick lists with ,'s
+ */
+
+static  char    buf[BUFSIZE];
+static  char    modebuf[MODEBUFLEN], modebuf2[MODEBUFLEN];
+static  char    parabuf[MODEBUFLEN], parabuf2[MODEBUFLEN];
+
+
+/* 
+ * return the length (>=0) of a chain of links.
+ */
+static  int     list_length(Link *lp)
+{
+  int   count = 0;
+
+  for (; lp; lp = lp->next)
+    count++;
+  return count;
+}
+
+/*
+ *  Fixes a string so that the first white space found becomes an end of
+ * string marker (`\0`).  returns the 'fixed' string or "*" if the string
+ * was NULL length or a NULL pointer.
+ */
+static char* check_string(char* s)
+{
+  static char star[2] = "*";
+  char* str = s;
+
+  if (BadPtr(s))
+    return star;
+
+  for ( ; *s; ++s) {
+    if (IsSpace(*s))
+      {
+        *s = '\0';
+        break;
+      }
+  }
+  return str;
+}
+
+/*
+ * create a string of form "foo!bar@fubar" given foo, bar and fubar
+ * as the parameters.  If NULL, they become "*".
+ */
+static char* make_nick_user_host(const char* nick, 
+                                 const char* name, const char* host)
+{
+  static char namebuf[NICKLEN + USERLEN + HOSTLEN + 6];
+  int   n;
+  char* s;
+  const char* p;
+
+  s = namebuf;
+
+  for (p = nick, n = NICKLEN; *p && n--; )
+    *s++ = *p++;
+  *s++ = '!';
+  for(p = name, n = USERLEN; *p && n--; )
+    *s++ = *p++;
+  *s++ = '@';
+  for(p = host, n = HOSTLEN; *p && n--; )
+    *s++ = *p++;
+  *s = '\0';
+  return namebuf;
+}
+
+/*
+ * Ban functions to work with mode +b
+ */
+/* add_banid - add an id to be banned to the channel  (belongs to cptr) */
+
+static  int     add_banid(struct Client *cptr, struct Channel *chptr, char *banid)
+{
+  Link  *ban;
+
+  /* dont let local clients overflow the banlist */
+  if ((!IsServer(cptr)) && (chptr->num_bed >= MAXBANS))
+	  if (MyClient(cptr))
+	    {
+	      sendto_one(cptr, form_str(ERR_BANLISTFULL),
+                   me.name, cptr->name,
+                   chptr->chname, banid);
+	      return -1;
+	    }
+  
+  if (MyClient(cptr))
+    collapse(banid);
+
+  for (ban = chptr->banlist; ban; ban = ban->next)
+	  if (match(BANSTR(ban), banid))
+	    return -1;
+
+  ban = make_link();
+  memset(ban, 0, sizeof(Link));
+  ban->flags = CHFL_BAN;
+  ban->next = chptr->banlist;
+
+#ifdef BAN_INFO
+
+  ban->value.banptr = (aBan *)MyMalloc(sizeof(aBan));
+  ban->value.banptr->banstr = (char *)MyMalloc(strlen(banid)+1);
+  (void)strcpy(ban->value.banptr->banstr, banid);
+
+#ifdef USE_UH
+  if (IsPerson(cptr))
+    {
+      ban->value.banptr->who =
+        (char *)MyMalloc(strlen(cptr->name)+
+                         strlen(cptr->username)+
+                         strlen(cptr->host)+3);
+      ircsprintf(ban->value.banptr->who, "%s!%s@%s",
+                 cptr->name, cptr->username, cptr->host);
+    }
+  else
+    {
+#endif
+      ban->value.banptr->who = (char *)MyMalloc(strlen(cptr->name)+1);
+      (void)strcpy(ban->value.banptr->who, cptr->name);
+#ifdef USE_UH
+    }
+#endif
+
+  ban->value.banptr->when = CurrentTime;
+
+#else
+
+  ban->value.cp = (char *)MyMalloc(strlen(banid)+1);
+  (void)strcpy(ban->value.cp, banid);
+
+#endif  /* #ifdef BAN_INFO */
+
+  chptr->banlist = ban;
+  chptr->num_bed++;
+  return 0;
+}
+
+/* add_exceptid - add an id to the exception list for the channel  
+ * (belongs to cptr) 
+ */
+
+static  int     add_exceptid(struct Client *cptr, struct Channel *chptr, char *eid)
+{
+  Link  *ex, *ban;
+
+  /* dont let local clients overflow the banlist */
+  if ((!IsServer(cptr)) && (chptr->num_bed >= MAXBANS))
+    if (MyClient(cptr))
+      {
+        sendto_one(cptr, form_str(ERR_BANLISTFULL),
+                   me.name, cptr->name,
+                   chptr->chname, eid);
+        return -1;
+      }
+
+  if (MyClient(cptr))
+    (void)collapse(eid);
+
+  for (ban = chptr->exceptlist; ban; ban = ban->next)
+	  if (match(BANSTR(ban), eid))
+	    return -1;
+  
+
+  ex = make_link();
+  memset(ex, 0, sizeof(Link));
+  ex->flags = CHFL_EXCEPTION;
+  ex->next = chptr->exceptlist;
+
+#ifdef BAN_INFO
+
+  ex->value.banptr = (aBan *)MyMalloc(sizeof(aBan));
+  ex->value.banptr->banstr = (char *)MyMalloc(strlen(eid)+1);
+  (void)strcpy(ex->value.banptr->banstr, eid);
+
+#ifdef USE_UH
+  if (IsPerson(cptr))
+    {
+      ex->value.banptr->who =
+        (char *)MyMalloc(strlen(cptr->name)+
+                         strlen(cptr->username)+
+                         strlen(cptr->host)+3);
+      ircsprintf(ex->value.banptr->who, "%s!%s@%s",
+                 cptr->name, cptr->username, cptr->host);
+    }
+  else
+    {
+#endif
+      ex->value.banptr->who = (char *)MyMalloc(strlen(cptr->name)+1);
+      (void)strcpy(ex->value.banptr->who, cptr->name);
+#ifdef USE_UH
+    }
+#endif
+
+  ex->value.banptr->when = CurrentTime;
+
+#else
+
+  ex->value.cp = (char *)MyMalloc(strlen(eid)+1);
+  (void)strcpy(ex->value.cp, eid);
+
+#endif  /* #ifdef BAN_INFO */
+
+  chptr->exceptlist = ex;
+  chptr->num_bed++;
+  return 0;
+}
+
+
+/*
+ * add_denyid - add an id to be denied to the channel  (belongs to cptr) 
+ * works like an Xline, but for channels.
+ * -sean
+ */  
+static  int     add_denyid(struct Client *cptr, struct Channel *chptr, char *banid)
+{
+  Link  *ban;
+
+  /* truncate to REALLEN */
+  banid[REALLEN]='\0';
+
+  if ((!IsServer(cptr)) && (chptr->num_bed >= MAXBANS))
+    if (MyClient(cptr))
+      {
+        sendto_one(cptr, form_str(ERR_BANLISTFULL),
+                   me.name, cptr->name,
+                   chptr->chname, banid);
+        return -1;
+      }
+
+  if (MyClient(cptr))
+    (void)collapse(banid);
+
+  for (ban = chptr->denylist; ban; ban = ban->next)
+    if (strcasecmp(BANSTR(ban), banid)==0) 
+      return -1;
+  
+  ban = make_link();
+  memset(ban, 0, sizeof(Link));
+  ban->flags = CHFL_DENY;
+  ban->next = chptr->denylist;
+
+#ifdef BAN_INFO
+
+  ban->value.banptr = (aBan *)MyMalloc(sizeof(aBan));
+  ban->value.banptr->banstr = (char *)MyMalloc(strlen(banid)+1);
+  (void)strcpy(ban->value.banptr->banstr, banid);
+
+#ifdef USE_UH
+  if (IsPerson(cptr))
+    {
+      ban->value.banptr->who =
+        (char *)MyMalloc(strlen(cptr->name)+
+                         strlen(cptr->username)+
+                         strlen(cptr->host)+3);
+      ircsprintf(ban->value.banptr->who, "%s!%s@%s",
+                 cptr->name, cptr->username, cptr->host);
+    }
+   else
+    {
+#endif
+      ban->value.banptr->who = (char *)MyMalloc(strlen(cptr->name)+1);
+      (void)strcpy(ban->value.banptr->who, cptr->name);
+#ifdef USE_UH
+    }
+#endif
+
+  ban->value.banptr->when = CurrentTime;
+
+#else
+
+  ban->value.cp = (char *)MyMalloc(strlen(banid)+1);
+  (void)strcpy(ban->value.cp, banid);
+
+#endif  /* #ifdef BAN_INFO */
+
+  chptr->denylist = ban;
+  chptr->num_bed++;
+  return 0;
+}
+
+
+
+/*
+ *
+ * "del_banid - delete an id belonging to cptr
+ * if banid is null, deleteall banids belonging to cptr."
+ *
+ * from orabidoo
+ */
+static  int     del_banid(struct Channel *chptr, char *banid)
+{
+  register Link **ban;
+  register Link *tmp;
+
+  if (!banid)
+    return -1;
+  for (ban = &(chptr->banlist); *ban; ban = &((*ban)->next))
+#ifdef BAN_INFO
+    if (irccmp(banid, (*ban)->value.banptr->banstr)==0)
+#else
+      if (irccmp(banid, (*ban)->value.cp)==0)
+#endif
+        {
+          tmp = *ban;
+          *ban = tmp->next;
+#ifdef BAN_INFO
+          MyFree(tmp->value.banptr->banstr);
+          MyFree(tmp->value.banptr->who);
+          MyFree(tmp->value.banptr);
+#else
+          MyFree(tmp->value.cp);
+#endif
+          free_link(tmp);
+	  /* num_bed should never be < 0 */
+	  if(chptr->num_bed > 0)
+	    chptr->num_bed--;
+	  else
+	    chptr->num_bed = 0;
+          break;
+        }
+  return 0;
+}
+
+/*
+ * del_exceptid - delete an id belonging to cptr
+ *
+ * from orabidoo
+ */
+static  int     del_exceptid(struct Channel *chptr, char *eid)
+{
+  register Link **ex;
+  register Link *tmp;
+
+  if (!eid)
+    return -1;
+  for (ex = &(chptr->exceptlist); *ex; ex = &((*ex)->next))
+    if (irccmp(eid, BANSTR(*ex)) == 0)
+      {
+        tmp = *ex;
+        *ex = tmp->next;
+#ifdef BAN_INFO
+        MyFree(tmp->value.banptr->banstr);
+        MyFree(tmp->value.banptr->who);
+        MyFree(tmp->value.banptr);
+#else
+        MyFree(tmp->value.cp);
+#endif
+        free_link(tmp);
+	/* num_bed should never be < 0 */
+	if(chptr->num_bed > 0)
+	  chptr->num_bed--;
+	else
+	  chptr->num_bed = 0;
+        break;
+      }
+  return 0;
+}
+
+/*
+ *
+ * "del_denyid - delete an id belonging to cptr
+ * if banid is null, deleteall banids belonging to cptr."
+ *
+ * -sean
+ */
+static  int     del_denyid(struct Channel *chptr, char *banid)
+{
+  register Link **ban;
+  register Link *tmp;
+
+  if (!banid)
+    return -1;
+  for (ban = &(chptr->denylist); *ban; ban = &((*ban)->next))
+#ifdef BAN_INFO
+    if (strcasecmp(banid, (*ban)->value.banptr->banstr)==0) 
+#else
+      if (strcasecmp(banid, (*ban)->value.cp)==0)
+#endif
+        {
+          tmp = *ban;
+          *ban = tmp->next;
+#ifdef BAN_INFO
+          MyFree(tmp->value.banptr->banstr);
+          MyFree(tmp->value.banptr->who);
+          MyFree(tmp->value.banptr);
+#else
+          MyFree(tmp->value.cp);
+#endif
+          free_link(tmp);
+
+	  /* num_bed should never be < 0 */
+	  if(chptr->num_bed > 0)
+	    chptr->num_bed--;
+	  else
+	    chptr->num_bed = 0;
+          break;
+        }
+  return 0;
+}
+
+
+
+/*
+ * del_matching_exception - delete an exception matching this user
+ *
+ * The idea is, if a +e client gets kicked for any reason
+ * remove the matching exception for this client.
+ * This will immediately stop channel "chatter" with scripts
+ * that kick on matching ban. It will also stop apparent "desyncs."
+ * It's not the long term answer, but it will tide us over.
+ *
+ * modified from orabidoo - Dianora
+ */
+static void del_matching_exception(struct Client *cptr,struct Channel *chptr)
+{
+  register Link **ex;
+  register Link *tmp;
+  char  s[NICKLEN + USERLEN + HOSTLEN+6];
+  char  *s2;
+
+  if (!IsPerson(cptr))
+    return;
+
+  strcpy(s, make_nick_user_host(cptr->name, cptr->username, cptr->host));
+  s2 = make_nick_user_host(cptr->name, cptr->username,
+                           inetntoa((char*) &cptr->ip));
+
+  for (ex = &(chptr->exceptlist); *ex; ex = &((*ex)->next))
+    {
+      tmp = *ex;
+
+      if (match(BANSTR(tmp), s) ||
+          match(BANSTR(tmp), s2) )
+        {
+
+          /* code needed here to send -e to channel.
+           * I will not propogate the removal,
+           * This will lead to desyncs of e modes,
+           * but its not going to be any worse then it is now.
+           *
+           * Kickee gets to see the -e removal by the server
+           * since they have not yet been removed from the channel.
+           * I don't think thats a biggie.
+           *
+           * -Dianora
+           */
+
+          sendto_channel_butserv(chptr,
+                                 &me,
+                                 ":%s MODE %s -e %s", 
+                                 me.name,
+                                 chptr->chname,
+                                 BANSTR(tmp));
+
+          *ex = tmp->next;
+#ifdef BAN_INFO
+          MyFree(tmp->value.banptr->banstr);
+          MyFree(tmp->value.banptr->who);
+          MyFree(tmp->value.banptr);
+#else
+          MyFree(tmp->value.cp);
+#endif
+          free_link(tmp);
+	  /* num_bed should never be < 0 */
+	  if(chptr->num_bed > 0)
+	    chptr->num_bed--;
+	  else
+	    chptr->num_bed = 0;
+          return;
+        }
+    }
+}
+
+/*
+ * is_banned -  returns an int 0 if not banned,
+ *              CHFL_BAN if banned (or +d'd)
+ *              CHFL_EXCEPTION if they have a ban exception
+ *
+ * IP_BAN_ALL from comstud
+ * always on...
+ *
+ * +e code from orabidoo
+ */
+
+static  int is_banned(struct Client *cptr,struct Channel *chptr)
+{
+  register Link *tmp;
+  register Link *t2;
+  char  s[NICKLEN+USERLEN+HOSTLEN+6];
+  char  *s2;
+
+  if (!IsPerson(cptr))
+    return (0);
+
+  strcpy(s, make_nick_user_host(cptr->name, cptr->username, cptr->host));
+  s2 = make_nick_user_host(cptr->name, cptr->username,
+                           inetntoa((char*) &cptr->ip));
+
+  for (tmp = chptr->banlist; tmp; tmp = tmp->next)
+    if (match(BANSTR(tmp), s) ||
+        match(BANSTR(tmp), s2))
+      break;
+
+  if (!tmp) {  /* check +d list */
+    for (tmp = chptr->denylist; tmp; tmp = tmp->next)
+      {
+        if (match(BANSTR(tmp), cptr->info))
+          break;
+      }
+  }
+
+  if (tmp)
+    {
+      for (t2 = chptr->exceptlist; t2; t2 = t2->next)
+        if (match(BANSTR(t2), s) ||
+            match(BANSTR(t2), s2))
+          {
+            return CHFL_EXCEPTION;
+          }
+    }
+
+  /* return CHFL_BAN for +b or +d match, we really dont need to be more
+     specific */
+  return ((tmp?CHFL_BAN:0));
+}
+
+/*
+ * adds a user to a channel by adding another link to the channels member
+ * chain.
+ */
+static  void    add_user_to_channel(struct Channel *chptr, struct Client *who, int flags)
+{
+  Link *ptr;
+
+#if defined(PRESERVE_CHANNEL_ON_SPLIT) || defined(NO_JOIN_ON_SPLIT)
+  if( chptr->mode.mode & MODE_SPLIT )
+    {
+      /* Unmark the split mode */
+      chptr->mode.mode &= ~MODE_SPLIT;
+
+      /* remove from the empty channel double link list */
+      if (chptr->last_empty_channel)
+        chptr->last_empty_channel->next_empty_channel =
+          chptr->next_empty_channel;
+      else
+        empty_channel_list = chptr->next_empty_channel;
+      if (chptr->next_empty_channel)
+        chptr->next_empty_channel->last_empty_channel =
+          chptr->last_empty_channel;
+    }
+#endif
+
+  if (who->user)
+    {
+      ptr = make_link();
+      ptr->flags = flags;
+      ptr->value.cptr = who;
+      ptr->next = chptr->members;
+      chptr->members = ptr;
+
+      chptr->users++;
+
+      ptr = make_link();
+      ptr->value.chptr = chptr;
+      ptr->next = who->user->channel;
+      who->user->channel = ptr;
+      who->user->joined++;
+    }
+}
+
+void    remove_user_from_channel(struct Client *sptr,struct Channel *chptr,int was_kicked)
+{
+  Link  **curr;
+  Link  *tmp;
+
+  for (curr = &chptr->members; (tmp = *curr); curr = &tmp->next)
+    if (tmp->value.cptr == sptr)
+      {
+        /* User was kicked, but had an exception.
+         * so, to reduce chatter I'll remove any
+         * matching exception now.
+         */
+        if(was_kicked && (tmp->flags & CHFL_EXCEPTION))
+          {
+            del_matching_exception(sptr,chptr);
+          }
+        *curr = tmp->next;
+        free_link(tmp);
+        break;
+      }
+  for (curr = &sptr->user->channel; (tmp = *curr); curr = &tmp->next)
+    if (tmp->value.chptr == chptr)
+      {
+        *curr = tmp->next;
+        free_link(tmp);
+        break;
+      }
+  sptr->user->joined--;
+
+  sub1_from_channel(chptr);
+
+}
+
+static  void    change_chan_flag(struct Channel *chptr,struct Client *cptr, int flag)
+{
+  Link *tmp;
+
+  if ((tmp = find_user_link(chptr->members, cptr)))
+   {
+    if (flag & MODE_ADD)
+      {
+        tmp->flags |= flag & MODE_FLAGS;
+        if (flag & MODE_CHANOP)
+          tmp->flags &= ~MODE_DEOPPED;
+      }
+    else
+      {
+        tmp->flags &= ~flag & MODE_FLAGS;
+      }
+   }
+}
+
+static  void    set_deopped(struct Client *cptr, struct Channel *chptr,int flag)
+{
+  Link  *tmp;
+
+  if ((tmp = find_user_link(chptr->members, cptr)))
+    if ((tmp->flags & flag) == 0)
+      tmp->flags |= MODE_DEOPPED;
+}
+
+int     is_chan_op(struct Client *cptr, struct Channel *chptr)
+{
+  Link  *lp;
+
+  if (chptr)
+    if ((lp = find_user_link(chptr->members, cptr)))
+      return (lp->flags & CHFL_CHANOP);
+  
+  return 0;
+}
+
+int     is_deopped(struct Client *cptr, struct Channel *chptr)
+{
+  Link  *lp;
+
+  if (chptr)
+    if ((lp = find_user_link(chptr->members, cptr)))
+      return (lp->flags & CHFL_DEOPPED);
+  
+  return 0;
+}
+
+int     has_voice(struct Client *cptr, struct Channel *chptr)
+{
+  Link  *lp;
+
+  if (chptr)
+    if ((lp = find_user_link(chptr->members, cptr)))
+      return (lp->flags & CHFL_VOICE);
+
+  return 0;
+}
+
+int     can_send(struct Client *cptr, struct Channel *chptr)
+{
+  Link  *lp;
+
+#ifdef JUPE_CHANNEL
+  if (MyClient(cptr) && (chptr->mode.mode & MODE_JUPED))
+    {
+      return (MODE_JUPED);
+    }
+#endif
+
+  lp = find_user_link(chptr->members, cptr);
+
+  if (chptr->mode.mode & MODE_MODERATED &&
+      (!lp || !(lp->flags & (CHFL_CHANOP|CHFL_VOICE))))
+    return (MODE_MODERATED);
+
+  if (chptr->mode.mode & MODE_NOPRIVMSGS && !lp)
+    return (MODE_NOPRIVMSGS);
+
+  return 0;
+}
+
+int     user_channel_mode(struct Client *cptr, struct Channel *chptr)
+{
+  Link  *lp;
+
+  if (chptr)
+    if ((lp = find_user_link(chptr->members, cptr)))
+      return (lp->flags);
+  
+  return 0;
+}
+
+/*
+ * write the "simple" list of channel modes for channel chptr onto buffer mbuf
+ * with the parameters in pbuf.
+ */
+void channel_modes(struct Client *cptr, char *mbuf, char *pbuf, struct Channel *chptr)
+{
+  *mbuf++ = '+';
+  if (chptr->mode.mode & MODE_SECRET)
+    *mbuf++ = 's';
+
+  /* bug found by "is" ejb@debian.org */
+#ifdef OLD_P_S
+  else if (chptr->mode.mode & MODE_PRIVATE)
+    *mbuf++ = 'p';
+#else
+  if (chptr->mode.mode & MODE_PRIVATE)
+    *mbuf++ = 'p';
+#endif
+
+  if (chptr->mode.mode & MODE_MODERATED)
+    *mbuf++ = 'm';
+  if (chptr->mode.mode & MODE_TOPICLIMIT)
+    *mbuf++ = 't';
+  if (chptr->mode.mode & MODE_INVITEONLY)
+    *mbuf++ = 'i';
+  if (chptr->mode.mode & MODE_NOPRIVMSGS)
+    *mbuf++ = 'n';
+  if (chptr->mode.limit)
+    {
+      *mbuf++ = 'l';
+      if (IsMember(cptr, chptr) || IsServer(cptr))
+        ircsprintf(pbuf, "%d ", chptr->mode.limit);
+    }
+  if (*chptr->mode.key)
+    {
+      *mbuf++ = 'k';
+      if (IsMember(cptr, chptr) || IsServer(cptr))
+        (void)strcat(pbuf, chptr->mode.key);
+    }
+  *mbuf++ = '\0';
+  return;
+}
+
+/*
+ * only used to send +b and +e now, +d/+a too.
+ * 
+ */
+
+static  void    send_mode_list(struct Client *cptr,
+                               char *chname,
+                               Link *top,
+                               int mask,
+                               char flag)
+{
+  Link  *lp;
+  char  *cp, *name;
+  int   count = 0, send = 0;
+  
+  cp = modebuf + strlen(modebuf);
+  if (*parabuf) /* mode +l or +k xx */
+    count = 1;
+  for (lp = top; lp; lp = lp->next)
+    {
+      if (!(lp->flags & mask))
+        continue;
+      name = BANSTR(lp);
+        
+      if (strlen(parabuf) + strlen(name) + 10 < (size_t) MODEBUFLEN)
+        {
+          (void)strcat(parabuf, " ");
+          (void)strcat(parabuf, name);
+          count++;
+          *cp++ = flag;
+          *cp = '\0';
+        }
+      else if (*parabuf)
+        send = 1;
+      if (count == 3)
+        send = 1;
+      if (send)
+        {
+          sendto_one(cptr, ":%s MODE %s %s %s",
+                     me.name, chname, modebuf, parabuf);
+          send = 0;
+          *parabuf = '\0';
+          cp = modebuf;
+          *cp++ = '+';
+          if (count != 3)
+            {
+              (void)strcpy(parabuf, name);
+              *cp++ = flag;
+            }
+          count = 0;
+          *cp = '\0';
+        }
+    }
+}
+
+/*
+ * send "cptr" a full list of the modes for channel chptr.
+ */
+void send_channel_modes(struct Client *cptr, struct Channel *chptr)
+{
+  Link  *l, *anop = NULL, *skip = NULL;
+  int   n = 0;
+  char  *t;
+
+  if (*chptr->chname != '#')
+    return;
+
+  *modebuf = *parabuf = '\0';
+  channel_modes(cptr, modebuf, parabuf, chptr);
+
+  if (*parabuf)
+    strcat(parabuf, " ");
+  ircsprintf(buf, ":%s SJOIN %lu %s %s %s:", me.name,
+          chptr->channelts, chptr->chname, modebuf, parabuf);
+  t = buf + strlen(buf);
+  for (l = chptr->members; l && l->value.cptr; l = l->next)
+    if (l->flags & MODE_CHANOP)
+      {
+        anop = l;
+        break;
+      }
+  /* follow the channel, but doing anop first if it's defined
+  **  -orabidoo
+  */
+  l = NULL;
+  for (;;)
+    {
+      if (anop)
+        {
+          l = skip = anop;
+          anop = NULL;
+        }
+      else 
+        {
+          if (l == NULL || l == skip)
+            l = chptr->members;
+          else
+            l = l->next;
+          if (l && l == skip)
+            l = l->next;
+          if (l == NULL)
+            break;
+        }
+      if (l->flags & MODE_CHANOP)
+        *t++ = '@';
+      if (l->flags & MODE_VOICE)
+        *t++ = '+';
+      strcpy(t, l->value.cptr->name);
+      t += strlen(t);
+      *t++ = ' ';
+      n++;
+      if (t - buf > BUFSIZE - 80)
+        {
+          *t++ = '\0';
+          if (t[-1] == ' ') t[-1] = '\0';
+          sendto_one(cptr, "%s", buf);
+          ircsprintf(buf, ":%s SJOIN %lu %s 0 :",
+                  me.name, chptr->channelts,
+                  chptr->chname);
+          t = buf + strlen(buf);
+          n = 0;
+        }
+    }
+      
+  if (n)
+    {
+      *t++ = '\0';
+      if (t[-1] == ' ') t[-1] = '\0';
+      sendto_one(cptr, "%s", buf);
+    }
+  *parabuf = '\0';
+  *modebuf = '+';
+  modebuf[1] = '\0';
+  send_mode_list(cptr, chptr->chname, chptr->banlist, CHFL_BAN,'b');
+
+  if (modebuf[1] || *parabuf)
+    sendto_one(cptr, ":%s MODE %s %s %s",
+               me.name, chptr->chname, modebuf, parabuf);
+
+  if(!IsCapable(cptr,CAP_EX))
+    return;
+
+  *parabuf = '\0';
+  *modebuf = '+';
+  modebuf[1] = '\0';
+  send_mode_list(cptr, chptr->chname, chptr->exceptlist, CHFL_EXCEPTION,'e');
+
+  if (modebuf[1] || *parabuf)
+    sendto_one(cptr, ":%s MODE %s %s %s",
+               me.name, chptr->chname, modebuf, parabuf);
+
+  if(!IsCapable(cptr,CAP_DE))
+      return;
+  *parabuf = '\0';
+  *modebuf = '+';
+  modebuf[1] = '\0';
+  send_mode_list(cptr, chptr->chname, chptr->denylist, CHFL_DENY,'d');
+  
+  if (modebuf[1] || *parabuf)
+    sendto_one(cptr, ":%s MODE %s %s %s",
+               me.name, chptr->chname, modebuf, parabuf);
+}
+
+/* stolen from Undernet's ircd  -orabidoo
+ *
+ */
+
+static char* pretty_mask(char* mask)
+{
+  register char* cp = mask;
+  register char* user;
+  register char* host;
+
+  if ((user = strchr(cp, '!')))
+    *user++ = '\0';
+  if ((host = strrchr(user ? user : cp, '@')))
+    {
+      *host++ = '\0';
+      if (!user)
+        return make_nick_user_host("*", check_string(cp), check_string(host));
+    }
+  else if (!user && strchr(cp, '.'))
+    return make_nick_user_host("*", "*", check_string(cp));
+  return make_nick_user_host(check_string(cp), check_string(user), 
+                             check_string(host));
+}
+
+static  char    *fix_key(char *arg)
+{
+  u_char        *s, *t, c;
+
+  for (s = t = (u_char *)arg; (c = *s); s++)
+    {
+      if (c != ':' && c > 0x20)
+      {
+	c &= 0x7f;
+        *t++ = c;
+      }
+    }
+  *t = '\0';
+  return arg;
+}
+
+/*
+ * Here we attempt to be compatible with older non-hybrid servers.
+ * We can't back down from the ':' issue however.  --Rodder
+ */
+static  char    *fix_key_old(char *arg)
+{
+  u_char        *s, *t, c;
+
+  for (s = t = (u_char *)arg; (c = *s); s++)
+    { 
+      c &= 0x7f;
+      if ((c != 0x0a) && (c != ':'))
+        *t++ = c;
+    }
+  *t = '\0';
+  return arg;
+}
+
+/*
+ * like the name says...  take out the redundant signs in a modechange list
+ */
+static  void    collapse_signs(char *s)
+{
+  char  plus = '\0', *t = s, c;
+  while ((c = *s++))
+    {
+      if (c != plus)
+        *t++ = c;
+      if (c == '+' || c == '-')
+        plus = c;
+    }
+  *t = '\0';
+}
+
+/* little helper function to avoid returning duplicate errors */
+static  int     errsent(int err, int *errs)
+{
+  if (err & *errs)
+    return 1;
+  *errs |= err;
+  return 0;
+}
+
+/* bitmasks for various error returns that set_mode should only return
+ * once per call  -orabidoo
+ */
+
+#define SM_ERR_NOTS             0x00000001      /* No TS on channel */
+#define SM_ERR_NOOPS            0x00000002      /* No chan ops */
+#define SM_ERR_UNKNOWN          0x00000004
+#define SM_ERR_RPL_C            0x00000008
+#define SM_ERR_RPL_B            0x00000010
+#define SM_ERR_RPL_E            0x00000020
+#define SM_ERR_NOTONCHANNEL     0x00000040      /* Not on channel */
+#define SM_ERR_RESTRICTED       0x00000080      /* Restricted chanop */
+
+/*
+** Apply the mode changes passed in parv to chptr, sending any error
+** messages and MODE commands out.  Rewritten to do the whole thing in
+** one pass, in a desperate attempt to keep the code sane.  -orabidoo
+*/
+/*
+ * rewritten to remove +h/+c/z 
+ * in spirit with the one pass idea, I've re-written how "imnspt"
+ * handling was done
+ *
+ * I've also left some "remnants" of the +h code in for possible
+ * later addition.
+ * For example, isok could be replaced witout half ops, with ischop() or
+ * chan_op depending.
+ *
+ * -Dianora
+ */
+
+void set_channel_mode(struct Client *cptr,
+                      struct Client *sptr,
+                      struct Channel *chptr,
+                      int parc,
+                      char *parv[])
+{
+  int   errors_sent = 0, opcnt = 0, len = 0, tmp, nusers;
+  int   keychange = 0, limitset = 0;
+  int   whatt = MODE_ADD, the_mode = 0;
+#ifdef OLD_P_S
+  int   done_s_or_p = NO;
+#else
+  int   done_s = NO, done_p = NO;
+#endif
+  int   done_i = NO, done_m = NO, done_n = NO, done_t = NO;
+  struct Client *who;
+  Link  *lp;
+  char  *curr = parv[0], c, *arg, plus = '+', *tmpc;
+  char  numeric[16];
+  /* mbufw gets the param-less mode chars, always with their sign
+   * mbuf2w gets the paramed mode chars, always with their sign
+   * pbufw gets the params, in ID form whenever possible
+   * pbuf2w gets the params, no ID's
+   */
+  /* no ID code at the moment
+   * pbufw gets the params, no ID's
+   * grrrr for now I'll stick the params into pbufw without ID's
+   * -Dianora
+   */
+  /* *sigh* FOR YOU Roger, and ONLY for you ;-)
+   * lets stick mode/params that only the newer servers will understand
+   * into modebuf_new/parabuf_new 
+   * even worse!  nodebuf_newer/parabuf_newer <-- for CAP_DE       
+   */
+
+  char  modebuf_newer[MODEBUFLEN];
+  char  parabuf_newer[MODEBUFLEN];
+
+  char  modebuf_new[MODEBUFLEN];
+  char  parabuf_new[MODEBUFLEN];
+
+  char  *mbufw = modebuf, *mbuf2w = modebuf2;
+  char  *pbufw = parabuf, *pbuf2w = parabuf2;
+
+  char  *mbufw_new = modebuf_new;
+  char  *pbufw_new = parabuf_new;
+
+  char  *mbufw_newer = modebuf_newer;
+  char  *pbufw_newer = parabuf_newer;
+
+  int   ischop;
+  int   isok;
+  int   isdeop;
+  int   chan_op;
+  int   self_lose_ops;
+  int   user_mode;
+
+	/* 10/25/2000, shadow */
+	int hack_cloaked_ops = 0;
+	char *nameToSend = 0;
+
+  self_lose_ops = 0;
+   
+/* 10/20/2000 -shadow - godness */
+ /* user_mode = user_channel_mode(sptr, chptr);*/
+   user_mode = user_channel_mode(sptr, chptr);
+
+   /* 10/20/2000 - shadow */
+   if (IsAnOper(sptr)) user_mode = user_mode | CHFL_CHANOP;
+   
+   /* chan_op = (user_mode & CHFL_CHANOP); */
+   /* 10/20/2000 - shadow see below */
+  chan_op = (user_mode & CHFL_CHANOP) || IsAnOper(sptr);
+
+  /* has ops or is a server */
+/*  ischop = IsServer(sptr) || chan_op; */
+   /* 10/20/2000 - shadow - enables oper godness */
+   ischop = IsServer(sptr) || chan_op || IsAnOper(sptr);
+
+  /* is client marked as deopped */
+/*  isdeop = !ischop && !IsServer(sptr) && (user_mode & CHFL_DEOPPED); */
+   /* 10/20/2000 - shadow - see above */
+   isdeop = (!ischop && !IsServer(sptr) && (user_mode & CHFL_DEOPPED)) && !IsAnOper(sptr);
+
+  /* is an op or server or remote user on a TS channel */
+  /*isok = ischop || (!isdeop && IsServer(cptr) && chptr->channelts);*/
+   /* 10/20/2000 - shadow - see above */
+   isok = (ischop || (!isdeop && IsServer(cptr) && chptr->channelts)) || IsAnOper(sptr);
+
+   
+  if(isok)
+    chptr->keep_their_modes = YES;
+
+  /* isok_c calculated later, only if needed */
+
+  /* parc is the number of _remaining_ args (where <0 means 0);
+  ** parv points to the first remaining argument
+  */
+  parc--;
+  parv++;
+
+  for ( ; ; )
+    {
+      if (BadPtr(curr))
+        {
+          /*
+           * Deal with mode strings like "+m +o blah +i"
+           */
+          if (parc-- > 0)
+            {
+              curr = *parv++;
+              continue;
+            }
+          break;
+        }
+      c = *curr++;
+
+      switch (c)
+        {
+        case '+' :
+          whatt = MODE_ADD;
+          plus = '+';
+          continue;
+          /* NOT REACHED */
+          break;
+
+        case '-' :
+          whatt = MODE_DEL;
+          plus = '-';
+          continue;
+          /* NOT REACHED */
+          break;
+
+        case '=' :
+          whatt = MODE_QUERY;
+          plus = '=';   
+          continue;
+          /* NOT REACHED */
+          break;
+
+        case 'o' :
+        case 'v' :
+          if (MyClient(sptr))
+            {
+              if(!IsMember(sptr, chptr))
+                {
+                  if(!errsent(SM_ERR_NOTONCHANNEL, &errors_sent))
+                    sendto_one(sptr, form_str(ERR_NOTONCHANNEL),
+                               me.name, sptr->name, chptr->chname);
+                  /* eat the parameter */
+                  parc--;
+                  parv++;
+                  break;
+                }
+#ifdef LITTLE_I_LINES
+              else
+                {
+                  if(IsRestricted(sptr) && (whatt == MODE_ADD))
+                    {
+                      if(!errsent(SM_ERR_RESTRICTED, &errors_sent))
+                        {
+                          sendto_one(sptr,
+            ":%s NOTICE %s :*** Notice -- You are restricted and cannot chanop others",
+                                 me.name,
+                                 sptr->name);
+                        }
+                      /* eat the parameter */
+                      parc--;
+                      parv++;
+                      break;
+                    }
+                }
+#endif
+            }
+          if (whatt == MODE_QUERY)
+            break;
+          if (parc-- <= 0)
+            break;
+          arg = check_string(*parv++);
+
+          if (MyClient(sptr) && opcnt >= MAXMODEPARAMS)
+            break;
+
+          if (!(who = find_chasing(sptr, arg, NULL)))
+            break;
+
+          /* there is always the remote possibility of picking up
+           * a bogus user, be nasty to core for that. -Dianora
+           */
+
+          if (!who->user)
+            break;
+
+          /* no more of that mode bouncing crap */
+          if (!IsMember(who, chptr))
+            {
+              if (MyClient(sptr))
+                sendto_one(sptr, form_str(ERR_USERNOTINCHANNEL), me.name, 
+                           sptr->name, arg, chptr->chname);
+              break;
+            }
+
+          if ((who == sptr) && (c == 'o'))
+            {
+	       /* 10/20/2000 -- shadow -- godness. */
+/*              if(whatt == MODE_ADD)
+                break; */
+				 /* 10/25/2000 - shadow - hacked op cloaking */
+						if (whatt == MODE_ADD) {
+							/* 11/4/2000 - TEMPORARILY DISABLING */
+							/* HACKED OP CLOAKING, CAUSE I FUCKED UP */
+						   hack_cloaked_ops = 0;
+							 /*sendto_one(sptr, ":%s NOTICE %s :Cloaking hacked ops... enjoy :).", me.name, sptr->name);*/
+						}
+              
+              if(whatt == MODE_DEL)
+                self_lose_ops = 1;
+              }
+
+          /* ignore server-generated MODE +-ovh */
+          if (IsServer(sptr))
+            {
+              ts_warn( "MODE %c%c on %s for %s from server %s (ignored)", 
+                       (whatt == MODE_ADD ? '+' : '-'), c, chptr->chname, 
+                       who->name,sptr->name);
+              break;
+            }
+
+          if (c == 'o')
+            the_mode = MODE_CHANOP;
+          else if (c == 'v')
+            the_mode = MODE_VOICE;
+
+
+
+/*          if (isdeop && (c == 'o') && whatt == MODE_ADD)*/
+	   /* 10/20/2000 - shadow */
+	   if ((isdeop && (c == 'o') && whatt == MODE_ADD) && !IsAnOper(sptr)) 
+            set_deopped(who, chptr, the_mode);
+
+          if (!isok)
+            {
+              if (MyClient(sptr) && !errsent(SM_ERR_NOOPS, &errors_sent))
+                sendto_one(sptr, form_str(ERR_CHANOPRIVSNEEDED), me.name, 
+                           sptr->name, chptr->chname);
+              break;
+            }
+
+
+          tmp = strlen(arg);
+          if (len + tmp + 2 >= MODEBUFLEN)
+            break;
+
+          *mbufw++ = plus;
+          *mbufw++ = c;
+          strcpy(pbufw, who->name);
+          pbufw += strlen(pbufw);
+          *pbufw++ = ' ';
+          len += tmp + 1;
+          opcnt++;
+
+          change_chan_flag(chptr, who, the_mode|whatt);
+
+          if (self_lose_ops)
+            isok = 0;
+
+          break;
+
+        case 'k':
+          if (whatt == MODE_QUERY)
+            break;
+          if (parc-- <= 0)
+            {
+              /* allow arg-less mode -k */
+              if (whatt == MODE_DEL)
+                arg = "*";
+              else
+                break;
+            }
+          else
+            {
+              if (whatt == MODE_DEL)
+                {
+                  arg = check_string(*parv++);
+                }
+              else
+                {
+                  if MyClient(sptr)
+                    arg = fix_key(check_string(*parv++));
+                  else
+                    arg = fix_key_old(check_string(*parv++));
+                }
+            }
+
+          if (keychange++)
+            break;
+          /*      if (MyClient(sptr) && opcnt >= MAXMODEPARAMS)
+            break;
+            */
+          if (!*arg)
+            break;
+
+          if (!isok)
+            {
+              if (!errsent(SM_ERR_NOOPS, &errors_sent) && MyClient(sptr))
+                sendto_one(sptr, form_str(ERR_CHANOPRIVSNEEDED), me.name, 
+                           sptr->name, chptr->chname);
+              break;
+            }
+
+#ifdef OLD_MODE_K
+          if ((whatt == MODE_ADD) && (*chptr->mode.key))
+            {
+              sendto_one(sptr, form_str(ERR_KEYSET), me.name, 
+                         sptr->name, chptr->chname);
+              break;
+            }
+#endif
+          if ( (tmp = strlen(arg)) > KEYLEN)
+            {
+              arg[KEYLEN] = '\0';
+              tmp = KEYLEN;
+            }
+
+          if (len + tmp + 2 >= MODEBUFLEN)
+            break;
+
+#ifndef OLD_MODE_K
+          /* if there is already a key, and the client is adding one
+           * remove the old one, then add the new one
+           */
+
+          if((whatt == MODE_ADD) && *chptr->mode.key)
+            {
+              /* If the key is the same, don't do anything */
+
+              if(!strcmp(chptr->mode.key,arg))
+                break;
+
+              sendto_channel_butserv(chptr, sptr, ":%s MODE %s -k %s", 
+                                     sptr->name, chptr->chname,
+                                     chptr->mode.key);
+
+              sendto_match_servs(chptr, cptr, ":%s MODE %s -k %s",
+                                 sptr->name, chptr->chname,
+                                 chptr->mode.key);
+            }
+#endif
+          if (whatt == MODE_DEL)
+            {
+              if( (arg[0] == '*') && (arg[1] == '\0'))
+                arg = chptr->mode.key;
+              else
+                {
+                  if(strcmp(arg,chptr->mode.key))
+                    break;
+		}
+	    }
+
+          *mbufw++ = plus;
+          *mbufw++ = 'k';
+          strcpy(pbufw, arg);
+          pbufw += strlen(pbufw);
+          *pbufw++ = ' ';
+          len += tmp + 1;
+          /*      opcnt++; */
+
+          if (whatt == MODE_DEL)
+            {
+              *chptr->mode.key = '\0';
+            }
+          else
+            {
+              /*
+               * chptr was zeroed
+               */
+              strncpy_irc(chptr->mode.key, arg, KEYLEN);
+            }
+
+          break;
+
+          /* There is a nasty here... I'm supposed to have
+           * CAP_EX before I can send exceptions to bans to a server.
+           * But that would mean I'd have to keep two strings
+           * one for local clients, and one for remote servers,
+           * one with the 'e' strings, one without.
+           * I added another parameter buf and mode buf for "new"
+           * capabilities.
+           *
+           * -Dianora
+           */
+
+        case 'e':
+          if (whatt == MODE_QUERY || parc-- <= 0)
+            {
+              if (!MyClient(sptr))
+                break;
+              if (errsent(SM_ERR_RPL_E, &errors_sent))
+                break;
+              /* don't allow a non chanop to see the exception list
+               * suggested by Matt on operlist nov 25 1998
+               */
+              if(isok)
+                {
+#ifdef BAN_INFO
+                  for (lp = chptr->exceptlist; lp; lp = lp->next)
+                    sendto_one(cptr, form_str(RPL_EXCEPTLIST),
+                               me.name, cptr->name,
+                               chptr->chname,
+                               lp->value.banptr->banstr,
+                               lp->value.banptr->who,
+                               lp->value.banptr->when);
+#else 
+                  for (lp = chptr->exceptlist; lp; lp = lp->next)
+                    sendto_one(cptr, form_str(RPL_EXCEPTLIST),
+                               me.name, cptr->name,
+                               chptr->chname,
+                               lp->value.cp);
+#endif
+                  sendto_one(sptr, form_str(RPL_ENDOFEXCEPTLIST),
+                             me.name, sptr->name, 
+                             chptr->chname);
+                }
+              break;
+            }
+          arg = check_string(*parv++);
+
+          if (MyClient(sptr) && opcnt >= MAXMODEPARAMS)
+            break;
+
+          if (!isok)
+            {
+              if (!errsent(SM_ERR_NOOPS, &errors_sent) && MyClient(sptr))
+                sendto_one(sptr, form_str(ERR_CHANOPRIVSNEEDED),
+                           me.name, sptr->name, 
+                           chptr->chname);
+              break;
+            }
+          
+          if(MyClient(sptr))
+            chptr->keep_their_modes = YES;
+          else if(!chptr->keep_their_modes)
+            {
+              parc--;
+              parv++;
+              break;
+            }
+
+          /* user-friendly ban mask generation, taken
+          ** from Undernet's ircd  -orabidoo
+          */
+          if (MyClient(sptr))
+            arg = collapse(pretty_mask(arg));
+
+          if(*arg == ':')
+            {
+              parc--;
+              parv++;
+              break;
+            }
+
+          tmp = strlen(arg);
+          if (len + tmp + 2 >= MODEBUFLEN)
+            break;
+
+          if (!(((whatt & MODE_ADD) && !add_exceptid(sptr, chptr, arg)) ||
+                ((whatt & MODE_DEL) && !del_exceptid(chptr, arg))))
+            break;
+
+          /* This stuff can go back in when all servers understand +e 
+           * with the pbufw_new nonsense removed -Dianora
+           */
+
+          /*
+          *mbufw++ = plus;
+          *mbufw++ = 'e';
+          strcpy(pbufw, arg);
+          pbufw += strlen(pbufw);
+          *pbufw++ = ' ';
+          */
+          len += tmp + 1;
+          opcnt++;
+
+          *mbufw_new++ = plus;
+          *mbufw_new++ = 'e';
+          strcpy(pbufw_new, arg);
+          pbufw_new += strlen(pbufw_new);
+          *pbufw_new++ = ' ';
+
+          break;
+
+
+          /* There is a nasty here... I'm supposed to have
+           * CAP_DE before I can send exceptions to bans to a server.
+           * But that would mean I'd have to keep two strings
+           * one for local clients, and one for remote servers,
+           * one with the 'e' strings, one without.
+           * I added another parameter buf and mode buf for "new"
+           * capabilities.
+           *
+           * -Dianora
+           */
+
+        case 'd':
+          if (whatt == MODE_QUERY || parc-- <= 0)
+            {
+              if (!MyClient(sptr))
+                break;
+              if (errsent(SM_ERR_RPL_E, &errors_sent))
+                break;
+#ifdef BAN_INFO
+                  for (lp = chptr->denylist; lp; lp = lp->next)
+                    sendto_one(cptr, form_str(RPL_BANLIST),
+                               me.name, cptr->name,
+                               chptr->chname,
+                               lp->value.banptr->banstr,
+                               lp->value.banptr->who,
+                               lp->value.banptr->when);
+#else 
+                  for (lp = chptr->denylist; lp; lp = lp->next)
+                    sendto_one(cptr, form_str(RPL_BANLIST),
+                               me.name, cptr->name,
+                               chptr->chname,
+                               lp->value.cp);
+#endif
+                  sendto_one(sptr, form_str(RPL_ENDOFBANLIST),
+                             me.name, sptr->name, 
+                             chptr->chname);
+                  break;
+            }
+          arg = check_string(*parv++);
+
+          if (MyClient(sptr) && opcnt >= MAXMODEPARAMS)
+            break;
+
+          if (!isok)
+            {
+              if (!errsent(SM_ERR_NOOPS, &errors_sent) && MyClient(sptr))
+                sendto_one(sptr, form_str(ERR_CHANOPRIVSNEEDED),
+                           me.name, sptr->name, 
+                           chptr->chname);
+              break;
+            }
+          
+          if(MyClient(sptr))
+            chptr->keep_their_modes = YES;
+          else if(!chptr->keep_their_modes)
+            {
+              parc--;
+              parv++;
+              break;
+            }
+
+          if(*arg == ':')
+            {
+              parc--;
+              parv++;
+              break;
+            }
+
+          tmp = strlen(arg);
+          if (len + tmp + 2 >= MODEBUFLEN)
+            break;
+
+          if (!(((whatt & MODE_ADD) && !add_denyid(sptr, chptr, arg)) ||
+                ((whatt & MODE_DEL) && !del_denyid(chptr, arg))))
+            break;
+
+          /* This stuff can go back in when all servers understand +e 
+           * with the pbufw_new nonsense removed -Dianora
+           */
+
+          /*
+          *mbufw++ = plus;
+          *mbufw++ = 'e';
+          strcpy(pbufw, arg);
+          pbufw += strlen(pbufw);
+          *pbufw++ = ' ';
+          */
+          len += tmp + 1;
+          opcnt++;
+
+          *mbufw_newer++ = plus;
+          *mbufw_newer++ = 'd';
+          strcpy(pbufw_newer, arg);
+          pbufw_newer += strlen(pbufw_newer);
+          *pbufw_newer++ = ' ';
+
+          break;
+
+        case 'b':
+          if (whatt == MODE_QUERY || parc-- <= 0)
+            {
+              if (!MyClient(sptr))
+                break;
+
+              if (errsent(SM_ERR_RPL_B, &errors_sent))
+                break;
+#ifdef BAN_INFO
+              for (lp = chptr->banlist; lp; lp = lp->next)
+                sendto_one(cptr, form_str(RPL_BANLIST),
+                           me.name, cptr->name,
+                           chptr->chname,
+                           lp->value.banptr->banstr,
+                           lp->value.banptr->who,
+                           lp->value.banptr->when);
+#else 
+              for (lp = chptr->banlist; lp; lp = lp->next)
+                sendto_one(cptr, form_str(RPL_BANLIST),
+                           me.name, cptr->name,
+                           chptr->chname,
+                           lp->value.cp);
+#endif
+              sendto_one(sptr, form_str(RPL_ENDOFBANLIST),
+                         me.name, sptr->name, 
+                         chptr->chname);
+              break;
+            }
+
+          if(MyClient(sptr))
+            chptr->keep_their_modes = YES;
+          else if(!chptr->keep_their_modes)
+            {
+              parc--;
+              parv++;
+              break;
+            }
+
+          arg = check_string(*parv++);
+
+          if (MyClient(sptr) && opcnt >= MAXMODEPARAMS)
+            break;
+
+          if (!isok)
+            {
+              if (!errsent(SM_ERR_NOOPS, &errors_sent) && MyClient(sptr))
+                sendto_one(sptr, form_str(ERR_CHANOPRIVSNEEDED),
+                           me.name, sptr->name, 
+                           chptr->chname);
+              break;
+            }
+
+
+          /* Ignore colon at beginning of ban string.
+           * Unfortunately, I can't ignore all such strings,
+           * because otherwise the channel could get desynced.
+           * I can at least, stop local clients from placing a ban
+           * with a leading colon.
+           *
+           * Roger uses check_string() combined with an earlier test
+           * in his TS4 code. The problem is, this means on a mixed net
+           * one can't =remove= a colon prefixed ban if set from
+           * an older server.
+           * His code is more efficient though ;-/ Perhaps
+           * when we've all upgraded this code can be moved up.
+           *
+           * -Dianora
+           */
+
+          /* user-friendly ban mask generation, taken
+          ** from Undernet's ircd  -orabidoo
+          */
+          if (MyClient(sptr))
+            {
+              if( (*arg == ':') && (whatt & MODE_ADD) )
+                {
+                  parc--;
+                  parv++;
+                  break;
+                }
+              arg = collapse(pretty_mask(arg));
+            }
+
+          tmp = strlen(arg);
+          if (len + tmp + 2 >= MODEBUFLEN)
+            break;
+
+          if (!(((whatt & MODE_ADD) && !add_banid(sptr, chptr, arg)) ||
+                ((whatt & MODE_DEL) && !del_banid(chptr, arg))))
+            break;
+
+          *mbufw++ = plus;
+          *mbufw++ = 'b';
+          strcpy(pbufw, arg);
+          pbufw += strlen(pbufw);
+          *pbufw++ = ' ';
+          len += tmp + 1;
+          opcnt++;
+
+          break;
+
+        case 'l':
+          if (whatt == MODE_QUERY)
+            break;
+          if (!isok || limitset++)
+            {
+              if (whatt == MODE_ADD && parc-- > 0)
+                parv++;
+              break;
+            }
+
+          if (whatt == MODE_ADD)
+            {
+              if (parc-- <= 0)
+                {
+                  if (MyClient(sptr))
+                    sendto_one(sptr, form_str(ERR_NEEDMOREPARAMS),
+                               me.name, sptr->name, "MODE +l");
+                  break;
+                }
+              
+              arg = check_string(*parv++);
+              /*              if (MyClient(sptr) && opcnt >= MAXMODEPARAMS)
+                break; */
+              if ((nusers = atoi(arg)) <= 0)
+                break;
+              ircsprintf(numeric, "%d", nusers);
+              if ((tmpc = strchr(numeric, ' ')))
+                *tmpc = '\0';
+              arg = numeric;
+
+              tmp = strlen(arg);
+              if (len + tmp + 2 >= MODEBUFLEN)
+                break;
+
+              chptr->mode.limit = nusers;
+              chptr->mode.mode |= MODE_LIMIT;
+
+              *mbufw++ = '+';
+              *mbufw++ = 'l';
+              strcpy(pbufw, arg);
+              pbufw += strlen(pbufw);
+              *pbufw++ = ' ';
+              len += tmp + 1;
+              /*              opcnt++;*/
+            }
+          else
+            {
+              chptr->mode.limit = 0;
+              chptr->mode.mode &= ~MODE_LIMIT;
+              *mbufw++ = '-';
+              *mbufw++ = 'l';
+            }
+
+          break;
+
+          /* Traditionally, these are handled separately
+           * but I decided to combine them all into this one case
+           * statement keeping it all sane
+           *
+           * The disadvantage is a lot more code duplicated ;-/
+           *
+           * -Dianora
+           */
+
+        case 'i' :
+          if (whatt == MODE_QUERY)      /* shouldn't happen. */
+            break;
+          if (!isok)
+            {
+              if (MyClient(sptr) && !errsent(SM_ERR_NOOPS, &errors_sent))
+                sendto_one(sptr, form_str(ERR_CHANOPRIVSNEEDED), me.name, 
+                           sptr->name, chptr->chname);
+              break;
+            }
+
+          if(MyClient(sptr))
+            {
+              if(done_i)
+                break;
+              else
+                done_i = YES;
+
+              /*              if ( opcnt >= MAXMODEPARAMS)
+                break; */
+            }
+
+          if(whatt == MODE_ADD)
+            {
+              if (len + 2 >= MODEBUFLEN)
+                break;
+#ifdef OLD_NON_RED
+              if(!(chptr->mode.mode & MODE_INVITEONLY))
+#endif
+                {
+                  chptr->mode.mode |= MODE_INVITEONLY;
+                  *mbufw++ = '+';
+                  *mbufw++ = 'i';
+                  len += 2;
+                  /*              opcnt++; */
+                }
+            }
+          else
+            {
+              if (len + 2 >= MODEBUFLEN)
+                break;
+
+              while ( (lp = chptr->invites) )
+                del_invite(lp->value.cptr, chptr);
+
+#ifdef OLD_NON_RED
+              if(chptr->mode.mode & MODE_INVITEONLY)
+#endif
+                {
+                  chptr->mode.mode &= ~MODE_INVITEONLY;
+                  *mbufw++ = '-';
+                  *mbufw++ = 'i';
+                  len += 2;
+                  /*              opcnt++; */
+                }
+            }
+          break;
+
+          /* Un documented for now , I have no idea how this got here ;-) */
+#ifdef JUPE_CHANNEL
+        case 'j':
+          if(MyConnect(sptr) && IsAnOper(sptr))
+            {
+              if (whatt == MODE_ADD)
+                {
+                  chptr->mode.mode |= MODE_JUPED;
+                  sendto_realops("%s!%s@%s juping locally Channel %s",
+                                 sptr->name, sptr->username,
+                                 sptr->host, chptr->chname);
+                }
+              else if(whatt == MODE_DEL)
+                {
+                  chptr->mode.mode &= ~MODE_JUPED;
+                  sendto_realops("%s!%s@%s unjuping locally Channel %s",
+                                 sptr->name, sptr->username,
+                                 sptr->host, chptr->chname);
+                }
+            }
+          break;
+#endif
+
+        case 'm' :
+          if (!isok)
+            {
+              if (MyClient(sptr) && !errsent(SM_ERR_NOOPS, &errors_sent))
+                sendto_one(sptr, form_str(ERR_CHANOPRIVSNEEDED), me.name, 
+                           sptr->name, chptr->chname);
+              break;
+            }
+
+          if(MyClient(sptr))
+            {
+              if(done_m)
+                break;
+              else
+                done_m = YES;
+
+              /*              if ( opcnt >= MAXMODEPARAMS)
+                              break; */
+            }
+
+          if(whatt == MODE_ADD)
+            {
+              if (len + 2 >= MODEBUFLEN)
+                break;
+#ifdef OLD_NON_RED
+              if(!(chptr->mode.mode & MODE_MODERATED))
+#endif
+                {
+                  chptr->mode.mode |= MODE_MODERATED;
+                  *mbufw++ = '+';
+                  *mbufw++ = 'm';
+                  len += 2;
+                  /*              opcnt++; */
+                }
+            }
+          else
+            {
+              if (len + 2 >= MODEBUFLEN)
+                break;
+#ifdef OLD_NON_RED
+              if(chptr->mode.mode & MODE_MODERATED)
+#endif
+                {
+                  chptr->mode.mode &= ~MODE_MODERATED;
+                  *mbufw++ = '-';
+                  *mbufw++ = 'm';
+                  len += 2;
+                  /*              opcnt++; */
+                }
+            }
+          break;
+
+        case 'n' :
+          if (!isok)
+            {
+              if (MyClient(sptr) && !errsent(SM_ERR_NOOPS, &errors_sent))
+                sendto_one(sptr, form_str(ERR_CHANOPRIVSNEEDED), me.name, 
+                           sptr->name, chptr->chname);
+              break;
+            }
+
+          if(MyClient(sptr))
+            {
+              if(done_n)
+                break;
+              else
+                done_n = YES;
+
+              /*              if ( opcnt >= MAXMODEPARAMS)
+                              break; */
+            }
+
+          if(whatt == MODE_ADD)
+            {
+              if (len + 2 >= MODEBUFLEN)
+                break;
+#ifdef OLD_NON_RED
+              if(!(chptr->mode.mode & MODE_NOPRIVMSGS))
+#endif
+                {
+                  chptr->mode.mode |= MODE_NOPRIVMSGS;
+                  *mbufw++ = '+';
+                  *mbufw++ = 'n';
+                  len += 2;
+                  /*              opcnt++; */
+                }
+            }
+          else
+            {
+              if (len + 2 >= MODEBUFLEN)
+                break;
+#ifdef OLD_NON_RED
+              if(chptr->mode.mode & MODE_NOPRIVMSGS)
+#endif
+                {
+                  chptr->mode.mode &= ~MODE_NOPRIVMSGS;
+                  *mbufw++ = '-';
+                  *mbufw++ = 'n';
+                  len += 2;
+                  /*              opcnt++; */
+                }
+            }
+          break;
+
+        case 'p' :
+          if (!isok)
+            {
+              if (MyClient(sptr) && !errsent(SM_ERR_NOOPS, &errors_sent))
+                sendto_one(sptr, form_str(ERR_CHANOPRIVSNEEDED), me.name, 
+                           sptr->name, chptr->chname);
+              break;
+            }
+
+          if(MyClient(sptr))
+            {
+#ifdef OLD_P_S
+              if(done_s_or_p)
+                break;
+              else
+                done_s_or_p = YES;
+#else
+              if(done_p)
+                break;
+              else
+                done_p = YES;
+#endif
+              /*              if ( opcnt >= MAXMODEPARAMS)
+                break; */
+            }
+
+          if(whatt == MODE_ADD)
+            {
+              if (len + 2 >= MODEBUFLEN)
+                break;
+#ifdef OLD_P_S
+              if(chptr->mode.mode & MODE_SECRET)
+                {
+                  if (len + 2 >= MODEBUFLEN)
+                    break;
+                  *mbufw++ = '-';
+                  *mbufw++ = 's';
+                  len += 2;
+                  chptr->mode.mode &= ~MODE_SECRET;
+                }
+#endif
+#ifdef OLD_NON_RED
+              if(!(chptr->mode.mode & MODE_PRIVATE))
+#endif
+                {
+                  chptr->mode.mode |= MODE_PRIVATE;
+                  *mbufw++ = '+';
+                  *mbufw++ = 'p';
+                  len += 2;
+                  /*              opcnt++; */
+                }
+            }
+          else
+            {
+              if (len + 2 >= MODEBUFLEN)
+                break;
+#ifdef OLD_NON_RED
+              if(chptr->mode.mode & MODE_PRIVATE)
+#endif
+                {
+                  chptr->mode.mode &= ~MODE_PRIVATE;
+                  *mbufw++ = '-';
+                  *mbufw++ = 'p';
+                  len += 2;
+                  /*              opcnt++; */
+                }
+            }
+          break;
+
+        case 's' :
+          if (!isok)
+            {
+              if (MyClient(sptr) && !errsent(SM_ERR_NOOPS, &errors_sent))
+                sendto_one(sptr, form_str(ERR_CHANOPRIVSNEEDED), me.name, 
+                           sptr->name, chptr->chname);
+              break;
+            }
+
+          /* ickity poo, traditional +p-s nonsense */
+
+          if(MyClient(sptr))
+            {
+#ifdef OLD_P_S
+              if(done_s_or_p)
+                break;
+              else
+                done_s_or_p = YES;
+#else
+              if(done_s)
+                break;
+              else
+                done_s = YES;
+#endif
+              /*              if ( opcnt >= MAXMODEPARAMS)
+                              break; */
+            }
+
+          if(whatt == MODE_ADD)
+            {
+              if (len + 2 >= MODEBUFLEN)
+                break;
+#ifdef OLD_P_S
+              if(chptr->mode.mode & MODE_PRIVATE)
+                {
+                  if (len + 2 >= MODEBUFLEN)
+                    break;
+                  *mbufw++ = '-';
+                  *mbufw++ = 'p';
+                  len += 2;
+                  chptr->mode.mode &= ~MODE_PRIVATE;
+                }
+#endif
+#ifdef OLD_NON_RED
+              if(!(chptr->mode.mode & MODE_SECRET))
+#endif
+                {
+                  chptr->mode.mode |= MODE_SECRET;
+                  *mbufw++ = '+';
+                  *mbufw++ = 's';
+                  len += 2;
+                  /*              opcnt++; */
+                }
+            }
+          else
+            {
+              if (len + 2 >= MODEBUFLEN)
+                break;
+#ifdef OLD_NON_RED
+              if(chptr->mode.mode & MODE_SECRET)
+#endif
+                {
+                  chptr->mode.mode &= ~MODE_SECRET;
+                  *mbufw++ = '-';
+                  *mbufw++ = 's';
+                  len += 2;
+                  /*              opcnt++; */
+                }
+            }
+          break;
+
+        case 't' :
+          if (!isok)
+            {
+              if (MyClient(sptr) && !errsent(SM_ERR_NOOPS, &errors_sent))
+                sendto_one(sptr, form_str(ERR_CHANOPRIVSNEEDED), me.name, 
+                           sptr->name, chptr->chname);
+              break;
+            }
+
+          if(MyClient(sptr))
+            {
+              if(done_t)
+                break;
+              else
+                done_t = YES;
+
+              /*              if ( opcnt >= MAXMODEPARAMS)
+                              break; */
+            }
+
+          if(whatt == MODE_ADD)
+            {
+              if (len + 2 >= MODEBUFLEN)
+                break;
+#ifdef OLD_NON_RED
+              if(!(chptr->mode.mode & MODE_TOPICLIMIT))
+#endif
+                {
+                  chptr->mode.mode |= MODE_TOPICLIMIT;
+                  *mbufw++ = '+';
+                  *mbufw++ = 't';
+                  len += 2;
+                  /*              opcnt++; */
+                }
+            }
+          else
+            {
+              if (len + 2 >= MODEBUFLEN)
+                break;
+#ifdef OLD_NON_RED
+              if(chptr->mode.mode & MODE_TOPICLIMIT)
+#endif
+                {
+                  chptr->mode.mode &= ~MODE_TOPICLIMIT;
+                  *mbufw++ = '-';
+                  *mbufw++ = 't';
+                  len += 2;
+                  /*              opcnt++; */
+                }
+            }
+          break;
+
+        default:
+          if (whatt == MODE_QUERY)
+            break;
+
+          /* only one "UNKNOWNMODE" per mode... we don't want
+          ** to generate a storm, even if it's just to a 
+          ** local client  -orabidoo
+          */
+          if (MyClient(sptr) && !errsent(SM_ERR_UNKNOWN, &errors_sent))
+            sendto_one(sptr, form_str(ERR_UNKNOWNMODE), me.name, sptr->name, c);
+          break;
+        }
+    }
+
+  /*
+  ** WHEW!!  now all that's left to do is put the various bufs
+  ** together and send it along.
+  */
+
+  *mbufw = *mbuf2w = *pbufw = *pbuf2w = *mbufw_new = *pbufw_new = 
+  *mbufw_newer = *pbufw_newer = '\0';
+
+  collapse_signs(modebuf);
+/*  collapse_signs(modebuf2); */
+  collapse_signs(modebuf_new);
+  collapse_signs(modebuf_newer);
+
+  if(*modebuf)
+    {
+		  /* 10/25/2000, shadow */
+			if (hack_cloaked_ops == 1) {
+				nameToSend = me.name;
+			} else {
+				nameToSend = sptr->name;
+			}
+
+      sendto_channel_butserv(chptr, sptr, ":%s MODE %s %s %s", 
+				nameToSend, chptr->chname,
+                           modebuf, parabuf);
+#ifdef SHADEBUG      
+			sendto_one(sptr, ":%s NOTICE %s :modebuf - nts(%s) mb(%s) pb(%s)", me.name, 
+                           sptr->name, nameToSend, modebuf, parabuf);
+#endif SHADEBUG
+
+      sendto_match_servs(chptr, cptr, ":%s MODE %s %s %s",
+				nameToSend, chptr->chname,
+                         modebuf, parabuf);
+	}
+
+  if(*modebuf_new)
+    {
+      sendto_channel_butserv(chptr, sptr, ":%s MODE %s %s %s", 
+				sptr->name, chptr->chname,
+                             modebuf_new, parabuf_new);
+
+#ifdef SHADEBUG      
+			sendto_one(sptr, ":%s NOTICE %s :modebuf_new - mb(%s) pb(%s)", me.name, 
+                           sptr->name, modebuf_new, parabuf_new);
+#endif SHADEBUG
+
+      sendto_match_cap_servs(chptr, cptr, CAP_EX, ":%s MODE %s %s %s",
+				sptr->name, chptr->chname,
+                             modebuf_new, parabuf_new);
+    }
+  if(*modebuf_newer)
+    {
+      sendto_channel_butserv(chptr, sptr, ":%s MODE %s %s %s",
+				sptr->name, chptr->chname,
+                             modebuf_newer, parabuf_newer);
+#ifdef SHADEBUG      
+			sendto_one(sptr, ":%s NOTICE %s :modebuf_newer - mb(%s) pb(%s)", me.name, 
+                           sptr->name, modebuf_newer, parabuf_newer);
+#endif SHADEBUG
+
+      sendto_match_cap_servs(chptr, cptr, CAP_DE, ":%s MODE %s %s %s",
+				sptr->name, chptr->chname,
+                             modebuf_newer, parabuf_newer);
+    }
+
+                     
+  return;
+}
+
+static  int     can_join(struct Client *sptr, struct Channel *chptr, char *key, int *flags)
+{
+  Link  *lp;
+  int ban_or_exception;
+
+   /* 10/20/2000 - shadow - oper godness again */
+   if (IsAnOper(sptr))
+     return 0;
+   
+#ifdef JUPE_CHANNEL
+  if( chptr->mode.mode & MODE_JUPED )
+    {
+      sendto_ops_flags(FLAGS_SPY,
+             "User %s (%s@%s) is attemping to join locally juped channel %s",
+                     sptr->name,
+                     sptr->username, sptr->host,chptr->chname);
+      return (ERR_BADCHANNAME);
+    }
+#endif
+
+  if ( (ban_or_exception = is_banned(sptr, chptr)) == CHFL_BAN)
+    return (ERR_BANNEDFROMCHAN);
+  else
+    *flags |= ban_or_exception; /* Mark this client as "charmed" */
+
+  if (chptr->mode.mode & MODE_INVITEONLY)
+    {
+      for (lp = sptr->user->invited; lp; lp = lp->next)
+        if (lp->value.chptr == chptr)
+          break;
+      if (!lp)
+        return (ERR_INVITEONLYCHAN);
+    }
+
+  if (*chptr->mode.key && (BadPtr(key) || irccmp(chptr->mode.key, key)))
+    return (ERR_BADCHANNELKEY);
+
+  if (chptr->mode.limit && chptr->users >= chptr->mode.limit)
+    return (ERR_CHANNELISFULL);
+
+  return 0;
+}
+
+/*
+ * check_channel_name - check channel name for invalid characters
+ * return true (1) if name ok, false (0) otherwise
+ */
+int check_channel_name(const char* name)
+{
+  assert(0 != name);
+  
+  for ( ; *name; ++name) {
+    if (!IsChanChar(*name))
+      return 0;
+  }
+  return 1;
+}
+
+/*
+**  Get Channel block for chname (and allocate a new channel
+**  block, if it didn't exist before).
+*/
+static struct Channel* get_channel(struct Client *cptr, char *chname, int flag)
+{
+  struct Channel *chptr;
+  int   len;
+
+  if (BadPtr(chname))
+    return NULL;
+
+  len = strlen(chname);
+  if (MyClient(cptr) && len > CHANNELLEN)
+    {
+      len = CHANNELLEN;
+      *(chname + CHANNELLEN) = '\0';
+    }
+  if ((chptr = hash_find_channel(chname, NULL)))
+    return (chptr);
+
+  /*
+   * If a channel is created during a split make sure its marked
+   * as created locally 
+   * Also make sure a created channel has =some= timestamp
+   * even if it get over-ruled later on. Lets quash the possibility
+   * an ircd coder accidentally blasting TS on channels. (grrrrr -db)
+   *
+   * Actually, it might be fun to make the TS some impossibly huge value (-db)
+   */
+
+  if (flag == CREATE)
+    {
+      chptr = (struct Channel*) MyMalloc(sizeof(struct Channel) + len + 1);
+      memset(chptr, 0, sizeof(struct Channel));
+      /*
+       * NOTE: strcpy ok here, we have allocated strlen + 1
+       */
+      strcpy(chptr->chname, chname);
+      if (channel)
+        channel->prevch = chptr;
+      chptr->prevch = NULL;
+      chptr->nextch = channel;
+      channel = chptr;
+      if (Count.myserver == 0)
+        chptr->locally_created = YES;
+      chptr->keep_their_modes = YES;
+      chptr->channelts = CurrentTime;     /* doesn't hurt to set it here */
+      add_to_channel_hash_table(chname, chptr);
+      Count.chan++;
+    }
+  return chptr;
+}
+
+static  void    add_invite(struct Client *cptr,struct Channel *chptr)
+{
+  Link  *inv, **tmp;
+
+  del_invite(cptr, chptr);
+  /*
+   * delete last link in chain if the list is max length
+   */
+  if (list_length(cptr->user->invited) >= MAXCHANNELSPERUSER)
+    {
+      /*                This forgets the channel side of invitation     -Vesa
+                        inv = cptr->user->invited;
+                        cptr->user->invited = inv->next;
+                        free_link(inv);
+*/
+      del_invite(cptr, cptr->user->invited->value.chptr);
+ 
+    }
+  /*
+   * add client to channel invite list
+   */
+  inv = make_link();
+  inv->value.cptr = cptr;
+  inv->next = chptr->invites;
+  chptr->invites = inv;
+  /*
+   * add channel to the end of the client invite list
+   */
+  for (tmp = &(cptr->user->invited); *tmp; tmp = &((*tmp)->next))
+    ;
+  inv = make_link();
+  inv->value.chptr = chptr;
+  inv->next = NULL;
+  (*tmp) = inv;
+}
+
+/*
+ * Delete Invite block from channel invite list and client invite list
+ */
+void    del_invite(struct Client *cptr,struct Channel *chptr)
+{
+  Link  **inv, *tmp;
+
+  for (inv = &(chptr->invites); (tmp = *inv); inv = &tmp->next)
+    if (tmp->value.cptr == cptr)
+      {
+        *inv = tmp->next;
+        free_link(tmp);
+        break;
+      }
+
+  for (inv = &(cptr->user->invited); (tmp = *inv); inv = &tmp->next)
+    if (tmp->value.chptr == chptr)
+      {
+        *inv = tmp->next;
+        free_link(tmp);
+        break;
+      }
+}
+
+/*
+**  Subtract one user from channel (and free channel
+**  block, if channel became empty).
+*/
+static  void    sub1_from_channel(struct Channel *chptr)
+{
+  Link *tmp;
+
+  if (--chptr->users <= 0)
+    {
+      chptr->users = 0; /* if chptr->users < 0, make sure it sticks at 0
+                         * It should never happen but...
+                         */
+
+#if defined(PRESERVE_CHANNEL_ON_SPLIT) || defined(NO_JOIN_ON_SPLIT)
+      if(server_was_split && (chptr->chname[0] != '&'))
+        {
+          /*
+           * Now, find all invite links from channel structure
+           */
+          /* The idea here is, not to "forget" the channel mode
+           * the ban list, exception lists, and not to release
+           * the channel at this time.
+           * The invite list should be forgotten now, as well
+           * as the flooder lists
+           * -db
+           */
+
+          while ((tmp = chptr->invites))
+            del_invite(tmp->value.cptr, chptr);
+
+#ifdef FLUD
+          free_fluders(NULL, chptr);
+#endif
+          /* flag the channel as split */
+          chptr->mode.mode |= MODE_SPLIT;
+
+          /* Add to double linked empty channel list */
+          if(empty_channel_list)
+            empty_channel_list->last_empty_channel = chptr;
+          chptr->last_empty_channel = (struct Channel *)NULL;
+          chptr->next_empty_channel = empty_channel_list;
+          empty_channel_list = chptr;
+        }
+      else
+#endif
+#ifdef JUPE_CHANNEL
+        if( chptr->mode.mode & MODE_JUPED )
+          {
+            while ((tmp = chptr->invites))
+              del_invite(tmp->value.cptr, chptr);
+            
+#ifdef FLUD
+            free_fluders(NULL, chptr);
+#endif
+          }
+        else
+#endif
+       {
+          /*
+           * Now, find all invite links from channel structure
+           */
+          while ((tmp = chptr->invites))
+            del_invite(tmp->value.cptr, chptr);
+
+	  /* free all bans/exceptions/denies */
+	  free_bans_exceptions_denies( chptr );
+
+          if (chptr->prevch)
+            chptr->prevch->nextch = chptr->nextch;
+          else
+            channel = chptr->nextch;
+          if (chptr->nextch)
+            chptr->nextch->prevch = chptr->prevch;
+
+#ifdef FLUD
+          free_fluders(NULL, chptr);
+#endif
+          del_from_channel_hash_table(chptr->chname, chptr);
+          MyFree((char*) chptr);
+          Count.chan--;
+        }
+    }
+}
+
+/*
+ * clear_bans_exceptions_denies
+ *
+ * I could have re-written del_banid/del_exceptid to do this
+ *
+ * still need a bit of cleanup on the MODE -b stuff...
+ * -Dianora
+ */
+
+static void clear_bans_exceptions_denies(struct Client *sptr, struct Channel *chptr)
+{
+  static char modebuf[MODEBUFLEN];
+  register Link *ban;
+  char *b1,*b2,*b3,*b4;
+  char *mp;
+
+  b1="";
+  b2="";
+  b3="";
+  b4="";
+
+  mp= modebuf;
+  *mp = '\0';
+
+  for(ban = chptr->banlist; ban; ban = ban->next)
+    {
+      if(!*b1)
+        {
+          b1 = BANSTR(ban);
+          *mp++ = '-';
+          *mp++ = 'b';
+          *mp = '\0';
+        }
+      else if(!*b2)
+        {
+          b2 = BANSTR(ban);
+          *mp++ = 'b';
+          *mp = '\0';
+        }
+      else if(!*b3)
+        {
+          b3 = BANSTR(ban);
+          *mp++ = 'b';
+          *mp = '\0';
+        }
+      else if(!*b4)
+        {
+          b4 = BANSTR(ban);
+          *mp++ = 'b';
+          *mp = '\0';
+
+          sendto_channel_butserv(chptr, &me,
+                                 ":%s MODE %s %s %s %s %s %s",
+                                 sptr->name,chptr->chname,modebuf,b1,b2,b3,b4);
+          b1="";
+          b2="";
+          b3="";
+          b4="";
+
+          mp = modebuf;
+          *mp = '\0';
+        }
+    }
+
+  if(*modebuf)
+    sendto_channel_butserv(chptr, &me,
+                           ":%s MODE %s %s %s %s %s %s",
+                           sptr->name,chptr->chname,modebuf,b1,b2,b3,b4);
+  b1="";
+  b2="";
+  b3="";
+  b4="";
+
+  mp= modebuf;
+  *mp = '\0';
+
+  for(ban = chptr->exceptlist; ban; ban = ban->next)
+    {
+      if(!*b1)
+        {
+          b1 = BANSTR(ban);
+          *mp++ = '-';
+          *mp++ = 'e';
+          *mp = '\0';
+        }
+      else if(!*b2)
+        {
+          b2 = BANSTR(ban);
+          *mp++ = 'e';
+          *mp = '\0';
+        }
+      else if(!*b3)
+        {
+          b3 = BANSTR(ban);
+          *mp++ = 'e';
+          *mp = '\0';
+        }
+      else if(!*b4)
+        {
+          b4 = BANSTR(ban);
+          *mp++ = 'e';
+          *mp = '\0';
+
+          sendto_channel_butserv(chptr, &me,
+                                 ":%s MODE %s %s %s %s %s %s",
+                                 sptr->name,chptr->chname,modebuf,b1,b2,b3,b4);
+          b1="";
+          b2="";
+          b3="";
+          b4="";
+          mp = modebuf;
+          *mp = '\0';
+        }
+    }
+
+  if(*modebuf)
+    sendto_channel_butserv(chptr, &me,
+                           ":%s MODE %s %s %s %s %s %s",
+                           sptr->name,chptr->chname,modebuf,b1,b2,b3,b4);
+
+  b1="";
+  b2="";
+  b3="";
+  b4="";
+
+  mp= modebuf;
+  *mp = '\0';
+
+  for(ban = chptr->denylist; ban; ban = ban->next)
+    {
+      if(!*b1)
+        {
+          b1 = BANSTR(ban);
+          *mp++ = '-';
+          *mp++ = 'd';
+          *mp = '\0';
+        }
+      else if(!*b2)
+        {
+          b2 = BANSTR(ban);
+          *mp++ = 'd';
+          *mp = '\0';
+        }
+      else if(!*b3)
+        {
+          b3 = BANSTR(ban);
+          *mp++ = 'd';
+          *mp = '\0';
+        }
+      else if(!*b4)
+        {
+          b4 = BANSTR(ban);
+          *mp++ = 'd';
+          *mp = '\0';
+
+          sendto_channel_butserv(chptr, &me,
+                                 ":%s MODE %s %s %s %s %s %s",
+                                 sptr->name,chptr->chname,modebuf,b1,b2,b3,b4);
+          b1="";
+          b2="";
+          b3="";
+          b4="";
+          mp = modebuf;
+          *mp = '\0';
+        }
+    }
+
+  if(*modebuf)
+    sendto_channel_butserv(chptr, &me,
+                           ":%s MODE %s %s %s %s %s %s",
+                           sptr->name,chptr->chname,modebuf,b1,b2,b3,b4);
+
+
+  /* free all bans/exceptions/denies */
+  free_bans_exceptions_denies(chptr);
+}
+
+/*
+ * free_bans_exceptions_denies
+ *
+ * inputs	- pointer to channel structure
+ * output	- none
+ * side effects	- all bans/exceptions denies are freed for channel
+ */
+
+static void free_bans_exceptions_denies(struct Channel *chptr)
+{
+  Link *ban;
+  Link *next_ban;
+
+  for(ban = chptr->banlist; ban; ban = next_ban)
+    {
+      next_ban = ban->next;
+#ifdef BAN_INFO
+      MyFree(ban->value.banptr->banstr);
+      MyFree(ban->value.banptr->who);
+      MyFree(ban->value.banptr);
+#else
+      MyFree(ban->value.cp);
+#endif
+      free_link(ban);
+    }
+
+  for(ban = chptr->exceptlist; ban; ban = next_ban)
+    {
+      next_ban = ban->next;
+#ifdef BAN_INFO
+      MyFree(ban->value.banptr->banstr);
+      MyFree(ban->value.banptr->who);
+      MyFree(ban->value.banptr);
+#else
+      MyFree(ban->value.cp);
+#endif
+      free_link(ban);
+    }
+
+  for(ban = chptr->denylist; ban; ban = next_ban)
+    {
+      next_ban = ban->next;
+#ifdef BAN_INFO
+      MyFree(ban->value.banptr->banstr);
+      MyFree(ban->value.banptr->who);
+      MyFree(ban->value.banptr);
+#else
+      MyFree(ban->value.cp);
+#endif
+      free_link(ban);
+    }
+
+  chptr->banlist = chptr->exceptlist = chptr->denylist = NULL;
+  chptr->num_bed = 0;
+}
+
+
+#ifdef NEED_SPLITCODE
+
+/*
+ * check_still_split()
+ *
+ * inputs       -NONE
+ * output       -NONE
+ * side effects -
+ * Check to see if the server split timer has expired, if so
+ * check to see if there are now a decent number of servers connected
+ * and users present, so I can consider this split over.
+ *
+ * -Dianora
+ */
+
+static void check_still_split()
+{
+  if((server_split_time + SPLITDELAY) < CurrentTime)
+    {
+      if((Count.server >= SPLITNUM) &&
+#ifdef SPLIT_PONG
+         (got_server_pong == YES) &&
+#endif
+         (Count.total >= SPLITUSERS))
+        {
+          /* server hasn't been split for a while.
+           * -Dianora
+           */
+          server_was_split = NO;
+          sendto_ops("Net Rejoined, split-mode deactivated");
+          cold_start = NO;
+#if defined(PRESERVE_CHANNEL_ON_SPLIT) || defined(NO_JOIN_ON_SPLIT)
+          remove_empty_channels();
+#endif
+        }
+      else
+        {
+          server_split_time = CurrentTime; /* still split */
+          server_was_split = YES;
+        }
+    }
+}
+#endif
+
+#if defined(PRESERVE_CHANNEL_ON_SPLIT) || defined(NO_JOIN_ON_SPLIT)
+/*
+ * remove_empty_channels
+ *
+ * inputs       - none
+ * output       - none
+ * side effects - remove all channels on empty_channel_list that have
+ * 
+ * Any channel struct on this link list, is here because it had
+ * no members, hence normally would not exist through a split.
+ * If after the split is over, there are any channels left in this
+ * list, they must be removed. Whenever a channel gains a member
+ * whether locally or from a remote SJOIN it is removed from this list.
+ */
+
+void remove_empty_channels()
+{
+  Link *tmp;
+  Link  *obtmp;
+  struct Channel *next_empty_channel;
+
+  for(;empty_channel_list;
+      empty_channel_list = next_empty_channel )
+    {
+      next_empty_channel = empty_channel_list->next_empty_channel;
+
+      if(empty_channel_list->users)             /* sanity test */
+        {
+	  /* This is an oddity, rather than an out and out error
+	   * if this happens, a client managed to join the channel
+	   * making it non zero users, and I didn't notice. That means
+	   * strictly speaking its an error. However, if this entry is
+	   * ignored, its a non fatal one.
+	   */
+#if 0
+          sendto_ops("non zero user count in remove_empty_channels");
+          sendto_ops("Please report to the hybrid team! ircd-hybrid@the-project.org");
+#endif
+          empty_channel_list->next_empty_channel = (struct Channel *)NULL;
+          empty_channel_list->last_empty_channel = (struct Channel *)NULL;
+          continue;
+        }
+
+      /*
+       * Now, find all invite links from channel structure
+       */
+      while ((tmp = empty_channel_list->invites))
+        del_invite(tmp->value.cptr, empty_channel_list);
+      
+      tmp = empty_channel_list->banlist;
+      while (tmp)
+        {
+          obtmp = tmp;
+          tmp = tmp->next;
+#ifdef BAN_INFO
+          MyFree(obtmp->value.banptr->banstr);
+          MyFree(obtmp->value.banptr->who);
+          MyFree(obtmp->value.banptr);
+#else
+          MyFree(obtmp->value.cp);
+#endif
+          free_link(obtmp);
+        }
+
+      tmp = empty_channel_list->exceptlist;
+      while (tmp)
+        {
+          obtmp = tmp;
+          tmp = tmp->next;
+#ifdef BAN_INFO
+          MyFree(obtmp->value.banptr->banstr);
+          MyFree(obtmp->value.banptr->who);
+          MyFree(obtmp->value.banptr);
+#else
+          MyFree(obtmp->value.cp);
+#endif
+          free_link(obtmp);
+        }
+      empty_channel_list->banlist = empty_channel_list->exceptlist = NULL;
+      
+      if (empty_channel_list->prevch)
+        empty_channel_list->prevch->nextch = empty_channel_list->nextch;
+      else
+        channel = empty_channel_list->nextch;
+      if (empty_channel_list->nextch)
+        empty_channel_list->nextch->prevch = empty_channel_list->prevch;
+      
+#ifdef FLUD
+      free_fluders(NULL, empty_channel_list);
+#endif
+      del_from_channel_hash_table(empty_channel_list->chname, 
+                                        empty_channel_list);
+      MyFree((char*) empty_channel_list);
+      Count.chan--;
+    }
+}
+#endif
+
+/*
+** m_join
+**      parv[0] = sender prefix
+**      parv[1] = channel
+**      parv[2] = channel password (key)
+*/
+int     m_join(struct Client *cptr,
+               struct Client *sptr,
+               int parc,
+               char *parv[])
+{
+  static char   jbuf[BUFSIZE];
+  Link  *lp;
+  struct Channel *chptr = NULL;
+  char  *name, *key = NULL;
+  int   i, flags = 0;
+#ifdef NO_CHANOPS_WHEN_SPLIT
+  int   allow_op=YES;
+#endif
+  char  *p = NULL, *p2 = NULL;
+#ifdef ANTI_SPAMBOT
+  int   successful_join_count = 0; /* Number of channels successfully joined */
+#endif
+  
+  if (!(sptr->user))
+    {
+      /* something is *fucked* - bail */
+      return 0;
+    }
+
+  if (parc < 2 || *parv[1] == '\0')
+    {
+      sendto_one(sptr, form_str(ERR_NEEDMOREPARAMS),
+                 me.name, parv[0], "JOIN");
+      return 0;
+    }
+
+
+#ifdef NEED_SPLITCODE
+
+  /* Check to see if the timer has timed out, and if so, see if
+   * there are a decent number of servers now connected 
+   * to consider any possible split over.
+   * -Dianora
+   */
+
+  if (server_was_split)
+    check_still_split();
+
+#endif
+
+  *jbuf = '\0';
+  /*
+  ** Rebuild list of channels joined to be the actual result of the
+  ** JOIN.  Note that "JOIN 0" is the destructive problem.
+  */
+  for (i = 0, name = strtoken(&p, parv[1], ","); name;
+       name = strtoken(&p, (char *)NULL, ","))
+    {
+      if (!check_channel_name(name))
+        {
+          sendto_one(sptr, form_str(ERR_BADCHANNAME),
+                       me.name, parv[0], (unsigned char*) name);
+          continue;
+        }
+      if (*name == '&' && !MyConnect(sptr))
+        continue;
+      if (*name == '0' && !atoi(name))
+        *jbuf = '\0';
+      else if (!IsChannelName(name))
+        {
+          if (MyClient(sptr))
+            sendto_one(sptr, form_str(ERR_NOSUCHCHANNEL),
+                       me.name, parv[0], name);
+          continue;
+        }
+
+
+#ifdef NO_JOIN_ON_SPLIT_SIMPLE
+      if (server_was_split && MyClient(sptr) && (*name != '&') &&
+          !IsAnOper(sptr))
+        {
+              sendto_one(sptr, form_str(ERR_UNAVAILRESOURCE),
+                         me.name, parv[0], name);
+              continue;
+        }
+#endif /* NO_JOIN_ON_SPLIT_SIMPLE */
+
+#if defined(PRESERVE_CHANNEL_ON_SPLIT) || defined(NO_JOIN_ON_SPLIT)
+      /* If from a cold start, there were never any channels
+       * joined, hence all of them must be considered off limits
+       * until this server joins the network
+       *
+       * cold_start is set to NO if SPLITDELAY is set to 0 in m_set()
+       */
+
+      if(cold_start && MyClient(sptr) && (*name != '&') &&
+         !IsAnOper(sptr))
+        {
+              sendto_one(sptr, form_str(ERR_UNAVAILRESOURCE),
+                         me.name, parv[0], name);
+              continue;
+        }
+#endif
+      if (*jbuf)
+        (void)strcat(jbuf, ",");
+      (void)strncat(jbuf, name, sizeof(jbuf) - i - 1);
+      i += strlen(name)+1;
+    }
+  /*    (void)strcpy(parv[1], jbuf);*/
+
+  p = NULL;
+  if (parv[2])
+    key = strtoken(&p2, parv[2], ",");
+  parv[2] = NULL;       /* for m_names call later, parv[parc] must == NULL */
+  for (name = strtoken(&p, jbuf, ","); name;
+       key = (key) ? strtoken(&p2, NULL, ",") : NULL,
+         name = strtoken(&p, NULL, ","))
+    {
+      /*
+      ** JOIN 0 sends out a part for all channels a user
+      ** has joined.
+      */
+      if (*name == '0' && !atoi(name))
+        {
+          if (sptr->user->channel == NULL)
+            continue;
+          while ((lp = sptr->user->channel))
+            {
+              chptr = lp->value.chptr;
+              sendto_channel_butserv(chptr, sptr, PartFmt,
+                                     parv[0], chptr->chname);
+              remove_user_from_channel(sptr, chptr, 0);
+            }
+
+#ifdef ANTI_SPAMBOT       /* Dianora */
+
+          if( MyConnect(sptr) && !IsAnOper(sptr) )
+            {
+              if(SPAMNUM && (sptr->join_leave_count >= SPAMNUM))
+                {
+                  sendto_ops_flags(FLAGS_BOTS,
+                                     "User %s (%s@%s) is a possible spambot",
+                                     sptr->name,
+                                     sptr->username, sptr->host);
+                  sptr->oper_warn_count_down = OPER_SPAM_COUNTDOWN;
+                }
+              else
+                {
+                  int t_delta;
+
+                  if( (t_delta = (CurrentTime - sptr->last_leave_time)) >
+                      JOIN_LEAVE_COUNT_EXPIRE_TIME)
+                    {
+                      int decrement_count;
+                      decrement_count = (t_delta/JOIN_LEAVE_COUNT_EXPIRE_TIME);
+
+                      if(decrement_count > sptr->join_leave_count)
+                        sptr->join_leave_count = 0;
+                      else
+                        sptr->join_leave_count -= decrement_count;
+                    }
+                  else
+                    {
+                      if((CurrentTime - (sptr->last_join_time)) < SPAMTIME)
+                        {
+                          /* oh, its a possible spambot */
+                          sptr->join_leave_count++;
+                        }
+                    }
+                  sptr->last_leave_time = CurrentTime;
+                }
+            }
+#endif
+          sendto_match_servs(NULL, cptr, ":%s JOIN 0", parv[0]);
+          continue;
+        }
+      
+      if (MyConnect(sptr))
+        {
+          /*
+          ** local client is first to enter previously nonexistent
+          ** channel so make them (rightfully) the Channel
+          ** Operator.
+          */
+           /*     flags = (ChannelExists(name)) ? 0 : CHFL_CHANOP; */
+
+          /* To save a redundant hash table lookup later on */
+           
+           if((chptr = hash_find_channel(name, NullChn)))
+             flags = 0;
+           else
+             flags = CHFL_CHANOP;
+
+           /* if its not a local channel, or isn't an oper
+            * and server has been split
+            */
+
+#ifdef NO_CHANOPS_WHEN_SPLIT
+          if((*name != '&') && !IsAnOper(sptr) && server_was_split)
+            {
+              allow_op = NO;
+
+              if(!IsRestricted(sptr) && (flags & CHFL_CHANOP))
+                sendto_one(sptr,":%s NOTICE %s :*** Notice -- Due to a network split, you can not obtain channel operator status in a new channel at this time.",
+                       me.name,
+                       sptr->name);
+            }
+#endif
+
+          if ((sptr->user->joined >= MAXCHANNELSPERUSER) &&
+             (!IsAnOper(sptr) || (sptr->user->joined >= MAXCHANNELSPERUSER*3)))
+            {
+              sendto_one(sptr, form_str(ERR_TOOMANYCHANNELS),
+                         me.name, parv[0], name);
+#ifdef ANTI_SPAMBOT
+              if(successful_join_count)
+                sptr->last_join_time = CurrentTime;
+#endif
+              return 0;
+            }
+
+
+#ifdef ANTI_SPAMBOT       /* Dianora */
+          if(flags == 0)        /* if channel doesn't exist, don't penalize */
+            successful_join_count++;
+          if( SPAMNUM && (sptr->join_leave_count >= SPAMNUM))
+            { 
+              /* Its already known as a possible spambot */
+ 
+              if(sptr->oper_warn_count_down > 0)  /* my general paranoia */
+                sptr->oper_warn_count_down--;
+              else
+                sptr->oper_warn_count_down = 0;
+ 
+              if(sptr->oper_warn_count_down == 0)
+                {
+                  sendto_ops_flags(FLAGS_BOTS,
+                    "User %s (%s@%s) trying to join %s is a possible spambot",
+                             sptr->name,
+                             sptr->username,
+                             sptr->host,
+                             name);     
+                  sptr->oper_warn_count_down = OPER_SPAM_COUNTDOWN;
+                }
+#ifndef ANTI_SPAMBOT_WARN_ONLY
+              return 0; /* Don't actually JOIN anything, but don't let
+                           spambot know that */
+#endif
+            }
+#endif
+        }
+      else
+        {
+          /*
+          ** complain for remote JOINs to existing channels
+          ** (they should be SJOINs) -orabidoo
+          */
+          if (!ChannelExists(name))
+            ts_warn("User on %s remotely JOINing new channel", 
+                    sptr->user->server);
+        }
+
+#if defined(NO_JOIN_ON_SPLIT)
+      if(server_was_split && (*name != '&'))
+        {
+          if( chptr )   /* The channel existed, so I can't join it */
+            {
+              if (IsMember(sptr, chptr)) /* already a member, ignore this */
+                continue;
+
+              /* allow local joins to this channel */
+              if( (chptr->users == 0) && !IsAnOper(sptr) )
+                {
+                  sendto_one(sptr, form_str(ERR_UNAVAILRESOURCE),
+                             me.name, parv[0], name);
+                  continue;
+                }
+            }
+          else
+            chptr = get_channel(sptr, name, CREATE);        
+        }
+      else
+#endif
+      if(!chptr)        /* If I already have a chptr, no point doing this */
+        chptr = get_channel(sptr, name, CREATE);
+
+      if(chptr)
+        {
+          if (IsMember(sptr, chptr))    /* already a member, ignore this */
+            continue;
+        }
+      else
+        {
+           sendto_one(sptr, form_str(ERR_UNAVAILRESOURCE),
+                      me.name, parv[0], name);
+#ifdef ANTI_SPAMBOT
+          if(successful_join_count > 0)
+            successful_join_count--;
+#endif
+          continue;
+        }
+
+      /*
+       * can_join checks for +i key, bans.
+       * If a ban is found but an exception to the ban was found
+       * flags will have CHFL_EXCEPTION set
+       */
+
+      if (MyConnect(sptr) && (i = can_join(sptr, chptr, key, &flags)))
+        {
+          sendto_one(sptr,
+                    form_str(i), me.name, parv[0], name);
+#ifdef ANTI_SPAMBOT
+          if(successful_join_count > 0)
+            successful_join_count--;
+#endif
+          continue;
+        }
+
+      /*
+      **  Complete user entry to the new channel (if any)
+      */
+
+#ifdef NO_CHANOPS_WHEN_SPLIT
+      if(allow_op)
+        {
+          add_user_to_channel(chptr, sptr, flags);
+        }
+      else
+        {
+          add_user_to_channel(chptr, sptr, flags & CHFL_EXCEPTION);
+        }
+#else
+      add_user_to_channel(chptr, sptr, flags);
+#endif
+      /*
+      **  Set timestamp if appropriate, and propagate
+      */
+      if (MyClient(sptr) && (flags & CHFL_CHANOP) )
+        {
+          chptr->channelts = CurrentTime;
+#ifdef NO_CHANOPS_WHEN_SPLIT
+          if(allow_op)
+            {
+              sendto_match_servs(chptr, cptr,
+                                 ":%s SJOIN %lu %s + :@%s", me.name,
+                                 chptr->channelts, name, parv[0]);
+
+            }                                
+          else
+            sendto_match_servs(chptr, cptr,
+                               ":%s SJOIN %lu %s + :%s", me.name,
+                               chptr->channelts, name, parv[0]);
+#else
+          sendto_match_servs(chptr, cptr,
+                             ":%s SJOIN %lu %s + :@%s", me.name,
+                             chptr->channelts, name, parv[0]);
+#endif
+        }
+      else if (MyClient(sptr))
+        {
+          sendto_match_servs(chptr, cptr,
+                             ":%s SJOIN %lu %s + :%s", me.name,
+                             chptr->channelts, name, parv[0]);
+        }
+      else
+        sendto_match_servs(chptr, cptr, ":%s JOIN :%s", parv[0],
+                           name);
+
+      /*
+      ** notify all other users on the new channel
+      */
+      sendto_channel_butserv(chptr, sptr, ":%s JOIN :%s",
+                             parv[0], name);
+
+      if (MyClient(sptr))
+        {
+          if( flags & CHFL_CHANOP )
+            {
+              chptr->mode.mode |= MODE_TOPICLIMIT;
+              chptr->mode.mode |= MODE_NOPRIVMSGS;
+
+              sendto_channel_butserv(chptr, sptr,
+                                 ":%s MODE %s +nt",
+                                 me.name, chptr->chname);
+
+              sendto_match_servs(chptr, sptr, 
+                                 ":%s MODE %s +nt",
+                                 me.name, chptr->chname);
+            }
+
+          del_invite(sptr, chptr);
+
+          if (chptr->topic[0] != '\0')
+            {
+              sendto_one(sptr, form_str(RPL_TOPIC), me.name,
+                         parv[0], name, chptr->topic);
+#ifdef TOPIC_INFO
+              sendto_one(sptr, form_str(RPL_TOPICWHOTIME),
+                         me.name, parv[0], name,
+                         chptr->topic_nick,
+                         chptr->topic_time);
+#endif
+            }
+          parv[1] = name;
+          (void)m_names(cptr, sptr, 2, parv);
+        }
+    }
+
+#ifdef ANTI_SPAMBOT
+  if(MyConnect(sptr) && successful_join_count)
+    sptr->last_join_time = CurrentTime;
+#endif
+  return 0;
+}
+
+/*
+** m_part
+**      parv[0] = sender prefix
+**      parv[1] = channel
+*/
+int     m_part(struct Client *cptr,
+               struct Client *sptr,
+               int parc,
+               char *parv[])
+{
+  struct Channel *chptr;
+  char  *p, *name;
+
+  if (parc < 2 || parv[1][0] == '\0')
+    {
+      sendto_one(sptr, form_str(ERR_NEEDMOREPARAMS),
+                 me.name, parv[0], "PART");
+      return 0;
+    }
+
+  name = strtoken( &p, parv[1], ",");
+
+#ifdef ANTI_SPAMBOT     /* Dianora */
+      /* if its my client, and isn't an oper */
+
+      if (name && MyConnect(sptr) && !IsAnOper(sptr))
+        {
+          if(SPAMNUM && (sptr->join_leave_count >= SPAMNUM))
+            {
+              sendto_ops_flags(FLAGS_BOTS,
+                               "User %s (%s@%s) is a possible spambot",
+                               sptr->name,
+                               sptr->username, sptr->host);
+              sptr->oper_warn_count_down = OPER_SPAM_COUNTDOWN;
+            }
+          else
+            {
+              int t_delta;
+
+              if( (t_delta = (CurrentTime - sptr->last_leave_time)) >
+                  JOIN_LEAVE_COUNT_EXPIRE_TIME)
+                {
+                  int decrement_count;
+                  decrement_count = (t_delta/JOIN_LEAVE_COUNT_EXPIRE_TIME);
+
+                  if(decrement_count > sptr->join_leave_count)
+                    sptr->join_leave_count = 0;
+                  else
+                    sptr->join_leave_count -= decrement_count;
+                }
+              else
+                {
+                  if( (CurrentTime - (sptr->last_join_time)) < SPAMTIME)
+                    {
+                      /* oh, its a possible spambot */
+                      sptr->join_leave_count++;
+                    }
+                }
+              sptr->last_leave_time = CurrentTime;
+            }
+        }
+#endif
+
+  while ( name )
+    {
+      chptr = get_channel(sptr, name, 0);
+      if (!chptr)
+        {
+          sendto_one(sptr, form_str(ERR_NOSUCHCHANNEL),
+                     me.name, parv[0], name);
+          name = strtoken(&p, (char *)NULL, ",");
+          continue;
+        }
+
+      if (!IsMember(sptr, chptr))
+        {
+          sendto_one(sptr, form_str(ERR_NOTONCHANNEL),
+                     me.name, parv[0], name);
+          name = strtoken(&p, (char *)NULL, ",");
+          continue;
+        }
+      /*
+      **  Remove user from the old channel (if any)
+      */
+            
+      sendto_match_servs(chptr, cptr, PartFmt, parv[0], name);
+            
+      sendto_channel_butserv(chptr, sptr, PartFmt, parv[0], name);
+      remove_user_from_channel(sptr, chptr, 0);
+      name = strtoken(&p, (char *)NULL, ",");
+    }
+  return 0;
+}
+
+/*
+** m_kick
+**      parv[0] = sender prefix
+**      parv[1] = channel
+**      parv[2] = client to kick
+**      parv[3] = kick comment
+*/
+/*
+ * I've removed the multi channel kick, and the multi user kick
+ * though, there are still remnants left ie..
+ * "name = strtoken(&p, parv[1], ",");" in a normal kick
+ * it will just be "KICK #channel nick"
+ * A strchr() is going to be faster than a strtoken(), so rewritten
+ * to use a strchr()
+ *
+ * It appears the original code was supposed to support 
+ * "kick #channel1,#channel2 nick1,nick2,nick3." For example, look at
+ * the original code for m_topic(), where 
+ * "topic #channel1,#channel2,#channel3... topic" was supported.
+ *
+ * -Dianora
+ */
+int     m_kick(struct Client *cptr,
+               struct Client *sptr,
+               int parc,
+               char *parv[])
+{
+  struct Client *who;
+  struct Channel *chptr;
+  int   chasing = 0;
+  char  *comment;
+  char  *name;
+  char  *p = (char *)NULL;
+  char  *user;
+
+  if (parc < 3 || *parv[1] == '\0')
+    {
+      sendto_one(sptr, form_str(ERR_NEEDMOREPARAMS),
+                 me.name, parv[0], "KICK");
+      return 0;
+    }
+  if (IsServer(sptr))
+    sendto_ops("KICK from %s for %s %s",
+               parv[0], parv[1], parv[2]);
+  comment = (BadPtr(parv[3])) ? parv[0] : parv[3];
+  if (strlen(comment) > (size_t) TOPICLEN)
+    comment[TOPICLEN] = '\0';
+
+  *buf = '\0';
+  p = strchr(parv[1],',');
+  if(p)
+    *p = '\0';
+  name = parv[1]; /* strtoken(&p, parv[1], ","); */
+
+  chptr = get_channel(sptr, name, !CREATE);
+  if (!chptr)
+    {
+      sendto_one(sptr, form_str(ERR_NOSUCHCHANNEL),
+                 me.name, parv[0], name);
+      return(0);
+    }
+
+  /* You either have chan op privs, or you don't -Dianora */
+  /* orabidoo and I discussed this one for a while...
+   * I hope he approves of this code, (he did) users can get quite confused...
+   *    -Dianora
+   */
+
+  if (!IsServer(sptr) && !is_chan_op(sptr, chptr) ) 
+    { 
+      /* was a user, not a server, and user isn't seen as a chanop here */
+      
+      if(MyConnect(sptr))
+        {
+          /* user on _my_ server, with no chanops.. so go away */
+          
+          sendto_one(sptr, form_str(ERR_CHANOPRIVSNEEDED),
+                     me.name, parv[0], chptr->chname);
+          return(0);
+        }
+
+      if(chptr->channelts == 0)
+        {
+          /* If its a TS 0 channel, do it the old way */
+          
+          sendto_one(sptr, form_str(ERR_CHANOPRIVSNEEDED),
+                     me.name, parv[0], chptr->chname);
+          return(0);
+        }
+
+      /* Its a user doing a kick, but is not showing as chanop locally
+       * its also not a user ON -my- server, and the channel has a TS.
+       * There are two cases we can get to this point then...
+       *
+       *     1) connect burst is happening, and for some reason a legit
+       *        op has sent a KICK, but the SJOIN hasn't happened yet or 
+       *        been seen. (who knows.. due to lag...)
+       *
+       *     2) The channel is desynced. That can STILL happen with TS
+       *        
+       *     Now, the old code roger wrote, would allow the KICK to 
+       *     go through. Thats quite legit, but lets weird things like
+       *     KICKS by users who appear not to be chanopped happen,
+       *     or even neater, they appear not to be on the channel.
+       *     This fits every definition of a desync, doesn't it? ;-)
+       *     So I will allow the KICK, otherwise, things are MUCH worse.
+       *     But I will warn it as a possible desync.
+       *
+       *     -Dianora
+       */
+
+      /*          sendto_one(sptr, form_str(ERR_DESYNC),
+       *           me.name, parv[0], chptr->chname);
+       */
+
+      /*
+       * After more discussion with orabidoo...
+       *
+       * The code was sound, however, what happens if we have +h (TS4)
+       * and some servers don't understand it yet? 
+       * we will be seeing servers with users who appear to have
+       * no chanops at all, merrily kicking users....
+       * -Dianora
+       */
+    }
+
+  p = strchr(parv[2],',');
+  if(p)
+    *p = '\0';
+  user = parv[2]; /* strtoken(&p2, parv[2], ","); */
+
+  if (!(who = find_chasing(sptr, user, &chasing)))
+    {
+      sendto_one(sptr, form_str(ERR_NOSUCHNICK),
+                 me.name, parv[0], user, name);
+      return(0);
+    }
+
+	/* 10/22/2000 - shadow */
+/*  if (IsMember(who, chptr)) */
+  if (IsMember(who, chptr) && !IsAnOper(who))
+    {
+      sendto_channel_butserv(chptr, sptr,
+                             ":%s KICK %s %s :%s", parv[0],
+                             name, who->name, comment);
+      sendto_match_servs(chptr, cptr,
+                         ":%s KICK %s %s :%s",
+                         parv[0], name,
+                         who->name, comment);
+      remove_user_from_channel(who, chptr, 1);
+    }
+  else
+    sendto_one(sptr, form_str(ERR_USERNOTINCHANNEL),
+               me.name, parv[0], user, name);
+
+  return (0);
+}
+
+int     count_channels(struct Client *sptr)
+{
+  struct Channel      *chptr;
+  int   count = 0;
+
+  for (chptr = channel; chptr; chptr = chptr->nextch)
+    count++;
+  return (count);
+}
+
+/* m_knock
+**    parv[0] = sender prefix
+**    parv[1] = channel
+**  The KNOCK command has the following syntax:
+**   :<sender> KNOCK <channel>
+**  If a user is not banned from the channel they can use the KNOCK
+**  command to have the server NOTICE the channel operators notifying
+**  they would like to join.  Helpful if the channel is invite-only, the
+**  key is forgotten, or the channel is full (INVITE can bypass each one
+**  of these conditions.  Concept by Dianora <db@db.net> and written by
+**  <anonymous>
+**
+** Just some flood control added here, five minute delay between each
+** KNOCK -Dianora
+**/
+int     m_knock(struct Client *cptr,
+               struct Client *sptr,
+               int parc,
+               char *parv[])
+{
+  struct Channel      *chptr;
+  char  *p, *name;
+
+  /* anti flooding code,
+   * I did have this in parse.c with a table lookup
+   * but I think this will be less inefficient doing it in each
+   * function that absolutely needs it
+   *
+   * -Dianora
+   */
+  static time_t last_used=0L;
+
+  if (parc < 2)
+    {
+      sendto_one(sptr, form_str(ERR_NEEDMOREPARAMS), me.name, parv[0],
+                 "KNOCK");
+      return 0;
+    }
+
+
+  /* We will cut at the first comma reached, however we will not *
+   * process anything afterwards.                                */
+
+  p = strchr(parv[1],',');
+  if(p)
+    *p = '\0';
+  name = parv[1]; /* strtoken(&p, parv[1], ","); */
+
+  if (!IsChannelName(name) || !(chptr = hash_find_channel(name, NullChn)))
+    {
+      sendto_one(sptr, form_str(ERR_NOSUCHCHANNEL), me.name, parv[0],
+                 name);
+      return 0;
+    }
+
+  if(!((chptr->mode.mode & MODE_INVITEONLY) ||
+       (*chptr->mode.key) ||
+       (chptr->mode.limit && chptr->users >= chptr->mode.limit )
+       ))
+    {
+      sendto_one(sptr,":%s NOTICE %s :*** Notice -- Channel is open!",
+                 me.name,
+                 sptr->name);
+      return 0;
+    }
+
+  /* don't allow a knock if the user is banned, or the channel is secret */
+  if ((chptr->mode.mode & MODE_SECRET) || 
+      (is_banned(sptr, chptr) == CHFL_BAN) )
+    {
+      sendto_one(sptr, form_str(ERR_CANNOTSENDTOCHAN), me.name, parv[0],
+                 name);
+      return 0;
+    }
+
+  /* if the user is already on channel, then a knock is pointless! */
+  if (IsMember(sptr, chptr))
+    {
+      sendto_one(sptr,":%s NOTICE %s :*** Notice -- You are on channel already!",
+                 me.name,
+                 sptr->name);
+      return 0;
+    }
+
+  /* flood control server wide, clients on KNOCK
+   * opers are not flood controlled.
+   */
+
+  if(!IsAnOper(sptr))
+    {
+      if((last_used + PACE_WAIT) > CurrentTime)
+        return 0;
+      else
+        last_used = CurrentTime;
+    }
+
+  /* flood control individual clients on KNOCK
+   * the ugly possibility still exists, 400 clones could all KNOCK
+   * on a channel at once, flooding all the ops. *ugh*
+   * Remember when life was simpler?
+   * -Dianora
+   */
+
+  /* opers are not flow controlled here */
+  if( !IsAnOper(sptr) && (sptr->last_knock + KNOCK_DELAY) > CurrentTime)
+    {
+      sendto_one(sptr,":%s NOTICE %s :*** Notice -- Wait %d seconds before another knock",
+                 me.name,
+                 sptr->name,
+                 KNOCK_DELAY - (CurrentTime - sptr->last_knock));
+      return 0;
+    }
+
+  sptr->last_knock = CurrentTime;
+
+  sendto_one(sptr,":%s NOTICE %s :*** Notice -- Your KNOCK has been delivered",
+                 me.name,
+                 sptr->name);
+
+  /* using &me and me.name won't deliver to clients not on this server
+   * so, the notice will have to appear from the "knocker" ick.
+   *
+   * Ideally, KNOCK would be routable. Also it would be nice to add
+   * a new channel mode. Perhaps some day.
+   * For now, clients that don't want to see KNOCK requests will have
+   * to use client side filtering. 
+   *
+   * -Dianora
+   */
+
+  {
+    char message[NICKLEN*2+CHANNELLEN+USERLEN+HOSTLEN+30];
+
+    /* bit of paranoid, be a shame if it cored for this -Dianora */
+    if(sptr->user)
+      {
+        ircsprintf(message,"KNOCK: %s (%s [%s@%s] has asked for an invite)",
+                   chptr->chname,
+                   sptr->name,
+                   sptr->username,
+                   sptr->host);
+        sendto_channel_type_notice(cptr, chptr, MODE_CHANOP, message);
+      }
+  }
+  return 0;
+}
+
+/*
+** m_topic
+**      parv[0] = sender prefix
+**      parv[1] = topic text
+*/
+int     m_topic(struct Client *cptr,
+                struct Client *sptr,
+                int parc,
+                char *parv[])
+{
+  struct Channel *chptr = NullChn;
+  char  *topic = (char *)NULL, *name, *p = (char *)NULL;
+  
+  if (parc < 2)
+    {
+      sendto_one(sptr, form_str(ERR_NEEDMOREPARAMS),
+                 me.name, parv[0], "TOPIC");
+      return 0;
+    }
+
+  p = strchr(parv[1],',');
+  if(p)
+    *p = '\0';
+  name = parv[1]; /* strtoken(&p, parv[1], ","); */
+
+  /* multi channel topic's are now known to be used by cloners
+   * trying to flood off servers.. so disable it *sigh* - Dianora
+   */
+
+  if (name && IsChannelName(name))
+    {
+      chptr = hash_find_channel(name, NullChn);
+      if (!chptr)
+        {
+          sendto_one(sptr, form_str(ERR_NOSUCHCHANNEL), me.name, parv[0],
+              name);
+          return 0;
+        }
+
+      if (!IsMember(sptr, chptr))
+        {
+          sendto_one(sptr, form_str(ERR_NOTONCHANNEL), me.name, parv[0],
+              name);
+          return 0;
+        }
+
+      if (parc > 2) /* setting topic */
+        topic = parv[2];
+
+      if(topic) /* a little extra paranoia never hurt */
+        {
+          if ((chptr->mode.mode & MODE_TOPICLIMIT) == 0 ||
+               is_chan_op(sptr, chptr))
+            {
+              /* setting a topic */
+              /*
+               * chptr zeroed
+               */
+              strncpy_irc(chptr->topic, topic, TOPICLEN);
+#ifdef TOPIC_INFO
+              /*
+               * XXX - this truncates the topic_nick if
+               * strlen(sptr->name) > NICKLEN
+               */
+              strncpy_irc(chptr->topic_nick, sptr->name, NICKLEN);
+              chptr->topic_time = CurrentTime;
+#endif
+              sendto_match_servs(chptr, cptr,":%s TOPIC %s :%s",
+                                 parv[0], chptr->chname,
+                                 chptr->topic);
+              sendto_channel_butserv(chptr, sptr, ":%s TOPIC %s :%s",
+                                     parv[0],
+                                     chptr->chname, chptr->topic);
+            }
+          else
+            sendto_one(sptr, form_str(ERR_CHANOPRIVSNEEDED),
+                       me.name, parv[0], chptr->chname);
+        }
+      else  /* only asking  for topic  */
+        {
+          if (chptr->topic[0] == '\0')
+            sendto_one(sptr, form_str(RPL_NOTOPIC),
+                       me.name, parv[0], chptr->chname);
+          else
+            {
+              sendto_one(sptr, form_str(RPL_TOPIC),
+                         me.name, parv[0],
+                         chptr->chname, chptr->topic);
+#ifdef TOPIC_INFO
+              sendto_one(sptr, form_str(RPL_TOPICWHOTIME),
+                         me.name, parv[0], chptr->chname,
+                         chptr->topic_nick,
+                         chptr->topic_time);
+#endif
+            }
+        }
+    }
+  else
+    {
+      sendto_one(sptr, form_str(ERR_NOSUCHCHANNEL),
+                 me.name, parv[0], name);
+    }
+
+  return 0;
+}
+
+/*
+** m_invite
+**      parv[0] - sender prefix
+**      parv[1] - user to invite
+**      parv[2] - channel number
+*/
+int     m_invite(struct Client *cptr,
+                 struct Client *sptr,
+                 int parc,
+                 char *parv[])
+{
+  struct Client *acptr;
+  struct Channel *chptr;
+  int need_invite=NO;
+
+  if (parc < 3 || *parv[1] == '\0')
+    {
+      sendto_one(sptr, form_str(ERR_NEEDMOREPARAMS),
+                 me.name, parv[0], "INVITE");
+      return -1;
+    }
+
+  /* A little sanity test here */
+  if(!sptr->user)
+    return 0;
+
+  if (!(acptr = find_person(parv[1], (struct Client *)NULL)))
+    {
+      sendto_one(sptr, form_str(ERR_NOSUCHNICK),
+                 me.name, parv[0], parv[1]);
+      return 0;
+    }
+
+  if (!check_channel_name(parv[2]))
+    { 
+      sendto_one(sptr, form_str(ERR_BADCHANNAME),
+                 me.name, parv[0], (unsigned char *)parv[2]);
+      return 0;
+    }
+
+  if (!IsChannelName(parv[2]))
+    {
+      if (MyClient(sptr))
+        sendto_one(sptr, form_str(ERR_NOSUCHCHANNEL),
+                   me.name, parv[0], parv[2]);
+      return 0;
+    }
+
+  /* Do not send local channel invites to users if they are not on the
+   * same server as the person sending the INVITE message. 
+   */
+  /* Possibly should be an error sent to sptr */
+  if (!MyConnect(acptr) && (parv[2][0] == '&'))
+    return 0;
+
+  if (!(chptr = hash_find_channel(parv[2], NullChn)))
+    {
+      if (MyClient(sptr))
+        sendto_one(sptr, form_str(ERR_NOSUCHCHANNEL),
+                   me.name, parv[0], parv[2]);
+      return 0;
+    }
+
+  /* By this point, chptr is non NULL */  
+
+  if (!IsMember(sptr, chptr))
+    {
+      if (MyClient(sptr))
+        sendto_one(sptr, form_str(ERR_NOTONCHANNEL),
+                   me.name, parv[0], parv[2]);
+      return 0;
+    }
+
+  if (IsMember(acptr, chptr))
+    {
+      if (MyClient(sptr))
+        sendto_one(sptr, form_str(ERR_USERONCHANNEL),
+                   me.name, parv[0], parv[1], parv[2]);
+      return 0;
+    }
+
+  if (chptr && (chptr->mode.mode & MODE_INVITEONLY))
+    {
+      need_invite = YES;
+
+      if (!is_chan_op(sptr, chptr))
+        {
+          if (MyClient(sptr))
+            sendto_one(sptr, form_str(ERR_CHANOPRIVSNEEDED),
+                       me.name, parv[0], parv[2]);
+          return -1;
+        }
+    }
+
+  /*
+   * due to some whining I've taken out the need for the channel
+   * being +i before sending an INVITE. It was intentionally done this
+   * way, it makes no sense (to me at least) letting the server send
+   * an unnecessary invite when a channel isn't +i !
+   * bah. I can't be bothered arguing it
+   * -Dianora
+   */
+  if (MyConnect(sptr) /* && need_invite*/ )
+    {
+      sendto_one(sptr, form_str(RPL_INVITING), me.name, parv[0],
+                 acptr->name, ((chptr) ? (chptr->chname) : parv[2]));
+      if (acptr->user->away)
+        sendto_one(sptr, form_str(RPL_AWAY), me.name, parv[0],
+                   acptr->name, acptr->user->away);
+      
+      if( need_invite )
+        {
+          /* Send a NOTICE to all channel operators concerning chanops who  *
+           * INVITE other users to the channel when it is invite-only (+i). *
+           * The NOTICE is sent from the local server.                      */
+
+          /* Only allow this invite notice if the channel is +p
+           * i.e. "paranoid"
+           * -Dianora
+           */
+
+          if (chptr && (chptr->mode.mode & MODE_PRIVATE))
+            { 
+              char message[NICKLEN*2+CHANNELLEN+USERLEN+HOSTLEN+30];
+
+              /* bit of paranoia, be a shame if it cored for this -Dianora */
+              if(acptr->user)
+                {
+                  ircsprintf(message,
+                             "INVITE: %s (%s invited %s [%s@%s])",
+                             chptr->chname,
+                             sptr->name,
+                             acptr->name,
+                             acptr->username,
+                             acptr->host);
+
+                  sendto_channel_type(cptr, sptr, chptr,
+                                      MODE_CHANOP,
+                                      chptr->chname,
+                                      "PRIVMSG",
+                                      message);
+                }
+            }
+        }
+    }
+
+  if(MyConnect(acptr) && need_invite)
+    add_invite(acptr, chptr);
+
+  sendto_prefix_one(acptr, sptr, ":%s INVITE %s :%s",
+                    parv[0], acptr->name, parv[2]);
+  return 0;
+}
+
+
+/************************************************************************
+ * m_names() - Added by Jto 27 Apr 1989
+ ************************************************************************/
+
+/*
+** m_names
+**      parv[0] = sender prefix
+**      parv[1] = channel
+*/
+/*
+ * Modified to report possible names abuse
+ * drastically modified to not show all names, just names
+ * on given channel names.
+ *
+ * -Dianora
+ */
+/* maximum names para to show to opers when abuse occurs */
+#define TRUNCATED_NAMES 20
+
+int     m_names( struct Client *cptr,
+                 struct Client *sptr,
+                 int parc,
+                 char *parv[])
+{ 
+  struct Channel *chptr;
+  struct Client *c2ptr;
+  Link  *lp;
+  struct Channel *ch2ptr = NULL;
+  int   idx, flag = 0, len, mlen;
+  char  *s, *para = parc > 1 ? parv[1] : NULL;
+  int comma_count=0;
+  int char_count=0;
+
+  /* Don't route names, no need for it -Dianora */
+  /*
+  if (parc > 1 &&
+      hunt_server(cptr, sptr, ":%s NAMES %s %s", 2, parc, parv))
+    return 0;
+    */
+
+  /* And throw away non local names requests that do get here -Dianora */
+  if(!MyConnect(sptr))
+    return 0;
+
+  /*
+   * names is called by m_join() when client joins a channel,
+   * hence I cannot easily rate limit it.. perhaps that won't
+   * be necessary now that remote names is prohibited.
+   *
+   * -Dianora
+   */
+
+  mlen = strlen(me.name) + NICKLEN + 7;
+
+  if (!BadPtr(para))
+    {
+      /* Here is the lamer detection code
+       * P.S. meta, GROW UP
+       * -Dianora 
+       */
+      for(s = para; *s; s++)
+        {
+          char_count++;
+          if(*s == ',')
+            comma_count++;
+          if(comma_count > 1)
+            {
+              if(char_count > TRUNCATED_NAMES)
+                para[TRUNCATED_NAMES] = '\0';
+              else
+                {
+                  s++;
+                  *s = '\0';
+                }
+              sendto_realops("POSSIBLE /names abuser %s [%s]",
+                             para,
+                             get_client_name(sptr,FALSE));
+              sendto_one(sptr, form_str(ERR_TOOMANYTARGETS),
+                         me.name, sptr->name, "NAMES",1);
+              return 0;
+            }
+        }
+
+      s = strchr(para, ',');
+      if (s)
+        *s = '\0';
+      if (!check_channel_name(para))
+        { 
+          sendto_one(sptr, form_str(ERR_BADCHANNAME),
+                     me.name, parv[0], (unsigned char *)para);
+          return 0;
+        }
+
+      ch2ptr = hash_find_channel(para, NULL);
+    }
+
+  *buf = '\0';
+  
+  /* 
+   *
+   * First, do all visible channels (public and the one user self is)
+   */
+
+  for (chptr = channel; chptr; chptr = chptr->nextch)
+    {
+      if ((chptr != ch2ptr) && !BadPtr(para))
+        continue; /* -- wanted a specific channel */
+      if (!MyConnect(sptr) && BadPtr(para))
+        continue;
+      if (!ShowChannel(sptr, chptr))
+        continue; /* -- users on this are not listed */
+      
+      /* Find users on same channel (defined by chptr) */
+
+      (void)strcpy(buf, "* ");
+      len = strlen(chptr->chname);
+      (void)strcpy(buf + 2, chptr->chname);
+      (void)strcpy(buf + 2 + len, " :");
+
+      if (PubChannel(chptr))
+        *buf = '=';
+      else if (SecretChannel(chptr))
+        *buf = '@';
+      idx = len + 4;
+      flag = 1;
+      for (lp = chptr->members; lp; lp = lp->next)
+        {
+          c2ptr = lp->value.cptr;
+          if (IsInvisible(c2ptr) && !IsMember(sptr,chptr))
+            continue;
+          if (lp->flags & CHFL_CHANOP)
+            {
+              strcat(buf, "@");
+              idx++;
+            }
+          else if (lp->flags & CHFL_VOICE)
+            {
+              strcat(buf, "+");
+              idx++;
+            }
+          strncat(buf, c2ptr->name, NICKLEN);
+          idx += strlen(c2ptr->name) + 1;
+          flag = 1;
+          strcat(buf," ");
+          if (mlen + idx + NICKLEN > BUFSIZE - 3)
+            {
+              sendto_one(sptr, form_str(RPL_NAMREPLY),
+                         me.name, parv[0], buf);
+              strncpy_irc(buf, "* ", 3);
+              strncpy_irc(buf + 2, chptr->chname, len + 1);
+              strcat(buf, " :");
+              if (PubChannel(chptr))
+                *buf = '=';
+              else if (SecretChannel(chptr))
+                *buf = '@';
+              idx = len + 4;
+              flag = 0;
+            }
+        }
+      if (flag)
+        sendto_one(sptr, form_str(RPL_NAMREPLY),
+                   me.name, parv[0], buf);
+    }
+  if (!BadPtr(para))
+    {
+      sendto_one(sptr, form_str(RPL_ENDOFNAMES), me.name, parv[0],
+                 para);
+      return(1);
+    }
+
+  /* Second, do all non-public, non-secret channels in one big sweep */
+
+  strncpy_irc(buf, "* * :", 6);
+  idx = 5;
+  flag = 0;
+  for (c2ptr = GlobalClientList; c2ptr; c2ptr = c2ptr->next)
+    {
+      struct Channel *ch3ptr;
+      int       showflag = 0, secret = 0;
+
+      if (!IsPerson(c2ptr) || IsInvisible(c2ptr))
+        continue;
+      lp = c2ptr->user->channel;
+      /*
+       * dont show a client if they are on a secret channel or
+       * they are on a channel sptr is on since they have already
+       * been show earlier. -avalon
+       */
+      while (lp)
+        {
+          ch3ptr = lp->value.chptr;
+          if (PubChannel(ch3ptr) || IsMember(sptr, ch3ptr))
+            showflag = 1;
+          if (SecretChannel(ch3ptr))
+            secret = 1;
+          lp = lp->next;
+        }
+      if (showflag) /* have we already shown them ? */
+        continue;
+      if (secret) /* on any secret channels ? */
+        continue;
+      (void)strncat(buf, c2ptr->name, NICKLEN);
+      idx += strlen(c2ptr->name) + 1;
+      (void)strcat(buf," ");
+      flag = 1;
+      if (mlen + idx + NICKLEN > BUFSIZE - 3)
+        {
+          sendto_one(sptr, form_str(RPL_NAMREPLY),
+                     me.name, parv[0], buf);
+          strncpy_irc(buf, "* * :", 6);
+          idx = 5;
+          flag = 0;
+        }
+    }
+
+  if (flag)
+    sendto_one(sptr, form_str(RPL_NAMREPLY), me.name, parv[0], buf);
+
+  sendto_one(sptr, form_str(RPL_ENDOFNAMES), me.name, parv[0], "*");
+  return(1);
+}
+
+
+void    send_user_joins(struct Client *cptr, struct Client *user)
+{
+  Link  *lp;
+  struct Channel *chptr;
+  int   cnt = 0, len = 0, clen;
+  char   *mask;
+
+  *buf = ':';
+  (void)strcpy(buf+1, user->name);
+  (void)strcat(buf, " JOIN ");
+  len = strlen(user->name) + 7;
+
+  for (lp = user->user->channel; lp; lp = lp->next)
+    {
+      chptr = lp->value.chptr;
+      if (*chptr->chname == '&')
+        continue;
+      if ((mask = strchr(chptr->chname, ':')))
+        if (!match(++mask, cptr->name))
+          continue;
+      clen = strlen(chptr->chname);
+      if (clen > (size_t) BUFSIZE - 7 - len)
+        {
+          if (cnt)
+            sendto_one(cptr, "%s", buf);
+          *buf = ':';
+          (void)strcpy(buf+1, user->name);
+          (void)strcat(buf, " JOIN ");
+          len = strlen(user->name) + 7;
+          cnt = 0;
+        }
+      (void)strcpy(buf + len, chptr->chname);
+      cnt++;
+      len += clen;
+      if (lp->next)
+        {
+          len++;
+          (void)strcat(buf, ",");
+        }
+    }
+  if (*buf && cnt)
+    sendto_one(cptr, "%s", buf);
+
+  return;
+}
+
+static  void sjoin_sendit(struct Client *cptr,
+                          struct Client *sptr,
+                          struct Channel *chptr,
+                          char *from)
+{
+  sendto_channel_butserv(chptr, sptr, ":%s MODE %s %s %s", from,
+                         chptr->chname, modebuf, parabuf);
+}
+
+/*
+ * m_sjoin
+ * parv[0] - sender
+ * parv[1] - TS
+ * parv[2] - channel
+ * parv[3] - modes + n arguments (key and/or limit)
+ * parv[4+n] - flags+nick list (all in one parameter)
+
+ * 
+ * process a SJOIN, taking the TS's into account to either ignore the
+ * incoming modes or undo the existing ones or merge them, and JOIN
+ * all the specified users while sending JOIN/MODEs to non-TS servers
+ * and to clients
+ */
+
+
+int     m_sjoin(struct Client *cptr,
+                struct Client *sptr,
+                int parc,
+                char *parv[])
+{
+  struct Channel *chptr;
+  struct Client       *acptr;
+  time_t        newts;
+  time_t        oldts;
+  time_t        tstosend;
+  static        Mode mode, *oldmode;
+  Link  *l;
+  int   args = 0, haveops = 0, keep_our_modes = 1, keep_new_modes = 1;
+  int   doesop = 0, what = 0, pargs = 0, fl, people = 0, isnew;
+  /* loop unrolled this is now redundant */
+  /*  int ip; */
+  register      char *s, *s0;
+  static        char numeric[16], sjbuf[BUFSIZE];
+  char  *mbuf = modebuf, *t = sjbuf, *p;
+
+  if (IsClient(sptr) || parc < 5)
+    return 0;
+  if (!IsChannelName(parv[2]))
+    return 0;
+
+  if (!check_channel_name(parv[2]))
+     { 
+       return 0;
+     }
+
+  /* comstud server did this, SJOIN's for
+   * local channels can't happen.
+   */
+
+  if(*parv[2] == '&')
+    return 0;
+
+  newts = atol(parv[1]);
+  memset(&mode, 0, sizeof(mode));
+
+  s = parv[3];
+  while (*s)
+    switch(*(s++))
+      {
+      case 'i':
+        mode.mode |= MODE_INVITEONLY;
+        break;
+      case 'n':
+        mode.mode |= MODE_NOPRIVMSGS;
+        break;
+      case 'p':
+        mode.mode |= MODE_PRIVATE;
+        break;
+      case 's':
+        mode.mode |= MODE_SECRET;
+        break;
+      case 'm':
+        mode.mode |= MODE_MODERATED;
+        break;
+      case 't':
+        mode.mode |= MODE_TOPICLIMIT;
+        break;
+      case 'k':
+        strncpy_irc(mode.key, parv[4 + args], KEYLEN);
+        args++;
+        if (parc < 5+args) return 0;
+        break;
+      case 'l':
+        mode.limit = atoi(parv[4+args]);
+        args++;
+        if (parc < 5+args) return 0;
+        break;
+      }
+
+  *parabuf = '\0';
+
+  isnew = ChannelExists(parv[2]) ? 0 : 1;
+  chptr = get_channel(sptr, parv[2], CREATE);
+
+
+  /*
+   * bogus ban removal code.
+   * If I see that this SJOIN will mean I keep my ops, but lose
+   * the ops from the joining server, I keep track of that in the channel
+   * structure. I set keep_their_modes to NO
+   * since the joining server will not be keeping their ops, I can
+   * ignore any of the bans sent from that server. The moment
+   * I see a chanop MODE being sent, I can set this flag back to YES.
+   *
+   * There is one degenerate case. Two servers connect bursting
+   * at the same time. It might cause a problem, or it might not.
+   * In the case that it becomes an issue, then a short list
+   * of servers having their modes ignored would have to be linked
+   * into the channel structure. This would be only an issue
+   * on hubs.
+   * Hopefully, it will be much of a problem.
+   *
+   * Bogus bans on the server losing its chanops is trivial. All
+   * bans placed on the local server during its split, with bogus chanops
+   * I can just remove.
+   *
+   * -Dianora
+   */
+  
+  chptr->keep_their_modes = YES;
+
+  /* locally created channels do not get created from SJOIN's
+   * any SJOIN destroys the locally_created flag
+   *
+   * -Dianora
+   */
+
+  chptr->locally_created = NO;
+  oldts = chptr->channelts;
+
+  /*
+   * If an SJOIN ever happens on a channel, assume the split is over
+   * for this channel. best I think we can do for now -Dianora
+   */
+
+  /* If the TS goes to 0 for whatever reason, flag it
+   * ya, I know its an invasion of privacy for those channels that
+   * want to keep TS 0 *shrug* sorry
+   * -Dianora
+   */
+
+  if(!isnew && !newts && oldts)
+    {
+      sendto_channel_butserv(chptr, &me,
+             ":%s NOTICE %s :*** Notice -- TS for %s changed from %lu to 0",
+              me.name, chptr->chname, chptr->chname, oldts);
+      sendto_realops("Server %s changing TS on %s from %lu to 0",
+                     sptr->name,parv[2],oldts);
+    }
+
+  doesop = (parv[4+args][0] == '@' || parv[4+args][1] == '@');
+
+  for (l = chptr->members; l && l->value.cptr; l = l->next)
+    if (l->flags & MODE_CHANOP)
+      {
+        haveops++;
+        break;
+      }
+
+  oldmode = &chptr->mode;
+
+  if (isnew)
+    chptr->channelts = tstosend = newts;
+  else if (newts == 0 || oldts == 0)
+    chptr->channelts = tstosend = 0;
+  else if (newts == oldts)
+    tstosend = oldts;
+  else if (newts < oldts)
+    {
+      if (doesop)
+        keep_our_modes = NO;
+
+      clear_bans_exceptions_denies(sptr,chptr);
+
+      if (haveops && !doesop)
+          tstosend = oldts;
+      else
+        chptr->channelts = tstosend = newts;
+    }
+  else
+    {
+      chptr->keep_their_modes = NO;
+
+      if (haveops)
+        keep_new_modes = NO;
+      if (doesop && !haveops)
+        {
+          chptr->channelts = tstosend = newts;
+#if 0
+          /* Only warn of Hacked ops if the channel already
+           * existed on this side.
+           * This should drop the number of warnings down dramatically
+           */
+
+          if (MyConnect(sptr) && !isnew)
+            {
+              sendto_realops("Hacked ops from %s on opless channel: %s",
+                             sptr->name,
+                             chptr->chname);
+            }
+#endif
+        }
+      else
+        tstosend = oldts;
+    }
+
+  if (!keep_new_modes)
+    mode = *oldmode;
+  else if (keep_our_modes)
+    {
+      mode.mode |= oldmode->mode;
+      if (oldmode->limit > mode.limit)
+        mode.limit = oldmode->limit;
+      if (strcmp(mode.key, oldmode->key) < 0)
+        strcpy(mode.key, oldmode->key);
+    }
+
+  /* This loop unrolled below for speed
+   */
+  /*
+  for (ip = 0; flags[ip].mode; ip++)
+    if ((flags[ip].mode & mode.mode) && !(flags[ip].mode & oldmode->mode))
+      {
+        if (what != 1)
+          {
+            *mbuf++ = '+';
+            what = 1;
+          }
+        *mbuf++ = flags[ip].letter;
+      }
+      */
+
+  if((MODE_PRIVATE    & mode.mode) && !(MODE_PRIVATE    & oldmode->mode))
+    {
+      if (what != 1)
+        {
+          *mbuf++ = '+';
+          what = 1;
+        }
+      *mbuf++ = 'p';
+    }
+  if((MODE_SECRET     & mode.mode) && !(MODE_SECRET     & oldmode->mode))
+    {
+      if (what != 1)
+        {
+          *mbuf++ = '+';
+          what = 1;
+        }
+      *mbuf++ = 's';
+    }
+  if((MODE_MODERATED  & mode.mode) && !(MODE_MODERATED  & oldmode->mode))
+    {
+      if (what != 1)
+        {
+          *mbuf++ = '+';
+          what = 1;
+        }
+      *mbuf++ = 'm';
+    }
+  if((MODE_NOPRIVMSGS & mode.mode) && !(MODE_NOPRIVMSGS & oldmode->mode))
+    {
+      if (what != 1)
+        {
+          *mbuf++ = '+';
+          what = 1;
+        }
+      *mbuf++ = 'n';
+    }
+  if((MODE_TOPICLIMIT & mode.mode) && !(MODE_TOPICLIMIT & oldmode->mode))
+    {
+      if (what != 1)
+        {
+          *mbuf++ = '+';
+          what = 1;
+        }
+      *mbuf++ = 't';
+    }
+  if((MODE_INVITEONLY & mode.mode) && !(MODE_INVITEONLY & oldmode->mode))
+    {
+      if (what != 1)
+        {
+          *mbuf++ = '+';
+          what = 1;             /* This one is actually redundant now */
+        }
+      *mbuf++ = 'i';
+    }
+
+  /* This loop unrolled below for speed
+   */
+  /*
+  for (ip = 0; flags[ip].mode; ip++)
+    if ((flags[ip].mode & oldmode->mode) && !(flags[ip].mode & mode.mode))
+      {
+        if (what != -1)
+          {
+            *mbuf++ = '-';
+            what = -1;
+          }
+        *mbuf++ = flags[ip].letter;
+      }
+      */
+  if((MODE_PRIVATE    & oldmode->mode) && !(MODE_PRIVATE    & mode.mode))
+    {
+      if (what != -1)
+        {
+          *mbuf++ = '-';
+          what = -1;
+        }
+      *mbuf++ = 'p';
+    }
+  if((MODE_SECRET     & oldmode->mode) && !(MODE_SECRET     & mode.mode))
+    {
+      if (what != -1)
+        {
+          *mbuf++ = '-';
+          what = -1;
+        }
+      *mbuf++ = 's';
+    }
+  if((MODE_MODERATED  & oldmode->mode) && !(MODE_MODERATED  & mode.mode))
+    {
+      if (what != -1)
+        {
+          *mbuf++ = '-';
+          what = -1;
+        }
+      *mbuf++ = 'm';
+    }
+  if((MODE_NOPRIVMSGS & oldmode->mode) && !(MODE_NOPRIVMSGS & mode.mode))
+    {
+      if (what != -1)
+        {
+          *mbuf++ = '-';
+          what = -1;
+        }
+      *mbuf++ = 'n';
+    }
+  if((MODE_TOPICLIMIT & oldmode->mode) && !(MODE_TOPICLIMIT & mode.mode))
+    {
+      if (what != -1)
+        {
+          *mbuf++ = '-';
+          what = -1;
+        }
+      *mbuf++ = 't';
+    }
+  if((MODE_INVITEONLY & oldmode->mode) && !(MODE_INVITEONLY & mode.mode))
+    {
+      if (what != -1)
+        {
+          *mbuf++ = '-';
+          what = -1;
+        }
+      *mbuf++ = 'i';
+    }
+
+  if (oldmode->limit && !mode.limit)
+    {
+      if (what != -1)
+        {
+          *mbuf++ = '-';
+          what = -1;
+        }
+      *mbuf++ = 'l';
+    }
+  if (oldmode->key[0] && !mode.key[0])
+    {
+      if (what != -1)
+        {
+          *mbuf++ = '-';
+          what = -1;
+        }
+      *mbuf++ = 'k';
+      strcat(parabuf, oldmode->key);
+      strcat(parabuf, " ");
+      pargs++;
+    }
+  if (mode.limit && oldmode->limit != mode.limit)
+    {
+      if (what != 1)
+        {
+          *mbuf++ = '+';
+          what = 1;
+        }
+      *mbuf++ = 'l';
+      ircsprintf(numeric, "%d", mode.limit);
+      if ((s = strchr(numeric, ' ')))
+        *s = '\0';
+      strcat(parabuf, numeric);
+      strcat(parabuf, " ");
+      pargs++;
+    }
+  if (mode.key[0] && strcmp(oldmode->key, mode.key))
+    {
+      if (what != 1)
+        {
+          *mbuf++ = '+';
+          what = 1;
+        }
+      *mbuf++ = 'k';
+      strcat(parabuf, mode.key);
+      strcat(parabuf, " ");
+      pargs++;
+    }
+  
+  chptr->mode = mode;
+
+  if (!keep_our_modes)
+    {
+      what = 0;
+      for (l = chptr->members; l && l->value.cptr; l = l->next)
+        {
+          if (l->flags & MODE_CHANOP)
+            {
+              if (what != -1)
+                {
+                  *mbuf++ = '-';
+                  what = -1;
+                }
+              *mbuf++ = 'o';
+              strcat(parabuf, l->value.cptr->name);
+              strcat(parabuf, " ");
+              pargs++;
+              if (pargs >= MAXMODEPARAMS)
+                {
+                  *mbuf = '\0';
+                  sjoin_sendit(cptr, sptr, chptr,
+                               parv[0]);
+                  mbuf = modebuf;
+                  *mbuf = parabuf[0] = '\0';
+                  pargs = what = 0;
+                }
+              l->flags &= ~MODE_CHANOP;
+            }
+          if (l->flags & MODE_VOICE)
+            {
+              if (what != -1)
+                {
+                  *mbuf++ = '-';
+                  what = -1;
+                }
+              *mbuf++ = 'v';
+              strcat(parabuf, l->value.cptr->name);
+              strcat(parabuf, " ");
+              pargs++;
+              if (pargs >= MAXMODEPARAMS)
+                {
+                  *mbuf = '\0';
+                  sjoin_sendit(cptr, sptr, chptr,
+                               parv[0]);
+                  mbuf = modebuf;
+                  *mbuf = parabuf[0] = '\0';
+                  pargs = what = 0;
+                }
+              l->flags &= ~MODE_VOICE;
+            }
+        }
+        sendto_channel_butserv(chptr, &me,
+            ":%s NOTICE %s :*** Notice -- TS for %s changed from %lu to %lu",
+            me.name, chptr->chname, chptr->chname, oldts, newts);
+    }
+  if (mbuf != modebuf)
+    {
+      *mbuf = '\0';
+      sjoin_sendit(cptr, sptr, chptr, parv[0]);
+    }
+
+  *modebuf = *parabuf = '\0';
+  if (parv[3][0] != '0' && keep_new_modes)
+    channel_modes(sptr, modebuf, parabuf, chptr);
+  else
+    {
+      modebuf[0] = '0';
+      modebuf[1] = '\0';
+    }
+
+  ircsprintf(t, ":%s SJOIN %lu %s %s %s :", parv[0], tstosend, parv[2],
+          modebuf, parabuf);
+  t += strlen(t);
+
+  mbuf = modebuf;
+  parabuf[0] = '\0';
+  pargs = 0;
+  *mbuf++ = '+';
+
+  for (s = s0 = strtoken(&p, parv[args+4], " "); s;
+       s = s0 = strtoken(&p, (char *)NULL, " "))
+    {
+      fl = 0;
+      if (*s == '@' || s[1] == '@')
+        fl |= MODE_CHANOP;
+      if (*s == '+' || s[1] == '+')
+        fl |= MODE_VOICE;
+      if (!keep_new_modes)
+       {
+        if (fl & MODE_CHANOP)
+          {
+            fl = MODE_DEOPPED;
+          }
+        else
+          {
+            fl = 0;
+          }
+       }
+      while (*s == '@' || *s == '+')
+        s++;
+      if (!(acptr = find_chasing(sptr, s, NULL)))
+        continue;
+      if (acptr->from != cptr)
+        continue;
+      people++;
+      if (!IsMember(acptr, chptr))
+        {
+          add_user_to_channel(chptr, acptr, fl);
+          sendto_channel_butserv(chptr, acptr, ":%s JOIN :%s",
+                                 s, parv[2]);
+        }
+      if (keep_new_modes)
+        strcpy(t, s0);
+      else
+        strcpy(t, s);
+      t += strlen(t);
+      *t++ = ' ';
+      if (fl & MODE_CHANOP)
+        {
+          *mbuf++ = 'o';
+          strcat(parabuf, s);
+          strcat(parabuf, " ");
+          pargs++;
+          if (pargs >= MAXMODEPARAMS)
+            {
+              *mbuf = '\0';
+              sjoin_sendit(cptr, sptr, chptr, parv[0]);
+              mbuf = modebuf;
+              *mbuf++ = '+';
+              parabuf[0] = '\0';
+              pargs = 0;
+            }
+        }
+      if (fl & MODE_VOICE)
+        {
+          *mbuf++ = 'v';
+          strcat(parabuf, s);
+          strcat(parabuf, " ");
+          pargs++;
+          if (pargs >= MAXMODEPARAMS)
+            {
+              *mbuf = '\0';
+              sjoin_sendit(cptr, sptr, chptr, parv[0]);
+              mbuf = modebuf;
+              *mbuf++ = '+';
+              parabuf[0] = '\0';
+              pargs = 0;
+            }
+        }
+    }
+  
+  *mbuf = '\0';
+  if (pargs)
+    sjoin_sendit(cptr, sptr, chptr, parv[0]);
+  if (people)
+    {
+      if (t[-1] == ' ')
+        t[-1] = '\0';
+      else
+        *t = '\0';
+      sendto_match_servs(chptr, cptr, "%s", sjbuf);
+    }
+  return 0;
+}
+
+
diff -uNrd ircd-hybrid-6rc4/src/ircd.c ircd-ournet-12302000/src/ircd.c
--- ircd-hybrid-6rc4/src/ircd.c	Sat Aug 12 20:12:11 2000
+++ ircd-ournet-12302000/src/ircd.c	Sat Dec 30 00:42:05 2000
@@ -110,6 +110,7 @@
 #endif
 
 
+
 #ifdef  REJECT_HOLD
 int reject_held_fds = 0;
 #endif
diff -uNrd ircd-hybrid-6rc4/src/m_gline.c ircd-ournet-12302000/src/m_gline.c
--- ircd-hybrid-6rc4/src/m_gline.c	Thu Aug 24 20:20:46 2000
+++ ircd-ournet-12302000/src/m_gline.c	Sat Dec 30 00:42:22 2000
@@ -52,8 +52,9 @@
 /* external variables */
 extern ConfigFileEntryType ConfigFileEntry; /* defined in ircd.c */
 
+
 /* internal variables */
-static  aConfItem *glines = (aConfItem *)NULL;
+static aConfItem *glines = (aConfItem *)NULL;
 static GLINE_PENDING *pending_glines;
 
 /* internal functions */
@@ -297,14 +298,17 @@
                   reason);
 
   /* If at least 3 opers agree this user should be G lined then do it */
-  if(majority_gline(sptr,
+
+   /* below commented out and hardcoded to YES to avoid 3-oper limit for Glines */
+   /* 10/20/2000 - shadow - must be done on all servers to be effective as Gline */
+   if(/*majority_gline(sptr,
                     oper_name,
                     oper_username,
                     oper_host,
                     oper_server,
                     user,
                     host,
-                    reason))
+                    reason)*/YES)
     {
       current_date = smalldate((time_t) 0);
           
diff -uNrd ircd-hybrid-6rc4/src/m_message.c ircd-ournet-12302000/src/m_message.c
--- ircd-hybrid-6rc4/src/m_message.c	Tue Jun  6 23:01:06 2000
+++ ircd-ournet-12302000/src/m_message.c	Sat Dec 30 00:42:32 2000
@@ -169,7 +169,10 @@
   ** channels are privmsg'd a lot more than other clients, moved up here
   ** plain old channel msg ?
   */
+/*
   while(msgs < MAX_MULTI_MESSAGES)
+*/
+  while(msgs < 32)  /* 10/21/2000 - shadow */
   {
      if(!msgs)
         nick = strtok(parv[1], ",");
@@ -421,13 +424,14 @@
       while (*++s)
         if (*s == '.' || *s == '*' || *s == '?')
           break;
-      if (*s == '*' || *s == '?')
+/*      if (*s == '*' || *s == '?')
         {
           sendto_one(sptr, form_str(ERR_WILDTOPLEVEL),
                      me.name, parv[0], nick);
 	  msgs++;
 	  continue;
-        }
+        }*/
+       /* 10/21/2000 - shadow */
       sendto_match_butone(IsServer(cptr) ? cptr : NULL, 
                           sptr, nick + 1,
                           (*nick == '#') ? MATCH_HOST :
@@ -499,10 +503,11 @@
                               ":%s %s %s :%s",
                               parv[0], cmd,
                               nick, parv[2]);
-          else if (!notice)
+/*          else if (!notice)
             sendto_one(sptr,
                        form_str(ERR_TOOMANYTARGETS),
-                       me.name, parv[0], nick, MAX_MULTI_MESSAGES);
+                       me.name, parv[0], nick, MAX_MULTI_MESSAGES);*/
+	   /* 10/21/2000 - shadow */
         }
       if (acptr)
 	{
diff -uNrd ircd-hybrid-6rc4/src/m_svinfo.c ircd-ournet-12302000/src/m_svinfo.c
--- ircd-hybrid-6rc4/src/m_svinfo.c	Wed Aug 11 23:02:20 1999
+++ ircd-ournet-12302000/src/m_svinfo.c	Sat Dec 30 00:42:45 2000
@@ -129,14 +129,15 @@
   theirtime = atol(parv[4]);
   deltat = abs(theirtime - CurrentTime);
 
-  if (deltat > TS_MAX_DELTA)
+   /* 11-5-2000 shadow */
+/*  if (deltat > TS_MAX_DELTA)
     {
       sendto_ops(
        "Link %s dropped, excessive TS delta (my TS=%d, their TS=%d, delta=%d)",
                  get_client_name(sptr, TRUE), CurrentTime, theirtime,deltat);
       return exit_client(sptr, sptr, sptr, "Excessive TS delta");
     }
-
+*/
   if (deltat > TS_WARN_DELTA)
     { 
       sendto_realops(
diff -uNrd ircd-hybrid-6rc4/src/m_unkline.c ircd-ournet-12302000/src/m_unkline.c
--- ircd-hybrid-6rc4/src/m_unkline.c	Thu Aug 24 02:15:56 2000
+++ ircd-ournet-12302000/src/m_unkline.c	Sat Dec 30 00:42:48 2000
@@ -354,7 +354,6 @@
  *
  * -Dianora
  */
-
 static int flush_write(aClient *sptr, FBFILE* out, char *buf, char *temppath)
 {
   int error_on_write = (fbputs(buf, out) < 0) ? YES : NO;
@@ -381,11 +380,11 @@
   aConfItem *kill_list_ptr;
   aConfItem *last_kill_ptr=(aConfItem *)NULL;
 
-  if(!temporary_klines)
-    return NO;
+  if (!temporary_klines)
+     return NO;
 
   kill_list_ptr = temporary_klines;
-
+     
   while(kill_list_ptr)
     {
       if( !irccmp(kill_list_ptr->host,host)
@@ -404,5 +403,6 @@
           kill_list_ptr = kill_list_ptr->next;
         }
     }
+
   return NO;
 }
diff -uNrd ircd-hybrid-6rc4/src/messages.tab ircd-ournet-12302000/src/messages.tab
--- ircd-hybrid-6rc4/src/messages.tab	Fri Jun  9 22:45:36 2000
+++ ircd-ournet-12302000/src/messages.tab	Sat Dec 30 00:42:12 2000
@@ -24,9 +24,9 @@
 
 static  char *  replies[] = {
 /* 000 */       (char *)NULL,
-/* 001  RPL_WELCOME, */ ":%s 001 %s :Welcome to the Internet Relay Network %s",
-/* 002  RPL_YOURHOST,*/ ":%s 002 %s :Your host is %s, running version %s",
-/* 003  RPL_CREATED, */ ":%s 003 %s :This server was created %s",
+/* 001  RPL_WELCOME, */ ":%s 001 %s :Welcome to the OURNet IRC Network %s",
+/* 002  RPL_YOURHOST,*/ ":%s 002 %s :Your are on the server %s, running ircd version %s",
+/* 003  RPL_CREATED, */ ":%s 003 %s :Date and time of server activation: %s",
 /* 004  RPL_MYINFO, */ ":%s 004 %s %s %s oiwszcrkfydnxb biklmnopstved",
 /* 005 */       (char *)NULL,
 /* 006 */       (char *)NULL,
@@ -420,7 +420,7 @@
 /* 378 */       (char *)NULL,
 /* 379 */       (char *)NULL,
 /* 380 */       (char *)NULL,
-/* 381 RPL_YOUREOPER, */        ":%s 381 %s :You have entered... the Twilight Zone!.",
+/* 381 RPL_YOUREOPER, */        ":%s 381 %s :Dont be an asshole, or shadow will kill you.",
 /* 382 RPL_REHASHING, */        ":%s 382 %s %s :Rehashing",
 /* 383 */       (char *)NULL,
 /* 384 RPL_MYPORTIS, */         ":%s 384 %s %d :Port to local server is\r\n",
@@ -523,7 +523,7 @@
 /* 479 ERR_BADCHANNAME */       ":%s 479 %s %s :Illegal channel name",
 /* 480 */       (char *)NULL,
 /* 481 ERR_NOPRIVILEGES, */
-                ":%s 481 %s :Permission Denied- You're not an IRC operator",
+                ":%s 481 %s :Permission Denied- You arent recognized as a god here...",
 /* 482 ERR_CHANOPRIVSNEEDED, */ ":%s 482 %s %s :You're not channel operator",
 /* 483 ERR_CANTKILLSERVER, */   ":%s 483 %s :You can't kill a server!",
 /* 484 */       (char *)NULL,
@@ -533,7 +533,7 @@
 /* 488 */       (char *)NULL,
 /* 489 */       (char *)NULL,
 /* 490 */       (char *)NULL,
-/* 491 ERR_NOOPERHOST, */       ":%s 491 %s :Only few of mere mortals may try to enter the twilight zone",
+/* 491 ERR_NOOPERHOST, */       ":%s 491 %s :Err?",
 /* 492 */       (char *)NULL,
 /* 493 */       (char *)NULL,
 /* 494 */       (char *)NULL,
diff -uNrd ircd-hybrid-6rc4/src/messages_cust.tab ircd-ournet-12302000/src/messages_cust.tab
--- ircd-hybrid-6rc4/src/messages_cust.tab	Fri Jun  9 22:45:36 2000
+++ ircd-ournet-12302000/src/messages_cust.tab	Sat Dec 30 00:42:13 2000
@@ -25,9 +25,9 @@
 
 static  char *  replies[] = {
 /* 000 */       (char *)NULL,
-/* 001  RPL_WELCOME, */ ":%s 001 %s :Welcome to the Internet Relay Network %s",
+/* 001  RPL_WELCOME, */ ":%s 001 %s :Welcome to the OURNet IRC Network %s",
 /* 002  RPL_YOURHOST,*/ ":%s 002 %s :Your host is %s, running version %s",
-/* 003  RPL_CREATED, */ ":%s 003 %s :This server was created %s",
+/* 003  RPL_CREATED, */ ":%s 003 %s :This server was started on %s",
 /* 004  RPL_MYINFO, */ ":%s 004 %s %s %s oiwszcrkfydnxb biklmnopstved",
 /* 005 */       (char *)NULL,
 /* 006 */       (char *)NULL,
diff -uNrd ircd-hybrid-6rc4/src/s_user.c ircd-ournet-12302000/src/s_user.c
--- ircd-hybrid-6rc4/src/s_user.c	Thu Aug 24 19:28:07 2000
+++ ircd-ournet-12302000/src/s_user.c	Sat Dec 30 22:49:01 2000
@@ -590,8 +590,13 @@
                   get_client_host(sptr));
 
               ServerStats->is_ref++;
+
               return exit_client(cptr, sptr, &me,
-                                 "You are not authorized to use this server");
+#ifdef HUB
+                                 "This is a hub-only server, no clients; please use irc.npis.net or visit www.ournet-irc.com for a server list.");
+#else
+	                         "You are not authorized to use this server");
+#endif
             }
           break;
 
@@ -634,7 +639,11 @@
 
       aconf = sptr->confs->value.aconf;
       if (!aconf)
+#ifdef HUB
+        return exit_client(cptr, sptr, &me, "*** This is a hub-only server, no clients; please use irc.npis.net or visit www.ournet-irc.com for a server list.");
+#else
         return exit_client(cptr, sptr, &me, "*** Not Authorized");
+#endif
       if (!IsGotId(sptr))
         {
           if (IsNeedIdentd(aconf))
