+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;
++}
++