]> jfr.im git - irc/quakenet/snircd-patchqueue.git/blob - welcome.patch
nickgline: include nick! bit in gline loggin
[irc/quakenet/snircd-patchqueue.git] / welcome.patch
1 Add welcome message functionality.
2
3 To inform our users about events we currently have 3 means at our disposal.
4
5 Outside of IRC like our website and other external/3rd party means such as facebook/twitter/whatever.
6 I never visit the website, and I cannot imagine many people do just so they wont miss that lonely newspost every couple of months.
7 (If it were an RSS newsfeed, then maybe it would reach more people..., but that's a different topic altogether.)
8
9 Within the IRC world there are two ways to mass relay information to users.
10
11 The first is the MOTD (Message of the Day).
12 This is a misleading name, it simply is a text file played to connecting clients, and in practice never changes
13 (our MOTDs display outdated information, and done so for years..., but again, that's a different topic altogether).
14 The MOTD cannot be altered from IRC, so it cannot be used to inform users about upcoming events or news.
15
16 The second are message on IRC sent to many recipients,
17 broadcasts to all users on the network/server/country/host,
18 and WALLUSERS to all users with mode +w set.
19 They are not suitable to inform users about upcoming events or news:
20
21 They only reach users which are connected at that time, and not everyone is connected to IRC all the time.
22 Doing a broadcast one time a few days in advance and again a few minutes in advance, simply does not reach all of our users.
23 Doing multiple broadcasts to make up of this, is annoying for users
24 (as seen in the past when several broadcasts were made for a tutorial session) and thus not an option.
25
26 Broadcasts are rather intrusive, and sending many of them probably wont be appreciated,
27 leading to people adding the service(s) to ignore or filtering them.
28
29 WALLUSERS is only received by a very small portion of our userbase, 1 to 2% of users on the network have set mode +w.
30 We could make +w a default usermode (opt-out instead of opt-in), but we are left with the same problem as with broadcasts.
31
32
33 We need a way to be able to announce news to all users,
34 in a timely manner straight from IRC (so not the website, not the MOTD),
35 in a non-intrusive way with a message on connect, and when people want to see it by command (so not broadcasts or wallusers).
36
37 This is where WELCOME comes in.
38 Messages set on IRC.
39 Messages set per server (mostly for maintenance) or global (for news/pr/and such).
40 Messages announced to connecting clients, and shown when users use /welcome.
41
42
43 Why not use a service for this?
44 Implemented in the IRCd it only uses local traffic, that is from server to client,
45 instead of traffic over server links from service to server, and then server to client.
46 Implemented in the IRCd it is not affected by netsplits as it would when done by a service.
47
48
49 (But even if this patch is accepted, it will not be on quakenet any time soon.
50 So it may still be worth it to create service for this in the mean time.)
51
52
53
54
55
56 client commands:
57 user:
58 /WELCOME [<server>]
59 shows welcome messages set, same is shown on connect
60 feature HIS_REMOTE controls whether ordinary users can request a listing from a remote server
61
62 operator:
63 /WELCOME [<server>] [[$][!][+|-]<N> :<message>]
64 to view welcome messages from a remote server
65 to set a local welcome message on this server or a remote server
66 set a global welcome message (server *)
67 the $ prefix makes the server annouce the welcome message to its clients when setting
68 the ! prefix forces the change, bypassing lastmod checks
69 the + prefix moves message in N and all after that one spot down, and inserts the new message
70 in spot N, if there is no room, the last entry is deleted
71 the - prefix is used when an entry is cleared (no text), then all entries after it are moved on place up, so all empty
72 spots are at the end (this prefix is always used when an oper clears the text)
73
74 server:
75 :<source> WE <target> [[$][!][+|-]<N> <timestamp> <lastmod> <who> :<text>]
76 who is who set the message, the server puts in the opername when a client sets it.
77 :<N> is a number 1 to WELCOME_MAX_ENTRIES - currently set at 10 (should be more than we ever need)
78 that means there is room for 10 local and 10 global entries
79
80 STATS W/welcome (/STATS w/userload made case sensitive)
81 :server 227 nick W # Target Who Timestamp LastMod :Text
82 :server 227 nick W 1 * opername 1233072583 1233072583 :Latest news: testing this welcome patch :)
83 :server 227 nick W 2 * opername 1233072583 1233072583 :
84 :server 227 nick W 1 servername opername 1233072590 1233072590 :This is a test server, expect restarts.
85 :server 219 nick W :End of /STATS report
86
87 listing welcomes or on connect:
88 :server NOTICE nick :[QuakeNet] Latest news: testing this welcome patch :)
89 :server NOTICE nick :[server] This is a test server, expect restarts.
90
91 announcement is done by a notice by the local server to $* ($servername for local) with the same message
92 format as for listing welcome messages.
93 :server NOTICE $* :[QuakeNet] Latest news: testing this welcome patch :)
94 :server NOTICE $server :[server] This is a test server, expect restarts.
95
96
97
98 Files:
99
100 include/handlers.h
101 add m_welcome mo_welcome ms_welcome functions
102
103 include/features.h
104 ircd/features.c
105 add features FEAT_WELCOME and FEAT_HIS_STATS_W
106
107 include/msg.h
108 add MSG_WELCOME TOK_WELCOME CMD_WELCOME
109
110 ircd/parse.c
111 add welcome message functions
112
113 include/numeric.h
114 ircd/s_err.c
115 add RPL_STATSWELCOME ERR_NOSUCHWELCOME
116
117 include/welcome.h
118 ircd/welcome.c
119 ircd/m_welcome.c
120 new
121
122 ircd/Makefile.in
123 add welcome.c and m_welcome.c files
124
125 ircd/s_serv.c
126 add burst welcome message
127
128 ircd/s_stats.c
129 add /STATS W/welcome
130
131 ircd/s_user.c
132 add showing of welcome messages on connect
133
134 ircd/s_debug.c
135 add count and memusage of welcome messages
136
137 include/client.h
138 ircd/client.c
139 ircd/ircd_lexer.l
140 ircd/ircd_parser.y
141 add PRIV_LOCAL_WELCOME PRIV_WELCOME
142
143 diff -r 9096546c6212 include/client.h
144 --- a/include/client.h Sat Jul 20 12:00:51 2013 +0100
145 +++ b/include/client.h Sat Jul 20 12:00:55 2013 +0100
146 @@ -142,6 +142,8 @@
147 PRIV_USER_PRIVACY, /* oper can bypass user privacy +x etc gives i.e. see real ip's */
148 PRIV_CHANNEL_PRIVACY, /* oper can bypass channel privacy i.e. can see modes on channels they are not on and channel keys */
149 PRIV_SERVERINFO, /* oper can use /get, /stats, /hash, retrieve remote information */
150 + PRIV_WELCOME, /* oper can WELCOME */
151 + PRIV_LOCAL_WELCOME, /* oper can local WELCOME */
152 PRIV_LAST_PRIV /**< number of privileges */
153 };
154
155 diff -r 9096546c6212 include/handlers.h
156 --- a/include/handlers.h Sat Jul 20 12:00:51 2013 +0100
157 +++ b/include/handlers.h Sat Jul 20 12:00:55 2013 +0100
158 @@ -138,6 +138,7 @@
159 extern int m_version(struct Client*, struct Client*, int, char*[]);
160 extern int m_wallchops(struct Client*, struct Client*, int, char*[]);
161 extern int m_wallvoices(struct Client*, struct Client*, int, char*[]);
162 +extern int m_welcome(struct Client*, struct Client*, int, char*[]);
163 extern int m_who(struct Client*, struct Client*, int, char*[]);
164 extern int m_whois(struct Client*, struct Client*, int, char*[]);
165 extern int m_whowas(struct Client*, struct Client*, int, char*[]);
166 @@ -172,6 +173,7 @@
167 extern int mo_version(struct Client*, struct Client*, int, char*[]);
168 extern int mo_wallops(struct Client*, struct Client*, int, char*[]);
169 extern int mo_wallusers(struct Client*, struct Client*, int, char*[]);
170 +extern int mo_welcome(struct Client*, struct Client*, int, char*[]);
171 extern int mo_xquery(struct Client*, struct Client*, int, char*[]);
172 extern int mr_error(struct Client*, struct Client*, int, char*[]);
173 extern int mr_error(struct Client*, struct Client*, int, char*[]);
174 @@ -231,6 +233,7 @@
175 extern int ms_wallops(struct Client*, struct Client*, int, char*[]);
176 extern int ms_wallusers(struct Client*, struct Client*, int, char*[]);
177 extern int ms_wallvoices(struct Client*, struct Client*, int, char*[]);
178 +extern int ms_welcome(struct Client*, struct Client*, int, char*[]);
179 extern int ms_whois(struct Client*, struct Client*, int, char*[]);
180 extern int ms_xquery(struct Client*, struct Client*, int, char*[]);
181 extern int ms_xreply(struct Client*, struct Client*, int, char*[]);
182 diff -r 9096546c6212 include/ircd_features.h
183 --- a/include/ircd_features.h Sat Jul 20 12:00:51 2013 +0100
184 +++ b/include/ircd_features.h Sat Jul 20 12:00:55 2013 +0100
185 @@ -101,6 +101,7 @@
186 FEAT_IRCD_RES_TIMEOUT,
187 FEAT_AUTH_TIMEOUT,
188 FEAT_ANNOUNCE_INVITES,
189 + FEAT_WELCOME,
190
191 /* features that affect all operators */
192 FEAT_EXTENDED_CHECKCMD,
193 @@ -142,6 +143,7 @@
194 FEAT_HIS_STATS_u,
195 FEAT_HIS_STATS_U,
196 FEAT_HIS_STATS_v,
197 + FEAT_HIS_STATS_W,
198 FEAT_HIS_STATS_w,
199 FEAT_HIS_STATS_x,
200 FEAT_HIS_STATS_y,
201 diff -r 9096546c6212 include/msg.h
202 --- a/include/msg.h Sat Jul 20 12:00:51 2013 +0100
203 +++ b/include/msg.h Sat Jul 20 12:00:55 2013 +0100
204 @@ -196,6 +196,10 @@
205 #define TOK_NOTICE "O"
206 #define CMD_NOTICE MSG_NOTICE, TOK_NOTICE
207
208 +#define MSG_WELCOME "WELCOME" /* WELC */
209 +#define TOK_WELCOME "WE"
210 +#define CMD_WELCOME MSG_WELCOME, TOK_WELCOME
211 +
212 #define MSG_WALLCHOPS "WALLCHOPS" /* WC */
213 #define TOK_WALLCHOPS "WC"
214 #define CMD_WALLCHOPS MSG_WALLCHOPS, TOK_WALLCHOPS
215 diff -r 9096546c6212 include/numeric.h
216 --- a/include/numeric.h Sat Jul 20 12:00:51 2013 +0100
217 +++ b/include/numeric.h Sat Jul 20 12:00:55 2013 +0100
218 @@ -116,6 +116,7 @@
219 RPL_STATSGLINE 227 Dalnet
220 RPL_STATSVLINE 227 unreal */
221 #define RPL_STATSALINE 226 /* Hybrid, Undernet */
222 +#define RPL_STATSWELCOME 227 /* QuakeNet extension */
223 #define RPL_STATSQLINE 228 /* Undernet extension */
224
225 /* RPL_SERVICEINFO 231 unused */
226 @@ -440,6 +441,8 @@
227 /* ERR_GHOSTEDCLIENT 503 efnet */
228 /* ERR_VWORLDWARN 503 austnet */
229
230 +#define ERR_NOSUCHWELCOME 509 /* QuakeNet extension */
231 +
232 #define ERR_SILELISTFULL 511 /* Undernet extension */
233 /* ERR_NOTIFYFULL 512 aircd */
234 /* ERR_TOOMANYWATCH 512 Numeric List: Dalnet */
235 diff -r 9096546c6212 include/welcome.h
236 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
237 +++ b/include/welcome.h Sat Jul 20 12:00:55 2013 +0100
238 @@ -0,0 +1,86 @@
239 +#ifndef INCLUDED_welcome_h
240 +#define INCLUDED_welcome_h
241 +/*
242 + * IRC - Internet Relay Chat, include/welcome.h
243 + * Copyright (C) 1990 Jarkko Oikarinen and
244 + * University of Oulu, Computing Center
245 + * Copyright (C) 2000 Kevin L. Mitchell <klmitch@mit.edu>
246 + *
247 + * This program is free software; you can redistribute it and/or modify
248 + * it under the terms of the GNU General Public License as published by
249 + * the Free Software Foundation; either version 2, or (at your option)
250 + * any later version.
251 + *
252 + * This program is distributed in the hope that it will be useful,
253 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
254 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
255 + * GNU General Public License for more details.
256 + *
257 + * You should have received a copy of the GNU General Public License
258 + * along with this program; if not, write to the Free Software
259 + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
260 + */
261 +/** @file
262 + * @brief Interface and declarations for welcome message handling.
263 + */
264 +
265 +struct Client;
266 +struct StatDesc;
267 +
268 +/* Maximum number of welcome entries (per type; X global, X local) */
269 +#define WELCOME_MAX_ENTRIES 10
270 +/* Maximum length of a welcome message
271 + * the maximum value for this is 300
272 + * when set larger, this could lead to truncation when announcing
273 + * ":server.name NOTICE $server.name :[server.name] text"
274 + * 510 - (1+63+1+6+1+1+63+1+1+1+63+1+1) = 306 for text
275 + * max length of a servername is 63 HOSTLEN
276 + */
277 +#define WELCOMELEN 300
278 +
279 +
280 +/* Test if a welcome entry is in a valid range */
281 +#define WelcomeArrayIsValid(x) ((unsigned) (x) <= 2 * WELCOME_MAX_ENTRIES -1)
282 +/* Test if a welcome name is in a valid range */
283 +#define WelcomeNameIsValid(x) ((unsigned) (x) <= WELCOME_MAX_ENTRIES)
284 +/* Test if a welcome entry is set */
285 +#define WelcomeIsSet(x) (WelcomeArray[(x)].lastmod > 0)
286 +/* Test if a welcome entry is empty */
287 +#define WelcomeIsEmpty(x) (*WelcomeArray[(x)].text == 0)
288 +
289 +/* Get welcome create timestamp */
290 +#define WelcomeCreate(x) (WelcomeArray[(x)].create)
291 +/* Get welcome lastmod timestamp */
292 +#define WelcomeLastMod(x) (WelcomeArray[(x)].lastmod)
293 +/* Get welcome who info */
294 +#define WelcomeWho(x) (WelcomeArray[(x)].who)
295 +/* Get welcome text */
296 +#define WelcomeText(x) (WelcomeArray[(x)].text)
297 +
298 +
299 +/* Describes a Welcome message entry. */
300 +struct Welcome {
301 + time_t create; /**< When it was set */
302 + time_t lastmod; /**< Last modification timestamp (used for resolving conflicts in burst) */
303 + char text[WELCOMELEN + 1]; /**< Message */
304 + char who[ACCOUNTLEN + 1]; /**< Who set it */
305 +};
306 +
307 +/** Welcome type flags */
308 +#define WELCOME_LOCAL 0x01 /**< welcome is local */
309 +/** Welcome action flags */
310 +#define WELCOME_ANNOUNCE 0x02 /**< announce new welcome to users */
311 +#define WELCOME_UNSET 0x04 /**< unset welcome */
312 +#define WELCOME_INSERT 0x08 /**< insert welcome message, move down all others one place */
313 +#define WELCOME_DELETE 0x10 /**< delete welcome message, move up all others one place */
314 +#define WELCOME_INCLASTMOD 0x20 /**< increase lastmod if needed */
315 +#define WELCOME_FORCE 0x40 /**< force change, bypass lastmod check */
316 +
317 +extern int welcome_do(struct Client *cptr, struct Client *sptr, char *name,
318 + time_t create, time_t lastmod, char *who, char *text, unsigned int flags);
319 +extern void welcome_burst(struct Client *cptr);
320 +extern int welcome_list(struct Client *sptr, int connect);
321 +extern void welcome_stats(struct Client *sptr, const struct StatDesc *sd, char *param);
322 +extern int welcome_memory_count(size_t *we_size);
323 +
324 +#endif /* INCLUDED_welcome_h */
325 diff -r 9096546c6212 ircd/Makefile.in
326 --- a/ircd/Makefile.in Sat Jul 20 12:00:51 2013 +0100
327 +++ b/ircd/Makefile.in Sat Jul 20 12:00:55 2013 +0100
328 @@ -187,6 +187,7 @@
329 m_wallops.c \
330 m_wallusers.c \
331 m_wallvoices.c \
332 + m_welcome.c \
333 m_who.c \
334 m_whois.c \
335 m_whowas.c \
336 @@ -216,6 +217,7 @@
337 send.c \
338 uping.c \
339 userload.c \
340 + welcome.c \
341 whocmds.c \
342 whowas.c \
343 y.tab.c
344 @@ -1162,6 +1164,11 @@
345 ../include/ircd_reply.h ../include/ircd_string.h \
346 ../include/ircd_chattr.h ../include/msg.h ../include/numeric.h \
347 ../include/numnicks.h ../include/s_user.h ../include/send.h
348 +m_welcome.o: m_welcome.c ../config.h ../include/channel.h \
349 + ../include/client.h ../include/hash.h ../include/ircd.h ../include/ircd_log.h \
350 + ../include/ircd_reply.h ../include/ircd_string.h ../include/msg.h \
351 + ../include/numeric.h ../include/numnicks.h ../include/s_user.h \
352 + ../include/send.h ../include/welcome.h
353 m_who.o: m_who.c ../config.h ../include/channel.h ../include/ircd_defs.h \
354 ../include/res.h ../config.h ../include/client.h ../include/dbuf.h \
355 ../include/msgq.h ../include/ircd_events.h ../include/ircd_handler.h \
356 @@ -1423,6 +1430,13 @@
357 ../include/numnicks.h ../include/querycmds.h ../include/ircd_features.h \
358 ../include/s_misc.h ../include/s_stats.h ../include/send.h \
359 ../include/struct.h ../include/sys.h
360 +welcome.o: welcome.c ../config.h ../include/client.h \
361 + ../include/hash.h ../include/ircd.h ../include/ircd_alloc.h \
362 + ../include/ircd_features.h ../include/ircd_log.h ../include/ircd_reply.h \
363 + ../include/match.h ../include/msg.h ../include/numeric.h \
364 + ../include/numnicks.h ../include/s_debug.h ../include/s_bsd.h \
365 + ../include/s_misc.h ../include/send.h ../include/struct.h \
366 + ../include/sys.h ../include/welcome.h
367 whocmds.o: whocmds.c ../config.h ../include/whocmds.h \
368 ../include/channel.h ../include/ircd_defs.h ../include/res.h \
369 ../config.h ../include/client.h ../include/dbuf.h ../include/msgq.h \
370 diff -r 9096546c6212 ircd/client.c
371 --- a/ircd/client.c Sat Jul 20 12:00:51 2013 +0100
372 +++ b/ircd/client.c Sat Jul 20 12:00:55 2013 +0100
373 @@ -177,6 +177,7 @@
374 FlagSet(&privs_local, PRIV_WHOX);
375 FlagSet(&privs_local, PRIV_DISPLAY);
376 FlagSet(&privs_local, PRIV_FORCE_LOCAL_OPMODE);
377 + FlagSet(&privs_local, PRIV_LOCAL_WELCOME);
378
379 privs_defaults_set = 1;
380 }
381 @@ -223,6 +224,7 @@
382 ClrPriv(client, PRIV_JUPE);
383 ClrPriv(client, PRIV_OPMODE);
384 ClrPriv(client, PRIV_BADCHAN);
385 + ClrPriv(client, PRIV_WELCOME);
386 }
387 }
388
389 @@ -244,7 +246,7 @@
390 P(CHANSERV), P(XTRA_OPER), P(NOIDLE), P(FREEFORM),
391 P(PARANOID), P(CHECK), P(WALL), P(CLOSE),
392 P(ROUTE), P(ROUTEINFO), P(SERVERINFO), P(CHANNEL_PRIVACY),
393 - P(USER_PRIVACY),
394 + P(USER_PRIVACY), P(WELCOME), P(LOCAL_WELCOME),
395 #undef P
396 { 0, 0 }
397 };
398 diff -r 9096546c6212 ircd/ircd_features.c
399 --- a/ircd/ircd_features.c Sat Jul 20 12:00:51 2013 +0100
400 +++ b/ircd/ircd_features.c Sat Jul 20 12:00:55 2013 +0100
401 @@ -366,6 +366,7 @@
402 F_I(IRCD_RES_TIMEOUT, 0, 4, 0),
403 F_I(AUTH_TIMEOUT, 0, 9, 0),
404 F_B(ANNOUNCE_INVITES, 0, 0, 0),
405 + F_B(WELCOME, 0, 1, 0),
406
407 /* features that affect all operators */
408 F_B(EXTENDED_CHECKCMD, 0, 0, 0),
409 @@ -407,6 +408,7 @@
410 F_B(HIS_STATS_u, 0, 1, 0),
411 F_B(HIS_STATS_U, 0, 1, 0),
412 F_B(HIS_STATS_v, 0, 1, 0),
413 + F_B(HIS_STATS_W, 0, 1, 0),
414 F_B(HIS_STATS_w, 0, 1, 0),
415 F_B(HIS_STATS_x, 0, 1, 0),
416 F_B(HIS_STATS_y, 0, 1, 0),
417 diff -r 9096546c6212 ircd/ircd_lexer.l
418 --- a/ircd/ircd_lexer.l Sat Jul 20 12:00:51 2013 +0100
419 +++ b/ircd/ircd_lexer.l Sat Jul 20 12:00:55 2013 +0100
420 @@ -166,6 +166,8 @@
421 { "serverinfo", TPRIV_SERVERINFO },
422 { "user_privacy", TPRIV_USER_PRIVACY },
423 { "channel_privacy", TPRIV_CHANNEL_PRIVACY },
424 + { "local_welcome", TPRIV_LOCAL_WELCOME },
425 + { "welcome", TPRIV_WELCOME },
426 { NULL, 0 }
427 };
428 static int ntokens;
429 diff -r 9096546c6212 ircd/ircd_parser.y
430 --- a/ircd/ircd_parser.y Sat Jul 20 12:00:51 2013 +0100
431 +++ b/ircd/ircd_parser.y Sat Jul 20 12:00:55 2013 +0100
432 @@ -189,6 +189,7 @@
433 %token TPRIV_CHANSERV TPRIV_XTRA_OPER TPRIV_NOIDLE TPRIV_FREEFORM TPRIV_PARANOID
434 %token TPRIV_CHECK TPRIV_WALL TPRIV_CLOSE TPRIV_ROUTE TPRIV_ROUTEINFO TPRIV_SERVERINFO
435 %token TPRIV_CHANNEL_PRIVACY TPRIV_USER_PRIVACY TPRIV_LIST_CHAN
436 +%token TPRIV_LOCAL_WELCOME TPRIV_WELCOME
437 /* and some types... */
438 %type <num> sizespec
439 %type <num> timespec timefactor factoredtimes factoredtime
440 @@ -703,6 +704,8 @@
441 TPRIV_SERVERINFO { $$ = PRIV_SERVERINFO ; } |
442 TPRIV_CHANNEL_PRIVACY { $$ = PRIV_CHANNEL_PRIVACY ; } |
443 TPRIV_USER_PRIVACY { $$ = PRIV_USER_PRIVACY ; } |
444 + TPRIV_LOCAL_WELCOME { $$ = PRIV_LOCAL_WELCOME; } |
445 + TPRIV_WELCOME { $$ = PRIV_WELCOME; } |
446 TPRIV_PARANOID { $$ = PRIV_PARANOID; } ;
447
448 yesorno: YES { $$ = 1; } | NO { $$ = 0; };
449 diff -r 9096546c6212 ircd/m_welcome.c
450 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
451 +++ b/ircd/m_welcome.c Sat Jul 20 12:00:55 2013 +0100
452 @@ -0,0 +1,360 @@
453 +/*
454 + * IRC - Internet Relay Chat, ircd/m_welcome.c
455 + * Copyright (C) 1990 Jarkko Oikarinen and
456 + * University of Oulu, Computing Center
457 + *
458 + * See file AUTHORS in IRC package for additional names of
459 + * the programmers.
460 + *
461 + * This program is free software; you can redistribute it and/or modify
462 + * it under the terms of the GNU General Public License as published by
463 + * the Free Software Foundation; either version 1, or (at your option)
464 + * any later version.
465 + *
466 + * This program is distributed in the hope that it will be useful,
467 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
468 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
469 + * GNU General Public License for more details.
470 + *
471 + * You should have received a copy of the GNU General Public License
472 + * along with this program; if not, write to the Free Software
473 + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
474 + *
475 + */
476 +
477 +/*
478 + * m_functions execute protocol messages on this server:
479 + *
480 + * cptr is always NON-NULL, pointing to a *LOCAL* client
481 + * structure (with an open socket connected!). This
482 + * identifies the physical socket where the message
483 + * originated (or which caused the m_function to be
484 + * executed--some m_functions may call others...).
485 + *
486 + * sptr is the source of the message, defined by the
487 + * prefix part of the message if present. If not
488 + * or prefix not found, then sptr==cptr.
489 + *
490 + * (!IsServer(cptr)) => (cptr == sptr), because
491 + * prefixes are taken *only* from servers...
492 + *
493 + * (IsServer(cptr))
494 + * (sptr == cptr) => the message didn't
495 + * have the prefix.
496 + *
497 + * (sptr != cptr && IsServer(sptr) means
498 + * the prefix specified servername. (?)
499 + *
500 + * (sptr != cptr && !IsServer(sptr) means
501 + * that message originated from a remote
502 + * user (not local).
503 + *
504 + * combining
505 + *
506 + * (!IsServer(sptr)) means that, sptr can safely
507 + * taken as defining the target structure of the
508 + * message in this server.
509 + *
510 + * *Always* true (if 'parse' and others are working correct):
511 + *
512 + * 1) sptr->from == cptr (note: cptr->from == cptr)
513 + *
514 + * 2) MyConnect(sptr) <=> sptr == cptr (e.g. sptr
515 + * *cannot* be a local connection, unless it's
516 + * actually cptr!). [MyConnect(x) should probably
517 + * be defined as (x == x->from) --msa ]
518 + *
519 + * parc number of variable parameter strings (if zero,
520 + * parv is allowed to be NULL)
521 + *
522 + * parv a NULL terminated list of parameter pointers,
523 + *
524 + * parv[0], sender (prefix string), if not present
525 + * this points to an empty string.
526 + * parv[1]...parv[parc-1]
527 + * pointers to additional parameters
528 + * parv[parc] == NULL, *always*
529 + *
530 + * note: it is guaranteed that parv[0]..parv[parc-1] are all
531 + * non-NULL pointers.
532 + */
533 +
534 +#include "client.h"
535 +#include "ircd.h"
536 +#include "ircd_features.h"
537 +#include "msg.h"
538 +#include "numeric.h"
539 +#include "s_user.h"
540 +#include "welcome.h"
541 +
542 +
543 +/*
544 + * m_welcome - local generic message handler
545 + *
546 + *
547 + * WELCOME
548 + *
549 + * listing:
550 + * parv[0] = Send prefix
551 + *
552 + *
553 + * WELCOME [<server>]
554 + *
555 + * remote listing:
556 + * parv[0] = Send prefix
557 + * parv[1] = Target
558 + *
559 + */
560 +int m_welcome(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
561 +{
562 + /* feature disabled */
563 + if (!feature_bool(FEAT_WELCOME))
564 + return send_reply(sptr, ERR_DISABLED, "WELCOME");
565 +
566 + /* only opers can set the welcome messages */
567 + if (parc > 2)
568 + return send_reply(sptr, ERR_NOPRIVILEGES);
569 +
570 + /* remote listing request, see if it is for me or a remote server
571 + * check FEAT_HIS_REMOTE to decide if an ordinary user can do this
572 + */
573 + if ((parc > 1) && (hunt_server_cmd(sptr, CMD_WELCOME, cptr, feature_int(FEAT_HIS_REMOTE),
574 + "%C", 1, parc, parv) != HUNTED_ISME))
575 + return 0;
576 +
577 + /* local listing */
578 + return welcome_list(sptr, 0);
579 +}
580 +
581 +
582 +/*
583 + * mo_welcome - oper message handler
584 + *
585 + *
586 + * WELCOME
587 + *
588 + * listing:
589 + * parv[0] = Send prefix
590 + *
591 + *
592 + * WELCOME <server>
593 + *
594 + * remote listing:
595 + * parv[0] = Send prefix
596 + * parv[1] = Target
597 + *
598 + *
599 + * WELCOME <server> <name> :<text>
600 + *
601 + * set global or on remote server:
602 + * parv[0] = Send prefix
603 + * parv[1] = Target: server or * for global (or left out for this server)
604 + * parv[2] = Name
605 + * parv[parc - 1] = Text
606 + *
607 + */
608 +int mo_welcome(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
609 +{
610 + char *target, *name, *who, *text, pattern[BUFSIZE];
611 + time_t create, lastmod;
612 + unsigned int flags = 0;
613 + int local = 0;
614 +
615 + /* feature disabled */
616 + if (!feature_bool(FEAT_WELCOME))
617 + return send_reply(sptr, ERR_DISABLED, "WELCOME");
618 +
619 + /* TODO: move feature check here? */
620 + /* remote listing request, see if it is for me or a remote server */
621 + if ((parc == 2) && (hunt_server_cmd(sptr, CMD_WELCOME, cptr, 0, "%C", 1, parc, parv) != HUNTED_ISME))
622 + return 0;
623 +
624 + /* local listing */
625 + if (parc <= 2)
626 + return welcome_list(sptr, 0);
627 +
628 + /* check PRIVS */
629 + /* local - need PRIV LOCAL_WELCOME or WELCOME */
630 + if (parc == 3 && !HasPriv(sptr,PRIV_LOCAL_WELCOME) && !HasPriv(sptr,PRIV_WELCOME))
631 + return send_reply(sptr, ERR_NOPRIVILEGES);
632 +
633 + /* global or remote - need PRIV WELCOME */
634 + if (parc >= 4 && !HasPriv(sptr,PRIV_WELCOME))
635 + return send_reply(sptr, ERR_NOPRIVILEGES);
636 +
637 + /* set the parameters */
638 +
639 + /* target not given, only name - setting local welcome */
640 + if (parc < 4) {
641 + local++;
642 + target = cli_name(&me);
643 + name = parv[1];
644 + flags |= WELCOME_LOCAL;
645 +
646 + /* target and name given */
647 + } else {
648 + target = parv[1];
649 + name = parv[2];
650 + }
651 + create = TStime();
652 + lastmod = TStime();
653 + who = cli_user(sptr)->opername;
654 + text = parv[parc - 1];
655 +
656 + /* target is not global */
657 + if (!(target[0] == '*' && target[1] == '\0') && !local) {
658 +
659 + /* build a pattern for hunt_server_cmd since we do not have all we need in parv */
660 + ircd_snprintf(0, pattern, sizeof(pattern), "%s %s %Tu %Tu %s :%s", "%C", name, create, lastmod, who, text);
661 + if (hunt_server_cmd(sptr, CMD_WELCOME, cptr, 0, pattern, 1, 2, parv) != HUNTED_ISME)
662 + return 0;
663 +
664 + /* else it is a local welcome, for me */
665 + flags |= WELCOME_LOCAL;
666 + }
667 +
668 + /* check for anounce prefix */
669 + if (*name == '$') {
670 + name++;
671 + /* only allow announce by oper for local welcome */
672 + if (flags & WELCOME_LOCAL)
673 + flags |= WELCOME_ANNOUNCE;
674 + }
675 +
676 + /* check for force prefix */
677 + if (*name == '!') {
678 + name++;
679 + flags |= WELCOME_FORCE;
680 + }
681 +
682 + /* check for insert prefix */
683 + if (*name == '+') {
684 + name++;
685 + flags |= WELCOME_INSERT;
686 + }
687 +
688 + /* check for delete prefix */
689 + else if (*name == '-') {
690 + name++;
691 + flags |= WELCOME_DELETE;
692 + }
693 +
694 + /* empty text, set unset and delete flag */
695 + if (*text == 0) {
696 + flags |= WELCOME_UNSET;
697 + flags |= WELCOME_DELETE;
698 + }
699 +
700 + /* and do it */
701 + return welcome_do(cptr, sptr, name, create, lastmod, who, text, flags);
702 +}
703 +
704 +
705 +/*
706 + * ms_welcome - server message handler
707 + *
708 + *
709 + * <source> WE <target>
710 + *
711 + * remote listing:
712 + * parv[0] = Send prefix
713 + * parv[1] = Target: server numeric or * for global
714 + *
715 + *
716 + * <source> WE <target> <name> <create> <lastmod> <who> :<text>
717 + *
718 + * set global or on remote server:
719 + * parv[0] = Send prefix
720 + * parv[1] = Target: server numeric or * for global
721 + * parv[2] = Name
722 + * parv[3] = Create
723 + * parv[4] = LastMod
724 + * parv[5] = Who
725 + * parv[parc - 1] = Text
726 + *
727 + */
728 +int ms_welcome(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
729 +{
730 + char *target, *name, *who, *text;
731 + time_t create, lastmod;
732 + unsigned int flags = 0;
733 +
734 + /* not enough - complain */
735 + if (parc < 2) {
736 + protocol_violation(cptr, "Received too few parameters for WELCOME from %C (got %d - need 2)", sptr, parc);
737 + return need_more_params(sptr, "WELCOME");
738 + }
739 +
740 + /* remote listing request, see if it is for me or a remote server */
741 + if (parc == 2) {
742 + if (IsServer(sptr))
743 + return protocol_violation(cptr, "Received WELCOME listing request from server %C", sptr);
744 + if (hunt_server_cmd(sptr, CMD_WELCOME, cptr, 0, "%C", 1, parc, parv) != HUNTED_ISME)
745 + return 0;
746 + return welcome_list(sptr, 0);
747 + }
748 +
749 + /* we need at least 7 parameters to continue - complain */
750 + if (parc < 7) {
751 + protocol_violation(cptr, "Received too few parameters for WELCOME from %C (got %d - need 7)", sptr, parc);
752 + return need_more_params(sptr, "WELCOME");
753 + }
754 +
755 + /* set the parameters */
756 + target = parv[1];
757 + name = parv[2];
758 + create = atoi(parv[3]);
759 + lastmod = atoi(parv[4]);
760 + who = parv[5];
761 + text = parv[parc - 1]; /* parse reason as last parameter */
762 +
763 + /* check if create is valid - create is 0 but parv[3] is not */
764 + if (create == 0 && !(parv[3][0] == '0' && parv[3][1] == '\0'))
765 + return protocol_violation(cptr, "Received WELCOME with invalid create timestamp %s from %C", parv[3], sptr);
766 +
767 + /* check if lastmod is valid - lastmod is 0 but parv[4] is not */
768 + if (lastmod == 0 && !(parv[4][0] == '0' && parv[4][1] == '\0'))
769 + return protocol_violation(cptr, "Received WELCOME with invalid lastmod timestamp %s from %C", parv[4], sptr);
770 +
771 + /* target is not global */
772 + if (!(target[0] == '*' && target[1] == '\0')) {
773 +
774 + /* not for me, and forward it */
775 + if (hunt_server_cmd(sptr, CMD_WELCOME, cptr, 0, "%C %s %s %s %s :%s", 1, parc, parv) != HUNTED_ISME)
776 + return 0;
777 +
778 + /* local welcome for me */
779 + flags |= WELCOME_LOCAL;
780 + }
781 +
782 + /* check for anounce prefix */
783 + if (*name == '$') {
784 + name++;
785 + flags |= WELCOME_ANNOUNCE;
786 + }
787 +
788 + /* check for force prefix */
789 + if (*name == '!') {
790 + name++;
791 + flags |= WELCOME_FORCE;
792 + }
793 +
794 + /* check for insert prefix */
795 + if (*name == '+') {
796 + name++;
797 + flags |= WELCOME_INSERT;
798 + }
799 +
800 + /* check for delete prefix */
801 + else if (*name == '-') {
802 + name++;
803 + flags |= WELCOME_DELETE;
804 + }
805 +
806 + /* empty text, set unset flag */
807 + if (*text == 0)
808 + flags |= WELCOME_UNSET;
809 +
810 + /* and do it */
811 + return welcome_do(cptr, sptr, name, create, lastmod, who, text, flags);
812 +}
813 diff -r 9096546c6212 ircd/parse.c
814 --- a/ircd/parse.c Sat Jul 20 12:00:51 2013 +0100
815 +++ b/ircd/parse.c Sat Jul 20 12:00:55 2013 +0100
816 @@ -668,6 +668,15 @@
817 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
818 { m_unregistered, m_not_oper, ms_check, mo_check, m_ignore }
819 },
820 +
821 + /* add command for WELCOME */
822 + {
823 + MSG_WELCOME,
824 + TOK_WELCOME,
825 + 0, MAXPARA, MFLG_SLOW, 0, NULL,
826 + /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
827 + { m_unregistered, m_welcome, ms_welcome, mo_welcome, m_ignore }
828 + },
829
830 /* This command is an alias for QUIT during the unregistered part of
831 * of the server. This is because someone jumping via a broken web
832 diff -r 9096546c6212 ircd/s_debug.c
833 --- a/ircd/s_debug.c Sat Jul 20 12:00:51 2013 +0100
834 +++ b/ircd/s_debug.c Sat Jul 20 12:00:55 2013 +0100
835 @@ -50,6 +50,7 @@
836 #include "send.h"
837 #include "struct.h"
838 #include "sys.h"
839 +#include "welcome.h"
840 #include "whowas.h"
841
842 /* #include <assert.h> -- Now using assert in ircd_log.h */
843 @@ -231,7 +232,8 @@
844 aw = 0, /* aways set */
845 wwa = 0, /* whowas aways */
846 gl = 0, /* glines */
847 - ju = 0; /* jupes */
848 + ju = 0, /* jupes */
849 + we = 0; /* welcomes */
850
851 size_t chm = 0, /* memory used by channels */
852 chbm = 0, /* memory used by channel bans */
853 @@ -244,6 +246,7 @@
854 wwm = 0, /* whowas array memory used */
855 glm = 0, /* memory used by glines */
856 jum = 0, /* memory used by jupes */
857 + wem = 0, /* memory used by welcomes */
858 com = 0, /* memory used by conf lines */
859 dbufs_allocated = 0, /* memory used by dbufs */
860 dbufs_used = 0, /* memory used by dbufs */
861 @@ -351,6 +354,10 @@
862 send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG,
863 ":Glines %d(%zu) Jupes %d(%zu)", gl, glm, ju, jum);
864
865 + we = welcome_memory_count(&wem);
866 + send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG,
867 + ":Welcomes %d(%zu)", we, wem);
868 +
869 send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG,
870 ":Hash: client %d(%zu), chan is the same", HASHSIZE,
871 sizeof(void *) * HASHSIZE);
872 diff -r 9096546c6212 ircd/s_err.c
873 --- a/ircd/s_err.c Sat Jul 20 12:00:51 2013 +0100
874 +++ b/ircd/s_err.c Sat Jul 20 12:00:55 2013 +0100
875 @@ -486,7 +486,7 @@
876 /* 226 */
877 { RPL_STATSALINE, "%s", "226" },
878 /* 227 */
879 - { 0 },
880 + { RPL_STATSWELCOME, "W %d %s %s %Tu %Tu :%s", "227" },
881 /* 228 */
882 { RPL_STATSQLINE, "Q %s :%s", "228" },
883 /* 229 */
884 @@ -1050,7 +1050,7 @@
885 /* 508 */
886 { 0 },
887 /* 509 */
888 - { 0 },
889 + { ERR_NOSUCHWELCOME, "%s :No such welcome", "509" },
890 /* 510 */
891 { 0 },
892 /* 511 */
893 diff -r 9096546c6212 ircd/s_serv.c
894 --- a/ircd/s_serv.c Sat Jul 20 12:00:51 2013 +0100
895 +++ b/ircd/s_serv.c Sat Jul 20 12:00:55 2013 +0100
896 @@ -57,6 +57,7 @@
897 #include "struct.h"
898 #include "sys.h"
899 #include "userload.h"
900 +#include "welcome.h"
901
902 /* #include <assert.h> -- Now using assert in ircd_log.h */
903 #include <stdlib.h>
904 @@ -196,6 +197,7 @@
905 */
906 gline_burst(cptr);
907 jupe_burst(cptr);
908 + welcome_burst(cptr);
909
910 /*
911 * Pass on my client information to the new server
912 diff -r 9096546c6212 ircd/s_stats.c
913 --- a/ircd/s_stats.c Sat Jul 20 12:00:51 2013 +0100
914 +++ b/ircd/s_stats.c Sat Jul 20 12:00:55 2013 +0100
915 @@ -54,6 +54,7 @@
916 #include "send.h"
917 #include "struct.h"
918 #include "userload.h"
919 +#include "welcome.h"
920
921 #include <stdio.h>
922 #include <stdlib.h>
923 @@ -650,9 +651,12 @@
924 { 'V', "vserversmach", (STAT_FLAG_OPERFEAT | STAT_FLAG_VARPARAM | STAT_FLAG_CASESENS), FEAT_HIS_STATS_v,
925 stats_servers_verbose, 0,
926 "Verbose server information." },
927 - { 'w', "userload", STAT_FLAG_OPERFEAT, FEAT_HIS_STATS_w,
928 + { 'w', "userload", STAT_FLAG_OPERFEAT | STAT_FLAG_CASESENS, FEAT_HIS_STATS_w,
929 calc_load, 0,
930 "Userload statistics." },
931 + { 'W', "welcome", STAT_FLAG_OPERFEAT | STAT_FLAG_CASESENS, FEAT_HIS_STATS_W,
932 + welcome_stats, 0,
933 + "Welcome messages." },
934 { 'x', "memusage", STAT_FLAG_OPERFEAT, FEAT_HIS_STATS_x,
935 stats_meminfo, 0,
936 "List usage information." },
937 diff -r 9096546c6212 ircd/s_user.c
938 --- a/ircd/s_user.c Sat Jul 20 12:00:51 2013 +0100
939 +++ b/ircd/s_user.c Sat Jul 20 12:00:55 2013 +0100
940 @@ -63,6 +63,7 @@
941 #include "userload.h"
942 #include "version.h"
943 #include "whowas.h"
944 +#include "welcome.h"
945
946 #include "handlers.h" /* m_motd and m_lusers */
947
948 @@ -402,6 +403,10 @@
949
950 IPcheck_connect_succeeded(sptr);
951
952 + /* send welcome */
953 + if (feature_bool(FEAT_WELCOME))
954 + welcome_list(sptr, 1);
955 +
956 /* TODO: */
957 /* apply auto sethost if needed */
958 apply_spoofblock(sptr);
959 diff -r 9096546c6212 ircd/welcome.c
960 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
961 +++ b/ircd/welcome.c Sat Jul 20 12:00:55 2013 +0100
962 @@ -0,0 +1,877 @@
963 +/*
964 + * IRC - Internet Relay Chat, ircd/welcome.c
965 + * Copyright (C) 1990 Jarkko Oikarinen and
966 + * University of Oulu, Finland
967 + * Copyright (C) 2000 Kevin L. Mitchell <klmitch@mit.edu>
968 + *
969 + * This program is free software; you can redistribute it and/or modify
970 + * it under the terms of the GNU General Public License as published by
971 + * the Free Software Foundation; either version 1, or (at your option)
972 + * any later version.
973 + *
974 + * This program is distributed in the hope that it will be useful,
975 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
976 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
977 + * GNU General Public License for more details.
978 + *
979 + * You should have received a copy of the GNU General Public License
980 + * along with this program; if not, write to the Free Software
981 + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
982 + */
983 +/** @file
984 + * @brief Implementation of welcome message handling functions.
985 + */
986 +
987 +#include "client.h"
988 +#include "ircd.h"
989 +#include "ircd_features.h"
990 +#include "ircd_log.h"
991 +#include "ircd_reply.h"
992 +#include "ircd_string.h"
993 +#include "msg.h"
994 +#include "numeric.h"
995 +#include "s_bsd.h"
996 +#include "s_debug.h"
997 +#include "send.h"
998 +#include "welcome.h"
999 +
1000 +
1001 +/** List of welcome messages - first MAX for global, second MAX for local */
1002 +static struct Welcome WelcomeArray[WELCOME_MAX_ENTRIES * 2] = { { 0 } };
1003 +
1004 +
1005 +/** Allocate a new welcome with the given parameters.
1006 + * @param[in] name Name of the welcome message.
1007 + * @param[in] text The welcome message.
1008 + * @param[in] who Who set it.
1009 + * @param[in] create When it was set.
1010 + * @param[in] lastmod Last modification timestamp.
1011 + * @return name Array number of the welcome set.
1012 + */
1013 +static int
1014 +welcome_make(int name, char *text, char *who, time_t create, time_t lastmod, unsigned int flags)
1015 +{
1016 +
1017 + /* assert */
1018 + assert(WelcomeArrayIsValid(name));
1019 + assert(NULL != text);
1020 + assert(NULL != who);
1021 + assert(flags & WELCOME_FORCE || lastmod > 0); /* lastmod must not be 0 unless forced */
1022 + assert(flags & WELCOME_LOCAL ||
1023 + flags & WELCOME_FORCE ||
1024 + lastmod >= WelcomeLastMod(name)); /* lastmod may not decrease for global welcome unless forced */
1025 +
1026 + /* debug */
1027 + Debug((DEBUG_DEBUG, "welcome_make(name=%d, text=\"%s\", who=%s, create=%Tu, lastmod=%Tu, "
1028 + "FLAGS(0x%04x): local=%s announce=%s force=%s unset=%s insert=%s delete=%s inclastmod=%s)",
1029 + name, text, who, create, lastmod, flags,
1030 + (flags & WELCOME_LOCAL) ? "yes" : "no",
1031 + (flags & WELCOME_ANNOUNCE) ? "yes" : "no",
1032 + (flags & WELCOME_FORCE) ? "yes" : "no",
1033 + (flags & WELCOME_UNSET) ? "yes" : "no",
1034 + (flags & WELCOME_INSERT) ? "yes" : "no",
1035 + (flags & WELCOME_DELETE) ? "yes" : "no",
1036 + (flags & WELCOME_INCLASTMOD) ? "yes" : "no"));
1037 +
1038 + /* forced and lastmod is zero, clear text and who */
1039 + if (flags & WELCOME_FORCE && lastmod == 0) {
1040 + text = "";
1041 + who = "";
1042 + }
1043 +
1044 + /* store it */
1045 + ircd_strncpy(WelcomeArray[name].text, text, WELCOMELEN);
1046 + ircd_strncpy(WelcomeArray[name].who, who, ACCOUNTLEN);
1047 +
1048 + if (flags & WELCOME_INCLASTMOD && /* take current lastmod+1 if needed */
1049 + !(flags & WELCOME_FORCE) && /* not forced */
1050 + WelcomeLastMod(name) >= lastmod) /* current lastmod greater or equal than lastmod */
1051 + WelcomeArray[name].lastmod = WelcomeLastMod(name) +1;
1052 + else
1053 + WelcomeArray[name].lastmod = lastmod;
1054 +
1055 + WelcomeArray[name].create = create;
1056 +
1057 + return name;
1058 +}
1059 +
1060 +
1061 +/** Propagate a welcome message.
1062 + * @param[in] cptr Local client that sent us the welcome.
1063 + * @param[in] sptr Originator of the welcome.
1064 + * @param[in] nameint Name of the message.
1065 + * @param[in] create When it was set.
1066 + * @param[in] lastmod Last modification timestamp.
1067 + * @param[in] who Who set this message.
1068 + * @param[in] text The welcome message.
1069 + * @param[in] flags Flags to set on welcome.
1070 + * @return Zero
1071 + */
1072 +int
1073 +welcome_propagate(struct Client *cptr, struct Client *sptr, int nameint,
1074 + time_t create, time_t lastmod, char *who, char *text, unsigned int flags)
1075 +{
1076 + /* assert */
1077 + assert(NULL != sptr);
1078 + assert(NULL != cptr);
1079 + assert(WelcomeNameIsValid(nameint));
1080 + assert(lastmod > 0 || flags & WELCOME_FORCE); /* lastmod must not be 0 unless forced */
1081 + assert(!(flags & WELCOME_LOCAL)); /* must not be local */
1082 +
1083 + sendcmdto_serv_butone(sptr, CMD_WELCOME, cptr, "* %s%s%s%s%d %Tu %Tu %s :%s",
1084 + (flags & WELCOME_ANNOUNCE) ? "$" : "",
1085 + (flags & WELCOME_FORCE) ? "!" : "",
1086 + (flags & WELCOME_INSERT) ? "+" : "",
1087 + (flags & WELCOME_DELETE) ? "-" : "",
1088 + nameint, create, lastmod, who, text);
1089 +
1090 + return 0;
1091 +}
1092 +
1093 +
1094 +/** Resend a welcome message.
1095 + * @param[in] cptr Local client that sent us the welcome.
1096 + * @param[in] nameint Name of the message.
1097 + * @param[in] namearray Name of the array item.
1098 + * @param[in] flags Flags to set on welcome.
1099 + * @return Zero
1100 + */
1101 +int
1102 +welcome_resend(struct Client *cptr, int nameint, int namearray, unsigned int flags)
1103 +{
1104 +
1105 + int name; /* loop variable */
1106 +
1107 + /* assert */
1108 + assert(NULL != cptr);
1109 + assert(IsServer(cptr));
1110 + assert(WelcomeNameIsValid(nameint));
1111 + assert(WelcomeArrayIsValid(namearray));
1112 + assert(nameint - 1 == namearray);
1113 + assert(!(flags & WELCOME_LOCAL)); /* must not be local */
1114 + assert(!(flags & WELCOME_FORCE)); /* must not be forced */
1115 +
1116 + /* send our version */
1117 + sendcmdto_one(&me, CMD_WELCOME, cptr, "* %d %Tu %Tu %s :%s",
1118 + nameint,
1119 + WelcomeCreate(namearray), WelcomeLastMod(namearray),
1120 + WelcomeWho(namearray), WelcomeText(namearray));
1121 +
1122 + /* bad welcome did not have insert or delete prefix */
1123 + if (!(flags & (WELCOME_INSERT|WELCOME_DELETE)))
1124 + return 0;
1125 +
1126 + /* loop over global entries - namearray +1 to max - 1 */
1127 + for (name = namearray +1; name <= WELCOME_MAX_ENTRIES - 1; name++) {
1128 +
1129 + /* not set, force it to be unset on the other end */
1130 + if (!WelcomeIsSet(name))
1131 + sendcmdto_one(&me, CMD_WELCOME, cptr, "* !%d 0 0 0 :", name +1);
1132 +
1133 + /* set, force change here too
1134 + * other side may have this lastmod+1, without force it would be ignored
1135 + */
1136 + else
1137 + sendcmdto_one(&me, CMD_WELCOME, cptr, "* !%d %Tu %Tu %s :%s",
1138 + name +1,
1139 + WelcomeCreate(name), WelcomeLastMod(name),
1140 + WelcomeWho(name), WelcomeText(name));
1141 + }
1142 +
1143 + return 0;
1144 +}
1145 +
1146 +
1147 +/** Log a welcome message.
1148 + * @param[in] sptr Originator of the welcome.
1149 + * @param[in] msg The message to show.
1150 + * @param[in] flags Flags to set on welcome.
1151 + * @return Zero
1152 + */
1153 +int
1154 +welcome_log(struct Client *sptr, char *msg, unsigned int flags)
1155 +{
1156 + /* assert */
1157 + assert(NULL != sptr);
1158 + assert(NULL != msg);
1159 +
1160 + /* inform ops */
1161 + sendto_opmask_butone(0, SNO_OLDSNO, "%s %s",
1162 + (feature_bool(FEAT_HIS_SNOTICES) || IsServer(sptr)) ?
1163 + get_client_name_and_opername(sptr) : cli_name((cli_user(sptr))->server), msg);
1164 +
1165 + /* log it */
1166 + log_write(LS_NETWORK, L_INFO, LOG_NOSNOTICE, "%s %s", get_client_name_and_opername(sptr), msg);
1167 +
1168 + /* welcome by remote oper, inform of success */
1169 + if ((flags & WELCOME_LOCAL) && IsUser(sptr) && !MyConnect(sptr)) {
1170 + sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :%s %s",
1171 + sptr, get_client_name_and_opername(sptr), msg);
1172 +
1173 + /* TODO: wallops all local changes, by both local and remote opers? */
1174 + /* tell all opers about the local message being set remotely */
1175 + sendwallto_group_butone(&me, WALL_WALLOPS, 0, "%s %s", get_client_name_and_opername(sptr), msg);
1176 + }
1177 +
1178 + return 0;
1179 +}
1180 +
1181 +
1182 +/** Announce a welcome message to local clients.
1183 + * @param[in] name Welcome message to announce.
1184 + * @param[in] flags Flags to set on welcome.
1185 + */
1186 +void
1187 +welcome_announce(int name, unsigned int flags)
1188 +{
1189 + struct Client *acptr; /* local user */
1190 + struct MsgBuf *msgbuf; /* message to send */
1191 + int i; /* loop variable */
1192 +
1193 + /* assert */
1194 + assert(flags & WELCOME_ANNOUNCE);
1195 + assert(WelcomeArrayIsValid(name));
1196 + assert(WelcomeIsSet(name));
1197 + assert(!WelcomeIsEmpty(name));
1198 +
1199 + /* build msgbuf */
1200 + msgbuf = msgq_make(0, ":%C %s $%s :[%s] %s", &me, MSG_NOTICE,
1201 + (flags & WELCOME_LOCAL) ? cli_name(&me) : "*",
1202 + (flags & WELCOME_LOCAL) ? cli_name(&me) : feature_str(FEAT_NETWORK),
1203 + WelcomeText(name));
1204 +
1205 + /* go over local clients */
1206 + for (i = HighestFd; i > 0; --i) {
1207 +
1208 + /* skip unregistered clients, skip servers */
1209 + if (!(acptr = LocalClientArray[i]) || !IsRegistered(acptr) || IsServer(acptr))
1210 + continue;
1211 +
1212 + /* send it away */
1213 + send_buffer(acptr, msgbuf, 0);
1214 + }
1215 +}
1216 +
1217 +
1218 +/** Set a welcome message.
1219 + * @param[in] cptr Local client that sent us the welcome.
1220 + * @param[in] sptr Originator of the welcome.
1221 + * @param[in] nameint Name of the message.
1222 + * @param[in] namearray Array entry.
1223 + * @param[in] create When it was set.
1224 + * @param[in] lastmod Last modification timestamp.
1225 + * @param[in] who Who set this message.
1226 + * @param[in] text The message.
1227 + * @param[in] flags Flags to set on welcome.
1228 + * @return Zero
1229 + */
1230 +int
1231 +welcome_set(struct Client *cptr, struct Client *sptr, int nameint,
1232 + int namearray, time_t create, time_t lastmod, char *who, char *text, unsigned int flags)
1233 +{
1234 + char msg[BUFSIZE]; /* msg for logging */
1235 + int new = 0; /* welcome is new - not set yet */
1236 +
1237 + /* assert */
1238 + assert(NULL != cptr);
1239 + assert(NULL != sptr);
1240 + assert(WelcomeNameIsValid(nameint));
1241 + assert(WelcomeArrayIsValid(namearray));
1242 + assert(lastmod > 0 || flags & WELCOME_FORCE); /* lastmod must not be 0 unless forced */
1243 + assert(NULL != who);
1244 + assert(NULL != text);
1245 + assert(!(flags & WELCOME_UNSET)); /* must not be unset */
1246 +
1247 + /* debug */
1248 + Debug((DEBUG_DEBUG, "welcome_set(cptr=%s, sptr=%s, nameint=%d, namearray=%d, "
1249 + "create=%Tu, lastmod=%Tu, who=%s, text=\"%s\", "
1250 + "FLAGS(0x%04x): local=%s announce=%s force=%s unset=%s insert=%s delete=%s)",
1251 + cli_name(cptr), cli_name(sptr), nameint, namearray, create, lastmod, who, text, flags,
1252 + (flags & WELCOME_LOCAL) ? "yes" : "no",
1253 + (flags & WELCOME_ANNOUNCE) ? "yes" : "no",
1254 + (flags & WELCOME_FORCE) ? "yes" : "no",
1255 + (flags & WELCOME_UNSET) ? "yes" : "no",
1256 + (flags & WELCOME_INSERT) ? "yes" : "no",
1257 + (flags & WELCOME_DELETE) ? "yes" : "no"));
1258 +
1259 + /* not set */
1260 + if (WelcomeIsEmpty(namearray))
1261 + new = 1;
1262 +
1263 + /* update */
1264 + welcome_make(namearray, text, who, create, lastmod, flags);
1265 +
1266 + /* create msg for log */
1267 + ircd_snprintf(0, msg, 0, "%s%s%s%s WELCOME %d \"%s\" %s [%Tu]",
1268 + (flags & WELCOME_FORCE) ? "force " : "",
1269 + new ? "setting" : "changing",
1270 + (flags & WELCOME_ANNOUNCE) ? " and announcing " : " ",
1271 + (flags & WELCOME_LOCAL) ? "local" : "global",
1272 + nameint, WelcomeText(namearray), WelcomeWho(namearray), create);
1273 +
1274 + /* log it */
1275 + welcome_log(sptr, msg, flags);
1276 +
1277 + /* propagate it */
1278 + if (!(flags & WELCOME_LOCAL))
1279 + welcome_propagate(cptr, sptr, nameint, create, lastmod, who, text, flags);
1280 +
1281 + /* announce it */
1282 + if (flags & WELCOME_ANNOUNCE)
1283 + welcome_announce(namearray, flags);
1284 +
1285 + return 0;
1286 +}
1287 +
1288 +
1289 +/** Unset a welcome message.
1290 + * @param[in] cptr Local client that sent us the welcome.
1291 + * @param[in] sptr Originator of the welcome.
1292 + * @param[in] nameint Name of the message.
1293 + * @param[in] namearray Array entry.
1294 + * @param[in] create When it was set.
1295 + * @param[in] lastmod Last modification timestamp.
1296 + * @param[in] who Who set this message.
1297 + * @param[in] flags Flags to set on welcome.
1298 + * @return Zero
1299 + */
1300 +int
1301 +welcome_unset(struct Client *cptr, struct Client *sptr, int nameint,
1302 + int namearray, time_t create, time_t lastmod, char *who, unsigned int flags)
1303 +{
1304 + char msg[BUFSIZE]; /* msg for logging */
1305 +
1306 + /* assert */
1307 + assert(NULL != cptr);
1308 + assert(NULL != sptr);
1309 + assert(WelcomeNameIsValid(nameint));
1310 + assert(WelcomeArrayIsValid(namearray));
1311 + assert(lastmod > 0 || flags & WELCOME_FORCE); /* lastmod must not be 0 unless forced */
1312 + assert(NULL != who);
1313 + assert(flags & (WELCOME_UNSET|WELCOME_INSERT|WELCOME_DELETE)); /* must be unset, insert or delete */
1314 +
1315 + /* debug */
1316 + Debug((DEBUG_DEBUG, "welcome_unset(cptr=%s, sptr=%s, nameint=%d, namearray=%d, "
1317 + "create=%Tu, lastmod=%Tu, who=%s, "
1318 + "FLAGS(0x%04x): local=%s announce=%s force=%s unset=%s insert=%s delete=%s)",
1319 + cli_name(cptr), cli_name(sptr), nameint, namearray, create, lastmod, who, flags,
1320 + (flags & WELCOME_LOCAL) ? "yes" : "no",
1321 + (flags & WELCOME_ANNOUNCE) ? "yes" : "no",
1322 + (flags & WELCOME_FORCE) ? "yes" : "no",
1323 + (flags & WELCOME_UNSET) ? "yes" : "no",
1324 + (flags & WELCOME_INSERT) ? "yes" : "no",
1325 + (flags & WELCOME_DELETE) ? "yes" : "no"));
1326 +
1327 + /* create msg for log */
1328 + ircd_snprintf(0, msg, 0, "%sunsetting %s WELCOME %d \"%s\" %s [%Tu]",
1329 + (flags & WELCOME_FORCE) ? "force " : "",
1330 + (flags & WELCOME_LOCAL) ? "local" : "global",
1331 + nameint, WelcomeText(namearray), WelcomeWho(namearray), create);
1332 +
1333 + /* log it but only if it was set
1334 + * welcome unset could have crossed with another welcome unset,
1335 + * still need to update lastmod
1336 + * can be a forced unset on a welcome that is not set
1337 + */
1338 + if (!WelcomeIsEmpty(namearray))
1339 + welcome_log(sptr, msg, flags);
1340 +
1341 + /* update,
1342 + * not when inserting, welcome_insert() handles that by calling welcome_set()
1343 + * not when deleting, welcome_delete() handles that
1344 + */
1345 + if (!(flags & (WELCOME_INSERT|WELCOME_DELETE)))
1346 + welcome_make(namearray, "", who, create, lastmod, flags);
1347 +
1348 + /* propagate it, but not when inserting */
1349 + if (!(flags & (WELCOME_LOCAL|WELCOME_INSERT)))
1350 + welcome_propagate(cptr, sptr, nameint, create, lastmod, who, "", flags);
1351 +
1352 + return 0;
1353 +}
1354 +
1355 +
1356 +/** Insert a welcome message.
1357 + * @param[in] cptr Local client that sent us the welcome.
1358 + * @param[in] sptr Originator of the welcome.
1359 + * @param[in] nameint Name of the message.
1360 + * @param[in] namearray Array entry.
1361 + * @param[in] create When it was set.
1362 + * @param[in] lastmod Last modification timestamp.
1363 + * @param[in] who Who set this message.
1364 + * @param[in] text The welcome message.
1365 + * @param[in] flags Flags to set on welcome.
1366 + * @return Zero
1367 + */
1368 +int
1369 +welcome_insert(struct Client *cptr, struct Client *sptr, int nameint,
1370 + int namearray, time_t create, time_t lastmod, char *who, char *text, unsigned int flags)
1371 +{
1372 + char msg[BUFSIZE]; /* msg for logging */
1373 + int i; /* loop variable */
1374 + int empty = -1; /* first empty spot in array after namearray */
1375 + int end = WELCOME_MAX_ENTRIES -1; /* last element to check in array */
1376 + int last = end; /* last welcome message to feed to welcome_unset */
1377 +
1378 + /* assert */
1379 + assert(NULL != cptr);
1380 + assert(NULL != sptr);
1381 + assert(WelcomeNameIsValid(nameint));
1382 + assert(WelcomeArrayIsValid(namearray));
1383 + assert(lastmod > 0 || flags & WELCOME_FORCE); /* lastmod must not be 0 unless forced */
1384 + assert(NULL != who);
1385 + assert(NULL != text);
1386 + assert(flags & WELCOME_INSERT); /* must be insert */
1387 +
1388 + /* debug */
1389 + Debug((DEBUG_DEBUG, "welcome_insert(cptr=%s, sptr=%s, nameint=%d, namearray=%d, "
1390 + "create=%Tu, lastmod=%Tu, who=%s, text=\"%s\", "
1391 + "FLAGS(0x%04x): local=%s announce=%s force=%s unset=%s insert=%s delete=%s)",
1392 + cli_name(cptr), cli_name(sptr), nameint, namearray, create, lastmod, who, text, flags,
1393 + (flags & WELCOME_LOCAL) ? "yes" : "no",
1394 + (flags & WELCOME_ANNOUNCE) ? "yes" : "no",
1395 + (flags & WELCOME_FORCE) ? "yes" : "no",
1396 + (flags & WELCOME_UNSET) ? "yes" : "no",
1397 + (flags & WELCOME_INSERT) ? "yes" : "no",
1398 + (flags & WELCOME_DELETE) ? "yes" : "no"));
1399 +
1400 + /* correct end for local offset */
1401 + if (flags & WELCOME_LOCAL)
1402 + end += WELCOME_MAX_ENTRIES;
1403 +
1404 + /* find first empty spot */
1405 + for (i = namearray; i <= end; i++) {
1406 + if (WelcomeIsEmpty(i)) {
1407 + empty = i;
1408 + break;
1409 + }
1410 + }
1411 +
1412 + /* no empty spot, need to unset last */
1413 + if (empty == -1) {
1414 + welcome_unset(cptr, sptr, end +1, end, create, lastmod, who, flags);
1415 + empty = end;
1416 + }
1417 +
1418 + /* move entries down, update lastmod */
1419 + for (i = empty; i > namearray; i--)
1420 + welcome_make(i, WelcomeText(i-1), WelcomeWho(i-1), WelcomeCreate(i-1),
1421 + lastmod, flags | WELCOME_INCLASTMOD);
1422 +
1423 + /* correct empty for local offset */
1424 + if (flags & WELCOME_LOCAL)
1425 + empty -= WELCOME_MAX_ENTRIES;
1426 +
1427 + /* create msg for log */
1428 + if (nameint == empty)
1429 + ircd_snprintf(0, msg, 0, "moving %s WELCOME message %d one place down",
1430 + (flags & WELCOME_LOCAL) ? "local" : "global", nameint);
1431 + else
1432 + ircd_snprintf(0, msg, 0, "moving %s WELCOME message %d %s %d one place down",
1433 + (flags & WELCOME_LOCAL) ? "local" : "global", nameint, (empty - nameint > 1) ? "to" : "and" , empty);
1434 +
1435 + /* log it */
1436 + welcome_log(sptr, msg, flags);
1437 +
1438 + /* set it */
1439 + welcome_set(cptr, sptr, nameint, namearray, create, lastmod, who, text, flags);
1440 +
1441 + return 0;
1442 +}
1443 +
1444 +
1445 +/** Delete a welcome message.
1446 + * @param[in] cptr Local client that sent us the welcome.
1447 + * @param[in] sptr Originator of the welcome.
1448 + * @param[in] nameint Name of the message.
1449 + * @param[in] namearray Array entry.
1450 + * @param[in] create When it was set.
1451 + * @param[in] lastmod Last modification timestamp.
1452 + * @param[in] who Who set this message.
1453 + * @param[in] flags Flags to set on welcome.
1454 + * @return Zero
1455 + */
1456 +int
1457 +welcome_delete(struct Client *cptr, struct Client *sptr, int nameint,
1458 + int namearray, time_t create, time_t lastmod, char *who, unsigned int flags)
1459 +{
1460 + int i; /* loop variable */
1461 + int empty = namearray; /* first empty spot in array after namearray */
1462 + int end = WELCOME_MAX_ENTRIES -1; /* last element to check in array */
1463 +
1464 + /* assert */
1465 + assert(NULL != cptr);
1466 + assert(NULL != sptr);
1467 + assert(WelcomeNameIsValid(nameint));
1468 + assert(WelcomeArrayIsValid(namearray));
1469 + assert(lastmod > 0 || flags & WELCOME_FORCE); /* lastmod must not be 0 unless forced */
1470 + assert(NULL != who);
1471 + assert(flags & WELCOME_UNSET); /* must be unset */
1472 + assert(flags & WELCOME_DELETE); /* must be delete */
1473 +
1474 + /* debug */
1475 + Debug((DEBUG_DEBUG, "welcome_delete(cptr=%s, sptr=%s, nameint=%d, namearray=%d, "
1476 + "create=%Tu, lastmod=%Tu, who=%s, "
1477 + "FLAGS(0x%04x): local=%s announce=%s force=%s unset=%s insert=%s delete=%s)",
1478 + cli_name(cptr), cli_name(sptr), nameint, namearray, create, lastmod, who, flags,
1479 + (flags & WELCOME_LOCAL) ? "yes" : "no",
1480 + (flags & WELCOME_ANNOUNCE) ? "yes" : "no",
1481 + (flags & WELCOME_FORCE) ? "yes" : "no",
1482 + (flags & WELCOME_UNSET) ? "yes" : "no",
1483 + (flags & WELCOME_INSERT) ? "yes" : "no",
1484 + (flags & WELCOME_DELETE) ? "yes" : "no"));
1485 +
1486 + /* unset it */
1487 + welcome_unset(cptr, sptr, nameint, namearray, create, lastmod, who, flags);
1488 +
1489 + /* correct end for local offset */
1490 + if (flags & WELCOME_LOCAL)
1491 + end += WELCOME_MAX_ENTRIES;
1492 +
1493 + /* move entries up, update lastmod */
1494 + for (i = namearray; i < end; i++) {
1495 + if (!WelcomeIsSet(i+1))
1496 + break;
1497 + welcome_make(i, WelcomeText(i+1), WelcomeWho(i+1), WelcomeCreate(i+1),
1498 + lastmod, flags | WELCOME_INCLASTMOD);
1499 + }
1500 +
1501 + /* clear last entry */
1502 + if (i == end)
1503 + welcome_make(i, "", who, create, lastmod, flags | WELCOME_INCLASTMOD);
1504 +
1505 + /* nothing was moved, clear entry */
1506 + if (i == namearray)
1507 + welcome_make(i, "", who, create, lastmod, flags);
1508 +
1509 + return 0;
1510 +}
1511 +
1512 +
1513 +/** Change a welcome message.
1514 + * @param[in] cptr Local client that sent us the welcome.
1515 + * @param[in] sptr Originator of the welcome.
1516 + * @param[in] name Name of the message.
1517 + * @param[in] create When it was set.
1518 + * @param[in] lastmod Last modification timestamp.
1519 + * @param[in] who Who set this message.
1520 + * @param[in] text The welcome message.
1521 + * @param[in] flags Flags to set on welcome.
1522 + * @return Zero
1523 + */
1524 +int
1525 +welcome_do(struct Client *cptr, struct Client *sptr, char *name,
1526 + time_t create, time_t lastmod, char *who, char *text, unsigned int flags)
1527 +{
1528 + int nameint = atoi(name); /* transform to int */
1529 + int namearray = nameint - 1; /* used to test the array element */
1530 + int start = 0; /* this is the first server setting this welcome message from a user */
1531 +
1532 + /* assert */
1533 + assert(NULL != cptr);
1534 + assert(NULL != sptr);
1535 + assert(NULL != name);
1536 + assert(NULL != text);
1537 + assert(NULL != who);
1538 +
1539 + /* debug */
1540 + Debug((DEBUG_DEBUG, "welcome_do(cptr=%s, sptr=%s, name=%s, "
1541 + "create=%Tu, lastmod=%Tu, who=%s, text=\"%s\", "
1542 + "FLAGS(0x%04x): local=%s announce=%s force=%s unset=%s insert=%s delete=%s)",
1543 + cli_name(cptr), cli_name(sptr), name, create, lastmod, who, text, flags,
1544 + (flags & WELCOME_LOCAL) ? "yes" : "no",
1545 + (flags & WELCOME_ANNOUNCE) ? "yes" : "no",
1546 + (flags & WELCOME_FORCE) ? "yes" : "no",
1547 + (flags & WELCOME_UNSET) ? "yes" : "no",
1548 + (flags & WELCOME_INSERT) ? "yes" : "no",
1549 + (flags & WELCOME_DELETE) ? "yes" : "no"));
1550 +
1551 + /* welcome from my user, or a local welcome from a local/remote user */
1552 + start = (IsUser(sptr) && (MyConnect(sptr) || flags & WELCOME_LOCAL));
1553 +
1554 + /* name empty after taking off the prefixes? */
1555 + if (*name == 0) {
1556 + if (start)
1557 + sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :Welcome: No message number given", sptr);
1558 + else
1559 + protocol_violation(cptr, "Received WELCOME with no message number from %C", sptr);
1560 + return 0;
1561 + }
1562 +
1563 + /* check name */
1564 + if (!WelcomeArrayIsValid(namearray)) {
1565 + if (start)
1566 + sendcmdto_one(&me, CMD_NOTICE, sptr,
1567 + "%C :Welcome: Invalid message number %s - should between 1 and %d",
1568 + sptr, name, WELCOME_MAX_ENTRIES);
1569 + else
1570 + protocol_violation(cptr,
1571 + "Received WELCOME with invalid message number %s from %C - should be between 1 and %d",
1572 + name, sptr, WELCOME_MAX_ENTRIES);
1573 + return 0;
1574 + }
1575 +
1576 + /* invalid lastmod */
1577 + if (lastmod <= 0) {
1578 + /* from my user - must be a glitch,
1579 + * someone set (my) network time to 0 ?
1580 + */
1581 + if (MyUser(sptr))
1582 + lastmod = 1;
1583 + /* not forced or negative, it is a protocol violation */
1584 + else if (!(flags & WELCOME_FORCE) || lastmod < 0)
1585 + return protocol_violation(cptr, "Received WELCOME with invalid lastmod timestamp %Tu from %C", lastmod, sptr);
1586 + }
1587 +
1588 + /* source is user, and is myuser or welcome is local, check length of the message */
1589 + if (start && strlen(text) > WELCOMELEN) {
1590 + sendcmdto_one(&me, CMD_NOTICE, sptr,
1591 + "%C :Welcome: The message is too long with %d chars - max is %d chars",
1592 + sptr, strlen(text), WELCOMELEN);
1593 + ircd_strncpy(text, text, WELCOMELEN);
1594 + sendcmdto_one(&me, CMD_NOTICE, sptr,
1595 + "%C :Welcome: Change or truncate the message to: \"%s\"", sptr, text);
1596 + return 0;
1597 + }
1598 +
1599 + /* correct namearray for local offset */
1600 + if (flags & WELCOME_LOCAL)
1601 + namearray += WELCOME_MAX_ENTRIES;
1602 +
1603 + /* must be true by now */
1604 + assert(WelcomeArrayIsValid(namearray));
1605 + assert(WelcomeNameIsValid(nameint));
1606 +
1607 + /* cannot unset welcome that is not set */
1608 + if (!WelcomeIsSet(namearray) && flags & WELCOME_UNSET) {
1609 +
1610 + /* from user, throw error */
1611 + if (start)
1612 + return send_reply(sptr, ERR_NOSUCHWELCOME, name);
1613 +
1614 + /* new local welcome from server, but empty - ignore
1615 + * we do accept a new global welcome message that is empty
1616 + */
1617 + if (flags & WELCOME_LOCAL)
1618 + return 0;
1619 + }
1620 +
1621 + /* check if there is something to change */
1622 + /* we got a record for it */
1623 + if (WelcomeIsSet(namearray)) {
1624 +
1625 + /* global and not forced */
1626 + if (!(flags & (WELCOME_LOCAL|WELCOME_FORCE))) {
1627 +
1628 + /* myuser changes it,
1629 + * WelcomeLastMod greater than or equal to lastmod, take WelcomeLastMod+1 as lastmod
1630 + * else the change is not accepted upstream because of the older TS
1631 + */
1632 + if (MyUser(sptr)) {
1633 + if (WelcomeLastMod(namearray) >= lastmod)
1634 + lastmod = WelcomeLastMod(namearray) +1;
1635 + }
1636 +
1637 + /* compare lastmod, ignore welcome when:
1638 + * we got a newer one
1639 + * or when lastmod is the same and our text is 'smaller'
1640 + */
1641 + else if (lastmod < WelcomeLastMod(namearray) || /* we got a newer one */
1642 + (lastmod == WelcomeLastMod(namearray) && /* same lastmod */
1643 + strcmp(WelcomeText(namearray), text) < 0)) { /* our text is 'smaller' */
1644 + /* burst or burst ack, cptr gets our version from the burst */
1645 + if (IsBurstOrBurstAck(cptr))
1646 + return 0;
1647 + /* sync server */
1648 + return welcome_resend(cptr, nameint, namearray, flags);
1649 + }
1650 +
1651 + /* local welcome - we use our idea of the time */
1652 + } else if (flags & WELCOME_LOCAL) {
1653 + create = TStime();
1654 + lastmod = TStime();
1655 + }
1656 +
1657 + /* welcome from my user or local welcome from local/remote user
1658 + * compare new message with old message
1659 + * use strcmp instead of ircd_strcmp - oper may wish to change case
1660 + */
1661 + if (start && strcmp(text, WelcomeText(namearray)) == 0) {
1662 + sendcmdto_one(&me, CMD_NOTICE, sptr,
1663 + "%C :Welcome: Cannot change %s message for %s - nothing to change",
1664 + sptr, (flags & WELCOME_LOCAL) ? "local" : "global", name);
1665 + return 0;
1666 + }
1667 + }
1668 +
1669 + /* welcome from my user, or local welcome from local/remote user
1670 + * forcing and unsetting, set create and lastmod to 0
1671 + * clear delete flag
1672 + */
1673 + if (start && (flags & WELCOME_FORCE) && (flags & WELCOME_UNSET)) {
1674 + flags &= ~WELCOME_DELETE;
1675 + create = 0;
1676 + lastmod = 0;
1677 + }
1678 +
1679 + /* clear insert flag
1680 + * when this flag is set, welcome_unset() assumes it is being called from welcome_insert()
1681 + * and does not propagate the change - welcome_delete() also calls welcome_unset()
1682 + * do not insert last global/local welcome
1683 + * do not insert when entry is not set
1684 + *
1685 + */
1686 + if (flags & WELCOME_INSERT &&
1687 + (flags & WELCOME_UNSET || !WelcomeIsSet(namearray) || nameint == WELCOME_MAX_ENTRIES))
1688 + flags &= ~WELCOME_INSERT;
1689 +
1690 + /* clear delete flag when not unsetting so we do not propagate the - delete prefix */
1691 + if (flags & WELCOME_DELETE && !(flags & WELCOME_UNSET))
1692 + flags &= ~WELCOME_DELETE;
1693 +
1694 + /* unset */
1695 + if (flags & WELCOME_UNSET) {
1696 +
1697 + /* delete */
1698 + if (flags & WELCOME_DELETE)
1699 + return welcome_delete(cptr, sptr, nameint, namearray, create, lastmod, who, flags);
1700 +
1701 + /* unset */
1702 + return welcome_unset(cptr, sptr, nameint, namearray, create, lastmod, who, flags);
1703 + }
1704 +
1705 + /* insert */
1706 + if (flags & WELCOME_INSERT)
1707 + return welcome_insert(cptr, sptr, nameint, namearray, create, lastmod, who, text, flags);
1708 +
1709 + /* new or change */
1710 + return welcome_set(cptr, sptr, nameint, namearray, create, lastmod, who, text, flags);
1711 +}
1712 +
1713 +
1714 +/** Send the full list of welcome message to \a cptr.
1715 + * @param[in] cptr Local server to send welcomes to.
1716 + */
1717 +void
1718 +welcome_burst(struct Client *cptr)
1719 +{
1720 + int name; /* loop variable */
1721 +
1722 + /* assert */
1723 + assert(NULL != cptr);
1724 +
1725 + /* loop over global entries - 0 to max - 1 */
1726 + for (name = 0; name <= WELCOME_MAX_ENTRIES - 1; name++) {
1727 + if (WelcomeIsSet(name))
1728 + sendcmdto_one(&me, CMD_WELCOME, cptr, "* %d %Tu %Tu %s :%s",
1729 + name + 1,
1730 + WelcomeCreate(name), WelcomeLastMod(name),
1731 + WelcomeWho(name), WelcomeText(name));
1732 + }
1733 +}
1734 +
1735 +
1736 +/** List welcome messages.
1737 + * @param[in] sptr Client requesting the listing.
1738 + * @param[in] connect When non zero do not report no welcome is set
1739 + * @return Zero.
1740 + */
1741 +int
1742 +welcome_list(struct Client *sptr, int connect)
1743 +{
1744 + int name; /* loop variable */
1745 + int found = 0; /* number of welcome messages set */
1746 + int local = 0; /* welcome is local or global */
1747 +
1748 + /* assert */
1749 + assert(NULL != sptr);
1750 +
1751 + /* loop over all entries - range 0 to 2 * max - 1 */
1752 + for (name = 0; name <= 2 * WELCOME_MAX_ENTRIES - 1; name++) {
1753 +
1754 + /* local entries now */
1755 + if (name == WELCOME_MAX_ENTRIES)
1756 + local = 1;
1757 +
1758 + /* not set or empty - skip */
1759 + if (!WelcomeIsSet(name) || WelcomeIsEmpty(name))
1760 + continue;
1761 +
1762 + /* got one */
1763 + found++;
1764 + sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :[%s] %s",
1765 + sptr, local ? cli_name(&me) : feature_str(FEAT_NETWORK), WelcomeText(name));
1766 + }
1767 +
1768 + /* nothing set */
1769 + if (!found && !connect)
1770 + sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :No welcome message set.", sptr);
1771 +
1772 + return 0;
1773 +}
1774 +
1775 +
1776 +/** Statistics callback to list Welcome messages.
1777 + * @param[in] sptr Client requesting statistics.
1778 + * @param[in] sd Stats descriptor for request (ignored).
1779 + * @param[in] param Extra parameter from user (ignored).
1780 + */
1781 +void
1782 +welcome_stats(struct Client *sptr, const struct StatDesc *sd, char *param)
1783 +{
1784 + int name; /* loop variable */
1785 + int local = 0; /* welcome is local or global */
1786 +
1787 + /* assert */
1788 + assert(NULL != sptr);
1789 +
1790 + /* stats header */
1791 + send_reply(sptr, SND_EXPLICIT | RPL_STATSWELCOME,
1792 + "W # Target Who Timestamp LastMod :Text");
1793 +
1794 + /* loop over all entries - range 0 to 2 * max - 1 */
1795 + for (name = 0; name <= 2 * WELCOME_MAX_ENTRIES - 1; name++) {
1796 +
1797 + /* local entries now */
1798 + if (name == WELCOME_MAX_ENTRIES)
1799 + local = 1;
1800 +
1801 + /* not set */
1802 + if (!WelcomeIsSet(name))
1803 + continue;
1804 +
1805 + /* send it */
1806 + send_reply(sptr, RPL_STATSWELCOME,
1807 + local ? name + 1 - WELCOME_MAX_ENTRIES : name + 1,
1808 + local ? cli_name(&me) : "*",
1809 + WelcomeWho(name),
1810 + WelcomeCreate(name), WelcomeLastMod(name),
1811 + WelcomeIsEmpty(name) ? "<Empty>" : WelcomeText(name));
1812 + }
1813 +}
1814 +
1815 +
1816 +/** Count welcome messages and memory used by them.
1817 + * @param[out] we_size Receives total number of bytes allocated for welcomes.
1818 + * @return Number of welcome messages currently allocated.
1819 + */
1820 +int
1821 +welcome_memory_count(size_t *we_size)
1822 +{
1823 + int name; /* loop variable */
1824 + unsigned int we = 0; /* number of welcome messages set */
1825 +
1826 + /* loop over all entries - range 0 to 2 * max - 1 */
1827 + for (name = 0; name <= 2 * WELCOME_MAX_ENTRIES - 1; name++) {
1828 +
1829 + /* not set */
1830 + if (!WelcomeIsSet(name))
1831 + continue;
1832 +
1833 + /* count */
1834 + we++;
1835 + *we_size += WelcomeText(name) ? (strlen(WelcomeText(name)) + 1) : 0;
1836 + *we_size += WelcomeWho(name) ? (strlen(WelcomeWho(name)) + 1) : 0;
1837 + }
1838 + return we;
1839 +}