diff src/protocols/qq/ip_location.c @ 13870:983fd420e86b

[gaim-migrate @ 16340] Performed minor cleanup of the OpenQ codebase and patched it into the Gaim trunk as a prpl, providing basic QQ functionality. committer: Tailor Script <tailor@pidgin.im>
author Mark Huetsch <markhuetsch>
date Mon, 26 Jun 2006 02:58:54 +0000
parents
children ef8490f9e823
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/qq/ip_location.c	Mon Jun 26 02:58:54 2006 +0000
@@ -0,0 +1,253 @@
+/**
+ * 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
+ *
+ * This code is based on
+ *  * qqwry.c, by lvxiang <srm2003@163.com>
+ *  * IpChecker.m, by Jeff_Ye
+ */
+
+// START OF FILE
+/*****************************************************************************/
+#include "internal.h"
+#include <string.h>		// memset
+#include "debug.h"
+#include "prefs.h"		// gaim_prefs_get_string
+
+#include "utils.h"
+#include "ip_location.h"
+
+#define DEFAULT_IP_LOCATION_FILE  "gaim/QQWry.dat"
+
+typedef struct _ip_finder ip_finder;
+
+// all offset is based the begining of the file
+struct _ip_finder {
+	guint32 offset_first_start_ip;	// first abs offset of start ip
+	guint32 offset_last_start_ip;	// last abs offset of start ip
+	guint32 cur_start_ip;	// start ip of current search range
+	guint32 cur_end_ip;	// end ip of current search range
+	guint32 offset_cur_end_ip;	// where is the current end ip saved
+	GIOChannel *io;		// IO Channel to read file
+};				// struct _ip_finder
+
+/*****************************************************************************/
+// convert 1-4 bytes array to integer.
+// Small endian (higher bytes in lower place)
+static guint32 _byte_array_to_int(guint8 * ip, gint count)
+{
+	guint32 ret, i;
+	g_return_val_if_fail(count >= 1 && count <= 4, 0);
+	ret = ip[0];
+	for (i = 0; i < count; i++)
+		ret |= ((guint32) ip[i]) << (8 * i);
+	return ret;
+}				// _byte_array_to_int
+
+/*****************************************************************************/
+// read len of bytes to buf, from io at offset
+static void _read_from(GIOChannel * io, guint32 offset, guint8 * buf, gint len)
+{
+	GError *err;
+	GIOStatus status;
+
+	err = NULL;
+	status = g_io_channel_seek_position(io, offset, G_SEEK_SET, &err);
+	if (err != NULL) {
+		gaim_debug(GAIM_DEBUG_ERROR, "QQ", "Fail seek file @offset[%d]: %s", offset, err->message);
+		g_error_free(err);
+		memset(buf, 0, len);
+		return;
+	}			// if err
+
+	status = g_io_channel_read_chars(io, buf, len, NULL, &err);
+	if (err != NULL) {
+		gaim_debug(GAIM_DEBUG_ERROR, "QQ", "Fail read %d bytes from file: %s", len, err->message);
+		g_error_free(err);
+		memset(buf, 0, len);
+		return;
+	}			// if err
+}				// _read_from
+
+/*****************************************************************************/
+// read len of bytes to buf, from io at offset
+static gsize _read_line_from(GIOChannel * io, guint32 offset, gchar ** ret_str)
+{
+	gsize bytes_read;
+	GError *err;
+	GIOStatus status;
+
+	err = NULL;
+	status = g_io_channel_seek_position(io, offset, G_SEEK_SET, &err);
+	if (err != NULL) {
+		gaim_debug(GAIM_DEBUG_ERROR, "QQ", "Fail seek file @offset[%d]: %s", offset, err->message);
+		g_error_free(err);
+		ret_str = NULL;
+		return -1;
+	}			// if err
+
+	status = g_io_channel_read_line(io, ret_str, &bytes_read, NULL, &err);
+	if (err != NULL) {
+		gaim_debug(GAIM_DEBUG_ERROR, "QQ", "Fail read from file: %s", err->message);
+		g_error_free(err);
+		ret_str = NULL;
+		return -1;
+	}			// if err
+
+	return bytes_read;
+}				// _read_from
+
+/*****************************************************************************/
+// read the string from io, at offset, it may jump several times
+// returns the offset of next readable string for area
+static guint32 _get_string(GIOChannel * io, guint32 offset, gchar ** ret)
+{
+	guint8 *buf;
+	g_return_val_if_fail(io != NULL, 0);
+
+	buf = g_new0(guint8, 3);
+	_read_from(io, offset, buf, 1);
+
+	switch (buf[0]) {	/* fixed by bluestar11 at gmail dot com, 04/12/20 */
+	case 0x01:		/* jump together */
+	  _read_from(io, offset + 1, buf, 3);
+	  return _get_string(io, _byte_array_to_int(buf, 3), ret);
+	case 0x02:		/* jump separately */
+	   _read_from(io, offset + 1, buf, 3);
+	  _get_string(io, _byte_array_to_int(buf, 3), ret);
+	  return offset + 4;
+	default:
+	  _read_line_from(io, offset, ret);
+	  return offset + strlen(*ret) + 1;
+	} /* switch */
+}				// _get_string
+
+/*****************************************************************************/
+// extract country and area starting from offset
+static void _get_country_city(GIOChannel * io, guint32 offset, gchar ** country, gchar ** area)
+{
+	guint32 next_offset;
+	g_return_if_fail(io != NULL);
+
+	next_offset = _get_string(io, offset, country);
+	if (next_offset == 0)
+		*area = g_strdup("");
+	else
+		_get_string(io, next_offset, area);
+}				// _get_country_city
+
+/*****************************************************************************/
+// set start_ip and end_ip of current range
+static void _set_ip_range(gint rec_no, ip_finder * f)
+{
+	guint8 *buf;
+	guint32 offset;
+
+	g_return_if_fail(f != NULL);
+
+	buf = g_newa(guint8, 7);
+	offset = f->offset_first_start_ip + rec_no * 7;
+
+	_read_from(f->io, offset, buf, 7);
+	f->cur_start_ip = _byte_array_to_int(buf, 4);
+	f->offset_cur_end_ip = _byte_array_to_int(buf + 4, 3);
+
+	_read_from(f->io, f->offset_cur_end_ip, buf, 4);
+	f->cur_end_ip = _byte_array_to_int(buf, 4);
+
+}				// _set_ip_range
+
+/*****************************************************************************/
+// set the country and area for given IP.
+// country and area needs to be freed later
+gboolean qq_ip_get_location(guint32 ip, gchar ** country, gchar ** area)
+{
+	gint rec, record_count, B, E;
+	guint8 *buf;
+	gchar *addr_file;
+	ip_finder *f;
+	GError *err;
+	const char *ip_fn;
+
+	if (ip == 0)
+		return FALSE;
+
+	f = g_new0(ip_finder, 1);
+	err = NULL;
+	ip_fn = gaim_prefs_get_string("/plugins/prpl/qq/ipfile");
+	if (ip_fn == NULL || strlen(ip_fn) == 0 || strncmp(ip_fn, "(null)", strlen("(null)")) == 0) {
+		addr_file = g_build_filename(DATADIR, DEFAULT_IP_LOCATION_FILE, NULL);
+	} else {
+		addr_file = g_build_filename(ip_fn, NULL);
+	}
+
+	f->io = g_io_channel_new_file(addr_file, "r", &err);
+	g_free(addr_file);
+	if (err != NULL) {
+		gaim_debug(GAIM_DEBUG_ERROR, "QQ", "Unable to open (%s): %s\n", addr_file, err->message);
+		g_error_free(err);
+		return FALSE;
+	} else
+		g_io_channel_set_encoding(f->io, NULL, NULL);	// set binary
+
+	buf = g_newa(guint8, 4);
+
+	_read_from(f->io, 0, buf, 4);
+	f->offset_first_start_ip = _byte_array_to_int(buf, 4);
+	_read_from(f->io, 4, buf, 4);
+	f->offset_last_start_ip = _byte_array_to_int(buf, 4);
+
+	record_count = (f->offset_last_start_ip - f->offset_first_start_ip) / 7;
+	if (record_count <= 1) {
+		gaim_debug(GAIM_DEBUG_ERROR, "QQ", "File data error, no records found\n");
+		g_io_channel_shutdown(f->io, FALSE, NULL);
+		return FALSE;;
+	}
+	// search for right range
+	B = 0;
+	E = record_count;
+	while (B < E - 1) {
+		rec = (B + E) / 2;
+		_set_ip_range(rec, f);
+		if (ip == f->cur_start_ip) {
+			B = rec;
+			break;
+		}
+		if (ip > f->cur_start_ip)
+			B = rec;
+		else
+			E = rec;
+	}			// while
+	_set_ip_range(B, f);
+
+	if (f->cur_start_ip <= ip && ip <= f->cur_end_ip) {
+		_get_country_city(f->io, f->offset_cur_end_ip + 4, country, area);
+	} else {		// not in this range... miss
+		*country = g_strdup("unkown");
+		*area = g_strdup(" ");
+	}			// if ip_start<=ip<=ip_end
+
+	g_io_channel_shutdown(f->io, FALSE, NULL);
+	return TRUE;
+
+}				// qq_ip_get_location
+
+/*****************************************************************************/
+// END OF FILE