libpurple/protocols/gg/pubdir-prpl.c

Sat, 21 Sep 2013 17:01:08 +0530

author
Ankit Vani <a@nevitus.org>
date
Sat, 21 Sep 2013 17:01:08 +0530
branch
soc.2013.gobjectification.plugins
changeset 36782
64936dae41a3
parent 36701
5acd461707ae
parent 34955
854143116575
child 36848
7fafdda14844
permissions
-rw-r--r--

Merged soc.2013.gobjectification branch

/* 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.
 *
 * Rewritten from scratch during Google Summer of Code 2012
 * by Tomek Wasilczyk (http://www.wasilczyk.pl).
 *
 * Previously implemented by:
 *  - Arkadiusz Miskiewicz <misiek@pld.org.pl> - first implementation (2001);
 *  - Bartosz Oler <bartosz@bzimage.us> - reimplemented during GSoC 2005;
 *  - Krzysztof Klinikowski <grommasher@gmail.com> - some parts (2009-2011).
 *
 * 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 "pubdir-prpl.h"

#include <debug.h>
#include <http.h>
#include <request.h>

#include "oauth/oauth-purple.h"
#include "xml.h"
#include "utils.h"
#include "status.h"

typedef struct
{
	PurpleConnection *gc;
	ggp_pubdir_request_cb cb;
	void *user_data;
	enum
	{
		GGP_PUBDIR_REQUEST_TYPE_INFO,
		GGP_PUBDIR_REQUEST_TYPE_SEARCH,
	} type;
	union
	{
		struct
		{
			uin_t uin;
		} user_info;
		ggp_pubdir_search_form *search_form;
	} params;
} ggp_pubdir_request;

void ggp_pubdir_request_free(ggp_pubdir_request *request);
void ggp_pubdir_record_free(ggp_pubdir_record *records, int count);

static void ggp_pubdir_get_info_got_token(PurpleConnection *gc,
	const gchar *token, gpointer _request);
static void ggp_pubdir_got_data(PurpleHttpConnection *http_conn,
	PurpleHttpResponse *response, gpointer user_data);

static void ggp_pubdir_get_info_protocol_got(PurpleConnection *gc,
	int records_count, const ggp_pubdir_record *records, int next_offset,
	void *_uin_p);

static void ggp_pubdir_request_buddy_alias_got(PurpleConnection *gc,
	int records_count, const ggp_pubdir_record *records, int next_offset,
	void *user_data);

// Searching for buddies.

#define GGP_PUBDIR_SEARCH_TITLE _("Gadu-Gadu Public Directory")
#define GGP_PUBDIR_SEARCH_PER_PAGE 20

struct _ggp_pubdir_search_form
{
	gchar *nick, *city;
	ggp_pubdir_gender gender;
	int offset;
	int limit;

	void *display_handle;
};

void ggp_pubdir_search_form_free(ggp_pubdir_search_form *form);
ggp_pubdir_search_form * ggp_pubdir_search_form_clone(
	const ggp_pubdir_search_form *form);

static void ggp_pubdir_search_request(PurpleConnection *gc,
	PurpleRequestFields *fields);
static gchar * ggp_pubdir_search_make_query(const ggp_pubdir_search_form *form);
static void ggp_pubdir_search_execute(PurpleConnection *gc,
	const ggp_pubdir_search_form *form,
	ggp_pubdir_request_cb cb, void *user_data);
static void ggp_pubdir_search_got_token(PurpleConnection *gc,
	const gchar *token, gpointer _request);
static void ggp_pubdir_search_results_display(PurpleConnection *gc,
	int records_count, const ggp_pubdir_record *records, int next_offset,
	void *user_data);
static void ggp_pubdir_search_results_close(gpointer _form);
static void ggp_pubdir_search_results_next(PurpleConnection *gc, GList *row,
	gpointer _form);
static void ggp_pubdir_search_results_add(PurpleConnection *gc, GList *row,
	gpointer _form);
static void ggp_pubdir_search_results_im(PurpleConnection *gc, GList *row,
	gpointer _form);
static void ggp_pubdir_search_results_info(PurpleConnection *gc, GList *row,
	gpointer _form);
static void ggp_pubdir_search_results_new(PurpleConnection *gc, GList *row,
	gpointer _form);

// Own profile.

static void ggp_pubdir_set_info_dialog(PurpleConnection *gc, int records_count,
	const ggp_pubdir_record *records, int next_offset, void *user_data);
static void ggp_pubdir_set_info_request(PurpleConnection *gc,
	PurpleRequestFields *fields);
static void ggp_pubdir_set_info_got_token(PurpleConnection *gc,
	const gchar *token, gpointer _record);

static void ggp_pubdir_set_info_got_response(PurpleHttpConnection *http_conn,
	PurpleHttpResponse *response, gpointer user_data);

/******************************************************************************/

static const gchar *ggp_pubdir_provinces[] =
{
	N_("Not specified"),
	"dolnośląskie",
	"kujawsko-pomorskie",
	"lubelskie",
	"lubuskie",
	"łódzkie",
	"małopolskie",
	"mazowieckie",
	"opolskie",
	"podkarpackie",
	"podlaskie",
	"pomorskie",
	"śląskie",
	"świętokrzyskie",
	"warmińsko-mazurskie",
	"wielkopolskie",
	"zachodniopomorskie",
};

static gsize ggp_pubdir_provinces_count = sizeof(ggp_pubdir_provinces)/sizeof(gchar*);

/******************************************************************************/

void ggp_pubdir_record_free(ggp_pubdir_record *records, int count)
{
	int i;
	for (i = 0; i < count; i++)
	{
		g_free(records[i].label);
		g_free(records[i].nickname);
		g_free(records[i].first_name);
		g_free(records[i].last_name);
		g_free(records[i].city);
	}
	g_free(records);
}

void ggp_pubdir_request_free(ggp_pubdir_request *request)
{
	if (request->type == GGP_PUBDIR_REQUEST_TYPE_SEARCH)
		ggp_pubdir_search_form_free(request->params.search_form);
	g_free(request);
}

void ggp_pubdir_get_info(PurpleConnection *gc, uin_t uin,
	ggp_pubdir_request_cb cb, void *user_data)
{
	ggp_pubdir_request *request = g_new0(ggp_pubdir_request, 1);
	gchar *url;

	request->type = GGP_PUBDIR_REQUEST_TYPE_INFO;
	request->gc = gc;
	request->cb = cb;
	request->user_data = user_data;
	request->params.user_info.uin = uin;

	url = g_strdup_printf("http://api.gadu-gadu.pl/users/%u", uin);
	ggp_oauth_request(gc, ggp_pubdir_get_info_got_token, request,
		"GET", url);
	g_free(url);
}

static void ggp_pubdir_get_info_got_token(PurpleConnection *gc,
	const gchar *token, gpointer _request)
{
	PurpleHttpRequest *req;
	ggp_pubdir_request *request = _request;

	if (!token || !PURPLE_CONNECTION_IS_VALID(gc))
	{
		request->cb(gc, -1, NULL, 0, request->user_data);
		ggp_pubdir_request_free(request);
		return;
	}

	req = purple_http_request_new(NULL);
	purple_http_request_set_url_printf(req,
		"http://api.gadu-gadu.pl/users/%u",
		request->params.user_info.uin);
	purple_http_request_header_set(req, "Authorization", token);
	purple_http_request(gc, req, ggp_pubdir_got_data, request);
	purple_http_request_unref(req);
}

static void ggp_pubdir_got_data(PurpleHttpConnection *http_conn,
	PurpleHttpResponse *response, gpointer _request)
{
	ggp_pubdir_request *request = _request;
	PurpleConnection *gc = request->gc;
	gboolean succ = TRUE;
	PurpleXmlNode *xml;
	const gchar *xml_raw;
	unsigned int status, next_offset;
	int record_count, i;
	ggp_pubdir_record *records;

	xml_raw = purple_http_response_get_data(response, NULL);

	if (purple_debug_is_verbose() && purple_debug_is_unsafe()) {
		purple_debug_misc("gg", "ggp_pubdir_got_data: xml=[%s]\n",
			xml_raw);
	}

	xml = purple_xmlnode_from_str(xml_raw, -1);
	if (xml == NULL)
	{
		purple_debug_error("gg", "ggp_pubdir_got_data: "
			"invalid xml\n");
		request->cb(gc, -1, NULL, 0, request->user_data);
		ggp_pubdir_request_free(request);
		return;
	}
	
	succ &= ggp_xml_get_uint(xml, "status", &status);
	if (!ggp_xml_get_uint(xml, "nextOffset", &next_offset))
		next_offset = 0;
	xml = purple_xmlnode_get_child(xml, "users");
	if (!succ || status != 0 || !xml)
	{
		purple_debug_error("gg", "ggp_pubdir_got_data: "
			"invalid reply\n");
		request->cb(gc, -1, NULL, 0, request->user_data);
		ggp_pubdir_request_free(request);
		return;
	}
	
	record_count = ggp_xml_child_count(xml, "user");
	records = g_new0(ggp_pubdir_record, record_count);
	
	xml = purple_xmlnode_get_child(xml, "user");
	i = 0;
	while (xml)
	{
		ggp_pubdir_record *record = &records[i++];
		gchar *city = NULL, *birth_s = NULL;
		unsigned int gender = 0;
		const gchar *uin_s;
		
		g_assert(i <= record_count);
		
		record->uin = ggp_str_to_uin(purple_xmlnode_get_attrib(xml, "uin"));
		if (record->uin == 0)
			ggp_xml_get_uint(xml, "uin", &record->uin);
		if (record->uin == 0)
			purple_debug_error("gg", "ggp_pubdir_got_data:"
				" invalid uin\n");
		uin_s = ggp_uin_to_str(record->uin);
		
		ggp_xml_get_string(xml, "label", &record->label);
		ggp_xml_get_string(xml, "nick", &record->nickname);
		ggp_xml_get_string(xml, "name", &record->first_name);
		ggp_xml_get_string(xml, "surname", &record->last_name);
		ggp_xml_get_string(xml, "city", &city);
		ggp_xml_get_string(xml, "birth", &birth_s);
		ggp_xml_get_uint(xml, "gender", &gender);
		ggp_xml_get_uint(xml, "age", &record->age);
		ggp_xml_get_uint(xml, "province", &record->province);
		
		record->label = ggp_free_if_equal(record->label, uin_s);
		record->label = ggp_free_if_equal(record->label, "");
		record->nickname = ggp_free_if_equal(record->nickname, uin_s);
		record->nickname = ggp_free_if_equal(record->nickname, "");
		record->first_name = ggp_free_if_equal(record->first_name, "");
		record->last_name = ggp_free_if_equal(record->last_name, "");
		
		if (record->label) {}
		else if (record->nickname)
			record->label = g_strdup(record->nickname);
		else if (record->first_name && record->last_name)
			record->label = g_strdup_printf("%s %s",
				record->first_name, record->last_name);
		else if (record->first_name)
			record->label = g_strdup(record->first_name);
		else if (record->last_name)
			record->label = g_strdup(record->last_name);
		if (record->label)
			g_strstrip(record->label);
		if (record->nickname)
			g_strstrip(record->nickname);
		
		if (gender == 1)
			record->gender = GGP_PUBDIR_GENDER_FEMALE;
		else if (gender == 2)
			record->gender = GGP_PUBDIR_GENDER_MALE;
		else
			record->gender = GGP_PUBDIR_GENDER_UNSPECIFIED;
		
		if (city && city[0] != '\0')
			record->city = g_strdup(city);
		if (record->city)
			g_strstrip(record->city);
		if (!record->city)
		{
			g_free(record->city);
			record->city = NULL;
		}
		
		record->birth = ggp_date_from_iso8601(birth_s);
		//TODO: calculate age from birth
		
		//TODO: verbose
		purple_debug_misc("gg", "ggp_pubdir_got_data: [uin:%d] "
			"[label:%s] [nick:%s] [first name:%s] [last name:%s] "
			"[city:%s] [gender:%d] [age:%d] [birth:%lu]\n",
			record->uin, record->label, record->nickname,
			record->first_name, record->last_name, record->city,
			record->gender, record->age, record->birth);
		
		g_free(city);
		
		xml = purple_xmlnode_get_next_twin(xml);
	}
	
	request->cb(gc, record_count, records, next_offset, request->user_data);
	
	ggp_pubdir_request_free(request);
	ggp_pubdir_record_free(records, record_count);
}

void ggp_pubdir_get_info_protocol(PurpleConnection *gc, const char *name)
{
	uin_t uin = ggp_str_to_uin(name);
	uin_t *uin_p = g_new0(uin_t, 1);

	*uin_p = uin;

	purple_debug_info("gg", "ggp_pubdir_get_info_protocol: %u\n", uin);

	ggp_pubdir_get_info(gc, uin, ggp_pubdir_get_info_protocol_got, uin_p);
}

static void ggp_pubdir_get_info_protocol_got(PurpleConnection *gc,
	int records_count, const ggp_pubdir_record *records, int next_offset,
	void *_uin_p)
{
	uin_t uin = *((uin_t*)_uin_p);
	PurpleNotifyUserInfo *info = purple_notify_user_info_new();
	const ggp_pubdir_record *record = &records[0];
	PurpleBuddy *buddy;
	
	g_free(_uin_p);
	
	if (records_count < 1)
	{
		purple_debug_error("gg", "ggp_pubdir_get_info_protocol_got: "
			"couldn't get info for %u\n", uin);
		purple_notify_user_info_add_pair_plaintext(info, NULL,
			_("Cannot get user information"));
		purple_notify_userinfo(gc, ggp_uin_to_str(uin), info,
			NULL, NULL);
		purple_notify_user_info_destroy(info);
		return;
	}
	
	purple_debug_info("gg", "ggp_pubdir_get_info_protocol_got: %u\n", uin);
	g_assert(uin == record->uin);
	g_assert(records_count == 1);
	
	buddy = purple_blist_find_buddy(purple_connection_get_account(gc),
		ggp_uin_to_str(uin));
	if (buddy)
	{
		const char *alias;
		PurpleStatus *status;
		gchar *status_message;
		
		alias = purple_buddy_get_alias_only(buddy);
		if (alias)
			purple_notify_user_info_add_pair_plaintext(info,
				_("Alias"), alias);
		
		status = purple_presence_get_active_status(
			purple_buddy_get_presence(buddy));
		ggp_status_from_purplestatus(status, &status_message);
		purple_notify_user_info_add_pair_plaintext(info, _("Status"),
			purple_status_get_name(status));
		if (status_message)
			purple_notify_user_info_add_pair_plaintext(info,
				_("Message"), status_message);
	}
	
	if (record->nickname)
		purple_notify_user_info_add_pair_plaintext(info,
			_("Nickname"), record->nickname);
	if (record->first_name)
		purple_notify_user_info_add_pair_plaintext(info,
			_("First name"), record->first_name);
	if (record->last_name)
		purple_notify_user_info_add_pair_plaintext(info,
			_("Last name"), record->last_name);
	if (record->gender != GGP_PUBDIR_GENDER_UNSPECIFIED)
		purple_notify_user_info_add_pair_plaintext(info, _("Gender"),
			record->gender == GGP_PUBDIR_GENDER_FEMALE ?
			_("Female") : _("Male"));
	if (record->city)
		purple_notify_user_info_add_pair_plaintext(info, _("City"),
			record->city);
	if (record->birth)
		purple_notify_user_info_add_pair_plaintext(info, _("Birthday"),
			ggp_date_strftime("%Y-%m-%d", record->birth));
	else if (record->age)
	{
		gchar *age_s = g_strdup_printf("%d", record->age);
		purple_notify_user_info_add_pair_plaintext(info, _("Age"),
			age_s);
		g_free(age_s);
	}
	
	purple_notify_userinfo(gc, ggp_uin_to_str(uin), info, NULL, NULL);
	purple_notify_user_info_destroy(info);
}

void ggp_pubdir_request_buddy_alias(PurpleConnection *gc, PurpleBuddy *buddy)
{
	uin_t uin = ggp_str_to_uin(purple_buddy_get_name(buddy));

	purple_debug_info("gg", "ggp_pubdir_request_buddy_alias: %u\n", uin);

	ggp_pubdir_get_info(gc, uin, ggp_pubdir_request_buddy_alias_got, NULL);
}

static void ggp_pubdir_request_buddy_alias_got(PurpleConnection *gc,
	int records_count, const ggp_pubdir_record *records, int next_offset,
	void *user_data)
{
	uin_t uin;
	const gchar *alias;
	
	if (records_count < 0)
	{
		purple_debug_error("gg", "ggp_pubdir_request_buddy_alias_got: "
			"couldn't get info for user\n");
		return;
	}
	uin = records[0].uin;
	
	alias = records[0].label;
	if (!alias)
	{
		purple_debug_info("gg", "ggp_pubdir_request_buddy_alias_got: "
			"public alias for %u is not available\n", uin);
		return;
	}

	purple_debug_info("gg", "ggp_pubdir_request_buddy_alias_got: "
		"public alias for %u is \"%s\"\n", uin, alias);
	
	serv_got_alias(gc, ggp_uin_to_str(uin), alias);
}

/*******************************************************************************
 * Searching for buddies.
 ******************************************************************************/

void ggp_pubdir_search_form_free(ggp_pubdir_search_form *form)
{
	g_free(form->nick);
	g_free(form->city);
	g_free(form);
}

ggp_pubdir_search_form * ggp_pubdir_search_form_clone(
	const ggp_pubdir_search_form *form)
{
	ggp_pubdir_search_form *dup = g_new(ggp_pubdir_search_form, 1);
	
	dup->nick = g_strdup(form->nick);
	dup->city = g_strdup(form->city);
	dup->gender = form->gender;
	dup->offset = form->offset;
	dup->limit = form->limit;

	dup->display_handle = form->display_handle;
	
	return dup;
}

void ggp_pubdir_search(PurpleConnection *gc,
	const ggp_pubdir_search_form *form)
{
	PurpleRequestFields *fields;
	PurpleRequestFieldGroup *group;
	PurpleRequestField *field;

	purple_debug_info("gg", "ggp_pubdir_search\n");
	
	fields = purple_request_fields_new();
	group = purple_request_field_group_new(NULL);
	purple_request_fields_add_group(fields, group);
	
	field = purple_request_field_string_new("name", _("Name"),
		form ? form->nick : NULL, FALSE);
	purple_request_field_group_add_field(group, field);
	
	field = purple_request_field_string_new("city", _("City"),
		form ? form->city : NULL, FALSE);
	purple_request_field_group_add_field(group, field);

	field = purple_request_field_choice_new("gender", _("Gender"),
		GINT_TO_POINTER(form->gender));
	purple_request_field_choice_add(field, _("Male or female"), 0);
	purple_request_field_choice_add(field, _("Male"),
		GINT_TO_POINTER(GGP_PUBDIR_GENDER_MALE));
	purple_request_field_choice_add(field, _("Female"),
		GINT_TO_POINTER(GGP_PUBDIR_GENDER_FEMALE));
	purple_request_field_group_add_field(group, field);
	
	purple_request_fields(gc, _("Find buddies"), _("Find buddies"),
		_("Please, enter your search criteria below"), fields,
		_("OK"), G_CALLBACK(ggp_pubdir_search_request),
		_("Cancel"), NULL,
		purple_request_cpar_from_connection(gc), gc);
}

static void ggp_pubdir_search_request(PurpleConnection *gc,
	PurpleRequestFields *fields)
{
	ggp_pubdir_search_form *form = g_new0(ggp_pubdir_search_form, 1);

	purple_debug_info("gg", "ggp_pubdir_search_request\n");
	
	form->nick = g_strdup(purple_request_fields_get_string(fields, "name"));
	form->city = g_strdup(purple_request_fields_get_string(fields, "city"));
	form->gender = GPOINTER_TO_INT(purple_request_fields_get_choice(fields,
		"gender"));
	form->offset = 0;
	form->limit = GGP_PUBDIR_SEARCH_PER_PAGE;
	
	ggp_pubdir_search_execute(gc, form, ggp_pubdir_search_results_display,
		form);
}

static gchar * ggp_pubdir_search_make_query(const ggp_pubdir_search_form *form)
{
	gchar *nick, *city, *gender;
	gchar *query;
	
	if (form->nick && form->nick[0] != '\0')
	{
		gchar *nick_e = g_uri_escape_string(form->nick, NULL, FALSE);
		nick = g_strdup_printf("&nick=%s", nick_e);
		g_free(nick_e);
	}
	else
		nick = g_strdup("");
	
	if (form->city && form->city[0] != '\0')
	{
		gchar *city_e = g_uri_escape_string(form->city, NULL, FALSE);
		city = g_strdup_printf("&city=%s", city_e);
		g_free(city_e);
	}
	else
		city = g_strdup("");
	
	if (form->gender != GGP_PUBDIR_GENDER_UNSPECIFIED)
		gender = g_strdup_printf("&gender=%d",
			form->gender == GGP_PUBDIR_GENDER_MALE ? 2 : 1);
	else
		gender = g_strdup("");
	
	query = g_strdup_printf("/users.xml?offset=%d&limit=%d%s%s%s",
		form->offset, form->limit, nick, city, gender);
	
	g_free(nick);
	g_free(city);
	g_free(gender);
	
	return query;
}

static void ggp_pubdir_search_execute(PurpleConnection *gc,
	const ggp_pubdir_search_form *form,
	ggp_pubdir_request_cb cb, void *user_data)
{
	ggp_pubdir_request *request = g_new0(ggp_pubdir_request, 1);
	gchar *url;
	ggp_pubdir_search_form *local_form = ggp_pubdir_search_form_clone(form);
	gchar *query;

	request->type = GGP_PUBDIR_REQUEST_TYPE_SEARCH;
	request->gc = gc;
	request->cb = cb;
	request->user_data = user_data;
	request->params.search_form = local_form;

	query = ggp_pubdir_search_make_query(form);
	purple_debug_misc("gg", "ggp_pubdir_search_execute: %s\n", query);
	url = g_strdup_printf("http://api.gadu-gadu.pl%s", query);
	ggp_oauth_request(gc, ggp_pubdir_search_got_token, request,
		"GET", url);
	g_free(query);
	g_free(url);
}

static void ggp_pubdir_search_got_token(PurpleConnection *gc,
	const gchar *token, gpointer _request)
{
	PurpleHttpRequest *req;
	ggp_pubdir_request *request = _request;
	gchar *query;

	if (!token || !PURPLE_CONNECTION_IS_VALID(gc))
	{
		request->cb(gc, -1, NULL, 0, request->user_data);
		ggp_pubdir_request_free(request);
		return;
	}

	purple_debug_misc("gg", "ggp_pubdir_search_got_token\n");

	query = ggp_pubdir_search_make_query(request->params.search_form);

	req = purple_http_request_new(NULL);
	purple_http_request_set_url_printf(req, "http://api.gadu-gadu.pl%s", query);
	purple_http_request_header_set(req, "Authorization", token);
	purple_http_request(gc, req, ggp_pubdir_got_data, request);
	purple_http_request_unref(req);

	g_free(query);
}


static void ggp_pubdir_search_results_display(PurpleConnection *gc,
	int records_count, const ggp_pubdir_record *records, int next_offset,
	void *_form)
{
	ggp_pubdir_search_form *form = _form;
	PurpleNotifySearchResults *results;
	int i;

	purple_debug_info("gg", "ggp_pubdir_search_results_display: "
		"got %d records (next offset: %d)\n",
		records_count, next_offset);

	if (records_count < 0 ||
		(records_count == 0 && form->offset != 0))
	{
		purple_notify_error(gc, GGP_PUBDIR_SEARCH_TITLE,
			_("Error while searching for buddies"), NULL,
			purple_request_cpar_from_connection(gc));
		ggp_pubdir_search_form_free(form);
		return;
	}
	
	if (records_count == 0)
	{
		purple_notify_info(gc, GGP_PUBDIR_SEARCH_TITLE,
			_("No matching users found"),
			_("There are no users matching your search criteria."),
			purple_request_cpar_from_connection(gc));
		ggp_pubdir_search_form_free(form);
		return;
	}
	
	form->offset = next_offset;
	
	results = purple_notify_searchresults_new();
	
	purple_notify_searchresults_column_add(results,
		purple_notify_searchresults_column_new(_("GG Number")));
	purple_notify_searchresults_column_add(results,
		purple_notify_searchresults_column_new(_("Name")));
	purple_notify_searchresults_column_add(results,
		purple_notify_searchresults_column_new(_("City")));
	purple_notify_searchresults_column_add(results,
		purple_notify_searchresults_column_new(_("Gender")));
	purple_notify_searchresults_column_add(results,
		purple_notify_searchresults_column_new(_("Age")));
	
	for (i = 0; i < records_count; i++)
	{
		GList *row = NULL;
		const ggp_pubdir_record *record = &records[i];
		gchar *gender = NULL, *age = NULL;

		if (record->gender == GGP_PUBDIR_GENDER_MALE)
			gender = g_strdup("male");
		else if (record->gender == GGP_PUBDIR_GENDER_FEMALE)
			gender = g_strdup("female");
		
		if (record->age)
			age = g_strdup_printf("%d", record->age);
		
		row = g_list_append(row, g_strdup(ggp_uin_to_str(record->uin)));
		row = g_list_append(row, g_strdup(record->label));
		row = g_list_append(row, g_strdup(record->city));
		row = g_list_append(row, gender);
		row = g_list_append(row, age);
		purple_notify_searchresults_row_add(results, row);
	}
	
	purple_notify_searchresults_button_add(results,
		PURPLE_NOTIFY_BUTTON_ADD, ggp_pubdir_search_results_add);
	purple_notify_searchresults_button_add(results,
		PURPLE_NOTIFY_BUTTON_IM, ggp_pubdir_search_results_im);
	purple_notify_searchresults_button_add(results,
		PURPLE_NOTIFY_BUTTON_INFO, ggp_pubdir_search_results_info);
	purple_notify_searchresults_button_add_labeled(results, _("New search"),
		ggp_pubdir_search_results_new);
	if (next_offset != 0)
		purple_notify_searchresults_button_add(results,
			PURPLE_NOTIFY_BUTTON_CONTINUE,
			ggp_pubdir_search_results_next);
	
	if (!form->display_handle)
		form->display_handle = purple_notify_searchresults(gc,
			GGP_PUBDIR_SEARCH_TITLE, _("Search results"), NULL,
			results, ggp_pubdir_search_results_close, form);
	else
		purple_notify_searchresults_new_rows(gc, results,
			form->display_handle);
	g_assert(form->display_handle);
}

static void ggp_pubdir_search_results_close(gpointer _form)
{
	ggp_pubdir_search_form *form = _form;
	ggp_pubdir_search_form_free(form);
}

static void ggp_pubdir_search_results_next(PurpleConnection *gc, GList *row,
	gpointer _form)
{
	ggp_pubdir_search_form *form = _form;
	ggp_pubdir_search_execute(gc, form, ggp_pubdir_search_results_display,
		form);
}

static void ggp_pubdir_search_results_add(PurpleConnection *gc, GList *row,
	gpointer _form)
{
	purple_blist_request_add_buddy(purple_connection_get_account(gc),
		g_list_nth_data(row, 0), NULL, g_list_nth_data(row, 1));
}

static void ggp_pubdir_search_results_im(PurpleConnection *gc, GList *row,
	gpointer _form)
{
	purple_conversation_present(PURPLE_CONVERSATION(purple_im_conversation_new(
		purple_connection_get_account(gc), g_list_nth_data(row, 0))));
}

static void ggp_pubdir_search_results_info(PurpleConnection *gc, GList *row,
	gpointer _form)
{
	ggp_pubdir_get_info_protocol(gc, g_list_nth_data(row, 0));
}

static void ggp_pubdir_search_results_new(PurpleConnection *gc, GList *row,
	gpointer _form)
{
	ggp_pubdir_search_form *form = _form;
	ggp_pubdir_search(gc, form);
}

/*******************************************************************************
 * Own profile.
 ******************************************************************************/

void ggp_pubdir_set_info(PurpleConnection *gc)
{
	ggp_pubdir_get_info(gc, ggp_str_to_uin(purple_account_get_username(
		purple_connection_get_account(gc))),
		ggp_pubdir_set_info_dialog, NULL);
}

static void ggp_pubdir_set_info_dialog(PurpleConnection *gc, int records_count,
	const ggp_pubdir_record *records, int next_offset, void *user_data)
{
	PurpleRequestFields *fields;
	PurpleRequestFieldGroup *group;
	PurpleRequestField *field;
	gsize i;
	const ggp_pubdir_record *record;
	
	purple_debug_info("gg", "ggp_pubdir_set_info_dialog (record: %d)\n",
		records_count);
	
	record = (records_count == 1 ? &records[0] : NULL);
	
	fields = purple_request_fields_new();
	group = purple_request_field_group_new(NULL);
	purple_request_fields_add_group(fields, group);
	
	field = purple_request_field_string_new("first_name", _("First name"),
		record ? record->first_name : NULL, FALSE);
	purple_request_field_group_add_field(group, field);

	field = purple_request_field_string_new("last_name", _("Last name"),
		record ? record->last_name : NULL, FALSE);
	purple_request_field_group_add_field(group, field);

	field = purple_request_field_choice_new("gender", _("Gender"),
		GINT_TO_POINTER(record->gender));
	purple_request_field_set_required(field, TRUE);
	purple_request_field_choice_add(field, _("Male"),
		GINT_TO_POINTER(GGP_PUBDIR_GENDER_MALE));
	purple_request_field_choice_add(field, _("Female"),
		GINT_TO_POINTER(GGP_PUBDIR_GENDER_FEMALE));
	purple_request_field_group_add_field(group, field);

	field = purple_request_field_string_new("birth_date", _("Birth Day"),
		(record && record->birth) ?
		ggp_date_strftime("%Y-%m-%d", record->birth) : NULL, FALSE);
	purple_request_field_set_required(field, TRUE);
	purple_request_field_group_add_field(group, field);

	field = purple_request_field_string_new("city", _("City"),
		record ? record->city : NULL, FALSE);
	purple_request_field_group_add_field(group, field);
	
	field = purple_request_field_choice_new("province", _("Voivodeship"), 0);
	purple_request_field_group_add_field(group, field);
	for (i = 0; i < ggp_pubdir_provinces_count; i++)
	{
		purple_request_field_choice_add(field, ggp_pubdir_provinces[i],
			GINT_TO_POINTER(i));
		if (record && i == record->province)
		{
			purple_request_field_choice_set_value(field, GINT_TO_POINTER(i));
			purple_request_field_choice_set_default_value(field, GINT_TO_POINTER(i));
		}
	}
	
	purple_request_fields(gc, _("Set User Info"), _("Set User Info"),
		NULL, fields,
		_("OK"), G_CALLBACK(ggp_pubdir_set_info_request),
		_("Cancel"), NULL,
		purple_request_cpar_from_connection(gc), gc);
	
}

static void ggp_pubdir_set_info_request(PurpleConnection *gc,
	PurpleRequestFields *fields)
{
	gchar *url;
	uin_t uin = ggp_str_to_uin(purple_account_get_username(
		purple_connection_get_account(gc)));
	ggp_pubdir_record *record = g_new0(ggp_pubdir_record, 1);
	gchar *birth_s;
	
	purple_debug_info("gg", "ggp_pubdir_set_info_request\n");

	record->uin = uin;
	record->first_name = g_strdup(purple_request_fields_get_string(fields,
		"first_name"));
	record->last_name = g_strdup(purple_request_fields_get_string(fields,
		"last_name"));
	record->gender = GPOINTER_TO_INT(
		purple_request_fields_get_choice(fields, "gender"));
	record->city = g_strdup(purple_request_fields_get_string(fields,
		"city"));
	record->province = GPOINTER_TO_INT(
		purple_request_fields_get_choice(fields, "province"));
	
	birth_s = g_strdup_printf("%sT10:00:00+00:00",
		purple_request_fields_get_string(fields, "birth_date"));
	record->birth = ggp_date_from_iso8601(birth_s);
	g_free(birth_s);
	purple_debug_info("gg", "ggp_pubdir_set_info_request: birth [%lu][%s]\n",
		record->birth, purple_request_fields_get_string(
		fields, "birth_date"));

	url = g_strdup_printf("http://api.gadu-gadu.pl/users/%u.xml", uin);
	ggp_oauth_request(gc, ggp_pubdir_set_info_got_token, record,
		"PUT", url);
	g_free(url);
}

static void ggp_pubdir_set_info_got_token(PurpleConnection *gc,
	const gchar *token, gpointer _record)
{
	PurpleHttpRequest *req;
	ggp_pubdir_record *record = _record;
	gchar *request_data;
	gchar *name, *surname, *city;
	uin_t uin = record->uin;

	if (!token || !PURPLE_CONNECTION_IS_VALID(gc))
	{
		// TODO: notify about failure
		ggp_pubdir_record_free(record, 1);
		return;
	}
	
	name = g_uri_escape_string(record->first_name, NULL, FALSE);
	surname = g_uri_escape_string(record->last_name, NULL, FALSE);
	city = g_uri_escape_string(record->city, NULL, FALSE);
	
	request_data = g_strdup_printf(
		"name=%s&"
		"surname=%s&"
		"birth=%sT10:00:00%%2B00:00&"
		"birth_priv=2&"
		"gender=%d&"
		"gender_priv=2&"
		"city=%s&"
		"province=%d",
		name, surname,
		ggp_date_strftime("%Y-%m-%d", record->birth),
		record->gender,
		city,
		record->province);

	if (purple_debug_is_verbose() && purple_debug_is_unsafe()) {
		purple_debug_misc("gg", "ggp_pubdir_set_info_got_token: "
			"query [%s]\n", request_data);
	}

	req = purple_http_request_new(NULL);
	purple_http_request_set_method(req, "PUT");
	purple_http_request_set_url_printf(req,
		"http://api.gadu-gadu.pl/users/%u.xml", uin);
	purple_http_request_header_set(req, "Authorization", token);
	purple_http_request_header_set(req, "Content-Type",
		"application/x-www-form-urlencoded");
	purple_http_request_set_contents(req, request_data, -1);
	purple_http_request(gc, req, ggp_pubdir_set_info_got_response, NULL);
	purple_http_request_unref(req);

	g_free(request_data);
	ggp_pubdir_record_free(record, 1);
}

static void ggp_pubdir_set_info_got_response(PurpleHttpConnection *http_conn,
	PurpleHttpResponse *response, gpointer user_data)
{
	if (!purple_http_response_is_successful(response)) {
		purple_debug_error("gg", "ggp_pubdir_set_info_got_response: "
			"failed\n");
		return;
	}

	purple_debug_info("gg", "ggp_pubdir_set_info_got_response: [%s]\n",
		purple_http_response_get_data(response, NULL));
	/* <result><status>0</status></result> */

	/* TODO: notify about failure */
}

mercurial