Commit Diff


commit - edfcc2f9d5b796fd30f60138591e4f96d54cfcf6
commit + bd3a7ccb158c9f2eac1af77804529b76d99c3e79
blob - d3098f4d62a5cbfe33ef6c3e7e3281abadbaf12f
blob + d89d3792aa9778dcc7bd9575cb5ebb495c568157
--- contrib/MacOSX/ngIRCd.xcodeproj/project.pbxproj
+++ 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 = "<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>"; };
@@ -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;
blob - /dev/null
blob + 9a692ea6cd273a4a6ec4c460ff6d9c0dc89e33d2 (mode 644)
--- /dev/null
+++ 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:
+<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.
blob - 1a792c5f95396e5fd1e2b894364a0e713b08f87c
blob + 92e019b832ad1bcd00b3ca0bdd4f4fe929a72472
--- doc/Makefile.am
+++ doc/Makefile.am
@@ -18,6 +18,7 @@ SUFFIXES = .tmpl
 
 static_docs = \
 	Bopm.txt \
+	Capabilities.txt \
 	FAQ.txt \
 	GIT.txt \
 	HowToRelease.txt \
blob - e96d14bee835d7a73246bc1a709b13eb5f4792e8
blob + 3a411a964b266a2ab9874342ee618018c953ba86
--- src/ngircd/Makefile.am
+++ 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 \
blob - /dev/null
blob + edaf2603bb79005a9b517a6d809702578fa50991 (mode 644)
--- /dev/null
+++ 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 <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- */
blob - def0549c2e8b65a0335bbe3bb72b02a99c106159
blob + bdad9ce93530ace226d5da1885cdb54cafa67ee9
--- src/ngircd/client.h
+++ 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
blob - /dev/null
blob + faec1c202948953df7b5e970082032adaf06a99a (mode 644)
--- /dev/null
+++ 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
blob - /dev/null
blob + 926943c859ab5f265ac3dda585e62ea2ff2bb4cb (mode 644)
--- /dev/null
+++ 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 <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- */
blob - /dev/null
blob + 7cd4c84108ea1f27d9f7146573be300a174caef8 (mode 644)
--- /dev/null
+++ 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- */
blob - 2c305402d77893158ac009ad989303ed53a8ddbf
blob + ad45219eca164aeb16cd3cf6f3ad485fe880439c
--- src/ngircd/login.c
+++ 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
blob - 90e0fdc2b906fe063cfc76be2837ca380cfc120d
blob + 96ff2dea2cc8ab431eef03adb084e7f16d1e3f69
--- src/ngircd/messages.h
+++ 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"
blob - 02ab8935d6ca465d33c70ac29f8e731f1f46307b
blob + 41e3872f66be7a07bff7ab424c46f77fe848d9f6
--- src/ngircd/parse.c
+++ 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