--- a/libpurple/protocols/irc/parse.c Mon Jan 20 07:39:26 2014 +0530 +++ b/libpurple/protocols/irc/parse.c Mon Jan 20 00:02:17 2014 -0800 @@ -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 { @@ -664,6 +669,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); @@ -720,7 +727,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': @@ -757,12 +766,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; } - tmp = irc_recv_convert(irc, from); - (msgent->cb)(irc, msgent->name, tmp, args); - g_free(tmp); + 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); + } for (i = 0; i < strlen(msgent->format); i++) { g_free(args[i]); }