]> jfr.im git - irc/rqf/shadowircd.git/blame - modules/core/m_message.c
Replace int fd; in local client object with an fde object.
[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"
44#include "event.h"
45#include "patricia.h"
46#include "s_newconf.h"
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);
53
54static int
55modinit(void)
56{
57 eventAddIsh("expire_tgchange", expire_tgchange, NULL, 300);
58 expire_tgchange(NULL);
59 return 0;
60}
61
62static void
63moddeinit(void)
64{
65 eventDelete(expire_tgchange, NULL);
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)
449 source_p->localClient->last = CurrentTime;
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)
533 source_p->localClient->last = CurrentTime;
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;
548 dlink_node *ptr, *next_ptr;
549
550 DLINK_FOREACH_SAFE(ptr, next_ptr, tgchange_list.head)
551 {
552 target = ptr->data;
553
554 if(target->expiry < CurrentTime)
555 {
556 dlinkDelete(ptr, &tgchange_list);
557 patricia_remove(tgchange_tree, target->pnode);
558 MyFree(target->ip);
559 MyFree(target);
560 }
561 }
562}
563
564static int
565add_target(struct Client *source_p, struct Client *target_p)
566{
567 int i, j;
568
569 /* can msg themselves or services without using any target slots */
570 if(source_p == target_p || IsService(target_p))
571 return 1;
572
573 /* special condition for those who have had PRIVMSG crippled to allow them
574 * to talk to IRCops still.
575 *
576 * XXX: is this controversial?
577 */
578 if(source_p->localClient->target_last > CurrentTime && IsOper(target_p))
579 return 1;
580
581 if(USED_TARGETS(source_p))
582 {
583 /* hunt for an existing target */
584 for(i = PREV_FREE_TARGET(source_p), j = USED_TARGETS(source_p);
585 j; --j, PREV_TARGET(i))
586 {
587 if(source_p->localClient->targets[i] == target_p)
588 return 1;
589 }
590
591 /* first message after connect, we may only start clearing
592 * slots after this message --anfl
593 */
594 if(!IsTGChange(source_p))
595 {
596 SetTGChange(source_p);
597 source_p->localClient->target_last = CurrentTime;
598 }
599 /* clear as many targets as we can */
600 else if((i = (CurrentTime - source_p->localClient->target_last) / 60))
601 {
602 if(i > USED_TARGETS(source_p))
603 USED_TARGETS(source_p) = 0;
604 else
605 USED_TARGETS(source_p) -= i;
606
607 source_p->localClient->target_last = CurrentTime;
608 }
609 /* cant clear any, full target list */
610 else if(USED_TARGETS(source_p) == 10)
611 {
612 add_tgchange(source_p->sockhost);
613 return 0;
614 }
615 }
616 /* no targets in use, reset their target_last so that they cant
617 * abuse a long idle to get targets back more quickly
618 */
619 else
620 {
621 source_p->localClient->target_last = CurrentTime;
622 SetTGChange(source_p);
623 }
624
625 source_p->localClient->targets[FREE_TARGET(source_p)] = target_p;
626 NEXT_TARGET(FREE_TARGET(source_p));
627 ++USED_TARGETS(source_p);
628 return 1;
629}
630
631/*
632 * msg_client
633 *
634 * inputs - flag 0 if PRIVMSG 1 if NOTICE. RFC
635 * say NOTICE must not auto reply
636 * - pointer to command, "PRIVMSG" or "NOTICE"
637 * - pointer to source_p source (struct Client *)
638 * - pointer to target_p target (struct Client *)
639 * - pointer to text
640 * output - NONE
641 * side effects - message given channel either chanop or voice
642 */
643static void
644msg_client(int p_or_n, const char *command,
645 struct Client *source_p, struct Client *target_p, const char *text)
646{
647 if(MyClient(source_p))
648 {
649 /* reset idle time for message only if its not to self
650 * and its not a notice */
651 if(p_or_n != NOTICE)
652 source_p->localClient->last = CurrentTime;
653
654 /* target change stuff, dont limit ctcp replies as that
655 * would allow people to start filling up random users
656 * targets just by ctcping them
657 */
658 if((p_or_n != NOTICE || *text != '\001') &&
659 ConfigFileEntry.target_change && !IsOper(source_p))
660 {
661 if(!add_target(source_p, target_p))
662 {
663 sendto_one(source_p, form_str(ERR_TARGCHANGE),
664 me.name, source_p->name, target_p->name);
665 return;
666 }
667 }
668 }
669 else if(source_p->from == target_p->from)
670 {
671 sendto_realops_snomask(SNO_DEBUG, L_ALL,
672 "Send message to %s[%s] dropped from %s(Fake Dir)",
673 target_p->name, target_p->from->name, source_p->name);
674 return;
675 }
676
677 if(MyConnect(source_p) && (p_or_n != NOTICE) && target_p->user && target_p->user->away)
678 sendto_one_numeric(source_p, RPL_AWAY, form_str(RPL_AWAY),
679 target_p->name, target_p->user->away);
680
681 if(MyClient(target_p))
682 {
683 /* XXX Controversial? allow opers always to send through a +g */
684 if(!IsServer(source_p) && (IsSetCallerId(target_p) ||
685 (IsSetRegOnlyMsg(target_p) && !source_p->user->suser[0])))
686 {
687 /* Here is the anti-flood bot/spambot code -db */
688 if(accept_message(source_p, target_p) || IsOper(source_p))
689 {
690 sendto_one(target_p, ":%s!%s@%s %s %s :%s",
691 source_p->name,
692 source_p->username,
693 source_p->host, command, target_p->name, text);
694 }
695 else if (IsSetRegOnlyMsg(target_p) && !source_p->user->suser[0])
696 {
697 if (p_or_n != NOTICE)
698 sendto_one_numeric(source_p, ERR_NONONREG,
699 form_str(ERR_NONONREG),
700 target_p->name);
701 /* Only so opers can watch for floods */
702 (void) flood_attack_client(p_or_n, source_p, target_p);
703 }
704 else
705 {
706 /* check for accept, flag recipient incoming message */
707 if(p_or_n != NOTICE)
708 {
709 sendto_one_numeric(source_p, ERR_TARGUMODEG,
710 form_str(ERR_TARGUMODEG),
711 target_p->name);
712 }
713
714 if((target_p->localClient->last_caller_id_time +
715 ConfigFileEntry.caller_id_wait) < CurrentTime)
716 {
717 if(p_or_n != NOTICE)
718 sendto_one_numeric(source_p, RPL_TARGNOTIFY,
719 form_str(RPL_TARGNOTIFY),
720 target_p->name);
721
722 sendto_one(target_p, form_str(RPL_UMODEGMSG),
723 me.name, target_p->name, source_p->name,
724 source_p->username, source_p->host);
725
726 target_p->localClient->last_caller_id_time = CurrentTime;
727 }
728 /* Only so opers can watch for floods */
729 (void) flood_attack_client(p_or_n, source_p, target_p);
730 }
731 }
732 else
733 {
734 /* If the client is remote, we dont perform a special check for
735 * flooding.. as we wouldnt block their message anyway.. this means
736 * we dont give warnings.. we then check if theyre opered
737 * (to avoid flood warnings), lastly if theyre our client
738 * and flooding -- fl */
739 if(!MyClient(source_p) || IsOper(source_p) ||
740 !flood_attack_client(p_or_n, source_p, target_p))
741 sendto_anywhere(target_p, source_p, command, ":%s", text);
742 }
743 }
744 else if(!MyClient(source_p) || IsOper(source_p) ||
745 !flood_attack_client(p_or_n, source_p, target_p))
746 sendto_anywhere(target_p, source_p, command, ":%s", text);
747
748 return;
749}
750
751/*
752 * flood_attack_client
753 * inputs - flag 0 if PRIVMSG 1 if NOTICE. RFC
754 * say NOTICE must not auto reply
755 * - pointer to source Client
756 * - pointer to target Client
757 * output - 1 if target is under flood attack
758 * side effects - check for flood attack on target target_p
759 */
760static int
761flood_attack_client(int p_or_n, struct Client *source_p, struct Client *target_p)
762{
763 int delta;
764
765 if(GlobalSetOptions.floodcount && MyConnect(target_p) && IsClient(source_p))
766 {
767 if((target_p->localClient->first_received_message_time + 1) < CurrentTime)
768 {
769 delta = CurrentTime - target_p->localClient->first_received_message_time;
770 target_p->localClient->received_number_of_privmsgs -= delta;
771 target_p->localClient->first_received_message_time = CurrentTime;
772 if(target_p->localClient->received_number_of_privmsgs <= 0)
773 {
774 target_p->localClient->received_number_of_privmsgs = 0;
775 target_p->localClient->flood_noticed = 0;
776 }
777 }
778
779 if((target_p->localClient->received_number_of_privmsgs >=
780 GlobalSetOptions.floodcount) || target_p->localClient->flood_noticed)
781 {
782 if(target_p->localClient->flood_noticed == 0)
783 {
9f8d60cc 784 sendto_realops_snomask(SNO_BOTS, L_NETWIDE,
212380e3 785 "Possible Flooder %s[%s@%s] on %s target: %s",
786 source_p->name, source_p->username,
63aecfb9 787 source_p->orighost,
c88cdb00 788 source_p->servptr->name, target_p->name);
212380e3 789 target_p->localClient->flood_noticed = 1;
790 /* add a bit of penalty */
791 target_p->localClient->received_number_of_privmsgs += 2;
792 }
793 if(MyClient(source_p) && (p_or_n != NOTICE))
794 sendto_one(source_p,
795 ":%s NOTICE %s :*** Message to %s throttled due to flooding",
796 me.name, source_p->name, target_p->name);
797 return 1;
798 }
799 else
800 target_p->localClient->received_number_of_privmsgs++;
801 }
802
803 return 0;
804}
805
806/*
807 * flood_attack_channel
808 * inputs - flag 0 if PRIVMSG 1 if NOTICE. RFC
809 * says NOTICE must not auto reply
810 * - pointer to source Client
811 * - pointer to target channel
812 * output - 1 if target is under flood attack
813 * side effects - check for flood attack on target chptr
814 */
815static int
816flood_attack_channel(int p_or_n, struct Client *source_p, struct Channel *chptr, char *chname)
817{
818 int delta;
819
820 if(GlobalSetOptions.floodcount && MyClient(source_p))
821 {
822 if((chptr->first_received_message_time + 1) < CurrentTime)
823 {
824 delta = CurrentTime - chptr->first_received_message_time;
825 chptr->received_number_of_privmsgs -= delta;
826 chptr->first_received_message_time = CurrentTime;
827 if(chptr->received_number_of_privmsgs <= 0)
828 {
829 chptr->received_number_of_privmsgs = 0;
830 chptr->flood_noticed = 0;
831 }
832 }
833
834 if((chptr->received_number_of_privmsgs >= GlobalSetOptions.floodcount)
835 || chptr->flood_noticed)
836 {
837 if(chptr->flood_noticed == 0)
838 {
9f8d60cc 839 sendto_realops_snomask(SNO_BOTS, *chptr->chname == '&' ? L_ALL : L_NETWIDE,
212380e3 840 "Possible Flooder %s[%s@%s] on %s target: %s",
841 source_p->name, source_p->username,
63aecfb9 842 source_p->orighost,
c88cdb00 843 source_p->servptr->name, chptr->chname);
212380e3 844 chptr->flood_noticed = 1;
845
846 /* Add a bit of penalty */
847 chptr->received_number_of_privmsgs += 2;
848 }
849 if(MyClient(source_p) && (p_or_n != NOTICE))
850 sendto_one(source_p,
851 ":%s NOTICE %s :*** Message to %s throttled due to flooding",
852 me.name, source_p->name, chptr->chname);
853 return 1;
854 }
855 else
856 chptr->received_number_of_privmsgs++;
857 }
858
859 return 0;
860}
861
862
863/*
864 * handle_special
865 *
866 * inputs - server pointer
867 * - client pointer
868 * - nick stuff to grok for opers
869 * - text to send if grok
870 * output - none
871 * side effects - all the traditional oper type messages are parsed here.
872 * i.e. "/msg #some.host."
873 * However, syntax has been changed.
874 * previous syntax "/msg #some.host.mask"
875 * now becomes "/msg $#some.host.mask"
876 * previous syntax of: "/msg $some.server.mask" remains
877 * This disambiguates the syntax.
878 */
879static void
880handle_special(int p_or_n, const char *command, struct Client *client_p,
881 struct Client *source_p, const char *nick, const char *text)
882{
883 struct Client *target_p;
884 char *host;
885 char *server;
886 char *s;
887 int count;
888
889 /* user[%host]@server addressed?
890 * NOTE: users can send to user@server, but not user%host@server
891 * or opers@server
892 */
893 if((server = strchr(nick, '@')) != NULL)
894 {
895 if((target_p = find_server(source_p, server + 1)) == NULL)
896 {
897 sendto_one_numeric(source_p, ERR_NOSUCHSERVER,
898 form_str(ERR_NOSUCHSERVER), server + 1);
899 return;
900 }
901
902 count = 0;
903
904 if(!IsOper(source_p))
905 {
906 if(strchr(nick, '%') || (strncmp(nick, "opers", 5) == 0))
907 {
908 sendto_one_numeric(source_p, ERR_NOSUCHNICK,
909 form_str(ERR_NOSUCHNICK), nick);
910 return;
911 }
912 }
913
914 /* somewhere else.. */
915 if(!IsMe(target_p))
916 {
917 sendto_one(target_p, ":%s %s %s :%s",
918 get_id(source_p, target_p), command, nick, text);
919 return;
920 }
921
922 *server = '\0';
923
924 if((host = strchr(nick, '%')) != NULL)
925 *host++ = '\0';
926
927 /* Check if someones msg'ing opers@our.server */
928 if(strcmp(nick, "opers") == 0)
929 {
930 sendto_realops_snomask(SNO_GENERAL, L_ALL, "To opers: From: %s: %s",
931 source_p->name, text);
932 return;
933 }
934
935 /*
936 * Look for users which match the destination host
937 * (no host == wildcard) and if one and one only is
938 * found connected to me, deliver message!
939 */
940 target_p = find_userhost(nick, host, &count);
941
942 if(target_p != NULL)
943 {
944 if(server != NULL)
945 *server = '@';
946 if(host != NULL)
947 *--host = '%';
948
949 if(count == 1)
950 sendto_anywhere(target_p, source_p, command, ":%s", text);
951 else
952 sendto_one(source_p, form_str(ERR_TOOMANYTARGETS),
953 get_id(&me, source_p), get_id(source_p, source_p), nick);
954 }
955 }
956
957 /*
958 * the following two cases allow masks in NOTICEs
959 * (for OPERs only)
960 *
961 * Armin, 8Jun90 (gruner@informatik.tu-muenchen.de)
962 */
963 if(IsOper(source_p) && *nick == '$')
964 {
965 if((*(nick + 1) == '$' || *(nick + 1) == '#'))
966 nick++;
967 else if(MyOper(source_p))
968 {
969 sendto_one(source_p,
970 ":%s NOTICE %s :The command %s %s is no longer supported, please use $%s",
971 me.name, source_p->name, command, nick, nick);
972 return;
973 }
974
c13a2d9a
JT
975 if(MyClient(source_p) && !IsOperMassNotice(source_p))
976 {
977 sendto_one(source_p, form_str(ERR_NOPRIVS),
978 me.name, source_p->name, "mass_notice");
979 return;
980 }
981
212380e3 982 if((s = strrchr(nick, '.')) == NULL)
983 {
984 sendto_one_numeric(source_p, ERR_NOTOPLEVEL,
985 form_str(ERR_NOTOPLEVEL), nick);
986 return;
987 }
988 while(*++s)
989 if(*s == '.' || *s == '*' || *s == '?')
990 break;
991 if(*s == '*' || *s == '?')
992 {
993 sendto_one_numeric(source_p, ERR_WILDTOPLEVEL,
994 form_str(ERR_WILDTOPLEVEL), nick);
995 return;
996 }
997
998 sendto_match_butone(IsServer(client_p) ? client_p : NULL, source_p,
999 nick + 1,
1000 (*nick == '#') ? MATCH_HOST : MATCH_SERVER,
1001 "%s $%s :%s", command, nick, text);
1002 return;
1003 }
1004}
1005
1006/*
1007 * find_userhost - find a user@host (server or user).
1008 * inputs - user name to look for
1009 * - host name to look for
1010 * - pointer to count of number of matches found
1011 * outputs - pointer to client if found
1012 * - count is updated
1013 * side effects - none
1014 *
1015 */
1016static struct Client *
1017find_userhost(const char *user, const char *host, int *count)
1018{
1019 struct Client *c2ptr;
1020 struct Client *res = NULL;
1021 char *u = LOCAL_COPY(user);
1022 dlink_node *ptr;
1023 *count = 0;
1024 if(collapse(u) != NULL)
1025 {
1026 DLINK_FOREACH(ptr, global_client_list.head)
1027 {
1028 c2ptr = ptr->data;
1029 if(!MyClient(c2ptr)) /* implies mine and an user */
1030 continue;
1031 if((!host || match(host, c2ptr->host)) && irccmp(u, c2ptr->username) == 0)
1032 {
1033 (*count)++;
1034 res = c2ptr;
1035 }
1036 }
1037 }
1038 return (res);
1039}