/[svn]/ircd-hybrid/branches/8.2.x/modules/core/m_sjoin.c
ViewVC logotype

Contents of /ircd-hybrid/branches/8.2.x/modules/core/m_sjoin.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 10026 - (show annotations)
Fri Jan 14 15:11:00 2022 UTC (4 months, 1 week ago) by michael
File MIME type: text/x-chdr
File size: 17052 byte(s)
- channel.c:channel_demote_members(): rewrite to remove all member flags in a single run. Also members having multiple flags set are now stuffed into the same MODE message (as long as pargs <= MAXMODEPARAMS)

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

Properties

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

svnadmin@ircd-hybrid.org
ViewVC Help
Powered by ViewVC 1.1.28