libpurple/protocols/gg/pubdir-prpl.c

Tue, 07 Mar 2023 01:04:42 -0600

author
Elliott Sales de Andrade <quantum.analyst@gmail.com>
date
Tue, 07 Mar 2023 01:04:42 -0600
changeset 42128
118067ca0367
parent 42127
18acb99a0fa6
child 42139
c053558f1236
permissions
-rw-r--r--

Convert PurpleRequestPage into a GObject

And rename it from `PurpleRequestFields`.

Also, implements `GListModel` for the groups, but nothing uses it that way get.

Testing Done:
Compiled, and opened Request Fields from Demo protocol.

Reviewed at https://reviews.imfreedom.org/r/2323/

/* 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 <glib/gi18n-lib.h>

#include <purple.h>

#include "pubdir-prpl.h"
#include "gg.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;

/* 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;
};

/* For ggp_pubdir_search_results_next, which is called by this. */
static void ggp_pubdir_search_results_display(PurpleConnection *gc,
	int records_count, const ggp_pubdir_record *records, int next_offset,
	void *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*);

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

static 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_date_time_unref(records[i].birth);
	}
	g_free(records);
}

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

static 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);
}

static void
ggp_pubdir_got_data(GObject *source, GAsyncResult *result, gpointer data) {
	ggp_pubdir_request *request = data;
	PurpleConnection *gc = request->gc;
	GBytes *response_body = NULL;
	gboolean succ = TRUE;
	PurpleXmlNode *xml;
	const char *xml_raw = NULL;
	gsize xml_size = 0;
	unsigned int status, next_offset;
	int record_count, i;
	ggp_pubdir_record *records;
	GError *error = NULL;

	response_body = soup_session_send_and_read_finish(SOUP_SESSION(source),
	                                                  result, &error);
	if (response_body == NULL) {
		purple_debug_error("gg", "ggp_pubdir_got_data: %s", error->message);
		request->cb(gc, -1, NULL, 0, request->user_data);
		ggp_pubdir_request_free(request);
		g_error_free(error);
		return;
	}

	xml_raw = g_bytes_unref_to_data(response_body, &xml_size);

	if (purple_debug_is_verbose() && purple_debug_is_unsafe()) {
		purple_debug_misc("gg", "ggp_pubdir_got_data: xml=[%.*s]",
		                  (int)xml_size, xml_raw);
	}

	xml = purple_xmlnode_from_str(xml_raw, xml_size);
	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, "");
		g_clear_pointer(&record->birth, g_date_time_unref);

		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 = g_date_time_new_from_iso8601(birth_s, NULL);
		/*TODO: calculate age from birth */

		if (purple_debug_is_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, g_date_time_to_unix(record->birth));
		}

		g_clear_pointer(&city, g_free);
		g_clear_pointer(&birth_s, g_free);

		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);
}

static void
ggp_pubdir_get_info_got_token(PurpleConnection *gc, const gchar *token,
                              gpointer _request)
{
	GGPInfo *info = NULL;
	gchar *url;
	SoupMessage *msg;
	ggp_pubdir_request *request = _request;

	PURPLE_ASSERT_CONNECTION_IS_VALID(gc);

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

	info = purple_connection_get_protocol_data(gc);

	url = g_strdup_printf("http://api.gadu-gadu.pl/users/%u",
	                      request->params.user_info.uin);
	msg = soup_message_new("GET", url);
	g_free(url);
	soup_message_headers_replace(soup_message_get_request_headers(msg),
	                             "Authorization", token);
	soup_session_send_and_read_async(info->http, msg, G_PRIORITY_DEFAULT, NULL,
	                                 ggp_pubdir_got_data, request);
	g_object_unref(msg);
}

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_protocol_got(PurpleConnection *gc, int records_count,
                                 const ggp_pubdir_record *records,
                                 G_GNUC_UNUSED 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) {
		gchar *bday = g_date_time_format(record->birth, "%Y-%m-%d");

		purple_notify_user_info_add_pair_plaintext(info, _("Birthday"), bday);

		g_free(bday);
	} 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_get_info_protocol(G_GNUC_UNUSED PurpleProtocolServer *protocol_server,
                             PurpleConnection *gc, const gchar *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", uin);

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

static void
ggp_pubdir_request_buddy_alias_got(PurpleConnection *gc, int records_count,
                                   const ggp_pubdir_record *records,
                                   G_GNUC_UNUSED int next_offset,
                                   G_GNUC_UNUSED gpointer 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);

	purple_serv_got_alias(gc, ggp_uin_to_str(uin), alias);
}

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", uin);

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

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

static 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;
}

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_got_token(PurpleConnection *gc,
	const gchar *token, gpointer _request)
{
	GGPInfo *info = NULL;
	gchar *url;
	SoupMessage *msg;
	ggp_pubdir_request *request = _request;
	gchar *query;

	PURPLE_ASSERT_CONNECTION_IS_VALID(gc);

	if (!token) {
		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);

	info = purple_connection_get_protocol_data(gc);

	url = g_strdup_printf("http://api.gadu-gadu.pl%s", query);
	msg = soup_message_new("GET", url);
	soup_message_headers_replace(soup_message_get_request_headers(msg),
	                             "Authorization", token);
	soup_session_send_and_read_async(info->http, msg, G_PRIORITY_DEFAULT, NULL,
	                                 ggp_pubdir_got_data, request);

	g_object_unref(msg);
	g_free(url);
	g_free(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", 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_results_new(PurpleConnection *gc, G_GNUC_UNUSED GList *row,
                              gpointer _form)
{
	ggp_pubdir_search_form *form = _form;
	ggp_pubdir_search(gc, form);
}

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, G_GNUC_UNUSED 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,
                              G_GNUC_UNUSED 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,
                             G_GNUC_UNUSED 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,
                               G_GNUC_UNUSED gpointer form)
{
	ggp_pubdir_get_info_protocol(NULL, gc, g_list_nth_data(row, 0));
}

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_request(PurpleConnection *gc, PurpleRequestPage *page) {
	ggp_pubdir_search_form *form = g_new0(ggp_pubdir_search_form, 1);

	purple_debug_info("gg", "ggp_pubdir_search_request");

	form->nick = g_strdup(purple_request_page_get_string(page, "name"));
	form->city = g_strdup(purple_request_page_get_string(page, "city"));
	form->gender =
	        GPOINTER_TO_INT(purple_request_page_get_choice(page, "gender"));
	form->offset = 0;
	form->limit = GGP_PUBDIR_SEARCH_PER_PAGE;

	ggp_pubdir_search_execute(gc, form, ggp_pubdir_search_results_display,
	                          form);
}

void
ggp_pubdir_search(PurpleConnection *gc, const ggp_pubdir_search_form *form)
{
	PurpleRequestPage *page;
	PurpleRequestGroup *group;
	PurpleRequestField *field;

	purple_debug_info("gg", "ggp_pubdir_search");

	page = purple_request_page_new();
	group = purple_request_group_new(NULL);
	purple_request_page_add_group(page, group);

	field = purple_request_field_string_new("name", _("Name"),
	                                        form ? form->nick : NULL, FALSE);
	purple_request_group_add_field(group, field);

	field = purple_request_field_string_new("city", _("City"),
	                                        form ? form->city : NULL, FALSE);
	purple_request_group_add_field(group, field);

	field = purple_request_field_choice_new(
	        "gender", _("Gender"), form ? GINT_TO_POINTER(form->gender) : NULL);
	purple_request_field_choice_add(field, _("Male or female"), NULL);
	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_group_add_field(group, field);

	purple_request_fields(gc, _("Find buddies"), _("Find buddies"),
	                      _("Please, enter your search criteria below"), page,
	                      _("OK"), G_CALLBACK(ggp_pubdir_search_request),
	                      _("Cancel"), NULL,
	                      purple_request_cpar_from_connection(gc), gc);
}

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

static void
ggp_pubdir_set_info_got_response(GObject *source, GAsyncResult *result,
                                 gpointer data)
{
	SoupMessage *msg = data;
	GBytes *response_body = NULL;
	const char *buffer = NULL;
	gsize size = 0;
	GError *error = NULL;

	if (!SOUP_STATUS_IS_SUCCESSFUL(soup_message_get_status(msg))) {
		purple_debug_error("gg", "ggp_pubdir_set_info_got_response: failed");
		g_object_unref(msg);
		return;
	}
	g_clear_object(&msg);

	response_body = soup_session_send_and_read_finish(SOUP_SESSION(source),
	                                                  result, &error);
	if (response_body == NULL) {
		purple_debug_error("gg",
		                   "ggp_pubdir_set_info_got_response: failed: %s",
		                   error->message);
		g_error_free(error);
		return;
	}

	buffer = g_bytes_get_data(response_body, &size);
	purple_debug_info("gg", "ggp_pubdir_set_info_got_response: [%.*s]",
	                  (int)size, buffer);
	/* <result><status>0</status></result> */

	/* TODO: notify about failure */

	g_bytes_unref(response_body);
}

static void ggp_pubdir_set_info_got_token(PurpleConnection *gc,
	const gchar *token, gpointer _record)
{
	ggp_pubdir_record *record = _record;
	GGPInfo *info = NULL;
	SoupMessage *msg;
	GBytes *body = NULL;
	gchar *url;
	gchar *request_data;
	gchar *name, *surname, *city;
	gchar *bday;
	uin_t uin = record->uin;

	PURPLE_ASSERT_CONNECTION_IS_VALID(gc);

	if (!token) {
		/* TODO: notify about failure */
		ggp_pubdir_record_free(record, 1);
		return;
	}

	info = purple_connection_get_protocol_data(gc);

	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);
	bday = g_date_time_format(record->birth, "%Y-%m-%d");

	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,
		bday,
		record->gender,
		city,
		record->province);

	g_free(name);
	g_free(surname);
	g_free(city);
	g_free(bday);

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

	url = g_strdup_printf("http://api.gadu-gadu.pl/users/%u.xml", uin);
	msg = soup_message_new("PUT", url);
	soup_message_headers_replace(soup_message_get_request_headers(msg),
	                             "Authorization", token);
	body = g_bytes_new_take(request_data, strlen(request_data));
	soup_message_set_request_body_from_bytes(msg,
	                                         "application/x-www-form-urlencoded",
	                                         body);
	g_bytes_unref(body);
	soup_session_send_and_read_async(info->http, msg, G_PRIORITY_DEFAULT, NULL,
	                                 ggp_pubdir_set_info_got_response, msg);

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

static void
ggp_pubdir_set_info_request(PurpleConnection *gc, PurpleRequestPage *page)
{
	PurpleAccount *account = purple_connection_get_account(gc);
	PurpleContactInfo *info = PURPLE_CONTACT_INFO(account);
	gchar *url;
	uin_t uin = ggp_str_to_uin(purple_contact_info_get_username(info));
	ggp_pubdir_record *record = g_new0(ggp_pubdir_record, 1);
	gchar *birth_s;

	purple_debug_info("gg", "ggp_pubdir_set_info_request");

	record->uin = uin;
	record->first_name = g_strdup(purple_request_page_get_string(page,
	                                                             "first_name"));
	record->last_name = g_strdup(purple_request_page_get_string(page,
	                                                            "last_name"));
	record->gender = GPOINTER_TO_INT(purple_request_page_get_choice(page,
	                                                                "gender"));
	record->city = g_strdup(purple_request_page_get_string(page, "city"));
	record->province = GPOINTER_TO_INT(purple_request_page_get_choice(page,
	                                                                  "province"));

	birth_s = g_strdup_printf("%sT10:00:00+00:00",
	                          purple_request_page_get_string(page, "birth_date"));
	record->birth = g_date_time_new_from_iso8601(birth_s, NULL);
	g_free(birth_s);
	purple_debug_info("gg", "ggp_pubdir_set_info_request: birth [%lu][%s]",
	                  g_date_time_to_unix(record->birth),
	                  purple_request_page_get_string(page, "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_dialog(PurpleConnection *gc, int records_count,
                           const ggp_pubdir_record *records,
                           G_GNUC_UNUSED int next_offset,
                           G_GNUC_UNUSED gpointer data)
{
	PurpleRequestPage *page;
	PurpleRequestGroup *group;
	PurpleRequestField *field;
	gchar *bday = NULL;
	gsize i;
	const ggp_pubdir_record *record;

	purple_debug_info("gg", "ggp_pubdir_set_info_dialog (record: %d)",
	                  records_count);

	record = (records_count == 1 ? &records[0] : NULL);

	page = purple_request_page_new();
	group = purple_request_group_new(NULL);
	purple_request_page_add_group(page, group);

	field = purple_request_field_string_new("first_name", _("First name"),
	                                        record ? record->first_name : NULL,
	                                        FALSE);
	purple_request_group_add_field(group, field);

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

	field = purple_request_field_choice_new(
	        "gender", _("Gender"),
	        record ? GINT_TO_POINTER(record->gender)
	               : GGP_PUBDIR_GENDER_UNSPECIFIED);
	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_group_add_field(group, field);

	if(record != NULL && record->birth != NULL) {
		bday = g_date_time_format(record->birth, "%Y-%m-%d");
	}

	field = purple_request_field_string_new(
	        "birth_date", _("Birth Day"), bday,
	        FALSE);
	g_free(bday);
	purple_request_field_set_required(field, TRUE);
	purple_request_group_add_field(group, field);

	field = purple_request_field_string_new(
	        "city", _("City"), record ? record->city : NULL, FALSE);
	purple_request_group_add_field(group, field);

	/* Translators: This word is basically used to describe a Polish
	   province. Gadu-Gadu users outside of Poland might choose to enter some
	   equivalent value for themselves. For example, users in the USA might
	   use their state (e.g. New York). If there is an equivalent term for
	   your language, feel free to use it. Otherwise it's probably acceptable
	   to leave it changed or transliterate it into your alphabet. */
	field = purple_request_field_choice_new("province", _("Voivodeship"), 0);
	purple_request_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,
	                      page, _("OK"),
	                      G_CALLBACK(ggp_pubdir_set_info_request), _("Cancel"),
	                      NULL, purple_request_cpar_from_connection(gc), gc);
}

void
ggp_pubdir_set_info(PurpleConnection *gc)
{
	PurpleAccount *account = purple_connection_get_account(gc);
	PurpleContactInfo *info = PURPLE_CONTACT_INFO(account);

	ggp_pubdir_get_info(gc,
	                    ggp_str_to_uin(purple_contact_info_get_username(info)),
	                    ggp_pubdir_set_info_dialog, NULL);
}

mercurial