1 Add welcome message functionality.
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
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
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
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
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.
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.
46 add m_welcome mo_welcome ms_welcome functions
50 add features FEAT_WELCOME and FEAT_HIS_STATS_W
53 add MSG_WELCOME TOK_WELCOME CMD_WELCOME
56 add welcome message functions
60 add RPL_STATSWELCOME ERR_NOSUCHWELCOME
68 add welcome.c and m_welcome.c files
71 add burst welcome message
77 add showing of welcome messages on connect
80 add count and memusage of welcome messages
86 add PRIV_LOCAL_WELCOME PRIV_WELCOME
88 diff -r 8bf1b05cdfe7 include/client.h
89 --- a/include/client.h
90 +++ b/include/client.h
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 */
100 diff -r 8bf1b05cdfe7 include/handlers.h
101 --- a/include/handlers.h
102 +++ b/include/handlers.h
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*[]);
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*[]);
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
131 FEAT_IRCD_RES_TIMEOUT,
133 FEAT_ANNOUNCE_INVITES,
136 /* features that affect all operators */
137 FEAT_EXTENDED_CHECKCMD,
146 diff -r 8bf1b05cdfe7 include/msg.h
150 #define TOK_NOTICE "O"
151 #define CMD_NOTICE MSG_NOTICE, TOK_NOTICE
153 +#define MSG_WELCOME "WELCOME" /* WELC */
154 +#define TOK_WELCOME "WE"
155 +#define CMD_WELCOME MSG_WELCOME, TOK_WELCOME
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
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 */
170 /* RPL_SERVICEINFO 231 unused */
172 /* ERR_GHOSTEDCLIENT 503 efnet */
173 /* ERR_VWORLDWARN 503 austnet */
175 +#define ERR_NOSUCHWELCOME 509 /* QuakeNet extension */
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
182 +++ b/include/welcome.h
184 +#ifndef INCLUDED_welcome_h
185 +#define INCLUDED_welcome_h
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>
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.
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.
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.
207 + * @brief Interface and declarations for welcome message handling.
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 + * the maximum value for this is 300
217 + * when set larger, this could lead to truncation when announcing
218 + * ":server.name NOTICE $server.name :[server.name] text"
219 + * 510 - (1+63+1+6+1+1+63+1+1+1+63+1+1) = 306 for text
220 + * max length of a servername is 63 HOSTLEN
222 +#define WELCOMELEN 300
225 +/* Test if a welcome entry is in a valid range */
226 +#define WelcomeArrayIsValid(x) ((unsigned) (x) <= 2 * WELCOME_MAX_ENTRIES -1)
227 +/* Test if a welcome name is in a valid range */
228 +#define WelcomeNameIsValid(x) ((unsigned) (x) <= WELCOME_MAX_ENTRIES)
229 +/* Test if a welcome entry is set */
230 +#define WelcomeIsSet(x) (WelcomeArray[(x)].lastmod > 0)
231 +/* Test if a welcome entry is empty */
232 +#define WelcomeIsEmpty(x) (*WelcomeArray[(x)].text == 0)
234 +/* Get welcome create timestamp */
235 +#define WelcomeCreate(x) (WelcomeArray[(x)].create)
236 +/* Get welcome lastmod timestamp */
237 +#define WelcomeLastMod(x) (WelcomeArray[(x)].lastmod)
238 +/* Get welcome who info */
239 +#define WelcomeWho(x) (WelcomeArray[(x)].who)
240 +/* Get welcome text */
241 +#define WelcomeText(x) (WelcomeArray[(x)].text)
244 +/* Describes a Welcome message entry. */
246 + time_t create; /**< When it was set */
247 + time_t lastmod; /**< Last modification timestamp (used for resolving conflicts in burst) */
248 + char text[WELCOMELEN + 1]; /**< Message */
249 + char who[ACCOUNTLEN + 1]; /**< Who set it */
252 +/** Welcome type flags */
253 +#define WELCOME_LOCAL 0x01 /**< welcome is local */
254 +/** Welcome action flags */
255 +#define WELCOME_ANNOUNCE 0x02 /**< announce new welcome to users */
256 +#define WELCOME_UNSET 0x04 /**< unset welcome */
257 +#define WELCOME_INSERT 0x08 /**< insert welcome message, move down all others one place */
258 +#define WELCOME_DELETE 0x10 /**< delete welcome message, move up all others one place */
259 +#define WELCOME_INCLASTMOD 0x20 /**< increase lastmod if needed */
260 +#define WELCOME_FORCE 0x40 /**< force change, bypass lastmod check */
262 +extern int welcome_do(struct Client *cptr, struct Client *sptr, char *name,
263 + time_t create, time_t lastmod, char *who, char *text, unsigned int flags);
264 +extern void welcome_burst(struct Client *cptr);
265 +extern int welcome_list(struct Client *sptr, int connect);
266 +extern void welcome_stats(struct Client *sptr, const struct StatDesc *sd, char *param);
267 +extern int welcome_memory_count(size_t *we_size);
269 +#endif /* INCLUDED_welcome_h */
270 diff -r 8bf1b05cdfe7 ircd/Makefile.in
271 --- a/ircd/Makefile.in
272 +++ b/ircd/Makefile.in
289 @@ -1161,6 +1163,11 @@
290 ../include/ircd_reply.h ../include/ircd_string.h \
291 ../include/ircd_chattr.h ../include/msg.h ../include/numeric.h \
292 ../include/numnicks.h ../include/s_user.h ../include/send.h
293 +m_welcome.o: m_welcome.c ../config.h ../include/channel.h \
294 + ../include/client.h ../include/hash.h ../include/ircd.h ../include/ircd_log.h \
295 + ../include/ircd_reply.h ../include/ircd_string.h ../include/msg.h \
296 + ../include/numeric.h ../include/numnicks.h ../include/s_user.h \
297 + ../include/send.h ../include/welcome.h
298 m_who.o: m_who.c ../config.h ../include/channel.h ../include/ircd_defs.h \
299 ../include/res.h ../config.h ../include/client.h ../include/dbuf.h \
300 ../include/msgq.h ../include/ircd_events.h ../include/ircd_handler.h \
301 @@ -1422,6 +1429,13 @@
302 ../include/numnicks.h ../include/querycmds.h ../include/ircd_features.h \
303 ../include/s_misc.h ../include/s_stats.h ../include/send.h \
304 ../include/struct.h ../include/sys.h
305 +welcome.o: welcome.c ../config.h ../include/client.h \
306 + ../include/hash.h ../include/ircd.h ../include/ircd_alloc.h \
307 + ../include/ircd_features.h ../include/ircd_log.h ../include/ircd_reply.h \
308 + ../include/match.h ../include/msg.h ../include/numeric.h \
309 + ../include/numnicks.h ../include/s_debug.h ../include/s_bsd.h \
310 + ../include/s_misc.h ../include/send.h ../include/struct.h \
311 + ../include/sys.h ../include/welcome.h
312 whocmds.o: whocmds.c ../config.h ../include/whocmds.h \
313 ../include/channel.h ../include/ircd_defs.h ../include/res.h \
314 ../config.h ../include/client.h ../include/dbuf.h ../include/msgq.h \
315 diff -r 8bf1b05cdfe7 ircd/client.c
319 FlagSet(&privs_local, PRIV_WHOX);
320 FlagSet(&privs_local, PRIV_DISPLAY);
321 FlagSet(&privs_local, PRIV_FORCE_LOCAL_OPMODE);
322 + FlagSet(&privs_local, PRIV_LOCAL_WELCOME);
324 privs_defaults_set = 1;
327 ClrPriv(client, PRIV_JUPE);
328 ClrPriv(client, PRIV_OPMODE);
329 ClrPriv(client, PRIV_BADCHAN);
330 + ClrPriv(client, PRIV_WELCOME);
335 P(CHANSERV), P(XTRA_OPER), P(NOIDLE), P(FREEFORM),
336 P(PARANOID), P(CHECK), P(WALL), P(CLOSE),
337 P(ROUTE), P(ROUTEINFO), P(SERVERINFO), P(CHANNEL_PRIVACY),
339 + P(USER_PRIVACY), P(WELCOME), P(LOCAL_WELCOME),
343 diff -r 8bf1b05cdfe7 ircd/ircd_features.c
344 --- a/ircd/ircd_features.c
345 +++ b/ircd/ircd_features.c
347 F_I(IRCD_RES_TIMEOUT, 0, 4, 0),
348 F_I(AUTH_TIMEOUT, 0, 9, 0),
349 F_B(ANNOUNCE_INVITES, 0, 0, 0),
350 + F_B(WELCOME, 0, 1, 0),
352 /* features that affect all operators */
353 F_B(EXTENDED_CHECKCMD, 0, 0, 0),
355 F_B(HIS_STATS_u, 0, 1, 0),
356 F_B(HIS_STATS_U, 0, 1, 0),
357 F_B(HIS_STATS_v, 0, 1, 0),
358 + F_B(HIS_STATS_W, 0, 1, 0),
359 F_B(HIS_STATS_w, 0, 1, 0),
360 F_B(HIS_STATS_x, 0, 1, 0),
361 F_B(HIS_STATS_y, 0, 1, 0),
362 diff -r 8bf1b05cdfe7 ircd/ircd_lexer.l
363 --- a/ircd/ircd_lexer.l
364 +++ b/ircd/ircd_lexer.l
366 { "serverinfo", TPRIV_SERVERINFO },
367 { "user_privacy", TPRIV_USER_PRIVACY },
368 { "channel_privacy", TPRIV_CHANNEL_PRIVACY },
369 + { "local_welcome", TPRIV_LOCAL_WELCOME },
370 + { "welcome", TPRIV_WELCOME },
374 diff -r 8bf1b05cdfe7 ircd/ircd_parser.y
375 --- a/ircd/ircd_parser.y
376 +++ b/ircd/ircd_parser.y
378 %token TPRIV_CHANSERV TPRIV_XTRA_OPER TPRIV_NOIDLE TPRIV_FREEFORM TPRIV_PARANOID
379 %token TPRIV_CHECK TPRIV_WALL TPRIV_CLOSE TPRIV_ROUTE TPRIV_ROUTEINFO TPRIV_SERVERINFO
380 %token TPRIV_CHANNEL_PRIVACY TPRIV_USER_PRIVACY TPRIV_LIST_CHAN
381 +%token TPRIV_LOCAL_WELCOME TPRIV_WELCOME
382 /* and some types... */
384 %type <num> timespec timefactor factoredtimes factoredtime
386 TPRIV_SERVERINFO { $$ = PRIV_SERVERINFO ; } |
387 TPRIV_CHANNEL_PRIVACY { $$ = PRIV_CHANNEL_PRIVACY ; } |
388 TPRIV_USER_PRIVACY { $$ = PRIV_USER_PRIVACY ; } |
389 + TPRIV_LOCAL_WELCOME { $$ = PRIV_LOCAL_WELCOME; } |
390 + TPRIV_WELCOME { $$ = PRIV_WELCOME; } |
391 TPRIV_PARANOID { $$ = PRIV_PARANOID; } ;
392 yesorno: YES { $$ = 1; } | NO { $$ = 0; };
394 diff -r 8bf1b05cdfe7 ircd/m_welcome.c
396 +++ b/ircd/m_welcome.c
399 + * IRC - Internet Relay Chat, ircd/m_welcome.c
400 + * Copyright (C) 1990 Jarkko Oikarinen and
401 + * University of Oulu, Computing Center
403 + * See file AUTHORS in IRC package for additional names of
406 + * This program is free software; you can redistribute it and/or modify
407 + * it under the terms of the GNU General Public License as published by
408 + * the Free Software Foundation; either version 1, or (at your option)
409 + * any later version.
411 + * This program is distributed in the hope that it will be useful,
412 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
413 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
414 + * GNU General Public License for more details.
416 + * You should have received a copy of the GNU General Public License
417 + * along with this program; if not, write to the Free Software
418 + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
423 + * m_functions execute protocol messages on this server:
425 + * cptr is always NON-NULL, pointing to a *LOCAL* client
426 + * structure (with an open socket connected!). This
427 + * identifies the physical socket where the message
428 + * originated (or which caused the m_function to be
429 + * executed--some m_functions may call others...).
431 + * sptr is the source of the message, defined by the
432 + * prefix part of the message if present. If not
433 + * or prefix not found, then sptr==cptr.
435 + * (!IsServer(cptr)) => (cptr == sptr), because
436 + * prefixes are taken *only* from servers...
439 + * (sptr == cptr) => the message didn't
442 + * (sptr != cptr && IsServer(sptr) means
443 + * the prefix specified servername. (?)
445 + * (sptr != cptr && !IsServer(sptr) means
446 + * that message originated from a remote
447 + * user (not local).
451 + * (!IsServer(sptr)) means that, sptr can safely
452 + * taken as defining the target structure of the
453 + * message in this server.
455 + * *Always* true (if 'parse' and others are working correct):
457 + * 1) sptr->from == cptr (note: cptr->from == cptr)
459 + * 2) MyConnect(sptr) <=> sptr == cptr (e.g. sptr
460 + * *cannot* be a local connection, unless it's
461 + * actually cptr!). [MyConnect(x) should probably
462 + * be defined as (x == x->from) --msa ]
464 + * parc number of variable parameter strings (if zero,
465 + * parv is allowed to be NULL)
467 + * parv a NULL terminated list of parameter pointers,
469 + * parv[0], sender (prefix string), if not present
470 + * this points to an empty string.
471 + * parv[1]...parv[parc-1]
472 + * pointers to additional parameters
473 + * parv[parc] == NULL, *always*
475 + * note: it is guaranteed that parv[0]..parv[parc-1] are all
476 + * non-NULL pointers.
481 +#include "ircd_features.h"
483 +#include "numeric.h"
485 +#include "welcome.h"
489 + * m_welcome - local generic message handler
495 + * parv[0] = Send prefix
498 + * WELCOME [<server>]
501 + * parv[0] = Send prefix
505 +int m_welcome(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
507 + /* feature disabled */
508 + if (!feature_bool(FEAT_WELCOME))
509 + return send_reply(sptr, ERR_DISABLED, "WELCOME");
511 + /* only opers can set the welcome messages */
513 + return send_reply(sptr, ERR_NOPRIVILEGES);
515 + /* remote listing request, see if it is for me or a remote server
516 + * check FEAT_HIS_REMOTE to decide if an ordinary user can do this
518 + if ((parc > 1) && (hunt_server_cmd(sptr, CMD_WELCOME, cptr, feature_int(FEAT_HIS_REMOTE),
519 + "%C", 1, parc, parv) != HUNTED_ISME))
522 + /* local listing */
523 + return welcome_list(sptr, 0);
528 + * mo_welcome - oper message handler
534 + * parv[0] = Send prefix
540 + * parv[0] = Send prefix
544 + * WELCOME <server> <name> :<text>
546 + * set global or on remote server:
547 + * parv[0] = Send prefix
548 + * parv[1] = Target: server or * for global (or left out for this server)
550 + * parv[parc - 1] = Text
553 +int mo_welcome(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
555 + char *target, *name, *who, *text, pattern[BUFSIZE];
556 + time_t create, lastmod;
557 + unsigned int flags = 0;
560 + /* feature disabled */
561 + if (!feature_bool(FEAT_WELCOME))
562 + return send_reply(sptr, ERR_DISABLED, "WELCOME");
564 + /* TODO: move feature check here? */
565 + /* remote listing request, see if it is for me or a remote server */
566 + if ((parc == 2) && (hunt_server_cmd(sptr, CMD_WELCOME, cptr, 0, "%C", 1, parc, parv) != HUNTED_ISME))
569 + /* local listing */
571 + return welcome_list(sptr, 0);
574 + /* local - need PRIV LOCAL_WELCOME or WELCOME */
575 + if (parc == 3 && !HasPriv(sptr,PRIV_LOCAL_WELCOME) && !HasPriv(sptr,PRIV_WELCOME))
576 + return send_reply(sptr, ERR_NOPRIVILEGES);
578 + /* global or remote - need PRIV WELCOME */
579 + if (parc >= 4 && !HasPriv(sptr,PRIV_WELCOME))
580 + return send_reply(sptr, ERR_NOPRIVILEGES);
582 + /* set the parameters */
584 + /* target not given, only name - setting local welcome */
587 + target = cli_name(&me);
589 + flags |= WELCOME_LOCAL;
591 + /* target and name given */
597 + lastmod = TStime();
598 + who = cli_user(sptr)->opername;
599 + text = parv[parc - 1];
601 + /* target is not global */
602 + if (!(target[0] == '*' && target[1] == '\0') && !local) {
604 + /* build a pattern for hunt_server_cmd since we do not have all we need in parv */
605 + ircd_snprintf(0, pattern, sizeof(pattern), "%s %s %Tu %Tu %s :%s", "%C", name, create, lastmod, who, text);
606 + if (hunt_server_cmd(sptr, CMD_WELCOME, cptr, 0, pattern, 1, 2, parv) != HUNTED_ISME)
609 + /* else it is a local welcome, for me */
610 + flags |= WELCOME_LOCAL;
613 + /* check for anounce prefix */
614 + if (*name == '$') {
616 + /* only allow announce by oper for local welcome */
617 + if (flags & WELCOME_LOCAL)
618 + flags |= WELCOME_ANNOUNCE;
621 + /* check for force prefix */
622 + if (*name == '!') {
624 + flags |= WELCOME_FORCE;
627 + /* check for insert prefix */
628 + if (*name == '+') {
630 + flags |= WELCOME_INSERT;
633 + /* check for delete prefix */
634 + else if (*name == '-') {
636 + flags |= WELCOME_DELETE;
639 + /* empty text, set unset and delete flag */
641 + flags |= WELCOME_UNSET;
642 + flags |= WELCOME_DELETE;
646 + return welcome_do(cptr, sptr, name, create, lastmod, who, text, flags);
651 + * ms_welcome - server message handler
654 + * <source> WE <target>
657 + * parv[0] = Send prefix
658 + * parv[1] = Target: server numeric or * for global
661 + * <source> WE <target> <name> <create> <lastmod> <who> :<text>
663 + * set global or on remote server:
664 + * parv[0] = Send prefix
665 + * parv[1] = Target: server numeric or * for global
668 + * parv[4] = LastMod
670 + * parv[parc - 1] = Text
673 +int ms_welcome(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
675 + char *target, *name, *who, *text;
676 + time_t create, lastmod;
677 + unsigned int flags = 0;
679 + /* not enough - complain */
681 + protocol_violation(cptr, "Received too few parameters for WELCOME from %C (got %d - need 2)", sptr, parc);
682 + return need_more_params(sptr, "WELCOME");
685 + /* remote listing request, see if it is for me or a remote server */
687 + if (IsServer(sptr))
688 + return protocol_violation(cptr, "Received WELCOME listing request from server %C", sptr);
689 + if (hunt_server_cmd(sptr, CMD_WELCOME, cptr, 0, "%C", 1, parc, parv) != HUNTED_ISME)
691 + return welcome_list(sptr, 0);
694 + /* we need at least 7 parameters to continue - complain */
696 + protocol_violation(cptr, "Received too few parameters for WELCOME from %C (got %d - need 7)", sptr, parc);
697 + return need_more_params(sptr, "WELCOME");
700 + /* set the parameters */
703 + create = atoi(parv[3]);
704 + lastmod = atoi(parv[4]);
706 + text = parv[parc - 1]; /* parse reason as last parameter */
708 + /* check if create is valid - create is 0 but parv[3] is not */
709 + if (create == 0 && !(parv[3][0] == '0' && parv[3][1] == '\0'))
710 + return protocol_violation(cptr, "Received WELCOME with invalid create timestamp %s from %C", parv[3], sptr);
712 + /* check if lastmod is valid - lastmod is 0 but parv[4] is not */
713 + if (lastmod == 0 && !(parv[4][0] == '0' && parv[4][1] == '\0'))
714 + return protocol_violation(cptr, "Received WELCOME with invalid lastmod timestamp %s from %C", parv[4], sptr);
716 + /* target is not global */
717 + if (!(target[0] == '*' && target[1] == '\0')) {
719 + /* not for me, and forward it */
720 + if (hunt_server_cmd(sptr, CMD_WELCOME, cptr, 0, "%C %s %s %s %s :%s", 1, parc, parv) != HUNTED_ISME)
723 + /* local welcome for me */
724 + flags |= WELCOME_LOCAL;
727 + /* check for anounce prefix */
728 + if (*name == '$') {
730 + flags |= WELCOME_ANNOUNCE;
733 + /* check for force prefix */
734 + if (*name == '!') {
736 + flags |= WELCOME_FORCE;
739 + /* check for insert prefix */
740 + if (*name == '+') {
742 + flags |= WELCOME_INSERT;
745 + /* check for delete prefix */
746 + else if (*name == '-') {
748 + flags |= WELCOME_DELETE;
751 + /* empty text, set unset flag */
753 + flags |= WELCOME_UNSET;
756 + return welcome_do(cptr, sptr, name, create, lastmod, who, text, flags);
758 diff -r 8bf1b05cdfe7 ircd/parse.c
762 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
763 { m_unregistered, m_not_oper, ms_check, mo_check, m_ignore }
766 + /* add command for WELCOME */
770 + 0, MAXPARA, MFLG_SLOW, 0, NULL,
771 + /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
772 + { m_unregistered, m_welcome, ms_welcome, mo_welcome, m_ignore }
775 /* This command is an alias for QUIT during the unregistered part of
776 * of the server. This is because someone jumping via a broken web
777 diff -r 8bf1b05cdfe7 ircd/s_debug.c
784 +#include "welcome.h"
787 /* #include <assert.h> -- Now using assert in ircd_log.h */
789 aw = 0, /* aways set */
790 wwa = 0, /* whowas aways */
792 - ju = 0; /* jupes */
793 + ju = 0, /* jupes */
794 + we = 0; /* welcomes */
796 size_t chm = 0, /* memory used by channels */
797 chbm = 0, /* memory used by channel bans */
799 wwm = 0, /* whowas array memory used */
800 glm = 0, /* memory used by glines */
801 jum = 0, /* memory used by jupes */
802 + wem = 0, /* memory used by welcomes */
803 com = 0, /* memory used by conf lines */
804 dbufs_allocated = 0, /* memory used by dbufs */
805 dbufs_used = 0, /* memory used by dbufs */
807 send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG,
808 ":Glines %d(%zu) Jupes %d(%zu)", gl, glm, ju, jum);
810 + we = welcome_memory_count(&wem);
811 + send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG,
812 + ":Welcomes %d(%zu)", we, wem);
814 send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG,
815 ":Hash: client %d(%zu), chan is the same", HASHSIZE,
816 sizeof(void *) * HASHSIZE);
817 diff -r 8bf1b05cdfe7 ircd/s_err.c
822 { RPL_STATSALINE, "%s", "226" },
825 + { RPL_STATSWELCOME, "W %d %s %s %Tu %Tu :%s", "227" },
827 { RPL_STATSQLINE, "Q %s :%s", "228" },
829 @@ -1050,7 +1050,7 @@
834 + { ERR_NOSUCHWELCOME, "%s :No such welcome", "509" },
838 diff -r 8bf1b05cdfe7 ircd/s_serv.c
844 #include "userload.h"
845 +#include "welcome.h"
847 /* #include <assert.h> -- Now using assert in ircd_log.h */
853 + welcome_burst(cptr);
856 * Pass on my client information to the new server
857 diff -r 8bf1b05cdfe7 ircd/s_stats.c
863 #include "userload.h"
864 +#include "welcome.h"
869 { 'V', "vserversmach", (STAT_FLAG_OPERFEAT | STAT_FLAG_VARPARAM | STAT_FLAG_CASESENS), FEAT_HIS_STATS_v,
870 stats_servers_verbose, 0,
871 "Verbose server information." },
872 - { 'w', "userload", STAT_FLAG_OPERFEAT, FEAT_HIS_STATS_w,
873 + { 'w', "userload", STAT_FLAG_OPERFEAT | STAT_FLAG_CASESENS, FEAT_HIS_STATS_w,
875 "Userload statistics." },
876 + { 'W', "welcome", STAT_FLAG_OPERFEAT | STAT_FLAG_CASESENS, FEAT_HIS_STATS_W,
878 + "Welcome messages." },
879 { 'x', "memusage", STAT_FLAG_OPERFEAT, FEAT_HIS_STATS_x,
881 "List usage information." },
882 diff -r 8bf1b05cdfe7 ircd/s_user.c
886 #include "userload.h"
889 +#include "welcome.h"
891 #include "handlers.h" /* m_motd and m_lusers */
895 IPcheck_connect_succeeded(sptr);
898 + if (feature_bool(FEAT_WELCOME))
899 + welcome_list(sptr, 1);
902 /* apply auto sethost if needed */
903 apply_spoofblock(sptr);
904 diff -r 8bf1b05cdfe7 ircd/welcome.c
909 + * IRC - Internet Relay Chat, ircd/welcome.c
910 + * Copyright (C) 1990 Jarkko Oikarinen and
911 + * University of Oulu, Finland
912 + * Copyright (C) 2000 Kevin L. Mitchell <klmitch@mit.edu>
914 + * This program is free software; you can redistribute it and/or modify
915 + * it under the terms of the GNU General Public License as published by
916 + * the Free Software Foundation; either version 1, or (at your option)
917 + * any later version.
919 + * This program is distributed in the hope that it will be useful,
920 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
921 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
922 + * GNU General Public License for more details.
924 + * You should have received a copy of the GNU General Public License
925 + * along with this program; if not, write to the Free Software
926 + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
929 + * @brief Implementation of welcome message handling functions.
934 +#include "ircd_features.h"
935 +#include "ircd_log.h"
936 +#include "ircd_reply.h"
937 +#include "ircd_string.h"
939 +#include "numeric.h"
941 +#include "s_debug.h"
943 +#include "welcome.h"
946 +/** List of welcome messages - first MAX for global, second MAX for local */
947 +static struct Welcome WelcomeArray[WELCOME_MAX_ENTRIES * 2] = { { 0 } };
950 +/** Allocate a new welcome with the given parameters.
951 + * @param[in] name Name of the welcome message.
952 + * @param[in] text The welcome message.
953 + * @param[in] who Who set it.
954 + * @param[in] create When it was set.
955 + * @param[in] lastmod Last modification timestamp.
956 + * @return name Array number of the welcome set.
959 +welcome_make(int name, char *text, char *who, time_t create, time_t lastmod, unsigned int flags)
963 + assert(WelcomeArrayIsValid(name));
964 + assert(NULL != text);
965 + assert(NULL != who);
966 + assert(flags & WELCOME_FORCE || lastmod > 0); /* lastmod must not be 0 unless forced */
967 + assert(flags & WELCOME_LOCAL ||
968 + flags & WELCOME_FORCE ||
969 + lastmod >= WelcomeLastMod(name)); /* lastmod may not decrease for global welcome unless forced */
972 + Debug((DEBUG_DEBUG, "welcome_make(name=%d, text=\"%s\", who=%s, create=%Tu, lastmod=%Tu, "
973 + "FLAGS(0x%04x): local=%s announce=%s force=%s unset=%s insert=%s delete=%s inclastmod=%s)",
974 + name, text, who, create, lastmod, flags,
975 + (flags & WELCOME_LOCAL) ? "yes" : "no",
976 + (flags & WELCOME_ANNOUNCE) ? "yes" : "no",
977 + (flags & WELCOME_FORCE) ? "yes" : "no",
978 + (flags & WELCOME_UNSET) ? "yes" : "no",
979 + (flags & WELCOME_INSERT) ? "yes" : "no",
980 + (flags & WELCOME_DELETE) ? "yes" : "no",
981 + (flags & WELCOME_INCLASTMOD) ? "yes" : "no"));
983 + /* forced and lastmod is zero, clear text and who */
984 + if (flags & WELCOME_FORCE && lastmod == 0) {
990 + ircd_strncpy(WelcomeArray[name].text, text, WELCOMELEN);
991 + ircd_strncpy(WelcomeArray[name].who, who, ACCOUNTLEN);
993 + if (flags & WELCOME_INCLASTMOD && /* take current lastmod+1 if needed */
994 + !(flags & WELCOME_FORCE) && /* not forced */
995 + WelcomeLastMod(name) >= lastmod) /* current lastmod greater or equal than lastmod */
996 + WelcomeArray[name].lastmod = WelcomeLastMod(name) +1;
998 + WelcomeArray[name].lastmod = lastmod;
1000 + WelcomeArray[name].create = create;
1006 +/** Propagate a welcome message.
1007 + * @param[in] cptr Local client that sent us the welcome.
1008 + * @param[in] sptr Originator of the welcome.
1009 + * @param[in] nameint Name of the message.
1010 + * @param[in] create When it was set.
1011 + * @param[in] lastmod Last modification timestamp.
1012 + * @param[in] who Who set this message.
1013 + * @param[in] text The welcome message.
1014 + * @param[in] flags Flags to set on welcome.
1018 +welcome_propagate(struct Client *cptr, struct Client *sptr, int nameint,
1019 + time_t create, time_t lastmod, char *who, char *text, unsigned int flags)
1022 + assert(NULL != sptr);
1023 + assert(NULL != cptr);
1024 + assert(WelcomeNameIsValid(nameint));
1025 + assert(lastmod > 0 || flags & WELCOME_FORCE); /* lastmod must not be 0 unless forced */
1026 + assert(!(flags & WELCOME_LOCAL)); /* must not be local */
1028 + sendcmdto_serv_butone(sptr, CMD_WELCOME, cptr, "* %s%s%s%s%d %Tu %Tu %s :%s",
1029 + (flags & WELCOME_ANNOUNCE) ? "$" : "",
1030 + (flags & WELCOME_FORCE) ? "!" : "",
1031 + (flags & WELCOME_INSERT) ? "+" : "",
1032 + (flags & WELCOME_DELETE) ? "-" : "",
1033 + nameint, create, lastmod, who, text);
1039 +/** Resend a welcome message.
1040 + * @param[in] cptr Local client that sent us the welcome.
1041 + * @param[in] nameint Name of the message.
1042 + * @param[in] namearray Name of the array item.
1043 + * @param[in] flags Flags to set on welcome.
1047 +welcome_resend(struct Client *cptr, int nameint, int namearray, unsigned int flags)
1050 + int name; /* loop variable */
1053 + assert(NULL != cptr);
1054 + assert(IsServer(cptr));
1055 + assert(WelcomeNameIsValid(nameint));
1056 + assert(WelcomeArrayIsValid(namearray));
1057 + assert(nameint - 1 == namearray);
1058 + assert(!(flags & WELCOME_LOCAL)); /* must not be local */
1059 + assert(!(flags & WELCOME_FORCE)); /* must not be forced */
1061 + /* send our version */
1062 + sendcmdto_one(&me, CMD_WELCOME, cptr, "* %d %Tu %Tu %s :%s",
1064 + WelcomeCreate(namearray), WelcomeLastMod(namearray),
1065 + WelcomeWho(namearray), WelcomeText(namearray));
1067 + /* bad welcome did not have insert or delete prefix */
1068 + if (!(flags & (WELCOME_INSERT|WELCOME_DELETE)))
1071 + /* loop over global entries - nameint+1 to max - 1 */
1072 + for (name = namearray +1; name <= WELCOME_MAX_ENTRIES - 1; name++) {
1074 + /* not set, force it to be unset on the other end */
1075 + if (!WelcomeIsSet(name))
1076 + sendcmdto_one(&me, CMD_WELCOME, cptr, "* !%d 0 0 0 :", name +1);
1078 + /* set, force change here too
1079 + * other side may have this lastmod+1, without force it would be ignored
1082 + sendcmdto_one(&me, CMD_WELCOME, cptr, "* !%d %Tu %Tu %s :%s",
1084 + WelcomeCreate(name), WelcomeLastMod(name),
1085 + WelcomeWho(name), WelcomeText(name));
1092 +/** Log a welcome message.
1093 + * @param[in] sptr Originator of the welcome.
1094 + * @param[in] msg The message to show.
1095 + * @param[in] flags Flags to set on welcome.
1099 +welcome_log(struct Client *sptr, char *msg, unsigned int flags)
1102 + assert(NULL != sptr);
1103 + assert(NULL != msg);
1106 + sendto_opmask_butone(0, SNO_OLDSNO, "%s %s",
1107 + (feature_bool(FEAT_HIS_SNOTICES) || IsServer(sptr)) ?
1108 + get_client_name_and_opername(sptr) : cli_name((cli_user(sptr))->server), msg);
1111 + log_write(LS_NETWORK, L_INFO, LOG_NOSNOTICE, "%s %s", get_client_name_and_opername(sptr), msg);
1113 + /* welcome by remote oper, inform of success */
1114 + if ((flags & WELCOME_LOCAL) && IsUser(sptr) && !MyConnect(sptr)) {
1115 + sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :%s %s",
1116 + sptr, get_client_name_and_opername(sptr), msg);
1118 + /* TODO: wallops all local changes, by both local and remote opers? */
1119 + /* tell all opers about the local message being set remotely */
1120 + sendwallto_group_butone(&me, WALL_WALLOPS, 0, "%s %s", get_client_name_and_opername(sptr), msg);
1127 +/** Announce a welcome message to local clients.
1128 + * @param[in] name Welcome message to announce.
1129 + * @param[in] flags Flags to set on welcome.
1132 +welcome_announce(int name, unsigned int flags)
1134 + struct Client *acptr; /* local user */
1135 + struct MsgBuf *msgbuf; /* message to send */
1136 + int i; /* loop variable */
1139 + assert(flags & WELCOME_ANNOUNCE);
1140 + assert(WelcomeArrayIsValid(name));
1141 + assert(WelcomeIsSet(name));
1142 + assert(!WelcomeIsEmpty(name));
1144 + /* build msgbuf */
1145 + msgbuf = msgq_make(0, ":%C %s $%s :[%s] %s", &me, MSG_NOTICE,
1146 + (flags & WELCOME_LOCAL) ? cli_name(&me) : "*",
1147 + (flags & WELCOME_LOCAL) ? cli_name(&me) : feature_str(FEAT_NETWORK),
1148 + WelcomeText(name));
1150 + /* go over local clients */
1151 + for (i = HighestFd; i > 0; --i) {
1153 + /* skip unregistered clients, skip servers */
1154 + if (!(acptr = LocalClientArray[i]) || !IsRegistered(acptr) || IsServer(acptr))
1157 + /* send it away */
1158 + send_buffer(acptr, msgbuf, 0);
1163 +/** Set a welcome message.
1164 + * @param[in] cptr Local client that sent us the welcome.
1165 + * @param[in] sptr Originator of the welcome.
1166 + * @param[in] nameint Name of the message.
1167 + * @param[in] namearray Array entry.
1168 + * @param[in] create When it was set.
1169 + * @param[in] lastmod Last modification timestamp.
1170 + * @param[in] who Who set this message.
1171 + * @param[in] text The message.
1172 + * @param[in] flags Flags to set on welcome.
1176 +welcome_set(struct Client *cptr, struct Client *sptr, int nameint,
1177 + int namearray, time_t create, time_t lastmod, char *who, char *text, unsigned int flags)
1179 + char msg[BUFSIZE]; /* msg for logging */
1180 + int new = 0; /* welcome is new - not set yet */
1183 + assert(NULL != cptr);
1184 + assert(NULL != sptr);
1185 + assert(WelcomeNameIsValid(nameint));
1186 + assert(WelcomeArrayIsValid(namearray));
1187 + assert(lastmod > 0 || flags & WELCOME_FORCE); /* lastmod must not be 0 unless forced */
1188 + assert(NULL != who);
1189 + assert(NULL != text);
1190 + assert(!(flags & WELCOME_UNSET)); /* must not be unset */
1193 + Debug((DEBUG_DEBUG, "welcome_set(cptr=%s, sptr=%s, nameint=%d, namearray=%d, "
1194 + "create=%Tu, lastmod=%Tu, who=%s, text=\"%s\", "
1195 + "FLAGS(0x%04x): local=%s announce=%s force=%s unset=%s insert=%s delete=%s)",
1196 + cli_name(cptr), cli_name(sptr), nameint, namearray, create, lastmod, who, text, flags,
1197 + (flags & WELCOME_LOCAL) ? "yes" : "no",
1198 + (flags & WELCOME_ANNOUNCE) ? "yes" : "no",
1199 + (flags & WELCOME_FORCE) ? "yes" : "no",
1200 + (flags & WELCOME_UNSET) ? "yes" : "no",
1201 + (flags & WELCOME_INSERT) ? "yes" : "no",
1202 + (flags & WELCOME_DELETE) ? "yes" : "no"));
1205 + if (WelcomeIsEmpty(namearray))
1209 + welcome_make(namearray, text, who, create, lastmod, flags);
1211 + /* create msg for log */
1212 + ircd_snprintf(0, msg, 0, "%s%s%s%s WELCOME %d \"%s\" %s [%Tu]",
1213 + (flags & WELCOME_FORCE) ? "force " : "",
1214 + new ? "setting" : "changing",
1215 + (flags & WELCOME_ANNOUNCE) ? " and announcing " : " ",
1216 + (flags & WELCOME_LOCAL) ? "local" : "global",
1217 + nameint, WelcomeText(namearray), WelcomeWho(namearray), create);
1220 + welcome_log(sptr, msg, flags);
1222 + /* propagate it */
1223 + if (!(flags & WELCOME_LOCAL))
1224 + welcome_propagate(cptr, sptr, nameint, create, lastmod, who, text, flags);
1227 + if (flags & WELCOME_ANNOUNCE)
1228 + welcome_announce(namearray, flags);
1234 +/** Unset a welcome message.
1235 + * @param[in] cptr Local client that sent us the welcome.
1236 + * @param[in] sptr Originator of the welcome.
1237 + * @param[in] nameint Name of the message.
1238 + * @param[in] namearray Array entry.
1239 + * @param[in] create When it was set.
1240 + * @param[in] lastmod Last modification timestamp.
1241 + * @param[in] who Who set this message.
1242 + * @param[in] flags Flags to set on welcome.
1246 +welcome_unset(struct Client *cptr, struct Client *sptr, int nameint,
1247 + int namearray, time_t create, time_t lastmod, char *who, unsigned int flags)
1249 + char msg[BUFSIZE]; /* msg for logging */
1252 + assert(NULL != cptr);
1253 + assert(NULL != sptr);
1254 + assert(WelcomeNameIsValid(nameint));
1255 + assert(WelcomeArrayIsValid(namearray));
1256 + assert(lastmod > 0 || flags & WELCOME_FORCE); /* lastmod must not be 0 unless forced */
1257 + assert(NULL != who);
1258 + assert(flags & (WELCOME_UNSET|WELCOME_INSERT|WELCOME_DELETE)); /* must be unset, insert or delete */
1261 + Debug((DEBUG_DEBUG, "welcome_unset(cptr=%s, sptr=%s, nameint=%d, namearray=%d, "
1262 + "create=%Tu, lastmod=%Tu, who=%s, "
1263 + "FLAGS(0x%04x): local=%s announce=%s force=%s unset=%s insert=%s delete=%s)",
1264 + cli_name(cptr), cli_name(sptr), nameint, namearray, create, lastmod, who, flags,
1265 + (flags & WELCOME_LOCAL) ? "yes" : "no",
1266 + (flags & WELCOME_ANNOUNCE) ? "yes" : "no",
1267 + (flags & WELCOME_FORCE) ? "yes" : "no",
1268 + (flags & WELCOME_UNSET) ? "yes" : "no",
1269 + (flags & WELCOME_INSERT) ? "yes" : "no",
1270 + (flags & WELCOME_DELETE) ? "yes" : "no"));
1272 + /* create msg for log */
1273 + ircd_snprintf(0, msg, 0, "%sunsetting %s WELCOME %d \"%s\" %s [%Tu]",
1274 + (flags & WELCOME_FORCE) ? "force " : "",
1275 + (flags & WELCOME_LOCAL) ? "local" : "global",
1276 + nameint, WelcomeText(namearray), WelcomeWho(namearray), create);
1278 + /* log it but only if it was set
1279 + * welcome unset could have crossed with another welcome unset,
1280 + * still need to update lastmod
1281 + * can be a forced unset on a welcome that is not set
1283 + if (!WelcomeIsEmpty(namearray))
1284 + welcome_log(sptr, msg, flags);
1287 + * not when inserting, welcome_insert() handles that by calling welcome_set()
1288 + * not when deleting, welcome_delete() handles that
1290 + if (!(flags & (WELCOME_INSERT|WELCOME_DELETE)))
1291 + welcome_make(namearray, "", who, create, lastmod, flags);
1293 + /* propagate it, but not when inserting */
1294 + if (!(flags & (WELCOME_LOCAL|WELCOME_INSERT)))
1295 + welcome_propagate(cptr, sptr, nameint, create, lastmod, who, "", flags);
1301 +/** Insert a welcome message.
1302 + * @param[in] cptr Local client that sent us the welcome.
1303 + * @param[in] sptr Originator of the welcome.
1304 + * @param[in] nameint Name of the message.
1305 + * @param[in] namearray Array entry.
1306 + * @param[in] create When it was set.
1307 + * @param[in] lastmod Last modification timestamp.
1308 + * @param[in] who Who set this message.
1309 + * @param[in] text The welcome message.
1310 + * @param[in] flags Flags to set on welcome.
1314 +welcome_insert(struct Client *cptr, struct Client *sptr, int nameint,
1315 + int namearray, time_t create, time_t lastmod, char *who, char *text, unsigned int flags)
1317 + char msg[BUFSIZE]; /* msg for logging */
1318 + int i; /* loop variable */
1319 + int empty = -1; /* first empty spot in array after namearray */
1320 + int end = WELCOME_MAX_ENTRIES -1; /* last element to check in array */
1321 + int last = end; /* last welcome message to feed to welcome_unset */
1324 + assert(NULL != cptr);
1325 + assert(NULL != sptr);
1326 + assert(WelcomeNameIsValid(nameint));
1327 + assert(WelcomeArrayIsValid(namearray));
1328 + assert(lastmod > 0 || flags & WELCOME_FORCE); /* lastmod must not be 0 unless forced */
1329 + assert(NULL != who);
1330 + assert(NULL != text);
1331 + assert(flags & WELCOME_INSERT); /* must be insert */
1334 + Debug((DEBUG_DEBUG, "welcome_insert(cptr=%s, sptr=%s, nameint=%d, namearray=%d, "
1335 + "create=%Tu, lastmod=%Tu, who=%s, text=\"%s\", "
1336 + "FLAGS(0x%04x): local=%s announce=%s force=%s unset=%s insert=%s delete=%s)",
1337 + cli_name(cptr), cli_name(sptr), nameint, namearray, create, lastmod, who, text, flags,
1338 + (flags & WELCOME_LOCAL) ? "yes" : "no",
1339 + (flags & WELCOME_ANNOUNCE) ? "yes" : "no",
1340 + (flags & WELCOME_FORCE) ? "yes" : "no",
1341 + (flags & WELCOME_UNSET) ? "yes" : "no",
1342 + (flags & WELCOME_INSERT) ? "yes" : "no",
1343 + (flags & WELCOME_DELETE) ? "yes" : "no"));
1345 + /* correct end for local offset */
1346 + if (flags & WELCOME_LOCAL)
1347 + end += WELCOME_MAX_ENTRIES;
1349 + /* find first empty spot */
1350 + for (i = namearray; i <= end; i++) {
1351 + if (WelcomeIsEmpty(i)) {
1357 + /* no empty spot, need to unset last */
1358 + if (empty == -1) {
1359 + welcome_unset(cptr, sptr, end +1, end, create, lastmod, who, flags);
1363 + /* move entries down, update lastmod */
1364 + for (i = empty; i > namearray; i--)
1365 + welcome_make(i, WelcomeText(i-1), WelcomeWho(i-1), WelcomeCreate(i-1),
1366 + lastmod, flags | WELCOME_INCLASTMOD);
1368 + /* correct empty for local offset */
1369 + if (flags & WELCOME_LOCAL)
1370 + empty -= WELCOME_MAX_ENTRIES;
1372 + /* create msg for log */
1373 + if (nameint == empty)
1374 + ircd_snprintf(0, msg, 0, "moving %s WELCOME message %d one place down",
1375 + (flags & WELCOME_LOCAL) ? "local" : "global", nameint);
1377 + ircd_snprintf(0, msg, 0, "moving %s WELCOME message %d %s %d one place down",
1378 + (flags & WELCOME_LOCAL) ? "local" : "global", nameint, (empty - nameint > 1) ? "to" : "and" , empty);
1381 + welcome_log(sptr, msg, flags);
1384 + welcome_set(cptr, sptr, nameint, namearray, create, lastmod, who, text, flags);
1390 +/** Delete a welcome message.
1391 + * @param[in] cptr Local client that sent us the welcome.
1392 + * @param[in] sptr Originator of the welcome.
1393 + * @param[in] nameint Name of the message.
1394 + * @param[in] namearray Array entry.
1395 + * @param[in] create When it was set.
1396 + * @param[in] lastmod Last modification timestamp.
1397 + * @param[in] who Who set this message.
1398 + * @param[in] flags Flags to set on welcome.
1402 +welcome_delete(struct Client *cptr, struct Client *sptr, int nameint,
1403 + int namearray, time_t create, time_t lastmod, char *who, unsigned int flags)
1405 + int i; /* loop variable */
1406 + int empty = namearray; /* first empty spot in array after namearray */
1407 + int end = WELCOME_MAX_ENTRIES -1; /* last element to check in array */
1410 + assert(NULL != cptr);
1411 + assert(NULL != sptr);
1412 + assert(WelcomeNameIsValid(nameint));
1413 + assert(WelcomeArrayIsValid(namearray));
1414 + assert(lastmod > 0 || flags & WELCOME_FORCE); /* lastmod must not be 0 unless forced */
1415 + assert(NULL != who);
1416 + assert(flags & WELCOME_UNSET); /* must be unset */
1417 + assert(flags & WELCOME_DELETE); /* must be delete */
1420 + Debug((DEBUG_DEBUG, "welcome_delete(cptr=%s, sptr=%s, nameint=%d, namearray=%d, "
1421 + "create=%Tu, lastmod=%Tu, who=%s, "
1422 + "FLAGS(0x%04x): local=%s announce=%s force=%s unset=%s insert=%s delete=%s)",
1423 + cli_name(cptr), cli_name(sptr), nameint, namearray, create, lastmod, who, flags,
1424 + (flags & WELCOME_LOCAL) ? "yes" : "no",
1425 + (flags & WELCOME_ANNOUNCE) ? "yes" : "no",
1426 + (flags & WELCOME_FORCE) ? "yes" : "no",
1427 + (flags & WELCOME_UNSET) ? "yes" : "no",
1428 + (flags & WELCOME_INSERT) ? "yes" : "no",
1429 + (flags & WELCOME_DELETE) ? "yes" : "no"));
1432 + welcome_unset(cptr, sptr, nameint, namearray, create, lastmod, who, flags);
1434 + /* correct end for local offset */
1435 + if (flags & WELCOME_LOCAL)
1436 + end += WELCOME_MAX_ENTRIES;
1438 + /* move entries up, update lastmod */
1439 + for (i = namearray; i < end; i++) {
1440 + if (!WelcomeIsSet(i+1))
1442 + welcome_make(i, WelcomeText(i+1), WelcomeWho(i+1), WelcomeCreate(i+1),
1443 + lastmod, flags | WELCOME_INCLASTMOD);
1446 + /* clear last entry */
1448 + welcome_make(i, "", who, create, lastmod, flags | WELCOME_INCLASTMOD);
1450 + /* nothing was moved, clear entry */
1451 + if (i == namearray)
1452 + welcome_make(i, "", who, create, lastmod, flags);
1458 +/** Change a welcome message.
1459 + * @param[in] cptr Local client that sent us the welcome.
1460 + * @param[in] sptr Originator of the welcome.
1461 + * @param[in] name Name of the message.
1462 + * @param[in] create When it was set.
1463 + * @param[in] lastmod Last modification timestamp.
1464 + * @param[in] who Who set this message.
1465 + * @param[in] text The welcome message.
1466 + * @param[in] flags Flags to set on welcome.
1470 +welcome_do(struct Client *cptr, struct Client *sptr, char *name,
1471 + time_t create, time_t lastmod, char *who, char *text, unsigned int flags)
1473 + int nameint = atoi(name); /* transform to int */
1474 + int namearray = nameint - 1; /* used to test the array element */
1475 + int start = 0; /* this is the first server setting this welcome message from a user */
1478 + assert(NULL != cptr);
1479 + assert(NULL != sptr);
1480 + assert(NULL != name);
1481 + assert(NULL != text);
1482 + assert(NULL != who);
1485 + Debug((DEBUG_DEBUG, "welcome_do(cptr=%s, sptr=%s, name=%s, "
1486 + "create=%Tu, lastmod=%Tu, who=%s, text=\"%s\", "
1487 + "FLAGS(0x%04x): local=%s announce=%s force=%s unset=%s insert=%s delete=%s)",
1488 + cli_name(cptr), cli_name(sptr), name, create, lastmod, who, text, flags,
1489 + (flags & WELCOME_LOCAL) ? "yes" : "no",
1490 + (flags & WELCOME_ANNOUNCE) ? "yes" : "no",
1491 + (flags & WELCOME_FORCE) ? "yes" : "no",
1492 + (flags & WELCOME_UNSET) ? "yes" : "no",
1493 + (flags & WELCOME_INSERT) ? "yes" : "no",
1494 + (flags & WELCOME_DELETE) ? "yes" : "no"));
1496 + /* welcome from my user, or a local welcome from a local/remote user */
1497 + start = (IsUser(sptr) && (MyConnect(sptr) || flags & WELCOME_LOCAL));
1499 + /* name empty after taking off the prefixes? */
1502 + sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :Welcome: No message number given", sptr);
1504 + protocol_violation(cptr, "Received WELCOME with no message number from %C", sptr);
1509 + if (!WelcomeArrayIsValid(namearray)) {
1511 + sendcmdto_one(&me, CMD_NOTICE, sptr,
1512 + "%C :Welcome: Invalid message number %s - should between 1 and %d",
1513 + sptr, name, WELCOME_MAX_ENTRIES);
1515 + protocol_violation(cptr,
1516 + "Received WELCOME with invalid message number %s from %C - should be between 1 and %d",
1517 + name, sptr, WELCOME_MAX_ENTRIES);
1521 + /* invalid lastmod */
1522 + if (lastmod <= 0) {
1523 + /* from my user - must be a glitch,
1524 + * someone set (my) network time to 0 ?
1528 + /* not forced or negative, it is a protocol violation */
1529 + else if (!(flags & WELCOME_FORCE) || lastmod < 0)
1530 + return protocol_violation(cptr, "Received WELCOME with invalid lastmod timestamp %Tu from %C", lastmod, sptr);
1533 + /* source is user, and is myuser or welcome is local, check length of the message */
1534 + if (start && strlen(text) > WELCOMELEN) {
1535 + sendcmdto_one(&me, CMD_NOTICE, sptr,
1536 + "%C :Welcome: The message is too long with %d chars - max is %d chars",
1537 + sptr, strlen(text), WELCOMELEN);
1538 + ircd_strncpy(text, text, WELCOMELEN);
1539 + sendcmdto_one(&me, CMD_NOTICE, sptr,
1540 + "%C :Welcome: Change or truncate the message to: \"%s\"", sptr, text);
1544 + /* correct namearray for local offset */
1545 + if (flags & WELCOME_LOCAL)
1546 + namearray += WELCOME_MAX_ENTRIES;
1548 + /* must be true by now */
1549 + assert(WelcomeArrayIsValid(namearray));
1550 + assert(WelcomeNameIsValid(nameint));
1552 + /* cannot unset welcome that is not set */
1553 + if (!WelcomeIsSet(namearray) && flags & WELCOME_UNSET) {
1555 + /* from user, throw error */
1557 + return send_reply(sptr, ERR_NOSUCHWELCOME, name);
1559 + /* new local welcome from server, but empty - ignore
1560 + * we do accept a new global welcome message that is empty
1562 + if (flags & WELCOME_LOCAL)
1566 + /* check if there is something to change */
1567 + /* we got a record for it */
1568 + if (WelcomeIsSet(namearray)) {
1570 + /* global and not forced */
1571 + if (!(flags & (WELCOME_LOCAL|WELCOME_FORCE))) {
1573 + /* myuser changes it,
1574 + * WelcomeLastMod greater than or equal to lastmod, take WelcomeLastMod+1 as lastmod
1575 + * else the change is not accepted upstream because of the older TS
1577 + if (MyUser(sptr)) {
1578 + if (WelcomeLastMod(namearray) >= lastmod)
1579 + lastmod = WelcomeLastMod(namearray) +1;
1582 + /* compare lastmod, ignore welcome when:
1583 + * we got a newer one
1584 + * or when lastmod is the same and our text is 'smaller'
1586 + else if (lastmod < WelcomeLastMod(namearray) || /* we got a newer one */
1587 + (lastmod == WelcomeLastMod(namearray) && /* same lastmod */
1588 + strcmp(WelcomeText(namearray), text) < 0)) { /* our text is 'smaller' */
1589 + /* burst or burst ack, cptr gets our version from the burst */
1590 + if (IsBurstOrBurstAck(cptr))
1593 + return welcome_resend(cptr, nameint, namearray, flags);
1596 + /* local welcome - we use our idea of the time */
1597 + } else if (flags & WELCOME_LOCAL) {
1598 + create = TStime();
1599 + lastmod = TStime();
1602 + /* welcome from my user or local welcome from local/remote user
1603 + * compare new message with old message
1604 + * use strcmp instead of ircd_strcmp - oper may wish to change case
1606 + if (start && strcmp(text, WelcomeText(namearray)) == 0) {
1607 + sendcmdto_one(&me, CMD_NOTICE, sptr,
1608 + "%C :Welcome: Cannot change %s message for %s - nothing to change",
1609 + sptr, (flags & WELCOME_LOCAL) ? "local" : "global", name);
1614 + /* welcome from my user, or local welcome from local/remote user
1615 + * forcing and unsetting, set create and lastmod to 0
1616 + * clear delete flag
1618 + if (start && (flags & WELCOME_FORCE) && (flags & WELCOME_UNSET)) {
1619 + flags &= ~WELCOME_DELETE;
1624 + /* clear insert flag
1625 + * when this flag is set, welcome_unset() assumes it is being called from welcome_insert()
1626 + * and does not propagate the change - welcome_delete() also calls welcome_unset()
1627 + * do not insert last global/local welcome
1628 + * do not insert when entry is not set
1631 + if (flags & WELCOME_INSERT &&
1632 + (flags & WELCOME_UNSET || !WelcomeIsSet(namearray) || nameint == WELCOME_MAX_ENTRIES))
1633 + flags &= ~WELCOME_INSERT;
1635 + /* clear delete flag when not unsetting so we do not propagate the - delete prefix */
1636 + if (flags & WELCOME_DELETE && !(flags & WELCOME_UNSET))
1637 + flags &= ~WELCOME_DELETE;
1640 + if (flags & WELCOME_UNSET) {
1643 + if (flags & WELCOME_DELETE)
1644 + return welcome_delete(cptr, sptr, nameint, namearray, create, lastmod, who, flags);
1647 + return welcome_unset(cptr, sptr, nameint, namearray, create, lastmod, who, flags);
1651 + if (flags & WELCOME_INSERT)
1652 + return welcome_insert(cptr, sptr, nameint, namearray, create, lastmod, who, text, flags);
1654 + /* new or change */
1655 + return welcome_set(cptr, sptr, nameint, namearray, create, lastmod, who, text, flags);
1659 +/** Send the full list of welcome message to \a cptr.
1660 + * @param[in] cptr Local server to send welcomes to.
1663 +welcome_burst(struct Client *cptr)
1665 + int name; /* loop variable */
1668 + assert(NULL != cptr);
1670 + /* loop over global entries - 0 to max - 1 */
1671 + for (name = 0; name <= WELCOME_MAX_ENTRIES - 1; name++) {
1672 + if (WelcomeIsSet(name))
1673 + sendcmdto_one(&me, CMD_WELCOME, cptr, "* %d %Tu %Tu %s :%s",
1675 + WelcomeCreate(name), WelcomeLastMod(name),
1676 + WelcomeWho(name), WelcomeText(name));
1681 +/** List welcome messages.
1682 + * @param[in] sptr Client requesting the listing.
1683 + * @param[in] connect When non zero do not report no welcome is set
1687 +welcome_list(struct Client *sptr, int connect)
1689 + int name; /* loop variable */
1690 + int found = 0; /* number of welcome messages set */
1691 + int local = 0; /* welcome is local or global */
1694 + assert(NULL != sptr);
1696 + /* loop over all entries - range 0 to 2 * max - 1 */
1697 + for (name = 0; name <= 2 * WELCOME_MAX_ENTRIES - 1; name++) {
1699 + /* local entries now */
1700 + if (name == WELCOME_MAX_ENTRIES)
1703 + /* not set or empty - skip */
1704 + if (!WelcomeIsSet(name) || WelcomeIsEmpty(name))
1709 + sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :[%s] %s",
1710 + sptr, local ? cli_name(&me) : feature_str(FEAT_NETWORK), WelcomeText(name));
1714 + if (!found && !connect)
1715 + sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :No welcome message set.", sptr);
1721 +/** Statistics callback to list Welcome messages.
1722 + * @param[in] sptr Client requesting statistics.
1723 + * @param[in] sd Stats descriptor for request (ignored).
1724 + * @param[in] param Extra parameter from user (ignored).
1727 +welcome_stats(struct Client *sptr, const struct StatDesc *sd, char *param)
1729 + int name; /* loop variable */
1730 + int local = 0; /* welcome is local or global */
1733 + assert(NULL != sptr);
1735 + /* stats header */
1736 + send_reply(sptr, SND_EXPLICIT | RPL_STATSWELCOME,
1737 + "W # Target Who Timestamp LastMod :Text");
1739 + /* loop over all entries - range 0 to 2 * max - 1 */
1740 + for (name = 0; name <= 2 * WELCOME_MAX_ENTRIES - 1; name++) {
1742 + /* local entries now */
1743 + if (name == WELCOME_MAX_ENTRIES)
1747 + if (!WelcomeIsSet(name))
1751 + send_reply(sptr, RPL_STATSWELCOME,
1752 + local ? name + 1 - WELCOME_MAX_ENTRIES : name + 1,
1753 + local ? cli_name(&me) : "*",
1755 + WelcomeCreate(name), WelcomeLastMod(name),
1756 + WelcomeIsEmpty(name) ? "<Empty>" : WelcomeText(name));
1761 +/** Count welcome messages and memory used by them.
1762 + * @param[out] we_size Receives total number of bytes allocated for welcomes.
1763 + * @return Number of welcome messages currently allocated.
1766 +welcome_memory_count(size_t *we_size)
1768 + int name; /* loop variable */
1769 + unsigned int we = 0; /* number of welcome messages set */
1771 + /* loop over all entries - range 0 to 2 * max - 1 */
1772 + for (name = 0; name <= 2 * WELCOME_MAX_ENTRIES - 1; name++) {
1775 + if (!WelcomeIsSet(name))
1780 + *we_size += WelcomeText(name) ? (strlen(WelcomeText(name)) + 1) : 0;
1781 + *we_size += WelcomeWho(name) ? (strlen(WelcomeWho(name)) + 1) : 0;