diff options
Diffstat (limited to 'irc_cap.c')
| -rw-r--r-- | irc_cap.c | 180 | 
1 files changed, 180 insertions, 0 deletions
| diff --git a/irc_cap.c b/irc_cap.c new file mode 100644 index 00000000..3b139c94 --- /dev/null +++ b/irc_cap.c @@ -0,0 +1,180 @@ +/********************************************************************\ +  * BitlBee -- An IRC to other IM-networks gateway                     * +  *                                                                    * +  * Copyright 2002-2013 Wilmer van der Gaast and others                * +  \********************************************************************/ + +/* IRCv3 CAP command + * + * Specs: + *  - v3.1: http://ircv3.net/specs/core/capability-negotiation-3.1.html + *  - v3.2: http://ircv3.net/specs/core/capability-negotiation-3.2.html + * + * */ + +/* +  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. + +  This program is distributed in the hope that it will be useful, +  but WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +  GNU General Public License for more details. + +  You should have received a copy of the GNU General Public License with +  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; +  if not, write to the Free Software Foundation, Inc., 51 Franklin St., +  Fifth Floor, Boston, MA  02110-1301  USA +*/ + +#include "bitlbee.h" + +typedef struct { +	char *name; +	irc_cap_flag_t flag; +} cap_info_t; + +static const cap_info_t supported_caps[] = { +	{"foo", CAP_FOO}, +	{"bar", CAP_BAR}, +	{NULL}, +}; + +static irc_cap_flag_t cap_flag_from_string(char *cap_name) +{ +	int i; + +	if (!cap_name || !cap_name[0]) { +		return 0; +	} + +	if (cap_name[0] == '-') { +		cap_name++; +	} + +	for (i = 0; supported_caps[i].name; i++) { +		if (strcmp(supported_caps[i].name, cap_name) == 0) { +			return supported_caps[i].flag; +		} +	} +	return 0; +} + +static gboolean irc_cmd_cap_req(irc_t *irc, char *caps) +{ +	int i; +	char *lower = NULL; +	char **split = NULL; +	irc_cap_flag_t new_caps = irc->caps; + +	if (!caps || !caps[0]) { +		return FALSE; +	} + +	lower = g_ascii_strdown(caps, -1); +	split = g_strsplit(lower, " ", -1); +	g_free(lower); + +	for (i = 0; split[i]; i++) { +		gboolean remove; +		irc_cap_flag_t flag; + +		if (!split[i][0]) { +			continue;   /* skip empty items (consecutive spaces) */ +		} + +		remove = (split[i][0] == '-'); +		flag = cap_flag_from_string(split[i]); +		 +		if (!flag || (remove && !(irc->caps & flag))) { +			/* unsupported cap, or removing something that isn't there */ +			g_strfreev(split); +			return FALSE; +		} + +		if (remove) { +			new_caps &= ~flag; +		} else { +			new_caps |= flag; +		} +	} + +	/* if we got here, set the new caps and ack */ +	irc->caps = new_caps; + +	g_strfreev(split); +	return TRUE; +} + +/* version can be "302" or NULL, but we don't need cap-3.2 for anything yet */ +static void irc_cmd_cap_ls(irc_t *irc, char *version) +{ +	int i; +	GString *str = g_string_sized_new(256); + +	for (i = 0; supported_caps[i].name; i++) { +		if (i != 0) { +			g_string_append_c(str, ' '); +		} +		g_string_append(str, supported_caps[i].name); +	} + +	irc_send_cap(irc, "LS", str->str); + +	g_string_free(str, TRUE); +} + +/* this one looks suspiciously similar to cap ls, + * but cap-3.2 will make them very different */ +static void irc_cmd_cap_list(irc_t *irc) +{ +	int i; +	gboolean first = TRUE; +	GString *str = g_string_sized_new(256); + +	for (i = 0; supported_caps[i].name; i++) { +		if (irc->caps & supported_caps[i].flag) { +			if (!first) { +				g_string_append_c(str, ' '); +			} +			first = FALSE; + +			g_string_append(str, supported_caps[i].name); +		} +	} + +	irc_send_cap(irc, "LIST", str->str); + +	g_string_free(str, TRUE); +} + +void irc_cmd_cap(irc_t *irc, char **cmd) +{ +	if (!(irc->status & USTATUS_LOGGED_IN)) { +		/* Put registration on hold until CAP END */ +		irc->status |= USTATUS_CAP_PENDING; +	} + +	if (g_strcasecmp(cmd[1], "LS") == 0) { +		irc_cmd_cap_ls(irc, cmd[2]); + +	} else if (g_strcasecmp(cmd[1], "LIST") == 0) { +		irc_cmd_cap_list(irc); + +	} else if (g_strcasecmp(cmd[1], "REQ") == 0) { +		gboolean ack = irc_cmd_cap_req(irc, cmd[2]); + +		irc_send_cap(irc, ack ? "ACK" : "NAK", cmd[2] ? : ""); + +	} else if (g_strcasecmp(cmd[1], "END") == 0) { +		irc->status &= ~USTATUS_CAP_PENDING; +		irc_check_login(irc); + +	} else { +		irc_send_num(irc, 410, "%s :Invalid CAP command", cmd[1]); +	} + +} + | 
