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