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