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