2 * ircd-ratbox: A slightly useful ircd.
3 * m_message.c: Sends a (PRIVMSG|NOTICE) message to a user or channel.
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
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.
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.
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
24 * $Id: m_message.c 3173 2007-01-31 23:57:18Z jilles $
38 #include "irc_string.h"
46 #include "s_newconf.h"
48 static int m_message(int, const char *, struct Client
*, struct Client
*, int, const char **);
49 static int m_privmsg(struct Client
*, struct Client
*, int, const char **);
50 static int m_notice(struct Client
*, struct Client
*, int, const char **);
52 static void expire_tgchange(void *unused
);
57 eventAddIsh("expire_tgchange", expire_tgchange
, NULL
, 300);
58 expire_tgchange(NULL
);
65 eventDelete(expire_tgchange
, NULL
);
68 struct Message privmsg_msgtab
= {
69 "PRIVMSG", 0, 0, 0, MFLG_SLOW
| MFLG_UNREG
,
70 {mg_unreg
, {m_privmsg
, 0}, {m_privmsg
, 0}, mg_ignore
, mg_ignore
, {m_privmsg
, 0}}
72 struct Message notice_msgtab
= {
73 "NOTICE", 0, 0, 0, MFLG_SLOW
,
74 {mg_unreg
, {m_notice
, 0}, {m_notice
, 0}, {m_notice
, 0}, mg_ignore
, {m_notice
, 0}}
77 mapi_clist_av1 message_clist
[] = { &privmsg_msgtab
, ¬ice_msgtab
, NULL
};
79 DECLARE_MODULE_AV1(message
, modinit
, moddeinit
, message_clist
, NULL
, NULL
, "$Revision: 3173 $");
88 static int build_target_list(int p_or_n
, const char *command
,
89 struct Client
*client_p
,
90 struct Client
*source_p
, const char *nicks_channels
, const char *text
);
92 static int flood_attack_client(int p_or_n
, struct Client
*source_p
, struct Client
*target_p
);
93 static int flood_attack_channel(int p_or_n
, struct Client
*source_p
,
94 struct Channel
*chptr
, char *chname
);
95 static struct Client
*find_userhost(const char *, const char *, int *);
98 #define ENTITY_CHANNEL 1
99 #define ENTITY_CHANOPS_ON_CHANNEL 2
100 #define ENTITY_CLIENT 3
102 static struct entity targets
[512];
103 static int ntargets
= 0;
105 static int duplicate_ptr(void *);
107 static void msg_channel(int p_or_n
, const char *command
,
108 struct Client
*client_p
,
109 struct Client
*source_p
, struct Channel
*chptr
, const char *text
);
111 static void msg_channel_flags(int p_or_n
, const char *command
,
112 struct Client
*client_p
,
113 struct Client
*source_p
,
114 struct Channel
*chptr
, int flags
, const char *text
);
116 static void msg_client(int p_or_n
, const char *command
,
117 struct Client
*source_p
, struct Client
*target_p
, const char *text
);
119 static void handle_special(int p_or_n
, const char *command
,
120 struct Client
*client_p
, struct Client
*source_p
, const char *nick
,
129 ** Another massive cleanup Nov, 2000
130 ** (I don't think there is a single line left from 6/91. Maybe.)
131 ** m_privmsg and m_notice do basically the same thing.
132 ** in the original 2.8.2 code base, they were the same function
133 ** "m_message.c." When we did the great cleanup in conjuncton with bleep
134 ** of ircu fame, we split m_privmsg.c and m_notice.c.
135 ** I don't see the point of that now. Its harder to maintain, its
136 ** easier to introduce bugs into one version and not the other etc.
137 ** Really, the penalty of an extra function call isn't that big a deal folks.
146 m_privmsg(struct Client
*client_p
, struct Client
*source_p
, int parc
, const char *parv
[])
148 return m_message(PRIVMSG
, "PRIVMSG", client_p
, source_p
, parc
, parv
);
152 m_notice(struct Client
*client_p
, struct Client
*source_p
, int parc
, const char *parv
[])
154 return m_message(NOTICE
, "NOTICE", client_p
, source_p
, parc
, parv
);
158 * inputs - flag privmsg or notice
159 * - pointer to command "PRIVMSG" or "NOTICE"
160 * - pointer to client_p
161 * - pointer to source_p
162 * - pointer to channel
165 m_message(int p_or_n
,
167 struct Client
*client_p
, struct Client
*source_p
, int parc
, const char *parv
[])
171 if(parc
< 2 || EmptyString(parv
[1]))
174 sendto_one(source_p
, form_str(ERR_NORECIPIENT
), me
.name
,
175 source_p
->name
, command
);
179 if(parc
< 3 || EmptyString(parv
[2]))
182 sendto_one(source_p
, form_str(ERR_NOTEXTTOSEND
), me
.name
, source_p
->name
);
186 /* Finish the flood grace period if theyre not messaging themselves
187 * as some clients (ircN) do this as a "lag check"
189 if(MyClient(source_p
) && !IsFloodDone(source_p
) && irccmp(source_p
->name
, parv
[1]))
190 flood_endgrace(source_p
);
192 if(build_target_list(p_or_n
, command
, client_p
, source_p
, parv
[1], parv
[2]) < 0)
197 for(i
= 0; i
< ntargets
; i
++)
199 switch (targets
[i
].type
)
202 msg_channel(p_or_n
, command
, client_p
, source_p
,
203 (struct Channel
*) targets
[i
].ptr
, parv
[2]);
206 case ENTITY_CHANOPS_ON_CHANNEL
:
207 msg_channel_flags(p_or_n
, command
, client_p
, source_p
,
208 (struct Channel
*) targets
[i
].ptr
,
209 targets
[i
].flags
, parv
[2]);
213 msg_client(p_or_n
, command
, source_p
,
214 (struct Client
*) targets
[i
].ptr
, parv
[2]);
225 * inputs - pointer to given client_p (server)
226 * - pointer to given source (oper/client etc.)
227 * - pointer to list of nicks/channels
228 * - pointer to table to place results
229 * - pointer to text (only used if source_p is an oper)
230 * output - number of valid entities
231 * side effects - target_table is modified to contain a list of
232 * pointers to channels or clients
233 * if source client is an oper
234 * all the classic old bizzare oper privmsg tricks
235 * are parsed and sent as is, if prefixed with $
241 build_target_list(int p_or_n
, const char *command
, struct Client
*client_p
,
242 struct Client
*source_p
, const char *nicks_channels
, const char *text
)
245 char *p
, *nick
, *target_list
;
246 struct Channel
*chptr
= NULL
;
247 struct Client
*target_p
;
249 target_list
= LOCAL_COPY(nicks_channels
); /* skip strcpy for non-lazyleafs */
253 for(nick
= strtoken(&p
, target_list
, ","); nick
; nick
= strtoken(&p
, NULL
, ","))
257 * channels are privmsg'd a lot more than other clients, moved up
258 * here plain old channel msg?
261 if(IsChanPrefix(*nick
))
263 /* ignore send of local channel to a server (should not happen) */
264 if(IsServer(client_p
) && *nick
== '&')
267 if((chptr
= find_channel(nick
)) != NULL
)
269 if(!duplicate_ptr(chptr
))
271 if(ntargets
>= ConfigFileEntry
.max_targets
)
273 sendto_one(source_p
, form_str(ERR_TOOMANYTARGETS
),
274 me
.name
, source_p
->name
, nick
);
277 targets
[ntargets
].ptr
= (void *) chptr
;
278 targets
[ntargets
++].type
= ENTITY_CHANNEL
;
282 /* non existant channel */
283 else if(p_or_n
!= NOTICE
)
284 sendto_one_numeric(source_p
, ERR_NOSUCHNICK
,
285 form_str(ERR_NOSUCHNICK
), nick
);
290 if(MyClient(source_p
))
291 target_p
= find_named_person(nick
);
293 target_p
= find_person(nick
);
295 /* look for a privmsg to another client */
298 if(!duplicate_ptr(target_p
))
300 if(ntargets
>= ConfigFileEntry
.max_targets
)
302 sendto_one(source_p
, form_str(ERR_TOOMANYTARGETS
),
303 me
.name
, source_p
->name
, nick
);
306 targets
[ntargets
].ptr
= (void *) target_p
;
307 targets
[ntargets
].type
= ENTITY_CLIENT
;
308 targets
[ntargets
++].flags
= 0;
313 /* @#channel or +#channel message ? */
317 /* allow %+@ if someone wants to do that */
322 else if(*nick
== '+')
323 type
|= CHFL_CHANOP
| CHFL_VOICE
;
332 if(EmptyString(nick
))
334 sendto_one(source_p
, form_str(ERR_NORECIPIENT
),
335 me
.name
, source_p
->name
, command
);
339 /* At this point, nick+1 should be a channel name i.e. #foo or &foo
340 * if the channel is found, fine, if not report an error
343 if((chptr
= find_channel(nick
)) != NULL
)
345 struct membership
*msptr
;
347 msptr
= find_channel_membership(chptr
, source_p
);
349 if(!IsServer(source_p
) && !IsService(source_p
) && !is_chanop_voiced(msptr
))
351 sendto_one(source_p
, form_str(ERR_CHANOPRIVSNEEDED
),
352 me
.name
, source_p
->name
, with_prefix
);
356 if(!duplicate_ptr(chptr
))
358 if(ntargets
>= ConfigFileEntry
.max_targets
)
360 sendto_one(source_p
, form_str(ERR_TOOMANYTARGETS
),
361 me
.name
, source_p
->name
, nick
);
364 targets
[ntargets
].ptr
= (void *) chptr
;
365 targets
[ntargets
].type
= ENTITY_CHANOPS_ON_CHANNEL
;
366 targets
[ntargets
++].flags
= type
;
369 else if(p_or_n
!= NOTICE
)
371 sendto_one_numeric(source_p
, ERR_NOSUCHNICK
,
372 form_str(ERR_NOSUCHNICK
), nick
);
378 if(strchr(nick
, '@') || (IsOper(source_p
) && (*nick
== '$')))
380 handle_special(p_or_n
, command
, client_p
, source_p
, nick
, text
);
384 /* no matching anything found - error if not NOTICE */
387 /* dont give this numeric when source is local,
388 * because its misleading --anfl
390 if(!MyClient(source_p
) && IsDigit(*nick
))
391 sendto_one(source_p
, ":%s %d %s * :Target left IRC. "
392 "Failed to deliver: [%.20s]",
393 get_id(&me
, source_p
), ERR_NOSUCHNICK
,
394 get_id(source_p
, source_p
), text
);
396 sendto_one_numeric(source_p
, ERR_NOSUCHNICK
,
397 form_str(ERR_NOSUCHNICK
), nick
);
407 * inputs - pointer to check
408 * - pointer to table of entities
409 * - number of valid entities so far
410 * output - YES if duplicate pointer in table, NO if not.
411 * note, this does the canonize using pointers
412 * side effects - NONE
415 duplicate_ptr(void *ptr
)
418 for(i
= 0; i
< ntargets
; i
++)
419 if(targets
[i
].ptr
== ptr
)
427 * inputs - flag privmsg or notice
428 * - pointer to command "PRIVMSG" or "NOTICE"
429 * - pointer to client_p
430 * - pointer to source_p
431 * - pointer to channel
433 * side effects - message given channel
435 * XXX - We need to rework this a bit, it's a tad ugly. --nenolod
438 msg_channel(int p_or_n
, const char *command
,
439 struct Client
*client_p
, struct Client
*source_p
, struct Channel
*chptr
,
445 if(MyClient(source_p
))
447 /* idle time shouldnt be reset by notices --fl */
449 source_p
->localClient
->last
= CurrentTime
;
452 if(chptr
->mode
.mode
& MODE_NOCOLOR
)
454 strlcpy(text2
, text
, BUFSIZE
);
457 if (EmptyString(text
))
459 /* could be empty after colour stripping and
460 * that would cause problems later */
462 sendto_one(source_p
, form_str(ERR_NOTEXTTOSEND
), me
.name
, source_p
->name
);
467 /* chanops and voiced can flood their own channel with impunity */
468 if((result
= can_send(chptr
, source_p
, NULL
)))
470 if(result
== CAN_SEND_OPV
||
471 !flood_attack_channel(p_or_n
, source_p
, chptr
, chptr
->chname
))
473 sendto_channel_flags(client_p
, ALL_MEMBERS
, source_p
, chptr
,
474 "%s %s :%s", command
, chptr
->chname
, text
);
477 else if(chptr
->mode
.mode
& MODE_OPMODERATE
&&
478 chptr
->mode
.mode
& MODE_MODERATED
&&
479 IsMember(source_p
, chptr
))
481 /* only do +z for +m channels for now, as bans/quiets
482 * aren't tested for remote clients -- jilles */
483 if(!flood_attack_channel(p_or_n
, source_p
, chptr
, chptr
->chname
))
485 sendto_channel_flags(client_p
, ONLY_CHANOPS
, source_p
, chptr
,
486 "%s %s :%s", command
, chptr
->chname
, text
);
492 sendto_one_numeric(source_p
, ERR_CANNOTSENDTOCHAN
,
493 form_str(ERR_CANNOTSENDTOCHAN
), chptr
->chname
);
500 * inputs - flag 0 if PRIVMSG 1 if NOTICE. RFC
501 * say NOTICE must not auto reply
502 * - pointer to command, "PRIVMSG" or "NOTICE"
503 * - pointer to client_p
504 * - pointer to source_p
505 * - pointer to channel
507 * - pointer to text to send
509 * side effects - message given channel either chanop or voice
512 msg_channel_flags(int p_or_n
, const char *command
, struct Client
*client_p
,
513 struct Client
*source_p
, struct Channel
*chptr
, int flags
, const char *text
)
518 if(flags
& CHFL_VOICE
)
520 type
= ONLY_CHANOPSVOICED
;
529 if(MyClient(source_p
))
531 /* idletime shouldnt be reset by notice --fl */
533 source_p
->localClient
->last
= CurrentTime
;
536 sendto_channel_flags(client_p
, type
, source_p
, chptr
, "%s %c%s :%s",
537 command
, c
, chptr
->chname
, text
);
540 #define PREV_FREE_TARGET(x) ((FREE_TARGET(x) == 0) ? 9 : FREE_TARGET(x) - 1)
541 #define PREV_TARGET(i) ((i == 0) ? i = 9 : --i)
542 #define NEXT_TARGET(i) ((i == 9) ? i = 0 : ++i)
545 expire_tgchange(void *unused
)
548 dlink_node
*ptr
, *next_ptr
;
550 DLINK_FOREACH_SAFE(ptr
, next_ptr
, tgchange_list
.head
)
554 if(target
->expiry
< CurrentTime
)
556 dlinkDelete(ptr
, &tgchange_list
);
557 patricia_remove(tgchange_tree
, target
->pnode
);
565 add_target(struct Client
*source_p
, struct Client
*target_p
)
569 /* can msg themselves or services without using any target slots */
570 if(source_p
== target_p
|| IsService(target_p
))
573 /* special condition for those who have had PRIVMSG crippled to allow them
574 * to talk to IRCops still.
576 * XXX: is this controversial?
578 if(source_p
->localClient
->target_last
> CurrentTime
&& IsOper(target_p
))
581 if(USED_TARGETS(source_p
))
583 /* hunt for an existing target */
584 for(i
= PREV_FREE_TARGET(source_p
), j
= USED_TARGETS(source_p
);
585 j
; --j
, PREV_TARGET(i
))
587 if(source_p
->localClient
->targets
[i
] == target_p
)
591 /* first message after connect, we may only start clearing
592 * slots after this message --anfl
594 if(!IsTGChange(source_p
))
596 SetTGChange(source_p
);
597 source_p
->localClient
->target_last
= CurrentTime
;
599 /* clear as many targets as we can */
600 else if((i
= (CurrentTime
- source_p
->localClient
->target_last
) / 60))
602 if(i
> USED_TARGETS(source_p
))
603 USED_TARGETS(source_p
) = 0;
605 USED_TARGETS(source_p
) -= i
;
607 source_p
->localClient
->target_last
= CurrentTime
;
609 /* cant clear any, full target list */
610 else if(USED_TARGETS(source_p
) == 10)
612 add_tgchange(source_p
->sockhost
);
616 /* no targets in use, reset their target_last so that they cant
617 * abuse a long idle to get targets back more quickly
621 source_p
->localClient
->target_last
= CurrentTime
;
622 SetTGChange(source_p
);
625 source_p
->localClient
->targets
[FREE_TARGET(source_p
)] = target_p
;
626 NEXT_TARGET(FREE_TARGET(source_p
));
627 ++USED_TARGETS(source_p
);
634 * inputs - flag 0 if PRIVMSG 1 if NOTICE. RFC
635 * say NOTICE must not auto reply
636 * - pointer to command, "PRIVMSG" or "NOTICE"
637 * - pointer to source_p source (struct Client *)
638 * - pointer to target_p target (struct Client *)
641 * side effects - message given channel either chanop or voice
644 msg_client(int p_or_n
, const char *command
,
645 struct Client
*source_p
, struct Client
*target_p
, const char *text
)
647 if(MyClient(source_p
))
649 /* reset idle time for message only if its not to self
650 * and its not a notice */
652 source_p
->localClient
->last
= CurrentTime
;
654 /* target change stuff, dont limit ctcp replies as that
655 * would allow people to start filling up random users
656 * targets just by ctcping them
658 if((p_or_n
!= NOTICE
|| *text
!= '\001') &&
659 ConfigFileEntry
.target_change
&& !IsOper(source_p
))
661 if(!add_target(source_p
, target_p
))
663 sendto_one(source_p
, form_str(ERR_TARGCHANGE
),
664 me
.name
, source_p
->name
, target_p
->name
);
669 else if(source_p
->from
== target_p
->from
)
671 sendto_realops_snomask(SNO_DEBUG
, L_ALL
,
672 "Send message to %s[%s] dropped from %s(Fake Dir)",
673 target_p
->name
, target_p
->from
->name
, source_p
->name
);
677 if(MyConnect(source_p
) && (p_or_n
!= NOTICE
) && target_p
->user
&& target_p
->user
->away
)
678 sendto_one_numeric(source_p
, RPL_AWAY
, form_str(RPL_AWAY
),
679 target_p
->name
, target_p
->user
->away
);
681 if(MyClient(target_p
))
683 /* XXX Controversial? allow opers always to send through a +g */
684 if(!IsServer(source_p
) && (IsSetCallerId(target_p
) ||
685 (IsSetRegOnlyMsg(target_p
) && !source_p
->user
->suser
[0])))
687 /* Here is the anti-flood bot/spambot code -db */
688 if(accept_message(source_p
, target_p
) || IsOper(source_p
))
690 sendto_one(target_p
, ":%s!%s@%s %s %s :%s",
693 source_p
->host
, command
, target_p
->name
, text
);
695 else if (IsSetRegOnlyMsg(target_p
) && !source_p
->user
->suser
[0])
697 if (p_or_n
!= NOTICE
)
698 sendto_one_numeric(source_p
, ERR_NONONREG
,
699 form_str(ERR_NONONREG
),
701 /* Only so opers can watch for floods */
702 (void) flood_attack_client(p_or_n
, source_p
, target_p
);
706 /* check for accept, flag recipient incoming message */
709 sendto_one_numeric(source_p
, ERR_TARGUMODEG
,
710 form_str(ERR_TARGUMODEG
),
714 if((target_p
->localClient
->last_caller_id_time
+
715 ConfigFileEntry
.caller_id_wait
) < CurrentTime
)
718 sendto_one_numeric(source_p
, RPL_TARGNOTIFY
,
719 form_str(RPL_TARGNOTIFY
),
722 sendto_one(target_p
, form_str(RPL_UMODEGMSG
),
723 me
.name
, target_p
->name
, source_p
->name
,
724 source_p
->username
, source_p
->host
);
726 target_p
->localClient
->last_caller_id_time
= CurrentTime
;
728 /* Only so opers can watch for floods */
729 (void) flood_attack_client(p_or_n
, source_p
, target_p
);
734 /* If the client is remote, we dont perform a special check for
735 * flooding.. as we wouldnt block their message anyway.. this means
736 * we dont give warnings.. we then check if theyre opered
737 * (to avoid flood warnings), lastly if theyre our client
738 * and flooding -- fl */
739 if(!MyClient(source_p
) || IsOper(source_p
) ||
740 !flood_attack_client(p_or_n
, source_p
, target_p
))
741 sendto_anywhere(target_p
, source_p
, command
, ":%s", text
);
744 else if(!MyClient(source_p
) || IsOper(source_p
) ||
745 !flood_attack_client(p_or_n
, source_p
, target_p
))
746 sendto_anywhere(target_p
, source_p
, command
, ":%s", text
);
752 * flood_attack_client
753 * inputs - flag 0 if PRIVMSG 1 if NOTICE. RFC
754 * say NOTICE must not auto reply
755 * - pointer to source Client
756 * - pointer to target Client
757 * output - 1 if target is under flood attack
758 * side effects - check for flood attack on target target_p
761 flood_attack_client(int p_or_n
, struct Client
*source_p
, struct Client
*target_p
)
765 if(GlobalSetOptions
.floodcount
&& MyConnect(target_p
) && IsClient(source_p
))
767 if((target_p
->localClient
->first_received_message_time
+ 1) < CurrentTime
)
769 delta
= CurrentTime
- target_p
->localClient
->first_received_message_time
;
770 target_p
->localClient
->received_number_of_privmsgs
-= delta
;
771 target_p
->localClient
->first_received_message_time
= CurrentTime
;
772 if(target_p
->localClient
->received_number_of_privmsgs
<= 0)
774 target_p
->localClient
->received_number_of_privmsgs
= 0;
775 target_p
->localClient
->flood_noticed
= 0;
779 if((target_p
->localClient
->received_number_of_privmsgs
>=
780 GlobalSetOptions
.floodcount
) || target_p
->localClient
->flood_noticed
)
782 if(target_p
->localClient
->flood_noticed
== 0)
784 sendto_realops_snomask(SNO_BOTS
, L_ALL
,
785 "Possible Flooder %s[%s@%s] on %s target: %s",
786 source_p
->name
, source_p
->username
,
788 source_p
->servptr
->name
, target_p
->name
);
789 target_p
->localClient
->flood_noticed
= 1;
790 /* add a bit of penalty */
791 target_p
->localClient
->received_number_of_privmsgs
+= 2;
793 if(MyClient(source_p
) && (p_or_n
!= NOTICE
))
795 ":%s NOTICE %s :*** Message to %s throttled due to flooding",
796 me
.name
, source_p
->name
, target_p
->name
);
800 target_p
->localClient
->received_number_of_privmsgs
++;
807 * flood_attack_channel
808 * inputs - flag 0 if PRIVMSG 1 if NOTICE. RFC
809 * says NOTICE must not auto reply
810 * - pointer to source Client
811 * - pointer to target channel
812 * output - 1 if target is under flood attack
813 * side effects - check for flood attack on target chptr
816 flood_attack_channel(int p_or_n
, struct Client
*source_p
, struct Channel
*chptr
, char *chname
)
820 if(GlobalSetOptions
.floodcount
&& MyClient(source_p
))
822 if((chptr
->first_received_message_time
+ 1) < CurrentTime
)
824 delta
= CurrentTime
- chptr
->first_received_message_time
;
825 chptr
->received_number_of_privmsgs
-= delta
;
826 chptr
->first_received_message_time
= CurrentTime
;
827 if(chptr
->received_number_of_privmsgs
<= 0)
829 chptr
->received_number_of_privmsgs
= 0;
830 chptr
->flood_noticed
= 0;
834 if((chptr
->received_number_of_privmsgs
>= GlobalSetOptions
.floodcount
)
835 || chptr
->flood_noticed
)
837 if(chptr
->flood_noticed
== 0)
839 sendto_realops_snomask(SNO_BOTS
, L_ALL
,
840 "Possible Flooder %s[%s@%s] on %s target: %s",
841 source_p
->name
, source_p
->username
,
843 source_p
->servptr
->name
, chptr
->chname
);
844 chptr
->flood_noticed
= 1;
846 /* Add a bit of penalty */
847 chptr
->received_number_of_privmsgs
+= 2;
849 if(MyClient(source_p
) && (p_or_n
!= NOTICE
))
851 ":%s NOTICE %s :*** Message to %s throttled due to flooding",
852 me
.name
, source_p
->name
, chptr
->chname
);
856 chptr
->received_number_of_privmsgs
++;
866 * inputs - server pointer
868 * - nick stuff to grok for opers
869 * - text to send if grok
871 * side effects - all the traditional oper type messages are parsed here.
872 * i.e. "/msg #some.host."
873 * However, syntax has been changed.
874 * previous syntax "/msg #some.host.mask"
875 * now becomes "/msg $#some.host.mask"
876 * previous syntax of: "/msg $some.server.mask" remains
877 * This disambiguates the syntax.
880 handle_special(int p_or_n
, const char *command
, struct Client
*client_p
,
881 struct Client
*source_p
, const char *nick
, const char *text
)
883 struct Client
*target_p
;
889 /* user[%host]@server addressed?
890 * NOTE: users can send to user@server, but not user%host@server
893 if((server
= strchr(nick
, '@')) != NULL
)
895 if((target_p
= find_server(source_p
, server
+ 1)) == NULL
)
897 sendto_one_numeric(source_p
, ERR_NOSUCHSERVER
,
898 form_str(ERR_NOSUCHSERVER
), server
+ 1);
904 if(!IsOper(source_p
))
906 if(strchr(nick
, '%') || (strncmp(nick
, "opers", 5) == 0))
908 sendto_one_numeric(source_p
, ERR_NOSUCHNICK
,
909 form_str(ERR_NOSUCHNICK
), nick
);
914 /* somewhere else.. */
917 sendto_one(target_p
, ":%s %s %s :%s",
918 get_id(source_p
, target_p
), command
, nick
, text
);
924 if((host
= strchr(nick
, '%')) != NULL
)
927 /* Check if someones msg'ing opers@our.server */
928 if(strcmp(nick
, "opers") == 0)
930 sendto_realops_snomask(SNO_GENERAL
, L_ALL
, "To opers: From: %s: %s",
931 source_p
->name
, text
);
936 * Look for users which match the destination host
937 * (no host == wildcard) and if one and one only is
938 * found connected to me, deliver message!
940 target_p
= find_userhost(nick
, host
, &count
);
950 sendto_anywhere(target_p
, source_p
, command
, ":%s", text
);
952 sendto_one(source_p
, form_str(ERR_TOOMANYTARGETS
),
953 get_id(&me
, source_p
), get_id(source_p
, source_p
), nick
);
958 * the following two cases allow masks in NOTICEs
961 * Armin, 8Jun90 (gruner@informatik.tu-muenchen.de)
963 if(IsOper(source_p
) && *nick
== '$')
965 if((*(nick
+ 1) == '$' || *(nick
+ 1) == '#'))
967 else if(MyOper(source_p
))
970 ":%s NOTICE %s :The command %s %s is no longer supported, please use $%s",
971 me
.name
, source_p
->name
, command
, nick
, nick
);
975 if((s
= strrchr(nick
, '.')) == NULL
)
977 sendto_one_numeric(source_p
, ERR_NOTOPLEVEL
,
978 form_str(ERR_NOTOPLEVEL
), nick
);
982 if(*s
== '.' || *s
== '*' || *s
== '?')
984 if(*s
== '*' || *s
== '?')
986 sendto_one_numeric(source_p
, ERR_WILDTOPLEVEL
,
987 form_str(ERR_WILDTOPLEVEL
), nick
);
991 sendto_match_butone(IsServer(client_p
) ? client_p
: NULL
, source_p
,
993 (*nick
== '#') ? MATCH_HOST
: MATCH_SERVER
,
994 "%s $%s :%s", command
, nick
, text
);
1000 * find_userhost - find a user@host (server or user).
1001 * inputs - user name to look for
1002 * - host name to look for
1003 * - pointer to count of number of matches found
1004 * outputs - pointer to client if found
1005 * - count is updated
1006 * side effects - none
1009 static struct Client
*
1010 find_userhost(const char *user
, const char *host
, int *count
)
1012 struct Client
*c2ptr
;
1013 struct Client
*res
= NULL
;
1014 char *u
= LOCAL_COPY(user
);
1017 if(collapse(u
) != NULL
)
1019 DLINK_FOREACH(ptr
, global_client_list
.head
)
1022 if(!MyClient(c2ptr
)) /* implies mine and an user */
1024 if((!host
|| match(host
, c2ptr
->host
)) && irccmp(u
, c2ptr
->username
) == 0)