]> jfr.im git - solanum.git/blob - modules/core/m_message.c
Remove user@server messages to local users.
[solanum.git] / modules / core / m_message.c
1 /*
2 * ircd-ratbox: A slightly useful ircd.
3 * m_message.c: Sends a (PRIVMSG|NOTICE) message to a user or channel.
4 *
5 * Copyright (C) 1990 Jarkko Oikarinen and University of Oulu, Co Center
6 * Copyright (C) 1996-2002 Hybrid Development Team
7 * Copyright (C) 2002-2005 ircd-ratbox development team
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
22 * USA
23 *
24 * $Id: m_message.c 3173 2007-01-31 23:57:18Z jilles $
25 */
26
27 #include "stdinc.h"
28 #include "client.h"
29 #include "ircd.h"
30 #include "numeric.h"
31 #include "common.h"
32 #include "s_conf.h"
33 #include "s_serv.h"
34 #include "msg.h"
35 #include "parse.h"
36 #include "modules.h"
37 #include "channel.h"
38 #include "match.h"
39 #include "hash.h"
40 #include "class.h"
41 #include "msg.h"
42 #include "packet.h"
43 #include "send.h"
44 #include "s_newconf.h"
45 #include "s_stats.h"
46 #include "inline/stringops.h"
47
48 static int m_message(int, const char *, struct Client *, struct Client *, int, const char **);
49 static int m_privmsg(struct Client *, struct Client *, int, const char **);
50 static int m_notice(struct Client *, struct Client *, int, const char **);
51
52 static void expire_tgchange(void *unused);
53 static struct ev_entry *expire_tgchange_event;
54
55 static int
56 modinit(void)
57 {
58 expire_tgchange_event = rb_event_addish("expire_tgchange", expire_tgchange, NULL, 300);
59 expire_tgchange(NULL);
60 return 0;
61 }
62
63 static void
64 moddeinit(void)
65 {
66 rb_event_delete(expire_tgchange_event);
67 }
68
69 struct Message privmsg_msgtab = {
70 "PRIVMSG", 0, 0, 0, MFLG_SLOW | MFLG_UNREG,
71 {mg_unreg, {m_privmsg, 0}, {m_privmsg, 0}, mg_ignore, mg_ignore, {m_privmsg, 0}}
72 };
73 struct Message notice_msgtab = {
74 "NOTICE", 0, 0, 0, MFLG_SLOW,
75 {mg_unreg, {m_notice, 0}, {m_notice, 0}, {m_notice, 0}, mg_ignore, {m_notice, 0}}
76 };
77
78 mapi_clist_av1 message_clist[] = { &privmsg_msgtab, &notice_msgtab, NULL };
79
80 DECLARE_MODULE_AV1(message, modinit, moddeinit, message_clist, NULL, NULL, "$Revision: 3173 $");
81
82 struct entity
83 {
84 void *ptr;
85 int type;
86 int flags;
87 };
88
89 static int build_target_list(int p_or_n, const char *command,
90 struct Client *client_p,
91 struct Client *source_p, const char *nicks_channels, const char *text);
92
93 static int flood_attack_client(int p_or_n, struct Client *source_p, struct Client *target_p);
94 static int flood_attack_channel(int p_or_n, struct Client *source_p,
95 struct Channel *chptr, char *chname);
96
97 #define ENTITY_NONE 0
98 #define ENTITY_CHANNEL 1
99 #define ENTITY_CHANOPS_ON_CHANNEL 2
100 #define ENTITY_CLIENT 3
101
102 static struct entity targets[512];
103 static int ntargets = 0;
104
105 static int duplicate_ptr(void *);
106
107 static void msg_channel(int p_or_n, const char *command,
108 struct Client *client_p,
109 struct Client *source_p, struct Channel *chptr, const char *text);
110
111 static void msg_channel_flags(int p_or_n, const char *command,
112 struct Client *client_p,
113 struct Client *source_p,
114 struct Channel *chptr, int flags, const char *text);
115
116 static void msg_client(int p_or_n, const char *command,
117 struct Client *source_p, struct Client *target_p, const char *text);
118
119 static void handle_special(int p_or_n, const char *command,
120 struct Client *client_p, struct Client *source_p, const char *nick,
121 const char *text);
122
123 /*
124 ** m_privmsg
125 **
126 ** massive cleanup
127 ** rev argv 6/91
128 **
129 ** Another massive cleanup Nov, 2000
130 ** (I don't think there is a single line left from 6/91. Maybe.)
131 ** m_privmsg and m_notice do basically the same thing.
132 ** in the original 2.8.2 code base, they were the same function
133 ** "m_message.c." When we did the great cleanup in conjuncton with bleep
134 ** of ircu fame, we split m_privmsg.c and m_notice.c.
135 ** I don't see the point of that now. Its harder to maintain, its
136 ** easier to introduce bugs into one version and not the other etc.
137 ** Really, the penalty of an extra function call isn't that big a deal folks.
138 ** -db Nov 13, 2000
139 **
140 */
141
142 #define PRIVMSG 0
143 #define NOTICE 1
144
145 static int
146 m_privmsg(struct Client *client_p, struct Client *source_p, int parc, const char *parv[])
147 {
148 return m_message(PRIVMSG, "PRIVMSG", client_p, source_p, parc, parv);
149 }
150
151 static int
152 m_notice(struct Client *client_p, struct Client *source_p, int parc, const char *parv[])
153 {
154 return m_message(NOTICE, "NOTICE", client_p, source_p, parc, parv);
155 }
156
157 /*
158 * inputs - flag privmsg or notice
159 * - pointer to command "PRIVMSG" or "NOTICE"
160 * - pointer to client_p
161 * - pointer to source_p
162 * - pointer to channel
163 */
164 static int
165 m_message(int p_or_n,
166 const char *command,
167 struct Client *client_p, struct Client *source_p, int parc, const char *parv[])
168 {
169 int i;
170
171 if(parc < 2 || EmptyString(parv[1]))
172 {
173 if(p_or_n != NOTICE)
174 sendto_one(source_p, form_str(ERR_NORECIPIENT), me.name,
175 source_p->name, command);
176 return 0;
177 }
178
179 if(parc < 3 || EmptyString(parv[2]))
180 {
181 if(p_or_n != NOTICE)
182 sendto_one(source_p, form_str(ERR_NOTEXTTOSEND), me.name, source_p->name);
183 return 0;
184 }
185
186 /* Finish the flood grace period if theyre not messaging themselves
187 * as some clients (ircN) do this as a "lag check"
188 */
189 if(MyClient(source_p) && !IsFloodDone(source_p) && irccmp(source_p->name, parv[1]))
190 flood_endgrace(source_p);
191
192 if(build_target_list(p_or_n, command, client_p, source_p, parv[1], parv[2]) < 0)
193 {
194 return 0;
195 }
196
197 for(i = 0; i < ntargets; i++)
198 {
199 switch (targets[i].type)
200 {
201 case ENTITY_CHANNEL:
202 msg_channel(p_or_n, command, client_p, source_p,
203 (struct Channel *) targets[i].ptr, parv[2]);
204 break;
205
206 case ENTITY_CHANOPS_ON_CHANNEL:
207 msg_channel_flags(p_or_n, command, client_p, source_p,
208 (struct Channel *) targets[i].ptr,
209 targets[i].flags, parv[2]);
210 break;
211
212 case ENTITY_CLIENT:
213 msg_client(p_or_n, command, source_p,
214 (struct Client *) targets[i].ptr, parv[2]);
215 break;
216 }
217 }
218
219 return 0;
220 }
221
222 /*
223 * build_target_list
224 *
225 * inputs - pointer to given client_p (server)
226 * - pointer to given source (oper/client etc.)
227 * - pointer to list of nicks/channels
228 * - pointer to table to place results
229 * - pointer to text (only used if source_p is an oper)
230 * output - number of valid entities
231 * side effects - target_table is modified to contain a list of
232 * pointers to channels or clients
233 * if source client is an oper
234 * all the classic old bizzare oper privmsg tricks
235 * are parsed and sent as is, if prefixed with $
236 * to disambiguate.
237 *
238 */
239
240 static int
241 build_target_list(int p_or_n, const char *command, struct Client *client_p,
242 struct Client *source_p, const char *nicks_channels, const char *text)
243 {
244 int type;
245 char *p, *nick, *target_list;
246 struct Channel *chptr = NULL;
247 struct Client *target_p;
248
249 target_list = LOCAL_COPY(nicks_channels); /* skip strcpy for non-lazyleafs */
250
251 ntargets = 0;
252
253 for(nick = rb_strtok_r(target_list, ",", &p); nick; nick = rb_strtok_r(NULL, ",", &p))
254 {
255 char *with_prefix;
256 /*
257 * channels are privmsg'd a lot more than other clients, moved up
258 * here plain old channel msg?
259 */
260
261 if(IsChanPrefix(*nick))
262 {
263 /* ignore send of local channel to a server (should not happen) */
264 if(IsServer(client_p) && *nick == '&')
265 continue;
266
267 if((chptr = find_channel(nick)) != NULL)
268 {
269 if(!duplicate_ptr(chptr))
270 {
271 if(ntargets >= ConfigFileEntry.max_targets)
272 {
273 sendto_one(source_p, form_str(ERR_TOOMANYTARGETS),
274 me.name, source_p->name, nick);
275 return (1);
276 }
277 targets[ntargets].ptr = (void *) chptr;
278 targets[ntargets++].type = ENTITY_CHANNEL;
279 }
280 }
281
282 /* non existant channel */
283 else if(p_or_n != NOTICE)
284 sendto_one_numeric(source_p, ERR_NOSUCHNICK,
285 form_str(ERR_NOSUCHNICK), nick);
286
287 continue;
288 }
289
290 if(MyClient(source_p))
291 target_p = find_named_person(nick);
292 else
293 target_p = find_person(nick);
294
295 /* look for a privmsg to another client */
296 if(target_p)
297 {
298 if(!duplicate_ptr(target_p))
299 {
300 if(ntargets >= ConfigFileEntry.max_targets)
301 {
302 sendto_one(source_p, form_str(ERR_TOOMANYTARGETS),
303 me.name, source_p->name, nick);
304 return (1);
305 }
306 targets[ntargets].ptr = (void *) target_p;
307 targets[ntargets].type = ENTITY_CLIENT;
308 targets[ntargets++].flags = 0;
309 }
310 continue;
311 }
312
313 /* @#channel or +#channel message ? */
314
315 type = 0;
316 with_prefix = nick;
317 /* allow %+@ if someone wants to do that */
318 for(;;)
319 {
320 if(*nick == '@')
321 type |= CHFL_CHANOP;
322 else if(*nick == '+')
323 type |= CHFL_CHANOP | CHFL_VOICE;
324 else
325 break;
326 nick++;
327 }
328
329 if(type != 0)
330 {
331 /* no recipient.. */
332 if(EmptyString(nick))
333 {
334 sendto_one(source_p, form_str(ERR_NORECIPIENT),
335 me.name, source_p->name, command);
336 continue;
337 }
338
339 /* At this point, nick+1 should be a channel name i.e. #foo or &foo
340 * if the channel is found, fine, if not report an error
341 */
342
343 if((chptr = find_channel(nick)) != NULL)
344 {
345 struct membership *msptr;
346
347 msptr = find_channel_membership(chptr, source_p);
348
349 if(!IsServer(source_p) && !IsService(source_p) && !is_chanop_voiced(msptr))
350 {
351 sendto_one(source_p, form_str(ERR_CHANOPRIVSNEEDED),
352 me.name, source_p->name, with_prefix);
353 return (-1);
354 }
355
356 if(!duplicate_ptr(chptr))
357 {
358 if(ntargets >= ConfigFileEntry.max_targets)
359 {
360 sendto_one(source_p, form_str(ERR_TOOMANYTARGETS),
361 me.name, source_p->name, nick);
362 return (1);
363 }
364 targets[ntargets].ptr = (void *) chptr;
365 targets[ntargets].type = ENTITY_CHANOPS_ON_CHANNEL;
366 targets[ntargets++].flags = type;
367 }
368 }
369 else if(p_or_n != NOTICE)
370 {
371 sendto_one_numeric(source_p, ERR_NOSUCHNICK,
372 form_str(ERR_NOSUCHNICK), nick);
373 }
374
375 continue;
376 }
377
378 if(strchr(nick, '@') || (IsOper(source_p) && (*nick == '$')))
379 {
380 handle_special(p_or_n, command, client_p, source_p, nick, text);
381 continue;
382 }
383
384 /* no matching anything found - error if not NOTICE */
385 if(p_or_n != NOTICE)
386 {
387 /* dont give this numeric when source is local,
388 * because its misleading --anfl
389 */
390 if(!MyClient(source_p) && IsDigit(*nick))
391 sendto_one(source_p, ":%s %d %s * :Target left IRC. "
392 "Failed to deliver: [%.20s]",
393 get_id(&me, source_p), ERR_NOSUCHNICK,
394 get_id(source_p, source_p), text);
395 else
396 sendto_one_numeric(source_p, ERR_NOSUCHNICK,
397 form_str(ERR_NOSUCHNICK), nick);
398 }
399
400 }
401 return (1);
402 }
403
404 /*
405 * duplicate_ptr
406 *
407 * inputs - pointer to check
408 * - pointer to table of entities
409 * - number of valid entities so far
410 * output - YES if duplicate pointer in table, NO if not.
411 * note, this does the canonize using pointers
412 * side effects - NONE
413 */
414 static int
415 duplicate_ptr(void *ptr)
416 {
417 int i;
418 for(i = 0; i < ntargets; i++)
419 if(targets[i].ptr == ptr)
420 return YES;
421 return NO;
422 }
423
424 /*
425 * msg_channel
426 *
427 * inputs - flag privmsg or notice
428 * - pointer to command "PRIVMSG" or "NOTICE"
429 * - pointer to client_p
430 * - pointer to source_p
431 * - pointer to channel
432 * output - NONE
433 * side effects - message given channel
434 *
435 * XXX - We need to rework this a bit, it's a tad ugly. --nenolod
436 */
437 static void
438 msg_channel(int p_or_n, const char *command,
439 struct Client *client_p, struct Client *source_p, struct Channel *chptr,
440 const char *text)
441 {
442 int result;
443 char text2[BUFSIZE];
444
445 if(MyClient(source_p))
446 {
447 /* idle time shouldnt be reset by notices --fl */
448 if(p_or_n != NOTICE)
449 source_p->localClient->last = rb_current_time();
450 }
451
452 if(chptr->mode.mode & MODE_NOCOLOR)
453 {
454 rb_strlcpy(text2, text, BUFSIZE);
455 strip_colour(text2);
456 text = text2;
457 if (EmptyString(text))
458 {
459 /* could be empty after colour stripping and
460 * that would cause problems later */
461 if(p_or_n != NOTICE)
462 sendto_one(source_p, form_str(ERR_NOTEXTTOSEND), me.name, source_p->name);
463 return;
464 }
465 }
466
467 /* chanops and voiced can flood their own channel with impunity */
468 if((result = can_send(chptr, source_p, NULL)))
469 {
470 if(result == CAN_SEND_OPV ||
471 !flood_attack_channel(p_or_n, source_p, chptr, chptr->chname))
472 {
473 sendto_channel_flags(client_p, ALL_MEMBERS, source_p, chptr,
474 "%s %s :%s", command, chptr->chname, text);
475 }
476 }
477 else if(chptr->mode.mode & MODE_OPMODERATE &&
478 chptr->mode.mode & MODE_MODERATED &&
479 IsMember(source_p, chptr))
480 {
481 /* only do +z for +m channels for now, as bans/quiets
482 * aren't tested for remote clients -- jilles */
483 if(!flood_attack_channel(p_or_n, source_p, chptr, chptr->chname))
484 {
485 sendto_channel_flags(client_p, ONLY_CHANOPS, source_p, chptr,
486 "%s %s :%s", command, chptr->chname, text);
487 }
488 }
489 else
490 {
491 if(p_or_n != NOTICE)
492 sendto_one_numeric(source_p, ERR_CANNOTSENDTOCHAN,
493 form_str(ERR_CANNOTSENDTOCHAN), chptr->chname);
494 }
495 }
496
497 /*
498 * msg_channel_flags
499 *
500 * inputs - flag 0 if PRIVMSG 1 if NOTICE. RFC
501 * say NOTICE must not auto reply
502 * - pointer to command, "PRIVMSG" or "NOTICE"
503 * - pointer to client_p
504 * - pointer to source_p
505 * - pointer to channel
506 * - flags
507 * - pointer to text to send
508 * output - NONE
509 * side effects - message given channel either chanop or voice
510 */
511 static void
512 msg_channel_flags(int p_or_n, const char *command, struct Client *client_p,
513 struct Client *source_p, struct Channel *chptr, int flags, const char *text)
514 {
515 int type;
516 char c;
517
518 if(flags & CHFL_VOICE)
519 {
520 type = ONLY_CHANOPSVOICED;
521 c = '+';
522 }
523 else
524 {
525 type = ONLY_CHANOPS;
526 c = '@';
527 }
528
529 if(MyClient(source_p))
530 {
531 /* idletime shouldnt be reset by notice --fl */
532 if(p_or_n != NOTICE)
533 source_p->localClient->last = rb_current_time();
534 }
535
536 sendto_channel_flags(client_p, type, source_p, chptr, "%s %c%s :%s",
537 command, c, chptr->chname, text);
538 }
539
540 #define PREV_FREE_TARGET(x) ((FREE_TARGET(x) == 0) ? 9 : FREE_TARGET(x) - 1)
541 #define PREV_TARGET(i) ((i == 0) ? i = 9 : --i)
542 #define NEXT_TARGET(i) ((i == 9) ? i = 0 : ++i)
543
544 static void
545 expire_tgchange(void *unused)
546 {
547 tgchange *target;
548 rb_dlink_node *ptr, *next_ptr;
549
550 RB_DLINK_FOREACH_SAFE(ptr, next_ptr, tgchange_list.head)
551 {
552 target = ptr->data;
553
554 if(target->expiry < rb_current_time())
555 {
556 rb_dlinkDelete(ptr, &tgchange_list);
557 rb_patricia_remove(tgchange_tree, target->pnode);
558 rb_free(target->ip);
559 rb_free(target);
560 }
561 }
562 }
563
564 static int
565 add_target(struct Client *source_p, struct Client *target_p)
566 {
567 int i, j;
568 uint32_t hashv;
569
570 /* can msg themselves or services without using any target slots */
571 if(source_p == target_p || IsService(target_p))
572 return 1;
573
574 /* special condition for those who have had PRIVMSG crippled to allow them
575 * to talk to IRCops still.
576 *
577 * XXX: is this controversial?
578 */
579 if(source_p->localClient->target_last > rb_current_time() && IsOper(target_p))
580 return 1;
581
582 hashv = fnv_hash_upper((const unsigned char *)use_id(target_p), 32);
583
584 if(USED_TARGETS(source_p))
585 {
586 /* hunt for an existing target */
587 for(i = PREV_FREE_TARGET(source_p), j = USED_TARGETS(source_p);
588 j; --j, PREV_TARGET(i))
589 {
590 if(source_p->localClient->targets[i] == hashv)
591 return 1;
592 }
593
594 /* first message after connect, we may only start clearing
595 * slots after this message --anfl
596 */
597 if(!IsTGChange(source_p))
598 {
599 SetTGChange(source_p);
600 source_p->localClient->target_last = rb_current_time();
601 }
602 /* clear as many targets as we can */
603 else if((i = (rb_current_time() - source_p->localClient->target_last) / 60))
604 {
605 if(i > USED_TARGETS(source_p))
606 USED_TARGETS(source_p) = 0;
607 else
608 USED_TARGETS(source_p) -= i;
609
610 source_p->localClient->target_last = rb_current_time();
611 }
612 /* cant clear any, full target list */
613 else if(USED_TARGETS(source_p) == 10)
614 {
615 ServerStats.is_tgch++;
616 add_tgchange(source_p->sockhost);
617 return 0;
618 }
619 }
620 /* no targets in use, reset their target_last so that they cant
621 * abuse a long idle to get targets back more quickly
622 */
623 else
624 {
625 source_p->localClient->target_last = rb_current_time();
626 SetTGChange(source_p);
627 }
628
629 source_p->localClient->targets[FREE_TARGET(source_p)] = hashv;
630 NEXT_TARGET(FREE_TARGET(source_p));
631 ++USED_TARGETS(source_p);
632 return 1;
633 }
634
635 /*
636 * msg_client
637 *
638 * inputs - flag 0 if PRIVMSG 1 if NOTICE. RFC
639 * say NOTICE must not auto reply
640 * - pointer to command, "PRIVMSG" or "NOTICE"
641 * - pointer to source_p source (struct Client *)
642 * - pointer to target_p target (struct Client *)
643 * - pointer to text
644 * output - NONE
645 * side effects - message given channel either chanop or voice
646 */
647 static void
648 msg_client(int p_or_n, const char *command,
649 struct Client *source_p, struct Client *target_p, const char *text)
650 {
651 if(MyClient(source_p))
652 {
653 /* reset idle time for message only if its not to self
654 * and its not a notice */
655 if(p_or_n != NOTICE)
656 source_p->localClient->last = rb_current_time();
657
658 /* target change stuff, dont limit ctcp replies as that
659 * would allow people to start filling up random users
660 * targets just by ctcping them
661 */
662 if((p_or_n != NOTICE || *text != '\001') &&
663 ConfigFileEntry.target_change && !IsOper(source_p))
664 {
665 if(!add_target(source_p, target_p))
666 {
667 sendto_one(source_p, form_str(ERR_TARGCHANGE),
668 me.name, source_p->name, target_p->name);
669 return;
670 }
671 }
672 }
673 else if(source_p->from == target_p->from)
674 {
675 sendto_realops_snomask(SNO_DEBUG, L_ALL,
676 "Send message to %s[%s] dropped from %s(Fake Dir)",
677 target_p->name, target_p->from->name, source_p->name);
678 return;
679 }
680
681 if(MyConnect(source_p) && (p_or_n != NOTICE) && target_p->user && target_p->user->away)
682 sendto_one_numeric(source_p, RPL_AWAY, form_str(RPL_AWAY),
683 target_p->name, target_p->user->away);
684
685 if(MyClient(target_p))
686 {
687 /* XXX Controversial? allow opers always to send through a +g */
688 if(!IsServer(source_p) && (IsSetCallerId(target_p) ||
689 (IsSetRegOnlyMsg(target_p) && !source_p->user->suser[0])))
690 {
691 /* Here is the anti-flood bot/spambot code -db */
692 if(accept_message(source_p, target_p) || IsOper(source_p))
693 {
694 sendto_one(target_p, ":%s!%s@%s %s %s :%s",
695 source_p->name,
696 source_p->username,
697 source_p->host, command, target_p->name, text);
698 }
699 else if (IsSetRegOnlyMsg(target_p) && !source_p->user->suser[0])
700 {
701 if (p_or_n != NOTICE)
702 sendto_one_numeric(source_p, ERR_NONONREG,
703 form_str(ERR_NONONREG),
704 target_p->name);
705 /* Only so opers can watch for floods */
706 if (MyClient(source_p))
707 (void) flood_attack_client(p_or_n, source_p, target_p);
708 }
709 else
710 {
711 /* check for accept, flag recipient incoming message */
712 if(p_or_n != NOTICE)
713 {
714 sendto_one_numeric(source_p, ERR_TARGUMODEG,
715 form_str(ERR_TARGUMODEG),
716 target_p->name);
717 }
718
719 if((target_p->localClient->last_caller_id_time +
720 ConfigFileEntry.caller_id_wait) < rb_current_time())
721 {
722 if(p_or_n != NOTICE)
723 sendto_one_numeric(source_p, RPL_TARGNOTIFY,
724 form_str(RPL_TARGNOTIFY),
725 target_p->name);
726
727 sendto_one(target_p, form_str(RPL_UMODEGMSG),
728 me.name, target_p->name, source_p->name,
729 source_p->username, source_p->host);
730
731 target_p->localClient->last_caller_id_time = rb_current_time();
732 }
733 /* Only so opers can watch for floods */
734 if (MyClient(source_p))
735 (void) flood_attack_client(p_or_n, source_p, target_p);
736 }
737 }
738 else
739 {
740 /* If the client is remote, we dont perform a special check for
741 * flooding.. as we wouldnt block their message anyway.. this means
742 * we dont give warnings.. we then check if theyre opered
743 * (to avoid flood warnings), lastly if theyre our client
744 * and flooding -- fl */
745 if(!MyClient(source_p) || IsOper(source_p) ||
746 !flood_attack_client(p_or_n, source_p, target_p))
747 sendto_anywhere(target_p, source_p, command, ":%s", text);
748 }
749 }
750 else if(!MyClient(source_p) || IsOper(source_p) ||
751 !flood_attack_client(p_or_n, source_p, target_p))
752 sendto_anywhere(target_p, source_p, command, ":%s", text);
753
754 return;
755 }
756
757 /*
758 * flood_attack_client
759 * inputs - flag 0 if PRIVMSG 1 if NOTICE. RFC
760 * say NOTICE must not auto reply
761 * - pointer to source Client
762 * - pointer to target Client
763 * output - 1 if target is under flood attack
764 * side effects - check for flood attack on target target_p
765 */
766 static int
767 flood_attack_client(int p_or_n, struct Client *source_p, struct Client *target_p)
768 {
769 int delta;
770
771 /* Services could get many messages legitimately and
772 * can be messaged without rate limiting via aliases
773 * and msg user@server.
774 * -- jilles
775 */
776 if(GlobalSetOptions.floodcount && IsClient(source_p) && source_p != target_p && !IsService(target_p))
777 {
778 if((target_p->first_received_message_time + 1) < rb_current_time())
779 {
780 delta = rb_current_time() - target_p->first_received_message_time;
781 target_p->received_number_of_privmsgs -= delta;
782 target_p->first_received_message_time = rb_current_time();
783 if(target_p->received_number_of_privmsgs <= 0)
784 {
785 target_p->received_number_of_privmsgs = 0;
786 target_p->flood_noticed = 0;
787 }
788 }
789
790 if((target_p->received_number_of_privmsgs >=
791 GlobalSetOptions.floodcount) || target_p->flood_noticed)
792 {
793 if(target_p->flood_noticed == 0)
794 {
795 sendto_realops_snomask(SNO_BOTS, L_NETWIDE,
796 "Possible Flooder %s[%s@%s] on %s target: %s",
797 source_p->name, source_p->username,
798 source_p->orighost,
799 source_p->servptr->name, target_p->name);
800 target_p->flood_noticed = 1;
801 /* add a bit of penalty */
802 target_p->received_number_of_privmsgs += 2;
803 }
804 if(MyClient(source_p) && (p_or_n != NOTICE))
805 sendto_one(source_p,
806 ":%s NOTICE %s :*** Message to %s throttled due to flooding",
807 me.name, source_p->name, target_p->name);
808 return 1;
809 }
810 else
811 target_p->received_number_of_privmsgs++;
812 }
813
814 return 0;
815 }
816
817 /*
818 * flood_attack_channel
819 * inputs - flag 0 if PRIVMSG 1 if NOTICE. RFC
820 * says NOTICE must not auto reply
821 * - pointer to source Client
822 * - pointer to target channel
823 * output - 1 if target is under flood attack
824 * side effects - check for flood attack on target chptr
825 */
826 static int
827 flood_attack_channel(int p_or_n, struct Client *source_p, struct Channel *chptr, char *chname)
828 {
829 int delta;
830
831 if(GlobalSetOptions.floodcount && MyClient(source_p))
832 {
833 if((chptr->first_received_message_time + 1) < rb_current_time())
834 {
835 delta = rb_current_time() - chptr->first_received_message_time;
836 chptr->received_number_of_privmsgs -= delta;
837 chptr->first_received_message_time = rb_current_time();
838 if(chptr->received_number_of_privmsgs <= 0)
839 {
840 chptr->received_number_of_privmsgs = 0;
841 chptr->flood_noticed = 0;
842 }
843 }
844
845 if((chptr->received_number_of_privmsgs >= GlobalSetOptions.floodcount)
846 || chptr->flood_noticed)
847 {
848 if(chptr->flood_noticed == 0)
849 {
850 sendto_realops_snomask(SNO_BOTS, *chptr->chname == '&' ? L_ALL : L_NETWIDE,
851 "Possible Flooder %s[%s@%s] on %s target: %s",
852 source_p->name, source_p->username,
853 source_p->orighost,
854 source_p->servptr->name, chptr->chname);
855 chptr->flood_noticed = 1;
856
857 /* Add a bit of penalty */
858 chptr->received_number_of_privmsgs += 2;
859 }
860 if(MyClient(source_p) && (p_or_n != NOTICE))
861 sendto_one(source_p,
862 ":%s NOTICE %s :*** Message to %s throttled due to flooding",
863 me.name, source_p->name, chptr->chname);
864 return 1;
865 }
866 else
867 chptr->received_number_of_privmsgs++;
868 }
869
870 return 0;
871 }
872
873
874 /*
875 * handle_special
876 *
877 * inputs - server pointer
878 * - client pointer
879 * - nick stuff to grok for opers
880 * - text to send if grok
881 * output - none
882 * side effects - all the traditional oper type messages are parsed here.
883 * i.e. "/msg #some.host."
884 * However, syntax has been changed.
885 * previous syntax "/msg #some.host.mask"
886 * now becomes "/msg $#some.host.mask"
887 * previous syntax of: "/msg $some.server.mask" remains
888 * This disambiguates the syntax.
889 */
890 static void
891 handle_special(int p_or_n, const char *command, struct Client *client_p,
892 struct Client *source_p, const char *nick, const char *text)
893 {
894 struct Client *target_p;
895 char *server;
896 char *s;
897 int count;
898
899 /* user[%host]@server addressed?
900 * NOTE: users can send to user@server, but not user%host@server
901 * or opers@server
902 */
903 if((server = strchr(nick, '@')) != NULL)
904 {
905 if((target_p = find_server(source_p, server + 1)) == NULL)
906 {
907 sendto_one_numeric(source_p, ERR_NOSUCHSERVER,
908 form_str(ERR_NOSUCHSERVER), server + 1);
909 return;
910 }
911
912 count = 0;
913
914 if(!IsOper(source_p))
915 {
916 if(strchr(nick, '%') || (strncmp(nick, "opers", 5) == 0))
917 {
918 sendto_one_numeric(source_p, ERR_NOSUCHNICK,
919 form_str(ERR_NOSUCHNICK), nick);
920 return;
921 }
922 }
923
924 /* somewhere else.. */
925 if(!IsMe(target_p))
926 {
927 sendto_one(target_p, ":%s %s %s :%s",
928 get_id(source_p, target_p), command, nick, text);
929 return;
930 }
931
932 /* Check if someones msg'ing opers@our.server */
933 if(strncmp(nick, "opers@", 6) == 0)
934 {
935 sendto_realops_snomask(SNO_GENERAL, L_ALL, "To opers: From: %s: %s",
936 source_p->name, text);
937 return;
938 }
939
940 /* This was not very useful except for bypassing certain
941 * restrictions. Note that we still allow sending to
942 * remote servers this way, for messaging pseudoservers
943 * securely whether they have a service{} block or not.
944 * -- jilles
945 */
946 sendto_one_numeric(source_p, ERR_NOSUCHNICK,
947 form_str(ERR_NOSUCHNICK), nick);
948 return;
949 }
950
951 /*
952 * the following two cases allow masks in NOTICEs
953 * (for OPERs only)
954 *
955 * Armin, 8Jun90 (gruner@informatik.tu-muenchen.de)
956 */
957 if(IsOper(source_p) && *nick == '$')
958 {
959 if((*(nick + 1) == '$' || *(nick + 1) == '#'))
960 nick++;
961 else if(MyOper(source_p))
962 {
963 sendto_one(source_p,
964 ":%s NOTICE %s :The command %s %s is no longer supported, please use $%s",
965 me.name, source_p->name, command, nick, nick);
966 return;
967 }
968
969 if(MyClient(source_p) && !IsOperMassNotice(source_p))
970 {
971 sendto_one(source_p, form_str(ERR_NOPRIVS),
972 me.name, source_p->name, "mass_notice");
973 return;
974 }
975
976 if((s = strrchr(nick, '.')) == NULL)
977 {
978 sendto_one_numeric(source_p, ERR_NOTOPLEVEL,
979 form_str(ERR_NOTOPLEVEL), nick);
980 return;
981 }
982 while(*++s)
983 if(*s == '.' || *s == '*' || *s == '?')
984 break;
985 if(*s == '*' || *s == '?')
986 {
987 sendto_one_numeric(source_p, ERR_WILDTOPLEVEL,
988 form_str(ERR_WILDTOPLEVEL), nick);
989 return;
990 }
991
992 sendto_match_butone(IsServer(client_p) ? client_p : NULL, source_p,
993 nick + 1,
994 (*nick == '#') ? MATCH_HOST : MATCH_SERVER,
995 "%s $%s :%s", command, nick, text);
996 return;
997 }
998 }