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