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