view src/protocols/qq/group_info.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 983fd420e86b
children ef8490f9e823
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 "debug.h"		// gaim_debug
#include "conversation.h"	// gaim_find_conversation_with_account

#include "buddy_status.h"	// QQ_BUDDY_ONLINE_NORMAL
#include "char_conv.h"		// convert_as_pascal_string
#include "group_find.h"		// qq_group_find_by_internal_group_id
#include "group_hash.h"		// qq_group_refresh
#include "group_info.h"
#include "buddy_status.h"	// is_online
#include "group_network.h"	// qq_send_group_cmd

// we check who needs to update member info every minutes
// this interval determines if their member info is outdated
#define QQ_GROUP_CHAT_REFRESH_NICKNAME_INTERNAL  180

/*****************************************************************************/
static gboolean _is_group_member_need_update_info(qq_buddy * member)
{
	g_return_val_if_fail(member != NULL, FALSE);
	return (member->nickname == NULL) ||
	    (time(NULL) - member->last_refresh) > QQ_GROUP_CHAT_REFRESH_NICKNAME_INTERNAL;
}				// _is_group_member_need_update_info

/*****************************************************************************/
// this is done when we receive the reply to get_online_member sub_cmd
// all member are set offline, and then only those in reply packets are online
static void _qq_group_set_members_all_offline(qq_group * group)
{
	GList *list;
	qq_buddy *member;
	g_return_if_fail(group != NULL);

	list = group->members;
	while (list != NULL) {
		member = (qq_buddy *) list->data;
		member->status = QQ_BUDDY_ONLINE_OFFLINE;
		list = list->next;
	}			// while list
}				// _qq_group_set_members_all_offline

/*****************************************************************************/
// send packet to get detailed information of one group
void qq_send_cmd_group_get_group_info(GaimConnection * gc, qq_group * group)
{
	guint8 *raw_data, *cursor;
	gint bytes, data_len;

	g_return_if_fail(gc != NULL && group != NULL);

	data_len = 5;
	raw_data = g_newa(guint8, data_len);
	cursor = raw_data;

	bytes = 0;
	bytes += create_packet_b(raw_data, &cursor, QQ_GROUP_CMD_GET_GROUP_INFO);
	bytes += create_packet_dw(raw_data, &cursor, group->internal_group_id);

	if (bytes != data_len)
		gaim_debug(GAIM_DEBUG_ERROR, "QQ",
			   "Fail create packet for %s\n", qq_group_cmd_get_desc(QQ_GROUP_CMD_GET_GROUP_INFO));
	else
		qq_send_group_cmd(gc, group, raw_data, data_len);
}				// qq_send_cmd_group_get_group_info

/*****************************************************************************/
// send packet to get online group member, called by keep_alive
void qq_send_cmd_group_get_online_member(GaimConnection * gc, qq_group * group) {
	guint8 *raw_data, *cursor;
	gint bytes, data_len;

	g_return_if_fail(gc != NULL && group != NULL);

	// only get online members when conversation window is on
	if (NULL == gaim_find_conversation_with_account(GAIM_CONV_TYPE_CHAT/*gfhuang*/,group->group_name_utf8, gaim_connection_get_account(gc))) {
		gaim_debug(GAIM_DEBUG_WARNING, "QQ",
			   "Conv windows for \"%s\" is not on, do not get online members\n", group->group_name_utf8);
		return;
	}			// if gaim_find_conversation_with_account

	data_len = 5;
	raw_data = g_newa(guint8, data_len);
	cursor = raw_data;

	bytes = 0;
	bytes += create_packet_b(raw_data, &cursor, QQ_GROUP_CMD_GET_ONLINE_MEMBER);
	bytes += create_packet_dw(raw_data, &cursor, group->internal_group_id);

	if (bytes != data_len)
		gaim_debug(GAIM_DEBUG_ERROR, "QQ",
			   "Fail create packet for %s\n", qq_group_cmd_get_desc(QQ_GROUP_CMD_GET_ONLINE_MEMBER));
	else
		qq_send_group_cmd(gc, group, raw_data, data_len);
}				// qq_send_cmd_group_search_group

/*****************************************************************************/
// send packet to get group member info
void qq_send_cmd_group_get_member_info(GaimConnection * gc, qq_group * group) {
	guint8 *raw_data, *cursor;
	gint bytes, data_len, i;
	GList *list;
	qq_buddy *member;

	g_return_if_fail(gc != NULL && group != NULL);
	for (i = 0, list = group->members; list != NULL; list = list->next) {
		member = (qq_buddy *) list->data;
		if (_is_group_member_need_update_info(member))
			i++;
	}			// for i

	if (i <= 0) {
		gaim_debug(GAIM_DEBUG_INFO, "QQ", "No group member needs to to update info now.\n");
		return;
	}			// if i

	data_len = 5 + 4 * i;
	raw_data = g_newa(guint8, data_len);
	cursor = raw_data;

	bytes = 0;
	bytes += create_packet_b(raw_data, &cursor, QQ_GROUP_CMD_GET_MEMBER_INFO);
	bytes += create_packet_dw(raw_data, &cursor, group->internal_group_id);

	list = group->members;
	while (list != NULL) {
		member = (qq_buddy *) list->data;
		if (_is_group_member_need_update_info(member))
			bytes += create_packet_dw(raw_data, &cursor, member->uid);
		list = list->next;
	}			// while list

	if (bytes != data_len)
		gaim_debug(GAIM_DEBUG_ERROR, "QQ",
			   "Fail create packet for %s\n", qq_group_cmd_get_desc(QQ_GROUP_CMD_GET_MEMBER_INFO));
	else
		qq_send_group_cmd(gc, group, raw_data, data_len);
}				// qq_send_cmd_group_get_member_info

/*****************************************************************************/
void qq_process_group_cmd_get_group_info(guint8 * data, guint8 ** cursor, gint len, GaimConnection * gc) {
	qq_group *group;
	qq_data *qd;
	guint8 orgnization, role; //gfhuang
	guint16 unknown;
	guint32 member_uid, internal_group_id;
	gint pascal_len, i;
	guint32 unknown4;
	guint8 unknown1;

	g_return_if_fail(gc != NULL && gc->proto_data != NULL);
	g_return_if_fail(data != NULL && len > 0);
	qd = (qq_data *) gc->proto_data;

	read_packet_dw(data, cursor, len, &(internal_group_id));
	g_return_if_fail(internal_group_id > 0);

	group = qq_group_find_by_internal_group_id(gc, internal_group_id);
	g_return_if_fail(group != NULL);

	read_packet_dw(data, cursor, len, &(group->external_group_id));
	read_packet_b(data, cursor, len, &(group->group_type));
	read_packet_dw(data, cursor, len, &unknown4);	//unknown 4 bytes, protocal changed by gfhuang
	read_packet_dw(data, cursor, len, &(group->creator_uid));
	read_packet_b(data, cursor, len, &(group->auth_type));
	read_packet_dw(data, cursor, len, &unknown4);	// oldCategory, by gfhuang
	read_packet_w(data, cursor, len, &unknown);	
	read_packet_dw(data, cursor, len, &(group->group_category));
	read_packet_w(data, cursor, len, &(unknown));	// 0x0000
	read_packet_b(data, cursor, len, &unknown1);
	read_packet_dw(data, cursor, len, &(unknown4));	// versionID, by gfhuang

	pascal_len = convert_as_pascal_string(*cursor, &(group->group_name_utf8), QQ_CHARSET_DEFAULT);
	*cursor += pascal_len;
	read_packet_w(data, cursor, len, &(unknown));	// 0x0000  
	pascal_len = convert_as_pascal_string(*cursor, &(group->notice_utf8), QQ_CHARSET_DEFAULT);
	*cursor += pascal_len;
	pascal_len = convert_as_pascal_string(*cursor, &(group->group_desc_utf8), QQ_CHARSET_DEFAULT);
	*cursor += pascal_len;

	i = 0;
	// now comes the member list separated by 0x00
	while (*cursor < data + len) {
		read_packet_dw(data, cursor, len, &member_uid);
		i++;
		read_packet_b(data, cursor, len, &orgnization);	// protocal changed, gfhuang
		read_packet_b(data, cursor, len, &role);// gfhuang

		if(orgnization != 0 || role != 0) {
			gaim_debug(GAIM_DEBUG_INFO, "QQ", "group member %d: orgnizatio=%d, role=%d\n", member_uid, orgnization, role);
		}
		qq_buddy *member = qq_group_find_or_add_member(gc, group, member_uid);
		member->role = role;
	}			// while *cursor
        if(*cursor > (data + len)) {
                         gaim_debug(GAIM_DEBUG_ERROR, "QQ", "group_cmd_get_group_info: Dangerous error! maybe protocal changed, notify me!");
        }

	gaim_debug(GAIM_DEBUG_INFO, "QQ", "group \"%s\" has %d members\n", group->group_name_utf8, i);

	if (group->creator_uid == qd->uid)
		group->my_status = QQ_GROUP_MEMBER_STATUS_IS_ADMIN;

	qq_group_refresh(gc, group);

	//added topic by gfhuang
	GaimConversation *gaim_conv = gaim_find_conversation_with_account(GAIM_CONV_TYPE_CHAT,group->group_name_utf8, gaim_connection_get_account(gc));
	if(NULL == gaim_conv) {
                gaim_debug(GAIM_DEBUG_WARNING, "QQ",
                           "Conv windows for \"%s\" is not on, do not set topic\n", group->group_name_utf8);
	}
	else {
		gaim_conv_chat_set_topic(GAIM_CONV_CHAT(gaim_conv), NULL, group->notice_utf8);
	}
}				// qq_process_group_cmd_get_group_info

/*****************************************************************************/
void qq_process_group_cmd_get_online_member(guint8 * data, guint8 ** cursor, gint len, GaimConnection * gc) {
	guint32 internal_group_id, member_uid;
	guint8 unknown;
	gint bytes, i;
	qq_group *group;
	qq_buddy *member;

	g_return_if_fail(gc != NULL && data != NULL && len > 0);

	if (data + len - *cursor < 4) {
		gaim_debug(GAIM_DEBUG_ERROR, "QQ", "Invalid group online member reply, discard it!\n");
		return;
	}			// if data_len-*cursor

	bytes = 0;
	i = 0;
	bytes += read_packet_dw(data, cursor, len, &internal_group_id);
	bytes += read_packet_b(data, cursor, len, &unknown);	// 0x3c ??
	g_return_if_fail(internal_group_id > 0);

	group = qq_group_find_by_internal_group_id(gc, internal_group_id);
	if (group == NULL) {
		gaim_debug(GAIM_DEBUG_ERROR, "QQ", "We have no group info for internal id [%d]\n", internal_group_id);
		return;
	}			// if group == NULL

	// set all offline first, then update those online
	_qq_group_set_members_all_offline(group);
	while (*cursor < data + len) {
		bytes += read_packet_dw(data, cursor, len, &member_uid);
		i++;
		member = qq_group_find_or_add_member(gc, group, member_uid);
		if (member != NULL)
			member->status = QQ_BUDDY_ONLINE_NORMAL;
	}			// while
        if(*cursor > (data + len)) {
                         gaim_debug(GAIM_DEBUG_ERROR, "QQ", "group_cmd_get_online_member: Dangerous error! maybe protocal changed, notify me!");
        }

	gaim_debug(GAIM_DEBUG_INFO, "QQ", "Group \"%s\" has %d online members\n", group->group_name_utf8, i);

}				// qq_process_group_cmd_get_online_member

/*****************************************************************************/
// process the reply to get_member_info packet
void qq_process_group_cmd_get_member_info(guint8 * data, guint8 ** cursor, gint len, GaimConnection * gc) {
	guint32 internal_group_id, member_uid;
	guint16 unknown;
	guint8 bar;
	gint pascal_len, i;
	qq_group *group;
	qq_buddy *member;

	g_return_if_fail(gc != NULL && data != NULL && len > 0);

	read_packet_dw(data, cursor, len, &internal_group_id);
	g_return_if_fail(internal_group_id > 0);

	group = qq_group_find_by_internal_group_id(gc, internal_group_id);
	g_return_if_fail(group != NULL);

	i = 0;
	// now starts the member info, as get buddy list reply
	while (*cursor < data + len) {
		read_packet_dw(data, cursor, len, &member_uid);
		g_return_if_fail(member_uid > 0);
		member = qq_group_find_member_by_uid(group, member_uid);
		g_return_if_fail(member != NULL);

		i++;
		read_packet_b(data, cursor, len, &bar);
		read_packet_b(data, cursor, len, &(member->icon));
		read_packet_b(data, cursor, len, &(member->age));
		read_packet_b(data, cursor, len, &(member->gender));
		pascal_len = convert_as_pascal_string(*cursor, &(member->nickname), QQ_CHARSET_DEFAULT);
		*cursor += pascal_len;
		read_packet_w(data, cursor, len, &unknown);
		read_packet_b(data, cursor, len, &(member->flag1));
		read_packet_b(data, cursor, len, &(member->comm_flag));

		member->last_refresh = time(NULL);
	}			// while
        if(*cursor > (data + len)) {
                         gaim_debug(GAIM_DEBUG_ERROR, "QQ", "group_cmd_get_member_info: Dangerous error! maybe protocal changed, notify me!");
        }
	gaim_debug(GAIM_DEBUG_INFO, "QQ", "Group \"%s\" obtained %d member info\n", group->group_name_utf8, i);

}				// qq_process_group_cmd_get_member_info

/*****************************************************************************/
// END OF FILE