fixed ssl.c bug when ssl backend returns IO_BLOCKED but IO engine doesn't get informe...
[ircu2.10.12-pk.git] / ircd / ircd_relay.c
1 /*
2  * IRC - Internet Relay Chat, ircd/ircd_relay.c
3  * Copyright (C) 1990 Jarkko Oikarinen and
4  *                    University of Oulu, Computing Center
5  *
6  * See file AUTHORS in IRC package for additional names of
7  * the programmers.
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 1, or (at your option)
12  * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
22  */
23 /** @file
24  * @brief Helper functions to relay various types of messages.
25  * @version $Id: ircd_relay.c 1271 2004-12-11 05:14:07Z klmitch $
26  *
27  * There are four basic types of messages, each with four subtypes.
28  *
29  * The basic types are: channel, directed, masked, and private.
30  * Channel messages are (perhaps obviously) sent directly to a
31  * channel.  Directed messages are sent to "NICK[%host]@server", but
32  * only allowed if the server is a services server (to avoid
33  * information leaks for normal clients).  Masked messages are sent to
34  * either *@*host.mask or *.server.mask.  Private messages are sent to
35  * NICK.
36  *
37  * The subtypes for each type are: client message, client notice,
38  * server message, and server notice.  Client subtypes are sent by a
39  * local user, and server subtypes are given to us by a server.
40  * Notice subtypes correspond to the NOTICE command, and message
41  * subtypes correspond to the PRIVMSG command.
42  *
43  * As a special note, directed messages do not have server subtypes,
44  * since there is no difference in handling them based on origin.
45  */
46 #include "config.h"
47
48 #include "ircd_relay.h"
49 #include "channel.h"
50 #include "client.h"
51 #include "hash.h"
52 #include "ircd.h"
53 #include "ircd_chattr.h"
54 #include "ircd_features.h"
55 #include "ircd_log.h"
56 #include "ircd_reply.h"
57 #include "ircd_string.h"
58 #include "match.h"
59 #include "msg.h"
60 #include "numeric.h"
61 #include "numnicks.h"
62 #include "s_debug.h"
63 #include "s_misc.h"
64 #include "s_user.h"
65 #include "send.h"
66
67 /* #include <assert.h> -- Now using assert in ircd_log.h */
68 #include <stdio.h>
69 #include <stdlib.h>
70 #include <string.h>
71
72 /*
73  * This file contains message relaying functions for client and server
74  * private messages and notices
75  * TODO: This file contains a lot of cut and paste code, and needs
76  * to be cleaned up a bit. The idea is to factor out the common checks
77  * but not introduce any IsOper/IsUser/MyUser/IsServer etc. stuff.
78  */
79
80 /** Relay a local user's message to a channel.
81  * Generates an error if the client cannot send to the channel.
82  * @param[in] sptr Client that originated the message.
83  * @param[in] name Name of target channel.
84  * @param[in] text %Message to relay.
85  * @param[in] ccount Count of channels we're sending the message to.
86  */
87 void relay_channel_message(struct Client* sptr, const char* name, const char* text, const int ccount)
88 {
89   struct Channel* chptr;
90   const char *c;
91   assert(0 != sptr);
92   assert(0 != name);
93   assert(0 != text);
94
95   if (0 == (chptr = FindChannel(name))) {
96     send_reply(sptr, ERR_NOSUCHCHANNEL, name);
97     return;
98   }
99
100   /* First thing we do is passing the message to the extended NOAMSG mode.
101    * It even can use the input when the message is dropped later on.
102    */
103   if(!IsXtraOp(sptr) && !HasPriv(sptr, PRIV_NOAMSG_OVERRIDE) && ext_amsg_block(sptr, chptr, text)) {
104     send_reply(sptr, ERR_CANNOTSENDTOCHAN, chptr->chname);
105     return;
106   }
107   
108   if(!IsXtraOp(sptr) && ext_noflood_block(sptr, chptr)) {
109     send_reply(sptr, ERR_CANNOTSENDTOCHAN, chptr->chname);
110     return;
111   }
112
113   /*
114    * This first: Almost never a server/service
115    */
116   if (!client_can_send_to_channel(sptr, chptr, 0)) {
117     send_reply(sptr, ERR_CANNOTSENDTOCHAN, chptr->chname);
118     return;
119   }
120   if ((chptr->mode.mode & MODE_NOPRIVMSGS) &&
121       check_target_limit(sptr, chptr, chptr->chname, 0))
122     return;
123
124   /* multi target message check */
125   if(!IsXtraOp(sptr) && !HasPriv(sptr, PRIV_NOAMSG_OVERRIDE) && (chptr->mode.mode & MODE_NOAMSGS) && (ccount > 1)) {
126     send_reply(sptr, ERR_CANNOTSENDTOCHAN, chptr->chname);
127     return;
128   }
129
130     /* Check for color/ctcp. */
131     if((chptr->mode.mode & MODE_NOCOLOUR) && !IsOverrideCC(sptr)) {
132         for(c = text; *c; c++) {
133             if(*c == 3 || *c == 27) {
134                 send_reply(sptr, ERR_CANNOTSENDTOCHAN, chptr->chname);
135                 return;
136             }
137         }
138     }
139     /* Still allow /me to function. */
140     if((chptr->mode.mode & MODE_NOCTCP) && !IsOverrideCC(sptr) && ircd_strncmp(text, "\001ACTION ", 8)) {
141         for(c = text; *c; ++c) {
142             if(*c == 1) {
143                 send_reply(sptr, ERR_CANNOTSENDTOCHAN, chptr->chname);
144                 return;
145             }
146         }
147     }
148
149   RevealDelayedJoinIfNeeded(sptr, chptr);
150   sendcmdto_channel_butone(sptr, CMD_PRIVATE, chptr, cli_from(sptr),
151                            SKIP_DEAF | SKIP_BURST, text[0], "%H :%s", chptr, text);
152 }
153
154 /** Relay a local user's notice to a channel.
155  * Silently exits if the client cannot send to the channel.
156  * @param[in] sptr Client that originated the message.
157  * @param[in] name Name of target channel.
158  * @param[in] text %Message to relay.
159  * @param[in] ccount Count of channels we're sending the message to.
160  */
161 void relay_channel_notice(struct Client* sptr, const char* name, const char* text, const int ccount)
162 {
163   struct Channel* chptr;
164   const char *c;
165   assert(0 != sptr);
166   assert(0 != name);
167   assert(0 != text);
168
169   if (0 == (chptr = FindChannel(name)))
170     return;
171
172   /* First thing we do is passing the message to the extended NOAMSG mode.
173    * It even can use the input when the message is dropped later on.
174    */
175   if(!IsXtraOp(sptr) && !HasPriv(sptr, PRIV_NOAMSG_OVERRIDE) && ext_amsg_block(sptr, chptr, text))
176     return;
177
178   if(!IsXtraOp(sptr) && ext_noflood_block(sptr, chptr)) {
179     send_reply(sptr, ERR_CANNOTSENDTOCHAN, chptr->chname);
180     return;
181   }
182
183   /* Now we check if the channel has the NONOTICE mode
184    * If NONOTICE is set only XtraOPS or ColorOverride users can send notices!
185    */
186   if((chptr->mode.mode & MODE_NONOTICE) && !IsOverrideCC(sptr) && !IsXtraOp(sptr)) {
187     send_reply(sptr, ERR_CANNOTSENDTOCHAN, chptr->chname);
188     return;
189   }
190
191   /*
192    * This first: Almost never a server/service
193    */
194   if (!client_can_send_to_channel(sptr, chptr, 0))
195     return;
196
197   if ((chptr->mode.mode & MODE_NOPRIVMSGS) &&
198       check_target_limit(sptr, chptr, chptr->chname, 0))
199     return;
200
201   /* multi target message check */
202   if(!IsXtraOp(sptr) && !HasPriv(sptr, PRIV_NOAMSG_OVERRIDE) && (chptr->mode.mode & MODE_NOAMSGS) && (ccount > 1))
203     return;
204
205     /* Check for color/ctcp. */
206     if((chptr->mode.mode & MODE_NOCOLOUR) && !IsOverrideCC(sptr)) {
207         for(c = text; *c; c++) {
208             if(*c == 3 || *c == 27) {
209                 return;
210             }
211         }
212     }
213     /* Still allow /me to function. */
214     if((chptr->mode.mode & MODE_NOCTCP) && !IsOverrideCC(sptr) && ircd_strncmp(text, "\001ACTION ", 8)) {
215         for(c = text; *c; ++c) {
216             if(*c == 1) {
217                 return;
218             }
219         }
220     }
221
222   RevealDelayedJoinIfNeeded(sptr, chptr);
223   sendcmdto_channel_butone(sptr, CMD_NOTICE, chptr, cli_from(sptr),
224                            SKIP_DEAF | SKIP_BURST, '\0', "%H :%s", chptr, text);
225 }
226
227 /** Relay a message to a channel.
228  * Generates an error if the client cannot send to the channel,
229  * or if the channel is a local channel
230  * @param[in] sptr Client that originated the message.
231  * @param[in] name Name of target channel.
232  * @param[in] text %Message to relay.
233  */
234 void server_relay_channel_message(struct Client* sptr, const char* name, const char* text)
235 {
236   struct Channel* chptr;
237   assert(0 != sptr);
238   assert(0 != name);
239   assert(0 != text);
240
241   if (IsLocalChannel(name) || 0 == (chptr = FindChannel(name))) {
242     send_reply(sptr, ERR_NOSUCHCHANNEL, name);
243     return;
244   }
245   /*
246    * This first: Almost never a server/service
247    * Servers may have channel services, need to check for it here
248    */
249   if (client_can_send_to_channel(sptr, chptr, 1) || IsChannelService(sptr)) {
250     sendcmdto_channel_butone(sptr, CMD_PRIVATE, chptr, cli_from(sptr),
251                              SKIP_DEAF | SKIP_BURST, text[0], "%H :%s", chptr, text);
252   }
253   else
254     send_reply(sptr, ERR_CANNOTSENDTOCHAN, chptr->chname);
255 }
256
257 /** Relay a notice to a channel.
258  * Generates an error if the client cannot send to the channel,
259  * or if the channel is a local channel
260  * @param[in] sptr Client that originated the message.
261  * @param[in] name Name of target channel.
262  * @param[in] text %Message to relay.
263  */
264 void server_relay_channel_notice(struct Client* sptr, const char* name, const char* text)
265 {
266   struct Channel* chptr;
267   assert(0 != sptr);
268   assert(0 != name);
269   assert(0 != text);
270
271   if (IsLocalChannel(name) || 0 == (chptr = FindChannel(name)))
272     return;
273   /*
274    * This first: Almost never a server/service
275    * Servers may have channel services, need to check for it here
276    */
277   if (client_can_send_to_channel(sptr, chptr, 1) || IsChannelService(sptr)) {
278     sendcmdto_channel_butone(sptr, CMD_NOTICE, chptr, cli_from(sptr),
279                              SKIP_DEAF | SKIP_BURST, '\0', "%H :%s", chptr, text);
280   }
281 }
282
283 /** Relay a directed message.
284  * Same as "relay_directed_message" but has some other privilege checks.
285  * Checks that the target user is on an ulined server.
286  * If this check fails, it takes \a server as an account
287  * name and checks whether \a name has the account set.
288  * If both checks fail, it prints a message to the user and returns.
289  * @param[in] sptr Client that originated the message.
290  * @param[in] name Target nickname.
291  * @param[in] server Name of target server.
292  * @param[in] text %Message to relay.
293  */
294 int relay_directed_account_server_message(struct Client* sptr, char* name, char* server, const char* text)
295 {
296   struct Client* acptr, *dest;
297   char*          host;
298   char*          serv;
299
300   assert(0 != sptr);
301   assert(0 != name);
302   assert(0 != text);
303   assert(0 != server);
304
305   serv = server + 1;
306   *server = '\0';
307   if ((host = strchr(name, '%')))
308     *host++ = '\0';
309
310   if (!(acptr = FindUser(name)) || (!EmptyString(host) && 0 != match(host, cli_user(acptr)->host)))
311   {
312     return 0;
313   }
314
315   if((dest = FindServer(serv)) == NULL || dest != cli_user(acptr)->server || !IsService(dest)) {
316     if(!IsNetServ(acptr) || !IsAccount(acptr) || 0 != ircd_strcmp(cli_user(acptr)->account, serv)) {
317       return 0;
318     }
319   }
320
321   if(dest) {
322     *server = '@';
323     if(host)
324       *--host = '%';
325     sendcmdto_one(sptr, CMD_PRIVATE, acptr, "%s :%s", name, text);
326     return 1;
327   }
328
329   if(!MyUser(acptr))
330   {
331     sendcmdto_one(sptr, CMD_PRIVATE, acptr, "%C :%s", acptr, text);
332   }
333   else
334   {
335     if (!(is_silenced(sptr, acptr)))
336       sendcmdto_one(sptr, CMD_PRIVATE, acptr, "%C :%s", acptr, text);
337   }
338
339   *server = '@';
340   if (host)
341     *--host = '%';
342
343   return 1;
344 }
345
346 /** Relay a directed message.
347  * Generates an error if the named server does not exist, if it is not
348  * a services server, or if \a name names a local user and a hostmask
349  * is specified but does not match.
350  * @param[in] sptr Client that originated the message.
351  * @param[in] name Target nickname, with optional "%hostname" suffix.
352  * @param[in] server Name of target server.
353  * @param[in] text %Message to relay.
354  */
355 void relay_directed_message(struct Client* sptr, char* name, char* server, const char* text)
356 {
357   struct Client* acptr;
358   char*          host;
359
360   assert(0 != sptr);
361   assert(0 != name);
362   assert(0 != text);
363   assert(0 != server);
364
365   if ((acptr = FindServer(server + 1)) == NULL || !IsService(acptr))
366   {
367     send_reply(sptr, ERR_NOSUCHNICK, name);
368     return;
369   }
370   /*
371    * NICK[%host]@server addressed? See if <server> is me first
372    */
373   if (!IsMe(acptr))
374   {
375     sendcmdto_one(sptr, CMD_PRIVATE, acptr, "%s :%s", name, text);
376     return;
377   }
378   /*
379    * Look for an user whose NICK is equal to <name> and then
380    * check if it's hostname matches <host> and if it's a local
381    * user.
382    */
383   *server = '\0';
384   if ((host = strchr(name, '%')))
385     *host++ = '\0';
386
387   /* As reported by Vampire-, it's possible to brute force finding users
388    * by sending a message to each server and see which one succeeded.
389    * This means we have to remove error reporting.  Sigh.  Better than
390    * removing the ability to send directed messages to client servers 
391    * Thanks for the suggestion Vampire=.  -- Isomer 2001-08-28
392    * Argh, /ping nick@server, disallow messages to non +k clients :/  I hate
393    * this. -- Isomer 2001-09-16
394    */
395   if (!(acptr = FindUser(name)) || !MyUser(acptr) ||
396       (!EmptyString(host) && 0 != match(host, cli_user(acptr)->host)) ||
397       !IsChannelService(acptr))
398   {
399     /*
400      * By this stage we might as well not bother because they will
401      * know that this server is currently linked because of the
402      * increased lag.
403      */
404     send_reply(sptr, ERR_NOSUCHNICK, name);
405     return;
406   }
407
408   *server = '@';
409   if (host)
410     *--host = '%';
411
412   if (!(is_silenced(sptr, acptr)))
413     sendcmdto_one(sptr, CMD_PRIVATE, acptr, "%s :%s", name, text);
414 }
415
416 /** Relay a directed notice.
417  * Generates an error if the named server does not exist, if it is not
418  * a services server, or if \a name names a local user and a hostmask
419  * is specified but does not match.
420  * @param[in] sptr Client that originated the message.
421  * @param[in] name Target nickname, with optional "%hostname" suffix.
422  * @param[in] server Name of target server.
423  * @param[in] text %Message to relay.
424  */
425 void relay_directed_notice(struct Client* sptr, char* name, char* server, const char* text)
426 {
427   struct Client* acptr;
428   char*          host;
429
430   assert(0 != sptr);
431   assert(0 != name);
432   assert(0 != text);
433   assert(0 != server);
434
435   if (0 == (acptr = FindServer(server + 1)))
436     return;
437   /*
438    * NICK[%host]@server addressed? See if <server> is me first
439    */
440   if (!IsMe(acptr)) {
441     sendcmdto_one(sptr, CMD_NOTICE, acptr, "%s :%s", name, text);
442     return;
443   }
444   /*
445    * Look for an user whose NICK is equal to <name> and then
446    * check if it's hostname matches <host> and if it's a local
447    * user.
448    */
449   *server = '\0';
450   if ((host = strchr(name, '%')))
451     *host++ = '\0';
452
453   if (!(acptr = FindUser(name)) || !MyUser(acptr) ||
454       (!EmptyString(host) && 0 != match(host, cli_user(acptr)->host)))
455     return;
456
457   *server = '@';
458   if (host)
459     *--host = '%';
460
461   if (!(is_silenced(sptr, acptr)))
462     sendcmdto_one(sptr, CMD_NOTICE, acptr, "%s :%s", name, text);
463 }
464
465 /** Relay a private message from a local user.
466  * Returns an error if the user does not exist or sending to him would
467  * exceed the source's free targets.  Sends an AWAY status message if
468  * the target is marked as away.
469  * @param[in] sptr Client that originated the message.
470  * @param[in] name Nickname of target user.
471  * @param[in] text %Message to relay.
472  */
473 void relay_private_message(struct Client* sptr, const char* name, const char* text)
474 {
475   struct Client* acptr;
476
477   assert(0 != sptr);
478   assert(0 != name);
479   assert(0 != text);
480
481   if (0 == (acptr = FindUser(name))) {
482     send_reply(sptr, ERR_NOSUCHNICK, name);
483     return;
484   }
485   if ((!IsChannelService(acptr) &&
486        check_target_limit(sptr, acptr, cli_name(acptr), 0)) ||
487       is_silenced(sptr, acptr))
488     return;
489
490   /*
491    * send away message if user away
492    */
493   if (cli_user(acptr) && cli_user(acptr)->away)
494     send_reply(sptr, RPL_AWAY, cli_name(acptr), cli_user(acptr)->away);
495   /*
496    * deliver the message
497    */
498   if (MyUser(acptr))
499     add_target(acptr, sptr);
500
501   sendcmdto_one(sptr, CMD_PRIVATE, acptr, "%C :%s", acptr, text);
502 }
503
504 /** Relay a private notice from a local user.
505  * Returns an error if the user does not exist or sending to him would
506  * exceed the source's free targets.  Sends an AWAY status message if
507  * the target is marked as away.
508  * @param[in] sptr Client that originated the message.
509  * @param[in] name Nickname of target user.
510  * @param[in] text %Message to relay.
511  */
512 void relay_private_notice(struct Client* sptr, const char* name, const char* text)
513 {
514   struct Client* acptr;
515   assert(0 != sptr);
516   assert(0 != name);
517   assert(0 != text);
518
519   if (0 == (acptr = FindUser(name)))
520     return;
521   if ((!IsChannelService(acptr) && 
522        check_target_limit(sptr, acptr, cli_name(acptr), 0)) ||
523       is_silenced(sptr, acptr))
524     return;
525   /*
526    * deliver the message
527    */
528   if (MyUser(acptr))
529     add_target(acptr, sptr);
530
531   sendcmdto_one(sptr, CMD_NOTICE, acptr, "%C :%s", acptr, text);
532 }
533
534 /** Relay a private message that arrived from a server.
535  * Returns an error if the user does not exist.
536  * @param[in] sptr Client that originated the message.
537  * @param[in] name Nickname of target user.
538  * @param[in] text %Message to relay.
539  */
540 void server_relay_private_message(struct Client* sptr, const char* name, const char* text)
541 {
542   struct Client* acptr;
543   assert(0 != sptr);
544   assert(0 != name);
545   assert(0 != text);
546   /*
547    * nickname addressed?
548    */
549   if (0 == (acptr = findNUser(name)) || !IsUser(acptr)) {
550     send_reply(sptr, SND_EXPLICIT | ERR_NOSUCHNICK, "* :Target left %s. "
551                "Failed to deliver: [%.20s]", feature_str(FEAT_NETWORK),
552                text);
553     return;
554   }
555   if (is_silenced(sptr, acptr))
556     return;
557
558   if (MyUser(acptr))
559     add_target(acptr, sptr);
560
561   sendcmdto_one(sptr, CMD_PRIVATE, acptr, "%C :%s", acptr, text);
562 }
563
564
565 /** Relay a private notice that arrived from a server.
566  * Returns an error if the user does not exist.
567  * @param[in] sptr Client that originated the message.
568  * @param[in] name Nickname of target user.
569  * @param[in] text %Message to relay.
570  */
571 void server_relay_private_notice(struct Client* sptr, const char* name, const char* text)
572 {
573   struct Client* acptr;
574   assert(0 != sptr);
575   assert(0 != name);
576   assert(0 != text);
577   /*
578    * nickname addressed?
579    */
580   if (0 == (acptr = findNUser(name)) || !IsUser(acptr))
581     return;
582
583   if (is_silenced(sptr, acptr))
584     return;
585
586   if (MyUser(acptr))
587     add_target(acptr, sptr);
588
589   sendcmdto_one(sptr, CMD_NOTICE, acptr, "%C :%s", acptr, text);
590 }
591
592 /** Relay a masked message from a local user.
593  * Sends an error response if there is no top-level domain label in \a
594  * mask, or if that TLD contains a wildcard.
595  * @param[in] sptr Client that originated the message.
596  * @param[in] mask Target mask for the message.
597  * @param[in] text %Message to relay.
598  */
599 void relay_masked_message(struct Client* sptr, const char* mask, const char* text)
600 {
601   const char* s;
602   int   host_mask = 0;
603
604   assert(0 != sptr);
605   assert(0 != mask);
606   assert(0 != text);
607   /*
608    * look for the last '.' in mask and scan forward
609    */
610   if (0 == (s = strrchr(mask, '.'))) {
611     send_reply(sptr, ERR_NOTOPLEVEL, mask);
612     return;
613   }
614   while (*++s) {
615     if (*s == '.' || *s == '*' || *s == '?')
616        break;
617   }
618   if (*s == '*' || *s == '?') {
619     send_reply(sptr, ERR_WILDTOPLEVEL, mask);
620     return;
621   }
622   s = mask;
623   if ('@' == *++s) {
624     host_mask = 1;
625     ++s;
626   }
627
628   sendcmdto_match_butone(sptr, CMD_PRIVATE, s,
629                          IsServer(cli_from(sptr)) ? cli_from(sptr) : 0,
630                          host_mask ? MATCH_HOST : MATCH_SERVER,
631                          "%s :%s", mask, text);
632 }
633
634 /** Relay a masked notice from a local user.
635  * Sends an error response if there is no top-level domain label in \a
636  * mask, or if that TLD contains a wildcard.
637  * @param[in] sptr Client that originated the message.
638  * @param[in] mask Target mask for the message.
639  * @param[in] text %Message to relay.
640  */
641 void relay_masked_notice(struct Client* sptr, const char* mask, const char* text)
642 {
643   const char* s;
644   int   host_mask = 0;
645
646   assert(0 != sptr);
647   assert(0 != mask);
648   assert(0 != text);
649   /*
650    * look for the last '.' in mask and scan forward
651    */
652   if (0 == (s = strrchr(mask, '.'))) {
653     send_reply(sptr, ERR_NOTOPLEVEL, mask);
654     return;
655   }
656   while (*++s) {
657     if (*s == '.' || *s == '*' || *s == '?')
658        break;
659   }
660   if (*s == '*' || *s == '?') {
661     send_reply(sptr, ERR_WILDTOPLEVEL, mask);
662     return;
663   }
664   s = mask;
665   if ('@' == *++s) {
666     host_mask = 1;
667     ++s;
668   }
669
670   sendcmdto_match_butone(sptr, CMD_NOTICE, s,
671                          IsServer(cli_from(sptr)) ? cli_from(sptr) : 0,
672                          host_mask ? MATCH_HOST : MATCH_SERVER,
673                          "%s :%s", mask, text);
674 }
675
676 /** Relay a masked message that arrived from a server.
677  * @param[in] sptr Client that originated the message.
678  * @param[in] mask Target mask for the message.
679  * @param[in] text %Message to relay.
680  */
681 void server_relay_masked_message(struct Client* sptr, const char* mask, const char* text)
682 {
683   const char* s = mask;
684   int         host_mask = 0;
685   assert(0 != sptr);
686   assert(0 != mask);
687   assert(0 != text);
688
689   if ('@' == *++s) {
690     host_mask = 1;
691     ++s;
692   }
693   sendcmdto_match_butone(sptr, CMD_PRIVATE, s,
694                          IsServer(cli_from(sptr)) ? cli_from(sptr) : 0,
695                          host_mask ? MATCH_HOST : MATCH_SERVER,
696                          "%s :%s", mask, text);
697 }
698
699 /** Relay a masked notice that arrived from a server.
700  * @param[in] sptr Client that originated the message.
701  * @param[in] mask Target mask for the message.
702  * @param[in] text %Message to relay.
703  */
704 void server_relay_masked_notice(struct Client* sptr, const char* mask, const char* text)
705 {
706   const char* s = mask;
707   int         host_mask = 0;
708   assert(0 != sptr);
709   assert(0 != mask);
710   assert(0 != text);
711
712   if ('@' == *++s) {
713     host_mask = 1;
714     ++s;
715   }
716   sendcmdto_match_butone(sptr, CMD_NOTICE, s,
717                          IsServer(cli_from(sptr)) ? cli_from(sptr) : 0,
718                          host_mask ? MATCH_HOST : MATCH_SERVER,
719                          "%s :%s", mask, text);
720 }
721