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