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