ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/ircd-hybrid/branches/8.2.x/modules/core/m_message.c
Revision: 10194
Committed: Fri Jul 15 12:46:01 2022 UTC (20 months, 1 week ago) by michael
Content type: text/x-csrc
File size: 17576 byte(s)
Log Message:
- Fix clang warning in sendto_match_butone() with -Wsign-conversion. Kill MATCH_HOST/MATCH_SERVER while at it.

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

Properties

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