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