view src/protocols/qq/utils.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 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 "stdlib.h"		// strtol
#include "limits.h"
#include "string.h"		// strlen

#ifdef _WIN32
#include "win32dep.h"
#endif

#include "debug.h"		// gaim_debug
#include "utils.h"
#include "char_conv.h"		// qq_to_utf8
#include "prefs.h"		// gaim_prefs_get_string

#define QQ_NAME_FORMAT    "qq-%d"

#ifndef g_str_has_prefix
gint g_str_has_prefix(const gchar *str, const gchar *prefix)
{
	gint len = strlen(prefix);
	return !strncmp(str, prefix, len);
}
#endif

/*****************************************************************************/
gchar *get_name_by_index_str(gchar ** array, const gchar * index_str, gint amount) {
	gint index;

	index = atoi(index_str);
	if (index < 0 || index >= amount)
		index = 0;

	return array[index];
}				// get_name_by_index_str

/*****************************************************************************/
gchar *get_index_str_by_name(gchar ** array, const gchar * name, gint amount) {
	gint index;

	for (index = 0; index <= amount; index++)
		if (g_ascii_strcasecmp(array[index], name) == 0)
			break;

	if (index >= amount)
		index = 0;	// meaning no match
	return g_strdup_printf("%d", index);
}				// get_index_str_by_name

/*****************************************************************************/
gint qq_string_to_dec_value(const gchar * str)
{
	g_return_val_if_fail(str != NULL, 0);
	return strtol(str, NULL, 10);
}				// _qq_string_to_dec_value

/*****************************************************************************/
// split the given data(len) with delimit,
// check the number of field matches the expected_fields (<=0 means all)
// return gchar* array (needs to be freed by g_strfreev later), or NULL
gchar **split_data(guint8 * data, gint len, const gchar * delimit, gint expected_fields) {

	guint8 *input;
	gchar **segments;
	gint i, j;

	g_return_val_if_fail(data != NULL && len != 0 && delimit != 0, NULL);

	// as the last field would be string, but data is not ended with 0x00
	// we have to duplicate the data and append a 0x00 at the end
	input = g_newa(guint8, len + 1);
	g_memmove(input, data, len);
	input[len] = 0x00;

	segments = g_strsplit(input, delimit, 0);
	if (expected_fields <= 0)
		return segments;

	for (i = 0; segments[i] != NULL; i++) {;
	}
	if (i < expected_fields) {	// not enough fields
		gaim_debug(GAIM_DEBUG_ERROR, "QQ",
			   "Invalid data, expect %d fields, found only %d, discard\n", expected_fields, i);
		g_strfreev(segments);
		return NULL;
	} else if (i > expected_fields) {	// more fields, OK
		gaim_debug(GAIM_DEBUG_WARNING, "QQ",
			   "Dangerous data, expect %d fields, found %d, return all\n", expected_fields, i);
		// free up those not used
		for (j = expected_fields; j < i; j++) {
			gaim_debug(GAIM_DEBUG_WARNING, "QQ", "field[%d] is %s\n", j, segments[j]);
			g_free(segments[j]); // bug found by gfhuang ! i -> j
		}
		
		segments[expected_fields] = NULL;
	}			// if i

	return segments;
}				// split_data

/*****************************************************************************/
// given a four-byte ip data, convert it into a human readable ip string
// the return needs to be freed
gchar *gen_ip_str(guint8 * ip)
{
	return g_strdup_printf("%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
}				// gen_ip_str

// by gfhuang
guint8 *str_ip_gen(gchar *str) {
	guint8 *ip = g_new(guint8, 4);
	int a, b, c, d;
	sscanf(str, "%d.%d.%d.%d", &a, &b, &c, &d);
	ip[0] = a;
	ip[1] = b;
	ip[2] = c;
	ip[3] = d;
	return ip;
}

/*****************************************************************************/
// return the QQ icon file name
// the return needs to be freed
gchar *get_icon_name(gint set, gint suffix)
{
	return g_strdup_printf("qq_%d-%d", set, suffix);
}				// get_icon_name

/*****************************************************************************/
// convert a QQ UID to a unique name of GAIM
// the return needs to be freed
gchar *uid_to_gaim_name(guint32 uid)
{
	return g_strdup_printf(QQ_NAME_FORMAT, uid);
}				// uid_to_gaim_name

/*****************************************************************************/
// convert GAIM name to original QQ UID
guint32 gaim_name_to_uid(const gchar * name)
{
	gchar *p;

	g_return_val_if_fail(g_str_has_prefix(name, QQ_NAME_PREFIX), 0);

	p = g_strrstr(name, QQ_NAME_PREFIX);
	// atoi is not thread-safe and also not async-cancel safe
	// atoi is deprecated by strtol() and should not be used in new code
	return (p == NULL) ? 0 : strtol(p + strlen(QQ_NAME_PREFIX), NULL, 10);
}				// gaim_name_to_uid

/*****************************************************************************/
// convert QQ icon index into its pixbuf
GdkPixbuf *get_face_gdkpixbuf(guint8 index)
{
	gint set, suffix;
	gchar *image_name, *file_name;
	GdkPixbuf *pixbuf;
	const gchar *datadir;

	set = (index / 3) + 1;
	suffix = (index % 3) + 1;

	image_name = g_strdup_printf("%s.png", get_icon_name(set, suffix));
	// we need to configure DATADIR in Makefile.am
	// st = -DDATADIR=\"$(datadir)\"
	datadir = gaim_prefs_get_string("/plugins/prpl/qq/datadir");
	if (datadir == NULL || strlen(datadir) == 0)
		file_name = g_build_filename(datadir, "pixmaps", "gaim", "status", "default", image_name, NULL);
	else
		file_name = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", image_name, NULL);

	pixbuf = gdk_pixbuf_new_from_file(file_name, NULL);

	g_free(image_name);
	g_free(file_name);

	return pixbuf;
}				// get_face_gdkpixbuf

/*****************************************************************************/
// try to dump the data as GBK
void try_dump_as_gbk(guint8 * data, gint len)
{
	gint i;
	guint8 *incoming;
	gchar *msg_utf8;

	incoming = g_newa(guint8, len + 1);
	g_memmove(incoming, data, len);
	incoming[len] = 0x00;
	// GBK code: 
	// Single-byte ASCII:      0x21-0x7E
	// GBK first byte range:   0x81-0xFE
	// GBK second byte range:  0x40-0x7E and 0x80-0xFE
	for (i = 0; i < len; i++)
		if (incoming[i] >= 0x81)
			break;

	msg_utf8 = i < len ? qq_to_utf8(&incoming[i], QQ_CHARSET_DEFAULT) : NULL;

	if (msg_utf8 != NULL) {
		gaim_debug(GAIM_DEBUG_WARNING, "QQ", "Try extract GB msg: %s\n", msg_utf8);
		g_free(msg_utf8);
	}			// msg_utf8 != NULL
}				// try_dump_gbk

/*****************************************************************************/
// dump a chunk of raw data into hex string
// the return should be freed later
gchar *hex_dump_to_str(const guint8 * buffer, gint bytes)
{
	GString *str;
	gchar *ret;
	gint i, j, ch;

	str = g_string_new("");
	for (i = 0; i < bytes; i += 16) {
		// length label
		g_string_append_printf(str, "%04d: ", i);

		// dump hex value
		for (j = 0; j < 16; j++)
			if ((i + j) < bytes)
				g_string_append_printf(str, " %02X", buffer[i + j]);
			else
				g_string_append(str, "   ");
		g_string_append(str, "  ");

		// dump ascii value
		for (j = 0; j < 16 && (i + j) < bytes; j++) {
			ch = buffer[i + j] & 127;
			if (ch < ' ' || ch == 127)
				g_string_append_c(str, '.');
			else
				g_string_append_c(str, ch);
		}		// for j
		g_string_append_c(str, '\n');
	}			// for i

	ret = str->str;
	// GString can be freed without freeing it character data
	g_string_free(str, FALSE);

	return ret;
}				// hex_dump_to_str

/*****************************************************************************/
// ENF OF FILE