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