2 * ngIRCd -- The Next Generation IRC Daemon
3 * Copyright (c)2001-2014 Alexander Barton (alex@barton.de) and Contributors.
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.
27 #include "conn-func.h"
40 #include "irc-macros.h"
41 #include "irc-write.h"
42 #include "client-cap.h"
50 t_diff(time_t *t, const time_t d)
58 return (unsigned int)diff;
62 uptime_days(time_t *now)
64 return t_diff(now, 60 * 60 * 24);
68 uptime_hrs(time_t *now)
70 return t_diff(now, 60 * 60);
74 uptime_mins(time_t *now)
76 return t_diff(now, 60);
80 write_whoreply(CLIENT *Client, CLIENT *c, const char *channelname, const char *flags)
82 return IRC_WriteStrClient(Client, RPL_WHOREPLY_MSG, Client_ID(Client),
83 channelname, Client_User(c),
84 Client_HostnameDisplayed(c),
85 Client_ID(Client_Introducer(c)), Client_ID(c),
86 flags, Client_Hops(c), Client_Info(c));
90 * Return channel user mode prefix(es).
92 * @param Client The client requesting the mode prefixes.
93 * @param chan_user_modes String with channel user modes.
94 * @param str String buffer to which the prefix(es) will be appended.
95 * @param len Size of "str" buffer.
96 * @return Pointer to "str".
99 who_flags_qualifier(CLIENT *Client, const char *chan_user_modes,
100 char *str, size_t len)
102 assert(Client != NULL);
104 if (Client_Cap(Client) & CLIENT_CAP_MULTI_PREFIX) {
105 if (strchr(chan_user_modes, 'q'))
106 strlcat(str, "~", len);
107 if (strchr(chan_user_modes, 'a'))
108 strlcat(str, "&", len);
109 if (strchr(chan_user_modes, 'o'))
110 strlcat(str, "@", len);
111 if (strchr(chan_user_modes, 'h'))
112 strlcat(str, "%", len);
113 if (strchr(chan_user_modes, 'v'))
114 strlcat(str, "+", len);
119 if (strchr(chan_user_modes, 'q'))
120 strlcat(str, "~", len);
121 else if (strchr(chan_user_modes, 'a'))
122 strlcat(str, "&", len);
123 else if (strchr(chan_user_modes, 'o'))
124 strlcat(str, "@", len);
125 else if (strchr(chan_user_modes, 'h'))
126 strlcat(str, "%", len);
127 else if (strchr(chan_user_modes, 'v'))
128 strlcat(str, "+", len);
134 * Send WHO reply for a "channel target" ("WHO #channel").
136 * @param Client Client requesting the information.
137 * @param Chan Channel being requested.
138 * @param OnlyOps Only display IRC operators.
139 * @return CONNECTED or DISCONNECTED.
142 IRC_WHO_Channel(CLIENT *Client, CHANNEL *Chan, bool OnlyOps)
144 bool is_visible, is_member, is_ircop;
150 assert( Client != NULL );
151 assert( Chan != NULL );
153 is_member = Channel_IsMemberOf(Chan, Client);
155 /* Secret channel? */
156 if (!is_member && Channel_HasMode(Chan, 's'))
157 return IRC_WriteStrClient(Client, RPL_ENDOFWHO_MSG,
158 Client_ID(Client), Channel_Name(Chan));
160 cl2chan = Channel_FirstMember(Chan);
161 for (; cl2chan ; cl2chan = Channel_NextMember(Chan, cl2chan)) {
162 c = Channel_GetClient(cl2chan);
164 is_ircop = Client_HasMode(c, 'o');
165 if (OnlyOps && !is_ircop)
168 is_visible = Client_HasMode(c, 'i');
169 if (is_member || is_visible) {
170 memset(flags, 0, sizeof(flags));
172 if (Client_HasMode(c, 'a'))
173 flags[0] = 'G'; /* away */
180 who_flags_qualifier(Client, Channel_UserModes(Chan, c),
181 flags, sizeof(flags));
183 if (!write_whoreply(Client, c, Channel_Name(Chan),
190 /* If there are a lot of clients, increase the penalty a bit */
191 if (count > MAX_RPL_WHO)
192 IRC_SetPenalty(Client, 1);
194 return IRC_WriteStrClient(Client, RPL_ENDOFWHO_MSG, Client_ID(Client),
199 * Send WHO reply for a "mask target" ("WHO m*sk").
201 * @param Client Client requesting the information.
202 * @param Mask Mask being requested or NULL for "all" clients.
203 * @param OnlyOps Only display IRC operators.
204 * @return CONNECTED or DISCONNECTED.
207 IRC_WHO_Mask(CLIENT *Client, char *Mask, bool OnlyOps)
212 bool client_match, is_visible;
216 assert (Client != NULL);
221 IRC_SetPenalty(Client, 3);
222 for (c = Client_First(); c != NULL; c = Client_Next(c)) {
223 if (Client_Type(c) != CLIENT_USER)
226 if (OnlyOps && !Client_HasMode(c, 'o'))
230 /* Match pattern against user host/server/name/nick */
231 client_match = MatchCaseInsensitive(Mask,
234 client_match = MatchCaseInsensitive(Mask,
235 Client_ID(Client_Introducer(c)));
237 client_match = MatchCaseInsensitive(Mask,
240 client_match = MatchCaseInsensitive(Mask,
243 continue; /* no match: skip this client */
246 is_visible = !Client_HasMode(c, 'i');
248 /* Target client is invisible, but mask matches exactly? */
249 if (!is_visible && Mask && strcasecmp(Client_ID(c), Mask) == 0)
252 /* Target still invisible, but are both on the same channel? */
254 cl2chan = Channel_FirstChannelOf(Client);
255 while (cl2chan && !is_visible) {
256 chan = Channel_GetChannel(cl2chan);
257 if (Channel_IsMemberOf(chan, c))
259 cl2chan = Channel_NextChannelOf(Client, cl2chan);
263 if (!is_visible) /* target user is not visible */
266 if (IRC_CheckListTooBig(Client, count, MAX_RPL_WHO, "WHO"))
269 memset(flags, 0, sizeof(flags));
271 if (Client_HasMode(c, 'a'))
272 flags[0] = 'G'; /* away */
276 if (Client_HasMode(c, 'o'))
279 if (!write_whoreply(Client, c, "*", flags))
284 return IRC_WriteStrClient(Client, RPL_ENDOFWHO_MSG, Client_ID(Client),
289 * Generate WHOIS reply of one actual client.
291 * @param Client The client from which this command has been received.
292 * @param from The client requesting the information ("originator").
293 * @param c The client of which information should be returned.
294 * @return CONNECTED or DISCONNECTED.
297 IRC_WHOIS_SendReply(CLIENT *Client, CLIENT *from, CLIENT *c)
299 char str[COMMAND_LEN];
303 assert(Client != NULL);
304 assert(from != NULL);
307 /* Nick, user, hostname and client info */
308 if (!IRC_WriteStrClient(from, RPL_WHOISUSER_MSG, Client_ID(from),
309 Client_ID(c), Client_User(c),
310 Client_HostnameDisplayed(c), Client_Info(c)))
314 if (!IRC_WriteStrClient(from, RPL_WHOISSERVER_MSG, Client_ID(from),
315 Client_ID(c), Client_ID(Client_Introducer(c)),
316 Client_Info(Client_Introducer(c))))
320 snprintf(str, sizeof(str), RPL_WHOISCHANNELS_MSG,
321 Client_ID(from), Client_ID(c));
322 cl2chan = Channel_FirstChannelOf(c);
324 chan = Channel_GetChannel(cl2chan);
325 assert(chan != NULL);
328 cl2chan = Channel_NextChannelOf(c, cl2chan);
330 /* Secret channel? */
331 if (Channel_HasMode(chan, 's')
332 && !Channel_IsMemberOf(chan, Client))
335 /* Local channel and request is not from a user? */
336 if (Client_Type(Client) == CLIENT_SERVER
337 && Channel_IsLocal(chan))
340 /* Concatenate channel names */
341 if (str[strlen(str) - 1] != ':')
342 strlcat(str, " ", sizeof(str));
344 who_flags_qualifier(Client, Channel_UserModes(chan, c),
346 strlcat(str, Channel_Name(chan), sizeof(str));
348 if (strlen(str) > (COMMAND_LEN - CHANNEL_NAME_LEN - 4)) {
349 /* Line becomes too long: send it! */
350 if (!IRC_WriteStrClient(Client, "%s", str))
352 snprintf(str, sizeof(str), RPL_WHOISCHANNELS_MSG,
353 Client_ID(from), Client_ID(c));
356 if(str[strlen(str) - 1] != ':') {
357 /* There is data left to send: */
358 if (!IRC_WriteStrClient(Client, "%s", str))
363 if (Client_Type(c) == CLIENT_SERVICE &&
364 !IRC_WriteStrClient(from, RPL_WHOISSERVICE_MSG,
365 Client_ID(from), Client_ID(c)))
369 if (Client_Type(c) != CLIENT_SERVICE &&
370 Client_HasMode(c, 'o') &&
371 !IRC_WriteStrClient(from, RPL_WHOISOPERATOR_MSG,
372 Client_ID(from), Client_ID(c)))
376 if (Client_HasMode(c, 'B') &&
377 !IRC_WriteStrClient(from, RPL_WHOISBOT_MSG,
378 Client_ID(from), Client_ID(c)))
381 /* Connected using SSL? */
382 if (Conn_UsesSSL(Client_Conn(c))) {
383 if (!IRC_WriteStrClient(from, RPL_WHOISSSL_MSG, Client_ID(from),
387 /* Certificate fingerprint? */
388 if (Conn_GetCertFp(Client_Conn(c)) &&
390 !IRC_WriteStrClient(from, RPL_WHOISCERTFP_MSG,
391 Client_ID(from), Client_ID(c),
392 Conn_GetCertFp(Client_Conn(c))))
396 /* Registered nickname? */
397 if (Client_HasMode(c, 'R') &&
398 !IRC_WriteStrClient(from, RPL_WHOISREGNICK_MSG,
399 Client_ID(from), Client_ID(c)))
402 /* Account name metadata? */
403 if (Client_AccountName(c) &&
404 !IRC_WriteStrClient(from, RPL_WHOISLOGGEDIN_MSG,
405 Client_ID(from), Client_ID(c),
406 Client_AccountName(c)))
409 /* Local client and requester is the user itself or an IRC Op? */
410 if (Client_Conn(c) > NONE &&
411 (from == c || (!Conf_MorePrivacy && Client_HasMode(from, 'o')))) {
412 /* Client hostname */
413 if (!IRC_WriteStrClient(from, RPL_WHOISHOST_MSG,
414 Client_ID(from), Client_ID(c),
415 Client_Hostname(c), Client_IPAText(c)))
418 if (!IRC_WriteStrClient(from, RPL_WHOISMODES_MSG,
419 Client_ID(from), Client_ID(c), Client_Modes(c)))
423 /* Idle and signon time (local clients only!) */
424 if (!Conf_MorePrivacy && Client_Conn(c) > NONE &&
425 !IRC_WriteStrClient(from, RPL_WHOISIDLE_MSG,
426 Client_ID(from), Client_ID(c),
427 (unsigned long)Conn_GetIdle(Client_Conn(c)),
428 (unsigned long)Conn_GetSignon(Client_Conn(c))))
432 if (Client_HasMode(c, 'a') &&
433 !IRC_WriteStrClient(from, RPL_AWAY_MSG,
434 Client_ID(from), Client_ID(c), Client_Away(c)))
441 WHOWAS_EntryWrite(CLIENT *prefix, WHOWAS *entry)
445 (void)strftime(t_str, sizeof(t_str), "%a %b %d %H:%M:%S %Y",
446 localtime(&entry->time));
448 if (!IRC_WriteStrClient(prefix, RPL_WHOWASUSER_MSG, Client_ID(prefix),
449 entry->id, entry->user, entry->host, entry->info))
452 return IRC_WriteStrClient(prefix, RPL_WHOISSERVER_MSG, Client_ID(prefix),
453 entry->id, entry->server, t_str);
458 Show_MOTD_SSLInfo(CLIENT *Client)
460 char buf[COMMAND_LEN];
463 if (Conn_GetCipherInfo(Client_Conn(Client), c_str, sizeof(c_str))) {
464 snprintf(buf, sizeof(buf), "Connected using Cipher %s", c_str);
465 if (!IRC_WriteStrClient(Client, RPL_MOTD_MSG,
466 Client_ID(Client), buf))
470 if (Conn_GetCertFp(Client_Conn(Client))) {
471 snprintf(buf, sizeof(buf),
472 "Your client certificate fingerprint is: %s",
473 Conn_GetCertFp(Client_Conn(Client)));
474 if (!IRC_WriteStrClient(Client, RPL_MOTD_MSG,
475 Client_ID(Client), buf))
483 Show_MOTD_SSLInfo(UNUSED CLIENT *c)
489 /* Global functions */
492 * Handler for the IRC command "ADMIN".
494 * @param Client The client from which this command has been received.
495 * @param Req Request structure with prefix and all parameters.
496 * @return CONNECTED or DISCONNECTED.
499 IRC_ADMIN(CLIENT *Client, REQUEST *Req )
501 CLIENT *target, *prefix;
503 assert( Client != NULL );
504 assert( Req != NULL );
506 _IRC_GET_SENDER_OR_RETURN_(prefix, Req, Client)
507 _IRC_GET_TARGET_SERVER_OR_RETURN_(target, Req, 0, prefix)
510 if(target != Client_ThisServer()) {
511 IRC_WriteStrClientPrefix(target, prefix,
512 "ADMIN %s", Client_ID(target));
516 if (!IRC_WriteStrClient(Client, RPL_ADMINME_MSG, Client_ID(prefix),
519 if (!IRC_WriteStrClient(Client, RPL_ADMINLOC1_MSG, Client_ID(prefix),
522 if (!IRC_WriteStrClient(Client, RPL_ADMINLOC2_MSG, Client_ID(prefix),
525 if (!IRC_WriteStrClient(Client, RPL_ADMINEMAIL_MSG, Client_ID(prefix),
526 Conf_ServerAdminMail))
533 * Handler for the IRC command "INFO".
535 * @param Client The client from which this command has been received.
536 * @param Req Request structure with prefix and all parameters.
537 * @return CONNECTED or DISCONNECTED.
540 IRC_INFO(CLIENT * Client, REQUEST * Req)
542 CLIENT *target, *prefix;
545 assert(Client != NULL);
548 _IRC_GET_SENDER_OR_RETURN_(prefix, Req, Client)
549 _IRC_GET_TARGET_SERVER_OR_RETURN_(target, Req, 0, prefix)
552 if (target != Client_ThisServer()) {
553 IRC_WriteStrClientPrefix(target, prefix, "INFO %s",
558 if (!IRC_WriteStrClient(Client, RPL_INFO_MSG, Client_ID(prefix),
562 #if defined(__DATE__) && defined(__TIME__)
563 snprintf(msg, sizeof(msg), "Birth Date: %s at %s", __DATE__, __TIME__);
564 if (!IRC_WriteStrClient(Client, RPL_INFO_MSG, Client_ID(prefix), msg))
568 strlcpy(msg, "On-line since ", sizeof(msg));
569 strlcat(msg, NGIRCd_StartStr, sizeof(msg));
570 if (!IRC_WriteStrClient(Client, RPL_INFO_MSG, Client_ID(prefix), msg))
573 if (!IRC_WriteStrClient(Client, RPL_ENDOFINFO_MSG, Client_ID(prefix)))
580 * Handler for the IRC "ISON" command.
582 * @param Client The client from which this command has been received.
583 * @param Req Request structure with prefix and all parameters.
584 * @return CONNECTED or DISCONNECTED.
587 IRC_ISON( CLIENT *Client, REQUEST *Req )
589 char rpl[COMMAND_LEN];
594 assert(Client != NULL);
597 strlcpy(rpl, RPL_ISON_MSG, sizeof rpl);
598 for (i = 0; i < Req->argc; i++) {
599 /* "All" ircd even parse ":<x> <y> ..." arguments and split
600 * them up; so we do the same ... */
601 ptr = strtok(Req->argv[i], " ");
604 c = Client_Search(ptr);
605 if (c && Client_Type(c) == CLIENT_USER) {
606 strlcat(rpl, Client_ID(c), sizeof(rpl));
607 strlcat(rpl, " ", sizeof(rpl));
609 ptr = strtok(NULL, " ");
612 ngt_TrimLastChr(rpl, ' ');
614 return IRC_WriteStrClient(Client, rpl, Client_ID(Client));
618 * Handler for the IRC "LINKS" command.
620 * @param Client The client from which this command has been received.
621 * @param Req Request structure with prefix and all parameters.
622 * @return CONNECTED or DISCONNECTED.
625 IRC_LINKS(CLIENT *Client, REQUEST *Req)
627 CLIENT *target, *from, *c;
630 assert(Client != NULL);
633 _IRC_GET_SENDER_OR_RETURN_(from, Req, Client)
635 /* Get pointer to server mask or "*", if none given */
637 mask = Req->argv[Req->argc - 1];
642 if (Req->argc == 2) {
643 _IRC_GET_TARGET_SERVER_OR_RETURN_(target, Req, 0, from)
644 if (target != Client_ThisServer()) {
645 IRC_WriteStrClientPrefix(target, from,
646 "LINKS %s %s", Client_ID(target),
654 if (Client_Type(c) == CLIENT_SERVER
655 && MatchCaseInsensitive(mask, Client_ID(c))) {
656 if (!IRC_WriteStrClient(from, RPL_LINKS_MSG,
657 Client_ID(from), Client_ID(c),
658 Client_ID(Client_TopServer(c)
659 ? Client_TopServer(c)
660 : Client_ThisServer()),
661 Client_Hops(c), Client_Info(c)))
666 return IRC_WriteStrClient(from, RPL_ENDOFLINKS_MSG,
667 Client_ID(from), mask);
671 * Handler for the IRC "LUSERS" command.
673 * @param Client The client from which this command has been received.
674 * @param Req Request structure with prefix and all parameters.
675 * @return CONNECTED or DISCONNECTED.
678 IRC_LUSERS( CLIENT *Client, REQUEST *Req )
680 CLIENT *target, *from;
682 assert( Client != NULL );
683 assert( Req != NULL );
685 _IRC_GET_SENDER_OR_RETURN_(from, Req, Client)
686 _IRC_GET_TARGET_SERVER_OR_RETURN_(target, Req, 1, from)
689 if (target != Client_ThisServer()) {
690 IRC_WriteStrClientPrefix(target, from,
691 "LUSERS %s %s", Req->argv[0],
696 return IRC_Send_LUSERS(from);
700 * Handler for the IRC command "SERVLIST".
702 * @param Client The client from which this command has been received.
703 * @param Req Request structure with prefix and all parameters.
704 * @return CONNECTED or DISCONNECTED.
707 IRC_SERVLIST(CLIENT *Client, REQUEST *Req)
711 assert(Client != NULL);
714 if (Req->argc < 2 || strcmp(Req->argv[1], "0") == 0) {
715 for (c = Client_First(); c!= NULL; c = Client_Next(c)) {
716 if (Client_Type(c) != CLIENT_SERVICE)
718 if (Req->argc > 0 && !MatchCaseInsensitive(Req->argv[0],
721 if (!IRC_WriteStrClient(Client, RPL_SERVLIST_MSG,
722 Client_ID(Client), Client_Mask(c),
723 Client_Mask(Client_Introducer(c)), "*",
724 0, Client_Hops(c), Client_Info(c)))
729 return IRC_WriteStrClient(Client, RPL_SERVLISTEND_MSG, Client_ID(Client),
730 Req->argc > 0 ? Req->argv[0] : "*",
731 Req->argc > 1 ? Req->argv[1] : "0");
735 * Handler for the IRC command "MOTD".
737 * @param Client The client from which this command has been received.
738 * @param Req Request structure with prefix and all parameters.
739 * @return CONNECTED or DISCONNECTED.
742 IRC_MOTD( CLIENT *Client, REQUEST *Req )
744 CLIENT *from, *target;
746 assert( Client != NULL );
747 assert( Req != NULL );
749 _IRC_GET_SENDER_OR_RETURN_(from, Req, Client)
750 _IRC_GET_TARGET_SERVER_OR_RETURN_(target, Req, 0, from)
753 if (target != Client_ThisServer()) {
754 IRC_WriteStrClientPrefix(target, from, "MOTD %s",
759 return IRC_Show_MOTD(from);
763 * Handler for the IRC command "NAMES".
765 * @param Client The client from which this command has been received.
766 * @param Req Request structure with prefix and all parameters.
767 * @return CONNECTED or DISCONNECTED.
770 IRC_NAMES( CLIENT *Client, REQUEST *Req )
772 char rpl[COMMAND_LEN], *ptr;
773 CLIENT *target, *from, *c;
776 assert( Client != NULL );
777 assert( Req != NULL );
779 _IRC_GET_SENDER_OR_RETURN_(from, Req, Client)
780 _IRC_GET_TARGET_SERVER_OR_RETURN_(target, Req, 1, from)
783 if (target != Client_ThisServer()) {
784 IRC_WriteStrClientPrefix(target, from, "NAMES %s :%s",
785 Req->argv[0], Client_ID(target));
790 /* Return NAMES list for specific channels */
791 ptr = strtok(Req->argv[0], ",");
793 chan = Channel_Search(ptr);
794 if (chan && !IRC_Send_NAMES(from, chan))
796 if (!IRC_WriteStrClient(from, RPL_ENDOFNAMES_MSG,
797 Client_ID(from), ptr))
799 ptr = strtok( NULL, "," );
804 chan = Channel_First();
806 if (!IRC_Send_NAMES(from, chan))
808 chan = Channel_Next(chan);
811 /* Now print all clients which are not in any channel */
813 snprintf(rpl, sizeof(rpl), RPL_NAMREPLY_MSG, Client_ID(from), "*", "*");
815 if (Client_Type(c) == CLIENT_USER
816 && Channel_FirstChannelOf(c) == NULL
817 && !Client_HasMode(c, 'i'))
819 /* its a user, concatenate ... */
820 if (rpl[strlen(rpl) - 1] != ':')
821 strlcat(rpl, " ", sizeof(rpl));
822 strlcat(rpl, Client_ID(c), sizeof(rpl));
824 if (strlen(rpl) > COMMAND_LEN - CLIENT_NICK_LEN - 4) {
825 /* Line is gwoing too long, send now */
826 if (!IRC_WriteStrClient(from, "%s", rpl))
828 snprintf(rpl, sizeof(rpl), RPL_NAMREPLY_MSG,
829 Client_ID(from), "*", "*");
834 if (rpl[strlen(rpl) - 1] != ':' && !IRC_WriteStrClient(from, "%s", rpl))
837 return IRC_WriteStrClient(from, RPL_ENDOFNAMES_MSG, Client_ID(from), "*");
841 * Handler for the IRC command "STATS".
843 * @param Client The client from which this command has been received.
844 * @param Req Request structure with prefix and all parameters.
845 * @return CONNECTED or DISCONNECTED.
848 IRC_STATS( CLIENT *Client, REQUEST *Req )
850 CLIENT *from, *target, *cl;
855 unsigned int days, hrs, mins;
856 struct list_head *list;
857 struct list_elem *list_item;
858 bool more_links = false;
860 assert(Client != NULL);
863 _IRC_GET_SENDER_OR_RETURN_(from, Req, Client)
864 _IRC_GET_TARGET_SERVER_OR_RETURN_(target, Req, 1, from)
867 if (target != Client_ThisServer()) {
868 IRC_WriteStrClientPrefix(target, from, "STATS %s %s",
869 Req->argv[0], Client_ID(target));
874 query = Req->argv[0][0] ? Req->argv[0][0] : '*';
879 case 'g': /* Network-wide bans ("G-Lines") */
881 case 'k': /* Server-local bans ("K-Lines") */
883 if (!Client_HasMode(from, 'o'))
884 return IRC_WriteErrClient(from, ERR_NOPRIVILEGES_MSG,
886 if (query == 'g' || query == 'G')
887 list = Class_GetList(CLASS_GLINE);
889 list = Class_GetList(CLASS_KLINE);
890 list_item = Lists_GetFirst(list);
892 if (!IRC_WriteStrClient(from, RPL_STATSXLINE_MSG,
893 Client_ID(from), query,
894 Lists_GetMask(list_item),
895 Lists_GetValidity(list_item),
896 Lists_GetReason(list_item)))
898 list_item = Lists_GetNext(list_item);
901 case 'L': /* Link status (servers and user links) */
902 if (!Op_Check(from, Req))
903 return Op_NoPrivileges(from, Req);
906 case 'l': /* Link status (servers and own link) */
907 time_now = time(NULL);
908 for (con = Conn_First(); con != NONE; con = Conn_Next(con)) {
909 cl = Conn_GetClient(con);
912 if (Client_Type(cl) == CLIENT_SERVER ||
914 (more_links && Client_Type(cl) == CLIENT_USER)) {
916 if (Conn_Options(con) & CONN_ZIP) {
917 if (!IRC_WriteStrClient
918 (from, RPL_STATSLINKINFOZIP_MSG,
919 Client_ID(from), Client_Mask(cl),
920 Conn_SendQ(con), Conn_SendMsg(con),
926 (long)(time_now - Conn_StartTime(con))))
931 if (!IRC_WriteStrClient
932 (from, RPL_STATSLINKINFO_MSG,
933 Client_ID(from), Client_Mask(cl),
934 Conn_SendQ(con), Conn_SendMsg(con),
935 Conn_SendBytes(con), Conn_RecvMsg(con),
937 (long)(time_now - Conn_StartTime(con))))
942 case 'm': /* IRC command status (usage count) */
944 cmd = Parse_GetCommandStruct();
945 for (; cmd->name; cmd++) {
946 if (cmd->lcount == 0 && cmd->rcount == 0)
948 if (!IRC_WriteStrClient
949 (from, RPL_STATSCOMMANDS_MSG, Client_ID(from),
950 cmd->name, cmd->lcount, cmd->bytes, cmd->rcount))
954 case 'u': /* Server uptime */
956 time_now = time(NULL) - NGIRCd_Start;
957 days = uptime_days(&time_now);
958 hrs = uptime_hrs(&time_now);
959 mins = uptime_mins(&time_now);
960 if (!IRC_WriteStrClient(from, RPL_STATSUPTIME, Client_ID(from),
961 days, hrs, mins, (unsigned int)time_now))
966 return IRC_WriteStrClient(from, RPL_ENDOFSTATS_MSG,
967 Client_ID(from), query);
971 * Handler for the IRC command "SUMMON".
973 * @param Client The client from which this command has been received.
974 * @param Req Request structure with prefix and all parameters.
975 * @return CONNECTED or DISCONNECTED.
978 IRC_SUMMON(CLIENT * Client, UNUSED REQUEST * Req)
980 assert(Client != NULL);
982 return IRC_WriteErrClient(Client, ERR_SUMMONDISABLED_MSG,
987 * Handler for the IRC command "TIME".
989 * @param Client The client from which this command has been received.
990 * @param Req Request structure with prefix and all parameters.
991 * @return CONNECTED or DISCONNECTED.
994 IRC_TIME( CLIENT *Client, REQUEST *Req )
996 CLIENT *from, *target;
1000 assert(Client != NULL);
1001 assert(Req != NULL);
1003 _IRC_GET_SENDER_OR_RETURN_(from, Req, Client)
1004 _IRC_GET_TARGET_SERVER_OR_RETURN_(target, Req, 0, from)
1007 if (target != Client_ThisServer()) {
1008 IRC_WriteStrClientPrefix(target, from, "TIME %s",
1014 (void)strftime(t_str, 60, "%A %B %d %Y -- %H:%M %Z", localtime(&t));
1015 return IRC_WriteStrClient(from, RPL_TIME_MSG, Client_ID(from),
1016 Client_ID(Client_ThisServer()), t_str);
1020 * Handler for the IRC command "USERHOST".
1022 * @param Client The client from which this command has been received.
1023 * @param Req Request structure with prefix and all parameters.
1024 * @return CONNECTED or DISCONNECTED.
1027 IRC_USERHOST(CLIENT *Client, REQUEST *Req)
1029 char rpl[COMMAND_LEN];
1033 assert(Client != NULL);
1034 assert(Req != NULL);
1041 strlcpy(rpl, RPL_USERHOST_MSG, sizeof rpl);
1042 for (i = 0; i < max; i++) {
1043 c = Client_Search(Req->argv[i]);
1044 if (c && (Client_Type(c) == CLIENT_USER)) {
1045 /* This Nick is "online" */
1046 strlcat(rpl, Client_ID(c), sizeof(rpl));
1047 if (Client_HasMode(c, 'o'))
1048 strlcat(rpl, "*", sizeof(rpl));
1049 strlcat(rpl, "=", sizeof(rpl));
1050 if (Client_HasMode(c, 'a'))
1051 strlcat(rpl, "-", sizeof(rpl));
1053 strlcat(rpl, "+", sizeof(rpl));
1054 strlcat(rpl, Client_User(c), sizeof(rpl));
1055 strlcat(rpl, "@", sizeof(rpl));
1056 strlcat(rpl, Client_HostnameDisplayed(c), sizeof(rpl));
1057 strlcat(rpl, " ", sizeof(rpl));
1060 ngt_TrimLastChr(rpl, ' ');
1062 return IRC_WriteStrClient(Client, rpl, Client_ID(Client));
1063 } /* IRC_USERHOST */
1066 * Handler for the IRC command "USERS".
1068 * @param Client The client from which this command has been received.
1069 * @param Req Request structure with prefix and all parameters.
1070 * @return CONNECTED or DISCONNECTED.
1073 IRC_USERS(CLIENT * Client, UNUSED REQUEST * Req)
1075 assert(Client != NULL);
1077 return IRC_WriteErrClient(Client, ERR_USERSDISABLED_MSG,
1082 * Handler for the IRC command "VERSION".
1084 * @param Client The client from which this command has been received.
1085 * @param Req Request structure with prefix and all parameters.
1086 * @return CONNECTED or DISCONNECTED.
1089 IRC_VERSION( CLIENT *Client, REQUEST *Req )
1091 CLIENT *target, *prefix;
1093 assert( Client != NULL );
1094 assert( Req != NULL );
1096 _IRC_GET_SENDER_OR_RETURN_(prefix, Req, Client)
1097 _IRC_GET_TARGET_SERVER_OR_RETURN_(target, Req, 0, prefix)
1100 if (target != Client_ThisServer()) {
1101 IRC_WriteStrClientPrefix(target, prefix, "VERSION %s",
1106 /* send version information */
1107 if (!IRC_WriteStrClient(Client, RPL_VERSION_MSG, Client_ID(prefix),
1108 PACKAGE_NAME, PACKAGE_VERSION,
1109 NGIRCd_DebugLevel, Conf_ServerName,
1110 NGIRCd_VersionAddition))
1111 return DISCONNECTED;
1114 /* send RPL_ISUPPORT(005) numerics */
1115 if (!IRC_Send_ISUPPORT(prefix))
1116 return DISCONNECTED;
1123 * Handler for the IRC "WHO" command.
1125 * @param Client The client from which this command has been received.
1126 * @param Req Request structure with prefix and all parameters.
1127 * @return CONNECTED or DISCONNECTED.
1130 IRC_WHO(CLIENT *Client, REQUEST *Req)
1135 assert (Client != NULL);
1136 assert (Req != NULL);
1139 if (Req->argc == 2) {
1140 if (strcmp(Req->argv[1], "o") == 0)
1144 return IRC_WriteErrClient(Client,
1145 ERR_NEEDMOREPARAMS_MSG,
1152 if (Req->argc >= 1) {
1153 /* Channel or mask given */
1154 chan = Channel_Search(Req->argv[0]);
1156 /* Members of a channel have been requested */
1157 return IRC_WHO_Channel(Client, chan, only_ops);
1159 if (strcmp(Req->argv[0], "0") != 0) {
1160 /* A mask has been given. But please note this RFC
1161 * stupidity: "0" is same as no arguments ... */
1162 return IRC_WHO_Mask(Client, Req->argv[0], only_ops);
1166 /* No channel or (valid) mask given */
1167 return IRC_WHO_Mask(Client, NULL, only_ops);
1171 * Handler for the IRC "WHOIS" command.
1173 * @param Client The client from which this command has been received.
1174 * @param Req Request structure with prefix and all parameters.
1175 * @return CONNECTED or DISCONNECTED.
1178 IRC_WHOIS( CLIENT *Client, REQUEST *Req )
1180 CLIENT *from, *target, *c;
1181 unsigned int match_count = 0, found = 0;
1182 bool has_wildcards, is_remote;
1183 bool got_wildcard = false;
1184 char mask[COMMAND_LEN], *query;
1186 assert( Client != NULL );
1187 assert( Req != NULL );
1189 /* Wrong number of parameters? */
1191 return IRC_WriteErrClient(Client, ERR_NONICKNAMEGIVEN_MSG,
1194 _IRC_ARGC_LE_OR_RETURN_(Client, Req, 2)
1195 _IRC_GET_SENDER_OR_RETURN_(from, Req, Client)
1197 /* Get target server for this command */
1198 if (Req->argc > 1) {
1199 _IRC_GET_TARGET_SERVER_OR_RETURN_(target, Req, 0, Client)
1201 target = Client_ThisServer();
1203 assert(target != NULL);
1206 if (target != Client_ThisServer()) {
1207 IRC_WriteStrClientPrefix(target, from, "WHOIS %s :%s",
1208 Req->argv[0], Req->argv[1]);
1212 is_remote = Client_Conn(from) < 0;
1213 strlcpy(mask, Req->argv[Req->argc - 1], sizeof(mask));
1214 for (query = strtok(ngt_LowerStr(mask), ",");
1216 query = strtok(NULL, ","), found++)
1218 has_wildcards = query[strcspn(query, "*?")] != 0;
1220 * follows ircd 2.10 implementation:
1221 * - handle up to 3 targets
1222 * - no wildcards for remote clients
1223 * - only one wildcard target per local client
1225 * Also, at most MAX_RPL_WHOIS matches are returned.
1227 if (!has_wildcards || is_remote) {
1228 c = Client_Search(query);
1229 if (c && (Client_Type(c) == CLIENT_USER
1230 || Client_Type(c) == CLIENT_SERVICE)) {
1231 if (!IRC_WHOIS_SendReply(Client, from, c))
1232 return DISCONNECTED;
1234 if (!IRC_WriteErrClient(Client,
1238 return DISCONNECTED;
1243 /* we already handled one wildcard query */
1244 if (!IRC_WriteErrClient(Client, ERR_NOSUCHNICK_MSG,
1245 Client_ID(Client), query))
1246 return DISCONNECTED;
1249 got_wildcard = true;
1250 /* Increase penalty for wildcard queries */
1251 IRC_SetPenalty(Client, 3);
1253 for (c = Client_First(); c; c = Client_Next(c)) {
1254 if (IRC_CheckListTooBig(Client, match_count,
1255 MAX_RPL_WHOIS, "WHOIS"))
1258 if (Client_Type(c) != CLIENT_USER)
1260 if (!MatchCaseInsensitive(query, Client_ID(c)))
1262 if (!IRC_WHOIS_SendReply(Client, from, c))
1263 return DISCONNECTED;
1268 if (match_count == 0)
1269 IRC_WriteErrClient(Client, ERR_NOSUCHNICK_MSG,
1271 Req->argv[Req->argc - 1]);
1273 return IRC_WriteStrClient(from, RPL_ENDOFWHOIS_MSG,
1274 Client_ID(from), Req->argv[Req->argc - 1]);
1278 * Handler for the IRC "WHOWAS" command.
1280 * @param Client The client from which this command has been received.
1281 * @param Req Request structure with prefix and all parameters.
1282 * @return CONNECTED or DISCONNECTED.
1285 IRC_WHOWAS( CLIENT *Client, REQUEST *Req )
1287 CLIENT *target, *prefix;
1289 char tok_buf[COMMAND_LEN];
1290 int max, last, count, i, nc;
1293 assert( Client != NULL );
1294 assert( Req != NULL );
1296 /* Wrong number of parameters? */
1298 return IRC_WriteErrClient(Client, ERR_NONICKNAMEGIVEN_MSG,
1301 _IRC_ARGC_LE_OR_RETURN_(Client, Req, 3)
1302 _IRC_GET_SENDER_OR_RETURN_(prefix, Req, Client)
1303 _IRC_GET_TARGET_SERVER_OR_RETURN_(target, Req, 2, prefix)
1305 /* Do not reveal any info on disconnected users? */
1306 if (Conf_MorePrivacy)
1310 if (target != Client_ThisServer()) {
1311 IRC_WriteStrClientPrefix(target, prefix, "WHOWAS %s %s %s",
1312 Req->argv[0], Req->argv[1],
1317 whowas = Client_GetWhowas( );
1318 last = Client_GetLastWhowasIndex( );
1322 max = DEF_RPL_WHOWAS;
1323 if (Req->argc > 1) {
1324 max = atoi(Req->argv[1]);
1326 max = MAX_RPL_WHOWAS;
1330 * Break up the nick argument into a list of nicks, if applicable
1331 * Can't modify Req->argv[0] because we need it for RPL_ENDOFWHOWAS_MSG.
1333 strlcpy(tok_buf, Req->argv[0], sizeof(tok_buf));
1334 nick = strtok(tok_buf, ",");
1336 for (i=last, count=0; nick != NULL ; nick = strtok(NULL, ",")) {
1340 if (whowas[i].time > 0 && strcasecmp(nick, whowas[i].id) == 0) {
1341 if (!WHOWAS_EntryWrite(prefix, &whowas[i]))
1342 return DISCONNECTED;
1346 /* previous entry */
1349 /* "underflow", wrap around */
1353 if (nc && count >= max)
1355 } while (i != last);
1357 if (nc == 0 && !IRC_WriteErrClient(prefix, ERR_WASNOSUCHNICK_MSG,
1358 Client_ID(prefix), nick))
1359 return DISCONNECTED;
1361 return IRC_WriteStrClient(prefix, RPL_ENDOFWHOWAS_MSG,
1362 Client_ID(prefix), Req->argv[0]);
1366 * Send LUSERS reply to a client.
1368 * @param Client The receipient of the information.
1369 * @return CONNECTED or DISCONNECTED.
1372 IRC_Send_LUSERS(CLIENT *Client)
1379 assert(Client != NULL);
1381 /* Users, services and servers in the network */
1382 if (!IRC_WriteStrClient(Client, RPL_LUSERCLIENT_MSG, Client_ID(Client),
1383 Client_UserCount(), Client_ServiceCount(),
1384 Client_ServerCount()))
1385 return DISCONNECTED;
1387 /* Number of IRC operators */
1388 cnt = Client_OperCount( );
1390 if (!IRC_WriteStrClient(Client, RPL_LUSEROP_MSG,
1391 Client_ID(Client), cnt))
1392 return DISCONNECTED;
1395 /* Unknown connections */
1396 cnt = Client_UnknownCount( );
1398 if (!IRC_WriteStrClient(Client, RPL_LUSERUNKNOWN_MSG,
1399 Client_ID(Client), cnt))
1400 return DISCONNECTED;
1403 /* Number of created channels */
1404 if (!IRC_WriteStrClient(Client, RPL_LUSERCHANNELS_MSG,
1406 Channel_CountVisible(Client)))
1407 return DISCONNECTED;
1409 /* Number of local users, services and servers */
1410 if (!IRC_WriteStrClient(Client, RPL_LUSERME_MSG, Client_ID(Client),
1411 Client_MyUserCount(), Client_MyServiceCount(),
1412 Client_MyServerCount()))
1413 return DISCONNECTED;
1416 /* Maximum number of local users */
1417 cnt = Client_MyUserCount();
1418 max = Client_MyMaxUserCount();
1419 if (! IRC_WriteStrClient(Client, RPL_LOCALUSERS_MSG, Client_ID(Client),
1420 cnt, max, cnt, max))
1421 return DISCONNECTED;
1422 /* Maximum number of users in the network */
1423 cnt = Client_UserCount();
1424 max = Client_MaxUserCount();
1425 if(! IRC_WriteStrClient(Client, RPL_NETUSERS_MSG, Client_ID(Client),
1426 cnt, max, cnt, max))
1427 return DISCONNECTED;
1428 /* Connection counters */
1429 if (! IRC_WriteStrClient(Client, RPL_STATSCONN_MSG, Client_ID(Client),
1430 Conn_CountMax(), Conn_CountAccepted()))
1431 return DISCONNECTED;
1435 } /* IRC_Send_LUSERS */
1438 IRC_Show_MOTD( CLIENT *Client )
1441 size_t len_tot, len_str;
1443 assert( Client != NULL );
1445 len_tot = array_bytes(&Conf_Motd);
1446 if (len_tot == 0 && !Conn_UsesSSL(Client_Conn(Client)))
1447 return IRC_WriteErrClient(Client, ERR_NOMOTD_MSG, Client_ID(Client));
1449 if (!IRC_WriteStrClient(Client, RPL_MOTDSTART_MSG, Client_ID(Client),
1450 Client_ID(Client_ThisServer())))
1451 return DISCONNECTED;
1453 line = array_start(&Conf_Motd);
1454 while (len_tot > 0) {
1455 len_str = strlen(line) + 1;
1457 assert(len_tot >= len_str);
1460 if (!IRC_WriteStrClient(Client, RPL_MOTD_MSG, Client_ID(Client), line))
1461 return DISCONNECTED;
1465 if (!Show_MOTD_SSLInfo(Client))
1466 return DISCONNECTED;
1468 if (!IRC_WriteStrClient(Client, RPL_ENDOFMOTD_MSG, Client_ID(Client)))
1469 return DISCONNECTED;
1471 if (*Conf_CloakHost)
1472 return IRC_WriteStrClient(Client, RPL_HOSTHIDDEN_MSG,
1474 Client_Hostname(Client));
1477 } /* IRC_Show_MOTD */
1480 * Send NAMES reply for a specific client and channel.
1482 * @param Client The client requesting the NAMES information.
1483 * @param Chan The channel
1484 * @return CONNECTED or DISCONNECTED.
1487 IRC_Send_NAMES(CLIENT * Client, CHANNEL * Chan)
1489 bool is_visible, is_member;
1490 char str[COMMAND_LEN];
1494 assert(Client != NULL);
1495 assert(Chan != NULL);
1497 if (Channel_IsMemberOf(Chan, Client))
1502 /* Do not print info on channel memberships to anyone that is not member? */
1503 if (Conf_MorePrivacy && !is_member)
1506 /* Secret channel? */
1507 if (!is_member && Channel_HasMode(Chan, 's'))
1510 snprintf(str, sizeof(str), RPL_NAMREPLY_MSG, Client_ID(Client), "=",
1511 Channel_Name(Chan));
1512 cl2chan = Channel_FirstMember(Chan);
1514 cl = Channel_GetClient(cl2chan);
1516 if (Client_HasMode(cl, 'i'))
1521 if (is_member || is_visible) {
1522 if (str[strlen(str) - 1] != ':')
1523 strlcat(str, " ", sizeof(str));
1525 who_flags_qualifier(Client, Channel_UserModes(Chan, cl),
1527 strlcat(str, Client_ID(cl), sizeof(str));
1529 if (strlen(str) > (COMMAND_LEN - CLIENT_NICK_LEN - 4)) {
1530 if (!IRC_WriteStrClient(Client, "%s", str))
1531 return DISCONNECTED;
1532 snprintf(str, sizeof(str), RPL_NAMREPLY_MSG,
1533 Client_ID(Client), "=",
1534 Channel_Name(Chan));
1538 cl2chan = Channel_NextMember(Chan, cl2chan);
1540 if (str[strlen(str) - 1] != ':') {
1541 if (!IRC_WriteStrClient(Client, "%s", str))
1542 return DISCONNECTED;
1546 } /* IRC_Send_NAMES */
1549 * Send the ISUPPORT numeric (005).
1550 * This numeric indicates the features that are supported by this server.
1551 * See <http://www.irc.org/tech_docs/005.html> for details.
1554 IRC_Send_ISUPPORT(CLIENT * Client)
1556 if (Conf_Network[0] && !IRC_WriteStrClient(Client, RPL_ISUPPORTNET_MSG,
1559 return DISCONNECTED;
1560 if (!IRC_WriteStrClient(Client, RPL_ISUPPORT1_MSG, Client_ID(Client),
1561 CHANTYPES, CHANTYPES, Conf_MaxJoins))
1562 return DISCONNECTED;
1563 return IRC_WriteStrClient(Client, RPL_ISUPPORT2_MSG, Client_ID(Client),
1564 CHANNEL_NAME_LEN - 1, Conf_MaxNickLength - 1,
1565 COMMAND_LEN - 23, CLIENT_AWAY_LEN - 1,
1566 COMMAND_LEN - 113, MAX_HNDL_MODES_ARG,
1567 MAX_HNDL_CHANNEL_LISTS);
1568 } /* IRC_Send_ISUPPORT */