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