]> arthur.barton.de Git - ngircd-alex.git/blob - src/ngircd/irc-info.c
Show account name in WHOIS
[ngircd-alex.git] / src / ngircd / irc-info.c
1 /*
2  * ngIRCd -- The Next Generation IRC Daemon
3  * Copyright (c)2001-2013 Alexander Barton (alex@barton.de) and Contributors.
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  * Please read the file COPYING, README and AUTHORS for more information.
10  */
11
12 #include "portab.h"
13
14 /**
15  * @file
16  * IRC info commands
17  */
18
19 #include "imp.h"
20 #include <assert.h>
21 #include <errno.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <strings.h>
26
27 #include "ngircd.h"
28 #include "conn-func.h"
29 #include "conn-zip.h"
30 #include "channel.h"
31 #include "class.h"
32 #include "conf.h"
33 #include "defines.h"
34 #include "lists.h"
35 #include "log.h"
36 #include "messages.h"
37 #include "match.h"
38 #include "tool.h"
39 #include "parse.h"
40 #include "irc.h"
41 #include "irc-macros.h"
42 #include "irc-write.h"
43 #include "client-cap.h"
44 #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         return IRC_WriteStrClient(Client, RPL_VERSION_MSG, Client_ID(prefix),
1110                                   PACKAGE_NAME, PACKAGE_VERSION,
1111                                   NGIRCd_DebugLevel, Conf_ServerName,
1112                                   NGIRCd_VersionAddition);
1113 } /* IRC_VERSION */
1114
1115 /**
1116  * Handler for the IRC "WHO" command.
1117  *
1118  * @param Client The client from which this command has been received.
1119  * @param Req Request structure with prefix and all parameters.
1120  * @return CONNECTED or DISCONNECTED.
1121  */
1122 GLOBAL bool
1123 IRC_WHO(CLIENT *Client, REQUEST *Req)
1124 {
1125         bool only_ops;
1126         CHANNEL *chan;
1127
1128         assert (Client != NULL);
1129         assert (Req != NULL);
1130
1131         only_ops = false;
1132         if (Req->argc == 2) {
1133                 if (strcmp(Req->argv[1], "o") == 0)
1134                         only_ops = true;
1135 #ifdef STRICT_RFC
1136                 else {
1137                         return IRC_WriteErrClient(Client,
1138                                                   ERR_NEEDMOREPARAMS_MSG,
1139                                                   Client_ID(Client),
1140                                                   Req->command);
1141                 }
1142 #endif
1143         }
1144
1145         if (Req->argc >= 1) {
1146                 /* Channel or mask given */
1147                 chan = Channel_Search(Req->argv[0]);
1148                 if (chan) {
1149                         /* Members of a channel have been requested */
1150                         return IRC_WHO_Channel(Client, chan, only_ops);
1151                 }
1152                 if (strcmp(Req->argv[0], "0") != 0) {
1153                         /* A mask has been given. But please note this RFC
1154                          * stupidity: "0" is same as no arguments ... */
1155                         return IRC_WHO_Mask(Client, Req->argv[0], only_ops);
1156                 }
1157         }
1158
1159         /* No channel or (valid) mask given */
1160         return IRC_WHO_Mask(Client, NULL, only_ops);
1161 } /* IRC_WHO */
1162
1163 /**
1164  * Handler for the IRC "WHOIS" command.
1165  *
1166  * @param Client The client from which this command has been received.
1167  * @param Req Request structure with prefix and all parameters.
1168  * @return CONNECTED or DISCONNECTED.
1169  */
1170 GLOBAL bool
1171 IRC_WHOIS( CLIENT *Client, REQUEST *Req )
1172 {
1173         CLIENT *from, *target, *c;
1174         unsigned int match_count = 0, found = 0;
1175         bool has_wildcards, is_remote;
1176         bool got_wildcard = false;
1177         char mask[COMMAND_LEN], *query;
1178
1179         assert( Client != NULL );
1180         assert( Req != NULL );
1181
1182         /* Wrong number of parameters? */
1183         if (Req->argc < 1)
1184                 return IRC_WriteErrClient(Client, ERR_NONICKNAMEGIVEN_MSG,
1185                                           Client_ID(Client));
1186
1187         _IRC_ARGC_LE_OR_RETURN_(Client, Req, 2)
1188         _IRC_GET_SENDER_OR_RETURN_(from, Req, Client)
1189
1190         /* Get target server for this command */
1191         if (Req->argc > 1) {
1192                 _IRC_GET_TARGET_SERVER_OR_RETURN_(target, Req, 0, Client)
1193         } else
1194                 target = Client_ThisServer();
1195
1196         assert(target != NULL);
1197
1198         /* Forward? */
1199         if (target != Client_ThisServer()) {
1200                 IRC_WriteStrClientPrefix(target, from, "WHOIS %s :%s",
1201                                          Req->argv[0], Req->argv[1]);
1202                 return CONNECTED;
1203         }
1204
1205         is_remote = Client_Conn(from) < 0;
1206         strlcpy(mask, Req->argv[Req->argc - 1], sizeof(mask));
1207         for (query = strtok(ngt_LowerStr(mask), ",");
1208                         query && found < 3;
1209                         query = strtok(NULL, ","), found++)
1210         {
1211                 has_wildcards = query[strcspn(query, "*?")] != 0;
1212                 /*
1213                  * follows ircd 2.10 implementation:
1214                  *  - handle up to 3 targets
1215                  *  - no wildcards for remote clients
1216                  *  - only one wildcard target per local client
1217                  *
1218                  *  Also, at most MAX_RPL_WHOIS matches are returned.
1219                  */
1220                 if (!has_wildcards || is_remote) {
1221                         c = Client_Search(query);
1222                         if (c && (Client_Type(c) == CLIENT_USER
1223                                   || Client_Type(c) == CLIENT_SERVICE)) {
1224                                 if (!IRC_WHOIS_SendReply(Client, from, c))
1225                                         return DISCONNECTED;
1226                         } else {
1227                                 if (!IRC_WriteErrClient(Client,
1228                                                         ERR_NOSUCHNICK_MSG,
1229                                                         Client_ID(Client),
1230                                                         query))
1231                                         return DISCONNECTED;
1232                         }
1233                         continue;
1234                 }
1235                 if (got_wildcard) {
1236                         /* we already handled one wildcard query */
1237                         if (!IRC_WriteErrClient(Client, ERR_NOSUCHNICK_MSG,
1238                              Client_ID(Client), query))
1239                                 return DISCONNECTED;
1240                         continue;
1241                 }
1242                 got_wildcard = true;
1243                 /* Increase penalty for wildcard queries */
1244                 IRC_SetPenalty(Client, 3);
1245
1246                 for (c = Client_First(); c; c = Client_Next(c)) {
1247                         if (IRC_CheckListTooBig(Client, match_count,
1248                                             MAX_RPL_WHOIS, "WHOIS"))
1249                                 break;
1250
1251                         if (Client_Type(c) != CLIENT_USER)
1252                                 continue;
1253                         if (!MatchCaseInsensitive(query, Client_ID(c)))
1254                                 continue;
1255                         if (!IRC_WHOIS_SendReply(Client, from, c))
1256                                 return DISCONNECTED;
1257
1258                         match_count++;
1259                 }
1260
1261                 if (match_count == 0)
1262                         IRC_WriteErrClient(Client, ERR_NOSUCHNICK_MSG,
1263                                            Client_ID(Client),
1264                                            Req->argv[Req->argc - 1]);
1265         }
1266         return IRC_WriteStrClient(from, RPL_ENDOFWHOIS_MSG,
1267                                   Client_ID(from), Req->argv[Req->argc - 1]);
1268 } /* IRC_WHOIS */
1269
1270 /**
1271  * Handler for the IRC "WHOWAS" command.
1272  *
1273  * @param Client The client from which this command has been received.
1274  * @param Req Request structure with prefix and all parameters.
1275  * @return CONNECTED or DISCONNECTED.
1276  */
1277 GLOBAL bool
1278 IRC_WHOWAS( CLIENT *Client, REQUEST *Req )
1279 {
1280         CLIENT *target, *prefix;
1281         WHOWAS *whowas;
1282         char tok_buf[COMMAND_LEN];
1283         int max, last, count, i, nc;
1284         const char *nick;
1285
1286         assert( Client != NULL );
1287         assert( Req != NULL );
1288
1289         /* Wrong number of parameters? */
1290         if (Req->argc < 1)
1291                 return IRC_WriteErrClient(Client, ERR_NONICKNAMEGIVEN_MSG,
1292                                           Client_ID(Client));
1293
1294         _IRC_ARGC_LE_OR_RETURN_(Client, Req, 3)
1295         _IRC_GET_SENDER_OR_RETURN_(prefix, Req, Client)
1296         _IRC_GET_TARGET_SERVER_OR_RETURN_(target, Req, 2, prefix)
1297
1298         /* Do not reveal any info on disconnected users? */
1299         if (Conf_MorePrivacy)
1300                 return CONNECTED;
1301
1302         /* Forward? */
1303         if (target != Client_ThisServer()) {
1304                 IRC_WriteStrClientPrefix(target, prefix, "WHOWAS %s %s %s",
1305                                          Req->argv[0], Req->argv[1],
1306                                          Client_ID(target));
1307                 return CONNECTED;
1308         }
1309
1310         whowas = Client_GetWhowas( );
1311         last = Client_GetLastWhowasIndex( );
1312         if (last < 0)
1313                 last = 0;
1314
1315         max = DEF_RPL_WHOWAS;
1316         if (Req->argc > 1) {
1317                 max = atoi(Req->argv[1]);
1318                 if (max < 1)
1319                         max = MAX_RPL_WHOWAS;
1320         }
1321
1322         /*
1323          * Break up the nick argument into a list of nicks, if applicable
1324          * Can't modify Req->argv[0] because we need it for RPL_ENDOFWHOWAS_MSG.
1325          */
1326         strlcpy(tok_buf, Req->argv[0], sizeof(tok_buf));
1327         nick = strtok(tok_buf, ",");
1328
1329         for (i=last, count=0; nick != NULL ; nick = strtok(NULL, ",")) {
1330                 nc = 0;
1331                 do {
1332                         /* Used entry? */
1333                         if (whowas[i].time > 0 && strcasecmp(nick, whowas[i].id) == 0) {
1334                                 if (!WHOWAS_EntryWrite(prefix, &whowas[i]))
1335                                         return DISCONNECTED;
1336                                 nc++;
1337                                 count++;
1338                         }
1339                         /* previous entry */
1340                         i--;
1341
1342                         /* "underflow", wrap around */
1343                         if (i < 0)
1344                                 i = MAX_WHOWAS - 1;
1345
1346                         if (nc && count >= max)
1347                                 break;
1348                 } while (i != last);
1349
1350                 if (nc == 0 && !IRC_WriteErrClient(prefix, ERR_WASNOSUCHNICK_MSG,
1351                                                 Client_ID(prefix), nick))
1352                         return DISCONNECTED;
1353         }
1354         return IRC_WriteStrClient(prefix, RPL_ENDOFWHOWAS_MSG,
1355                                   Client_ID(prefix), Req->argv[0]);
1356 } /* IRC_WHOWAS */
1357
1358 /**
1359  * Send LUSERS reply to a client.
1360  *
1361  * @param Client The receipient of the information.
1362  * @return CONNECTED or DISCONNECTED.
1363  */
1364 GLOBAL bool
1365 IRC_Send_LUSERS(CLIENT *Client)
1366 {
1367         unsigned long cnt;
1368 #ifndef STRICT_RFC
1369         unsigned long max;
1370 #endif
1371
1372         assert(Client != NULL);
1373
1374         /* Users, services and servers in the network */
1375         if (!IRC_WriteStrClient(Client, RPL_LUSERCLIENT_MSG, Client_ID(Client),
1376                                 Client_UserCount(), Client_ServiceCount(),
1377                                 Client_ServerCount()))
1378                 return DISCONNECTED;
1379
1380         /* Number of IRC operators */
1381         cnt = Client_OperCount( );
1382         if (cnt > 0) {
1383                 if (!IRC_WriteStrClient(Client, RPL_LUSEROP_MSG,
1384                                         Client_ID(Client), cnt))
1385                         return DISCONNECTED;
1386         }
1387
1388         /* Unknown connections */
1389         cnt = Client_UnknownCount( );
1390         if (cnt > 0) {
1391                 if (!IRC_WriteStrClient(Client, RPL_LUSERUNKNOWN_MSG,
1392                                         Client_ID(Client), cnt))
1393                         return DISCONNECTED;
1394         }
1395
1396         /* Number of created channels */
1397         if (!IRC_WriteStrClient(Client, RPL_LUSERCHANNELS_MSG,
1398                                 Client_ID(Client),
1399                                 Channel_CountVisible(Client)))
1400                 return DISCONNECTED;
1401
1402         /* Number of local users, services and servers */
1403         if (!IRC_WriteStrClient(Client, RPL_LUSERME_MSG, Client_ID(Client),
1404                                 Client_MyUserCount(), Client_MyServiceCount(),
1405                                 Client_MyServerCount()))
1406                 return DISCONNECTED;
1407
1408 #ifndef STRICT_RFC
1409         /* Maximum number of local users */
1410         cnt = Client_MyUserCount();
1411         max = Client_MyMaxUserCount();
1412         if (! IRC_WriteStrClient(Client, RPL_LOCALUSERS_MSG, Client_ID(Client),
1413                         cnt, max, cnt, max))
1414                 return DISCONNECTED;
1415         /* Maximum number of users in the network */
1416         cnt = Client_UserCount();
1417         max = Client_MaxUserCount();
1418         if(! IRC_WriteStrClient(Client, RPL_NETUSERS_MSG, Client_ID(Client),
1419                         cnt, max, cnt, max))
1420                 return DISCONNECTED;
1421         /* Connection counters */
1422         if (! IRC_WriteStrClient(Client, RPL_STATSCONN_MSG, Client_ID(Client),
1423                         Conn_CountMax(), Conn_CountAccepted()))
1424                 return DISCONNECTED;
1425 #endif
1426
1427         return CONNECTED;
1428 } /* IRC_Send_LUSERS */
1429
1430 GLOBAL bool
1431 IRC_Show_MOTD( CLIENT *Client )
1432 {
1433         const char *line;
1434         size_t len_tot, len_str;
1435
1436         assert( Client != NULL );
1437
1438         len_tot = array_bytes(&Conf_Motd);
1439         if (len_tot == 0 && !Conn_UsesSSL(Client_Conn(Client)))
1440                 return IRC_WriteErrClient(Client, ERR_NOMOTD_MSG, Client_ID(Client));
1441
1442         if (!IRC_WriteStrClient(Client, RPL_MOTDSTART_MSG, Client_ID(Client),
1443                                 Client_ID(Client_ThisServer())))
1444                 return DISCONNECTED;
1445
1446         line = array_start(&Conf_Motd);
1447         while (len_tot > 0) {
1448                 len_str = strlen(line) + 1;
1449
1450                 assert(len_tot >= len_str);
1451                 len_tot -= len_str;
1452
1453                 if (!IRC_WriteStrClient(Client, RPL_MOTD_MSG, Client_ID(Client), line))
1454                         return DISCONNECTED;
1455                 line += len_str;
1456         }
1457
1458         if (!Show_MOTD_SSLInfo(Client))
1459                 return DISCONNECTED;
1460
1461         if (!IRC_WriteStrClient(Client, RPL_ENDOFMOTD_MSG, Client_ID(Client)))
1462                 return DISCONNECTED;
1463
1464         if (*Conf_CloakHost)
1465                 return IRC_WriteStrClient(Client, RPL_HOSTHIDDEN_MSG,
1466                                           Client_ID(Client),
1467                                           Client_Hostname(Client));
1468
1469         return CONNECTED;
1470 } /* IRC_Show_MOTD */
1471
1472 /**
1473  * Send NAMES reply for a specific client and channel.
1474  *
1475  * @param Client The client requesting the NAMES information.
1476  * @param Chan The channel
1477  * @return CONNECTED or DISCONNECTED.
1478  */
1479 GLOBAL bool
1480 IRC_Send_NAMES(CLIENT * Client, CHANNEL * Chan)
1481 {
1482         bool is_visible, is_member;
1483         char str[COMMAND_LEN];
1484         CL2CHAN *cl2chan;
1485         CLIENT *cl;
1486
1487         assert(Client != NULL);
1488         assert(Chan != NULL);
1489
1490         if (Channel_IsMemberOf(Chan, Client))
1491                 is_member = true;
1492         else
1493                 is_member = false;
1494
1495         /* Do not print info on channel memberships to anyone that is not member? */
1496         if (Conf_MorePrivacy && !is_member)
1497                 return CONNECTED;
1498
1499         /* Secret channel? */
1500         if (!is_member && Channel_HasMode(Chan, 's'))
1501                 return CONNECTED;
1502
1503         snprintf(str, sizeof(str), RPL_NAMREPLY_MSG, Client_ID(Client), "=",
1504                  Channel_Name(Chan));
1505         cl2chan = Channel_FirstMember(Chan);
1506         while (cl2chan) {
1507                 cl = Channel_GetClient(cl2chan);
1508
1509                 if (Client_HasMode(cl, 'i'))
1510                         is_visible = false;
1511                 else
1512                         is_visible = true;
1513
1514                 if (is_member || is_visible) {
1515                         if (str[strlen(str) - 1] != ':')
1516                                 strlcat(str, " ", sizeof(str));
1517
1518                         who_flags_qualifier(Client, Channel_UserModes(Chan, cl),
1519                                             str, sizeof(str));
1520                         strlcat(str, Client_ID(cl), sizeof(str));
1521
1522                         if (strlen(str) > (COMMAND_LEN - CLIENT_NICK_LEN - 4)) {
1523                                 if (!IRC_WriteStrClient(Client, "%s", str))
1524                                         return DISCONNECTED;
1525                                 snprintf(str, sizeof(str), RPL_NAMREPLY_MSG,
1526                                          Client_ID(Client), "=",
1527                                          Channel_Name(Chan));
1528                         }
1529                 }
1530
1531                 cl2chan = Channel_NextMember(Chan, cl2chan);
1532         }
1533         if (str[strlen(str) - 1] != ':') {
1534                 if (!IRC_WriteStrClient(Client, "%s", str))
1535                         return DISCONNECTED;
1536         }
1537
1538         return CONNECTED;
1539 } /* IRC_Send_NAMES */
1540
1541 /**
1542  * Send the ISUPPORT numeric (005).
1543  * This numeric indicates the features that are supported by this server.
1544  * See <http://www.irc.org/tech_docs/005.html> for details.
1545  */
1546 GLOBAL bool
1547 IRC_Send_ISUPPORT(CLIENT * Client)
1548 {
1549         if (Conf_Network[0] && !IRC_WriteStrClient(Client, RPL_ISUPPORTNET_MSG,
1550                                                    Client_ID(Client),
1551                                                    Conf_Network))
1552                 return DISCONNECTED;
1553         if (!IRC_WriteStrClient(Client, RPL_ISUPPORT1_MSG, Client_ID(Client),
1554                                 CHANTYPES, CHANTYPES, Conf_MaxJoins))
1555                 return DISCONNECTED;
1556         return IRC_WriteStrClient(Client, RPL_ISUPPORT2_MSG, Client_ID(Client),
1557                                   CHANNEL_NAME_LEN - 1, Conf_MaxNickLength - 1,
1558                                   COMMAND_LEN - 23, CLIENT_AWAY_LEN - 1,
1559                                   COMMAND_LEN - 113, MAX_HNDL_MODES_ARG,
1560                                   MAX_HNDL_CHANNEL_LISTS);
1561 } /* IRC_Send_ISUPPORT */
1562
1563 /* -eof- */