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