]> jfr.im git - solanum.git/blame - modules/core/m_message.c
Message handlers should return void.
[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
eeabf33a
EM
47static const char message_desc[] =
48 "Provides the PRIVMSG and NOTICE commands to send messages to users and channels";
49
3c7d6fcc
EM
50static void m_message(enum message_type, struct MsgBuf *, struct Client *, struct Client *, int, const char **);
51static void m_privmsg(struct MsgBuf *, struct Client *, struct Client *, int, const char **);
52static void m_notice(struct MsgBuf *, struct Client *, struct Client *, int, const char **);
212380e3
AC
53
54static void expire_tgchange(void *unused);
0e7cb7e6 55static struct ev_entry *expire_tgchange_event;
212380e3
AC
56
57static int
58modinit(void)
59{
0e7cb7e6 60 expire_tgchange_event = rb_event_addish("expire_tgchange", expire_tgchange, NULL, 300);
212380e3
AC
61 expire_tgchange(NULL);
62 return 0;
63}
64
65static void
66moddeinit(void)
67{
0e7cb7e6 68 rb_event_delete(expire_tgchange_event);
212380e3
AC
69}
70
71struct Message privmsg_msgtab = {
7baa37a9 72 "PRIVMSG", 0, 0, 0, 0,
212380e3
AC
73 {mg_unreg, {m_privmsg, 0}, {m_privmsg, 0}, mg_ignore, mg_ignore, {m_privmsg, 0}}
74};
75struct Message notice_msgtab = {
7baa37a9 76 "NOTICE", 0, 0, 0, 0,
212380e3
AC
77 {mg_unreg, {m_notice, 0}, {m_notice, 0}, {m_notice, 0}, mg_ignore, {m_notice, 0}}
78};
79
80mapi_clist_av1 message_clist[] = { &privmsg_msgtab, &notice_msgtab, NULL };
81
8661d5ae 82DECLARE_MODULE_AV2(message, modinit, moddeinit, message_clist, NULL, NULL, NULL, NULL, message_desc);
212380e3
AC
83
84struct entity
85{
86 void *ptr;
87 int type;
88 int flags;
89};
90
ca4c2a86 91static int build_target_list(enum message_type msgtype,
212380e3
AC
92 struct Client *client_p,
93 struct Client *source_p, const char *nicks_channels, const char *text);
94
3c7d6fcc 95static bool flood_attack_client(enum message_type msgtype, struct Client *source_p, struct Client *target_p);
212380e3 96
aa9c9ed2
JT
97/* Fifteen seconds should be plenty for a client to reply a ctcp */
98#define LARGE_CTCP_TIME 15
99
212380e3
AC
100#define ENTITY_NONE 0
101#define ENTITY_CHANNEL 1
c4d2d014
JT
102#define ENTITY_CHANNEL_OPMOD 2
103#define ENTITY_CHANOPS_ON_CHANNEL 3
104#define ENTITY_CLIENT 4
212380e3
AC
105
106static struct entity targets[512];
107static int ntargets = 0;
108
3c7d6fcc 109static bool duplicate_ptr(void *);
212380e3 110
ca4c2a86 111static void msg_channel(enum message_type msgtype,
212380e3
AC
112 struct Client *client_p,
113 struct Client *source_p, struct Channel *chptr, const char *text);
114
ca4c2a86 115static void msg_channel_opmod(enum message_type msgtype,
c4d2d014
JT
116 struct Client *client_p,
117 struct Client *source_p, struct Channel *chptr,
118 const char *text);
119
ca4c2a86 120static void msg_channel_flags(enum message_type msgtype,
212380e3
AC
121 struct Client *client_p,
122 struct Client *source_p,
123 struct Channel *chptr, int flags, const char *text);
124
ca4c2a86 125static void msg_client(enum message_type msgtype,
212380e3
AC
126 struct Client *source_p, struct Client *target_p, const char *text);
127
ca4c2a86 128static void handle_special(enum message_type msgtype,
212380e3
AC
129 struct Client *client_p, struct Client *source_p, const char *nick,
130 const char *text);
131
132/*
133** m_privmsg
134**
135** massive cleanup
136** rev argv 6/91
137**
138** Another massive cleanup Nov, 2000
139** (I don't think there is a single line left from 6/91. Maybe.)
140** m_privmsg and m_notice do basically the same thing.
141** in the original 2.8.2 code base, they were the same function
142** "m_message.c." When we did the great cleanup in conjuncton with bleep
143** of ircu fame, we split m_privmsg.c and m_notice.c.
144** I don't see the point of that now. Its harder to maintain, its
145** easier to introduce bugs into one version and not the other etc.
146** Really, the penalty of an extra function call isn't that big a deal folks.
147** -db Nov 13, 2000
148**
149*/
ca4c2a86
AC
150const char *cmdname[MESSAGE_TYPE_COUNT] = {
151 [MESSAGE_TYPE_PRIVMSG] = "PRIVMSG",
152 [MESSAGE_TYPE_NOTICE] = "NOTICE",
153};
212380e3 154
3c7d6fcc 155static void
428ca87b 156m_privmsg(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *source_p, int parc, const char *parv[])
212380e3 157{
3c7d6fcc 158 m_message(MESSAGE_TYPE_PRIVMSG, msgbuf_p, client_p, source_p, parc, parv);
212380e3
AC
159}
160
3c7d6fcc 161static void
428ca87b 162m_notice(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *source_p, int parc, const char *parv[])
212380e3 163{
3c7d6fcc 164 m_message(MESSAGE_TYPE_NOTICE, msgbuf_p, client_p, source_p, parc, parv);
212380e3
AC
165}
166
167/*
168 * inputs - flag privmsg or notice
212380e3
AC
169 * - pointer to client_p
170 * - pointer to source_p
171 * - pointer to channel
172 */
3c7d6fcc 173static void
428ca87b 174m_message(enum message_type msgtype, struct MsgBuf *msgbuf_p,
212380e3
AC
175 struct Client *client_p, struct Client *source_p, int parc, const char *parv[])
176{
177 int i;
178
179 if(parc < 2 || EmptyString(parv[1]))
180 {
ca4c2a86 181 if(msgtype != MESSAGE_TYPE_NOTICE)
212380e3 182 sendto_one(source_p, form_str(ERR_NORECIPIENT), me.name,
ca4c2a86 183 source_p->name, cmdname[msgtype]);
3c7d6fcc 184 return;
212380e3
AC
185 }
186
187 if(parc < 3 || EmptyString(parv[2]))
188 {
ca4c2a86 189 if(msgtype != MESSAGE_TYPE_NOTICE)
212380e3 190 sendto_one(source_p, form_str(ERR_NOTEXTTOSEND), me.name, source_p->name);
3c7d6fcc 191 return;
212380e3
AC
192 }
193
194 /* Finish the flood grace period if theyre not messaging themselves
195 * as some clients (ircN) do this as a "lag check"
196 */
197 if(MyClient(source_p) && !IsFloodDone(source_p) && irccmp(source_p->name, parv[1]))
198 flood_endgrace(source_p);
199
ca4c2a86 200 if(build_target_list(msgtype, client_p, source_p, parv[1], parv[2]) < 0)
212380e3 201 {
3c7d6fcc 202 return;
212380e3
AC
203 }
204
205 for(i = 0; i < ntargets; i++)
206 {
207 switch (targets[i].type)
208 {
209 case ENTITY_CHANNEL:
ca4c2a86 210 msg_channel(msgtype, client_p, source_p,
212380e3
AC
211 (struct Channel *) targets[i].ptr, parv[2]);
212 break;
213
c4d2d014 214 case ENTITY_CHANNEL_OPMOD:
ca4c2a86 215 msg_channel_opmod(msgtype, client_p, source_p,
c4d2d014
JT
216 (struct Channel *) targets[i].ptr, parv[2]);
217 break;
218
212380e3 219 case ENTITY_CHANOPS_ON_CHANNEL:
ca4c2a86 220 msg_channel_flags(msgtype, client_p, source_p,
212380e3
AC
221 (struct Channel *) targets[i].ptr,
222 targets[i].flags, parv[2]);
223 break;
224
225 case ENTITY_CLIENT:
ca4c2a86 226 msg_client(msgtype, source_p,
212380e3
AC
227 (struct Client *) targets[i].ptr, parv[2]);
228 break;
229 }
230 }
212380e3
AC
231}
232
233/*
234 * build_target_list
235 *
236 * inputs - pointer to given client_p (server)
237 * - pointer to given source (oper/client etc.)
238 * - pointer to list of nicks/channels
239 * - pointer to table to place results
240 * - pointer to text (only used if source_p is an oper)
241 * output - number of valid entities
242 * side effects - target_table is modified to contain a list of
243 * pointers to channels or clients
244 * if source client is an oper
245 * all the classic old bizzare oper privmsg tricks
246 * are parsed and sent as is, if prefixed with $
247 * to disambiguate.
212380e3
AC
248 */
249
250static int
ca4c2a86 251build_target_list(enum message_type msgtype, struct Client *client_p,
212380e3
AC
252 struct Client *source_p, const char *nicks_channels, const char *text)
253{
254 int type;
255 char *p, *nick, *target_list;
256 struct Channel *chptr = NULL;
257 struct Client *target_p;
258
259 target_list = LOCAL_COPY(nicks_channels); /* skip strcpy for non-lazyleafs */
260
261 ntargets = 0;
262
4a2651e5 263 for(nick = rb_strtok_r(target_list, ",", &p); nick; nick = rb_strtok_r(NULL, ",", &p))
212380e3
AC
264 {
265 char *with_prefix;
266 /*
267 * channels are privmsg'd a lot more than other clients, moved up
268 * here plain old channel msg?
269 */
270
271 if(IsChanPrefix(*nick))
272 {
273 /* ignore send of local channel to a server (should not happen) */
274 if(IsServer(client_p) && *nick == '&')
275 continue;
276
277 if((chptr = find_channel(nick)) != NULL)
278 {
279 if(!duplicate_ptr(chptr))
280 {
281 if(ntargets >= ConfigFileEntry.max_targets)
282 {
283 sendto_one(source_p, form_str(ERR_TOOMANYTARGETS),
284 me.name, source_p->name, nick);
285 return (1);
286 }
287 targets[ntargets].ptr = (void *) chptr;
288 targets[ntargets++].type = ENTITY_CHANNEL;
289 }
290 }
291
292 /* non existant channel */
ca4c2a86 293 else if(msgtype != MESSAGE_TYPE_NOTICE)
212380e3
AC
294 sendto_one_numeric(source_p, ERR_NOSUCHNICK,
295 form_str(ERR_NOSUCHNICK), nick);
296
297 continue;
298 }
299
300 if(MyClient(source_p))
301 target_p = find_named_person(nick);
302 else
303 target_p = find_person(nick);
304
305 /* look for a privmsg to another client */
306 if(target_p)
307 {
308 if(!duplicate_ptr(target_p))
309 {
310 if(ntargets >= ConfigFileEntry.max_targets)
311 {
312 sendto_one(source_p, form_str(ERR_TOOMANYTARGETS),
313 me.name, source_p->name, nick);
314 return (1);
315 }
316 targets[ntargets].ptr = (void *) target_p;
317 targets[ntargets].type = ENTITY_CLIENT;
318 targets[ntargets++].flags = 0;
319 }
320 continue;
321 }
322
323 /* @#channel or +#channel message ? */
324
325 type = 0;
326 with_prefix = nick;
327 /* allow %+@ if someone wants to do that */
328 for(;;)
329 {
330 if(*nick == '@')
331 type |= CHFL_CHANOP;
332 else if(*nick == '+')
333 type |= CHFL_CHANOP | CHFL_VOICE;
334 else
335 break;
336 nick++;
337 }
338
339 if(type != 0)
340 {
341 /* no recipient.. */
342 if(EmptyString(nick))
343 {
344 sendto_one(source_p, form_str(ERR_NORECIPIENT),
ca4c2a86 345 me.name, source_p->name, cmdname[msgtype]);
212380e3
AC
346 continue;
347 }
348
349 /* At this point, nick+1 should be a channel name i.e. #foo or &foo
350 * if the channel is found, fine, if not report an error
351 */
352
353 if((chptr = find_channel(nick)) != NULL)
354 {
355 struct membership *msptr;
356
357 msptr = find_channel_membership(chptr, source_p);
358
359 if(!IsServer(source_p) && !IsService(source_p) && !is_chanop_voiced(msptr))
360 {
361 sendto_one(source_p, form_str(ERR_CHANOPRIVSNEEDED),
631b4a54
JT
362 get_id(&me, source_p),
363 get_id(source_p, source_p),
364 with_prefix);
bc75c3ae 365 continue;
212380e3
AC
366 }
367
368 if(!duplicate_ptr(chptr))
369 {
370 if(ntargets >= ConfigFileEntry.max_targets)
371 {
372 sendto_one(source_p, form_str(ERR_TOOMANYTARGETS),
373 me.name, source_p->name, nick);
374 return (1);
375 }
376 targets[ntargets].ptr = (void *) chptr;
377 targets[ntargets].type = ENTITY_CHANOPS_ON_CHANNEL;
378 targets[ntargets++].flags = type;
379 }
380 }
ca4c2a86 381 else if(msgtype != MESSAGE_TYPE_NOTICE)
212380e3
AC
382 {
383 sendto_one_numeric(source_p, ERR_NOSUCHNICK,
384 form_str(ERR_NOSUCHNICK), nick);
385 }
386
387 continue;
388 }
389
c4d2d014
JT
390 if(IsServer(client_p) && *nick == '=' && nick[1] == '#')
391 {
392 nick++;
393 if((chptr = find_channel(nick)) != NULL)
394 {
395 if(!duplicate_ptr(chptr))
396 {
397 if(ntargets >= ConfigFileEntry.max_targets)
398 {
399 sendto_one(source_p, form_str(ERR_TOOMANYTARGETS),
400 me.name, source_p->name, nick);
401 return (1);
402 }
403 targets[ntargets].ptr = (void *) chptr;
404 targets[ntargets++].type = ENTITY_CHANNEL_OPMOD;
405 }
406 }
407
408 /* non existant channel */
ca4c2a86 409 else if(msgtype != MESSAGE_TYPE_NOTICE)
c4d2d014
JT
410 sendto_one_numeric(source_p, ERR_NOSUCHNICK,
411 form_str(ERR_NOSUCHNICK), nick);
412
413 continue;
414 }
415
1fd171a5
JT
416 if(strchr(nick, '@') || (IsOper(source_p) && (*nick == '$')))
417 {
ca4c2a86 418 handle_special(msgtype, client_p, source_p, nick, text);
1fd171a5
JT
419 continue;
420 }
421
212380e3 422 /* no matching anything found - error if not NOTICE */
ca4c2a86 423 if(msgtype != MESSAGE_TYPE_NOTICE)
212380e3
AC
424 {
425 /* dont give this numeric when source is local,
426 * because its misleading --anfl
427 */
428 if(!MyClient(source_p) && IsDigit(*nick))
429 sendto_one(source_p, ":%s %d %s * :Target left IRC. "
430 "Failed to deliver: [%.20s]",
431 get_id(&me, source_p), ERR_NOSUCHNICK,
432 get_id(source_p, source_p), text);
433 else
434 sendto_one_numeric(source_p, ERR_NOSUCHNICK,
435 form_str(ERR_NOSUCHNICK), nick);
436 }
437
438 }
439 return (1);
440}
441
442/*
443 * duplicate_ptr
444 *
445 * inputs - pointer to check
446 * - pointer to table of entities
447 * - number of valid entities so far
3c7d6fcc 448 * output - true if duplicate pointer in table, false if not.
212380e3
AC
449 * note, this does the canonize using pointers
450 * side effects - NONE
451 */
3c7d6fcc 452static bool
212380e3
AC
453duplicate_ptr(void *ptr)
454{
455 int i;
456 for(i = 0; i < ntargets; i++)
457 if(targets[i].ptr == ptr)
3c7d6fcc
EM
458 return true;
459 return false;
212380e3
AC
460}
461
462/*
463 * msg_channel
464 *
465 * inputs - flag privmsg or notice
212380e3
AC
466 * - pointer to client_p
467 * - pointer to source_p
468 * - pointer to channel
469 * output - NONE
470 * side effects - message given channel
471 *
472 * XXX - We need to rework this a bit, it's a tad ugly. --nenolod
473 */
474static void
ca4c2a86 475msg_channel(enum message_type msgtype,
212380e3
AC
476 struct Client *client_p, struct Client *source_p, struct Channel *chptr,
477 const char *text)
478{
479 int result;
ca4c2a86 480 hook_data_privmsg_channel hdata;
212380e3
AC
481
482 if(MyClient(source_p))
483 {
484 /* idle time shouldnt be reset by notices --fl */
ca4c2a86 485 if(msgtype != MESSAGE_TYPE_NOTICE)
e3354945 486 source_p->localClient->last = rb_current_time();
212380e3
AC
487 }
488
ca4c2a86
AC
489 hdata.msgtype = msgtype;
490 hdata.source_p = source_p;
491 hdata.chptr = chptr;
492 hdata.text = text;
493 hdata.approved = 0;
494
495 call_hook(h_privmsg_channel, &hdata);
496
497 /* memory buffer address may have changed, update pointer */
498 text = hdata.text;
499
500 if (hdata.approved != 0)
501 return;
502
6eb03360
AC
503 /* hook may have reduced the string to nothing. */
504 if (EmptyString(text))
505 {
506 /* could be empty after colour stripping and
507 * that would cause problems later */
508 if(msgtype != MESSAGE_TYPE_NOTICE)
509 sendto_one(source_p, form_str(ERR_NOTEXTTOSEND), me.name, source_p->name);
510 return;
511 }
512
212380e3
AC
513 /* chanops and voiced can flood their own channel with impunity */
514 if((result = can_send(chptr, source_p, NULL)))
515 {
717238d2
JT
516 if(result != CAN_SEND_OPV && MyClient(source_p) &&
517 !IsOper(source_p) &&
518 !add_channel_target(source_p, chptr))
519 {
520 sendto_one(source_p, form_str(ERR_TARGCHANGE),
521 me.name, source_p->name, chptr->chname);
522 return;
523 }
212380e3 524 if(result == CAN_SEND_OPV ||
ca4c2a86 525 !flood_attack_channel(msgtype, source_p, chptr, chptr->chname))
212380e3
AC
526 {
527 sendto_channel_flags(client_p, ALL_MEMBERS, source_p, chptr,
ca4c2a86 528 "%s %s :%s", cmdname[msgtype], chptr->chname, text);
212380e3
AC
529 }
530 }
531 else if(chptr->mode.mode & MODE_OPMODERATE &&
8feca176
JT
532 (!(chptr->mode.mode & MODE_NOPRIVMSGS) ||
533 IsMember(source_p, chptr)))
212380e3 534 {
717238d2
JT
535 if(MyClient(source_p) && !IsOper(source_p) &&
536 !add_channel_target(source_p, chptr))
537 {
538 sendto_one(source_p, form_str(ERR_TARGCHANGE),
539 me.name, source_p->name, chptr->chname);
540 return;
541 }
ca4c2a86 542 if(!flood_attack_channel(msgtype, source_p, chptr, chptr->chname))
212380e3 543 {
c4d2d014 544 sendto_channel_opmod(client_p, source_p, chptr,
ca4c2a86 545 cmdname[msgtype], text);
c4d2d014
JT
546 }
547 }
548 else
549 {
ca4c2a86 550 if(msgtype != MESSAGE_TYPE_NOTICE)
c4d2d014
JT
551 sendto_one_numeric(source_p, ERR_CANNOTSENDTOCHAN,
552 form_str(ERR_CANNOTSENDTOCHAN), chptr->chname);
553 }
554}
555/*
556 * msg_channel_opmod
557 *
558 * inputs - flag privmsg or notice
c4d2d014
JT
559 * - pointer to client_p
560 * - pointer to source_p
561 * - pointer to channel
562 * output - NONE
563 * side effects - message given channel ops
564 *
565 * XXX - We need to rework this a bit, it's a tad ugly. --nenolod
566 */
567static void
ca4c2a86 568msg_channel_opmod(enum message_type msgtype,
c4d2d014
JT
569 struct Client *client_p, struct Client *source_p,
570 struct Channel *chptr, const char *text)
571{
ca4c2a86 572 hook_data_privmsg_channel hdata;
c4d2d014 573
ca4c2a86
AC
574 hdata.msgtype = msgtype;
575 hdata.source_p = source_p;
576 hdata.chptr = chptr;
577 hdata.text = text;
578 hdata.approved = 0;
579
580 call_hook(h_privmsg_channel, &hdata);
581
582 /* memory buffer address may have changed, update pointer */
583 text = hdata.text;
584
585 if (hdata.approved != 0)
586 return;
587
6eb03360
AC
588 /* hook may have reduced the string to nothing. */
589 if (EmptyString(text))
590 {
591 /* could be empty after colour stripping and
592 * that would cause problems later */
593 if(msgtype != MESSAGE_TYPE_NOTICE)
594 sendto_one(source_p, form_str(ERR_NOTEXTTOSEND), me.name, source_p->name);
595 return;
596 }
597
c4d2d014
JT
598 if(chptr->mode.mode & MODE_OPMODERATE &&
599 (!(chptr->mode.mode & MODE_NOPRIVMSGS) ||
600 IsMember(source_p, chptr)))
601 {
ca4c2a86 602 if(!flood_attack_channel(msgtype, source_p, chptr, chptr->chname))
c4d2d014
JT
603 {
604 sendto_channel_opmod(client_p, source_p, chptr,
ca4c2a86 605 cmdname[msgtype], text);
212380e3
AC
606 }
607 }
608 else
609 {
ca4c2a86 610 if(msgtype != MESSAGE_TYPE_NOTICE)
212380e3
AC
611 sendto_one_numeric(source_p, ERR_CANNOTSENDTOCHAN,
612 form_str(ERR_CANNOTSENDTOCHAN), chptr->chname);
613 }
614}
615
616/*
617 * msg_channel_flags
618 *
55abcbb2 619 * inputs - flag 0 if PRIVMSG 1 if NOTICE. RFC
212380e3 620 * say NOTICE must not auto reply
212380e3
AC
621 * - pointer to client_p
622 * - pointer to source_p
623 * - pointer to channel
624 * - flags
625 * - pointer to text to send
626 * output - NONE
627 * side effects - message given channel either chanop or voice
628 */
629static void
ca4c2a86 630msg_channel_flags(enum message_type msgtype, struct Client *client_p,
212380e3
AC
631 struct Client *source_p, struct Channel *chptr, int flags, const char *text)
632{
633 int type;
634 char c;
ca4c2a86 635 hook_data_privmsg_channel hdata;
212380e3
AC
636
637 if(flags & CHFL_VOICE)
638 {
639 type = ONLY_CHANOPSVOICED;
640 c = '+';
641 }
642 else
643 {
644 type = ONLY_CHANOPS;
645 c = '@';
646 }
647
648 if(MyClient(source_p))
649 {
650 /* idletime shouldnt be reset by notice --fl */
ca4c2a86 651 if(msgtype != MESSAGE_TYPE_NOTICE)
e3354945 652 source_p->localClient->last = rb_current_time();
212380e3
AC
653 }
654
ca4c2a86
AC
655 hdata.msgtype = msgtype;
656 hdata.source_p = source_p;
657 hdata.chptr = chptr;
658 hdata.text = text;
659 hdata.approved = 0;
660
661 call_hook(h_privmsg_channel, &hdata);
662
663 /* memory buffer address may have changed, update pointer */
664 text = hdata.text;
665
666 if (hdata.approved != 0)
667 return;
668
6eb03360
AC
669 if (EmptyString(text))
670 {
671 /* could be empty after colour stripping and
672 * that would cause problems later */
673 if(msgtype != MESSAGE_TYPE_NOTICE)
674 sendto_one(source_p, form_str(ERR_NOTEXTTOSEND), me.name, source_p->name);
675 return;
676 }
677
212380e3 678 sendto_channel_flags(client_p, type, source_p, chptr, "%s %c%s :%s",
ca4c2a86 679 cmdname[msgtype], c, chptr->chname, text);
212380e3
AC
680}
681
212380e3
AC
682static void
683expire_tgchange(void *unused)
684{
685 tgchange *target;
637c4932 686 rb_dlink_node *ptr, *next_ptr;
212380e3 687
637c4932 688 RB_DLINK_FOREACH_SAFE(ptr, next_ptr, tgchange_list.head)
212380e3
AC
689 {
690 target = ptr->data;
691
e3354945 692 if(target->expiry < rb_current_time())
212380e3 693 {
ba200635
AC
694 rb_dlinkDelete(ptr, &tgchange_list);
695 rb_patricia_remove(tgchange_tree, target->pnode);
bd198292
VY
696 rb_free(target->ip);
697 rb_free(target);
212380e3
AC
698 }
699 }
700}
701
212380e3
AC
702/*
703 * msg_client
704 *
55abcbb2 705 * inputs - flag 0 if PRIVMSG 1 if NOTICE. RFC
212380e3 706 * say NOTICE must not auto reply
212380e3
AC
707 * - pointer to source_p source (struct Client *)
708 * - pointer to target_p target (struct Client *)
709 * - pointer to text
710 * output - NONE
711 * side effects - message given channel either chanop or voice
712 */
713static void
ca4c2a86 714msg_client(enum message_type msgtype,
212380e3
AC
715 struct Client *source_p, struct Client *target_p, const char *text)
716{
c2874388 717 int do_floodcount = 0;
ca4c2a86 718 hook_data_privmsg_user hdata;
c2874388 719
212380e3
AC
720 if(MyClient(source_p))
721 {
f5455d2c
JT
722 /*
723 * XXX: Controversial? Allow target users to send replies
724 * through a +g. Rationale is that people can presently use +g
725 * as a way to taunt users, e.g. harass them and hide behind +g
726 * as a way of griefing. --nenolod
727 */
a7433e33
AC
728 if(msgtype != MESSAGE_TYPE_NOTICE &&
729 (IsSetCallerId(source_p) ||
730 (IsSetRegOnlyMsg(source_p) && !target_p->user->suser[0])) &&
f5455d2c
JT
731 !accept_message(target_p, source_p) &&
732 !IsOper(target_p))
733 {
734 if(rb_dlink_list_length(&source_p->localClient->allow_list) <
9279ad64 735 (unsigned long)ConfigFileEntry.max_accept)
f5455d2c
JT
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
55abcbb2 749 /* reset idle time for message only if its not to self
212380e3 750 * and its not a notice */
ca4c2a86 751 if(msgtype != MESSAGE_TYPE_NOTICE)
e3354945 752 source_p->localClient->last = rb_current_time();
212380e3 753
c2874388
JT
754 /* auto cprivmsg/cnotice */
755 do_floodcount = !IsOper(source_p) &&
756 !find_allowing_channel(source_p, target_p);
757
212380e3
AC
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 */
ca4c2a86 762 if((msgtype != MESSAGE_TYPE_NOTICE || *text != '\001') &&
c2874388 763 ConfigFileEntry.target_change && do_floodcount)
212380e3
AC
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 }
b7b1d686 772
ca4c2a86 773 if (do_floodcount && msgtype == MESSAGE_TYPE_NOTICE && *text == '\001' &&
aa9c9ed2
JT
774 target_p->large_ctcp_sent + LARGE_CTCP_TIME >= rb_current_time())
775 do_floodcount = 0;
776
b7b1d686 777 if (do_floodcount &&
ca4c2a86 778 flood_attack_client(msgtype, source_p, target_p))
b7b1d686 779 return;
212380e3
AC
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
ca4c2a86 789 if(MyConnect(source_p) && (msgtype != MESSAGE_TYPE_NOTICE) && target_p->user && target_p->user->away)
212380e3 790 sendto_one_numeric(source_p, RPL_AWAY, form_str(RPL_AWAY),
c127b45b 791 target_p->name, target_p->user->away);
212380e3
AC
792
793 if(MyClient(target_p))
794 {
ca4c2a86
AC
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
6eb03360
AC
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
212380e3
AC
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 {
c9f01c4f 825 add_reply_target(target_p, source_p);
212380e3
AC
826 sendto_one(target_p, ":%s!%s@%s %s %s :%s",
827 source_p->name,
828 source_p->username,
ca4c2a86 829 source_p->host, cmdname[msgtype], target_p->name, text);
212380e3
AC
830 }
831 else if (IsSetRegOnlyMsg(target_p) && !source_p->user->suser[0])
832 {
ca4c2a86 833 if (msgtype != MESSAGE_TYPE_NOTICE)
212380e3
AC
834 sendto_one_numeric(source_p, ERR_NONONREG,
835 form_str(ERR_NONONREG),
836 target_p->name);
212380e3
AC
837 }
838 else
839 {
840 /* check for accept, flag recipient incoming message */
ca4c2a86 841 if(msgtype != MESSAGE_TYPE_NOTICE)
212380e3
AC
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 +
e3354945 849 ConfigFileEntry.caller_id_wait) < rb_current_time())
212380e3 850 {
ca4c2a86 851 if(msgtype != MESSAGE_TYPE_NOTICE)
212380e3
AC
852 sendto_one_numeric(source_p, RPL_TARGNOTIFY,
853 form_str(RPL_TARGNOTIFY),
854 target_p->name);
855
c9f01c4f 856 add_reply_target(target_p, source_p);
212380e3
AC
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
e3354945 861 target_p->localClient->last_caller_id_time = rb_current_time();
212380e3 862 }
212380e3
AC
863 }
864 }
865 else
c9f01c4f
JT
866 {
867 add_reply_target(target_p, source_p);
ca4c2a86 868 sendto_anywhere(target_p, source_p, cmdname[msgtype], ":%s", text);
c9f01c4f 869 }
212380e3 870 }
b7b1d686 871 else
ca4c2a86 872 sendto_anywhere(target_p, source_p, cmdname[msgtype], ":%s", text);
212380e3
AC
873
874 return;
875}
876
877/*
878 * flood_attack_client
879 * inputs - flag 0 if PRIVMSG 1 if NOTICE. RFC
3c7d6fcc 880 * says NOTICE must not auto reply
55abcbb2 881 * - pointer to source Client
212380e3 882 * - pointer to target Client
3c7d6fcc 883 * output - true if target is under flood attack
212380e3
AC
884 * side effects - check for flood attack on target target_p
885 */
3c7d6fcc 886static bool
ca4c2a86 887flood_attack_client(enum message_type msgtype, struct Client *source_p, struct Client *target_p)
212380e3
AC
888{
889 int delta;
890
c24efdc0
JT
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))
212380e3 897 {
c24efdc0 898 if((target_p->first_received_message_time + 1) < rb_current_time())
212380e3 899 {
c24efdc0
JT
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)
212380e3 904 {
c24efdc0
JT
905 target_p->received_number_of_privmsgs = 0;
906 target_p->flood_noticed = 0;
212380e3
AC
907 }
908 }
909
c24efdc0
JT
910 if((target_p->received_number_of_privmsgs >=
911 GlobalSetOptions.floodcount) || target_p->flood_noticed)
212380e3 912 {
c24efdc0 913 if(target_p->flood_noticed == 0)
212380e3 914 {
f2c1b06b 915 sendto_realops_snomask(SNO_BOTS, L_NETWIDE,
212380e3
AC
916 "Possible Flooder %s[%s@%s] on %s target: %s",
917 source_p->name, source_p->username,
63aecfb9 918 source_p->orighost,
c88cdb00 919 source_p->servptr->name, target_p->name);
c24efdc0 920 target_p->flood_noticed = 1;
212380e3 921 /* add a bit of penalty */
c24efdc0 922 target_p->received_number_of_privmsgs += 2;
212380e3 923 }
ca4c2a86 924 if(MyClient(source_p) && (msgtype != MESSAGE_TYPE_NOTICE))
212380e3
AC
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);
3c7d6fcc 928 return true;
212380e3
AC
929 }
930 else
c24efdc0 931 target_p->received_number_of_privmsgs++;
212380e3
AC
932 }
933
3c7d6fcc 934 return false;
212380e3
AC
935}
936
212380e3
AC
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 */
953static void
ca4c2a86 954handle_special(enum message_type msgtype, struct Client *client_p,
212380e3
AC
955 struct Client *source_p, const char *nick, const char *text)
956{
957 struct Client *target_p;
212380e3
AC
958 char *server;
959 char *s;
212380e3
AC
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
212380e3
AC
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",
ca4c2a86 988 get_id(source_p, target_p), cmdname[msgtype], nick, text);
212380e3
AC
989 return;
990 }
991
212380e3 992 /* Check if someones msg'ing opers@our.server */
ef57f7fb 993 if(strncmp(nick, "opers@", 6) == 0)
212380e3
AC
994 {
995 sendto_realops_snomask(SNO_GENERAL, L_ALL, "To opers: From: %s: %s",
996 source_p->name, text);
997 return;
998 }
999
ef57f7fb
JT
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
212380e3 1005 */
ef57f7fb
JT
1006 sendto_one_numeric(source_p, ERR_NOSUCHNICK,
1007 form_str(ERR_NOSUCHNICK), nick);
1008 return;
212380e3
AC
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",
ca4c2a86 1025 me.name, source_p->name, cmdname[msgtype], nick, nick);
212380e3
AC
1026 return;
1027 }
1028
a6f4368b
JT
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
212380e3
AC
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,
ca4c2a86
AC
1055 "%s $%s :%s", cmdname[msgtype], nick, text);
1056 if (msgtype != MESSAGE_TYPE_NOTICE && *text == '\001')
aa9c9ed2 1057 source_p->large_ctcp_sent = rb_current_time();
212380e3
AC
1058 return;
1059 }
1060}