1 /*
2  *  ircd-hybrid: an advanced, lightweight Internet Relay Chat Daemon (ircd)
3  *
4  *  Copyright (c) 1997-2021 ircd-hybrid development team
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
19  *  USA
20  */
21 
22 /*! \file m_whois.c
23  * \brief Includes required functions for processing the WHOIS command.
24  * \version $Id: m_whois.c 9858 2021-01-01 04:43:42Z michael $
25  */
26 
27 #include "stdinc.h"
28 #include "list.h"
29 #include "client.h"
30 #include "client_svstag.h"
31 #include "hash.h"
32 #include "channel.h"
33 #include "channel_mode.h"
34 #include "ircd.h"
35 #include "numeric.h"
36 #include "conf.h"
37 #include "misc.h"
38 #include "server.h"
39 #include "user.h"
40 #include "send.h"
41 #include "irc_string.h"
42 #include "parse.h"
43 #include "modules.h"
44 
45 
46 enum
47 {
48   WHOIS_SHOW_NORMAL,
49   WHOIS_SHOW_PREFIXED,
50   WHOIS_SHOW_NO
51 };
52 
53 static int
whois_channel_show_type(struct Channel * channel,struct Client * source_p,struct Client * target_p)54 whois_channel_show_type(struct Channel *channel,
55                         struct Client *source_p,
56                         struct Client *target_p)
57 {
58   if (PubChannel(channel) && !HasUMode(target_p, UMODE_HIDECHANS))
59     return WHOIS_SHOW_NORMAL;
60 
61   if (source_p == target_p || member_find_link(source_p, channel))
62     return WHOIS_SHOW_NORMAL;
63 
64   if (HasUMode(source_p, UMODE_OPER))
65     return WHOIS_SHOW_PREFIXED;
66   return WHOIS_SHOW_NO;
67 }
68 
69 /* whois_person()
70  *
71  * inputs	- source_p client to report to
72  *		- target_p client to report on
73  * output	- NONE
74  * side effects	-
75  */
76 static void
whois_person(struct Client * source_p,struct Client * target_p)77 whois_person(struct Client *source_p, struct Client *target_p)
78 {
79   dlink_node *node;
80   const struct ServicesTag *svstag = NULL;
81 
82   sendto_one_numeric(source_p, &me, RPL_WHOISUSER, target_p->name,
83                      target_p->username, target_p->host,
84                      target_p->info);
85 
86   if (dlink_list_length(&target_p->channel))
87   {
88     char buf[IRCD_BUFSIZE];
89     char *bufptr = buf;
90 
91     /* :me.name 319 source_p->name target_p->name :~@#chan1 +#chan2 #chan3 ...\r\n */
92     /* 1       23456              7              89                           0 1  */
93     size_t len = strlen(target_p->name) + 11;
94 
95     if (MyConnect(source_p))
96       len += strlen(me.name) + strlen(source_p->name);
97     else
98       len += IRCD_MAX(strlen(me.name), strlen(me.id)) + IRCD_MAX(strlen(source_p->name), strlen(source_p->id));
99 
100     DLINK_FOREACH(node, target_p->channel.head)
101     {
102       const struct ChannelMember *member = node->data;
103       int show = whois_channel_show_type(member->channel, source_p, target_p);
104 
105       if (show != WHOIS_SHOW_NO)
106       {
107         if ((bufptr - buf) + member->channel->name_len + 1 + (show == WHOIS_SHOW_PREFIXED) + member_get_prefix_len(member, true) + len > sizeof(buf))
108         {
109           *(bufptr - 1) = '\0';
110           sendto_one_numeric(source_p, &me, RPL_WHOISCHANNELS, target_p->name, buf);
111           bufptr = buf;
112         }
113 
114         bufptr += snprintf(bufptr, sizeof(buf) - (bufptr - buf), "%s%s%s ",
115                            show == WHOIS_SHOW_PREFIXED ? "~" : "", member_get_prefix(member, true), member->channel->name);
116       }
117     }
118 
119     if (bufptr != buf)
120     {
121       *(bufptr - 1) = '\0';
122       sendto_one_numeric(source_p, &me, RPL_WHOISCHANNELS, target_p->name, buf);
123     }
124   }
125 
126   if ((ConfigServerHide.hide_servers || IsHidden(target_p->servptr)) &&
127       !(HasUMode(source_p, UMODE_OPER) || source_p == target_p))
128     sendto_one_numeric(source_p, &me, RPL_WHOISSERVER, target_p->name,
129                        ConfigServerHide.hidden_name,
130                        ConfigServerInfo.network_description);
131   else
132     sendto_one_numeric(source_p, &me, RPL_WHOISSERVER, target_p->name,
133                        target_p->servptr->name, target_p->servptr->info);
134 
135   if (HasUMode(target_p, UMODE_REGISTERED))
136     sendto_one_numeric(source_p, &me, RPL_WHOISREGNICK, target_p->name);
137 
138   if (strcmp(target_p->account, "*"))
139     sendto_one_numeric(source_p, &me, RPL_WHOISACCOUNT, target_p->name,
140                        target_p->account, "is");
141 
142   if (target_p->away[0])
143     sendto_one_numeric(source_p, &me, RPL_AWAY, target_p->name,
144                        target_p->away);
145 
146   if (HasUMode(target_p, UMODE_CALLERID | UMODE_SOFTCALLERID))
147   {
148     bool callerid = HasUMode(target_p, UMODE_CALLERID) != 0;
149 
150     sendto_one_numeric(source_p, &me, RPL_TARGUMODEG, target_p->name,
151                        callerid ? "+g" : "+G",
152                        callerid ? "server side ignore" :
153                                   "server side ignore with the exception of common channels");
154   }
155 
156   if (HasUMode(target_p, UMODE_OPER) || HasFlag(target_p, FLAGS_SERVICE))
157   {
158     if (!HasUMode(target_p, UMODE_HIDDEN) || HasUMode(source_p, UMODE_OPER))
159     {
160       if (target_p->svstags.head)
161         svstag = target_p->svstags.head->data;
162 
163       if (svstag == NULL || svstag->numeric != RPL_WHOISOPERATOR)
164       {
165         const char *text;
166         if (HasFlag(target_p, FLAGS_SERVICE))
167           text = "is a Network Service";
168         else if (HasUMode(target_p, UMODE_ADMIN))
169           text = "is a Server Administrator";
170         else  /* HasUMode(target_p, UMODE_OPER) == true */
171           text = "is an IRC Operator";
172         sendto_one_numeric(source_p, &me, RPL_WHOISOPERATOR, target_p->name, text);
173       }
174     }
175   }
176 
177   DLINK_FOREACH(node, target_p->svstags.head)
178   {
179     svstag = node->data;
180 
181     if (svstag->numeric == RPL_WHOISOPERATOR)
182       if (HasUMode(target_p, UMODE_HIDDEN) && !HasUMode(source_p, UMODE_OPER))
183         continue;
184 
185     if (svstag->umodes == 0 || HasUMode(source_p, svstag->umodes))
186       sendto_one_numeric(source_p, &me, svstag->numeric | SND_EXPLICIT, "%s :%s",
187                          target_p->name, svstag->tag);
188   }
189 
190   if (HasUMode(target_p, UMODE_BOT))
191     sendto_one_numeric(source_p, &me, RPL_WHOISBOT, target_p->name);
192 
193   if (HasUMode(target_p, UMODE_WEBIRC))
194     sendto_one_numeric(source_p, &me, RPL_WHOISTEXT, target_p->name,
195                        "User connected using a webirc gateway");
196 
197   if (HasUMode(source_p, UMODE_OPER) || source_p == target_p)
198   {
199     char buf[UMODE_MAX_STR];
200     char *m = buf;
201 
202     *m++ = '+';
203     for (const struct user_modes *tab = umode_tab; tab->c; ++tab)
204       if (HasUMode(target_p, tab->flag))
205         *m++ = tab->c;
206     *m = '\0';
207 
208     sendto_one_numeric(source_p, &me, RPL_WHOISMODES, target_p->name, buf);
209   }
210 
211   if (HasUMode(source_p, UMODE_OPER) || source_p == target_p)
212     sendto_one_numeric(source_p, &me, RPL_WHOISACTUALLY, target_p->name,
213                        target_p->username, target_p->realhost,
214                        target_p->sockhost);
215 
216   if (HasUMode(target_p, UMODE_SECURE))
217     sendto_one_numeric(source_p, &me, RPL_WHOISSECURE, target_p->name);
218 
219   if (!EmptyString(target_p->tls_certfp))
220     if (HasUMode(source_p, UMODE_OPER) || source_p == target_p)
221       sendto_one_numeric(source_p, &me, RPL_WHOISCERTFP, target_p->name, target_p->tls_certfp);
222 
223   if (MyConnect(target_p))
224     if (!HasUMode(target_p, UMODE_HIDEIDLE) || HasUMode(source_p, UMODE_OPER) ||
225         source_p == target_p)
226       sendto_one_numeric(source_p, &me, RPL_WHOISIDLE, target_p->name,
227                          client_get_idle_time(source_p, target_p),
228                          target_p->connection->created_real);
229 
230   if (HasUMode(target_p, UMODE_SPY) && source_p != target_p)
231     sendto_one_notice(target_p, &me, ":*** Notice -- %s (%s@%s) [%s] is doing a /whois on you",
232                       source_p->name, source_p->username, source_p->host, source_p->servptr->name);
233 }
234 
235 /* do_whois()
236  *
237  * inputs       - pointer to /whois source
238  *              - number of parameters
239  *              - pointer to parameters array
240  * output       - pointer to void
241  * side effects - Does whois
242  */
243 static void
do_whois(struct Client * source_p,const char * name)244 do_whois(struct Client *source_p, const char *name)
245 {
246   struct Client *target_p;
247 
248   if ((target_p = hash_find_client(name)) && IsClient(target_p))
249     whois_person(source_p, target_p);
250   else
251     sendto_one_numeric(source_p, &me, ERR_NOSUCHNICK, name);
252 
253   sendto_one_numeric(source_p, &me, RPL_ENDOFWHOIS, name);
254 }
255 
256 /*! \brief WHOIS command handler
257  *
258  * \param source_p Pointer to allocated Client struct from which the message
259  *                 originally comes from.  This can be a local or remote client.
260  * \param parc     Integer holding the number of supplied arguments.
261  * \param parv     Argument vector where parv[0] .. parv[parc-1] are non-NULL
262  *                 pointers.
263  * \note Valid arguments for this command are:
264  *      - parv[0] = command
265  *      - parv[1] = nickname/servername
266  *      - parv[2] = nickname
267  */
268 static void
m_whois(struct Client * source_p,int parc,char * parv[])269 m_whois(struct Client *source_p, int parc, char *parv[])
270 {
271   static uintmax_t last_used = 0;
272 
273   if (EmptyString(parv[1]))
274   {
275     sendto_one_numeric(source_p, &me, ERR_NONICKNAMEGIVEN);
276     return;
277   }
278 
279   if (!EmptyString(parv[2]))
280   {
281     /* seeing as this is going across servers, we should limit it */
282     if ((last_used + ConfigGeneral.pace_wait_simple) > event_base->time.sec_monotonic)
283     {
284       sendto_one_numeric(source_p, &me, RPL_LOAD2HI, "WHOIS");
285       return;
286     }
287 
288     last_used = event_base->time.sec_monotonic;
289 
290     /*
291      * if we have serverhide enabled, they can either ask the clients
292      * server, or our server.. I don't see why they would need to ask
293      * anything else for info about the client.. --fl_
294      */
295     if (ConfigServerHide.disable_remote_commands)
296       parv[1] = parv[2];
297 
298     if (server_hunt(source_p, ":%s WHOIS %s :%s", 1, parv)->ret != HUNTED_ISME)
299       return;
300 
301     parv[1] = parv[2];
302   }
303 
304   do_whois(source_p, parv[1]);
305 }
306 
307 /*! \brief WHOIS command handler
308  *
309  * \param source_p Pointer to allocated Client struct from which the message
310  *                 originally comes from.  This can be a local or remote client.
311  * \param parc     Integer holding the number of supplied arguments.
312  * \param parv     Argument vector where parv[0] .. parv[parc-1] are non-NULL
313  *                 pointers.
314  * \note Valid arguments for this command are:
315  *      - parv[0] = command
316  *      - parv[1] = nickname/servername
317  *      - parv[2] = nickname
318  */
319 static void
mo_whois(struct Client * source_p,int parc,char * parv[])320 mo_whois(struct Client *source_p, int parc, char *parv[])
321 {
322   if (EmptyString(parv[1]))
323   {
324     sendto_one_numeric(source_p, &me, ERR_NONICKNAMEGIVEN);
325     return;
326   }
327 
328   if (!EmptyString(parv[2]))
329   {
330     if (server_hunt(source_p, ":%s WHOIS %s :%s", 1, parv)->ret != HUNTED_ISME)
331       return;
332 
333     parv[1] = parv[2];
334   }
335 
336   do_whois(source_p, parv[1]);
337 }
338 
339 static struct Message whois_msgtab =
340 {
341   .cmd = "WHOIS",
342   .handlers[UNREGISTERED_HANDLER] = { .handler = m_unregistered },
343   .handlers[CLIENT_HANDLER] = { .handler = m_whois },
344   .handlers[SERVER_HANDLER] = { .handler = mo_whois },
345   .handlers[ENCAP_HANDLER] = { .handler = m_ignore },
346   .handlers[OPER_HANDLER] = { .handler = mo_whois }
347 };
348 
349 static void
module_init(void)350 module_init(void)
351 {
352   mod_add_cmd(&whois_msgtab);
353 }
354 
355 static void
module_exit(void)356 module_exit(void)
357 {
358   mod_del_cmd(&whois_msgtab);
359 }
360 
361 struct module module_entry =
362 {
363   .version = "$Revision: 9858 $",
364   .modinit = module_init,
365   .modexit = module_exit,
366 };
367