From: Alexander Barton Date: Sat, 31 Mar 2012 13:59:06 +0000 (+0200) Subject: Implement core IRC capability handling and "CAP" command X-Git-Tag: rel-19.2-rc1~20^2^2~6 X-Git-Url: https://arthur.barton.de/cgi-bin/gitweb.cgi?p=ngircd.git;a=commitdiff_plain;h=bd3a7ccb158c9f2eac1af77804529b76d99c3e79 Implement core IRC capability handling and "CAP" command This patch implements the core functions to support "IRC Capabilities" and the IRC "CAP" command as used by other servers and specified here: . It enables ngIRCd to support the defined handshake, but it doesn't implement any capabilities, so "CAP LS" and "CAP LIST" always return the empty set and "CAP REQ ..." always fails with "CAP NAK". --- diff --git a/contrib/MacOSX/ngIRCd.xcodeproj/project.pbxproj b/contrib/MacOSX/ngIRCd.xcodeproj/project.pbxproj index d3098f4d..d89d3792 100644 --- a/contrib/MacOSX/ngIRCd.xcodeproj/project.pbxproj +++ b/contrib/MacOSX/ngIRCd.xcodeproj/project.pbxproj @@ -41,6 +41,8 @@ FAA3D27B0F139CDC00B2447E /* conn-ssl.c in Sources */ = {isa = PBXBuildFile; fileRef = FAA3D2790F139CDC00B2447E /* conn-ssl.c */; }; FAA97C57124A271400D5BBA9 /* sighandlers.c in Sources */ = {isa = PBXBuildFile; fileRef = FAA97C55124A271400D5BBA9 /* sighandlers.c */; }; FAACD5F514A6099C006ED74F /* class.c in Sources */ = {isa = PBXBuildFile; fileRef = FAACD5F314A6099C006ED74F /* class.c */; }; + FAD5853215271AAB00328741 /* client-cap.c in Sources */ = {isa = PBXBuildFile; fileRef = FAD5853015271AAB00328741 /* client-cap.c */; }; + FAD5853515271AB800328741 /* irc-cap.c in Sources */ = {isa = PBXBuildFile; fileRef = FAD5853315271AB800328741 /* irc-cap.c */; }; FAD5853815272C2600328741 /* login.c in Sources */ = {isa = PBXBuildFile; fileRef = FAD5853615272C2500328741 /* login.c */; }; FAE5CC2E0CF2308A007D69B6 /* numeric.c in Sources */ = {isa = PBXBuildFile; fileRef = FAE5CC2D0CF2308A007D69B6 /* numeric.c */; }; /* End PBXBuildFile section */ @@ -232,6 +234,11 @@ FAA97C56124A271400D5BBA9 /* sighandlers.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; path = sighandlers.h; sourceTree = ""; }; FAACD5F314A6099C006ED74F /* class.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = class.c; sourceTree = ""; }; FAACD5F414A6099C006ED74F /* class.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = class.h; sourceTree = ""; }; + FAD5852F15271A7800328741 /* Capabilities.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = Capabilities.txt; sourceTree = ""; }; + FAD5853015271AAB00328741 /* client-cap.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "client-cap.c"; sourceTree = ""; }; + FAD5853115271AAB00328741 /* client-cap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "client-cap.h"; sourceTree = ""; }; + FAD5853315271AB800328741 /* irc-cap.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "irc-cap.c"; sourceTree = ""; }; + FAD5853415271AB800328741 /* irc-cap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "irc-cap.h"; sourceTree = ""; }; FAD5853615272C2500328741 /* login.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = login.c; sourceTree = ""; }; FAD5853715272C2500328741 /* login.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = login.h; sourceTree = ""; }; FAE22BD215270EA300F1A5AB /* Bopm.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = Bopm.txt; sourceTree = ""; }; @@ -318,6 +325,8 @@ FAACD5F414A6099C006ED74F /* class.h */, FA322CDD0CEF74B1001761B3 /* client.c */, FA322CDE0CEF74B1001761B3 /* client.h */, + FAD5853015271AAB00328741 /* client-cap.c */, + FAD5853115271AAB00328741 /* client-cap.h */, FA322CDF0CEF74B1001761B3 /* conf.c */, FA322CE00CEF74B1001761B3 /* conf.h */, FAA3D2780F139CDC00B2447E /* conf-ssl.h */, @@ -332,6 +341,8 @@ FA322CE90CEF74B1001761B3 /* hash.h */, FA322CEA0CEF74B1001761B3 /* io.c */, FA322CEB0CEF74B1001761B3 /* io.h */, + FAD5853315271AB800328741 /* irc-cap.c */, + FAD5853415271AB800328741 /* irc-cap.h */, FA322CEC0CEF74B1001761B3 /* irc-channel.c */, FA322CED0CEF74B1001761B3 /* irc-channel.h */, FA322CEE0CEF74B1001761B3 /* irc-info.c */, @@ -576,6 +587,7 @@ children = ( FA322D9B0CEF752C001761B3 /* Makefile.am */, FAE22BD215270EA300F1A5AB /* Bopm.txt */, + FAD5852F15271A7800328741 /* Capabilities.txt */, FAE22BD415270EA300F1A5AB /* Contributing.txt */, FA322D9A0CEF752C001761B3 /* FAQ.txt */, FA407F380DB15AC700271AF1 /* GIT.txt */, @@ -735,6 +747,8 @@ FA2D564A11EA158B00D37A35 /* pam.c in Sources */, FAA97C57124A271400D5BBA9 /* sighandlers.c in Sources */, FAACD5F514A6099C006ED74F /* class.c in Sources */, + FAD5853215271AAB00328741 /* client-cap.c in Sources */, + FAD5853515271AB800328741 /* irc-cap.c in Sources */, FAD5853815272C2600328741 /* login.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/doc/Capabilities.txt b/doc/Capabilities.txt new file mode 100644 index 00000000..9a692ea6 --- /dev/null +++ b/doc/Capabilities.txt @@ -0,0 +1,23 @@ + + ngIRCd - Next Generation IRC Server + http://ngircd.barton.de/ + + (c)2001-2012 Alexander Barton and Contributors. + ngIRCd is free software and published under the + terms of the GNU General Public License. + + -- Capabilities.txt -- + + +This document lists and describes the "IRC capabilities" that ngIRCd supports +and can be requested by a IRC/IRCv3 client that supports the "CAP" command. + +ngIRCd implements the "IRC Client Capabilities Extension" as described here: + + + +I. Supported Capabilities +~~~~~~~~~~~~~~~~~~~~~~~~~ + +None. At the moment, ngIRCd supports the "CAP" command and its sub-commands +but offers no capabilities that could be requested by a client. diff --git a/doc/Makefile.am b/doc/Makefile.am index 1a792c5f..92e019b8 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -18,6 +18,7 @@ SUFFIXES = .tmpl static_docs = \ Bopm.txt \ + Capabilities.txt \ FAQ.txt \ GIT.txt \ HowToRelease.txt \ diff --git a/src/ngircd/Makefile.am b/src/ngircd/Makefile.am index e96d14be..3a411a96 100644 --- a/src/ngircd/Makefile.am +++ b/src/ngircd/Makefile.am @@ -24,6 +24,7 @@ ngircd_SOURCES = \ channel.c \ class.c \ client.c \ + client-cap.c \ conf.c \ conn.c \ conn-func.c \ @@ -32,6 +33,7 @@ ngircd_SOURCES = \ hash.c \ io.c \ irc.c \ + irc-cap.c \ irc-channel.c \ irc-info.c \ irc-login.c \ @@ -62,6 +64,7 @@ noinst_HEADERS = \ channel.h \ class.h \ client.h \ + client-cap.h \ conf.h \ conf-ssl.h \ conn.h \ @@ -72,6 +75,7 @@ noinst_HEADERS = \ hash.h \ io.h \ irc.h \ + irc-cap.h \ irc-channel.h \ irc-info.h \ irc-login.h \ diff --git a/src/ngircd/client-cap.c b/src/ngircd/client-cap.c new file mode 100644 index 00000000..edaf2603 --- /dev/null +++ b/src/ngircd/client-cap.c @@ -0,0 +1,62 @@ +/* + * ngIRCd -- The Next Generation IRC Daemon + * Copyright (c)2001-2012 Alexander Barton (alex@barton.de) and Contributors. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * Please read the file COPYING, README and AUTHORS for more information. + */ + +#define __client_cap_c__ + +#include "portab.h" + +/** + * @file + * Functions to deal with IRC Capabilities + */ + +#include "imp.h" +#include + +#include "defines.h" +#include "conn.h" +#include "client.h" +#include "log.h" + +#include "exp.h" +#include "client-cap.h" + +GLOBAL int +Client_Cap(CLIENT *Client) +{ + assert (Client != NULL); + + return Client->capabilities; +} + +GLOBAL void +Client_CapAdd(CLIENT *Client, int Cap) +{ + assert(Client != NULL); + assert(Cap > 0); + + Client->capabilities |= Cap; + LogDebug("Add capability %d, new capability of \"%s\" is %d.", + Cap, Client_ID(Client), Client->capabilities); +} + +GLOBAL void +Client_CapDel(CLIENT *Client, int Cap) +{ + assert(Client != NULL); + assert(Cap > 0); + + Client->capabilities &= ~Cap; + LogDebug("Delete capability %d, new capability of \"%s\" is %d.", + Cap, Client_ID(Client), Client->capabilities); +} + +/* -eof- */ diff --git a/src/ngircd/client-cap.h b/src/ngircd/client-cap.h new file mode 100644 index 00000000..faec1c20 --- /dev/null +++ b/src/ngircd/client-cap.h @@ -0,0 +1,28 @@ +/* + * ngIRCd -- The Next Generation IRC Daemon + * Copyright (c)2001-2012 Alexander Barton (alex@barton.de) and Contributors. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * Please read the file COPYING, README and AUTHORS for more information. + */ + +#ifndef __client_cap_h__ +#define __client_cap_h__ + +/** + * @file + * Functions to deal with IRC Capabilities (header) + */ + +#define CLIENT_CAP_PENDING 1 /* Capability negotiation pending */ +#define CLIENT_CAP_SUPPORTED 2 /* Client supports IRC capabilities */ + +GLOBAL int Client_Cap PARAMS((CLIENT *Client)); + +GLOBAL void Client_CapAdd PARAMS((CLIENT *Client, int Cap)); +GLOBAL void Client_CapDel PARAMS((CLIENT *Client, int Cap)); + +#endif diff --git a/src/ngircd/client.h b/src/ngircd/client.h index def0549c..bdad9ce9 100644 --- a/src/ngircd/client.h +++ b/src/ngircd/client.h @@ -34,7 +34,7 @@ #include "defines.h" -#if defined(__client_c__) | defined(S_SPLINT_S) +#if defined(__client_c__) | defined(__client_cap_c__) | defined(S_SPLINT_S) typedef struct _CLIENT { @@ -58,6 +58,7 @@ typedef struct _CLIENT bool oper_by_me; /* client is local IRC operator on this server? */ char away[CLIENT_AWAY_LEN]; /* AWAY text (valid if mode 'a' is set) */ char flags[CLIENT_FLAGS_LEN]; /* flags of the client */ + int capabilities; /* enabled IRC capabilities */ } CLIENT; #else diff --git a/src/ngircd/irc-cap.c b/src/ngircd/irc-cap.c new file mode 100644 index 00000000..926943c8 --- /dev/null +++ b/src/ngircd/irc-cap.c @@ -0,0 +1,192 @@ +/* + * ngIRCd -- The Next Generation IRC Daemon + * Copyright (c)2001-2012 Alexander Barton (alex@barton.de) and Contributors. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * Please read the file COPYING, README and AUTHORS for more information. + */ + +#include "portab.h" + +/** + * @file + * Handler for IRC capability ("CAP") commands + */ + +#include "imp.h" +#include +#include + +#include "defines.h" +#include "conn.h" +#include "channel.h" +#include "client-cap.h" +#include "irc-write.h" +#include "log.h" +#include "login.h" +#include "messages.h" +#include "parse.h" + +#include "exp.h" +#include "irc-cap.h" + +bool Handle_CAP_LS PARAMS((CLIENT *Client, char *Arg)); +bool Handle_CAP_LIST PARAMS((CLIENT *Client, char *Arg)); +bool Handle_CAP_REQ PARAMS((CLIENT *Client, char *Arg)); +bool Handle_CAP_ACK PARAMS((CLIENT *Client, char *Arg)); +bool Handle_CAP_CLEAR PARAMS((CLIENT *Client)); +bool Handle_CAP_END PARAMS((CLIENT *Client)); + +/** + * Handler for the IRCv3 "CAP" command. + * + * @param Client The client from which this command has been received. + * @param Req Request structure with prefix and all parameters. + * @returns CONNECTED or DISCONNECTED. + */ +GLOBAL bool +IRC_CAP(CLIENT *Client, REQUEST *Req) +{ + assert(Client != NULL); + assert(Req != NULL); + + /* Bad number of prameters? */ + if (Req->argc < 1 || Req->argc > 2) + return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG, + Client_ID(Client), Req->command); + + LogDebug("Got \"%s %s\" command from \"%s\" ...", + Req->command, Req->argv[0], Client_ID(Client)); + + if (Req->argc == 1) { + if (strcasecmp(Req->argv[0], "CLEAR") == 0) + return Handle_CAP_CLEAR(Client); + if (strcasecmp(Req->argv[0], "END") == 0) + return Handle_CAP_END(Client); + } + if (Req->argc >= 1 && Req->argc <= 2) { + if (strcasecmp(Req->argv[0], "LS") == 0) + return Handle_CAP_LS(Client, Req->argv[1]); + if (strcasecmp(Req->argv[0], "LIST") == 0) + return Handle_CAP_LIST(Client, Req->argv[1]); + } + if (Req->argc == 2) { + if (strcasecmp(Req->argv[0], "REQ") == 0) + return Handle_CAP_REQ(Client, Req->argv[1]); + if (strcasecmp(Req->argv[0], "ACK") == 0) + return Handle_CAP_ACK(Client, Req->argv[1]); + } + + return IRC_WriteStrClient(Client, ERR_INVALIDCAP_MSG, + Client_ID(Client), Req->argv[0]); +} + +/** + * Handler for the "CAP LS" command. + * + * @param Client The client from which this command has been received. + * @param Arg Command argument or NULL. + * @returns CONNECTED or DISCONNECTED. + */ +bool +Handle_CAP_LS(CLIENT *Client, UNUSED char *Arg) +{ + assert(Client != NULL); + + if (Client_Type(Client) != CLIENT_USER) + Client_CapAdd(Client, CLIENT_CAP_PENDING); + + Client_CapAdd(Client, CLIENT_CAP_SUPPORTED); + return IRC_WriteStrClient(Client, "CAP %s LS :", Client_ID(Client)); +} + +/** + * Handler for the "CAP LIST" command. + * + * @param Client The client from which this command has been received. + * @param Arg Command argument or NULL. + * @returns CONNECTED or DISCONNECTED. + */ +bool +Handle_CAP_LIST(CLIENT *Client, UNUSED char *Arg) +{ + assert(Client != NULL); + + return IRC_WriteStrClient(Client, "CAP %s LIST :", Client_ID(Client)); +} + +/** + * Handler for the "CAP REQ" command. + * + * @param Client The client from which this command has been received. + * @param Arg Command argument. + * @returns CONNECTED or DISCONNECTED. + */ +bool +Handle_CAP_REQ(CLIENT *Client, char *Arg) +{ + assert(Client != NULL); + assert(Arg != NULL); + + return IRC_WriteStrClient(Client, "CAP %s NAK :%s", + Client_ID(Client), Arg); +} + +/** + * Handler for the "CAP ACK" command. + * + * @param Client The client from which this command has been received. + * @param Arg Command argument. + * @returns CONNECTED or DISCONNECTED. + */ +bool +Handle_CAP_ACK(CLIENT *Client, char *Arg) +{ + assert(Client != NULL); + assert(Arg != NULL); + + return CONNECTED; +} + +/** + * Handler for the "CAP CLEAR" command. + * + * @param Client The client from which this command has been received. + * @returns CONNECTED or DISCONNECTED. + */ +bool +Handle_CAP_CLEAR(CLIENT *Client) +{ + assert(Client != NULL); + + return IRC_WriteStrClient(Client, "CAP %s ACK :", Client_ID(Client)); +} + +/** + * Handler for the "CAP END" command. + * + * @param Client The client from which this command has been received. + * @returns CONNECTED or DISCONNECTED. + */ +bool +Handle_CAP_END(CLIENT *Client) +{ + assert(Client != NULL); + + if (Client_Type(Client) != CLIENT_USER) { + /* User is still logging in ... */ + Client_CapDel(Client, CLIENT_CAP_PENDING); + + if (Client_Type(Client) == CLIENT_GOTUSER) { + /* Only "CAP END" was missing: log in! */ + return Login_User(Client); + } + } + + return CONNECTED; +} + +/* -eof- */ diff --git a/src/ngircd/irc-cap.h b/src/ngircd/irc-cap.h new file mode 100644 index 00000000..7cd4c841 --- /dev/null +++ b/src/ngircd/irc-cap.h @@ -0,0 +1,24 @@ +/* + * ngIRCd -- The Next Generation IRC Daemon + * Copyright (c)2001-2010 Alexander Barton (alex@barton.de). + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * Please read the file COPYING, README and AUTHORS for more information. + */ + +#ifndef __irc_cap_h__ +#define __irc_cap_h__ + +/** + * @file + * Handler for IRC capability ("CAP") commands (header) + */ + +GLOBAL bool IRC_CAP PARAMS((CLIENT *Client, REQUEST *Req)); + +#endif + +/* -eof- */ diff --git a/src/ngircd/login.c b/src/ngircd/login.c index 2c305402..ad45219e 100644 --- a/src/ngircd/login.c +++ b/src/ngircd/login.c @@ -26,6 +26,7 @@ #include "conn.h" #include "class.h" #include "client.h" +#include "client-cap.h" #include "channel.h" #include "conf.h" #include "io.h" @@ -78,6 +79,10 @@ Login_User(CLIENT * Client) } #endif + /* Still waiting for "CAP END" command? */ + if (Client_Cap(Client) & CLIENT_CAP_PENDING) + return CONNECTED; + #ifdef PAM if (!Conf_PAM) { /* Don't do any PAM authentication at all, instead emulate diff --git a/src/ngircd/messages.h b/src/ngircd/messages.h index 90e0fdc2..96ff2dea 100644 --- a/src/ngircd/messages.h +++ b/src/ngircd/messages.h @@ -103,6 +103,7 @@ #define ERR_TOOMANYCHANNELS_MSG "405 %s %s :You have joined too many channels" #define ERR_WASNOSUCHNICK_MSG "406 %s %s :There was no such nickname" #define ERR_NOORIGIN_MSG "409 %s :No origin specified" +#define ERR_INVALIDCAP_MSG "410 %s %s :Invalid CAP subcommand" #define ERR_NORECIPIENT_MSG "411 %s :No recipient given (%s)" #define ERR_NOTEXTTOSEND_MSG "412 %s :No text to send" #define ERR_WILDTOPLEVEL "414 %s :Wildcard in toplevel domain" diff --git a/src/ngircd/parse.c b/src/ngircd/parse.c index 02ab8935..41e3872f 100644 --- a/src/ngircd/parse.c +++ b/src/ngircd/parse.c @@ -36,6 +36,7 @@ #include "imp.h" #include "irc.h" +#include "irc-cap.h" #include "irc-channel.h" #include "irc-info.h" #include "irc-login.h" @@ -113,6 +114,7 @@ static COMMAND My_Commands[] = { "CHANINFO", IRC_CHANINFO, CLIENT_SERVER, 0, 0, 0 }, #endif #ifndef STRICT_RFC + { "CAP", IRC_CAP, CLIENT_UNKNOWN|CLIENT_GOTNICK|CLIENT_GOTPASS|CLIENT_GOTUSER|CLIENT_USER, 0, 0, 0 }, { "GET", IRC_QUIT_HTTP, CLIENT_UNKNOWN, 0, 0, 0 }, { "POST", IRC_QUIT_HTTP, CLIENT_UNKNOWN, 0, 0, 0 }, #endif