# HG changeset patch # User Paul Aurich # Date 1337749274 0 # Node ID 3b1070cb4f2940d64b14614ba8a2832a00cdecc7 # Parent 804239d704ffc5ceac1d41a34ee94c108c4aea33 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? diff -r 804239d704ff -r 3b1070cb4f29 ChangeLog --- 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 diff -r 804239d704ff -r 3b1070cb4f29 libpurple/protocols/jabber/jabber.c --- 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 <mood> [text]: Set current user mood"), NULL); commands = g_slist_prepend(commands, GUINT_TO_POINTER(id)); g_hash_table_insert(jabber_cmds, plugin, commands); diff -r 804239d704ff -r 3b1070cb4f29 libpurple/protocols/jabber/usermood.c --- 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) diff -r 804239d704ff -r 3b1070cb4f29 libpurple/protocols/jabber/usermood.h --- 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);