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