libpurple/protocols/facebook/util.c

Thu, 11 Jun 2015 01:33:37 -0400

author
James Geboski <jgeboski@gmail.com>
date
Thu, 11 Jun 2015 01:33:37 -0400
branch
facebook
changeset 37258
291b6e1acc24
parent 37250
3f5570a17b15
child 37260
473934b0c408
permissions
-rw-r--r--

facebook: implemented group chat support

This is a fairly comprehensive implementation of the group chats which
are provided by the Messenger protocol. However, this does leave some
features out, such as removing users from a chat, as well as removing
oneself from a chat. This also does not support the inviting of users
which are not in the friend list of the user.

/* purple
 *
 * Purple is the legal property of its developers, whose names are too numerous
 * to list here.  Please refer to the COPYRIGHT file distributed with this
 * source distribution.
 *
 * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 */

#include "internal.h"

#include <string.h>
#include <zlib.h>

#include "util.h"

gchar *
fb_util_randstr(gsize size)
{
	gchar *ret;
	GRand *rand;
	guint i;
	guint j;

	static const gchar chars[] =
		"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
		"abcdefghijklmnopqrstuvwxyz"
		"0123456789";
	static const gsize charc = G_N_ELEMENTS(chars) - 1;

	if (G_UNLIKELY(size < 1)) {
		return NULL;
	}

	rand = g_rand_new();
	ret = g_new(gchar, size + 1);

	for (i = 0; i < size; i++) {
		j = g_rand_int_range(rand, 0, charc);
		ret[i] = chars[j];
	}

	ret[size] = 0;
	g_rand_free(rand);
	return ret;
}

static void
fb_util_request_buddy_ok(gpointer *mata, PurpleRequestFields *fields)
{
	FbUtilRequestBuddyFunc func = mata[0];
	GList *l;
	GList *select;
	gpointer data = mata[2];
	GSList *ret = NULL;
	PurpleBuddy *bdy;
	PurpleRequestField *field;

	if (func == NULL) {
		g_free(mata);
		return;
	}

	field = purple_request_fields_get_field(fields, "buddy");
	select = purple_request_field_list_get_selected(field);

	for (l = select; l != NULL; l = l->next) {
		bdy = purple_request_field_list_get_data(field, l->data);
		ret = g_slist_prepend(ret, bdy);
	}

	ret = g_slist_reverse(ret);
	func(ret, data);

	g_slist_free(ret);
	g_free(mata);
}

static void
fb_util_request_buddy_cancel(gpointer *mata, PurpleRequestFields *fields)
{
	FbUtilRequestBuddyFunc func = mata[1];
	gpointer data = mata[2];

	if (func != NULL) {
		func(NULL, data);
	}

	g_free(mata);
}

gpointer
fb_util_request_buddy(PurpleConnection *gc, const gchar *title,
                      const gchar *primary, const gchar *secondary,
                      GSList *select, gboolean multi, GCallback ok_cb,
		      GCallback cancel_cb, gpointer data)
{
	const gchar *alias;
	const gchar *name;
	gchar *str;
	GList *items = NULL;
	gpointer *mata;
	GSList *buddies;
	GSList *l;
	PurpleAccount *acct;
	PurpleRequestCommonParameters *cpar;
	PurpleRequestField *field;
	PurpleRequestFieldGroup *group;
	PurpleRequestFields *fields;

	mata = g_new0(gpointer, 3);
	mata[0] = ok_cb;
	mata[1] = cancel_cb;
	mata[2] = data;

	acct = purple_connection_get_account(gc);
	buddies = purple_blist_find_buddies(acct, NULL);
	buddies = g_slist_sort(buddies, (GCompareFunc) g_ascii_strcasecmp);

	fields = purple_request_fields_new();
	group = purple_request_field_group_new(NULL);
	purple_request_fields_add_group(fields, group);

	field = purple_request_field_list_new("buddy", NULL);
	purple_request_field_list_set_multi_select(field, multi);
	purple_request_field_set_required(field, TRUE);
	purple_request_field_group_add_field(group, field);

	for (l = buddies; l != NULL; l = l->next) {
		name = purple_buddy_get_name(l->data);
		alias = purple_buddy_get_alias(l->data);
		str = g_strdup_printf("%s (%s)", alias, name);
		purple_request_field_list_add_icon(field, str, NULL, l->data);
		g_free(str);
	}

	for (l = select; l != NULL; l = l->next) {
		name = purple_buddy_get_name(l->data);
		alias = purple_buddy_get_alias(l->data);
		str = g_strdup_printf("%s (%s)", alias, name);
		items = g_list_append(items, str);
	}

	purple_request_field_list_set_selected(field, items);
	g_slist_free(buddies);
	g_list_free_full(items, g_free);

	cpar = purple_request_cpar_from_connection(gc);
	return purple_request_fields(gc, title, primary, secondary, fields,
	                             _("Ok"),
	                             G_CALLBACK(fb_util_request_buddy_ok),
				     _("Cancel"),
	                             G_CALLBACK(fb_util_request_buddy_cancel),
				     cpar, mata);
}

gboolean
fb_util_str_is(const gchar *str, GAsciiType type)
{
	gsize i;
	gsize size;
	guchar c;

	g_return_val_if_fail(str != NULL, FALSE);
	size = strlen(str);

	for (i = 0; i < size; i++) {
		c = (guchar) str[i];

		if ((g_ascii_table[c] & type) == 0) {
			return FALSE;
		}
	}

	return TRUE;
}

static voidpf
fb_util_zalloc(voidpf opaque, uInt items, uInt size)
{
	return g_malloc(size * items);
}

static void
fb_util_zfree(voidpf opaque, voidpf address)
{
	g_free(address);
}

gboolean
fb_util_zcompressed(const GByteArray *bytes)
{
	guint8 b0;
	guint8 b1;

	g_return_val_if_fail(bytes != NULL, FALSE);

	if (bytes->len < 2) {
		return FALSE;
	}

	b0 = *(bytes->data + 0);
	b1 = *(bytes->data + 1);

	return ((((b0 << 8) | b1) % 31) == 0) && /* Check the header */
	       ((b0 & 0x0F) == Z_DEFLATED);      /* Check the method */
}

GByteArray *
fb_util_zcompress(const GByteArray *bytes)
{
	GByteArray *ret;
	gint res;
	gsize size;
	z_stream zs;

	g_return_val_if_fail(bytes != NULL, NULL);

	memset(&zs, 0, sizeof zs);
	zs.zalloc = fb_util_zalloc;
	zs.zfree = fb_util_zfree;
	zs.next_in = bytes->data;
	zs.avail_in = bytes->len;

	if (deflateInit(&zs, Z_BEST_COMPRESSION) != Z_OK) {
		return NULL;
	}

	size = compressBound(bytes->len);
	ret = g_byte_array_new();

	g_byte_array_set_size(ret, size);

	zs.next_out = ret->data;
	zs.avail_out = size;

	res = deflate(&zs, Z_FINISH);

	if (res != Z_STREAM_END) {
		deflateEnd(&zs);
		g_byte_array_free(ret, TRUE);
		return NULL;
	}

	size -= zs.avail_out;
	g_byte_array_remove_range(ret, size, ret->len - size);

	deflateEnd(&zs);
	return ret;
}

GByteArray *
fb_util_zuncompress(const GByteArray *bytes)
{
	GByteArray *ret;
	gint res;
	guint8 out[1024];
	z_stream zs;

	g_return_val_if_fail(bytes != NULL, NULL);

	memset(&zs, 0, sizeof zs);
	zs.zalloc = fb_util_zalloc;
	zs.zfree = fb_util_zfree;
	zs.next_in = bytes->data;
	zs.avail_in = bytes->len;

	if (inflateInit(&zs) != Z_OK) {
		return NULL;
	}

	ret = g_byte_array_new();

	do {
		zs.next_out = out;
		zs.avail_out = sizeof out;

		res = inflate(&zs, Z_NO_FLUSH);

		if ((res != Z_OK) && (res != Z_STREAM_END)) {
			inflateEnd(&zs);
			g_byte_array_free(ret, TRUE);
			return NULL;
		}

		g_byte_array_append(ret, out, sizeof out - zs.avail_out);
	} while (res != Z_STREAM_END);

	inflateEnd(&zs);
	return ret;
}

mercurial