Index: include/channel.h
===================================================================
RCS file: /home/coder-com/cvs/ircu2.10/include/channel.h,v
retrieving revision 1.32
diff -u -r1.32 channel.h
--- include/channel.h	2002/04/03 21:16:01	1.32
+++ include/channel.h	2002/04/12 13:47:52
@@ -95,6 +95,8 @@
 #define MODE_BURSTADDED	0x80000	/* channel was created by a BURST */
 #define MODE_UPASS	0x100000
 #define MODE_APASS	0x200000
+#define MODE_EMPTY	0x400000 /* LazyLeaf: no more locals, remove channel asap */
+
 /*
  * mode flags which take another parameter (With PARAmeterS)
  */
@@ -222,15 +224,20 @@
   time_t             creationtime;
   time_t             topic_time;
   unsigned int       users;
+  unsigned int       locals;
   struct Membership* members;
   struct SLink*      invites;
   struct SLink*      banlist;
   struct Mode        mode;
   char               topic[TOPICLEN + 1];
   char               topic_nick[NICKLEN + 1];
-  char               chname[1];
+  unsigned long      ll_bits;		/* LazyLeaf */
+  char               chname[1];		/* This *must* be last */
 };
 
+#define LeafKnowsChannel(c,x)	(cli_serv(c)->ll_mask & (x)->ll_bits)
+#define LL_ALL			(~0UL)
+
 struct ListingArgs {
   time_t max_time;
   time_t min_time;
@@ -350,6 +357,7 @@
 extern int is_zombie(struct Client *cptr, struct Channel *chptr);
 extern int has_voice(struct Client *cptr, struct Channel *chptr);
 extern void send_channel_modes(struct Client *cptr, struct Channel *chptr);
+extern void ll_send_channel(struct Client *cptr, struct Channel *chptr);
 extern char *pretty_mask(char *mask);
 extern void del_invite(struct Client *cptr, struct Channel *chptr);
 extern void list_next_channels(struct Client *cptr, int nr);
Index: include/client.h
===================================================================
RCS file: /home/coder-com/cvs/ircu2.10/include/client.h,v
retrieving revision 1.26
diff -u -r1.26 client.h
--- include/client.h	2002/04/05 11:36:58	1.26
+++ include/client.h	2002/04/12 13:47:52
@@ -369,6 +369,7 @@
 #define FLAGS_DOID      0x00040000      /* I-lines say must use ident return */
 #define FLAGS_NONL      0x00080000      /* No \n in buffer */
 #define FLAGS_TS8       0x00100000      /* Why do you want to know? */
+#define FLAGS_LAZY	0x00200000	/* LazyLeaf */
 #define FLAGS_MAP       0x00800000      /* Show server on the map */
 #define FLAGS_JUNCTION  0x01000000      /* Junction causing the net.burst */
 #define FLAGS_DEAF      0x02000000      /* Makes user deaf */
@@ -413,6 +414,7 @@
 #define IsAccount(x)            (cli_flags(x) & FLAGS_ACCOUNT)
 #define IsHiddenHost(x)		(cli_flags(x) & FLAGS_HIDDENHOST)
 #define HasHiddenHost(x)	(IsAccount(x) && IsHiddenHost(x))
+#define IsLazy(x)		(cli_flags(x) & FLAGS_LAZY)
 
 #define IsPrivileged(x)         (IsAnOper(x) || IsServer(x))
 
@@ -435,6 +437,7 @@
 #define SetService(x)           (cli_flags(x) |= FLAGS_SERVICE)
 #define SetAccount(x)           (cli_flags(x) |= FLAGS_ACCOUNT)
 #define SetHiddenHost(x)	(cli_flags(x) |= FLAGS_HIDDENHOST)
+#define SetLazy(x)		(cli_flags(x) |= FLAGS_LAZY)
 
 #define ClearAccess(x)          (cli_flags(x) &= ~FLAGS_CHKACCESS)
 #define ClearBurst(x)           (cli_flags(x) &= ~FLAGS_BURST)
@@ -450,6 +453,7 @@
 #define ClearWallops(x)         (cli_flags(x) &= ~FLAGS_WALLOP)
 #define ClearServNotice(x)      (cli_flags(x) &= ~FLAGS_SERVNOTICE)
 #define ClearHiddenHost(x)	(cli_flags(x) &= ~FLAGS_HIDDENHOST)
+#define ClearLazy(x)		(cli_flags(x) &= ~FLAGS_LAZY)
 
 /* free flags */
 #define FREEFLAG_SOCKET	0x0001	/* socket needs to be freed */
Index: include/handlers.h
===================================================================
RCS file: /home/coder-com/cvs/ircu2.10/include/handlers.h,v
retrieving revision 1.16
diff -u -r1.16 handlers.h
--- include/handlers.h	2002/03/19 22:03:36	1.16
+++ include/handlers.h	2002/04/12 13:47:52
@@ -183,6 +183,7 @@
 extern int ms_gline(struct Client*, struct Client*, int, char*[]);
 extern int ms_info(struct Client*, struct Client*, int, char*[]);
 extern int ms_invite(struct Client*, struct Client*, int, char*[]);
+extern int ms_forget(struct Client*, struct Client*, int, char*[]);
 extern int ms_join(struct Client*, struct Client*, int, char*[]);
 extern int ms_jupe(struct Client*, struct Client*, int, char*[]);
 extern int ms_kick(struct Client*, struct Client*, int, char*[]);
Index: include/ircd_features.h
===================================================================
RCS file: /home/coder-com/cvs/ircu2.10/include/ircd_features.h,v
retrieving revision 1.15
diff -u -r1.15 ircd_features.h
--- include/ircd_features.h	2002/04/03 15:23:47	1.15
+++ include/ircd_features.h	2002/04/12 13:47:52
@@ -37,6 +37,7 @@
   FEAT_KILL_IPMISMATCH,
   FEAT_IDLE_FROM_MSG,
   FEAT_HUB,
+  FEAT_LAZY_LEAF,
   FEAT_WALLOPS_OPER_ONLY,
   FEAT_NODNS,
   FEAT_RANDOM_SEED,
Index: include/msg.h
===================================================================
RCS file: /home/coder-com/cvs/ircu2.10/include/msg.h,v
retrieving revision 1.12
diff -u -r1.12 msg.h
--- include/msg.h	2002/02/14 00:20:40	1.12
+++ include/msg.h	2002/04/12 13:47:52
@@ -330,6 +330,10 @@
 #define TOK_ACCOUNT		"AC"
 #define CMD_ACCOUNT		MSG_ACCOUNT, TOK_ACCOUNT
 
+#define MSG_FORGET		"FORGET"	/* FORGET */
+#define TOK_FORGET		"FO"
+#define CMD_FORGET		MSG_FORGET, TOK_FORGET
+
 #define MSG_POST                "POST"          /* POST */
 #define TOK_POST                "POST"
 
Index: include/s_serv.h
===================================================================
RCS file: /home/coder-com/cvs/ircu2.10/include/s_serv.h,v
retrieving revision 1.6
diff -u -r1.6 s_serv.h
--- include/s_serv.h	2001/06/08 23:12:16	1.6
+++ include/s_serv.h	2002/04/12 13:47:52
@@ -12,9 +12,11 @@
 
 struct ConfItem;
 struct Client;
+struct Channel;
 
 extern unsigned int max_connection_count;
 extern unsigned int max_client_count;
+extern unsigned long GlobalLeafBits;
 
 /*
  * Prototypes
@@ -24,5 +26,8 @@
 extern int a_kills_b_too(struct Client *a, struct Client *b);
 extern int server_estab(struct Client *cptr, struct ConfItem *aconf);
 
+extern int ll_add(struct Client *cptr);
+extern void ll_remove(struct Client *cptr);
+extern void ll_check_channel(struct Client *cptr, struct Channel *chptr);
 
 #endif /* INCLUDED_s_serv_h */
Index: include/send.h
===================================================================
RCS file: /home/coder-com/cvs/ircu2.10/include/send.h,v
retrieving revision 1.17
diff -u -r1.17 send.h
--- include/send.h	2002/02/14 00:20:41	1.17
+++ include/send.h	2002/04/12 13:47:52
@@ -47,6 +47,12 @@
 				  const char *tok, struct Client *one,
 				  const char *pattern, ...);
 
+/* Same as above, but only when the server's ll_mask matches */
+extern void sendcmdto_mask_butone(struct Client *from, const char *cmd,
+				  const char *tok, unsigned long ll_mask,
+				  struct Client *one,
+				  const char *pattern, ...);
+
 /* Send command to all channels user is on */
 extern void sendcmdto_common_channels_butone(struct Client *from,
 					     const char *cmd,
Index: include/struct.h
===================================================================
RCS file: /home/coder-com/cvs/ircu2.10/include/struct.h,v
retrieving revision 1.3
diff -u -r1.3 struct.h
--- include/struct.h	2002/02/14 00:20:41	1.3
+++ include/struct.h	2002/04/12 13:47:52
@@ -52,6 +52,7 @@
   unsigned short  nn_last;      /* Last numeric nick for p9 servers only */
   unsigned int    nn_mask;      /* [Remote] FD_SETSIZE - 1 */
   char          nn_capacity[4]; /* numeric representation of server capacity */
+  unsigned long   ll_mask;	/* LazyLeaf mask */
 
   char *last_error_msg;         /* Allocated memory with last message receive with an ERROR */
   char by[NICKLEN + 1];
Index: ircd/Makefile.in
===================================================================
RCS file: /home/coder-com/cvs/ircu2.10/ircd/Makefile.in,v
retrieving revision 1.43
diff -u -r1.43 Makefile.in
--- ircd/Makefile.in	2002/04/03 21:16:01	1.43
+++ ircd/Makefile.in	2002/04/12 13:47:52
@@ -120,6 +120,7 @@
 	m_die.c \
 	m_endburst.c \
 	m_error.c \
+	m_forget.c \
 	m_get.c \
 	m_gline.c \
 	m_help.c \
Index: ircd/channel.c
===================================================================
RCS file: /home/coder-com/cvs/ircu2.10/ircd/channel.c,v
retrieving revision 1.81
diff -u -r1.81 channel.c
--- ircd/channel.c	2002/04/03 21:16:01	1.81
+++ ircd/channel.c	2002/04/12 13:47:52
@@ -46,6 +46,7 @@
 #include "s_conf.h"
 #include "s_debug.h"
 #include "s_misc.h"
+#include "s_serv.h"
 #include "s_user.h"
 #include "send.h"
 #include "struct.h"
@@ -533,6 +534,8 @@
       remove_destruct_event(chptr);
     ++chptr->users;
     ++((cli_user(who))->joined);
+    if (MyUser(who))
+      ++chptr->locals;
   }
 }
 
@@ -562,6 +565,8 @@
     (cli_user(member->user))->channel = member->next_channel;
 
   --(cli_user(member->user))->joined;
+  if (MyUser(member->user))
+    --chptr->locals;
 
   member->next_member = membershipFreeList;
   membershipFreeList = member;
@@ -587,6 +592,17 @@
   struct Membership* member;
   assert(0 != chptr);
 
+  if (chptr->mode.mode & MODE_EMPTY) {
+    assert(feature_bool(FEAT_LAZY_LEAF));
+  
+    /* Channel has no more locals, free it */
+    do {
+      assert(!MyUser(chptr->members->user));
+    } while (remove_member_from_channel(chptr->members));
+
+    return;
+  }
+
   if ((member = find_member_link(chptr, cptr))) {
     if (remove_member_from_channel(member)) {
       if (channel_all_zombies(chptr)) {
@@ -1417,6 +1433,7 @@
     for (; acptr != &me; acptr = (cli_serv(acptr))->up)
       if (acptr == (cli_user(who))->server)   /* Case d) (server 5) */
       {
+        ll_check_channel(who, chptr);
         remove_user_from_channel(who, chptr);
         return;
       }
@@ -1769,8 +1786,8 @@
 
     if (mbuf->mb_dest & MODEBUF_DEST_OPMODE) {
       /* If OPMODE was set, we're propagating the mode as an OPMODE message */
-      sendcmdto_serv_butone(mbuf->mb_source, CMD_OPMODE, mbuf->mb_connect,
-			    "%H %s%s%s%s%s%s", mbuf->mb_channel,
+      sendcmdto_mask_butone(mbuf->mb_source, CMD_OPMODE, mbuf->mb_channel->ll_bits,
+			    mbuf->mb_connect, "%H %s%s%s%s%s%s", mbuf->mb_channel,
 			    rembuf_i ? "-" : "", rembuf, addbuf_i ? "+" : "",
 			    addbuf, remstr, addstr);
     } else if (mbuf->mb_dest & MODEBUF_DEST_BOUNCE) {
@@ -1789,14 +1806,16 @@
        * we send the actual channel TS unless this is a HACK3 or a HACK4
        */
       if (IsServer(mbuf->mb_source))
-	sendcmdto_serv_butone(mbuf->mb_source, CMD_MODE, mbuf->mb_connect,
+	sendcmdto_mask_butone(mbuf->mb_source, CMD_MODE,
+			      mbuf->mb_channel->ll_bits, mbuf->mb_connect,
 			      "%H %s%s%s%s%s%s %Tu", mbuf->mb_channel,
 			      rembuf_i ? "-" : "", rembuf, addbuf_i ? "+" : "",
 			      addbuf, remstr, addstr,
 			      (mbuf->mb_dest & MODEBUF_DEST_HACK4) ? 0 :
 			      mbuf->mb_channel->creationtime);
       else
-	sendcmdto_serv_butone(mbuf->mb_source, CMD_MODE, mbuf->mb_connect,
+	sendcmdto_mask_butone(mbuf->mb_source, CMD_MODE, 
+			      mbuf->mb_channel->ll_bits, mbuf->mb_connect,
 			      "%H %s%s%s%s%s%s", mbuf->mb_channel,
 			      rembuf_i ? "-" : "", rembuf, addbuf_i ? "+" : "",
 			      addbuf, remstr, addstr);
@@ -3143,7 +3162,7 @@
 
     /* send notification to all servers */
     if (jbuf->jb_type != JOINBUF_TYPE_CREATE && !IsLocalChannel(chan->chname))
-      sendcmdto_serv_butone(jbuf->jb_source, CMD_JOIN, jbuf->jb_connect,
+      sendcmdto_mask_butone(jbuf->jb_source, CMD_JOIN, chan->ll_bits, jbuf->jb_connect,
 			    "%H %Tu", chan, chan->creationtime);
 
     /* Send the notification to the channel */
@@ -3192,9 +3211,37 @@
     build_string(chanlist, &chanlist_i,
 		 jbuf->jb_channels[i] ? jbuf->jb_channels[i]->chname : "0", 0,
 		 i == 0 ? '\0' : ',');
-    if (JOINBUF_TYPE_PART == jbuf->jb_type)
+
+    /*
+     * For lazy leafs, joins/parts have to be sent separately for 
+     * each channel. 
+     */
+    switch (jbuf->jb_type) {
+    case JOINBUF_TYPE_CREATE:
+      sendcmdto_mask_butone(jbuf->jb_source, CMD_CREATE,
+			    jbuf->jb_channels[i] ? jbuf->jb_channels[i]->ll_bits : LL_ALL,
+			    jbuf->jb_connect, "%s %Tu",
+			    jbuf->jb_channels[i] ? jbuf->jb_channels[i]->chname : "0",
+			    jbuf->jb_create);
+      break;
+
+    case JOINBUF_TYPE_PART:
+      sendcmdto_mask_butone(jbuf->jb_source, CMD_PART,
+			    jbuf->jb_channels[i]->ll_bits,
+			    jbuf->jb_connect,
+			    jbuf->jb_comment ? "%s :%s" : "%s",
+			    jbuf->jb_channels[i]->chname,
+			    jbuf->jb_comment);
+      break;
+    }
+
+    if (JOINBUF_TYPE_PART == jbuf->jb_type) {
+      /* Check now, as remove_user* may free the channel */
+      ll_check_channel(jbuf->jb_source, jbuf->jb_channels[i]);
+	
       /* Remove user from channel */
       remove_user_from_channel(jbuf->jb_source, jbuf->jb_channels[i]);
+    }
 
     jbuf->jb_channels[i] = 0; /* mark slot empty */
   }
@@ -3204,6 +3251,7 @@
 		      STARTJOINLEN : STARTCREATELEN) +
 		     (jbuf->jb_comment ? strlen(jbuf->jb_comment) + 2 : 0));
 
+#if 0
   /* and send the appropriate command */
   switch (jbuf->jb_type) {
   case JOINBUF_TYPE_CREATE:
@@ -3217,6 +3265,7 @@
 			  jbuf->jb_comment);
     break;
   }
+#endif
 
   return 0;
 }
Index: ircd/ircd_features.c
===================================================================
RCS file: /home/coder-com/cvs/ircu2.10/ircd/ircd_features.c,v
retrieving revision 1.19
diff -u -r1.19 ircd_features.c
--- ircd/ircd_features.c	2002/04/03 15:23:48	1.19
+++ ircd/ircd_features.c	2002/04/12 13:47:52
@@ -244,6 +244,7 @@
   F_B(KILL_IPMISMATCH, FEAT_OPER, 0, 0),
   F_B(IDLE_FROM_MSG, 0, 1, 0),
   F_B(HUB, 0, 0, 0),
+  F_B(LAZY_LEAF, 0, 0, 0),
   F_B(WALLOPS_OPER_ONLY, 0, 0, 0),
   F_B(NODNS, 0, 0, 0),
   F_N(RANDOM_SEED, FEAT_NODISP, random_seed_set, 0, 0, 0, 0, 0, 0),
Index: ircd/m_burst.c
===================================================================
RCS file: /home/coder-com/cvs/ircu2.10/ircd/m_burst.c,v
retrieving revision 1.16
diff -u -r1.16 m_burst.c
--- ircd/m_burst.c	2002/03/27 22:30:24	1.16
+++ ircd/m_burst.c	2002/04/12 13:47:52
@@ -89,6 +89,7 @@
 #include "ircd_policy.h"
 #include "ircd_reply.h"
 #include "ircd_string.h"
+#include "ircd_features.h"
 #include "list.h"
 #include "match.h"
 #include "msg.h"
@@ -463,6 +464,11 @@
       lp->flags &= (CHFL_BAN | CHFL_BAN_IPMASK); /* reset the flag */
       lp_p = &(*lp_p)->next;
     }
+  }
+
+  if (IsLazy(cptr) && !LeafKnowsChannel(cptr, chptr)) {
+    chptr->ll_bits |= cli_serv(cptr)->ll_mask;
+    send_channel_modes(cptr, chptr);
   }
 
   return mbuf ? modebuf_flush(mbuf) : 0;
Index: ircd/m_clearmode.c
===================================================================
RCS file: /home/coder-com/cvs/ircu2.10/ircd/m_clearmode.c,v
retrieving revision 1.20
diff -u -r1.20 m_clearmode.c
--- ircd/m_clearmode.c	2002/02/14 00:20:42	1.20
+++ ircd/m_clearmode.c	2002/04/12 13:47:52
@@ -234,8 +234,8 @@
 
   /* Then send it */
   if (!IsLocalChannel(chptr->chname))
-    sendcmdto_serv_butone(sptr, CMD_CLEARMODE, cptr, "%H %s", chptr,
-			  control_buf);
+    sendcmdto_mask_butone(sptr, CMD_CLEARMODE, chptr->ll_bits, cptr,
+			  "%H %s", chptr, control_buf);
 
   return 0;
 }
Index: ircd/m_create.c
===================================================================
RCS file: /home/coder-com/cvs/ircu2.10/ircd/m_create.c,v
retrieving revision 1.12
diff -u -r1.12 m_create.c
--- ircd/m_create.c	2002/02/14 00:20:42	1.12
+++ ircd/m_create.c	2002/04/12 13:47:52
@@ -154,6 +154,7 @@
       continue;
 
     if ((chptr = FindChannel(name))) {
+      int hack2 = IsLazy(cptr) ? 0 : MODEBUF_DEST_HACK2;
       name = chptr->chname;
 
       /* Check if we need to bounce a mode */
@@ -162,7 +163,7 @@
 	   chptr->creationtime != MAGIC_REMOTE_JOIN_TS)) {
 	modebuf_init(&mbuf, sptr, cptr, chptr,
 		     (MODEBUF_DEST_SERVER |  /* Send mode to server */
-		      MODEBUF_DEST_HACK2  |  /* Send a HACK(2) message */
+		      hack2               |  /* Send a HACK(2) message */
 		      MODEBUF_DEST_BOUNCE)); /* And bounce the mode */
 
 	modebuf_mode_client(&mbuf, MODE_ADD | MODE_CHANOP, sptr);
@@ -180,6 +181,11 @@
     joinbuf_join(badop ? &join : &create, chptr,
 		 (badop || IsModelessChannel(name)) ?
 		 CHFL_DEOPPED : CHFL_CHANOP);
+
+    if (IsLazy(cptr) && !LeafKnowsChannel(cptr, chptr)) {
+      chptr->ll_bits |= cli_serv(cptr)->ll_mask;
+      send_channel_modes(cptr, chptr);
+    }
   }
 
   joinbuf_flush(&join); /* flush out the joins and creates */
Index: ircd/m_join.c
===================================================================
RCS file: /home/coder-com/cvs/ircu2.10/ircd/m_join.c,v
retrieving revision 1.19
diff -u -r1.19 m_join.c
--- ircd/m_join.c	2002/03/13 09:19:21	1.19
+++ ircd/m_join.c	2002/04/12 13:47:52
@@ -373,6 +373,11 @@
 	chptr->creationtime = creation;
     } 
 
+    if (IsLazy(cptr) && !LeafKnowsChannel(cptr, chptr)) {
+      chptr->ll_bits |= cli_serv(cptr)->ll_mask;
+      send_channel_modes(cptr, chptr);
+    }
+
     joinbuf_join(&join, chptr, flags);
   }
 
Index: ircd/m_kick.c
===================================================================
RCS file: /home/coder-com/cvs/ircu2.10/ircd/m_kick.c,v
retrieving revision 1.8
diff -u -r1.8 m_kick.c
--- ircd/m_kick.c	2002/03/13 09:19:21	1.8
+++ ircd/m_kick.c	2002/04/12 13:47:52
@@ -150,7 +150,7 @@
   comment = EmptyString(parv[parc - 1]) ? parv[0] : parv[parc - 1];
 
   if (!IsLocalChannel(name))
-    sendcmdto_serv_butone(sptr, CMD_KICK, cptr, "%H %C :%s", chptr, who,
+    sendcmdto_mask_butone(sptr, CMD_KICK, chptr->ll_bits, cptr, "%H %C :%s", chptr, who,
 			  comment);
 
   sendcmdto_channel_butserv_butone(sptr, CMD_KICK, chptr, NULL, "%H %C :%s", chptr, who,
@@ -228,7 +228,7 @@
     }
   } else {
     /* Propagate kick... */
-    sendcmdto_serv_butone(sptr, CMD_KICK, cptr, "%H %C :%s", chptr, who,
+    sendcmdto_mask_butone(sptr, CMD_KICK, chptr->ll_bits, cptr, "%H %C :%s", chptr, who,
 			  comment);
 
     if (member) { /* and tell the channel about it */
Index: ircd/m_server.c
===================================================================
RCS file: /home/coder-com/cvs/ircu2.10/ircd/m_server.c,v
retrieving revision 1.26
diff -u -r1.26 m_server.c
--- ircd/m_server.c	2002/03/19 22:03:36	1.26
+++ ircd/m_server.c	2002/04/12 13:47:52
@@ -181,6 +181,9 @@
       case 's':
 	SetService(cptr);
 	break;
+      case 'l':
+        SetLazy(cptr);
+	break;
       }
   }
 
Index: ircd/m_topic.c
===================================================================
RCS file: /home/coder-com/cvs/ircu2.10/ircd/m_topic.c,v
retrieving revision 1.10
diff -u -r1.10 m_topic.c
--- ircd/m_topic.c	2002/02/14 00:20:43	1.10
+++ ircd/m_topic.c	2002/04/12 13:47:52
@@ -115,8 +115,8 @@
    chptr->topic_time = CurrentTime;
    /* Fixed in 2.10.11: Don't propergate local topics */
    if (!IsLocalChannel(chptr->chname))
-     sendcmdto_serv_butone(sptr, CMD_TOPIC, cptr, "%H :%s", chptr,
-		           chptr->topic);
+     sendcmdto_mask_butone(sptr, CMD_TOPIC, chptr->ll_bits, cptr, "%H %Tu :%s", chptr,
+		           chptr->topic_time, chptr->topic);
    if (newtopic)
       sendcmdto_channel_butserv_butone(sptr, CMD_TOPIC, chptr, NULL,
       				       "%H :%s", chptr, chptr->topic);
Index: ircd/parse.c
===================================================================
RCS file: /home/coder-com/cvs/ircu2.10/ircd/parse.c,v
retrieving revision 1.34
diff -u -r1.34 parse.c
--- ircd/parse.c	2002/03/19 22:03:36	1.34
+++ ircd/parse.c	2002/04/12 13:47:53
@@ -577,6 +577,13 @@
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { m_ignore, m_ignore, ms_account, m_ignore, m_ignore }
   },
+  {
+    MSG_FORGET,
+    TOK_FORGET,
+    0, MAXPARA, MFLG_SLOW, 0,
+    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+    { m_ignore, m_ignore, ms_forget, m_ignore, m_ignore }
+  },
   /* This command is an alias for QUIT during the unregistered part of
    * of the server.  This is because someone jumping via a broken web
    * proxy will send a 'POST' as their first command - which we will
Index: ircd/s_bsd.c
===================================================================
RCS file: /home/coder-com/cvs/ircu2.10/ircd/s_bsd.c,v
retrieving revision 1.48
diff -u -r1.48 s_bsd.c
--- ircd/s_bsd.c	2002/04/03 06:45:49	1.48
+++ ircd/s_bsd.c	2002/04/12 13:47:53
@@ -465,7 +465,8 @@
   sendrawto_one(cptr, MSG_SERVER " %s 1 %Tu %Tu J%s %s%s +%s :%s",
                 cli_name(&me), cli_serv(&me)->timestamp, newts,
 		MAJOR_PROTOCOL, NumServCap(&me),
-		feature_bool(FEAT_HUB) ? "h" : "", cli_info(&me));
+		feature_bool(FEAT_HUB) ? "h" :
+		feature_bool(FEAT_LAZY_LEAF) ? "l" : "", cli_info(&me));
 
   return (IsDead(cptr)) ? 0 : 1;
 }
Index: ircd/s_misc.c
===================================================================
RCS file: /home/coder-com/cvs/ircu2.10/ircd/s_misc.c,v
retrieving revision 1.31
diff -u -r1.31 s_misc.c
--- ircd/s_misc.c	2002/04/02 00:26:47	1.31
+++ ircd/s_misc.c	2002/04/12 13:47:53
@@ -48,6 +48,7 @@
 #include "s_conf.h"
 #include "s_debug.h"
 #include "s_user.h"
+#include "s_serv.h"
 #include "send.h"
 #include "struct.h"
 #include "support.h"
@@ -477,6 +478,8 @@
     sendto_opmask_butone(0, SNO_NETWORK, "Net break: %C %C (%s)",
 			 cli_serv(victim)->up, victim, comment);
 
+    if (IsLazy(victim))
+      ll_remove(victim);
 #if defined(HEAD_IN_SAND_MAP) || defined(HEAD_IN_SAND_LINKS)    
     map_update(victim);
 #endif
Index: ircd/s_serv.c
===================================================================
RCS file: /home/coder-com/cvs/ircu2.10/ircd/s_serv.c,v
retrieving revision 1.28
diff -u -r1.28 s_serv.c
--- ircd/s_serv.c	2002/02/14 00:20:44	1.28
+++ ircd/s_serv.c	2002/04/12 13:47:53
@@ -36,6 +36,7 @@
 #include "ircd_string.h"
 #include "ircd_snprintf.h"
 #include "ircd_xopen.h"
+#include "ircd_log.h"
 #include "jupe.h"
 #include "list.h"
 #include "match.h"
@@ -61,6 +62,7 @@
 
 unsigned int max_connection_count = 0;
 unsigned int max_client_count = 0;
+unsigned long GlobalLeafBits = 0;
 
 int exit_new_server(struct Client *cptr, struct Client *sptr, const char *host,
                     time_t timestamp, const char *pattern, ...)
@@ -113,7 +115,8 @@
     sendrawto_one(cptr, MSG_SERVER " %s 1 %Tu %Tu J%s %s%s +%s :%s",
 		  cli_name(&me), cli_serv(&me)->timestamp,
 		  cli_serv(cptr)->timestamp, MAJOR_PROTOCOL, NumServCap(&me),
-		  feature_bool(FEAT_HUB) ? "h" : "",
+		  feature_bool(FEAT_HUB) ? "h" :
+		  feature_bool(FEAT_LAZY_LEAF) ? "l" : "",
 		  *(cli_info(&me)) ? cli_info(&me) : "IRCers United");
     /*
      * Don't charge this IP# for connecting
@@ -135,6 +138,9 @@
 
   SetBurst(cptr);
 
+  if (IsLazy(cptr) && !ll_add(cptr))
+    ClearLazy(cptr);
+
 /*    nextping = CurrentTime; */
 
   /*
@@ -241,6 +247,7 @@
    * Last, send the BURST.
    * (Or for 2.9 servers: pass all channels plus statuses)
    */
+  if (!IsLazy(cptr))
   {
     struct Channel *chptr;
     for (chptr = GlobalChannelList; chptr; chptr = chptr->next)
@@ -252,3 +259,42 @@
   return 0;
 }
 
+int ll_add(struct Client *cptr)
+{
+  int i = 1;
+
+  assert(IsLazy(cptr));
+
+  while ((GlobalLeafBits & i) && i < 0x80000000)
+    i <<= 1;
+  if (GlobalLeafBits & i) {
+    sendto_opmask_butone(NULL, SNO_OLDSNO, "No more bits for LazyLeaf %s.", cli_name(cptr));
+    return 0;
+  }
+  GlobalLeafBits |= i;
+  cli_serv(cptr)->ll_mask = i;
+  log_write(LS_DEBUG, L_DEBUG, 0, "Added LazyLeaf %s with mask 0x%lx. GlobalLeafBits=0x%lx", cli_name(cptr), i, GlobalLeafBits);
+  return 1;
+}
+
+void ll_remove(struct Client *cptr)
+{
+  struct Channel *chptr;
+
+  assert(IsLazy(cptr));
+
+  for (chptr = GlobalChannelList; chptr; chptr = chptr->next)
+    chptr->ll_bits &= ~cli_serv(cptr)->ll_mask;
+
+  GlobalLeafBits &= ~cli_serv(cptr)->ll_mask;
+  log_write(LS_DEBUG, L_DEBUG, 0, "Removed LazyLeaf %s with mask 0x%lx. GlobalLeafBits=0x%lx", cli_name(cptr), cli_serv(cptr)->ll_mask, GlobalLeafBits);
+}
+
+void ll_check_channel(struct Client *cptr, struct Channel *chptr)
+{
+  if (feature_bool(FEAT_LAZY_LEAF) && MyUser(cptr) && chptr->locals <= 1) {
+    log_write(LS_DEBUG, L_DEBUG, 0, "LazyLeaf: Channel %s has no more locals", chptr->chname);
+    sendcmdto_serv_butone(&me, CMD_FORGET, NULL, "%s", chptr->chname);
+    chptr->mode.mode |= MODE_EMPTY;
+  }
+}
Index: ircd/send.c
===================================================================
RCS file: /home/coder-com/cvs/ircu2.10/ircd/send.c,v
retrieving revision 1.46
diff -u -r1.46 send.c
--- ircd/send.c	2002/02/14 00:20:45	1.46
+++ ircd/send.c	2002/04/12 13:47:53
@@ -328,6 +328,35 @@
   msgq_clean(mb);
 }
 
+void sendcmdto_mask_butone(struct Client *from, const char *cmd,
+			   const char *tok, unsigned long ll_mask,
+			   struct Client *one,
+			   const char *pattern, ...)
+{
+  struct VarData vd;
+  struct MsgBuf *mb;
+  struct DLink *lp;
+
+  vd.vd_format = pattern; /* set up the struct VarData for %v */
+  va_start(vd.vd_args, pattern);
+
+  /* use token */
+  mb = msgq_make(&me, "%C %s %v", from, tok, &vd);
+  va_end(vd.vd_args);
+
+  /* send it to our downlinks */
+  for (lp = cli_serv(&me)->down; lp; lp = lp->next) {
+    if (one && lp->value.cptr == cli_from(one))
+      continue;
+    if (IsLazy(lp->value.cptr) && !(cli_serv(lp->value.cptr)->ll_mask & ll_mask))
+      continue;
+    send_buffer(lp->value.cptr, mb, 0);
+  }
+
+  msgq_clean(mb);
+}
+
+
 /*
  * Send a (prefix) command originating from <from> to all channels
  * <from> is locally on.  <from> must be a user. <tok> is ignored in
--- /dev/null	Thu Aug 24 12:00:32 2000
+++ doc/readme.lazylinks	Fri Apr 12 16:47:36 2002
@@ -0,0 +1,46 @@
+Concept
+~~~~~~~
+The idea behind lazy links is that leafs often don't need much of the
+state information they are sent. Currently, only lazy channels are
+implemented; this means lazy leafs will only be burst channels that
+they have local users on.
+
+Protocol
+~~~~~~~~
+If a leaf has FEAT_LAZY_LEAF set, it sends a +l flag in the SERVER message
+it sends to its hub (note that if FEAT_HUB is also set, it takes precedence
+over FEAT_LAZY_LEAF). The hub will then mark this leaf as 'lazy', and will
+not burst any channels to it. The hub will also keep a bitmask of which leaves
+know which channels. Subsequently, when the leaf tries to announce a channel
+to its hub (via a BURST, JOIN or CREATE) and the leaf doesn't "know" about
+that channel from the hub's point of view, the hub will send a full BURST of
+the channel back to the leaf, and mark the channel as "known" to the leaf.
+Note that a server with FEAT_LAZY_LEAF set *will* accept BURST messages outside
+of net.burst. When a channel has no more local clients, the leaf will send a
+FORGET message to the hub and destroy the channel locally. Upon receipt of this
+meessage, the hub will remove the "known" bit for that channel/leaf pair, and
+it will burst the channel again if the leaf tries to create it later on. The
+FORGET message has the following syntax:
+	<server numeric> FO <#channel>
+
+Code
+~~~~
+struct Server has a ll_mask field which is assigned to each lazy leaf on its
+uplink hub. Every leaf gets a bit, so the maximum number of leafs is 32 on 
+32-bit machines. struct Channel now has a ll_bits bitmask field which stores
+which leaves "know" the channel. A new sendcmd_to_mask_butone function was
+used instead of sendcmdto_serv_butone which doesn't send to lazy leaves that
+don't match the specified mask (currently, chptr->ll_bits).
+
+Bugs
+~~~~
+This documentation is less than complete.
+
+Commands like LIST, TOPIC, MODE issued on a lazy leaf for channels that hasn't
+been burst to it will incorrectly report the channels doesn't exist. This should
+be handled by forwarding those messages to its uplink.
+
+joinbuf_flush now sends each join/part as a separate message, because they each
+have to be matched against the leaves' "known channel" bits.
+
+Probably more.
--- /dev/null	Thu Aug 24 12:00:32 2000
+++ ircd/m_forget.c	Wed Apr  3 23:07:14 2002
@@ -0,0 +1,123 @@
+/*
+ * IRC - Internet Relay Chat, ircd/m_forget.c
+ * Copyright (C) 2002 Alex Badea <vampire@p16.pub.ro>
+ *
+ * See file AUTHORS in IRC package for additional names of
+ * the programmers.
+ *
+ * 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.
+ *
+ * $Id: lazy.diff,v 1.2 2002/04/12 13:50:27 vampire Exp $
+ */
+
+/*
+ * m_functions execute protocol messages on this server:
+ *
+ *    cptr    is always NON-NULL, pointing to a *LOCAL* client
+ *            structure (with an open socket connected!). This
+ *            identifies the physical socket where the message
+ *            originated (or which caused the m_function to be
+ *            executed--some m_functions may call others...).
+ *
+ *    sptr    is the source of the message, defined by the
+ *            prefix part of the message if present. If not
+ *            or prefix not found, then sptr==cptr.
+ *
+ *            (!IsServer(cptr)) => (cptr == sptr), because
+ *            prefixes are taken *only* from servers...
+ *
+ *            (IsServer(cptr))
+ *                    (sptr == cptr) => the message didn't
+ *                    have the prefix.
+ *
+ *                    (sptr != cptr && IsServer(sptr) means
+ *                    the prefix specified servername. (?)
+ *
+ *                    (sptr != cptr && !IsServer(sptr) means
+ *                    that message originated from a remote
+ *                    user (not local).
+ *
+ *            combining
+ *
+ *            (!IsServer(sptr)) means that, sptr can safely
+ *            taken as defining the target structure of the
+ *            message in this server.
+ *
+ *    *Always* true (if 'parse' and others are working correct):
+ *
+ *    1)      sptr->from == cptr  (note: cptr->from == cptr)
+ *
+ *    2)      MyConnect(sptr) <=> sptr == cptr (e.g. sptr
+ *            *cannot* be a local connection, unless it's
+ *            actually cptr!). [MyConnect(x) should probably
+ *            be defined as (x == x->from) --msa ]
+ *
+ *    parc    number of variable parameter strings (if zero,
+ *            parv is allowed to be NULL)
+ *
+ *    parv    a NULL terminated list of parameter pointers,
+ *
+ *                    parv[0], sender (prefix string), if not present
+ *                            this points to an empty string.
+ *                    parv[1]...parv[parc-1]
+ *                            pointers to additional parameters
+ *                    parv[parc] == NULL, *always*
+ *
+ *            note:   it is guaranteed that parv[0]..parv[parc-1] are all
+ *                    non-NULL pointers.
+ */
+#include "config.h"
+
+#include "client.h"
+#include "hash.h"
+#include "ircd.h"
+#include "ircd_reply.h"
+#include "ircd_string.h"
+#include "msg.h"
+#include "numeric.h"
+#include "numnicks.h"
+#include "send.h"
+#include "channel.h"
+#include "ircd_log.h"
+
+#include <assert.h>
+#include <stdlib.h>
+
+/*
+ * ms_forget - server message handler
+ */
+int ms_forget(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
+{
+  struct Channel *chptr;
+
+  assert(0 != cptr);
+  assert(0 != sptr);
+  assert(IsServer(cptr));
+
+  if (parc < 2)
+    return need_more_params(sptr, "FORGET");
+
+  /*
+   * Only lazy leafs may forget about channels.
+   * Ignore forget messages for channels that don't exist.
+   */
+  if (!IsLazy(cptr) || !(chptr = FindChannel(parv[1])))
+    return 0;
+
+  chptr->ll_bits &= ~cli_serv(cptr)->ll_mask;
+  log_write(LS_DEBUG, L_DEBUG, 0, "LazyLeaf %s forgot about %s", cli_name(cptr), chptr->chname);
+
+  return 0;
+}
