]> arthur.barton.de Git - ngircd-alex.git/blob - src/ngircd/irc-info.c
Handle services in IRC_WHOIS_SendReply()
[ngircd-alex.git] / src / ngircd / irc-info.c
1 /*
2  * ngIRCd -- The Next Generation IRC Daemon
3  * Copyright (c)2001-2013 Alexander Barton (alex@barton.de) and Contributors.
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  * Please read the file COPYING, README and AUTHORS for more information.
10  */
11
12 #include "portab.h"
13
14 /**
15  * @file
16  * IRC info commands
17  */
18
19 #include "imp.h"
20 #include <assert.h>
21 #include <errno.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <strings.h>
26
27 #include "ngircd.h"
28 #include "conn-func.h"
29 #include "conn-zip.h"
30 #include "channel.h"
31 #include "class.h"
32 #include "conf.h"
33 #include "defines.h"
34 #include "lists.h"
35 #include "log.h"
36 #include "messages.h"
37 #include "match.h"
38 #include "tool.h"
39 #include "parse.h"
40 #include "irc.h"
41 #include "irc-macros.h"
42 #include "irc-write.h"
43 #include "client-cap.h"
44
45 #include "exp.h"
46 #include "irc-info.h"
47
48 /* Local functions */
49
50 static unsigned int
51 t_diff(time_t *t, const time_t d)
52 {
53         time_t diff, remain;
54
55         diff = *t / d;
56         remain = diff * d;
57         *t -= remain;
58
59         return (unsigned int)diff;
60 }
61
62 static unsigned int
63 uptime_days(time_t *now)
64 {
65         return t_diff(now, 60 * 60 * 24);
66 }
67
68 static unsigned int
69 uptime_hrs(time_t *now)
70 {
71         return t_diff(now, 60 * 60);
72 }
73
74 static unsigned int
75 uptime_mins(time_t *now)
76 {
77         return t_diff(now, 60);
78 }
79
80 static bool
81 write_whoreply(CLIENT *Client, CLIENT *c, const char *channelname, const char *flags)
82 {
83         return IRC_WriteStrClient(Client, RPL_WHOREPLY_MSG, Client_ID(Client),
84                                   channelname, Client_User(c),
85                                   Client_HostnameDisplayed(c),
86                                   Client_ID(Client_Introducer(c)), Client_ID(c),
87                                   flags, Client_Hops(c), Client_Info(c));
88 }
89
90 static const char *
91 who_flags_status(const char *client_modes)
92 {
93         if (strchr(client_modes, 'a'))
94                 return "G"; /* away */
95         return "H";
96 }
97
98 /**
99  * Return channel user mode prefix(es).
100  *
101  * @param Client The client requesting the mode prefixes.
102  * @param chan_user_modes String with channel user modes.
103  * @param str String buffer to which the prefix(es) will be appended.
104  * @param len Size of "str" buffer.
105  * @return Pointer to "str".
106  */
107 static char *
108 who_flags_qualifier(CLIENT *Client, const char *chan_user_modes,
109                     char *str, size_t len)
110 {
111         assert(Client != NULL);
112
113         if (Client_Cap(Client) & CLIENT_CAP_MULTI_PREFIX) {
114                 if (strchr(chan_user_modes, 'q'))
115                         strlcat(str, "~", len);
116                 if (strchr(chan_user_modes, 'a'))
117                         strlcat(str, "&", len);
118                 if (strchr(chan_user_modes, 'o'))
119                         strlcat(str, "@", len);
120                 if (strchr(chan_user_modes, 'h'))
121                         strlcat(str, "%", len);
122                 if (strchr(chan_user_modes, 'v'))
123                         strlcat(str, "+", len);
124
125                 return str;
126         }
127
128         if (strchr(chan_user_modes, 'q'))
129                 strlcat(str, "~", len);
130         else if (strchr(chan_user_modes, 'a'))
131                 strlcat(str, "&", len);
132         else if (strchr(chan_user_modes, 'o'))
133                 strlcat(str, "@", len);
134         else if (strchr(chan_user_modes, 'h'))
135                 strlcat(str, "%", len);
136         else if (strchr(chan_user_modes, 'v'))
137                 strlcat(str, "+", len);
138
139         return str;
140 }
141
142 /**
143  * Send WHO reply for a "channel target" ("WHO #channel").
144  *
145  * @param Client Client requesting the information.
146  * @param Chan Channel being requested.
147  * @param OnlyOps Only display IRC operators.
148  * @return CONNECTED or DISCONNECTED.
149  */
150 static bool
151 IRC_WHO_Channel(CLIENT *Client, CHANNEL *Chan, bool OnlyOps)
152 {
153         bool is_visible, is_member, is_ircop;
154         CL2CHAN *cl2chan;
155         const char *client_modes;
156         char flags[10];
157         CLIENT *c;
158         int count = 0;
159
160         assert( Client != NULL );
161         assert( Chan != NULL );
162
163         IRC_SetPenalty(Client, 1);
164
165         is_member = Channel_IsMemberOf(Chan, Client);
166
167         /* Secret channel? */
168         if (!is_member && strchr(Channel_Modes(Chan), 's'))
169                 return IRC_WriteStrClient(Client, RPL_ENDOFWHO_MSG,
170                                           Client_ID(Client), Channel_Name(Chan));
171
172         cl2chan = Channel_FirstMember(Chan);
173         for (; cl2chan ; cl2chan = Channel_NextMember(Chan, cl2chan)) {
174                 c = Channel_GetClient(cl2chan);
175
176                 client_modes = Client_Modes(c);
177                 is_ircop = strchr(client_modes, 'o') != NULL;
178                 if (OnlyOps && !is_ircop)
179                         continue;
180
181                 is_visible = strchr(client_modes, 'i') == NULL;
182                 if (is_member || is_visible) {
183                         strlcpy(flags, who_flags_status(client_modes),
184                                 sizeof(flags));
185                         if (is_ircop)
186                                 strlcat(flags, "*", sizeof(flags));
187
188                         who_flags_qualifier(Client, Channel_UserModes(Chan, c),
189                                             flags, sizeof(flags));
190
191                         if (!write_whoreply(Client, c, Channel_Name(Chan),
192                                             flags))
193                                 return DISCONNECTED;
194                         count++;
195                 }
196         }
197
198         /* If there are a lot of clients, augment penalty a bit */
199         if (count > MAX_RPL_WHO)
200                 IRC_SetPenalty(Client, 1);
201
202         return IRC_WriteStrClient(Client, RPL_ENDOFWHO_MSG, Client_ID(Client),
203                                   Channel_Name(Chan));
204 }
205
206 /**
207  * Send WHO reply for a "mask target" ("WHO m*sk").
208  *
209  * @param Client Client requesting the information.
210  * @param Mask Mask being requested or NULL for "all" clients.
211  * @param OnlyOps Only display IRC operators.
212  * @return CONNECTED or DISCONNECTED.
213  */
214 static bool
215 IRC_WHO_Mask(CLIENT *Client, char *Mask, bool OnlyOps)
216 {
217         CLIENT *c;
218         CL2CHAN *cl2chan;
219         CHANNEL *chan;
220         bool client_match, is_visible;
221         char flags[4];
222         int count = 0;
223
224         assert (Client != NULL);
225
226         if (Mask)
227                 ngt_LowerStr(Mask);
228
229         IRC_SetPenalty(Client, 3);
230         for (c = Client_First(); c != NULL; c = Client_Next(c)) {
231                 if (Client_Type(c) != CLIENT_USER)
232                         continue;
233
234                 if (OnlyOps && !Client_HasMode(c, 'o'))
235                         continue;
236
237                 if (Mask) {
238                         /* Match pattern against user host/server/name/nick */
239                         client_match = MatchCaseInsensitive(Mask,
240                                                             Client_Hostname(c));
241                         if (!client_match)
242                                 client_match = MatchCaseInsensitive(Mask,
243                                                                     Client_ID(Client_Introducer(c)));
244                         if (!client_match)
245                                 client_match = MatchCaseInsensitive(Mask,
246                                                                     Client_Info(c));
247                         if (!client_match)
248                                 client_match = MatchCaseInsensitive(Mask,
249                                                                     Client_ID(c));
250                         if (!client_match)
251                                 continue;       /* no match: skip this client */
252                 }
253
254                 is_visible = !Client_HasMode(c, 'i');
255
256                 /* Target client is invisible, but mask matches exactly? */
257                 if (!is_visible && Mask && strcasecmp(Client_ID(c), Mask) == 0)
258                         is_visible = true;
259
260                 /* Target still invisible, but are both on the same channel? */
261                 if (!is_visible) {
262                         cl2chan = Channel_FirstChannelOf(Client);
263                         while (cl2chan && !is_visible) {
264                                 chan = Channel_GetChannel(cl2chan);
265                                 if (Channel_IsMemberOf(chan, c))
266                                         is_visible = true;
267                                 cl2chan = Channel_NextChannelOf(Client, cl2chan);
268                         }
269                 }
270
271                 if (!is_visible)        /* target user is not visible */
272                         continue;
273
274                 if (IRC_CheckListTooBig(Client, count, MAX_RPL_WHO, "WHO"))
275                         break;
276
277                 strlcpy(flags, who_flags_status(Client_Modes(c)), sizeof(flags));
278                 if (strchr(Client_Modes(c), 'o'))
279                         strlcat(flags, "*", sizeof(flags));
280
281                 if (!write_whoreply(Client, c, "*", flags))
282                         return DISCONNECTED;
283                 count++;
284         }
285
286         return IRC_WriteStrClient(Client, RPL_ENDOFWHO_MSG, Client_ID(Client),
287                                   Mask ? Mask : "*");
288 }
289
290 /**
291  * Generate WHOIS reply of one actual client.
292  *
293  * @param Client The client from which this command has been received.
294  * @param from The client requesting the information ("originator").
295  * @param c The client of which information should be returned.
296  * @return CONNECTED or DISCONNECTED.
297  */
298 static bool
299 IRC_WHOIS_SendReply(CLIENT *Client, CLIENT *from, CLIENT *c)
300 {
301         char str[COMMAND_LEN];
302         CL2CHAN *cl2chan;
303         CHANNEL *chan;
304
305         assert(Client != NULL);
306         assert(from != NULL);
307         assert(c != NULL);
308
309         /* Nick, user, hostname and client info */
310         if (!IRC_WriteStrClient(from, RPL_WHOISUSER_MSG, Client_ID(from),
311                                 Client_ID(c), Client_User(c),
312                                 Client_HostnameDisplayed(c), Client_Info(c)))
313                 return DISCONNECTED;
314
315         /* Server */
316         if (!IRC_WriteStrClient(from, RPL_WHOISSERVER_MSG, Client_ID(from),
317                                 Client_ID(c), Client_ID(Client_Introducer(c)),
318                                 Client_Info(Client_Introducer(c))))
319                 return DISCONNECTED;
320
321         /* Channels */
322         snprintf(str, sizeof(str), RPL_WHOISCHANNELS_MSG,
323                  Client_ID(from), Client_ID(c));
324         cl2chan = Channel_FirstChannelOf(c);
325         while (cl2chan) {
326                 chan = Channel_GetChannel(cl2chan);
327                 assert(chan != NULL);
328
329                 /* next */
330                 cl2chan = Channel_NextChannelOf(c, cl2chan);
331
332                 /* Secret channel? */
333                 if (strchr(Channel_Modes(chan), 's')
334                     && !Channel_IsMemberOf(chan, Client))
335                         continue;
336
337                 /* Local channel and request is not from a user? */
338                 if (Client_Type(Client) == CLIENT_SERVER
339                     && Channel_IsLocal(chan))
340                         continue;
341
342                 /* Concatenate channel names */
343                 if (str[strlen(str) - 1] != ':')
344                         strlcat(str, " ", sizeof(str));
345
346                 who_flags_qualifier(Client, Channel_UserModes(chan, c),
347                                     str, sizeof(str));
348                 strlcat(str, Channel_Name(chan), sizeof(str));
349
350                 if (strlen(str) > (COMMAND_LEN - CHANNEL_NAME_LEN - 4)) {
351                         /* Line becomes too long: send it! */
352                         if (!IRC_WriteStrClient(Client, "%s", str))
353                                 return DISCONNECTED;
354                         snprintf(str, sizeof(str), RPL_WHOISCHANNELS_MSG,
355                                  Client_ID(from), Client_ID(c));
356                 }
357         }
358         if(str[strlen(str) - 1] != ':') {
359                 /* There is data left to send: */
360                 if (!IRC_WriteStrClient(Client, "%s", str))
361                         return DISCONNECTED;
362         }
363
364         /* IRC-Services? */
365         if (Client_Type(c) == CLIENT_SERVICE &&
366             !IRC_WriteStrClient(from, RPL_WHOISSERVICE_MSG,
367                                 Client_ID(from), Client_ID(c)))
368                 return DISCONNECTED;
369
370         /* IRC-Operator? */
371         if (Client_Type(c) != CLIENT_SERVICE &&
372             Client_HasMode(c, 'o') &&
373             !IRC_WriteStrClient(from, RPL_WHOISOPERATOR_MSG,
374                                 Client_ID(from), Client_ID(c)))
375                 return DISCONNECTED;
376
377         /* IRC-Bot? */
378         if (Client_HasMode(c, 'B') &&
379             !IRC_WriteStrClient(from, RPL_WHOISBOT_MSG,
380                                 Client_ID(from), Client_ID(c)))
381                 return DISCONNECTED;
382
383         /* Connected using SSL? */
384         if (Conn_UsesSSL(Client_Conn(c)) &&
385             !IRC_WriteStrClient(from, RPL_WHOISSSL_MSG, Client_ID(from),
386                                 Client_ID(c)))
387                 return DISCONNECTED;
388
389         /* Registered nickname? */
390         if (Client_HasMode(c, 'R') &&
391             !IRC_WriteStrClient(from, RPL_WHOISREGNICK_MSG,
392                                 Client_ID(from), Client_ID(c)))
393                 return DISCONNECTED;
394
395         /* Local client and requester is the user itself or an IRC Op? */
396         if (Client_Conn(c) > NONE &&
397             (from == c || (!Conf_MorePrivacy && Client_HasMode(from, 'o')))) {
398                 /* Client hostname */
399                 if (!IRC_WriteStrClient(from, RPL_WHOISHOST_MSG,
400                                         Client_ID(from), Client_ID(c), Client_Hostname(c),
401                                         Conn_GetIPAInfo(Client_Conn(c))))
402                         return DISCONNECTED;
403                 /* Client modes */
404                 if (!IRC_WriteStrClient(from, RPL_WHOISMODES_MSG,
405                                         Client_ID(from), Client_ID(c), Client_Modes(c)))
406                         return DISCONNECTED;
407         }
408
409         /* Idle and signon time (local clients only!) */
410         if (!Conf_MorePrivacy && Client_Conn(c) > NONE &&
411             !IRC_WriteStrClient(from, RPL_WHOISIDLE_MSG,
412                                 Client_ID(from), Client_ID(c),
413                                 (unsigned long)Conn_GetIdle(Client_Conn(c)),
414                                 (unsigned long)Conn_GetSignon(Client_Conn(c))))
415                 return DISCONNECTED;
416
417         /* Away? */
418         if (Client_HasMode(c, 'a') &&
419             !IRC_WriteStrClient(from, RPL_AWAY_MSG,
420                                 Client_ID(from), Client_ID(c), Client_Away(c)))
421                 return DISCONNECTED;
422
423         return CONNECTED;
424 }
425
426 static bool
427 WHOWAS_EntryWrite(CLIENT *prefix, WHOWAS *entry)
428 {
429         char t_str[60];
430
431         (void)strftime(t_str, sizeof(t_str), "%a %b %d %H:%M:%S %Y",
432                        localtime(&entry->time));
433
434         if (!IRC_WriteStrClient(prefix, RPL_WHOWASUSER_MSG, Client_ID(prefix),
435                                 entry->id, entry->user, entry->host, entry->info))
436                 return DISCONNECTED;
437
438         return IRC_WriteStrClient(prefix, RPL_WHOISSERVER_MSG, Client_ID(prefix),
439                                   entry->id, entry->server, t_str);
440 }
441
442 static bool
443 Show_MOTD_Start(CLIENT *Client)
444 {
445         return IRC_WriteStrClient(Client, RPL_MOTDSTART_MSG,
446                                   Client_ID( Client ), Client_ID( Client_ThisServer( )));
447 }
448
449 static bool
450 Show_MOTD_Sendline(CLIENT *Client, const char *msg)
451 {
452         return IRC_WriteStrClient(Client, RPL_MOTD_MSG, Client_ID( Client ), msg);
453 }
454
455 static bool
456 Show_MOTD_End(CLIENT *Client)
457 {
458         if (!IRC_WriteStrClient(Client, RPL_ENDOFMOTD_MSG, Client_ID(Client)))
459                 return DISCONNECTED;
460
461         if (*Conf_CloakHost)
462                 return IRC_WriteStrClient(Client, RPL_HOSTHIDDEN_MSG,
463                                           Client_ID(Client),
464                                           Client_Hostname(Client));
465
466         return CONNECTED;
467 }
468
469 #ifdef SSL_SUPPORT
470 static bool Show_MOTD_SSLInfo(CLIENT *Client)
471 {
472         bool ret = true;
473         char buf[COMMAND_LEN] = "Connected using Cipher ";
474
475         if (!Conn_GetCipherInfo(Client_Conn(Client), buf + 23, sizeof buf - 23))
476                 return true;
477
478         if (!Show_MOTD_Sendline(Client, buf))
479                 ret = false;
480
481         return ret;
482 }
483 #else
484 static inline bool
485 Show_MOTD_SSLInfo(UNUSED CLIENT *c)
486 { return true; }
487 #endif
488
489 /* Global functions */
490
491 /**
492  * Handler for the IRC command "ADMIN".
493  *
494  * @param Client The client from which this command has been received.
495  * @param Req Request structure with prefix and all parameters.
496  * @return CONNECTED or DISCONNECTED.
497  */
498 GLOBAL bool
499 IRC_ADMIN(CLIENT *Client, REQUEST *Req )
500 {
501         CLIENT *target, *prefix;
502
503         assert( Client != NULL );
504         assert( Req != NULL );
505
506         IRC_SetPenalty(Client, 1);
507
508         _IRC_ARGC_LE_OR_RETURN_(Client, Req, 1)
509         _IRC_GET_SENDER_OR_RETURN_(prefix, Req, Client)
510         _IRC_GET_TARGET_SERVER_OR_RETURN_(target, Req, 0, prefix)
511
512         /* Forward? */
513         if(target != Client_ThisServer()) {
514                 IRC_WriteStrClientPrefix(target, prefix,
515                                          "ADMIN %s", Client_ID(target));
516                 return CONNECTED;
517         }
518
519         if (!IRC_WriteStrClient(Client, RPL_ADMINME_MSG, Client_ID(prefix),
520                                 Conf_ServerName))
521                 return DISCONNECTED;
522         if (!IRC_WriteStrClient(Client, RPL_ADMINLOC1_MSG, Client_ID(prefix),
523                                 Conf_ServerAdmin1))
524                 return DISCONNECTED;
525         if (!IRC_WriteStrClient(Client, RPL_ADMINLOC2_MSG, Client_ID(prefix),
526                                 Conf_ServerAdmin2))
527                 return DISCONNECTED;
528         if (!IRC_WriteStrClient(Client, RPL_ADMINEMAIL_MSG, Client_ID(prefix),
529                                 Conf_ServerAdminMail))
530                 return DISCONNECTED;
531
532         return CONNECTED;
533 } /* IRC_ADMIN */
534
535 /**
536  * Handler for the IRC command "INFO".
537  *
538  * @param Client The client from which this command has been received.
539  * @param Req Request structure with prefix and all parameters.
540  * @return CONNECTED or DISCONNECTED.
541  */
542 GLOBAL bool
543 IRC_INFO(CLIENT * Client, REQUEST * Req)
544 {
545         CLIENT *target, *prefix;
546         char msg[510];
547
548         assert(Client != NULL);
549         assert(Req != NULL);
550
551         IRC_SetPenalty(Client, 2);
552
553         _IRC_ARGC_LE_OR_RETURN_(Client, Req, 1)
554         _IRC_GET_SENDER_OR_RETURN_(prefix, Req, Client)
555         _IRC_GET_TARGET_SERVER_OR_RETURN_(target, Req, 0, prefix)
556
557         /* Forward? */
558         if (target != Client_ThisServer()) {
559                 IRC_WriteStrClientPrefix(target, prefix, "INFO %s",
560                                          Client_ID(target));
561                 return CONNECTED;
562         }
563
564         if (!IRC_WriteStrClient(Client, RPL_INFO_MSG, Client_ID(prefix),
565                                 NGIRCd_Version))
566                 return DISCONNECTED;
567
568 #if defined(__DATE__) && defined(__TIME__)
569         snprintf(msg, sizeof(msg), "Birth Date: %s at %s", __DATE__, __TIME__);
570         if (!IRC_WriteStrClient(Client, RPL_INFO_MSG, Client_ID(prefix), msg))
571                 return DISCONNECTED;
572 #endif
573
574         strlcpy(msg, "On-line since ", sizeof(msg));
575         strlcat(msg, NGIRCd_StartStr, sizeof(msg));
576         if (!IRC_WriteStrClient(Client, RPL_INFO_MSG, Client_ID(prefix), msg))
577                 return DISCONNECTED;
578
579         if (!IRC_WriteStrClient(Client, RPL_ENDOFINFO_MSG, Client_ID(prefix)))
580                 return DISCONNECTED;
581
582         return CONNECTED;
583 } /* IRC_INFO */
584
585 /**
586  * Handler for the IRC "ISON" command.
587  *
588  * @param Client The client from which this command has been received.
589  * @param Req Request structure with prefix and all parameters.
590  * @return CONNECTED or DISCONNECTED.
591  */
592 GLOBAL bool
593 IRC_ISON( CLIENT *Client, REQUEST *Req )
594 {
595         char rpl[COMMAND_LEN];
596         CLIENT *c;
597         char *ptr;
598         int i;
599
600         assert(Client != NULL);
601         assert(Req != NULL);
602
603         _IRC_ARGC_GE_OR_RETURN_(Client, Req, 1)
604
605         strlcpy(rpl, RPL_ISON_MSG, sizeof rpl);
606         for (i = 0; i < Req->argc; i++) {
607                 /* "All" ircd even parse ":<x> <y> ..." arguments and split
608                  * them up; so we do the same ... */
609                 ptr = strtok(Req->argv[i], " ");
610                 while (ptr) {
611                         ngt_TrimStr(ptr);
612                         c = Client_Search(ptr);
613                         if (c && Client_Type(c) == CLIENT_USER) {
614                                 strlcat(rpl, Client_ID(c), sizeof(rpl));
615                                 strlcat(rpl, " ", sizeof(rpl));
616                         }
617                         ptr = strtok(NULL, " ");
618                 }
619         }
620         ngt_TrimLastChr(rpl, ' ');
621
622         return IRC_WriteStrClient(Client, rpl, Client_ID(Client));
623 } /* IRC_ISON */
624
625 /**
626  * Handler for the IRC "LINKS" command.
627  *
628  * @param Client The client from which this command has been received.
629  * @param Req Request structure with prefix and all parameters.
630  * @return CONNECTED or DISCONNECTED.
631  */
632 GLOBAL bool
633 IRC_LINKS(CLIENT *Client, REQUEST *Req)
634 {
635         CLIENT *target, *from, *c;
636         char *mask;
637
638         assert(Client != NULL);
639         assert(Req != NULL);
640
641         IRC_SetPenalty(Client, 1);
642
643         _IRC_ARGC_LE_OR_RETURN_(Client, Req, 2)
644         _IRC_GET_SENDER_OR_RETURN_(from, Req, Client)
645
646         /* Get pointer to server mask or "*", if none given */
647         if (Req->argc > 0)
648                 mask = Req->argv[Req->argc - 1];
649         else
650                 mask = "*";
651
652         /* Forward? */
653         if (Req->argc == 2) {
654                 _IRC_GET_TARGET_SERVER_OR_RETURN_(target, Req, 0, from)
655                 if (target != Client_ThisServer()) {
656                         IRC_WriteStrClientPrefix(target, from,
657                                         "LINKS %s %s", Client_ID(target),
658                                         Req->argv[1]);
659                         return CONNECTED;
660                 }
661         }
662
663         c = Client_First();
664         while (c) {
665                 if (Client_Type(c) == CLIENT_SERVER
666                     && MatchCaseInsensitive(mask, Client_ID(c))) {
667                         if (!IRC_WriteStrClient(from, RPL_LINKS_MSG,
668                                         Client_ID(from), Client_ID(c),
669                                         Client_ID(Client_TopServer(c)
670                                                   ? Client_TopServer(c)
671                                                   : Client_ThisServer()),
672                                         Client_Hops(c), Client_Info(c)))
673                                 return DISCONNECTED;
674                 }
675                 c = Client_Next(c);
676         }
677         return IRC_WriteStrClient(from, RPL_ENDOFLINKS_MSG,
678                                   Client_ID(from), mask);
679 } /* IRC_LINKS */
680
681 /**
682  * Handler for the IRC "LUSERS" command.
683  *
684  * @param Client The client from which this command has been received.
685  * @param Req Request structure with prefix and all parameters.
686  * @return CONNECTED or DISCONNECTED.
687  */
688 GLOBAL bool
689 IRC_LUSERS( CLIENT *Client, REQUEST *Req )
690 {
691         CLIENT *target, *from;
692
693         assert( Client != NULL );
694         assert( Req != NULL );
695
696         IRC_SetPenalty(Client, 1);
697
698         _IRC_ARGC_LE_OR_RETURN_(Client, Req, 2)
699         _IRC_GET_SENDER_OR_RETURN_(from, Req, Client)
700         _IRC_GET_TARGET_SERVER_OR_RETURN_(target, Req, 1, from)
701
702         /* Forward? */
703         if (target != Client_ThisServer()) {
704                 IRC_WriteStrClientPrefix(target, from,
705                                          "LUSERS %s %s", Req->argv[0],
706                                          Client_ID(target));
707                 return CONNECTED;
708         }
709
710         return IRC_Send_LUSERS(from);
711 } /* IRC_LUSERS */
712
713 /**
714  * Handler for the IRC command "SERVLIST".
715  *
716  * @param Client The client from which this command has been received.
717  * @param Req Request structure with prefix and all parameters.
718  * @return CONNECTED or DISCONNECTED.
719  */
720 GLOBAL bool
721 IRC_SERVLIST(CLIENT *Client, REQUEST *Req)
722 {
723         CLIENT *c;
724
725         IRC_SetPenalty(Client, 1);
726
727         assert(Client != NULL);
728         assert(Req != NULL);
729
730         _IRC_ARGC_LE_OR_RETURN_(Client, Req, 2)
731
732         if (Req->argc < 2 || strcmp(Req->argv[1], "0") == 0) {
733                 for (c = Client_First(); c!= NULL; c = Client_Next(c)) {
734                         if (Client_Type(c) != CLIENT_SERVICE)
735                                 continue;
736                         if (Req->argc > 0 && !MatchCaseInsensitive(Req->argv[0],
737                                                                   Client_ID(c)))
738                                 continue;
739                         if (!IRC_WriteStrClient(Client, RPL_SERVLIST_MSG,
740                                         Client_ID(Client), Client_Mask(c),
741                                         Client_Mask(Client_Introducer(c)), "*",
742                                         0, Client_Hops(c), Client_Info(c)))
743                                 return DISCONNECTED;
744                 }
745         }
746
747         return IRC_WriteStrClient(Client, RPL_SERVLISTEND_MSG, Client_ID(Client),
748                                   Req->argc > 0 ? Req->argv[0] : "*",
749                                   Req->argc > 1 ? Req->argv[1] : "0");
750 } /* IRC_SERVLIST */
751
752 /**
753  * Handler for the IRC command "MOTD".
754  *
755  * @param Client The client from which this command has been received.
756  * @param Req Request structure with prefix and all parameters.
757  * @return CONNECTED or DISCONNECTED.
758  */
759 GLOBAL bool
760 IRC_MOTD( CLIENT *Client, REQUEST *Req )
761 {
762         CLIENT *from, *target;
763
764         assert( Client != NULL );
765         assert( Req != NULL );
766
767         IRC_SetPenalty(Client, 3);
768
769         _IRC_ARGC_LE_OR_RETURN_(Client, Req, 1)
770         _IRC_GET_SENDER_OR_RETURN_(from, Req, Client)
771         _IRC_GET_TARGET_SERVER_OR_RETURN_(target, Req, 0, from)
772
773         /* Forward? */
774         if (target != Client_ThisServer()) {
775                 IRC_WriteStrClientPrefix(target, from, "MOTD %s",
776                                          Client_ID(target));
777                 return CONNECTED;
778         }
779
780         return IRC_Show_MOTD(from);
781 } /* IRC_MOTD */
782
783 /**
784  * Handler for the IRC command "NAMES".
785  *
786  * @param Client The client from which this command has been received.
787  * @param Req Request structure with prefix and all parameters.
788  * @return CONNECTED or DISCONNECTED.
789  */
790 GLOBAL bool
791 IRC_NAMES( CLIENT *Client, REQUEST *Req )
792 {
793         char rpl[COMMAND_LEN], *ptr;
794         CLIENT *target, *from, *c;
795         CHANNEL *chan;
796
797         assert( Client != NULL );
798         assert( Req != NULL );
799
800         IRC_SetPenalty(Client, 1);
801
802         _IRC_ARGC_LE_OR_RETURN_(Client, Req, 2)
803         _IRC_GET_SENDER_OR_RETURN_(from, Req, Client)
804         _IRC_GET_TARGET_SERVER_OR_RETURN_(target, Req, 1, from)
805
806         /* Forward? */
807         if (target != Client_ThisServer()) {
808                 IRC_WriteStrClientPrefix(target, from, "NAMES %s :%s",
809                                          Req->argv[0], Client_ID(target));
810                 return CONNECTED;
811         }
812
813         if (Req->argc > 0) {
814                 /* Return NAMES list for specific channels */
815                 ptr = strtok(Req->argv[0], ",");
816                 while(ptr) {
817                         chan = Channel_Search(ptr);
818                         if (chan && !IRC_Send_NAMES(from, chan))
819                                 return DISCONNECTED;
820                         if (!IRC_WriteStrClient(from, RPL_ENDOFNAMES_MSG,
821                                                 Client_ID(from), ptr))
822                                 return DISCONNECTED;
823                         ptr = strtok( NULL, "," );
824                 }
825                 return CONNECTED;
826         }
827
828         chan = Channel_First();
829         while (chan) {
830                 if (!IRC_Send_NAMES(from, chan))
831                         return DISCONNECTED;
832                 chan = Channel_Next(chan);
833         }
834
835         /* Now print all clients which are not in any channel */
836         c = Client_First();
837         snprintf(rpl, sizeof(rpl), RPL_NAMREPLY_MSG, Client_ID(from), "*", "*");
838         while (c) {
839                 if (Client_Type(c) == CLIENT_USER
840                     && Channel_FirstChannelOf(c) == NULL
841                     && !strchr(Client_Modes(c), 'i'))
842                 {
843                         /* its a user, concatenate ... */
844                         if (rpl[strlen(rpl) - 1] != ':')
845                                 strlcat(rpl, " ", sizeof(rpl));
846                         strlcat(rpl, Client_ID(c), sizeof(rpl));
847
848                         if (strlen(rpl) > COMMAND_LEN - CLIENT_NICK_LEN - 4) {
849                                 /* Line is gwoing too long, send now */
850                                 if (!IRC_WriteStrClient(from, "%s", rpl))
851                                         return DISCONNECTED;
852                                 snprintf(rpl, sizeof(rpl), RPL_NAMREPLY_MSG,
853                                          Client_ID(from), "*", "*");
854                         }
855                 }
856                 c = Client_Next(c);
857         }
858         if (rpl[strlen(rpl) - 1] != ':' && !IRC_WriteStrClient(from, "%s", rpl))
859                 return DISCONNECTED;
860
861         return IRC_WriteStrClient(from, RPL_ENDOFNAMES_MSG, Client_ID(from), "*");
862 } /* IRC_NAMES */
863
864 /**
865  * Handler for the IRC command "STATS".
866  *
867  * @param Client The client from which this command has been received.
868  * @param Req Request structure with prefix and all parameters.
869  * @return CONNECTED or DISCONNECTED.
870  */
871 GLOBAL bool
872 IRC_STATS( CLIENT *Client, REQUEST *Req )
873 {
874         CLIENT *from, *target, *cl;
875         CONN_ID con;
876         char query;
877         COMMAND *cmd;
878         time_t time_now;
879         unsigned int days, hrs, mins;
880         struct list_head *list;
881         struct list_elem *list_item;
882
883         assert(Client != NULL);
884         assert(Req != NULL);
885
886         IRC_SetPenalty(Client, 2);
887
888         _IRC_ARGC_LE_OR_RETURN_(Client, Req, 2)
889         _IRC_GET_SENDER_OR_RETURN_(from, Req, Client)
890         _IRC_GET_TARGET_SERVER_OR_RETURN_(target, Req, 1, from)
891
892         /* Forward? */
893         if (target != Client_ThisServer()) {
894                 IRC_WriteStrClientPrefix(target, from, "STATS %s %s",
895                                          Req->argv[0], Client_ID(target));
896                 return CONNECTED;
897         }
898
899         if (Req->argc > 0)
900                 query = Req->argv[0][0] ? Req->argv[0][0] : '*';
901         else
902                 query = '*';
903
904         switch (query) {
905         case 'g':       /* Network-wide bans ("G-Lines") */
906         case 'G':
907         case 'k':       /* Server-local bans ("K-Lines") */
908         case 'K':
909                 if (!Client_HasMode(from, 'o'))
910                     return IRC_WriteStrClient(from, ERR_NOPRIVILEGES_MSG,
911                                               Client_ID(from));
912                 if (query == 'g' || query == 'G')
913                         list = Class_GetList(CLASS_GLINE);
914                 else
915                         list = Class_GetList(CLASS_KLINE);
916                         list_item = Lists_GetFirst(list);
917                         while (list_item) {
918                                 if (!IRC_WriteStrClient(from, RPL_STATSXLINE_MSG,
919                                                 Client_ID(from), query,
920                                                 Lists_GetMask(list_item),
921                                                 Lists_GetValidity(list_item),
922                                                 Lists_GetReason(list_item)))
923                                         return DISCONNECTED;
924                                 list_item = Lists_GetNext(list_item);
925                         }
926                 break;
927         case 'l':       /* Link status (servers and own link) */
928         case 'L':
929                 time_now = time(NULL);
930                 for (con = Conn_First(); con != NONE; con = Conn_Next(con)) {
931                         cl = Conn_GetClient(con);
932                         if (!cl)
933                                 continue;
934                         if ((Client_Type(cl) == CLIENT_SERVER)
935                             || (cl == Client)) {
936                                 /* Server link or our own connection */
937 #ifdef ZLIB
938                                 if (Conn_Options(con) & CONN_ZIP) {
939                                         if (!IRC_WriteStrClient
940                                             (from, RPL_STATSLINKINFOZIP_MSG,
941                                              Client_ID(from), Client_Mask(cl),
942                                              Conn_SendQ(con), Conn_SendMsg(con),
943                                              Zip_SendBytes(con),
944                                              Conn_SendBytes(con),
945                                              Conn_RecvMsg(con),
946                                              Zip_RecvBytes(con),
947                                              Conn_RecvBytes(con),
948                                              (long)(time_now - Conn_StartTime(con))))
949                                                 return DISCONNECTED;
950                                         continue;
951                                 }
952 #endif
953                                 if (!IRC_WriteStrClient
954                                     (from, RPL_STATSLINKINFO_MSG,
955                                      Client_ID(from), Client_Mask(cl),
956                                      Conn_SendQ(con), Conn_SendMsg(con),
957                                      Conn_SendBytes(con), Conn_RecvMsg(con),
958                                      Conn_RecvBytes(con),
959                                      (long)(time_now - Conn_StartTime(con))))
960                                         return DISCONNECTED;
961                         }
962                 }
963                 break;
964         case 'm':       /* IRC command status (usage count) */
965         case 'M':
966                 cmd = Parse_GetCommandStruct();
967                 for (; cmd->name; cmd++) {
968                         if (cmd->lcount == 0 && cmd->rcount == 0)
969                                 continue;
970                         if (!IRC_WriteStrClient
971                             (from, RPL_STATSCOMMANDS_MSG, Client_ID(from),
972                              cmd->name, cmd->lcount, cmd->bytes, cmd->rcount))
973                                 return DISCONNECTED;
974                 }
975                 break;
976         case 'u':       /* Server uptime */
977         case 'U':
978                 time_now = time(NULL) - NGIRCd_Start;
979                 days = uptime_days(&time_now);
980                 hrs = uptime_hrs(&time_now);
981                 mins = uptime_mins(&time_now);
982                 if (!IRC_WriteStrClient(from, RPL_STATSUPTIME, Client_ID(from),
983                                        days, hrs, mins, (unsigned int)time_now))
984                         return DISCONNECTED;
985                 break;
986         }
987
988         return IRC_WriteStrClient(from, RPL_ENDOFSTATS_MSG,
989                                   Client_ID(from), query);
990 } /* IRC_STATS */
991
992 /**
993  * Handler for the IRC command "SUMMON".
994  *
995  * @param Client The client from which this command has been received.
996  * @param Req Request structure with prefix and all parameters.
997  * @return CONNECTED or DISCONNECTED.
998  */
999 GLOBAL bool
1000 IRC_SUMMON(CLIENT * Client, UNUSED REQUEST * Req)
1001 {
1002         assert(Client != NULL);
1003
1004         IRC_SetPenalty(Client, 1);
1005
1006         return IRC_WriteStrClient(Client, ERR_SUMMONDISABLED_MSG,
1007                                   Client_ID(Client));
1008 } /* IRC_SUMMON */
1009
1010 /**
1011  * Handler for the IRC command "TIME".
1012  *
1013  * @param Client The client from which this command has been received.
1014  * @param Req Request structure with prefix and all parameters.
1015  * @return CONNECTED or DISCONNECTED.
1016  */
1017 GLOBAL bool
1018 IRC_TIME( CLIENT *Client, REQUEST *Req )
1019 {
1020         CLIENT *from, *target;
1021         char t_str[64];
1022         time_t t;
1023
1024         assert(Client != NULL);
1025         assert(Req != NULL);
1026
1027         IRC_SetPenalty(Client, 1);
1028
1029         _IRC_ARGC_LE_OR_RETURN_(Client, Req, 1)
1030         _IRC_GET_SENDER_OR_RETURN_(from, Req, Client)
1031         _IRC_GET_TARGET_SERVER_OR_RETURN_(target, Req, 0, from)
1032
1033         /* Forward? */
1034         if (target != Client_ThisServer()) {
1035                 IRC_WriteStrClientPrefix(target, from, "TIME %s",
1036                                          Client_ID(target));
1037                 return CONNECTED;
1038         }
1039
1040         t = time( NULL );
1041         (void)strftime(t_str, 60, "%A %B %d %Y -- %H:%M %Z", localtime(&t));
1042         return IRC_WriteStrClient(from, RPL_TIME_MSG, Client_ID(from),
1043                                   Client_ID(Client_ThisServer()), t_str);
1044 } /* IRC_TIME */
1045
1046 /**
1047  * Handler for the IRC command "USERHOST".
1048  *
1049  * @param Client The client from which this command has been received.
1050  * @param Req Request structure with prefix and all parameters.
1051  * @return CONNECTED or DISCONNECTED.
1052  */
1053 GLOBAL bool
1054 IRC_USERHOST(CLIENT *Client, REQUEST *Req)
1055 {
1056         char rpl[COMMAND_LEN];
1057         CLIENT *c;
1058         int max, i;
1059
1060         assert(Client != NULL);
1061         assert(Req != NULL);
1062
1063         IRC_SetPenalty(Client, 1);
1064
1065         _IRC_ARGC_GE_OR_RETURN_(Client, Req, 1)
1066
1067         if (Req->argc > 5)
1068                 max = 5;
1069         else
1070                 max = Req->argc;
1071
1072         strlcpy(rpl, RPL_USERHOST_MSG, sizeof rpl);
1073         for (i = 0; i < max; i++) {
1074                 c = Client_Search(Req->argv[i]);
1075                 if (c && (Client_Type(c) == CLIENT_USER)) {
1076                         /* This Nick is "online" */
1077                         strlcat(rpl, Client_ID(c), sizeof(rpl));
1078                         if (Client_HasMode(c, 'o'))
1079                                 strlcat(rpl, "*", sizeof(rpl));
1080                         strlcat(rpl, "=", sizeof(rpl));
1081                         if (Client_HasMode(c, 'a'))
1082                                 strlcat(rpl, "-", sizeof(rpl));
1083                         else
1084                                 strlcat(rpl, "+", sizeof(rpl));
1085                         strlcat(rpl, Client_User(c), sizeof(rpl));
1086                         strlcat(rpl, "@", sizeof(rpl));
1087                         strlcat(rpl, Client_HostnameDisplayed(c), sizeof(rpl));
1088                         strlcat(rpl, " ", sizeof(rpl));
1089                 }
1090         }
1091         ngt_TrimLastChr(rpl, ' ');
1092
1093         return IRC_WriteStrClient(Client, rpl, Client_ID(Client));
1094 } /* IRC_USERHOST */
1095
1096 /**
1097  * Handler for the IRC command "USERS".
1098  *
1099  * @param Client The client from which this command has been received.
1100  * @param Req Request structure with prefix and all parameters.
1101  * @return CONNECTED or DISCONNECTED.
1102  */
1103 GLOBAL bool
1104 IRC_USERS(CLIENT * Client, UNUSED REQUEST * Req)
1105 {
1106         assert(Client != NULL);
1107
1108         IRC_SetPenalty(Client, 1);
1109
1110         return IRC_WriteStrClient(Client, ERR_USERSDISABLED_MSG,
1111                                   Client_ID(Client));
1112 } /* IRC_USERS */
1113
1114 /**
1115  * Handler for the IRC command "VERSION".
1116  *
1117  * @param Client The client from which this command has been received.
1118  * @param Req Request structure with prefix and all parameters.
1119  * @return CONNECTED or DISCONNECTED.
1120  */
1121 GLOBAL bool
1122 IRC_VERSION( CLIENT *Client, REQUEST *Req )
1123 {
1124         CLIENT *target, *prefix;
1125
1126         assert( Client != NULL );
1127         assert( Req != NULL );
1128
1129         IRC_SetPenalty(Client, 1);
1130
1131         _IRC_ARGC_LE_OR_RETURN_(Client, Req, 1)
1132         _IRC_GET_SENDER_OR_RETURN_(prefix, Req, Client)
1133         _IRC_GET_TARGET_SERVER_OR_RETURN_(target, Req, 0, prefix)
1134
1135         /* Forward? */
1136         if (target != Client_ThisServer()) {
1137                 IRC_WriteStrClientPrefix(target, prefix, "VERSION %s",
1138                                          Client_ID(target));
1139                 return CONNECTED;
1140         }
1141
1142         /* send version information */
1143         return IRC_WriteStrClient(Client, RPL_VERSION_MSG, Client_ID(prefix),
1144                                   PACKAGE_NAME, PACKAGE_VERSION,
1145                                   NGIRCd_DebugLevel, Conf_ServerName,
1146                                   NGIRCd_VersionAddition);
1147 } /* IRC_VERSION */
1148
1149 /**
1150  * Handler for the IRC "WHO" command.
1151  *
1152  * @param Client The client from which this command has been received.
1153  * @param Req Request structure with prefix and all parameters.
1154  * @return CONNECTED or DISCONNECTED.
1155  */
1156 GLOBAL bool
1157 IRC_WHO(CLIENT *Client, REQUEST *Req)
1158 {
1159         bool only_ops;
1160         CHANNEL *chan;
1161
1162         assert (Client != NULL);
1163         assert (Req != NULL);
1164
1165         IRC_SetPenalty(Client, 1);
1166
1167         _IRC_ARGC_LE_OR_RETURN_(Client, Req, 2)
1168
1169         only_ops = false;
1170         if (Req->argc == 2) {
1171                 if (strcmp(Req->argv[1], "o") == 0)
1172                         only_ops = true;
1173 #ifdef STRICT_RFC
1174                 else
1175                         return IRC_WriteStrClient(Client,
1176                                                   ERR_NEEDMOREPARAMS_MSG,
1177                                                   Client_ID(Client),
1178                                                   Req->command);
1179 #endif
1180         }
1181
1182         if (Req->argc >= 1) {
1183                 /* Channel or mask given */
1184                 chan = Channel_Search(Req->argv[0]);
1185                 if (chan) {
1186                         /* Members of a channel have been requested */
1187                         return IRC_WHO_Channel(Client, chan, only_ops);
1188                 }
1189                 if (strcmp(Req->argv[0], "0") != 0) {
1190                         /* A mask has been given. But please note this RFC
1191                          * stupidity: "0" is same as no arguments ... */
1192                         return IRC_WHO_Mask(Client, Req->argv[0], only_ops);
1193                 }
1194         }
1195
1196         /* No channel or (valid) mask given */
1197         IRC_SetPenalty(Client, 2);
1198         return IRC_WHO_Mask(Client, NULL, only_ops);
1199 } /* IRC_WHO */
1200
1201 /**
1202  * Handler for the IRC "WHOIS" command.
1203  *
1204  * @param Client The client from which this command has been received.
1205  * @param Req Request structure with prefix and all parameters.
1206  * @return CONNECTED or DISCONNECTED.
1207  */
1208 GLOBAL bool
1209 IRC_WHOIS( CLIENT *Client, REQUEST *Req )
1210 {
1211         CLIENT *from, *target, *c;
1212         unsigned int match_count = 0, found = 0;
1213         bool has_wildcards, is_remote;
1214         bool got_wildcard = false;
1215         char mask[COMMAND_LEN], *query;
1216
1217         assert( Client != NULL );
1218         assert( Req != NULL );
1219
1220         IRC_SetPenalty(Client, 1);
1221
1222         /* Bad number of parameters? */
1223         if (Req->argc < 1 || Req->argc > 2)
1224                 return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
1225                                           Client_ID(Client), Req->command);
1226
1227         _IRC_GET_SENDER_OR_RETURN_(from, Req, Client)
1228
1229         /* Get target server for this command */
1230         if (Req->argc > 1) {
1231                 _IRC_GET_TARGET_SERVER_OR_RETURN_(target, Req, 0, Client)
1232         } else
1233                 target = Client_ThisServer();
1234
1235         assert(target != NULL);
1236
1237         /* Forward? */
1238         if (target != Client_ThisServer()) {
1239                 IRC_WriteStrClientPrefix(target, from, "WHOIS %s :%s",
1240                                          Req->argv[0], Req->argv[1]);
1241                 return CONNECTED;
1242         }
1243
1244         is_remote = Client_Conn(from) < 0;
1245         strlcpy(mask, Req->argv[Req->argc - 1], sizeof(mask));
1246         for (query = strtok(ngt_LowerStr(mask), ",");
1247                         query && found < 3;
1248                         query = strtok(NULL, ","), found++)
1249         {
1250                 has_wildcards = query[strcspn(query, "*?")] != 0;
1251                 /*
1252                  * follows ircd 2.10 implementation:
1253                  *  - handle up to 3 targets
1254                  *  - no wildcards for remote clients
1255                  *  - only one wildcard target per local client
1256                  *
1257                  *  Also, at most MAX_RPL_WHOIS matches are returned.
1258                  */
1259                 if (!has_wildcards || is_remote) {
1260                         c = Client_Search(query);
1261                         if (c && (Client_Type(c) == CLIENT_USER
1262                                   || Client_Type(c) == CLIENT_SERVICE)) {
1263                                 if (!IRC_WHOIS_SendReply(Client, from, c))
1264                                         return DISCONNECTED;
1265                         } else {
1266                                 if (!IRC_WriteStrClient(Client,
1267                                                         ERR_NOSUCHNICK_MSG,
1268                                                         Client_ID(Client),
1269                                                         query))
1270                                         return DISCONNECTED;
1271                         }
1272                         continue;
1273                 }
1274                 if (got_wildcard) {
1275                         /* we already handled one wildcard query */
1276                         if (!IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG,
1277                              Client_ID(Client), query))
1278                                 return DISCONNECTED;
1279                         continue;
1280                 }
1281                 got_wildcard = true;
1282                 IRC_SetPenalty(Client, 3);
1283
1284                 for (c = Client_First(); c; c = Client_Next(c)) {
1285                         if (IRC_CheckListTooBig(Client, match_count,
1286                                             MAX_RPL_WHOIS, "WHOIS"))
1287                                 break;
1288
1289                         if (Client_Type(c) != CLIENT_USER)
1290                                 continue;
1291                         if (!MatchCaseInsensitive(query, Client_ID(c)))
1292                                 continue;
1293                         if (!IRC_WHOIS_SendReply(Client, from, c))
1294                                 return DISCONNECTED;
1295
1296                         match_count++;
1297                 }
1298
1299                 if (match_count == 0)
1300                         IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG,
1301                                            Client_ID(Client),
1302                                            Req->argv[Req->argc - 1]);
1303         }
1304         return IRC_WriteStrClient(from, RPL_ENDOFWHOIS_MSG,
1305                                   Client_ID(from), Req->argv[Req->argc - 1]);
1306 } /* IRC_WHOIS */
1307
1308 /**
1309  * Handler for the IRC "WHOWAS" command.
1310  *
1311  * @param Client The client from which this command has been received.
1312  * @param Req Request structure with prefix and all parameters.
1313  * @return CONNECTED or DISCONNECTED.
1314  */
1315 GLOBAL bool
1316 IRC_WHOWAS( CLIENT *Client, REQUEST *Req )
1317 {
1318         CLIENT *target, *prefix;
1319         WHOWAS *whowas;
1320         char tok_buf[COMMAND_LEN];
1321         int max, last, count, i, nc;
1322         const char *nick;
1323
1324         assert( Client != NULL );
1325         assert( Req != NULL );
1326
1327         /* Do not reveal any info on disconnected users? */
1328         if (Conf_MorePrivacy)
1329                 return CONNECTED;
1330
1331         /* Wrong number of parameters? */
1332         if (Req->argc < 1)
1333                 return IRC_WriteStrClient(Client, ERR_NONICKNAMEGIVEN_MSG,
1334                                           Client_ID(Client));
1335
1336         _IRC_ARGC_LE_OR_RETURN_(Client, Req, 3)
1337         _IRC_GET_SENDER_OR_RETURN_(prefix, Req, Client)
1338         _IRC_GET_TARGET_SERVER_OR_RETURN_(target, Req, 2, prefix)
1339
1340         /* Forward? */
1341         if (target != Client_ThisServer()) {
1342                 IRC_WriteStrClientPrefix(target, prefix, "WHOWAS %s %s %s",
1343                                          Req->argv[0], Req->argv[1],
1344                                          Client_ID(target));
1345                 return CONNECTED;
1346         }
1347
1348         whowas = Client_GetWhowas( );
1349         last = Client_GetLastWhowasIndex( );
1350         if (last < 0)
1351                 last = 0;
1352
1353         max = DEF_RPL_WHOWAS;
1354         if (Req->argc > 1) {
1355                 max = atoi(Req->argv[1]);
1356                 if (max < 1)
1357                         max = MAX_RPL_WHOWAS;
1358         }
1359
1360         /*
1361          * Break up the nick argument into a list of nicks, if applicable
1362          * Can't modify Req->argv[0] because we need it for RPL_ENDOFWHOWAS_MSG.
1363          */
1364         strlcpy(tok_buf, Req->argv[0], sizeof(tok_buf));
1365         nick = strtok(tok_buf, ",");
1366
1367         for (i=last, count=0; nick != NULL ; nick = strtok(NULL, ",")) {
1368                 nc = 0;
1369                 do {
1370                         /* Used entry? */
1371                         if (whowas[i].time > 0 && strcasecmp(nick, whowas[i].id) == 0) {
1372                                 if (!WHOWAS_EntryWrite(prefix, &whowas[i]))
1373                                         return DISCONNECTED;
1374                                 nc++;
1375                                 count++;
1376                         }
1377                         /* previous entry */
1378                         i--;
1379
1380                         /* "underflow", wrap around */
1381                         if (i < 0)
1382                                 i = MAX_WHOWAS - 1;
1383
1384                         if (nc && count >= max)
1385                                 break;
1386                 } while (i != last);
1387
1388                 if (nc == 0 && !IRC_WriteStrClient(prefix, ERR_WASNOSUCHNICK_MSG,
1389                                                 Client_ID(prefix), nick))
1390                         return DISCONNECTED;
1391         }
1392         return IRC_WriteStrClient(prefix, RPL_ENDOFWHOWAS_MSG,
1393                                   Client_ID(prefix), Req->argv[0]);
1394 } /* IRC_WHOWAS */
1395
1396 /**
1397  * Send LUSERS reply to a client.
1398  *
1399  * @param Client The receipient of the information.
1400  * @return CONNECTED or DISCONNECTED.
1401  */
1402 GLOBAL bool
1403 IRC_Send_LUSERS(CLIENT *Client)
1404 {
1405         unsigned long cnt;
1406 #ifndef STRICT_RFC
1407         unsigned long max;
1408 #endif
1409
1410         assert(Client != NULL);
1411
1412         /* Users, services and serevers in the network */
1413         if (!IRC_WriteStrClient(Client, RPL_LUSERCLIENT_MSG, Client_ID(Client),
1414                                 Client_UserCount(), Client_ServiceCount(),
1415                                 Client_ServerCount()))
1416                 return DISCONNECTED;
1417
1418         /* Number of IRC operators */
1419         cnt = Client_OperCount( );
1420         if (cnt > 0) {
1421                 if (!IRC_WriteStrClient(Client, RPL_LUSEROP_MSG,
1422                                         Client_ID(Client), cnt))
1423                         return DISCONNECTED;
1424         }
1425
1426         /* Unknown connections */
1427         cnt = Client_UnknownCount( );
1428         if (cnt > 0) {
1429                 if (!IRC_WriteStrClient(Client, RPL_LUSERUNKNOWN_MSG,
1430                                         Client_ID(Client), cnt))
1431                         return DISCONNECTED;
1432         }
1433
1434         /* Number of created channels */
1435         if (!IRC_WriteStrClient(Client, RPL_LUSERCHANNELS_MSG,
1436                                 Client_ID(Client),
1437                                 Channel_CountVisible(Client)))
1438                 return DISCONNECTED;
1439
1440         /* Number of local users, services and servers */
1441         if (!IRC_WriteStrClient(Client, RPL_LUSERME_MSG, Client_ID(Client),
1442                                 Client_MyUserCount(), Client_MyServiceCount(),
1443                                 Client_MyServerCount()))
1444                 return DISCONNECTED;
1445
1446 #ifndef STRICT_RFC
1447         /* Maximum number of local users */
1448         cnt = Client_MyUserCount();
1449         max = Client_MyMaxUserCount();
1450         if (! IRC_WriteStrClient(Client, RPL_LOCALUSERS_MSG, Client_ID(Client),
1451                         cnt, max, cnt, max))
1452                 return DISCONNECTED;
1453         /* Maximum number of users in the network */
1454         cnt = Client_UserCount();
1455         max = Client_MaxUserCount();
1456         if(! IRC_WriteStrClient(Client, RPL_NETUSERS_MSG, Client_ID(Client),
1457                         cnt, max, cnt, max))
1458                 return DISCONNECTED;
1459         /* Connection counters */
1460         if (! IRC_WriteStrClient(Client, RPL_STATSCONN_MSG, Client_ID(Client),
1461                         Conn_CountMax(), Conn_CountAccepted()))
1462                 return DISCONNECTED;
1463 #endif
1464
1465         return CONNECTED;
1466 } /* IRC_Send_LUSERS */
1467
1468 GLOBAL bool
1469 IRC_Show_MOTD( CLIENT *Client )
1470 {
1471         const char *line;
1472         size_t len_tot, len_str;
1473
1474         assert( Client != NULL );
1475
1476         len_tot = array_bytes(&Conf_Motd);
1477         if (len_tot == 0 && !Conn_UsesSSL(Client_Conn(Client)))
1478                 return IRC_WriteStrClient(Client, ERR_NOMOTD_MSG, Client_ID(Client));
1479
1480         if (!Show_MOTD_Start(Client))
1481                 return DISCONNECTED;
1482
1483         line = array_start(&Conf_Motd);
1484         while (len_tot > 0) {
1485                 len_str = strlen(line) + 1;
1486
1487                 assert(len_tot >= len_str);
1488                 len_tot -= len_str;
1489
1490                 if (!Show_MOTD_Sendline(Client, line))
1491                         return DISCONNECTED;
1492                 line += len_str;
1493         }
1494
1495         if (!Show_MOTD_SSLInfo(Client))
1496                 return DISCONNECTED;
1497         return Show_MOTD_End(Client);
1498 } /* IRC_Show_MOTD */
1499
1500 /**
1501  * Send NAMES reply for a specific client and channel.
1502  *
1503  * @param Client The client requesting the NAMES information.
1504  * @param Chan The channel
1505  * @return CONNECTED or DISCONNECTED.
1506  */
1507 GLOBAL bool
1508 IRC_Send_NAMES(CLIENT * Client, CHANNEL * Chan)
1509 {
1510         bool is_visible, is_member;
1511         char str[COMMAND_LEN];
1512         CL2CHAN *cl2chan;
1513         CLIENT *cl;
1514
1515         assert(Client != NULL);
1516         assert(Chan != NULL);
1517
1518         if (Channel_IsMemberOf(Chan, Client))
1519                 is_member = true;
1520         else
1521                 is_member = false;
1522
1523         /* Do not print info on channel memberships to anyone that is not member? */
1524         if (Conf_MorePrivacy && !is_member)
1525                 return CONNECTED;
1526
1527         /* Secret channel? */
1528         if (!is_member && strchr(Channel_Modes(Chan), 's'))
1529                 return CONNECTED;
1530
1531         snprintf(str, sizeof(str), RPL_NAMREPLY_MSG, Client_ID(Client), "=",
1532                  Channel_Name(Chan));
1533         cl2chan = Channel_FirstMember(Chan);
1534         while (cl2chan) {
1535                 cl = Channel_GetClient(cl2chan);
1536
1537                 if (strchr(Client_Modes(cl), 'i'))
1538                         is_visible = false;
1539                 else
1540                         is_visible = true;
1541
1542                 if (is_member || is_visible) {
1543                         if (str[strlen(str) - 1] != ':')
1544                                 strlcat(str, " ", sizeof(str));
1545
1546                         who_flags_qualifier(Client, Channel_UserModes(Chan, cl),
1547                                             str, sizeof(str));
1548                         strlcat(str, Client_ID(cl), sizeof(str));
1549
1550                         if (strlen(str) > (COMMAND_LEN - CLIENT_NICK_LEN - 4)) {
1551                                 if (!IRC_WriteStrClient(Client, "%s", str))
1552                                         return DISCONNECTED;
1553                                 snprintf(str, sizeof(str), RPL_NAMREPLY_MSG,
1554                                          Client_ID(Client), "=",
1555                                          Channel_Name(Chan));
1556                         }
1557                 }
1558
1559                 cl2chan = Channel_NextMember(Chan, cl2chan);
1560         }
1561         if (str[strlen(str) - 1] != ':') {
1562                 if (!IRC_WriteStrClient(Client, "%s", str))
1563                         return DISCONNECTED;
1564         }
1565
1566         return CONNECTED;
1567 } /* IRC_Send_NAMES */
1568
1569 /**
1570  * Send the ISUPPORT numeric (005).
1571  * This numeric indicates the features that are supported by this server.
1572  * See <http://www.irc.org/tech_docs/005.html> for details.
1573  */
1574 GLOBAL bool
1575 IRC_Send_ISUPPORT(CLIENT * Client)
1576 {
1577         if (!IRC_WriteStrClient(Client, RPL_ISUPPORT1_MSG, Client_ID(Client),
1578                                 Conf_MaxJoins))
1579                 return DISCONNECTED;
1580         return IRC_WriteStrClient(Client, RPL_ISUPPORT2_MSG, Client_ID(Client),
1581                                   CHANNEL_NAME_LEN - 1, Conf_MaxNickLength - 1,
1582                                   COMMAND_LEN - 23, CLIENT_AWAY_LEN - 1,
1583                                   COMMAND_LEN - 113, MAX_HNDL_MODES_ARG,
1584                                   MAX_HNDL_CHANNEL_LISTS);
1585 } /* IRC_Send_ISUPPORT */
1586
1587 /* -eof- */