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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 8280 - (show annotations)
Tue Feb 20 19:30:33 2018 UTC (4 years, 7 months ago) by michael
File MIME type: text/x-chdr
File size: 18215 byte(s)
- Update copyright years

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

Properties

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

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