]> jfr.im git - irc/quakenet/snircd-patchqueue.git/commitdiff
some more patches: split functionality, make stats output clearer, start to add ...
authorwiebe <redacted>
Sun, 18 Jan 2009 16:53:15 +0000 (17:53 +0100)
committerwiebe <redacted>
Sun, 18 Jan 2009 16:53:15 +0000 (17:53 +0100)
cmdhelp.patch [new file with mode: 0644]
glinesnomask.patch
opername.patch
series
split.patch [new file with mode: 0644]
statsheader.patch [new file with mode: 0644]

diff --git a/cmdhelp.patch b/cmdhelp.patch
new file mode 100644 (file)
index 0000000..e982ddc
--- /dev/null
@@ -0,0 +1,26 @@
+make /HELP <command> work - oper only feature?
+need to make small test case to see how hard/easy this would be to add.
+
+diff -r ce13b0a1253e ircd/m_help.c
+--- a/ircd/m_help.c    Wed Jan 14 14:19:43 2009 +0100
++++ b/ircd/m_help.c    Wed Jan 14 15:01:46 2009 +0100
+@@ -100,7 +100,7 @@
+ int m_help(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
+ {
+   int i;
+-
++/* check here for param and find command, then call the function set for that entry? */
+   for (i = 0; msgtab[i].cmd; i++)
+     sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :%s", sptr, msgtab[i].cmd);
+   return 0;
+diff -r ce13b0a1253e ircd/parse.c
+--- a/ircd/parse.c     Wed Jan 14 14:19:43 2009 +0100
++++ b/ircd/parse.c     Wed Jan 14 15:01:46 2009 +0100
+@@ -105,6 +105,7 @@
+ static struct MessageTree tok_tree;
+ /** Array of all supported commands. */
++/* add mh_<command> here? mh = message help */
+ struct Message msgtab[] = {
+   {
+     MSG_PRIVATE,
index f970e80f29de6d4b14841f1fdf015a29a1cf2541..4917714b441e55c942c614493025192969bf2f2d 100644 (file)
@@ -1,4 +1,7 @@
 SNO_GLINE and SNO_AUTO
+
+change of plans.
+
 before, glines with reason starting with "AUTO" end up in SNO_AUTO, all other in SNO_GLINE
 now
 /* figure out what snomask to send to, send to SNO_GLINE when:
index 9e1dfaac6fce18875752a2dbef4bcd7417e974d9..20a6c67d5e8b5810ef2c6c342ba6e32b1637f181 100644 (file)
@@ -1,8 +1,8 @@
 Add opername to snomask, remote connect wallops, and logging
 
-diff -r 897950943d34 include/client.h
---- a/include/client.h Mon Jan 12 18:45:19 2009 +0100
-+++ b/include/client.h Mon Jan 12 19:23:07 2009 +0100
+diff -r dffb1fdabb12 include/client.h
+--- a/include/client.h Wed Jan 14 14:19:42 2009 +0100
++++ b/include/client.h Wed Jan 14 15:27:42 2009 +0100
 @@ -806,6 +806,7 @@
  #define IPV6USERBITS 64
  
@@ -11,10 +11,10 @@ diff -r 897950943d34 include/client.h
  extern const char* client_get_default_umode(const struct Client* sptr);
  extern int client_get_ping(const struct Client* local_client);
  extern void client_drop_sendq(struct Connection* con);
-diff -r 897950943d34 ircd/channel.c
---- a/ircd/channel.c   Mon Jan 12 18:45:19 2009 +0100
-+++ b/ircd/channel.c   Mon Jan 12 19:23:07 2009 +0100
-@@ -1850,8 +1850,8 @@
+diff -r dffb1fdabb12 ircd/channel.c
+--- a/ircd/channel.c   Wed Jan 14 14:19:42 2009 +0100
++++ b/ircd/channel.c   Wed Jan 14 15:27:43 2009 +0100
+@@ -1848,8 +1848,8 @@
      if (mbuf->mb_dest & MODEBUF_DEST_HACK4)
        sendto_opmask_butone(0, SNO_HACK4, "HACK(4): %s MODE %s %s%s%s%s%s%s "
                           "[%Tu]",
@@ -25,19 +25,10 @@ diff -r 897950943d34 ircd/channel.c
                           mbuf->mb_channel->chname,
                           rembuf_i ? "-" : "", rembuf, addbuf_i ? "+" : "",
                           addbuf, remstr, addstr,
-@@ -1859,7 +1859,7 @@
-     if (mbuf->mb_dest & MODEBUF_DEST_LOG)
-       log_write(LS_OPERMODE, L_INFO, LOG_NOSNOTICE,
--              "%#C OPMODE %H %s%s%s%s%s%s", mbuf->mb_source,
-+              "%s OPMODE %H %s%s%s%s%s%s", get_client_name_and_opername(mbuf->mb_source),
-               mbuf->mb_channel, rembuf_i ? "-" : "", rembuf,
-               addbuf_i ? "+" : "", addbuf, remstr, addstr);
-diff -r 897950943d34 ircd/gline.c
---- a/ircd/gline.c     Mon Jan 12 18:45:19 2009 +0100
-+++ b/ircd/gline.c     Mon Jan 12 19:23:07 2009 +0100
-@@ -566,7 +566,7 @@
+diff -r dffb1fdabb12 ircd/gline.c
+--- a/ircd/gline.c     Wed Jan 14 14:19:42 2009 +0100
++++ b/ircd/gline.c     Wed Jan 14 15:27:43 2009 +0100
+@@ -577,7 +577,7 @@
    sendto_opmask_butone(0, snomask, "%s adding %s %s for %s%s%s%s%s, expiring at "
                         "%Tu: %s",
                         (feature_bool(FEAT_HIS_SNOTICES) || IsServer(sptr)) ?
@@ -46,16 +37,7 @@ diff -r 897950943d34 ircd/gline.c
                           cli_name((cli_user(sptr))->server),
                       (flags & GLINE_LOCAL) ? "local" : "global",
                       (flags & GLINE_BADCHAN) ? "BADCHAN" : "GLINE",
-@@ -579,7 +579,7 @@
-   /* and log it */
-   log_write(LS_GLINE, L_INFO, LOG_NOSNOTICE,
--          "%#C adding %s %s for %s%s%s%s%s, expiring at %Tu: %s", sptr,
-+          "%s adding %s %s for %s%s%s%s%s, expiring at %Tu: %s", get_client_name_and_opername(sptr),
-           flags & GLINE_LOCAL ? "local" : "global",
-           flags & GLINE_BADCHAN ? "BADCHAN" : "GLINE",
-             flags & (GLINE_BADCHAN|GLINE_REALNAME) ? "" : nick,
-@@ -652,7 +652,7 @@
+@@ -663,7 +663,7 @@
    sendto_opmask_butone(0, SNO_GLINE, "%s activating global %s for %s%s%s%s%s, "
                         "expiring at %Tu: %s",
                         (feature_bool(FEAT_HIS_SNOTICES) || IsServer(sptr)) ?
@@ -64,16 +46,7 @@ diff -r 897950943d34 ircd/gline.c
                           cli_name((cli_user(sptr))->server),
                         GlineIsBadChan(gline) ? "BADCHAN" : "GLINE",
                         GlineIsBadChan(gline)|GlineIsRealName(gline) ? "" : gline->gl_nick,
-@@ -662,7 +662,7 @@
-                        gline->gl_expire + TSoffset, gline->gl_reason);
-   
-   log_write(LS_GLINE, L_INFO, LOG_NOSNOTICE,
--            "%#C activating global %s for %s%s%s%s%s, expiring at %Tu: %s", sptr,
-+            "%s activating global %s for %s%s%s%s%s, expiring at %Tu: %s", get_client_name_and_opername(sptr),
-             GlineIsBadChan(gline) ? "BADCHAN" : "GLINE",
-             GlineIsBadChan(gline)|GlineIsRealName(gline) ? "" : gline->gl_nick,
-             GlineIsBadChan(gline)|GlineIsRealName(gline) ? "" : "!",
-@@ -725,7 +725,7 @@
+@@ -736,7 +736,7 @@
    sendto_opmask_butone(0, SNO_GLINE, "%s %s %s for %s%s%s%s%s, expiring at %Tu: "
                       "%s",
                         (feature_bool(FEAT_HIS_SNOTICES) || IsServer(sptr)) ?
@@ -82,16 +55,7 @@ diff -r 897950943d34 ircd/gline.c
                           cli_name((cli_user(sptr))->server),
                       msg, GlineIsBadChan(gline) ? "BADCHAN" : "GLINE",
                         GlineIsBadChan(gline)|GlineIsRealName(gline) ? "" : gline->gl_nick,
-@@ -735,7 +735,7 @@
-                      gline->gl_expire + TSoffset, gline->gl_reason);
-   log_write(LS_GLINE, L_INFO, LOG_NOSNOTICE,
--            "%#C %s %s for %s%s%s%s%s, expiring at %Tu: %s", sptr, msg,
-+            "%s %s %s for %s%s%s%s%s, expiring at %Tu: %s", get_client_name_and_opername(sptr), msg,
-             GlineIsBadChan(gline) ? "BADCHAN" : "GLINE",
-             GlineIsBadChan(gline)|GlineIsRealName(gline) ? "" : gline->gl_nick,
-             GlineIsBadChan(gline)|GlineIsRealName(gline) ? "" : "!",
-@@ -940,7 +940,7 @@
+@@ -987,7 +987,7 @@
    /* All right, inform ops... */
    sendto_opmask_butone(0, SNO_GLINE, "%s modifying global %s for %s%s%s%s%s:%s",
                       (feature_bool(FEAT_HIS_SNOTICES) || IsServer(sptr)) ?
@@ -100,16 +64,7 @@ diff -r 897950943d34 ircd/gline.c
                       GlineIsBadChan(gline) ? "BADCHAN" : "GLINE",
                         GlineIsBadChan(gline)|GlineIsRealName(gline) ? "" : gline->gl_nick,
                         GlineIsBadChan(gline)|GlineIsRealName(gline) ? "" : "!",
-@@ -949,7 +949,7 @@
-   /* and log the change */
-   log_write(LS_GLINE, L_INFO, LOG_NOSNOTICE,
--          "%#C modifying global %s for %s%s%s:%s", sptr,
-+          "%s modifying global %s for %s%s%s:%s", get_client_name_and_opername(sptr),
-           GlineIsBadChan(gline) ? "BADCHAN" : "GLINE", gline->gl_user,
-           gline->gl_host ? "@" : "", gline->gl_host ? gline->gl_host : "",
-           buf);
-@@ -997,14 +997,14 @@
+@@ -1044,7 +1044,7 @@
    /* Inform ops and log it */
    sendto_opmask_butone(0, SNO_GLINE, "%s removing local %s for %s%s%s%s%s",
                       (feature_bool(FEAT_HIS_SNOTICES) || IsServer(sptr)) ?
@@ -118,36 +73,10 @@ diff -r 897950943d34 ircd/gline.c
                       GlineIsBadChan(gline) ? "BADCHAN" : "GLINE",
                         GlineIsBadChan(gline)|GlineIsRealName(gline) ? "" : gline->gl_nick,
                         GlineIsBadChan(gline)|GlineIsRealName(gline) ? "" : "!",
-                      gline->gl_user, gline->gl_host ? "@" : "",
-                      gline->gl_host ? gline->gl_host : "");
-   log_write(LS_GLINE, L_INFO, LOG_NOSNOTICE,
--          "%#C removing local %s for %s%s%s%s%s", sptr,
-+          "%s removing local %s for %s%s%s%s%s", get_client_name_and_opername(sptr),
-           GlineIsBadChan(gline) ? "BADCHAN" : "GLINE", 
-             GlineIsBadChan(gline)|GlineIsRealName(gline) ? "" : gline->gl_nick,
-             GlineIsBadChan(gline)|GlineIsRealName(gline) ? "" : "!",
-diff -r 897950943d34 ircd/ircd_log.c
---- a/ircd/ircd_log.c  Mon Jan 12 18:45:19 2009 +0100
-+++ b/ircd/ircd_log.c  Mon Jan 12 19:23:07 2009 +0100
-@@ -469,11 +469,11 @@
- {
-   if (MyUser(victim))
-     log_write(IsServer(killer) ? LS_SERVKILL : LS_OPERKILL, L_TRACE, 0,
--            "A local client %#C KILLED by %#C Path: %s!%s %s",
--            victim, killer, inpath, path, msg);
-+            "A local client %#C KILLED by %s Path: %s!%s %s",
-+            victim, get_client_name_and_opername(killer), inpath, path, msg);
-   else
-     log_write(IsServer(killer) ? LS_SERVKILL : LS_OPERKILL, L_TRACE, 0,
--            "KILL from %C For %C Path: %s!%s %s", killer, victim, inpath,
-+            "KILL from %s For %C Path: %s!%s %s", get_client_name_and_opername(killer), victim, inpath,
-             path, msg);
- }
-diff -r 897950943d34 ircd/jupe.c
---- a/ircd/jupe.c      Mon Jan 12 18:45:19 2009 +0100
-+++ b/ircd/jupe.c      Mon Jan 12 19:23:07 2009 +0100
-@@ -156,13 +156,13 @@
+diff -r dffb1fdabb12 ircd/jupe.c
+--- a/ircd/jupe.c      Wed Jan 14 14:19:42 2009 +0100
++++ b/ircd/jupe.c      Wed Jan 14 15:27:43 2009 +0100
+@@ -156,7 +156,7 @@
    sendto_opmask_butone(0, SNO_NETWORK, "%s adding %sJUPE for %s, expiring at "
                         "%Tu: %s",
                         (feature_bool(FEAT_HIS_SNOTICES) || IsServer(sptr)) ?
@@ -156,14 +85,7 @@ diff -r 897950943d34 ircd/jupe.c
                           cli_name((cli_user(sptr))->server),
                       flags & JUPE_LOCAL ? "local " : "", server,
                       expire + TSoffset, reason);
-   log_write(LS_JUPE, L_INFO, LOG_NOSNOTICE,
--          "%#C adding %sJUPE for %s, expiring at %Tu: %s", sptr,
-+          "%s adding %sJUPE for %s, expiring at %Tu: %s", get_client_name_and_opername(sptr),
-           flags & JUPE_LOCAL ? "local " : "", server, expire + TSoffset,
-           reason);
-@@ -216,13 +216,13 @@
+@@ -216,7 +216,7 @@
    sendto_opmask_butone(0, SNO_NETWORK, "%s activating JUPE for %s, expiring "
                       "at %Tu: %s",
                         (feature_bool(FEAT_HIS_SNOTICES) || IsServer(sptr)) ?
@@ -172,14 +94,7 @@ diff -r 897950943d34 ircd/jupe.c
                           cli_name((cli_user(sptr))->server),
                       jupe->ju_server, jupe->ju_expire + TSoffset,
                       jupe->ju_reason);
-   log_write(LS_JUPE, L_INFO, LOG_NOSNOTICE,
--          "%#C activating JUPE for %s, expiring at %Tu: %s",sptr,
-+          "%s activating JUPE for %s, expiring at %Tu: %s", get_client_name_and_opername(sptr),
-           jupe->ju_server, jupe->ju_expire + TSoffset, jupe->ju_reason);
-   if (!(flags & JUPE_LOCAL)) /* don't propagate local changes */
-@@ -269,14 +269,14 @@
+@@ -269,7 +269,7 @@
    sendto_opmask_butone(0, SNO_NETWORK, "%s %s JUPE for %s, expiring at %Tu: "
                       "%s",
                         (feature_bool(FEAT_HIS_SNOTICES) || IsServer(sptr)) ?
@@ -188,32 +103,21 @@ diff -r 897950943d34 ircd/jupe.c
                           cli_name((cli_user(sptr))->server),
                       JupeIsLocal(jupe) ? "removing local" : "deactivating",
                       jupe->ju_server, jupe->ju_expire + TSoffset,
-                      jupe->ju_reason);
-   log_write(LS_JUPE, L_INFO, LOG_NOSNOTICE,
--          "%#C %s JUPE for %s, expiring at %Tu: %s", sptr,
-+          "%s %s JUPE for %s, expiring at %Tu: %s", get_client_name_and_opername(sptr),
-           JupeIsLocal(jupe) ? "removing local" : "deactivating",
-           jupe->ju_server, jupe->ju_expire + TSoffset, jupe->ju_reason);
-diff -r 897950943d34 ircd/m_connect.c
---- a/ircd/m_connect.c Mon Jan 12 18:45:19 2009 +0100
-+++ b/ircd/m_connect.c Mon Jan 12 19:23:07 2009 +0100
-@@ -197,8 +197,9 @@
+diff -r dffb1fdabb12 ircd/m_connect.c
+--- a/ircd/m_connect.c Wed Jan 14 14:19:42 2009 +0100
++++ b/ircd/m_connect.c Wed Jan 14 15:27:43 2009 +0100
+@@ -197,7 +197,7 @@
    sendwallto_group_butone(&me, WALL_WALLOPS, 0,
                         "Remote CONNECT %s %s from %s", aconf->name,
                        parv[2] ? parv[2] : "",
 -                      get_client_name(sptr, HIDE_IP));
--  log_write(LS_NETWORK, L_INFO, 0, "CONNECT From %C : %s %s", sptr, aconf->name,
 +                      get_client_name_and_opername(sptr));
-+  log_write(LS_NETWORK, L_INFO, 0, "CONNECT From %s : %s %s",
-+            get_client_name_and_opername(sptr), aconf->name,
+   log_write(LS_NETWORK, L_INFO, 0, "CONNECT From %C : %s %s", sptr, aconf->name,
            parv[2] ? parv[2] : "");
  
-   if (connect_server(aconf, sptr)) {
-diff -r 897950943d34 ircd/m_join.c
---- a/ircd/m_join.c    Mon Jan 12 18:45:19 2009 +0100
-+++ b/ircd/m_join.c    Mon Jan 12 19:23:07 2009 +0100
+diff -r dffb1fdabb12 ircd/m_join.c
+--- a/ircd/m_join.c    Wed Jan 14 14:19:42 2009 +0100
++++ b/ircd/m_join.c    Wed Jan 14 15:27:43 2009 +0100
 @@ -242,8 +242,8 @@
          }
          /* send accountability notice */
@@ -225,9 +129,9 @@ diff -r 897950943d34 ircd/m_join.c
          err = 0;
        }
  
-diff -r 897950943d34 ircd/m_kill.c
---- a/ircd/m_kill.c    Mon Jan 12 18:45:19 2009 +0100
-+++ b/ircd/m_kill.c    Mon Jan 12 19:23:07 2009 +0100
+diff -r dffb1fdabb12 ircd/m_kill.c
+--- a/ircd/m_kill.c    Wed Jan 14 14:19:42 2009 +0100
++++ b/ircd/m_kill.c    Wed Jan 14 15:27:43 2009 +0100
 @@ -127,7 +127,7 @@
     */
    sendto_opmask_butone(0, snomask,
@@ -237,10 +141,10 @@ diff -r 897950943d34 ircd/m_kill.c
                         inpath, path, msg);
    log_write_kill(victim, sptr, inpath, path, msg);
  
-diff -r 897950943d34 ircd/m_rehash.c
---- a/ircd/m_rehash.c  Mon Jan 12 18:45:19 2009 +0100
-+++ b/ircd/m_rehash.c  Mon Jan 12 19:23:07 2009 +0100
-@@ -121,10 +121,10 @@
+diff -r dffb1fdabb12 ircd/m_rehash.c
+--- a/ircd/m_rehash.c  Wed Jan 14 14:19:42 2009 +0100
++++ b/ircd/m_rehash.c  Wed Jan 14 15:27:43 2009 +0100
+@@ -121,8 +121,8 @@
    }
  
    send_reply(sptr, RPL_REHASHING, configfile);
@@ -249,14 +153,11 @@ diff -r 897950943d34 ircd/m_rehash.c
 +  sendto_opmask_butone(0, SNO_OLDSNO, "%s is rehashing Server config file",
 +                     get_client_name_and_opername(sptr));
  
--  log_write(LS_SYSTEM, L_INFO, 0, "REHASH From %#C", sptr);
-+  log_write(LS_SYSTEM, L_INFO, 0, "REHASH From %s", get_client_name_and_opername(sptr));
+   log_write(LS_SYSTEM, L_INFO, 0, "REHASH From %#C", sptr);
  
-   return rehash(cptr, flag);
- }
-diff -r 897950943d34 ircd/m_settime.c
---- a/ircd/m_settime.c Mon Jan 12 18:45:19 2009 +0100
-+++ b/ircd/m_settime.c Mon Jan 12 19:23:07 2009 +0100
+diff -r dffb1fdabb12 ircd/m_settime.c
+--- a/ircd/m_settime.c Wed Jan 14 14:19:42 2009 +0100
++++ b/ircd/m_settime.c Wed Jan 14 15:27:43 2009 +0100
 @@ -177,7 +177,7 @@
    else /* tell opers about time change */
    {
@@ -275,9 +176,9 @@ diff -r 897950943d34 ircd/m_settime.c
                         (dt < 0) ? "forwards" : "backwards");
      TSoffset -= dt; /* apply time change */
      if (IsUser(sptr)) /* let user know what we did */
-diff -r 897950943d34 ircd/s_misc.c
---- a/ircd/s_misc.c    Mon Jan 12 18:45:19 2009 +0100
-+++ b/ircd/s_misc.c    Mon Jan 12 19:23:07 2009 +0100
+diff -r dffb1fdabb12 ircd/s_misc.c
+--- a/ircd/s_misc.c    Wed Jan 14 14:19:42 2009 +0100
++++ b/ircd/s_misc.c    Wed Jan 14 15:27:43 2009 +0100
 @@ -169,6 +169,20 @@
    ircd_snprintf(0, nbuf, sizeof(nbuf), "%s[%s@%s]", cli_name(sptr),
                  IsIdented(sptr) ? cli_username(sptr) : "",
diff --git a/series b/series
index 50781ea5aaf87f51e8c9395a2e54318e38db0062..bb886f05af1247b972fd1730652663fe6f17370e 100644 (file)
--- a/series
+++ b/series
@@ -1,3 +1,4 @@
+statsheader.patch
 addnickchasetomodenick.patch #+nickchase
 chanopaccountabilityforkickdelayedjoin.patch
 addopkickcmd.patch #+opkick
@@ -12,3 +13,5 @@ commonchansumode.patch
 remoteglinejupe.patch
 whoban.patch
 opername.patch
+cmdhelp.patch
+split.patch
diff --git a/split.patch b/split.patch
new file mode 100644 (file)
index 0000000..92a35cd
--- /dev/null
@@ -0,0 +1,1693 @@
+work in progress
+Add split functionality into ircd
+Add /split command
+Add /stats S/split (make /STATS s/S case sensitive, s spoofhosts)
+Add feature SPLIT
+Add split.c split.h m_split.c
+
+diff -r 2da61ac38fa1 include/handlers.h
+--- a/include/handlers.h       Sun Jan 18 14:18:36 2009 +0100
++++ b/include/handlers.h       Sun Jan 18 15:26:56 2009 +0100
+@@ -139,6 +139,7 @@
+ extern int m_registered(struct Client*, struct Client*, int, char*[]);
+ extern int m_sethost(struct Client*, struct Client*, int, char*[]);
+ extern int m_silence(struct Client*, struct Client*, int, char*[]);
++extern int m_split(struct Client*, struct Client*, int, char*[]);
+ extern int m_stats(struct Client*, struct Client*, int, char*[]);
+ extern int m_time(struct Client*, struct Client*, int, char*[]);
+ extern int m_topic(struct Client*, struct Client*, int, char*[]);
+@@ -178,6 +179,7 @@
+ extern int mo_rping(struct Client*, struct Client*, int, char*[]);
+ extern int mo_set(struct Client*, struct Client*, int, char*[]);
+ extern int mo_settime(struct Client*, struct Client*, int, char*[]);
++extern int mo_split(struct Client*, struct Client*, int, char*[]);
+ extern int mo_squit(struct Client*, struct Client*, int, char*[]);
+ extern int mo_stats(struct Client*, struct Client*, int, char*[]);
+ extern int mo_trace(struct Client*, struct Client*, int, char*[]);
+@@ -232,6 +234,7 @@
+ extern int ms_sethost(struct Client*, struct Client*, int, char*[]);
+ extern int ms_settime(struct Client*, struct Client*, int, char*[]);
+ extern int ms_silence(struct Client*, struct Client*, int, char*[]);
++extern int ms_split(struct Client*, struct Client*, int, char*[]);
+ extern int ms_squit(struct Client*, struct Client*, int, char*[]);
+ extern int ms_stats(struct Client*, struct Client*, int, char*[]);
+ extern int ms_topic(struct Client*, struct Client*, int, char*[]);
+diff -r 2da61ac38fa1 include/ircd_features.h
+--- a/include/ircd_features.h  Sun Jan 18 14:18:36 2009 +0100
++++ b/include/ircd_features.h  Sun Jan 18 15:26:56 2009 +0100
+@@ -109,6 +109,7 @@
+   FEAT_SETHOST,
+   FEAT_SETHOST_USER,
+   FEAT_SETHOST_AUTO,
++  FEAT_SPLIT,
+   /* HEAD_IN_SAND Features */
+   FEAT_HIS_SNOTICES,
+@@ -137,6 +138,7 @@
+   FEAT_HIS_STATS_q,
+   FEAT_HIS_STATS_R,
+   FEAT_HIS_STATS_r,
++  FEAT_HIS_STATS_S,
+   FEAT_HIS_STATS_s,
+   FEAT_HIS_STATS_t,
+   FEAT_HIS_STATS_T,
+diff -r 2da61ac38fa1 include/msg.h
+--- a/include/msg.h    Sun Jan 18 14:18:36 2009 +0100
++++ b/include/msg.h    Sun Jan 18 15:26:56 2009 +0100
+@@ -332,6 +332,10 @@
+ #define TOK_JUPE                "JU"
+ #define CMD_JUPE              MSG_JUPE, TOK_JUPE
++#define MSG_SPLIT                "SPLIT"          /* SPLIT */
++#define TOK_SPLIT                "SP"
++#define CMD_SPLIT             MSG_SPLIT, TOK_SPLIT
++
+ #define MSG_OPMODE              "OPMODE"        /* OPMO */
+ #define TOK_OPMODE              "OM"
+ #define CMD_OPMODE            MSG_OPMODE, TOK_OPMODE
+diff -r 2da61ac38fa1 include/numeric.h
+--- a/include/numeric.h        Sun Jan 18 14:18:36 2009 +0100
++++ b/include/numeric.h        Sun Jan 18 15:26:56 2009 +0100
+@@ -117,6 +117,7 @@
+       RPL_STATSVLINE       227           unreal */
+ #define RPL_STATSALINE       226        /* Hybrid, Undernet */
+ #define RPL_STATSQLINE       228        /* Undernet extension */
++#define RPL_STATSSPLIT       229        /* QuakeNet extension */
+ /*      RPL_SERVICEINFO      231      unused */
+ /*      RPL_ENDOFSERVICES    232      unused */
+@@ -177,6 +178,8 @@
+ #define RPL_STATSDLINE       275        /* Undernet extension */
+ #define RPL_STATSRLINE       276        /* Undernet extension */
++#define RPL_SPLITLIST        278        /* QuakeNet extension */
++#define RPL_ENDOFSPLITLIST   279        /* QuakeNet extension */
+ #define RPL_GLIST            280        /* Undernet extension */
+ #define RPL_ENDOFGLIST       281        /* Undernet extension */
+ #define RPL_JUPELIST         282        /* Undernet extension - jupe -Kev */
+@@ -440,6 +443,7 @@
+ /*      ERR_GHOSTEDCLIENT    503           efnet */
+ /*    ERR_VWORLDWARN       503           austnet */
++#define ERR_NOSUCHSPLIT      510        /* QuakeNet extension */
+ #define ERR_SILELISTFULL     511        /* Undernet extension */
+ /*      ERR_NOTIFYFULL       512           aircd */
+ /*    ERR_TOOMANYWATCH     512           Numeric List: Dalnet */
+diff -r 2da61ac38fa1 include/split.h
+--- /dev/null  Thu Jan 01 00:00:00 1970 +0000
++++ b/include/split.h  Sun Jan 18 15:26:56 2009 +0100
+@@ -0,0 +1,121 @@
++/* TODO: ifndef ? */
++#ifndef INCLUDED_jupe_h
++#define INCLUDED_jupe_h
++/*
++ * IRC - Internet Relay Chat, include/split.h
++ * Copyright (C) 1990 Jarkko Oikarinen and
++ *                    University of Oulu, Computing Center
++ * Copyright (C) 2000 Kevin L. Mitchell <klmitch@mit.edu>
++ *
++ * 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 2, 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.
++ */
++/** @file
++ * @brief  Interface and declarations for split server handling.
++ * @version $Id: jupe.h 1208 2004-10-03 14:12:35Z entrope $
++ */
++/* TODO: ifndef ? */
++#ifndef INCLUDED_sys_types_h
++#include <sys/types.h>
++#define INCLUDED_sys_types_h
++#endif
++
++
++struct Client;
++struct StatDesc;
++
++#define SPLIT_MAX_EXPIRE   604800   /**< Maximum split expiration time (7 days). */
++#define SPLIT_AUTO_EXPIRE  604800   /**< Expireation time used for auto created entries. */
++
++/* Describes a SPLIT server entry. */
++struct Split {
++  struct Split*   sp_next;     /**< Pointer to next Split. */
++  struct Split**  sp_prev_p;   /**< Pointer to previous next pointer. */
++  char*           sp_server;   /**< Name of server. */
++  char*           sp_reason;   /**< Reason. */
++  time_t          sp_creation; /**< TODO: Creation time. What are we using this for then? */
++  time_t          sp_expire;   /**< Expiration time. */
++  time_t          sp_lastmod;  /**< Last modification time. */
++  time_t          sp_lifetime; /**< Life time. */
++  unsigned int    sp_flags;    /**< Status flags. */
++};
++
++/** Split state flags. */
++#define SPLIT_ACTIVE      0x0001  /**< Split is active. */
++#define SPLIT_REMOVING   0x0002  /**< Split is about to be destroyed. */
++
++/* TODO: these ; after } needed ? */
++/* Actions to perform on a SPLIT. */
++enum SplitAction {
++  SPLIT_ACTIVATE,    /**< SPLIT should be activated. */
++  SPLIT_DEACTIVATE,  /**< SPLIT should be deactivated. */
++  SPLIT_MODIFY,      /**< SPLIT should be modified. */
++  SPLIT_REMOVE       /**< SPLIT should be removed. */
++};
++
++/* TODO: what values to use here? */
++/* Split update flags. */
++#define SPLIT_EXPIRE     0x0002  /**< Expiration time update. */
++#define SPLIT_LIFETIME   0x0004  /**< Record lifetime update. */
++#define SPLIT_REASON     0x0008  /**< Reason update. */
++#define SPLIT_CREATION   0x0010  /**< Creation time update. */
++#define SPLIT_MODIFY     0x0020  /**< No state change. */
++
++/* mask for Split update flags. */
++#define SPLIT_UPDATE (SPLIT_EXPIRE | SPLIT_LIFETIME | SPLIT_REASON)
++
++/* test whether a split entry is active. */
++#define SplitIsActive(s)      ((s)->sp_flags & SPLIT_ACTIVE)
++/* test whether a split entry is marked for forecd removal. */
++#define SplitIsRemoving(s)     ((s)->sp_flags & SPLIT_REMOVING)
++
++/* TODO: these are not used? some are, check! */
++/* get the server name for a split entry. */
++#define SplitServer(s)        ((s)->sp_server)
++/* get the reason for a split entry. */
++#define SplitReason(s)        ((s)->sp_reason)
++/* get the creation time for split entry. */
++#define SplitCreation(s)      ((s)->sp_creation)
++/* get the expiration time for split entry. */
++#define SplitExpire(s)        ((s)->sp_expire)
++/* get the last modification time for split entry. */
++#define SplitLastMod(s)       ((s)->sp_lastmod)
++/* get the life time for split entry. */
++#define SplitLifeTime(s)      ((s)->sp_lifetime)
++
++extern int split_add(struct Client *cptr, struct Client *sptr,
++  char *server, const char *reason,
++  time_t creation, time_t expire, time_t lastmod, time_t lifetime,
++  unsigned int flags);
++extern int split_modify(struct Client *cptr, struct Client *sptr,
++  struct Split *split, enum SplitAction action, const char *reason,
++  time_t creation, time_t expire, time_t lastmod, time_t lifetime,
++  unsigned int flags);
++extern int split_remove(struct Client *cptr, struct Client *sptr,
++  struct Split *split, const char *reason);
++
++extern struct Split* split_find(char *server);
++extern void split_free(struct Split *split);
++extern int split_expire(struct Split* split);
++extern void split_burst(struct Client *cptr);
++extern int split_resend(struct Client *cptr, struct Split *split);
++extern int split_list(struct Client *sptr, char *server);
++extern int split_merge(struct Client *server);
++extern int split_break(struct Client *server, const char *reason);
++extern void split_conf();
++extern void split_stats(struct Client *sptr, const struct StatDesc *sd, char *param);
++extern int split_memory_count(size_t *sp_size);
++
++/* TODO: endif ? */
++#endif /* INCLUDED_jupe_h */
+diff -r 2da61ac38fa1 ircd/Makefile.in
+--- a/ircd/Makefile.in Sun Jan 18 14:18:36 2009 +0100
++++ b/ircd/Makefile.in Sun Jan 18 15:26:56 2009 +0100
+@@ -173,6 +173,7 @@
+       m_sethost.c \
+       m_settime.c \
+       m_silence.c \
++      m_split.c \
+       m_squit.c \
+       m_stats.c \
+       m_time.c \
+@@ -212,6 +213,7 @@
+       s_stats.c \
+       s_user.c \
+       send.c \
++      split.c \
+       uping.c \
+       userload.c \
+       whocmds.c \
+@@ -1052,6 +1054,15 @@
+   ../include/ircd_chattr.h ../include/list.h ../include/msg.h \
+   ../include/numeric.h ../include/numnicks.h ../include/s_user.h \
+   ../include/send.h ../include/struct.h
++m_split.o: m_split.c ../config.h ../include/client.h ../include/ircd_defs.h \
++  ../include/dbuf.h ../include/msgq.h ../include/ircd_events.h \
++  ../config.h ../include/ircd_handler.h ../include/res.h \
++  ../include/capab.h ../include/split.h ../include/hash.h \
++  ../include/ircd.h ../include/struct.h ../include/ircd_features.h \
++  ../include/ircd_log.h ../include/ircd_reply.h ../include/ircd_string.h \
++  ../include/ircd_chattr.h ../include/match.h ../include/msg.h \
++  ../include/numeric.h ../include/numnicks.h ../include/s_conf.h \
++  ../include/client.h ../include/s_misc.h ../include/send.h
+ m_squit.o: m_squit.c ../config.h ../include/client.h \
+   ../include/ircd_defs.h ../include/dbuf.h ../include/msgq.h \
+   ../include/ircd_events.h ../config.h ../include/ircd_handler.h \
+@@ -1325,6 +1336,7 @@
+   ../include/msgq.h ../include/numeric.h ../include/numnicks.h \
+   ../include/res.h ../include/s_bsd.h ../include/s_conf.h \
+   ../include/s_user.h ../include/s_stats.h ../include/send.h \
++  ../include/split.h \
+   ../include/struct.h ../include/sys.h ../include/whowas.h
+ s_err.o: s_err.c ../config.h ../include/numeric.h ../include/ircd_log.h \
+   ../include/s_debug.h ../config.h ../include/ircd_defs.h
+@@ -1410,6 +1422,16 @@
+   ../include/msg.h ../include/numnicks.h ../include/parse.h \
+   ../include/s_bsd.h ../include/s_debug.h ../include/s_misc.h \
+   ../include/s_user.h ../include/struct.h ../include/sys.h
++split.o: split.c ../config.h ../include/split.h ../include/client.h \
++  ../include/ircd_defs.h ../include/dbuf.h ../include/msgq.h \
++  ../include/ircd_events.h ../config.h ../include/ircd_handler.h \
++  ../include/res.h ../include/capab.h ../include/hash.h ../include/ircd.h \
++  ../include/struct.h ../include/ircd_alloc.h ../include/ircd_features.h \
++  ../include/ircd_log.h ../include/ircd_reply.h ../include/ircd_string.h \
++  ../include/ircd_chattr.h ../include/match.h ../include/msg.h \
++  ../include/numeric.h ../include/numnicks.h ../include/s_bsd.h \
++  ../include/s_misc.h ../include/send.h ../include/struct.h \
++  ../include/sys.h
+ uping.o: uping.c ../config.h ../include/uping.h ../include/ircd_defs.h \
+   ../include/ircd_events.h ../config.h ../include/res.h \
+   ../include/client.h ../include/dbuf.h ../include/msgq.h \
+diff -r 2da61ac38fa1 ircd/ircd.c
+--- a/ircd/ircd.c      Sun Jan 18 14:18:36 2009 +0100
++++ b/ircd/ircd.c      Sun Jan 18 15:26:56 2009 +0100
+@@ -55,6 +55,7 @@
+ #include "s_misc.h"
+ #include "s_stats.h"
+ #include "send.h"
++#include "split.h"
+ #include "sys.h"
+ #include "uping.h"
+ #include "userload.h"
+@@ -763,6 +764,9 @@
+   Debug((DEBUG_NOTICE, "Server ready..."));
+   log_write(LS_SYSTEM, L_NOTICE, 0, "Server Ready");
++  /* create SPLITs */
++  split_conf();
++
+   event_loop();
+   return 0;
+diff -r 2da61ac38fa1 ircd/ircd_features.c
+--- a/ircd/ircd_features.c     Sun Jan 18 14:18:36 2009 +0100
++++ b/ircd/ircd_features.c     Sun Jan 18 15:26:56 2009 +0100
+@@ -363,6 +363,7 @@
+   F_B(SETHOST, 0, 0, 0),
+   F_B(SETHOST_USER, 0, 0, 0),
+   F_B(SETHOST_AUTO, 0, 0, 0),
++  F_B(SPLIT, 0, 1, 0),
+   /* HEAD_IN_SAND Features */
+   F_B(HIS_SNOTICES, 0, 1, 0),
+@@ -391,6 +392,7 @@
+   F_B(HIS_STATS_q, 0, 1, 0),
+   F_B(HIS_STATS_R, 0, 1, 0),
+   F_B(HIS_STATS_r, 0, 1, 0),
++  F_B(HIS_STATS_S, 0, 1, 0),
+   F_B(HIS_STATS_s, 0, 1, 0),
+   F_B(HIS_STATS_t, 0, 1, 0),
+   F_B(HIS_STATS_T, 0, 1, 0),
+diff -r 2da61ac38fa1 ircd/m_endburst.c
+--- a/ircd/m_endburst.c        Sun Jan 18 14:18:36 2009 +0100
++++ b/ircd/m_endburst.c        Sun Jan 18 15:26:56 2009 +0100
+@@ -85,6 +85,7 @@
+ #include "client.h"
+ #include "hash.h"
+ #include "ircd.h"
++#include "ircd_features.h"
+ #include "ircd_log.h"
+ #include "ircd_reply.h"
+ #include "ircd_string.h"
+@@ -92,6 +93,7 @@
+ #include "numeric.h"
+ #include "numnicks.h"
+ #include "send.h"
++#include "split.h"
+ /* #include <assert.h> -- Now using assert in ircd_log.h */
+@@ -125,6 +127,7 @@
+ int ms_end_of_burst(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
+ {
+   struct Channel *chan, *next_chan;
++  int split;
+   assert(0 != cptr);
+   assert(0 != sptr);
+@@ -135,6 +138,10 @@
+   dump_map(sptr, "*", 0, report_new_links, 0);
+   sendto_opmask_butone(0, SNO_NETWORK, "Completed net.burst from %C.", 
+       sptr);
++  /* cleanup SPLITs, but only bother ops when FEAT_SPLIT is enabled. */
++  split = split_merge(sptr);
++  if (feature_bool(FEAT_SPLIT))
++    sendto_opmask_butone(0, SNO_NETWORK, "Removed %d SPLIT entries.", split);
+   if (MyConnect(sptr))
+     sendcmdto_one(&me, CMD_END_OF_BURST_ACK, sptr, "");
+diff -r 2da61ac38fa1 ircd/m_reburst.c
+--- a/ircd/m_reburst.c Sun Jan 18 14:18:36 2009 +0100
++++ b/ircd/m_reburst.c Sun Jan 18 15:26:56 2009 +0100
+@@ -102,6 +102,7 @@
+ #include "ircd_snprintf.h"
+ #include "gline.h"
+ #include "jupe.h"
++#include "split.h"
+ /* #include <assert.h> -- Now using assert in ircd_log.h */
+ #include <stdlib.h>
+@@ -130,6 +131,10 @@
+     case 'J': 
+       jupe_burst(sptr);
+       break;
++    case 's':
++    case 'S':
++      split_burst(sptr);
++      break;
+     default:
+       break;
+   }
+diff -r 2da61ac38fa1 ircd/m_split.c
+--- /dev/null  Thu Jan 01 00:00:00 1970 +0000
++++ b/ircd/m_split.c   Sun Jan 18 15:26:56 2009 +0100
+@@ -0,0 +1,374 @@
++/*
++ * IRC - Internet Relay Chat, ircd/m_split.c
++ * Copyright (C) 1990 Jarkko Oikarinen and
++ *                    University of Oulu, Computing Center
++ * Copyright (C) 2000 Kevin L. Mitchell <klmitch@mit.edu>
++ *
++ * 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: m_split.c 1737 2006-12-19 05:20:48Z entrope $
++ */
++
++/*
++ * 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 "split.h"
++#include "hash.h"
++#include "ircd.h"
++#include "ircd_features.h"
++#include "ircd_log.h"
++#include "ircd_reply.h"
++#include "ircd_string.h"
++#include "match.h"
++#include "msg.h"
++#include "numeric.h"
++#include "numnicks.h"
++#include "s_conf.h"
++#include "s_debug.h"
++#include "s_misc.h"
++#include "send.h"
++
++/* #include <assert.h> -- Now using assert in ircd_log.h */
++#include <stdlib.h>
++#include <string.h>
++
++/*
++ * ms_split - server message handler
++ *
++ * parv[0] = Send prefix
++ * parv[1] = (+|-)<server name>
++ * parv[2] = Creation time
++ * parv[3] = Expiration time
++ * parv[4] = Last modification time
++ * parv[5] = Life time
++ * parv[6] = Comment
++ *
++ */
++int ms_split(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
++{
++  struct Client *acptr = 0;
++  struct Split *asplit = 0;
++  unsigned int flags = 0;
++  enum SplitAction action = SPLIT_MODIFY;
++  time_t creation = 0, expire = 0, lastmod = 0, lifetime = 0;
++  char *server = parv[1], *tmp = 0;
++  const char *reason = "No reason";
++
++  /* TODO: perhaps make some fields optional? reason? */
++  if (parc < 6)
++    return need_more_params(sptr, "SPLIT");
++
++  if (*server == '!') {
++    server++;
++    action = SPLIT_REMOVE; /* removing entry */
++  }
++  
++  switch (*server) { /* handle + and - */
++    case '+': /* activate the split entry */
++    if (action != SPLIT_REMOVE)
++      action = SPLIT_ACTIVATE;
++    server++;
++    break;
++
++    case '-': /* deactivate the entry */
++    if (action != SPLIT_REMOVE)
++      action = SPLIT_DEACTIVATE;
++    server++;
++    break;
++  }
++
++  /* Next, try to find the split entry... */
++  asplit = split_find(server);
++
++  /* We now have all the pieces to tell us what we've got; let's put
++   * it all together and convert the rest of the arguments.
++   */
++
++  /* can't modify a split entry that doesn't exist, so remap to activate */
++  if (!asplit && action == SPLIT_MODIFY)
++    action = SPLIT_ACTIVATE;
++
++  /* OK, let's figure out what other parameters we may have... */
++  creation = atoi(parv[2]);
++  expire = atoi(parv[3]);
++  lastmod = atoi(parv[4]);
++  lifetime = atoi(parv[5]);
++  reason = parv[parc - 1];
++
++  Debug((DEBUG_DEBUG, "I have a SPLIT I am acting upon now; "
++    "server %s, action %s, creation %Tu, expire %Tu, "
++    "lastmod %Tu, lifetime %Tu, reason: %s; split %s!", server,
++        action == SPLIT_REMOVE ? "!-" :
++         (action == SPLIT_ACTIVATE ? "+" :
++          (action == SPLIT_DEACTIVATE ? "-" : "(MODIFY)")),
++       creation, expire, lastmod, lifetime, reason,
++       asplit ? "EXISTS" : "does not exist"));
++
++  /* OK, at this point, we have converted all available parameters.
++   * Let's actually do the action!
++   */
++  if (asplit) {
++    if (action == SPLIT_REMOVE)
++      return split_remove(cptr, sptr, asplit, reason);
++    return split_modify(cptr, sptr, asplit, action, reason, creation, expire,
++                      lastmod, lifetime, flags);
++  }
++
++  assert(action != SPLIT_MODIFY);
++
++  return split_add(cptr, sptr, server, reason, creation, expire, lastmod, lifetime,
++                 flags | ((action == SPLIT_ACTIVATE) ? SPLIT_ACTIVE : 0));
++}
++
++
++/*
++ * mo_split - oper message handler
++ *
++ * Local listing: 1 or 2 params
++ * parv[0] = Send prefix
++ * parv[1] = [Server or mask to match]
++ *
++ * Add or modify entry: 3, 4 or 5 params (3 is * then)
++ * parv[0] = Send prefix
++ * parv[1] = [+|-]<server name>
++ * parv[2] = [Expiration offset] (required for new)
++ * parv[3] = [Comment] (required for new)
++ *
++ */
++int mo_split(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
++{
++  struct Client *acptr = 0;
++  struct Split *asplit = 0;
++  unsigned int flags = 0;
++  enum SplitAction action = SPLIT_MODIFY;
++  time_t expire = 0, lastmod = CurrentTime, creation = CurrentTime;
++  char *server = parv[1], *end;
++  const char *reason = NULL;
++
++  if (parc < 2) {
++    if (!HasPriv(sptr, PRIV_ROUTEINFO)) 
++      return send_reply(sptr, ERR_NOPRIVILEGES);
++    else
++      return split_list(sptr, 0);
++  }
++
++  if (*server == '!') {
++    server++;
++    action = SPLIT_REMOVE; /* force removal */
++  }
++
++  switch (*server) { /* handle + and - */
++    case '+': /* activate the split entry */
++    if (action != SPLIT_REMOVE)
++      action = SPLIT_ACTIVATE;
++    server++;
++    break;
++
++    case '-': /* deactivate the split entry */
++    if (action != SPLIT_REMOVE)
++      action = SPLIT_DEACTIVATE;
++    server++;
++    break;
++  }
++  
++  /* OK, let's figure out the parameters... */
++  switch (action) {
++    /* no specific action */
++    case SPLIT_MODIFY:
++    /* user wants a listing of a specific SPLIT */
++    if (parc == 2)
++      return split_list(sptr, server);
++    expire = strtol(parv[2], &end, 10) + CurrentTime; /* and the expiration */
++    if (*end != '\0')
++      return send_reply(sptr, SND_EXPLICIT | ERR_BADEXPIRE,
++        "%s :Bad expire time", parv[2]);
++
++    flags |= SPLIT_EXPIRE; /* remember that we got an expire time */
++    
++    if (parc > 3) { /* also got a reason... */
++      reason = parv[parc - 1];
++      flags |= SPLIT_REASON;
++    }
++    break;
++    
++    case SPLIT_REMOVE: /* TODO: require reason for this, but not expire? */
++    if (parc < 4)
++      return need_more_params(sptr, "SPLIT");
++    reason = parv[parc - 1];
++    flags |= SPLIT_REASON;      
++    break;
++    
++    case SPLIT_ACTIVATE: /* TODO: require expire and reason when new */
++    if (parc > 2) {
++      expire = strtol(parv[2], &end, 10) + CurrentTime; /* and the expiration */
++      if (*end != '\0')
++        return send_reply(sptr, SND_EXPLICIT | ERR_BADEXPIRE,
++          "%s :Bad expire time", parv[2]);
++      flags |= SPLIT_EXPIRE; /* remember that we got an expire time */
++    }
++    
++    if (parc > 3) { /* also got a reason... */
++      reason = parv[parc - 1];
++      flags |= SPLIT_REASON;
++    }
++    break;
++    
++    case SPLIT_DEACTIVATE: /* TODO: duplicate code? must be a cleaner way */
++    if (parc > 2) {
++      expire = strtol(parv[2], &end, 10) + CurrentTime; /* and the expiration */
++      if (*end != '\0')
++        return send_reply(sptr, SND_EXPLICIT | ERR_BADEXPIRE,
++          "%s :Bad expire time", parv[2]);
++      flags |= SPLIT_EXPIRE; /* remember that we got an expire time */
++    }
++    
++    if (parc > 3) { /* also got a reason... */
++      reason = parv[parc - 1];
++      flags |= SPLIT_REASON;
++    }
++    break;
++  }
++  
++
++  /* check for permissions... */
++  if (!feature_bool(FEAT_SPLIT)) /* TODO: but allow force removal? */
++    return send_reply(sptr, ERR_DISABLED, "SPLIT");
++  else if (!HasPriv(sptr, PRIV_SERVERINFO)) /* TODO: create PRIV_SPLIT - need help there */
++    return send_reply(sptr, ERR_NOPRIVILEGES);
++
++  /* Next, try to find the SPLIT... */
++  asplit = split_find(server);
++
++  /* We now have all the pieces to tell us what we've got; let's put
++   * it all together and convert the rest of the arguments.
++   */
++
++  /* SPLIT not found and thus we:
++   *   cannot remove SPLIT we do not have
++   *   cannot add new SPLIT without expire and reason
++   */
++  if (!asplit &&
++      ((action == SPLIT_REMOVE) ||
++       (action == SPLIT_ACTIVATE && !reason) ||
++       (action == SPLIT_DEACTIVATE && !reason) ||
++       (action == SPLIT_MODIFY && !reason)))
++    return send_reply(sptr, ERR_NOSUCHSPLIT, server);
++
++  /* can't modify a split entry that doesn't exist, so remap to activate */
++  if (!asplit && action == SPLIT_MODIFY)
++    action = SPLIT_ACTIVATE;
++
++  Debug((DEBUG_DEBUG, "I have a SPLIT I am acting upon now; "
++    "server %s, action %s, expire %Tu, "
++    "reason: %s; split %s!  (fields present: %s %s)", server,
++    action == SPLIT_REMOVE ? "!-" : 
++     (action == SPLIT_ACTIVATE ? "+" :
++      (action == SPLIT_DEACTIVATE ? "-" : "(MODIFY)")),
++    expire, reason, asplit ? "EXISTS" : "does not exist",
++    flags & SPLIT_EXPIRE ? "expire" : "",
++    flags & SPLIT_REASON ? "reason" : ""));
++
++  if (asplit) { /* modifying an existing SPLIT */
++    if (action == SPLIT_REMOVE)
++      return split_remove(cptr, sptr, asplit, reason);
++    return split_modify(cptr, sptr, asplit, action, reason,
++      asplit->sp_creation, expire, lastmod, asplit->sp_lifetime, flags);
++  }
++
++  assert(action != SPLIT_MODIFY);
++  assert(action != SPLIT_REMOVE);
++
++  /* create a new SPLIT */
++  return split_add(cptr, sptr, server, reason,
++    creation, expire, lastmod, expire,
++    flags | ((action == SPLIT_ACTIVATE) ? SPLIT_ACTIVE : 0));
++}
++
++/*
++ * m_split - user message handler
++ *
++ * parv[0] = Send prefix
++ *
++ * From user:
++ *
++ * parv[1] = [<server name>]
++ *
++ */
++int m_split(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
++{
++  if (parc < 2)
++    return split_list(sptr, 0);
++
++  return split_list(sptr, parv[1]);
++}
+diff -r 2da61ac38fa1 ircd/parse.c
+--- a/ircd/parse.c     Sun Jan 18 14:18:36 2009 +0100
++++ b/ircd/parse.c     Sun Jan 18 15:26:56 2009 +0100
+@@ -505,6 +505,13 @@
+     0, MAXPARA, MFLG_SLOW, 0, NULL,
+     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+     { m_unregistered, m_not_oper, ms_jupe, mo_jupe, m_ignore }
++  },
++  {
++    MSG_SPLIT,
++    TOK_SPLIT,
++    0, MAXPARA, MFLG_SLOW, 0, NULL,
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
++    { m_unregistered, m_not_oper, ms_split, mo_split, m_ignore }
+   },
+   {
+     MSG_OPMODE,
+diff -r 2da61ac38fa1 ircd/s_conf.c
+--- a/ircd/s_conf.c    Sun Jan 18 14:18:36 2009 +0100
++++ b/ircd/s_conf.c    Sun Jan 18 15:26:56 2009 +0100
+@@ -53,6 +53,7 @@
+ #include "s_debug.h"
+ #include "s_misc.h"
+ #include "send.h"
++#include "split.h"
+ #include "struct.h"
+ #include "sys.h"
+@@ -1005,6 +1006,7 @@
+   }
+   attach_conf_uworld(&me);
++  split_conf();
+   return ret;
+ }
+diff -r 2da61ac38fa1 ircd/s_debug.c
+--- a/ircd/s_debug.c   Sun Jan 18 14:18:36 2009 +0100
++++ b/ircd/s_debug.c   Sun Jan 18 15:26:57 2009 +0100
+@@ -48,6 +48,7 @@
+ #include "s_user.h"
+ #include "s_stats.h"
+ #include "send.h"
++#include "split.h"
+ #include "struct.h"
+ #include "sys.h"
+ #include "whowas.h"
+@@ -231,7 +232,8 @@
+       aw = 0,                   /* aways set */
+       wwa = 0,                  /* whowas aways */
+       gl = 0,                   /* glines */
+-      ju = 0;                   /* jupes */
++      ju = 0,                   /* jupes */
++      sp = 0;                   /* split entries */
+   size_t chm = 0,               /* memory used by channels */
+       chbm = 0,                 /* memory used by channel bans */
+@@ -244,6 +246,7 @@
+       wwm = 0,                  /* whowas array memory used */
+       glm = 0,                  /* memory used by glines */
+       jum = 0,                  /* memory used by jupes */
++      spm = 0,                  /* memory used by split entries */
+       com = 0,                  /* memory used by conf lines */
+       dbufs_allocated = 0,      /* memory used by dbufs */
+       dbufs_used = 0,           /* memory used by dbufs */
+@@ -348,8 +351,9 @@
+   gl = gline_memory_count(&glm);
+   ju = jupe_memory_count(&jum);
++  sp = split_memory_count(&spm);
+   send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG,
+-           ":Glines %d(%zu) Jupes %d(%zu)", gl, glm, ju, jum);
++           ":Glines %d(%zu) Jupes %d(%zu) Splits %d(%zu)", gl, glm, ju, jum, sp, spm);
+   send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG,
+            ":Hash: client %d(%zu), chan is the same", HASHSIZE,
+diff -r 2da61ac38fa1 ircd/s_err.c
+--- a/ircd/s_err.c     Sun Jan 18 14:18:36 2009 +0100
++++ b/ircd/s_err.c     Sun Jan 18 15:26:57 2009 +0100
+@@ -490,7 +490,7 @@
+ /* 228 */
+   { RPL_STATSQLINE, "Q %s :%s", "228" },
+ /* 229 */
+-  { 0 },
++  { RPL_STATSSPLIT, "S %s %d %d %d %d %c :%s", "229" },
+ /* 230 */
+   { 0 },
+ /* 231 */
+@@ -588,9 +588,9 @@
+ /* 277 */
+   { 0 },
+ /* 278 */
+-  { 0 },
++  { RPL_SPLITLIST, "%s %Tu %Tu %Tu %Tu %c :%s", "278" },
+ /* 279 */
+-  { 0 },
++  { RPL_ENDOFSPLITLIST, ":End of Split List", "279" },
+ /* 280 */
+   { RPL_GLIST, "%s%s%s%s%s %Tu %Tu %Tu %s %s%c :%s", "280" },
+ /* 281 */
+@@ -1052,7 +1052,7 @@
+ /* 509 */
+   { 0 },
+ /* 510 */
+-  { 0 },
++  { ERR_NOSUCHSPLIT, "%s :No such split", "510" },
+ /* 511 */
+   { ERR_SILELISTFULL, "%s :Your silence list is full", "511" },
+ /* 512 */
+diff -r 2da61ac38fa1 ircd/s_misc.c
+--- a/ircd/s_misc.c    Sun Jan 18 14:18:36 2009 +0100
++++ b/ircd/s_misc.c    Sun Jan 18 15:26:57 2009 +0100
+@@ -53,6 +53,7 @@
+ #include "s_stats.h"
+ #include "s_user.h"
+ #include "send.h"
++#include "split.h"
+ #include "struct.h"
+ #include "sys.h"
+ #include "uping.h"
+@@ -391,6 +392,7 @@
+   time_t on_for;
+   char comment1[HOSTLEN + HOSTLEN + 2];
++  char splitreason[BUFSIZE];
+   assert(killer);
+   if (MyConnect(victim))
+   {
+@@ -497,6 +499,14 @@
+     sendto_opmask_butone(0, SNO_NETWORK, "Net break: %C %C (%s)",
+                        cli_serv(victim)->up, victim, comment);
+     dump_map(victim, "*", 0, report_lost_links, 0);
++    if (feature_bool(FEAT_SPLIT)) {
++      ircd_snprintf(0, splitreason, sizeof(splitreason),
++        "Net break: %C %C (%s%s%s%s)", cli_serv(victim)->up, victim,
++        IsUser(killer) ? "SQUIT by " : "", IsUser(killer) ? cli_name(killer) : "",
++        IsUser(killer) ? ": " : "", comment);
++      sendto_opmask_butone(0, SNO_NETWORK, "Created %d SPLIT entries.",
++          split_break(victim, splitreason));
++    }
+   }
+   /*
+diff -r 2da61ac38fa1 ircd/s_serv.c
+--- a/ircd/s_serv.c    Sun Jan 18 14:18:36 2009 +0100
++++ b/ircd/s_serv.c    Sun Jan 18 15:26:57 2009 +0100
+@@ -54,6 +54,7 @@
+ #include "s_misc.h"
+ #include "s_user.h"
+ #include "send.h"
++#include "split.h"
+ #include "struct.h"
+ #include "sys.h"
+ #include "userload.h"
+@@ -196,6 +197,7 @@
+    */
+   gline_burst(cptr);
+   jupe_burst(cptr);
++  split_burst(cptr);
+   /*
+    * Pass on my client information to the new server
+diff -r 2da61ac38fa1 ircd/s_stats.c
+--- a/ircd/s_stats.c   Sun Jan 18 14:18:36 2009 +0100
++++ b/ircd/s_stats.c   Sun Jan 18 15:26:57 2009 +0100
+@@ -52,6 +52,7 @@
+ #include "s_stats.h"
+ #include "s_user.h"
+ #include "send.h"
++#include "split.h"
+ #include "struct.h"
+ #include "userload.h"
+@@ -633,7 +634,10 @@
+     send_usage, 0,
+     "System resource usage (Debug only)." },
+ #endif
+-  { 's', "spoofhosts", (STAT_FLAG_OPERFEAT | STAT_FLAG_VARPARAM), FEAT_HIS_STATS_s,
++  { 'S', "splits", (STAT_FLAG_OPERFEAT | STAT_FLAG_VARPARAM | STAT_FLAG_CASESENS), FEAT_HIS_STATS_S,
++    split_stats, 0,
++    "Server SPLITs information."},
++  { 's', "spoofhosts", (STAT_FLAG_OPERFEAT | STAT_FLAG_VARPARAM | STAT_FLAG_CASESENS), FEAT_HIS_STATS_s,
+     stats_sline, 0,
+     "Spoofed hosts information." },
+   { 'T', "motds", (STAT_FLAG_OPERFEAT | STAT_FLAG_CASESENS), FEAT_HIS_STATS_T,
+diff -r 2da61ac38fa1 ircd/split.c
+--- /dev/null  Thu Jan 01 00:00:00 1970 +0000
++++ b/ircd/split.c     Sun Jan 18 15:26:57 2009 +0100
+@@ -0,0 +1,746 @@
++/*
++ * IRC - Internet Relay Chat, ircd/split.c
++ * Copyright (C) 1990 Jarkko Oikarinen and
++ *                    University of Oulu, Finland
++ * Copyright (C) 2000 Kevin L. Mitchell <klmitch@mit.edu>
++ *
++ * 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.
++ */
++/** @file
++ * @brief Implementation of split server handling functions.
++ * @version $Id: split.c 1633 2006-03-25 03:46:56Z entrope $
++ */
++#include "config.h"
++
++#include "split.h"
++#include "client.h"
++#include "hash.h"
++#include "ircd.h"
++#include "ircd_alloc.h"
++#include "ircd_features.h"
++#include "ircd_log.h"
++#include "ircd_reply.h"
++#include "ircd_string.h"
++#include "list.h"
++#include "match.h"
++#include "msg.h"
++#include "numeric.h"
++#include "numnicks.h"
++#include "s_conf.h"
++#include "s_bsd.h"
++#include "s_debug.h"
++#include "s_misc.h"
++#include "send.h"
++#include "struct.h"
++#include "sys.h"    /* FALSE bleah */
++
++/* #include <assert.h> -- Now using assert in ircd_log.h */
++#include <string.h>
++
++/** List of split entries. */
++static struct Split *GlobalSplitList = 0;
++
++/** Allocate a new Split entry with the given parameters.
++ * @param[in] server Server name for split entry.
++ * @param[in] reason Reason for the split entry.
++ * @param[in] creation Creation time for split entry.
++ * @param[in] expire Expiration time for split entry.
++ * @param[in] lastmod Last modification time for split entry.
++ * @param[in] lifetime Life time for split entry.
++ * @param[in] flags Flags to set for the split entry.
++ */
++static struct Split *
++split_make(char *server, const char *reason,
++  time_t creation, time_t expire, time_t lastmod, time_t lifetime,
++  unsigned int flags)
++{
++  struct Split *asplit;
++
++  asplit = (struct Split*) MyMalloc(sizeof(struct Split)); /* alloc memory */
++  assert(0 != asplit);
++
++  memset(asplit, 0, sizeof(*asplit));
++  /* TODO: some limit for servername length? HOSTLEN? */
++  DupString(asplit->sp_server, server); /* copy vital information */
++  /* TODO:  some limit for reason length? QUITLEN TOPICLEN? */
++  DupString(asplit->sp_reason, reason);
++  /* TODO: what we use creation for and do we need it? */
++  asplit->sp_creation = creation;
++  asplit->sp_expire = expire;
++  /* TODO: are we using nettime etc.? CurrentTime is used. */
++  asplit->sp_lastmod = lastmod;
++  asplit->sp_lifetime = lifetime;
++  /* CHECK: does this make it active upon creation regardless of the flags given? */
++  asplit->sp_flags = flags & SPLIT_ACTIVE; /* set split flags */
++
++  asplit->sp_next = GlobalSplitList; /* link it into the list */
++  asplit->sp_prev_p = &GlobalSplitList;
++  if (GlobalSplitList)
++    GlobalSplitList->sp_prev_p = &asplit->sp_next;
++  GlobalSplitList = asplit;
++
++  return asplit;
++}
++
++/** Forward a split entry to another server.
++ * @param[in] cptr Local client that sent us the split entry.
++ * @param[in] sptr Originator of the split entry.
++ * @param[in] split Split entry to forward.
++ * @param[in] reason Reason to send upstream (used by split_remove)
++ */
++static void
++split_propagate(struct Client *cptr, struct Client *sptr,
++  struct Split *split, const char *reason)
++{
++  sendcmdto_serv_butone(sptr, CMD_SPLIT, cptr, "%s%c%s %Tu %Tu %Tu %Tu :%s",
++    SplitIsRemoving(split) ? "!" : "",
++    SplitIsActive(split) && !SplitIsRemoving(split) ? '+' : '-', /* always !- not !+ */
++    split->sp_server, split->sp_creation, split->sp_expire,
++    split->sp_lastmod, split->sp_lifetime,
++    reason != NULL ? reason : split->sp_reason);
++}
++
++/** Add a new server split entry.
++ * @param[in] cptr Local client that sent us the split entry.
++ * @param[in] sptr Originator of the split entry.
++ * @param[in] server Server name to split entry.
++ * @param[in] reason Reason for the split entry.
++ * @param[in] expire Expiration timestamp.
++ * @param[in] lastmod Last modification timestamp.
++ * @param[in] flags Flags to set on the split entry.
++ * @return Zero
++ */
++int
++split_add(struct Client *cptr, struct Client *sptr, char *server, const char *reason,
++       time_t creation, time_t expire, time_t lastmod, time_t lifetime,
++       unsigned int flags)
++{
++
++  /* TODO: check for proper masks - at least one dot and no wildcards? */
++  struct Split *asplit;
++  struct Client *acptr;
++
++  assert(0 != server);
++  assert(0 != reason);
++  assert(NULL != cptr);
++  assert(NULL != sptr);
++
++  Debug((DEBUG_DEBUG, "split_add(\"%s\", \"%s\", \"%s\", \"%s\", %Tu, %Tu, %Tu, "
++       "%Tu, 0x%04x)", cli_name(cptr), cli_name(sptr), server, reason,
++       creation, expire, lastmod, lifetime, flags));
++
++  /* not adding SPLIT for server that is linked
++   *   if sptr is my user throw error
++   *   otherwise ignore - SERVER and SPLIT messages can cross,
++   *   or a server is bursting and it will see our end and destroy the SPLITs
++   */
++  if ((acptr = FindServer(server))) {
++    if (MyUser(sptr))
++      sendcmdto_one(&me, CMD_NOTICE, sptr,
++        "%C :Cannot add SPLIT %s - server is linked.", sptr, server);
++    return 0;
++  }
++
++  /*
++   * You cannot set a negative (or zero) duration, nor can you set an
++   * duration greater than SPLIT_MAX_EXPIRE.
++   */
++  if (expire - CurrentTime <= 0 || expire - CurrentTime > SPLIT_MAX_EXPIRE) {
++    if (!IsServer(cptr) && MyConnect(cptr))
++      return send_reply(cptr, ERR_BADEXPIRE, expire - CurrentTime);
++    if (expire <= CurrentTime) /* no point going further */
++      /* TODO: check lifetime then ? because the SPLIT may simply be deactivated
++       * and we did not see it before
++       * if it were to be activated again we would get it again
++       * but should we not keep the same state on each server?
++       */
++      /* CHECK: sptr may have the wrong idea about the nettime?
++       * or we could be wrong?
++       * SETTIME ? could be dangerous and mess up things..
++       * perhaps raise some sort of warning to ops
++       * maybe if the difference is larger than a poor RTT over the network?
++       * 60 seconds?
++       * no no no! - see above
++       */
++      return 0;
++  }
++
++  /* inform ops and log it */
++  sendto_opmask_butone(0, SNO_NETWORK, "%s adding%sSPLIT for %s, expiring at %Tu: %s",
++    (feature_bool(FEAT_HIS_SNOTICES) || IsServer(sptr)) ?
++      cli_name(sptr) : cli_name((cli_user(sptr))->server),
++    !(flags & SPLIT_ACTIVE) ? " deactivated " : " ",
++    server, expire, reason);
++
++  /* TODO: add SPLIT log stuff or use JUPE? */
++  log_write(LS_JUPE, L_INFO, LOG_NOSNOTICE,
++    "%#C adding%sSPLIT for %s, expiring at %Tu: %s",
++    sptr, !(flags & SPLIT_ACTIVE) ? " deactivated " : " ",
++    server, expire, reason);
++
++  /* make the split entry */
++  asplit = split_make(server, reason, creation, expire, lastmod, lifetime, flags);
++  
++  assert(asplit);
++  
++  /* and propagate it */
++  split_propagate(cptr, sptr, asplit, NULL);
++
++  return 0;
++}
++
++
++/** Modify a split entry.
++ * @param[in] cptr Client that sent us the split modification.
++ * @param[in] sptr Client that originated the split modification.
++ * @param[in] split Split entry being modified.
++ * @param[in] action Resultant status of the G-line.
++ * @param[in] reason Reason.
++ * @param[in] expire Expiration time.
++ * @param[in] lastmod Last modification time.
++ * @param[in] lifetime Lifetime.
++ * @param[in] flags Bitwise combination of SPLIT_* flags.
++ * @return Zero.
++ */
++int
++split_modify(struct Client *cptr, struct Client *sptr, struct Split *split,
++  enum SplitAction action, const char *reason,
++  time_t creation, time_t expire, time_t lastmod, time_t lifetime,
++  unsigned int flags)
++{
++  struct Client* acptr;
++  char buf[BUFSIZE];
++  int pos = 0;
++
++  assert(split);
++  assert(NULL != cptr);
++  assert(NULL != sptr);
++
++  /* TODO: add action in the debug notice too */
++  Debug((DEBUG_DEBUG, "split_modify(\"%s\", \"%s\", \"%s\", \"%s\", %Tu, %Tu, %Tu, "
++       "%Tu, 0x%04x)", cli_name(cptr), cli_name(sptr), split->sp_server, reason,
++       creation, expire, lastmod, lifetime, flags));
++
++  /* not modifying SPLIT for server that is linked
++   *   if sptr is my user throw error
++   *   otherwise ignore - SERVER and SPLIT messages can cross.
++   *
++   * note: we cleanup SPLIT entries at end of burst,
++   *       and not when a server is introduced.
++   *       so between net junction and end of burst,
++   *       we can get SPLITs for a linked server.
++   */
++  /* TODO: should we free it here or let that be done in end of burst?
++   *   only when that is the ONLY situation we can get to this point!
++   *   but if we free here and it is a burst, the count is incorrect in
++   *   split_netmerge
++   *   IsBurst() is that true for only that single server or for all its downlinks?
++   *     I would guess single server only.. argh!
++   */
++  if ((acptr = FindServer(split->sp_server))) {
++    if (MyUser(sptr))
++      sendcmdto_one(&me, CMD_NOTICE, sptr,
++        "%C :Cannot modify SPLIT %s - server is linked.", sptr, split->sp_server);
++    split_free(split); /* and free it */
++    return 0;
++  }
++  
++  /* First, let's check lastmod... */
++  if (SplitLastMod(split) > lastmod) { /* we have a more recent version */
++    if (IsBurstOrBurstAck(cptr))
++      return 0; /* middle of a burst, it'll resync on its own */
++    return split_resend(cptr, split); /* resync the server */
++  } else if (SplitLastMod(split) == lastmod)
++     return 0; /* we have that version of the split entry... */
++  
++  /* All right, we know that there's a change of some sort.  What is it? */
++  /* first, check out the expiration time... */
++  /* TODO: expire != 0 or NULL - check  that we have something? */
++  if (expire != 0 && expire != split->sp_expire) {
++    if (expire - CurrentTime <= 0 || expire - CurrentTime > SPLIT_MAX_EXPIRE) {
++      if (!IsServer(sptr) && MyConnect(sptr)) /* bad expiration time */
++           send_reply(sptr, ERR_BADEXPIRE, expire - CurrentTime);
++         if (expire - CurrentTime <= 0) /* no point in going further */
++           /* TODO: same as in split_add - check lifetime? */
++        return 0;
++    }
++    flags |= SPLIT_EXPIRE;
++  } else
++    flags &= ~SPLIT_EXPIRE;
++  
++  /* Next, check out lifetime */
++  if (!(flags & SPLIT_LIFETIME) || !lifetime)
++    lifetime = split->sp_lifetime; /* use Split lifetime */
++
++  lifetime = IRCD_MAX(lifetime, expire); /* set lifetime to the max */
++
++  /* OK, let's see which is greater... */
++  if (lifetime > split->sp_lifetime)
++    flags |= SPLIT_LIFETIME; /* have to update lifetime */
++  else {
++    flags &= ~SPLIT_LIFETIME; /* no change to lifetime */
++    lifetime = 0;
++  }
++  
++  /* Finally, let's see if the reason needs to be updated */
++  /* TODO: (reason) or use != NULL / 0 ? */
++  if ((flags & SPLIT_REASON) && reason &&
++      ircd_strcmp(split->sp_reason, reason) != 0)
++    flags &= ~SPLIT_REASON; /* no changes to the reason */
++
++  /* OK, now let's take a look at the action... */
++  if ((action == SPLIT_ACTIVATE && SplitIsActive(split)) ||
++      (action == SPLIT_DEACTIVATE && !SplitIsActive(split)) ||
++      /* can't activate an expired split entry */
++      (IRCD_MAX(split->sp_expire, expire) <= CurrentTime))
++    action = SPLIT_MODIFY; /* no activity state modifications */
++
++  Debug((DEBUG_DEBUG,  "About to perform changes; flags 0x%04x, action %s",
++       flags, action == SPLIT_ACTIVATE ? "SPLIT_ACTIVATE" :
++       (action == SPLIT_DEACTIVATE ? "SPLIT_DEACTIVATE" :
++     (action == SPLIT_MODIFY ? "SPLIT_MODIFY" : "<UNKNOWN>"))));
++
++  /* If there are no changes to perform, do no changes */
++  if (!(flags & SPLIT_UPDATE) && action == SPLIT_MODIFY)
++    return 0;
++
++  /* Start by updating lastmod, if indicated... */
++  split->sp_lastmod = lastmod;
++
++  /* Then move on to activity status changes... */
++  switch (action) {
++  case SPLIT_ACTIVATE: /* activating split entry */
++    split->sp_flags |= SPLIT_ACTIVE; /* make it active... */
++    pos += ircd_snprintf(0, buf, sizeof(buf), " activating SPLIT");
++    break;
++
++  case SPLIT_DEACTIVATE: /* deactivating split entry */
++    split->sp_flags &= ~SPLIT_ACTIVE; /* make it inactive... */
++    pos += ircd_snprintf(0, buf, sizeof(buf), " deactivating SPLIT");
++    break;
++
++  case SPLIT_MODIFY: /* no change to activity status */
++    break;
++  }
++
++  /* Handle expiration changes... */
++  if (flags & SPLIT_EXPIRE) {
++    split->sp_expire = expire; /* save new expiration time */
++    if (pos < BUFSIZE)
++      pos += ircd_snprintf(0, buf + pos, sizeof(buf) - pos,
++               "%s%s changing expiration time to %Tu",
++               pos ? ";" : "",
++               pos && !(flags & (SPLIT_LIFETIME | SPLIT_REASON)) ?
++               " and" : "", expire);
++  }
++  
++  /* Next, handle lifetime changes... */
++  if (flags & SPLIT_LIFETIME) {
++    split->sp_lifetime = lifetime; /* save new lifetime */
++    if (pos < BUFSIZE)
++      pos += ircd_snprintf(0, buf + pos, sizeof(buf) - pos,
++               "%s%s extending record lifetime to %Tu",
++               pos ? ";" : "", pos && !(flags & SPLIT_REASON) ?
++               " and" : "", lifetime);
++  }
++
++  /* Now, handle reason changes... */
++  if (flags & SPLIT_REASON) {
++    MyFree(split->sp_reason); /* release old reason */
++    DupString(split->sp_reason, reason); /* store new reason */
++    if (pos < BUFSIZE)
++      pos += ircd_snprintf(0, buf + pos, sizeof(buf) - pos,
++               "%s%s changing reason to \"%s\"",
++               pos ? ";" : "", pos ? " and" : "", reason);
++  }
++
++  /* All right, inform ops... */
++  sendto_opmask_butone(0, SNO_NETWORK, "%s modifying SPLIT for %s:%s",
++    (feature_bool(FEAT_HIS_SNOTICES) || IsServer(sptr)) ?
++      cli_name(sptr) : cli_name((cli_user(sptr))->server),
++    split->sp_server, buf);
++
++  /* TODO: add SPLIT log stuff or use JUPE? */
++  /* and log the change */
++  log_write(LS_JUPE, L_INFO, LOG_NOSNOTICE,
++    "%#C modifying SPLIT for %s:%s",
++    sptr, split->sp_server, buf);
++
++  /* and propagate it */
++  split_propagate(cptr, sptr, split, NULL);
++
++  return 0;
++}
++
++
++/** Remove a split entry.
++ * @param[in] cptr Local client that sent us the split entry.
++ * @param[in] sptr Originator of the split entry.
++ * @param[in] split Split entry to remove.
++ * @param[in] reason  Reason for removing this split entry.
++ * @return Zero.
++ */
++int
++split_remove(struct Client *cptr, struct Client *sptr, struct Split *split, const char *reason)
++{
++  unsigned int saveflags = 0;
++
++  assert(0 != split);
++  assert(NULL != cptr);
++  assert(NULL != sptr);
++
++  Debug((DEBUG_DEBUG, "split_remove(\"%s\", \"%s\", \"%s\", \"%s\")",
++    cli_name(cptr), cli_name(sptr), split->sp_server, reason));
++
++  /* deactivate entry and mark it for removal (used in split_propagate) */
++  split->sp_flags |= SPLIT_REMOVING;
++  /* CHECK: turn bit SPLIT_ACTIVE off? */
++  split->sp_flags |= SPLIT_ACTIVE;
++
++  /* inform ops and log it */
++  sendto_opmask_butone(0, SNO_NETWORK, "%s removing SPLIT for %s, expiring at %Tu: %s (%s)",
++    (feature_bool(FEAT_HIS_SNOTICES) || IsServer(sptr)) ?
++      cli_name(sptr) : cli_name((cli_user(sptr))->server),
++    split->sp_server, split->sp_expire, split->sp_reason, reason);
++
++  /* TODO: add SPLIT log stuff or use JUPE? */
++  log_write(LS_JUPE, L_INFO, LOG_NOSNOTICE,
++    "%#C removing SPLIT for %s, expiring at %Tu: %s (%s)",
++    sptr, split->sp_server, split->sp_expire, split->sp_reason, reason);
++
++  /* TODO: the reason supplied for removing this SPLIT does not go upstream
++   * either propagate manually here, or update the record or?
++   */
++  /* propagate it */
++  split_propagate(cptr, sptr, split, reason);
++
++  /* destroy it */
++  split_free(split);
++
++  return 0;
++}
++
++
++/** Find a split entry by name.
++ * @param[in] server Split entry name to search for.
++ * @return Matching split entry (or NULL if none match).
++ */
++struct Split *
++split_find(char *server)
++{
++  struct Split* split;
++  struct Split* ssplit;
++
++  for (split = GlobalSplitList; split; split = ssplit) { /* go through splits */
++    ssplit = split->sp_next;
++
++    if (split_expire(split)) /* expire any that need expiring */
++      split_free(split);
++    else if (0 == ircd_strcmp(server, split->sp_server)) /* found it yet? */
++      return split;
++  }
++  /* TODO: we return 0 not NULL? */
++  return 0;
++}
++
++/** Unlink and free an unused split entry.
++ * @param[in] split Server split entry to free.
++ */
++void
++split_free(struct Split* split)
++{
++  /* TODO: use 0 or NULL ? */
++  assert(0 != split);
++
++  *split->sp_prev_p = split->sp_next; /* squeeze this split entry out */
++  if (split->sp_next)
++    split->sp_next->sp_prev_p = split->sp_prev_p;
++
++  /* CHECK: the other fields in this struct are destroyed with MyFree() call? */
++  MyFree(split->sp_server);  /* and free up the memory */
++  MyFree(split->sp_reason);
++  MyFree(split);
++}
++
++
++/** Check whether a split entry has past its life time.
++ *   when entry is active and past expire time, but not life time, deactivate it
++ * @param[in] split Server split entry to check.
++ * @return 1 when entry can be free'd, 0 otherwise.
++ */
++int
++split_expire(struct Split* split)
++{
++  assert(0 != split);
++
++  /* past lifetime */
++  if (split->sp_lifetime <= CurrentTime)
++    return 1;
++
++  /* past expire time, deactivate entry if it is active */
++  if ((split->sp_expire <= CurrentTime) && SplitIsActive(split))
++    /* CHECK: turn bit SPLIT_ACTIVE off */
++    split->sp_flags &= SPLIT_ACTIVE;
++
++  return 0;
++}
++
++/** Send the full list of split entries to \a cptr.
++ * @param[in] cptr Local server to send split entries to.
++ */
++void
++split_burst(struct Client *cptr)
++{
++  struct Split *split;
++  struct Split *ssplit;
++
++  assert(NULL != cptr);
++
++  for (split = GlobalSplitList; split; split = ssplit) { /* go through splits */
++    ssplit = split->sp_next;
++
++    /* expire any that need expiring */
++    if (split_expire(split))      
++      split_free(split);
++    /* if we have an entry for cptr, dont send it - but do not free here yet
++     *   free it at end of burst, to get the correct count for SPLITs removed.
++     */
++    else if (ircd_strcmp(split->sp_server, cli_name(cptr)) == 0)
++      continue;
++    else
++      sendcmdto_one(&me, CMD_SPLIT, cptr, "%c%s %Tu %Tu %Tu %Tu :%s",
++        SplitIsActive(split) ? '+' : '-', split->sp_server,
++        split->sp_creation, split->sp_expire,
++        split->sp_lastmod, split->sp_lifetime,
++        split->sp_reason);
++  }
++}
++
++/** Forward a split to another server.
++ * @param[in] cptr Server to send split entries to.
++ * @param[in] split Split to forward.
++ * @return Zero.
++ */
++int
++split_resend(struct Client *cptr, struct Split *split)
++{
++  sendcmdto_one(&me, CMD_SPLIT, cptr, "%c%s %Tu %Tu %Tu %Tu :%s",
++    SplitIsActive(split) ? '+' : '-', split->sp_server,
++    split->sp_creation, split->sp_expire,
++    split->sp_lastmod, split->sp_lifetime,
++    split->sp_reason);
++
++  return 0;
++}
++
++/** Send a split entry (or a list of split entries) to a server.
++ * @param[in] sptr Client searching for split entries.
++ * @param[in] server Name of split entry to search for (if NULL, list all).
++ * @return Zero.
++ */
++int
++split_list(struct Client *sptr, char *server)
++{
++  struct Split *split;
++  struct Split *ssplit;
++
++  assert(NULL != sptr);
++
++  /* TODO: wildcard matching? */
++  if (server) {
++    if (!(split = split_find(server))) /* no such split */
++      return send_reply(sptr, ERR_NOSUCHSPLIT, server);
++
++    /* send split information along */
++    send_reply(sptr, RPL_SPLITLIST, split->sp_server,
++      split->sp_creation, split->sp_expire,
++      split->sp_lastmod, split->sp_lifetime,
++      SplitIsActive(split) ? '+' : '-', split->sp_reason);
++  } else {
++    for (split = GlobalSplitList; split; split = ssplit) { /* go through splits */
++      ssplit = split->sp_next;
++
++      if (split_expire(split)) /* expire any that need expiring */
++        split_free(split);
++      else /* send split information along */
++        send_reply(sptr, RPL_SPLITLIST, split->sp_server,
++          split->sp_creation, split->sp_expire,
++          split->sp_lastmod, split->sp_lifetime,
++          SplitIsActive(split) ? '+' : '-', split->sp_reason);
++    }
++  }
++
++  /* end of splitlist information */
++  return send_reply(sptr, RPL_ENDOFSPLITLIST);
++}
++
++
++/** Auto destroy SPLITs for servers gained in a netmerge
++ * @param[in] cptr Server that link to the network
++ * @return Number of destroyed SPLITs
++ */
++int
++split_merge(struct Client *server)
++{
++  struct DLink *lp;
++  struct Split *split;
++  int count = 0;
++
++  assert(NULL != server);
++
++  Debug((DEBUG_DEBUG, "split_merge(\"%s\")", cli_name(server)));
++
++  /* find the SPLIT for this server */
++  if ((split = split_find(cli_name(server)))) {
++    split_free(split);
++    count++;
++  }
++
++  /* go over its downlinks */
++  for (lp = cli_serv(server)->down; lp; lp = lp->next)
++    count += split_merge(lp->value.cptr);
++  return count;
++}
++
++
++/** Auto create SPLITs for servers lost in a netbreak
++ * @param[in] server Server that lost link to network
++ * @param[in] reason Reason to add to SPLITs
++ * @return Number of created SPLITs
++ */
++int
++split_break(struct Client *server, const char *reason)
++{
++  struct DLink *lp;
++  struct Split *split;
++  int count = 0;
++  time_t creation = CurrentTime, expire = CurrentTime + SPLIT_AUTO_EXPIRE,
++    lastmod = CurrentTime, lifetime = expire;
++  unsigned int flags = SPLIT_ACTIVE;
++
++  assert(NULL != server);
++
++  Debug((DEBUG_DEBUG, "split_break(\"%s\", \"%s\")", cli_name(server), reason));
++
++  /* find the SPLIT for this server */
++  if (!(split = split_find(cli_name(server)))) {
++    split_make(cli_name(server), reason, creation, expire, lastmod, lifetime, flags);
++    count++;
++  }
++
++  /* go over its downlinks */
++  for (lp = cli_serv(server)->down; lp; lp = lp->next)
++    count += split_break(lp->value.cptr, reason);
++  return count;
++}
++
++
++/** Auto create SPLITs for servers we have a Connect Block for
++ * 
++ */
++void
++split_conf()
++{
++  struct ConfItem *conf;
++  struct Client *server;
++  struct Split *split;
++  struct Split *asplit;
++  time_t creation = CurrentTime, expire = CurrentTime + SPLIT_AUTO_EXPIRE,
++    lastmod = CurrentTime, lifetime = expire;
++  unsigned int flags = SPLIT_ACTIVE;
++  char reason[BUFSIZE];
++
++  Debug((DEBUG_DEBUG, "split_conf()"));
++
++  /* we are not set to generate SPLITs */
++  if (!feature_bool(FEAT_SPLIT))
++    return;
++
++  ircd_snprintf(0, reason, sizeof(reason),
++    "Generated upon loading conf file on %s", cli_name(&me));
++
++  /* go over the conf contents */
++  for (conf = GlobalConfList; conf; conf = conf->next) {
++    /* not a Connect Block */
++    if (CONF_SERVER != conf->status)
++      continue;
++    /* server is linked */
++    if (server = FindServer(conf->name))
++      continue;
++    /* we have a SPLIT for this server already */
++    if (split = split_find(conf->name))
++      continue;
++    /* inform ops and log it */
++    sendto_opmask_butone(0, SNO_NETWORK, "%C adding SPLIT for %s, expiring at %Tu: %s",
++      &me, conf->name, expire, reason);
++
++    /* TODO: add SPLIT log stuff or use JUPE? */
++    log_write(LS_JUPE, L_INFO, LOG_NOSNOTICE,
++      "%C adding SPLIT for %s, expiring at %Tu: %s",
++      &me, conf->name, expire, reason);
++
++    /* make the split entry */
++    asplit = split_make(conf->name, reason, creation, expire, lastmod, lifetime, flags);
++  
++    assert(asplit);
++  
++    /* and propagate it */
++    split_propagate(&me, &me, asplit, NULL);
++  }
++}
++
++
++/** Statistics callback to list SPLITs.
++ * @param[in] sptr Client requesting statistics.
++ * @param[in] sd Stats descriptor for request (ignored).
++ * @param[in] param Extra parameter from user (ignored).
++ */
++void
++split_stats(struct Client *sptr, const struct StatDesc *sd,
++            char *param)
++{
++  struct Split *split;
++  struct Split *ssplit;
++
++  send_reply(sptr, SND_EXPLICIT | RPL_STATSSPLIT,
++    "S servername creation expire lastmod lifetime active :reason");
++  for (split = GlobalSplitList; split; split = split->sp_next)
++    send_reply(sptr, RPL_STATSSPLIT, split->sp_server,
++      split->sp_creation, split->sp_expire, split->sp_lastmod, split->sp_lifetime, 
++      SplitIsActive(split) ? '+' : '-', split->sp_reason);
++}
++
++
++/** Count split entries and memory used by them.
++ * @param[out] sp_size Receives total number of bytes allocated for split entries.
++ * @return Number of split entries currently allocated.
++ */
++int
++split_memory_count(size_t *sp_size)
++{
++  struct Split *split;
++  unsigned int sp = 0;
++
++  /* TODO: check for expired entries? */
++  for (split = GlobalSplitList; split; split = split->sp_next)
++  {
++    sp++;
++    *sp_size += sizeof(struct Split);
++    *sp_size += split->sp_server ? (strlen(split->sp_server) + 1) : 0;
++    *sp_size += split->sp_reason ? (strlen(split->sp_reason) + 1) : 0;
++  }
++  return sp;
++}
++
diff --git a/statsheader.patch b/statsheader.patch
new file mode 100644 (file)
index 0000000..62f0e83
--- /dev/null
@@ -0,0 +1,138 @@
+add header to /STATS output and make TOTAL: line in /STATS z more clear.
+
+diff -r 92f6cb6562e2 include/numeric.h
+--- a/include/numeric.h        Tue Jan 13 22:17:04 2009 +0000
++++ b/include/numeric.h        Sun Jan 18 17:44:05 2009 +0100
+@@ -117,6 +117,7 @@
+       RPL_STATSVLINE       227           unreal */
+ #define RPL_STATSALINE       226        /* Hybrid, Undernet */
+ #define RPL_STATSQLINE       228        /* Undernet extension */
++#define RPL_STATSHEADER      230        /* QuakeNet extension */
+ /*      RPL_SERVICEINFO      231      unused */
+ /*      RPL_ENDOFSERVICES    232      unused */
+diff -r 92f6cb6562e2 ircd/ircd_res.c
+--- a/ircd/ircd_res.c  Tue Jan 13 22:17:04 2009 +0000
++++ b/ircd/ircd_res.c  Sun Jan 18 17:44:05 2009 +0100
+@@ -917,6 +917,10 @@
+   int i;
+   char ipaddr[128];
++  /* send header so the client knows what we are showing */
++  send_reply(source_p, SND_EXPLICIT | RPL_STATSHEADER,
++    "A DNS-server");
++
+   for (i = 0; i < irc_nscount; i++)
+   {
+     ircd_ntoa_r(ipaddr, &irc_nsaddr_list[i].addr);
+diff -r 92f6cb6562e2 ircd/s_debug.c
+--- a/ircd/s_debug.c   Tue Jan 13 22:17:04 2009 +0000
++++ b/ircd/s_debug.c   Sun Jan 18 17:44:05 2009 +0100
+@@ -389,7 +389,7 @@
+ #endif
+   send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG,
+-           ":Total: ww %zu ch %zu cl %zu co %zu db %zu ms %zu mb %zu",
++           ":Total: Whowas %zu Channels %zu Clients %zu Config %zu DBufs %zu Msgs %zu MsgBufs %zu",
+            totww, totch, totcl, com, dbufs_allocated, msg_allocated,
+            msgbuf_allocated);
+ }
+diff -r 92f6cb6562e2 ircd/s_err.c
+--- a/ircd/s_err.c     Tue Jan 13 22:17:04 2009 +0000
++++ b/ircd/s_err.c     Sun Jan 18 17:44:05 2009 +0100
+@@ -492,7 +492,7 @@
+ /* 229 */
+   { 0 },
+ /* 230 */
+-  { 0 },
++  { RPL_STATSHEADER, 0, "230" },
+ /* 231 */
+   { 0 },
+ /* 232 */
+diff -r 92f6cb6562e2 ircd/s_stats.c
+--- a/ircd/s_stats.c   Tue Jan 13 22:17:04 2009 +0000
++++ b/ircd/s_stats.c   Sun Jan 18 17:44:05 2009 +0100
+@@ -92,6 +92,16 @@
+   int maximum;
+   char *host, *pass, *name, *username, *hub_limit;
++  /* send header so the client knows what we are showing */
++  if (sd->sd_funcdata == CONF_UWORLD)
++    send_reply(sptr, SND_EXPLICIT | RPL_STATSHEADER, "U server");
++  else if (sd->sd_funcdata == CONF_SERVER)
++    send_reply(sptr, SND_EXPLICIT | RPL_STATSHEADER,
++      "C server * port hoplimit hubmask class");
++  else if (sd->sd_funcdata == CONF_OPERATOR)
++    send_reply(sptr, SND_EXPLICIT | RPL_STATSHEADER,
++      "O user@host * name class");
++
+   for (tmp = GlobalConfList; tmp; tmp = tmp->next)
+   {
+     if ((tmp->status & sd->sd_funcdata))
+@@ -137,6 +147,10 @@
+ {
+   const struct CRuleConf* p = conf_get_crule_list();
++  /* send header so the client knows what we are showing */
++  send_reply(to, SND_EXPLICIT | RPL_STATSHEADER,
++    "%c server rule", p->type & CRULE_ALL ? 'D' : 'd');
++
+   for ( ; p; p = p->next)
+   {
+     if (p->type & sd->sd_funcdata)
+@@ -167,6 +181,10 @@
+   int wilds = 0;
+   int count = 1000;
++  /* send header so the client knows what we are showing */
++  send_reply(to, SND_EXPLICIT | RPL_STATSHEADER,
++    "I [user@]hostmask maximum IPmask port class");
++
+   if (!param)
+   {
+     stats_configured_links(to, sd, param);
+@@ -204,6 +222,11 @@
+ report_deny_list(struct Client* to)
+ {
+   const struct DenyConf* p = conf_get_deny_list();
++
++  /* send header so the client knows what we are showing */
++  send_reply(to, SND_EXPLICIT | RPL_STATSHEADER,
++    "K user@host \"message or path/file\" \"realname\" 0 0");
++
+   for ( ; p; p = p->next)
+     send_reply(to, RPL_STATSKLINE, p->bits > 0 ? 'k' : 'K',
+                p->usermask ? p->usermask : "*",
+@@ -251,6 +274,10 @@
+   }
+   else
+     host = mask;
++
++  /* send header so the client knows what we are showing */
++  send_reply(sptr, SND_EXPLICIT | RPL_STATSHEADER,
++    "K user@host \"message or path/file\" \"realname\" 0 0");
+   for (conf = conf_get_deny_list(); conf; conf = conf->next)
+   {
+@@ -377,6 +404,10 @@
+ {
+   struct Message *mptr;
++  /* send header so the client knows what we are showing */
++  send_reply(to, SND_EXPLICIT | RPL_STATSHEADER,
++    "m command count bytes");
++
+   for (mptr = msgtab; mptr->cmd; mptr++)
+     if (mptr->count)
+       send_reply(to, RPL_STATSCOMMANDS, mptr->cmd, mptr->count, mptr->bytes);
+@@ -391,6 +422,10 @@
+ stats_quarantine(struct Client* to, const struct StatDesc* sd, char* param)
+ {
+   struct qline *qline;
++
++  /* send header so the client knows what we are showing */
++  send_reply(to, SND_EXPLICIT | RPL_STATSHEADER,
++    "Q channel reason");
+   for (qline = GlobalQuarantineList; qline; qline = qline->next)
+   {