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