]> jfr.im git - irc/rqf/shadowircd.git/blob - modules/core/m_message.c
'ServerStats->' -> 'ServerStats.'
[irc/rqf/shadowircd.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 "irc_string.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
47 static int m_message(int, const char *, struct Client *, struct Client *, int, const char **);
48 static int m_privmsg(struct Client *, struct Client *, int, const char **);
49 static int m_notice(struct Client *, struct Client *, int, const char **);
50
51 static void expire_tgchange(void *unused);
52 static struct ev_entry *expire_tgchange_event;
53
54 static int
55 modinit(void)
56 {
57 expire_tgchange_event = rb_event_addish("expire_tgchange", expire_tgchange, NULL, 300);
58 expire_tgchange(NULL);
59 return 0;
60 }
61
62 static void
63 moddeinit(void)
64 {
65 rb_event_delete(expire_tgchange_event);
66 }
67
68 struct Message privmsg_msgtab = {
69 "PRIVMSG", 0, 0, 0, MFLG_SLOW | MFLG_UNREG,
70 {mg_unreg, {m_privmsg, 0}, {m_privmsg, 0}, mg_ignore, mg_ignore, {m_privmsg, 0}}
71 };
72 struct Message notice_msgtab = {
73 "NOTICE", 0, 0, 0, MFLG_SLOW,
74 {mg_unreg, {m_notice, 0}, {m_notice, 0}, {m_notice, 0}, mg_ignore, {m_notice, 0}}
75 };
76
77 mapi_clist_av1 message_clist[] = { &privmsg_msgtab, &notice_msgtab, NULL };
78
79 DECLARE_MODULE_AV1(message, modinit, moddeinit, message_clist, NULL, NULL, "$Revision: 3173 $");
80
81 struct entity
82 {
83 void *ptr;
84 int type;
85 int flags;
86 };
87
88 static int build_target_list(int p_or_n, const char *command,
89 struct Client *client_p,
90 struct Client *source_p, const char *nicks_channels, const char *text);
91
92 static int flood_attack_client(int p_or_n, struct Client *source_p, struct Client *target_p);
93 static int flood_attack_channel(int p_or_n, struct Client *source_p,
94 struct Channel *chptr, char *chname);
95 static struct Client *find_userhost(const char *, const char *, int *);
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 = strtoken(&p, target_list, ","); nick; nick = strtoken(&p, NULL, ","))
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 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 add_tgchange(source_p->sockhost);
616 return 0;
617 }
618 }
619 /* no targets in use, reset their target_last so that they cant
620 * abuse a long idle to get targets back more quickly
621 */
622 else
623 {
624 source_p->localClient->target_last = rb_current_time();
625 SetTGChange(source_p);
626 }
627
628 source_p->localClient->targets[FREE_TARGET(source_p)] = hashv;
629 NEXT_TARGET(FREE_TARGET(source_p));
630 ++USED_TARGETS(source_p);
631 return 1;
632 }
633
634 /*
635 * msg_client
636 *
637 * inputs - flag 0 if PRIVMSG 1 if NOTICE. RFC
638 * say NOTICE must not auto reply
639 * - pointer to command, "PRIVMSG" or "NOTICE"
640 * - pointer to source_p source (struct Client *)
641 * - pointer to target_p target (struct Client *)
642 * - pointer to text
643 * output - NONE
644 * side effects - message given channel either chanop or voice
645 */
646 static void
647 msg_client(int p_or_n, const char *command,
648 struct Client *source_p, struct Client *target_p, const char *text)
649 {
650 if(MyClient(source_p))
651 {
652 /* reset idle time for message only if its not to self
653 * and its not a notice */
654 if(p_or_n != NOTICE)
655 source_p->localClient->last = rb_current_time();
656
657 /* target change stuff, dont limit ctcp replies as that
658 * would allow people to start filling up random users
659 * targets just by ctcping them
660 */
661 if((p_or_n != NOTICE || *text != '\001') &&
662 ConfigFileEntry.target_change && !IsOper(source_p))
663 {
664 if(!add_target(source_p, target_p))
665 {
666 sendto_one(source_p, form_str(ERR_TARGCHANGE),
667 me.name, source_p->name, target_p->name);
668 return;
669 }
670 }
671 }
672 else if(source_p->from == target_p->from)
673 {
674 sendto_realops_snomask(SNO_DEBUG, L_ALL,
675 "Send message to %s[%s] dropped from %s(Fake Dir)",
676 target_p->name, target_p->from->name, source_p->name);
677 return;
678 }
679
680 if(MyConnect(source_p) && (p_or_n != NOTICE) && target_p->user && target_p->user->away)
681 sendto_one_numeric(source_p, RPL_AWAY, form_str(RPL_AWAY),
682 target_p->name, target_p->user->away);
683
684 if(MyClient(target_p))
685 {
686 /* XXX Controversial? allow opers always to send through a +g */
687 if(!IsServer(source_p) && (IsSetCallerId(target_p) ||
688 (IsSetRegOnlyMsg(target_p) && !source_p->user->suser[0])))
689 {
690 /* Here is the anti-flood bot/spambot code -db */
691 if(accept_message(source_p, target_p) || IsOper(source_p))
692 {
693 sendto_one(target_p, ":%s!%s@%s %s %s :%s",
694 source_p->name,
695 source_p->username,
696 source_p->host, command, target_p->name, text);
697 }
698 else if (IsSetRegOnlyMsg(target_p) && !source_p->user->suser[0])
699 {
700 if (p_or_n != NOTICE)
701 sendto_one_numeric(source_p, ERR_NONONREG,
702 form_str(ERR_NONONREG),
703 target_p->name);
704 /* Only so opers can watch for floods */
705 (void) flood_attack_client(p_or_n, source_p, target_p);
706 }
707 else
708 {
709 /* check for accept, flag recipient incoming message */
710 if(p_or_n != NOTICE)
711 {
712 sendto_one_numeric(source_p, ERR_TARGUMODEG,
713 form_str(ERR_TARGUMODEG),
714 target_p->name);
715 }
716
717 if((target_p->localClient->last_caller_id_time +
718 ConfigFileEntry.caller_id_wait) < rb_current_time())
719 {
720 if(p_or_n != NOTICE)
721 sendto_one_numeric(source_p, RPL_TARGNOTIFY,
722 form_str(RPL_TARGNOTIFY),
723 target_p->name);
724
725 sendto_one(target_p, form_str(RPL_UMODEGMSG),
726 me.name, target_p->name, source_p->name,
727 source_p->username, source_p->host);
728
729 target_p->localClient->last_caller_id_time = rb_current_time();
730 }
731 /* Only so opers can watch for floods */
732 (void) flood_attack_client(p_or_n, source_p, target_p);
733 }
734 }
735 else
736 {
737 /* If the client is remote, we dont perform a special check for
738 * flooding.. as we wouldnt block their message anyway.. this means
739 * we dont give warnings.. we then check if theyre opered
740 * (to avoid flood warnings), lastly if theyre our client
741 * and flooding -- fl */
742 if(!MyClient(source_p) || IsOper(source_p) ||
743 !flood_attack_client(p_or_n, source_p, target_p))
744 sendto_anywhere(target_p, source_p, command, ":%s", text);
745 }
746 }
747 else if(!MyClient(source_p) || IsOper(source_p) ||
748 !flood_attack_client(p_or_n, source_p, target_p))
749 sendto_anywhere(target_p, source_p, command, ":%s", text);
750
751 return;
752 }
753
754 /*
755 * flood_attack_client
756 * inputs - flag 0 if PRIVMSG 1 if NOTICE. RFC
757 * say NOTICE must not auto reply
758 * - pointer to source Client
759 * - pointer to target Client
760 * output - 1 if target is under flood attack
761 * side effects - check for flood attack on target target_p
762 */
763 static int
764 flood_attack_client(int p_or_n, struct Client *source_p, struct Client *target_p)
765 {
766 int delta;
767
768 if(GlobalSetOptions.floodcount && MyConnect(target_p) && IsClient(source_p))
769 {
770 if((target_p->localClient->first_received_message_time + 1) < rb_current_time())
771 {
772 delta = rb_current_time() - target_p->localClient->first_received_message_time;
773 target_p->localClient->received_number_of_privmsgs -= delta;
774 target_p->localClient->first_received_message_time = rb_current_time();
775 if(target_p->localClient->received_number_of_privmsgs <= 0)
776 {
777 target_p->localClient->received_number_of_privmsgs = 0;
778 target_p->localClient->flood_noticed = 0;
779 }
780 }
781
782 if((target_p->localClient->received_number_of_privmsgs >=
783 GlobalSetOptions.floodcount) || target_p->localClient->flood_noticed)
784 {
785 if(target_p->localClient->flood_noticed == 0)
786 {
787 sendto_realops_snomask(SNO_BOTS, L_NETWIDE,
788 "Possible Flooder %s[%s@%s] on %s target: %s",
789 source_p->name, source_p->username,
790 source_p->orighost,
791 source_p->servptr->name, target_p->name);
792 target_p->localClient->flood_noticed = 1;
793 /* add a bit of penalty */
794 target_p->localClient->received_number_of_privmsgs += 2;
795 }
796 if(MyClient(source_p) && (p_or_n != NOTICE))
797 sendto_one(source_p,
798 ":%s NOTICE %s :*** Message to %s throttled due to flooding",
799 me.name, source_p->name, target_p->name);
800 return 1;
801 }
802 else
803 target_p->localClient->received_number_of_privmsgs++;
804 }
805
806 return 0;
807 }
808
809 /*
810 * flood_attack_channel
811 * inputs - flag 0 if PRIVMSG 1 if NOTICE. RFC
812 * says NOTICE must not auto reply
813 * - pointer to source Client
814 * - pointer to target channel
815 * output - 1 if target is under flood attack
816 * side effects - check for flood attack on target chptr
817 */
818 static int
819 flood_attack_channel(int p_or_n, struct Client *source_p, struct Channel *chptr, char *chname)
820 {
821 int delta;
822
823 if(GlobalSetOptions.floodcount && MyClient(source_p))
824 {
825 if((chptr->first_received_message_time + 1) < rb_current_time())
826 {
827 delta = rb_current_time() - chptr->first_received_message_time;
828 chptr->received_number_of_privmsgs -= delta;
829 chptr->first_received_message_time = rb_current_time();
830 if(chptr->received_number_of_privmsgs <= 0)
831 {
832 chptr->received_number_of_privmsgs = 0;
833 chptr->flood_noticed = 0;
834 }
835 }
836
837 if((chptr->received_number_of_privmsgs >= GlobalSetOptions.floodcount)
838 || chptr->flood_noticed)
839 {
840 if(chptr->flood_noticed == 0)
841 {
842 sendto_realops_snomask(SNO_BOTS, *chptr->chname == '&' ? L_ALL : L_NETWIDE,
843 "Possible Flooder %s[%s@%s] on %s target: %s",
844 source_p->name, source_p->username,
845 source_p->orighost,
846 source_p->servptr->name, chptr->chname);
847 chptr->flood_noticed = 1;
848
849 /* Add a bit of penalty */
850 chptr->received_number_of_privmsgs += 2;
851 }
852 if(MyClient(source_p) && (p_or_n != NOTICE))
853 sendto_one(source_p,
854 ":%s NOTICE %s :*** Message to %s throttled due to flooding",
855 me.name, source_p->name, chptr->chname);
856 return 1;
857 }
858 else
859 chptr->received_number_of_privmsgs++;
860 }
861
862 return 0;
863 }
864
865
866 /*
867 * handle_special
868 *
869 * inputs - server pointer
870 * - client pointer
871 * - nick stuff to grok for opers
872 * - text to send if grok
873 * output - none
874 * side effects - all the traditional oper type messages are parsed here.
875 * i.e. "/msg #some.host."
876 * However, syntax has been changed.
877 * previous syntax "/msg #some.host.mask"
878 * now becomes "/msg $#some.host.mask"
879 * previous syntax of: "/msg $some.server.mask" remains
880 * This disambiguates the syntax.
881 */
882 static void
883 handle_special(int p_or_n, const char *command, struct Client *client_p,
884 struct Client *source_p, const char *nick, const char *text)
885 {
886 struct Client *target_p;
887 char *host;
888 char *server;
889 char *s;
890 int count;
891
892 /* user[%host]@server addressed?
893 * NOTE: users can send to user@server, but not user%host@server
894 * or opers@server
895 */
896 if((server = strchr(nick, '@')) != NULL)
897 {
898 if((target_p = find_server(source_p, server + 1)) == NULL)
899 {
900 sendto_one_numeric(source_p, ERR_NOSUCHSERVER,
901 form_str(ERR_NOSUCHSERVER), server + 1);
902 return;
903 }
904
905 count = 0;
906
907 if(!IsOper(source_p))
908 {
909 if(strchr(nick, '%') || (strncmp(nick, "opers", 5) == 0))
910 {
911 sendto_one_numeric(source_p, ERR_NOSUCHNICK,
912 form_str(ERR_NOSUCHNICK), nick);
913 return;
914 }
915 }
916
917 /* somewhere else.. */
918 if(!IsMe(target_p))
919 {
920 sendto_one(target_p, ":%s %s %s :%s",
921 get_id(source_p, target_p), command, nick, text);
922 return;
923 }
924
925 *server = '\0';
926
927 if((host = strchr(nick, '%')) != NULL)
928 *host++ = '\0';
929
930 /* Check if someones msg'ing opers@our.server */
931 if(strcmp(nick, "opers") == 0)
932 {
933 sendto_realops_snomask(SNO_GENERAL, L_ALL, "To opers: From: %s: %s",
934 source_p->name, text);
935 return;
936 }
937
938 /*
939 * Look for users which match the destination host
940 * (no host == wildcard) and if one and one only is
941 * found connected to me, deliver message!
942 */
943 target_p = find_userhost(nick, host, &count);
944
945 if(target_p != NULL)
946 {
947 if(server != NULL)
948 *server = '@';
949 if(host != NULL)
950 *--host = '%';
951
952 if(count == 1)
953 sendto_anywhere(target_p, source_p, command, ":%s", text);
954 else
955 sendto_one(source_p, form_str(ERR_TOOMANYTARGETS),
956 get_id(&me, source_p), get_id(source_p, source_p), nick);
957 }
958 }
959
960 /*
961 * the following two cases allow masks in NOTICEs
962 * (for OPERs only)
963 *
964 * Armin, 8Jun90 (gruner@informatik.tu-muenchen.de)
965 */
966 if(IsOper(source_p) && *nick == '$')
967 {
968 if((*(nick + 1) == '$' || *(nick + 1) == '#'))
969 nick++;
970 else if(MyOper(source_p))
971 {
972 sendto_one(source_p,
973 ":%s NOTICE %s :The command %s %s is no longer supported, please use $%s",
974 me.name, source_p->name, command, nick, nick);
975 return;
976 }
977
978 if(MyClient(source_p) && !IsOperMassNotice(source_p))
979 {
980 sendto_one(source_p, form_str(ERR_NOPRIVS),
981 me.name, source_p->name, "mass_notice");
982 return;
983 }
984
985 if((s = strrchr(nick, '.')) == NULL)
986 {
987 sendto_one_numeric(source_p, ERR_NOTOPLEVEL,
988 form_str(ERR_NOTOPLEVEL), nick);
989 return;
990 }
991 while(*++s)
992 if(*s == '.' || *s == '*' || *s == '?')
993 break;
994 if(*s == '*' || *s == '?')
995 {
996 sendto_one_numeric(source_p, ERR_WILDTOPLEVEL,
997 form_str(ERR_WILDTOPLEVEL), nick);
998 return;
999 }
1000
1001 sendto_match_butone(IsServer(client_p) ? client_p : NULL, source_p,
1002 nick + 1,
1003 (*nick == '#') ? MATCH_HOST : MATCH_SERVER,
1004 "%s $%s :%s", command, nick, text);
1005 return;
1006 }
1007 }
1008
1009 /*
1010 * find_userhost - find a user@host (server or user).
1011 * inputs - user name to look for
1012 * - host name to look for
1013 * - pointer to count of number of matches found
1014 * outputs - pointer to client if found
1015 * - count is updated
1016 * side effects - none
1017 *
1018 */
1019 static struct Client *
1020 find_userhost(const char *user, const char *host, int *count)
1021 {
1022 struct Client *c2ptr;
1023 struct Client *res = NULL;
1024 char *u = LOCAL_COPY(user);
1025 rb_dlink_node *ptr;
1026 *count = 0;
1027 if(collapse(u) != NULL)
1028 {
1029 RB_DLINK_FOREACH(ptr, global_client_list.head)
1030 {
1031 c2ptr = ptr->data;
1032 if(!MyClient(c2ptr)) /* implies mine and an user */
1033 continue;
1034 if((!host || match(host, c2ptr->host)) && irccmp(u, c2ptr->username) == 0)
1035 {
1036 (*count)++;
1037 res = c2ptr;
1038 }
1039 }
1040 }
1041 return (res);
1042 }