summaryrefslogtreecommitdiff
path: root/libpurple/protocols/qq/qq_network.c
diff options
context:
space:
mode:
Diffstat (limited to 'libpurple/protocols/qq/qq_network.c')
-rw-r--r--libpurple/protocols/qq/qq_network.c217
1 files changed, 130 insertions, 87 deletions
diff --git a/libpurple/protocols/qq/qq_network.c b/libpurple/protocols/qq/qq_network.c
index aee961c930..e4ea521f72 100644
--- a/libpurple/protocols/qq/qq_network.c
+++ b/libpurple/protocols/qq/qq_network.c
@@ -28,9 +28,9 @@
#include "buddy_info.h"
#include "group_info.h"
-#include "group_free.h"
+#include "group_internal.h"
#include "qq_crypt.h"
-#include "header_info.h"
+#include "qq_define.h"
#include "qq_base.h"
#include "buddy_list.h"
#include "packet_parse.h"
@@ -160,7 +160,7 @@ static gboolean connect_check(gpointer data)
qd->connect_watcher = 0;
}
- if (qd->fd >= 0 && qd->token != NULL && qd->token_len >= 0) {
+ if (qd->fd >= 0 && qd->ld.token != NULL && qd->ld.token_len > 0) {
purple_debug_info("QQ", "Connect ok\n");
return FALSE;
}
@@ -202,7 +202,8 @@ gboolean qq_connect_later(gpointer data)
if (qd->curr_server == NULL || strlen (qd->curr_server) == 0 || qd->connect_retry <= 0) {
if ( set_new_server(qd) != TRUE) {
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ purple_connection_error_reason(gc,
+ PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
_("Failed to connect all servers"));
return FALSE;
}
@@ -220,7 +221,8 @@ gboolean qq_connect_later(gpointer data)
qd->connect_retry--;
if ( !connect_to_server(gc, server, port) ) {
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ purple_connection_error_reason(gc,
+ PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
_("Unable to connect."));
}
@@ -228,6 +230,19 @@ gboolean qq_connect_later(gpointer data)
return FALSE; /* timeout callback stops */
}
+static void redirect_server(PurpleConnection *gc)
+{
+ qq_data *qd;
+ qd = (qq_data *) gc->proto_data;
+
+ if (qd->check_watcher > 0) {
+ purple_timeout_remove(qd->check_watcher);
+ qd->check_watcher = 0;
+ }
+ if (qd->connect_watcher > 0) purple_timeout_remove(qd->connect_watcher);
+ qd->connect_watcher = purple_timeout_add_seconds(QQ_CONNECT_INTERVAL, qq_connect_later, gc);
+}
+
/* process the incoming packet from qq_pending */
static gboolean packet_process(PurpleConnection *gc, guint8 *buf, gint buf_len)
{
@@ -242,6 +257,7 @@ static gboolean packet_process(PurpleConnection *gc, guint8 *buf, gint buf_len)
guint32 room_id;
gint update_class;
guint32 ship32;
+ int ret;
qq_transaction *trans;
@@ -285,23 +301,22 @@ static gboolean packet_process(PurpleConnection *gc, guint8 *buf, gint buf_len)
update_class = qq_trans_get_class(trans);
ship32 = qq_trans_get_ship(trans);
+ if (update_class != 0 || ship32 != 0) {
+ purple_debug_info("QQ", "Process in Update class %d, ship32 %d\n",
+ update_class, ship32);
+ }
switch (cmd) {
case QQ_CMD_TOKEN:
- if (qq_process_token_reply(gc, buf + bytes, bytes_not_read) == QQ_TOKEN_REPLY_OK) {
- qq_send_packet_login(gc);
- }
- break;
+ case QQ_CMD_GET_SERVER:
+ case QQ_CMD_TOKEN_EX:
+ case QQ_CMD_CHECK_PWD:
case QQ_CMD_LOGIN:
- qq_proc_login_cmd(gc, buf + bytes, bytes_not_read);
- /* check is redirect or not, and do it now */
- if (qd->redirect_ip.s_addr != 0) {
- if (qd->check_watcher > 0) {
- purple_timeout_remove(qd->check_watcher);
- qd->check_watcher = 0;
+ ret = qq_proc_login_cmds(gc, cmd, seq, buf + bytes, bytes_not_read, update_class, ship32);
+ if (ret != QQ_LOGIN_REPLY_OK) {
+ if (ret == QQ_LOGIN_REPLY_REDIRECT) {
+ redirect_server(gc);
}
- if (qd->connect_watcher > 0) purple_timeout_remove(qd->connect_watcher);
- qd->connect_watcher = purple_timeout_add_seconds(QQ_CONNECT_INTERVAL, qq_connect_later, gc);
return FALSE; /* do nothing after this function and return now */
}
break;
@@ -312,10 +327,10 @@ static gboolean packet_process(PurpleConnection *gc, guint8 *buf, gint buf_len)
purple_debug_info("QQ", "%s (0x%02X) for room %d, len %d\n",
qq_get_room_cmd_desc(room_cmd), room_cmd, room_id, buf_len);
#endif
- qq_proc_room_cmd(gc, seq, room_cmd, room_id, buf + bytes, bytes_not_read, update_class, ship32);
+ qq_proc_room_cmds(gc, seq, room_cmd, room_id, buf + bytes, bytes_not_read, update_class, ship32);
break;
default:
- qq_proc_client_cmd(gc, cmd, seq, buf + bytes, bytes_not_read, update_class, ship32);
+ qq_proc_client_cmds(gc, cmd, seq, buf + bytes, bytes_not_read, update_class, ship32);
break;
}
@@ -342,7 +357,8 @@ static void tcp_pending(gpointer data, gint source, PurpleInputCondition cond)
qd = (qq_data *) gc->proto_data;
if(cond != PURPLE_INPUT_READ) {
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ purple_connection_error_reason(gc,
+ PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
_("Socket error"));
return;
}
@@ -366,7 +382,9 @@ static void tcp_pending(gpointer data, gint source, PurpleInputCondition cond)
return;
error_msg = g_strdup_printf(_("Lost connection with server:\n%d, %s"), errno, g_strerror(errno));
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_msg);
+ purple_connection_error_reason(gc,
+ PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ error_msg);
g_free(error_msg);
return;
} else if (buf_len == 0) {
@@ -468,7 +486,8 @@ static void udp_pending(gpointer data, gint source, PurpleInputCondition cond)
qd = (qq_data *) gc->proto_data;
if(cond != PURPLE_INPUT_READ) {
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ purple_connection_error_reason(gc,
+ PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
_("Socket error"));
return;
}
@@ -478,7 +497,8 @@ static void udp_pending(gpointer data, gint source, PurpleInputCondition cond)
/* here we have UDP proxy suppport */
buf_len = read(source, buf, MAX_PACKET_SIZE);
if (buf_len <= 0) {
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ purple_connection_error_reason(gc,
+ PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
_("Unable to read from socket"));
return;
}
@@ -526,7 +546,9 @@ static gint udp_send_out(PurpleConnection *gc, guint8 *data, gint data_len)
if (ret < 0) {
/* TODO: what to do here - do we really have to disconnect? */
purple_debug_error("UDP_SEND_OUT", "Send failed: %d, %s\n", errno, g_strerror(errno));
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, g_strerror(errno));
+ purple_connection_error_reason(gc,
+ PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ g_strerror(errno));
}
return ret;
}
@@ -558,8 +580,9 @@ static void tcp_can_write(gpointer data, gint source, PurpleInputCondition cond)
return;
else if (ret < 0) {
/* TODO: what to do here - do we really have to disconnect? */
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
- _("Write Error"));
+ purple_connection_error_reason(gc,
+ PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ _("Write Error"));
return;
}
@@ -603,7 +626,9 @@ static gint tcp_send_out(PurpleConnection *gc, guint8 *data, gint data_len)
/* TODO: what to do here - do we really have to disconnect? */
purple_debug_error("TCP_SEND_OUT",
"Send to socket %d failed: %d, %s\n", qd->fd, errno, g_strerror(errno));
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, g_strerror(errno));
+ purple_connection_error_reason(gc,
+ PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ g_strerror(errno));
return ret;
}
@@ -630,7 +655,8 @@ static gboolean network_timeout(gpointer data)
is_lost_conn = qq_trans_scan(gc);
if (is_lost_conn) {
purple_connection_error_reason(gc,
- PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Connection lost"));
+ PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ _("Connection lost"));
return TRUE;
}
@@ -641,7 +667,13 @@ static gboolean network_timeout(gpointer data)
qd->itv_count.keep_alive--;
if (qd->itv_count.keep_alive <= 0) {
qd->itv_count.keep_alive = qd->itv_config.keep_alive;
- qq_send_packet_keep_alive(gc);
+ if (qd->client_version >= 2008) {
+ qq_request_keep_alive_2008(gc);
+ } else if (qd->client_version >= 2007) {
+ qq_request_keep_alive_2007(gc);
+ } else {
+ qq_request_keep_alive(gc);
+ }
return TRUE;
}
@@ -659,12 +691,15 @@ static gboolean network_timeout(gpointer data)
return TRUE; /* if return FALSE, timeout callback stops */
}
-static void do_request_token(PurpleConnection *gc)
+static void set_all_keys(PurpleConnection *gc)
{
qq_data *qd;
- gchar *conn_msg;
const gchar *passwd;
-
+ guint8 *dest;
+ int dest_len = QQ_KEY_LENGTH;
+#ifndef DEBUG
+ int bytes;
+#endif
/* _qq_show_socket("Got login socket", source); */
g_return_if_fail(gc != NULL && gc->proto_data != NULL);
@@ -675,27 +710,25 @@ static void do_request_token(PurpleConnection *gc)
qd->send_seq = rand() & 0xffff;
qd->is_login = FALSE;
- qd->channel = 1;
qd->uid = strtol(purple_account_get_username(purple_connection_get_account(gc)), NULL, 10);
+#ifdef DEBUG
+ memset(qd->ld.random_key, 0x01, sizeof(qd->ld.random_key));
+#else
+ for (bytes = 0; bytes < sizeof(qd->ld.random_key); bytes++) {
+ qd->ld.random_key[bytes] = (guint8) (rand() & 0xff);
+ }
+#endif
+
/* now generate md5 processed passwd */
passwd = purple_account_get_password(purple_connection_get_account(gc));
/* use twice-md5 of user password as session key since QQ 2003iii */
- qq_get_md5(qd->password_twice_md5, sizeof(qd->password_twice_md5),
- (guint8 *)passwd, strlen(passwd));
- qq_get_md5(qd->password_twice_md5, sizeof(qd->password_twice_md5),
- qd->password_twice_md5, sizeof(qd->password_twice_md5));
-
- g_return_if_fail(qd->network_watcher == 0);
- qd->network_watcher = purple_timeout_add_seconds(qd->itv_config.resend, network_timeout, gc);
-
- /* Update the login progress status display */
- conn_msg = g_strdup_printf(_("Request token"));
- purple_connection_update_progress(gc, conn_msg, 2, QQ_CONNECT_STEPS);
- g_free(conn_msg);
+ dest = qd->ld.pwd_md5;
+ qq_get_md5(dest, dest_len, (guint8 *)passwd, strlen(passwd));
- qq_send_packet_token(gc);
+ dest = qd->ld.pwd_twice_md5;
+ qq_get_md5(dest, dest_len, qd->ld.pwd_md5, dest_len);
}
/* the callback function after socket is built
@@ -740,7 +773,19 @@ static void connect_cb(gpointer data, gint source, const gchar *error_message)
conn->input_handler = purple_input_add(source, PURPLE_INPUT_READ, udp_pending, gc);
}
- do_request_token( gc );
+ g_return_if_fail(qd->network_watcher == 0);
+ qd->network_watcher = purple_timeout_add_seconds(qd->itv_config.resend, network_timeout, gc);
+
+ set_all_keys( gc );
+
+ if (qd->client_version >= 2007) {
+ purple_connection_update_progress(gc, _("Get server ..."), 2, QQ_CONNECT_STEPS);
+ qq_request_get_server(gc);
+ return;
+ }
+
+ purple_connection_update_progress(gc, _("Request token"), 2, QQ_CONNECT_STEPS);
+ qq_request_token(gc);
}
#ifndef purple_proxy_connect_udp
@@ -811,8 +856,8 @@ static void udp_host_resolved(GSList *hosts, gpointer data, const char *error_me
if (!hosts || !hosts->data) {
purple_connection_error_reason(gc,
- PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
- _("Couldn't resolve host"));
+ PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ _("Couldn't resolve host"));
return;
}
@@ -884,21 +929,19 @@ gboolean connect_to_server(PurpleConnection *gc, gchar *server, gint port)
{
PurpleAccount *account ;
qq_data *qd;
- gchar *conn_msg;
g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, FALSE);
account = purple_connection_get_account(gc);
qd = (qq_data *) gc->proto_data;
if (server == NULL || strlen(server) == 0 || port == 0) {
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ purple_connection_error_reason(gc,
+ PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
_("Invalid server or port"));
return FALSE;
}
- conn_msg = g_strdup_printf( _("Connecting server %s, retries %d"), server, port);
- purple_connection_update_progress(gc, conn_msg, 1, QQ_CONNECT_STEPS);
- g_free(conn_msg);
+ purple_connection_update_progress(gc, _("Connecting server ..."), 1, QQ_CONNECT_STEPS);
purple_debug_info("QQ", "Connect to %s:%d\n", server, port);
@@ -959,7 +1002,7 @@ void qq_disconnect(PurpleConnection *gc)
/* finish all I/O */
if (qd->fd >= 0 && qd->is_login) {
- qq_send_packet_logout(gc);
+ qq_request_logout(gc);
}
/* not connected */
@@ -984,23 +1027,20 @@ void qq_disconnect(PurpleConnection *gc)
qq_trans_remove_all(gc);
- if (qd->token) {
- purple_debug_info("QQ", "free token\n");
- g_free(qd->token);
- qd->token = NULL;
- qd->token_len = 0;
- }
- memset(qd->inikey, 0, sizeof(qd->inikey));
- memset(qd->password_twice_md5, 0, sizeof(qd->password_twice_md5));
+ memset(qd->ld.random_key, 0, sizeof(qd->ld.random_key));
+ memset(qd->ld.pwd_md5, 0, sizeof(qd->ld.pwd_md5));
+ memset(qd->ld.pwd_twice_md5, 0, sizeof(qd->ld.pwd_twice_md5));
+ memset(qd->ld.login_key, 0, sizeof(qd->ld.login_key));
memset(qd->session_key, 0, sizeof(qd->session_key));
memset(qd->session_md5, 0, sizeof(qd->session_md5));
+ qd->my_local_ip.s_addr = 0;
+ qd->my_local_port = 0;
qd->my_ip.s_addr = 0;
+ qd->my_port = 0;
- qq_group_free_all(qd);
- qq_add_buddy_request_free(qd);
- qq_info_query_free(qd);
- qq_buddies_list_free(gc->account, qd);
+ qq_room_data_free_all(gc);
+ qq_buddy_data_free_all(gc);
}
static gint packet_encap(qq_data *qd, guint8 *buf, gint maxlen, guint16 cmd, guint16 seq,
@@ -1017,7 +1057,7 @@ static gint packet_encap(qq_data *qd, guint8 *buf, gint maxlen, guint16 cmd, gui
}
/* now comes the normal QQ packet as UDP */
bytes += qq_put8(buf + bytes, QQ_PACKET_TAG);
- bytes += qq_put16(buf + bytes, QQ_CLIENT);
+ bytes += qq_put16(buf + bytes, qd->client_tag);
bytes += qq_put16(buf + bytes, cmd);
bytes += qq_put16(buf + bytes, seq);
@@ -1064,7 +1104,7 @@ static gint packet_send_out(PurpleConnection *gc, guint16 cmd, guint16 seq, guin
}
gint qq_send_cmd_encrypted(PurpleConnection *gc, guint16 cmd, guint16 seq,
- guint8 *encrypted_data, gint encrypted_len, gboolean is_save2trans)
+ guint8 *encrypted, gint encrypted_len, gboolean is_save2trans)
{
gint sent_len;
@@ -1074,9 +1114,9 @@ gint qq_send_cmd_encrypted(PurpleConnection *gc, guint16 cmd, guint16 seq,
seq, qq_get_cmd_desc(cmd), cmd, encrypted_len);
#endif
- sent_len = packet_send_out(gc, cmd, seq, encrypted_data, encrypted_len);
+ sent_len = packet_send_out(gc, cmd, seq, encrypted, encrypted_len);
if (is_save2trans) {
- qq_trans_add_client_cmd(gc, cmd, seq, encrypted_data, encrypted_len, 0, 0);
+ qq_trans_add_client_cmd(gc, cmd, seq, encrypted, encrypted_len, 0, 0);
}
return sent_len;
}
@@ -1086,7 +1126,7 @@ static gint send_cmd_detail(PurpleConnection *gc, guint16 cmd, guint16 seq,
guint8 *data, gint data_len, gboolean is_save2trans, gint update_class, guint32 ship32)
{
qq_data *qd;
- guint8 *encrypted_data;
+ guint8 *encrypted;
gint encrypted_len;
gint bytes_sent;
@@ -1095,18 +1135,18 @@ static gint send_cmd_detail(PurpleConnection *gc, guint16 cmd, guint16 seq,
g_return_val_if_fail(data != NULL && data_len > 0, -1);
/* at most 16 bytes more */
- encrypted_data = g_newa(guint8, data_len + 16);
- encrypted_len = qq_encrypt(encrypted_data, data, data_len, qd->session_key);
+ encrypted = g_newa(guint8, data_len + 16);
+ encrypted_len = qq_encrypt(encrypted, data, data_len, qd->session_key);
if (encrypted_len < 16) {
purple_debug_error("QQ_ENCRYPT", "Error len %d: [%05d] 0x%04X %s\n",
encrypted_len, seq, cmd, qq_get_cmd_desc(cmd));
return -1;
}
- bytes_sent = packet_send_out(gc, cmd, seq, encrypted_data, encrypted_len);
+ bytes_sent = packet_send_out(gc, cmd, seq, encrypted, encrypted_len);
if (is_save2trans) {
- qq_trans_add_client_cmd(gc, cmd, seq, encrypted_data, encrypted_len,
+ qq_trans_add_client_cmd(gc, cmd, seq, encrypted, encrypted_len,
update_class, ship32);
}
return bytes_sent;
@@ -1159,7 +1199,7 @@ gint qq_send_cmd(PurpleConnection *gc, guint16 cmd, guint8 *data, gint data_len)
gint qq_send_server_reply(PurpleConnection *gc, guint16 cmd, guint16 seq, guint8 *data, gint data_len)
{
qq_data *qd;
- guint8 *encrypted_data;
+ guint8 *encrypted;
gint encrypted_len;
gint bytes_sent;
@@ -1172,16 +1212,16 @@ gint qq_send_server_reply(PurpleConnection *gc, guint16 cmd, guint16 seq, guint8
seq, qq_get_cmd_desc(cmd), cmd, data_len);
#endif
/* at most 16 bytes more */
- encrypted_data = g_newa(guint8, data_len + 16);
- encrypted_len = qq_encrypt(encrypted_data, data, data_len, qd->session_key);
+ encrypted = g_newa(guint8, data_len + 16);
+ encrypted_len = qq_encrypt(encrypted, data, data_len, qd->session_key);
if (encrypted_len < 16) {
purple_debug_error("QQ_ENCRYPT", "Error len %d: [%05d] 0x%04X %s\n",
encrypted_len, seq, cmd, qq_get_cmd_desc(cmd));
return -1;
}
- bytes_sent = packet_send_out(gc, cmd, seq, encrypted_data, encrypted_len);
- qq_trans_add_server_reply(gc, cmd, seq, encrypted_data, encrypted_len);
+ bytes_sent = packet_send_out(gc, cmd, seq, encrypted, encrypted_len);
+ qq_trans_add_server_reply(gc, cmd, seq, encrypted, encrypted_len);
return bytes_sent;
}
@@ -1192,7 +1232,7 @@ static gint send_room_cmd(PurpleConnection *gc, guint8 room_cmd, guint32 room_id
qq_data *qd;
guint8 *buf;
gint buf_len;
- guint8 *encrypted_data;
+ guint8 *encrypted;
gint encrypted_len;
gint bytes_sent;
guint16 seq;
@@ -1217,17 +1257,17 @@ static gint send_room_cmd(PurpleConnection *gc, guint8 room_cmd, guint32 room_id
qd->send_seq++;
seq = qd->send_seq;
- /* Encrypt to encrypted_data with session_key */
+ /* Encrypt to encrypted with session_key */
/* at most 16 bytes more */
- encrypted_data = g_newa(guint8, buf_len + 16);
- encrypted_len = qq_encrypt(encrypted_data, buf, buf_len, qd->session_key);
+ encrypted = g_newa(guint8, buf_len + 16);
+ encrypted_len = qq_encrypt(encrypted, buf, buf_len, qd->session_key);
if (encrypted_len < 16) {
purple_debug_error("QQ_ENCRYPT", "Error len %d: [%05d] %s (0x%02X)\n",
encrypted_len, seq, qq_get_room_cmd_desc(room_cmd), room_cmd);
return -1;
}
- bytes_sent = packet_send_out(gc, QQ_CMD_ROOM, seq, encrypted_data, encrypted_len);
+ bytes_sent = packet_send_out(gc, QQ_CMD_ROOM, seq, encrypted, encrypted_len);
#if 1
/* qq_show_packet("send_room_cmd", buf, buf_len); */
purple_debug_info("QQ",
@@ -1235,7 +1275,7 @@ static gint send_room_cmd(PurpleConnection *gc, guint8 room_cmd, guint32 room_id
seq, qq_get_room_cmd_desc(room_cmd), room_cmd, room_id, buf_len);
#endif
- qq_trans_add_room_cmd(gc, seq, room_cmd, room_id, encrypted_data, encrypted_len,
+ qq_trans_add_room_cmd(gc, seq, room_cmd, room_id, encrypted, encrypted_len,
update_class, ship32);
return bytes_sent;
}
@@ -1243,18 +1283,21 @@ static gint send_room_cmd(PurpleConnection *gc, guint8 room_cmd, guint32 room_id
gint qq_send_room_cmd_mess(PurpleConnection *gc, guint8 room_cmd, guint32 room_id,
guint8 *data, gint data_len, gint update_class, guint32 ship32)
{
+ g_return_val_if_fail(room_cmd > 0, -1);
return send_room_cmd(gc, room_cmd, room_id, data, data_len, update_class, ship32);
}
gint qq_send_room_cmd(PurpleConnection *gc, guint8 room_cmd, guint32 room_id,
guint8 *data, gint data_len)
{
+ g_return_val_if_fail(room_cmd > 0 && room_id > 0, -1);
return send_room_cmd(gc, room_cmd, room_id, data, data_len, 0, 0);
}
gint qq_send_room_cmd_noid(PurpleConnection *gc, guint8 room_cmd,
guint8 *data, gint data_len)
{
+ g_return_val_if_fail(room_cmd > 0, -1);
return send_room_cmd(gc, room_cmd, 0, data, data_len, 0, 0);
}