From 2242724c34294e7ca9c12b145f7566f750581386 Mon Sep 17 00:00:00 2001
From: Přemysl Janouch <p.janouch@gmail.com>
Date: Mon, 1 Jun 2015 21:45:04 +0200
Subject: degesch: parse more of RPL_ISUPPORT

---
 degesch.c | 132 ++++++++++++++++++++++++++++++++++++++++++++++++++++----------
 1 file changed, 111 insertions(+), 21 deletions(-)

diff --git a/degesch.c b/degesch.c
index 97b804e..17965ef 100644
--- a/degesch.c
+++ b/degesch.c
@@ -1035,11 +1035,6 @@ struct server
 
 	// IRC:
 
-	// TODO: casemapping-specific strxfrm and/or tolower (CASEMAPPING=rfc1459)
-	// TODO: channel name prefixes (CHANTYPES=#& + IDCHAN)
-	char *irc_channel_prefixes;         ///< Channel user prefixes
-	char *irc_channel_modes;            ///< Channel user modes
-
 	struct str_map irc_users;           ///< IRC user data
 	struct str_map irc_channels;        ///< IRC channel data
 	struct str_map irc_buffer_map;      ///< Maps IRC identifiers to buffers
@@ -1048,6 +1043,22 @@ struct server
 	char *irc_user_mode;                ///< Our current user mode
 	char *irc_user_host;                ///< Our current user@host
 
+	// Server-specific information (from RPL_ISUPPORT):
+
+	// TODO: implement a generic strcmp() on top of "irc_tolower"
+	/// Convert an IRC identifier character to lower-case
+	int (*irc_tolower) (int);
+
+	/// Key conversion function for hashmap lookups
+	size_t (*irc_strxfrm) (char *, const char *, size_t);
+
+	char *irc_chantypes;                ///< Channel types (name prefixes)
+	char *irc_idchan_prefixes;          ///< Prefixes for "safe channels"
+	char *irc_statusmsg;                ///< Prefixes for channel targets
+
+	char *irc_chanuser_prefixes;        ///< Channel user prefixes
+	char *irc_chanuser_modes;           ///< Channel user modes
+
 	// Events:
 
 	struct poller_timer ping_tmr;       ///< We should send a ping
@@ -1068,9 +1079,16 @@ server_init (struct server *self, struct poller *poller)
 	str_init (&self->read_buffer);
 	self->state = IRC_DISCONNECTED;
 
-	// RFC 1459 as per the RPL_ISUPPORT draft
-	self->irc_channel_prefixes = xstrdup ("@+");
-	self->irc_channel_modes    = xstrdup ("ov");
+	// Defaults as per the RPL_ISUPPORT draft
+	self->irc_tolower = irc_tolower;
+	self->irc_strxfrm = irc_strxfrm;
+
+	self->irc_chantypes         = xstrdup ("#&");
+	self->irc_idchan_prefixes   = xstrdup ("");
+	self->irc_statusmsg         = xstrdup ("");
+
+	self->irc_chanuser_prefixes = xstrdup ("@+");
+	self->irc_chanuser_modes    = xstrdup ("ov");
 
 	str_map_init (&self->irc_users);
 	self->irc_users.key_xfrm = irc_strxfrm;
@@ -1119,8 +1137,12 @@ server_free (struct server *self)
 	free (self->irc_user_mode);
 	free (self->irc_user_host);
 
-	free (self->irc_channel_prefixes);
-	free (self->irc_channel_modes);
+	free (self->irc_chantypes);
+	free (self->irc_idchan_prefixes);
+	free (self->irc_statusmsg);
+
+	free (self->irc_chanuser_prefixes);
+	free (self->irc_chanuser_modes);
 
 	str_map_free (&self->irc_users);
 	str_map_free (&self->irc_channels);
@@ -3731,7 +3753,6 @@ irc_is_highlight (struct server *s, const char *message)
 // --- Input handling ----------------------------------------------------------
 
 // TODO: we will need a proper mode parser; to be shared with kike
-// TODO: we alse definitely need to parse server capability messages
 
 static void
 irc_handle_join (struct server *s, const struct irc_message *msg)
@@ -4430,7 +4451,7 @@ irc_process_names (struct server *s, struct channel *channel)
 	for (size_t i = 0; i < updates->len; i++)
 	{
 		const char *item = updates->vector[i];
-		const char *nick = item + strspn (item, s->irc_channel_modes);
+		const char *nick = item + strspn (item, s->irc_chanuser_modes);
 		struct channel_user *channel_user = str_map_find (&map, nick);
 		if (!channel_user)
 		{
@@ -4479,11 +4500,72 @@ irc_handle_isupport_prefix (struct server *s, char *value)
 	if (*modes++ != '(' || !prefixes++ || strlen (value) != 2 * n_prefixes--)
 		return;
 
-	free (s->irc_channel_modes);
-	free (s->irc_channel_prefixes);
+	free (s->irc_chanuser_modes);
+	free (s->irc_chanuser_prefixes);
+
+	s->irc_chanuser_modes    = xstrndup (modes,    n_prefixes);
+	s->irc_chanuser_prefixes = xstrndup (prefixes, n_prefixes);
+}
+
+static void
+irc_handle_isupport_casemapping (struct server *s, char *value)
+{
+	// TODO: reinitialize hashtables with the new tolower() and strxfrm(),
+	//   note that collisions may arise on reconnecting
+
+	if      (!strcmp (value, "ascii"))
+	{
+		s->irc_tolower = tolower_ascii;
+		s->irc_strxfrm = tolower_ascii_strxfrm;
+	}
+	else if (!strcmp (value, "rfc1459"))
+	{
+		s->irc_tolower = irc_tolower;
+		s->irc_strxfrm = irc_strxfrm;
+	}
+	else if (!strcmp (value, "rfc1459-strict"))
+	{
+		// TODO: implement
+		s->irc_tolower = irc_tolower;
+		s->irc_strxfrm = irc_strxfrm;
+	}
+}
+
+static void
+irc_handle_isupport_chantypes (struct server *s, char *value)
+{
+	free (s->irc_chantypes);
+	s->irc_chantypes = xstrdup (value);
+}
+
+static void
+irc_handle_isupport_idchan (struct server *s, char *value)
+{
+	struct str prefixes;
+	str_init (&prefixes);
+
+	struct str_vector v;
+	str_vector_init (&v);
+	split_str_ignore_empty (value, ',', &v);
+	for (size_t i = 0; i < v.len; i++)
+	{
+		// Not using or validating the numeric part
+		const char *pair = v.vector[i];
+		const char *colon = strchr (pair, ':');
+		if (colon)
+			str_append_data (&prefixes, pair, colon - pair);
+	}
+	str_vector_free (&v);
+
+	free (s->irc_idchan_prefixes);
+	s->irc_idchan_prefixes = str_steal (&prefixes);
+}
 
-	s->irc_channel_modes    = xstrndup (modes,    n_prefixes);
-	s->irc_channel_prefixes = xstrndup (prefixes, n_prefixes);
+static void
+irc_handle_isupport_statusmsg (struct server *s, char *value)
+{
+	free (s->irc_statusmsg);
+	s->irc_statusmsg = xstrdup (value);
 }
 
 static void
@@ -4522,14 +4604,22 @@ irc_handle_rpl_isupport (struct server *s, const struct irc_message *msg)
 		str_init (&value_unescaped);
 		unescape_isupport_value (value, &value_unescaped);
 
-		if (!strcmp (param, "PREFIX"))
-			irc_handle_isupport_prefix (s, value_unescaped.str);
+		if      (!strcmp (param, "PREFIX"))
+			irc_handle_isupport_prefix      (s, value_unescaped.str);
+		else if (!strcmp (param, "CASEMAPPING"))
+			irc_handle_isupport_casemapping (s, value_unescaped.str);
+		else if (!strcmp (param, "CHANTYPES"))
+			irc_handle_isupport_chantypes   (s, value_unescaped.str);
+		else if (!strcmp (param, "IDCHAN"))
+			irc_handle_isupport_idchan      (s, value_unescaped.str);
+		else if (!strcmp (param, "STATUSMSG"))
+			irc_handle_isupport_statusmsg   (s, value_unescaped.str);
+
+		// TODO: also parse MODES, TARGMAX, CHANMODES and make use of them
+		//   to split client commands as necessary
 
 		str_free (&value_unescaped);
 	}
-
-	// TODO: initialize key_strxfrm according to server properties;
-	//   note that collisions may arise on reconnecting
 }
 
 static void
-- 
cgit v1.2.3-70-g09d2