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