ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/ircd-hybrid/branches/8.2.x/modules/core/m_sjoin.c
Revision: 10024
Committed: Sat Jan 1 10:20:46 2022 UTC (2 years, 2 months ago) by michael
Content type: text/x-csrc
File size: 17195 byte(s)
Log Message:
- Bump copyright years

File Contents

# Content
1 /*
2 * ircd-hybrid: an advanced, lightweight Internet Relay Chat Daemon (ircd)
3 *
4 * Copyright (c) 1997-2022 ircd-hybrid development team
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
19 * USA
20 */
21
22 /*! \file m_sjoin.c
23 * \brief Includes required functions for processing the SJOIN command.
24 * \version $Id$
25 */
26
27 #include "stdinc.h"
28 #include "list.h"
29 #include "channel.h"
30 #include "channel_invite.h"
31 #include "channel_mode.h"
32 #include "client.h"
33 #include "hash.h"
34 #include "irc_string.h"
35 #include "ircd.h"
36 #include "numeric.h"
37 #include "send.h"
38 #include "parse.h"
39 #include "modules.h"
40 #include "server.h"
41 #include "conf.h"
42 #include "misc.h"
43
44
45 /* set_final_mode
46 *
47 * inputs - channel mode
48 * - old channel mode
49 * output - NONE
50 * side effects - walk through all the channel modes turning off modes
51 * that were on in oldmode but aren't on in mode.
52 * Then walk through turning on modes that are on in mode
53 * but were not set in oldmode.
54 */
55 static void
56 set_final_mode(const struct Mode *mode, const struct Mode *oldmode, char *mbuf, char *pbuf)
57 {
58 int what = MODE_QUERY;
59
60 for (const struct chan_mode *tab = cmode_tab; tab->letter; ++tab)
61 {
62 if (tab->mode && (tab->mode & mode->mode) && !(tab->mode & oldmode->mode))
63 {
64 if (what != MODE_ADD)
65 {
66 *mbuf++ = '+';
67 what = MODE_ADD;
68 }
69
70 *mbuf++ = tab->letter;
71 }
72 }
73
74 for (const struct chan_mode *tab = cmode_tab; tab->letter; ++tab)
75 {
76 if (tab->mode && (tab->mode & oldmode->mode) && !(tab->mode & mode->mode))
77 {
78 if (what != MODE_DEL)
79 {
80 *mbuf++ = '-';
81 what = MODE_DEL;
82 }
83
84 *mbuf++ = tab->letter;
85 }
86 }
87
88 if (oldmode->limit && mode->limit == 0)
89 {
90 if (what != MODE_DEL)
91 {
92 *mbuf++ = '-';
93 what = MODE_DEL;
94 }
95
96 *mbuf++ = 'l';
97 }
98
99 if (oldmode->key[0] && mode->key[0] == '\0')
100 {
101 if (what != MODE_DEL)
102 {
103 *mbuf++ = '-';
104 what = MODE_DEL;
105 }
106
107 *mbuf++ = 'k';
108 pbuf += sprintf(pbuf, "%s ", oldmode->key);
109 }
110
111 if (mode->limit && oldmode->limit != mode->limit)
112 {
113 if (what != MODE_ADD)
114 {
115 *mbuf++ = '+';
116 what = MODE_ADD;
117 }
118
119 *mbuf++ = 'l';
120 pbuf += sprintf(pbuf, "%u ", mode->limit);
121 }
122
123 if (mode->key[0] && strcmp(oldmode->key, mode->key))
124 {
125 if (what != MODE_ADD)
126 {
127 *mbuf++ = '+';
128 what = MODE_ADD;
129 }
130
131 *mbuf++ = 'k';
132 pbuf += sprintf(pbuf, "%s ", mode->key);
133 }
134
135 *mbuf = '\0';
136 }
137
138 /* remove_ban_list()
139 *
140 * inputs - channel, source, list to remove, char of mode
141 * outputs - none
142 * side effects - given ban list is removed, modes are sent to local clients
143 */
144 static void
145 remove_ban_list(struct Channel *channel, const struct Client *client, dlink_list *list, char c)
146 {
147 char modebuf[IRCD_BUFSIZE];
148 char parabuf[IRCD_BUFSIZE];
149 char *mbuf;
150 char *pbuf;
151 int count = 0;
152 int cur_len, mlen;
153
154 if (dlink_list_length(list) == 0)
155 return;
156
157 cur_len = mlen = snprintf(modebuf, sizeof(modebuf), ":%s MODE %s -",
158 client->name, channel->name);
159 mbuf = modebuf + mlen;
160 pbuf = parabuf;
161
162 while (list->head)
163 {
164 struct Ban *ban = list->head->data;
165 int plen = ban->banstr_len + 2; /* +2 = b and space */
166
167 if (count >= MAXMODEPARAMS ||
168 (cur_len + 1 /* space between */ + (plen - 1)) > IRCD_BUFSIZE - 2)
169 {
170 /* NUL-terminate and remove trailing space */
171 *mbuf = *(pbuf - 1) = '\0';
172 sendto_channel_local(NULL, channel, 0, 0, 0, "%s %s", modebuf, parabuf);
173
174 cur_len = mlen;
175 mbuf = modebuf + mlen;
176 pbuf = parabuf;
177 count = 0;
178 }
179
180 *mbuf++ = c;
181 cur_len += plen;
182 pbuf += sprintf(pbuf, "%s ", ban->banstr);
183 ++count;
184
185 remove_ban(ban, list);
186 }
187
188 *mbuf = *(pbuf - 1) = '\0';
189 sendto_channel_local(NULL, channel, 0, 0, 0, "%s %s", modebuf, parabuf);
190 }
191
192 /* ms_sjoin()
193 *
194 * parv[0] - command
195 * parv[1] - TS
196 * parv[2] - channel
197 * parv[3] - modes + n arguments (key and/or limit)
198 * parv[4+n] - flags+nick list (all in one parameter)
199 *
200 * process a SJOIN, taking the TS's into account to either ignore the
201 * incoming modes or undo the existing ones or merge them, and JOIN
202 * all the specified users while sending JOIN/MODEs to local clients
203 */
204 static void
205 ms_sjoin(struct Client *source_p, int parc, char *parv[])
206 {
207 struct Client *target_p = NULL;
208 struct Mode mode = { .mode = 0, .limit = 0, .key[0] = '\0' };
209 int args = 0;
210 bool isnew = false;
211 bool keep_our_modes = true;
212 bool keep_new_modes = true;
213 bool have_many_uids = false;
214 unsigned int lcount;
215 char uid_prefix[CMEMBER_STATUS_FLAGS_LEN + 1];
216 char *up = NULL;
217 int len_uid = 0;
218 int buflen = 0;
219 int slen;
220 unsigned int fl;
221 char *s;
222 char *sptr;
223 char uid_buf[IRCD_BUFSIZE]; /* buffer for modes/prefixes */
224 char *uid_ptr;
225 char *p; /* pointer used making sjbuf */
226 const char *para[MAXMODEPARAMS];
227 char sendbuf[MODEBUFLEN] = "";
228 char modebuf[MODEBUFLEN] = "";
229 char parabuf[MODEBUFLEN] = "";
230 unsigned int pargs = 0;
231
232 if (!IsServer(source_p))
233 return;
234
235 if (channel_check_name(parv[2], false) == false)
236 {
237 sendto_realops_flags(UMODE_DEBUG, L_ALL, SEND_NOTICE,
238 "*** Too long or invalid channel name from %s(via %s): %s",
239 source_p->name, source_p->from->name, parv[2]);
240 return;
241 }
242
243 for (const char *modes = parv[3]; *modes; ++modes)
244 {
245 switch (*modes)
246 {
247 case 'k':
248 strlcpy(mode.key, parv[4 + args], sizeof(mode.key));
249 ++args;
250
251 if (parc < 5 + args)
252 return;
253 break;
254
255 case 'l':
256 mode.limit = atoi(parv[4 + args]);
257 ++args;
258
259 if (parc < 5 + args)
260 return;
261 break;
262
263 default:
264 {
265 const struct chan_mode *cmode = cmode_map[(unsigned char)*modes];
266
267 if (cmode)
268 mode.mode |= cmode->mode;
269 break;
270 }
271 }
272 }
273
274 struct Channel *channel = hash_find_channel(parv[2]);
275 if (channel == NULL)
276 {
277 isnew = true;
278 channel = channel_make(parv[2]);
279 }
280
281 uintmax_t newts = strtoumax(parv[1], NULL, 10);
282 uintmax_t oldts = channel->creation_time;
283
284 if (newts == 0 && isnew == false && oldts)
285 {
286 sendto_channel_local(NULL, channel, 0, 0, 0,
287 ":%s NOTICE %s :*** Notice -- TS for %s changed from %ju to 0",
288 me.name, channel->name, channel->name, oldts);
289 sendto_realops_flags(UMODE_SERVNOTICE, L_ALL, SEND_NOTICE,
290 "Server %s changing TS on %s from %ju to 0",
291 source_p->name, channel->name, oldts);
292 }
293
294 if (isnew == true)
295 channel->creation_time = newts;
296 else if (newts == 0 || oldts == 0)
297 channel->creation_time = 0;
298 else if (newts == oldts)
299 ;
300 else if (newts < oldts)
301 {
302 keep_our_modes = false;
303 channel->creation_time = newts;
304 }
305 else
306 keep_new_modes = false;
307
308 struct Mode *oldmode = &channel->mode;
309
310 if (keep_new_modes == false)
311 mode = *oldmode;
312 else if (keep_our_modes == true)
313 {
314 mode.mode |= oldmode->mode;
315
316 if (oldmode->limit > mode.limit)
317 mode.limit = oldmode->limit;
318 if (strcmp(mode.key, oldmode->key) < 0)
319 strlcpy(mode.key, oldmode->key, sizeof(mode.key));
320 }
321
322 set_final_mode(&mode, oldmode, modebuf, parabuf);
323 channel->mode = mode;
324
325 const struct Client *origin = source_p;
326 if (IsHidden(source_p) || ConfigServerHide.hide_servers)
327 origin = &me;
328
329 /* Lost the TS, other side wins, so remove modes on this side */
330 if (keep_our_modes == false)
331 {
332 /* Update channel name to be the correct case */
333 strlcpy(channel->name, parv[2], sizeof(channel->name));
334
335 channel_demote_members(channel, origin, CHFL_CHANOP, 'o');
336 channel_demote_members(channel, origin, CHFL_HALFOP, 'h');
337 channel_demote_members(channel, origin, CHFL_VOICE, 'v');
338
339 remove_ban_list(channel, origin, &channel->banlist, 'b');
340 remove_ban_list(channel, origin, &channel->exceptlist, 'e');
341 remove_ban_list(channel, origin, &channel->invexlist, 'I');
342
343 clear_ban_cache_list(&channel->members_local);
344 invite_clear_list(&channel->invites);
345
346 if (channel->topic[0])
347 {
348 channel_set_topic(channel, "", "", 0, false);
349 sendto_channel_local(NULL, channel, 0, 0, 0, ":%s TOPIC %s :",
350 origin->name, channel->name);
351 }
352
353 sendto_channel_local(NULL, channel, 0, 0, 0,
354 ":%s NOTICE %s :*** Notice -- TS for %s changed from %ju to %ju",
355 me.name, channel->name, channel->name,
356 oldts, newts);
357 }
358
359 if (*modebuf)
360 sendto_channel_local(NULL, channel, 0, 0, 0, ":%s MODE %s %s %s",
361 origin->name, channel->name, modebuf, parabuf);
362
363 if (*parv[3] != '0' && keep_new_modes == true)
364 channel_modes(channel, source_p, NULL, modebuf, parabuf);
365 else
366 {
367 modebuf[0] = '0';
368 modebuf[1] = '\0';
369 }
370
371 buflen = snprintf(uid_buf, sizeof(uid_buf), ":%s SJOIN %ju %s %s %s:",
372 source_p->id, channel->creation_time,
373 channel->name, modebuf, parabuf);
374 uid_ptr = uid_buf + buflen;
375
376 /*
377 * Check we can fit a nick on the end, as well as \r\n and a prefix "
378 * @%+", and a space.
379 */
380 if (buflen >= (IRCD_BUFSIZE - IDLEN - 2 - CMEMBER_STATUS_FLAGS_LEN - 1))
381 {
382 sendto_realops_flags(UMODE_SERVNOTICE, L_ALL, SEND_NOTICE,
383 "Long SJOIN from server: %s(via %s) (ignored)",
384 source_p->name, source_p->from->name);
385 return;
386 }
387
388 char *mbuf = modebuf;
389 *mbuf++ = '+';
390
391 s = parv[args + 4];
392 while (*s == ' ')
393 ++s;
394
395 if ((p = strchr(s, ' ')))
396 {
397 *p++ = '\0';
398
399 while (*p == ' ')
400 ++p;
401 have_many_uids = *p != '\0';
402 }
403
404 while (*s)
405 {
406 bool valid_mode = true;
407 fl = 0;
408
409 do
410 {
411 switch (*s)
412 {
413 case '@':
414 fl |= CHFL_CHANOP;
415 ++s;
416 break;
417 case '%':
418 fl |= CHFL_HALFOP;
419 ++s;
420 break;
421 case '+':
422 fl |= CHFL_VOICE;
423 ++s;
424 break;
425 default:
426 valid_mode = false;
427 break;
428 }
429 } while (valid_mode == true);
430
431 /*
432 * If the client doesn't exist, or if it's fake direction/server, skip.
433 * we cannot send ERR_NOSUCHNICK here because if it's a UID, we cannot
434 * lookup the nick, and it's better to never send the numeric than only
435 * sometimes.
436 */
437 if ((target_p = find_person(source_p, s)) == NULL || target_p->from != source_p->from)
438 goto nextnick;
439
440 len_uid = strlen(target_p->id);
441 up = uid_prefix;
442
443 if (keep_new_modes == true)
444 {
445 if (fl & CHFL_CHANOP)
446 {
447 *up++ = '@';
448 len_uid++;
449 }
450
451 if (fl & CHFL_HALFOP)
452 {
453 *up++ = '%';
454 len_uid++;
455 }
456
457 if (fl & CHFL_VOICE)
458 {
459 *up++ = '+';
460 len_uid++;
461 }
462 }
463 else
464 fl = 0;
465
466 *up = '\0';
467
468 if ((uid_ptr - uid_buf + len_uid) > (IRCD_BUFSIZE - 2))
469 {
470 sendto_server(source_p, 0, 0, "%s", uid_buf);
471
472 buflen = snprintf(uid_buf, sizeof(uid_buf), ":%s SJOIN %ju %s %s %s:",
473 source_p->id, channel->creation_time,
474 channel->name, modebuf, parabuf);
475 uid_ptr = uid_buf + buflen;
476 }
477
478 uid_ptr += sprintf(uid_ptr, "%s%s ", uid_prefix, target_p->id);
479
480 if (member_find_link(target_p, channel) == NULL)
481 {
482 add_user_to_channel(channel, target_p, fl, have_many_uids == false);
483
484 sendto_channel_local(NULL, channel, 0, CAP_EXTENDED_JOIN, 0, ":%s!%s@%s JOIN %s %s :%s",
485 target_p->name, target_p->username,
486 target_p->host, channel->name, target_p->account, target_p->info);
487 sendto_channel_local(NULL, channel, 0, 0, CAP_EXTENDED_JOIN, ":%s!%s@%s JOIN :%s",
488 target_p->name, target_p->username,
489 target_p->host, channel->name);
490
491 if (target_p->away[0])
492 sendto_channel_local(target_p, channel, 0, CAP_AWAY_NOTIFY, 0,
493 ":%s!%s@%s AWAY :%s",
494 target_p->name, target_p->username,
495 target_p->host, target_p->away);
496 }
497
498 if (fl & CHFL_CHANOP)
499 {
500 *mbuf++ = 'o';
501 para[pargs++] = target_p->name;
502
503 if (pargs >= MAXMODEPARAMS)
504 {
505 /*
506 * Ok, the code is now going to "walk" through
507 * sendbuf, filling in para strings. So, I will use sptr
508 * to point into the sendbuf.
509 * Notice, that ircsprintf() returns the number of chars
510 * successfully inserted into string.
511 * - Dianora
512 */
513
514 sptr = sendbuf;
515 *mbuf = '\0';
516
517 for (lcount = 0; lcount < MAXMODEPARAMS; ++lcount)
518 {
519 slen = sprintf(sptr, " %s", para[lcount]); /* see? */
520 sptr += slen; /* ready for next */
521 }
522
523 sendto_channel_local(NULL, channel, 0, 0, 0, ":%s MODE %s %s%s",
524 origin->name, channel->name, modebuf, sendbuf);
525 mbuf = modebuf;
526 *mbuf++ = '+';
527
528 sendbuf[0] = '\0';
529 pargs = 0;
530 }
531 }
532
533 if (fl & CHFL_HALFOP)
534 {
535 *mbuf++ = 'h';
536 para[pargs++] = target_p->name;
537
538 if (pargs >= MAXMODEPARAMS)
539 {
540 sptr = sendbuf;
541 *mbuf = '\0';
542
543 for (lcount = 0; lcount < MAXMODEPARAMS; ++lcount)
544 {
545 slen = sprintf(sptr, " %s", para[lcount]);
546 sptr += slen;
547 }
548
549 sendto_channel_local(NULL, channel, 0, 0, 0, ":%s MODE %s %s%s",
550 origin->name, channel->name, modebuf, sendbuf);
551
552 mbuf = modebuf;
553 *mbuf++ = '+';
554
555 sendbuf[0] = '\0';
556 pargs = 0;
557 }
558 }
559
560 if (fl & CHFL_VOICE)
561 {
562 *mbuf++ = 'v';
563 para[pargs++] = target_p->name;
564
565 if (pargs >= MAXMODEPARAMS)
566 {
567 sptr = sendbuf;
568 *mbuf = '\0';
569
570 for (lcount = 0; lcount < MAXMODEPARAMS; ++lcount)
571 {
572 slen = sprintf(sptr, " %s", para[lcount]);
573 sptr += slen;
574 }
575
576 sendto_channel_local(NULL, channel, 0, 0, 0, ":%s MODE %s %s%s",
577 origin->name, channel->name, modebuf, sendbuf);
578
579 mbuf = modebuf;
580 *mbuf++ = '+';
581
582 sendbuf[0] = '\0';
583 pargs = 0;
584 }
585 }
586
587 nextnick:
588 if ((s = p) == NULL)
589 break;
590
591 while (*s == ' ')
592 ++s;
593
594 if ((p = strchr(s, ' ')))
595 {
596 *p++ = '\0';
597
598 while (*p == ' ')
599 ++p;
600 }
601 }
602
603 *mbuf = '\0';
604 *(uid_ptr - 1) = '\0';
605
606 /*
607 * checking for lcount < MAXMODEPARAMS at this time is wrong
608 * since the code has already verified above that pargs < MAXMODEPARAMS
609 * checking for para[lcount] != '\0' is also wrong, since
610 * there is no place where para[lcount] is set!
611 * - Dianora
612 */
613
614 if (pargs)
615 {
616 sptr = sendbuf;
617
618 for (lcount = 0; lcount < pargs; ++lcount)
619 {
620 slen = sprintf(sptr, " %s", para[lcount]);
621 sptr += slen;
622 }
623
624 sendto_channel_local(NULL, channel, 0, 0, 0, ":%s MODE %s %s%s",
625 origin->name, channel->name, modebuf, sendbuf);
626 }
627
628 /*
629 * If this happens, it's the result of a malformed SJOIN
630 * a remnant from the old persistent channel code. *sigh*
631 * Or it could be the result of a client just leaving
632 * and leaving us with a channel formed just as the client parts.
633 * - Dianora
634 */
635 if (dlink_list_length(&channel->members) == 0 && isnew == true)
636 {
637 channel_free(channel);
638 return;
639 }
640
641 if (*parv[4 + args] == '\0')
642 return;
643
644 sendto_server(source_p, 0, 0, "%s", uid_buf);
645 }
646
647 static struct Message sjoin_msgtab =
648 {
649 .cmd = "SJOIN",
650 .handlers[UNREGISTERED_HANDLER] = { .handler = m_unregistered },
651 .handlers[CLIENT_HANDLER] = { .handler = m_ignore },
652 .handlers[SERVER_HANDLER] = { .handler = ms_sjoin, .args_min = 5, .empty_last_arg = true },
653 .handlers[ENCAP_HANDLER] = { .handler = m_ignore },
654 .handlers[OPER_HANDLER] = { .handler = m_ignore }
655 };
656
657 static void
658 module_init(void)
659 {
660 mod_add_cmd(&sjoin_msgtab);
661 }
662
663 static void
664 module_exit(void)
665 {
666 mod_del_cmd(&sjoin_msgtab);
667 }
668
669 struct module module_entry =
670 {
671 .version = "$Revision$",
672 .modinit = module_init,
673 .modexit = module_exit,
674 .is_core = true
675 };

Properties

Name Value
svn:eol-style native
svn:keywords Id Revision