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