ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/ircd-hybrid/trunk/modules/core/m_message.c
Revision: 6648
Committed: Sun Oct 25 14:36:13 2015 UTC (9 years, 10 months ago) by michael
Content type: text/x-csrc
File size: 20105 byte(s)
Log Message:
- m_message.c: minor style changes

File Contents

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

Properties

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