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