]> jfr.im git - irc/quakenet/snircd-patchqueue.git/blame - split.patch
some more patches: split functionality, make stats output clearer, start to add ...
[irc/quakenet/snircd-patchqueue.git] / split.patch
CommitLineData
12e82c05 1work in progress
2Add split functionality into ircd
3Add /split command
4Add /stats S/split (make /STATS s/S case sensitive, s spoofhosts)
5Add feature SPLIT
6Add split.c split.h m_split.c
7
8diff -r 2da61ac38fa1 include/handlers.h
9--- a/include/handlers.h Sun Jan 18 14:18:36 2009 +0100
10+++ b/include/handlers.h Sun Jan 18 15:26:56 2009 +0100
11@@ -139,6 +139,7 @@
12 extern int m_registered(struct Client*, struct Client*, int, char*[]);
13 extern int m_sethost(struct Client*, struct Client*, int, char*[]);
14 extern int m_silence(struct Client*, struct Client*, int, char*[]);
15+extern int m_split(struct Client*, struct Client*, int, char*[]);
16 extern int m_stats(struct Client*, struct Client*, int, char*[]);
17 extern int m_time(struct Client*, struct Client*, int, char*[]);
18 extern int m_topic(struct Client*, struct Client*, int, char*[]);
19@@ -178,6 +179,7 @@
20 extern int mo_rping(struct Client*, struct Client*, int, char*[]);
21 extern int mo_set(struct Client*, struct Client*, int, char*[]);
22 extern int mo_settime(struct Client*, struct Client*, int, char*[]);
23+extern int mo_split(struct Client*, struct Client*, int, char*[]);
24 extern int mo_squit(struct Client*, struct Client*, int, char*[]);
25 extern int mo_stats(struct Client*, struct Client*, int, char*[]);
26 extern int mo_trace(struct Client*, struct Client*, int, char*[]);
27@@ -232,6 +234,7 @@
28 extern int ms_sethost(struct Client*, struct Client*, int, char*[]);
29 extern int ms_settime(struct Client*, struct Client*, int, char*[]);
30 extern int ms_silence(struct Client*, struct Client*, int, char*[]);
31+extern int ms_split(struct Client*, struct Client*, int, char*[]);
32 extern int ms_squit(struct Client*, struct Client*, int, char*[]);
33 extern int ms_stats(struct Client*, struct Client*, int, char*[]);
34 extern int ms_topic(struct Client*, struct Client*, int, char*[]);
35diff -r 2da61ac38fa1 include/ircd_features.h
36--- a/include/ircd_features.h Sun Jan 18 14:18:36 2009 +0100
37+++ b/include/ircd_features.h Sun Jan 18 15:26:56 2009 +0100
38@@ -109,6 +109,7 @@
39 FEAT_SETHOST,
40 FEAT_SETHOST_USER,
41 FEAT_SETHOST_AUTO,
42+ FEAT_SPLIT,
43
44 /* HEAD_IN_SAND Features */
45 FEAT_HIS_SNOTICES,
46@@ -137,6 +138,7 @@
47 FEAT_HIS_STATS_q,
48 FEAT_HIS_STATS_R,
49 FEAT_HIS_STATS_r,
50+ FEAT_HIS_STATS_S,
51 FEAT_HIS_STATS_s,
52 FEAT_HIS_STATS_t,
53 FEAT_HIS_STATS_T,
54diff -r 2da61ac38fa1 include/msg.h
55--- a/include/msg.h Sun Jan 18 14:18:36 2009 +0100
56+++ b/include/msg.h Sun Jan 18 15:26:56 2009 +0100
57@@ -332,6 +332,10 @@
58 #define TOK_JUPE "JU"
59 #define CMD_JUPE MSG_JUPE, TOK_JUPE
60
61+#define MSG_SPLIT "SPLIT" /* SPLIT */
62+#define TOK_SPLIT "SP"
63+#define CMD_SPLIT MSG_SPLIT, TOK_SPLIT
64+
65 #define MSG_OPMODE "OPMODE" /* OPMO */
66 #define TOK_OPMODE "OM"
67 #define CMD_OPMODE MSG_OPMODE, TOK_OPMODE
68diff -r 2da61ac38fa1 include/numeric.h
69--- a/include/numeric.h Sun Jan 18 14:18:36 2009 +0100
70+++ b/include/numeric.h Sun Jan 18 15:26:56 2009 +0100
71@@ -117,6 +117,7 @@
72 RPL_STATSVLINE 227 unreal */
73 #define RPL_STATSALINE 226 /* Hybrid, Undernet */
74 #define RPL_STATSQLINE 228 /* Undernet extension */
75+#define RPL_STATSSPLIT 229 /* QuakeNet extension */
76
77 /* RPL_SERVICEINFO 231 unused */
78 /* RPL_ENDOFSERVICES 232 unused */
79@@ -177,6 +178,8 @@
80 #define RPL_STATSDLINE 275 /* Undernet extension */
81 #define RPL_STATSRLINE 276 /* Undernet extension */
82
83+#define RPL_SPLITLIST 278 /* QuakeNet extension */
84+#define RPL_ENDOFSPLITLIST 279 /* QuakeNet extension */
85 #define RPL_GLIST 280 /* Undernet extension */
86 #define RPL_ENDOFGLIST 281 /* Undernet extension */
87 #define RPL_JUPELIST 282 /* Undernet extension - jupe -Kev */
88@@ -440,6 +443,7 @@
89 /* ERR_GHOSTEDCLIENT 503 efnet */
90 /* ERR_VWORLDWARN 503 austnet */
91
92+#define ERR_NOSUCHSPLIT 510 /* QuakeNet extension */
93 #define ERR_SILELISTFULL 511 /* Undernet extension */
94 /* ERR_NOTIFYFULL 512 aircd */
95 /* ERR_TOOMANYWATCH 512 Numeric List: Dalnet */
96diff -r 2da61ac38fa1 include/split.h
97--- /dev/null Thu Jan 01 00:00:00 1970 +0000
98+++ b/include/split.h Sun Jan 18 15:26:56 2009 +0100
99@@ -0,0 +1,121 @@
100+/* TODO: ifndef ? */
101+#ifndef INCLUDED_jupe_h
102+#define INCLUDED_jupe_h
103+/*
104+ * IRC - Internet Relay Chat, include/split.h
105+ * Copyright (C) 1990 Jarkko Oikarinen and
106+ * University of Oulu, Computing Center
107+ * Copyright (C) 2000 Kevin L. Mitchell <klmitch@mit.edu>
108+ *
109+ * This program is free software; you can redistribute it and/or modify
110+ * it under the terms of the GNU General Public License as published by
111+ * the Free Software Foundation; either version 2, or (at your option)
112+ * any later version.
113+ *
114+ * This program is distributed in the hope that it will be useful,
115+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
116+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
117+ * GNU General Public License for more details.
118+ *
119+ * You should have received a copy of the GNU General Public License
120+ * along with this program; if not, write to the Free Software
121+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
122+ */
123+/** @file
124+ * @brief Interface and declarations for split server handling.
125+ * @version $Id: jupe.h 1208 2004-10-03 14:12:35Z entrope $
126+ */
127+/* TODO: ifndef ? */
128+#ifndef INCLUDED_sys_types_h
129+#include <sys/types.h>
130+#define INCLUDED_sys_types_h
131+#endif
132+
133+
134+struct Client;
135+struct StatDesc;
136+
137+#define SPLIT_MAX_EXPIRE 604800 /**< Maximum split expiration time (7 days). */
138+#define SPLIT_AUTO_EXPIRE 604800 /**< Expireation time used for auto created entries. */
139+
140+/* Describes a SPLIT server entry. */
141+struct Split {
142+ struct Split* sp_next; /**< Pointer to next Split. */
143+ struct Split** sp_prev_p; /**< Pointer to previous next pointer. */
144+ char* sp_server; /**< Name of server. */
145+ char* sp_reason; /**< Reason. */
146+ time_t sp_creation; /**< TODO: Creation time. What are we using this for then? */
147+ time_t sp_expire; /**< Expiration time. */
148+ time_t sp_lastmod; /**< Last modification time. */
149+ time_t sp_lifetime; /**< Life time. */
150+ unsigned int sp_flags; /**< Status flags. */
151+};
152+
153+/** Split state flags. */
154+#define SPLIT_ACTIVE 0x0001 /**< Split is active. */
155+#define SPLIT_REMOVING 0x0002 /**< Split is about to be destroyed. */
156+
157+/* TODO: these ; after } needed ? */
158+/* Actions to perform on a SPLIT. */
159+enum SplitAction {
160+ SPLIT_ACTIVATE, /**< SPLIT should be activated. */
161+ SPLIT_DEACTIVATE, /**< SPLIT should be deactivated. */
162+ SPLIT_MODIFY, /**< SPLIT should be modified. */
163+ SPLIT_REMOVE /**< SPLIT should be removed. */
164+};
165+
166+/* TODO: what values to use here? */
167+/* Split update flags. */
168+#define SPLIT_EXPIRE 0x0002 /**< Expiration time update. */
169+#define SPLIT_LIFETIME 0x0004 /**< Record lifetime update. */
170+#define SPLIT_REASON 0x0008 /**< Reason update. */
171+#define SPLIT_CREATION 0x0010 /**< Creation time update. */
172+#define SPLIT_MODIFY 0x0020 /**< No state change. */
173+
174+/* mask for Split update flags. */
175+#define SPLIT_UPDATE (SPLIT_EXPIRE | SPLIT_LIFETIME | SPLIT_REASON)
176+
177+/* test whether a split entry is active. */
178+#define SplitIsActive(s) ((s)->sp_flags & SPLIT_ACTIVE)
179+/* test whether a split entry is marked for forecd removal. */
180+#define SplitIsRemoving(s) ((s)->sp_flags & SPLIT_REMOVING)
181+
182+/* TODO: these are not used? some are, check! */
183+/* get the server name for a split entry. */
184+#define SplitServer(s) ((s)->sp_server)
185+/* get the reason for a split entry. */
186+#define SplitReason(s) ((s)->sp_reason)
187+/* get the creation time for split entry. */
188+#define SplitCreation(s) ((s)->sp_creation)
189+/* get the expiration time for split entry. */
190+#define SplitExpire(s) ((s)->sp_expire)
191+/* get the last modification time for split entry. */
192+#define SplitLastMod(s) ((s)->sp_lastmod)
193+/* get the life time for split entry. */
194+#define SplitLifeTime(s) ((s)->sp_lifetime)
195+
196+extern int split_add(struct Client *cptr, struct Client *sptr,
197+ char *server, const char *reason,
198+ time_t creation, time_t expire, time_t lastmod, time_t lifetime,
199+ unsigned int flags);
200+extern int split_modify(struct Client *cptr, struct Client *sptr,
201+ struct Split *split, enum SplitAction action, const char *reason,
202+ time_t creation, time_t expire, time_t lastmod, time_t lifetime,
203+ unsigned int flags);
204+extern int split_remove(struct Client *cptr, struct Client *sptr,
205+ struct Split *split, const char *reason);
206+
207+extern struct Split* split_find(char *server);
208+extern void split_free(struct Split *split);
209+extern int split_expire(struct Split* split);
210+extern void split_burst(struct Client *cptr);
211+extern int split_resend(struct Client *cptr, struct Split *split);
212+extern int split_list(struct Client *sptr, char *server);
213+extern int split_merge(struct Client *server);
214+extern int split_break(struct Client *server, const char *reason);
215+extern void split_conf();
216+extern void split_stats(struct Client *sptr, const struct StatDesc *sd, char *param);
217+extern int split_memory_count(size_t *sp_size);
218+
219+/* TODO: endif ? */
220+#endif /* INCLUDED_jupe_h */
221diff -r 2da61ac38fa1 ircd/Makefile.in
222--- a/ircd/Makefile.in Sun Jan 18 14:18:36 2009 +0100
223+++ b/ircd/Makefile.in Sun Jan 18 15:26:56 2009 +0100
224@@ -173,6 +173,7 @@
225 m_sethost.c \
226 m_settime.c \
227 m_silence.c \
228+ m_split.c \
229 m_squit.c \
230 m_stats.c \
231 m_time.c \
232@@ -212,6 +213,7 @@
233 s_stats.c \
234 s_user.c \
235 send.c \
236+ split.c \
237 uping.c \
238 userload.c \
239 whocmds.c \
240@@ -1052,6 +1054,15 @@
241 ../include/ircd_chattr.h ../include/list.h ../include/msg.h \
242 ../include/numeric.h ../include/numnicks.h ../include/s_user.h \
243 ../include/send.h ../include/struct.h
244+m_split.o: m_split.c ../config.h ../include/client.h ../include/ircd_defs.h \
245+ ../include/dbuf.h ../include/msgq.h ../include/ircd_events.h \
246+ ../config.h ../include/ircd_handler.h ../include/res.h \
247+ ../include/capab.h ../include/split.h ../include/hash.h \
248+ ../include/ircd.h ../include/struct.h ../include/ircd_features.h \
249+ ../include/ircd_log.h ../include/ircd_reply.h ../include/ircd_string.h \
250+ ../include/ircd_chattr.h ../include/match.h ../include/msg.h \
251+ ../include/numeric.h ../include/numnicks.h ../include/s_conf.h \
252+ ../include/client.h ../include/s_misc.h ../include/send.h
253 m_squit.o: m_squit.c ../config.h ../include/client.h \
254 ../include/ircd_defs.h ../include/dbuf.h ../include/msgq.h \
255 ../include/ircd_events.h ../config.h ../include/ircd_handler.h \
256@@ -1325,6 +1336,7 @@
257 ../include/msgq.h ../include/numeric.h ../include/numnicks.h \
258 ../include/res.h ../include/s_bsd.h ../include/s_conf.h \
259 ../include/s_user.h ../include/s_stats.h ../include/send.h \
260+ ../include/split.h \
261 ../include/struct.h ../include/sys.h ../include/whowas.h
262 s_err.o: s_err.c ../config.h ../include/numeric.h ../include/ircd_log.h \
263 ../include/s_debug.h ../config.h ../include/ircd_defs.h
264@@ -1410,6 +1422,16 @@
265 ../include/msg.h ../include/numnicks.h ../include/parse.h \
266 ../include/s_bsd.h ../include/s_debug.h ../include/s_misc.h \
267 ../include/s_user.h ../include/struct.h ../include/sys.h
268+split.o: split.c ../config.h ../include/split.h ../include/client.h \
269+ ../include/ircd_defs.h ../include/dbuf.h ../include/msgq.h \
270+ ../include/ircd_events.h ../config.h ../include/ircd_handler.h \
271+ ../include/res.h ../include/capab.h ../include/hash.h ../include/ircd.h \
272+ ../include/struct.h ../include/ircd_alloc.h ../include/ircd_features.h \
273+ ../include/ircd_log.h ../include/ircd_reply.h ../include/ircd_string.h \
274+ ../include/ircd_chattr.h ../include/match.h ../include/msg.h \
275+ ../include/numeric.h ../include/numnicks.h ../include/s_bsd.h \
276+ ../include/s_misc.h ../include/send.h ../include/struct.h \
277+ ../include/sys.h
278 uping.o: uping.c ../config.h ../include/uping.h ../include/ircd_defs.h \
279 ../include/ircd_events.h ../config.h ../include/res.h \
280 ../include/client.h ../include/dbuf.h ../include/msgq.h \
281diff -r 2da61ac38fa1 ircd/ircd.c
282--- a/ircd/ircd.c Sun Jan 18 14:18:36 2009 +0100
283+++ b/ircd/ircd.c Sun Jan 18 15:26:56 2009 +0100
284@@ -55,6 +55,7 @@
285 #include "s_misc.h"
286 #include "s_stats.h"
287 #include "send.h"
288+#include "split.h"
289 #include "sys.h"
290 #include "uping.h"
291 #include "userload.h"
292@@ -763,6 +764,9 @@
293 Debug((DEBUG_NOTICE, "Server ready..."));
294 log_write(LS_SYSTEM, L_NOTICE, 0, "Server Ready");
295
296+ /* create SPLITs */
297+ split_conf();
298+
299 event_loop();
300
301 return 0;
302diff -r 2da61ac38fa1 ircd/ircd_features.c
303--- a/ircd/ircd_features.c Sun Jan 18 14:18:36 2009 +0100
304+++ b/ircd/ircd_features.c Sun Jan 18 15:26:56 2009 +0100
305@@ -363,6 +363,7 @@
306 F_B(SETHOST, 0, 0, 0),
307 F_B(SETHOST_USER, 0, 0, 0),
308 F_B(SETHOST_AUTO, 0, 0, 0),
309+ F_B(SPLIT, 0, 1, 0),
310
311 /* HEAD_IN_SAND Features */
312 F_B(HIS_SNOTICES, 0, 1, 0),
313@@ -391,6 +392,7 @@
314 F_B(HIS_STATS_q, 0, 1, 0),
315 F_B(HIS_STATS_R, 0, 1, 0),
316 F_B(HIS_STATS_r, 0, 1, 0),
317+ F_B(HIS_STATS_S, 0, 1, 0),
318 F_B(HIS_STATS_s, 0, 1, 0),
319 F_B(HIS_STATS_t, 0, 1, 0),
320 F_B(HIS_STATS_T, 0, 1, 0),
321diff -r 2da61ac38fa1 ircd/m_endburst.c
322--- a/ircd/m_endburst.c Sun Jan 18 14:18:36 2009 +0100
323+++ b/ircd/m_endburst.c Sun Jan 18 15:26:56 2009 +0100
324@@ -85,6 +85,7 @@
325 #include "client.h"
326 #include "hash.h"
327 #include "ircd.h"
328+#include "ircd_features.h"
329 #include "ircd_log.h"
330 #include "ircd_reply.h"
331 #include "ircd_string.h"
332@@ -92,6 +93,7 @@
333 #include "numeric.h"
334 #include "numnicks.h"
335 #include "send.h"
336+#include "split.h"
337
338 /* #include <assert.h> -- Now using assert in ircd_log.h */
339
340@@ -125,6 +127,7 @@
341 int ms_end_of_burst(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
342 {
343 struct Channel *chan, *next_chan;
344+ int split;
345
346 assert(0 != cptr);
347 assert(0 != sptr);
348@@ -135,6 +138,10 @@
349 dump_map(sptr, "*", 0, report_new_links, 0);
350 sendto_opmask_butone(0, SNO_NETWORK, "Completed net.burst from %C.",
351 sptr);
352+ /* cleanup SPLITs, but only bother ops when FEAT_SPLIT is enabled. */
353+ split = split_merge(sptr);
354+ if (feature_bool(FEAT_SPLIT))
355+ sendto_opmask_butone(0, SNO_NETWORK, "Removed %d SPLIT entries.", split);
356 if (MyConnect(sptr))
357 sendcmdto_one(&me, CMD_END_OF_BURST_ACK, sptr, "");
358
359diff -r 2da61ac38fa1 ircd/m_reburst.c
360--- a/ircd/m_reburst.c Sun Jan 18 14:18:36 2009 +0100
361+++ b/ircd/m_reburst.c Sun Jan 18 15:26:56 2009 +0100
362@@ -102,6 +102,7 @@
363 #include "ircd_snprintf.h"
364 #include "gline.h"
365 #include "jupe.h"
366+#include "split.h"
367
368 /* #include <assert.h> -- Now using assert in ircd_log.h */
369 #include <stdlib.h>
370@@ -130,6 +131,10 @@
371 case 'J':
372 jupe_burst(sptr);
373 break;
374+ case 's':
375+ case 'S':
376+ split_burst(sptr);
377+ break;
378 default:
379 break;
380 }
381diff -r 2da61ac38fa1 ircd/m_split.c
382--- /dev/null Thu Jan 01 00:00:00 1970 +0000
383+++ b/ircd/m_split.c Sun Jan 18 15:26:56 2009 +0100
384@@ -0,0 +1,374 @@
385+/*
386+ * IRC - Internet Relay Chat, ircd/m_split.c
387+ * Copyright (C) 1990 Jarkko Oikarinen and
388+ * University of Oulu, Computing Center
389+ * Copyright (C) 2000 Kevin L. Mitchell <klmitch@mit.edu>
390+ *
391+ * See file AUTHORS in IRC package for additional names of
392+ * the programmers.
393+ *
394+ * This program is free software; you can redistribute it and/or modify
395+ * it under the terms of the GNU General Public License as published by
396+ * the Free Software Foundation; either version 1, or (at your option)
397+ * any later version.
398+ *
399+ * This program is distributed in the hope that it will be useful,
400+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
401+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
402+ * GNU General Public License for more details.
403+ *
404+ * You should have received a copy of the GNU General Public License
405+ * along with this program; if not, write to the Free Software
406+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
407+ *
408+ * $Id: m_split.c 1737 2006-12-19 05:20:48Z entrope $
409+ */
410+
411+/*
412+ * m_functions execute protocol messages on this server:
413+ *
414+ * cptr is always NON-NULL, pointing to a *LOCAL* client
415+ * structure (with an open socket connected!). This
416+ * identifies the physical socket where the message
417+ * originated (or which caused the m_function to be
418+ * executed--some m_functions may call others...).
419+ *
420+ * sptr is the source of the message, defined by the
421+ * prefix part of the message if present. If not
422+ * or prefix not found, then sptr==cptr.
423+ *
424+ * (!IsServer(cptr)) => (cptr == sptr), because
425+ * prefixes are taken *only* from servers...
426+ *
427+ * (IsServer(cptr))
428+ * (sptr == cptr) => the message didn't
429+ * have the prefix.
430+ *
431+ * (sptr != cptr && IsServer(sptr) means
432+ * the prefix specified servername. (?)
433+ *
434+ * (sptr != cptr && !IsServer(sptr) means
435+ * that message originated from a remote
436+ * user (not local).
437+ *
438+ * combining
439+ *
440+ * (!IsServer(sptr)) means that, sptr can safely
441+ * taken as defining the target structure of the
442+ * message in this server.
443+ *
444+ * *Always* true (if 'parse' and others are working correct):
445+ *
446+ * 1) sptr->from == cptr (note: cptr->from == cptr)
447+ *
448+ * 2) MyConnect(sptr) <=> sptr == cptr (e.g. sptr
449+ * *cannot* be a local connection, unless it's
450+ * actually cptr!). [MyConnect(x) should probably
451+ * be defined as (x == x->from) --msa ]
452+ *
453+ * parc number of variable parameter strings (if zero,
454+ * parv is allowed to be NULL)
455+ *
456+ * parv a NULL terminated list of parameter pointers,
457+ *
458+ * parv[0], sender (prefix string), if not present
459+ * this points to an empty string.
460+ * parv[1]...parv[parc-1]
461+ * pointers to additional parameters
462+ * parv[parc] == NULL, *always*
463+ *
464+ * note: it is guaranteed that parv[0]..parv[parc-1] are all
465+ * non-NULL pointers.
466+ */
467+#include "config.h"
468+
469+#include "client.h"
470+#include "split.h"
471+#include "hash.h"
472+#include "ircd.h"
473+#include "ircd_features.h"
474+#include "ircd_log.h"
475+#include "ircd_reply.h"
476+#include "ircd_string.h"
477+#include "match.h"
478+#include "msg.h"
479+#include "numeric.h"
480+#include "numnicks.h"
481+#include "s_conf.h"
482+#include "s_debug.h"
483+#include "s_misc.h"
484+#include "send.h"
485+
486+/* #include <assert.h> -- Now using assert in ircd_log.h */
487+#include <stdlib.h>
488+#include <string.h>
489+
490+/*
491+ * ms_split - server message handler
492+ *
493+ * parv[0] = Send prefix
494+ * parv[1] = (+|-)<server name>
495+ * parv[2] = Creation time
496+ * parv[3] = Expiration time
497+ * parv[4] = Last modification time
498+ * parv[5] = Life time
499+ * parv[6] = Comment
500+ *
501+ */
502+int ms_split(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
503+{
504+ struct Client *acptr = 0;
505+ struct Split *asplit = 0;
506+ unsigned int flags = 0;
507+ enum SplitAction action = SPLIT_MODIFY;
508+ time_t creation = 0, expire = 0, lastmod = 0, lifetime = 0;
509+ char *server = parv[1], *tmp = 0;
510+ const char *reason = "No reason";
511+
512+ /* TODO: perhaps make some fields optional? reason? */
513+ if (parc < 6)
514+ return need_more_params(sptr, "SPLIT");
515+
516+ if (*server == '!') {
517+ server++;
518+ action = SPLIT_REMOVE; /* removing entry */
519+ }
520+
521+ switch (*server) { /* handle + and - */
522+ case '+': /* activate the split entry */
523+ if (action != SPLIT_REMOVE)
524+ action = SPLIT_ACTIVATE;
525+ server++;
526+ break;
527+
528+ case '-': /* deactivate the entry */
529+ if (action != SPLIT_REMOVE)
530+ action = SPLIT_DEACTIVATE;
531+ server++;
532+ break;
533+ }
534+
535+ /* Next, try to find the split entry... */
536+ asplit = split_find(server);
537+
538+ /* We now have all the pieces to tell us what we've got; let's put
539+ * it all together and convert the rest of the arguments.
540+ */
541+
542+ /* can't modify a split entry that doesn't exist, so remap to activate */
543+ if (!asplit && action == SPLIT_MODIFY)
544+ action = SPLIT_ACTIVATE;
545+
546+ /* OK, let's figure out what other parameters we may have... */
547+ creation = atoi(parv[2]);
548+ expire = atoi(parv[3]);
549+ lastmod = atoi(parv[4]);
550+ lifetime = atoi(parv[5]);
551+ reason = parv[parc - 1];
552+
553+ Debug((DEBUG_DEBUG, "I have a SPLIT I am acting upon now; "
554+ "server %s, action %s, creation %Tu, expire %Tu, "
555+ "lastmod %Tu, lifetime %Tu, reason: %s; split %s!", server,
556+ action == SPLIT_REMOVE ? "!-" :
557+ (action == SPLIT_ACTIVATE ? "+" :
558+ (action == SPLIT_DEACTIVATE ? "-" : "(MODIFY)")),
559+ creation, expire, lastmod, lifetime, reason,
560+ asplit ? "EXISTS" : "does not exist"));
561+
562+ /* OK, at this point, we have converted all available parameters.
563+ * Let's actually do the action!
564+ */
565+ if (asplit) {
566+ if (action == SPLIT_REMOVE)
567+ return split_remove(cptr, sptr, asplit, reason);
568+ return split_modify(cptr, sptr, asplit, action, reason, creation, expire,
569+ lastmod, lifetime, flags);
570+ }
571+
572+ assert(action != SPLIT_MODIFY);
573+
574+ return split_add(cptr, sptr, server, reason, creation, expire, lastmod, lifetime,
575+ flags | ((action == SPLIT_ACTIVATE) ? SPLIT_ACTIVE : 0));
576+}
577+
578+
579+/*
580+ * mo_split - oper message handler
581+ *
582+ * Local listing: 1 or 2 params
583+ * parv[0] = Send prefix
584+ * parv[1] = [Server or mask to match]
585+ *
586+ * Add or modify entry: 3, 4 or 5 params (3 is * then)
587+ * parv[0] = Send prefix
588+ * parv[1] = [+|-]<server name>
589+ * parv[2] = [Expiration offset] (required for new)
590+ * parv[3] = [Comment] (required for new)
591+ *
592+ */
593+int mo_split(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
594+{
595+ struct Client *acptr = 0;
596+ struct Split *asplit = 0;
597+ unsigned int flags = 0;
598+ enum SplitAction action = SPLIT_MODIFY;
599+ time_t expire = 0, lastmod = CurrentTime, creation = CurrentTime;
600+ char *server = parv[1], *end;
601+ const char *reason = NULL;
602+
603+ if (parc < 2) {
604+ if (!HasPriv(sptr, PRIV_ROUTEINFO))
605+ return send_reply(sptr, ERR_NOPRIVILEGES);
606+ else
607+ return split_list(sptr, 0);
608+ }
609+
610+ if (*server == '!') {
611+ server++;
612+ action = SPLIT_REMOVE; /* force removal */
613+ }
614+
615+ switch (*server) { /* handle + and - */
616+ case '+': /* activate the split entry */
617+ if (action != SPLIT_REMOVE)
618+ action = SPLIT_ACTIVATE;
619+ server++;
620+ break;
621+
622+ case '-': /* deactivate the split entry */
623+ if (action != SPLIT_REMOVE)
624+ action = SPLIT_DEACTIVATE;
625+ server++;
626+ break;
627+ }
628+
629+ /* OK, let's figure out the parameters... */
630+ switch (action) {
631+ /* no specific action */
632+ case SPLIT_MODIFY:
633+ /* user wants a listing of a specific SPLIT */
634+ if (parc == 2)
635+ return split_list(sptr, server);
636+ expire = strtol(parv[2], &end, 10) + CurrentTime; /* and the expiration */
637+ if (*end != '\0')
638+ return send_reply(sptr, SND_EXPLICIT | ERR_BADEXPIRE,
639+ "%s :Bad expire time", parv[2]);
640+
641+ flags |= SPLIT_EXPIRE; /* remember that we got an expire time */
642+
643+ if (parc > 3) { /* also got a reason... */
644+ reason = parv[parc - 1];
645+ flags |= SPLIT_REASON;
646+ }
647+ break;
648+
649+ case SPLIT_REMOVE: /* TODO: require reason for this, but not expire? */
650+ if (parc < 4)
651+ return need_more_params(sptr, "SPLIT");
652+ reason = parv[parc - 1];
653+ flags |= SPLIT_REASON;
654+ break;
655+
656+ case SPLIT_ACTIVATE: /* TODO: require expire and reason when new */
657+ if (parc > 2) {
658+ expire = strtol(parv[2], &end, 10) + CurrentTime; /* and the expiration */
659+ if (*end != '\0')
660+ return send_reply(sptr, SND_EXPLICIT | ERR_BADEXPIRE,
661+ "%s :Bad expire time", parv[2]);
662+ flags |= SPLIT_EXPIRE; /* remember that we got an expire time */
663+ }
664+
665+ if (parc > 3) { /* also got a reason... */
666+ reason = parv[parc - 1];
667+ flags |= SPLIT_REASON;
668+ }
669+ break;
670+
671+ case SPLIT_DEACTIVATE: /* TODO: duplicate code? must be a cleaner way */
672+ if (parc > 2) {
673+ expire = strtol(parv[2], &end, 10) + CurrentTime; /* and the expiration */
674+ if (*end != '\0')
675+ return send_reply(sptr, SND_EXPLICIT | ERR_BADEXPIRE,
676+ "%s :Bad expire time", parv[2]);
677+ flags |= SPLIT_EXPIRE; /* remember that we got an expire time */
678+ }
679+
680+ if (parc > 3) { /* also got a reason... */
681+ reason = parv[parc - 1];
682+ flags |= SPLIT_REASON;
683+ }
684+ break;
685+ }
686+
687+
688+ /* check for permissions... */
689+ if (!feature_bool(FEAT_SPLIT)) /* TODO: but allow force removal? */
690+ return send_reply(sptr, ERR_DISABLED, "SPLIT");
691+ else if (!HasPriv(sptr, PRIV_SERVERINFO)) /* TODO: create PRIV_SPLIT - need help there */
692+ return send_reply(sptr, ERR_NOPRIVILEGES);
693+
694+ /* Next, try to find the SPLIT... */
695+ asplit = split_find(server);
696+
697+ /* We now have all the pieces to tell us what we've got; let's put
698+ * it all together and convert the rest of the arguments.
699+ */
700+
701+ /* SPLIT not found and thus we:
702+ * cannot remove SPLIT we do not have
703+ * cannot add new SPLIT without expire and reason
704+ */
705+ if (!asplit &&
706+ ((action == SPLIT_REMOVE) ||
707+ (action == SPLIT_ACTIVATE && !reason) ||
708+ (action == SPLIT_DEACTIVATE && !reason) ||
709+ (action == SPLIT_MODIFY && !reason)))
710+ return send_reply(sptr, ERR_NOSUCHSPLIT, server);
711+
712+ /* can't modify a split entry that doesn't exist, so remap to activate */
713+ if (!asplit && action == SPLIT_MODIFY)
714+ action = SPLIT_ACTIVATE;
715+
716+ Debug((DEBUG_DEBUG, "I have a SPLIT I am acting upon now; "
717+ "server %s, action %s, expire %Tu, "
718+ "reason: %s; split %s! (fields present: %s %s)", server,
719+ action == SPLIT_REMOVE ? "!-" :
720+ (action == SPLIT_ACTIVATE ? "+" :
721+ (action == SPLIT_DEACTIVATE ? "-" : "(MODIFY)")),
722+ expire, reason, asplit ? "EXISTS" : "does not exist",
723+ flags & SPLIT_EXPIRE ? "expire" : "",
724+ flags & SPLIT_REASON ? "reason" : ""));
725+
726+ if (asplit) { /* modifying an existing SPLIT */
727+ if (action == SPLIT_REMOVE)
728+ return split_remove(cptr, sptr, asplit, reason);
729+ return split_modify(cptr, sptr, asplit, action, reason,
730+ asplit->sp_creation, expire, lastmod, asplit->sp_lifetime, flags);
731+ }
732+
733+ assert(action != SPLIT_MODIFY);
734+ assert(action != SPLIT_REMOVE);
735+
736+ /* create a new SPLIT */
737+ return split_add(cptr, sptr, server, reason,
738+ creation, expire, lastmod, expire,
739+ flags | ((action == SPLIT_ACTIVATE) ? SPLIT_ACTIVE : 0));
740+}
741+
742+/*
743+ * m_split - user message handler
744+ *
745+ * parv[0] = Send prefix
746+ *
747+ * From user:
748+ *
749+ * parv[1] = [<server name>]
750+ *
751+ */
752+int m_split(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
753+{
754+ if (parc < 2)
755+ return split_list(sptr, 0);
756+
757+ return split_list(sptr, parv[1]);
758+}
759diff -r 2da61ac38fa1 ircd/parse.c
760--- a/ircd/parse.c Sun Jan 18 14:18:36 2009 +0100
761+++ b/ircd/parse.c Sun Jan 18 15:26:56 2009 +0100
762@@ -505,6 +505,13 @@
763 0, MAXPARA, MFLG_SLOW, 0, NULL,
764 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
765 { m_unregistered, m_not_oper, ms_jupe, mo_jupe, m_ignore }
766+ },
767+ {
768+ MSG_SPLIT,
769+ TOK_SPLIT,
770+ 0, MAXPARA, MFLG_SLOW, 0, NULL,
771+ /* UNREG, CLIENT, SERVER, OPER, SERVICE */
772+ { m_unregistered, m_not_oper, ms_split, mo_split, m_ignore }
773 },
774 {
775 MSG_OPMODE,
776diff -r 2da61ac38fa1 ircd/s_conf.c
777--- a/ircd/s_conf.c Sun Jan 18 14:18:36 2009 +0100
778+++ b/ircd/s_conf.c Sun Jan 18 15:26:56 2009 +0100
779@@ -53,6 +53,7 @@
780 #include "s_debug.h"
781 #include "s_misc.h"
782 #include "send.h"
783+#include "split.h"
784 #include "struct.h"
785 #include "sys.h"
786
787@@ -1005,6 +1006,7 @@
788 }
789
790 attach_conf_uworld(&me);
791+ split_conf();
792
793 return ret;
794 }
795diff -r 2da61ac38fa1 ircd/s_debug.c
796--- a/ircd/s_debug.c Sun Jan 18 14:18:36 2009 +0100
797+++ b/ircd/s_debug.c Sun Jan 18 15:26:57 2009 +0100
798@@ -48,6 +48,7 @@
799 #include "s_user.h"
800 #include "s_stats.h"
801 #include "send.h"
802+#include "split.h"
803 #include "struct.h"
804 #include "sys.h"
805 #include "whowas.h"
806@@ -231,7 +232,8 @@
807 aw = 0, /* aways set */
808 wwa = 0, /* whowas aways */
809 gl = 0, /* glines */
810- ju = 0; /* jupes */
811+ ju = 0, /* jupes */
812+ sp = 0; /* split entries */
813
814 size_t chm = 0, /* memory used by channels */
815 chbm = 0, /* memory used by channel bans */
816@@ -244,6 +246,7 @@
817 wwm = 0, /* whowas array memory used */
818 glm = 0, /* memory used by glines */
819 jum = 0, /* memory used by jupes */
820+ spm = 0, /* memory used by split entries */
821 com = 0, /* memory used by conf lines */
822 dbufs_allocated = 0, /* memory used by dbufs */
823 dbufs_used = 0, /* memory used by dbufs */
824@@ -348,8 +351,9 @@
825
826 gl = gline_memory_count(&glm);
827 ju = jupe_memory_count(&jum);
828+ sp = split_memory_count(&spm);
829 send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG,
830- ":Glines %d(%zu) Jupes %d(%zu)", gl, glm, ju, jum);
831+ ":Glines %d(%zu) Jupes %d(%zu) Splits %d(%zu)", gl, glm, ju, jum, sp, spm);
832
833 send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG,
834 ":Hash: client %d(%zu), chan is the same", HASHSIZE,
835diff -r 2da61ac38fa1 ircd/s_err.c
836--- a/ircd/s_err.c Sun Jan 18 14:18:36 2009 +0100
837+++ b/ircd/s_err.c Sun Jan 18 15:26:57 2009 +0100
838@@ -490,7 +490,7 @@
839 /* 228 */
840 { RPL_STATSQLINE, "Q %s :%s", "228" },
841 /* 229 */
842- { 0 },
843+ { RPL_STATSSPLIT, "S %s %d %d %d %d %c :%s", "229" },
844 /* 230 */
845 { 0 },
846 /* 231 */
847@@ -588,9 +588,9 @@
848 /* 277 */
849 { 0 },
850 /* 278 */
851- { 0 },
852+ { RPL_SPLITLIST, "%s %Tu %Tu %Tu %Tu %c :%s", "278" },
853 /* 279 */
854- { 0 },
855+ { RPL_ENDOFSPLITLIST, ":End of Split List", "279" },
856 /* 280 */
857 { RPL_GLIST, "%s%s%s%s%s %Tu %Tu %Tu %s %s%c :%s", "280" },
858 /* 281 */
859@@ -1052,7 +1052,7 @@
860 /* 509 */
861 { 0 },
862 /* 510 */
863- { 0 },
864+ { ERR_NOSUCHSPLIT, "%s :No such split", "510" },
865 /* 511 */
866 { ERR_SILELISTFULL, "%s :Your silence list is full", "511" },
867 /* 512 */
868diff -r 2da61ac38fa1 ircd/s_misc.c
869--- a/ircd/s_misc.c Sun Jan 18 14:18:36 2009 +0100
870+++ b/ircd/s_misc.c Sun Jan 18 15:26:57 2009 +0100
871@@ -53,6 +53,7 @@
872 #include "s_stats.h"
873 #include "s_user.h"
874 #include "send.h"
875+#include "split.h"
876 #include "struct.h"
877 #include "sys.h"
878 #include "uping.h"
879@@ -391,6 +392,7 @@
880 time_t on_for;
881
882 char comment1[HOSTLEN + HOSTLEN + 2];
883+ char splitreason[BUFSIZE];
884 assert(killer);
885 if (MyConnect(victim))
886 {
887@@ -497,6 +499,14 @@
888 sendto_opmask_butone(0, SNO_NETWORK, "Net break: %C %C (%s)",
889 cli_serv(victim)->up, victim, comment);
890 dump_map(victim, "*", 0, report_lost_links, 0);
891+ if (feature_bool(FEAT_SPLIT)) {
892+ ircd_snprintf(0, splitreason, sizeof(splitreason),
893+ "Net break: %C %C (%s%s%s%s)", cli_serv(victim)->up, victim,
894+ IsUser(killer) ? "SQUIT by " : "", IsUser(killer) ? cli_name(killer) : "",
895+ IsUser(killer) ? ": " : "", comment);
896+ sendto_opmask_butone(0, SNO_NETWORK, "Created %d SPLIT entries.",
897+ split_break(victim, splitreason));
898+ }
899 }
900
901 /*
902diff -r 2da61ac38fa1 ircd/s_serv.c
903--- a/ircd/s_serv.c Sun Jan 18 14:18:36 2009 +0100
904+++ b/ircd/s_serv.c Sun Jan 18 15:26:57 2009 +0100
905@@ -54,6 +54,7 @@
906 #include "s_misc.h"
907 #include "s_user.h"
908 #include "send.h"
909+#include "split.h"
910 #include "struct.h"
911 #include "sys.h"
912 #include "userload.h"
913@@ -196,6 +197,7 @@
914 */
915 gline_burst(cptr);
916 jupe_burst(cptr);
917+ split_burst(cptr);
918
919 /*
920 * Pass on my client information to the new server
921diff -r 2da61ac38fa1 ircd/s_stats.c
922--- a/ircd/s_stats.c Sun Jan 18 14:18:36 2009 +0100
923+++ b/ircd/s_stats.c Sun Jan 18 15:26:57 2009 +0100
924@@ -52,6 +52,7 @@
925 #include "s_stats.h"
926 #include "s_user.h"
927 #include "send.h"
928+#include "split.h"
929 #include "struct.h"
930 #include "userload.h"
931
932@@ -633,7 +634,10 @@
933 send_usage, 0,
934 "System resource usage (Debug only)." },
935 #endif
936- { 's', "spoofhosts", (STAT_FLAG_OPERFEAT | STAT_FLAG_VARPARAM), FEAT_HIS_STATS_s,
937+ { 'S', "splits", (STAT_FLAG_OPERFEAT | STAT_FLAG_VARPARAM | STAT_FLAG_CASESENS), FEAT_HIS_STATS_S,
938+ split_stats, 0,
939+ "Server SPLITs information."},
940+ { 's', "spoofhosts", (STAT_FLAG_OPERFEAT | STAT_FLAG_VARPARAM | STAT_FLAG_CASESENS), FEAT_HIS_STATS_s,
941 stats_sline, 0,
942 "Spoofed hosts information." },
943 { 'T', "motds", (STAT_FLAG_OPERFEAT | STAT_FLAG_CASESENS), FEAT_HIS_STATS_T,
944diff -r 2da61ac38fa1 ircd/split.c
945--- /dev/null Thu Jan 01 00:00:00 1970 +0000
946+++ b/ircd/split.c Sun Jan 18 15:26:57 2009 +0100
947@@ -0,0 +1,746 @@
948+/*
949+ * IRC - Internet Relay Chat, ircd/split.c
950+ * Copyright (C) 1990 Jarkko Oikarinen and
951+ * University of Oulu, Finland
952+ * Copyright (C) 2000 Kevin L. Mitchell <klmitch@mit.edu>
953+ *
954+ * This program is free software; you can redistribute it and/or modify
955+ * it under the terms of the GNU General Public License as published by
956+ * the Free Software Foundation; either version 1, or (at your option)
957+ * any later version.
958+ *
959+ * This program is distributed in the hope that it will be useful,
960+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
961+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
962+ * GNU General Public License for more details.
963+ *
964+ * You should have received a copy of the GNU General Public License
965+ * along with this program; if not, write to the Free Software
966+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
967+ */
968+/** @file
969+ * @brief Implementation of split server handling functions.
970+ * @version $Id: split.c 1633 2006-03-25 03:46:56Z entrope $
971+ */
972+#include "config.h"
973+
974+#include "split.h"
975+#include "client.h"
976+#include "hash.h"
977+#include "ircd.h"
978+#include "ircd_alloc.h"
979+#include "ircd_features.h"
980+#include "ircd_log.h"
981+#include "ircd_reply.h"
982+#include "ircd_string.h"
983+#include "list.h"
984+#include "match.h"
985+#include "msg.h"
986+#include "numeric.h"
987+#include "numnicks.h"
988+#include "s_conf.h"
989+#include "s_bsd.h"
990+#include "s_debug.h"
991+#include "s_misc.h"
992+#include "send.h"
993+#include "struct.h"
994+#include "sys.h" /* FALSE bleah */
995+
996+/* #include <assert.h> -- Now using assert in ircd_log.h */
997+#include <string.h>
998+
999+/** List of split entries. */
1000+static struct Split *GlobalSplitList = 0;
1001+
1002+/** Allocate a new Split entry with the given parameters.
1003+ * @param[in] server Server name for split entry.
1004+ * @param[in] reason Reason for the split entry.
1005+ * @param[in] creation Creation time for split entry.
1006+ * @param[in] expire Expiration time for split entry.
1007+ * @param[in] lastmod Last modification time for split entry.
1008+ * @param[in] lifetime Life time for split entry.
1009+ * @param[in] flags Flags to set for the split entry.
1010+ */
1011+static struct Split *
1012+split_make(char *server, const char *reason,
1013+ time_t creation, time_t expire, time_t lastmod, time_t lifetime,
1014+ unsigned int flags)
1015+{
1016+ struct Split *asplit;
1017+
1018+ asplit = (struct Split*) MyMalloc(sizeof(struct Split)); /* alloc memory */
1019+ assert(0 != asplit);
1020+
1021+ memset(asplit, 0, sizeof(*asplit));
1022+ /* TODO: some limit for servername length? HOSTLEN? */
1023+ DupString(asplit->sp_server, server); /* copy vital information */
1024+ /* TODO: some limit for reason length? QUITLEN TOPICLEN? */
1025+ DupString(asplit->sp_reason, reason);
1026+ /* TODO: what we use creation for and do we need it? */
1027+ asplit->sp_creation = creation;
1028+ asplit->sp_expire = expire;
1029+ /* TODO: are we using nettime etc.? CurrentTime is used. */
1030+ asplit->sp_lastmod = lastmod;
1031+ asplit->sp_lifetime = lifetime;
1032+ /* CHECK: does this make it active upon creation regardless of the flags given? */
1033+ asplit->sp_flags = flags & SPLIT_ACTIVE; /* set split flags */
1034+
1035+ asplit->sp_next = GlobalSplitList; /* link it into the list */
1036+ asplit->sp_prev_p = &GlobalSplitList;
1037+ if (GlobalSplitList)
1038+ GlobalSplitList->sp_prev_p = &asplit->sp_next;
1039+ GlobalSplitList = asplit;
1040+
1041+ return asplit;
1042+}
1043+
1044+/** Forward a split entry to another server.
1045+ * @param[in] cptr Local client that sent us the split entry.
1046+ * @param[in] sptr Originator of the split entry.
1047+ * @param[in] split Split entry to forward.
1048+ * @param[in] reason Reason to send upstream (used by split_remove)
1049+ */
1050+static void
1051+split_propagate(struct Client *cptr, struct Client *sptr,
1052+ struct Split *split, const char *reason)
1053+{
1054+ sendcmdto_serv_butone(sptr, CMD_SPLIT, cptr, "%s%c%s %Tu %Tu %Tu %Tu :%s",
1055+ SplitIsRemoving(split) ? "!" : "",
1056+ SplitIsActive(split) && !SplitIsRemoving(split) ? '+' : '-', /* always !- not !+ */
1057+ split->sp_server, split->sp_creation, split->sp_expire,
1058+ split->sp_lastmod, split->sp_lifetime,
1059+ reason != NULL ? reason : split->sp_reason);
1060+}
1061+
1062+/** Add a new server split entry.
1063+ * @param[in] cptr Local client that sent us the split entry.
1064+ * @param[in] sptr Originator of the split entry.
1065+ * @param[in] server Server name to split entry.
1066+ * @param[in] reason Reason for the split entry.
1067+ * @param[in] expire Expiration timestamp.
1068+ * @param[in] lastmod Last modification timestamp.
1069+ * @param[in] flags Flags to set on the split entry.
1070+ * @return Zero
1071+ */
1072+int
1073+split_add(struct Client *cptr, struct Client *sptr, char *server, const char *reason,
1074+ time_t creation, time_t expire, time_t lastmod, time_t lifetime,
1075+ unsigned int flags)
1076+{
1077+
1078+ /* TODO: check for proper masks - at least one dot and no wildcards? */
1079+ struct Split *asplit;
1080+ struct Client *acptr;
1081+
1082+ assert(0 != server);
1083+ assert(0 != reason);
1084+ assert(NULL != cptr);
1085+ assert(NULL != sptr);
1086+
1087+ Debug((DEBUG_DEBUG, "split_add(\"%s\", \"%s\", \"%s\", \"%s\", %Tu, %Tu, %Tu, "
1088+ "%Tu, 0x%04x)", cli_name(cptr), cli_name(sptr), server, reason,
1089+ creation, expire, lastmod, lifetime, flags));
1090+
1091+ /* not adding SPLIT for server that is linked
1092+ * if sptr is my user throw error
1093+ * otherwise ignore - SERVER and SPLIT messages can cross,
1094+ * or a server is bursting and it will see our end and destroy the SPLITs
1095+ */
1096+ if ((acptr = FindServer(server))) {
1097+ if (MyUser(sptr))
1098+ sendcmdto_one(&me, CMD_NOTICE, sptr,
1099+ "%C :Cannot add SPLIT %s - server is linked.", sptr, server);
1100+ return 0;
1101+ }
1102+
1103+ /*
1104+ * You cannot set a negative (or zero) duration, nor can you set an
1105+ * duration greater than SPLIT_MAX_EXPIRE.
1106+ */
1107+ if (expire - CurrentTime <= 0 || expire - CurrentTime > SPLIT_MAX_EXPIRE) {
1108+ if (!IsServer(cptr) && MyConnect(cptr))
1109+ return send_reply(cptr, ERR_BADEXPIRE, expire - CurrentTime);
1110+ if (expire <= CurrentTime) /* no point going further */
1111+ /* TODO: check lifetime then ? because the SPLIT may simply be deactivated
1112+ * and we did not see it before
1113+ * if it were to be activated again we would get it again
1114+ * but should we not keep the same state on each server?
1115+ */
1116+ /* CHECK: sptr may have the wrong idea about the nettime?
1117+ * or we could be wrong?
1118+ * SETTIME ? could be dangerous and mess up things..
1119+ * perhaps raise some sort of warning to ops
1120+ * maybe if the difference is larger than a poor RTT over the network?
1121+ * 60 seconds?
1122+ * no no no! - see above
1123+ */
1124+ return 0;
1125+ }
1126+
1127+ /* inform ops and log it */
1128+ sendto_opmask_butone(0, SNO_NETWORK, "%s adding%sSPLIT for %s, expiring at %Tu: %s",
1129+ (feature_bool(FEAT_HIS_SNOTICES) || IsServer(sptr)) ?
1130+ cli_name(sptr) : cli_name((cli_user(sptr))->server),
1131+ !(flags & SPLIT_ACTIVE) ? " deactivated " : " ",
1132+ server, expire, reason);
1133+
1134+ /* TODO: add SPLIT log stuff or use JUPE? */
1135+ log_write(LS_JUPE, L_INFO, LOG_NOSNOTICE,
1136+ "%#C adding%sSPLIT for %s, expiring at %Tu: %s",
1137+ sptr, !(flags & SPLIT_ACTIVE) ? " deactivated " : " ",
1138+ server, expire, reason);
1139+
1140+ /* make the split entry */
1141+ asplit = split_make(server, reason, creation, expire, lastmod, lifetime, flags);
1142+
1143+ assert(asplit);
1144+
1145+ /* and propagate it */
1146+ split_propagate(cptr, sptr, asplit, NULL);
1147+
1148+ return 0;
1149+}
1150+
1151+
1152+/** Modify a split entry.
1153+ * @param[in] cptr Client that sent us the split modification.
1154+ * @param[in] sptr Client that originated the split modification.
1155+ * @param[in] split Split entry being modified.
1156+ * @param[in] action Resultant status of the G-line.
1157+ * @param[in] reason Reason.
1158+ * @param[in] expire Expiration time.
1159+ * @param[in] lastmod Last modification time.
1160+ * @param[in] lifetime Lifetime.
1161+ * @param[in] flags Bitwise combination of SPLIT_* flags.
1162+ * @return Zero.
1163+ */
1164+int
1165+split_modify(struct Client *cptr, struct Client *sptr, struct Split *split,
1166+ enum SplitAction action, const char *reason,
1167+ time_t creation, time_t expire, time_t lastmod, time_t lifetime,
1168+ unsigned int flags)
1169+{
1170+ struct Client* acptr;
1171+ char buf[BUFSIZE];
1172+ int pos = 0;
1173+
1174+ assert(split);
1175+ assert(NULL != cptr);
1176+ assert(NULL != sptr);
1177+
1178+ /* TODO: add action in the debug notice too */
1179+ Debug((DEBUG_DEBUG, "split_modify(\"%s\", \"%s\", \"%s\", \"%s\", %Tu, %Tu, %Tu, "
1180+ "%Tu, 0x%04x)", cli_name(cptr), cli_name(sptr), split->sp_server, reason,
1181+ creation, expire, lastmod, lifetime, flags));
1182+
1183+ /* not modifying SPLIT for server that is linked
1184+ * if sptr is my user throw error
1185+ * otherwise ignore - SERVER and SPLIT messages can cross.
1186+ *
1187+ * note: we cleanup SPLIT entries at end of burst,
1188+ * and not when a server is introduced.
1189+ * so between net junction and end of burst,
1190+ * we can get SPLITs for a linked server.
1191+ */
1192+ /* TODO: should we free it here or let that be done in end of burst?
1193+ * only when that is the ONLY situation we can get to this point!
1194+ * but if we free here and it is a burst, the count is incorrect in
1195+ * split_netmerge
1196+ * IsBurst() is that true for only that single server or for all its downlinks?
1197+ * I would guess single server only.. argh!
1198+ */
1199+ if ((acptr = FindServer(split->sp_server))) {
1200+ if (MyUser(sptr))
1201+ sendcmdto_one(&me, CMD_NOTICE, sptr,
1202+ "%C :Cannot modify SPLIT %s - server is linked.", sptr, split->sp_server);
1203+ split_free(split); /* and free it */
1204+ return 0;
1205+ }
1206+
1207+ /* First, let's check lastmod... */
1208+ if (SplitLastMod(split) > lastmod) { /* we have a more recent version */
1209+ if (IsBurstOrBurstAck(cptr))
1210+ return 0; /* middle of a burst, it'll resync on its own */
1211+ return split_resend(cptr, split); /* resync the server */
1212+ } else if (SplitLastMod(split) == lastmod)
1213+ return 0; /* we have that version of the split entry... */
1214+
1215+ /* All right, we know that there's a change of some sort. What is it? */
1216+ /* first, check out the expiration time... */
1217+ /* TODO: expire != 0 or NULL - check that we have something? */
1218+ if (expire != 0 && expire != split->sp_expire) {
1219+ if (expire - CurrentTime <= 0 || expire - CurrentTime > SPLIT_MAX_EXPIRE) {
1220+ if (!IsServer(sptr) && MyConnect(sptr)) /* bad expiration time */
1221+ send_reply(sptr, ERR_BADEXPIRE, expire - CurrentTime);
1222+ if (expire - CurrentTime <= 0) /* no point in going further */
1223+ /* TODO: same as in split_add - check lifetime? */
1224+ return 0;
1225+ }
1226+ flags |= SPLIT_EXPIRE;
1227+ } else
1228+ flags &= ~SPLIT_EXPIRE;
1229+
1230+ /* Next, check out lifetime */
1231+ if (!(flags & SPLIT_LIFETIME) || !lifetime)
1232+ lifetime = split->sp_lifetime; /* use Split lifetime */
1233+
1234+ lifetime = IRCD_MAX(lifetime, expire); /* set lifetime to the max */
1235+
1236+ /* OK, let's see which is greater... */
1237+ if (lifetime > split->sp_lifetime)
1238+ flags |= SPLIT_LIFETIME; /* have to update lifetime */
1239+ else {
1240+ flags &= ~SPLIT_LIFETIME; /* no change to lifetime */
1241+ lifetime = 0;
1242+ }
1243+
1244+ /* Finally, let's see if the reason needs to be updated */
1245+ /* TODO: (reason) or use != NULL / 0 ? */
1246+ if ((flags & SPLIT_REASON) && reason &&
1247+ ircd_strcmp(split->sp_reason, reason) != 0)
1248+ flags &= ~SPLIT_REASON; /* no changes to the reason */
1249+
1250+ /* OK, now let's take a look at the action... */
1251+ if ((action == SPLIT_ACTIVATE && SplitIsActive(split)) ||
1252+ (action == SPLIT_DEACTIVATE && !SplitIsActive(split)) ||
1253+ /* can't activate an expired split entry */
1254+ (IRCD_MAX(split->sp_expire, expire) <= CurrentTime))
1255+ action = SPLIT_MODIFY; /* no activity state modifications */
1256+
1257+ Debug((DEBUG_DEBUG, "About to perform changes; flags 0x%04x, action %s",
1258+ flags, action == SPLIT_ACTIVATE ? "SPLIT_ACTIVATE" :
1259+ (action == SPLIT_DEACTIVATE ? "SPLIT_DEACTIVATE" :
1260+ (action == SPLIT_MODIFY ? "SPLIT_MODIFY" : "<UNKNOWN>"))));
1261+
1262+ /* If there are no changes to perform, do no changes */
1263+ if (!(flags & SPLIT_UPDATE) && action == SPLIT_MODIFY)
1264+ return 0;
1265+
1266+ /* Start by updating lastmod, if indicated... */
1267+ split->sp_lastmod = lastmod;
1268+
1269+ /* Then move on to activity status changes... */
1270+ switch (action) {
1271+ case SPLIT_ACTIVATE: /* activating split entry */
1272+ split->sp_flags |= SPLIT_ACTIVE; /* make it active... */
1273+ pos += ircd_snprintf(0, buf, sizeof(buf), " activating SPLIT");
1274+ break;
1275+
1276+ case SPLIT_DEACTIVATE: /* deactivating split entry */
1277+ split->sp_flags &= ~SPLIT_ACTIVE; /* make it inactive... */
1278+ pos += ircd_snprintf(0, buf, sizeof(buf), " deactivating SPLIT");
1279+ break;
1280+
1281+ case SPLIT_MODIFY: /* no change to activity status */
1282+ break;
1283+ }
1284+
1285+ /* Handle expiration changes... */
1286+ if (flags & SPLIT_EXPIRE) {
1287+ split->sp_expire = expire; /* save new expiration time */
1288+ if (pos < BUFSIZE)
1289+ pos += ircd_snprintf(0, buf + pos, sizeof(buf) - pos,
1290+ "%s%s changing expiration time to %Tu",
1291+ pos ? ";" : "",
1292+ pos && !(flags & (SPLIT_LIFETIME | SPLIT_REASON)) ?
1293+ " and" : "", expire);
1294+ }
1295+
1296+ /* Next, handle lifetime changes... */
1297+ if (flags & SPLIT_LIFETIME) {
1298+ split->sp_lifetime = lifetime; /* save new lifetime */
1299+ if (pos < BUFSIZE)
1300+ pos += ircd_snprintf(0, buf + pos, sizeof(buf) - pos,
1301+ "%s%s extending record lifetime to %Tu",
1302+ pos ? ";" : "", pos && !(flags & SPLIT_REASON) ?
1303+ " and" : "", lifetime);
1304+ }
1305+
1306+ /* Now, handle reason changes... */
1307+ if (flags & SPLIT_REASON) {
1308+ MyFree(split->sp_reason); /* release old reason */
1309+ DupString(split->sp_reason, reason); /* store new reason */
1310+ if (pos < BUFSIZE)
1311+ pos += ircd_snprintf(0, buf + pos, sizeof(buf) - pos,
1312+ "%s%s changing reason to \"%s\"",
1313+ pos ? ";" : "", pos ? " and" : "", reason);
1314+ }
1315+
1316+ /* All right, inform ops... */
1317+ sendto_opmask_butone(0, SNO_NETWORK, "%s modifying SPLIT for %s:%s",
1318+ (feature_bool(FEAT_HIS_SNOTICES) || IsServer(sptr)) ?
1319+ cli_name(sptr) : cli_name((cli_user(sptr))->server),
1320+ split->sp_server, buf);
1321+
1322+ /* TODO: add SPLIT log stuff or use JUPE? */
1323+ /* and log the change */
1324+ log_write(LS_JUPE, L_INFO, LOG_NOSNOTICE,
1325+ "%#C modifying SPLIT for %s:%s",
1326+ sptr, split->sp_server, buf);
1327+
1328+ /* and propagate it */
1329+ split_propagate(cptr, sptr, split, NULL);
1330+
1331+ return 0;
1332+}
1333+
1334+
1335+/** Remove a split entry.
1336+ * @param[in] cptr Local client that sent us the split entry.
1337+ * @param[in] sptr Originator of the split entry.
1338+ * @param[in] split Split entry to remove.
1339+ * @param[in] reason Reason for removing this split entry.
1340+ * @return Zero.
1341+ */
1342+int
1343+split_remove(struct Client *cptr, struct Client *sptr, struct Split *split, const char *reason)
1344+{
1345+ unsigned int saveflags = 0;
1346+
1347+ assert(0 != split);
1348+ assert(NULL != cptr);
1349+ assert(NULL != sptr);
1350+
1351+ Debug((DEBUG_DEBUG, "split_remove(\"%s\", \"%s\", \"%s\", \"%s\")",
1352+ cli_name(cptr), cli_name(sptr), split->sp_server, reason));
1353+
1354+ /* deactivate entry and mark it for removal (used in split_propagate) */
1355+ split->sp_flags |= SPLIT_REMOVING;
1356+ /* CHECK: turn bit SPLIT_ACTIVE off? */
1357+ split->sp_flags |= SPLIT_ACTIVE;
1358+
1359+ /* inform ops and log it */
1360+ sendto_opmask_butone(0, SNO_NETWORK, "%s removing SPLIT for %s, expiring at %Tu: %s (%s)",
1361+ (feature_bool(FEAT_HIS_SNOTICES) || IsServer(sptr)) ?
1362+ cli_name(sptr) : cli_name((cli_user(sptr))->server),
1363+ split->sp_server, split->sp_expire, split->sp_reason, reason);
1364+
1365+ /* TODO: add SPLIT log stuff or use JUPE? */
1366+ log_write(LS_JUPE, L_INFO, LOG_NOSNOTICE,
1367+ "%#C removing SPLIT for %s, expiring at %Tu: %s (%s)",
1368+ sptr, split->sp_server, split->sp_expire, split->sp_reason, reason);
1369+
1370+ /* TODO: the reason supplied for removing this SPLIT does not go upstream
1371+ * either propagate manually here, or update the record or?
1372+ */
1373+ /* propagate it */
1374+ split_propagate(cptr, sptr, split, reason);
1375+
1376+ /* destroy it */
1377+ split_free(split);
1378+
1379+ return 0;
1380+}
1381+
1382+
1383+/** Find a split entry by name.
1384+ * @param[in] server Split entry name to search for.
1385+ * @return Matching split entry (or NULL if none match).
1386+ */
1387+struct Split *
1388+split_find(char *server)
1389+{
1390+ struct Split* split;
1391+ struct Split* ssplit;
1392+
1393+ for (split = GlobalSplitList; split; split = ssplit) { /* go through splits */
1394+ ssplit = split->sp_next;
1395+
1396+ if (split_expire(split)) /* expire any that need expiring */
1397+ split_free(split);
1398+ else if (0 == ircd_strcmp(server, split->sp_server)) /* found it yet? */
1399+ return split;
1400+ }
1401+ /* TODO: we return 0 not NULL? */
1402+ return 0;
1403+}
1404+
1405+/** Unlink and free an unused split entry.
1406+ * @param[in] split Server split entry to free.
1407+ */
1408+void
1409+split_free(struct Split* split)
1410+{
1411+ /* TODO: use 0 or NULL ? */
1412+ assert(0 != split);
1413+
1414+ *split->sp_prev_p = split->sp_next; /* squeeze this split entry out */
1415+ if (split->sp_next)
1416+ split->sp_next->sp_prev_p = split->sp_prev_p;
1417+
1418+ /* CHECK: the other fields in this struct are destroyed with MyFree() call? */
1419+ MyFree(split->sp_server); /* and free up the memory */
1420+ MyFree(split->sp_reason);
1421+ MyFree(split);
1422+}
1423+
1424+
1425+/** Check whether a split entry has past its life time.
1426+ * when entry is active and past expire time, but not life time, deactivate it
1427+ * @param[in] split Server split entry to check.
1428+ * @return 1 when entry can be free'd, 0 otherwise.
1429+ */
1430+int
1431+split_expire(struct Split* split)
1432+{
1433+ assert(0 != split);
1434+
1435+ /* past lifetime */
1436+ if (split->sp_lifetime <= CurrentTime)
1437+ return 1;
1438+
1439+ /* past expire time, deactivate entry if it is active */
1440+ if ((split->sp_expire <= CurrentTime) && SplitIsActive(split))
1441+ /* CHECK: turn bit SPLIT_ACTIVE off */
1442+ split->sp_flags &= SPLIT_ACTIVE;
1443+
1444+ return 0;
1445+}
1446+
1447+/** Send the full list of split entries to \a cptr.
1448+ * @param[in] cptr Local server to send split entries to.
1449+ */
1450+void
1451+split_burst(struct Client *cptr)
1452+{
1453+ struct Split *split;
1454+ struct Split *ssplit;
1455+
1456+ assert(NULL != cptr);
1457+
1458+ for (split = GlobalSplitList; split; split = ssplit) { /* go through splits */
1459+ ssplit = split->sp_next;
1460+
1461+ /* expire any that need expiring */
1462+ if (split_expire(split))
1463+ split_free(split);
1464+ /* if we have an entry for cptr, dont send it - but do not free here yet
1465+ * free it at end of burst, to get the correct count for SPLITs removed.
1466+ */
1467+ else if (ircd_strcmp(split->sp_server, cli_name(cptr)) == 0)
1468+ continue;
1469+ else
1470+ sendcmdto_one(&me, CMD_SPLIT, cptr, "%c%s %Tu %Tu %Tu %Tu :%s",
1471+ SplitIsActive(split) ? '+' : '-', split->sp_server,
1472+ split->sp_creation, split->sp_expire,
1473+ split->sp_lastmod, split->sp_lifetime,
1474+ split->sp_reason);
1475+ }
1476+}
1477+
1478+/** Forward a split to another server.
1479+ * @param[in] cptr Server to send split entries to.
1480+ * @param[in] split Split to forward.
1481+ * @return Zero.
1482+ */
1483+int
1484+split_resend(struct Client *cptr, struct Split *split)
1485+{
1486+ sendcmdto_one(&me, CMD_SPLIT, cptr, "%c%s %Tu %Tu %Tu %Tu :%s",
1487+ SplitIsActive(split) ? '+' : '-', split->sp_server,
1488+ split->sp_creation, split->sp_expire,
1489+ split->sp_lastmod, split->sp_lifetime,
1490+ split->sp_reason);
1491+
1492+ return 0;
1493+}
1494+
1495+/** Send a split entry (or a list of split entries) to a server.
1496+ * @param[in] sptr Client searching for split entries.
1497+ * @param[in] server Name of split entry to search for (if NULL, list all).
1498+ * @return Zero.
1499+ */
1500+int
1501+split_list(struct Client *sptr, char *server)
1502+{
1503+ struct Split *split;
1504+ struct Split *ssplit;
1505+
1506+ assert(NULL != sptr);
1507+
1508+ /* TODO: wildcard matching? */
1509+ if (server) {
1510+ if (!(split = split_find(server))) /* no such split */
1511+ return send_reply(sptr, ERR_NOSUCHSPLIT, server);
1512+
1513+ /* send split information along */
1514+ send_reply(sptr, RPL_SPLITLIST, split->sp_server,
1515+ split->sp_creation, split->sp_expire,
1516+ split->sp_lastmod, split->sp_lifetime,
1517+ SplitIsActive(split) ? '+' : '-', split->sp_reason);
1518+ } else {
1519+ for (split = GlobalSplitList; split; split = ssplit) { /* go through splits */
1520+ ssplit = split->sp_next;
1521+
1522+ if (split_expire(split)) /* expire any that need expiring */
1523+ split_free(split);
1524+ else /* send split information along */
1525+ send_reply(sptr, RPL_SPLITLIST, split->sp_server,
1526+ split->sp_creation, split->sp_expire,
1527+ split->sp_lastmod, split->sp_lifetime,
1528+ SplitIsActive(split) ? '+' : '-', split->sp_reason);
1529+ }
1530+ }
1531+
1532+ /* end of splitlist information */
1533+ return send_reply(sptr, RPL_ENDOFSPLITLIST);
1534+}
1535+
1536+
1537+/** Auto destroy SPLITs for servers gained in a netmerge
1538+ * @param[in] cptr Server that link to the network
1539+ * @return Number of destroyed SPLITs
1540+ */
1541+int
1542+split_merge(struct Client *server)
1543+{
1544+ struct DLink *lp;
1545+ struct Split *split;
1546+ int count = 0;
1547+
1548+ assert(NULL != server);
1549+
1550+ Debug((DEBUG_DEBUG, "split_merge(\"%s\")", cli_name(server)));
1551+
1552+ /* find the SPLIT for this server */
1553+ if ((split = split_find(cli_name(server)))) {
1554+ split_free(split);
1555+ count++;
1556+ }
1557+
1558+ /* go over its downlinks */
1559+ for (lp = cli_serv(server)->down; lp; lp = lp->next)
1560+ count += split_merge(lp->value.cptr);
1561+ return count;
1562+}
1563+
1564+
1565+/** Auto create SPLITs for servers lost in a netbreak
1566+ * @param[in] server Server that lost link to network
1567+ * @param[in] reason Reason to add to SPLITs
1568+ * @return Number of created SPLITs
1569+ */
1570+int
1571+split_break(struct Client *server, const char *reason)
1572+{
1573+ struct DLink *lp;
1574+ struct Split *split;
1575+ int count = 0;
1576+ time_t creation = CurrentTime, expire = CurrentTime + SPLIT_AUTO_EXPIRE,
1577+ lastmod = CurrentTime, lifetime = expire;
1578+ unsigned int flags = SPLIT_ACTIVE;
1579+
1580+ assert(NULL != server);
1581+
1582+ Debug((DEBUG_DEBUG, "split_break(\"%s\", \"%s\")", cli_name(server), reason));
1583+
1584+ /* find the SPLIT for this server */
1585+ if (!(split = split_find(cli_name(server)))) {
1586+ split_make(cli_name(server), reason, creation, expire, lastmod, lifetime, flags);
1587+ count++;
1588+ }
1589+
1590+ /* go over its downlinks */
1591+ for (lp = cli_serv(server)->down; lp; lp = lp->next)
1592+ count += split_break(lp->value.cptr, reason);
1593+ return count;
1594+}
1595+
1596+
1597+/** Auto create SPLITs for servers we have a Connect Block for
1598+ *
1599+ */
1600+void
1601+split_conf()
1602+{
1603+ struct ConfItem *conf;
1604+ struct Client *server;
1605+ struct Split *split;
1606+ struct Split *asplit;
1607+ time_t creation = CurrentTime, expire = CurrentTime + SPLIT_AUTO_EXPIRE,
1608+ lastmod = CurrentTime, lifetime = expire;
1609+ unsigned int flags = SPLIT_ACTIVE;
1610+ char reason[BUFSIZE];
1611+
1612+ Debug((DEBUG_DEBUG, "split_conf()"));
1613+
1614+ /* we are not set to generate SPLITs */
1615+ if (!feature_bool(FEAT_SPLIT))
1616+ return;
1617+
1618+ ircd_snprintf(0, reason, sizeof(reason),
1619+ "Generated upon loading conf file on %s", cli_name(&me));
1620+
1621+ /* go over the conf contents */
1622+ for (conf = GlobalConfList; conf; conf = conf->next) {
1623+ /* not a Connect Block */
1624+ if (CONF_SERVER != conf->status)
1625+ continue;
1626+ /* server is linked */
1627+ if (server = FindServer(conf->name))
1628+ continue;
1629+ /* we have a SPLIT for this server already */
1630+ if (split = split_find(conf->name))
1631+ continue;
1632+ /* inform ops and log it */
1633+ sendto_opmask_butone(0, SNO_NETWORK, "%C adding SPLIT for %s, expiring at %Tu: %s",
1634+ &me, conf->name, expire, reason);
1635+
1636+ /* TODO: add SPLIT log stuff or use JUPE? */
1637+ log_write(LS_JUPE, L_INFO, LOG_NOSNOTICE,
1638+ "%C adding SPLIT for %s, expiring at %Tu: %s",
1639+ &me, conf->name, expire, reason);
1640+
1641+ /* make the split entry */
1642+ asplit = split_make(conf->name, reason, creation, expire, lastmod, lifetime, flags);
1643+
1644+ assert(asplit);
1645+
1646+ /* and propagate it */
1647+ split_propagate(&me, &me, asplit, NULL);
1648+ }
1649+}
1650+
1651+
1652+/** Statistics callback to list SPLITs.
1653+ * @param[in] sptr Client requesting statistics.
1654+ * @param[in] sd Stats descriptor for request (ignored).
1655+ * @param[in] param Extra parameter from user (ignored).
1656+ */
1657+void
1658+split_stats(struct Client *sptr, const struct StatDesc *sd,
1659+ char *param)
1660+{
1661+ struct Split *split;
1662+ struct Split *ssplit;
1663+
1664+ send_reply(sptr, SND_EXPLICIT | RPL_STATSSPLIT,
1665+ "S servername creation expire lastmod lifetime active :reason");
1666+ for (split = GlobalSplitList; split; split = split->sp_next)
1667+ send_reply(sptr, RPL_STATSSPLIT, split->sp_server,
1668+ split->sp_creation, split->sp_expire, split->sp_lastmod, split->sp_lifetime,
1669+ SplitIsActive(split) ? '+' : '-', split->sp_reason);
1670+}
1671+
1672+
1673+/** Count split entries and memory used by them.
1674+ * @param[out] sp_size Receives total number of bytes allocated for split entries.
1675+ * @return Number of split entries currently allocated.
1676+ */
1677+int
1678+split_memory_count(size_t *sp_size)
1679+{
1680+ struct Split *split;
1681+ unsigned int sp = 0;
1682+
1683+ /* TODO: check for expired entries? */
1684+ for (split = GlobalSplitList; split; split = split->sp_next)
1685+ {
1686+ sp++;
1687+ *sp_size += sizeof(struct Split);
1688+ *sp_size += split->sp_server ? (strlen(split->sp_server) + 1) : 0;
1689+ *sp_size += split->sp_reason ? (strlen(split->sp_reason) + 1) : 0;
1690+ }
1691+ return sp;
1692+}
1693+