Attempt to fix SF bug #2568366 (sending JOIN before hitting a target limit).
[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$
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  */
86 void relay_channel_message(struct Client* sptr, const char* name, const char* text)
87 {
88   struct Channel* chptr;
89   assert(0 != sptr);
90   assert(0 != name);
91   assert(0 != text);
92
93   if (0 == (chptr = FindChannel(name))) {
94     send_reply(sptr, ERR_NOSUCHCHANNEL, name);
95     return;
96   }
97   /*
98    * This first: Almost never a server/service
99    */
100   if (!client_can_send_to_channel(sptr, chptr, 0)) {
101     send_reply(sptr, ERR_CANNOTSENDTOCHAN, chptr->chname);
102     return;
103   }
104   if ((chptr->mode.mode & MODE_NOPRIVMSGS) &&
105       check_target_limit(sptr, chptr, chptr->chname, 0))
106     return;
107
108   RevealDelayedJoinIfNeeded(sptr, chptr);
109   sendcmdto_channel_butone(sptr, CMD_PRIVATE, chptr, cli_from(sptr),
110                            SKIP_DEAF | SKIP_BURST, "%H :%s", chptr, text);
111 }
112
113 /** Relay a local user's notice to a channel.
114  * Silently exits if the client cannot send to the channel.
115  * @param[in] sptr Client that originated the message.
116  * @param[in] name Name of target channel.
117  * @param[in] text %Message to relay.
118  */
119 void relay_channel_notice(struct Client* sptr, const char* name, const char* text)
120 {
121   struct Channel* chptr;
122   assert(0 != sptr);
123   assert(0 != name);
124   assert(0 != text);
125
126   if (0 == (chptr = FindChannel(name)))
127     return;
128   /*
129    * This first: Almost never a server/service
130    */
131   if (!client_can_send_to_channel(sptr, chptr, 0))
132     return;
133
134   if ((chptr->mode.mode & MODE_NOPRIVMSGS) &&
135       check_target_limit(sptr, chptr, chptr->chname, 0))
136     return;
137
138   RevealDelayedJoinIfNeeded(sptr, chptr);
139   sendcmdto_channel_butone(sptr, CMD_NOTICE, chptr, cli_from(sptr),
140                            SKIP_DEAF | SKIP_BURST, "%H :%s", chptr, text);
141 }
142
143 /** Relay a message to a channel.
144  * Generates an error if the client cannot send to the channel,
145  * or if the channel is a local channel
146  * @param[in] sptr Client that originated the message.
147  * @param[in] name Name of target channel.
148  * @param[in] text %Message to relay.
149  */
150 void server_relay_channel_message(struct Client* sptr, const char* name, const char* text)
151 {
152   struct Channel* chptr;
153   assert(0 != sptr);
154   assert(0 != name);
155   assert(0 != text);
156
157   if (IsLocalChannel(name) || 0 == (chptr = FindChannel(name))) {
158     send_reply(sptr, ERR_NOSUCHCHANNEL, name);
159     return;
160   }
161   /*
162    * This first: Almost never a server/service
163    * Servers may have channel services, need to check for it here
164    */
165   if (client_can_send_to_channel(sptr, chptr, 1) || IsChannelService(sptr)) {
166     sendcmdto_channel_butone(sptr, CMD_PRIVATE, chptr, cli_from(sptr),
167                              SKIP_DEAF | SKIP_BURST, "%H :%s", chptr, text);
168   }
169   else
170     send_reply(sptr, ERR_CANNOTSENDTOCHAN, chptr->chname);
171 }
172
173 /** Relay a notice to a channel.
174  * Generates an error if the client cannot send to the channel,
175  * or if the channel is a local channel
176  * @param[in] sptr Client that originated the message.
177  * @param[in] name Name of target channel.
178  * @param[in] text %Message to relay.
179  */
180 void server_relay_channel_notice(struct Client* sptr, const char* name, const char* text)
181 {
182   struct Channel* chptr;
183   assert(0 != sptr);
184   assert(0 != name);
185   assert(0 != text);
186
187   if (IsLocalChannel(name) || 0 == (chptr = FindChannel(name)))
188     return;
189   /*
190    * This first: Almost never a server/service
191    * Servers may have channel services, need to check for it here
192    */
193   if (client_can_send_to_channel(sptr, chptr, 1) || IsChannelService(sptr)) {
194     sendcmdto_channel_butone(sptr, CMD_NOTICE, chptr, cli_from(sptr),
195                              SKIP_DEAF | SKIP_BURST, "%H :%s", chptr, text);
196   }
197 }
198
199 /** Relay a directed message.
200  * Generates an error if the named server does not exist, if it is not
201  * a services server, or if \a name names a local user and a hostmask
202  * is specified but does not match.
203  * @param[in] sptr Client that originated the message.
204  * @param[in] name Target nickname, with optional "%hostname" suffix.
205  * @param[in] server Name of target server.
206  * @param[in] text %Message to relay.
207  */
208 void relay_directed_message(struct Client* sptr, char* name, char* server, const char* text)
209 {
210   struct Client* acptr;
211   char*          host;
212
213   assert(0 != sptr);
214   assert(0 != name);
215   assert(0 != text);
216   assert(0 != server);
217
218   if ((acptr = FindServer(server + 1)) == NULL || !IsService(acptr))
219   {
220     send_reply(sptr, ERR_NOSUCHNICK, name);
221     return;
222   }
223   /*
224    * NICK[%host]@server addressed? See if <server> is me first
225    */
226   if (!IsMe(acptr))
227   {
228     sendcmdto_one(sptr, CMD_PRIVATE, acptr, "%s :%s", name, text);
229     return;
230   }
231   /*
232    * Look for an user whose NICK is equal to <name> and then
233    * check if it's hostname matches <host> and if it's a local
234    * user.
235    */
236   *server = '\0';
237   if ((host = strchr(name, '%')))
238     *host++ = '\0';
239
240   /* As reported by Vampire-, it's possible to brute force finding users
241    * by sending a message to each server and see which one succeeded.
242    * This means we have to remove error reporting.  Sigh.  Better than
243    * removing the ability to send directed messages to client servers 
244    * Thanks for the suggestion Vampire=.  -- Isomer 2001-08-28
245    * Argh, /ping nick@server, disallow messages to non +k clients :/  I hate
246    * this. -- Isomer 2001-09-16
247    */
248   if (!(acptr = FindUser(name)) || !MyUser(acptr) ||
249       (!EmptyString(host) && 0 != match(host, cli_user(acptr)->host)) ||
250       !IsChannelService(acptr))
251   {
252     /*
253      * By this stage we might as well not bother because they will
254      * know that this server is currently linked because of the
255      * increased lag.
256      */
257     send_reply(sptr, ERR_NOSUCHNICK, name);
258     return;
259   }
260
261   *server = '@';
262   if (host)
263     *--host = '%';
264
265   if (!(is_silenced(sptr, acptr)))
266     sendcmdto_one(sptr, CMD_PRIVATE, acptr, "%s :%s", name, text);
267 }
268
269 /** Relay a directed notice.
270  * Generates an error if the named server does not exist, if it is not
271  * a services server, or if \a name names a local user and a hostmask
272  * is specified but does not match.
273  * @param[in] sptr Client that originated the message.
274  * @param[in] name Target nickname, with optional "%hostname" suffix.
275  * @param[in] server Name of target server.
276  * @param[in] text %Message to relay.
277  */
278 void relay_directed_notice(struct Client* sptr, char* name, char* server, const char* text)
279 {
280   struct Client* acptr;
281   char*          host;
282
283   assert(0 != sptr);
284   assert(0 != name);
285   assert(0 != text);
286   assert(0 != server);
287
288   if (0 == (acptr = FindServer(server + 1)))
289     return;
290   /*
291    * NICK[%host]@server addressed? See if <server> is me first
292    */
293   if (!IsMe(acptr)) {
294     sendcmdto_one(sptr, CMD_NOTICE, acptr, "%s :%s", name, text);
295     return;
296   }
297   /*
298    * Look for an user whose NICK is equal to <name> and then
299    * check if it's hostname matches <host> and if it's a local
300    * user.
301    */
302   *server = '\0';
303   if ((host = strchr(name, '%')))
304     *host++ = '\0';
305
306   if (!(acptr = FindUser(name)) || !MyUser(acptr) ||
307       (!EmptyString(host) && 0 != match(host, cli_user(acptr)->host)))
308     return;
309
310   *server = '@';
311   if (host)
312     *--host = '%';
313
314   if (!(is_silenced(sptr, acptr)))
315     sendcmdto_one(sptr, CMD_NOTICE, acptr, "%s :%s", name, text);
316 }
317
318 /** Relay a private message from a local user.
319  * Returns an error if the user does not exist or sending to him would
320  * exceed the source's free targets.  Sends an AWAY status message if
321  * the target is marked as away.
322  * @param[in] sptr Client that originated the message.
323  * @param[in] name Nickname of target user.
324  * @param[in] text %Message to relay.
325  */
326 void relay_private_message(struct Client* sptr, const char* name, const char* text)
327 {
328   struct Client* acptr;
329
330   assert(0 != sptr);
331   assert(0 != name);
332   assert(0 != text);
333
334   if (0 == (acptr = FindUser(name))) {
335     send_reply(sptr, ERR_NOSUCHNICK, name);
336     return;
337   }
338   if ((!IsChannelService(acptr) &&
339        check_target_limit(sptr, acptr, cli_name(acptr), 0)) ||
340       is_silenced(sptr, acptr))
341     return;
342
343   /*
344    * send away message if user away
345    */
346   if (cli_user(acptr) && cli_user(acptr)->away)
347     send_reply(sptr, RPL_AWAY, cli_name(acptr), cli_user(acptr)->away);
348   /*
349    * deliver the message
350    */
351   if (MyUser(acptr))
352     add_target(acptr, sptr);
353
354   sendcmdto_one(sptr, CMD_PRIVATE, acptr, "%C :%s", acptr, text);
355 }
356
357 /** Relay a private notice from a local user.
358  * Returns an error if the user does not exist or sending to him would
359  * exceed the source's free targets.  Sends an AWAY status message if
360  * the target is marked as away.
361  * @param[in] sptr Client that originated the message.
362  * @param[in] name Nickname of target user.
363  * @param[in] text %Message to relay.
364  */
365 void relay_private_notice(struct Client* sptr, const char* name, const char* text)
366 {
367   struct Client* acptr;
368   assert(0 != sptr);
369   assert(0 != name);
370   assert(0 != text);
371
372   if (0 == (acptr = FindUser(name)))
373     return;
374   if ((!IsChannelService(acptr) && 
375        check_target_limit(sptr, acptr, cli_name(acptr), 0)) ||
376       is_silenced(sptr, acptr))
377     return;
378   /*
379    * deliver the message
380    */
381   if (MyUser(acptr))
382     add_target(acptr, sptr);
383
384   sendcmdto_one(sptr, CMD_NOTICE, acptr, "%C :%s", acptr, text);
385 }
386
387 /** Relay a private message that arrived from a server.
388  * Returns an error if the user does not exist.
389  * @param[in] sptr Client that originated the message.
390  * @param[in] name Nickname of target user.
391  * @param[in] text %Message to relay.
392  */
393 void server_relay_private_message(struct Client* sptr, const char* name, const char* text)
394 {
395   struct Client* acptr;
396   assert(0 != sptr);
397   assert(0 != name);
398   assert(0 != text);
399   /*
400    * nickname addressed?
401    */
402   if (0 == (acptr = findNUser(name)) || !IsUser(acptr)) {
403     send_reply(sptr, SND_EXPLICIT | ERR_NOSUCHNICK, "* :Target left %s. "
404                "Failed to deliver: [%.20s]", feature_str(FEAT_NETWORK),
405                text);
406     return;
407   }
408   if (is_silenced(sptr, acptr))
409     return;
410
411   if (MyUser(acptr))
412     add_target(acptr, sptr);
413
414   sendcmdto_one(sptr, CMD_PRIVATE, acptr, "%C :%s", acptr, text);
415 }
416
417
418 /** Relay a private notice that arrived from a server.
419  * Returns an error if the user does not exist.
420  * @param[in] sptr Client that originated the message.
421  * @param[in] name Nickname of target user.
422  * @param[in] text %Message to relay.
423  */
424 void server_relay_private_notice(struct Client* sptr, const char* name, const char* text)
425 {
426   struct Client* acptr;
427   assert(0 != sptr);
428   assert(0 != name);
429   assert(0 != text);
430   /*
431    * nickname addressed?
432    */
433   if (0 == (acptr = findNUser(name)) || !IsUser(acptr))
434     return;
435
436   if (is_silenced(sptr, acptr))
437     return;
438
439   if (MyUser(acptr))
440     add_target(acptr, sptr);
441
442   sendcmdto_one(sptr, CMD_NOTICE, acptr, "%C :%s", acptr, text);
443 }
444
445 /** Relay a masked message from a local user.
446  * Sends an error response if there is no top-level domain label in \a
447  * mask, or if that TLD contains a wildcard.
448  * @param[in] sptr Client that originated the message.
449  * @param[in] mask Target mask for the message.
450  * @param[in] text %Message to relay.
451  */
452 void relay_masked_message(struct Client* sptr, const char* mask, const char* text)
453 {
454   const char* s;
455   int   host_mask = 0;
456
457   assert(0 != sptr);
458   assert(0 != mask);
459   assert(0 != text);
460   /*
461    * look for the last '.' in mask and scan forward
462    */
463   if (0 == (s = strrchr(mask, '.'))) {
464     send_reply(sptr, ERR_NOTOPLEVEL, mask);
465     return;
466   }
467   while (*++s) {
468     if (*s == '.' || *s == '*' || *s == '?')
469        break;
470   }
471   if (*s == '*' || *s == '?') {
472     send_reply(sptr, ERR_WILDTOPLEVEL, mask);
473     return;
474   }
475   s = mask;
476   if ('@' == *++s) {
477     host_mask = 1;
478     ++s;
479   }
480
481   sendcmdto_match_butone(sptr, CMD_PRIVATE, s,
482                          IsServer(cli_from(sptr)) ? cli_from(sptr) : 0,
483                          host_mask ? MATCH_HOST : MATCH_SERVER,
484                          "%s :%s", mask, text);
485 }
486
487 /** Relay a masked notice from a local user.
488  * Sends an error response if there is no top-level domain label in \a
489  * mask, or if that TLD contains a wildcard.
490  * @param[in] sptr Client that originated the message.
491  * @param[in] mask Target mask for the message.
492  * @param[in] text %Message to relay.
493  */
494 void relay_masked_notice(struct Client* sptr, const char* mask, const char* text)
495 {
496   const char* s;
497   int   host_mask = 0;
498
499   assert(0 != sptr);
500   assert(0 != mask);
501   assert(0 != text);
502   /*
503    * look for the last '.' in mask and scan forward
504    */
505   if (0 == (s = strrchr(mask, '.'))) {
506     send_reply(sptr, ERR_NOTOPLEVEL, mask);
507     return;
508   }
509   while (*++s) {
510     if (*s == '.' || *s == '*' || *s == '?')
511        break;
512   }
513   if (*s == '*' || *s == '?') {
514     send_reply(sptr, ERR_WILDTOPLEVEL, mask);
515     return;
516   }
517   s = mask;
518   if ('@' == *++s) {
519     host_mask = 1;
520     ++s;
521   }
522
523   sendcmdto_match_butone(sptr, CMD_NOTICE, s,
524                          IsServer(cli_from(sptr)) ? cli_from(sptr) : 0,
525                          host_mask ? MATCH_HOST : MATCH_SERVER,
526                          "%s :%s", mask, text);
527 }
528
529 /** Relay a masked message that arrived from a server.
530  * @param[in] sptr Client that originated the message.
531  * @param[in] mask Target mask for the message.
532  * @param[in] text %Message to relay.
533  */
534 void server_relay_masked_message(struct Client* sptr, const char* mask, const char* text)
535 {
536   const char* s = mask;
537   int         host_mask = 0;
538   assert(0 != sptr);
539   assert(0 != mask);
540   assert(0 != text);
541
542   if ('@' == *++s) {
543     host_mask = 1;
544     ++s;
545   }
546   sendcmdto_match_butone(sptr, CMD_PRIVATE, s,
547                          IsServer(cli_from(sptr)) ? cli_from(sptr) : 0,
548                          host_mask ? MATCH_HOST : MATCH_SERVER,
549                          "%s :%s", mask, text);
550 }
551
552 /** Relay a masked notice that arrived from a server.
553  * @param[in] sptr Client that originated the message.
554  * @param[in] mask Target mask for the message.
555  * @param[in] text %Message to relay.
556  */
557 void server_relay_masked_notice(struct Client* sptr, const char* mask, const char* text)
558 {
559   const char* s = mask;
560   int         host_mask = 0;
561   assert(0 != sptr);
562   assert(0 != mask);
563   assert(0 != text);
564
565   if ('@' == *++s) {
566     host_mask = 1;
567     ++s;
568   }
569   sendcmdto_match_butone(sptr, CMD_NOTICE, s,
570                          IsServer(cli_from(sptr)) ? cli_from(sptr) : 0,
571                          host_mask ? MATCH_HOST : MATCH_SERVER,
572                          "%s :%s", mask, text);
573 }
574