]> jfr.im git - irc/quakenet/snircd-patchqueue.git/blame_incremental - welcome.patch
welcome: add assertion checks in welcome_announce
[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 a9b437e961ec 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 a9b437e961ec 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 a9b437e961ec 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 a9b437e961ec 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 a9b437e961ec 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 a9b437e961ec include/welcome.h
173--- /dev/null
174+++ b/include/welcome.h
175@@ -0,0 +1,78 @@
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+/* Maximum timestamp drift in seconds allowed ahead of our idea of nettime
214+ * before we throw a warning to ops
215+ */
216+#define WELCOME_MAX_DRIFT 600
217+
218+/* Test if a welcome entry is in a valid range */
219+#define WelcomeIsValid(x) ((x) >= 0 && (x) <= 2 * WELCOME_MAX_ENTRIES - 1)
220+/* Test if a welcome entry is set */
221+#define WelcomeIsSet(x) (WelcomeArray[(x)].timestamp > 0)
222+/* Test if a welcome entry is empty */
223+#define WelcomeIsEmpty(x) (*WelcomeArray[(x)].text == 0)
224+
225+/* Get welcome timestamp */
226+#define WelcomeTS(x) (WelcomeArray[(x)].timestamp)
227+/* Get welcome text */
228+#define WelcomeText(x) (WelcomeArray[(x)].text)
229+/* Get welcome who info */
230+#define WelcomeWho(x) (WelcomeArray[(x)].who)
231+
232+
233+/* Describes a Welcome message entry. */
234+struct Welcome {
235+ time_t timestamp; /**< Timestamp of the welcome */
236+ char text[WELCOMELEN + 1]; /**< Message */
237+ char who[ACCOUNTLEN + 1]; /**< Who set it */
238+};
239+
240+/** Welcome type flags */
241+#define WELCOME_LOCAL 0x01 /**< welcome is local */
242+/** Welcome action flags */
243+#define WELCOME_ANNOUNCE 0x02 /**< announce change to users */
244+#define WELCOME_INSERT 0x04 /**< insert welcome message, move down all others one place */
245+
246+extern int welcome_do(struct Client *cptr, struct Client *sptr, char *name,
247+ time_t timestamp, char *who, char *text, unsigned int flags);
248+extern void welcome_announce(int name);
249+extern void welcome_burst(struct Client *cptr);
250+extern int welcome_list(struct Client *sptr, int connect);
251+extern void welcome_stats(struct Client *sptr, const struct StatDesc *sd, char *param);
252+
253+#endif /* INCLUDED_welcome_h */
254diff -r a9b437e961ec ircd/Makefile.in
255--- a/ircd/Makefile.in
256+++ b/ircd/Makefile.in
257@@ -186,6 +186,7 @@
258 m_wallops.c \
259 m_wallusers.c \
260 m_wallvoices.c \
261+ m_welcome.c \
262 m_who.c \
263 m_whois.c \
264 m_whowas.c \
265@@ -215,6 +216,7 @@
266 send.c \
267 uping.c \
268 userload.c \
269+ welcome.c \
270 whocmds.c \
271 whowas.c \
272 y.tab.c
273@@ -1161,6 +1163,11 @@
274 ../include/ircd_reply.h ../include/ircd_string.h \
275 ../include/ircd_chattr.h ../include/msg.h ../include/numeric.h \
276 ../include/numnicks.h ../include/s_user.h ../include/send.h
277+m_welcome.o: m_welcome.c ../config.h ../include/channel.h \
278+ ../include/client.h ../include/hash.h ../include/ircd.h ../include/ircd_log.h \
279+ ../include/ircd_reply.h ../include/ircd_string.h ../include/msg.h \
280+ ../include/numeric.h ../include/numnicks.h ../include/s_user.h \
281+ ../include/send.h ../include/welcome.h
282 m_who.o: m_who.c ../config.h ../include/channel.h ../include/ircd_defs.h \
283 ../include/res.h ../config.h ../include/client.h ../include/dbuf.h \
284 ../include/msgq.h ../include/ircd_events.h ../include/ircd_handler.h \
285@@ -1422,6 +1429,13 @@
286 ../include/numnicks.h ../include/querycmds.h ../include/ircd_features.h \
287 ../include/s_misc.h ../include/s_stats.h ../include/send.h \
288 ../include/struct.h ../include/sys.h
289+welcome.o: welcome.c ../config.h ../include/client.h \
290+ ../include/hash.h ../include/ircd.h ../include/ircd_alloc.h \
291+ ../include/ircd_features.h ../include/ircd_log.h ../include/ircd_reply.h \
292+ ../include/match.h ../include/msg.h ../include/numeric.h \
293+ ../include/numnicks.h ../include/s_debug.h ../include/s_bsd.h \
294+ ../include/s_misc.h ../include/send.h ../include/struct.h \
295+ ../include/sys.h ../include/welcome.h
296 whocmds.o: whocmds.c ../config.h ../include/whocmds.h \
297 ../include/channel.h ../include/ircd_defs.h ../include/res.h \
298 ../config.h ../include/client.h ../include/dbuf.h ../include/msgq.h \
299diff -r a9b437e961ec ircd/client.c
300--- a/ircd/client.c
301+++ b/ircd/client.c
302@@ -177,6 +177,7 @@
303 FlagSet(&privs_local, PRIV_WHOX);
304 FlagSet(&privs_local, PRIV_DISPLAY);
305 FlagSet(&privs_local, PRIV_FORCE_LOCAL_OPMODE);
306+ FlagSet(&privs_local, PRIV_LOCAL_WELCOME);
307
308 privs_defaults_set = 1;
309 }
310@@ -223,6 +224,7 @@
311 ClrPriv(client, PRIV_JUPE);
312 ClrPriv(client, PRIV_OPMODE);
313 ClrPriv(client, PRIV_BADCHAN);
314+ ClrPriv(client, PRIV_WELCOME);
315 }
316 }
317
318@@ -244,7 +246,7 @@
319 P(CHANSERV), P(XTRA_OPER), P(NOIDLE), P(FREEFORM),
320 P(PARANOID), P(CHECK), P(WALL), P(CLOSE),
321 P(ROUTE), P(ROUTEINFO), P(SERVERINFO), P(CHANNEL_PRIVACY),
322- P(USER_PRIVACY),
323+ P(USER_PRIVACY), P(WELCOME), P(LOCAL_WELCOME),
324 #undef P
325 { 0, 0 }
326 };
327diff -r a9b437e961ec ircd/ircd_features.c
328--- a/ircd/ircd_features.c
329+++ b/ircd/ircd_features.c
330@@ -366,6 +366,7 @@
331 F_I(IRCD_RES_TIMEOUT, 0, 4, 0),
332 F_I(AUTH_TIMEOUT, 0, 9, 0),
333 F_B(ANNOUNCE_INVITES, 0, 0, 0),
334+ F_B(WELCOME, 0, 1, 0),
335
336 /* features that affect all operators */
337 F_B(EXTENDED_CHECKCMD, 0, 0, 0),
338@@ -407,6 +408,7 @@
339 F_B(HIS_STATS_u, 0, 1, 0),
340 F_B(HIS_STATS_U, 0, 1, 0),
341 F_B(HIS_STATS_v, 0, 1, 0),
342+ F_B(HIS_STATS_W, 0, 1, 0),
343 F_B(HIS_STATS_w, 0, 1, 0),
344 F_B(HIS_STATS_x, 0, 1, 0),
345 F_B(HIS_STATS_y, 0, 1, 0),
346diff -r a9b437e961ec ircd/ircd_lexer.l
347--- a/ircd/ircd_lexer.l
348+++ b/ircd/ircd_lexer.l
349@@ -166,6 +166,8 @@
350 { "serverinfo", TPRIV_SERVERINFO },
351 { "user_privacy", TPRIV_USER_PRIVACY },
352 { "channel_privacy", TPRIV_CHANNEL_PRIVACY },
353+ { "local_welcome", TPRIV_LOCAL_WELCOME },
354+ { "welcome", TPRIV_WELCOME },
355 { NULL, 0 }
356 };
357 static int ntokens;
358diff -r a9b437e961ec ircd/ircd_parser.y
359--- a/ircd/ircd_parser.y
360+++ b/ircd/ircd_parser.y
361@@ -189,6 +189,7 @@
362 %token TPRIV_CHANSERV TPRIV_XTRA_OPER TPRIV_NOIDLE TPRIV_FREEFORM TPRIV_PARANOID
363 %token TPRIV_CHECK TPRIV_WALL TPRIV_CLOSE TPRIV_ROUTE TPRIV_ROUTEINFO TPRIV_SERVERINFO
364 %token TPRIV_CHANNEL_PRIVACY TPRIV_USER_PRIVACY TPRIV_LIST_CHAN
365+%token TPRIV_LOCAL_WELCOME TPRIV_WELCOME
366 /* and some types... */
367 %type <num> sizespec
368 %type <num> timespec timefactor factoredtimes factoredtime
369@@ -703,6 +704,8 @@
370 TPRIV_SERVERINFO { $$ = PRIV_SERVERINFO ; } |
371 TPRIV_CHANNEL_PRIVACY { $$ = PRIV_CHANNEL_PRIVACY ; } |
372 TPRIV_USER_PRIVACY { $$ = PRIV_USER_PRIVACY ; } |
373+ TPRIV_LOCAL_WELCOME { $$ = PRIV_LOCAL_WELCOME; } |
374+ TPRIV_WELCOME { $$ = PRIV_WELCOME; } |
375 TPRIV_PARANOID { $$ = PRIV_PARANOID; } ;
376 yesorno: YES { $$ = 1; } | NO { $$ = 0; };
377
378diff -r a9b437e961ec ircd/m_welcome.c
379--- /dev/null
380+++ b/ircd/m_welcome.c
381@@ -0,0 +1,296 @@
382+/*
383+ * IRC - Internet Relay Chat, ircd/m_welcome.c
384+ * Copyright (C) 1990 Jarkko Oikarinen and
385+ * University of Oulu, Computing Center
386+ *
387+ * See file AUTHORS in IRC package for additional names of
388+ * the programmers.
389+ *
390+ * This program is free software; you can redistribute it and/or modify
391+ * it under the terms of the GNU General Public License as published by
392+ * the Free Software Foundation; either version 1, or (at your option)
393+ * any later version.
394+ *
395+ * This program is distributed in the hope that it will be useful,
396+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
397+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
398+ * GNU General Public License for more details.
399+ *
400+ * You should have received a copy of the GNU General Public License
401+ * along with this program; if not, write to the Free Software
402+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
403+ *
404+ */
405+
406+/*
407+ * m_functions execute protocol messages on this server:
408+ *
409+ * cptr is always NON-NULL, pointing to a *LOCAL* client
410+ * structure (with an open socket connected!). This
411+ * identifies the physical socket where the message
412+ * originated (or which caused the m_function to be
413+ * executed--some m_functions may call others...).
414+ *
415+ * sptr is the source of the message, defined by the
416+ * prefix part of the message if present. If not
417+ * or prefix not found, then sptr==cptr.
418+ *
419+ * (!IsServer(cptr)) => (cptr == sptr), because
420+ * prefixes are taken *only* from servers...
421+ *
422+ * (IsServer(cptr))
423+ * (sptr == cptr) => the message didn't
424+ * have the prefix.
425+ *
426+ * (sptr != cptr && IsServer(sptr) means
427+ * the prefix specified servername. (?)
428+ *
429+ * (sptr != cptr && !IsServer(sptr) means
430+ * that message originated from a remote
431+ * user (not local).
432+ *
433+ * combining
434+ *
435+ * (!IsServer(sptr)) means that, sptr can safely
436+ * taken as defining the target structure of the
437+ * message in this server.
438+ *
439+ * *Always* true (if 'parse' and others are working correct):
440+ *
441+ * 1) sptr->from == cptr (note: cptr->from == cptr)
442+ *
443+ * 2) MyConnect(sptr) <=> sptr == cptr (e.g. sptr
444+ * *cannot* be a local connection, unless it's
445+ * actually cptr!). [MyConnect(x) should probably
446+ * be defined as (x == x->from) --msa ]
447+ *
448+ * parc number of variable parameter strings (if zero,
449+ * parv is allowed to be NULL)
450+ *
451+ * parv a NULL terminated list of parameter pointers,
452+ *
453+ * parv[0], sender (prefix string), if not present
454+ * this points to an empty string.
455+ * parv[1]...parv[parc-1]
456+ * pointers to additional parameters
457+ * parv[parc] == NULL, *always*
458+ *
459+ * note: it is guaranteed that parv[0]..parv[parc-1] are all
460+ * non-NULL pointers.
461+ */
462+#include "config.h"
463+
464+#include "channel.h"
465+#include "client.h"
466+#include "hash.h"
467+#include "ircd.h"
468+#include "ircd_features.h"
469+#include "ircd_log.h"
470+#include "ircd_reply.h"
471+#include "ircd_snprintf.h"
472+#include "ircd_string.h"
473+#include "msg.h"
474+#include "numeric.h"
475+#include "numnicks.h"
476+#include "s_user.h"
477+#include "send.h"
478+#include "welcome.h"
479+
480+/* #include <assert.h> -- Now using assert in ircd_log.h */
481+
482+/*
483+ * m_welcome - local generic message handler
484+ *
485+ * parv[0] = Send prefix
486+ * parv[1] = [remote server to query]
487+ */
488+int m_welcome(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
489+{
490+ /* feature disabled */
491+ if (!feature_bool(FEAT_WELCOME))
492+ return send_reply(sptr, ERR_DISABLED, "WELCOME");
493+
494+ /* only opers can set the welcome messages */
495+ if (parc > 2)
496+ return send_reply(sptr, ERR_NOPRIVILEGES);
497+
498+ /* remote listing request, see if it is for me or a remote server
499+ * check FEAT_HIS_REMOTE to decide if an ordinary user can do this
500+ */
501+ if ((parc > 1) && (hunt_server_cmd(sptr, CMD_WELCOME, cptr, feature_int(FEAT_HIS_REMOTE),
502+ "%C", 1, parc, parv) != HUNTED_ISME))
503+ return 0;
504+
505+ /* local listing */
506+ return welcome_list(sptr, 0);
507+}
508+
509+
510+/*
511+ * mo_welcome - oper message handler
512+ *
513+ * listing:
514+ * parv[0] = Send prefix
515+ *
516+ * remote listing:
517+ * parv[0] = Send prefix
518+ * parv[1] = Target
519+ *
520+ * set global or on remote server:
521+ * parv[0] = Send prefix
522+ * parv[1] = Target: server or * for global (or left out for this server)
523+ * parv[2] = Name
524+ * parv[3] = Text
525+ */
526+int mo_welcome(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
527+{
528+ char *target, *name, *who, *text, pattern[BUFSIZE];
529+ time_t timestamp;
530+ unsigned int flags = 0;
531+ int local = 0;
532+
533+ /* feature disabled */
534+ if (!feature_bool(FEAT_WELCOME))
535+ return send_reply(sptr, ERR_DISABLED, "WELCOME");
536+
537+ /* TODO: move feature check here? */
538+ /* remote listing request, see if it is for me or a remote server */
539+ if ((parc == 2) && (hunt_server_cmd(sptr, CMD_WELCOME, cptr, 0, "%C", 1, parc, parv) != HUNTED_ISME))
540+ return 0;
541+
542+ /* local listing */
543+ if (parc <= 2)
544+ return welcome_list(sptr, 0);
545+
546+ /* check PRIVS */
547+ /* local - need PRIV LOCAL_WELCOME or WELCOME */
548+ if (parc == 3 && !HasPriv(sptr,PRIV_LOCAL_WELCOME) && !HasPriv(sptr,PRIV_WELCOME))
549+ return send_reply(sptr, ERR_NOPRIVILEGES);
550+
551+ /* global or remote - need PRIV WELCOME */
552+ if (parc >= 4 && !HasPriv(sptr,PRIV_WELCOME))
553+ return send_reply(sptr, ERR_NOPRIVILEGES);
554+
555+ /* set the parameters */
556+
557+ /* target not given, only name - setting local welcome */
558+ if (parc < 4) {
559+ local++;
560+ target = cli_name(&me);
561+ name = parv[1];
562+ flags |= WELCOME_LOCAL;
563+
564+ /* target and name given */
565+ } else {
566+ target = parv[1];
567+ name = parv[2];
568+ }
569+ timestamp = TStime();
570+ who = cli_user(sptr)->opername;
571+ text = parv[parc - 1];
572+
573+ /* target is not global */
574+ if (!(target[0] == '*' && target[1] == '\0') && !local) {
575+
576+ /* build a pattern for hunt_server_cmd since we do not have all we need in parv */
577+ ircd_snprintf(0, pattern, sizeof(pattern), "%s %s %Tu %s :%s", "%C", name, timestamp, who, text);
578+ if (hunt_server_cmd(sptr, CMD_WELCOME, cptr, 0, pattern, 1, 2, parv) != HUNTED_ISME)
579+ return 0;
580+
581+ /* else it is a local welcome, for me */
582+ flags |= WELCOME_LOCAL;
583+ }
584+
585+ /* TODO: disallow global announcement from oper?
586+ * as PRIVMSG/NOTICE to $* is not allowed either by the ircd
587+ * when PRIV for that is added, use that here? PRIV_BROADCAST or something
588+ *
589+ * change prefix to $ ?
590+ */
591+ /* check for anounce prefix */
592+ if (*name == '!') {
593+ name++;
594+ flags |= WELCOME_ANNOUNCE;
595+ }
596+
597+ /* check for insert prefix */
598+ if (*name == '+') {
599+ name++;
600+ flags |= WELCOME_INSERT;
601+ }
602+
603+ /* and do it */
604+ return welcome_do(cptr, sptr, name, timestamp, who, text, flags);
605+}
606+
607+
608+/*
609+ * ms_welcome - server message handler
610+ *
611+ * parv[0] = Send prefix
612+ * parv[1] = Target: server numeric or * for global
613+ * parv[2] = Name
614+ * parv[3] = Timestamp
615+ * parv[4] = Who
616+ * parv[5] = Text
617+ */
618+int ms_welcome(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
619+{
620+ char *target, *name, *who, *text;
621+ time_t timestamp;
622+ unsigned int flags = 0;
623+
624+ /* not enough - complain */
625+ if (parc < 2) {
626+ protocol_violation(sptr, "Too few parameters for WELCOME (got %d - need 2)", parc);
627+ return need_more_params(sptr, "WELCOME");
628+ }
629+
630+ /* remote listing request, see if it is for me or a remote server */
631+ if (parc == 2) {
632+ if (IsServer(sptr))
633+ return protocol_violation(cptr, "WELCOME listing request from server %C", sptr);
634+ if (hunt_server_cmd(sptr, CMD_WELCOME, cptr, 0, "%C", 1, parc, parv) != HUNTED_ISME)
635+ return 0;
636+ return welcome_list(sptr, 0);
637+ }
638+
639+ /* we need at least 6 parameters to continue - complain */
640+ if (parc < 6) {
641+ protocol_violation(sptr, "Too few parameters for WELCOME (got %d - need 6)", parc);
642+ return need_more_params(sptr, "WELCOME");
643+ }
644+
645+ /* set the parameters */
646+ target = parv[1];
647+ name = parv[2];
648+ timestamp = atoi(parv[3]);
649+ who = parv[4];
650+ text = parv[parc - 1]; /* parse reason as last parameter */
651+
652+ /* target is not global */
653+ if (!(target[0] == '*' && target[1] == '\0')) {
654+
655+ /* not for me, and forward it */
656+ if (hunt_server_cmd(sptr, CMD_WELCOME, cptr, 0, "%C %s %s %s :%s", 1, parc, parv) != HUNTED_ISME)
657+ return 0;
658+
659+ /* local welcome for me */
660+ flags |= WELCOME_LOCAL;
661+ }
662+
663+ /* check for anounce prefix */
664+ if (*name == '!') {
665+ name++;
666+ flags |= WELCOME_ANNOUNCE;
667+ }
668+
669+ /* check for insert prefix */
670+ if (*name == '+') {
671+ name++;
672+ flags |= WELCOME_INSERT;
673+ }
674+
675+ /* and do it */
676+ return welcome_do(cptr, sptr, name, timestamp, who, text, flags);
677+}
678diff -r a9b437e961ec ircd/parse.c
679--- a/ircd/parse.c
680+++ b/ircd/parse.c
681@@ -661,6 +661,15 @@
682 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
683 { m_unregistered, m_not_oper, ms_check, mo_check, m_ignore }
684 },
685+
686+ /* add command for WELCOME */
687+ {
688+ MSG_WELCOME,
689+ TOK_WELCOME,
690+ 0, MAXPARA, MFLG_SLOW, 0, NULL,
691+ /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
692+ { m_unregistered, m_welcome, ms_welcome, mo_welcome, m_ignore }
693+ },
694
695 /* This command is an alias for QUIT during the unregistered part of
696 * of the server. This is because someone jumping via a broken web
697diff -r a9b437e961ec ircd/s_err.c
698--- a/ircd/s_err.c
699+++ b/ircd/s_err.c
700@@ -486,7 +486,7 @@
701 /* 226 */
702 { RPL_STATSALINE, "%s", "226" },
703 /* 227 */
704- { 0 },
705+ { RPL_STATSWELCOME, "W %d %s %s %Tu :%s", "227" },
706 /* 228 */
707 { RPL_STATSQLINE, "Q %s :%s", "228" },
708 /* 229 */
709@@ -1050,7 +1050,7 @@
710 /* 508 */
711 { 0 },
712 /* 509 */
713- { 0 },
714+ { ERR_NOSUCHWELCOME, "%s :No such welcome", "509" },
715 /* 510 */
716 { 0 },
717 /* 511 */
718diff -r a9b437e961ec ircd/s_serv.c
719--- a/ircd/s_serv.c
720+++ b/ircd/s_serv.c
721@@ -57,6 +57,7 @@
722 #include "struct.h"
723 #include "sys.h"
724 #include "userload.h"
725+#include "welcome.h"
726
727 /* #include <assert.h> -- Now using assert in ircd_log.h */
728 #include <stdlib.h>
729@@ -196,6 +197,7 @@
730 */
731 gline_burst(cptr);
732 jupe_burst(cptr);
733+ welcome_burst(cptr);
734
735 /*
736 * Pass on my client information to the new server
737diff -r a9b437e961ec ircd/s_stats.c
738--- a/ircd/s_stats.c
739+++ b/ircd/s_stats.c
740@@ -54,6 +54,7 @@
741 #include "send.h"
742 #include "struct.h"
743 #include "userload.h"
744+#include "welcome.h"
745
746 #include <stdio.h>
747 #include <stdlib.h>
748@@ -654,9 +655,12 @@
749 { 'V', "vserversmach", (STAT_FLAG_OPERFEAT | STAT_FLAG_VARPARAM | STAT_FLAG_CASESENS), FEAT_HIS_STATS_v,
750 stats_servers_verbose, 0,
751 "Verbose server information." },
752- { 'w', "userload", STAT_FLAG_OPERFEAT, FEAT_HIS_STATS_w,
753+ { 'w', "userload", STAT_FLAG_OPERFEAT | STAT_FLAG_CASESENS, FEAT_HIS_STATS_w,
754 calc_load, 0,
755 "Userload statistics." },
756+ { 'W', "welcome", STAT_FLAG_OPERFEAT | STAT_FLAG_CASESENS, FEAT_HIS_STATS_W,
757+ welcome_stats, 0,
758+ "Welcome messages." },
759 { 'x', "memusage", STAT_FLAG_OPERFEAT, FEAT_HIS_STATS_x,
760 stats_meminfo, 0,
761 "List usage information." },
762diff -r a9b437e961ec ircd/s_user.c
763--- a/ircd/s_user.c
764+++ b/ircd/s_user.c
765@@ -63,6 +63,7 @@
766 #include "userload.h"
767 #include "version.h"
768 #include "whowas.h"
769+#include "welcome.h"
770
771 #include "handlers.h" /* m_motd and m_lusers */
772
773@@ -410,6 +411,9 @@
774 cli_info(sptr), NumNick(cptr) /* two %s's */);
775
776 IPcheck_connect_succeeded(sptr);
777+
778+ if (feature_bool(FEAT_WELCOME))
779+ welcome_list(sptr, 1);
780 }
781 else {
782 struct Client *acptr = user->server;
783diff -r a9b437e961ec ircd/welcome.c
784--- /dev/null
785+++ b/ircd/welcome.c
786@@ -0,0 +1,594 @@
787+/*
788+ * IRC - Internet Relay Chat, ircd/welcome.c
789+ * Copyright (C) 1990 Jarkko Oikarinen and
790+ * University of Oulu, Finland
791+ * Copyright (C) 2000 Kevin L. Mitchell <klmitch@mit.edu>
792+ *
793+ * This program is free software; you can redistribute it and/or modify
794+ * it under the terms of the GNU General Public License as published by
795+ * the Free Software Foundation; either version 1, or (at your option)
796+ * any later version.
797+ *
798+ * This program is distributed in the hope that it will be useful,
799+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
800+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
801+ * GNU General Public License for more details.
802+ *
803+ * You should have received a copy of the GNU General Public License
804+ * along with this program; if not, write to the Free Software
805+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
806+ */
807+/** @file
808+ * @brief Implementation of welcome message handling functions.
809+ */
810+#include "config.h"
811+
812+#include "client.h"
813+#include "hash.h"
814+#include "ircd.h"
815+#include "ircd_alloc.h"
816+#include "ircd_features.h"
817+#include "ircd_log.h"
818+#include "ircd_reply.h"
819+#include "ircd_string.h"
820+#include "match.h"
821+#include "msg.h"
822+#include "numeric.h"
823+#include "numnicks.h"
824+#include "s_bsd.h"
825+#include "s_debug.h"
826+#include "s_misc.h"
827+#include "send.h"
828+#include "struct.h"
829+#include "sys.h" /* FALSE bleah */
830+#include "welcome.h"
831+
832+/* #include <assert.h> -- Now using assert in ircd_log.h */
833+#include <string.h>
834+
835+
836+/** List of welcome messages - first MAX for global, second MAX for local */
837+static struct Welcome WelcomeArray[WELCOME_MAX_ENTRIES * 2] = { { 0 } };
838+
839+
840+/** Allocate a new welcome with the given parameters.
841+ * @param[in] name Name of the welcome message.
842+ * @param[in] text The welcome message.
843+ * @param[in] who Who set it.
844+ * @param[in] timestamp When it was set.
845+ * @return name Array number of the welcome set.
846+ */
847+static int
848+welcome_make(int name, char *text, char *who, time_t timestamp)
849+{
850+ /* valid range */
851+ assert(WelcomeIsValid(name));
852+
853+ /* store it */
854+ ircd_strncpy(WelcomeArray[name].text, text, WELCOMELEN);
855+ ircd_strncpy(WelcomeArray[name].who, who, ACCOUNTLEN);
856+ WelcomeArray[name].timestamp = timestamp;
857+
858+ return name;
859+}
860+
861+
862+/** Propagate a welcome message.
863+ * @param[in] cptr Local client that sent us the welcome.
864+ * @param[in] sptr Originator of the welcome.
865+ * @param[in] nameint Name of the message.
866+ * @param[in] timestamp Timestamp of when the message was set.
867+ * @param[in] who Who set this message.
868+ * @param[in] text The welcome message.
869+ * @param[in] flags Flags to set on welcome.
870+ * @return Zero
871+ */
872+int
873+welcome_propagate(struct Client *cptr, struct Client *sptr, int nameint,
874+ time_t timestamp, char *who, char *text, unsigned int flags)
875+{
876+ /* must be global */
877+ assert(!(flags & WELCOME_LOCAL));
878+
879+ sendcmdto_serv_butone(sptr, CMD_WELCOME, cptr, "* %s%s%d %Tu %s :%s",
880+ (flags & WELCOME_ANNOUNCE) ? "!" : "", (flags & WELCOME_INSERT) ? "+" : "",
881+ nameint, timestamp, who, text);
882+
883+ return 0;
884+}
885+
886+
887+/** Log a welcome message.
888+ * @param[in] sptr Originator of the welcome.
889+ * @param[in] msg The message to show.
890+ * @param[in] flags Flags to set on welcome.
891+ * @return Zero
892+ */
893+int
894+welcome_log(struct Client *sptr, char *msg, unsigned int flags)
895+{
896+
897+ /* inform ops */
898+ sendto_opmask_butone(0, SNO_OLDSNO, "%s %s",
899+ (feature_bool(FEAT_HIS_SNOTICES) || IsServer(sptr)) ?
900+ get_client_name_and_opername(sptr) : cli_name((cli_user(sptr))->server), msg);
901+
902+ /* log it */
903+ log_write(LS_NETWORK, L_INFO, LOG_NOSNOTICE, "%s %s", get_client_name_and_opername(sptr), msg);
904+
905+ /* welcome by remote user, inform oper of success */
906+ if ((flags & WELCOME_LOCAL) && IsUser(sptr) && !MyUser(sptr)) {
907+ sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :%s %s",
908+ sptr, get_client_name_and_opername(sptr), msg);
909+
910+ /* TODO: wallops all local changes, by both local and remote opers? */
911+ /* tell all opers about the local message being set remotely */
912+ sendwallto_group_butone(&me, WALL_WALLOPS, 0, "%s %s", get_client_name_and_opername(sptr), msg);
913+ }
914+
915+ return 0;
916+}
917+
918+
919+/** Set a welcome message.
920+ * @param[in] cptr Local client that sent us the welcome.
921+ * @param[in] sptr Originator of the welcome.
922+ * @param[in] nameint Name of the message.
923+ * @param[in] namearray Array entry.
924+ * @param[in] timestamp Timestamp of when the message was set.
925+ * @param[in] who Who set this message.
926+ * @param[in] text The message.
927+ * @param[in] flags Flags to set on welcome.
928+ * @return Zero
929+ */
930+int
931+welcome_set(struct Client *cptr, struct Client *sptr, int nameint,
932+ int namearray, time_t timestamp, char *who, char *text, unsigned int flags)
933+{
934+ char msg[BUFSIZE]; /* msg for logging */
935+ int new = 0;
936+
937+ /* debug */
938+ Debug((DEBUG_DEBUG, "welcome_set(\"%s\", \"%s\", %d, %d, %Tu, \"%s\", \"%s\", 0x%04x)",
939+ cli_name(cptr), cli_name(sptr), nameint, namearray, timestamp, who, text, flags));
940+
941+ /* not set */
942+ if (WelcomeIsEmpty(namearray))
943+ new = 1;
944+
945+ /* update */
946+ welcome_make(namearray, text, who, timestamp);
947+
948+ /* create msg for log */
949+ ircd_snprintf(0, msg, 0, "%s%s%s WELCOME %d \"%s\" %s [%Tu]",
950+ new ? "setting" : "changing",
951+ (flags & WELCOME_ANNOUNCE) ? " and announcing " : " ",
952+ (flags & WELCOME_LOCAL) ? "local" : "global",
953+ nameint, text, who, timestamp);
954+
955+ /* log it */
956+ welcome_log(sptr, msg, flags);
957+
958+ /* propagate it */
959+ if (!(flags & WELCOME_LOCAL))
960+ welcome_propagate(cptr, sptr, nameint, timestamp, who, text, flags);
961+
962+ /* announce it */
963+ if (flags & WELCOME_ANNOUNCE)
964+ welcome_announce(namearray);
965+
966+ return 0;
967+}
968+
969+
970+/** Unset a welcome message.
971+ * @param[in] cptr Local client that sent us the welcome.
972+ * @param[in] sptr Originator of the welcome.
973+ * @param[in] nameint Name of the message.
974+ * @param[in] namearray Array entry.
975+ * @param[in] timestamp Timestamp of when the message was set.
976+ * @param[in] who Who set this message.
977+ * @param[in] flags Flags to set on welcome.
978+ * @return Zero
979+ */
980+int
981+welcome_unset(struct Client *cptr, struct Client *sptr, int nameint,
982+ int namearray, time_t timestamp, char *who, unsigned int flags)
983+{
984+ char msg[BUFSIZE]; /* msg for logging */
985+ char text[WELCOMELEN + 1]; /* save old text */
986+ int i; /* loop variable */
987+ int empty = namearray; /* first empty spot in array after namearray */
988+ int end = WELCOME_MAX_ENTRIES -1; /* last element to check in array */
989+
990+ /* debug */
991+ Debug((DEBUG_DEBUG, "welcome_unset(\"%s\", \"%s\", %d, %d, %Tu, \"%s\", 0x%04x)",
992+ cli_name(cptr), cli_name(sptr), nameint, namearray, timestamp, who, flags));
993+
994+ /* save text */
995+ ircd_strncpy(text, WelcomeText(namearray), WELCOMELEN);
996+
997+ /* update */
998+ welcome_make(namearray, "", who, timestamp);
999+
1000+ /* create msg for log */
1001+ ircd_snprintf(0, msg, 0, "unsetting %s WELCOME %d \"%s\" %s [%Tu]",
1002+ (flags & WELCOME_LOCAL) ? "local" : "global", nameint, text, who, timestamp);
1003+
1004+ /* log it */
1005+ welcome_log(sptr, msg, flags);
1006+
1007+ /* propagate it, but not when inserting */
1008+ if (!(flags & (WELCOME_LOCAL|WELCOME_INSERT)))
1009+ welcome_propagate(cptr, sptr, nameint, timestamp, who, "", flags);
1010+
1011+ /* correct end for local offset */
1012+ if (flags & WELCOME_LOCAL)
1013+ end += WELCOME_MAX_ENTRIES;
1014+
1015+ /* move entries up, update timestamp */
1016+ for (i = namearray; i < end; i++)
1017+ welcome_make(i, WelcomeText(i+1), WelcomeWho(i+1), timestamp);
1018+
1019+ /* clear last entry, update timestamp */
1020+ welcome_make(end, "", who, timestamp);
1021+
1022+ return 0;
1023+}
1024+
1025+
1026+/** Insert a welcome message.
1027+ * @param[in] cptr Local client that sent us the welcome.
1028+ * @param[in] sptr Originator of the welcome.
1029+ * @param[in] nameint Name of the message.
1030+ * @param[in] namearray Array entry.
1031+ * @param[in] timestamp Timestamp of when the message was set.
1032+ * @param[in] who Who set this message.
1033+ * @param[in] text The welcome message.
1034+ * @param[in] flags Flags to set on welcome.
1035+ * @return Zero
1036+ */
1037+int
1038+welcome_insert(struct Client *cptr, struct Client *sptr, int nameint,
1039+ int namearray, time_t timestamp, char *who, char *text, unsigned int flags)
1040+{
1041+ char msg[BUFSIZE]; /* msg for logging */
1042+ int i; /* loop variable */
1043+ int empty = -1; /* first empty spot in array after namearray */
1044+ int end = WELCOME_MAX_ENTRIES -1; /* last element to check in array */
1045+ int last = end; /* last welcome message to feed to welcome_unset */
1046+
1047+ /* debug */
1048+ Debug((DEBUG_DEBUG, "welcome_insert(\"%s\", \"%s\", %d, %d, %Tu, \"%s\", \"%s\", 0x%04x)",
1049+ cli_name(cptr), cli_name(sptr), nameint, namearray, timestamp, who, text, flags));
1050+
1051+ /* correct end for local offset */
1052+ if (flags & WELCOME_LOCAL)
1053+ end += WELCOME_MAX_ENTRIES;
1054+
1055+ /* find first empty spot */
1056+ for (i = namearray; i <= end; i++) {
1057+ if (WelcomeIsEmpty(i)) {
1058+ empty = i;
1059+ break;
1060+ }
1061+ }
1062+
1063+ /* no empty spot, need to unset last */
1064+ if (empty == -1) {
1065+ welcome_unset(cptr, sptr, end, namearray, timestamp, who, flags);
1066+ empty = end;
1067+ }
1068+
1069+ /* move entries down, update timestamp */
1070+ for (i = empty; i > namearray; i--)
1071+ welcome_make(i, WelcomeText(i-1), WelcomeWho(i-1), timestamp);
1072+
1073+ /* correct empty for local offset */
1074+ if (flags & WELCOME_LOCAL)
1075+ empty -= WELCOME_MAX_ENTRIES;
1076+
1077+ /* create msg for log */
1078+ if (nameint == empty)
1079+ ircd_snprintf(0, msg, 0, "moving %s WELCOME message %d one place down",
1080+ (flags & WELCOME_LOCAL) ? "local" : "global", nameint);
1081+ else
1082+ ircd_snprintf(0, msg, 0, "moving %s WELCOME message %d %s %d one place down",
1083+ (flags & WELCOME_LOCAL) ? "local" : "global", nameint, (empty - nameint > 1) ? "to" : "and" , empty);
1084+
1085+ /* log it */
1086+ welcome_log(sptr, msg, flags);
1087+
1088+ /* set it */
1089+ welcome_set(cptr, sptr, nameint, namearray, timestamp, who, text, flags);
1090+
1091+ return 0;
1092+}
1093+
1094+
1095+/** Change a welcome message.
1096+ * @param[in] cptr Local client that sent us the welcome.
1097+ * @param[in] sptr Originator of the welcome.
1098+ * @param[in] name Name of the message.
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_do(struct Client *cptr, struct Client *sptr, char *name,
1107+ time_t timestamp, char *who, char *text, unsigned int flags)
1108+{
1109+ int nameint = atoi(name); /* transform to int */
1110+ int namearray = nameint - 1; /* used to test the array element */
1111+ int max = WELCOME_MAX_ENTRIES; /* max number of entries */
1112+ static time_t rate; /* rate limit snomask message */
1113+
1114+ assert(NULL != cptr);
1115+ assert(NULL != sptr);
1116+ assert(NULL != name);
1117+ assert(NULL != text);
1118+ assert(NULL != who);
1119+
1120+ /* debug */
1121+ Debug((DEBUG_DEBUG, "welcome_do(\"%s\", \"%s\", \"%s\", %Tu, \"%s\", \"%s\", 0x%04x)",
1122+ cli_name(cptr), cli_name(sptr), name, timestamp, who, text, flags));
1123+
1124+ /* if for some reason timestamp is 0, increase it */
1125+ if (timestamp == 0)
1126+ timestamp++;
1127+
1128+ /* name empty after taking off the prefixes? */
1129+ if (EmptyString(name)) {
1130+ if (IsUser(sptr))
1131+ sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :WELCOME: No message number given", sptr);
1132+ else
1133+ protocol_violation(cptr, "WELCOME: No message number given by %C", sptr);
1134+ return 0;
1135+ }
1136+
1137+ /* check name */
1138+ if (!WelcomeIsValid(namearray)) {
1139+ if (IsUser(sptr))
1140+ sendcmdto_one(&me, CMD_NOTICE, sptr,
1141+ "%C :WELCOME: Invalid message number %s - should between 1 and %d",
1142+ sptr, name, max);
1143+ else {
1144+ protocol_violation(cptr, "WELCOME: Invalid message number %s from %C - should be between 1 and %d",
1145+ name, sptr, max);
1146+ /* nameint greater than max, perhaps we are upgrading, but used extra slots too soon?
1147+ * propagate it manually
1148+ * TODO: cant do announce here?
1149+ */
1150+ if (nameint > max && !(flags & WELCOME_LOCAL))
1151+ welcome_propagate(cptr, sptr, nameint, timestamp, who, text, flags);
1152+ }
1153+ return 0;
1154+ }
1155+
1156+ /* source is user, and is myuser or welcome is local, check length of the message */
1157+ if ((IsUser(sptr)) && ((MyUser(sptr)) || (flags & WELCOME_LOCAL)) && (strlen(text) > WELCOMELEN)) {
1158+ sendcmdto_one(&me, CMD_NOTICE, sptr,
1159+ "%C :WELCOME: The message is too long with %d chars - max is %d chars",
1160+ sptr, strlen(text), WELCOMELEN);
1161+ ircd_strncpy(text, text, WELCOMELEN);
1162+ sendcmdto_one(&me, CMD_NOTICE, sptr,
1163+ "%C :WELCOME: Change or truncate the message to: \"%s\"", sptr, text);
1164+ return 0;
1165+ }
1166+
1167+ /* correct namearray for local offset */
1168+ if (flags & WELCOME_LOCAL)
1169+ namearray += WELCOME_MAX_ENTRIES;
1170+
1171+ /* must be true by now */
1172+ assert(WelcomeIsValid(namearray));
1173+
1174+ /* cannot unset welcome that is not set */
1175+ if (!WelcomeIsSet(namearray) && EmptyString(text)) {
1176+
1177+ /* from user, throw error */
1178+ if (IsUser(sptr))
1179+ return send_reply(sptr, ERR_NOSUCHWELCOME, name);
1180+
1181+ /* new local welcome from server, but empty - ignore
1182+ * we do accept a new global welcome message that is empty
1183+ */
1184+ if (flags & WELCOME_LOCAL)
1185+ return 0;
1186+ }
1187+
1188+ /* check if there is something to change */
1189+ /* we got a record for it */
1190+ if (WelcomeIsSet(namearray)) {
1191+
1192+ /* global */
1193+ if (!(flags & WELCOME_LOCAL)) {
1194+
1195+ /* netburst and we got the same or a newer one
1196+ *
1197+ * we only use the timestamp for resolving conflicts in net burst
1198+ * outside of netburst, we simply parse whatever we get
1199+ * this way we will not get stuck with a welcome message set by a server
1200+ * running ahead with the time
1201+ */
1202+ if (IsBurstOrBurstAck(cptr) && timestamp <= WelcomeTS(namearray))
1203+ return 0;
1204+
1205+ /* local welcome - we use our idea of the time */
1206+ } else
1207+ timestamp = TStime();
1208+
1209+ /* compare new message with old message */
1210+ if (ircd_strcmp(text, WelcomeText(namearray)) == 0) {
1211+ if (IsUser(sptr))
1212+ sendcmdto_one(&me, CMD_NOTICE, sptr,
1213+ "%C :WELCOME: Cannot change %s message for %s - nothing to change.",
1214+ sptr, (flags & WELCOME_LOCAL) ? "local" : "global", name);
1215+ return 0;
1216+ }
1217+ }
1218+
1219+ /* do not insert for last global/local entry and when not set yet */
1220+ if ((flags & WELCOME_INSERT) && ((!WelcomeIsSet(namearray)) || (nameint == max)))
1221+ flags &= ~WELCOME_INSERT;
1222+
1223+ /* TODO: rate limited for what? max 10 welcome messages..? */
1224+ /* possible timestamp drift - warn ops */
1225+ if (timestamp - TStime() > WELCOME_MAX_DRIFT) {
1226+ sendto_opmask_butone_ratelimited(0, SNO_NETWORK, &rate,
1227+ "Possible timestamp drift from %C; timestamp in WELCOME message is %is ahead of time",
1228+ IsServer(sptr) ? sptr : cli_user(sptr)->server, timestamp - TStime());
1229+
1230+ /* warn remote oper too */
1231+ if (IsUser(sptr))
1232+ sendcmdto_one(&me, CMD_NOTICE, sptr,
1233+ "%C :Possible timestamp drift from %C; timestamp in WELCOME message is %is ahead of time",
1234+ sptr, cli_user(sptr)->server, timestamp - TStime());
1235+ }
1236+
1237+ /* unset */
1238+ if (EmptyString(text)) {
1239+ /* clear insert flag,
1240+ * when this flag is set, welcome_unset() assumes it is being called from welcome_insert()
1241+ * and wont propagate the change
1242+ */
1243+ flags &= ~WELCOME_INSERT;
1244+ return welcome_unset(cptr, sptr, nameint, namearray, timestamp, who, flags);
1245+ }
1246+
1247+ /* insert */
1248+ if (flags & WELCOME_INSERT)
1249+ return welcome_insert(cptr, sptr, nameint, namearray, timestamp, who, text, flags);
1250+
1251+ /* new or change */
1252+ return welcome_set(cptr, sptr, nameint, namearray, timestamp, who, text, flags);
1253+}
1254+
1255+
1256+/** Announce a welcome message to local clients.
1257+ * @param[in] name Welcome message to announce.
1258+ */
1259+void
1260+welcome_announce(int name)
1261+{
1262+ struct Client *acptr;
1263+ struct MsgBuf *msgbuf;
1264+ int i;
1265+
1266+ /* valid range, set and not empty */
1267+ assert(WelcomeIsValid(name));
1268+ assert(WelcomeIsSet(name));
1269+ assert(!WelcomeIsEmpty(name));
1270+
1271+ /* TODO: target is $* as if it were a global broadcast
1272+ * could make it $servername for local message announcement
1273+ * but the type is shown between [ ] already
1274+ * either [Network] or [servername] - using $* is just shorter.
1275+ */
1276+ /* build msgbuf */
1277+ msgbuf = msgq_make(0, ":%C %s $* :[%s] %s", &me, MSG_NOTICE,
1278+ name >= WELCOME_MAX_ENTRIES ? cli_name(&me) : feature_str(FEAT_NETWORK),
1279+ WelcomeText(name));
1280+
1281+ /* go over local clients */
1282+ for (i = HighestFd; i > 0; --i) {
1283+
1284+ /* skip unregistered clients, skip servers */
1285+ if (!(acptr = LocalClientArray[i]) || !IsRegistered(acptr) || IsServer(acptr))
1286+ continue;
1287+
1288+ /* send it away */
1289+ send_buffer(acptr, msgbuf, 0);
1290+ }
1291+}
1292+
1293+
1294+/** Send the full list of welcome message to \a cptr.
1295+ * @param[in] cptr Local server to send welcomes to.
1296+ */
1297+void
1298+welcome_burst(struct Client *cptr)
1299+{
1300+ int name;
1301+
1302+ assert(NULL != cptr);
1303+
1304+ /* loop over global entries - 0 to max - 1*/
1305+ for (name = 0; name <= WELCOME_MAX_ENTRIES - 1; name++) {
1306+ if (WelcomeIsSet(name))
1307+ sendcmdto_one(&me, CMD_WELCOME, cptr, "* %d %Tu %s :%s",
1308+ name + 1, WelcomeTS(name), WelcomeWho(name), WelcomeText(name));
1309+ }
1310+}
1311+
1312+
1313+/** List welcome messages.
1314+ * @param[in] sptr Client requesting the listing.
1315+ * @param[in] connect When non zero do not report no welcome is set
1316+ * @return Zero.
1317+ */
1318+int
1319+welcome_list(struct Client *sptr, int connect)
1320+{
1321+ int found = 0, local = 0, name;
1322+
1323+ assert(NULL != sptr);
1324+
1325+ /* loop over all entries - range 0 to 2 * max - 1 */
1326+ for (name = 0; name <= 2 * WELCOME_MAX_ENTRIES - 1; name++) {
1327+
1328+ /* local entries now */
1329+ if (name == WELCOME_MAX_ENTRIES)
1330+ local = 1;
1331+
1332+ /* not set or empty - skip */
1333+ if (!WelcomeIsSet(name) || WelcomeIsEmpty(name))
1334+ continue;
1335+
1336+ /* got one */
1337+ found++;
1338+ sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :[%s] %s",
1339+ sptr, local ? cli_name(&me) : feature_str(FEAT_NETWORK), WelcomeText(name));
1340+ }
1341+
1342+ /* nothing set */
1343+ if (!found && !connect)
1344+ sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :No welcome message set.", sptr);
1345+
1346+ return 0;
1347+}
1348+
1349+
1350+/** Statistics callback to list Welcome messages.
1351+ * @param[in] sptr Client requesting statistics.
1352+ * @param[in] sd Stats descriptor for request (ignored).
1353+ * @param[in] param Extra parameter from user (ignored).
1354+ */
1355+void
1356+welcome_stats(struct Client *sptr, const struct StatDesc *sd, char *param)
1357+{
1358+ int name, local = 0;
1359+
1360+ assert(NULL != sptr);
1361+
1362+ /* loop over all entries - range 0 to 2 * max - 1*/
1363+ for (name = 0; name <= 2 * WELCOME_MAX_ENTRIES - 1; name++) {
1364+
1365+ /* local entries now */
1366+ if (name == WELCOME_MAX_ENTRIES)
1367+ local = 1;
1368+
1369+ /* not set */
1370+ if (!WelcomeIsSet(name))
1371+ continue;
1372+
1373+ /* send it */
1374+ send_reply(sptr, RPL_STATSWELCOME,
1375+ local ? name + 1 - WELCOME_MAX_ENTRIES : name + 1,
1376+ local ? cli_name(&me) : "*",
1377+ WelcomeWho(name), WelcomeTS(name),
1378+ WelcomeIsEmpty(name) ? "<Empty>" : WelcomeText(name));
1379+ }
1380+}