diff -ruN -x configure -x *~ -x ircd-hybrid-6.0.orig -x *.rej ircd-hybrid-6.0.orig/include/channel.h ircd-hybrid-6.0/include/channel.h
--- ircd-hybrid-6.0.orig/include/channel.h	Sat May 26 14:04:00 2001
+++ ircd-hybrid-6.0/include/channel.h	Sat May 26 13:55:56 2001
@@ -44,6 +44,14 @@
   unsigned int  mode;
   int   limit;
   char  key[KEYLEN + 1];
+#ifdef INVITE_CHANNEL_FORWARDING
+  /* I need this to sync on SJOIN
+   * (well, the way I'm doing it, anyway)
+   *  -- asuffield
+   */
+  char  invite_forward_channel_name[CHANNELLEN + 1];
+  struct Channel* invite_forward_channel; /* -goober */
+#endif
 };
 
 /* channel structure */
@@ -158,6 +166,9 @@
 #define MODE_INVEX      0x2000
 
 #define MODE_LIMIT      0x4000  /* was 0x1000 */
+#ifdef INVITE_CHANNEL_FORWARDING
+#define MODE_FORWARD    0x8000  /* -goober */
+#endif
 #define MODE_FLAGS      0x4fff  /* was 0x1fff */
 
 #ifdef NEED_SPLITCODE
@@ -233,7 +244,15 @@
   char *banstr;
   char *who;
   time_t when;
+#ifdef BAN_CHANNEL_FORWARDING
+  struct Channel *ban_forward_chptr;
+#endif
 } aBan;
+#endif
+
+#if defined(INVITE_CHANNEL_FORWARDING) || defined(BAN_CHANNEL_FORWARDING)
+#define INVITE_FORWARD 1
+#define BAN_FORWARD 2
 #endif
 
 #ifdef NEED_SPLITCODE
diff -ruN -x configure -x *~ -x ircd-hybrid-6.0.orig -x *.rej ircd-hybrid-6.0.orig/include/config.h ircd-hybrid-6.0/include/config.h
--- ircd-hybrid-6.0.orig/include/config.h	Sat May 26 14:04:01 2001
+++ ircd-hybrid-6.0/include/config.h	Sat May 26 13:55:56 2001
@@ -1135,6 +1135,24 @@
  */
 #define MAX_MAP_NODES 30
 
+#define INVITE_CHANNEL_FORWARDING
+
+#ifdef BAN_INFO
+#define BAN_CHANNEL_FORWARDING /* Ban channel forwarding is dependant on BAN_INFO structure */
+#endif
+
+#if defined(INVITE_CHANNEL_FORWARDING) || defined(BAN_CHANNEL_FORWARDING)
+/* MAX_FORWARDING_RECURSION - when using channel forwarding for bans or
+ * on invite only channels (channel mode +f) this sets the maximum
+ * level of forwarding that will take place on a join before a normal
+ * invite only message is returned. ex.: With the chain A->B->C->D->E the
+ * recursion level of 3 will return an invite only if joining A but
+ * will chain to E if joining B.
+ * [ for ban forwarding: /mode +b <nick>!<host>!<forwarding channel> ]
+ */
+#define MAX_FORWARDING_RECURSION 3
+#endif
+
 #define CONFIG_H_LEVEL_6
 
 #endif /* INCLUDED_config_h */
diff -ruN -x configure -x *~ -x ircd-hybrid-6.0.orig -x *.rej ircd-hybrid-6.0.orig/include/numeric.h ircd-hybrid-6.0/include/numeric.h
--- ircd-hybrid-6.0.orig/include/numeric.h	Sat May 26 14:04:01 2001
+++ ircd-hybrid-6.0/include/numeric.h	Sat May 26 13:55:56 2001
@@ -278,6 +278,7 @@
 
 #define RPL_MAP              377
 #define RPL_ENDOFMAP         378
+#define RPL_FORWARD          379
 
 #define RPL_YOUREOPER        381
 #define RPL_REHASHING        382
@@ -365,6 +366,8 @@
 #define ERR_SILELISTFULL     511
 
 #define ERR_MAXMAPNODES      512
+
+#define ERR_MAXFORWARDING    513
 
 #define ERR_BLOCKING_NOTID   505
 
diff -ruN -x configure -x *~ -x ircd-hybrid-6.0.orig -x *.rej ircd-hybrid-6.0.orig/src/channel.c ircd-hybrid-6.0/src/channel.c
--- ircd-hybrid-6.0.orig/src/channel.c	Sat May 26 14:04:01 2001
+++ ircd-hybrid-6.0/src/channel.c	Sat May 26 14:03:53 2001
@@ -78,6 +78,20 @@
 static  int     is_banned (struct Client *, struct Channel *);
 static  void    sub1_from_channel (struct Channel *);
 
+#if defined(INVITE_CHANNEL_FORWARDING) || defined(BAN_CHANNEL_FORWARDING)
+int m_join_redirect(struct Client *,struct Client *,int,char *[],struct Channel*);
+void attempt_channel_redirection(struct Client *,struct Client *,int,char *[],int,char *,struct Channel *,int);
+
+#endif
+
+#ifdef BAN_CHANNEL_FORWARDING
+char *banstr_remove_channel(char *);
+char *banstr_get_channel(char *);
+void add_ban_channel_forward(struct Channel *,char *, char *);
+void del_ban_channel_forward(struct Channel *,char *);
+struct Channel *get_ban_channel_forward(struct Client *,struct Channel *);
+#endif
+
 /* static functions used in set_mode */
 static char *fix_key(char *);
 static char *fix_key_old(char *);
@@ -659,6 +673,17 @@
       if (IsMember(cptr, chptr) || IsServer(cptr))
         ircsprintf(pbuf, "%d ", chptr->mode.limit);
     }
+#ifdef INVITE_CHANNEL_FORWARDING
+  if (chptr->mode.mode & MODE_FORWARD) /* -goober */
+    {
+      *mbuf++ = 'f';
+      if (IsMember(cptr, chptr) || IsServer(cptr))
+	{
+	  strcat(pbuf, chptr->mode.invite_forward_channel_name);
+	  strcat(pbuf, " ");
+	}
+    }
+#endif
   if (*chptr->mode.key)
     {
       *mbuf++ = 'k';
@@ -683,6 +708,8 @@
   Link  *lp;
   char  *cp, *name;
   int   count = 0, send = 0;
+  char *bf_name;
+  int bf_size=0;
   
   cp = modebuf + strlen(modebuf);
   if (*parabuf) /* mode +l or +k xx */
@@ -692,11 +719,26 @@
       if (!(lp->flags & mask))
         continue;
       name = BANSTR(lp);
-        
-      if (strlen(parabuf) + strlen(name) + 10 < (size_t) MODEBUFLEN)
+#ifdef BAN_CHANNEL_FORWARDING     
+      if ((mask & CHFL_BAN) && (lp->value.banptr->ban_forward_chptr))
+	{
+	  bf_name = lp->value.banptr->ban_forward_chptr->chname;
+	  bf_size=1+strlen(bf_name);  // +1 => includes '!'
+	}
+      else bf_name=NULL;
+#endif
+
+      if (strlen(parabuf) + strlen(name) + bf_size + 10 < (size_t) MODEBUFLEN)
         {
           (void)strcat(parabuf, " ");
           (void)strcat(parabuf, name);
+#ifdef BAN_CHANNEL_FORWARDING     
+	  if (bf_name) 
+	    {
+	      (void)strcat(parabuf,"!");
+	      (void)strcat(parabuf, bf_name);
+	    }
+#endif
           count++;
           *cp++ = flag;
           *cp = '\0';
@@ -716,6 +758,13 @@
           if (count != 3)
             {
               (void)strcpy(parabuf, name);
+#ifdef BAN_CHANNEL_FORWARDING     
+	      if (bf_name) 
+		{
+		  (void)strcat(parabuf,"!");
+		  (void)strcat(parabuf, bf_name);
+		}
+#endif
               *cp++ = flag;
             }
           count = 0;
@@ -799,6 +848,12 @@
       if (t[-1] == ' ') t[-1] = '\0';
       sendto_one(cptr, "%s", buf);
     }
+
+#ifdef INVITE_CHANNEL_FORWARDING
+  if ((chptr->mode.mode & MODE_FORWARD) && (chptr->mode.invite_forward_channel))
+    sendto_one(cptr, ":%s MODE %s +f %s",me.name, chptr->chname, chptr->mode.invite_forward_channel->chname);
+#endif
+
   *parabuf = '\0';
   *modebuf = '+';
   modebuf[1] = '\0';
@@ -974,6 +1029,9 @@
   int   done_s = NO, done_p = NO;
 #endif
   int   done_i = NO, done_m = NO, done_n = NO, done_t = NO;
+#ifdef INVITE_CHANNEL_FORWARDING
+  int done_f = NO;
+#endif
   struct Client *who;
   Link  *lp;
   char  *curr = parv[0], c, *arg, plus = '+', *tmpc;
@@ -997,6 +1055,9 @@
   int   chan_op;
   int   self_lose_ops;
   int   user_mode;
+#ifdef BAN_CHANNEL_FORWARDING
+  char *fchname;
+#endif
 
   self_lose_ops = 0;
 
@@ -1689,6 +1750,27 @@
               arg = collapse(pretty_mask(arg));
             }
 
+#ifdef BAN_CHANNEL_FORWARDING
+	  /* add ban forwarding logic here
+	     parse the arg such that if there are
+	     2 ! in the banstr, then add a channel forward fchannel
+	     as in nick!host!fchannel
+	  */
+
+	  if ((fchname=banstr_get_channel(arg))) 
+	    {
+	      arg=banstr_remove_channel(arg);
+	      if (!ChannelExists(fchname))
+		{
+		  sendto_one(sptr, form_str(ERR_NOSUCHCHANNEL),
+			     me.name, sptr->name, "MODE +b redirection channel:");
+		  break;
+		}
+	      /* add test for CHANOPRIVSNEEDED in fchname here if needed */
+	    }
+	  
+#endif
+
           tmp = strlen(arg);
           if (len + tmp + 2 >= MODEBUFLEN)
             break;
@@ -1697,9 +1779,25 @@
                 ((whatt & MODE_DEL) && !del_id(chptr, arg, CHFL_BAN))))
             break;
 
+#ifdef BAN_CHANNEL_FORWARDING
+	  if ((whatt & MODE_ADD) && fchname)
+	    {
+	      add_ban_channel_forward(chptr,arg,fchname);
+	    }
+	  else
+	    del_ban_channel_forward(chptr,arg); /* also sets ptr to null if !fchname */
+#endif
+
           *mbufw++ = plus;
           *mbufw++ = 'b';
-          strcpy(pbufw, arg);
+	  strcpy(pbufw, arg);
+#ifdef BAN_CHANNEL_FORWARDING
+	  if ((whatt & MODE_ADD) && fchname)
+	    {
+	      strcat(pbufw,"!");
+	      strcat(pbufw,fchname);
+	    }
+#endif
           pbufw += strlen(pbufw);
           *pbufw++ = ' ';
           len += tmp + 1;
@@ -2125,6 +2223,78 @@
             }
           break;
 
+#ifdef INVITE_CHANNEL_FORWARDING
+	  /* invite channel forwarding -goober */
+        case 'f' :
+	  if (whatt == MODE_QUERY)
+	    break;
+	  
+          if (!isok)
+            {
+              if (MyClient(sptr))
+                sendto_one(sptr, form_str(ERR_CHANOPRIVSNEEDED), me.name, 
+                           sptr->name, chptr->chname);
+                return;
+            }
+
+          if(MyClient(sptr))
+            {
+              if(done_f)
+                break;
+              else
+                done_f = YES;
+            }
+
+          if(whatt == MODE_ADD)
+            {
+	      struct Channel *fchptr;
+
+	      if (parc-- <= 0)
+                {
+                  if (MyClient(sptr))
+                    sendto_one(sptr, form_str(ERR_NEEDMOREPARAMS),  me.name,
+			       sptr->name, "MODE +f");
+                  break;
+                }
+
+	      arg = check_string(*parv++);
+	      tmp = strlen(arg);
+	      
+	      if (!ChannelExists(arg))
+		{
+		  sendto_one(sptr, form_str(ERR_NOSUCHCHANNEL),
+			     me.name, sptr->name, "MODE +f redirection channel:");
+		  break;
+		}
+
+	      fchptr=hash_find_channel(arg,NULL);
+	      /* add test for CHANOPRIVSNEEDED in fchptr here if needed */
+
+	      if (len + tmp + 2 >= MODEBUFLEN)
+                break;
+		  
+	      chptr->mode.invite_forward_channel = fchptr;
+	      strncpy_irc(chptr->mode.invite_forward_channel_name, arg, CHANNELLEN);
+	      chptr->mode.mode |= MODE_FORWARD;
+	      *mbufw++ = '+';
+	      *mbufw++ = 'f';
+	      strcpy(pbufw,arg);
+	      pbufw += strlen(pbufw);
+	      *pbufw++ = ' ';
+	      len += tmp + 1;
+            }
+          else
+            {
+	      chptr->mode.mode &= ~MODE_FORWARD;
+	      *mbufw++ = '-';
+	      *mbufw++ = 'f';
+	      len += 2;
+	      chptr->mode.invite_forward_channel=NULL;
+	      chptr->mode.invite_forward_channel_name[0] = '\0';
+            }
+          break;
+#endif
+
         default:
           if (whatt == MODE_QUERY)
             break;
@@ -3113,7 +3283,23 @@
 
       if (MyConnect(sptr) && (i = can_join(sptr, chptr, key, &flags)))
         {
-          sendto_one(sptr,
+#ifdef INVITE_CHANNEL_FORWARDING
+	  if ((i==ERR_INVITEONLYCHAN) && (chptr->mode.mode & MODE_FORWARD))
+	    {
+	      attempt_channel_redirection(cptr,sptr,parc,parv,i,name,chptr,INVITE_FORWARD);
+	      continue;
+	    }
+	  else
+#endif
+#ifdef BAN_CHANNEL_FORWARDING
+	    if (i==ERR_BANNEDFROMCHAN)
+	    {
+	      attempt_channel_redirection(cptr,sptr,parc,parv,i,name,chptr,BAN_FORWARD);
+	      continue;
+	    }
+	  else
+#endif
+	    sendto_one(sptr,
                     form_str(i), me.name, parv[0], name);
 #ifdef ANTI_SPAMBOT
           if(successful_join_count > 0)
@@ -4257,6 +4443,12 @@
         args++;
         if (parc < 5+args) return 0;
         break;
+      case 'f':
+	mode.mode |= MODE_FORWARD;
+	strncpy_irc(mode.invite_forward_channel_name, parv[4+args], CHANNELLEN);
+	args++;
+	if (parc < 5+args) return 0;
+	break;
       }
 
   *parabuf = '\0';
@@ -4385,6 +4577,8 @@
         mode.limit = oldmode->limit;
       if (strcmp(mode.key, oldmode->key) < 0)
         strcpy(mode.key, oldmode->key);
+      if (oldmode->invite_forward_channel_name[0])
+ 	strncpy_irc(mode.invite_forward_channel_name, oldmode->invite_forward_channel_name, CHANNELLEN);
     }
 
   /* This loop unrolled below for speed
@@ -4551,6 +4745,15 @@
       strcat(parabuf, " ");
       pargs++;
     }
+  if (oldmode->invite_forward_channel_name[0] && !mode.invite_forward_channel_name[0])
+    {
+      if (what != -1)
+        {
+          *mbuf++ = '-';
+          what = -1;
+        }
+      *mbuf++ = 'f';
+    }
   if (mode.limit && oldmode->limit != mode.limit)
     {
       if (what != 1)
@@ -4578,6 +4781,18 @@
       strcat(parabuf, " ");
       pargs++;
     }
+  if (mode.invite_forward_channel_name[0] && strcmp(oldmode->invite_forward_channel_name, mode.invite_forward_channel_name))
+    {
+      if (what != 1)
+        {
+          *mbuf++ = '+';
+          what = 1;
+        }
+      *mbuf++ = 'f';
+      strcat(parabuf, mode.invite_forward_channel_name);
+      strcat(parabuf, " ");
+      pargs++;
+    }
   
   chptr->mode = mode;
 
@@ -4770,3 +4985,142 @@
 }
 
 
+#if defined(INVITE_CHANNEL_FORWARDING) || defined(BAN_CHANNEL_FORWARDING)
+/*
+** m_join_redirect called recursively from m_join 
+* -goober
+*/
+int m_join_redirect(struct Client *cptr,
+               struct Client *sptr,
+               int parc,
+               char *parv[],
+	       struct Channel* to_chptr)
+{
+  
+  char *parv1;
+
+  sendto_one(sptr,form_str(RPL_FORWARD),me.name,parv[0],parv[1],to_chptr->chname);
+
+  if (ChannelExists(to_chptr->chname))
+    {
+      parv1=parv[1];
+      parv[1]=to_chptr->chname;
+      m_join(cptr,sptr,parc,parv);
+      parv[1]=parv1;
+      return 1;
+    }
+  else
+    return 0;
+}
+#endif
+
+#ifdef BAN_CHANNEL_FORWARDING
+char *banstr_remove_channel(char *banstr)
+{
+  char *pos;
+  if ((pos=strrchr(banstr,'!'))!=strchr(banstr,'!')) *pos='\0';
+  return banstr;
+}
+
+char *banstr_get_channel(char *banstr)
+{
+  char *pos;
+  if ((pos=strrchr(banstr,'!'))!=strchr(banstr,'!')) 
+    return pos+1;
+  else
+    return 0;
+}
+
+void add_ban_channel_forward(struct Channel *chptr,char *banstr, char *to_chname)
+{
+  register Link *tmp;
+
+  for (tmp = chptr->banlist; tmp; tmp = tmp->next)
+    if ( match(BANSTR(tmp), banstr))
+      break;
+  if (tmp && ChannelExists(to_chname))
+    tmp->value.banptr->ban_forward_chptr=hash_find_channel(to_chname,NULL);
+}
+
+void del_ban_channel_forward(struct Channel *chptr,char *banstr)
+{
+  register Link *tmp;
+
+  for (tmp = chptr->banlist; tmp; tmp = tmp->next)
+    if ( match(BANSTR(tmp), banstr))
+      break;
+  if (tmp)
+    tmp->value.banptr->ban_forward_chptr=NULL;
+}
+
+
+struct Channel *get_ban_channel_forward(struct Client *cptr,struct Channel *chptr)
+{
+  register Link *tmp;
+  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) 
+    return tmp->value.banptr->ban_forward_chptr;
+  else
+    return 0;
+}
+#endif
+
+
+#if defined(INVITE_CHANNEL_FORWARDING) || defined(BAN_CHANNEL_FORWARDING)
+void attempt_channel_redirection(struct Client *cptr,
+				struct Client *sptr,
+				int parc,
+				char *parv[],
+				int err_flag,
+				char *name,
+				struct Channel *chptr,
+				int redir_type)
+{
+  static int recurse_level=0;
+  struct Channel *fchptr;
+  
+  if (redir_type==INVITE_FORWARD) 
+    {
+      fchptr = chptr->mode.invite_forward_channel;
+      if ((!fchptr) && (chptr->mode.invite_forward_channel_name[0]))
+	{
+	  /* We have a name but no pointer. Fix. -- asuffield */
+	  chptr->mode.invite_forward_channel = fchptr = hash_find_channel(chptr->mode.invite_forward_channel_name, NULL);
+	}
+    }
+  else
+    fchptr=get_ban_channel_forward(cptr,chptr);
+  
+  if (fchptr)
+    {
+      if (recurse_level<MAX_FORWARDING_RECURSION)
+	{
+	  recurse_level++;
+	  if (!m_join_redirect(cptr,sptr,parc,parv,fchptr))
+	    sendto_one(sptr,form_str(err_flag), me.name, parv[0], name);
+	  recurse_level=0;
+	}
+      else
+	{
+	  sendto_one(sptr,form_str(ERR_MAXFORWARDING), me.name, parv[0]);
+	  sendto_one(sptr,form_str(err_flag), me.name, parv[0], name);
+	}
+    }
+  else
+    sendto_one(sptr,form_str(err_flag), me.name, parv[0], name);
+}
+#endif
diff -ruN -x configure -x *~ -x ircd-hybrid-6.0.orig -x *.rej ircd-hybrid-6.0.orig/src/messages.tab ircd-hybrid-6.0/src/messages.tab
--- ircd-hybrid-6.0.orig/src/messages.tab	Sat May 26 14:04:01 2001
+++ ircd-hybrid-6.0/src/messages.tab	Sat May 26 13:55:56 2001
@@ -418,7 +418,7 @@
 /* 376 RPL_ENDOFMOTD, */        ":%s 376 %s :End of /MOTD command.",
 /* 377 RPL_MAP, */              ":%s 377 %s %s%s",
 /* 378 RPL_ENDOFMAP */          ":%s 378 %s :End of /MAP list.",
-/* 379 */       (char *)NULL,
+/* 379 RPL_FORWARD */           ":%s 379 %s :Forwarding from %s to %s",
 /* 380 */       (char *)NULL,
 /* 381 RPL_YOUREOPER, */        ":%s 381 %s :You have entered... the Twilight Zone!.",
 /* 382 RPL_REHASHING, */        ":%s 382 %s %s :Rehashing",
@@ -555,7 +555,8 @@
 /* 509 */       (char *)NULL,
 /* 510 */       (char *)NULL,
 /* 511 ERR_SILELISTFULL, */ ":%s 511 %s :Your silence list is full" ,
-/* 512 ERR_MAXMAPNODES */  ":%s 512 %s :Maximum nodes for /MAP"
+/* 512 ERR_MAXMAPNODES */  ":%s 512 %s :Maximum nodes for /MAP" ,
+/* 513 ERR_MAXFORWARDING */ ":%s 513 %s :Maximum forwarding reached"
 };
 
 
diff -ruN -x configure -x *~ -x ircd-hybrid-6.0.orig -x *.rej ircd-hybrid-6.0.orig/src/messages_cust.tab ircd-hybrid-6.0/src/messages_cust.tab
--- ircd-hybrid-6.0.orig/src/messages_cust.tab	Sat May 26 14:04:01 2001
+++ ircd-hybrid-6.0/src/messages_cust.tab	Sat May 26 13:55:56 2001
@@ -419,7 +419,7 @@
 /* 376 RPL_ENDOFMOTD, */        ":%s 376 %s :End of /MOTD command.",
 /* 377 RPL_MAP, */              ":%s 377 %s %s%s",
 /* 378 RPL_ENDOFMAP */          ":%s 378 %s :End of /MAP list.",
-/* 379 */       (char *)NULL,
+/* 379 RPL_FORWARD */           ":%s 379 %s :Forwarding from %s to %s",
 /* 380 */       (char *)NULL,
 /* 381 RPL_YOUREOPER, */        ":%s 381 %s :You are now a deity -- be nice or else!",
 /* 382 RPL_REHASHING, */        ":%s 382 %s %s :Rehashing the server configuration file, dear.",
@@ -556,7 +556,9 @@
 /* 509 */       (char *)NULL,
 /* 510 */       (char *)NULL,
 /* 511 ERR_SILELISTFULL, */ ":%s 511 %s :Your silence list is full" ,
-/* 512 ERR_MAXMAPNODES */  ":%s 512 %s :Maximum nodes for /MAP"
+/* 512 ERR_MAXMAPNODES */  ":%s 512 %s :Maximum nodes for /MAP" ,
+/* 513 ERR_MAXFORWARDING */ ":%s 513 %s :Maximum forwarding reached"
+
 };
 
 
