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