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_message.c
23 * \brief Includes required functions for processing the PRIVMSG/NOTICE command.
24 * \version $Id: m_message.c 9858 2021-01-01 04:43:42Z michael $
25 */
26
27 #include "stdinc.h"
28 #include "list.h"
29 #include "client.h"
30 #include "ircd.h"
31 #include "numeric.h"
32 #include "conf.h"
33 #include "server.h"
34 #include "send.h"
35 #include "parse.h"
36 #include "modules.h"
37 #include "channel.h"
38 #include "channel_mode.h"
39 #include "irc_string.h"
40 #include "hash.h"
41 #include "packet.h"
42
43
44 enum
45 {
46 ENTITY_NONE,
47 ENTITY_CHANNEL,
48 ENTITY_CLIENT
49 };
50
51 static const char *const command[] =
52 {
53 [false] = "PRIVMSG",
54 [true] = "NOTICE"
55 };
56
57 static struct
58 {
59 void *ptr;
60 unsigned int type;
61 unsigned int flags;
62 } targets[IRCD_BUFSIZE];
63
64 static unsigned int ntargets;
65
66
67 /* duplicate_ptr()
68 *
69 * inputs - pointer to check
70 * - pointer to table of entities
71 * - number of valid entities so far
72 * output - YES if duplicate pointer in table, NO if not.
73 * note, this does the canonize using pointers
74 * side effects - NONE
75 */
76 static bool
duplicate_ptr(const void * const ptr)77 duplicate_ptr(const void *const ptr)
78 {
79 for (unsigned int i = 0; i < ntargets; ++i)
80 if (targets[i].ptr == ptr)
81 return true;
82
83 return false;
84 }
85
86 /* flood_attack_client()
87 *
88 * inputs - flag 0 if PRIVMSG 1 if NOTICE. RFC
89 * say NOTICE must not auto reply
90 * - pointer to source Client
91 * - pointer to target Client
92 * output - 1 if target is under flood attack
93 * side effects - check for flood attack on target target_p
94 */
95 static bool
flood_attack_client(bool notice,struct Client * source_p,struct Client * target_p)96 flood_attack_client(bool notice, struct Client *source_p, struct Client *target_p)
97 {
98 assert(MyClient(target_p));
99 assert(IsClient(source_p));
100
101 if (!(GlobalSetOptions.floodcount && GlobalSetOptions.floodtime))
102 return false;
103
104 if (HasUMode(source_p, UMODE_OPER))
105 return false;
106
107 if (HasFlag(source_p, FLAGS_SERVICE | FLAGS_CANFLOOD))
108 return false;
109
110 if (target_p->connection->first_received_message_time + GlobalSetOptions.floodtime < event_base->time.sec_monotonic)
111 {
112 if (target_p->connection->received_number_of_privmsgs)
113 target_p->connection->received_number_of_privmsgs = 0;
114 else
115 DelFlag(target_p, FLAGS_FLOOD_NOTICED);
116
117 target_p->connection->first_received_message_time = event_base->time.sec_monotonic;
118 }
119
120 if (target_p->connection->received_number_of_privmsgs >= GlobalSetOptions.floodcount)
121 {
122 if (!HasFlag(target_p, FLAGS_FLOOD_NOTICED))
123 {
124 sendto_realops_flags(UMODE_FLOOD, L_ALL, SEND_NOTICE, "Possible Flooder %s on %s target: %s",
125 client_get_name(source_p, HIDE_IP),
126 source_p->servptr->name, target_p->name);
127 AddFlag(target_p, FLAGS_FLOOD_NOTICED);
128 }
129
130 if (notice == false)
131 sendto_one_notice(source_p, &me, ":*** Message to %s throttled due to flooding",
132 target_p->name);
133 return true;
134 }
135
136 ++target_p->connection->received_number_of_privmsgs;
137 return false;
138 }
139
140 /* flood_attack_channel()
141 *
142 * inputs - flag 0 if PRIVMSG 1 if NOTICE. RFC
143 * says NOTICE must not auto reply
144 * - pointer to source Client
145 * - pointer to target channel
146 * output - 1 if target is under flood attack
147 * side effects - check for flood attack on target channel
148 */
149 static bool
flood_attack_channel(bool notice,struct Client * source_p,struct Channel * channel)150 flood_attack_channel(bool notice, struct Client *source_p, struct Channel *channel)
151 {
152 if (!(GlobalSetOptions.floodcount && GlobalSetOptions.floodtime))
153 return false;
154
155 if (HasUMode(source_p, UMODE_OPER))
156 return false;
157
158 if (HasFlag(source_p, FLAGS_SERVICE | FLAGS_CANFLOOD))
159 return false;
160
161 if (channel->first_received_message_time + GlobalSetOptions.floodtime < event_base->time.sec_monotonic)
162 {
163 if (channel->received_number_of_privmsgs)
164 channel->received_number_of_privmsgs = 0;
165 else
166 ClearFloodNoticed(channel);
167
168 channel->first_received_message_time = event_base->time.sec_monotonic;
169 }
170
171 if (channel->received_number_of_privmsgs >= GlobalSetOptions.floodcount)
172 {
173 if (!IsSetFloodNoticed(channel))
174 {
175 sendto_realops_flags(UMODE_FLOOD, L_ALL, SEND_NOTICE, "Possible Flooder %s on %s target: %s",
176 client_get_name(source_p, HIDE_IP),
177 source_p->servptr->name, channel->name);
178 SetFloodNoticed(channel);
179 }
180
181 if (MyClient(source_p))
182 {
183 if (notice == false)
184 sendto_one_notice(source_p, &me, ":*** Message to %s throttled due to flooding",
185 channel->name);
186 return true;
187 }
188 }
189
190 ++channel->received_number_of_privmsgs;
191 return false;
192 }
193
194 /* msg_channel_flags()
195 *
196 * inputs - flag 0 if PRIVMSG 1 if NOTICE. RFC
197 * say NOTICE must not auto reply
198 * - pointer to source_p
199 * - pointer to channel
200 * - flags
201 * - pointer to text to send
202 * output - NONE
203 * side effects - message given channel either chanop or voice
204 */
205 static void
msg_channel(bool notice,struct Client * source_p,struct Channel * channel,unsigned int flags,const char * text)206 msg_channel(bool notice, struct Client *source_p, struct Channel *channel,
207 unsigned int flags, const char *text)
208 {
209 unsigned int type = 0;
210 const char *prefix = "";
211
212 if (flags & CHFL_VOICE)
213 {
214 type = CHFL_CHANOP | CHFL_HALFOP | CHFL_VOICE;
215 prefix = "+";
216 }
217 else if (flags & CHFL_HALFOP)
218 {
219 type = CHFL_CHANOP | CHFL_HALFOP;
220 prefix = "%";
221 }
222 else if (flags & CHFL_CHANOP)
223 {
224 type = CHFL_CHANOP;
225 prefix = "@";
226 }
227
228 /* Chanops and voiced can flood their own channel with impunity */
229 int ret = can_send(channel, source_p, NULL, text, notice);
230 if (ret < 0)
231 {
232 if (ret == CAN_SEND_OPV || flood_attack_channel(notice, source_p, channel) == false)
233 sendto_channel_butone(source_p, source_p, channel, type, "%s %s%s :%s",
234 command[notice], prefix, channel->name, text);
235 }
236 else if (notice == false)
237 sendto_one_numeric(source_p, &me, ret, channel->name, text);
238 }
239
240 /* msg_client()
241 *
242 * inputs - flag 0 if PRIVMSG 1 if NOTICE. RFC
243 * say NOTICE must not auto reply
244 * - pointer to source_p source (struct Client *)
245 * - pointer to target_p target (struct Client *)
246 * - pointer to text
247 * output - NONE
248 * side effects - message given channel either chanop or voice
249 */
250 static void
msg_client(bool notice,struct Client * source_p,struct Client * target_p,const char * text)251 msg_client(bool notice, struct Client *source_p, struct Client *target_p,
252 const char *text)
253 {
254 if (MyClient(source_p))
255 {
256 if (target_p->away[0] && notice == false)
257 sendto_one_numeric(source_p, &me, RPL_AWAY, target_p->name, target_p->away);
258
259 if (HasUMode(target_p, UMODE_REGONLY) && target_p != source_p)
260 {
261 if (!HasUMode(source_p, UMODE_REGISTERED | UMODE_OPER))
262 {
263 if (notice == false)
264 sendto_one_numeric(source_p, &me, ERR_NONONREG, target_p->name);
265 return;
266 }
267 }
268 }
269
270 if (MyClient(target_p) && IsClient(source_p))
271 {
272 if (HasUMode(target_p, UMODE_CALLERID | UMODE_SOFTCALLERID) &&
273 accept_message(source_p, target_p) == false)
274 {
275 bool callerid = HasUMode(target_p, UMODE_CALLERID) != 0;
276
277 /* check for accept, flag recipient incoming message */
278 if (notice == false)
279 sendto_one_numeric(source_p, &me, RPL_TARGUMODEG,
280 target_p->name,
281 callerid ? "+g" : "+G",
282 callerid ? "server side ignore" :
283 "server side ignore with the exception of common channels");
284
285 if ((target_p->connection->last_caller_id_time +
286 ConfigGeneral.caller_id_wait) < event_base->time.sec_monotonic)
287 {
288 if (notice == false)
289 sendto_one_numeric(source_p, &me, RPL_TARGNOTIFY, target_p->name);
290
291 sendto_one_numeric(target_p, &me, RPL_UMODEGMSG,
292 source_p->name, source_p->username, source_p->host,
293 callerid ? "+g" : "+G");
294 target_p->connection->last_caller_id_time = event_base->time.sec_monotonic;
295 }
296
297 /* Only so opers can watch for floods */
298 flood_attack_client(notice, source_p, target_p);
299 return;
300 }
301
302 if (flood_attack_client(notice, source_p, target_p) == true)
303 return;
304 }
305
306 sendto_anywhere(target_p, source_p, command[notice], ":%s", text);
307 }
308
309 /* handle_special()
310 *
311 * inputs - client pointer
312 * - nick stuff to grok for opers
313 * - text to send if grok
314 * output - none
315 * side effects - old style nick@server is handled here for non opers
316 * all the traditional oper type messages are also parsed here.
317 * i.e. "/msg #some.host."
318 * However, syntax has been changed.
319 * previous syntax "/msg #some.host.mask"
320 * now becomes "/msg $#some.host.mask"
321 * previous syntax of: "/msg $some.server.mask" remains
322 * This disambiguates the syntax.
323 */
324 static void
handle_special(bool notice,struct Client * source_p,const char * nick,const char * text)325 handle_special(bool notice, struct Client *source_p,
326 const char *nick, const char *text)
327 {
328 /*
329 * nick@server addressed?
330 */
331 const char *server = strchr(nick, '@');
332 if (server)
333 {
334 struct Client *target_p = hash_find_server(server + 1);
335 if (target_p == NULL)
336 {
337 sendto_one_numeric(source_p, &me, ERR_NOSUCHSERVER, server + 1);
338 return;
339 }
340
341 if (!IsMe(target_p))
342 {
343 sendto_one(target_p, ":%s %s %s :%s", source_p->id, command[notice], nick, text);
344 return;
345 }
346
347 sendto_one_numeric(source_p, &me, ERR_NOSUCHNICK, nick);
348 return;
349 }
350
351 if (!HasUMode(source_p, UMODE_OPER))
352 {
353 sendto_one_numeric(source_p, &me, ERR_NOPRIVILEGES);
354 return;
355 }
356
357 /*
358 * The following two cases allow masks in NOTICEs
359 * (for OPERs only)
360 *
361 * Armin, 8Jun90 (gruner@informatik.tu-muenchen.de)
362 */
363 if (*nick == '$')
364 {
365 if (*(nick + 1) == '$' || *(nick + 1) == '#')
366 ++nick;
367 else if (MyClient(source_p))
368 {
369 sendto_one_notice(source_p, &me, ":The command %s %s is no longer supported, please use $%s",
370 command[notice], nick, nick);
371 return;
372 }
373
374 const char *s = strrchr(nick, '.');
375 if (s == NULL)
376 {
377 sendto_one_numeric(source_p, &me, ERR_NOTOPLEVEL, nick);
378 return;
379 }
380
381 while (*++s)
382 if (*s == '.' || *s == '*' || *s == '?')
383 break;
384
385 if (*s == '*' || *s == '?')
386 {
387 sendto_one_numeric(source_p, &me, ERR_WILDTOPLEVEL, nick);
388 return;
389 }
390
391 sendto_match_butone(IsServer(source_p->from) ? source_p->from : NULL, source_p,
392 nick + 1, (*nick == '#') ? MATCH_HOST : MATCH_SERVER,
393 "%s $%s :%s", command[notice], nick, text);
394 }
395 }
396
397 /* build_target_list()
398 *
399 * inputs - pointer to given source (oper/client etc.)
400 * - pointer to list of nicks/channels
401 * - pointer to table to place results
402 * - pointer to text (only used if source_p is an oper)
403 * output - number of valid entities
404 * side effects - target_table is modified to contain a list of
405 * pointers to channels or clients
406 * if source client is an oper
407 * all the classic old bizzare oper privmsg tricks
408 * are parsed and sent as is, if prefixed with $
409 * to disambiguate.
410 *
411 */
412 static void
build_target_list(bool notice,struct Client * source_p,char * list,const char * text)413 build_target_list(bool notice, struct Client *source_p, char *list, const char *text)
414 {
415 char *p = NULL;
416 void *target = NULL;
417
418 ntargets = 0;
419
420 for (const char *name = strtok_r(list, ",", &p); name;
421 name = strtok_r(NULL, ",", &p))
422 {
423 /*
424 * Channels are privmsg'd a lot more than other clients, moved up
425 * here plain old channel msg?
426 */
427 if (IsChanPrefix(*name))
428 {
429 if ((target = hash_find_channel(name)))
430 {
431 if (duplicate_ptr(target) == false)
432 {
433 if (ntargets >= ConfigGeneral.max_targets)
434 {
435 sendto_one_numeric(source_p, &me, ERR_TOOMANYTARGETS,
436 name, ConfigGeneral.max_targets);
437 return;
438 }
439
440 targets[ntargets].ptr = target;
441 targets[ntargets].type = ENTITY_CHANNEL;
442 targets[ntargets++].flags = 0;
443 }
444 }
445 else if (notice == false)
446 sendto_one_numeric(source_p, &me, ERR_NOSUCHNICK, name);
447
448 continue;
449 }
450
451 /* Look for a PRIVMSG/NOTICE to another client */
452 if ((target = find_person(source_p, name)))
453 {
454 if (duplicate_ptr(target) == false)
455 {
456 if (ntargets >= ConfigGeneral.max_targets)
457 {
458 sendto_one_numeric(source_p, &me, ERR_TOOMANYTARGETS,
459 name, ConfigGeneral.max_targets);
460 return;
461 }
462
463 targets[ntargets].ptr = target;
464 targets[ntargets].type = ENTITY_CLIENT;
465 targets[ntargets++].flags = 0;
466 }
467
468 continue;
469 }
470
471 /* @#channel or +#channel message ? */
472 unsigned int type = 0;
473 const char *with_prefix = name;
474
475 /* Allow %+@ if someone wants to do that */
476 while (true)
477 {
478 if (*name == '@')
479 type |= CHFL_CHANOP;
480 else if (*name == '%')
481 type |= CHFL_CHANOP | CHFL_HALFOP;
482 else if (*name == '+')
483 type |= CHFL_CHANOP | CHFL_HALFOP | CHFL_VOICE;
484 else
485 break;
486 ++name;
487 }
488
489 if (type)
490 {
491 if (EmptyString(name)) /* If it's a '\0' dump it, there is no recipient */
492 {
493 sendto_one_numeric(source_p, &me, ERR_NORECIPIENT, command[notice]);
494 continue;
495 }
496
497 /*
498 * At this point, name should be a channel name i.e. #foo. If the channel
499 * is found, fine, if not report an error.
500 */
501 if ((target = hash_find_channel(name)))
502 {
503 if (IsClient(source_p) && !HasFlag(source_p, FLAGS_SERVICE))
504 {
505 if (member_has_flags(member_find_link(source_p, target),
506 CHFL_CHANOP|CHFL_HALFOP|CHFL_VOICE) == false)
507 {
508 sendto_one_numeric(source_p, &me, ERR_CHANOPRIVSNEEDED, with_prefix);
509 continue;
510 }
511 }
512
513 if (duplicate_ptr(target) == false)
514 {
515 if (ntargets >= ConfigGeneral.max_targets)
516 {
517 sendto_one_numeric(source_p, &me, ERR_TOOMANYTARGETS,
518 name, ConfigGeneral.max_targets);
519 return;
520 }
521
522 targets[ntargets].ptr = target;
523 targets[ntargets].type = ENTITY_CHANNEL;
524 targets[ntargets++].flags = type;
525 }
526 }
527 else if (notice == false)
528 sendto_one_numeric(source_p, &me, ERR_NOSUCHNICK, name);
529
530 continue;
531 }
532
533 if (*name == '$' || strchr(name, '@'))
534 handle_special(notice, source_p, name, text);
535 else if (notice == false)
536 {
537 if (!IsDigit(*name) || MyClient(source_p))
538 sendto_one_numeric(source_p, &me, ERR_NOSUCHNICK, name);
539 }
540 }
541 }
542
543 /*
544 * inputs - flag privmsg or notice
545 * - pointer to source_p
546 * - pointer to channel
547 */
548 static void
m_message(bool notice,struct Client * source_p,int parc,char * parv[])549 m_message(bool notice, struct Client *source_p, int parc, char *parv[])
550 {
551 if (EmptyString(parv[1]))
552 {
553 if (notice == false)
554 sendto_one_numeric(source_p, &me, ERR_NORECIPIENT, command[notice]);
555 return;
556 }
557
558 if (EmptyString(parv[2]))
559 {
560 if (notice == false)
561 sendto_one_numeric(source_p, &me, ERR_NOTEXTTOSEND);
562 return;
563 }
564
565 build_target_list(notice, source_p, parv[1], parv[2]);
566
567 for (unsigned int i = 0; i < ntargets; ++i)
568 {
569 switch (targets[i].type)
570 {
571 case ENTITY_CLIENT:
572 msg_client(notice, source_p, targets[i].ptr, parv[2]);
573 break;
574
575 case ENTITY_CHANNEL:
576 msg_channel(notice, source_p, targets[i].ptr, targets[i].flags, parv[2]);
577 break;
578 }
579 }
580 }
581
582 static void
m_privmsg(struct Client * source_p,int parc,char * parv[])583 m_privmsg(struct Client *source_p, int parc, char *parv[])
584 {
585 /*
586 * Servers have no reason to send privmsgs, yet sometimes there is cause
587 * for a notice.. (for example remote kline replies) --fl_
588 */
589 if (!IsClient(source_p))
590 return;
591
592 if (MyConnect(source_p))
593 source_p->connection->last_privmsg = event_base->time.sec_monotonic;
594
595 m_message(false, source_p, parc, parv);
596 }
597
598 static void
m_notice(struct Client * source_p,int parc,char * parv[])599 m_notice(struct Client *source_p, int parc, char *parv[])
600 {
601 m_message(true, source_p, parc, parv);
602 }
603
604 static struct Message privmsg_msgtab =
605 {
606 .cmd = "PRIVMSG",
607 .handlers[UNREGISTERED_HANDLER] = { .handler = m_unregistered },
608 .handlers[CLIENT_HANDLER] = { .handler = m_privmsg, .end_grace_period = true },
609 .handlers[SERVER_HANDLER] = { .handler = m_privmsg },
610 .handlers[ENCAP_HANDLER] = { .handler = m_ignore },
611 .handlers[OPER_HANDLER] = { .handler = m_privmsg, .end_grace_period = true }
612 };
613
614 static struct Message notice_msgtab =
615 {
616 .cmd = "NOTICE",
617 .handlers[UNREGISTERED_HANDLER] = { .handler = m_unregistered },
618 .handlers[CLIENT_HANDLER] = { .handler = m_notice },
619 .handlers[SERVER_HANDLER] = { .handler = m_notice },
620 .handlers[ENCAP_HANDLER] = { .handler = m_ignore },
621 .handlers[OPER_HANDLER] = { .handler = m_notice }
622 };
623
624 static void
module_init(void)625 module_init(void)
626 {
627 mod_add_cmd(&privmsg_msgtab);
628 mod_add_cmd(¬ice_msgtab);
629 }
630
631 static void
module_exit(void)632 module_exit(void)
633 {
634 mod_del_cmd(&privmsg_msgtab);
635 mod_del_cmd(¬ice_msgtab);
636 }
637
638 struct module module_entry =
639 {
640 .version = "$Revision: 9858 $",
641 .modinit = module_init,
642 .modexit = module_exit,
643 .is_core = true
644 };
645