]> granicus.if.org Git - ngircd/commitdiff
Implement core IRC capability handling and "CAP" command
authorAlexander Barton <alex@barton.de>
Sat, 31 Mar 2012 13:59:06 +0000 (15:59 +0200)
committerAlexander Barton <alex@barton.de>
Sat, 31 Mar 2012 13:59:06 +0000 (15:59 +0200)
This patch implements the core functions to support "IRC Capabilities"
and the IRC "CAP" command as used by other servers and specified here:
<http://www.leeh.co.uk/draft-mitchell-irc-capabilities-02.html>.

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".

12 files changed:
contrib/MacOSX/ngIRCd.xcodeproj/project.pbxproj
doc/Capabilities.txt [new file with mode: 0644]
doc/Makefile.am
src/ngircd/Makefile.am
src/ngircd/client-cap.c [new file with mode: 0644]
src/ngircd/client-cap.h [new file with mode: 0644]
src/ngircd/client.h
src/ngircd/irc-cap.c [new file with mode: 0644]
src/ngircd/irc-cap.h [new file with mode: 0644]
src/ngircd/login.c
src/ngircd/messages.h
src/ngircd/parse.c

index d3098f4d62a5cbfe33ef6c3e7e3281abadbaf12f..d89d3792aa9778dcc7bd9575cb5ebb495c568157 100644 (file)
@@ -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 */
                FAA97C56124A271400D5BBA9 /* sighandlers.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; path = sighandlers.h; sourceTree = "<group>"; };
                FAACD5F314A6099C006ED74F /* class.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = class.c; sourceTree = "<group>"; };
                FAACD5F414A6099C006ED74F /* class.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = class.h; sourceTree = "<group>"; };
+               FAD5852F15271A7800328741 /* Capabilities.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = Capabilities.txt; sourceTree = "<group>"; };
+               FAD5853015271AAB00328741 /* client-cap.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "client-cap.c"; sourceTree = "<group>"; };
+               FAD5853115271AAB00328741 /* client-cap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "client-cap.h"; sourceTree = "<group>"; };
+               FAD5853315271AB800328741 /* irc-cap.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "irc-cap.c"; sourceTree = "<group>"; };
+               FAD5853415271AB800328741 /* irc-cap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "irc-cap.h"; sourceTree = "<group>"; };
                FAD5853615272C2500328741 /* login.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = login.c; sourceTree = "<group>"; };
                FAD5853715272C2500328741 /* login.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = login.h; sourceTree = "<group>"; };
                FAE22BD215270EA300F1A5AB /* Bopm.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = Bopm.txt; sourceTree = "<group>"; };
                                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 */,
                                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 */,
                        children = (
                                FA322D9B0CEF752C001761B3 /* Makefile.am */,
                                FAE22BD215270EA300F1A5AB /* Bopm.txt */,
+                               FAD5852F15271A7800328741 /* Capabilities.txt */,
                                FAE22BD415270EA300F1A5AB /* Contributing.txt */,
                                FA322D9A0CEF752C001761B3 /* FAQ.txt */,
                                FA407F380DB15AC700271AF1 /* GIT.txt */,
                                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 (file)
index 0000000..9a692ea
--- /dev/null
@@ -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:
+<http://www.leeh.co.uk/draft-mitchell-irc-capabilities-02.html>
+
+
+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.
index 1a792c5f95396e5fd1e2b894364a0e713b08f87c..92e019b832ad1bcd00b3ca0bdd4f4fe929a72472 100644 (file)
@@ -18,6 +18,7 @@ SUFFIXES = .tmpl
 
 static_docs = \
        Bopm.txt \
+       Capabilities.txt \
        FAQ.txt \
        GIT.txt \
        HowToRelease.txt \
index e96d14bee835d7a73246bc1a709b13eb5f4792e8..3a411a964b266a2ab9874342ee618018c953ba86 100644 (file)
@@ -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 (file)
index 0000000..edaf260
--- /dev/null
@@ -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 <assert.h>
+
+#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 (file)
index 0000000..faec1c2
--- /dev/null
@@ -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
index def0549c2e8b65a0335bbe3bb72b02a99c106159..bdad9ce93530ace226d5da1885cdb54cafa67ee9 100644 (file)
@@ -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 (file)
index 0000000..926943c
--- /dev/null
@@ -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 <assert.h>
+#include <string.h>
+
+#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 (file)
index 0000000..7cd4c84
--- /dev/null
@@ -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- */
index 2c305402d77893158ac009ad989303ed53a8ddbf..ad45219eca164aeb16cd3cf6f3ad485fe880439c 100644 (file)
@@ -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
index 90e0fdc2b906fe063cfc76be2837ca380cfc120d..96ff2dea2cc8ab431eef03adb084e7f16d1e3f69 100644 (file)
 #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"
index 02ab8935d6ca465d33c70ac29f8e731f1f46307b..41e3872f66be7a07bff7ab424c46f77fe848d9f6 100644 (file)
@@ -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