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