about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/ngircd/Makefile.am4
-rw-r--r--src/ngircd/client-cap.c62
-rw-r--r--src/ngircd/client-cap.h28
-rw-r--r--src/ngircd/client.h3
-rw-r--r--src/ngircd/irc-cap.c192
-rw-r--r--src/ngircd/irc-cap.h24
-rw-r--r--src/ngircd/login.c5
-rw-r--r--src/ngircd/messages.h1
-rw-r--r--src/ngircd/parse.c2
9 files changed, 320 insertions, 1 deletions
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 <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
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 <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
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