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