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