changeset 32777:3b1070cb4f29

jabber: Validate user moods, and make the /mood cmd behave flexibly. A user in Pidgin running "/mood ?" or "/mood -" would result in invalid XML being sent to the server (similar to #14342, except '<-/>' or '<?/>'). Prevent this by ensuring the user is specifying something from the list. The /mood command will also now try to treat its entire arguments as a single string, and set that as the mood -- I figure this is what a user would expect?
author Paul Aurich <paul@darkrain42.org>
date Wed, 23 May 2012 05:01:14 +0000
parents 804239d704ff
children 9686216828f9
files ChangeLog libpurple/protocols/jabber/jabber.c libpurple/protocols/jabber/usermood.c libpurple/protocols/jabber/usermood.h
diffstat 4 files changed, 85 insertions(+), 28 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Wed May 23 03:25:01 2012 +0000
+++ b/ChangeLog	Wed May 23 05:01:14 2012 +0000
@@ -39,6 +39,7 @@
 	  to the core (and UIs) as incoming messages (Thijs Alkemade).
 	  (#14529)
 	* Support file transfers up to ~9 EiB.
+	* Invalid user moods can no longer be sent to the server.
 
 	Plugins:
 	* The Voice/Video Settings plugin supports using the sndio GStreamer
--- a/libpurple/protocols/jabber/jabber.c	Wed May 23 03:25:01 2012 +0000
+++ b/libpurple/protocols/jabber/jabber.c	Wed May 23 05:01:14 2012 +0000
@@ -3575,16 +3575,32 @@
 	JabberStream *js = purple_connection_get_protocol_data(purple_account_get_connection(account));
 
 	if (js->pep) {
-		/* if no argument was given, unset mood */
+		gboolean ret;
+
 		if (!args || !args[0]) {
-			jabber_mood_set(js, NULL, NULL);
-		} else if (!args[1]) {
-			jabber_mood_set(js, args[0], NULL);
+			/* No arguments; unset mood */
+			ret = jabber_mood_set(js, NULL, NULL);
 		} else {
-			jabber_mood_set(js, args[0], args[1]);
+			/* At least one argument.  Relying on the list of arguments
+			 * being NULL-terminated.
+			 */
+			ret = jabber_mood_set(js, args[0], args[1]);
+			if (!ret) {
+				/* Let's try again */
+				char *tmp = g_strjoin(" ", args[0], args[1], NULL);
+				ret = jabber_mood_set(js, "undefined", tmp);
+				g_free(tmp);
+			}
 		}
 
-		return PURPLE_CMD_RET_OK;
+		if (ret) {
+			return PURPLE_CMD_RET_OK;
+		} else {
+			purple_conversation_write(conv, NULL,
+				_("Failed to specify mood"),
+				PURPLE_MESSAGE_ERROR, time(NULL));
+			return PURPLE_CMD_RET_FAILED;
+		}
 	} else {
 		/* account does not support PEP, can't set a mood */
 		purple_conversation_write(conv, NULL,
@@ -3713,7 +3729,7 @@
 	    			  PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_IM |
 	    			  PURPLE_CMD_FLAG_PRPL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS,
 	    			  "prpl-jabber", jabber_cmd_mood,
-	    			  _("mood: Set current user mood"), NULL);
+	    			  _("mood &lt;mood&gt; [text]: Set current user mood"), NULL);
 	commands = g_slist_prepend(commands, GUINT_TO_POINTER(id));
 
 	g_hash_table_insert(jabber_cmds, plugin, commands);
--- a/libpurple/protocols/jabber/usermood.c	Wed May 23 03:25:01 2012 +0000
+++ b/libpurple/protocols/jabber/usermood.c	Wed May 23 05:01:14 2012 +0000
@@ -115,10 +115,26 @@
 	{"undefined", N_("Undefined"), NULL},
 	{"weak", N_("Weak"), NULL},
 	{"worried", N_("Worried"), NULL},
-	/* Mark the last record. */
+	/* Mar last record. */
 	{NULL, NULL, NULL}
 };
 
+static const PurpleMood*
+find_mood_by_name(const gchar *name)
+{
+	int i;
+
+	g_return_val_if_fail(name && *name, NULL);
+
+	for (i = 0; moods[i].mood != NULL; ++i) {
+		if (g_str_equal(name, moods[i].mood)) {
+			return &moods[i];
+		}
+	}
+
+	return NULL;
+}
+
 static void jabber_mood_cb(JabberStream *js, const char *from, xmlnode *items) {
 	/* it doesn't make sense to have more than one item here, so let's just pick the first one */
 	xmlnode *item = xmlnode_get_child(items, "item");
@@ -139,15 +155,13 @@
 				if (!moodtext) /* only pick the first one */
 					moodtext = xmlnode_get_data(moodinfo);
 			} else {
-				int i;
-				for (i = 0; moods[i].mood; ++i) {
-					/* verify that the mood is known (valid) */
-					if (!strcmp(moodinfo->name, moods[i].mood)) {
-						newmood = moods[i].mood;
-						break;
-					}
-				}
+				const PurpleMood *target_mood;
+
+				/* verify that the mood is known (valid) */
+				target_mood = find_mood_by_name(moodinfo->name);
+				newmood = target_mood ? target_mood->mood : NULL;
 			}
+
 			if (newmood != NULL && moodtext != NULL)
 			   break;
 		}
@@ -170,26 +184,41 @@
 	jabber_pep_register_handler("http://jabber.org/protocol/mood", jabber_mood_cb);
 }
 
-void jabber_mood_set(JabberStream *js, const char *mood, const char *text) {
+gboolean
+jabber_mood_set(JabberStream *js, const char *mood, const char *text)
+{
+	const PurpleMood *target_mood = NULL;
 	xmlnode *publish, *moodnode;
 
+	if (mood && *mood) {
+		target_mood = find_mood_by_name(mood);
+		/* Mood specified, but is invalid --
+		 * fail so that the command can handle this.
+		 */
+		if (!target_mood)
+			return FALSE;
+	}
+
 	publish = xmlnode_new("publish");
 	xmlnode_set_attrib(publish,"node","http://jabber.org/protocol/mood");
 	moodnode = xmlnode_new_child(xmlnode_new_child(publish, "item"), "mood");
 	xmlnode_set_namespace(moodnode, "http://jabber.org/protocol/mood");
-	if (mood && *mood) {
-		/* if mood is NULL, set an empty mood node, meaning: unset mood */
+
+	if (target_mood) {
+		/* If target_mood is not NULL, then
+		 * target_mood->mood == mood, and is a valid element name.
+		 */
 	    xmlnode_new_child(moodnode, mood);
-	}
 
-	if (text && *text) {
-		xmlnode *textnode = xmlnode_new_child(moodnode, "text");
-		xmlnode_insert_data(textnode, text, -1);
+		/* Only set text when setting a mood */
+		if (text && *text) {
+			xmlnode *textnode = xmlnode_new_child(moodnode, "text");
+			xmlnode_insert_data(textnode, text, -1);
+		}
 	}
 
 	jabber_pep_publish(js, publish);
-	/* publish is freed by jabber_pep_publish -> jabber_iq_send -> jabber_iq_free
-	   (yay for well-defined memory management rules) */
+	return TRUE;
 }
 
 PurpleMood *jabber_get_moods(PurpleAccount *account)
--- a/libpurple/protocols/jabber/usermood.h	Wed May 23 03:25:01 2012 +0000
+++ b/libpurple/protocols/jabber/usermood.h	Wed May 23 05:01:14 2012 +0000
@@ -30,9 +30,20 @@
 
 void jabber_mood_init(void);
 
-void jabber_mood_set(JabberStream *js,
-		     const char *mood, /* must be one of the valid strings defined in the XEP */
-		     const char *text /* might be NULL */);
+/**
+ * Sets / unsets the mood for the specified account.  The mood passed in
+ * must either be NULL, "", or one of the moods returned by
+ * jabber_get_moods().
+ *
+ * @param js The JabberStream object.
+ * @param mood The mood to set, NULL, or ""
+ * @param text Optional text that goes along with a mood.  Only used when
+ *             setting a mood (not when unsetting a mood).
+ *
+ * @return FALSE if an invalid mood was specified, or TRUE otherwise.
+ */
+gboolean
+jabber_mood_set(JabberStream *js, const char *mood, const char *text);
 
 PurpleMood *jabber_get_moods(PurpleAccount *account);