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