]> jfr.im git - irc/rqf/shadowircd.git/blame - modules/core/m_message.c
Reverting to 398.. trying again with native charybdis hash
[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"
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"
212380e3 44#include "s_newconf.h"
26f754d9 45#include "s_stats.h"
212380e3 46
47static int m_message(int, const char *, struct Client *, struct Client *, int, const char **);
48static int m_privmsg(struct Client *, struct Client *, int, const char **);
49static int m_notice(struct Client *, struct Client *, int, const char **);
50
51static void expire_tgchange(void *unused);
bfccb2c0 52static struct ev_entry *expire_tgchange_event;
212380e3 53
54static int
55modinit(void)
56{
bfccb2c0 57 expire_tgchange_event = rb_event_addish("expire_tgchange", expire_tgchange, NULL, 300);
212380e3 58 expire_tgchange(NULL);
59 return 0;
60}
61
62static void
63moddeinit(void)
64{
bfccb2c0 65 rb_event_delete(expire_tgchange_event);
212380e3 66}
67
68struct 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};
72struct 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
77mapi_clist_av1 message_clist[] = { &privmsg_msgtab, &notice_msgtab, NULL };
78
63aecfb9 79DECLARE_MODULE_AV1(message, modinit, moddeinit, message_clist, NULL, NULL, "$Revision: 3173 $");
212380e3 80
81struct entity
82{
83 void *ptr;
84 int type;
85 int flags;
86};
87
88static 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
92static int flood_attack_client(int p_or_n, struct Client *source_p, struct Client *target_p);
93static int flood_attack_channel(int p_or_n, struct Client *source_p,
94 struct Channel *chptr, char *chname);
95static 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
102static struct entity targets[512];
103static int ntargets = 0;
104
105static int duplicate_ptr(void *);
106
107static 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
111static 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
116static void msg_client(int p_or_n, const char *command,
117 struct Client *source_p, struct Client *target_p, const char *text);
118
119static 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
145static int
146m_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
151static int
152m_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 */
164static int
165m_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
240static int
241build_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 */
414static int
415duplicate_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 */
437static void
438msg_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)
9f6bbe3c 449 source_p->localClient->last = rb_current_time();
212380e3 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 */
511static void
512msg_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)
9f6bbe3c 533 source_p->localClient->last = rb_current_time();
212380e3 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
544static void
545expire_tgchange(void *unused)
546{
547 tgchange *target;
90a3c35b 548 rb_dlink_node *ptr, *next_ptr;
212380e3 549
90a3c35b 550 RB_DLINK_FOREACH_SAFE(ptr, next_ptr, tgchange_list.head)
212380e3 551 {
552 target = ptr->data;
553
9f6bbe3c 554 if(target->expiry < rb_current_time())
212380e3 555 {
31c047d7
WP
556 rb_dlinkDelete(ptr, &tgchange_list);
557 rb_patricia_remove(tgchange_tree, target->pnode);
a55e5724
VY
558 rb_free(target->ip);
559 rb_free(target);
212380e3 560 }
561 }
562}
563
58e8319c
VY
564static int
565add_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;
212380e3 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 */
647static void
648msg_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)
9f6bbe3c 656 source_p->localClient->last = rb_current_time();
212380e3 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 (void) flood_attack_client(p_or_n, source_p, target_p);
707 }
708 else
709 {
710 /* check for accept, flag recipient incoming message */
711 if(p_or_n != NOTICE)
712 {
713 sendto_one_numeric(source_p, ERR_TARGUMODEG,
714 form_str(ERR_TARGUMODEG),
715 target_p->name);
716 }
717
718 if((target_p->localClient->last_caller_id_time +
9f6bbe3c 719 ConfigFileEntry.caller_id_wait) < rb_current_time())
212380e3 720 {
721 if(p_or_n != NOTICE)
722 sendto_one_numeric(source_p, RPL_TARGNOTIFY,
723 form_str(RPL_TARGNOTIFY),
724 target_p->name);
725
726 sendto_one(target_p, form_str(RPL_UMODEGMSG),
727 me.name, target_p->name, source_p->name,
728 source_p->username, source_p->host);
729
9f6bbe3c 730 target_p->localClient->last_caller_id_time = rb_current_time();
212380e3 731 }
732 /* Only so opers can watch for floods */
733 (void) flood_attack_client(p_or_n, source_p, target_p);
734 }
735 }
736 else
737 {
738 /* If the client is remote, we dont perform a special check for
739 * flooding.. as we wouldnt block their message anyway.. this means
740 * we dont give warnings.. we then check if theyre opered
741 * (to avoid flood warnings), lastly if theyre our client
742 * and flooding -- fl */
743 if(!MyClient(source_p) || IsOper(source_p) ||
744 !flood_attack_client(p_or_n, source_p, target_p))
745 sendto_anywhere(target_p, source_p, command, ":%s", text);
746 }
747 }
748 else if(!MyClient(source_p) || IsOper(source_p) ||
749 !flood_attack_client(p_or_n, source_p, target_p))
750 sendto_anywhere(target_p, source_p, command, ":%s", text);
751
752 return;
753}
754
755/*
756 * flood_attack_client
757 * inputs - flag 0 if PRIVMSG 1 if NOTICE. RFC
758 * say NOTICE must not auto reply
759 * - pointer to source Client
760 * - pointer to target Client
761 * output - 1 if target is under flood attack
762 * side effects - check for flood attack on target target_p
763 */
764static int
765flood_attack_client(int p_or_n, struct Client *source_p, struct Client *target_p)
766{
767 int delta;
768
769 if(GlobalSetOptions.floodcount && MyConnect(target_p) && IsClient(source_p))
770 {
9f6bbe3c 771 if((target_p->localClient->first_received_message_time + 1) < rb_current_time())
212380e3 772 {
9f6bbe3c 773 delta = rb_current_time() - target_p->localClient->first_received_message_time;
212380e3 774 target_p->localClient->received_number_of_privmsgs -= delta;
9f6bbe3c 775 target_p->localClient->first_received_message_time = rb_current_time();
212380e3 776 if(target_p->localClient->received_number_of_privmsgs <= 0)
777 {
778 target_p->localClient->received_number_of_privmsgs = 0;
779 target_p->localClient->flood_noticed = 0;
780 }
781 }
782
783 if((target_p->localClient->received_number_of_privmsgs >=
784 GlobalSetOptions.floodcount) || target_p->localClient->flood_noticed)
785 {
786 if(target_p->localClient->flood_noticed == 0)
787 {
9f8d60cc 788 sendto_realops_snomask(SNO_BOTS, L_NETWIDE,
212380e3 789 "Possible Flooder %s[%s@%s] on %s target: %s",
790 source_p->name, source_p->username,
63aecfb9 791 source_p->orighost,
c88cdb00 792 source_p->servptr->name, target_p->name);
212380e3 793 target_p->localClient->flood_noticed = 1;
794 /* add a bit of penalty */
795 target_p->localClient->received_number_of_privmsgs += 2;
796 }
797 if(MyClient(source_p) && (p_or_n != NOTICE))
798 sendto_one(source_p,
799 ":%s NOTICE %s :*** Message to %s throttled due to flooding",
800 me.name, source_p->name, target_p->name);
801 return 1;
802 }
803 else
804 target_p->localClient->received_number_of_privmsgs++;
805 }
806
807 return 0;
808}
809
810/*
811 * flood_attack_channel
812 * inputs - flag 0 if PRIVMSG 1 if NOTICE. RFC
813 * says NOTICE must not auto reply
814 * - pointer to source Client
815 * - pointer to target channel
816 * output - 1 if target is under flood attack
817 * side effects - check for flood attack on target chptr
818 */
819static int
820flood_attack_channel(int p_or_n, struct Client *source_p, struct Channel *chptr, char *chname)
821{
822 int delta;
823
824 if(GlobalSetOptions.floodcount && MyClient(source_p))
825 {
9f6bbe3c 826 if((chptr->first_received_message_time + 1) < rb_current_time())
212380e3 827 {
9f6bbe3c 828 delta = rb_current_time() - chptr->first_received_message_time;
212380e3 829 chptr->received_number_of_privmsgs -= delta;
9f6bbe3c 830 chptr->first_received_message_time = rb_current_time();
212380e3 831 if(chptr->received_number_of_privmsgs <= 0)
832 {
833 chptr->received_number_of_privmsgs = 0;
834 chptr->flood_noticed = 0;
835 }
836 }
837
838 if((chptr->received_number_of_privmsgs >= GlobalSetOptions.floodcount)
839 || chptr->flood_noticed)
840 {
841 if(chptr->flood_noticed == 0)
842 {
9f8d60cc 843 sendto_realops_snomask(SNO_BOTS, *chptr->chname == '&' ? L_ALL : L_NETWIDE,
212380e3 844 "Possible Flooder %s[%s@%s] on %s target: %s",
845 source_p->name, source_p->username,
63aecfb9 846 source_p->orighost,
c88cdb00 847 source_p->servptr->name, chptr->chname);
212380e3 848 chptr->flood_noticed = 1;
849
850 /* Add a bit of penalty */
851 chptr->received_number_of_privmsgs += 2;
852 }
853 if(MyClient(source_p) && (p_or_n != NOTICE))
854 sendto_one(source_p,
855 ":%s NOTICE %s :*** Message to %s throttled due to flooding",
856 me.name, source_p->name, chptr->chname);
857 return 1;
858 }
859 else
860 chptr->received_number_of_privmsgs++;
861 }
862
863 return 0;
864}
865
866
867/*
868 * handle_special
869 *
870 * inputs - server pointer
871 * - client pointer
872 * - nick stuff to grok for opers
873 * - text to send if grok
874 * output - none
875 * side effects - all the traditional oper type messages are parsed here.
876 * i.e. "/msg #some.host."
877 * However, syntax has been changed.
878 * previous syntax "/msg #some.host.mask"
879 * now becomes "/msg $#some.host.mask"
880 * previous syntax of: "/msg $some.server.mask" remains
881 * This disambiguates the syntax.
882 */
883static void
884handle_special(int p_or_n, const char *command, struct Client *client_p,
885 struct Client *source_p, const char *nick, const char *text)
886{
887 struct Client *target_p;
888 char *host;
889 char *server;
890 char *s;
891 int count;
892
893 /* user[%host]@server addressed?
894 * NOTE: users can send to user@server, but not user%host@server
895 * or opers@server
896 */
897 if((server = strchr(nick, '@')) != NULL)
898 {
899 if((target_p = find_server(source_p, server + 1)) == NULL)
900 {
901 sendto_one_numeric(source_p, ERR_NOSUCHSERVER,
902 form_str(ERR_NOSUCHSERVER), server + 1);
903 return;
904 }
905
906 count = 0;
907
908 if(!IsOper(source_p))
909 {
910 if(strchr(nick, '%') || (strncmp(nick, "opers", 5) == 0))
911 {
912 sendto_one_numeric(source_p, ERR_NOSUCHNICK,
913 form_str(ERR_NOSUCHNICK), nick);
914 return;
915 }
916 }
917
918 /* somewhere else.. */
919 if(!IsMe(target_p))
920 {
921 sendto_one(target_p, ":%s %s %s :%s",
922 get_id(source_p, target_p), command, nick, text);
923 return;
924 }
925
926 *server = '\0';
927
928 if((host = strchr(nick, '%')) != NULL)
929 *host++ = '\0';
930
931 /* Check if someones msg'ing opers@our.server */
932 if(strcmp(nick, "opers") == 0)
933 {
934 sendto_realops_snomask(SNO_GENERAL, L_ALL, "To opers: From: %s: %s",
935 source_p->name, text);
936 return;
937 }
938
939 /*
940 * Look for users which match the destination host
941 * (no host == wildcard) and if one and one only is
942 * found connected to me, deliver message!
943 */
944 target_p = find_userhost(nick, host, &count);
945
946 if(target_p != NULL)
947 {
948 if(server != NULL)
949 *server = '@';
950 if(host != NULL)
951 *--host = '%';
952
953 if(count == 1)
954 sendto_anywhere(target_p, source_p, command, ":%s", text);
955 else
956 sendto_one(source_p, form_str(ERR_TOOMANYTARGETS),
957 get_id(&me, source_p), get_id(source_p, source_p), nick);
958 }
959 }
960
961 /*
962 * the following two cases allow masks in NOTICEs
963 * (for OPERs only)
964 *
965 * Armin, 8Jun90 (gruner@informatik.tu-muenchen.de)
966 */
967 if(IsOper(source_p) && *nick == '$')
968 {
969 if((*(nick + 1) == '$' || *(nick + 1) == '#'))
970 nick++;
971 else if(MyOper(source_p))
972 {
973 sendto_one(source_p,
974 ":%s NOTICE %s :The command %s %s is no longer supported, please use $%s",
975 me.name, source_p->name, command, nick, nick);
976 return;
977 }
978
c13a2d9a
JT
979 if(MyClient(source_p) && !IsOperMassNotice(source_p))
980 {
981 sendto_one(source_p, form_str(ERR_NOPRIVS),
982 me.name, source_p->name, "mass_notice");
983 return;
984 }
985
212380e3 986 if((s = strrchr(nick, '.')) == NULL)
987 {
988 sendto_one_numeric(source_p, ERR_NOTOPLEVEL,
989 form_str(ERR_NOTOPLEVEL), nick);
990 return;
991 }
992 while(*++s)
993 if(*s == '.' || *s == '*' || *s == '?')
994 break;
995 if(*s == '*' || *s == '?')
996 {
997 sendto_one_numeric(source_p, ERR_WILDTOPLEVEL,
998 form_str(ERR_WILDTOPLEVEL), nick);
999 return;
1000 }
1001
1002 sendto_match_butone(IsServer(client_p) ? client_p : NULL, source_p,
1003 nick + 1,
1004 (*nick == '#') ? MATCH_HOST : MATCH_SERVER,
1005 "%s $%s :%s", command, nick, text);
1006 return;
1007 }
1008}
1009
1010/*
1011 * find_userhost - find a user@host (server or user).
1012 * inputs - user name to look for
1013 * - host name to look for
1014 * - pointer to count of number of matches found
1015 * outputs - pointer to client if found
1016 * - count is updated
1017 * side effects - none
1018 *
1019 */
1020static struct Client *
1021find_userhost(const char *user, const char *host, int *count)
1022{
1023 struct Client *c2ptr;
1024 struct Client *res = NULL;
1025 char *u = LOCAL_COPY(user);
08d11e34 1026 rb_dlink_node *ptr;
212380e3 1027 *count = 0;
1028 if(collapse(u) != NULL)
1029 {
08d11e34 1030 RB_DLINK_FOREACH(ptr, global_client_list.head)
212380e3 1031 {
1032 c2ptr = ptr->data;
1033 if(!MyClient(c2ptr)) /* implies mine and an user */
1034 continue;
1035 if((!host || match(host, c2ptr->host)) && irccmp(u, c2ptr->username) == 0)
1036 {
1037 (*count)++;
1038 res = c2ptr;
1039 }
1040 }
1041 }
1042 return (res);
1043}