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