]> jfr.im git - irc/quakenet/snircd-patchqueue.git/blame_incremental - welcome.patch
whonoidle: hide idle time of users with mode +I in non-HIS setup in WHO
[irc/quakenet/snircd-patchqueue.git] / welcome.patch
... / ...
CommitLineData
1Add welcome message functionality.
2
3client commands:
4user:
5/WELCOME
6shows welcome messages set, same is shown on connect
7
8oper:
9/WELCOME [<target>] [[!]<name> :<message>]
10to view welcome messages from a remote server
11to set a local welcome message on this server or a remote server
12set a global welcome message (target *)
13the ! prefix makes the server annouce the welcome message to its clients when setting
14
15server:
16:<source> WE <target> [[!]<name> <timestamp> <who> :<text>]
17who is who set the message, the server puts in the opername when a client sets it.
18:<name> is a number 1 to WELCOME_MAX_ENTRIES - currently set at 10 (should be more than we ever need)
19that means there is room for 10 local and 10 global entries
20
21STATS W/welcome (/STATS w/userload made case sensitive)
22:server 230 nick W Name Target Who Timestamp :Message
23:server 227 nick W 1 * opername 1233072583 :Latest news: testing this welcome patch :)
24:server 227 nick W 2 * opername 1233072583 :
25:server 227 nick W 1 servername opername 1233072590 :This is a test server, expect restarts.
26:server 219 nick W :End of /STATS report
27
28listing welcomes or on connect:
29:server NOTICE nick :[QuakeNet] Latest news: testing this welcome patch :)
30:server NOTICE nick :[server] This is a test server, expect restarts.
31
32announcement is done by a notice by the local server to $* with the same message
33format as for listing welcome messages.
34:server NOTICE $* :[QuakeNet] Latest news: testing this welcome patch :)
35:server NOTICE $* :[server] This is a test server, expect restarts.
36
37
38Files:
39
40include/handlers.h
41add m_welcome mo_welcome ms_welcome mh_welcome functions
42
43include/features.h
44ircd/features.c
45add features FEAT_WELCOME and FEAT_HIS_STATS_W
46
47include/msg.h
48add MSG_WELCOME TOK_WELCOME CMD_WELCOME
49
50ircd/parse.c
51add welcome message functions
52
53include/numeric.h
54ircd/s_err.c
55add RPL_STATSWELCOME ERR_NOSUCHWELCOME
56
57include/welcome.h
58ircd/welcome.c
59ircd/m_welcome.c
60new
61
62ircd/Makefile.in
63add welcome.c and m_welcome.c files
64
65ircd/s_serv.c
66add burst welcome message
67
68ircd/s_stats.c
69add /STATS W/welcome
70
71ircd/s_user.c
72add showing of welcome messages on connect
73
74include/client.h
75ircd/client.c
76ircd/ircd_lexer.l
77ircd/ircd_parser.y
78add PRIV_LOCAL_WELCOME PRIV_WELCOME
79
80diff -r ea8f262d805b include/client.h
81--- a/include/client.h
82+++ b/include/client.h
83@@ -142,6 +142,8 @@
84 PRIV_USER_PRIVACY, /* oper can bypass user privacy +x etc gives i.e. see real ip's */
85 PRIV_CHANNEL_PRIVACY, /* oper can bypass channel privacy i.e. can see modes on channels they are not on and channel keys */
86 PRIV_SERVERINFO, /* oper can use /get, /stats, /hash, retrieve remote information */
87+ PRIV_WELCOME, /* oper can WELCOME */
88+ PRIV_LOCAL_WELCOME, /* oper can local WELCOME */
89 PRIV_LAST_PRIV /**< number of privileges */
90 };
91
92diff -r ea8f262d805b include/handlers.h
93--- a/include/handlers.h
94+++ b/include/handlers.h
95@@ -138,6 +138,7 @@
96 extern int m_version(struct Client*, struct Client*, int, char*[]);
97 extern int m_wallchops(struct Client*, struct Client*, int, char*[]);
98 extern int m_wallvoices(struct Client*, struct Client*, int, char*[]);
99+extern int m_welcome(struct Client*, struct Client*, int, char*[]);
100 extern int m_who(struct Client*, struct Client*, int, char*[]);
101 extern int m_whois(struct Client*, struct Client*, int, char*[]);
102 extern int m_whowas(struct Client*, struct Client*, int, char*[]);
103@@ -172,6 +173,7 @@
104 extern int mo_version(struct Client*, struct Client*, int, char*[]);
105 extern int mo_wallops(struct Client*, struct Client*, int, char*[]);
106 extern int mo_wallusers(struct Client*, struct Client*, int, char*[]);
107+extern int mo_welcome(struct Client*, struct Client*, int, char*[]);
108 extern int mo_xquery(struct Client*, struct Client*, int, char*[]);
109 extern int mr_error(struct Client*, struct Client*, int, char*[]);
110 extern int mr_error(struct Client*, struct Client*, int, char*[]);
111@@ -230,6 +232,7 @@
112 extern int ms_wallops(struct Client*, struct Client*, int, char*[]);
113 extern int ms_wallusers(struct Client*, struct Client*, int, char*[]);
114 extern int ms_wallvoices(struct Client*, struct Client*, int, char*[]);
115+extern int ms_welcome(struct Client*, struct Client*, int, char*[]);
116 extern int ms_whois(struct Client*, struct Client*, int, char*[]);
117 extern int ms_xquery(struct Client*, struct Client*, int, char*[]);
118 extern int ms_xreply(struct Client*, struct Client*, int, char*[]);
119diff -r ea8f262d805b include/ircd_features.h
120--- a/include/ircd_features.h
121+++ b/include/ircd_features.h
122@@ -101,6 +101,7 @@
123 FEAT_IRCD_RES_TIMEOUT,
124 FEAT_AUTH_TIMEOUT,
125 FEAT_ANNOUNCE_INVITES,
126+ FEAT_WELCOME,
127
128 /* features that affect all operators */
129 FEAT_EXTENDED_CHECKCMD,
130@@ -142,6 +143,7 @@
131 FEAT_HIS_STATS_u,
132 FEAT_HIS_STATS_U,
133 FEAT_HIS_STATS_v,
134+ FEAT_HIS_STATS_W,
135 FEAT_HIS_STATS_w,
136 FEAT_HIS_STATS_x,
137 FEAT_HIS_STATS_y,
138diff -r ea8f262d805b include/msg.h
139--- a/include/msg.h
140+++ b/include/msg.h
141@@ -196,6 +196,10 @@
142 #define TOK_NOTICE "O"
143 #define CMD_NOTICE MSG_NOTICE, TOK_NOTICE
144
145+#define MSG_WELCOME "WELCOME" /* WELC */
146+#define TOK_WELCOME "WE"
147+#define CMD_WELCOME MSG_WELCOME, TOK_WELCOME
148+
149 #define MSG_WALLCHOPS "WALLCHOPS" /* WC */
150 #define TOK_WALLCHOPS "WC"
151 #define CMD_WALLCHOPS MSG_WALLCHOPS, TOK_WALLCHOPS
152diff -r ea8f262d805b include/numeric.h
153--- a/include/numeric.h
154+++ b/include/numeric.h
155@@ -116,6 +116,7 @@
156 RPL_STATSGLINE 227 Dalnet
157 RPL_STATSVLINE 227 unreal */
158 #define RPL_STATSALINE 226 /* Hybrid, Undernet */
159+#define RPL_STATSWELCOME 227 /* QuakeNet extension */
160 #define RPL_STATSQLINE 228 /* Undernet extension */
161
162 /* RPL_SERVICEINFO 231 unused */
163@@ -440,6 +441,8 @@
164 /* ERR_GHOSTEDCLIENT 503 efnet */
165 /* ERR_VWORLDWARN 503 austnet */
166
167+#define ERR_NOSUCHWELCOME 509 /* QuakeNet extension */
168+
169 #define ERR_SILELISTFULL 511 /* Undernet extension */
170 /* ERR_NOTIFYFULL 512 aircd */
171 /* ERR_TOOMANYWATCH 512 Numeric List: Dalnet */
172diff -r ea8f262d805b include/welcome.h
173--- /dev/null
174+++ b/include/welcome.h
175@@ -0,0 +1,76 @@
176+#ifndef INCLUDED_welcome_h
177+#define INCLUDED_welcome_h
178+/*
179+ * IRC - Internet Relay Chat, include/welcome.h
180+ * Copyright (C) 1990 Jarkko Oikarinen and
181+ * University of Oulu, Computing Center
182+ * Copyright (C) 2000 Kevin L. Mitchell <klmitch@mit.edu>
183+ *
184+ * This program is free software; you can redistribute it and/or modify
185+ * it under the terms of the GNU General Public License as published by
186+ * the Free Software Foundation; either version 2, or (at your option)
187+ * any later version.
188+ *
189+ * This program is distributed in the hope that it will be useful,
190+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
191+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
192+ * GNU General Public License for more details.
193+ *
194+ * You should have received a copy of the GNU General Public License
195+ * along with this program; if not, write to the Free Software
196+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
197+ */
198+/** @file
199+ * @brief Interface and declarations for welcome message handling.
200+ */
201+#ifndef INCLUDED_sys_types_h
202+#include <sys/types.h>
203+#define INCLUDED_sys_types_h
204+#endif
205+
206+struct Client;
207+struct StatDesc;
208+
209+/* Maximum number of welcome entries (per type; X global, X local) */
210+#define WELCOME_MAX_ENTRIES 10
211+/* Maximum length of a welcome message */
212+#define WELCOMELEN TOPICLEN
213+
214+
215+/* Test if a welcome entry is in a valid range */
216+#define WelcomeIsValid(x) ((unsigned) (x) <= 2 * WELCOME_MAX_ENTRIES -1)
217+/* Test if a welcome entry is set */
218+#define WelcomeIsSet(x) (WelcomeArray[(x)].timestamp > 0)
219+/* Test if a welcome entry is empty */
220+#define WelcomeIsEmpty(x) (*WelcomeArray[(x)].text == 0)
221+
222+/* Get welcome timestamp */
223+#define WelcomeTS(x) (WelcomeArray[(x)].timestamp)
224+/* Get welcome text */
225+#define WelcomeText(x) (WelcomeArray[(x)].text)
226+/* Get welcome who info */
227+#define WelcomeWho(x) (WelcomeArray[(x)].who)
228+
229+
230+/* Describes a Welcome message entry. */
231+struct Welcome {
232+ time_t timestamp; /**< Timestamp of the welcome */
233+ char text[WELCOMELEN + 1]; /**< Message */
234+ char who[ACCOUNTLEN + 1]; /**< Who set it */
235+};
236+
237+/** Welcome type flags */
238+#define WELCOME_LOCAL 0x01 /**< welcome is local */
239+/** Welcome action flags */
240+#define WELCOME_ANNOUNCE 0x02 /**< announce change to users */
241+#define WELCOME_INSERT 0x04 /**< insert welcome message, move down all others one place */
242+
243+extern int welcome_do(struct Client *cptr, struct Client *sptr, char *name,
244+ time_t timestamp, char *who, char *text, unsigned int flags);
245+extern void welcome_announce(int name);
246+extern void welcome_burst(struct Client *cptr);
247+extern int welcome_list(struct Client *sptr, int connect);
248+extern void welcome_stats(struct Client *sptr, const struct StatDesc *sd, char *param);
249+extern int welcome_memory_count(size_t *we_size);
250+
251+#endif /* INCLUDED_welcome_h */
252diff -r ea8f262d805b ircd/Makefile.in
253--- a/ircd/Makefile.in
254+++ b/ircd/Makefile.in
255@@ -186,6 +186,7 @@
256 m_wallops.c \
257 m_wallusers.c \
258 m_wallvoices.c \
259+ m_welcome.c \
260 m_who.c \
261 m_whois.c \
262 m_whowas.c \
263@@ -215,6 +216,7 @@
264 send.c \
265 uping.c \
266 userload.c \
267+ welcome.c \
268 whocmds.c \
269 whowas.c \
270 y.tab.c
271@@ -1161,6 +1163,11 @@
272 ../include/ircd_reply.h ../include/ircd_string.h \
273 ../include/ircd_chattr.h ../include/msg.h ../include/numeric.h \
274 ../include/numnicks.h ../include/s_user.h ../include/send.h
275+m_welcome.o: m_welcome.c ../config.h ../include/channel.h \
276+ ../include/client.h ../include/hash.h ../include/ircd.h ../include/ircd_log.h \
277+ ../include/ircd_reply.h ../include/ircd_string.h ../include/msg.h \
278+ ../include/numeric.h ../include/numnicks.h ../include/s_user.h \
279+ ../include/send.h ../include/welcome.h
280 m_who.o: m_who.c ../config.h ../include/channel.h ../include/ircd_defs.h \
281 ../include/res.h ../config.h ../include/client.h ../include/dbuf.h \
282 ../include/msgq.h ../include/ircd_events.h ../include/ircd_handler.h \
283@@ -1422,6 +1429,13 @@
284 ../include/numnicks.h ../include/querycmds.h ../include/ircd_features.h \
285 ../include/s_misc.h ../include/s_stats.h ../include/send.h \
286 ../include/struct.h ../include/sys.h
287+welcome.o: welcome.c ../config.h ../include/client.h \
288+ ../include/hash.h ../include/ircd.h ../include/ircd_alloc.h \
289+ ../include/ircd_features.h ../include/ircd_log.h ../include/ircd_reply.h \
290+ ../include/match.h ../include/msg.h ../include/numeric.h \
291+ ../include/numnicks.h ../include/s_debug.h ../include/s_bsd.h \
292+ ../include/s_misc.h ../include/send.h ../include/struct.h \
293+ ../include/sys.h ../include/welcome.h
294 whocmds.o: whocmds.c ../config.h ../include/whocmds.h \
295 ../include/channel.h ../include/ircd_defs.h ../include/res.h \
296 ../config.h ../include/client.h ../include/dbuf.h ../include/msgq.h \
297diff -r ea8f262d805b ircd/client.c
298--- a/ircd/client.c
299+++ b/ircd/client.c
300@@ -177,6 +177,7 @@
301 FlagSet(&privs_local, PRIV_WHOX);
302 FlagSet(&privs_local, PRIV_DISPLAY);
303 FlagSet(&privs_local, PRIV_FORCE_LOCAL_OPMODE);
304+ FlagSet(&privs_local, PRIV_LOCAL_WELCOME);
305
306 privs_defaults_set = 1;
307 }
308@@ -223,6 +224,7 @@
309 ClrPriv(client, PRIV_JUPE);
310 ClrPriv(client, PRIV_OPMODE);
311 ClrPriv(client, PRIV_BADCHAN);
312+ ClrPriv(client, PRIV_WELCOME);
313 }
314 }
315
316@@ -244,7 +246,7 @@
317 P(CHANSERV), P(XTRA_OPER), P(NOIDLE), P(FREEFORM),
318 P(PARANOID), P(CHECK), P(WALL), P(CLOSE),
319 P(ROUTE), P(ROUTEINFO), P(SERVERINFO), P(CHANNEL_PRIVACY),
320- P(USER_PRIVACY),
321+ P(USER_PRIVACY), P(WELCOME), P(LOCAL_WELCOME),
322 #undef P
323 { 0, 0 }
324 };
325diff -r ea8f262d805b ircd/ircd_features.c
326--- a/ircd/ircd_features.c
327+++ b/ircd/ircd_features.c
328@@ -366,6 +366,7 @@
329 F_I(IRCD_RES_TIMEOUT, 0, 4, 0),
330 F_I(AUTH_TIMEOUT, 0, 9, 0),
331 F_B(ANNOUNCE_INVITES, 0, 0, 0),
332+ F_B(WELCOME, 0, 1, 0),
333
334 /* features that affect all operators */
335 F_B(EXTENDED_CHECKCMD, 0, 0, 0),
336@@ -407,6 +408,7 @@
337 F_B(HIS_STATS_u, 0, 1, 0),
338 F_B(HIS_STATS_U, 0, 1, 0),
339 F_B(HIS_STATS_v, 0, 1, 0),
340+ F_B(HIS_STATS_W, 0, 1, 0),
341 F_B(HIS_STATS_w, 0, 1, 0),
342 F_B(HIS_STATS_x, 0, 1, 0),
343 F_B(HIS_STATS_y, 0, 1, 0),
344diff -r ea8f262d805b ircd/ircd_lexer.l
345--- a/ircd/ircd_lexer.l
346+++ b/ircd/ircd_lexer.l
347@@ -166,6 +166,8 @@
348 { "serverinfo", TPRIV_SERVERINFO },
349 { "user_privacy", TPRIV_USER_PRIVACY },
350 { "channel_privacy", TPRIV_CHANNEL_PRIVACY },
351+ { "local_welcome", TPRIV_LOCAL_WELCOME },
352+ { "welcome", TPRIV_WELCOME },
353 { NULL, 0 }
354 };
355 static int ntokens;
356diff -r ea8f262d805b ircd/ircd_parser.y
357--- a/ircd/ircd_parser.y
358+++ b/ircd/ircd_parser.y
359@@ -189,6 +189,7 @@
360 %token TPRIV_CHANSERV TPRIV_XTRA_OPER TPRIV_NOIDLE TPRIV_FREEFORM TPRIV_PARANOID
361 %token TPRIV_CHECK TPRIV_WALL TPRIV_CLOSE TPRIV_ROUTE TPRIV_ROUTEINFO TPRIV_SERVERINFO
362 %token TPRIV_CHANNEL_PRIVACY TPRIV_USER_PRIVACY TPRIV_LIST_CHAN
363+%token TPRIV_LOCAL_WELCOME TPRIV_WELCOME
364 /* and some types... */
365 %type <num> sizespec
366 %type <num> timespec timefactor factoredtimes factoredtime
367@@ -703,6 +704,8 @@
368 TPRIV_SERVERINFO { $$ = PRIV_SERVERINFO ; } |
369 TPRIV_CHANNEL_PRIVACY { $$ = PRIV_CHANNEL_PRIVACY ; } |
370 TPRIV_USER_PRIVACY { $$ = PRIV_USER_PRIVACY ; } |
371+ TPRIV_LOCAL_WELCOME { $$ = PRIV_LOCAL_WELCOME; } |
372+ TPRIV_WELCOME { $$ = PRIV_WELCOME; } |
373 TPRIV_PARANOID { $$ = PRIV_PARANOID; } ;
374 yesorno: YES { $$ = 1; } | NO { $$ = 0; };
375
376diff -r ea8f262d805b ircd/m_welcome.c
377--- /dev/null
378+++ b/ircd/m_welcome.c
379@@ -0,0 +1,292 @@
380+/*
381+ * IRC - Internet Relay Chat, ircd/m_welcome.c
382+ * Copyright (C) 1990 Jarkko Oikarinen and
383+ * University of Oulu, Computing Center
384+ *
385+ * See file AUTHORS in IRC package for additional names of
386+ * the programmers.
387+ *
388+ * This program is free software; you can redistribute it and/or modify
389+ * it under the terms of the GNU General Public License as published by
390+ * the Free Software Foundation; either version 1, or (at your option)
391+ * any later version.
392+ *
393+ * This program is distributed in the hope that it will be useful,
394+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
395+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
396+ * GNU General Public License for more details.
397+ *
398+ * You should have received a copy of the GNU General Public License
399+ * along with this program; if not, write to the Free Software
400+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
401+ *
402+ */
403+
404+/*
405+ * m_functions execute protocol messages on this server:
406+ *
407+ * cptr is always NON-NULL, pointing to a *LOCAL* client
408+ * structure (with an open socket connected!). This
409+ * identifies the physical socket where the message
410+ * originated (or which caused the m_function to be
411+ * executed--some m_functions may call others...).
412+ *
413+ * sptr is the source of the message, defined by the
414+ * prefix part of the message if present. If not
415+ * or prefix not found, then sptr==cptr.
416+ *
417+ * (!IsServer(cptr)) => (cptr == sptr), because
418+ * prefixes are taken *only* from servers...
419+ *
420+ * (IsServer(cptr))
421+ * (sptr == cptr) => the message didn't
422+ * have the prefix.
423+ *
424+ * (sptr != cptr && IsServer(sptr) means
425+ * the prefix specified servername. (?)
426+ *
427+ * (sptr != cptr && !IsServer(sptr) means
428+ * that message originated from a remote
429+ * user (not local).
430+ *
431+ * combining
432+ *
433+ * (!IsServer(sptr)) means that, sptr can safely
434+ * taken as defining the target structure of the
435+ * message in this server.
436+ *
437+ * *Always* true (if 'parse' and others are working correct):
438+ *
439+ * 1) sptr->from == cptr (note: cptr->from == cptr)
440+ *
441+ * 2) MyConnect(sptr) <=> sptr == cptr (e.g. sptr
442+ * *cannot* be a local connection, unless it's
443+ * actually cptr!). [MyConnect(x) should probably
444+ * be defined as (x == x->from) --msa ]
445+ *
446+ * parc number of variable parameter strings (if zero,
447+ * parv is allowed to be NULL)
448+ *
449+ * parv a NULL terminated list of parameter pointers,
450+ *
451+ * parv[0], sender (prefix string), if not present
452+ * this points to an empty string.
453+ * parv[1]...parv[parc-1]
454+ * pointers to additional parameters
455+ * parv[parc] == NULL, *always*
456+ *
457+ * note: it is guaranteed that parv[0]..parv[parc-1] are all
458+ * non-NULL pointers.
459+ */
460+#include "config.h"
461+
462+#include "channel.h"
463+#include "client.h"
464+#include "hash.h"
465+#include "ircd.h"
466+#include "ircd_features.h"
467+#include "ircd_log.h"
468+#include "ircd_reply.h"
469+#include "ircd_snprintf.h"
470+#include "ircd_string.h"
471+#include "msg.h"
472+#include "numeric.h"
473+#include "numnicks.h"
474+#include "s_user.h"
475+#include "send.h"
476+#include "welcome.h"
477+
478+/* #include <assert.h> -- Now using assert in ircd_log.h */
479+
480+/*
481+ * m_welcome - local generic message handler
482+ *
483+ * parv[0] = Send prefix
484+ * parv[1] = [remote server to query]
485+ */
486+int m_welcome(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
487+{
488+ /* feature disabled */
489+ if (!feature_bool(FEAT_WELCOME))
490+ return send_reply(sptr, ERR_DISABLED, "WELCOME");
491+
492+ /* only opers can set the welcome messages */
493+ if (parc > 2)
494+ return send_reply(sptr, ERR_NOPRIVILEGES);
495+
496+ /* remote listing request, see if it is for me or a remote server
497+ * check FEAT_HIS_REMOTE to decide if an ordinary user can do this
498+ */
499+ if ((parc > 1) && (hunt_server_cmd(sptr, CMD_WELCOME, cptr, feature_int(FEAT_HIS_REMOTE),
500+ "%C", 1, parc, parv) != HUNTED_ISME))
501+ return 0;
502+
503+ /* local listing */
504+ return welcome_list(sptr, 0);
505+}
506+
507+
508+/*
509+ * mo_welcome - oper message handler
510+ *
511+ * listing:
512+ * parv[0] = Send prefix
513+ *
514+ * remote listing:
515+ * parv[0] = Send prefix
516+ * parv[1] = Target
517+ *
518+ * set global or on remote server:
519+ * parv[0] = Send prefix
520+ * parv[1] = Target: server or * for global (or left out for this server)
521+ * parv[2] = Name
522+ * parv[3] = Text
523+ */
524+int mo_welcome(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
525+{
526+ char *target, *name, *who, *text, pattern[BUFSIZE];
527+ time_t timestamp;
528+ unsigned int flags = 0;
529+ int local = 0;
530+
531+ /* feature disabled */
532+ if (!feature_bool(FEAT_WELCOME))
533+ return send_reply(sptr, ERR_DISABLED, "WELCOME");
534+
535+ /* TODO: move feature check here? */
536+ /* remote listing request, see if it is for me or a remote server */
537+ if ((parc == 2) && (hunt_server_cmd(sptr, CMD_WELCOME, cptr, 0, "%C", 1, parc, parv) != HUNTED_ISME))
538+ return 0;
539+
540+ /* local listing */
541+ if (parc <= 2)
542+ return welcome_list(sptr, 0);
543+
544+ /* check PRIVS */
545+ /* local - need PRIV LOCAL_WELCOME or WELCOME */
546+ if (parc == 3 && !HasPriv(sptr,PRIV_LOCAL_WELCOME) && !HasPriv(sptr,PRIV_WELCOME))
547+ return send_reply(sptr, ERR_NOPRIVILEGES);
548+
549+ /* global or remote - need PRIV WELCOME */
550+ if (parc >= 4 && !HasPriv(sptr,PRIV_WELCOME))
551+ return send_reply(sptr, ERR_NOPRIVILEGES);
552+
553+ /* set the parameters */
554+
555+ /* target not given, only name - setting local welcome */
556+ if (parc < 4) {
557+ local++;
558+ target = cli_name(&me);
559+ name = parv[1];
560+ flags |= WELCOME_LOCAL;
561+
562+ /* target and name given */
563+ } else {
564+ target = parv[1];
565+ name = parv[2];
566+ }
567+ timestamp = TStime();
568+ who = cli_user(sptr)->opername;
569+ text = parv[parc - 1];
570+
571+ /* target is not global */
572+ if (!(target[0] == '*' && target[1] == '\0') && !local) {
573+
574+ /* build a pattern for hunt_server_cmd since we do not have all we need in parv */
575+ ircd_snprintf(0, pattern, sizeof(pattern), "%s %s %Tu %s :%s", "%C", name, timestamp, who, text);
576+ if (hunt_server_cmd(sptr, CMD_WELCOME, cptr, 0, pattern, 1, 2, parv) != HUNTED_ISME)
577+ return 0;
578+
579+ /* else it is a local welcome, for me */
580+ flags |= WELCOME_LOCAL;
581+ }
582+
583+ /* check for anounce prefix */
584+ if (*name == '$') {
585+ name++;
586+ /* only allow announce by oper for local welcome */
587+ if (flags & WELCOME_LOCAL)
588+ flags |= WELCOME_ANNOUNCE;
589+ }
590+
591+ /* check for insert prefix */
592+ if (*name == '+') {
593+ name++;
594+ flags |= WELCOME_INSERT;
595+ }
596+
597+ /* and do it */
598+ return welcome_do(cptr, sptr, name, timestamp, who, text, flags);
599+}
600+
601+
602+/*
603+ * ms_welcome - server message handler
604+ *
605+ * parv[0] = Send prefix
606+ * parv[1] = Target: server numeric or * for global
607+ * parv[2] = Name
608+ * parv[3] = Timestamp
609+ * parv[4] = Who
610+ * parv[5] = Text
611+ */
612+int ms_welcome(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
613+{
614+ char *target, *name, *who, *text;
615+ time_t timestamp;
616+ unsigned int flags = 0;
617+
618+ /* not enough - complain */
619+ if (parc < 2) {
620+ protocol_violation(sptr, "Too few parameters for WELCOME (got %d - need 2)", parc);
621+ return need_more_params(sptr, "WELCOME");
622+ }
623+
624+ /* remote listing request, see if it is for me or a remote server */
625+ if (parc == 2) {
626+ if (IsServer(sptr))
627+ return protocol_violation(cptr, "WELCOME listing request from server %C", sptr);
628+ if (hunt_server_cmd(sptr, CMD_WELCOME, cptr, 0, "%C", 1, parc, parv) != HUNTED_ISME)
629+ return 0;
630+ return welcome_list(sptr, 0);
631+ }
632+
633+ /* we need at least 6 parameters to continue - complain */
634+ if (parc < 6) {
635+ protocol_violation(sptr, "Too few parameters for WELCOME (got %d - need 6)", parc);
636+ return need_more_params(sptr, "WELCOME");
637+ }
638+
639+ /* set the parameters */
640+ target = parv[1];
641+ name = parv[2];
642+ timestamp = atoi(parv[3]);
643+ who = parv[4];
644+ text = parv[parc - 1]; /* parse reason as last parameter */
645+
646+ /* target is not global */
647+ if (!(target[0] == '*' && target[1] == '\0')) {
648+
649+ /* not for me, and forward it */
650+ if (hunt_server_cmd(sptr, CMD_WELCOME, cptr, 0, "%C %s %s %s :%s", 1, parc, parv) != HUNTED_ISME)
651+ return 0;
652+
653+ /* local welcome for me */
654+ flags |= WELCOME_LOCAL;
655+ }
656+
657+ /* check for anounce prefix */
658+ if (*name == '$') {
659+ name++;
660+ flags |= WELCOME_ANNOUNCE;
661+ }
662+
663+ /* check for insert prefix */
664+ if (*name == '+') {
665+ name++;
666+ flags |= WELCOME_INSERT;
667+ }
668+
669+ /* and do it */
670+ return welcome_do(cptr, sptr, name, timestamp, who, text, flags);
671+}
672diff -r ea8f262d805b ircd/parse.c
673--- a/ircd/parse.c
674+++ b/ircd/parse.c
675@@ -661,6 +661,15 @@
676 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
677 { m_unregistered, m_not_oper, ms_check, mo_check, m_ignore }
678 },
679+
680+ /* add command for WELCOME */
681+ {
682+ MSG_WELCOME,
683+ TOK_WELCOME,
684+ 0, MAXPARA, MFLG_SLOW, 0, NULL,
685+ /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
686+ { m_unregistered, m_welcome, ms_welcome, mo_welcome, m_ignore }
687+ },
688
689 /* This command is an alias for QUIT during the unregistered part of
690 * of the server. This is because someone jumping via a broken web
691diff -r ea8f262d805b ircd/s_debug.c
692--- a/ircd/s_debug.c
693+++ b/ircd/s_debug.c
694@@ -50,6 +50,7 @@
695 #include "send.h"
696 #include "struct.h"
697 #include "sys.h"
698+#include "welcome.h"
699 #include "whowas.h"
700
701 /* #include <assert.h> -- Now using assert in ircd_log.h */
702@@ -231,7 +232,8 @@
703 aw = 0, /* aways set */
704 wwa = 0, /* whowas aways */
705 gl = 0, /* glines */
706- ju = 0; /* jupes */
707+ ju = 0, /* jupes */
708+ we = 0; /* welcomes */
709
710 size_t chm = 0, /* memory used by channels */
711 chbm = 0, /* memory used by channel bans */
712@@ -244,6 +246,7 @@
713 wwm = 0, /* whowas array memory used */
714 glm = 0, /* memory used by glines */
715 jum = 0, /* memory used by jupes */
716+ wem = 0, /* memory used by welcomes */
717 com = 0, /* memory used by conf lines */
718 dbufs_allocated = 0, /* memory used by dbufs */
719 dbufs_used = 0, /* memory used by dbufs */
720@@ -351,6 +354,10 @@
721 send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG,
722 ":Glines %d(%zu) Jupes %d(%zu)", gl, glm, ju, jum);
723
724+ we = welcome_memory_count(&wem);
725+ send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG,
726+ ":Welcomes %d(%zu)", we, wem);
727+
728 send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG,
729 ":Hash: client %d(%zu), chan is the same", HASHSIZE,
730 sizeof(void *) * HASHSIZE);
731diff -r ea8f262d805b ircd/s_err.c
732--- a/ircd/s_err.c
733+++ b/ircd/s_err.c
734@@ -486,7 +486,7 @@
735 /* 226 */
736 { RPL_STATSALINE, "%s", "226" },
737 /* 227 */
738- { 0 },
739+ { RPL_STATSWELCOME, "W %d %s %s %Tu :%s", "227" },
740 /* 228 */
741 { RPL_STATSQLINE, "Q %s :%s", "228" },
742 /* 229 */
743@@ -1050,7 +1050,7 @@
744 /* 508 */
745 { 0 },
746 /* 509 */
747- { 0 },
748+ { ERR_NOSUCHWELCOME, "%s :No such welcome", "509" },
749 /* 510 */
750 { 0 },
751 /* 511 */
752diff -r ea8f262d805b ircd/s_serv.c
753--- a/ircd/s_serv.c
754+++ b/ircd/s_serv.c
755@@ -57,6 +57,7 @@
756 #include "struct.h"
757 #include "sys.h"
758 #include "userload.h"
759+#include "welcome.h"
760
761 /* #include <assert.h> -- Now using assert in ircd_log.h */
762 #include <stdlib.h>
763@@ -196,6 +197,7 @@
764 */
765 gline_burst(cptr);
766 jupe_burst(cptr);
767+ welcome_burst(cptr);
768
769 /*
770 * Pass on my client information to the new server
771diff -r ea8f262d805b ircd/s_stats.c
772--- a/ircd/s_stats.c
773+++ b/ircd/s_stats.c
774@@ -54,6 +54,7 @@
775 #include "send.h"
776 #include "struct.h"
777 #include "userload.h"
778+#include "welcome.h"
779
780 #include <stdio.h>
781 #include <stdlib.h>
782@@ -650,9 +651,12 @@
783 { 'V', "vserversmach", (STAT_FLAG_OPERFEAT | STAT_FLAG_VARPARAM | STAT_FLAG_CASESENS), FEAT_HIS_STATS_v,
784 stats_servers_verbose, 0,
785 "Verbose server information." },
786- { 'w', "userload", STAT_FLAG_OPERFEAT, FEAT_HIS_STATS_w,
787+ { 'w', "userload", STAT_FLAG_OPERFEAT | STAT_FLAG_CASESENS, FEAT_HIS_STATS_w,
788 calc_load, 0,
789 "Userload statistics." },
790+ { 'W', "welcome", STAT_FLAG_OPERFEAT | STAT_FLAG_CASESENS, FEAT_HIS_STATS_W,
791+ welcome_stats, 0,
792+ "Welcome messages." },
793 { 'x', "memusage", STAT_FLAG_OPERFEAT, FEAT_HIS_STATS_x,
794 stats_meminfo, 0,
795 "List usage information." },
796diff -r ea8f262d805b ircd/s_user.c
797--- a/ircd/s_user.c
798+++ b/ircd/s_user.c
799@@ -63,6 +63,7 @@
800 #include "userload.h"
801 #include "version.h"
802 #include "whowas.h"
803+#include "welcome.h"
804
805 #include "handlers.h" /* m_motd and m_lusers */
806
807@@ -402,6 +403,10 @@
808
809 IPcheck_connect_succeeded(sptr);
810
811+ /* send welcome */
812+ if (feature_bool(FEAT_WELCOME))
813+ welcome_list(sptr, 1);
814+
815 /* TODO: */
816 /* apply auto sethost if needed */
817 apply_spoofblock(sptr);
818diff -r ea8f262d805b ircd/welcome.c
819--- /dev/null
820+++ b/ircd/welcome.c
821@@ -0,0 +1,645 @@
822+/*
823+ * IRC - Internet Relay Chat, ircd/welcome.c
824+ * Copyright (C) 1990 Jarkko Oikarinen and
825+ * University of Oulu, Finland
826+ * Copyright (C) 2000 Kevin L. Mitchell <klmitch@mit.edu>
827+ *
828+ * This program is free software; you can redistribute it and/or modify
829+ * it under the terms of the GNU General Public License as published by
830+ * the Free Software Foundation; either version 1, or (at your option)
831+ * any later version.
832+ *
833+ * This program is distributed in the hope that it will be useful,
834+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
835+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
836+ * GNU General Public License for more details.
837+ *
838+ * You should have received a copy of the GNU General Public License
839+ * along with this program; if not, write to the Free Software
840+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
841+ */
842+/** @file
843+ * @brief Implementation of welcome message handling functions.
844+ */
845+#include "config.h"
846+
847+#include "client.h"
848+#include "hash.h"
849+#include "ircd.h"
850+#include "ircd_alloc.h"
851+#include "ircd_features.h"
852+#include "ircd_log.h"
853+#include "ircd_reply.h"
854+#include "ircd_string.h"
855+#include "match.h"
856+#include "msg.h"
857+#include "numeric.h"
858+#include "numnicks.h"
859+#include "s_bsd.h"
860+#include "s_debug.h"
861+#include "s_misc.h"
862+#include "send.h"
863+#include "struct.h"
864+#include "sys.h" /* FALSE bleah */
865+#include "welcome.h"
866+
867+/* #include <assert.h> -- Now using assert in ircd_log.h */
868+#include <string.h>
869+
870+
871+/** List of welcome messages - first MAX for global, second MAX for local */
872+static struct Welcome WelcomeArray[WELCOME_MAX_ENTRIES * 2] = { { 0 } };
873+
874+
875+/** Allocate a new welcome with the given parameters.
876+ * @param[in] name Name of the welcome message.
877+ * @param[in] text The welcome message.
878+ * @param[in] who Who set it.
879+ * @param[in] timestamp When it was set.
880+ * @return name Array number of the welcome set.
881+ */
882+static int
883+welcome_make(int name, char *text, char *who, time_t timestamp)
884+{
885+ assert(WelcomeIsValid(name));
886+ assert(NULL != text);
887+ assert(NULL != who);
888+
889+ /* store it */
890+ ircd_strncpy(WelcomeArray[name].text, text, WELCOMELEN);
891+ ircd_strncpy(WelcomeArray[name].who, who, ACCOUNTLEN);
892+ WelcomeArray[name].timestamp = timestamp;
893+
894+ return name;
895+}
896+
897+
898+/** Propagate a welcome message.
899+ * @param[in] cptr Local client that sent us the welcome.
900+ * @param[in] sptr Originator of the welcome.
901+ * @param[in] nameint Name of the message.
902+ * @param[in] timestamp Timestamp of when the message was set.
903+ * @param[in] who Who set this message.
904+ * @param[in] text The welcome message.
905+ * @param[in] flags Flags to set on welcome.
906+ * @return Zero
907+ */
908+int
909+welcome_propagate(struct Client *cptr, struct Client *sptr, int nameint,
910+ time_t timestamp, char *who, char *text, unsigned int flags)
911+{
912+ assert(NULL != sptr);
913+ assert(NULL != cptr);
914+ assert(nameint > 0 && nameint <= WELCOME_MAX_ENTRIES);
915+ assert(!(flags & WELCOME_LOCAL));
916+
917+ sendcmdto_serv_butone(sptr, CMD_WELCOME, cptr, "* %s%s%d %Tu %s :%s",
918+ (flags & WELCOME_ANNOUNCE) ? "$" : "", (flags & WELCOME_INSERT) ? "+" : "",
919+ nameint, timestamp, who, text);
920+
921+ return 0;
922+}
923+
924+
925+/** Resend a welcome message.
926+ * @param[in] cptr Local client that sent us the welcome.
927+ * @param[in] nameint Name of the message.
928+ * @param[in] namearray Name of the array item.
929+ * @return Zero
930+ */
931+int
932+welcome_resend(struct Client *cptr, int nameint, int namearray)
933+{
934+ assert(NULL != cptr);
935+ assert(IsServer(cptr));
936+ assert(nameint > 0 && nameint <= WELCOME_MAX_ENTRIES);
937+ assert(WelcomeIsValid(namearray));
938+
939+ sendcmdto_one(&me, CMD_WELCOME, cptr, "* %d %Tu %s :%s",
940+ nameint, WelcomeTS(namearray), WelcomeWho(namearray), WelcomeText(namearray));
941+
942+ return 0;
943+}
944+
945+
946+/** Log a welcome message.
947+ * @param[in] sptr Originator of the welcome.
948+ * @param[in] msg The message to show.
949+ * @param[in] flags Flags to set on welcome.
950+ * @return Zero
951+ */
952+int
953+welcome_log(struct Client *sptr, char *msg, unsigned int flags)
954+{
955+ assert(NULL != sptr);
956+ assert(NULL != msg);
957+
958+ /* inform ops */
959+ sendto_opmask_butone(0, SNO_OLDSNO, "%s %s",
960+ (feature_bool(FEAT_HIS_SNOTICES) || IsServer(sptr)) ?
961+ get_client_name_and_opername(sptr) : cli_name((cli_user(sptr))->server), msg);
962+
963+ /* log it */
964+ log_write(LS_NETWORK, L_INFO, LOG_NOSNOTICE, "%s %s", get_client_name_and_opername(sptr), msg);
965+
966+ /* welcome by remote user, inform oper of success */
967+ if ((flags & WELCOME_LOCAL) && IsUser(sptr) && !MyUser(sptr)) {
968+ sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :%s %s",
969+ sptr, get_client_name_and_opername(sptr), msg);
970+
971+ /* TODO: wallops all local changes, by both local and remote opers? */
972+ /* tell all opers about the local message being set remotely */
973+ sendwallto_group_butone(&me, WALL_WALLOPS, 0, "%s %s", get_client_name_and_opername(sptr), msg);
974+ }
975+
976+ return 0;
977+}
978+
979+
980+/** Set a welcome message.
981+ * @param[in] cptr Local client that sent us the welcome.
982+ * @param[in] sptr Originator of the welcome.
983+ * @param[in] nameint Name of the message.
984+ * @param[in] namearray Array entry.
985+ * @param[in] timestamp Timestamp of when the message was set.
986+ * @param[in] who Who set this message.
987+ * @param[in] text The message.
988+ * @param[in] flags Flags to set on welcome.
989+ * @return Zero
990+ */
991+int
992+welcome_set(struct Client *cptr, struct Client *sptr, int nameint,
993+ int namearray, time_t timestamp, char *who, char *text, unsigned int flags)
994+{
995+ char msg[BUFSIZE]; /* msg for logging */
996+ int new = 0;
997+
998+ assert(NULL != cptr);
999+ assert(NULL != sptr);
1000+ assert(nameint > 0 && nameint <= WELCOME_MAX_ENTRIES);
1001+ assert(WelcomeIsValid(namearray));
1002+
1003+ /* debug */
1004+ Debug((DEBUG_DEBUG, "welcome_set(\"%s\", \"%s\", %d, %d, %Tu, \"%s\", \"%s\", 0x%04x)",
1005+ cli_name(cptr), cli_name(sptr), nameint, namearray, timestamp, who, text, flags));
1006+
1007+ /* not set */
1008+ if (WelcomeIsEmpty(namearray))
1009+ new = 1;
1010+
1011+ /* update */
1012+ welcome_make(namearray, text, who, timestamp);
1013+
1014+ /* create msg for log */
1015+ ircd_snprintf(0, msg, 0, "%s%s%s WELCOME %d \"%s\" %s [%Tu]",
1016+ new ? "setting" : "changing",
1017+ (flags & WELCOME_ANNOUNCE) ? " and announcing " : " ",
1018+ (flags & WELCOME_LOCAL) ? "local" : "global",
1019+ nameint, WelcomeText(namearray), WelcomeWho(namearray), timestamp);
1020+
1021+ /* log it */
1022+ welcome_log(sptr, msg, flags);
1023+
1024+ /* propagate it */
1025+ if (!(flags & WELCOME_LOCAL))
1026+ welcome_propagate(cptr, sptr, nameint, timestamp, who, text, flags);
1027+
1028+ /* announce it */
1029+ if (flags & WELCOME_ANNOUNCE)
1030+ welcome_announce(namearray);
1031+
1032+ return 0;
1033+}
1034+
1035+
1036+/** Unset a welcome message.
1037+ * @param[in] cptr Local client that sent us the welcome.
1038+ * @param[in] sptr Originator of the welcome.
1039+ * @param[in] nameint Name of the message.
1040+ * @param[in] namearray Array entry.
1041+ * @param[in] timestamp Timestamp of when the message was set.
1042+ * @param[in] who Who set this message.
1043+ * @param[in] flags Flags to set on welcome.
1044+ * @return Zero
1045+ */
1046+int
1047+welcome_unset(struct Client *cptr, struct Client *sptr, int nameint,
1048+ int namearray, time_t timestamp, char *who, unsigned int flags)
1049+{
1050+ char msg[BUFSIZE]; /* msg for logging */
1051+ int i; /* loop variable */
1052+ int empty = namearray; /* first empty spot in array after namearray */
1053+ int end = WELCOME_MAX_ENTRIES -1; /* last element to check in array */
1054+
1055+ assert(NULL != cptr);
1056+ assert(NULL != sptr);
1057+ assert(nameint > 0 && nameint <= WELCOME_MAX_ENTRIES);
1058+ assert(WelcomeIsValid(namearray));
1059+
1060+ /* debug */
1061+ Debug((DEBUG_DEBUG, "welcome_unset(\"%s\", \"%s\", %d, %d, %Tu, \"%s\", 0x%04x)",
1062+ cli_name(cptr), cli_name(sptr), nameint, namearray, timestamp, who, flags));
1063+
1064+ /* create msg for log */
1065+ ircd_snprintf(0, msg, 0, "unsetting %s WELCOME %d \"%s\" %s [%Tu]",
1066+ (flags & WELCOME_LOCAL) ? "local" : "global",
1067+ nameint, WelcomeText(namearray), WelcomeWho(namearray), timestamp);
1068+
1069+ /* log it */
1070+ welcome_log(sptr, msg, flags);
1071+
1072+ /* update */
1073+ welcome_make(namearray, "", who, timestamp);
1074+
1075+ /* propagate it, but not when inserting */
1076+ if (!(flags & (WELCOME_LOCAL|WELCOME_INSERT)))
1077+ welcome_propagate(cptr, sptr, nameint, timestamp, who, "", flags);
1078+
1079+ /* correct end for local offset */
1080+ if (flags & WELCOME_LOCAL)
1081+ end += WELCOME_MAX_ENTRIES;
1082+
1083+ /* move entries up, update timestamp */
1084+ for (i = namearray; i < end; i++)
1085+ welcome_make(i, WelcomeText(i+1), WelcomeWho(i+1), timestamp);
1086+
1087+ /* clear last entry, update timestamp */
1088+ welcome_make(end, "", who, timestamp);
1089+
1090+ return 0;
1091+}
1092+
1093+
1094+/** Insert a welcome message.
1095+ * @param[in] cptr Local client that sent us the welcome.
1096+ * @param[in] sptr Originator of the welcome.
1097+ * @param[in] nameint Name of the message.
1098+ * @param[in] namearray Array entry.
1099+ * @param[in] timestamp Timestamp of when the message was set.
1100+ * @param[in] who Who set this message.
1101+ * @param[in] text The welcome message.
1102+ * @param[in] flags Flags to set on welcome.
1103+ * @return Zero
1104+ */
1105+int
1106+welcome_insert(struct Client *cptr, struct Client *sptr, int nameint,
1107+ int namearray, time_t timestamp, char *who, char *text, unsigned int flags)
1108+{
1109+ char msg[BUFSIZE]; /* msg for logging */
1110+ int i; /* loop variable */
1111+ int empty = -1; /* first empty spot in array after namearray */
1112+ int end = WELCOME_MAX_ENTRIES -1; /* last element to check in array */
1113+ int last = end; /* last welcome message to feed to welcome_unset */
1114+
1115+ assert(NULL != cptr);
1116+ assert(NULL != sptr);
1117+ assert(nameint > 0 && nameint <= WELCOME_MAX_ENTRIES);
1118+ assert(WelcomeIsValid(namearray));
1119+
1120+ /* debug */
1121+ Debug((DEBUG_DEBUG, "welcome_insert(\"%s\", \"%s\", %d, %d, %Tu, \"%s\", \"%s\", 0x%04x)",
1122+ cli_name(cptr), cli_name(sptr), nameint, namearray, timestamp, who, text, flags));
1123+
1124+ /* correct end for local offset */
1125+ if (flags & WELCOME_LOCAL)
1126+ end += WELCOME_MAX_ENTRIES;
1127+
1128+ /* find first empty spot */
1129+ for (i = namearray; i <= end; i++) {
1130+ if (WelcomeIsEmpty(i)) {
1131+ empty = i;
1132+ break;
1133+ }
1134+ }
1135+
1136+ /* no empty spot, need to unset last */
1137+ if (empty == -1) {
1138+ welcome_unset(cptr, sptr, end, namearray, timestamp, who, flags);
1139+ empty = end;
1140+ }
1141+
1142+ /* move entries down, update timestamp */
1143+ for (i = empty; i > namearray; i--)
1144+ welcome_make(i, WelcomeText(i-1), WelcomeWho(i-1), timestamp);
1145+
1146+ /* correct empty for local offset */
1147+ if (flags & WELCOME_LOCAL)
1148+ empty -= WELCOME_MAX_ENTRIES;
1149+
1150+ /* create msg for log */
1151+ if (nameint == empty)
1152+ ircd_snprintf(0, msg, 0, "moving %s WELCOME message %d one place down",
1153+ (flags & WELCOME_LOCAL) ? "local" : "global", nameint);
1154+ else
1155+ ircd_snprintf(0, msg, 0, "moving %s WELCOME message %d %s %d one place down",
1156+ (flags & WELCOME_LOCAL) ? "local" : "global", nameint, (empty - nameint > 1) ? "to" : "and" , empty);
1157+
1158+ /* log it */
1159+ welcome_log(sptr, msg, flags);
1160+
1161+ /* set it */
1162+ welcome_set(cptr, sptr, nameint, namearray, timestamp, who, text, flags);
1163+
1164+ return 0;
1165+}
1166+
1167+
1168+/** Change a welcome message.
1169+ * @param[in] cptr Local client that sent us the welcome.
1170+ * @param[in] sptr Originator of the welcome.
1171+ * @param[in] name Name of the message.
1172+ * @param[in] timestamp Timestamp of when the message was set.
1173+ * @param[in] who Who set this message.
1174+ * @param[in] text The welcome message.
1175+ * @param[in] flags Flags to set on welcome.
1176+ * @return Zero
1177+ */
1178+int
1179+welcome_do(struct Client *cptr, struct Client *sptr, char *name,
1180+ time_t timestamp, char *who, char *text, unsigned int flags)
1181+{
1182+ int nameint = atoi(name); /* transform to int */
1183+ int namearray = nameint - 1; /* used to test the array element */
1184+
1185+ assert(NULL != cptr);
1186+ assert(NULL != sptr);
1187+ assert(NULL != name);
1188+ assert(NULL != text);
1189+ assert(NULL != who);
1190+
1191+ /* debug */
1192+ Debug((DEBUG_DEBUG, "welcome_do(\"%s\", \"%s\", \"%s\", %Tu, \"%s\", \"%s\", 0x%04x)",
1193+ cli_name(cptr), cli_name(sptr), name, timestamp, who, text, flags));
1194+
1195+ /* if for some reason timestamp is 0, increase it */
1196+ if (timestamp == 0)
1197+ timestamp++;
1198+
1199+ /* name empty after taking off the prefixes? */
1200+ if (EmptyString(name)) {
1201+ if (IsUser(sptr))
1202+ sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :WELCOME: No message number given", sptr);
1203+ else
1204+ protocol_violation(cptr, "WELCOME: No message number given by %C", sptr);
1205+ return 0;
1206+ }
1207+
1208+ /* check name */
1209+ if (!WelcomeIsValid(namearray)) {
1210+ if (IsUser(sptr))
1211+ sendcmdto_one(&me, CMD_NOTICE, sptr,
1212+ "%C :WELCOME: Invalid message number %s - should between 1 and %d",
1213+ sptr, name, WELCOME_MAX_ENTRIES);
1214+ else
1215+ protocol_violation(cptr,
1216+ "WELCOME: Invalid message number %s from %C - should be between 1 and %d",
1217+ name, sptr, WELCOME_MAX_ENTRIES);
1218+ return 0;
1219+ }
1220+
1221+ /* source is user, and is myuser or welcome is local, check length of the message */
1222+ if ((IsUser(sptr)) && ((MyUser(sptr)) || (flags & WELCOME_LOCAL)) && (strlen(text) > WELCOMELEN)) {
1223+ sendcmdto_one(&me, CMD_NOTICE, sptr,
1224+ "%C :WELCOME: The message is too long with %d chars - max is %d chars",
1225+ sptr, strlen(text), WELCOMELEN);
1226+ ircd_strncpy(text, text, WELCOMELEN);
1227+ sendcmdto_one(&me, CMD_NOTICE, sptr,
1228+ "%C :WELCOME: Change or truncate the message to: \"%s\"", sptr, text);
1229+ return 0;
1230+ }
1231+
1232+ /* correct namearray for local offset */
1233+ if (flags & WELCOME_LOCAL)
1234+ namearray += WELCOME_MAX_ENTRIES;
1235+
1236+ /* must be true by now */
1237+ assert(WelcomeIsValid(namearray));
1238+ assert(nameint > 0 && nameint <= WELCOME_MAX_ENTRIES);
1239+
1240+ /* cannot unset welcome that is not set */
1241+ if (!WelcomeIsSet(namearray) && EmptyString(text)) {
1242+
1243+ /* from user, throw error */
1244+ if (IsUser(sptr))
1245+ return send_reply(sptr, ERR_NOSUCHWELCOME, name);
1246+
1247+ /* new local welcome from server, but empty - ignore
1248+ * we do accept a new global welcome message that is empty
1249+ */
1250+ if (flags & WELCOME_LOCAL)
1251+ return 0;
1252+ }
1253+
1254+ /* check if there is something to change */
1255+ /* we got a record for it */
1256+ if (WelcomeIsSet(namearray)) {
1257+
1258+ /* global */
1259+ if (!(flags & WELCOME_LOCAL)) {
1260+
1261+ /* myuser changes it,
1262+ * welcomeTS greater than or equal to timestamp, take welcomeTS+1 as timestamp
1263+ * else the change is not accepted upstream because of the older TS
1264+ */
1265+ if (MyUser(sptr)) {
1266+ if (WelcomeTS(namearray) >= timestamp)
1267+ timestamp = WelcomeTS(namearray) +1;
1268+ }
1269+
1270+ /* compare timestamps, ignore welcome when:
1271+ * we got a newer one
1272+ * or when timestamps are the same and our text is 'smaller'
1273+ */
1274+ else if ((timestamp < WelcomeTS(namearray)) || /* we got a newer one */
1275+ ((timestamp == WelcomeTS(namearray)) && /* same timestamp */
1276+ (strcmp(WelcomeText(namearray), text) < 0))) { /* our text is 'smaller' */
1277+ /* burst or burst ack, cptr gets our version from the burst */
1278+ if (IsBurstOrBurstAck(cptr))
1279+ return 0;
1280+ /* sync server */
1281+ return welcome_resend(cptr, nameint, namearray);
1282+ }
1283+
1284+ /* local welcome - we use our idea of the time */
1285+ } else
1286+ timestamp = TStime();
1287+
1288+ /* new global welcome from my user or local welcome
1289+ * compare new message with old message
1290+ */
1291+ if (IsUser(sptr) && (MyUser(sptr) || (flags & WELCOME_LOCAL))) {
1292+ if (strcmp(text, WelcomeText(namearray)) == 0) { /* use strcmp because the user may wish to change case */
1293+ sendcmdto_one(&me, CMD_NOTICE, sptr,
1294+ "%C :WELCOME: Cannot change %s message for %s - nothing to change",
1295+ sptr, (flags & WELCOME_LOCAL) ? "local" : "global", name);
1296+ return 0;
1297+ }
1298+ }
1299+ }
1300+
1301+ /* do not insert for last global/local entry and when not set yet */
1302+ if ((flags & WELCOME_INSERT) &&
1303+ ((!WelcomeIsSet(namearray)) || (nameint == WELCOME_MAX_ENTRIES)))
1304+ flags &= ~WELCOME_INSERT;
1305+
1306+ /* unset */
1307+ if (EmptyString(text)) {
1308+ /* clear insert flag,
1309+ * when this flag is set, welcome_unset() assumes it is being called from welcome_insert()
1310+ * and wont propagate the change
1311+ */
1312+ flags &= ~WELCOME_INSERT;
1313+ return welcome_unset(cptr, sptr, nameint, namearray, timestamp, who, flags);
1314+ }
1315+
1316+ /* insert */
1317+ if (flags & WELCOME_INSERT)
1318+ return welcome_insert(cptr, sptr, nameint, namearray, timestamp, who, text, flags);
1319+
1320+ /* new or change */
1321+ return welcome_set(cptr, sptr, nameint, namearray, timestamp, who, text, flags);
1322+}
1323+
1324+
1325+/** Announce a welcome message to local clients.
1326+ * @param[in] name Welcome message to announce.
1327+ */
1328+void
1329+welcome_announce(int name)
1330+{
1331+ struct Client *acptr;
1332+ struct MsgBuf *msgbuf;
1333+ int i;
1334+
1335+ /* valid range, set and not empty */
1336+ assert(WelcomeIsValid(name));
1337+ assert(WelcomeIsSet(name));
1338+ assert(!WelcomeIsEmpty(name));
1339+
1340+ /* build msgbuf */
1341+ msgbuf = msgq_make(0, ":%C %s $%s :[%s] %s", &me, MSG_NOTICE,
1342+ name >= WELCOME_MAX_ENTRIES ? cli_name(&me) : "*",
1343+ name >= WELCOME_MAX_ENTRIES ? cli_name(&me) : feature_str(FEAT_NETWORK),
1344+ WelcomeText(name));
1345+
1346+ /* go over local clients */
1347+ for (i = HighestFd; i > 0; --i) {
1348+
1349+ /* skip unregistered clients, skip servers */
1350+ if (!(acptr = LocalClientArray[i]) || !IsRegistered(acptr) || IsServer(acptr))
1351+ continue;
1352+
1353+ /* send it away */
1354+ send_buffer(acptr, msgbuf, 0);
1355+ }
1356+}
1357+
1358+
1359+/** Send the full list of welcome message to \a cptr.
1360+ * @param[in] cptr Local server to send welcomes to.
1361+ */
1362+void
1363+welcome_burst(struct Client *cptr)
1364+{
1365+ int name;
1366+
1367+ assert(NULL != cptr);
1368+
1369+ /* loop over global entries - 0 to max - 1*/
1370+ for (name = 0; name <= WELCOME_MAX_ENTRIES - 1; name++) {
1371+ if (WelcomeIsSet(name))
1372+ sendcmdto_one(&me, CMD_WELCOME, cptr, "* %d %Tu %s :%s",
1373+ name + 1, WelcomeTS(name), WelcomeWho(name), WelcomeText(name));
1374+ }
1375+}
1376+
1377+
1378+/** List welcome messages.
1379+ * @param[in] sptr Client requesting the listing.
1380+ * @param[in] connect When non zero do not report no welcome is set
1381+ * @return Zero.
1382+ */
1383+int
1384+welcome_list(struct Client *sptr, int connect)
1385+{
1386+ int found = 0, local = 0, name;
1387+
1388+ assert(NULL != sptr);
1389+
1390+ /* loop over all entries - range 0 to 2 * max - 1 */
1391+ for (name = 0; name <= 2 * WELCOME_MAX_ENTRIES - 1; name++) {
1392+
1393+ /* local entries now */
1394+ if (name == WELCOME_MAX_ENTRIES)
1395+ local = 1;
1396+
1397+ /* not set or empty - skip */
1398+ if (!WelcomeIsSet(name) || WelcomeIsEmpty(name))
1399+ continue;
1400+
1401+ /* got one */
1402+ found++;
1403+ sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :[%s] %s",
1404+ sptr, local ? cli_name(&me) : feature_str(FEAT_NETWORK), WelcomeText(name));
1405+ }
1406+
1407+ /* nothing set */
1408+ if (!found && !connect)
1409+ sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :No welcome message set.", sptr);
1410+
1411+ return 0;
1412+}
1413+
1414+
1415+/** Statistics callback to list Welcome messages.
1416+ * @param[in] sptr Client requesting statistics.
1417+ * @param[in] sd Stats descriptor for request (ignored).
1418+ * @param[in] param Extra parameter from user (ignored).
1419+ */
1420+void
1421+welcome_stats(struct Client *sptr, const struct StatDesc *sd, char *param)
1422+{
1423+ int name, local = 0;
1424+
1425+ assert(NULL != sptr);
1426+
1427+ /* loop over all entries - range 0 to 2 * max - 1*/
1428+ for (name = 0; name <= 2 * WELCOME_MAX_ENTRIES - 1; name++) {
1429+
1430+ /* local entries now */
1431+ if (name == WELCOME_MAX_ENTRIES)
1432+ local = 1;
1433+
1434+ /* not set */
1435+ if (!WelcomeIsSet(name))
1436+ continue;
1437+
1438+ /* send it */
1439+ send_reply(sptr, RPL_STATSWELCOME,
1440+ local ? name + 1 - WELCOME_MAX_ENTRIES : name + 1,
1441+ local ? cli_name(&me) : "*",
1442+ WelcomeWho(name), WelcomeTS(name),
1443+ WelcomeIsEmpty(name) ? "<Empty>" : WelcomeText(name));
1444+ }
1445+}
1446+
1447+
1448+/** Count welcome messages and memory used by them.
1449+ * @param[out] we_size Receives total number of bytes allocated for welcomes.
1450+ * @return Number of welcome messages currently allocated.
1451+ */
1452+int
1453+welcome_memory_count(size_t *we_size)
1454+{
1455+ int name;
1456+ unsigned int we = 0;
1457+
1458+ for (name = 0; name <= 2 * WELCOME_MAX_ENTRIES - 1; name++) {
1459+ if (!WelcomeIsSet(name))
1460+ continue;
1461+ we++;
1462+ *we_size += WelcomeText(name) ? (strlen(WelcomeText(name)) + 1) : 0;
1463+ *we_size += WelcomeWho(name) ? (strlen(WelcomeWho(name)) + 1) : 0;
1464+ }
1465+ return we;
1466+}