1 /*
2 * ircd-hybrid: an advanced, lightweight Internet Relay Chat Daemon (ircd)
3 *
4 * Copyright (c) 1997-2021 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: m_sjoin.c 9858 2021-01-01 04:43:42Z michael $
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
set_final_mode(const struct Mode * mode,const struct Mode * oldmode,char * mbuf,char * pbuf)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
remove_ban_list(struct Channel * channel,const struct Client * client,dlink_list * list,char c)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
ms_sjoin(struct Client * source_p,int parc,char * parv[])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 if (isnew == false)
334 strlcpy(channel->name, parv[2], sizeof(channel->name));
335
336 channel_demote_members(channel, origin, CHFL_CHANOP, 'o');
337 channel_demote_members(channel, origin, CHFL_HALFOP, 'h');
338 channel_demote_members(channel, origin, CHFL_VOICE, 'v');
339
340 remove_ban_list(channel, origin, &channel->banlist, 'b');
341 remove_ban_list(channel, origin, &channel->exceptlist, 'e');
342 remove_ban_list(channel, origin, &channel->invexlist, 'I');
343
344 clear_ban_cache_list(&channel->members_local);
345 invite_clear_list(&channel->invites);
346
347 if (channel->topic[0])
348 {
349 channel_set_topic(channel, "", "", 0, false);
350 sendto_channel_local(NULL, channel, 0, 0, 0, ":%s TOPIC %s :",
351 origin->name, channel->name);
352 }
353
354 sendto_channel_local(NULL, channel, 0, 0, 0,
355 ":%s NOTICE %s :*** Notice -- TS for %s changed from %ju to %ju",
356 me.name, channel->name, channel->name,
357 oldts, newts);
358 }
359
360 if (*modebuf)
361 sendto_channel_local(NULL, channel, 0, 0, 0, ":%s MODE %s %s %s",
362 origin->name, channel->name, modebuf, parabuf);
363
364 if (*parv[3] != '0' && keep_new_modes == true)
365 channel_modes(channel, source_p, NULL, modebuf, parabuf);
366 else
367 {
368 modebuf[0] = '0';
369 modebuf[1] = '\0';
370 }
371
372 buflen = snprintf(uid_buf, sizeof(uid_buf), ":%s SJOIN %ju %s %s %s:",
373 source_p->id, channel->creation_time,
374 channel->name, modebuf, parabuf);
375 uid_ptr = uid_buf + buflen;
376
377 /*
378 * Check we can fit a nick on the end, as well as \r\n and a prefix "
379 * @%+", and a space.
380 */
381 if (buflen >= (IRCD_BUFSIZE - IDLEN - 2 - CMEMBER_STATUS_FLAGS_LEN - 1))
382 {
383 sendto_realops_flags(UMODE_SERVNOTICE, L_ALL, SEND_NOTICE,
384 "Long SJOIN from server: %s(via %s) (ignored)",
385 source_p->name, source_p->from->name);
386 return;
387 }
388
389 char *mbuf = modebuf;
390 *mbuf++ = '+';
391
392 s = parv[args + 4];
393 while (*s == ' ')
394 ++s;
395
396 if ((p = strchr(s, ' ')))
397 {
398 *p++ = '\0';
399
400 while (*p == ' ')
401 ++p;
402 have_many_uids = *p != '\0';
403 }
404
405 while (*s)
406 {
407 bool valid_mode = true;
408 fl = 0;
409
410 do
411 {
412 switch (*s)
413 {
414 case '@':
415 fl |= CHFL_CHANOP;
416 ++s;
417 break;
418 case '%':
419 fl |= CHFL_HALFOP;
420 ++s;
421 break;
422 case '+':
423 fl |= CHFL_VOICE;
424 ++s;
425 break;
426 default:
427 valid_mode = false;
428 break;
429 }
430 } while (valid_mode == true);
431
432 /*
433 * If the client doesn't exist, or if it's fake direction/server, skip.
434 * we cannot send ERR_NOSUCHNICK here because if it's a UID, we cannot
435 * lookup the nick, and it's better to never send the numeric than only
436 * sometimes.
437 */
438 if ((target_p = find_person(source_p, s)) == NULL || target_p->from != source_p->from)
439 goto nextnick;
440
441 len_uid = strlen(target_p->id);
442 up = uid_prefix;
443
444 if (keep_new_modes == true)
445 {
446 if (fl & CHFL_CHANOP)
447 {
448 *up++ = '@';
449 len_uid++;
450 }
451
452 if (fl & CHFL_HALFOP)
453 {
454 *up++ = '%';
455 len_uid++;
456 }
457
458 if (fl & CHFL_VOICE)
459 {
460 *up++ = '+';
461 len_uid++;
462 }
463 }
464 else
465 fl = 0;
466
467 *up = '\0';
468
469 if ((uid_ptr - uid_buf + len_uid) > (IRCD_BUFSIZE - 2))
470 {
471 sendto_server(source_p, 0, 0, "%s", uid_buf);
472
473 buflen = snprintf(uid_buf, sizeof(uid_buf), ":%s SJOIN %ju %s %s %s:",
474 source_p->id, channel->creation_time,
475 channel->name, modebuf, parabuf);
476 uid_ptr = uid_buf + buflen;
477 }
478
479 uid_ptr += sprintf(uid_ptr, "%s%s ", uid_prefix, target_p->id);
480
481 if (member_find_link(target_p, channel) == NULL)
482 {
483 add_user_to_channel(channel, target_p, fl, have_many_uids == false);
484
485 sendto_channel_local(NULL, channel, 0, CAP_EXTENDED_JOIN, 0, ":%s!%s@%s JOIN %s %s :%s",
486 target_p->name, target_p->username,
487 target_p->host, channel->name, target_p->account, target_p->info);
488 sendto_channel_local(NULL, channel, 0, 0, CAP_EXTENDED_JOIN, ":%s!%s@%s JOIN :%s",
489 target_p->name, target_p->username,
490 target_p->host, channel->name);
491
492 if (target_p->away[0])
493 sendto_channel_local(target_p, channel, 0, CAP_AWAY_NOTIFY, 0,
494 ":%s!%s@%s AWAY :%s",
495 target_p->name, target_p->username,
496 target_p->host, target_p->away);
497 }
498
499 if (fl & CHFL_CHANOP)
500 {
501 *mbuf++ = 'o';
502 para[pargs++] = target_p->name;
503
504 if (pargs >= MAXMODEPARAMS)
505 {
506 /*
507 * Ok, the code is now going to "walk" through
508 * sendbuf, filling in para strings. So, I will use sptr
509 * to point into the sendbuf.
510 * Notice, that ircsprintf() returns the number of chars
511 * successfully inserted into string.
512 * - Dianora
513 */
514
515 sptr = sendbuf;
516 *mbuf = '\0';
517
518 for (lcount = 0; lcount < MAXMODEPARAMS; ++lcount)
519 {
520 slen = sprintf(sptr, " %s", para[lcount]); /* see? */
521 sptr += slen; /* ready for next */
522 }
523
524 sendto_channel_local(NULL, channel, 0, 0, 0, ":%s MODE %s %s%s",
525 origin->name, channel->name, modebuf, sendbuf);
526 mbuf = modebuf;
527 *mbuf++ = '+';
528
529 sendbuf[0] = '\0';
530 pargs = 0;
531 }
532 }
533
534 if (fl & CHFL_HALFOP)
535 {
536 *mbuf++ = 'h';
537 para[pargs++] = target_p->name;
538
539 if (pargs >= MAXMODEPARAMS)
540 {
541 sptr = sendbuf;
542 *mbuf = '\0';
543
544 for (lcount = 0; lcount < MAXMODEPARAMS; ++lcount)
545 {
546 slen = sprintf(sptr, " %s", para[lcount]);
547 sptr += slen;
548 }
549
550 sendto_channel_local(NULL, channel, 0, 0, 0, ":%s MODE %s %s%s",
551 origin->name, channel->name, modebuf, sendbuf);
552
553 mbuf = modebuf;
554 *mbuf++ = '+';
555
556 sendbuf[0] = '\0';
557 pargs = 0;
558 }
559 }
560
561 if (fl & CHFL_VOICE)
562 {
563 *mbuf++ = 'v';
564 para[pargs++] = target_p->name;
565
566 if (pargs >= MAXMODEPARAMS)
567 {
568 sptr = sendbuf;
569 *mbuf = '\0';
570
571 for (lcount = 0; lcount < MAXMODEPARAMS; ++lcount)
572 {
573 slen = sprintf(sptr, " %s", para[lcount]);
574 sptr += slen;
575 }
576
577 sendto_channel_local(NULL, channel, 0, 0, 0, ":%s MODE %s %s%s",
578 origin->name, channel->name, modebuf, sendbuf);
579
580 mbuf = modebuf;
581 *mbuf++ = '+';
582
583 sendbuf[0] = '\0';
584 pargs = 0;
585 }
586 }
587
588 nextnick:
589 if ((s = p) == NULL)
590 break;
591
592 while (*s == ' ')
593 ++s;
594
595 if ((p = strchr(s, ' ')))
596 {
597 *p++ = '\0';
598
599 while (*p == ' ')
600 ++p;
601 }
602 }
603
604 *mbuf = '\0';
605 *(uid_ptr - 1) = '\0';
606
607 /*
608 * checking for lcount < MAXMODEPARAMS at this time is wrong
609 * since the code has already verified above that pargs < MAXMODEPARAMS
610 * checking for para[lcount] != '\0' is also wrong, since
611 * there is no place where para[lcount] is set!
612 * - Dianora
613 */
614
615 if (pargs)
616 {
617 sptr = sendbuf;
618
619 for (lcount = 0; lcount < pargs; ++lcount)
620 {
621 slen = sprintf(sptr, " %s", para[lcount]);
622 sptr += slen;
623 }
624
625 sendto_channel_local(NULL, channel, 0, 0, 0, ":%s MODE %s %s%s",
626 origin->name, channel->name, modebuf, sendbuf);
627 }
628
629 /*
630 * If this happens, it's the result of a malformed SJOIN
631 * a remnant from the old persistent channel code. *sigh*
632 * Or it could be the result of a client just leaving
633 * and leaving us with a channel formed just as the client parts.
634 * - Dianora
635 */
636 if (dlink_list_length(&channel->members) == 0 && isnew == true)
637 {
638 channel_free(channel);
639 return;
640 }
641
642 if (*parv[4 + args] == '\0')
643 return;
644
645 sendto_server(source_p, 0, 0, "%s", uid_buf);
646 }
647
648 static struct Message sjoin_msgtab =
649 {
650 .cmd = "SJOIN",
651 .handlers[UNREGISTERED_HANDLER] = { .handler = m_unregistered },
652 .handlers[CLIENT_HANDLER] = { .handler = m_ignore },
653 .handlers[SERVER_HANDLER] = { .handler = ms_sjoin, .args_min = 5, .empty_last_arg = true },
654 .handlers[ENCAP_HANDLER] = { .handler = m_ignore },
655 .handlers[OPER_HANDLER] = { .handler = m_ignore }
656 };
657
658 static void
module_init(void)659 module_init(void)
660 {
661 mod_add_cmd(&sjoin_msgtab);
662 }
663
664 static void
module_exit(void)665 module_exit(void)
666 {
667 mod_del_cmd(&sjoin_msgtab);
668 }
669
670 struct module module_entry =
671 {
672 .version = "$Revision: 9858 $",
673 .modinit = module_init,
674 .modexit = module_exit,
675 .is_core = true
676 };
677