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