Description: fix crash via IRC argument parsing
Origin: upstream, http://hg.pidgin.im/pidgin/main/rev/a167504359e5
Origin: upstream, http://hg.pidgin.im/pidgin/main/rev/9f132a6855cd
Origin: upstream, http://hg.pidgin.im/pidgin/main/rev/5845d9fa7084
Origin: upstream, http://hg.pidgin.im/pidgin/main/rev/6b0e0566af20
Origin: upstream, http://hg.pidgin.im/pidgin/main/rev/4d9be297d399
Origin: upstream, http://hg.pidgin.im/pidgin/main/rev/7d0fb0c6d8d4

Index: pidgin-2.10.7/libpurple/protocols/irc/msgs.c
===================================================================
--- pidgin-2.10.7.orig/libpurple/protocols/irc/msgs.c	2013-02-11 04:16:52.000000000 -0500
+++ pidgin-2.10.7/libpurple/protocols/irc/msgs.c	2014-02-05 15:06:11.752167365 -0500
@@ -20,6 +20,12 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  */
 
+/*
+ * Note: If you change any of these functions to use additional args you
+ * MUST ensure the arg count is correct in parse.c. Otherwise it may be
+ * possible for a malicious server or man-in-the-middle to trigger a crash.
+ */
+
 #include "internal.h"
 
 #include "conversation.h"
@@ -203,9 +209,6 @@
 	gchar **features;
 	int i;
 
-	if (!args || !args[0] || !args[1])
-		return;
-
 	features = g_strsplit(args[1], " ", -1);
 	for (i = 0; features[i]; i++) {
 		char *val;
@@ -220,9 +223,6 @@
 
 void irc_msg_luser(struct irc_conn *irc, const char *name, const char *from, char **args)
 {
-	if (!args || !args[0])
-		return;
-
 	if (!strcmp(name, "251")) {
 		/* 251 is required, so we pluck our nick from here and
 		 * finalize connection */
@@ -238,9 +238,6 @@
 	PurpleConnection *gc;
 	char *msg;
 
-	if (!args || !args[1])
-		return;
-
 	if (irc->whois.nick && !purple_utf8_strcasecmp(irc->whois.nick, args[1])) {
 		/* We're doing a whois, show this in the whois dialog */
 		irc_msg_whois(irc, name, from, args);
@@ -259,8 +256,7 @@
 {
 	PurpleConnection *gc = purple_account_get_connection(irc->account);
 
-	if (!args || !args[1] || !gc)
-		return;
+	g_return_if_fail(gc);
 
 	purple_notify_error(gc, NULL, _("Bad mode"), args[1]);
 }
@@ -269,17 +265,13 @@
 {
 	PurpleConversation *convo;
 
-	if (!args || !args[0] || !args[1])
-		return;
-
 	convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
 						      args[1], irc->account);
 
 	if (!strcmp(name, "367")) {
 		char *msg = NULL;
 		/* Ban list entry */
-		if (!args[2])
-			return;
+		g_return_if_fail(args[2]);
 		if (args[3] && args[4]) {
 			/* This is an extended syntax, not in RFC 1459 */
 			int t1 = atoi(args[4]);
@@ -315,8 +307,7 @@
 	PurpleConnection *gc = purple_account_get_connection(irc->account);
 	char *buf;
 
-	if (!args || !args[1] || !gc)
-		return;
+	g_return_if_fail(gc);
 
 	buf = g_strdup_printf(_("You are banned from %s."), args[1]);
 	purple_notify_error(gc, _("Banned"), _("Banned"), buf);
@@ -328,9 +319,6 @@
 	PurpleConversation *convo;
 	char *buf, *nick;
 
-	if (!args || !args[0] || !args[1] || !args[2])
-		return;
-
 	convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, args[1], irc->account);
 	if (!convo)
 		return;
@@ -349,9 +337,6 @@
 	PurpleConversation *convo;
 	char *buf, *escaped;
 
-	if (!args || !args[1] || !args[2])
-		return;
-
 	convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, args[1], irc->account);
 	if (!convo)	/* XXX punt on channels we are not in for now */
 		return;
@@ -489,13 +474,6 @@
 		PurpleConvChatBuddyFlags flags;
 		GList *keys = NULL, *values = NULL;
 
-		if (!args || !args[0] || !args[1] || !args[2] || !args[3]
-		    || !args[4] || !args[5] || !args[6] || !args[7]) {
-			purple_debug(PURPLE_DEBUG_ERROR, "irc",
-				     "Got a WHO response with not enough arguments\n");
-			return;
-		}
-
 		conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, args[1], irc->account);
 		if (!conv) {
 			purple_debug(PURPLE_DEBUG_ERROR, "irc","Got a WHO response for %s, which doesn't exist\n", args[1]);
@@ -571,9 +549,6 @@
 		PurpleRoomlistRoom *room;
 		char *topic;
 
-		if (!args[0] || !args[1] || !args[2] || !args[3])
-			return;
-
 		if (!purple_roomlist_get_in_progress(irc->roomlist)) {
 			purple_debug_warning("irc", "Buggy server didn't send RPL_LISTSTART.\n");
 			purple_roomlist_set_in_progress(irc->roomlist, TRUE);
@@ -595,13 +570,13 @@
 	PurpleConversation *convo;
 
 	if (!strcmp(name, "topic")) {
-		if (!args[0] || !args[1])
-			return;
+		g_return_if_fail(args[0]);
+		g_return_if_fail(args[1]);
 		chan = args[0];
 		topic = irc_mirc2txt (args[1]);
 	} else {
-		if (!args[0] || !args[1] || !args[2])
-			return;
+		g_return_if_fail(args[1]);
+		g_return_if_fail(args[2]);
 		chan = args[1];
 		topic = irc_mirc2txt (args[2]);
 	}
@@ -652,9 +627,6 @@
 	struct tm *tm;
 	time_t t;
 	char *msg, *timestamp, *datestamp;
-	
-	if (!args || !args[1] || !args[2] || !args[3])
-		return;
 
 	convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, args[1], irc->account);
 	if (!convo) {
@@ -683,8 +655,7 @@
 	PurpleConnection *gc = purple_account_get_connection(irc->account);
 	char *buf;
 
-	if (!args || !args[1] || !gc)
-		return;
+	g_return_if_fail(gc);
 
 	buf = g_strdup_printf(_("Unknown message '%s'"), args[1]);
 	purple_notify_error(gc, _("Unknown message"), buf, _("The IRC server received a message it did not understand."));
@@ -776,9 +747,6 @@
 {
 	char *escaped;
 
-	if (!args || !args[0])
-		return;
-
 	if (!strcmp(name, "375")) {
 		if (irc->motd)
 			g_string_free(irc->motd, TRUE);
@@ -815,11 +783,9 @@
 
 void irc_msg_time(struct irc_conn *irc, const char *name, const char *from, char **args)
 {
-	PurpleConnection *gc;
+	PurpleConnection *gc = purple_account_get_connection(irc->account);
 
-	gc = purple_account_get_connection(irc->account);
-	if (gc == NULL || args == NULL || args[2] == NULL)
-		return;
+	g_return_if_fail(gc);
 
 	purple_notify_message(gc, PURPLE_NOTIFY_MSG_INFO, _("Time Response"),
 			    _("The IRC server's local time is:"),
@@ -830,8 +796,7 @@
 {
 	PurpleConnection *gc = purple_account_get_connection(irc->account);
 
-	if (gc == NULL || args == NULL || args[1] == NULL)
-		return;
+	g_return_if_fail(gc);
 
 	purple_notify_error(gc, NULL, _("No such channel"), args[1]);
 }
@@ -892,9 +857,6 @@
 {
 	PurpleConversation *convo;
 
-	if (!args || !args[1] || !args[2])
-		return;
-
 	convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, args[1], irc->account);
 	if (!convo)
 		return;
@@ -908,8 +870,7 @@
 	GHashTable *components;
 	gchar *nick;
 
-	if (!args || !args[1] || !gc)
-		return;
+	g_return_if_fail(gc);
 
 	components = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
 	nick = irc_mask_nick(from);
@@ -925,8 +886,7 @@
 	PurpleConnection *gc = purple_account_get_connection(irc->account);
 	char *buf;
 
-	if (!args || !args[1] || !gc)
-		return;
+	g_return_if_fail(gc);
 
 	buf = g_strdup_printf(_("Joining %s requires an invitation."), args[1]);
 	purple_notify_error(gc, _("Invitation only"), _("Invitation only"), buf);
@@ -939,9 +899,6 @@
 	struct irc_buddy *ib;
 	int i;
 
-	if (!args || !args[1])
-		return;
-
 	nicks = g_strsplit(args[1], " ", -1);
 	for (i = 0; nicks[i]; i++) {
 		if ((ib = g_hash_table_lookup(irc->buddies, (gconstpointer)nicks[i])) == NULL) {
@@ -982,14 +939,13 @@
 	PurpleConvChat *chat;
 	PurpleConvChatBuddy *cb;
 
-	char *nick = irc_mask_nick(from), *userhost, *buf;
+	char *nick, *userhost, *buf;
 	struct irc_buddy *ib;
 	static int id = 1;
 
-	if (!gc) {
-		g_free(nick);
-		return;
-	}
+	g_return_if_fail(gc);
+
+	nick = irc_mask_nick(from);
 
 	if (!purple_utf8_strcasecmp(nick, purple_connection_get_display_name(gc))) {
 		/* We are joining a channel for the first time */
@@ -1049,12 +1005,11 @@
 {
 	PurpleConnection *gc = purple_account_get_connection(irc->account);
 	PurpleConversation *convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, args[0], irc->account);
-	char *nick = irc_mask_nick(from), *buf;
+	char *nick, *buf;
 
-	if (!gc) {
-		g_free(nick);
-		return;
-	}
+	g_return_if_fail(gc);
+
+	nick = irc_mask_nick(from);
 
 	if (!convo) {
 		purple_debug(PURPLE_DEBUG_ERROR, "irc", "Received a KICK for unknown channel %s\n", args[0]);
@@ -1197,9 +1152,6 @@
 	char *newnick, *buf, *end;
 	PurpleConnection *gc = purple_account_get_connection(irc->account);
 
-	if (!args || !args[1])
-		return;
-
 	if (gc && purple_connection_get_state(gc) == PURPLE_CONNECTED) {
 		/* We only want to do the following dance if the connection
 		   has not been successfully completed.  If it has, just
@@ -1238,9 +1190,6 @@
 
 void irc_msg_notice(struct irc_conn *irc, const char *name, const char *from, char **args)
 {
-	if (!args || !args[0] || !args[1])
-		return;
-
 	irc_msg_handle_privmsg(irc, name, from, args[0], args[1], TRUE);
 }
 
@@ -1248,8 +1197,7 @@
 {
 	PurpleConnection *gc = purple_account_get_connection(irc->account);
 
-	if (!args || !args[2] || !gc)
-		return;
+	g_return_if_fail(gc);
 
 	purple_notify_error(gc, _("Cannot change nick"), _("Could not change nick"), args[2]);
 }
@@ -1260,8 +1208,7 @@
 	PurpleConversation *convo;
 	char *nick, *msg, *channel;
 
-	if (!args || !args[0] || !gc)
-		return;
+	g_return_if_fail(gc);
 
 	/* Undernet likes to :-quote the channel name, for no good reason
 	 * that I can see.  This catches that. */
@@ -1294,8 +1241,6 @@
 void irc_msg_ping(struct irc_conn *irc, const char *name, const char *from, char **args)
 {
 	char *buf;
-	if (!args || !args[0])
-		return;
 
 	buf = irc_format(irc, "v:", "PONG", args[0]);
 	irc_send(irc, buf);
@@ -1309,9 +1254,6 @@
 	char **parts, *msg;
 	time_t oldstamp;
 
-	if (!args || !args[1])
-		return;
-
 	parts = g_strsplit(args[1], " ", 2);
 
 	if (!parts[0] || !parts[1]) {
@@ -1345,9 +1287,6 @@
 
 void irc_msg_privmsg(struct irc_conn *irc, const char *name, const char *from, char **args)
 {
-	if (!args || !args[0] || !args[1])
-		return;
-
 	irc_msg_handle_privmsg(irc, name, from, args[0], args[1], FALSE);
 }
 
@@ -1401,8 +1340,7 @@
 	PurpleConversation *convo;
 	char *msg;
 
-	if (!args || !args[1] || !args[2] || !gc)
-		return;
+	g_return_if_fail(gc);
 
 	convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, args[1], irc->account);
 	if (convo) {
@@ -1424,8 +1362,7 @@
 	struct irc_buddy *ib;
 	char *data[2];
 
-	if (!args || !args[0] || !gc)
-		return;
+	g_return_if_fail(gc);
 
 	data[0] = irc_mask_nick(from);
 	data[1] = args[0];
@@ -1445,9 +1382,6 @@
 {
 	PurpleConnection *gc = purple_account_get_connection(irc->account);
 
-	if (!args || !args[1])
-		return;
-
 	purple_notify_error(gc, NULL, _("Nick or channel is temporarily unavailable."), args[1]);
 }
 
@@ -1456,8 +1390,7 @@
 	PurpleConnection *gc = purple_account_get_connection(irc->account);
 	char *nick, *msg;
 
-	if (!args || !args[0] || !gc)
-		return;
+	g_return_if_fail(gc);
 
 	nick = irc_mask_nick(from);
 	msg = g_strdup_printf (_("Wallops from %s"), nick);
@@ -1633,7 +1566,7 @@
 	PurpleConnection *gc = purple_account_get_connection(irc->account);
 	const char *mech_list = NULL;
 
-	if (!args[1] || !args[2] || strncmp(args[2], "sasl ", 6))
+	if (strncmp(args[2], "sasl ", 6))
 		return;
 	if (strncmp(args[1], "ACK", 4)) {
 		const char *tmp = _("SASL authentication failed: Server does not support SASL authentication.");
Index: pidgin-2.10.7/libpurple/protocols/irc/parse.c
===================================================================
--- pidgin-2.10.7.orig/libpurple/protocols/irc/parse.c	2013-02-11 04:16:52.000000000 -0500
+++ pidgin-2.10.7/libpurple/protocols/irc/parse.c	2014-02-05 15:06:09.228167298 -0500
@@ -50,80 +50,85 @@
 static struct _irc_msg {
 	char *name;
 	char *format;
+
+	/** The required parameter count, based on values we use, not protocol
+	 *  specification. */
+	int req_cnt;
+
 	void (*cb)(struct irc_conn *irc, const char *name, const char *from, char **args);
 } _irc_msgs[] = {
-	{ "005", "n*", irc_msg_features },	/* Feature list			*/
-	{ "251", "n:", irc_msg_luser },		/* Client & Server count	*/
-	{ "255", "n:", irc_msg_luser },		/* Client & Server count Mk. II	*/
-	{ "301", "nn:", irc_msg_away },		/* User is away			*/
-	{ "303", "n:", irc_msg_ison },		/* ISON reply			*/
-	{ "311", "nnvvv:", irc_msg_whois },	/* Whois user			*/
-	{ "312", "nnv:", irc_msg_whois },	/* Whois server			*/
-	{ "313", "nn:", irc_msg_whois },	/* Whois ircop			*/
-	{ "317", "nnvv", irc_msg_whois },	/* Whois idle			*/
-	{ "318", "nt:", irc_msg_endwhois },	/* End of WHOIS			*/
-	{ "319", "nn:", irc_msg_whois },	/* Whois channels		*/
-	{ "320", "nn:", irc_msg_whois },	/* Whois (fn ident)		*/
-	{ "314", "nnnvv:", irc_msg_whois },	/* Whowas user			*/
-	{ "315", "nt:", irc_msg_who },      /* end of WHO channel   */
-	{ "369", "nt:", irc_msg_endwhois },	/* End of WHOWAS		*/
-	{ "321", "*", irc_msg_list },		/* Start of list		*/
-	{ "322", "ncv:", irc_msg_list },	/* List.			*/
-	{ "323", ":", irc_msg_list },		/* End of list.			*/
-	{ "324", "ncv:", irc_msg_chanmode },	/* Channel modes		*/
-	{ "331", "nc:",	irc_msg_topic },	/* No channel topic		*/
-	{ "332", "nc:", irc_msg_topic },	/* Channel topic		*/
-	{ "333", "ncvv", irc_msg_topicinfo },	/* Topic setter stuff		*/
-	{ "352", "ncvvvnv:", irc_msg_who },	/* Channel WHO			*/
-	{ "353", "nvc:", irc_msg_names },	/* Names list			*/
-	{ "366", "nc:", irc_msg_names },	/* End of names			*/
-	{ "367", "ncnnv", irc_msg_ban },	/* Ban list			*/
-	{ "368", "nc:", irc_msg_ban },		/* End of ban list		*/
-	{ "372", "n:", irc_msg_motd },		/* MOTD				*/
-	{ "375", "n:", irc_msg_motd },		/* Start MOTD			*/
-	{ "376", "n:", irc_msg_motd },		/* End of MOTD			*/
-	{ "391", "nv:", irc_msg_time },		/* Time reply			*/
-	{ "401", "nt:", irc_msg_nonick },	/* No such nick/chan		*/
-	{ "406", "nt:", irc_msg_nonick },	/* No such nick for WHOWAS	*/
-	{ "403", "nc:", irc_msg_nochan },	/* No such channel		*/
-	{ "404", "nt:", irc_msg_nosend },	/* Cannot send to chan		*/
-	{ "421", "nv:", irc_msg_unknown },	/* Unknown command		*/
-	{ "422", "n:", irc_msg_motd },		/* MOTD file missing		*/
-	{ "432", "vn:", irc_msg_badnick },	/* Erroneous nickname		*/
-	{ "433", "vn:", irc_msg_nickused },	/* Nickname already in use	*/
-	{ "437", "nc:", irc_msg_unavailable },  /* Nick/channel is unavailable */
-	{ "438", "nn:", irc_msg_nochangenick },	/* Nick may not change		*/
-	{ "442", "nc:", irc_msg_notinchan },	/* Not in channel		*/
-	{ "473", "nc:", irc_msg_inviteonly },	/* Tried to join invite-only	*/
-	{ "474", "nc:", irc_msg_banned },	/* Banned from channel		*/
-	{ "477", "nc:", irc_msg_regonly },	/* Registration Required	*/
-	{ "478", "nct:", irc_msg_banfull },	/* Banlist is full		*/
-	{ "482", "nc:", irc_msg_notop },	/* Need to be op to do that	*/
-	{ "501", "n:", irc_msg_badmode },	/* Unknown mode flag		*/
-	{ "506", "nc:", irc_msg_nosend },	/* Must identify to send	*/
-	{ "515", "nc:", irc_msg_regonly },	/* Registration required	*/
+	{ "005", "n*", 2, irc_msg_features },		/* Feature list			*/
+	{ "251", "n:", 1, irc_msg_luser },		/* Client & Server count	*/
+	{ "255", "n:", 1, irc_msg_luser },		/* Client & Server count Mk. II	*/
+	{ "301", "nn:", 3, irc_msg_away },		/* User is away			*/
+	{ "303", "n:", 2, irc_msg_ison },		/* ISON reply			*/
+	{ "311", "nnvvv:", 6, irc_msg_whois },		/* Whois user			*/
+	{ "312", "nnv:", 4, irc_msg_whois },		/* Whois server			*/
+	{ "313", "nn:", 2, irc_msg_whois },		/* Whois ircop			*/
+	{ "317", "nnvv", 4, irc_msg_whois },		/* Whois idle			*/
+	{ "318", "nt:", 2, irc_msg_endwhois },		/* End of WHOIS			*/
+	{ "319", "nn:", 3, irc_msg_whois },		/* Whois channels		*/
+	{ "320", "nn:", 2, irc_msg_whois },		/* Whois (fn ident)		*/
+	{ "314", "nnnvv:", 6, irc_msg_whois },		/* Whowas user			*/
+	{ "315", "nt:", 0, irc_msg_who },		/* end of WHO channel		*/
+	{ "369", "nt:", 2, irc_msg_endwhois },		/* End of WHOWAS		*/
+	{ "321", "*", 0, irc_msg_list },		/* Start of list		*/
+	{ "322", "ncv:", 4, irc_msg_list },		/* List.			*/
+	{ "323", ":", 0, irc_msg_list },		/* End of list.			*/
+	{ "324", "ncv:", 3, irc_msg_chanmode },		/* Channel modes		*/
+	{ "331", "nc:", 3, irc_msg_topic },		/* No channel topic		*/
+	{ "332", "nc:", 3, irc_msg_topic },		/* Channel topic		*/
+	{ "333", "ncvv", 4, irc_msg_topicinfo },	/* Topic setter stuff		*/
+	{ "352", "ncvvvnv:", 8, irc_msg_who },		/* Channel WHO			*/
+	{ "353", "nvc:", 4, irc_msg_names },		/* Names list			*/
+	{ "366", "nc:", 2, irc_msg_names },		/* End of names			*/
+	{ "367", "ncnnv", 3, irc_msg_ban },		/* Ban list			*/
+	{ "368", "nc:", 2, irc_msg_ban },		/* End of ban list		*/
+	{ "372", "n:", 1, irc_msg_motd },		/* MOTD				*/
+	{ "375", "n:", 1, irc_msg_motd },		/* Start MOTD			*/
+	{ "376", "n:", 1, irc_msg_motd },		/* End of MOTD			*/
+	{ "391", "nv:", 3, irc_msg_time },		/* Time reply			*/
+	{ "401", "nt:", 2, irc_msg_nonick },		/* No such nick/chan		*/
+	{ "406", "nt:", 2, irc_msg_nonick },		/* No such nick for WHOWAS	*/
+	{ "403", "nc:", 2, irc_msg_nochan },		/* No such channel		*/
+	{ "404", "nt:", 3, irc_msg_nosend },		/* Cannot send to chan		*/
+	{ "421", "nv:", 2, irc_msg_unknown },		/* Unknown command		*/
+	{ "422", "n:", 1, irc_msg_motd },		/* MOTD file missing		*/
+	{ "432", "vn:", 0, irc_msg_badnick },		/* Erroneous nickname		*/
+	{ "433", "vn:", 2, irc_msg_nickused },		/* Nickname already in use	*/
+	{ "437", "nc:", 2, irc_msg_unavailable },	/* Nick/channel is unavailable	*/
+	{ "438", "nn:", 3, irc_msg_nochangenick },	/* Nick may not change		*/
+	{ "442", "nc:", 3, irc_msg_notinchan },		/* Not in channel		*/
+	{ "473", "nc:", 2, irc_msg_inviteonly },	/* Tried to join invite-only	*/
+	{ "474", "nc:", 2, irc_msg_banned },		/* Banned from channel		*/
+	{ "477", "nc:", 3, irc_msg_regonly },		/* Registration Required	*/
+	{ "478", "nct:", 3, irc_msg_banfull },		/* Banlist is full		*/
+	{ "482", "nc:", 3, irc_msg_notop },		/* Need to be op to do that	*/
+	{ "501", "n:", 2, irc_msg_badmode },		/* Unknown mode flag		*/
+	{ "506", "nc:", 3, irc_msg_nosend },		/* Must identify to send	*/
+	{ "515", "nc:", 3, irc_msg_regonly },		/* Registration required	*/
 #ifdef HAVE_CYRUS_SASL
-	{ "903", "*", irc_msg_authok},		/* SASL auth successful		*/
-	{ "904", "*", irc_msg_authtryagain },	/* SASL auth failed, can recover		*/
-	{ "905", "*", irc_msg_authfail },	/* SASL auth failed		*/
-	{ "906", "*", irc_msg_authfail },	/* SASL auth failed		*/
-	{ "907", "*", irc_msg_authfail },	/* SASL auth failed		*/
-	{ "cap", "vv:", irc_msg_cap },		/* SASL capable			*/
+	{ "903", "*", 0, irc_msg_authok},		/* SASL auth successful		*/
+	{ "904", "*", 0, irc_msg_authtryagain },	/* SASL auth failed, can recover*/
+	{ "905", "*", 0, irc_msg_authfail },		/* SASL auth failed		*/
+	{ "906", "*", 0, irc_msg_authfail },		/* SASL auth failed		*/
+	{ "907", "*", 0, irc_msg_authfail },		/* SASL auth failed		*/
+	{ "cap", "vv:", 3, irc_msg_cap },		/* SASL capable			*/
 #endif
-	{ "invite", "n:", irc_msg_invite },	/* Invited			*/
-	{ "join", ":", irc_msg_join },		/* Joined a channel		*/
-	{ "kick", "cn:", irc_msg_kick },	/* KICK				*/
-	{ "mode", "tv:", irc_msg_mode },	/* MODE for channel		*/
-	{ "nick", ":", irc_msg_nick },		/* Nick change			*/
-	{ "notice", "t:", irc_msg_notice },	/* NOTICE recv			*/
-	{ "part", "c:", irc_msg_part },		/* Parted a channel		*/
-	{ "ping", ":", irc_msg_ping },		/* Received PING from server	*/
-	{ "pong", "v:", irc_msg_pong },		/* Received PONG from server	*/
-	{ "privmsg", "t:", irc_msg_privmsg },	/* Received private message	*/
-	{ "topic", "c:", irc_msg_topic },	/* TOPIC command		*/
-	{ "quit", ":", irc_msg_quit },		/* QUIT notice			*/
-	{ "wallops", ":", irc_msg_wallops },	/* WALLOPS command		*/
-	{ NULL, NULL, NULL }
+	{ "invite", "n:", 2, irc_msg_invite },		/* Invited			*/
+	{ "join", ":", 1, irc_msg_join },		/* Joined a channel		*/
+	{ "kick", "cn:", 3, irc_msg_kick },		/* KICK				*/
+	{ "mode", "tv:", 2, irc_msg_mode },		/* MODE for channel		*/
+	{ "nick", ":", 1, irc_msg_nick },		/* Nick change			*/
+	{ "notice", "t:", 2, irc_msg_notice },		/* NOTICE recv			*/
+	{ "part", "c:", 1, irc_msg_part },		/* Parted a channel		*/
+	{ "ping", ":", 1, irc_msg_ping },		/* Received PING from server	*/
+	{ "pong", "v:", 2, irc_msg_pong },		/* Received PONG from server	*/
+	{ "privmsg", "t:", 2, irc_msg_privmsg },	/* Received private message	*/
+	{ "topic", "c:", 2, irc_msg_topic },		/* TOPIC command		*/
+	{ "quit", ":", 1, irc_msg_quit },		/* QUIT notice			*/
+	{ "wallops", ":", 1, irc_msg_wallops },		/* WALLOPS command		*/
+	{ NULL, NULL, 0, NULL }
 };
 
 static struct _irc_user_cmd {
@@ -660,6 +665,8 @@
 	char *cur, *end, *tmp, *from, *msgname, *fmt, **args, *msg;
 	guint i;
 	PurpleConnection *gc = purple_account_get_connection(irc->account);
+	gboolean fmt_valid;
+	int args_cnt;
 
 	irc->recv_time = time(NULL);
 
@@ -716,7 +723,9 @@
 	}
 	g_free(msgname);
 
+	fmt_valid = TRUE;
 	args = g_new0(char *, strlen(msgent->format));
+	args_cnt = 0;
 	for (cur = end, fmt = msgent->format, i = 0; fmt[i] && *cur++; i++) {
 		switch (fmt[i]) {
 		case 'v':
@@ -753,12 +762,23 @@
 			break;
 		default:
 			purple_debug(PURPLE_DEBUG_ERROR, "irc", "invalid message format character '%c'\n", fmt[i]);
+			fmt_valid = FALSE;
 			break;
 		}
+		if (fmt_valid)
+			args_cnt = i + 1;
+	}
+	if (G_UNLIKELY(!fmt_valid)) {
+		purple_debug_error("irc", "message format was invalid");
+	} else if (G_LIKELY(args_cnt >= msgent->req_cnt)) {
+		tmp = irc_recv_convert(irc, from);
+		(msgent->cb)(irc, msgent->name, tmp, args);
+		g_free(tmp);
+	} else {
+		purple_debug_error("irc", "args count (%d) doesn't reach "
+			"expected value of %d for the '%s' command",
+			args_cnt, msgent->req_cnt, msgent->name);
 	}
-	tmp = irc_recv_convert(irc, from);
-	(msgent->cb)(irc, msgent->name, tmp, args);
-	g_free(tmp);
 	for (i = 0; i < strlen(msgent->format); i++) {
 		g_free(args[i]);
 	}
