]> jfr.im git - irc/quakenet/snircd-patchqueue.git/blame - welcome.patch
add checkoutput.patch - made a few changes
[irc/quakenet/snircd-patchqueue.git] / welcome.patch
CommitLineData
a87bc2c2 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:
cb0b5df1 16:<source> WE <target> [[!]<name> <timestamp> <who> :<text>]
a87bc2c2 17who is who set the message, the server puts in the opername when a client sets it.
cb0b5df1 18:<name> is a number 1 to WELCOME_MAX_ENTRIES - currently set at 10 (should be more than we ever need)
a87bc2c2 19that means there is room for 10 local and 10 global entries
a87bc2c2 20
cb0b5df1 21STATS W/welcome (/STATS w/userload made case sensitive)
a87bc2c2 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
a87bc2c2 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
cb0b5df1 74diff -r ed0f71fe7e22 include/handlers.h
75--- a/include/handlers.h Wed Jan 28 14:01:58 2009 +0100
76+++ b/include/handlers.h Wed Jan 28 15:15:51 2009 +0100
cddf800b 77@@ -151,6 +151,7 @@
78 extern int m_version(struct Client*, struct Client*, int, char*[]);
79 extern int m_wallchops(struct Client*, struct Client*, int, char*[]);
80 extern int m_wallvoices(struct Client*, struct Client*, int, char*[]);
81+extern int m_welcome(struct Client*, struct Client*, int, char*[]);
82 extern int m_who(struct Client*, struct Client*, int, char*[]);
83 extern int m_whois(struct Client*, struct Client*, int, char*[]);
84 extern int m_whowas(struct Client*, struct Client*, int, char*[]);
85@@ -185,6 +186,7 @@
86 extern int mo_version(struct Client*, struct Client*, int, char*[]);
87 extern int mo_wallops(struct Client*, struct Client*, int, char*[]);
88 extern int mo_wallusers(struct Client*, struct Client*, int, char*[]);
89+extern int mo_welcome(struct Client*, struct Client*, int, char*[]);
90 extern int mr_error(struct Client*, struct Client*, int, char*[]);
91 extern int mr_error(struct Client*, struct Client*, int, char*[]);
92 extern int mr_pong(struct Client*, struct Client*, int, char*[]);
93@@ -242,6 +244,7 @@
94 extern int ms_wallops(struct Client*, struct Client*, int, char*[]);
95 extern int ms_wallusers(struct Client*, struct Client*, int, char*[]);
96 extern int ms_wallvoices(struct Client*, struct Client*, int, char*[]);
97+extern int ms_welcome(struct Client*, struct Client*, int, char*[]);
98 extern int ms_whois(struct Client*, struct Client*, int, char*[]);
99
a87bc2c2 100 extern int mh_nohelp(struct Client*, struct Client*, int, char*[]);
101@@ -308,6 +311,7 @@
102 extern int mh_wallops(struct Client*, struct Client*, int, char*[]);
103 extern int mh_wallusers(struct Client*, struct Client*, int, char*[]);
104 extern int mh_wallvoices(struct Client*, struct Client*, int, char*[]);
105+extern int mh_welcome(struct Client*, struct Client*, int, char*[]);
106 extern int mh_who(struct Client*, struct Client*, int, char*[]);
107 extern int mh_whois(struct Client*, struct Client*, int, char*[]);
108 extern int mh_whowas(struct Client*, struct Client*, int, char*[]);
cb0b5df1 109diff -r ed0f71fe7e22 include/ircd_features.h
110--- a/include/ircd_features.h Wed Jan 28 14:01:58 2009 +0100
111+++ b/include/ircd_features.h Wed Jan 28 15:15:51 2009 +0100
a87bc2c2 112@@ -101,6 +101,7 @@
113 FEAT_IRCD_RES_TIMEOUT,
114 FEAT_AUTH_TIMEOUT,
115 FEAT_ANNOUNCE_INVITES,
116+ FEAT_WELCOME,
117
118 /* features that affect all operators */
119 FEAT_EXTENDED_CHECKCMD,
120@@ -143,6 +144,7 @@
121 FEAT_HIS_STATS_u,
122 FEAT_HIS_STATS_U,
123 FEAT_HIS_STATS_v,
124+ FEAT_HIS_STATS_W,
125 FEAT_HIS_STATS_w,
126 FEAT_HIS_STATS_x,
127 FEAT_HIS_STATS_y,
cb0b5df1 128diff -r ed0f71fe7e22 include/msg.h
129--- a/include/msg.h Wed Jan 28 14:01:58 2009 +0100
130+++ b/include/msg.h Wed Jan 28 15:15:51 2009 +0100
cddf800b 131@@ -195,6 +195,10 @@
132 #define MSG_NOTICE "NOTICE" /* NOTI */
133 #define TOK_NOTICE "O"
134 #define CMD_NOTICE MSG_NOTICE, TOK_NOTICE
135+
136+#define MSG_WELCOME "WELCOME" /* WELC */
137+#define TOK_WELCOME "WE"
138+#define CMD_WELCOME MSG_WELCOME, TOK_WELCOME
139
140 #define MSG_WALLCHOPS "WALLCHOPS" /* WC */
141 #define TOK_WALLCHOPS "WC"
cb0b5df1 142diff -r ed0f71fe7e22 include/numeric.h
143--- a/include/numeric.h Wed Jan 28 14:01:58 2009 +0100
144+++ b/include/numeric.h Wed Jan 28 15:15:51 2009 +0100
a87bc2c2 145@@ -116,6 +116,7 @@
146 RPL_STATSGLINE 227 Dalnet
147 RPL_STATSVLINE 227 unreal */
148 #define RPL_STATSALINE 226 /* Hybrid, Undernet */
149+#define RPL_STATSWELCOME 227 /* QuakeNet extension */
150 #define RPL_STATSQLINE 228 /* Undernet extension */
151 #define RPL_STATSHEADER 230 /* QuakeNet extension */
152
cb0b5df1 153@@ -445,6 +446,8 @@
a87bc2c2 154 /* ERR_GHOSTEDCLIENT 503 efnet */
155 /* ERR_VWORLDWARN 503 austnet */
156
157+#define ERR_NOSUCHWELCOME 509 /* QuakeNet extension */
158+
159 #define ERR_SILELISTFULL 511 /* Undernet extension */
160 /* ERR_NOTIFYFULL 512 aircd */
161 /* ERR_TOOMANYWATCH 512 Numeric List: Dalnet */
cb0b5df1 162diff -r ed0f71fe7e22 include/welcome.h
cddf800b 163--- /dev/null Thu Jan 01 00:00:00 1970 +0000
cb0b5df1 164+++ b/include/welcome.h Wed Jan 28 15:15:51 2009 +0100
165@@ -0,0 +1,56 @@
cddf800b 166+#ifndef INCLUDED_welcome_h
167+#define INCLUDED_welcome_h
168+/*
169+ * IRC - Internet Relay Chat, include/welcome.h
170+ * Copyright (C) 1990 Jarkko Oikarinen and
171+ * University of Oulu, Computing Center
172+ * Copyright (C) 2000 Kevin L. Mitchell <klmitch@mit.edu>
173+ *
174+ * This program is free software; you can redistribute it and/or modify
175+ * it under the terms of the GNU General Public License as published by
176+ * the Free Software Foundation; either version 2, or (at your option)
177+ * any later version.
178+ *
179+ * This program is distributed in the hope that it will be useful,
180+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
181+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
182+ * GNU General Public License for more details.
183+ *
184+ * You should have received a copy of the GNU General Public License
185+ * along with this program; if not, write to the Free Software
186+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
187+ */
188+/** @file
a87bc2c2 189+ * @brief Interface and declarations for welcome message handling.
cddf800b 190+ */
191+#ifndef INCLUDED_sys_types_h
192+#include <sys/types.h>
193+#define INCLUDED_sys_types_h
194+#endif
195+
a87bc2c2 196+struct Client;
197+struct StatDesc;
198+
cb0b5df1 199+/* Maximum number of welcome entries (per type; X global, X local) */
200+#define WELCOME_MAX_ENTRIES 10
a87bc2c2 201+
202+/* Describes a Welcome message entry. */
203+struct Welcome {
cb0b5df1 204+ char text[TOPICLEN]; /**< Message */
205+ char who[ACCOUNTLEN]; /**< Who set it */
206+ time_t timestamp; /**< Timestamp of the welcome */
a87bc2c2 207+};
208+
cb0b5df1 209+/** Welcome type flags */
a87bc2c2 210+#define WELCOME_LOCAL 0x01 /**< welcome is local */
211+/** Welcome action flags */
212+#define WELCOME_ANNOUNCE 0x02 /**< announce change to users */
213+
a87bc2c2 214+extern int welcome_do(struct Client *cptr, struct Client *sptr, char *name, char *text,
215+ char *who, time_t timestamp, unsigned int flags);
cb0b5df1 216+extern void welcome_announce(int name);
cddf800b 217+extern void welcome_burst(struct Client *cptr);
a87bc2c2 218+extern int welcome_list(struct Client *sptr, int connect);
219+extern void welcome_stats(struct Client *sptr, const struct StatDesc *sd, char *param);
cddf800b 220+
221+#endif /* INCLUDED_welcome_h */
222\ No newline at end of file
cb0b5df1 223diff -r ed0f71fe7e22 ircd/Makefile.in
224--- a/ircd/Makefile.in Wed Jan 28 14:01:58 2009 +0100
225+++ b/ircd/Makefile.in Wed Jan 28 15:15:51 2009 +0100
a87bc2c2 226@@ -187,6 +187,7 @@
227 m_wallops.c \
228 m_wallusers.c \
229 m_wallvoices.c \
230+ m_welcome.c \
231 m_who.c \
232 m_whois.c \
233 m_whowas.c \
234@@ -214,6 +215,7 @@
235 send.c \
236 uping.c \
237 userload.c \
238+ welcome.c \
239 whocmds.c \
240 whowas.c \
241 y.tab.c
242@@ -1168,6 +1170,11 @@
243 ../include/ircd_reply.h ../include/ircd_string.h \
244 ../include/ircd_chattr.h ../include/msg.h ../include/numeric.h \
245 ../include/numnicks.h ../include/s_user.h ../include/send.h
246+m_welcome.o: m_welcome.c ../config.h ../include/channel.h \
247+ ../include/client.h ../include/hash.h ../include/ircd.h ../include/ircd_log.h \
248+ ../include/ircd_reply.h ../include/ircd_string.h ../include/msg.h \
249+ ../include/numeric.h ../include/numnicks.h ../include/s_user.h \
250+ ../include/send.h ../include/welcome.h
251 m_who.o: m_who.c ../config.h ../include/channel.h ../include/ircd_defs.h \
252 ../include/res.h ../config.h ../include/client.h ../include/dbuf.h \
253 ../include/msgq.h ../include/ircd_events.h ../include/ircd_handler.h \
254@@ -1429,6 +1436,13 @@
255 ../include/numnicks.h ../include/querycmds.h ../include/ircd_features.h \
256 ../include/s_misc.h ../include/s_stats.h ../include/send.h \
257 ../include/struct.h ../include/sys.h
258+welcome.o: welcome.c ../config.h ../include/client.h \
259+ ../include/hash.h ../include/ircd.h ../include/ircd_alloc.h \
260+ ../include/ircd_features.h ../include/ircd_log.h ../include/ircd_reply.h \
261+ ../include/match.h ../include/msg.h ../include/numeric.h \
262+ ../include/numnicks.h ../include/s_debug.h ../include/s_bsd.h \
263+ ../include/s_misc.h ../include/send.h ../include/struct.h \
264+ ../include/sys.h ../include/welcome.h
265 whocmds.o: whocmds.c ../config.h ../include/whocmds.h \
266 ../include/channel.h ../include/ircd_defs.h ../include/res.h \
267 ../config.h ../include/client.h ../include/dbuf.h ../include/msgq.h \
cb0b5df1 268diff -r ed0f71fe7e22 ircd/ircd_features.c
269--- a/ircd/ircd_features.c Wed Jan 28 14:01:58 2009 +0100
270+++ b/ircd/ircd_features.c Wed Jan 28 15:15:51 2009 +0100
a87bc2c2 271@@ -355,6 +355,7 @@
272 F_I(IRCD_RES_TIMEOUT, 0, 4, 0),
273 F_I(AUTH_TIMEOUT, 0, 9, 0),
274 F_B(ANNOUNCE_INVITES, 0, 0, 0),
275+ F_B(WELCOME, 0, 1, 0),
276
277 /* features that affect all operators */
278 F_B(EXTENDED_CHECKCMD, 0, 0, 0),
279@@ -397,6 +398,7 @@
280 F_B(HIS_STATS_u, 0, 1, 0),
281 F_B(HIS_STATS_U, 0, 1, 0),
282 F_B(HIS_STATS_v, 0, 1, 0),
283+ F_B(HIS_STATS_W, 0, 1, 0),
284 F_B(HIS_STATS_w, 0, 1, 0),
285 F_B(HIS_STATS_x, 0, 1, 0),
286 F_B(HIS_STATS_y, 0, 1, 0),
cb0b5df1 287diff -r ed0f71fe7e22 ircd/m_welcome.c
cddf800b 288--- /dev/null Thu Jan 01 00:00:00 1970 +0000
cb0b5df1 289+++ b/ircd/m_welcome.c Wed Jan 28 15:15:51 2009 +0100
290@@ -0,0 +1,269 @@
cddf800b 291+/*
292+ * IRC - Internet Relay Chat, ircd/m_welcome.c
293+ * Copyright (C) 1990 Jarkko Oikarinen and
294+ * University of Oulu, Computing Center
295+ *
296+ * See file AUTHORS in IRC package for additional names of
297+ * the programmers.
298+ *
299+ * This program is free software; you can redistribute it and/or modify
300+ * it under the terms of the GNU General Public License as published by
301+ * the Free Software Foundation; either version 1, or (at your option)
302+ * any later version.
303+ *
304+ * This program is distributed in the hope that it will be useful,
305+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
306+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
307+ * GNU General Public License for more details.
308+ *
309+ * You should have received a copy of the GNU General Public License
310+ * along with this program; if not, write to the Free Software
311+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
312+ *
cddf800b 313+ */
314+
315+/*
316+ * m_functions execute protocol messages on this server:
317+ *
318+ * cptr is always NON-NULL, pointing to a *LOCAL* client
319+ * structure (with an open socket connected!). This
320+ * identifies the physical socket where the message
321+ * originated (or which caused the m_function to be
322+ * executed--some m_functions may call others...).
323+ *
324+ * sptr is the source of the message, defined by the
325+ * prefix part of the message if present. If not
326+ * or prefix not found, then sptr==cptr.
327+ *
328+ * (!IsServer(cptr)) => (cptr == sptr), because
329+ * prefixes are taken *only* from servers...
330+ *
331+ * (IsServer(cptr))
332+ * (sptr == cptr) => the message didn't
333+ * have the prefix.
334+ *
335+ * (sptr != cptr && IsServer(sptr) means
336+ * the prefix specified servername. (?)
337+ *
338+ * (sptr != cptr && !IsServer(sptr) means
339+ * that message originated from a remote
340+ * user (not local).
341+ *
342+ * combining
343+ *
344+ * (!IsServer(sptr)) means that, sptr can safely
345+ * taken as defining the target structure of the
346+ * message in this server.
347+ *
348+ * *Always* true (if 'parse' and others are working correct):
349+ *
350+ * 1) sptr->from == cptr (note: cptr->from == cptr)
351+ *
352+ * 2) MyConnect(sptr) <=> sptr == cptr (e.g. sptr
353+ * *cannot* be a local connection, unless it's
354+ * actually cptr!). [MyConnect(x) should probably
355+ * be defined as (x == x->from) --msa ]
356+ *
357+ * parc number of variable parameter strings (if zero,
358+ * parv is allowed to be NULL)
359+ *
360+ * parv a NULL terminated list of parameter pointers,
361+ *
362+ * parv[0], sender (prefix string), if not present
363+ * this points to an empty string.
364+ * parv[1]...parv[parc-1]
365+ * pointers to additional parameters
366+ * parv[parc] == NULL, *always*
367+ *
368+ * note: it is guaranteed that parv[0]..parv[parc-1] are all
369+ * non-NULL pointers.
370+ */
371+#include "config.h"
372+
373+#include "channel.h"
374+#include "client.h"
375+#include "hash.h"
376+#include "ircd.h"
a87bc2c2 377+#include "ircd_features.h"
cddf800b 378+#include "ircd_log.h"
379+#include "ircd_reply.h"
380+#include "ircd_string.h"
381+#include "msg.h"
382+#include "numeric.h"
383+#include "numnicks.h"
384+#include "s_user.h"
385+#include "send.h"
a87bc2c2 386+#include "welcome.h"
cddf800b 387+
388+/* #include <assert.h> -- Now using assert in ircd_log.h */
389+
390+/*
391+ * m_welcome - local generic message handler
392+ *
393+ * parv[0] = Send prefix
394+ */
395+int m_welcome(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
396+{
a87bc2c2 397+ /* feature disabled */
398+ if (!feature_bool(FEAT_WELCOME))
399+ return send_reply(sptr, ERR_DISABLED, "WELCOME");
400+ return welcome_list(sptr, 0);
cddf800b 401+}
402+
403+
404+/*
405+ * mo_welcome - oper message handler
406+ *
407+ * listing:
408+ * parv[0] = Send prefix
409+ *
410+ * remote listing:
411+ * parv[0] = Send prefix
412+ * parv[1] = Target
413+ *
414+ * set global or on remote server:
415+ * parv[0] = Send prefix
416+ * parv[1] = Target: server or * for global
a87bc2c2 417+ * parv[2] = Name
418+ * parv[3] = Text
cddf800b 419+ */
420+int mo_welcome(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
421+{
cb0b5df1 422+ char *target, *name, *who, *text, pattern[BUFSIZE];
a87bc2c2 423+ time_t timestamp;
424+ unsigned int flags = 0;
425+ int local = 0; /* 1 when it is for me */
a87bc2c2 426+
427+ /* feature disabled */
428+ if (!feature_bool(FEAT_WELCOME))
429+ return send_reply(sptr, ERR_DISABLED, "WELCOME");
430+
431+ /* listing */
432+ if (parc < 2)
433+ return welcome_list(sptr, 0);
434+
cb0b5df1 435+ /* TODO: add PRIV_WELCOME? */
a87bc2c2 436+ /* check PRIVS */
437+ if (!HasPriv(sptr,PRIV_SERVERINFO))
438+ return send_reply(sptr, ERR_NOPRIVILEGES);
439+
440+ /* remote listing request, see if it is for me or a remote server */
441+ if (parc == 2) {
442+ if (hunt_server_cmd(sptr, CMD_WELCOME, cptr, 0, "%C", 1, parc, parv) != HUNTED_ISME)
443+ return 0;
444+ return welcome_list(sptr, 0);
445+ }
446+
447+ /* set the parameters */
448+ /* less than 4 parameters, assume target was left out, making it local for me */
449+ if (parc < 4) {
450+ local++;
451+ target = cli_name(&me);
452+ name = parv[1];
453+ flags |= WELCOME_LOCAL;
454+ } else { /* otherwise set as it should */
455+ target = parv[1];
456+ name = parv[2];
457+ }
458+ timestamp = TStime();
459+ who = cli_user(sptr)->opername;
460+ text = parv[parc - 1];
461+
462+ /* target is not global */
463+ if (!(target[0] == '*' && target[1] == '\0') && !local) {
464+ /* build a pattern for hunt_server_cmd since we do not have all we need in parv */
465+ ircd_snprintf(0, pattern, sizeof(pattern), "%s %s %Tu %s :%s", "%C", name, timestamp, who, text);
466+ if (hunt_server_cmd(sptr, CMD_WELCOME, cptr, 0, pattern, 1, 2, parv) != HUNTED_ISME)
467+ return 0;
468+ flags |= WELCOME_LOCAL; /* local welcome, for me */
469+ }
470+
cb0b5df1 471+ /* TODO: disallow global announcement from oper?
472+ * as PRIVMSG/NOTICE to $* is not allowed either by the ircd
473+ * when PRIV for that is added, use that here? PRIV_BROADCAST or something
474+ */
a87bc2c2 475+ /* check for anounce prefix */
476+ if (*name == '!') {
477+ name++;
478+ flags |= WELCOME_ANNOUNCE;
479+ }
480+
481+ /* and do it */
482+ return welcome_do(cptr, sptr, name, text, who, timestamp, flags);
cddf800b 483+}
484+
485+
486+/*
487+ * ms_welcome - server message handler
488+ *
489+ * parv[0] = Send prefix
490+ * parv[1] = Target: server numeric or * for global
a87bc2c2 491+ * parv[2] = Name
492+ * parv[3] = Timestamp
493+ * parv[4] = Who
494+ * parv[5] = Text
cddf800b 495+ */
496+int ms_welcome(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
497+{
cb0b5df1 498+ char *target, *name, *who, *text;
a87bc2c2 499+ time_t timestamp;
500+ unsigned int flags = 0;
501+
502+ /* not enough */
503+ if (parc < 2)
504+ return need_more_params(sptr, "WELCOME");
505+
506+ /* remote listing request, see if it is for me or a remote server */
507+ if (parc == 2) {
508+ if (hunt_server_cmd(sptr, CMD_WELCOME, cptr, 0, "%C", 1, parc, parv) == HUNTED_ISME)
509+ return welcome_list(sptr, 0);
510+ }
511+
512+ /* we need at least 6 parameters to continue */
513+ if (parc < 6)
514+ return need_more_params(sptr, "WELCOME");
515+
516+ /* set the parameters */
517+ target = parv[1];
518+ name = parv[2];
519+ timestamp = atoi(parv[3]);
520+ who = parv[4];
521+ text = parv[parc - 1];
522+
523+ /* target is not global */
524+ if (!(target[0] == '*' && target[1] == '\0')) {
525+ /* not for me, and forward it */
526+ if (hunt_server_cmd(sptr, CMD_WELCOME, cptr, 0, "%C %s %s %s :%s", 1, parc, parv) != HUNTED_ISME)
527+ return 0;
528+ flags |= WELCOME_LOCAL; /* local welcome, for me */
529+ }
530+
531+ /* check for anounce prefix */
532+ if (*name == '!') {
533+ name++;
534+ flags |= WELCOME_ANNOUNCE;
535+ }
536+
537+ /* and do it */
538+ return welcome_do(cptr, sptr, name, text, who, timestamp, flags);
539+}
540+
541+
542+/*
543+ * mh_welcome - help message handler
544+ */
545+int mh_welcome(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
546+{
547+ if (!IsAnOper(sptr)) {
548+ send_reply(sptr, SND_EXPLICIT | RPL_HELP,
549+ "WELCOME :WELCOME");
550+ send_reply(sptr, SND_EXPLICIT | RPL_HELP,
551+ "WELCOME :Shows welcome messages set on the server.");
552+ } else {
553+ send_reply(sptr, SND_EXPLICIT | RPL_HELP,
554+ "WELCOME :WELCOME [<target>] [[!]<name> :<message>]");
555+ send_reply(sptr, SND_EXPLICIT | RPL_HELP,
556+ "WELCOME :Shows or sets welcome messages on a server.");
557+ }
cddf800b 558+ return 0;
559+}
cb0b5df1 560diff -r ed0f71fe7e22 ircd/parse.c
561--- a/ircd/parse.c Wed Jan 28 14:01:58 2009 +0100
562+++ b/ircd/parse.c Wed Jan 28 15:15:51 2009 +0100
a87bc2c2 563@@ -667,6 +667,15 @@
564 /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
565 { m_unregistered, m_not_oper, ms_opkick, mo_opkick, m_ignore, mh_nohelp }
cddf800b 566 },
567+
568+ /* add command for WELCOME */
569+ {
570+ MSG_WELCOME,
571+ TOK_WELCOME,
572+ 0, MAXPARA, MFLG_SLOW, 0, NULL,
a87bc2c2 573+ /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
574+ { m_unregistered, m_welcome, ms_welcome, mo_welcome, m_ignore, mh_welcome }
cddf800b 575+ },
576
577 /* This command is an alias for QUIT during the unregistered part of
578 * of the server. This is because someone jumping via a broken web
cb0b5df1 579diff -r ed0f71fe7e22 ircd/s_err.c
580--- a/ircd/s_err.c Wed Jan 28 14:01:58 2009 +0100
581+++ b/ircd/s_err.c Wed Jan 28 15:15:51 2009 +0100
a87bc2c2 582@@ -486,7 +486,7 @@
583 /* 226 */
584 { RPL_STATSALINE, "%s", "226" },
585 /* 227 */
586- { 0 },
587+ { RPL_STATSWELCOME, "W %d %s %s %Tu :%s", "227" },
588 /* 228 */
589 { RPL_STATSQLINE, "Q %s :%s", "228" },
590 /* 229 */
591@@ -1050,7 +1050,7 @@
592 /* 508 */
593 { 0 },
594 /* 509 */
595- { 0 },
596+ { ERR_NOSUCHWELCOME, "%s :No such welcome", "509" },
597 /* 510 */
598 { 0 },
599 /* 511 */
cb0b5df1 600diff -r ed0f71fe7e22 ircd/s_serv.c
601--- a/ircd/s_serv.c Wed Jan 28 14:01:58 2009 +0100
602+++ b/ircd/s_serv.c Wed Jan 28 15:15:51 2009 +0100
a87bc2c2 603@@ -57,6 +57,7 @@
604 #include "struct.h"
605 #include "sys.h"
606 #include "userload.h"
607+#include "welcome.h"
608
609 /* #include <assert.h> -- Now using assert in ircd_log.h */
610 #include <stdlib.h>
611@@ -196,6 +197,7 @@
612 */
613 gline_burst(cptr);
614 jupe_burst(cptr);
615+ welcome_burst(cptr);
616
617 /*
618 * Pass on my client information to the new server
cb0b5df1 619diff -r ed0f71fe7e22 ircd/s_stats.c
620--- a/ircd/s_stats.c Wed Jan 28 14:01:58 2009 +0100
621+++ b/ircd/s_stats.c Wed Jan 28 15:15:51 2009 +0100
a87bc2c2 622@@ -54,6 +54,7 @@
623 #include "send.h"
624 #include "struct.h"
625 #include "userload.h"
626+#include "welcome.h"
627
628 #include <stdio.h>
629 #include <stdlib.h>
630@@ -689,9 +690,12 @@
631 { 'V', "vserversmach", (STAT_FLAG_OPERFEAT | STAT_FLAG_VARPARAM | STAT_FLAG_CASESENS), FEAT_HIS_STATS_v,
632 stats_servers_verbose, 0,
633 "Verbose server information." },
634- { 'w', "userload", STAT_FLAG_OPERFEAT, FEAT_HIS_STATS_w,
635+ { 'w', "userload", STAT_FLAG_OPERFEAT | STAT_FLAG_CASESENS, FEAT_HIS_STATS_w,
636 calc_load, 0,
637 "Userload statistics." },
638+ { 'W', "welcome", STAT_FLAG_OPERFEAT | STAT_FLAG_CASESENS, FEAT_HIS_STATS_W,
639+ welcome_stats, 0,
640+ "Welcome messages." },
641 { 'x', "memusage", STAT_FLAG_OPERFEAT, FEAT_HIS_STATS_x,
642 stats_meminfo, 0,
643 "List usage information." },
cb0b5df1 644diff -r ed0f71fe7e22 ircd/s_user.c
645--- a/ircd/s_user.c Wed Jan 28 14:01:58 2009 +0100
646+++ b/ircd/s_user.c Wed Jan 28 15:15:51 2009 +0100
a87bc2c2 647@@ -63,6 +63,7 @@
648 #include "userload.h"
649 #include "version.h"
650 #include "whowas.h"
651+#include "welcome.h"
652
653 #include "handlers.h" /* m_motd and m_lusers */
654
655@@ -411,6 +412,9 @@
656 cli_info(sptr), NumNick(cptr) /* two %s's */);
657
658 IPcheck_connect_succeeded(sptr);
659+
660+ if (feature_bool(FEAT_WELCOME))
661+ welcome_list(sptr, 1);
662 }
663 else {
664 struct Client *acptr = user->server;
cb0b5df1 665diff -r ed0f71fe7e22 ircd/welcome.c
cddf800b 666--- /dev/null Thu Jan 01 00:00:00 1970 +0000
cb0b5df1 667+++ b/ircd/welcome.c Wed Jan 28 15:15:51 2009 +0100
668@@ -0,0 +1,355 @@
cddf800b 669+/*
a87bc2c2 670+ * IRC - Internet Relay Chat, ircd/welcome.c
cddf800b 671+ * Copyright (C) 1990 Jarkko Oikarinen and
672+ * University of Oulu, Finland
673+ * Copyright (C) 2000 Kevin L. Mitchell <klmitch@mit.edu>
674+ *
675+ * This program is free software; you can redistribute it and/or modify
676+ * it under the terms of the GNU General Public License as published by
677+ * the Free Software Foundation; either version 1, or (at your option)
678+ * any later version.
679+ *
680+ * This program is distributed in the hope that it will be useful,
681+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
682+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
683+ * GNU General Public License for more details.
684+ *
685+ * You should have received a copy of the GNU General Public License
686+ * along with this program; if not, write to the Free Software
687+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
688+ */
689+/** @file
a87bc2c2 690+ * @brief Implementation of welcome message handling functions.
cddf800b 691+ */
692+#include "config.h"
693+
694+#include "client.h"
695+#include "hash.h"
696+#include "ircd.h"
697+#include "ircd_alloc.h"
698+#include "ircd_features.h"
699+#include "ircd_log.h"
700+#include "ircd_reply.h"
701+#include "ircd_string.h"
702+#include "match.h"
703+#include "msg.h"
704+#include "numeric.h"
705+#include "numnicks.h"
706+#include "s_bsd.h"
a87bc2c2 707+#include "s_debug.h"
cddf800b 708+#include "s_misc.h"
709+#include "send.h"
710+#include "struct.h"
711+#include "sys.h" /* FALSE bleah */
712+#include "welcome.h"
713+
714+/* #include <assert.h> -- Now using assert in ircd_log.h */
715+#include <string.h>
716+
cddf800b 717+
cb0b5df1 718+/** List of welcome messages - first MAX for global, second MAX for local */
719+static struct Welcome WelcomeArray[WELCOME_MAX_ENTRIES * 2];
720+
cddf800b 721+
cddf800b 722+
a87bc2c2 723+/** Allocate a new welcome with the given parameters.
724+ * @param[in] name Name of the welcome message.
725+ * @param[in] text The welcome message.
726+ * @param[in] who Who set it.
727+ * @param[in] timestamp When it was set.
cb0b5df1 728+ * @return name Array number of the welcome set.
cddf800b 729+ */
cb0b5df1 730+static int
731+welcome_make(int name, char *text, char *who, time_t timestamp)
cddf800b 732+{
cb0b5df1 733+ /* range 0 to 2 * max - 1 */
734+ assert(name >= 0 && name <= 2 * WELCOME_MAX_ENTRIES - 1);
735+
736+ /* store it */
737+ ircd_strncpy(WelcomeArray[name].text, text, TOPICLEN);
738+ ircd_strncpy(WelcomeArray[name].who, who, ACCOUNTLEN);
739+ WelcomeArray[name].timestamp = timestamp;
740+
741+ return name;
cddf800b 742+}
743+
cddf800b 744+
a87bc2c2 745+/** Change a welcome message.
746+ * @param[in] cptr Local client that sent us the welcome.
747+ * @param[in] sptr Originator of the welcome.
748+ * @param[in] name Name of the message.
749+ * @param[in] text The welcome message.
750+ * @param[in] timestamp Timestamp of when the message was set.
751+ * @param[in] flags Flags to set on welcome.
752+ * @return Zero
cddf800b 753+ */
754+int
a87bc2c2 755+welcome_do(struct Client *cptr, struct Client *sptr, char *name, char *text,
756+ char *who, time_t timestamp, unsigned int flags)
cddf800b 757+{
cb0b5df1 758+ int nameint = atoi(name); /* transform to int */
759+ int namearray = nameint - 1; /* used to test the array element */
760+ int notext = (text[0] == '\0') ? 1 : 0; /* 1 when text is empty */
761+ char *action; /* used for logging */
762+ /* TODO: these needed? */
763+ /*
764+ who[ACCOUNTLEN-1] = 0;
765+ text[WELCOMELEN-1] = 0;
766+ */
a87bc2c2 767+
cb0b5df1 768+ /* TODO: NULL or 0 ? they are pointers, so NULL? */
a87bc2c2 769+ assert(0 != cptr);
770+ assert(0 != sptr);
771+ assert(0 != name);
cb0b5df1 772+ /* TODO: assert(0 != text); */
773+ assert(0 != who);
a87bc2c2 774+
cb0b5df1 775+ /* debug */
a87bc2c2 776+ Debug((DEBUG_DEBUG, "welcome_do(\"%s\", \"%s\", \"%s\", \"%s\" \"%s\", %Tu, 0x%04x)",
777+ cli_name(cptr), cli_name(sptr), name, text, who, timestamp, flags));
778+
779+ /* check name */
cb0b5df1 780+ if (nameint < 1 || nameint > WELCOME_MAX_ENTRIES) {
a87bc2c2 781+ if (IsUser(sptr))
782+ sendcmdto_one(&me, CMD_NOTICE, sptr,
783+ "%C :WELCOME: Invalid message number %s - should between 1 and %d",
784+ sptr, name, WELCOME_MAX_ENTRIES);
cddf800b 785+ return 0;
786+ }
787+
cb0b5df1 788+ /* correct namearray for local offset */
789+ if (flags & WELCOME_LOCAL)
790+ namearray += WELCOME_MAX_ENTRIES;
a87bc2c2 791+
792+ /* cannot unset welcome that is not set */
cb0b5df1 793+ if (WelcomeArray[namearray].timestamp == 0 && notext) {
a87bc2c2 794+ /* from user, throw error */
795+ if (IsUser(sptr))
796+ return send_reply(sptr, ERR_NOSUCHWELCOME, name);
797+ /* new local welcome from server, but empty - ignore */
798+ if (flags & WELCOME_LOCAL)
799+ return 0;
800+ /* new global welcome message, but empty, accept from server
801+ * otherwise we do not have the same state as them
802+ */
803+ }
cddf800b 804+
cb0b5df1 805+ /* TODO: check if we receive an entry with a timestamp >> TStime()
806+ * ..so from the future
807+ * something is wrong there - either side is wrong about the time (or both)
808+ *
809+ * if we accept a global welcome message with a timestamp far into the future
810+ * it will not be possible to change it - well, not until we pass that point in time
811+ * or /settime to get to it, or hack with a service..
812+ *
813+ * how to handle this - like CREATE and settime? hm, or throw error to ops?
814+ */
a87bc2c2 815+ /* check if there is something to change */
cb0b5df1 816+ if (WelcomeArray[namearray].timestamp != 0) { /* we got a record for it */
817+ if (namearray < WELCOME_MAX_ENTRIES) { /* global */
818+ if (timestamp == WelcomeArray[namearray].timestamp) /* we got this version already */
a87bc2c2 819+ return 0;
cb0b5df1 820+ if (timestamp < WelcomeArray[namearray].timestamp) { /* we got a later version */
a87bc2c2 821+ if (IsBurstOrBurstAck(cptr)) /* middle of a burst, it will resync on its own */
822+ return 0;
cb0b5df1 823+ return welcome_resend(cptr, namearray); /* resync the server */
a87bc2c2 824+ }
cb0b5df1 825+ } else
826+ /* local welcome, set timestamp to nettime, accept any change
827+ * we parse local welcome messages in the order we receive them
828+ * they cannot cross on the network and are not bursted,
829+ * and thus timestamp is not required for conflict resolving.
830+ */
a87bc2c2 831+ timestamp = TStime();
cb0b5df1 832+
a87bc2c2 833+ /* compare new message with old message */
cb0b5df1 834+ if (ircd_strcmp(text, WelcomeArray[namearray].text) == 0) {
a87bc2c2 835+ if (IsUser(sptr))
836+ sendcmdto_one(&me, CMD_NOTICE, sptr,
cb0b5df1 837+ "%C :WELCOME: Cannot change %smessage for %s - nothing to change.",
838+ sptr, (flags & WELCOME_LOCAL) ? "local " : "", name);
a87bc2c2 839+ return 0;
840+ }
841+ }
cddf800b 842+
cb0b5df1 843+ /* update */
844+ welcome_make(namearray, text, who, timestamp);
845+
a87bc2c2 846+ /* set action */
847+ if (flags & WELCOME_LOCAL)
848+ action = notext ? "removing local" : "changing local";
849+ else
850+ action = notext ? "unsetting" : "changing";
851+
852+ /* TODO: WALLOPS for local welcome messages, so that all operators know about it? */
cb0b5df1 853+ /* inform ops */
854+ sendto_opmask_butone(0, SNO_OLDSNO, "%s %s WELCOME for %d%s%s",
a87bc2c2 855+ (feature_bool(FEAT_HIS_SNOTICES) || IsServer(sptr)) ?
856+ get_client_name_and_opername(sptr) : cli_name((cli_user(sptr))->server),
cb0b5df1 857+ action, nameint, notext ? "" : " to :", notext ? "" : WelcomeArray[namearray].text);
a87bc2c2 858+
859+ /* log it */
cb0b5df1 860+ log_write(LS_NETWORK, L_INFO, LOG_NOSNOTICE, "%#C (%s) %s WELCOME for %d%s%s [%Tu]",
861+ sptr, WelcomeArray[namearray].who, action, nameint,
862+ notext ? "" : " to :", notext ? "" : WelcomeArray[namearray].text,
863+ WelcomeArray[namearray].timestamp);
a87bc2c2 864+
cb0b5df1 865+ /* TODO: or WALLOPS here, local message remotely set? */
a87bc2c2 866+ /* welcome set by remote user, inform oper of success */
867+ if ((flags & WELCOME_LOCAL) && IsUser(sptr) && !MyUser(sptr))
cb0b5df1 868+ sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :%s %s WELCOME for %d%s%s",
869+ sptr, get_client_name_and_opername(sptr), action, nameint,
870+ notext ? "" : " to :", notext ? "" : WelcomeArray[namearray].text);
cddf800b 871+
cb0b5df1 872+ /* propagate it */
a87bc2c2 873+ if (!(flags & WELCOME_LOCAL))
874+ sendcmdto_serv_butone(sptr, CMD_WELCOME, cptr, "* %s%d %Tu %s :%s",
cb0b5df1 875+ (flags & WELCOME_ANNOUNCE) ? "!" : "", nameint,
876+ WelcomeArray[namearray].timestamp, WelcomeArray[namearray].who,
877+ WelcomeArray[namearray].text);
cddf800b 878+
a87bc2c2 879+ /* announce it */
880+ if ((flags & WELCOME_ANNOUNCE) && !notext)
cb0b5df1 881+ welcome_announce(namearray);
a87bc2c2 882+
883+ return 0;
cddf800b 884+}
885+
a87bc2c2 886+
887+/** Announce a welcome message to local clients.
cb0b5df1 888+ * @param[in] name Welcome message to announce.
cddf800b 889+ */
a87bc2c2 890+void
cb0b5df1 891+welcome_announce(int name)
cddf800b 892+{
a87bc2c2 893+ struct Client *acptr;
894+ struct MsgBuf *msgbuf;
895+
cb0b5df1 896+ /* range 0 to 2 * max - 1 */
897+ assert(name >= 0 && name <= 2 * WELCOME_MAX_ENTRIES - 1);
a87bc2c2 898+
cb0b5df1 899+ /* TODO: target is $* as if it were a global broadcast
900+ * could make it $servername for local message announcement
901+ * but the type is shown between [ ] already
902+ * either [Network] or [servername] - using $* is just shorter.
903+ */
a87bc2c2 904+ /* build msgbuf */
905+ msgbuf = msgq_make(0, ":%C %s $* :[%s] %s", &me, MSG_NOTICE,
cb0b5df1 906+ name >= WELCOME_MAX_ENTRIES ? cli_name(&me) : feature_str(FEAT_NETWORK),
907+ WelcomeArray[name].text);
a87bc2c2 908+
909+ /* go over clients */
910+ for (acptr = GlobalClientList; acptr; acptr = cli_next(acptr)) {
cb0b5df1 911+
a87bc2c2 912+ /* skip remote users
cb0b5df1 913+ * skip unregistered clients - they see the message during login
a87bc2c2 914+ */
915+ if (!MyUser(acptr) || !IsRegistered(acptr))
916+ continue;
cb0b5df1 917+
a87bc2c2 918+ /* send it away */
919+ send_buffer(acptr, msgbuf, 0);
cddf800b 920+ }
cddf800b 921+}
922+
a87bc2c2 923+
a87bc2c2 924+/** Send the full list of welcome message to \a cptr.
925+ * @param[in] cptr Local server to send welcomes to.
cddf800b 926+ */
927+void
a87bc2c2 928+welcome_burst(struct Client *cptr)
cddf800b 929+{
cb0b5df1 930+ int name;
a87bc2c2 931+
cb0b5df1 932+ /* loop over global entries - 0 to max - 1*/
933+ for (name = 0; name <= WELCOME_MAX_ENTRIES - 1; name++) {
934+ if (WelcomeArray[name].timestamp != 0)
a87bc2c2 935+ sendcmdto_one(&me, CMD_WELCOME, cptr, "* %d %Tu %s :%s",
cb0b5df1 936+ name + 1, WelcomeArray[name].timestamp, WelcomeArray[name].who,
937+ WelcomeArray[name].text);
cddf800b 938+ }
939+}
940+
a87bc2c2 941+
942+/** Forward a welcome to another server.
943+ * @param[in] cptr %Server to send welcome to.
944+ * @param[in] welcome Welcome to forward.
cddf800b 945+ */
946+int
cb0b5df1 947+welcome_resend(struct Client *cptr, int name)
cddf800b 948+{
a87bc2c2 949+ sendcmdto_one(&me, CMD_WELCOME, cptr, "* %d %Tu %s :%s",
cb0b5df1 950+ name + 1, WelcomeArray[name].timestamp, WelcomeArray[name].who,
951+ WelcomeArray[name].text);
cddf800b 952+ return 0;
953+}
954+
a87bc2c2 955+
956+/** List welcome messages.
957+ * @param[in] sptr Client requesting the listing.
958+ * @param[in] connect When non zero do not report no welcome is set
cddf800b 959+ * @return Zero.
960+ */
961+int
a87bc2c2 962+welcome_list(struct Client *sptr, int connect)
cddf800b 963+{
cb0b5df1 964+ int found = 0, local = 0, name;
a87bc2c2 965+
cb0b5df1 966+ /* loop over all entries - range 0 to 2 * max - 1 */
967+ for (name = 0; name <= 2 * WELCOME_MAX_ENTRIES - 1; name++) {
a87bc2c2 968+
cb0b5df1 969+ /* local entries now */
970+ if (name == WELCOME_MAX_ENTRIES)
971+ local = 1;
a87bc2c2 972+
cb0b5df1 973+ /* not set or empty - skip */
974+ if (WelcomeArray[name].timestamp == 0 || *WelcomeArray[name].text == 0)
a87bc2c2 975+ continue;
cb0b5df1 976+
977+ /* got one */
a87bc2c2 978+ found++;
cb0b5df1 979+ sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :[%s] %s",
980+ sptr, local ? cli_name(&me) : feature_str(FEAT_NETWORK), WelcomeArray[name].text);
a87bc2c2 981+ }
cb0b5df1 982+
983+ /* nothing set */
a87bc2c2 984+ if (!found && !connect)
cb0b5df1 985+ sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :No welcome message set.", sptr);
986+
a87bc2c2 987+ return 0;
988+}
cddf800b 989+
cddf800b 990+
a87bc2c2 991+/** Statistics callback to list Welcome messages.
992+ * @param[in] sptr Client requesting statistics.
993+ * @param[in] sd Stats descriptor for request (ignored).
994+ * @param[in] param Extra parameter from user (ignored).
995+ */
996+void
997+welcome_stats(struct Client *sptr, const struct StatDesc *sd, char *param)
998+{
cb0b5df1 999+ int name, local = 0;
a87bc2c2 1000+
cb0b5df1 1001+ /* TODO: change "Who" to "Opername" or "OperID" as that is what it is? */
a87bc2c2 1002+ /* send header so the client knows what we are showing */
1003+ send_reply(sptr, SND_EXPLICIT | RPL_STATSHEADER,
1004+ "W Name Target Who Timestamp :Message");
1005+
cb0b5df1 1006+ /* loop over all entries - range 0 to 2 * max - 1*/
1007+ for (name = 0; name <= 2 * WELCOME_MAX_ENTRIES - 1; name++) {
cddf800b 1008+
cb0b5df1 1009+ /* local entries now */
1010+ if (name == WELCOME_MAX_ENTRIES)
1011+ local = 1;
a87bc2c2 1012+
cb0b5df1 1013+ /* not set */
1014+ if (WelcomeArray[name].timestamp == 0)
1015+ continue;
cddf800b 1016+
cb0b5df1 1017+ /* send it */
1018+ send_reply(sptr, RPL_STATSWELCOME,
1019+ local ? name + 1 - WELCOME_MAX_ENTRIES : name + 1, local ? cli_name(&me) : "*",
1020+ WelcomeArray[name].who, WelcomeArray[name].timestamp,
1021+ (*WelcomeArray[name].text == 0) ? "<Empty>" : WelcomeArray[name].text);
cddf800b 1022+ }
cddf800b 1023+}