diff options
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | irc.h | 8 | ||||
-rw-r--r-- | irc_cap.c | 180 | ||||
-rw-r--r-- | irc_commands.c | 149 | ||||
-rw-r--r-- | tests/Makefile | 2 |
5 files changed, 190 insertions, 151 deletions
@@ -9,7 +9,7 @@ -include Makefile.settings # Program variables -objects = bitlbee.o dcc.o help.o ipc.o irc.o irc_im.o irc_channel.o irc_commands.o irc_send.o irc_user.o irc_util.o nick.o $(OTR_BI) query.o root_commands.o set.o storage.o $(STORAGE_OBJS) unix.o conf.o log.o +objects = bitlbee.o dcc.o help.o ipc.o irc.o irc_im.o irc_cap.o irc_channel.o irc_commands.o irc_send.o irc_user.o irc_util.o nick.o $(OTR_BI) query.o root_commands.o set.o storage.o $(STORAGE_OBJS) unix.o conf.o log.o headers = $(wildcard $(_SRCDIR_)*.h $(_SRCDIR_)lib/*.h $(_SRCDIR_)protocols/*.h) subdirs = lib protocols @@ -65,6 +65,11 @@ typedef enum { IRC_UTF8_NICKS = 0x10000, /* Disable ASCII restrictions on buddy nicks. */ } irc_status_t; +typedef enum { + CAP_FOO = (1 << 0), + CAP_BAR = (1 << 1), +} irc_cap_flag_t; + struct irc_user; typedef struct irc { @@ -351,4 +356,7 @@ char *irc_format_timestamp(irc_t *irc, time_t msg_ts); void bee_irc_channel_update(irc_t *irc, irc_channel_t *ic, irc_user_t *iu); void bee_irc_user_nick_reset(irc_user_t *iu); +/* irc_cap.c */ +void irc_cmd_cap(irc_t *irc, char **cmd); + #endif 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]); + } + +} + diff --git a/irc_commands.c b/irc_commands.c index ebcc300b..14a3fd9d 100644 --- a/irc_commands.c +++ b/irc_commands.c @@ -28,155 +28,6 @@ #include "help.h" #include "ipc.h" -typedef guint32 cap_flag; /* 32 bits ought to be enough for anybody */ - -typedef struct _cap_info { - char *name; - cap_flag flag; -} cap_info_t; - -#define CAP_FOO (1 << 0) -#define CAP_BAR (1 << 1) - -static const cap_info_t supported_caps[] = { - {"foo", CAP_FOO}, - {"bar", CAP_BAR}, - {NULL}, -}; - -static cap_flag 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; - cap_flag 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; - cap_flag 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); -} - -static 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]); - } - -} - static void irc_cmd_pass(irc_t *irc, char **cmd) { if (irc->status & USTATUS_LOGGED_IN) { diff --git a/tests/Makefile b/tests/Makefile index d77fb1d1..efca9bff 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -14,7 +14,7 @@ clean: distclean: clean -main_objs = bitlbee.o conf.o dcc.o help.o ipc.o irc.o irc_channel.o irc_commands.o irc_im.o irc_send.o irc_user.o irc_util.o irc_commands.o log.o nick.o query.o root_commands.o set.o storage.o storage_xml.o +main_objs = bitlbee.o conf.o dcc.o help.o ipc.o irc.o irc_cap.o irc_channel.o irc_commands.o irc_im.o irc_send.o irc_user.o irc_util.o irc_commands.o log.o nick.o query.o root_commands.o set.o storage.o storage_xml.o test_objs = check.o check_util.o check_nick.o check_md5.o check_arc.o check_irc.o check_help.o check_user.o check_set.o check_jabber_sasl.o check_jabber_util.o |