Mercurial > pidgin
view src/protocols/qq/buddy_status.c @ 13967:99b9b58b19dd
[gaim-migrate @ 16523]
Fix a crazy MSN crash. Basically it's possible to have more than one
slplink associated with a given switchboard, but our code did not
allow for that. I think it happens when you're in a multi-user
chat and you do stuff with multiple users that involves slplinks.
Like maybe file transfer and buddy icon related stuff.
Tracking this down took an ungodly amount of time, but thanks to
Meebo for letting me do it :-)
committer: Tailor Script <tailor@pidgin.im>
author | Mark Doliner <mark@kingant.net> |
---|---|
date | Thu, 20 Jul 2006 07:31:15 +0000 |
parents | 2be9dfa9569b |
children | 16102b9c5c4a |
line wrap: on
line source
/** * The QQ2003C protocol plugin * * for gaim * * Copyright (C) 2004 Puzzlebird * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ // START OF FILE /*****************************************************************************/ #include <string.h> // g_memmove #include "debug.h" // gaim_debug #include "prefs.h" // gaim_prefs_get_bool #include "utils.h" // get_ip_str #include "packet_parse.h" // create_packet, #include "buddy_status.h" #include "crypt.h" // qq_crypt.h #include "header_info.h" // cmd alias #include "keep_alive.h" // qq_update_buddy_contact #include "send_core.h" // qq_send_cmd #define QQ_MISC_STATUS_HAVING_VIIDEO 0x00000001 #define QQ_ICON_SUFFIX_DEFAULT QQ_ICON_SUFFIX_OFFLINE #define QQ_CHANGE_ONLINE_STATUS_REPLY_OK 0x30 // ASCii value of "0" enum { QQ_ICON_SUFFIX_NORMAL = 1, QQ_ICON_SUFFIX_OFFLINE = 2, QQ_ICON_SUFFIX_AWAY = 3, }; /*****************************************************************************/ static void _qq_buddy_status_dump_unclear(qq_buddy_status * s) { GString *dump; g_return_if_fail(s != NULL); dump = g_string_new(""); g_string_append_printf(dump, "unclear fields for [%d]:\n", s->uid); g_string_append_printf(dump, "004: %02x (unknown)\n", s->unknown1); g_string_append_printf(dump, "011: %02x (unknown)\n", s->unknown2); gaim_debug(GAIM_DEBUG_INFO, "QQ", "Buddy status entry, %s", dump->str); g_string_free(dump, TRUE); } // _qq_buddy_status_dump_unclear /*****************************************************************************/ // parse the data into qq_buddy_status gint _qq_buddy_status_read(guint8 * data, guint8 ** cursor, gint len, qq_buddy_status * s) { gint bytes; g_return_val_if_fail(data != NULL && *cursor != NULL && s != NULL, -1); bytes = 0; // 000-003: uid bytes += read_packet_dw(data, cursor, len, &s->uid); // 004-004: 0x01 bytes += read_packet_b(data, cursor, len, &s->unknown1); // 005-008: ip s->ip = g_new0(guint8, 4); bytes += read_packet_data(data, cursor, len, s->ip, 4); // 009-010: port bytes += read_packet_w(data, cursor, len, &s->port); // 011-011: 0x00 bytes += read_packet_b(data, cursor, len, &s->unknown2); // 012-012: status bytes += read_packet_b(data, cursor, len, &s->status); // 013-014: client_version bytes += read_packet_w(data, cursor, len, &s->client_version); // 015-030: unknown key s->unknown_key = g_new0(guint8, QQ_KEY_LENGTH); bytes += read_packet_data(data, cursor, len, s->unknown_key, QQ_KEY_LENGTH); if (s->uid == 0 || bytes != 31) return -1; return bytes; } // _qq_buddy_status_read /*****************************************************************************/ // check if status means online or offline gboolean is_online(guint8 status) { // return (status == QQ_BUDDY_ONLINE_NORMAL ? TRUE : (status == QQ_BUDDY_ONLINE_AWAY ? TRUE : FALSE)); //rewritten by gfhuang switch(status) { case QQ_BUDDY_ONLINE_NORMAL: case QQ_BUDDY_ONLINE_AWAY: case QQ_BUDDY_ONLINE_INVISIBLE: return TRUE; case QQ_BUDDY_ONLINE_OFFLINE: return FALSE; } return FALSE; } // is_online /*****************************************************************************/ // the icon suffix is detemined by status // although QQ server may return the right icon, I set it here myself gchar get_suffix_from_status(guint8 status) { switch (status) { case QQ_BUDDY_ONLINE_NORMAL: return QQ_ICON_SUFFIX_NORMAL; case QQ_BUDDY_ONLINE_AWAY: return QQ_ICON_SUFFIX_AWAY; case QQ_BUDDY_ONLINE_INVISIBLE: case QQ_BUDDY_ONLINE_OFFLINE: return QQ_ICON_SUFFIX_OFFLINE; default: return QQ_ICON_SUFFIX_DEFAULT; } // switch } // get_suffix_from_status /*****************************************************************************/ // send a packet to change my online status void qq_send_packet_change_status(GaimConnection * gc) { qq_data *qd; guint8 *raw_data, *cursor, away_cmd; guint32 misc_status; gboolean fake_video; g_return_if_fail(gc != NULL && gc->proto_data != NULL); qd = (qq_data *) gc->proto_data; if (!qd->logged_in) return; switch (qd->status) { case QQ_SELF_STATUS_AVAILABLE: away_cmd = QQ_BUDDY_ONLINE_NORMAL; break; case QQ_SELF_STATUS_INVISIBLE: away_cmd = QQ_BUDDY_ONLINE_INVISIBLE; break; case QQ_SELF_STATUS_AWAY: case QQ_SELF_STATUS_IDLE: case QQ_SELF_STATUS_CUSTOM: away_cmd = QQ_BUDDY_ONLINE_AWAY; break; default: away_cmd = QQ_BUDDY_ONLINE_NORMAL; } // switch raw_data = g_new0(guint8, 5); cursor = raw_data; misc_status = 0x00000000; fake_video = gaim_prefs_get_bool("/plugins/prpl/qq/show_fake_video"); if (fake_video) misc_status |= QQ_MISC_STATUS_HAVING_VIIDEO; create_packet_b(raw_data, &cursor, away_cmd); create_packet_dw(raw_data, &cursor, misc_status); qq_send_cmd(gc, QQ_CMD_CHANGE_ONLINE_STATUS, TRUE, 0, TRUE, raw_data, 5); g_free(raw_data); } // qq_send_packet_change_status /*****************************************************************************/ // parse the reply packet for change_status void qq_process_change_status_reply(guint8 * buf, gint buf_len, GaimConnection * gc) { qq_data *qd; gint len; guint8 *data, *cursor, reply; g_return_if_fail(gc != NULL && gc->proto_data != NULL); g_return_if_fail(buf != NULL && buf_len != 0); qd = (qq_data *) gc->proto_data; len = buf_len; data = g_newa(guint8, len); if (qq_crypt(DECRYPT, buf, buf_len, qd->session_key, data, &len)) { cursor = data; read_packet_b(data, &cursor, len, &reply); if (reply != QQ_CHANGE_ONLINE_STATUS_REPLY_OK) { gaim_debug(GAIM_DEBUG_WARNING, "QQ", "Change status fail\n"); } else gaim_debug(GAIM_DEBUG_INFO, "QQ", "Change status OK\n"); } else gaim_debug(GAIM_DEBUG_ERROR, "QQ", "Error decrypt chg status reply\n"); } // qq_process_change_status_reply /*****************************************************************************/ // it is a server message // indicating that one of my buddies changes its status void qq_process_friend_change_status(guint8 * buf, gint buf_len, GaimConnection * gc) { qq_data *qd; gint len, bytes; guint32 my_uid; guint8 *data, *cursor; GaimBuddy *b; qq_buddy *q_bud; qq_buddy_status *s; gchar *name; g_return_if_fail(gc != NULL && gc->proto_data != NULL); g_return_if_fail(buf != NULL && buf_len != 0); qd = (qq_data *) gc->proto_data; len = buf_len; data = g_newa(guint8, len); cursor = data; if (qq_crypt(DECRYPT, buf, buf_len, qd->session_key, data, &len)) { s = g_new0(qq_buddy_status, 1); bytes = 0; // 000-030: qq_buddy_status; bytes += _qq_buddy_status_read(data, &cursor, len, s); // 031-034: my uid bytes += read_packet_dw(data, &cursor, len, &my_uid); if (my_uid == 0 || bytes != 35) { gaim_debug(GAIM_DEBUG_ERROR, "QQ", "my_uid == 0 || bytes(%d) != 35\n", bytes); g_free(s->ip); g_free(s->unknown_key); g_free(s); return; } // if (QQ_DEBUG) gfhuang // _qq_buddy_status_dump_unclear(s); name = uid_to_gaim_name(s->uid); //by gfhuang b = gaim_find_buddy(gc->account, name); g_free(name); q_bud = (b == NULL) ? NULL : (qq_buddy *) b->proto_data; if (q_bud) { gaim_debug(GAIM_DEBUG_INFO, "QQ", "s->uid = %d, q_bud->uid = %d\n", s->uid , q_bud->uid); if(0 != *((guint32 *)s->ip)) { //by gfhuang g_memmove(q_bud->ip, s->ip, 4); q_bud->port = s->port; } q_bud->status = s->status; if(0 != s->client_version) q_bud->client_version = s->client_version; //gfhuang qq_update_buddy_contact(gc, q_bud); } else gaim_debug(GAIM_DEBUG_ERROR, "QQ", "got information of unknown buddy by gfhuang %d\n", s->uid); g_free(s->ip); g_free(s->unknown_key); g_free(s); } else gaim_debug(GAIM_DEBUG_ERROR, "QQ", "Error decrypt buddy status change packet\n"); } // qq_process_friend_change_status /*****************************************************************************/ // END OF FILE