pidgin/pidgin

Remove the GaduGadu protocol plugin

2 months ago, Gary Kramlich
89319cf6fa50
Parents df9aafbae930
Children 7a034b8021a7
Remove the GaduGadu protocol plugin

This protocol plugin depends on libgadu which hasn't been maintained since
2019. That said it is still currently functional as far as I know. However, the
protocol plugin needs a lot of work for the changes in purple 3 and at this
point, it is probably easier to rewrite it even if it still uses libgadu.

Testing Done:
Ran meson dist.

Reviewed at https://reviews.imfreedom.org/r/3051/
  • +0 -344
    libpurple/protocols/gg/avatar.c
  • +0 -46
    libpurple/protocols/gg/avatar.h
  • +0 -201
    libpurple/protocols/gg/blist.c
  • +0 -64
    libpurple/protocols/gg/blist.h
  • +0 -648
    libpurple/protocols/gg/chat.c
  • +0 -59
    libpurple/protocols/gg/chat.h
  • +0 -1344
    libpurple/protocols/gg/edisc.c
  • +0 -59
    libpurple/protocols/gg/edisc.h
  • +0 -1452
    libpurple/protocols/gg/gg.c
  • +0 -82
    libpurple/protocols/gg/gg.h
  • +0 -179
    libpurple/protocols/gg/html.c
  • +0 -59
    libpurple/protocols/gg/html.h
  • +0 -262
    libpurple/protocols/gg/image-prpl.c
  • +0 -68
    libpurple/protocols/gg/image-prpl.h
  • +0 -106
    libpurple/protocols/gg/keymapper.c
  • +0 -49
    libpurple/protocols/gg/keymapper.h
  • +0 -120
    libpurple/protocols/gg/libgadu-events.c
  • +0 -43
    libpurple/protocols/gg/libgadu-events.h
  • +0 -215
    libpurple/protocols/gg/libgaduw.c
  • +0 -62
    libpurple/protocols/gg/libgaduw.h
  • +0 -66
    libpurple/protocols/gg/meson.build
  • +0 -699
    libpurple/protocols/gg/message-prpl.c
  • +0 -49
    libpurple/protocols/gg/message-prpl.h
  • +0 -264
    libpurple/protocols/gg/multilogon.c
  • +0 -46
    libpurple/protocols/gg/multilogon.h
  • +0 -301
    libpurple/protocols/gg/oauth/oauth-purple.c
  • +0 -42
    libpurple/protocols/gg/oauth/oauth-purple.h
  • +0 -185
    libpurple/protocols/gg/oauth/oauth.c
  • +0 -35
    libpurple/protocols/gg/oauth/oauth.h
  • +0 -1025
    libpurple/protocols/gg/pubdir-prpl.c
  • +0 -73
    libpurple/protocols/gg/pubdir-prpl.h
  • +0 -153
    libpurple/protocols/gg/purplew.c
  • +0 -76
    libpurple/protocols/gg/purplew.h
  • +0 -205
    libpurple/protocols/gg/resolver-purple.c
  • +0 -38
    libpurple/protocols/gg/resolver-purple.h
  • +0 -11
    libpurple/protocols/gg/resources/gg.gresource.xml
  • +0 -0
    libpurple/protocols/gg/resources/icons/16x16/apps/im-gadu-gadu.png
  • +0 -365
    libpurple/protocols/gg/resources/icons/16x16/apps/scalable/im-gadu-gadu.svg
  • +0 -0
    libpurple/protocols/gg/resources/icons/22x22/apps/im-gadu-gadu.png
  • +0 -206
    libpurple/protocols/gg/resources/icons/22x22/apps/scalable/im-gadu-gadu.svg
  • +0 -0
    libpurple/protocols/gg/resources/icons/48x48/apps/im-gadu-gadu.png
  • +0 -215
    libpurple/protocols/gg/resources/icons/scalable/apps/im-gadu-gadu.svg
  • +0 -1092
    libpurple/protocols/gg/roster.c
  • +0 -73
    libpurple/protocols/gg/roster.h
  • +0 -118
    libpurple/protocols/gg/servconn.c
  • +0 -42
    libpurple/protocols/gg/servconn.h
  • +0 -449
    libpurple/protocols/gg/status.c
  • +0 -64
    libpurple/protocols/gg/status.h
  • +0 -265
    libpurple/protocols/gg/tcpsocket.c
  • +0 -41
    libpurple/protocols/gg/tcpsocket.h
  • +0 -172
    libpurple/protocols/gg/utils.c
  • +0 -94
    libpurple/protocols/gg/utils.h
  • +0 -80
    libpurple/protocols/gg/validator.c
  • +0 -41
    libpurple/protocols/gg/validator.h
  • +0 -163
    libpurple/protocols/gg/xml.c
  • +0 -47
    libpurple/protocols/gg/xml.h
  • +0 -1
    libpurple/protocols/meson.build
  • +1 -26
    meson.build
  • --- a/libpurple/protocols/gg/avatar.c Tue Apr 09 21:50:31 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,344 +0,0 @@
    -/* 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 "avatar.h"
    -
    -#include "libpurple/glibcompat.h"
    -
    -#include "gg.h"
    -#include "utils.h"
    -#include "oauth/oauth-purple.h"
    -
    -/* Common */
    -
    -#define GGP_AVATAR_USERAGENT "GG Client build 11.0.0.7562"
    -#define GGP_AVATAR_SIZE_MAX 1048576
    -
    -/* Buddy avatars updating */
    -
    -typedef struct
    -{
    - uin_t uin;
    - time_t timestamp;
    - PurpleConnection *gc;
    - SoupMessage *msg;
    -} ggp_avatar_buddy_update_req;
    -
    -#define GGP_AVATAR_BUDDY_URL "http://avatars.gg.pl/%u/s,big"
    -
    -/* Own avatar setting */
    -
    -struct _ggp_avatar_session_data {
    - PurpleImage *own_img;
    -};
    -
    -#define GGP_AVATAR_RESPONSE_MAX 10240
    -
    -/*******************************************************************************
    - * Common.
    - ******************************************************************************/
    -
    -static inline ggp_avatar_session_data *
    -ggp_avatar_get_avdata(PurpleConnection *gc)
    -{
    - GGPInfo *accdata = purple_connection_get_protocol_data(gc);
    - return accdata->avatar_data;
    -}
    -
    -void ggp_avatar_setup(PurpleConnection *gc)
    -{
    - GGPInfo *info = purple_connection_get_protocol_data(gc);
    -
    - info->avatar_data = g_new0(ggp_avatar_session_data, 1);
    -}
    -
    -void ggp_avatar_cleanup(PurpleConnection *gc)
    -{
    - GGPInfo *info = purple_connection_get_protocol_data(gc);
    -
    - g_free(info->avatar_data);
    -}
    -
    -/*******************************************************************************
    - * Buddy avatars updating.
    - ******************************************************************************/
    -
    -void ggp_avatar_buddy_remove(PurpleConnection *gc, uin_t uin)
    -{
    - if (purple_debug_is_verbose()) {
    - purple_debug_misc("gg", "ggp_avatar_buddy_remove(%p, %u)\n", gc, uin);
    - }
    -
    - purple_buddy_icons_set_for_user(purple_connection_get_account(gc),
    - ggp_uin_to_str(uin), NULL, 0, NULL);
    -}
    -
    -static void
    -ggp_avatar_buddy_update_received(GObject *source, GAsyncResult *result,
    - gpointer data)
    -{
    - ggp_avatar_buddy_update_req *pending_update = data;
    - GBytes *response_body = NULL;
    - GError *error = NULL;
    - const char *error_message = NULL;
    - PurpleBuddy *buddy;
    - PurpleAccount *account;
    - PurpleConnection *gc = pending_update->gc;
    - gchar timestamp_str[20];
    - char *got_data = NULL;
    - gsize got_len = 0;
    -
    - PURPLE_ASSERT_CONNECTION_IS_VALID(gc);
    -
    - if(SOUP_STATUS_IS_SUCCESSFUL(soup_message_get_status(pending_update->msg))) {
    - response_body = soup_session_send_and_read_finish(SOUP_SESSION(source),
    - result, &error);
    - error_message = error != NULL ? error->message : "unknown";
    - } else {
    - error_message = soup_message_get_reason_phrase(pending_update->msg);
    - }
    - if(response_body == NULL) {
    - purple_debug_error("gg",
    - "ggp_avatar_buddy_update_received: bad response "
    - "while getting avatar for %u: %s",
    - pending_update->uin, error_message);
    - g_object_unref(pending_update->msg);
    - g_free(pending_update);
    - g_clear_error(&error);
    - return;
    - }
    -
    - account = purple_connection_get_account(gc);
    - buddy = purple_blist_find_buddy(account,
    - ggp_uin_to_str(pending_update->uin));
    -
    - if (!buddy) {
    - purple_debug_warning(
    - "gg", "ggp_avatar_buddy_update_received: buddy %u disappeared",
    - pending_update->uin);
    - g_object_unref(pending_update->msg);
    - g_free(pending_update);
    - return;
    - }
    -
    - g_snprintf(timestamp_str, sizeof(timestamp_str), "%lu",
    - pending_update->timestamp);
    - got_data = g_bytes_unref_to_data(response_body, &got_len);
    - purple_buddy_icons_set_for_user(account, purple_buddy_get_name(buddy),
    - got_data, got_len, timestamp_str);
    -
    - purple_debug_info("gg",
    - "ggp_avatar_buddy_update_received: got avatar for buddy "
    - "%u [ts=%lu]",
    - pending_update->uin, pending_update->timestamp);
    - g_object_unref(pending_update->msg);
    - g_free(pending_update);
    -}
    -
    -void
    -ggp_avatar_buddy_update(PurpleConnection *gc, uin_t uin, time_t timestamp)
    -{
    - GGPInfo *info = purple_connection_get_protocol_data(gc);
    - gchar *url;
    - SoupMessage *req;
    - ggp_avatar_buddy_update_req *pending_update;
    - PurpleBuddy *buddy;
    - PurpleAccount *account = purple_connection_get_account(gc);
    - time_t old_timestamp;
    - const char *old_timestamp_str;
    -
    - if (purple_debug_is_verbose()) {
    - purple_debug_misc("gg", "ggp_avatar_buddy_update(%p, %u, %lu)", gc, uin,
    - timestamp);
    - }
    -
    - buddy = purple_blist_find_buddy(account, ggp_uin_to_str(uin));
    -
    - if (!buddy) {
    - PurpleContactInfo *info = PURPLE_CONTACT_INFO(account);
    -
    - if (ggp_str_to_uin(purple_contact_info_get_username(info)) == uin) {
    - purple_debug_misc(
    - "gg",
    - "ggp_avatar_buddy_update(%p): own avatar update requested, "
    - "but we don't have ourselves on buddy list",
    - gc);
    - } else {
    - purple_debug_warning("gg",
    - "ggp_avatar_buddy_update(%p): %u update "
    - "requested, but he's not on buddy list",
    - gc, uin);
    - }
    - return;
    - }
    -
    - old_timestamp_str = purple_buddy_icons_get_checksum_for_user(buddy);
    - old_timestamp = old_timestamp_str ? g_ascii_strtoull(
    - old_timestamp_str, NULL, 10) : 0;
    - if (old_timestamp == timestamp) {
    - if (purple_debug_is_verbose()) {
    - purple_debug_misc("gg",
    - "ggp_avatar_buddy_update(%p): %u have up to date "
    - "avatar with ts=%lu",
    - gc, uin, timestamp);
    - }
    - return;
    - }
    - if (old_timestamp > timestamp) {
    - purple_debug_warning("gg",
    - "ggp_avatar_buddy_update(%p): saved timestamp for "
    - "%u is newer than received (%lu > %lu)",
    - gc, uin, old_timestamp, timestamp);
    - }
    -
    - purple_debug_info("gg",
    - "ggp_avatar_buddy_update(%p): updating %u with ts=%lu...",
    - gc, uin, timestamp);
    -
    - pending_update = g_new(ggp_avatar_buddy_update_req, 1);
    - pending_update->uin = uin;
    - pending_update->timestamp = timestamp;
    - pending_update->gc = gc;
    -
    - url = g_strdup_printf(GGP_AVATAR_BUDDY_URL, pending_update->uin);
    - pending_update->msg = req = soup_message_new("GET", url);
    - g_free(url);
    - soup_message_headers_replace(soup_message_get_request_headers(req),
    - "User-Agent", GGP_AVATAR_USERAGENT);
    - // purple_http_request_set_max_len(req, GGP_AVATAR_SIZE_MAX);
    - soup_session_send_and_read_async(info->http, req, G_PRIORITY_DEFAULT, NULL,
    - ggp_avatar_buddy_update_received,
    - pending_update);
    -}
    -
    -/*******************************************************************************
    - * Own avatar setting.
    - ******************************************************************************/
    -
    -/*
    - * TODO: use new, GG11 method, when IMToken will be provided by libgadu.
    - *
    - * POST https://avatars.mpa.gg.pl/avatars/user,<uin>/0
    - * Authorization: IMToken 0123456789abcdef0123456789abcdef01234567
    - * photo=<avatar content>
    - */
    -
    -static void
    -ggp_avatar_own_sent(GObject *source, GAsyncResult *result, gpointer data) {
    - SoupMessage *msg = data;
    - GBytes *response_body = NULL;
    - GError *error = NULL;
    - const char *buffer = NULL;
    - gsize size = 0;
    -
    - if (!SOUP_STATUS_IS_SUCCESSFUL(soup_message_get_status(msg))) {
    - purple_debug_error("gg", "ggp_avatar_own_sent: avatar not sent. %s",
    - soup_message_get_reason_phrase(msg));
    - 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_avatar_own_sent: avatar not sent. %s",
    - error->message);
    - g_error_free(error);
    - return;
    - }
    -
    - buffer = g_bytes_get_data(response_body, &size);
    - purple_debug_info("gg", "ggp_avatar_own_sent: %.*s", (int)size, buffer);
    - g_bytes_unref(response_body);
    -}
    -
    -static void
    -ggp_avatar_own_got_token(PurpleConnection *gc, const gchar *token,
    - gpointer _img)
    -{
    - GGPInfo *info = purple_connection_get_protocol_data(gc);
    - ggp_avatar_session_data *avdata = ggp_avatar_get_avdata(gc);
    - SoupMessage *req;
    - SoupMessageHeaders *headers;
    - PurpleImage *img = _img;
    - gchar *img_data, *uin_str;
    - PurpleAccount *account = purple_connection_get_account(gc);
    - PurpleContactInfo *contact_info = PURPLE_CONTACT_INFO(account);
    - uin_t uin = ggp_str_to_uin(purple_contact_info_get_username(contact_info));
    -
    - if (img != avdata->own_img) {
    - purple_debug_warning("gg", "ggp_avatar_own_got_token: "
    - "avatar was changed in meantime\n");
    - return;
    - }
    - avdata->own_img = NULL;
    -
    - img_data = g_base64_encode(purple_image_get_data(img),
    - purple_image_get_data_size(img));
    - uin_str = g_strdup_printf("%d", uin);
    -
    - purple_debug_misc("gg", "ggp_avatar_own_got_token: "
    - "uploading new avatar...\n");
    -
    - req = soup_message_new_from_encoded_form(
    - "POST", "http://avatars.nowe.gg/upload",
    - soup_form_encode("uin", uin_str, "photo", img_data, NULL));
    - // purple_http_request_set_max_len(req, GGP_AVATAR_RESPONSE_MAX);
    - headers = soup_message_get_request_headers(req);
    - soup_message_headers_replace(headers, "Authorization", token);
    - soup_message_headers_replace(headers, "From", "avatars to avatars");
    - soup_session_send_and_read_async(info->http, req, G_PRIORITY_DEFAULT, NULL,
    - ggp_avatar_own_sent, req);
    - g_free(img_data);
    - g_free(uin_str);
    -}
    -
    -void
    -ggp_avatar_own_set(G_GNUC_UNUSED PurpleProtocolServer *protocol_server,
    - PurpleConnection *gc, PurpleImage *img)
    -{
    - ggp_avatar_session_data *avdata;
    -
    - PURPLE_ASSERT_CONNECTION_IS_VALID(gc);
    -
    - purple_debug_info("gg", "ggp_avatar_own_set(%p, %p)", gc, img);
    -
    - avdata = ggp_avatar_get_avdata(gc);
    -
    - if (img == NULL) {
    - purple_debug_warning("gg", "ggp_avatar_own_set: avatar removing is "
    - "probably not possible within old protocol");
    - return;
    - }
    -
    - avdata->own_img = img;
    -
    - ggp_oauth_request(gc, ggp_avatar_own_got_token, img, NULL, NULL);
    -}
    --- a/libpurple/protocols/gg/avatar.h Tue Apr 09 21:50:31 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,46 +0,0 @@
    -/* 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
    - */
    -
    -#ifndef PURPLE_GG_AVATAR_H
    -#define PURPLE_GG_AVATAR_H
    -
    -#include <purple.h>
    -#include <libgadu.h>
    -
    -typedef struct _ggp_avatar_session_data ggp_avatar_session_data;
    -
    -void ggp_avatar_setup(PurpleConnection *gc);
    -void ggp_avatar_cleanup(PurpleConnection *gc);
    -
    -void ggp_avatar_buddy_update(PurpleConnection *gc, uin_t uin, time_t timestamp);
    -void ggp_avatar_buddy_remove(PurpleConnection *gc, uin_t uin);
    -
    -void ggp_avatar_own_set(PurpleProtocolServer *protocol_server, PurpleConnection *gc, PurpleImage *img);
    -
    -#endif /* PURPLE_GG_AVATAR_H */
    --- a/libpurple/protocols/gg/blist.c Tue Apr 09 21:50:31 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,201 +0,0 @@
    -/**
    - * @file buddylist.c
    - *
    - * purple
    - *
    - * Copyright (C) 2005 Bartosz Oler <bartosz@bzimage.us>
    - *
    - * 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 <libgadu.h>
    -
    -#include "gg.h"
    -#include "utils.h"
    -#include "blist.h"
    -
    -#define F_FIRSTNAME 0
    -#define F_LASTNAME 1
    -/* #define F_ 2 */
    -#define F_NICKNAME 3
    -#define F_PHONE 4
    -#define F_GROUP 5
    -#define F_UIN 6
    -
    -/* void ggp_buddylist_send(PurpleConnection *gc) {{{ */
    -/* this is for for notify purposes, not synchronizing buddy list */
    -void ggp_buddylist_send(PurpleConnection *gc)
    -{
    - GGPInfo *info = purple_connection_get_protocol_data(gc);
    - PurpleAccount *account = purple_connection_get_account(gc);
    - GSList *buddies;
    - uin_t *userlist;
    - gchar *types;
    - int i = 0, ret = 0;
    - int size;
    -
    - buddies = purple_blist_find_buddies(account, NULL);
    -
    - size = g_slist_length(buddies);
    - userlist = g_new(uin_t, size);
    - types = g_new(gchar, size);
    -
    - for (buddies = purple_blist_find_buddies(account, NULL); buddies;
    - buddies = g_slist_delete_link(buddies, buddies), ++i)
    - {
    - PurpleBuddy *buddy = buddies->data;
    - const gchar *name = purple_buddy_get_name(buddy);
    -
    - userlist[i] = ggp_str_to_uin(name);
    - types[i] = GG_USER_NORMAL;
    - purple_debug_info("gg", "ggp_buddylist_send: adding %d\n",
    - userlist[i]);
    - }
    -
    - ret = gg_notify_ex(info->session, userlist, types, size);
    - purple_debug_info("gg", "send: ret=%d; size=%d\n", ret, size);
    -
    - if (userlist) {
    - g_free(userlist);
    - g_free(types);
    - }
    -}
    -/* }}} */
    -
    -/* void ggp_buddylist_load(PurpleConnection *gc, char *buddylist) {{{ */
    -void ggp_buddylist_load(PurpleConnection *gc, char *buddylist)
    -{
    - PurpleBuddy *buddy;
    - PurpleGroup *group;
    - gchar **users_tbl;
    - int i;
    - char *utf8buddylist = ggp_convert_from_cp1250(buddylist);
    -
    - /* Don't limit the number of records in a buddylist. */
    - users_tbl = g_strsplit(utf8buddylist, "\r\n", -1);
    -
    - for (i = 0; users_tbl[i] != NULL; i++) {
    - gchar **data_tbl;
    - gchar *name, *show, *g;
    -
    - if (!*users_tbl[i])
    - continue;
    -
    - data_tbl = g_strsplit(users_tbl[i], ";", 8);
    - if (g_strv_length(data_tbl) < 8) {
    - purple_debug_warning("gg",
    - "Something is wrong on line %d of the buddylist. Skipping.\n",
    - i + 1);
    - continue;
    - }
    -
    - show = data_tbl[F_NICKNAME];
    - name = data_tbl[F_UIN];
    - if ('\0' == *name || !atol(name)) {
    - purple_debug_warning("gg",
    - "Identifier on line %d of the buddylist is not a number. Skipping.\n",
    - i + 1);
    - continue;
    - }
    -
    - if ('\0' == *show) {
    - show = name;
    - }
    -
    - purple_debug_info("gg", "got buddy: name=%s; show=%s\n", name, show);
    -
    - if (purple_blist_find_buddy(purple_connection_get_account(gc), name)) {
    - g_strfreev(data_tbl);
    - continue;
    - }
    -
    - g = g_strdup("Gadu-Gadu");
    -
    - if ('\0' != *(data_tbl[F_GROUP])) {
    - /* XXX: Probably buddy should be added to all the groups. */
    - /* Hard limit to at most 50 groups */
    - gchar **group_tbl = g_strsplit(data_tbl[F_GROUP], ",", 50);
    - if (g_strv_length(group_tbl) > 0) {
    - g_free(g);
    - g = g_strdup(group_tbl[0]);
    - }
    - g_strfreev(group_tbl);
    - }
    -
    - buddy = purple_buddy_new(purple_connection_get_account(gc),
    - name, *show == '\0' ? NULL : show);
    -
    - if (!(group = purple_blist_find_group(g))) {
    - group = purple_group_new(g);
    - purple_blist_add_group(group, NULL);
    - }
    -
    - purple_blist_add_buddy(buddy, NULL, group, NULL);
    - g_free(g);
    -
    - g_strfreev(data_tbl);
    - }
    - g_strfreev(users_tbl);
    - g_free(utf8buddylist);
    -
    - ggp_buddylist_send(gc);
    -}
    -/* }}} */
    -
    -/* char *ggp_buddylist_dump(PurpleAccount *account) {{{ */
    -char *ggp_buddylist_dump(PurpleAccount *account)
    -{
    - GSList *buddies;
    - GString *buddylist = g_string_sized_new(1024);
    - char *ptr;
    -
    - for (buddies = purple_blist_find_buddies(account, NULL); buddies;
    - buddies = g_slist_delete_link(buddies, buddies))
    - {
    - PurpleBuddy *buddy = buddies->data;
    - PurpleGroup *group = purple_buddy_get_group(buddy);
    - const char *bname = purple_buddy_get_name(buddy);
    - const char *gname = purple_group_get_name(group);
    - const char *alias = purple_buddy_get_alias(buddy);
    -
    - if (alias == NULL)
    - alias = bname;
    -
    - g_string_append_printf(buddylist,
    - "%s;%s;%s;%s;%s;%s;%s;%s%s\r\n",
    - alias, alias, alias, alias,
    - "", gname, bname, "", "");
    - }
    -
    - ptr = ggp_convert_to_cp1250(buddylist->str);
    - g_string_free(buddylist, TRUE);
    - return ptr;
    -}
    -/* }}} */
    -
    -const char * ggp_buddylist_get_buddy_name(PurpleConnection *gc, uin_t uin)
    -{
    - const char *uin_s = ggp_uin_to_str(uin);
    - PurpleBuddy *buddy = purple_blist_find_buddy(
    - purple_connection_get_account(gc), uin_s);
    -
    - if (buddy != NULL)
    - return purple_buddy_get_alias(buddy);
    - else
    - return uin_s;
    -}
    -
    -/* vim: set ts=8 sts=0 sw=8 noet: */
    --- a/libpurple/protocols/gg/blist.h Tue Apr 09 21:50:31 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,64 +0,0 @@
    -/**
    - * @file blist.h
    - *
    - * purple
    - *
    - * Copyright (C) 2005 Bartosz Oler <bartosz@bzimage.us>
    - *
    - * 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
    - */
    -
    -#ifndef PURPLE_GG_BLIST_H
    -#define PURPLE_GG_BLIST_H
    -
    -#include <purple.h>
    -
    -void
    -ggp_buddylist_send(PurpleConnection *gc);
    -
    -/**
    - * Load buddylist from server into the roster.
    - *
    - * @param gc PurpleConnection
    - * @param buddylist Pointer to the buddylist that will be loaded.
    - */
    -/* void ggp_buddylist_load(PurpleConnection *gc, char *buddylist) {{{ */
    -void
    -ggp_buddylist_load(PurpleConnection *gc, char *buddylist);
    -
    -/**
    - * Get all the buddies in the current account.
    - *
    - * @param account Current account.
    - *
    - * @return List of buddies.
    - */
    -char *
    -ggp_buddylist_dump(PurpleAccount *account);
    -
    -/**
    - * Returns the best name of a buddy from the buddylist.
    - *
    - * @param gc PurpleConnection instance.
    - * @param uin UIN of the buddy.
    - *
    - * @return Name of the buddy, or UIN converted to string, if there is no such
    - * user on the list.
    - */
    -const char * ggp_buddylist_get_buddy_name(PurpleConnection *gc, uin_t uin);
    -
    -#endif /* PURPLE_GG_BLIST_H */
    -
    -/* vim: set ts=8 sts=0 sw=8 noet: */
    --- a/libpurple/protocols/gg/chat.c Tue Apr 09 21:50:31 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,648 +0,0 @@
    -/* 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.
    - *
    - * Component written by Tomek Wasilczyk (http://www.wasilczyk.pl).
    - *
    - * This file is dual-licensed under the GPL2+ and the X11 (MIT) licences.
    - * As a recipient of this file you may choose, which license to receive the
    - * code under. As a contributor, you have to ensure the new code is
    - * compatible with both.
    - *
    - * 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.h>
    -#include <glib/gi18n-lib.h>
    -
    -#include "chat.h"
    -
    -#include "gg.h"
    -#include "utils.h"
    -#include "message-prpl.h"
    -
    -typedef struct _ggp_chat_local_info ggp_chat_local_info;
    -
    -struct _ggp_chat_session_data
    -{
    - ggp_chat_local_info *chats;
    - int chats_count;
    -
    - gboolean got_all_chats_info;
    - GSList *pending_joins;
    -};
    -
    -struct _ggp_chat_local_info
    -{
    - int local_id;
    - uint64_t id;
    -
    - PurpleChatConversation *conv;
    - PurpleConnection *gc;
    -
    - gboolean left;
    - gboolean previously_joined;
    -
    - uin_t *participants;
    - int participants_count;
    -};
    -
    -static ggp_chat_local_info * ggp_chat_new(PurpleConnection *gc, uint64_t id);
    -static ggp_chat_local_info * ggp_chat_get(PurpleConnection *gc, uint64_t id);
    -static void ggp_chat_open_conv(ggp_chat_local_info *chat);
    -static ggp_chat_local_info * ggp_chat_get_local(PurpleConnection *gc,
    - int local_id);
    -static void ggp_chat_joined(ggp_chat_local_info *chat, uin_t uin);
    -static void ggp_chat_left(ggp_chat_local_info *chat, uin_t uin);
    -static const gchar * ggp_chat_get_name_from_id(uint64_t id);
    -static uint64_t ggp_chat_get_id_from_name(const gchar * name);
    -static void ggp_chat_join_id(PurpleConnection *gc, uint64_t id);
    -
    -static inline ggp_chat_session_data *
    -ggp_chat_get_sdata(PurpleConnection *gc)
    -{
    - GGPInfo *accdata = purple_connection_get_protocol_data(gc);
    - return accdata->chat_data;
    -}
    -
    -void ggp_chat_setup(PurpleConnection *gc)
    -{
    - GGPInfo *accdata = purple_connection_get_protocol_data(gc);
    - ggp_chat_session_data *sdata = g_new0(ggp_chat_session_data, 1);
    -
    - accdata->chat_data = sdata;
    -}
    -
    -void ggp_chat_cleanup(PurpleConnection *gc)
    -{
    - ggp_chat_session_data *sdata = ggp_chat_get_sdata(gc);
    - int i;
    -
    - g_slist_free_full(sdata->pending_joins, g_free);
    - for (i = 0; i < sdata->chats_count; i++)
    - g_free(sdata->chats[i].participants);
    - g_free(sdata->chats);
    - g_free(sdata);
    -}
    -
    -static ggp_chat_local_info * ggp_chat_new(PurpleConnection *gc, uint64_t id)
    -{
    - ggp_chat_session_data *sdata = ggp_chat_get_sdata(gc);
    - int local_id;
    - ggp_chat_local_info *chat;
    -
    - if (NULL != (chat = ggp_chat_get(gc, id)))
    - return chat;
    -
    - local_id = sdata->chats_count++;
    - sdata->chats = g_realloc(sdata->chats,
    - sdata->chats_count * sizeof(ggp_chat_local_info));
    - chat = &sdata->chats[local_id];
    -
    - chat->local_id = local_id;
    - chat->id = id;
    - chat->conv = NULL;
    - chat->gc = gc;
    - chat->left = FALSE;
    - chat->previously_joined = FALSE;
    - chat->participants = NULL;
    - chat->participants_count = 0;
    -
    - return chat;
    -}
    -
    -static ggp_chat_local_info * ggp_chat_get(PurpleConnection *gc, uint64_t id)
    -{
    - ggp_chat_session_data *sdata = ggp_chat_get_sdata(gc);
    - int i;
    -
    - for (i = 0; i < sdata->chats_count; i++) {
    - if (sdata->chats[i].id == id)
    - return &sdata->chats[i];
    - }
    -
    - return NULL;
    -}
    -
    -static void ggp_chat_open_conv(ggp_chat_local_info *chat)
    -{
    - PurpleConversation *conv;
    - int i;
    -
    - if (chat->conv != NULL)
    - return;
    -
    - conv = purple_serv_got_joined_chat(chat->gc, chat->local_id,
    - ggp_chat_get_name_from_id(chat->id));
    - chat->conv = PURPLE_CHAT_CONVERSATION(conv);
    - if (chat->previously_joined) {
    - purple_conversation_write_system_message(
    - PURPLE_CONVERSATION(chat->conv),
    - _("You have re-joined the chat"), 0);
    - }
    - chat->previously_joined = TRUE;
    -
    - purple_chat_conversation_clear_users(chat->conv);
    - for (i = 0; i < chat->participants_count; i++) {
    - purple_chat_conversation_add_user(chat->conv,
    - ggp_uin_to_str(chat->participants[i]), NULL,
    - PURPLE_CHAT_USER_NONE, FALSE);
    - }
    -}
    -
    -static ggp_chat_local_info * ggp_chat_get_local(PurpleConnection *gc,
    - int local_id)
    -{
    - ggp_chat_session_data *sdata = ggp_chat_get_sdata(gc);
    - int i;
    -
    - for (i = 0; i < sdata->chats_count; i++) {
    - if (sdata->chats[i].local_id == local_id)
    - return &sdata->chats[i];
    - }
    -
    - return NULL;
    -}
    -
    -void ggp_chat_got_event(PurpleConnection *gc, const struct gg_event *ev)
    -{
    - ggp_chat_session_data *sdata = ggp_chat_get_sdata(gc);
    - ggp_chat_local_info *chat;
    - uint32_t i;
    -
    - if (ev->type == GG_EVENT_CHAT_INFO) {
    - const struct gg_event_chat_info *eci = &ev->event.chat_info;
    - chat = ggp_chat_new(gc, eci->id);
    - for (i = 0; i < eci->participants_count; i++)
    - ggp_chat_joined(chat, eci->participants[i]);
    - } else if (ev->type == GG_EVENT_CHAT_INFO_GOT_ALL) {
    - GSList *it = sdata->pending_joins;
    - sdata->got_all_chats_info = TRUE;
    - while (it) {
    - uint64_t *id_p = it->data;
    - ggp_chat_join_id(gc, *id_p);
    - it = g_slist_next(it);
    - }
    - g_clear_slist(&sdata->pending_joins, g_free);
    - } else if (ev->type == GG_EVENT_CHAT_INFO_UPDATE) {
    - const struct gg_event_chat_info_update *eciu =
    - &ev->event.chat_info_update;
    - chat = ggp_chat_get(gc, eciu->id);
    - if (!chat) {
    - purple_debug_error("gg", "ggp_chat_got_event: "
    - "chat %" G_GUINT64_FORMAT " not found\n",
    - eciu->id);
    - return;
    - }
    - if (eciu->type == GG_CHAT_INFO_UPDATE_ENTERED)
    - ggp_chat_joined(chat, eciu->participant);
    - else if (eciu->type == GG_CHAT_INFO_UPDATE_EXITED)
    - ggp_chat_left(chat, eciu->participant);
    - else
    - purple_debug_warning("gg", "ggp_chat_got_event: "
    - "unknown update type - %d", eciu->type);
    - } else if (ev->type == GG_EVENT_CHAT_CREATED) {
    - PurpleAccount *account = purple_connection_get_account(gc);
    - PurpleContactInfo *info = PURPLE_CONTACT_INFO(account);
    - const struct gg_event_chat_created *ecc = &ev->event.chat_created;
    - uin_t me = ggp_str_to_uin(purple_contact_info_get_username(info));
    -
    - chat = ggp_chat_new(gc, ecc->id);
    - ggp_chat_joined(chat, me);
    - ggp_chat_open_conv(chat);
    - } else if (ev->type == GG_EVENT_CHAT_INVITE_ACK) {
    - /* ignore */
    - } else {
    - purple_debug_error("gg", "ggp_chat_got_event: unexpected event - %d",
    - ev->type);
    - }
    -}
    -
    -static int ggp_chat_participant_find(ggp_chat_local_info *chat, uin_t uin)
    -{
    - int i;
    - for (i = 0; i < chat->participants_count; i++)
    - if (chat->participants[i] == uin)
    - return i;
    - return -1;
    -}
    -
    -static void ggp_chat_joined(ggp_chat_local_info *chat, uin_t uin)
    -{
    - int idx = ggp_chat_participant_find(chat, uin);
    - if (idx >= 0) {
    - purple_debug_warning("gg", "ggp_chat_joined: "
    - "user %u is already present in chat %" G_GUINT64_FORMAT
    - "\n", uin, chat->id);
    - return;
    - }
    - chat->participants_count++;
    - chat->participants = g_realloc(chat->participants,
    - sizeof(uin) * chat->participants_count);
    - chat->participants[chat->participants_count - 1] = uin;
    -
    - if (!chat->conv)
    - return;
    - purple_chat_conversation_add_user(chat->conv,
    - ggp_uin_to_str(uin), NULL, PURPLE_CHAT_USER_NONE, TRUE);
    -}
    -
    -static void ggp_chat_left(ggp_chat_local_info *chat, uin_t uin)
    -{
    - PurpleAccount *account = NULL;
    - PurpleContactInfo *info = NULL;
    - uin_t me;
    - int idx = ggp_chat_participant_find(chat, uin);
    -
    - if (idx < 0) {
    - purple_debug_warning("gg", "ggp_chat_joined: "
    - "user %u isn't present in chat %" G_GUINT64_FORMAT "\n",
    - uin, chat->id);
    - return;
    - }
    - chat->participants[idx] =
    - chat->participants[chat->participants_count - 1];
    - chat->participants_count--;
    - chat->participants = g_realloc(chat->participants,
    - sizeof(uin) * chat->participants_count);
    -
    - if (chat->conv == NULL)
    - return;
    -
    - account = purple_connection_get_account(chat->gc);
    - info = PURPLE_CONTACT_INFO(account);
    - me = ggp_str_to_uin(purple_contact_info_get_username(info));
    -
    - if (me == uin) {
    - purple_conversation_write_system_message(
    - PURPLE_CONVERSATION(chat->conv),
    - _("You have left the chat"), 0);
    - purple_serv_got_chat_left(chat->gc, chat->local_id);
    - chat->conv = NULL;
    - chat->left = TRUE;
    - }
    - purple_chat_conversation_remove_user(chat->conv, ggp_uin_to_str(uin), NULL);
    -}
    -
    -GList *
    -ggp_chat_info(G_GNUC_UNUSED PurpleProtocolChat *protocol_chat,
    - G_GNUC_UNUSED PurpleConnection *gc)
    -{
    - GList *m = NULL;
    - PurpleProtocolChatEntry *pce;
    -
    - pce = g_new0(PurpleProtocolChatEntry, 1);
    - pce->label = _("_Conference identifier");
    - pce->identifier = "id";
    - pce->required = FALSE;
    - m = g_list_append(m, pce);
    -
    - return m;
    -}
    -
    -GHashTable *
    -ggp_chat_info_defaults(G_GNUC_UNUSED PurpleProtocolChat *protocol_chat,
    - G_GNUC_UNUSED PurpleConnection *gc,
    - const gchar *chat_name)
    -{
    - GHashTable *defaults;
    -
    - defaults = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free);
    -
    - if (chat_name != NULL && ggp_chat_get_id_from_name(chat_name) != 0)
    - g_hash_table_insert(defaults, "id", g_strdup(chat_name));
    -
    - return defaults;
    -}
    -
    -gchar *
    -ggp_chat_get_name(G_GNUC_UNUSED PurpleProtocolChat *protocol_chat,
    - GHashTable *components)
    -{
    - return g_strdup((gchar*)g_hash_table_lookup(components, "id"));
    -}
    -
    -static const gchar * ggp_chat_get_name_from_id(uint64_t id)
    -{
    - static gchar buff[30];
    - g_snprintf(buff, sizeof(buff), "%" G_GUINT64_FORMAT, id);
    - return buff;
    -}
    -
    -static uint64_t ggp_chat_get_id_from_name(const gchar * name)
    -{
    - uint64_t id;
    - gchar *endptr;
    -
    - if (name == NULL)
    - return 0;
    -
    - id = g_ascii_strtoull(name, &endptr, 10);
    -
    - if (*endptr != '\0' || id == G_MAXUINT64)
    - return 0;
    -
    - return id;
    -}
    -
    -void
    -ggp_chat_join(G_GNUC_UNUSED PurpleProtocolChat *protocol_chat,
    - PurpleConnection *gc, GHashTable *components)
    -{
    - ggp_chat_session_data *sdata = ggp_chat_get_sdata(gc);
    - GGPInfo *info = purple_connection_get_protocol_data(gc);
    - const gchar *id_cs;
    - gchar *id_s;
    - uint64_t id;
    -
    - id_cs = g_hash_table_lookup(components, "id");
    - id_s = g_strdup(id_cs);
    - if (id_s)
    - g_strstrip(id_s);
    - if (id_s == NULL || id_s[0] == '\0') {
    - g_free(id_s);
    - if (gg_chat_create(info->session) < 0) {
    - purple_debug_error("gg", "ggp_chat_join; "
    - "cannot create\n");
    - purple_serv_got_join_chat_failed(gc, components);
    - }
    - return;
    - }
    - id = ggp_chat_get_id_from_name(id_s);
    - g_free(id_s);
    -
    - if (!id) {
    - char *buff = g_strdup_printf(
    - _("%s is not a valid room identifier"), id_cs);
    - purple_notify_error(gc, _("Invalid Room Identifier"),
    - _("Invalid Room Identifier"), buff, NULL);
    - g_free(buff);
    - purple_serv_got_join_chat_failed(gc, components);
    - return;
    - }
    -
    - if (sdata->got_all_chats_info)
    - ggp_chat_join_id(gc, id);
    - else {
    - uint64_t *id_p = g_new(uint64_t, 1);
    - *id_p = id;
    - sdata->pending_joins = g_slist_append(sdata->pending_joins, id_p);
    - }
    -
    -}
    -
    -static void ggp_chat_join_id(PurpleConnection *gc, uint64_t id)
    -{
    - GHashTable *components;
    - PurpleProtocol *protocol = purple_connection_get_protocol(gc);
    -
    - ggp_chat_local_info *chat = ggp_chat_get(gc, id);
    -
    - if (chat && !chat->left) {
    - ggp_chat_open_conv(chat);
    - return;
    - }
    -
    - if (!chat) {
    - char *id_s = g_strdup_printf("%" G_GUINT64_FORMAT, id);
    - char *buff = g_strdup_printf(
    - _("%s is not a valid room identifier"), id_s);
    - g_free(id_s);
    - purple_notify_error(gc, _("Invalid Room Identifier"),
    - _("Invalid Room Identifier"), buff, NULL);
    - g_free(buff);
    - } else { /* if (chat->left) */
    - purple_notify_error(gc, _("Could not join chat room"),
    - _("Could not join chat room"),
    - _("You have to ask for invitation from another chat "
    - "participant"), NULL);
    - }
    -
    - components = ggp_chat_info_defaults(PURPLE_PROTOCOL_CHAT(protocol), gc,
    - ggp_chat_get_name_from_id(id));
    - purple_serv_got_join_chat_failed(gc, components);
    - g_hash_table_destroy(components);
    -}
    -
    -void
    -ggp_chat_leave(G_GNUC_UNUSED PurpleProtocolChat *protocol_chat,
    - PurpleConnection *gc, gint local_id)
    -{
    - PurpleAccount *account = NULL;
    - PurpleContactInfo *contact_info = NULL;
    - GGPInfo *info = purple_connection_get_protocol_data(gc);
    - ggp_chat_local_info *chat;
    - uin_t me;
    -
    - chat = ggp_chat_get_local(gc, local_id);
    - if (!chat) {
    - purple_debug_error("gg", "ggp_chat_leave: "
    - "chat %u doesn't exists\n", local_id);
    - return;
    - }
    -
    - if (gg_chat_leave(info->session, chat->id) < 0) {
    - purple_debug_error("gg", "ggp_chat_leave: "
    - "unable to leave chat %" G_GUINT64_FORMAT "\n",
    - chat->id);
    - }
    - chat->conv = NULL;
    -
    - account = purple_connection_get_account(chat->gc);
    - contact_info = PURPLE_CONTACT_INFO(account);
    - me = ggp_str_to_uin(purple_contact_info_get_username(contact_info));
    -
    - ggp_chat_left(chat, me);
    - chat->left = TRUE;
    -}
    -
    -void
    -ggp_chat_invite(G_GNUC_UNUSED PurpleProtocolChat *protocol_chat,
    - PurpleConnection *gc, gint local_id,
    - G_GNUC_UNUSED const gchar *message, const gchar *who)
    -{
    - GGPInfo *info = purple_connection_get_protocol_data(gc);
    - ggp_chat_local_info *chat;
    - uin_t invited;
    -
    - chat = ggp_chat_get_local(gc, local_id);
    - if (!chat) {
    - purple_debug_error("gg", "ggp_chat_invite: "
    - "chat %u doesn't exists\n", local_id);
    - return;
    - }
    -
    - invited = ggp_str_to_uin(who);
    - if (gg_chat_invite(info->session, chat->id, &invited, 1) < 0) {
    - purple_debug_error("gg", "ggp_chat_invite: "
    - "unable to invite %s to chat %" G_GUINT64_FORMAT "\n",
    - who, chat->id);
    - }
    -}
    -
    -gint
    -ggp_chat_send(G_GNUC_UNUSED PurpleProtocolChat *protocol_chat,
    - PurpleConnection *gc, gint local_id,
    - G_GNUC_UNUSED PurpleConversation *conversation,
    - PurpleMessage *msg)
    -{
    - PurpleAccount *account = NULL;
    - PurpleContactInfo *contact_info = NULL;
    - GGPInfo *info = purple_connection_get_protocol_data(gc);
    - GDateTime *dt = NULL;
    - PurpleConversation *conv;
    - PurpleConversationManager *manager;
    - ggp_chat_local_info *chat;
    - gboolean succ = TRUE;
    - const gchar *me;
    - gchar *gg_msg;
    -
    - chat = ggp_chat_get_local(gc, local_id);
    - if (!chat) {
    - purple_debug_error("gg", "ggp_chat_send: "
    - "chat %u doesn't exists\n", local_id);
    - return -1;
    - }
    -
    - manager = purple_conversation_manager_get_default();
    - conv = purple_conversation_manager_find_chat(manager,
    - purple_connection_get_account(gc),
    - ggp_chat_get_name_from_id(chat->id));
    -
    - gg_msg = ggp_message_format_to_gg(conv,
    - purple_message_get_contents(msg));
    -
    - if (gg_chat_send_message(info->session, chat->id, gg_msg, TRUE) < 0)
    - succ = FALSE;
    - g_free(gg_msg);
    -
    - account = purple_connection_get_account(gc);
    - contact_info = PURPLE_CONTACT_INFO(account);
    - me = purple_contact_info_get_username(contact_info);
    - dt = purple_message_get_timestamp(msg);
    - purple_serv_got_chat_in(gc, chat->local_id, me,
    - purple_message_get_flags(msg),
    - purple_message_get_contents(msg),
    - (time_t)g_date_time_to_unix(dt));
    -
    - return succ ? 0 : -1;
    -}
    -
    -void ggp_chat_got_message(PurpleConnection *gc, uint64_t chat_id,
    - const char *message, time_t time, uin_t who)
    -{
    - PurpleAccount *account = NULL;
    - PurpleContactInfo *info = NULL;
    - ggp_chat_local_info *chat;
    - uin_t me;
    -
    - account = purple_connection_get_account(gc);
    - info = PURPLE_CONTACT_INFO(account);
    - me = ggp_str_to_uin(purple_contact_info_get_username(info));
    -
    - chat = ggp_chat_get(gc, chat_id);
    - if (!chat) {
    - purple_debug_error("gg", "ggp_chat_got_message: "
    - "chat %" G_GUINT64_FORMAT " doesn't exists\n", chat_id);
    - return;
    - }
    -
    - ggp_chat_open_conv(chat);
    - if (who == me) {
    - GDateTime *dt = NULL;
    - PurpleMessage *pmsg;
    - const char *username = NULL;
    -
    - username = purple_contact_info_get_name_for_display(info);
    -
    - pmsg = purple_message_new_outgoing(username, ggp_uin_to_str(who),
    - message, 0);
    -
    - dt = g_date_time_new_from_unix_local((gint64)time);
    - purple_message_set_timestamp(pmsg, dt);
    - g_date_time_unref(dt);
    -
    - purple_conversation_write_message(
    - PURPLE_CONVERSATION(chat->conv), pmsg);
    - } else {
    - purple_serv_got_chat_in(gc, chat->local_id, ggp_uin_to_str(who),
    - PURPLE_MESSAGE_RECV, message, time);
    - }
    -}
    -
    -static gboolean ggp_chat_roomlist_get_list_finish(gpointer roomlist)
    -{
    - purple_roomlist_set_in_progress(PURPLE_ROOMLIST(roomlist), FALSE);
    - g_object_unref(roomlist);
    -
    - return FALSE;
    -}
    -
    -PurpleRoomlist *
    -ggp_chat_roomlist_get_list(G_GNUC_UNUSED PurpleProtocolRoomlist *protocol_roomlist,
    - PurpleConnection *gc)
    -{
    - ggp_chat_session_data *sdata = ggp_chat_get_sdata(gc);
    - PurpleRoomlist *roomlist;
    - int i;
    -
    - purple_debug_info("gg", "ggp_chat_roomlist_get_list\n");
    -
    - roomlist = purple_roomlist_new(purple_connection_get_account(gc));
    -
    - for (i = sdata->chats_count - 1; i >= 0 ; i--) {
    - PurpleRoomlistRoom *room;
    - ggp_chat_local_info *chat = &sdata->chats[i];
    - const gchar *name;
    - GDateTime *date = NULL;
    - const gchar *status;
    - int count = chat->participants_count;
    -
    - date = g_date_time_new_from_unix_local((uint32_t)(chat->id >> 32));
    -
    - if (chat->conv) {
    - status = _("Joined");
    - } else if (chat->left) {
    - /* Translators: For Gadu-Gadu, this is one possible status for a
    - chat room. It means you had previously joined the chat room but
    - you have since left it. You cannot rejoin without another
    - invitation. */
    - status = _("Chat left");
    - } else {
    - status = _("Can join chat");
    - count--;
    - }
    -
    - name = ggp_chat_get_name_from_id(chat->id);
    - room = purple_roomlist_room_new(name, NULL);
    - purple_roomlist_room_set_user_count(room, (guint)count);
    - purple_roomlist_room_add_field(room, "id", g_strdup(name));
    - purple_roomlist_room_add_field(room, "date",
    - g_date_time_format(date, "%c"));
    - purple_roomlist_room_add_field(room, "status", g_strdup(status));
    - purple_roomlist_room_add(roomlist, room);
    - g_object_unref(room);
    - g_date_time_unref(date);
    - }
    -
    - /* TODO
    - * purple_roomlist_set_in_progress(roomlist, FALSE);
    - */
    - g_object_ref(roomlist);
    - g_timeout_add(1, ggp_chat_roomlist_get_list_finish, roomlist);
    - return roomlist;
    -}
    --- a/libpurple/protocols/gg/chat.h Tue Apr 09 21:50:31 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,59 +0,0 @@
    -/* 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.
    - *
    - * Component written by Tomek Wasilczyk (http://www.wasilczyk.pl).
    - *
    - * This file is dual-licensed under the GPL2+ and the X11 (MIT) licences.
    - * As a recipient of this file you may choose, which license to receive the
    - * code under. As a contributor, you have to ensure the new code is
    - * compatible with both.
    - *
    - * 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
    - */
    -
    -#ifndef PURPLE_GG_CHAT_H
    -#define PURPLE_GG_CHAT_H
    -
    -#include <purple.h>
    -#include <libgadu.h>
    -
    -typedef struct _ggp_chat_session_data ggp_chat_session_data;
    -
    -#include "gg.h"
    -
    -void ggp_chat_setup(PurpleConnection *gc);
    -void ggp_chat_cleanup(PurpleConnection *gc);
    -
    -void ggp_chat_got_event(PurpleConnection *gc, const struct gg_event *ev);
    -
    -GList * ggp_chat_info(PurpleProtocolChat *protocol_chat, PurpleConnection *gc);
    -GHashTable * ggp_chat_info_defaults(PurpleProtocolChat *protocol_chat, PurpleConnection *gc,
    - const char *chat_name);
    -char * ggp_chat_get_name(PurpleProtocolChat *protocol_chat, GHashTable *components);
    -void ggp_chat_join(PurpleProtocolChat *protocol_chat, PurpleConnection *gc, GHashTable *components);
    -void ggp_chat_leave(PurpleProtocolChat *protocol_chat, PurpleConnection *gc, int local_id);
    -void ggp_chat_invite(PurpleProtocolChat *protocol_chat, PurpleConnection *gc, int local_id, const char *message,
    - const char *who);
    -int ggp_chat_send(PurpleProtocolChat *protocol_chat, PurpleConnection *gc, int local_id, PurpleConversation *conversation, PurpleMessage *msg);
    -
    -void ggp_chat_got_message(PurpleConnection *gc, uint64_t chat_id,
    - const char *message, time_t time, uin_t who);
    -
    -PurpleRoomlist * ggp_chat_roomlist_get_list(PurpleProtocolRoomlist *protocol_roomlist, PurpleConnection *gc);
    -
    -#endif /* PURPLE_GG_CHAT_H */
    --- a/libpurple/protocols/gg/edisc.c Tue Apr 09 21:50:31 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,1344 +0,0 @@
    -/* 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.
    - *
    - * Component written by Tomek Wasilczyk (http://www.wasilczyk.pl).
    - *
    - * This file is dual-licensed under the GPL2+ and the X11 (MIT) licences.
    - * As a recipient of this file you may choose, which license to receive the
    - * code under. As a contributor, you have to ensure the new code is
    - * compatible with both.
    - *
    - * 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 "edisc.h"
    -
    -#include "gg.h"
    -#include "libgaduw.h"
    -#include "utils.h"
    -
    -#include <json-glib/json-glib.h>
    -
    -#include <glib/gi18n-lib.h>
    -
    -#include <purple.h>
    -
    -#define GGP_EDISC_OS "WINNT x86-msvc"
    -#define GGP_EDISC_TYPE "desktop"
    -#define GGP_EDISC_API "6"
    -
    -#define GGP_EDISC_RESPONSE_MAX 10240
    -#define GGP_EDISC_FNAME_ALLOWED "1234567890" \
    - "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" \
    - " [](){}-+=_;'<>,.&$!"
    -
    -typedef struct _ggp_edisc_xfer ggp_edisc_xfer;
    -
    -struct _ggp_edisc_session_data
    -{
    - GHashTable *xfers_initialized;
    - GHashTable *xfers_history;
    -
    - SoupSession *session;
    - gchar *security_token;
    -
    - SoupMessage *auth_request;
    - gboolean auth_done;
    - GSList *auth_pending;
    -};
    -
    -struct _GGPXfer
    -{
    - PurpleXfer parent;
    - GCancellable *cancellable;
    -
    - gchar *filename;
    - gchar *ticket_id;
    -
    - gboolean allowed, ready;
    -
    - PurpleConnection *gc;
    - SoupMessage *msg;
    - gint handler;
    -};
    -
    -typedef enum
    -{
    - GGP_EDISC_XFER_ACK_STATUS_UNKNOWN,
    - GGP_EDISC_XFER_ACK_STATUS_ALLOWED,
    - GGP_EDISC_XFER_ACK_STATUS_REJECTED
    -} ggp_edisc_xfer_ack_status;
    -
    -typedef void (*ggp_ggdrive_auth_cb)(PurpleConnection *gc, gboolean success,
    - gpointer user_data);
    -
    -/*******************************************************************************
    - * Setting up.
    - ******************************************************************************/
    -
    -static inline ggp_edisc_session_data *
    -ggp_edisc_get_sdata(PurpleConnection *gc)
    -{
    - GGPInfo *accdata;
    -
    - PURPLE_ASSERT_CONNECTION_IS_VALID(gc);
    -
    - accdata = purple_connection_get_protocol_data(gc);
    - g_return_val_if_fail(accdata != NULL, NULL);
    -
    - return accdata->edisc_data;
    -}
    -
    -void
    -ggp_edisc_setup(PurpleConnection *gc, GProxyResolver *resolver)
    -{
    - GGPInfo *accdata = purple_connection_get_protocol_data(gc);
    - ggp_edisc_session_data *sdata = g_new0(ggp_edisc_session_data, 1);
    -
    - accdata->edisc_data = sdata;
    -
    - sdata->session = soup_session_new_with_options("proxy-resolver", resolver,
    - NULL);
    - soup_session_add_feature_by_type(sdata->session, SOUP_TYPE_COOKIE_JAR);
    - sdata->xfers_initialized = g_hash_table_new(g_str_hash, g_str_equal);
    - sdata->xfers_history = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
    -}
    -
    -void ggp_edisc_cleanup(PurpleConnection *gc)
    -{
    - ggp_edisc_session_data *sdata = ggp_edisc_get_sdata(gc);
    -
    - g_return_if_fail(sdata != NULL);
    -
    - soup_session_abort(sdata->session);
    - g_slist_free_full(sdata->auth_pending, g_free);
    - g_free(sdata->security_token);
    -
    - g_object_unref(sdata->session);
    - g_hash_table_destroy(sdata->xfers_initialized);
    - g_hash_table_destroy(sdata->xfers_history);
    -
    - g_free(sdata);
    -}
    -
    -/*******************************************************************************
    - * Misc.
    - ******************************************************************************/
    -
    -static void
    -ggp_edisc_set_defaults(SoupMessage *msg)
    -{
    - SoupMessageHeaders *headers = soup_message_get_request_headers(msg);
    -
    - // purple_http_request_set_max_len(msg, GGP_EDISC_RESPONSE_MAX);
    - soup_message_headers_replace(headers, "X-gged-api-version",
    - GGP_EDISC_API);
    -
    - /* optional fields */
    - soup_message_headers_replace(
    - headers, "User-Agent",
    - "Mozilla/5.0 (Windows NT 6.1; rv:11.0) Gecko/20120613 "
    - "GG/11.0.0.8169 (WINNT_x86-msvc; pl; beta; standard)");
    - soup_message_headers_replace(
    - headers, "Accept",
    - "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
    - soup_message_headers_replace(headers, "Accept-Language",
    - "pl,en-us;q=0.7,en;q=0.3");
    - /* soup_message_headers_replace(headers, "Accept-Encoding",
    - * "gzip, deflate"); */
    - soup_message_headers_replace(headers, "Accept-Charset",
    - "ISO-8859-2,utf-8;q=0.7,*;q=0.7");
    - soup_message_headers_replace(headers, "Connection", "keep-alive");
    - soup_message_headers_replace(
    - headers, "Content-Type",
    - "application/x-www-form-urlencoded; charset=UTF-8");
    -}
    -
    -static int ggp_edisc_parse_error(const gchar *data)
    -{
    - JsonParser *parser;
    - JsonObject *result;
    - int error_id;
    -
    - parser = ggp_json_parse(data);
    - result = json_node_get_object(json_parser_get_root(parser));
    - result = json_object_get_object_member(result, "result");
    - error_id = json_object_get_int_member(result, "appStatus");
    - purple_debug_info("gg", "edisc error: %s (%d)\n",
    - json_object_get_string_member(result, "errorMsg"),
    - error_id);
    - g_object_unref(parser);
    -
    - return error_id;
    -}
    -
    -static ggp_edisc_xfer_ack_status
    -ggp_edisc_xfer_parse_ack_status(const gchar *str)
    -{
    - g_return_val_if_fail(str != NULL, GGP_EDISC_XFER_ACK_STATUS_UNKNOWN);
    -
    - if (g_strcmp0("unknown", str) == 0) {
    - return GGP_EDISC_XFER_ACK_STATUS_UNKNOWN;
    - }
    - if (g_strcmp0("allowed", str) == 0) {
    - return GGP_EDISC_XFER_ACK_STATUS_ALLOWED;
    - }
    - if (g_strcmp0("rejected", str) == 0) {
    - return GGP_EDISC_XFER_ACK_STATUS_REJECTED;
    - }
    -
    - purple_debug_warning(
    - "gg", "ggp_edisc_xfer_parse_ack_status: unknown status (%s)", str);
    - return GGP_EDISC_XFER_ACK_STATUS_UNKNOWN;
    -}
    -
    -/*******************************************************************************
    - * General xfer functions.
    - ******************************************************************************/
    -
    -static const gchar *
    -ggp_edisc_xfer_ticket_url(const gchar *ticket_id)
    -{
    - static gchar ticket_url[150];
    -
    - g_snprintf(ticket_url, sizeof(ticket_url),
    - "https://drive.mpa.gg.pl/send_ticket/%s", ticket_id);
    -
    - return ticket_url;
    -}
    -
    -static void ggp_edisc_xfer_error(PurpleXfer *xfer, const gchar *msg)
    -{
    - if (purple_xfer_is_cancelled(xfer))
    - g_return_if_reached();
    - purple_xfer_set_status(xfer, PURPLE_XFER_STATUS_CANCEL_REMOTE);
    - purple_xfer_conversation_write(xfer, msg, TRUE);
    - purple_xfer_error(
    - purple_xfer_get_xfer_type(xfer),
    - purple_xfer_get_account(xfer),
    - purple_xfer_get_remote_user(xfer),
    - msg);
    - purple_xfer_end(xfer);
    -}
    -
    -/*******************************************************************************
    - * Authentication.
    - ******************************************************************************/
    -
    -typedef struct _ggp_edisc_auth_data {
    - ggp_ggdrive_auth_cb cb;
    - gpointer user_data;
    -} ggp_edisc_auth_data;
    -
    -static void
    -ggp_ggdrive_auth_results(PurpleConnection *gc, gboolean success)
    -{
    - ggp_edisc_session_data *sdata = ggp_edisc_get_sdata(gc);
    - GSList *it;
    -
    - purple_debug_info("gg", "ggp_ggdrive_auth_results(gc=%p): %d", gc, success);
    -
    - g_return_if_fail(sdata != NULL);
    -
    - for (it = sdata->auth_pending; it; it = g_slist_delete_link(it, it)) {
    - ggp_edisc_auth_data *auth = it->data;
    -
    - auth->cb(gc, success, auth->user_data);
    - g_free(auth);
    - }
    - sdata->auth_pending = NULL;
    - sdata->auth_done = TRUE;
    -}
    -
    -static void
    -ggp_ggdrive_auth_done(GObject *source, GAsyncResult *async_result,
    - gpointer data)
    -{
    - PurpleConnection *gc = data;
    - ggp_edisc_session_data *sdata = ggp_edisc_get_sdata(gc);
    - GBytes *response_body = NULL;
    - const char *buffer = NULL;
    - gsize size = 0;
    - SoupStatus status_code;
    - JsonParser *parser;
    - JsonObject *result;
    - int status = -1;
    - GError *error = NULL;
    -
    - g_return_if_fail(sdata != NULL);
    -
    - status_code = soup_message_get_status(sdata->auth_request);
    - if (!SOUP_STATUS_IS_SUCCESSFUL(status_code)) {
    - purple_debug_misc("gg",
    - "ggp_ggdrive_auth_done: authentication failed due to "
    - "unsuccessful request (code = %d)",
    - status_code);
    - g_clear_object(&sdata->auth_request);
    - ggp_ggdrive_auth_results(gc, FALSE);
    - return;
    - }
    -
    - response_body = soup_session_send_and_read_finish(SOUP_SESSION(source),
    - async_result, &error);
    - if(response_body == NULL) {
    - purple_debug_misc("gg",
    - "ggp_ggdrive_auth_done: authentication failed due to "
    - "unsuccessful request (%s)",
    - error->message);
    - g_error_free(error);
    - g_clear_object(&sdata->auth_request);
    - ggp_ggdrive_auth_results(gc, FALSE);
    - return;
    - }
    -
    - buffer = g_bytes_get_data(response_body, &size);
    - parser = ggp_json_parse(buffer);
    - result = json_node_get_object(json_parser_get_root(parser));
    - result = json_object_get_object_member(result, "result");
    - if (json_object_has_member(result, "status"))
    - status = json_object_get_int_member(result, "status");
    - g_object_unref(parser);
    -
    - if (status != 0) {
    - purple_debug_misc("gg",
    - "ggp_ggdrive_auth_done: authentication failed due to "
    - "bad result (status=%d)",
    - status);
    - if (purple_debug_is_verbose()) {
    - purple_debug_misc("gg", "ggp_ggdrive_auth_done: result = %.*s",
    - (int)size, buffer);
    - }
    - g_bytes_unref(response_body);
    - g_clear_object(&sdata->auth_request);
    - ggp_ggdrive_auth_results(gc, FALSE);
    - return;
    - }
    -
    - sdata->security_token = g_strdup(soup_message_headers_get_one(
    - soup_message_get_response_headers(sdata->auth_request),
    - "X-gged-security-token"));
    - if (!sdata->security_token) {
    - purple_debug_misc("gg", "ggp_ggdrive_auth_done: authentication failed "
    - "due to missing security token header");
    - g_bytes_unref(response_body);
    - g_clear_object(&sdata->auth_request);
    - ggp_ggdrive_auth_results(gc, FALSE);
    - return;
    - }
    -
    - if (purple_debug_is_unsafe()) {
    - purple_debug_misc("gg", "ggp_ggdrive_auth_done: security_token=%s",
    - sdata->security_token);
    - }
    -
    - g_clear_pointer(&response_body, g_bytes_unref);
    - g_clear_object(&sdata->auth_request);
    -
    - ggp_ggdrive_auth_results(gc, TRUE);
    -}
    -
    -static void
    -ggp_ggdrive_auth(PurpleConnection *gc, ggp_ggdrive_auth_cb cb,
    - gpointer user_data)
    -{
    - GGPInfo *accdata = purple_connection_get_protocol_data(gc);
    - ggp_edisc_session_data *sdata = ggp_edisc_get_sdata(gc);
    - ggp_edisc_auth_data *auth;
    - const gchar *imtoken;
    - gchar *metadata;
    - gchar *tmp;
    - SoupMessage *msg;
    - SoupMessageHeaders *headers;
    -
    - g_return_if_fail(sdata != NULL);
    -
    - imtoken = ggp_get_imtoken(gc);
    - if (!imtoken) {
    - cb(gc, FALSE, user_data);
    - return;
    - }
    -
    - if (sdata->auth_done) {
    - cb(gc, sdata->security_token != NULL, user_data);
    - return;
    - }
    -
    - auth = g_new0(ggp_edisc_auth_data, 1);
    - auth->cb = cb;
    - auth->user_data = user_data;
    - sdata->auth_pending = g_slist_prepend(sdata->auth_pending, auth);
    -
    - if (sdata->auth_request) {
    - return;
    - }
    -
    - purple_debug_info("gg", "ggp_ggdrive_auth(gc=%p)", gc);
    -
    - msg = soup_message_new("PUT", "https://drive.mpa.gg.pl/signin");
    - ggp_edisc_set_defaults(msg);
    - headers = soup_message_get_request_headers(msg);
    -
    - metadata = g_strdup_printf("{"
    - "\"id\": \"%032x\", "
    - "\"name\": \"%s\", "
    - "\"os_version\": \"" GGP_EDISC_OS "\", "
    - "\"client_version\": \"%s\", "
    - "\"type\": \"" GGP_EDISC_TYPE "\"}",
    - g_random_int_range(1, 1 << 16),
    - g_get_host_name(), ggp_libgaduw_version(gc));
    -
    - tmp = g_strdup_printf("IMToken %s", imtoken);
    - soup_message_headers_replace(headers, "Authorization", tmp);
    - g_free(tmp);
    - tmp = g_strdup_printf("gg/pl:%u", accdata->session->uin);
    - soup_message_headers_replace(headers, "X-gged-user", tmp);
    - g_free(tmp);
    - soup_message_headers_replace(headers, "X-gged-client-metadata", metadata);
    - g_free(metadata);
    -
    - sdata->auth_request = msg;
    - soup_session_send_and_read_async(sdata->session, msg, G_PRIORITY_DEFAULT,
    - NULL, ggp_ggdrive_auth_done, gc);
    -}
    -
    -static void
    -ggp_edisc_xfer_send_ticket_changed(G_GNUC_UNUSED PurpleConnection *gc,
    - PurpleXfer *xfer, gboolean is_allowed)
    -{
    - GGPXfer *edisc_xfer = GGP_XFER(xfer);
    - if (!edisc_xfer) {
    - purple_debug_error(
    - "gg",
    - "ggp_edisc_event_ticket_changed: transfer %p already free'd",
    - xfer);
    - return;
    - }
    -
    - if (!is_allowed) {
    - purple_debug_info(
    - "gg", "ggp_edisc_event_ticket_changed: transfer %p rejected",
    - xfer);
    - purple_xfer_cancel_remote(xfer);
    - return;
    - }
    -
    - if (edisc_xfer->allowed) {
    - purple_debug_misc(
    - "gg",
    - "ggp_edisc_event_ticket_changed: transfer %p already allowed",
    - xfer);
    - return;
    - }
    - edisc_xfer->allowed = TRUE;
    -
    - purple_xfer_start(xfer, -1, NULL, 0);
    -}
    -
    -/*******************************************************************************
    - * Sending a file.
    - ******************************************************************************/
    -
    -gboolean
    -ggp_edisc_xfer_can_receive_file(G_GNUC_UNUSED PurpleProtocolXfer *prplxfer,
    - PurpleConnection *gc, const char *who)
    -{
    - PurpleBuddy *buddy;
    -
    - g_return_val_if_fail(gc != NULL, FALSE);
    - g_return_val_if_fail(who != NULL, FALSE);
    -
    - buddy = purple_blist_find_buddy(purple_connection_get_account(gc), who);
    - if (buddy == NULL) {
    - return FALSE;
    - }
    -
    - /* TODO: check, if this buddy have us on his list */
    -
    - return PURPLE_BUDDY_IS_ONLINE(buddy);
    -}
    -
    -static void
    -ggp_edisc_xfer_send_init_ticket_created(GObject *source, GAsyncResult *result,
    - gpointer data)
    -{
    - PurpleXfer *xfer = data;
    - GGPXfer *edisc_xfer = GGP_XFER(xfer);
    - ggp_edisc_session_data *sdata = ggp_edisc_get_sdata(edisc_xfer->gc);
    - GBytes *response_body = NULL;
    - const char *buffer = NULL;
    - gsize size = 0;
    - ggp_edisc_xfer_ack_status ack_status;
    - JsonParser *parser;
    - JsonObject *ticket;
    - GError *error = NULL;
    -
    - if (purple_xfer_is_cancelled(xfer))
    - return;
    -
    - g_return_if_fail(sdata != NULL);
    -
    - response_body = soup_session_send_and_read_finish(SOUP_SESSION(source),
    - result, &error);
    - if(response_body == NULL) {
    - purple_debug_error("gg",
    - "ggp_edisc_xfer_send_init_ticket_created: failed "
    - "to send file: %s", error->message);
    - g_clear_object(&edisc_xfer->msg);
    - ggp_edisc_xfer_error(xfer, _("Unable to send file"));
    - g_error_free(error);
    - return;
    - }
    -
    - buffer = g_bytes_get_data(response_body, &size);
    -
    - if(!SOUP_STATUS_IS_SUCCESSFUL(soup_message_get_status(edisc_xfer->msg))) {
    - int error_id = ggp_edisc_parse_error(buffer);
    -
    - g_bytes_unref(response_body);
    - g_clear_object(&edisc_xfer->msg);
    -
    - if(error_id == 206) {
    - /* recipient not logged in */
    - ggp_edisc_xfer_error(xfer, _("Recipient not logged in"));
    - } else if(error_id == 207) {
    - /* bad sender recipient relation */
    - ggp_edisc_xfer_error(xfer,
    - _("You aren't on the recipient's buddy list"));
    - } else {
    - ggp_edisc_xfer_error(xfer, _("Unable to send file"));
    - }
    - return;
    - }
    -
    - parser = ggp_json_parse(buffer);
    - ticket = json_node_get_object(json_parser_get_root(parser));
    - ticket = json_object_get_object_member(ticket, "result");
    - ticket = json_object_get_object_member(ticket, "send_ticket");
    - edisc_xfer->ticket_id = g_strdup(json_object_get_string_member(
    - ticket, "id"));
    - ack_status = ggp_edisc_xfer_parse_ack_status(
    - json_object_get_string_member(ticket, "ack_status"));
    - /* send_mode: "normal", "publink" (for legacy clients) */
    -
    - g_object_unref(parser);
    - g_bytes_unref(response_body);
    - g_clear_object(&edisc_xfer->msg);
    -
    - if (edisc_xfer->ticket_id == NULL) {
    - purple_debug_error("gg",
    - "ggp_edisc_xfer_send_init_ticket_created: "
    - "couldn't get ticket id\n");
    - return;
    - }
    -
    - purple_debug_info("gg", "ggp_edisc_xfer_send_init_ticket_created: "
    - "ticket \"%s\" created\n", edisc_xfer->ticket_id);
    -
    - g_hash_table_insert(sdata->xfers_initialized, edisc_xfer->ticket_id, xfer);
    - g_hash_table_add(sdata->xfers_history, g_strdup(edisc_xfer->ticket_id));
    -
    - if (ack_status != GGP_EDISC_XFER_ACK_STATUS_UNKNOWN)
    - ggp_edisc_xfer_send_ticket_changed(edisc_xfer->gc, xfer,
    - ack_status == GGP_EDISC_XFER_ACK_STATUS_ALLOWED);
    -}
    -
    -static void
    -ggp_edisc_xfer_send_init_authenticated(PurpleConnection *gc, gboolean success,
    - gpointer _xfer)
    -{
    - ggp_edisc_session_data *sdata = ggp_edisc_get_sdata(gc);
    - SoupMessage *msg;
    - PurpleXfer *xfer = _xfer;
    - GGPXfer *edisc_xfer = GGP_XFER(xfer);
    - gchar *data;
    - GBytes *body = NULL;
    -
    - if (purple_xfer_is_cancelled(xfer)) {
    - return;
    - }
    -
    - if (!success) {
    - ggp_edisc_xfer_error(xfer, _("Authentication failed"));
    - return;
    - }
    -
    - g_return_if_fail(sdata != NULL);
    -
    - msg = soup_message_new("PUT", "https://drive.mpa.gg.pl/send_ticket");
    - ggp_edisc_set_defaults(msg);
    -
    - soup_message_headers_replace(soup_message_get_request_headers(msg),
    - "X-gged-security-token",
    - sdata->security_token);
    -
    - data = g_strdup_printf("{\"send_ticket\":{"
    - "\"recipient\":\"%s\","
    - "\"file_name\":\"%s\","
    - "\"file_size\":\"%u\""
    - "}}",
    - purple_xfer_get_remote_user(xfer),
    - edisc_xfer->filename,
    - (int)purple_xfer_get_size(xfer));
    - body = g_bytes_new_take(data, strlen(data));
    - soup_message_set_request_body_from_bytes(msg,
    - "application/x-www-form-urlencoded; charset=UTF-8",
    - body);
    - g_bytes_unref(body);
    -
    - edisc_xfer->msg = msg;
    - soup_session_send_and_read_async(sdata->session, msg, G_PRIORITY_DEFAULT,
    - edisc_xfer->cancellable,
    - ggp_edisc_xfer_send_init_ticket_created,
    - xfer);
    -}
    -
    -static void
    -ggp_edisc_xfer_send_init(PurpleXfer *xfer)
    -{
    - GGPXfer *edisc_xfer = GGP_XFER(xfer);
    -
    - purple_xfer_set_status(xfer, PURPLE_XFER_STATUS_NOT_STARTED);
    -
    - edisc_xfer->filename = g_strdup(purple_xfer_get_filename(xfer));
    - g_strcanon(edisc_xfer->filename, GGP_EDISC_FNAME_ALLOWED, '_');
    -
    - ggp_ggdrive_auth(edisc_xfer->gc, ggp_edisc_xfer_send_init_authenticated,
    - xfer);
    -}
    -
    -static void
    -ggp_edisc_xfer_send_done(GObject *source, GAsyncResult *async_result,
    - gpointer data)
    -{
    - PurpleXfer *xfer = data;
    - GGPXfer *edisc_xfer = GGP_XFER(xfer);
    - GBytes *response_body = NULL;
    - JsonParser *parser = NULL;
    - JsonObject *result = NULL;
    - int result_status = -1;
    - GError *error = NULL;
    -
    - if(purple_xfer_is_cancelled(xfer)) {
    - return;
    - }
    -
    - g_return_if_fail(edisc_xfer != NULL);
    -
    - if(!SOUP_STATUS_IS_SUCCESSFUL(soup_message_get_status(edisc_xfer->msg))) {
    - g_clear_object(&edisc_xfer->msg);
    - ggp_edisc_xfer_error(xfer, _("Error while sending a file"));
    - return;
    - }
    -
    - response_body = soup_session_send_and_read_finish(SOUP_SESSION(source),
    - async_result, &error);
    - if(response_body == NULL) {
    - g_clear_object(&edisc_xfer->msg);
    - ggp_edisc_xfer_error(xfer, _("Error while sending a file"));
    - g_error_free(error);
    - return;
    - }
    -
    - parser = ggp_json_parse(g_bytes_get_data(response_body, NULL));
    - result = json_node_get_object(json_parser_get_root(parser));
    - result = json_object_get_object_member(result, "result");
    - if(json_object_has_member(result, "status")) {
    - result_status = json_object_get_int_member(result, "status");
    - }
    - g_object_unref(parser);
    - g_clear_pointer(&response_body, g_bytes_unref);
    - g_clear_object(&edisc_xfer->msg);
    -
    - if(result_status == 0) {
    - purple_xfer_set_completed(xfer, TRUE);
    - purple_xfer_end(xfer);
    - } else {
    - ggp_edisc_xfer_error(xfer, _("Error while sending a file"));
    - }
    -}
    -
    -static void
    -ggp_edisc_xfer_send_start_msg_cb(SoupMessage *msg, gpointer data) {
    - PurpleXfer *xfer = data;
    - GInputStream *stream = NULL;
    - /* TODO: Actually fill in stream with something. */
    - soup_message_set_request_body(msg, NULL, stream,
    - purple_xfer_get_size(xfer));
    -}
    -
    -static void ggp_edisc_xfer_send_start(PurpleXfer *xfer)
    -{
    - ggp_edisc_session_data *sdata;
    - GGPXfer *edisc_xfer;
    - gchar *upload_url, *filename_e;
    - SoupMessage *msg;
    - SoupMessageHeaders *headers;
    -
    - g_return_if_fail(xfer != NULL);
    - edisc_xfer = GGP_XFER(xfer);
    - g_return_if_fail(edisc_xfer != NULL);
    - sdata = ggp_edisc_get_sdata(edisc_xfer->gc);
    - g_return_if_fail(sdata != NULL);
    -
    - filename_e = purple_strreplace(edisc_xfer->filename, " ", "%20");
    - upload_url = g_strdup_printf("https://drive.mpa.gg.pl/me/file/outbox/"
    - "%s%%2C%s", edisc_xfer->ticket_id, filename_e);
    - g_free(filename_e);
    - msg = soup_message_new("PUT", upload_url);
    - g_free(upload_url);
    -
    - ggp_edisc_set_defaults(msg);
    -
    - headers = soup_message_get_request_headers(msg);
    - soup_message_headers_replace(headers, "X-gged-local-revision", "0");
    - soup_message_headers_replace(headers, "X-gged-security-token",
    - sdata->security_token);
    - soup_message_headers_replace(headers, "X-gged-metadata",
    - "{\"node_type\": \"file\"}");
    -
    - soup_message_headers_set_content_length(headers,
    - purple_xfer_get_size(xfer));
    - edisc_xfer->msg = msg;
    -
    - g_signal_connect(msg, "starting",
    - G_CALLBACK(ggp_edisc_xfer_send_start_msg_cb), xfer);
    - g_signal_connect(msg, "restarted",
    - G_CALLBACK(ggp_edisc_xfer_send_start_msg_cb), xfer);
    - soup_session_send_and_read_async(sdata->session, msg, G_PRIORITY_DEFAULT,
    - edisc_xfer->cancellable,
    - ggp_edisc_xfer_send_done, xfer);
    -}
    -
    -PurpleXfer *
    -ggp_edisc_xfer_send_new(G_GNUC_UNUSED PurpleProtocolXfer *prplxfer,
    - PurpleConnection *gc, const char *who)
    -{
    - GGPXfer *xfer;
    -
    - g_return_val_if_fail(gc != NULL, NULL);
    - g_return_val_if_fail(who != NULL, NULL);
    -
    - xfer = g_object_new(
    - GGP_TYPE_XFER,
    - "account", purple_connection_get_account(gc),
    - "type", PURPLE_XFER_TYPE_SEND,
    - "remote-user", who,
    - NULL
    - );
    -
    - xfer->gc = gc;
    -
    - return PURPLE_XFER(xfer);
    -}
    -
    -void ggp_edisc_xfer_send_file(PurpleProtocolXfer *prplxfer, PurpleConnection *gc, const char *who,
    - const char *filename)
    -{
    - PurpleXfer *xfer;
    -
    - g_return_if_fail(gc != NULL);
    - g_return_if_fail(who != NULL);
    -
    - /* Nothing interesting here, this code is common among protocols.
    - * See ggp_edisc_xfer_send_new. */
    -
    - xfer = ggp_edisc_xfer_send_new(prplxfer, gc, who);
    - if (filename)
    - purple_xfer_request_accepted(xfer, filename);
    - else
    - purple_xfer_request(xfer);
    -}
    -
    -/*******************************************************************************
    - * Receiving a file.
    - ******************************************************************************/
    -
    -static PurpleXfer *
    -ggp_edisc_xfer_recv_new(PurpleConnection *gc, const char *who)
    -{
    - GGPXfer *xfer;
    -
    - g_return_val_if_fail(gc != NULL, NULL);
    - g_return_val_if_fail(who != NULL, NULL);
    -
    - xfer = g_object_new(GGP_TYPE_XFER, "account",
    - purple_connection_get_account(gc), "type",
    - PURPLE_XFER_TYPE_RECEIVE, "remote-user", who, NULL);
    -
    - xfer->gc = gc;
    -
    - return PURPLE_XFER(xfer);
    -}
    -
    -static void
    -ggp_edisc_xfer_recv_ack_done(GObject *source, GAsyncResult *result,
    - gpointer data)
    -{
    - PurpleXfer *xfer = data;
    - GGPXfer *edisc_xfer = NULL;
    - GBytes *response_body = NULL;
    - const gchar *buffer = NULL;
    - gsize size = 0;
    - GError *error = NULL;
    -
    - if (purple_xfer_is_cancelled(xfer)) {
    - g_return_if_reached();
    - }
    -
    - edisc_xfer = GGP_XFER(xfer);
    -
    - if(!SOUP_STATUS_IS_SUCCESSFUL(soup_message_get_status(edisc_xfer->msg))) {
    - g_clear_object(&edisc_xfer->msg);
    - ggp_edisc_xfer_error(xfer, _("Cannot confirm file transfer."));
    - return;
    - }
    -
    - response_body = soup_session_send_and_read_finish(SOUP_SESSION(source),
    - result, &error);
    - if(response_body == NULL) {
    - purple_debug_error("gg", "ggp_edisc_xfer_recv_ack_done: failed: %s",
    - error->message);
    - g_error_free(error);
    - g_clear_object(&edisc_xfer->msg);
    - ggp_edisc_xfer_error(xfer, _("Cannot confirm file transfer."));
    - return;
    - }
    -
    - buffer = g_bytes_get_data(response_body, &size);
    - purple_debug_info("gg", "ggp_edisc_xfer_recv_ack_done: [%.*s]", (int)size,
    - buffer);
    -
    - g_bytes_unref(response_body);
    - g_clear_object(&edisc_xfer->msg);
    -}
    -
    -static void ggp_edisc_xfer_recv_ack(PurpleXfer *xfer, gboolean accept)
    -{
    - GGPXfer *edisc_xfer = GGP_XFER(xfer);
    - ggp_edisc_session_data *sdata = ggp_edisc_get_sdata(edisc_xfer->gc);
    - SoupMessage *msg;
    - SoupMessageHeaders *headers;
    -
    - g_return_if_fail(sdata != NULL);
    -
    - edisc_xfer->allowed = accept;
    -
    - msg = soup_message_new("PUT",
    - ggp_edisc_xfer_ticket_url(edisc_xfer->ticket_id));
    - ggp_edisc_set_defaults(msg);
    -
    - headers = soup_message_get_request_headers(msg);
    - soup_message_headers_replace(headers, "X-gged-security-token",
    - sdata->security_token);
    - soup_message_headers_replace(headers, "X-gged-ack-status",
    - accept ? "allow" : "reject");
    -
    - if(accept) {
    - edisc_xfer->msg = msg;
    - soup_session_send_and_read_async(sdata->session, msg,
    - G_PRIORITY_DEFAULT,
    - edisc_xfer->cancellable,
    - ggp_edisc_xfer_recv_ack_done, xfer);
    - } else {
    - edisc_xfer->msg = NULL;
    - soup_session_send_and_read_async(sdata->session, msg,
    - G_PRIORITY_DEFAULT,
    - edisc_xfer->cancellable, NULL, NULL);
    - g_object_unref(msg);
    - }
    -}
    -
    -static void
    -ggp_edisc_xfer_recv_reject(PurpleXfer *xfer)
    -{
    - ggp_edisc_xfer_recv_ack(xfer, FALSE);
    -}
    -
    -static void
    -ggp_edisc_xfer_recv_accept(PurpleXfer *xfer)
    -{
    - ggp_edisc_xfer_recv_ack(xfer, TRUE);
    -}
    -
    -static void ggp_edisc_xfer_recv_ticket_completed(PurpleXfer *xfer)
    -{
    - GGPXfer *edisc_xfer = GGP_XFER(xfer);
    -
    - if (edisc_xfer->ready)
    - return;
    - edisc_xfer->ready = TRUE;
    -
    - purple_xfer_start(xfer, -1, NULL, 0);
    -}
    -
    -static gboolean
    -ggp_edisc_xfer_recv_pollable_source_cb(GObject *pollable_stream, gpointer data)
    -{
    - PurpleXfer *xfer = data;
    - GGPXfer *edisc_xfer = GGP_XFER(xfer);
    - guchar buf[4096];
    - gssize len;
    - gboolean stored;
    - GError *error = NULL;
    -
    - do {
    - len = g_pollable_input_stream_read_nonblocking(
    - G_POLLABLE_INPUT_STREAM(pollable_stream), buf, sizeof(buf),
    - edisc_xfer->cancellable, &error);
    - if(len == 0) {
    - /* End of file */
    - if(purple_xfer_get_bytes_remaining(xfer) == 0) {
    - purple_xfer_set_completed(xfer, TRUE);
    - purple_xfer_end(xfer);
    - } else {
    - purple_debug_warning("gg", "ggp_edisc_xfer_recv_done: didn't "
    - "receive everything");
    - ggp_edisc_xfer_error(xfer, _("Error while receiving a file"));
    - }
    - edisc_xfer->handler = 0;
    - return G_SOURCE_REMOVE;
    -
    - } else if(len < 0) {
    - /* Errors occurred */
    - if(error->code == G_IO_ERROR_WOULD_BLOCK) {
    - g_error_free(error);
    - return G_SOURCE_CONTINUE;
    - } else if(error->code == G_IO_ERROR_CANCELLED) {
    - g_error_free(error);
    - } else {
    - purple_debug_warning("gg", "ggp_edisc_xfer_recv_done: lost "
    - "connection with server: %s",
    - error->message);
    - ggp_edisc_xfer_error(xfer, _("Error while receiving a file"));
    - g_error_free(error);
    - }
    - edisc_xfer->handler = 0;
    - return G_SOURCE_REMOVE;
    - }
    -
    - if(len > purple_xfer_get_bytes_remaining(xfer)) {
    - purple_debug_error("gg", "ggp_edisc_xfer_recv_writer: saved too "
    - "much (%" G_GSIZE_FORMAT
    - " > %" G_GOFFSET_FORMAT ")",
    - len, purple_xfer_get_bytes_remaining(xfer));
    - ggp_edisc_xfer_error(xfer, _("Error while receiving a file"));
    - edisc_xfer->handler = 0;
    - return G_SOURCE_REMOVE;
    - }
    -
    - stored = purple_xfer_write_file(xfer, buf, len);
    - if(!stored) {
    - purple_debug_error("gg", "ggp_edisc_xfer_recv_writer: failed to save");
    - ggp_edisc_xfer_error(xfer, _("Error while receiving a file"));
    - edisc_xfer->handler = 0;
    - return G_SOURCE_REMOVE;
    - }
    - } while(len > 0);
    -
    - return G_SOURCE_CONTINUE;
    -}
    -
    -static void
    -ggp_edisc_xfer_recv_done_cb(GObject *source, GAsyncResult *result,
    - gpointer data)
    -{
    - PurpleXfer *xfer = data;
    - GGPXfer *edisc_xfer = GGP_XFER(xfer);
    - GInputStream *input = NULL;
    - GSource *poll = NULL;
    - GError *error = NULL;
    -
    - if(!SOUP_STATUS_IS_SUCCESSFUL(soup_message_get_status(edisc_xfer->msg))) {
    - g_clear_object(&edisc_xfer->msg);
    - ggp_edisc_xfer_error(xfer, _("Error while receiving a file"));
    - return;
    - }
    -
    - input = soup_session_send_finish(SOUP_SESSION(source), result, &error);
    - if(input == NULL) {
    - purple_debug_warning("gg", "ggp_edisc_xfer_recv_done_cb: error "
    - "receiving file: %s", error->message);
    - g_error_free(error);
    - g_clear_object(&edisc_xfer->msg);
    - ggp_edisc_xfer_error(xfer, _("Error while receiving a file"));
    - return;
    - }
    -
    - poll = g_pollable_input_stream_create_source(G_POLLABLE_INPUT_STREAM(input),
    - edisc_xfer->cancellable);
    - g_source_set_callback(poll,
    - G_SOURCE_FUNC(ggp_edisc_xfer_recv_pollable_source_cb),
    - xfer, NULL);
    - edisc_xfer->handler = g_source_attach(poll, NULL);
    - g_source_unref(poll);
    - g_clear_object(&edisc_xfer->msg);
    -}
    -
    -static void
    -ggp_edisc_xfer_recv_start(PurpleXfer *xfer)
    -{
    - ggp_edisc_session_data *sdata;
    - GGPXfer *edisc_xfer;
    - gchar *upload_url;
    - SoupMessage *msg;
    -
    - g_return_if_fail(xfer != NULL);
    - edisc_xfer = GGP_XFER(xfer);
    - g_return_if_fail(edisc_xfer != NULL);
    - sdata = ggp_edisc_get_sdata(edisc_xfer->gc);
    - g_return_if_fail(sdata != NULL);
    -
    - upload_url =
    - g_strdup_printf("https://drive.mpa.gg.pl/me/file/inbox/"
    - "%s,%s?api_version=%s&security_token=%s",
    - edisc_xfer->ticket_id,
    - purple_url_encode(purple_xfer_get_filename(xfer)),
    - GGP_EDISC_API, sdata->security_token);
    - msg = soup_message_new("GET", upload_url);
    - g_free(upload_url);
    -
    - ggp_edisc_set_defaults(msg);
    - // purple_http_request_set_max_len(msg, purple_xfer_get_size(xfer) + 1);
    -
    - edisc_xfer->msg = msg;
    - soup_session_send_async(sdata->session, msg, G_PRIORITY_DEFAULT,
    - edisc_xfer->cancellable,
    - ggp_edisc_xfer_recv_done_cb, xfer);
    -}
    -
    -static void
    -ggp_edisc_xfer_recv_ticket_update_got(GObject *source,
    - GAsyncResult *async_result,
    - gpointer data)
    -{
    - SoupMessage *msg = data;
    - PurpleConnection *gc = NULL;
    - GBytes *response_body = NULL;
    - PurpleXfer *xfer;
    - GGPXfer *edisc_xfer;
    - const char *buffer = NULL;
    - gsize size = 0;
    - JsonParser *parser;
    - JsonObject *result;
    - int status = -1;
    - ggp_edisc_session_data *sdata;
    -
    - const gchar *ticket_id, *file_name, *send_mode_str;
    - uin_t sender, recipient;
    - int file_size;
    - SoupStatus status_code;
    - GError *error = NULL;
    -
    - status_code = soup_message_get_status(msg);
    - if (!SOUP_STATUS_IS_SUCCESSFUL(status_code)) {
    - purple_debug_error("gg",
    - "ggp_edisc_xfer_recv_ticket_update_got: cannot "
    - "fetch update for ticket (code=%d)",
    - status_code);
    - g_object_unref(msg);
    - return;
    - }
    -
    - response_body = soup_session_send_and_read_finish(SOUP_SESSION(source),
    - async_result, &error);
    - if(response_body == NULL) {
    - purple_debug_error("gg",
    - "ggp_edisc_xfer_recv_ticket_update_got: cannot "
    - "fetch update for ticket (%s)",
    - error->message);
    - g_error_free(error);
    - g_object_unref(msg);
    - return;
    - }
    -
    - gc = g_object_get_data(G_OBJECT(msg), "purple-connection");
    - sdata = ggp_edisc_get_sdata(gc);
    - if(sdata == NULL) {
    - g_bytes_unref(response_body);
    - g_object_unref(msg);
    - g_return_if_reached();
    - return;
    - }
    -
    - buffer = g_bytes_get_data(response_body, &size);
    - parser = ggp_json_parse(buffer);
    - g_clear_pointer(&response_body, g_bytes_unref);
    - g_clear_object(&msg);
    -
    - result = json_node_get_object(json_parser_get_root(parser));
    - result = json_object_get_object_member(result, "result");
    - if (json_object_has_member(result, "status"))
    - status = json_object_get_int_member(result, "status");
    - result = json_object_get_object_member(result, "send_ticket");
    -
    - if (status != 0) {
    - purple_debug_warning("gg",
    - "ggp_edisc_xfer_recv_ticket_update_got: failed to "
    - "get update (status=%d)",
    - status);
    - g_object_unref(parser);
    - return;
    - }
    -
    - ticket_id = json_object_get_string_member(result, "id");
    - sender = ggp_str_to_uin(json_object_get_string_member(result, "sender"));
    - recipient =
    - ggp_str_to_uin(json_object_get_string_member(result, "recipient"));
    - file_size = g_ascii_strtoll(
    - json_object_get_string_member(result, "file_size"), NULL, 10);
    - file_name = json_object_get_string_member(result, "file_name");
    -
    - /* GG11: normal
    - * AQQ 2.4.2.10: direct_inbox
    - */
    - send_mode_str = json_object_get_string_member(result, "send_mode");
    -
    - /* more fields:
    - * send_progress (float), ack_status, send_status
    - */
    -
    - if (purple_debug_is_verbose() && purple_debug_is_unsafe()) {
    - purple_debug_info("gg",
    - "Got ticket update: id=%s, sender=%u, recipient=%u, "
    - "file name=\"%s\", file size=%d, send mode=%s)",
    - ticket_id, sender, recipient, file_name, file_size,
    - send_mode_str);
    - }
    -
    - xfer = g_hash_table_lookup(sdata->xfers_initialized, ticket_id);
    - if (xfer != NULL) {
    - purple_debug_misc("gg",
    - "ggp_edisc_xfer_recv_ticket_update_got: ticket %s "
    - "already updated",
    - purple_debug_is_unsafe() ? ticket_id : "");
    - g_object_unref(parser);
    - return;
    - }
    -
    - if (recipient != ggp_get_my_uin(gc)) {
    - purple_debug_misc("gg",
    - "ggp_edisc_xfer_recv_ticket_update_got: ticket %s is "
    - "not for incoming transfer (its from %u to %u)",
    - purple_debug_is_unsafe() ? ticket_id : "", sender,
    - recipient);
    - g_object_unref(parser);
    - return;
    - }
    -
    - xfer = ggp_edisc_xfer_recv_new(gc, ggp_uin_to_str(sender));
    - purple_xfer_set_filename(xfer, file_name);
    - purple_xfer_set_size(xfer, file_size);
    - purple_xfer_request(xfer);
    - edisc_xfer = GGP_XFER(xfer);
    - edisc_xfer->ticket_id = g_strdup(ticket_id);
    - g_hash_table_insert(sdata->xfers_initialized, edisc_xfer->ticket_id, xfer);
    - g_hash_table_add(sdata->xfers_history, g_strdup(ticket_id));
    -
    - g_object_unref(parser);
    -}
    -
    -static void
    -ggp_edisc_xfer_recv_ticket_update_authenticated(PurpleConnection *gc,
    - gboolean success,
    - gpointer _ticket)
    -{
    - ggp_edisc_session_data *sdata = ggp_edisc_get_sdata(gc);
    - SoupMessage *msg;
    - gchar *ticket = _ticket;
    -
    - g_return_if_fail(sdata != NULL);
    -
    - if (!success) {
    - purple_debug_warning(
    - "gg",
    - "ggp_edisc_xfer_recv_ticket_update_authenticated: update of "
    - "ticket %s aborted due to authentication failure",
    - ticket);
    - g_free(ticket);
    - return;
    - }
    -
    - msg = soup_message_new("GET", ggp_edisc_xfer_ticket_url(ticket));
    - g_free(ticket);
    -
    - ggp_edisc_set_defaults(msg);
    -
    - soup_message_headers_replace(soup_message_get_request_headers(msg),
    - "X-gged-security-token",
    - sdata->security_token);
    -
    - g_object_set_data(G_OBJECT(msg), "purple-connection", gc);
    - soup_session_send_and_read_async(sdata->session, msg, G_PRIORITY_DEFAULT,
    - NULL,
    - ggp_edisc_xfer_recv_ticket_update_got,
    - msg);
    -}
    -
    -static void
    -ggp_edisc_xfer_recv_ticket_got(PurpleConnection *gc, const gchar *ticket_id)
    -{
    - ggp_edisc_session_data *sdata = ggp_edisc_get_sdata(gc);
    -
    - g_return_if_fail(sdata != NULL);
    -
    - if (g_hash_table_contains(sdata->xfers_history, ticket_id)) {
    - return;
    - }
    -
    - ggp_ggdrive_auth(gc, ggp_edisc_xfer_recv_ticket_update_authenticated,
    - g_strdup(ticket_id));
    -}
    -
    -void
    -ggp_edisc_xfer_ticket_changed(PurpleConnection *gc, const char *data)
    -{
    - ggp_edisc_session_data *sdata = ggp_edisc_get_sdata(gc);
    - PurpleXfer *xfer;
    - JsonParser *parser;
    - JsonObject *ticket;
    - const gchar *ticket_id, *send_status;
    - ggp_edisc_xfer_ack_status ack_status;
    - gboolean is_completed;
    -
    - g_return_if_fail(sdata != NULL);
    -
    - parser = ggp_json_parse(data);
    - ticket = json_node_get_object(json_parser_get_root(parser));
    - ticket_id = json_object_get_string_member(ticket, "id");
    - ack_status = ggp_edisc_xfer_parse_ack_status(
    - json_object_get_string_member(ticket, "ack_status"));
    - send_status = json_object_get_string_member(ticket, "send_status");
    -
    - if (ticket_id == NULL) {
    - ticket_id = "";
    - }
    - xfer = g_hash_table_lookup(sdata->xfers_initialized, ticket_id);
    - if (xfer == NULL) {
    - purple_debug_misc("gg",
    - "ggp_edisc_event_ticket_changed: ticket %s not "
    - "found, updating it...",
    - purple_debug_is_unsafe() ? ticket_id : "");
    - ggp_edisc_xfer_recv_ticket_got(gc, ticket_id);
    - g_object_unref(parser);
    - return;
    - }
    -
    - is_completed = FALSE;
    - if (g_strcmp0("in_progress", send_status) == 0) {
    - /* do nothing */
    - } else if (g_strcmp0("completed", send_status) == 0) {
    - is_completed = TRUE;
    - } else if (g_strcmp0("expired", send_status) == 0)
    - ggp_edisc_xfer_error(xfer, _("File transfer expired."));
    - else {
    - purple_debug_warning(
    - "gg", "ggp_edisc_event_ticket_changed: unknown send_status=%s",
    - send_status);
    - g_object_unref(parser);
    - return;
    - }
    -
    - g_object_unref(parser);
    -
    - if (purple_xfer_get_xfer_type(xfer) == PURPLE_XFER_TYPE_RECEIVE) {
    - if (is_completed) {
    - ggp_edisc_xfer_recv_ticket_completed(xfer);
    - }
    - } else {
    - if (ack_status != GGP_EDISC_XFER_ACK_STATUS_UNKNOWN) {
    - ggp_edisc_xfer_send_ticket_changed(
    - gc, xfer, ack_status == GGP_EDISC_XFER_ACK_STATUS_ALLOWED);
    - }
    - }
    -}
    -
    -/*******************************************************************************
    - * GObject implementation
    - ******************************************************************************/
    -
    -G_DEFINE_DYNAMIC_TYPE_EXTENDED(GGPXfer, ggp_xfer, PURPLE_TYPE_XFER,
    - G_TYPE_FLAG_FINAL, {})
    -
    -static void
    -ggp_xfer_init_xfer(PurpleXfer *xfer) {
    - PurpleXferType type = purple_xfer_get_xfer_type(xfer);
    -
    - if(type == PURPLE_XFER_TYPE_SEND) {
    - ggp_edisc_xfer_send_init(xfer);
    - } else if(type == PURPLE_XFER_TYPE_RECEIVE) {
    - ggp_edisc_xfer_recv_accept(xfer);
    - }
    -}
    -
    -static void
    -ggp_xfer_start(PurpleXfer *xfer) {
    - PurpleXferType type = purple_xfer_get_xfer_type(xfer);
    -
    - if(type == PURPLE_XFER_TYPE_SEND) {
    - ggp_edisc_xfer_send_start(xfer);
    - } else if(type == PURPLE_XFER_TYPE_RECEIVE) {
    - ggp_edisc_xfer_recv_start(xfer);
    - }
    -}
    -
    -static void
    -ggp_xfer_init(GGPXfer *xfer) {
    - xfer->cancellable = g_cancellable_new();
    -}
    -
    -static void
    -ggp_xfer_finalize(GObject *obj) {
    - GGPXfer *edisc_xfer = GGP_XFER(obj);
    - ggp_edisc_session_data *sdata;
    -
    - sdata = ggp_edisc_get_sdata(edisc_xfer->gc);
    -
    - g_free(edisc_xfer->filename);
    - g_cancellable_cancel(edisc_xfer->cancellable);
    - g_clear_object(&edisc_xfer->cancellable);
    - g_clear_object(&edisc_xfer->msg);
    -
    - g_clear_handle_id(&edisc_xfer->handler, g_source_remove);
    -
    - if (edisc_xfer->ticket_id != NULL) {
    - g_hash_table_remove(sdata->xfers_initialized,
    - edisc_xfer->ticket_id);
    - }
    -
    - G_OBJECT_CLASS(ggp_xfer_parent_class)->finalize(obj);
    -}
    -
    -static void
    -ggp_xfer_class_finalize(G_GNUC_UNUSED GGPXferClass *klass) {
    -}
    -
    -static void
    -ggp_xfer_class_init(GGPXferClass *klass) {
    - GObjectClass *obj_class = G_OBJECT_CLASS(klass);
    - PurpleXferClass *xfer_class = PURPLE_XFER_CLASS(klass);
    -
    - obj_class->finalize = ggp_xfer_finalize;
    -
    - xfer_class->init = ggp_xfer_init_xfer;
    - xfer_class->start = ggp_xfer_start;
    - xfer_class->request_denied = ggp_edisc_xfer_recv_reject;
    -}
    -
    -void
    -ggp_xfer_register(GTypeModule *module) {
    - ggp_xfer_register_type(module);
    -}
    --- a/libpurple/protocols/gg/edisc.h Tue Apr 09 21:50:31 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,59 +0,0 @@
    -/* 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.
    - *
    - * Component written by Tomek Wasilczyk (http://www.wasilczyk.pl).
    - *
    - * This file is dual-licensed under the GPL2+ and the X11 (MIT) licences.
    - * As a recipient of this file you may choose, which license to receive the
    - * code under. As a contributor, you have to ensure the new code is
    - * compatible with both.
    - *
    - * 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
    - */
    -
    -#ifndef PURPLE_GG_EDISC_H
    -#define PURPLE_GG_EDISC_H
    -
    -#include <purple.h>
    -
    -typedef struct _ggp_edisc_session_data ggp_edisc_session_data;
    -
    -G_BEGIN_DECLS
    -
    -#define GGP_TYPE_XFER (ggp_xfer_get_type())
    -G_DECLARE_FINAL_TYPE(GGPXfer, ggp_xfer, GGP, XFER, PurpleXfer);
    -
    -void ggp_xfer_register(GTypeModule *module);
    -
    -/* Setting up. */
    -void ggp_edisc_setup(PurpleConnection *gc, GProxyResolver *resolver);
    -void ggp_edisc_cleanup(PurpleConnection *gc);
    -
    -/* General xfer functions. */
    -void ggp_edisc_xfer_ticket_changed(PurpleConnection *gc,
    - const char *data);
    -
    -/* Sending a file. */
    -gboolean ggp_edisc_xfer_can_receive_file(PurpleProtocolXfer *prplxfer, PurpleConnection *gc, const char *who);
    -void ggp_edisc_xfer_send_file(PurpleProtocolXfer *prplxfer, PurpleConnection *gc, const char *who,
    - const char *filename);
    -PurpleXfer * ggp_edisc_xfer_send_new(PurpleProtocolXfer *prplxfer, PurpleConnection *gc, const char *who);
    -
    -G_END_DECLS
    -
    -#endif /* PURPLE_GG_EDISC_H */
    --- a/libpurple/protocols/gg/gg.c Tue Apr 09 21:50:31 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,1452 +0,0 @@
    -/**
    - * @file gg.c Gadu-Gadu protocol plugin
    - *
    - * purple
    - *
    - * Copyright (C) 2005 Bartosz Oler <bartosz@bzimage.us>
    - *
    - * Some parts of the code are adapted or taken from the previous implementation
    - * of this plugin written by Arkadiusz Miskiewicz <misiek@pld.org.pl>
    - * Some parts Copyright (C) 2009 Krzysztof Klinikowski <grommasher@gmail.com>
    - *
    - * Thanks to Google's Summer of Code Program.
    - *
    - * 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 <gplugin.h>
    -#include <gplugin-native.h>
    -
    -#include <purple.h>
    -
    -#include "gg.h"
    -#include "chat.h"
    -#include "search.h"
    -#include "blist.h"
    -#include "utils.h"
    -#include "resolver-purple.h"
    -#include "purplew.h"
    -#include "libgadu-events.h"
    -#include "multilogon.h"
    -#include "status.h"
    -#include "servconn.h"
    -#include "tcpsocket.h"
    -#include "pubdir-prpl.h"
    -#include "message-prpl.h"
    -#include "html.h"
    -#include "libgaduw.h"
    -
    -struct _GGPProtocol {
    - PurpleProtocol parent;
    -};
    -
    -/* ---------------------------------------------------------------------- */
    -static PurpleProtocol *my_protocol = NULL;
    -
    -/* ---------------------------------------------------------------------- */
    -
    -ggp_buddy_data * ggp_buddy_get_data(PurpleBuddy *buddy)
    -{
    - ggp_buddy_data *buddy_data = purple_buddy_get_protocol_data(buddy);
    - if (buddy_data)
    - return buddy_data;
    -
    - buddy_data = g_new0(ggp_buddy_data, 1); /* TODO: leak */
    - purple_buddy_set_protocol_data(buddy, buddy_data);
    - return buddy_data;
    -}
    -
    -static void
    -ggp_buddy_free(G_GNUC_UNUSED PurpleProtocolClient *client, PurpleBuddy *buddy)
    -{
    - ggp_buddy_data *buddy_data = purple_buddy_get_protocol_data(buddy);
    -
    - if (!buddy_data) {
    - return;
    - }
    -
    - g_free(buddy_data);
    - purple_buddy_set_protocol_data(buddy, NULL);
    -}
    -
    -const gchar * ggp_get_imtoken(PurpleConnection *gc)
    -{
    - GGPInfo *accdata = purple_connection_get_protocol_data(gc);
    -
    - if (accdata->imtoken)
    - return accdata->imtoken;
    -
    - if (accdata->imtoken_warned)
    - return NULL;
    - accdata->imtoken_warned = TRUE;
    -
    - purple_notify_error(gc, _("Authentication failed"),
    - _("IMToken value has not been received."),
    - _("Some features will be disabled. "
    - "You may try again after a while."),
    - purple_request_cpar_from_connection(gc));
    - return NULL;
    -}
    -
    -uin_t
    -ggp_own_uin(PurpleConnection *gc) {
    - PurpleAccount *account = purple_connection_get_account(gc);
    - PurpleContactInfo *info = PURPLE_CONTACT_INFO(account);
    -
    - return ggp_str_to_uin(purple_contact_info_get_username(info));
    -}
    -
    -/* ---------------------------------------------------------------------- */
    -/* buddy list import/export from/to file */
    -
    -static void ggp_callback_buddylist_save_ok(PurpleConnection *gc, const char *filename)
    -{
    - PurpleAccount *account = purple_connection_get_account(gc);
    -
    - char *buddylist = ggp_buddylist_dump(account);
    -
    - purple_debug_info("gg", "Saving...\n");
    - purple_debug_info("gg", "file = %s\n", filename);
    -
    - if (buddylist == NULL) {
    - purple_notify_info(account, _("Save Buddylist..."), _("Your "
    - "buddylist is empty, nothing was written to the file."),
    - NULL, purple_request_cpar_from_connection(gc));
    - return;
    - }
    -
    - if (g_file_set_contents(filename, buddylist, -1, NULL)) {
    - purple_notify_info(account, _("Save Buddylist..."),
    - _("Buddylist saved successfully!"), NULL,
    - purple_request_cpar_from_connection(gc));
    - } else {
    - PurpleContactInfo *info = PURPLE_CONTACT_INFO(account);
    - gchar *primary = NULL;
    -
    - primary = g_strdup_printf(_("Couldn't write buddy list for %s to %s"),
    - purple_contact_info_get_username(info),
    - filename);
    - purple_notify_error(account, _("Save Buddylist..."),
    - primary, NULL, purple_request_cpar_from_connection(gc));
    - g_free(primary);
    - }
    -
    - g_free(buddylist);
    -}
    -
    -static void ggp_callback_buddylist_load_ok(PurpleConnection *gc, gchar *file)
    -{
    - PurpleAccount *account = purple_connection_get_account(gc);
    - GError *error = NULL;
    - char *buddylist = NULL;
    - gsize length;
    -
    - purple_debug_info("gg", "file_name = %s\n", file);
    -
    - if (!g_file_get_contents(file, &buddylist, &length, &error)) {
    - purple_notify_error(account, _("Couldn't load buddylist"),
    - _("Couldn't load buddylist"), error->message,
    - purple_request_cpar_from_connection(gc));
    -
    - purple_debug_error("gg",
    - "Couldn't load buddylist. file = %s; error = %s\n",
    - file, error->message);
    -
    - g_error_free(error);
    -
    - return;
    - }
    -
    - ggp_buddylist_load(gc, buddylist);
    - g_free(buddylist);
    -
    - purple_notify_info(account, _("Load Buddylist..."),
    - _("Buddylist loaded successfully!"), NULL,
    - purple_request_cpar_from_connection(gc));
    -}
    -/* }}} */
    -
    -static void
    -ggp_action_buddylist_save(G_GNUC_UNUSED GSimpleAction *action,
    - GVariant *parameter,
    - G_GNUC_UNUSED gpointer data)
    -{
    - const gchar *account_id = NULL;
    - PurpleAccountManager *manager = NULL;
    - PurpleAccount *account = NULL;
    - PurpleConnection *connection = NULL;
    -
    - if(!g_variant_is_of_type(parameter, G_VARIANT_TYPE_STRING)) {
    - g_critical("GG save buddylist action parameter is of incorrect type %s",
    - g_variant_get_type_string(parameter));
    - }
    -
    - account_id = g_variant_get_string(parameter, NULL);
    - manager = purple_account_manager_get_default();
    - account = purple_account_manager_find_by_id(manager, account_id);
    - connection = purple_account_get_connection(account);
    -
    - purple_request_file(action, _("Save buddylist..."), NULL, TRUE,
    - G_CALLBACK(ggp_callback_buddylist_save_ok), NULL,
    - purple_request_cpar_from_connection(connection), connection);
    -
    - g_clear_object(&account);
    -}
    -
    -static void
    -ggp_action_buddylist_load(G_GNUC_UNUSED GSimpleAction *action,
    - GVariant *parameter,
    - G_GNUC_UNUSED gpointer data)
    -{
    - const gchar *account_id = NULL;
    - PurpleAccountManager *manager = NULL;
    - PurpleAccount *account = NULL;
    - PurpleConnection *connection = NULL;
    -
    - if(!g_variant_is_of_type(parameter, G_VARIANT_TYPE_STRING)) {
    - g_critical("GG load buddylist action parameter is of incorrect type %s",
    - g_variant_get_type_string(parameter));
    - }
    -
    - account_id = g_variant_get_string(parameter, NULL);
    - manager = purple_account_manager_get_default();
    - account = purple_account_manager_find_by_id(manager, account_id);
    - connection = purple_account_get_connection(account);
    -
    - purple_request_file(action, _("Load buddylist from file..."), NULL,
    - FALSE, G_CALLBACK(ggp_callback_buddylist_load_ok), NULL,
    - purple_request_cpar_from_connection(connection), connection);
    -
    - g_clear_object(&account);
    -}
    -
    -/* ----- BLOCK BUDDIES -------------------------------------------------- */
    -
    -#if 0
    -static void ggp_add_deny(PurpleProtocolPrivacy *privacy, PurpleConnection *gc,
    - const char *who)
    -{
    - GGPInfo *info = purple_connection_get_protocol_data(gc);
    - uin_t uin = ggp_str_to_uin(who);
    -
    - purple_debug_info("gg", "ggp_add_deny: %u\n", uin);
    -
    - gg_remove_notify_ex(info->session, uin, GG_USER_NORMAL);
    - gg_add_notify_ex(info->session, uin, GG_USER_BLOCKED);
    -}
    -
    -static void ggp_remove_deny(PurpleProtocolPrivacy *privacy,
    - PurpleConnection *gc, const char *who)
    -{
    - GGPInfo *info = purple_connection_get_protocol_data(gc);
    - uin_t uin = ggp_str_to_uin(who);
    -
    - purple_debug_info("gg", "ggp_rem_deny: %u\n", uin);
    -
    - gg_remove_notify_ex(info->session, uin, GG_USER_BLOCKED);
    - gg_add_notify_ex(info->session, uin, GG_USER_NORMAL);
    -}
    -#endif
    -
    -/* ---------------------------------------------------------------------- */
    -/* ----- INTERNAL CALLBACKS --------------------------------------------- */
    -/* ---------------------------------------------------------------------- */
    -
    -static void ggp_typing_notification_handler(PurpleConnection *gc, uin_t uin, int length) {
    - gchar *from;
    -
    - from = g_strdup_printf("%u", uin);
    - if (length)
    - purple_serv_got_typing(gc, from, 0, PURPLE_IM_TYPING);
    - else
    - purple_serv_got_typing_stopped(gc, from);
    - g_free(from);
    -}
    -
    -/**
    - * Handling of XML events.
    - *
    - * @param gc PurpleConnection.
    - * @param data Raw XML contents.
    - *
    - * @see http://toxygen.net/libgadu/protocol/#ch1.13
    - * @todo: this may not be necessary anymore
    - */
    -static void
    -ggp_xml_event_handler(G_GNUC_UNUSED PurpleConnection *gc, char *data)
    -{
    - PurpleXmlNode *xml = NULL;
    - PurpleXmlNode *xmlnode_next_event;
    -
    - xml = purple_xmlnode_from_str(data, -1);
    - if (xml == NULL) {
    - purple_debug_error("gg", "ggp_xml_event_handler: "
    - "invalid xml: [%s]\n", data);
    - goto out;
    - }
    -
    - xmlnode_next_event = purple_xmlnode_get_child(xml, "event");
    - while (xmlnode_next_event != NULL) {
    - PurpleXmlNode *xmlnode_current_event = xmlnode_next_event;
    -
    - PurpleXmlNode *xmlnode_type;
    - char *event_type_raw;
    - int event_type = 0;
    -
    - PurpleXmlNode *xmlnode_sender;
    - char *event_sender_raw;
    - uin_t event_sender = 0;
    -
    - xmlnode_next_event = purple_xmlnode_get_next_twin(xmlnode_next_event);
    -
    - xmlnode_type = purple_xmlnode_get_child(xmlnode_current_event, "type");
    - if (xmlnode_type == NULL)
    - continue;
    - event_type_raw = purple_xmlnode_get_data(xmlnode_type);
    - if (event_type_raw != NULL)
    - event_type = atoi(event_type_raw);
    - g_free(event_type_raw);
    -
    - xmlnode_sender = purple_xmlnode_get_child(xmlnode_current_event, "sender");
    - if (xmlnode_sender != NULL) {
    - event_sender_raw = purple_xmlnode_get_data(xmlnode_sender);
    - if (event_sender_raw != NULL)
    - event_sender = ggp_str_to_uin(event_sender_raw);
    - g_free(event_sender_raw);
    - }
    -
    - switch (event_type)
    - {
    - case 28: /* avatar update */
    - purple_debug_info("gg",
    - "ggp_xml_event_handler: avatar updated (uid: %u)\n",
    - event_sender);
    - break;
    - default:
    - purple_debug_error("gg",
    - "ggp_xml_event_handler: unsupported event type=%d from=%u\n",
    - event_type, event_sender);
    - }
    - }
    -
    - out:
    - if (xml)
    - purple_xmlnode_free(xml);
    -}
    -
    -static void
    -ggp_callback_recv(gpointer _gc, G_GNUC_UNUSED gint fd,
    - G_GNUC_UNUSED PurpleInputCondition cond)
    -{
    - PurpleConnection *gc = _gc;
    - GGPInfo *info = purple_connection_get_protocol_data(gc);
    - struct gg_event *ev;
    -
    - if (!(ev = gg_watch_fd(info->session))) {
    - purple_debug_error("gg",
    - "ggp_callback_recv: gg_watch_fd failed -- CRITICAL!\n");
    - purple_connection_error (gc,
    - PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
    - _("Unable to read from socket"));
    - return;
    - }
    -
    - if (purple_debug_is_verbose()) {
    - purple_debug_misc("gg", "ggp_callback_recv: got event %s",
    - gg_debug_event(ev->type));
    - }
    -
    - g_source_remove(info->inpa);
    - info->inpa = purple_input_add(info->session->fd,
    - ggp_tcpsocket_inputcond_gg_to_purple(info->session->check),
    - ggp_callback_recv, gc);
    -
    - switch (ev->type) {
    - case GG_EVENT_NONE:
    - /* Nothing happened. */
    - break;
    - case GG_EVENT_CONN_FAILED:
    - purple_connection_error (gc,
    - PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
    - _("Server disconnected"));
    - break;
    - case GG_EVENT_MSG:
    - ggp_message_got(gc, &ev->event.msg);
    - break;
    - case GG_EVENT_ACK:
    - case GG_EVENT_ACK110:
    - break;
    - case GG_EVENT_IMAGE_REPLY:
    - ggp_image_recv(gc, &ev->event.image_reply);
    - break;
    - case GG_EVENT_IMAGE_REQUEST:
    - ggp_image_send(gc, &ev->event.image_request);
    - break;
    - case GG_EVENT_NOTIFY60:
    - case GG_EVENT_STATUS60:
    - ggp_status_got_others(gc, ev);
    - break;
    - case GG_EVENT_TYPING_NOTIFICATION:
    - ggp_typing_notification_handler(gc, ev->event.typing_notification.uin,
    - ev->event.typing_notification.length);
    - break;
    - case GG_EVENT_XML_EVENT:
    - purple_debug_info("gg", "GG_EVENT_XML_EVENT\n");
    - ggp_xml_event_handler(gc, ev->event.xml_event.data);
    - break;
    - case GG_EVENT_USER_DATA:
    - ggp_events_user_data(gc, &ev->event.user_data);
    - break;
    - case GG_EVENT_JSON_EVENT:
    - ggp_events_json(gc, &ev->event.json_event);
    - break;
    - case GG_EVENT_USERLIST100_VERSION:
    - ggp_roster_version(gc, &ev->event.userlist100_version);
    - break;
    - case GG_EVENT_USERLIST100_REPLY:
    - ggp_roster_reply(gc, &ev->event.userlist100_reply);
    - break;
    - case GG_EVENT_MULTILOGON_MSG:
    - ggp_message_got_multilogon(gc, &ev->event.multilogon_msg);
    - break;
    - case GG_EVENT_MULTILOGON_INFO:
    - ggp_multilogon_info(gc, &ev->event.multilogon_info);
    - break;
    - case GG_EVENT_IMTOKEN:
    - purple_debug_info("gg", "gg11: got IMTOKEN\n");
    - g_free(info->imtoken);
    - info->imtoken = g_strdup(ev->event.imtoken.imtoken);
    - break;
    - case GG_EVENT_PONG110:
    - purple_debug_info("gg", "gg11: got PONG110 %lu\n",
    - (long unsigned)ev->event.pong110.time);
    - break;
    - case GG_EVENT_CHAT_INFO:
    - case GG_EVENT_CHAT_INFO_GOT_ALL:
    - case GG_EVENT_CHAT_INFO_UPDATE:
    - case GG_EVENT_CHAT_CREATED:
    - case GG_EVENT_CHAT_INVITE_ACK:
    - ggp_chat_got_event(gc, ev);
    - break;
    - case GG_EVENT_DISCONNECT:
    - ggp_servconn_remote_disconnect(gc);
    - break;
    - default:
    - purple_debug_warning("gg",
    - "unsupported event type=%d\n", ev->type);
    - break;
    - }
    -
    - gg_free_event(ev);
    -}
    -
    -void
    -ggp_async_login_handler(gpointer _gc, G_GNUC_UNUSED gint fd,
    - G_GNUC_UNUSED PurpleInputCondition cond)
    -{
    - PurpleConnection *gc = _gc;
    - GGPInfo *info;
    - struct gg_event *ev;
    -
    - PURPLE_ASSERT_CONNECTION_IS_VALID(gc);
    -
    - info = purple_connection_get_protocol_data(gc);
    -
    - purple_debug_info("gg", "login_handler: session: check = %d; state = %d;\n",
    - info->session->check, info->session->state);
    -
    - switch (info->session->state) {
    - case GG_STATE_ERROR:
    - purple_debug_info("gg", "GG_STATE_ERROR\n");
    - break;
    - case GG_STATE_RESOLVING:
    - purple_debug_info("gg", "GG_STATE_RESOLVING\n");
    - break;
    - case GG_STATE_RESOLVING_GG:
    - purple_debug_info("gg", "GG_STATE_RESOLVING_GG\n");
    - break;
    - case GG_STATE_CONNECTING_HUB:
    - purple_debug_info("gg", "GG_STATE_CONNECTING_HUB\n");
    - break;
    - case GG_STATE_READING_DATA:
    - purple_debug_info("gg", "GG_STATE_READING_DATA\n");
    - break;
    - case GG_STATE_CONNECTING_GG:
    - purple_debug_info("gg", "GG_STATE_CONNECTING_GG\n");
    - break;
    - case GG_STATE_READING_KEY:
    - purple_debug_info("gg", "GG_STATE_READING_KEY\n");
    - break;
    - case GG_STATE_READING_REPLY:
    - purple_debug_info("gg", "GG_STATE_READING_REPLY\n");
    - break;
    - case GG_STATE_TLS_NEGOTIATION:
    - purple_debug_info("gg", "GG_STATE_TLS_NEGOTIATION\n");
    - break;
    - case GG_STATE_RESOLVING_HUB:
    - purple_debug_info("gg", "GG_STATE_RESOLVING_HUB\n");
    - break;
    - case GG_STATE_READING_HUB:
    - purple_debug_info("gg", "GG_STATE_READING_HUB\n");
    - break;
    - default:
    - purple_debug_error("gg", "unknown state = %d\n",
    - info->session->state);
    - break;
    - }
    -
    - if (!(ev = gg_watch_fd(info->session))) {
    - purple_debug_error("gg", "login_handler: gg_watch_fd failed!\n");
    - purple_connection_error (gc,
    - PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
    - _("Unable to read from socket"));
    - return;
    - }
    - purple_debug_info("gg", "login_handler: session->fd = %d\n", info->session->fd);
    - purple_debug_info("gg", "login_handler: session: check = %d; state = %d;\n",
    - info->session->check, info->session->state);
    -
    - g_clear_handle_id(&info->inpa, g_source_remove);
    -
    - /** XXX I think that this shouldn't be done if ev->type is GG_EVENT_CONN_FAILED or GG_EVENT_CONN_SUCCESS -datallah */
    - if (info->session->fd >= 0)
    - info->inpa = purple_input_add(info->session->fd,
    - (info->session->check == 1) ? PURPLE_INPUT_WRITE :
    - PURPLE_INPUT_READ,
    - ggp_async_login_handler, gc);
    -
    - switch (ev->type) {
    - case GG_EVENT_NONE:
    - /* Nothing happened. */
    - purple_debug_info("gg", "GG_EVENT_NONE\n");
    - break;
    - case GG_EVENT_CONN_SUCCESS:
    - {
    - purple_debug_info("gg", "GG_EVENT_CONN_SUCCESS:"
    - " successfully connected to %s\n",
    - info->session->connect_host);
    - ggp_servconn_add_server(info->session->
    - connect_host);
    - g_source_remove(info->inpa);
    - info->inpa = purple_input_add(info->session->fd,
    - PURPLE_INPUT_READ,
    - ggp_callback_recv, gc);
    -
    - purple_connection_set_state(gc, PURPLE_CONNECTION_STATE_CONNECTED);
    -
    - ggp_buddylist_send(gc);
    - ggp_roster_request_update(gc);
    - }
    - break;
    - case GG_EVENT_CONN_FAILED:
    - g_clear_handle_id(&info->inpa, g_source_remove);
    - purple_debug_info("gg", "Connection failure: %d\n",
    - ev->event.failure);
    - switch (ev->event.failure) {
    - case GG_FAILURE_RESOLVING:
    - purple_connection_error(gc,
    - PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
    - _("Unable to resolve "
    - "hostname"));
    - break;
    - case GG_FAILURE_PASSWORD:
    - purple_connection_error(gc,
    - PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED,
    - _("Incorrect password"));
    - break;
    - case GG_FAILURE_TLS:
    - purple_connection_error(gc,
    - PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR,
    - _("SSL Connection Failed"));
    - break;
    - case GG_FAILURE_INTRUDER:
    - purple_connection_error(gc,
    - PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED,
    - _("Your account has been "
    - "disabled because too many "
    - "incorrect passwords were "
    - "entered"));
    - break;
    - case GG_FAILURE_UNAVAILABLE:
    - purple_connection_error(gc,
    - PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
    - _("Service temporarily "
    - "unavailable"));
    - break;
    - case GG_FAILURE_PROXY:
    - purple_connection_error(gc,
    - PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
    - _("Error connecting to proxy "
    - "server"));
    - break;
    - case GG_FAILURE_HUB:
    - purple_connection_error(gc,
    - PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
    - _("Error connecting to master "
    - "server"));
    - break;
    - case GG_FAILURE_INTERNAL:
    - purple_connection_error(gc,
    - PURPLE_CONNECTION_ERROR_OTHER_ERROR,
    - _("Internal error"));
    - break;
    - default:
    - purple_connection_error(gc,
    - PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
    - _("Connection failed"));
    - }
    - break;
    - case GG_EVENT_MSG:
    - if (ev->event.msg.sender == 0) {
    - if (ev->event.msg.message == NULL)
    - break;
    -
    - /* system messages are mostly ads */
    - purple_debug_info("gg", "System message:\n%s\n",
    - ev->event.msg.message);
    - } else {
    - purple_debug_warning("gg", "GG_EVENT_MSG: message from user %u "
    - "unexpected while connecting:\n%s\n",
    - ev->event.msg.sender,
    - ev->event.msg.message);
    - }
    - break;
    - default:
    - purple_debug_error("gg", "strange event: %d\n", ev->type);
    - break;
    - }
    -
    - gg_free_event(ev);
    -}
    -
    -static gboolean
    -gg_uri_handler_find_account(PurpleAccount *account,
    - G_GNUC_UNUSED gconstpointer data)
    -{
    - const gchar *protocol_id;
    -
    - protocol_id = purple_account_get_protocol_id(account);
    -
    - return (purple_strequal(protocol_id, "prpl-gg") &&
    - purple_account_is_connected(account));
    -}
    -
    -static gboolean
    -gg_uri_handler(const gchar *scheme, const gchar *screenname,
    - G_GNUC_UNUSED GHashTable *params)
    -{
    - PurpleAccountManager *manager = NULL;
    - PurpleAccount *account;
    - PurpleConversation *im;
    -
    - g_return_val_if_fail(screenname != NULL, FALSE);
    -
    - if (!purple_strequal(scheme, "gg")) {
    - return FALSE;
    - }
    -
    - if (screenname[0] == '\0') {
    - purple_debug_warning("gg", "Invalid empty screenname in URI");
    - return FALSE;
    - }
    -
    - /* Find online Gadu-Gadu account */
    - manager = purple_account_manager_get_default();
    - account = purple_account_manager_find_custom(manager,
    - (GEqualFunc)gg_uri_handler_find_account,
    - NULL);
    -
    - if (account == NULL) {
    - return FALSE;
    - }
    -
    - im = purple_im_conversation_new(account, screenname);
    - purple_conversation_present(im);
    -
    - g_clear_object(&account);
    -
    - return TRUE;
    -}
    -
    -/* ---------------------------------------------------------------------- */
    -/* ----- PurpleProtocol ----------------------------------------- */
    -/* ---------------------------------------------------------------------- */
    -
    -static PurpleBuddyIconSpec *
    -ggp_protocol_get_buddy_icon_spec(G_GNUC_UNUSED PurpleProtocol *protocol) {
    - return purple_buddy_icon_spec_new("png",
    - 1, 1, 200, 200, 0,
    - PURPLE_ICON_SCALE_DISPLAY |
    - PURPLE_ICON_SCALE_SEND);
    -}
    -
    -static GList *
    -ggp_protocol_get_account_options(G_GNUC_UNUSED PurpleProtocol *protocol) {
    - PurpleAccountOption *option = NULL;
    - PurpleKeyValuePair *kvp = NULL;
    - GList *encryption_options = NULL;
    - GList *protocol_version = NULL;
    - GList *opts = NULL;
    -
    - option = purple_account_option_string_new(_("GG server"), "gg_server", "");
    - opts = g_list_append(opts, option);
    -
    - /* setup encryption options */
    - kvp = purple_key_value_pair_new(_("Use encryption if available"),
    - "opportunistic_tls");
    - encryption_options = g_list_append(encryption_options, kvp);
    -
    - kvp = purple_key_value_pair_new(_("Require encryption"), "require_tls");
    - encryption_options = g_list_append(encryption_options, kvp);
    -
    - kvp = purple_key_value_pair_new(_("Don't use encryption"), "none");
    - encryption_options = g_list_append(encryption_options, kvp);
    -
    - option = purple_account_option_list_new(_("Connection security"),
    - "encryption", encryption_options);
    - opts = g_list_append(opts, option);
    -
    - /* setup the protocol version */
    - kvp = purple_key_value_pair_new(_("Default"), "default");
    - protocol_version = g_list_append(protocol_version, kvp);
    -
    - kvp = purple_key_value_pair_new("GG 10", "gg10");
    - protocol_version = g_list_append(protocol_version, kvp);
    -
    - kvp = purple_key_value_pair_new("GG 11", "gg11");
    - protocol_version = g_list_append(protocol_version, kvp);
    -
    - option = purple_account_option_list_new(_("Protocol version"),
    - "protocol_version",
    - protocol_version);
    - opts = g_list_append(opts, option);
    -
    - option = purple_account_option_bool_new(_("Show links from strangers"),
    - "show_links_from_strangers", 1);
    - opts = g_list_append(opts, option);
    -
    - return opts;
    -}
    -
    -static const char *
    -ggp_normalize(G_GNUC_UNUSED PurpleProtocolClient *client,
    - G_GNUC_UNUSED PurpleAccount *account, const char *who)
    -{
    - static char normalized[21]; /* maximum unsigned long long int size */
    -
    - uin_t uin = ggp_str_to_uin(who);
    - if(uin <= 0) {
    - return NULL;
    - }
    -
    - g_snprintf(normalized, sizeof(normalized), "%u", uin);
    -
    - return normalized;
    -}
    -
    -static void
    -ggp_login(G_GNUC_UNUSED PurpleProtocol *protocol, PurpleAccount *account) {
    - PurpleConnection *gc = purple_account_get_connection(account);
    - PurpleContactInfo *contact_info = PURPLE_CONTACT_INFO(account);
    - struct gg_login_params *glp;
    - GGPInfo *info;
    - const char *address;
    - const gchar *encryption_type, *protocol_version;
    - GProxyResolver *resolver;
    - GError *error = NULL;
    -
    - purple_connection_set_flags(gc,
    - PURPLE_CONNECTION_FLAG_HTML |
    - PURPLE_CONNECTION_FLAG_NO_URLDESC);
    -
    - resolver = purple_proxy_get_proxy_resolver(account, &error);
    - if (resolver == NULL) {
    - purple_debug_error("gg", "Unable to get account proxy resolver: %s",
    - error->message);
    - purple_connection_take_error(gc, error);
    - return;
    - }
    -
    - glp = g_new0(struct gg_login_params, 1);
    - glp->struct_size = sizeof(struct gg_login_params);
    - info = g_new0(GGPInfo, 1);
    -
    - purple_connection_set_protocol_data(gc, info);
    -
    - info->http = soup_session_new_with_options("proxy-resolver", resolver,
    - NULL);
    -
    - ggp_tcpsocket_setup(gc, glp);
    - ggp_image_setup(gc);
    - ggp_avatar_setup(gc);
    - ggp_roster_setup(gc);
    - ggp_multilogon_setup(gc);
    - ggp_status_setup(gc);
    - ggp_chat_setup(gc);
    - ggp_message_setup(gc);
    - ggp_edisc_setup(gc, resolver);
    - g_object_unref(resolver);
    -
    - glp->uin = ggp_str_to_uin(purple_contact_info_get_username(contact_info));
    - glp->password =
    - ggp_convert_to_cp1250(purple_connection_get_password(gc));
    -
    - if (glp->uin == 0) {
    - purple_connection_error(gc,
    - PURPLE_CONNECTION_ERROR_INVALID_USERNAME,
    - _("The username specified is invalid."));
    - purple_str_wipe(glp->password);
    - g_free(glp);
    - return;
    - }
    -
    - glp->image_size = 255;
    - glp->status_flags = GG_STATUS_FLAG_UNKNOWN;
    -
    - if (purple_account_get_bool(account, "show_links_from_strangers", 1))
    - glp->status_flags |= GG_STATUS_FLAG_SPAM;
    -
    - glp->encoding = GG_ENCODING_UTF8;
    - glp->protocol_features = (GG_FEATURE_DND_FFC |
    - GG_FEATURE_TYPING_NOTIFICATION | GG_FEATURE_MULTILOGON |
    - GG_FEATURE_USER_DATA);
    -
    - glp->async = 1;
    -
    - encryption_type = purple_account_get_string(account, "encryption",
    - "opportunistic_tls");
    - purple_debug_info("gg", "Requested encryption type: %s\n",
    - encryption_type);
    - if (purple_strequal(encryption_type, "opportunistic_tls"))
    - glp->tls = GG_SSL_ENABLED;
    - else if (purple_strequal(encryption_type, "require_tls")) {
    - if (gg_libgadu_check_feature(GG_LIBGADU_FEATURE_SSL))
    - glp->tls = GG_SSL_REQUIRED;
    - else {
    - purple_connection_error(gc,
    - PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT,
    - _("SSL support unavailable"));
    - purple_str_wipe(glp->password);
    - g_free(glp);
    - return;
    - }
    - }
    - else /* encryption_type == "none" */
    - glp->tls = GG_SSL_DISABLED;
    - purple_debug_misc("gg", "TLS mode: %d\n", glp->tls);
    -
    - protocol_version = purple_account_get_string(account,
    - "protocol_version", "default");
    - purple_debug_info("gg", "Requested protocol version: %s\n",
    - protocol_version);
    - if (purple_strequal(protocol_version, "gg10"))
    - glp->protocol_version = GG_PROTOCOL_VERSION_100;
    - else if (purple_strequal(protocol_version, "gg11"))
    - glp->protocol_version = GG_PROTOCOL_VERSION_110;
    - glp->compatibility = GG_COMPAT_1_12_0;
    -
    - ggp_status_set_initial(gc, glp);
    -
    - address = purple_account_get_string(account, "gg_server", "");
    - if (address && *address)
    - glp->connect_host = g_strdup(address);
    -
    - info->session = gg_login(glp);
    - g_free(glp->connect_host);
    - purple_str_wipe(glp->password);
    - g_free(glp);
    -
    - if (info->session == NULL) {
    - purple_connection_error (gc,
    - PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
    - _("Connection failed"));
    - return;
    - }
    -
    - if (info->session->fd > 0) {
    - info->inpa = purple_input_add(info->session->fd,
    - PURPLE_INPUT_READ, ggp_async_login_handler, gc);
    - }
    -}
    -
    -static void
    -ggp_close(G_GNUC_UNUSED PurpleProtocol *protocol, PurpleConnection *gc) {
    - PurpleAccount *account;
    - GGPInfo *info;;
    -
    - g_return_if_fail(gc != NULL);
    -
    - account = purple_connection_get_account(gc);
    - info = purple_connection_get_protocol_data(gc);
    -
    - purple_notify_close_with_handle(gc);
    -
    - if (info) {
    - if (info->session != NULL) {
    - ggp_status_set_disconnected(account);
    - gg_logoff(info->session);
    - gg_free_session(info->session);
    - }
    -
    - ggp_image_cleanup(gc);
    - ggp_avatar_cleanup(gc);
    - ggp_roster_cleanup(gc);
    - ggp_multilogon_cleanup(gc);
    - ggp_status_cleanup(gc);
    - ggp_chat_cleanup(gc);
    - ggp_message_cleanup(gc);
    - ggp_edisc_cleanup(gc);
    -
    - g_clear_handle_id(&info->inpa, g_source_remove);
    - g_free(info->imtoken);
    -
    - if (info->http) {
    - soup_session_abort(info->http);
    - g_object_unref(info->http);
    - }
    -
    - purple_connection_set_protocol_data(gc, NULL);
    - g_free(info);
    - }
    -
    - purple_debug_info("gg", "Connection closed.\n");
    -}
    -
    -static unsigned int
    -ggp_send_typing(G_GNUC_UNUSED PurpleProtocolIM *im, PurpleConnection *gc,
    - const char *name, PurpleIMTypingState state)
    -{
    - GGPInfo *info = purple_connection_get_protocol_data(gc);
    - int dummy_length; /* we don't send real length of typed message */
    -
    - if (state == PURPLE_IM_TYPED) /* not supported */
    - return 1;
    -
    - if (state == PURPLE_IM_TYPING)
    - dummy_length = (int)g_random_int();
    - else /* PURPLE_IM_NOT_TYPING */
    - dummy_length = 0;
    -
    - gg_typing_notification(
    - info->session,
    - ggp_str_to_uin(name),
    - dummy_length);
    -
    - return 1; /* wait 1 second before another notification */
    -}
    -
    -static void
    -ggp_add_buddy(PurpleProtocolServer *protocol_server, PurpleConnection *gc,
    - PurpleBuddy *buddy, PurpleGroup *group, const gchar *message)
    -{
    - PurpleAccount *account = purple_connection_get_account(gc);
    - PurpleContactInfo *contact_info = PURPLE_CONTACT_INFO(account);
    - GGPInfo *info = purple_connection_get_protocol_data(gc);
    - const gchar *name = purple_buddy_get_name(buddy);
    -
    - gg_add_notify(info->session, ggp_str_to_uin(name));
    -
    - /* gg server won't tell us our status here */
    - if(purple_strequal(purple_contact_info_get_username(contact_info), name)) {
    - ggp_status_fake_to_self(gc);
    - }
    -
    - ggp_roster_add_buddy(protocol_server, gc, buddy, group, message);
    - ggp_pubdir_request_buddy_alias(gc, buddy);
    -}
    -
    -static void
    -ggp_remove_buddy(PurpleProtocolServer *protocol_server, PurpleConnection *gc,
    - PurpleBuddy *buddy, PurpleGroup *group)
    -{
    - GGPInfo *info = purple_connection_get_protocol_data(gc);
    -
    - gg_remove_notify(info->session, ggp_str_to_uin(purple_buddy_get_name(buddy)));
    - ggp_roster_remove_buddy(protocol_server, gc, buddy, group);
    -}
    -
    -static void
    -ggp_keepalive(G_GNUC_UNUSED PurpleProtocolServer *protocol_server,
    - PurpleConnection *gc)
    -{
    - GGPInfo *info = purple_connection_get_protocol_data(gc);
    -
    - /* purple_debug_info("gg", "Keeping connection alive....\n"); */
    -
    - if (gg_ping(info->session) < 0) {
    - purple_debug_info("gg", "Not connected to the server "
    - "or gg_session is not correct\n");
    - purple_connection_error (gc,
    - PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
    - _("Not connected to the server"));
    - }
    -}
    -
    -static void
    -ggp_action_multilogon(G_GNUC_UNUSED GSimpleAction *action,
    - GVariant *parameter,
    - G_GNUC_UNUSED gpointer data)
    -{
    - const gchar *account_id = NULL;
    - PurpleAccountManager *manager = NULL;
    - PurpleAccount *account = NULL;
    - PurpleConnection *connection = NULL;
    -
    - if(!g_variant_is_of_type(parameter, G_VARIANT_TYPE_STRING)) {
    - g_critical("GG multilogin action parameter is of incorrect type %s",
    - g_variant_get_type_string(parameter));
    - }
    -
    - account_id = g_variant_get_string(parameter, NULL);
    - manager = purple_account_manager_get_default();
    - account = purple_account_manager_find_by_id(manager, account_id);
    - connection = purple_account_get_connection(account);
    - g_clear_object(&account);
    -
    - ggp_multilogon_dialog(connection);
    -}
    -
    -static void
    -ggp_action_status_broadcasting(G_GNUC_UNUSED GSimpleAction *action,
    - GVariant *parameter,
    - G_GNUC_UNUSED gpointer data)
    -{
    - const gchar *account_id = NULL;
    - PurpleAccountManager *manager = NULL;
    - PurpleAccount *account = NULL;
    - PurpleConnection *connection = NULL;
    -
    - if(!g_variant_is_of_type(parameter, G_VARIANT_TYPE_STRING)) {
    - g_critical("GG broadcast action parameter is of incorrect type %s",
    - g_variant_get_type_string(parameter));
    - }
    -
    - account_id = g_variant_get_string(parameter, NULL);
    - manager = purple_account_manager_get_default();
    - account = purple_account_manager_find_by_id(manager, account_id);
    - connection = purple_account_get_connection(account);
    - g_clear_object(&account);
    -
    - ggp_status_broadcasting_dialog(connection);
    -}
    -
    -static void
    -ggp_action_search(G_GNUC_UNUSED GSimpleAction *action,
    - GVariant *parameter,
    - G_GNUC_UNUSED gpointer data)
    -{
    - const gchar *account_id = NULL;
    - PurpleAccountManager *manager = NULL;
    - PurpleAccount *account = NULL;
    - PurpleConnection *connection = NULL;
    -
    - if(!g_variant_is_of_type(parameter, G_VARIANT_TYPE_STRING)) {
    - g_critical("GG search action parameter is of incorrect type %s",
    - g_variant_get_type_string(parameter));
    - }
    -
    - account_id = g_variant_get_string(parameter, NULL);
    - manager = purple_account_manager_get_default();
    - account = purple_account_manager_find_by_id(manager, account_id);
    - connection = purple_account_get_connection(account);
    - g_clear_object(&account);
    -
    - ggp_pubdir_search(connection, NULL);
    -}
    -
    -static void
    -ggp_action_set_info(G_GNUC_UNUSED GSimpleAction *action,
    - GVariant *parameter,
    - G_GNUC_UNUSED gpointer data)
    -{
    - const gchar *account_id = NULL;
    - PurpleAccountManager *manager = NULL;
    - PurpleAccount *account = NULL;
    - PurpleConnection *connection = NULL;
    -
    - if(!g_variant_is_of_type(parameter, G_VARIANT_TYPE_STRING)) {
    - g_critical("GG set info action parameter is of incorrect type %s",
    - g_variant_get_type_string(parameter));
    - }
    -
    - account_id = g_variant_get_string(parameter, NULL);
    - manager = purple_account_manager_get_default();
    - account = purple_account_manager_find_by_id(manager, account_id);
    - connection = purple_account_get_connection(account);
    - g_clear_object(&account);
    -
    - ggp_pubdir_set_info(connection);
    -}
    -
    -static const gchar *
    -ggp_protocol_actions_get_prefix(G_GNUC_UNUSED PurpleProtocolActions *actions) {
    - return "prpl-gg";
    -}
    -
    -static GActionGroup *
    -ggp_protocol_actions_get_action_group(G_GNUC_UNUSED PurpleProtocolActions *actions,
    - G_GNUC_UNUSED PurpleConnection *connection)
    -{
    - GSimpleActionGroup *group = NULL;
    - GActionEntry entries[] = {
    - {
    - .name = "multilogon",
    - .activate = ggp_action_multilogon,
    - .parameter_type = "s",
    - },
    - {
    - .name = "broadcasting",
    - .activate = ggp_action_status_broadcasting,
    - .parameter_type = "s",
    - },
    - {
    - .name = "search",
    - .activate = ggp_action_search,
    - .parameter_type = "s",
    - },
    - {
    - .name = "set-info",
    - .activate = ggp_action_set_info,
    - .parameter_type = "s",
    - },
    - {
    - .name = "save-buddylist",
    - .activate = ggp_action_buddylist_save,
    - .parameter_type = "s",
    - },
    - {
    - .name = "load-buddylist",
    - .activate = ggp_action_buddylist_load,
    - .parameter_type = "s",
    - },
    - };
    - gsize nentries = G_N_ELEMENTS(entries);
    -
    - group = g_simple_action_group_new();
    - g_action_map_add_action_entries(G_ACTION_MAP(group), entries, nentries,
    - NULL);
    -
    - return G_ACTION_GROUP(group);
    -}
    -
    -static GMenu *
    -ggp_protocol_actions_get_menu(G_GNUC_UNUSED PurpleProtocolActions *actions,
    - G_GNUC_UNUSED PurpleConnection *connection) {
    - GMenu *menu = NULL, *submenu = NULL;
    - GMenuItem *item = NULL;
    -
    - menu = g_menu_new();
    -
    - item = g_menu_item_new(_("Show other sessions"), "prpl-gg.multilogon");
    - g_menu_item_set_attribute(item, PURPLE_MENU_ATTRIBUTE_DYNAMIC_TARGET, "s",
    - "account");
    - g_menu_append_item(menu, item);
    - g_object_unref(item);
    -
    - item = g_menu_item_new(_("Show status only for buddies"),
    - "prpl-gg.broadcasting");
    - g_menu_item_set_attribute(item, PURPLE_MENU_ATTRIBUTE_DYNAMIC_TARGET, "s",
    - "account");
    - g_menu_append_item(menu, item);
    - g_object_unref(item);
    -
    - item = g_menu_item_new(_("Find buddies..."), "prpl-gg.search");
    - g_menu_item_set_attribute(item, PURPLE_MENU_ATTRIBUTE_DYNAMIC_TARGET, "s",
    - "account");
    - g_menu_append_item(menu, item);
    - g_object_unref(item);
    -
    - item = g_menu_item_new(_("Set User Info"), "prpl-gg.set-info");
    - g_menu_item_set_attribute(item, PURPLE_MENU_ATTRIBUTE_DYNAMIC_TARGET, "s",
    - "account");
    - g_menu_append_item(menu, item);
    - g_object_unref(item);
    -
    - /* Buddy list management. */
    - submenu = g_menu_new();
    -
    - item = g_menu_item_new(_("Save to file..."), "prpl-gg.save-buddylist");
    - g_menu_item_set_attribute(item, PURPLE_MENU_ATTRIBUTE_DYNAMIC_TARGET, "s",
    - "account");
    - g_menu_append_item(menu, item);
    - g_object_unref(item);
    -
    - item = g_menu_item_new(_("Load from file..."), "prpl-gg.load-buddylist");
    - g_menu_item_set_attribute(item, PURPLE_MENU_ATTRIBUTE_DYNAMIC_TARGET, "s",
    - "account");
    - g_menu_append_item(menu, item);
    - g_object_unref(item);
    -
    - g_menu_append_submenu(menu, _("Buddy list"), G_MENU_MODEL(submenu));
    - g_object_unref(submenu);
    -
    - return menu;
    -}
    -
    -static const char *
    -ggp_list_emblem(G_GNUC_UNUSED PurpleProtocolClient *client,
    - PurpleBuddy *buddy)
    -{
    - ggp_buddy_data *buddy_data = ggp_buddy_get_data(buddy);
    -
    - if (buddy_data->blocked)
    - return "not-authorized";
    - if (buddy_data->not_a_friend)
    - return "unavailable";
    -
    - return NULL;
    -}
    -
    -static gboolean
    -ggp_offline_message(G_GNUC_UNUSED PurpleProtocolClient *client,
    - G_GNUC_UNUSED PurpleBuddy *buddy)
    -{
    - return TRUE;
    -}
    -
    -static GHashTable *
    -ggp_get_account_text_table(G_GNUC_UNUSED PurpleProtocolClient *client,
    - G_GNUC_UNUSED PurpleAccount *account)
    -{
    - GHashTable *table;
    - table = g_hash_table_new(g_str_hash, g_str_equal);
    - g_hash_table_insert(table, "login_label", (gpointer)_("GG number..."));
    - return table;
    -}
    -
    -static gssize
    -ggp_get_max_message_size(G_GNUC_UNUSED PurpleProtocolClient *client,
    - G_GNUC_UNUSED PurpleConversation *conv)
    -{
    - /* TODO: it may depend on protocol version or other factors */
    - return 1200; /* no more than 1232 */
    -}
    -
    -static void
    -ggp_protocol_init(G_GNUC_UNUSED GGPProtocol *self) {
    -}
    -
    -static void
    -ggp_protocol_class_init(GGPProtocolClass *klass)
    -{
    - PurpleProtocolClass *protocol_class = PURPLE_PROTOCOL_CLASS(klass);
    -
    - protocol_class->login = ggp_login;
    - protocol_class->close = ggp_close;
    - protocol_class->status_types = ggp_status_types;
    -
    - protocol_class->get_account_options = ggp_protocol_get_account_options;
    - protocol_class->get_buddy_icon_spec = ggp_protocol_get_buddy_icon_spec;
    -}
    -
    -static void
    -ggp_protocol_class_finalize(G_GNUC_UNUSED GGPProtocolClass *klass)
    -{
    -}
    -
    -static void
    -ggp_protocol_actions_iface_init(PurpleProtocolActionsInterface *iface)
    -{
    - iface->get_prefix = ggp_protocol_actions_get_prefix;
    - iface->get_action_group = ggp_protocol_actions_get_action_group;
    - iface->get_menu = ggp_protocol_actions_get_menu;
    -}
    -
    -static void
    -ggp_protocol_client_iface_init(PurpleProtocolClientInterface *client_iface)
    -{
    - client_iface->list_emblem = ggp_list_emblem;
    - client_iface->buddy_free = ggp_buddy_free;
    - client_iface->normalize = ggp_normalize;
    - client_iface->offline_message = ggp_offline_message;
    - client_iface->get_account_text_table = ggp_get_account_text_table;
    - client_iface->get_max_message_size = ggp_get_max_message_size;
    -}
    -
    -static void
    -ggp_protocol_server_iface_init(PurpleProtocolServerInterface *server_iface)
    -{
    - server_iface->get_info = ggp_pubdir_get_info_protocol;
    - server_iface->set_status = ggp_status_set_purplestatus;
    - server_iface->add_buddy = ggp_add_buddy;
    - server_iface->remove_buddy = ggp_remove_buddy;
    - server_iface->keepalive = ggp_keepalive;
    - server_iface->alias_buddy = ggp_roster_alias_buddy;
    - server_iface->group_buddy = ggp_roster_group_buddy;
    - server_iface->rename_group = ggp_roster_rename_group;
    - server_iface->set_buddy_icon = ggp_avatar_own_set;
    -}
    -
    -static void
    -ggp_protocol_im_iface_init(PurpleProtocolIMInterface *im_iface)
    -{
    - im_iface->send = ggp_message_send_im;
    - im_iface->send_typing = ggp_send_typing;
    -}
    -
    -static void
    -ggp_protocol_chat_iface_init(PurpleProtocolChatInterface *chat_iface)
    -{
    - chat_iface->info = ggp_chat_info;
    - chat_iface->info_defaults = ggp_chat_info_defaults;
    - chat_iface->join = ggp_chat_join;
    - chat_iface->get_name = ggp_chat_get_name;
    - chat_iface->invite = ggp_chat_invite;
    - chat_iface->leave = ggp_chat_leave;
    - chat_iface->send = ggp_chat_send;
    -
    - chat_iface->reject = NULL; /* TODO */
    -}
    -
    -static void
    -ggp_protocol_roomlist_iface_init(PurpleProtocolRoomlistInterface *roomlist_iface)
    -{
    - roomlist_iface->get_list = ggp_chat_roomlist_get_list;
    -}
    -
    -static void
    -ggp_protocol_xfer_iface_init(PurpleProtocolXferInterface *xfer_iface)
    -{
    - xfer_iface->can_receive = ggp_edisc_xfer_can_receive_file;
    - xfer_iface->send_file = ggp_edisc_xfer_send_file;
    - xfer_iface->new_xfer = ggp_edisc_xfer_send_new;
    -}
    -
    -G_DEFINE_DYNAMIC_TYPE_EXTENDED(
    - GGPProtocol,
    - ggp_protocol,
    - PURPLE_TYPE_PROTOCOL,
    - G_TYPE_FLAG_FINAL,
    - G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_ACTIONS,
    - ggp_protocol_actions_iface_init)
    - G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_CLIENT,
    - ggp_protocol_client_iface_init)
    - G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_SERVER,
    - ggp_protocol_server_iface_init)
    - G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_IM,
    - ggp_protocol_im_iface_init)
    - G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_CHAT,
    - ggp_protocol_chat_iface_init)
    - G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_ROOMLIST,
    - ggp_protocol_roomlist_iface_init)
    - G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_XFER,
    - ggp_protocol_xfer_iface_init))
    -
    -static PurpleProtocol *
    -ggp_protocol_new(void) {
    - return g_object_new(
    - GGP_TYPE_PROTOCOL,
    - "id", "prpl-gg",
    - "name", "Gadu-Gadu",
    - "description", _("Gadu-Gadu is a Polish instant messaging network."),
    - "icon-name", "im-gadu-gadu",
    - "icon-resource-path", "/im/pidgin/libpurple/gg/icons",
    - NULL);
    -}
    -
    -static GPluginPluginInfo *
    -gg_query(G_GNUC_UNUSED GError **error)
    -{
    - GPluginPluginInfo *info = NULL;
    - gchar *description = NULL;
    - const gchar * const authors[] = {
    - "boler@sourceforge.net",
    - NULL
    - };
    -
    - description = g_strdup_printf(N_("Polish popular IM\nlibgadu version %s"),
    - gg_libgadu_version());
    -
    - info = purple_plugin_info_new(
    - "id", "prpl-gg",
    - "name", "Gadu-Gadu Protocol",
    - "version", DISPLAY_VERSION,
    - "category", N_("Protocol"),
    - "summary", N_("Gadu-Gadu Protocol Plugin"),
    - "description", description,
    - "authors", authors,
    - "website", PURPLE_WEBSITE,
    - "abi-version", PURPLE_ABI_VERSION,
    - "flags", PURPLE_PLUGIN_INFO_FLAGS_INTERNAL |
    - PURPLE_PLUGIN_INFO_FLAGS_AUTO_LOAD,
    - NULL
    - );
    -
    - g_free(description);
    -
    - return info;
    -}
    -
    -static gboolean
    -gg_load(GPluginPlugin *plugin, GError **error)
    -{
    - PurpleProtocolManager *manager = purple_protocol_manager_get_default();
    -
    - ggp_protocol_register_type(G_TYPE_MODULE(plugin));
    -
    - ggp_xfer_register(G_TYPE_MODULE(plugin));
    -
    - my_protocol = ggp_protocol_new();
    - if(!purple_protocol_manager_register(manager, my_protocol, error)) {
    - g_clear_object(&my_protocol);
    -
    - return FALSE;
    - }
    -
    - purple_prefs_add_none("/plugins/prpl/gg");
    -
    - purple_debug_info("gg", "Loading Gadu-Gadu protocol plugin with "
    - "libgadu %s...\n", gg_libgadu_version());
    -
    - ggp_libgaduw_setup();
    - ggp_resolver_purple_setup();
    - ggp_servconn_setup(NULL);
    - ggp_html_setup();
    - ggp_message_setup_global();
    -
    - purple_signal_connect(purple_get_core(), "uri-handler", plugin,
    - G_CALLBACK(gg_uri_handler), NULL);
    -
    - return TRUE;
    -}
    -
    -static gboolean
    -gg_unload(GPluginPlugin *plugin, G_GNUC_UNUSED gboolean shutdown,
    - GError **error)
    -{
    - PurpleProtocolManager *manager = purple_protocol_manager_get_default();
    -
    - if(!purple_protocol_manager_unregister(manager, my_protocol, error)) {
    - return FALSE;
    - }
    -
    - purple_signal_disconnect(purple_get_core(), "uri-handler", plugin,
    - G_CALLBACK(gg_uri_handler));
    -
    - ggp_servconn_cleanup();
    - ggp_html_cleanup();
    - ggp_message_cleanup_global();
    - ggp_libgaduw_cleanup();
    -
    - g_clear_object(&my_protocol);
    -
    - return TRUE;
    -}
    -
    -GPLUGIN_NATIVE_PLUGIN_DECLARE(gg)
    -
    -/* vim: set ts=8 sts=0 sw=8 noet: */
    --- a/libpurple/protocols/gg/gg.h Tue Apr 09 21:50:31 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,82 +0,0 @@
    -/**
    - * @file gg.h
    - *
    - * purple
    - *
    - * Copyright (C) 2005 Bartosz Oler <bartosz@bzimage.us>
    - *
    - * 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
    - */
    -
    -#ifndef PURPLE_GG_GG_H
    -#define PURPLE_GG_GG_H
    -
    -#define GGP_UIN_LEN_MAX 10
    -
    -#include <gmodule.h>
    -#include <libgadu.h>
    -#include <libsoup/soup.h>
    -
    -#include "search.h"
    -
    -#include <purple.h>
    -
    -#include "image-prpl.h"
    -#include "avatar.h"
    -#include "roster.h"
    -#include "multilogon.h"
    -#include "status.h"
    -#include "chat.h"
    -#include "message-prpl.h"
    -#include "edisc.h"
    -
    -#define GGP_TYPE_PROTOCOL (ggp_protocol_get_type())
    -
    -G_MODULE_EXPORT
    -G_DECLARE_FINAL_TYPE(GGPProtocol, ggp_protocol, GGP, PROTOCOL, PurpleProtocol)
    -
    -typedef struct {
    - struct gg_session *session;
    - SoupSession *http;
    - guint inpa;
    -
    - gchar *imtoken;
    - gboolean imtoken_warned;
    -
    - ggp_image_session_data *image_data;
    - ggp_avatar_session_data *avatar_data;
    - ggp_roster_session_data roster_data;
    - ggp_multilogon_session_data *multilogon_data;
    - ggp_status_session_data *status_data;
    - ggp_chat_session_data *chat_data;
    - ggp_message_session_data *message_data;
    - ggp_edisc_session_data *edisc_data;
    -} GGPInfo;
    -
    -typedef struct
    -{
    - gboolean blocked;
    - gboolean not_a_friend;
    -} ggp_buddy_data;
    -
    -ggp_buddy_data * ggp_buddy_get_data(PurpleBuddy *buddy);
    -
    -const gchar * ggp_get_imtoken(PurpleConnection *gc);
    -
    -uin_t ggp_own_uin(PurpleConnection *gc);
    -
    -void ggp_async_login_handler(gpointer _gc, gint fd, PurpleInputCondition cond);
    -
    -#endif /* PURPLE_GG_GG_H */
    --- a/libpurple/protocols/gg/html.c Tue Apr 09 21:50:31 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,179 +0,0 @@
    -/* 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.
    - *
    - * Component written by Tomek Wasilczyk (http://www.wasilczyk.pl).
    - *
    - * This file is dual-licensed under the GPL2+ and the X11 (MIT) licences.
    - * As a recipient of this file you may choose, which license to receive the
    - * code under. As a contributor, you have to ensure the new code is
    - * compatible with both.
    - *
    - * 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 "html.h"
    -
    -typedef struct
    -{
    - GRegex *re_html_attr;
    - GRegex *re_css_attr;
    - GRegex *re_color_hex;
    - GRegex *re_color_rgb;
    -} ggp_html_global_data;
    -
    -static ggp_html_global_data global_data;
    -
    -void ggp_html_setup(void)
    -{
    - global_data.re_html_attr = g_regex_new(
    - "([a-z-]+)=\"([^\"]+)\"",
    - G_REGEX_OPTIMIZE, 0, NULL);
    - global_data.re_css_attr = g_regex_new(
    - "([a-z-]+): *([^;]+)",
    - G_REGEX_OPTIMIZE, 0, NULL);
    - global_data.re_color_hex = g_regex_new(
    - "^#([0-9a-fA-F]+){6}$",
    - G_REGEX_OPTIMIZE, 0, NULL);
    - global_data.re_color_rgb = g_regex_new(
    - "^rgb\\(([0-9]+), *([0-9]+), *([0-9]+)\\)$",
    - G_REGEX_OPTIMIZE, 0, NULL);
    -}
    -
    -void ggp_html_cleanup(void)
    -{
    - g_regex_unref(global_data.re_html_attr);
    - g_regex_unref(global_data.re_css_attr);
    - g_regex_unref(global_data.re_color_hex);
    - g_regex_unref(global_data.re_color_rgb);
    -}
    -
    -GHashTable * ggp_html_tag_attribs(const gchar *attribs_str)
    -{
    - GMatchInfo *match;
    - GHashTable *attribs = g_hash_table_new_full(g_str_hash, g_str_equal,
    - g_free, g_free);
    -
    - if (attribs_str == NULL)
    - return attribs;
    -
    - g_regex_match(global_data.re_html_attr, attribs_str, 0, &match);
    - while (g_match_info_matches(match)) {
    - g_hash_table_insert(attribs,
    - g_match_info_fetch(match, 1),
    - g_match_info_fetch(match, 2));
    -
    - g_match_info_next(match, NULL);
    - }
    - g_match_info_free(match);
    -
    - return attribs;
    -}
    -
    -GHashTable * ggp_html_css_attribs(const gchar *attribs_str)
    -{
    - GMatchInfo *match;
    - GHashTable *attribs = g_hash_table_new_full(g_str_hash, g_str_equal,
    - g_free, g_free);
    -
    - if (attribs_str == NULL)
    - return attribs;
    -
    - g_regex_match(global_data.re_css_attr, attribs_str, 0, &match);
    - while (g_match_info_matches(match)) {
    - g_hash_table_insert(attribs,
    - g_match_info_fetch(match, 1),
    - g_match_info_fetch(match, 2));
    -
    - g_match_info_next(match, NULL);
    - }
    - g_match_info_free(match);
    -
    - return attribs;
    -}
    -
    -int ggp_html_decode_color(const gchar *str)
    -{
    - GMatchInfo *match;
    - int color = -1;
    -
    - g_regex_match(global_data.re_color_hex, str, 0, &match);
    - if (g_match_info_matches(match)) {
    - if (sscanf(str + 1, "%x", &color) != 1)
    - color = -1;
    - }
    - g_match_info_free(match);
    - if (color >= 0)
    - return color;
    -
    - g_regex_match(global_data.re_color_rgb, str, 0, &match);
    - if (g_match_info_matches(match)) {
    - int r = -1, g = -1, b = -1;
    - gchar *c_str;
    -
    - c_str = g_match_info_fetch(match, 1);
    - if (c_str)
    - r = atoi(c_str);
    - g_free(c_str);
    -
    - c_str = g_match_info_fetch(match, 2);
    - if (c_str)
    - g = atoi(c_str);
    - g_free(c_str);
    -
    - c_str = g_match_info_fetch(match, 3);
    - if (c_str)
    - b = atoi(c_str);
    - g_free(c_str);
    -
    - if (r >= 0 && r < 256 && g >= 0 && g < 256 && b >= 0 && b < 256)
    - color = (r << 16) | (g << 8) | b;
    - }
    - g_match_info_free(match);
    - if (color >= 0)
    - return color;
    -
    - return -1;
    -}
    -
    -ggp_html_tag ggp_html_parse_tag(const gchar *tag_str)
    -{
    - if (0 == g_ascii_strcasecmp(tag_str, "eom"))
    - return GGP_HTML_TAG_EOM;
    - if (0 == g_ascii_strcasecmp(tag_str, "span"))
    - return GGP_HTML_TAG_SPAN;
    - if (0 == g_ascii_strcasecmp(tag_str, "div"))
    - return GGP_HTML_TAG_DIV;
    - if (0 == g_ascii_strcasecmp(tag_str, "br"))
    - return GGP_HTML_TAG_BR;
    - if (0 == g_ascii_strcasecmp(tag_str, "a"))
    - return GGP_HTML_TAG_A;
    - if (0 == g_ascii_strcasecmp(tag_str, "b"))
    - return GGP_HTML_TAG_B;
    - if (0 == g_ascii_strcasecmp(tag_str, "i"))
    - return GGP_HTML_TAG_I;
    - if (0 == g_ascii_strcasecmp(tag_str, "u"))
    - return GGP_HTML_TAG_U;
    - if (0 == g_ascii_strcasecmp(tag_str, "s"))
    - return GGP_HTML_TAG_S;
    - if (0 == g_ascii_strcasecmp(tag_str, "img"))
    - return GGP_HTML_TAG_IMG;
    - if (0 == g_ascii_strcasecmp(tag_str, "font"))
    - return GGP_HTML_TAG_FONT;
    - if (0 == g_ascii_strcasecmp(tag_str, "hr"))
    - return GGP_HTML_TAG_HR;
    - return GGP_HTML_TAG_UNKNOWN;
    -}
    --- a/libpurple/protocols/gg/html.h Tue Apr 09 21:50:31 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,59 +0,0 @@
    -/* 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.
    - *
    - * Component written by Tomek Wasilczyk (http://www.wasilczyk.pl).
    - *
    - * This file is dual-licensed under the GPL2+ and the X11 (MIT) licences.
    - * As a recipient of this file you may choose, which license to receive the
    - * code under. As a contributor, you have to ensure the new code is
    - * compatible with both.
    - *
    - * 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
    - */
    -
    -#ifndef PURPLE_GG_HTML_H
    -#define PURPLE_GG_HTML_H
    -
    -#include <purple.h>
    -
    -typedef enum
    -{
    - GGP_HTML_TAG_UNKNOWN,
    - GGP_HTML_TAG_EOM,
    - GGP_HTML_TAG_A,
    - GGP_HTML_TAG_B,
    - GGP_HTML_TAG_I,
    - GGP_HTML_TAG_U,
    - GGP_HTML_TAG_S,
    - GGP_HTML_TAG_IMG,
    - GGP_HTML_TAG_FONT,
    - GGP_HTML_TAG_SPAN,
    - GGP_HTML_TAG_DIV,
    - GGP_HTML_TAG_BR,
    - GGP_HTML_TAG_HR,
    -} ggp_html_tag;
    -
    -void ggp_html_setup(void);
    -void ggp_html_cleanup(void);
    -
    -GHashTable * ggp_html_tag_attribs(const gchar *attribs_str);
    -GHashTable * ggp_html_css_attribs(const gchar *attribs_str);
    -int ggp_html_decode_color(const gchar *str);
    -ggp_html_tag ggp_html_parse_tag(const gchar *tag_str);
    -
    -#endif /* PURPLE_GG_HTML_H */
    --- a/libpurple/protocols/gg/image-prpl.c Tue Apr 09 21:50:31 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,262 +0,0 @@
    -/* 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 "image-prpl.h"
    -
    -#include "gg.h"
    -#include "utils.h"
    -
    -#include <purple.h>
    -
    -struct _ggp_image_session_data
    -{
    - GHashTable *recv_images;
    - GHashTable *sent_images;
    -};
    -
    -typedef struct
    -{
    - PurpleImage *image;
    - gchar *conv_name; /* TODO: callback */
    -} ggp_image_sent;
    -
    -static void ggp_image_sent_free(gpointer _sent_image)
    -{
    - ggp_image_sent *sent_image = _sent_image;
    - g_object_unref(sent_image->image);
    - g_free(sent_image->conv_name);
    - g_free(sent_image);
    -}
    -
    -static uint64_t ggp_image_params_to_id(uint32_t crc32, uint32_t size)
    -{
    - return ((uint64_t)crc32 << 32) | size;
    -}
    -
    -static inline ggp_image_session_data *
    -ggp_image_get_sdata(PurpleConnection *gc)
    -{
    - GGPInfo *accdata = purple_connection_get_protocol_data(gc);
    - return accdata->image_data;
    -}
    -
    -void ggp_image_setup(PurpleConnection *gc)
    -{
    - GGPInfo *accdata = purple_connection_get_protocol_data(gc);
    - ggp_image_session_data *sdata = g_new0(ggp_image_session_data, 1);
    -
    - accdata->image_data = sdata;
    -
    - sdata->recv_images = g_hash_table_new_full(
    - g_int64_hash, g_int64_equal, g_free, g_object_unref);
    - sdata->sent_images = g_hash_table_new_full(
    - g_int64_hash, g_int64_equal, g_free,
    - ggp_image_sent_free);
    -}
    -
    -void ggp_image_cleanup(PurpleConnection *gc)
    -{
    - ggp_image_session_data *sdata = ggp_image_get_sdata(gc);
    -
    - g_hash_table_destroy(sdata->recv_images);
    - g_hash_table_destroy(sdata->sent_images);
    - g_free(sdata);
    -}
    -
    -ggp_image_prepare_result
    -ggp_image_prepare(PurpleConversation *conv, PurpleImage *image, uint64_t *id)
    -{
    - PurpleConnection *gc = purple_conversation_get_connection(conv);
    - ggp_image_session_data *sdata = ggp_image_get_sdata(gc);
    - size_t image_size;
    - gconstpointer image_data;
    - uint32_t image_crc;
    - ggp_image_sent *sent_image;
    -
    - g_return_val_if_fail(image, GGP_IMAGE_PREPARE_FAILURE);
    -
    - image_size = purple_image_get_data_size(image);
    -
    - if (image_size > GGP_IMAGE_SIZE_MAX) {
    - purple_debug_warning("gg", "ggp_image_prepare: image "
    - "is too big (max bytes: %d)\n", GGP_IMAGE_SIZE_MAX);
    - return GGP_IMAGE_PREPARE_TOO_BIG;
    - }
    -
    - g_object_ref(image);
    - image_data = purple_image_get_data(image);
    - image_crc = gg_crc32(0, image_data, image_size);
    -
    - purple_debug_info("gg", "ggp_image_prepare: image prepared "
    - "[crc=%u, size=%" G_GSIZE_FORMAT "]",
    - image_crc, image_size);
    -
    - *id = ggp_image_params_to_id(image_crc, image_size);
    -
    - g_object_ref(image);
    - sent_image = g_new(ggp_image_sent, 1);
    - sent_image->image = image;
    - sent_image->conv_name = g_strdup(purple_conversation_get_name(conv));
    - g_hash_table_insert(sdata->sent_images, ggp_uint64dup(*id),
    - sent_image);
    -
    - return GGP_IMAGE_PREPARE_OK;
    -}
    -
    -void ggp_image_recv(PurpleConnection *gc,
    - const struct gg_event_image_reply *image_reply)
    -{
    - ggp_image_session_data *sdata = ggp_image_get_sdata(gc);
    - PurpleImage *img;
    - uint64_t id;
    -
    - id = ggp_image_params_to_id(image_reply->crc32, image_reply->size);
    - img = g_hash_table_lookup(sdata->recv_images, &id);
    - if (!img) {
    - purple_debug_warning("gg", "ggp_image_recv: "
    - "image " GGP_IMAGE_ID_FORMAT " wasn't requested\n",
    - id);
    - return;
    - }
    -
    - purple_debug_info("gg", "ggp_image_recv: got image "
    - "[crc=%u, size=%u, filename=%s, id=" GGP_IMAGE_ID_FORMAT "]",
    - image_reply->crc32, image_reply->size,
    - image_reply->filename, id);
    -
    - img = purple_image_new_from_data(
    - (const guint8 *)image_reply->image,
    - image_reply->size
    - );
    - purple_image_set_friendly_filename(img, image_reply->filename);
    -
    - g_hash_table_insert(sdata->recv_images, &id, img);
    -}
    -
    -void ggp_image_send(PurpleConnection *gc,
    - const struct gg_event_image_request *image_request)
    -{
    - PurpleAccount *account = purple_connection_get_account(gc);
    - PurpleContactInfo *info = PURPLE_CONTACT_INFO(account);
    - GGPInfo *accdata = purple_connection_get_protocol_data(gc);
    - ggp_image_session_data *sdata = ggp_image_get_sdata(gc);
    - ggp_image_sent *sent_image;
    - PurpleConversation *conv;
    - PurpleConversationManager *manager;
    - uint64_t id;
    - uin_t sender;
    - gchar *gg_filename;
    -
    - purple_debug_info("gg", "ggp_image_send: got image request "
    - "[uin=%u, crc=%u, size=%u]\n",
    - image_request->sender,
    - image_request->crc32,
    - image_request->size);
    -
    - id = ggp_image_params_to_id(image_request->crc32, image_request->size);
    -
    - sent_image = g_hash_table_lookup(sdata->sent_images, &id);
    -
    - sender = ggp_str_to_uin(purple_contact_info_get_username(info));
    - if (sent_image == NULL && image_request->sender == sender) {
    - purple_debug_misc("gg", "ggp_image_send: requested image "
    - "not found, but this may be another session request\n");
    - return;
    - }
    - if (sent_image == NULL) {
    - purple_debug_warning("gg", "ggp_image_send: requested image "
    - "not found\n");
    - return;
    - }
    -
    - purple_debug_misc("gg", "ggp_image_send: requested image found "
    - "[id=" GGP_IMAGE_ID_FORMAT ", conv=%s]\n",
    - id, sent_image->conv_name);
    -
    - g_return_if_fail(sent_image->image);
    -
    - /* TODO: check allowed recipients */
    - gg_filename = g_strdup_printf(GGP_IMAGE_ID_FORMAT, id);
    - gg_image_reply(accdata->session, image_request->sender,
    - gg_filename,
    - purple_image_get_data(sent_image->image),
    - purple_image_get_data_size(sent_image->image));
    - g_free(gg_filename);
    -
    - manager = purple_conversation_manager_get_default();
    - conv = purple_conversation_manager_find(manager,
    - purple_connection_get_account(gc),
    - sent_image->conv_name);
    - if (conv != NULL) {
    - gchar *msg = g_strdup_printf(_("Image delivered to %u."),
    - image_request->sender);
    - purple_conversation_write_system_message(conv, msg,
    - PURPLE_MESSAGE_NO_LOG | PURPLE_MESSAGE_NOTIFY);
    - g_free(msg);
    - }
    -}
    -
    -PurpleImage *
    -ggp_image_request(PurpleConnection *gc, uin_t uin, uint64_t id)
    -{
    - GGPInfo *accdata = purple_connection_get_protocol_data(gc);
    - ggp_image_session_data *sdata = ggp_image_get_sdata(gc);
    - PurpleImage *img;
    - uint32_t crc = id >> 32;
    - uint32_t size = id;
    -
    - if (size > GGP_IMAGE_SIZE_MAX && crc <= GGP_IMAGE_SIZE_MAX) {
    - uint32_t tmp;
    - purple_debug_warning("gg", "ggp_image_request: "
    - "crc and size are swapped!\n");
    - tmp = crc;
    - crc = size;
    - size = tmp;
    - id = ggp_image_params_to_id(crc, size);
    - }
    -
    - img = g_hash_table_lookup(sdata->recv_images, &id);
    - if (img) {
    - purple_debug_info("gg", "ggp_image_request: "
    - "image " GGP_IMAGE_ID_FORMAT " got from cache", id);
    - return img;
    - }
    -
    -
    - g_hash_table_insert(sdata->recv_images, ggp_uint64dup(id), NULL);
    -
    - purple_debug_info("gg", "ggp_image_request: requesting image "
    - GGP_IMAGE_ID_FORMAT, id);
    - if (gg_image_request(accdata->session, uin, size, crc) != 0)
    - purple_debug_error("gg", "ggp_image_request: failed");
    -
    - return img;
    -}
    --- a/libpurple/protocols/gg/image-prpl.h Tue Apr 09 21:50:31 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,68 +0,0 @@
    -/* 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
    - */
    -
    -#ifndef PURPLE_GG_IMAGE_PRPL_H
    -#define PURPLE_GG_IMAGE_PRPL_H
    -
    -#include <purple.h>
    -#include <libgadu.h>
    -
    -#define GGP_IMAGE_SIZE_MAX 255000
    -#define GGP_IMAGE_ID_FORMAT "%016" G_GINT64_MODIFIER "x"
    -
    -typedef struct _ggp_image_session_data ggp_image_session_data;
    -
    -typedef enum
    -{
    - GGP_IMAGE_PREPARE_OK = 0,
    - GGP_IMAGE_PREPARE_FAILURE,
    - GGP_IMAGE_PREPARE_TOO_BIG
    -} ggp_image_prepare_result;
    -
    -void
    -ggp_image_setup(PurpleConnection *gc);
    -
    -void
    -ggp_image_cleanup(PurpleConnection *gc);
    -
    -ggp_image_prepare_result
    -ggp_image_prepare(PurpleConversation *conv, PurpleImage *image, uint64_t *id);
    -
    -void
    -ggp_image_recv(PurpleConnection *gc,
    - const struct gg_event_image_reply *image_reply);
    -
    -void
    -ggp_image_send(PurpleConnection *gc,
    - const struct gg_event_image_request *image_request);
    -
    -PurpleImage *
    -ggp_image_request(PurpleConnection *gc, uin_t uin, uint64_t id);
    -
    -#endif /* PURPLE_GG_IMAGE_PRPL_H */
    --- a/libpurple/protocols/gg/keymapper.c Tue Apr 09 21:50:31 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,106 +0,0 @@
    -/* 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.
    - *
    - * Component written by Tomek Wasilczyk (http://www.wasilczyk.pl).
    - *
    - * This file is dual-licensed under the GPL2+ and the X11 (MIT) licences.
    - * As a recipient of this file you may choose, which license to receive the
    - * code under. As a contributor, you have to ensure the new code is
    - * compatible with both.
    - *
    - * 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 "keymapper.h"
    -
    -/* The problem: we want to convert 64-bit unique integers into unique gpointer
    - * keys (that may be 32-bit or 64-bit, or whatever else). We also want to
    - * convert it back.
    - *
    - * The idea: let's store every value in our internal memory. Then, its address
    - * can be also an unique key. We also need a map, to quickly figure out the
    - * address for any previously stored value.
    - *
    - * The naming problem: values becomes the keys and vice-versa.
    - */
    -
    -/* TODO
    - * For a 64-bit gpointer, keymapper could just do nothing and return the value
    - * as a key. But it have to be figured out at a compile time.
    - */
    -
    -struct _ggp_keymapper
    -{
    - /* Table keys: pointers to 64-bit mapped *values*.
    - * Table values: keys (gpointers) corresponding to mapped values.
    - *
    - * Ultimately, both keys and values are the same pointers.
    - *
    - * Yes, it's hard to comment it well enough.
    - */
    - GHashTable *val_to_key;
    -};
    -
    -ggp_keymapper *
    -ggp_keymapper_new(void)
    -{
    - ggp_keymapper *km;
    -
    - km = g_new0(ggp_keymapper, 1);
    - km->val_to_key = g_hash_table_new_full(g_int64_hash, g_int64_equal,
    - g_free, NULL);
    -
    - return km;
    -}
    -
    -void
    -ggp_keymapper_free(ggp_keymapper *km)
    -{
    - if (km == NULL)
    - return;
    -
    - g_hash_table_destroy(km->val_to_key);
    - g_free(km);
    -}
    -
    -gpointer
    -ggp_keymapper_to_key(ggp_keymapper *km, guint64 val)
    -{
    - guint64 *key;
    -
    - g_return_val_if_fail(km != NULL, NULL);
    -
    - key = g_hash_table_lookup(km->val_to_key, &val);
    - if (key)
    - return key;
    -
    - key = g_new(guint64, 1);
    - *key = val;
    -
    - g_hash_table_insert(km->val_to_key, key, key);
    -
    - return key;
    -}
    -
    -guint64
    -ggp_keymapper_from_key(ggp_keymapper *km, gpointer key)
    -{
    - g_return_val_if_fail(km != NULL, 0);
    - g_return_val_if_fail(key != NULL, 0);
    -
    - return *((guint64*)key);
    -}
    --- a/libpurple/protocols/gg/keymapper.h Tue Apr 09 21:50:31 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,49 +0,0 @@
    -/* 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.
    - *
    - * Component written by Tomek Wasilczyk (http://www.wasilczyk.pl).
    - *
    - * This file is dual-licensed under the GPL2+ and the X11 (MIT) licences.
    - * As a recipient of this file you may choose, which license to receive the
    - * code under. As a contributor, you have to ensure the new code is
    - * compatible with both.
    - *
    - * 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
    - */
    -
    -#ifndef PURPLE_GG_KEYMAPPER_H
    -#define PURPLE_GG_KEYMAPPER_H
    -
    -typedef struct _ggp_keymapper ggp_keymapper;
    -
    -#include <purple.h>
    -
    -ggp_keymapper *
    -ggp_keymapper_new(void);
    -
    -void
    -ggp_keymapper_free(ggp_keymapper *km);
    -
    -gpointer
    -ggp_keymapper_to_key(ggp_keymapper *km, guint64 val);
    -
    -/* The key have to be valid. */
    -guint64
    -ggp_keymapper_from_key(ggp_keymapper *km, gpointer key);
    -
    -#endif /* PURPLE_GG_KEYMAPPER_H */
    --- a/libpurple/protocols/gg/libgadu-events.c Tue Apr 09 21:50:31 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,120 +0,0 @@
    -/* 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 "libgadu-events.h"
    -
    -#include "avatar.h"
    -#include "edisc.h"
    -
    -void ggp_events_user_data(PurpleConnection *gc, struct gg_event_user_data *data)
    -{
    - guint user_idx;
    - gboolean is_update;
    -
    - purple_debug_info("gg", "GG_EVENT_USER_DATA [type=%d, user_count=%"
    - G_GSIZE_FORMAT "]\n", data->type, data->user_count);
    -
    - /*
    - type =
    - 1, 3: user information sent after connecting (divided by
    - 20 contacts; 3 - last one; 1 - rest of them)
    - 0: data update
    - */
    - is_update = (data->type == 0);
    -
    - for (user_idx = 0; user_idx < data->user_count; user_idx++) {
    - struct gg_event_user_data_user *data_user =
    - &data->users[user_idx];
    - uin_t uin = data_user->uin;
    - guint attr_idx;
    - gboolean got_avatar = FALSE;
    - for (attr_idx = 0; attr_idx < data_user->attr_count; attr_idx++) {
    - struct gg_event_user_data_attr *data_attr =
    - &data_user->attrs[attr_idx];
    - if (strcmp(data_attr->key, "avatar") == 0) {
    - time_t timestamp;
    - if (data_attr->type == 0) {
    - ggp_avatar_buddy_remove(gc, uin);
    - continue;
    - }
    -
    - timestamp = atoi(data_attr->value);
    - if (timestamp <= 0)
    - continue;
    - got_avatar = TRUE;
    - ggp_avatar_buddy_update(gc, uin, timestamp);
    - }
    - }
    -
    - if (!is_update && !got_avatar)
    - ggp_avatar_buddy_remove(gc, uin);
    - }
    -}
    -
    -static void ggp_events_new_version(const gchar *data)
    -{
    - /* data = {"severity":"download"} */
    - purple_debug_info("gg", "Gadu-Gadu server reports new client version."
    - " %s", data);
    -}
    -
    -void ggp_events_json(PurpleConnection *gc, struct gg_event_json_event *ev)
    -{
    - static const gchar *ignored_events[] = {
    - "edisc/scope_files_changed",
    - "notifications/state",
    - "invitations/list",
    - "notifications/list", /* gifts */
    - NULL
    - };
    - const gchar **it;
    -
    - if (g_strcmp0("edisc/send_ticket_changed", ev->type) == 0) {
    - ggp_edisc_xfer_ticket_changed(gc, ev->data);
    - return;
    - }
    -
    - if (g_strcmp0("updates/new-version", ev->type) == 0) {
    - ggp_events_new_version(ev->data);
    - return;
    - }
    -
    - for (it = ignored_events; *it != NULL; it++) {
    - if (g_strcmp0(*it, ev->type) == 0)
    - return;
    - }
    -
    - if (purple_debug_is_unsafe() && purple_debug_is_verbose())
    - purple_debug_warning("gg", "ggp_events_json: "
    - "unhandled event \"%s\": %s\n",
    - ev->type, ev->data);
    - else
    - purple_debug_warning("gg", "ggp_events_json: "
    - "unhandled event \"%s\"\n", ev->type);
    -}
    --- a/libpurple/protocols/gg/libgadu-events.h Tue Apr 09 21:50:31 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,43 +0,0 @@
    -/* 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
    - */
    -
    -#ifndef PURPLE_GG_LIBGADU_EVENTS_H
    -#define PURPLE_GG_LIBGADU_EVENTS_H
    -
    -#include <purple.h>
    -#include <libgadu.h>
    -
    -#include "gg.h"
    -
    -void ggp_events_user_data(PurpleConnection *gc,
    - struct gg_event_user_data *data);
    -
    -void ggp_events_json(PurpleConnection *gc, struct gg_event_json_event *ev);
    -
    -#endif /* PURPLE_GG_LIBGADU_EVENTS_H */
    --- a/libpurple/protocols/gg/libgaduw.c Tue Apr 09 21:50:31 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,215 +0,0 @@
    -/* 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 "libgaduw.h"
    -
    -#include "purplew.h"
    -#include "gg.h"
    -
    -#include <ctype.h>
    -
    -static void ggp_libgaduw_debug_handler(int level, const char * format,
    - va_list args);
    -
    -/*******************************************************************************
    - * Setup/cleanup.
    - ******************************************************************************/
    -
    -void ggp_libgaduw_setup(void)
    -{
    - gg_debug_handler = ggp_libgaduw_debug_handler;
    -}
    -
    -void ggp_libgaduw_cleanup(void)
    -{
    - gg_debug_handler = NULL;
    -}
    -
    -/*******************************************************************************
    - * General.
    - ******************************************************************************/
    -
    -const gchar * ggp_libgaduw_version(PurpleConnection *gc)
    -{
    - GGPInfo *accdata = purple_connection_get_protocol_data(gc);
    - const gchar *ver = accdata->session->client_version;
    -
    - if (ver != NULL && isdigit(ver[0]))
    - return ver;
    - return GG_DEFAULT_CLIENT_VERSION;
    -}
    -
    -static void ggp_libgaduw_debug_handler(int level, const char * format,
    - va_list args)
    -{
    - PurpleDebugLevel purple_level;
    - char msgbuff[1000];
    - int ret;
    -
    - if ((level & GG_DEBUG_NET) || (level & GG_DEBUG_FUNCTION) ||
    - (level & GG_DEBUG_VERBOSE))
    - {
    - if (!purple_debug_is_verbose())
    - return;
    - }
    -
    - if ((level & GG_DEBUG_DUMP) || /* GG session protocol packets */
    - (level & GG_DEBUG_TRAFFIC)) /* HTTP traffic */
    - {
    - if (!purple_debug_is_verbose() || !purple_debug_is_unsafe())
    - return;
    - }
    -
    - /* Don't use glib's printf family, since it might not support
    - * system-specific formatting modifiers (like %Iu for size on win32). */
    - ret = vsnprintf(msgbuff, sizeof(msgbuff) / sizeof(char), format, args);
    -
    - if (ret <= 0) {
    - purple_debug_error("gg",
    - "failed to printf the following message: %s",
    - format ? format : "(null)");
    -
    - return;
    - }
    -
    - if (level & GG_DEBUG_ERROR)
    - purple_level = PURPLE_DEBUG_ERROR;
    - else if (level & GG_DEBUG_WARNING)
    - purple_level = PURPLE_DEBUG_WARNING;
    - else
    - purple_level = PURPLE_DEBUG_MISC;
    -
    - purple_debug(purple_level, "gg", "%s", msgbuff);
    -}
    -
    -/*******************************************************************************
    - * HTTP requests.
    - ******************************************************************************/
    -
    -static void ggp_libgaduw_http_processing_cancel(PurpleConnection *gc,
    - void *_req);
    -
    -static void ggp_libgaduw_http_handler(gpointer _req, gint fd,
    - PurpleInputCondition cond);
    -
    -static void ggp_libgaduw_http_finish(ggp_libgaduw_http_req *req,
    - gboolean success);
    -
    -/******************************************************************************/
    -
    -ggp_libgaduw_http_req * ggp_libgaduw_http_watch(PurpleConnection *gc,
    - struct gg_http *h, ggp_libgaduw_http_cb cb,
    - gpointer user_data, gboolean show_processing)
    -{
    - ggp_libgaduw_http_req *req;
    - purple_debug_misc("gg", "ggp_libgaduw_http_watch(h=%p, "
    - "show_processing=%d)\n", h, show_processing);
    -
    - req = g_new(ggp_libgaduw_http_req, 1);
    - req->user_data = user_data;
    - req->cb = cb;
    - req->cancelled = FALSE;
    - req->h = h;
    - req->processing = NULL;
    - if (show_processing)
    - req->processing = ggp_purplew_request_processing(gc, NULL,
    - req, ggp_libgaduw_http_processing_cancel);
    - req->inpa = ggp_purplew_http_input_add(h, ggp_libgaduw_http_handler,
    - req);
    -
    - return req;
    -}
    -
    -static void
    -ggp_libgaduw_http_processing_cancel(G_GNUC_UNUSED PurpleConnection *gc,
    - void *_req)
    -{
    - ggp_libgaduw_http_req *req = _req;
    - req->processing = NULL;
    - ggp_libgaduw_http_cancel(req);
    -}
    -
    -static void
    -ggp_libgaduw_http_handler(gpointer _req, G_GNUC_UNUSED gint fd,
    - G_GNUC_UNUSED PurpleInputCondition cond)
    -{
    - ggp_libgaduw_http_req *req = _req;
    -
    - if (req->h->callback(req->h) == -1 || req->h->state == GG_STATE_ERROR) {
    - purple_debug_error("gg", "ggp_libgaduw_http_handler: failed to "
    - "make http request: %d\n", req->h->error);
    - ggp_libgaduw_http_finish(req, FALSE);
    - return;
    - }
    -
    - if (purple_debug_is_verbose()) {
    - purple_debug_misc("gg", "ggp_libgaduw_http_handler: got fd "
    - "update [check=%d, state=%d]\n", req->h->check,
    - req->h->state);
    - }
    -
    - if (req->h->state != GG_STATE_DONE) {
    - g_source_remove(req->inpa);
    - req->inpa = ggp_purplew_http_input_add(req->h,
    - ggp_libgaduw_http_handler, req);
    - return;
    - }
    -
    - if (!req->h->data || !req->h->body) {
    - purple_debug_error("gg", "ggp_libgaduw_http_handler: got empty "
    - "http response: %d\n", req->h->error);
    - ggp_libgaduw_http_finish(req, FALSE);
    - return;
    - }
    -
    - ggp_libgaduw_http_finish(req, TRUE);
    -}
    -
    -void ggp_libgaduw_http_cancel(ggp_libgaduw_http_req *req)
    -{
    - purple_debug_misc("gg", "ggp_libgaduw_http_cancel\n");
    - req->cancelled = TRUE;
    - gg_http_stop(req->h);
    - ggp_libgaduw_http_finish(req, FALSE);
    -}
    -
    -static void ggp_libgaduw_http_finish(ggp_libgaduw_http_req *req,
    - gboolean success)
    -{
    - purple_debug_misc("gg", "ggp_libgaduw_http_finish(h=%p, processing=%p):"
    - " success=%d\n", req->h, req->processing, success);
    - if (req->processing) {
    - ggp_purplew_request_processing_done(req->processing);
    - req->processing = NULL;
    - }
    - g_source_remove(req->inpa);
    - req->cb(req->h, success, req->cancelled, req->user_data);
    - req->h->destroy(req->h);
    - g_free(req);
    -}
    --- a/libpurple/protocols/gg/libgaduw.h Tue Apr 09 21:50:31 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,62 +0,0 @@
    -/* 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
    - */
    -
    -#ifndef PURPLE_GG_LIBGADUW_H
    -#define PURPLE_GG_LIBGADUW_H
    -
    -#include <purple.h>
    -#include <libgadu.h>
    -
    -#include "purplew.h"
    -
    -typedef void (*ggp_libgaduw_http_cb)(struct gg_http *h, gboolean success,
    - gboolean cancelled, gpointer user_data);
    -
    -typedef struct
    -{
    - gpointer user_data;
    - ggp_libgaduw_http_cb cb;
    -
    - gboolean cancelled;
    - struct gg_http *h;
    - ggp_purplew_request_processing_handle *processing;
    - guint inpa;
    -} ggp_libgaduw_http_req;
    -
    -void ggp_libgaduw_setup(void);
    -void ggp_libgaduw_cleanup(void);
    -
    -const gchar * ggp_libgaduw_version(PurpleConnection *gc);
    -
    -ggp_libgaduw_http_req * ggp_libgaduw_http_watch(PurpleConnection *gc,
    - struct gg_http *h, ggp_libgaduw_http_cb cb, gpointer user_data,
    - gboolean show_processing);
    -void ggp_libgaduw_http_cancel(ggp_libgaduw_http_req *req);
    -
    -#endif /* PURPLE_GG_LIBGADUW_H */
    --- a/libpurple/protocols/gg/meson.build Tue Apr 09 21:50:31 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,66 +0,0 @@
    -GG_SOURCES = [
    - 'avatar.c',
    - 'avatar.h',
    - 'blist.c',
    - 'blist.h',
    - 'chat.c',
    - 'chat.h',
    - 'edisc.c',
    - 'edisc.h',
    - 'gg.c',
    - 'gg.h',
    - 'html.c',
    - 'html.h',
    - 'image-prpl.c',
    - 'image-prpl.h',
    - 'keymapper.c',
    - 'keymapper.h',
    - 'libgadu-events.c',
    - 'libgadu-events.h',
    - 'libgaduw.c',
    - 'libgaduw.h',
    - 'message-prpl.c',
    - 'message-prpl.h',
    - 'multilogon.c',
    - 'multilogon.h',
    - 'pubdir-prpl.c',
    - 'pubdir-prpl.h',
    - 'purplew.c',
    - 'purplew.h',
    - 'resolver-purple.c',
    - 'resolver-purple.h',
    - 'roster.c',
    - 'roster.h',
    - 'servconn.c',
    - 'servconn.h',
    - 'status.c',
    - 'status.h',
    - 'tcpsocket.c',
    - 'tcpsocket.h',
    - 'utils.c',
    - 'utils.h',
    - 'validator.c',
    - 'validator.h',
    - 'xml.c',
    - 'xml.h',
    - 'oauth/oauth.c',
    - 'oauth/oauth.h',
    - 'oauth/oauth-purple.c',
    - 'oauth/oauth-purple.h'
    -]
    -
    -if DYNAMIC_GG
    - gg_resources = gnome.compile_resources('ggresource',
    - 'resources/gg.gresource.xml',
    - source_dir : 'resources',
    - c_name : 'gg')
    - GG_SOURCES += gg_resources
    -
    - shared_library('gg', GG_SOURCES,
    - c_args : ['-DG_LOG_USE_STRUCTURED', '-DG_LOG_DOMAIN="Purple-GaduGadu"'],
    - gnu_symbol_visibility : 'hidden',
    - dependencies : [libgadu, json, libpurple_dep, libsoup, glib],
    - install : true, install_dir : PURPLE_PLUGINDIR)
    -
    - devenv.append('PURPLE_PLUGIN_PATH', meson.current_build_dir())
    -endif
    --- a/libpurple/protocols/gg/message-prpl.c Tue Apr 09 21:50:31 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,699 +0,0 @@
    -/* 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.
    - *
    - * Component written by Tomek Wasilczyk (http://www.wasilczyk.pl).
    - *
    - * This file is dual-licensed under the GPL2+ and the X11 (MIT) licences.
    - * As a recipient of this file you may choose, which license to receive the
    - * code under. As a contributor, you have to ensure the new code is
    - * compatible with both.
    - *
    - * 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 <errno.h>
    -
    -#include "message-prpl.h"
    -
    -#include "gg.h"
    -#include "chat.h"
    -#include "utils.h"
    -#include "html.h"
    -
    -#define GGP_GG10_DEFAULT_FORMAT "<span style=\"color:#000000; " \
    - "font-family:'MS Shell Dlg 2'; font-size:9pt; \">"
    -#define GGP_GG10_DEFAULT_FORMAT_REPLACEMENT "<span>"
    -#define GGP_GG11_FORCE_COMPAT FALSE
    -
    -typedef struct
    -{
    - enum
    - {
    - GGP_MESSAGE_GOT_TYPE_IM,
    - GGP_MESSAGE_GOT_TYPE_CHAT,
    - GGP_MESSAGE_GOT_TYPE_MULTILOGON
    - } type;
    -
    - uin_t user;
    - gchar *text;
    - time_t time;
    - uint64_t chat_id;
    -
    - PurpleConnection *gc;
    -} ggp_message_got_data;
    -
    -typedef struct
    -{
    - GRegex *re_html_tag;
    - GRegex *re_gg_img;
    -} ggp_message_global_data;
    -
    -static ggp_message_global_data global_data;
    -
    -struct _ggp_message_session_data
    -{
    -};
    -
    -typedef struct
    -{
    - int size;
    - gchar *face;
    - int color, bgcolor;
    - gboolean b, i, u, s;
    -} ggp_font;
    -
    -static ggp_font * ggp_font_new(void);
    -static ggp_font * ggp_font_clone(ggp_font *font);
    -static void ggp_font_free(gpointer font);
    -
    -static PurpleConversation * ggp_message_get_conv(PurpleConnection *gc,
    - uin_t uin);
    -static void ggp_message_got_data_free(ggp_message_got_data *msg);
    -static void ggp_message_got_display(PurpleConnection *gc,
    - ggp_message_got_data *msg);
    -static void ggp_message_format_from_gg(ggp_message_got_data *msg,
    - const gchar *text);
    -
    -/**************/
    -
    -void ggp_message_setup_global(void)
    -{
    - global_data.re_html_tag = g_regex_new(
    - "<(/)?([a-zA-Z]+)( [^>]+)?>",
    - G_REGEX_OPTIMIZE, 0, NULL);
    - global_data.re_gg_img = g_regex_new(
    - "<img name=\"([0-9a-fA-F]+)\"/?>",
    - G_REGEX_OPTIMIZE, 0, NULL);
    -}
    -
    -void ggp_message_cleanup_global(void)
    -{
    - g_regex_unref(global_data.re_html_tag);
    - g_regex_unref(global_data.re_gg_img);
    -}
    -
    -static inline ggp_message_session_data *
    -ggp_message_get_sdata(PurpleConnection *gc)
    -{
    - GGPInfo *accdata = purple_connection_get_protocol_data(gc);
    - return accdata->message_data;
    -}
    -
    -void ggp_message_setup(PurpleConnection *gc)
    -{
    - GGPInfo *accdata = purple_connection_get_protocol_data(gc);
    - ggp_message_session_data *sdata = g_new0(ggp_message_session_data, 1);
    -
    - accdata->message_data = sdata;
    -}
    -
    -void ggp_message_cleanup(PurpleConnection *gc)
    -{
    - ggp_message_session_data *sdata = ggp_message_get_sdata(gc);
    -
    - g_free(sdata);
    -}
    -
    -static ggp_font * ggp_font_new(void)
    -{
    - ggp_font *font;
    -
    - font = g_new0(ggp_font, 1);
    - font->color = -1;
    - font->bgcolor = -1;
    -
    - return font;
    -}
    -
    -static ggp_font * ggp_font_clone(ggp_font * font)
    -{
    - ggp_font *clone = g_new0(ggp_font, 1);
    -
    - *clone = *font;
    - clone->face = g_strdup(font->face);
    -
    - return clone;
    -}
    -
    -static void ggp_font_free(gpointer _font)
    -{
    - ggp_font *font = _font;
    -
    - g_free(font->face);
    - g_free(font);
    -}
    -
    -/**/
    -
    -static PurpleConversation * ggp_message_get_conv(PurpleConnection *gc,
    - uin_t uin)
    -{
    - PurpleAccount *account = purple_connection_get_account(gc);
    - PurpleConversation *im;
    - PurpleConversationManager *manager;
    - const gchar *who = ggp_uin_to_str(uin);
    -
    - manager = purple_conversation_manager_get_default();
    - im = purple_conversation_manager_find_im(manager, account, who);
    - if (im) {
    - return im;
    - }
    - im = purple_im_conversation_new(account, who);
    - return im;
    -}
    -
    -static void ggp_message_got_data_free(ggp_message_got_data *msg)
    -{
    - g_free(msg->text);
    - g_free(msg);
    -}
    -
    -void ggp_message_got(PurpleConnection *gc, const struct gg_event_msg *ev)
    -{
    - ggp_message_got_data *msg = g_new0(ggp_message_got_data, 1);
    -
    - msg->gc = gc;
    - msg->time = ev->time;
    - msg->user = ev->sender;
    -
    - if (ev->chat_id != 0) {
    - msg->type = GGP_MESSAGE_GOT_TYPE_CHAT;
    - msg->chat_id = ev->chat_id;
    - } else {
    - msg->type = GGP_MESSAGE_GOT_TYPE_IM;
    - }
    -
    - ggp_message_format_from_gg(msg, ev->xhtml_message);
    -
    - ggp_message_got_display(gc, msg);
    - ggp_message_got_data_free(msg);
    -}
    -
    -void ggp_message_got_multilogon(PurpleConnection *gc,
    - const struct gg_event_msg *ev)
    -{
    - ggp_message_got_data *msg = g_new0(ggp_message_got_data, 1);
    -
    - msg->gc = gc;
    - msg->time = ev->time;
    - msg->user = ev->sender; /* not really a sender*/
    -
    - if (ev->chat_id != 0) {
    - msg->type = GGP_MESSAGE_GOT_TYPE_CHAT;
    - msg->chat_id = ev->chat_id;
    - } else {
    - msg->type = GGP_MESSAGE_GOT_TYPE_MULTILOGON;
    - }
    -
    - ggp_message_format_from_gg(msg, ev->xhtml_message);
    -
    - ggp_message_got_display(gc, msg);
    - ggp_message_got_data_free(msg);
    -}
    -
    -static void ggp_message_got_display(PurpleConnection *gc,
    - ggp_message_got_data *msg)
    -{
    - if (msg->type == GGP_MESSAGE_GOT_TYPE_IM) {
    - purple_serv_got_im(gc, ggp_uin_to_str(msg->user), msg->text,
    - PURPLE_MESSAGE_RECV, msg->time);
    - } else if (msg->type == GGP_MESSAGE_GOT_TYPE_CHAT) {
    - ggp_chat_got_message(gc, msg->chat_id, msg->text, msg->time,
    - msg->user);
    - } else if (msg->type == GGP_MESSAGE_GOT_TYPE_MULTILOGON) {
    - GDateTime *dt = NULL;
    - PurpleAccount *account = NULL;
    - PurpleContactInfo *info = NULL;
    - PurpleConversation *im = ggp_message_get_conv(gc, msg->user);
    - PurpleMessage *pmsg;
    - const gchar *me = NULL;
    -
    - account = purple_connection_get_account(gc);
    - info = PURPLE_CONTACT_INFO(account);
    - me = purple_contact_info_get_name_for_display(info);
    -
    - pmsg = purple_message_new_outgoing(me, NULL, msg->text, 0);
    -
    - dt = g_date_time_new_from_unix_local((gint64)msg->time);
    - purple_message_set_timestamp(pmsg, dt);
    - g_date_time_unref(dt);
    -
    - purple_conversation_write_message(im, pmsg);
    -
    - g_object_unref(pmsg);
    - } else {
    - purple_debug_error("gg", "ggp_message_got_display: "
    - "unexpected message type: %d\n", msg->type);
    - }
    -}
    -
    -static gboolean ggp_message_format_from_gg_found_img(const GMatchInfo *info,
    - GString *res, gpointer data)
    -{
    - ggp_message_got_data *msg = data;
    - gchar *name, *replacement;
    - int64_t id;
    - PurpleImage *image;
    - guint image_id;
    -
    - name = g_match_info_fetch(info, 1);
    - if (sscanf(name, "%" G_GINT64_MODIFIER "x", &id) != 1)
    - id = 0;
    - g_free(name);
    - if (!id) {
    - /* TODO: stock broken image? */
    - g_string_append_printf(res, "[%s]", _("broken image"));
    - return FALSE;
    - }
    -
    - image = ggp_image_request(msg->gc, msg->user, id);
    - if (!image) {
    - purple_debug_warning("gg", "ggp_message_format_from_gg_"
    - "found_img: couldn't request image");
    - g_string_append_printf(res, "[%s]", _("broken image"));
    - return FALSE;
    - }
    -
    - image_id = purple_image_store_add_weak(image);
    - replacement = g_strdup_printf("<img src=\""
    - PURPLE_IMAGE_STORE_PROTOCOL "%u\">", image_id);
    - g_string_append(res, replacement);
    - g_free(replacement);
    -
    - return FALSE;
    -}
    -
    -static void ggp_message_format_from_gg(ggp_message_got_data *msg,
    - const gchar *text)
    -{
    - gchar *text_new, *tmp;
    -
    - if (text == NULL) {
    - msg->text = g_strdup("");
    - return;
    - }
    -
    - text_new = g_strdup(text);
    - purple_str_strip_char(text_new, '\r');
    -
    - tmp = text_new;
    - text_new = purple_strreplace(text_new, GGP_GG10_DEFAULT_FORMAT,
    - GGP_GG10_DEFAULT_FORMAT_REPLACEMENT);
    - g_free(tmp);
    -
    - tmp = text_new;
    - text_new = g_regex_replace_eval(global_data.re_gg_img, text_new, -1, 0,
    - 0, ggp_message_format_from_gg_found_img, msg, NULL);
    - g_free(tmp);
    -
    - msg->text = text_new;
    -}
    -
    -gchar *
    -ggp_message_format_to_gg(PurpleConversation *conv, const gchar *text)
    -{
    - gchar *text_new, *tmp;
    - GString *reformatted_text = NULL;
    - GMatchInfo *match;
    - guint pos = 0;
    - GString *pending_objects = NULL;
    - GList *font_stack = NULL;
    - static int html_sizes_pt[7] = { 7, 8, 9, 10, 12, 14, 16 };
    -
    - ggp_font *font_new, *font_current, *font_base;
    - gboolean font_changed = FALSE;
    - gboolean in_any_tag = FALSE;
    -
    - if (purple_debug_is_verbose())
    - purple_debug_info("gg", "ggp formatting text: [%s]", text);
    -
    - /* default font */
    - font_base = ggp_font_new();
    - font_current = ggp_font_new();
    - font_new = ggp_font_new();
    -
    - /* GG11 doesn't use nbsp, it just print spaces */
    - text_new = purple_strreplace(text, "&nbsp;", " ");
    -
    - /* add end-of-message tag */
    - if (strstr(text_new, "<eom>") != NULL) {
    - tmp = text_new;
    - text_new = purple_strreplace(text_new, "<eom>", "");
    - g_free(tmp);
    - purple_debug_warning("gg", "ggp_message_format_to_gg: "
    - "unexpected <eom> tag\n");
    - }
    - tmp = text_new;
    - text_new = g_strdup_printf("%s<eom></eom>", text_new);
    - g_free(tmp);
    -
    - reformatted_text = g_string_new(NULL);
    - pending_objects = g_string_new(NULL);
    - g_regex_match(global_data.re_html_tag, text_new, 0, &match);
    - while (g_match_info_matches(match)) {
    - int m_start, m_end, m_pos;
    - gboolean tag_close;
    - gchar *tag_str, *attribs_str;
    - ggp_html_tag tag;
    - gboolean text_before;
    -
    - /* reading tag and its contents */
    - g_match_info_fetch_pos(match, 0, &m_start, &m_end);
    - g_assert(m_start >= 0 && m_end >= 0);
    - text_before = ((guint)m_start > pos);
    - g_match_info_fetch_pos(match, 1, &m_pos, NULL);
    - tag_close = (m_pos >= 0);
    - tag_str = g_match_info_fetch(match, 2);
    - tag = ggp_html_parse_tag(tag_str);
    - attribs_str = g_match_info_fetch(match, 3);
    - g_match_info_next(match, NULL);
    -
    - if (tag == GGP_HTML_TAG_UNKNOWN) {
    - purple_debug_warning(
    - "gg",
    - "ggp_message_format_to_gg: unknown tag %s",
    - tag_str);
    - }
    -
    - /* closing *all* formatting-related tags (GG11 weirdness)
    - * and adding pending objects */
    - if ((text_before && (font_changed || pending_objects->len > 0)) ||
    - (tag == GGP_HTML_TAG_EOM && tag_close)) {
    - font_changed = FALSE;
    - if (in_any_tag) {
    - in_any_tag = FALSE;
    - if (font_current->s && !GGP_GG11_FORCE_COMPAT) {
    - g_string_append(reformatted_text, "</s>");
    - }
    - if (font_current->u) {
    - g_string_append(reformatted_text, "</u>");
    - }
    - if (font_current->i) {
    - g_string_append(reformatted_text, "</i>");
    - }
    - if (font_current->b) {
    - g_string_append(reformatted_text, "</b>");
    - }
    - g_string_append(reformatted_text, "</span>");
    - }
    - if (pending_objects->len > 0) {
    - g_string_append(reformatted_text, pending_objects->str);
    - g_string_truncate(pending_objects, 0);
    - }
    - }
    -
    - /* opening formatting-related tags again */
    - if (text_before && !in_any_tag) {
    - gboolean has_size = (font_new->size > 0 &&
    - font_new->size <= 7 && font_new->size != 3);
    - gboolean has_style =
    - has_size || font_new->face ||
    - (font_new->bgcolor >= 0 && !GGP_GG11_FORCE_COMPAT) ||
    - font_new->color >= 0;
    -
    - if (has_style) {
    - g_string_append(reformatted_text, "<span style=\"");
    -
    - if (has_size) {
    - g_string_append_printf(reformatted_text, "font-size:%dpt;",
    - html_sizes_pt[font_new->size - 1]);
    - }
    - if (font_new->face) {
    - g_string_append_printf(reformatted_text, "font-family:%s;",
    - font_new->face);
    - }
    - if (font_new->bgcolor >= 0 && !GGP_GG11_FORCE_COMPAT) {
    - g_string_append_printf(reformatted_text,
    - "background-color:#%06x;",
    - font_new->bgcolor);
    - }
    - if (font_new->color >= 0) {
    - g_string_append_printf(reformatted_text, "color:#%06x;",
    - font_new->color);
    - }
    -
    - g_string_append(reformatted_text, "\">");
    - } else {
    - g_string_append(reformatted_text, "<span>");
    - }
    -
    - if (font_new->b) {
    - g_string_append(reformatted_text, "<b>");
    - }
    - if (font_new->i) {
    - g_string_append(reformatted_text, "<i>");
    - }
    - if (font_new->u) {
    - g_string_append(reformatted_text, "<u>");
    - }
    - if (font_new->s && !GGP_GG11_FORCE_COMPAT) {
    - g_string_append(reformatted_text, "<s>");
    - }
    -
    - ggp_font_free(font_current);
    - font_current = font_new;
    - font_new = ggp_font_clone(font_current);
    -
    - in_any_tag = TRUE;
    - }
    - if (text_before) {
    - g_string_append_len(reformatted_text, text_new + pos,
    - m_start - pos);
    - }
    -
    - /* set formatting of a following text */
    - if (tag == GGP_HTML_TAG_B) {
    - font_changed |= (font_new->b != !tag_close);
    - font_new->b = !tag_close;
    - } else if (tag == GGP_HTML_TAG_I) {
    - font_changed |= (font_new->i != !tag_close);
    - font_new->i = !tag_close;
    - } else if (tag == GGP_HTML_TAG_U) {
    - font_changed |= (font_new->u != !tag_close);
    - font_new->u = !tag_close;
    - } else if (tag == GGP_HTML_TAG_S) {
    - font_changed |= (font_new->s != !tag_close);
    - font_new->s = !tag_close;
    - } else if (tag == GGP_HTML_TAG_IMG && !tag_close) {
    - GHashTable *attribs = ggp_html_tag_attribs(attribs_str);
    - gchar *val = NULL;
    - uint64_t id;
    - ggp_image_prepare_result res = -1;
    - PurpleImage *image = NULL;
    -
    - val = g_hash_table_lookup(attribs, "src");
    - if (val)
    - image = purple_image_store_get_from_uri(val);
    -
    - if (image != NULL)
    - res = ggp_image_prepare(conv, image, &id);
    -
    - if (res == GGP_IMAGE_PREPARE_OK) {
    - g_string_append_printf(pending_objects,
    - "<img name=\"" GGP_IMAGE_ID_FORMAT "\">",
    - id);
    - } else if (res == GGP_IMAGE_PREPARE_TOO_BIG) {
    - purple_conversation_write_system_message(conv,
    - _("Image is too large, please try "
    - "smaller one."), PURPLE_MESSAGE_ERROR);
    - } else {
    - purple_conversation_write_system_message(conv,
    - _("Image cannot be sent."),
    - PURPLE_MESSAGE_ERROR);
    - }
    -
    - g_hash_table_destroy(attribs);
    - } else if (tag == GGP_HTML_TAG_FONT && !tag_close) {
    - GHashTable *attribs = ggp_html_tag_attribs(attribs_str);
    - gchar *val = NULL;
    -
    - font_stack = g_list_prepend(font_stack,
    - ggp_font_clone(font_new));
    -
    - if ((val = g_hash_table_lookup(attribs, "size")) != NULL
    - && val[0] >= '1' && val[0] <= '7' &&
    - val[1] == '\0')
    - {
    - int size = val[0] - '0';
    - font_changed |= (font_new->size != size);
    - font_new->size = size;
    - }
    -
    - if ((val = g_hash_table_lookup(attribs, "face"))
    - != NULL)
    - {
    - font_changed |=
    - (g_strcmp0(font_new->face, val) != 0);
    - g_free(font_new->face);
    - font_new->face = g_strdup(val);
    - }
    -
    - if ((val = g_hash_table_lookup(attribs, "color"))
    - != NULL && val[0] == '#' && strlen(val) == 7)
    - {
    - int color = ggp_html_decode_color(val);
    - font_changed |= (font_new->color != color);
    - font_new->color = color;
    - }
    -
    - g_hash_table_destroy(attribs);
    - }
    - else if ((tag == GGP_HTML_TAG_SPAN || tag == GGP_HTML_TAG_DIV)
    - && !tag_close)
    - {
    - GHashTable *attribs, *styles = NULL;
    - gchar *style = NULL;
    - gchar *val = NULL;
    -
    - attribs = ggp_html_tag_attribs(attribs_str);
    -
    - font_stack = g_list_prepend(font_stack,
    - ggp_font_clone(font_new));
    - if (tag == GGP_HTML_TAG_DIV) {
    - g_string_append(pending_objects, "<br>");
    - }
    -
    - style = g_hash_table_lookup(attribs, "style");
    - if (style)
    - styles = ggp_html_css_attribs(style);
    -
    - if (styles && (val = g_hash_table_lookup(styles,
    - "background-color")) != NULL)
    - {
    - int color = ggp_html_decode_color(val);
    - font_changed |= (font_new->bgcolor != color);
    - font_new->bgcolor = color;
    - }
    -
    - if (styles && (val = g_hash_table_lookup(styles,
    - "color")) != NULL)
    - {
    - int color = ggp_html_decode_color(val);
    - font_changed |= (font_new->color != color);
    - font_new->color = color;
    - }
    -
    - g_clear_pointer(&styles, g_hash_table_destroy);
    - g_hash_table_destroy(attribs);
    - }
    - else if ((tag == GGP_HTML_TAG_FONT || tag == GGP_HTML_TAG_SPAN
    - || tag == GGP_HTML_TAG_DIV) && tag_close)
    - {
    - font_changed = TRUE;
    -
    - ggp_font_free(font_new);
    - if (font_stack) {
    - font_new = (ggp_font*)font_stack->data;
    - font_stack = g_list_delete_link(
    - font_stack, font_stack);
    - }
    - else
    - font_new = ggp_font_clone(font_base);
    - } else if (tag == GGP_HTML_TAG_BR) {
    - g_string_append(pending_objects, "<br>");
    - } else if (tag == GGP_HTML_TAG_HR) {
    - g_string_append(pending_objects, "<br><span>---</span><br>");
    - } else if (tag == GGP_HTML_TAG_A || tag == GGP_HTML_TAG_EOM) {
    - /* do nothing */
    - } else if (tag == GGP_HTML_TAG_UNKNOWN) {
    - purple_debug_warning(
    - "gg",
    - "ggp_message_format_to_gg: unknown tag %s",
    - tag_str);
    - } else {
    - purple_debug_error("gg", "ggp_message_format_to_gg: "
    - "not handled tag %s\n", tag_str);
    - }
    -
    - pos = m_end;
    - g_free(tag_str);
    - g_free(attribs_str);
    - }
    - g_match_info_free(match);
    -
    - if (pos < strlen(text_new) || in_any_tag) {
    - purple_debug_error(
    - "gg",
    - "ggp_message_format_to_gg: end of message not reached");
    - }
    -
    - /* releasing fonts resources */
    - ggp_font_free(font_new);
    - ggp_font_free(font_current);
    - ggp_font_free(font_base);
    - g_list_free_full(font_stack, ggp_font_free);
    -
    - /* combining reformatted text info one string */
    - g_free(text_new);
    - g_string_free(pending_objects, TRUE);
    - text_new = g_string_free(reformatted_text, FALSE);
    -
    - if (purple_debug_is_verbose())
    - purple_debug_info("gg", "reformatted text: [%s]", text_new);
    -
    - return text_new;
    -}
    -
    -int
    -ggp_message_send_im(G_GNUC_UNUSED PurpleProtocolIM *pim,
    - PurpleConnection *gc,
    - G_GNUC_UNUSED PurpleConversation *conversation,
    - PurpleMessage *msg)
    -{
    - GGPInfo *info = purple_connection_get_protocol_data(gc);
    - PurpleConversation *im;
    - PurpleConversationManager *manager;
    - ggp_buddy_data *buddy_data;
    - gchar *gg_msg;
    - gboolean succ;
    - const gchar *rcpt = purple_message_get_recipient(msg);
    -
    - /* TODO: return -ENOTCONN, if not connected */
    -
    - if (purple_message_is_empty(msg))
    - return 0;
    -
    - buddy_data = ggp_buddy_get_data(purple_blist_find_buddy(
    - purple_connection_get_account(gc), rcpt));
    -
    - if (buddy_data->blocked)
    - return -1;
    -
    - manager = purple_conversation_manager_get_default();
    - im = purple_conversation_manager_find_im(manager,
    - purple_connection_get_account(gc),
    - rcpt);
    -
    - gg_msg = ggp_message_format_to_gg(im,
    - purple_message_get_contents(msg));
    -
    - /* TODO: splitting messages */
    - if (strlen(gg_msg) > GG_MSG_MAXSIZE) {
    - g_free(gg_msg);
    - return -E2BIG;
    - }
    -
    - succ = (gg_send_message_html(info->session, GG_CLASS_CHAT,
    - ggp_str_to_uin(rcpt), (unsigned char *)gg_msg) >= 0);
    -
    - g_free(gg_msg);
    -
    - return succ ? 1 : -1;
    -}
    --- a/libpurple/protocols/gg/message-prpl.h Tue Apr 09 21:50:31 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,49 +0,0 @@
    -/* 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.
    - *
    - * Component written by Tomek Wasilczyk (http://www.wasilczyk.pl).
    - *
    - * This file is dual-licensed under the GPL2+ and the X11 (MIT) licences.
    - * As a recipient of this file you may choose, which license to receive the
    - * code under. As a contributor, you have to ensure the new code is
    - * compatible with both.
    - *
    - * 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
    - */
    -
    -#ifndef PURPLE_GG_MESSAGE_PRPL_H
    -#define PURPLE_GG_MESSAGE_PRPL_H
    -
    -#include <purple.h>
    -#include <libgadu.h>
    -
    -typedef struct _ggp_message_session_data ggp_message_session_data;
    -
    -void ggp_message_setup_global(void);
    -void ggp_message_cleanup_global(void);
    -void ggp_message_setup(PurpleConnection *gc);
    -void ggp_message_cleanup(PurpleConnection *gc);
    -
    -void ggp_message_got(PurpleConnection *gc, const struct gg_event_msg *ev);
    -void ggp_message_got_multilogon(PurpleConnection *gc,
    - const struct gg_event_msg *ev);
    -
    -int ggp_message_send_im(PurpleProtocolIM *im, PurpleConnection *gc, PurpleConversation *conversation, PurpleMessage *msg);
    -gchar * ggp_message_format_to_gg(PurpleConversation *conv, const gchar *text);
    -
    -#endif /* PURPLE_GG_MESSAGE_PRPL_H */
    --- a/libpurple/protocols/gg/multilogon.c Tue Apr 09 21:50:31 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,264 +0,0 @@
    -/* 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 "multilogon.h"
    -
    -#include <glib/gi18n-lib.h>
    -
    -#include "gg.h"
    -#include "keymapper.h"
    -#include "utils.h"
    -#include "message-prpl.h"
    -
    -typedef struct
    -{
    - uint64_t id;
    - uint32_t remote_addr;
    - gchar *name;
    - GDateTime *logon_time;
    -} ggp_multilogon_session_info;
    -
    -struct _ggp_multilogon_session_data
    -{
    - int session_count;
    - ggp_multilogon_session_info *sessions;
    - PurpleRequestDatasheet *sheet_handle;
    - gpointer dialog_handle;
    - ggp_keymapper *sid_mapper;
    -};
    -
    -static inline ggp_multilogon_session_data *
    -ggp_multilogon_get_mldata(PurpleConnection *gc);
    -
    -
    -static inline ggp_multilogon_session_data *
    -ggp_multilogon_get_mldata(PurpleConnection *gc)
    -{
    - GGPInfo *accdata = purple_connection_get_protocol_data(gc);
    - return accdata->multilogon_data;
    -}
    -
    -void
    -ggp_multilogon_setup(PurpleConnection *gc)
    -{
    - GGPInfo *accdata = purple_connection_get_protocol_data(gc);
    -
    - ggp_multilogon_session_data *mldata =
    - g_new0(ggp_multilogon_session_data, 1);
    - accdata->multilogon_data = mldata;
    -
    - mldata->sid_mapper = ggp_keymapper_new();
    -}
    -
    -static void
    -ggp_multilogon_free_sessions(PurpleConnection *gc)
    -{
    - ggp_multilogon_session_data *mldata = ggp_multilogon_get_mldata(gc);
    - int i;
    -
    - for (i = 0; i < mldata->session_count; i++) {
    - g_free(mldata->sessions[i].name);
    - g_clear_pointer(&mldata->sessions[i].logon_time, g_date_time_unref);
    - }
    - g_free(mldata->sessions);
    -
    - mldata->sessions = NULL;
    - mldata->session_count = 0;
    -}
    -
    -void
    -ggp_multilogon_cleanup(PurpleConnection *gc)
    -{
    - ggp_multilogon_session_data *mldata = ggp_multilogon_get_mldata(gc);
    -
    - if (mldata->dialog_handle) {
    - purple_request_close(PURPLE_REQUEST_FIELDS,
    - mldata->dialog_handle);
    - mldata->dialog_handle = NULL;
    - }
    -
    - ggp_multilogon_free_sessions(gc);
    - ggp_keymapper_free(mldata->sid_mapper);
    - g_free(mldata);
    -}
    -
    -static uint64_t
    -ggp_multilogon_sid_from_libgadu(gg_multilogon_id_t lsid)
    -{
    - uint64_t sid;
    -
    - memcpy(&sid, lsid.id, sizeof(uint64_t));
    -
    - return sid;
    -}
    -
    -static void
    -ggp_multilogon_sid_to_libgadu(uint64_t sid, gg_multilogon_id_t *lsid)
    -{
    - g_return_if_fail(lsid != NULL);
    -
    - memcpy(lsid->id, &sid, sizeof(uint64_t));
    -}
    -
    -static void
    -ggp_multilogon_fill_sessions(PurpleRequestDatasheet *sheet,
    - PurpleConnection *gc)
    -{
    - ggp_multilogon_session_data *mldata = ggp_multilogon_get_mldata(gc);
    - ggp_keymapper *km = mldata->sid_mapper;
    - int i;
    -
    - purple_request_datasheet_record_mark_all_for_rem(sheet);
    -
    - for (i = 0; i < mldata->session_count; i++) {
    - ggp_multilogon_session_info *sess = &mldata->sessions[i];
    - PurpleRequestDatasheetRecord *rec;
    - gchar *tmp = NULL;
    -
    - rec = purple_request_datasheet_record_add(sheet,
    - ggp_keymapper_to_key(km, sess->id));
    -
    - purple_request_datasheet_record_set_string_data(rec, 0,
    - ggp_ipv4_to_str(sess->remote_addr));
    -
    - tmp = g_date_time_format(sess->logon_time, "%c");
    - purple_request_datasheet_record_set_string_data(rec, 1, tmp);
    - g_free(tmp);
    -
    - purple_request_datasheet_record_set_string_data(rec, 2, sess->name);
    - }
    -
    - purple_request_datasheet_record_remove_marked(sheet);
    -}
    -
    -void
    -ggp_multilogon_info(PurpleConnection *gc, struct gg_event_multilogon_info *info)
    -{
    - ggp_multilogon_session_data *mldata = ggp_multilogon_get_mldata(gc);
    - int i;
    -
    - ggp_multilogon_free_sessions(gc);
    -
    - purple_debug_info("gg", "ggp_multilogon_info: session list changed "
    - "(count now: %d)\n", info->count);
    -
    - mldata->sessions = g_new(ggp_multilogon_session_info, info->count);
    - for (i = 0; i < info->count; i++) {
    - struct gg_multilogon_session *lsess = &info->sessions[i];
    - ggp_multilogon_session_info *psess = &mldata->sessions[i];
    -
    - psess->id = ggp_multilogon_sid_from_libgadu(lsess->id);
    - psess->remote_addr = lsess->remote_addr;
    - psess->name = g_strdup(lsess->name);
    - psess->logon_time = g_date_time_new_from_unix_local(lsess->logon_time);
    - }
    -
    - mldata->session_count = info->count;
    -
    - if (mldata->sheet_handle != NULL)
    - ggp_multilogon_fill_sessions(mldata->sheet_handle, gc);
    -}
    -
    -static void
    -ggp_multilogon_disconnect(PurpleRequestDatasheetRecord *rec, gpointer _gc)
    -{
    - PurpleConnection *gc = _gc;
    - ggp_multilogon_session_data *mldata = ggp_multilogon_get_mldata(gc);
    - GGPInfo *accdata = purple_connection_get_protocol_data(gc);
    - uint64_t sid;
    - gpointer key;
    - gg_multilogon_id_t lsid;
    -
    - key = purple_request_datasheet_record_get_key(rec);
    - sid = ggp_keymapper_from_key(mldata->sid_mapper, key);
    -
    - ggp_multilogon_sid_to_libgadu(sid, &lsid);
    - gg_multilogon_disconnect(accdata->session, lsid);
    -
    - purple_request_datasheet_record_remove(
    - purple_request_datasheet_record_get_datasheet(rec), key);
    -}
    -
    -static void
    -ggp_multilogin_close_request(ggp_multilogon_session_data *mldata)
    -{
    - mldata->sheet_handle = NULL;
    - mldata->dialog_handle = NULL;
    -}
    -
    -void
    -ggp_multilogon_dialog(PurpleConnection *gc)
    -{
    - ggp_multilogon_session_data *mldata = ggp_multilogon_get_mldata(gc);
    - PurpleRequestField *field;
    - PurpleRequestPage *page;
    - PurpleRequestGroup *group;
    - PurpleRequestCommonParameters *cpar;
    - PurpleRequestDatasheet *sheet;
    - PurpleRequestDatasheetAction *action;
    - gpointer dialog_handle;
    -
    - if (mldata->dialog_handle != NULL)
    - return;
    -
    - page = purple_request_page_new();
    - group = purple_request_group_new(NULL);
    - purple_request_page_add_group(page, group);
    -
    - sheet = purple_request_datasheet_new();
    - purple_request_datasheet_add_column(sheet,
    - PURPLE_REQUEST_DATASHEET_COLUMN_STRING, _("IP"));
    - purple_request_datasheet_add_column(sheet,
    - PURPLE_REQUEST_DATASHEET_COLUMN_STRING, _("Logon time"));
    - purple_request_datasheet_add_column(sheet,
    - PURPLE_REQUEST_DATASHEET_COLUMN_STRING, _("Session"));
    -
    - action = purple_request_datasheet_action_new();
    - purple_request_datasheet_action_set_label(action, _("Disconnect"));
    - purple_request_datasheet_action_set_cb(action,
    - ggp_multilogon_disconnect, gc);
    - purple_request_datasheet_add_action(sheet, action);
    - ggp_multilogon_fill_sessions(sheet, gc);
    -
    - field = purple_request_field_datasheet_new("sessions", NULL, sheet);
    - purple_request_group_add_field(group, field);
    -
    - cpar = purple_request_cpar_new();
    - purple_request_cpar_set_icon(cpar, PURPLE_REQUEST_ICON_DIALOG);
    -
    - dialog_handle = purple_request_fields(gc,
    - _("Other Gadu-Gadu sessions"), NULL, NULL, page,
    - NULL, NULL, _("Close"), NULL,
    - cpar, NULL);
    - mldata->sheet_handle = sheet;
    - mldata->dialog_handle = dialog_handle;
    -
    - purple_request_add_close_notify(dialog_handle,
    - (GDestroyNotify)ggp_multilogin_close_request, mldata);
    -}
    --- a/libpurple/protocols/gg/multilogon.h Tue Apr 09 21:50:31 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,46 +0,0 @@
    -/* 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
    - */
    -
    -#ifndef PURPLE_GG_MULTILOGON_H
    -#define PURPLE_GG_MULTILOGON_H
    -
    -#include <purple.h>
    -#include <libgadu.h>
    -
    -typedef struct _ggp_multilogon_session_data ggp_multilogon_session_data;
    -
    -void ggp_multilogon_setup(PurpleConnection *gc);
    -void ggp_multilogon_cleanup(PurpleConnection *gc);
    -
    -void ggp_multilogon_info(PurpleConnection *gc,
    - struct gg_event_multilogon_info *msg);
    -
    -void ggp_multilogon_dialog(PurpleConnection *gc);
    -
    -#endif /* PURPLE_GG_MULTILOGON_H */
    --- a/libpurple/protocols/gg/oauth/oauth-purple.c Tue Apr 09 21:50:31 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,301 +0,0 @@
    -/* 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 "oauth-purple.h"
    -#include "gg.h"
    -
    -#include "oauth.h"
    -#include "../utils.h"
    -#include "../xml.h"
    -
    -#include <purple.h>
    -
    -#define GGP_OAUTH_RESPONSE_MAX 10240
    -
    -typedef struct
    -{
    - SoupMessage *msg;
    - PurpleConnection *gc;
    - ggp_oauth_request_cb callback;
    - gpointer user_data;
    - gchar *token;
    - gchar *token_secret;
    -
    - gchar *sign_method, *sign_url;
    -} ggp_oauth_data;
    -
    -static void ggp_oauth_data_free(ggp_oauth_data *data)
    -{
    - g_object_unref(data->msg);
    - g_free(data->token);
    - g_free(data->token_secret);
    - g_free(data->sign_method);
    - g_free(data->sign_url);
    - g_free(data);
    -}
    -
    -static void
    -ggp_oauth_access_token_got(GObject *source, GAsyncResult *result,
    - gpointer user_data)
    -{
    - ggp_oauth_data *data = user_data;
    - GBytes *response_body = NULL;
    - const char *buffer = NULL;
    - gsize size = 0;
    - gchar *token = NULL, *token_secret = NULL;
    - PurpleXmlNode *xml;
    - gboolean succ = TRUE;
    - 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_oauth_access_token_got: failed: %s",
    - error->message);
    - ggp_oauth_data_free(data);
    - g_error_free(error);
    - return;
    - }
    -
    - buffer = g_bytes_get_data(response_body, &size);
    - xml = purple_xmlnode_from_str(buffer, size);
    - g_bytes_unref(response_body);
    -
    - if (xml == NULL) {
    - purple_debug_error("gg", "ggp_oauth_access_token_got: invalid xml");
    - ggp_oauth_data_free(data);
    - return;
    - }
    -
    - succ &= ggp_xml_get_string(xml, "oauth_token", &token);
    - succ &= ggp_xml_get_string(xml, "oauth_token_secret", &token_secret);
    - purple_xmlnode_free(xml);
    - if (!succ || strlen(token) < 10) {
    - purple_debug_error("gg", "ggp_oauth_access_token_got: invalid xml - "
    - "token is not present");
    - g_free(token);
    - g_free(token_secret);
    - ggp_oauth_data_free(data);
    - return;
    - }
    -
    - if (data->sign_url) {
    - PurpleAccount *account;
    - PurpleContactInfo *info = NULL;
    - gchar *auth;
    -
    - purple_debug_misc("gg", "ggp_oauth_access_token_got: got access token, "
    - "returning signed url");
    -
    - account = purple_connection_get_account(data->gc);
    - info = PURPLE_CONTACT_INFO(account);
    - auth = gg_oauth_generate_header(
    - data->sign_method, data->sign_url,
    - purple_contact_info_get_username(info),
    - purple_connection_get_password(data->gc), token, token_secret);
    - data->callback(data->gc, auth, data->user_data);
    - } else {
    - purple_debug_misc(
    - "gg",
    - "ggp_oauth_access_token_got: got access token, returning it");
    - data->callback(data->gc, token, data->user_data);
    - }
    -
    - g_free(token);
    - g_free(token_secret);
    - ggp_oauth_data_free(data);
    -}
    -
    -static void
    -ggp_oauth_authorization_done(GObject *source,
    - G_GNUC_UNUSED GAsyncResult *result,
    - gpointer user_data)
    -{
    - ggp_oauth_data *data = user_data;
    - PurpleAccount *account;
    - PurpleContactInfo *info = NULL;
    - SoupStatus status_code;
    - char *auth;
    - SoupMessage *msg = NULL;
    - const char *method = "POST";
    - const char *url = "http://api.gadu-gadu.pl/access_token";
    -
    - PURPLE_ASSERT_CONNECTION_IS_VALID(data->gc);
    -
    - account = purple_connection_get_account(data->gc);
    - info = PURPLE_CONTACT_INFO(account);
    -
    - status_code = soup_message_get_status(data->msg);
    - if (status_code != 302) {
    - purple_debug_error("gg",
    - "ggp_oauth_authorization_done: failed (code = %d)",
    - status_code);
    - ggp_oauth_data_free(data);
    - return;
    - }
    -
    - purple_debug_misc("gg", "ggp_oauth_authorization_done: authorization done, "
    - "requesting access token...");
    -
    - auth = gg_oauth_generate_header(method, url,
    - purple_contact_info_get_username(info),
    - purple_connection_get_password(data->gc),
    - data->token, data->token_secret);
    -
    - g_clear_object(&data->msg);
    - data->msg = msg = soup_message_new(method, url);
    - // purple_http_request_set_max_len(req, GGP_OAUTH_RESPONSE_MAX);
    - soup_message_headers_replace(soup_message_get_request_headers(msg),
    - "Authorization", auth);
    - soup_session_send_and_read_async(SOUP_SESSION(source), msg,
    - G_PRIORITY_DEFAULT, NULL,
    - ggp_oauth_access_token_got, data);
    -
    - g_free(auth);
    -}
    -
    -static void
    -ggp_oauth_request_token_got(GObject *source, GAsyncResult *result,
    - gpointer user_data)
    -{
    - SoupSession *session = SOUP_SESSION(source);
    - ggp_oauth_data *data = user_data;
    - GBytes *response_body = NULL;
    - const char *buffer = NULL;
    - gsize size = 0;
    - PurpleAccount *account;
    - PurpleXmlNode *xml;
    - SoupMessage *msg = NULL;
    - gchar *request_data;
    - GBytes *body = NULL;
    - gboolean succ = TRUE;
    - GError *error = NULL;
    -
    - PURPLE_ASSERT_CONNECTION_IS_VALID(data->gc);
    -
    - account = purple_connection_get_account(data->gc);
    -
    - if(!SOUP_STATUS_IS_SUCCESSFUL(soup_message_get_status(data->msg))) {
    - purple_debug_error("gg", "ggp_oauth_request_token_got: "
    - "requested token not received\n");
    - ggp_oauth_data_free(data);
    - return;
    - }
    -
    - response_body = soup_session_send_and_read_finish(session, result, &error);
    - if(response_body == NULL) {
    - purple_debug_error("gg", "ggp_oauth_access_token_got: failed: %s",
    - error->message);
    - ggp_oauth_data_free(data);
    - g_error_free(error);
    - return;
    - }
    -
    - purple_debug_misc("gg", "ggp_oauth_request_token_got: "
    - "got request token, doing authorization...\n");
    -
    - buffer = g_bytes_get_data(response_body, &size);
    - xml = purple_xmlnode_from_str(buffer, size);
    - g_bytes_unref(response_body);
    -
    - if (xml == NULL) {
    - purple_debug_error("gg", "ggp_oauth_request_token_got: "
    - "invalid xml\n");
    - ggp_oauth_data_free(data);
    - return;
    - }
    -
    - succ &= ggp_xml_get_string(xml, "oauth_token", &data->token);
    - succ &= ggp_xml_get_string(xml, "oauth_token_secret",
    - &data->token_secret);
    - purple_xmlnode_free(xml);
    - if (!succ) {
    - purple_debug_error("gg", "ggp_oauth_request_token_got: "
    - "invalid xml - token is not present\n");
    - ggp_oauth_data_free(data);
    - return;
    - }
    -
    - request_data = g_strdup_printf(
    - "callback_url=http://www.mojageneracja.pl&request_token=%s&"
    - "uin=%s&password=%s", data->token,
    - purple_contact_info_get_username(PURPLE_CONTACT_INFO(account)),
    - purple_connection_get_password(data->gc));
    -
    - g_clear_object(&data->msg);
    - data->msg = msg = soup_message_new("POST",
    - "https://login.gadu-gadu.pl/authorize");
    - // purple_http_request_set_max_len(msg, GGP_OAUTH_RESPONSE_MAX);
    - /* we don't need any results, nor 302 redirection */
    - soup_message_set_flags(msg, SOUP_MESSAGE_NO_REDIRECT);
    - 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(session, msg, G_PRIORITY_DEFAULT, NULL,
    - ggp_oauth_authorization_done, data);
    -}
    -
    -void
    -ggp_oauth_request(PurpleConnection *gc, ggp_oauth_request_cb callback,
    - gpointer user_data, const gchar *sign_method,
    - const gchar *sign_url)
    -{
    - GGPInfo *info = purple_connection_get_protocol_data(gc);
    - PurpleAccount *account = purple_connection_get_account(gc);
    - SoupMessage *msg;
    - char *auth;
    - const char *method = "POST";
    - const char *url = "http://api.gadu-gadu.pl/request_token";
    - ggp_oauth_data *data;
    -
    - purple_debug_misc("gg", "ggp_oauth_request: requesting token...\n");
    -
    - auth = gg_oauth_generate_header(
    - method, url,
    - purple_contact_info_get_username(PURPLE_CONTACT_INFO(account)),
    - purple_connection_get_password(gc), NULL, NULL);
    -
    - data = g_new0(ggp_oauth_data, 1);
    - data->gc = gc;
    - data->callback = callback;
    - data->user_data = user_data;
    - data->sign_method = g_strdup(sign_method);
    - data->sign_url = g_strdup(sign_url);
    -
    - data->msg = msg = soup_message_new(method, url);
    - // purple_http_request_set_max_len(req, GGP_OAUTH_RESPONSE_MAX);
    - soup_message_headers_replace(soup_message_get_request_headers(msg),
    - "Authorization", auth);
    - soup_session_send_and_read_async(info->http, msg, G_PRIORITY_DEFAULT, NULL,
    - ggp_oauth_request_token_got, data);
    -
    - g_free(auth);
    -}
    --- a/libpurple/protocols/gg/oauth/oauth-purple.h Tue Apr 09 21:50:31 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,42 +0,0 @@
    -/* 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
    - */
    -
    -#ifndef _GGP_OAUTH_PURPLE_H
    -#define _GGP_OAUTH_PURPLE_H
    -
    -#include <purple.h>
    -#include <libgadu.h>
    -
    -typedef void (*ggp_oauth_request_cb)(PurpleConnection *gc, const gchar *token,
    - gpointer user_data);
    -
    -void ggp_oauth_request(PurpleConnection *gc, ggp_oauth_request_cb callback,
    - gpointer user_data, const gchar *sign_method, const gchar *sign_url);
    -
    -#endif /* _GGP_OAUTH_PURPLE_H */
    --- a/libpurple/protocols/gg/oauth/oauth.c Tue Apr 09 21:50:31 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,185 +0,0 @@
    -/* 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.
    - *
    - * Code adapted from libgadu (C) 2008 Wojtek Kaniewski <wojtekka@irc.pl>
    - * (http://toxygen.net/libgadu/) during Google Summer of Code 2012
    - * by Tomek Wasilczyk (http://www.wasilczyk.pl).
    - *
    - * 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 "oauth.h"
    -
    -#include <time.h>
    -
    -#include <glib.h>
    -
    -char *gg_oauth_static_nonce; /* dla unit testów */
    -char *gg_oauth_static_timestamp; /* dla unit testów */
    -
    -static char *gg_oauth_generate_request(gboolean header, ...) G_GNUC_NULL_TERMINATED;
    -
    -static void gg_oauth_generate_nonce(char *buf, int len)
    -{
    - const char charset[] = "0123456789";
    -
    - if (buf == NULL || len < 1)
    - return;
    -
    - while (len > 1) {
    - *buf++ = charset[(unsigned) (((float) sizeof(charset) - 1.0) * g_random_int() / (RAND_MAX + 1.0))];
    - len--;
    - }
    -
    - *buf = 0;
    -}
    -
    -/* Returns a comma separated header value if header is true,
    - * or a url-encoded request otherwise
    - */
    -static char *
    -gg_oauth_generate_request(gboolean header, ...)
    -{
    - GString *res = g_string_new(NULL);
    - va_list params;
    - const gchar *key;
    - gboolean truncate = FALSE;
    -
    - if(header) {
    - res = g_string_append(res, "OAuth ");
    - }
    -
    - va_start(params, header);
    - while((key = va_arg(params, const gchar *))) {
    - const gchar *value = va_arg(params, const gchar *);
    - gchar *escaped = g_uri_escape_string(value, NULL, FALSE);
    -
    - if(header) {
    - g_string_append_printf(res, "%s=\"%s\",", key, escaped);
    - } else {
    - g_string_append_printf(res, "%s=%s&", key, escaped);
    - }
    -
    - g_free(escaped);
    -
    - truncate = TRUE;
    - }
    - va_end(params);
    -
    - if(truncate) {
    - /* remove trailing separator */
    - res = g_string_truncate(res, res->len - 1);
    - }
    -
    - return g_string_free(res, FALSE);
    -}
    -
    -static gchar *gg_hmac_sha1(const char *key, const char *message)
    -{
    - GHmac *hmac;
    - guchar digest[20];
    - gsize digest_len = 20;
    -
    - hmac = g_hmac_new(G_CHECKSUM_SHA1, (guchar *)key, strlen(key));
    - g_hmac_update(hmac, (guchar *)message, -1);
    - g_hmac_get_digest(hmac, digest, &digest_len);
    - g_hmac_unref(hmac);
    -
    - return g_base64_encode(digest, sizeof(digest));
    -}
    -
    -static char *
    -gg_oauth_generate_signature(const char *method, const char *url,
    - const char *request, const char *consumer_secret,
    - const char *token_secret)
    -{
    - char *text, *key, *res;
    - gchar *url_e, *request_e, *consumer_secret_e, *token_secret_e;
    -
    - url_e = g_uri_escape_string(url, "?", FALSE);
    - g_strdelimit(url_e, "?", '\0');
    - request_e = g_uri_escape_string(request, NULL, FALSE);
    - text = g_strdup_printf("%s&%s&%s", method, url_e, request_e);
    - g_free(url_e);
    - g_free(request_e);
    -
    - consumer_secret_e = g_uri_escape_string(consumer_secret, NULL, FALSE);
    - token_secret_e = token_secret ? g_uri_escape_string(token_secret, NULL, FALSE) : NULL;
    - key = g_strdup_printf("%s&%s", consumer_secret_e, token_secret ? token_secret_e : "");
    - g_free(consumer_secret_e);
    - g_free(token_secret_e);
    -
    - res = gg_hmac_sha1(key, text);
    -
    - g_free(key);
    - g_free(text);
    -
    - return res;
    -}
    -
    -char *
    -gg_oauth_generate_header(const char *method, const char *url,
    - const char *consumer_key, const char *consumer_secret,
    - const char *token, const char *token_secret)
    -{
    - char *request, *signature, *res;
    - char nonce[80], timestamp[16];
    -
    - if (gg_oauth_static_nonce == NULL)
    - gg_oauth_generate_nonce(nonce, sizeof(nonce));
    - else {
    - strncpy(nonce, gg_oauth_static_nonce, sizeof(nonce) - 1);
    - nonce[sizeof(nonce) - 1] = 0;
    - }
    -
    - if (gg_oauth_static_timestamp == NULL) {
    - g_snprintf(timestamp, sizeof(timestamp), "%ld", time(NULL));
    - } else {
    - strncpy(timestamp, gg_oauth_static_timestamp, sizeof(timestamp) - 1);
    - timestamp[sizeof(timestamp) - 1] = 0;
    - }
    -
    - request = gg_oauth_generate_request(FALSE,
    - "oauth_consumer_key", consumer_key,
    - "oauth_nonce", nonce,
    - "oauth_signature_method", "HMAC-SHA1",
    - "oauth_timestamp", timestamp,
    - "oauth_token", token,
    - "oauth_version", "1.0",
    - NULL);
    -
    - signature = gg_oauth_generate_signature(method, url, request, consumer_secret, token_secret);
    -
    - g_free(request);
    -
    - if (signature == NULL)
    - return NULL;
    -
    - res = gg_oauth_generate_request(TRUE,
    - "oauth_version", "1.0",
    - "oauth_nonce", nonce,
    - "oauth_timestamp", timestamp,
    - "oauth_consumer_key", consumer_key,
    - "oauth_token", token,
    - "oauth_signature_method", "HMAC-SHA1",
    - "oauth_signature", signature,
    - NULL);
    - g_free(signature);
    -
    - return res;
    -}
    --- a/libpurple/protocols/gg/oauth/oauth.h Tue Apr 09 21:50:31 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,35 +0,0 @@
    -/* 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.
    - *
    - * Code adapted from libgadu (C) 2008 Wojtek Kaniewski <wojtekka@irc.pl>
    - * (http://toxygen.net/libgadu/) during Google Summer of Code 2012
    - * by Tomek Wasilczyk (http://www.wasilczyk.pl).
    - *
    - * 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
    - */
    -
    -#ifndef _GGP_OAUTH_H
    -#define _GGP_OAUTH_H
    -
    -#include <libgadu.h>
    -
    -char * gg_oauth_generate_header(const char *method, const char *url,
    - const char *consumer_key, const char *consumer_secret,
    - const char *token, const char *token_secret);
    -
    -#endif /* _GGP_OAUTH_H */
    --- a/libpurple/protocols/gg/pubdir-prpl.c Tue Apr 09 21:50:31 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,1025 +0,0 @@
    -/* 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;
    - PurpleRequestFieldChoice *choice;
    -
    - 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);
    - choice = PURPLE_REQUEST_FIELD_CHOICE(field);
    - purple_request_field_choice_add(choice, _("Male or female"), NULL);
    - purple_request_field_choice_add(choice, _("Male"),
    - GINT_TO_POINTER(GGP_PUBDIR_GENDER_MALE));
    - purple_request_field_choice_add(choice, _("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;
    - PurpleRequestFieldChoice *choice;
    - 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);
    - choice = PURPLE_REQUEST_FIELD_CHOICE(field);
    - purple_request_field_set_required(field, TRUE);
    - purple_request_field_choice_add(choice, _("Male"),
    - GINT_TO_POINTER(GGP_PUBDIR_GENDER_MALE));
    - purple_request_field_choice_add(choice, _("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);
    - choice = PURPLE_REQUEST_FIELD_CHOICE(field);
    - purple_request_group_add_field(group, field);
    - for (i = 0; i < ggp_pubdir_provinces_count; i++) {
    - purple_request_field_choice_add(choice, ggp_pubdir_provinces[i],
    - GINT_TO_POINTER(i));
    - if (record && i == record->province) {
    - purple_request_field_choice_set_value(choice, GINT_TO_POINTER(i));
    - purple_request_field_choice_set_default_value(choice,
    - 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);
    -}
    --- a/libpurple/protocols/gg/pubdir-prpl.h Tue Apr 09 21:50:31 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,73 +0,0 @@
    -/* 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
    - */
    -
    -#ifndef PURPLE_GG_PUBDIR_PRPL_H
    -#define PURPLE_GG_PUBDIR_PRPL_H
    -
    -#include <purple.h>
    -#include <libgadu.h>
    -
    -typedef enum
    -{
    - GGP_PUBDIR_GENDER_UNSPECIFIED,
    - GGP_PUBDIR_GENDER_FEMALE,
    - GGP_PUBDIR_GENDER_MALE,
    -} ggp_pubdir_gender;
    -
    -typedef struct
    -{
    - uin_t uin;
    - gchar *label;
    - gchar *nickname;
    - gchar *first_name;
    - gchar *last_name;
    - ggp_pubdir_gender gender;
    - gchar *city;
    - unsigned int province;
    - GDateTime *birth;
    - unsigned int age;
    -} ggp_pubdir_record;
    -
    -typedef struct _ggp_pubdir_search_form ggp_pubdir_search_form;
    -
    -typedef void (*ggp_pubdir_request_cb)(PurpleConnection *gc, int records_count,
    - const ggp_pubdir_record *records, int next_offset, void *user_data);
    -
    -void ggp_pubdir_get_info(PurpleConnection *gc, uin_t uin,
    - ggp_pubdir_request_cb cb, void *user_data);
    -
    -void ggp_pubdir_get_info_protocol(PurpleProtocolServer *protocol_server, PurpleConnection *gc, const char *name);
    -void ggp_pubdir_request_buddy_alias(PurpleConnection *gc, PurpleBuddy *buddy);
    -
    -void ggp_pubdir_search(PurpleConnection *gc,
    - const ggp_pubdir_search_form *form);
    -
    -void ggp_pubdir_set_info(PurpleConnection *gc);
    -
    -#endif /* PURPLE_GG_PUBDIR_PRPL_H */
    --- a/libpurple/protocols/gg/purplew.c Tue Apr 09 21:50:31 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,153 +0,0 @@
    -/* 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 "purplew.h"
    -
    -#include "tcpsocket.h"
    -
    -#include <glib/gi18n-lib.h>
    -
    -guint ggp_purplew_http_input_add(struct gg_http *http_req,
    - PurpleInputFunction func, gpointer user_data)
    -{
    - if (purple_debug_is_verbose()) {
    - purple_debug_misc("gg", "ggp_purplew_http_input_add: "
    - "[req=%p, fd=%d, cond=%d]\n",
    - http_req, http_req->fd, http_req->check);
    - }
    - return purple_input_add(http_req->fd,
    - ggp_tcpsocket_inputcond_gg_to_purple(http_req->check),
    - func, user_data);
    -}
    -
    -static void
    -ggp_purplew_request_processing_cancel(ggp_purplew_request_processing_handle *handle,
    - G_GNUC_UNUSED gint id)
    -{
    - handle->cancel_cb(handle->gc, handle->user_data);
    - g_free(handle);
    -}
    -
    -ggp_purplew_request_processing_handle * ggp_purplew_request_processing(
    - PurpleConnection *gc, const gchar *msg, void *user_data,
    - ggp_purplew_request_processing_cancel_cb cancel_cb)
    -{
    - ggp_purplew_request_processing_handle *handle =
    - g_new(ggp_purplew_request_processing_handle, 1);
    -
    - handle->gc = gc;
    - handle->cancel_cb = cancel_cb;
    - handle->user_data = user_data;
    - handle->request_handle = purple_request_action(gc, _("Please wait..."),
    - (msg ? msg : _("Please wait...")), NULL,
    - PURPLE_DEFAULT_ACTION_NONE,
    - purple_request_cpar_from_connection(gc), handle, 1,
    - _("Cancel"), G_CALLBACK(ggp_purplew_request_processing_cancel));
    -
    - return handle;
    -}
    -
    -void ggp_purplew_request_processing_done(
    - ggp_purplew_request_processing_handle *handle)
    -{
    - purple_request_close(PURPLE_REQUEST_ACTION, handle->request_handle);
    - g_free(handle);
    -}
    -
    -PurpleGroup * ggp_purplew_buddy_get_group_only(PurpleBuddy *buddy)
    -{
    - PurpleGroup *group = purple_buddy_get_group(buddy);
    - if (!group)
    - return NULL;
    - if (0 == g_strcmp0(PURPLE_BLIST_DEFAULT_GROUP_NAME,
    - purple_group_get_name(group)))
    - {
    - return NULL;
    - }
    - if (0 == g_strcmp0("Buddies", purple_group_get_name(group)))
    - return NULL;
    - return group;
    -}
    -
    -GList * ggp_purplew_group_get_buddies(PurpleGroup *group, PurpleAccount *account)
    -{
    - GList *buddies = NULL;
    - PurpleBlistNode *gnode, *cnode, *bnode;
    -
    - g_return_val_if_fail(group != NULL, NULL);
    -
    - gnode = PURPLE_BLIST_NODE(group);
    - for (cnode = gnode->child; cnode; cnode = cnode->next) {
    - if (!PURPLE_IS_META_CONTACT(cnode))
    - continue;
    - for (bnode = cnode->child; bnode; bnode = bnode->next) {
    - PurpleBuddy *buddy;
    - if (!PURPLE_IS_BUDDY(bnode))
    - continue;
    -
    - buddy = PURPLE_BUDDY(bnode);
    - if (account == NULL ||
    - purple_buddy_get_account(buddy) == account)
    - {
    - buddies = g_list_append(buddies, buddy);
    - }
    - }
    - }
    -
    - return buddies;
    -}
    -
    -GList * ggp_purplew_account_get_groups(PurpleAccount *account, gboolean exclusive)
    -{
    - PurpleBlistNode *bnode;
    - GList *groups = NULL;
    - for (bnode = purple_blist_get_default_root(); bnode;
    - bnode = bnode->next) {
    - PurpleGroup *group;
    - GSList *accounts;
    - gboolean have_specified = FALSE, have_others = FALSE;
    -
    - if (!PURPLE_IS_GROUP(bnode))
    - continue;
    -
    - group = PURPLE_GROUP(bnode);
    - for (accounts = purple_group_get_accounts(group); accounts;
    - accounts = g_slist_delete_link(accounts, accounts))
    - {
    - if (accounts->data == account)
    - have_specified = TRUE;
    - else
    - have_others = TRUE;
    - }
    -
    - if (have_specified && (!exclusive || !have_others))
    - groups = g_list_append(groups, group);
    - }
    - return groups;
    -}
    --- a/libpurple/protocols/gg/purplew.h Tue Apr 09 21:50:31 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,76 +0,0 @@
    -/* 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
    - */
    -
    -#ifndef PURPLE_GG_PURPLEW_H
    -#define PURPLE_GG_PURPLEW_H
    -
    -#include <purple.h>
    -#include <libgadu.h>
    -
    -/**
    - * Adds an input handler in purple event loop for http request.
    - *
    - * @see purple_input_add
    - *
    - * @param http_req Http connection to watch.
    - * @param func The callback function for data.
    - * @param user_data User-specified data.
    - *
    - * @return The resulting handle (will be greater than 0).
    - */
    -guint ggp_purplew_http_input_add(struct gg_http *http_req,
    - PurpleInputFunction func, gpointer user_data);
    -
    -typedef void (*ggp_purplew_request_processing_cancel_cb)(PurpleConnection *gc,
    - void *user_data);
    -
    -typedef struct
    -{
    - PurpleConnection *gc;
    - ggp_purplew_request_processing_cancel_cb cancel_cb;
    - void *request_handle;
    - void *user_data;
    -} ggp_purplew_request_processing_handle;
    -
    -ggp_purplew_request_processing_handle * ggp_purplew_request_processing(
    - PurpleConnection *gc, const gchar *msg, void *user_data,
    - ggp_purplew_request_processing_cancel_cb oncancel);
    -
    -void ggp_purplew_request_processing_done(
    - ggp_purplew_request_processing_handle *handle);
    -
    -/* ignores default group */
    -PurpleGroup * ggp_purplew_buddy_get_group_only(PurpleBuddy *buddy);
    -
    -GList * ggp_purplew_group_get_buddies(PurpleGroup *group, PurpleAccount *account);
    -
    -/* you must g_free returned list */
    -GList * ggp_purplew_account_get_groups(PurpleAccount *account, gboolean exclusive);
    -
    -#endif /* PURPLE_GG_PURPLEW_H */
    --- a/libpurple/protocols/gg/resolver-purple.c Tue Apr 09 21:50:31 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,205 +0,0 @@
    -/* 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 <purple.h>
    -#include "libpurple/glibcompat.h"
    -
    -#include <libgadu.h>
    -#include "resolver-purple.h"
    -
    -#include <gio/gio.h>
    -
    -static int ggp_resolver_purple_start(int *fd, void **private_data,
    - const char *hostname);
    -
    -static void ggp_resolver_purple_cleanup(void **private_data, int force);
    -
    -static void ggp_resolver_purple_cb(GObject *sender, GAsyncResult *res, gpointer data);
    -
    -typedef struct
    -{
    - GCancellable *cancellable;
    -
    - /**
    - * File descriptors:
    - * pipes[0] - for reading
    - * pipes[1] - for writing
    - */
    - int pipes[2];
    -} ggp_resolver_purple_data;
    -
    -
    -extern void ggp_resolver_purple_setup(void)
    -{
    - if (gg_global_set_custom_resolver(ggp_resolver_purple_start,
    - ggp_resolver_purple_cleanup) != 0)
    - {
    - purple_debug_error("gg", "failed to set custom resolver\n");
    - }
    -}
    -
    -void ggp_resolver_purple_cb(GObject *sender, GAsyncResult *res, gpointer cbdata) {
    - GList *addresses = NULL, *in_addrs = NULL, *l = NULL;
    - GError *error = NULL;
    - gsize native_size = 0; /* this is kind of dirty, but it'll be initialized before we use it */
    -
    - ggp_resolver_purple_data *data = (ggp_resolver_purple_data*)cbdata;
    - const int fd = data->pipes[1];
    -
    - addresses = g_resolver_lookup_by_name_finish(G_RESOLVER(sender),
    - res, &error);
    - if(addresses == NULL) {
    - purple_debug_error("gg", "ggp_resolver_purple_cb failed: %s\n",
    - error->message);
    -
    - g_error_free(error);
    - } else {
    - purple_debug_misc("gg", "ggp_resolver_purple_cb succeeded: (%p, %p)\n",
    - addresses, cbdata);
    - }
    -
    - g_object_unref(data->cancellable);
    - data->cancellable = NULL;
    -
    - for(l = addresses; l; l = l->next) {
    - GInetAddress *inet_address = G_INET_ADDRESS(l->data);
    - GSocketFamily family = G_SOCKET_FAMILY_INVALID;
    - gchar *ip_address = g_inet_address_to_string(inet_address);
    -
    - family = g_inet_address_get_family(inet_address);
    -
    - switch(family) {
    - case G_SOCKET_FAMILY_IPV4:
    - purple_debug_misc("gg", "ggp_resolver_purple_cb "
    - "ipv4: %s\n", ip_address);
    -
    - native_size = g_inet_address_get_native_size(inet_address);
    - in_addrs = g_list_append(in_addrs, g_memdup2(g_inet_address_to_bytes(inet_address), native_size));
    -
    - break;
    - case G_SOCKET_FAMILY_IPV6:
    - purple_debug_misc("gg", "ggp_resolver_purple_cb "
    - "ipv6 (ignore): %s\n", ip_address);
    -
    - break;
    - default:
    - purple_debug_warning("gg", "ggp_resolver_purple_cb "
    - "unexpected sa_family: %d\n",
    - family);
    -
    - break;
    - }
    -
    - g_free(ip_address);
    - }
    -
    - for(l = in_addrs; l; l = l->next) {
    - gint write_size = native_size;
    - if(write(fd, l->data, write_size) != write_size) {
    - purple_debug_error("gg",
    - "ggp_resolver_purple_cb write error on %p\n", l->data);
    - }
    -
    - g_free(l->data);
    - }
    -
    - g_list_free(in_addrs);
    - g_resolver_free_addresses(addresses);
    -}
    -
    -int ggp_resolver_purple_start(int *fd, void **private_data,
    - const char *hostname)
    -{
    - ggp_resolver_purple_data *data;
    - GResolver *resolver;
    -
    - purple_debug_misc("gg", "ggp_resolver_purple_start(%p, %p, \"%s\")\n",
    - fd, private_data, hostname);
    -
    - data = g_new0(ggp_resolver_purple_data, 1);
    - *private_data = (void*)data;
    - data->cancellable = NULL;
    - data->pipes[0] = 0;
    - data->pipes[1] = 0;
    -
    - if (purple_input_pipe(data->pipes) != 0) {
    - purple_debug_error("gg", "ggp_resolver_purple_start: "
    - "unable to create pipe\n");
    - ggp_resolver_purple_cleanup(private_data, 0);
    - return -1;
    - }
    -
    - *fd = data->pipes[0];
    -
    - /* account and port is unknown in this context */
    - data->cancellable = g_cancellable_new();
    -
    - resolver = g_resolver_get_default();
    - g_resolver_lookup_by_name_async(resolver,
    - hostname,
    - data->cancellable,
    - ggp_resolver_purple_cb,
    - (gpointer)data);
    - g_object_unref(resolver);
    -
    - if (!data->cancellable) {
    - purple_debug_error("gg", "ggp_resolver_purple_start: "
    - "unable to call purple_dnsquery_a\n");
    - ggp_resolver_purple_cleanup(private_data, 0);
    - return -1;
    - }
    -
    - return 0;
    -}
    -
    -void ggp_resolver_purple_cleanup(void **private_data, int force)
    -{
    - ggp_resolver_purple_data *data =
    - (ggp_resolver_purple_data*)(*private_data);
    -
    - purple_debug_misc("gg", "ggp_resolver_purple_cleanup(%p, %d)\n",
    - private_data, force);
    -
    - if (!data)
    - return;
    - *private_data = NULL;
    -
    - if (G_IS_CANCELLABLE(data->cancellable)) {
    - g_cancellable_cancel(data->cancellable);
    -
    - g_object_unref(data->cancellable);
    - }
    -
    - if (data->pipes[0])
    - close(data->pipes[0]);
    - if (data->pipes[1])
    - close(data->pipes[1]);
    -
    - g_free(data);
    -}
    --- a/libpurple/protocols/gg/resolver-purple.h Tue Apr 09 21:50:31 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,38 +0,0 @@
    -/* 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
    - */
    -
    -#ifndef PURPLE_GG_RESOLVER_PURPLE_H
    -#define PURPLE_GG_RESOLVER_PURPLE_H
    -
    -/**
    - * Registers custom resolver for libgadu, that uses libpurple for DNS queries.
    - */
    -void ggp_resolver_purple_setup(void);
    -
    -#endif /* PURPLE_GG_RESOLVER_PURPLE_H */
    --- a/libpurple/protocols/gg/resources/gg.gresource.xml Tue Apr 09 21:50:31 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,11 +0,0 @@
    -<?xml version="1.0" encoding="UTF-8"?>
    -<gresources>
    - <gresource prefix="/im/pidgin/libpurple/gg">
    - <file>icons/16x16/apps/im-gadu-gadu.png</file>
    - <file>icons/16x16/apps/scalable/im-gadu-gadu.svg</file>
    - <file>icons/22x22/apps/im-gadu-gadu.png</file>
    - <file>icons/22x22/apps/scalable/im-gadu-gadu.svg</file>
    - <file>icons/48x48/apps/im-gadu-gadu.png</file>
    - <file>icons/scalable/apps/im-gadu-gadu.svg</file>
    - </gresource>
    -</gresources>
    Binary file libpurple/protocols/gg/resources/icons/16x16/apps/im-gadu-gadu.png has changed
    --- a/libpurple/protocols/gg/resources/icons/16x16/apps/scalable/im-gadu-gadu.svg Tue Apr 09 21:50:31 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,365 +0,0 @@
    -<?xml version="1.0" encoding="UTF-8" standalone="no"?>
    -<!-- Created with Inkscape (http://www.inkscape.org/) -->
    -<svg
    - xmlns:dc="http://purl.org/dc/elements/1.1/"
    - xmlns:cc="http://creativecommons.org/ns#"
    - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
    - xmlns:svg="http://www.w3.org/2000/svg"
    - xmlns="http://www.w3.org/2000/svg"
    - xmlns:xlink="http://www.w3.org/1999/xlink"
    - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
    - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
    - width="16px"
    - height="16px"
    - id="svg10335"
    - sodipodi:version="0.32"
    - inkscape:version="0.46"
    - sodipodi:docname="emote-select.svg"
    - inkscape:output_extension="org.inkscape.output.svg.inkscape"
    - inkscape:export-filename="/home/hbons/Bureaublad/gadu-gadu.png"
    - inkscape:export-xdpi="90"
    - inkscape:export-ydpi="90">
    - <defs
    - id="defs10337">
    - <linearGradient
    - id="linearGradient8687"
    - inkscape:collect="always">
    - <stop
    - id="stop8689"
    - offset="0"
    - style="stop-color:#f57575;stop-opacity:1" />
    - <stop
    - id="stop8691"
    - offset="1"
    - style="stop-color:#fea523;stop-opacity:0;" />
    - </linearGradient>
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient8653">
    - <stop
    - style="stop-color:#a40000;stop-opacity:1;"
    - offset="0"
    - id="stop8655" />
    - <stop
    - style="stop-color:#ef2929;stop-opacity:1"
    - offset="1"
    - id="stop8657" />
    - </linearGradient>
    - <inkscape:perspective
    - sodipodi:type="inkscape:persp3d"
    - inkscape:vp_x="0 : 8 : 1"
    - inkscape:vp_y="0 : 1000 : 0"
    - inkscape:vp_z="16 : 8 : 1"
    - inkscape:persp3d-origin="8 : 5.3333333 : 1"
    - id="perspective44" />
    - <linearGradient
    - id="linearGradient11170"
    - inkscape:collect="always">
    - <stop
    - id="stop11172"
    - offset="0"
    - style="stop-color:#000000;stop-opacity:1" />
    - <stop
    - id="stop11174"
    - offset="1"
    - style="stop-color:#2e3436;stop-opacity:0;" />
    - </linearGradient>
    - <linearGradient
    - id="linearGradient11164"
    - inkscape:collect="always">
    - <stop
    - id="stop11166"
    - offset="0"
    - style="stop-color:#000000;stop-opacity:1" />
    - <stop
    - id="stop11168"
    - offset="1"
    - style="stop-color:#2e3436;stop-opacity:0;" />
    - </linearGradient>
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient22870">
    - <stop
    - style="stop-color:#ffffff;stop-opacity:1;"
    - offset="0"
    - id="stop22872" />
    - <stop
    - style="stop-color:#ffffff;stop-opacity:0;"
    - offset="1"
    - id="stop22874" />
    - </linearGradient>
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient22870"
    - id="linearGradient20933"
    - gradientUnits="userSpaceOnUse"
    - x1="-11.187567"
    - y1="-14.181667"
    - x2="31.90255"
    - y2="35.477989" />
    - <linearGradient
    - id="linearGradient11461"
    - inkscape:collect="always">
    - <stop
    - id="stop11463"
    - offset="0"
    - style="stop-color:#fbfeac;stop-opacity:1" />
    - <stop
    - id="stop11465"
    - offset="1"
    - style="stop-color:#eedb3c;stop-opacity:0;" />
    - </linearGradient>
    - <radialGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient11461"
    - id="radialGradient20931"
    - gradientUnits="userSpaceOnUse"
    - cx="11.806158"
    - cy="10.04414"
    - fx="11.88395"
    - fy="6.1128922"
    - r="10.561975"
    - gradientTransform="matrix(1.9034559,3.0329373e-8,-4.2282289e-8,2.6533656,-10.666343,-17.069444)" />
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient22526"
    - id="linearGradient20942"
    - gradientUnits="userSpaceOnUse"
    - gradientTransform="matrix(0.8000001,0,0,0.8980646,-0.6999993,-2.2354831)"
    - x1="8.5062485"
    - y1="9.7487373"
    - x2="8.7451591"
    - y2="10.765122" />
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient22526">
    - <stop
    - style="stop-color:#f57575;stop-opacity:1"
    - offset="0"
    - id="stop22528" />
    - <stop
    - style="stop-color:#fea523;stop-opacity:0;"
    - offset="1"
    - id="stop22530" />
    - </linearGradient>
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient8687"
    - id="linearGradient20915"
    - gradientUnits="userSpaceOnUse"
    - gradientTransform="translate(7.000002,1)"
    - x1="9.2937651"
    - y1="8.8493233"
    - x2="8.7171106"
    - y2="10" />
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient11164"
    - id="linearGradient11158"
    - gradientUnits="userSpaceOnUse"
    - x1="9.432662"
    - y1="9.6526775"
    - x2="10.732871"
    - y2="9.6526775" />
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient11170"
    - id="linearGradient11160"
    - gradientUnits="userSpaceOnUse"
    - x1="9.432662"
    - y1="9.6526775"
    - x2="10.732871"
    - y2="9.6526775" />
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient8653"
    - id="linearGradient8659"
    - x1="14.605568"
    - y1="14.845765"
    - x2="9.3490162"
    - y2="3.9909718"
    - gradientUnits="userSpaceOnUse" />
    - </defs>
    - <sodipodi:namedview
    - id="base"
    - pagecolor="#ffffff"
    - bordercolor="#666666"
    - borderopacity="1.0"
    - inkscape:pageopacity="0.0"
    - inkscape:pageshadow="2"
    - inkscape:zoom="31.392433"
    - inkscape:cx="15.513311"
    - inkscape:cy="8.506005"
    - inkscape:current-layer="layer1"
    - showgrid="true"
    - inkscape:grid-bbox="true"
    - inkscape:document-units="px"
    - inkscape:snap-nodes="false"
    - inkscape:snap-bbox="true"
    - objecttolerance="10"
    - gridtolerance="10"
    - inkscape:window-width="1440"
    - inkscape:window-height="847"
    - inkscape:window-x="0"
    - inkscape:window-y="0">
    - <inkscape:grid
    - type="xygrid"
    - id="grid10376"
    - visible="true"
    - enabled="true" />
    - </sodipodi:namedview>
    - <metadata
    - id="metadata10340">
    - <rdf:RDF>
    - <cc:Work
    - rdf:about="">
    - <dc:format>image/svg+xml</dc:format>
    - <dc:type
    - rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
    - </cc:Work>
    - </rdf:RDF>
    - </metadata>
    - <g
    - id="layer1"
    - inkscape:label="Layer 1"
    - inkscape:groupmode="layer">
    - <rect
    - style="opacity:1;fill:#a40000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
    - id="rect8683"
    - width="2.0000002"
    - height="2"
    - x="12"
    - y="12"
    - rx="1"
    - ry="1" />
    - <rect
    - style="opacity:1;fill:#a40000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
    - id="rect8685"
    - width="2.0000002"
    - height="2"
    - x="2"
    - y="12"
    - rx="1"
    - ry="1" />
    - <rect
    - style="opacity:1;fill:#ef2929;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
    - id="rect8681"
    - width="2.0000002"
    - height="2"
    - x="12"
    - y="2"
    - rx="1"
    - ry="1" />
    - <rect
    - style="opacity:1;fill:#ef2929;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
    - id="rect8679"
    - width="2.0000002"
    - height="2"
    - x="1.9999998"
    - y="2"
    - rx="1"
    - ry="1" />
    - <rect
    - style="opacity:1;fill:#a40000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
    - id="rect8675"
    - width="2.0000002"
    - height="2"
    - x="14"
    - y="7"
    - rx="1"
    - ry="1" />
    - <rect
    - style="opacity:1;fill:#ef2929;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
    - id="rect8671"
    - width="2.0000002"
    - height="2"
    - x="-2.3841858e-07"
    - y="7"
    - rx="1"
    - ry="1" />
    - <rect
    - style="opacity:1;fill:#a40000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
    - id="rect8665"
    - width="2.0000002"
    - height="2"
    - x="7"
    - y="14"
    - rx="1"
    - ry="1" />
    - <rect
    - style="opacity:1;fill:#ef2929;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
    - id="rect8663"
    - width="2.0000002"
    - height="2"
    - x="7"
    - y="0"
    - rx="1"
    - ry="1" />
    - <rect
    - inkscape:label="22x22"
    - y="-0.90471238"
    - x="-0.90471238"
    - height="17.809425"
    - width="17.809425"
    - id="rect6749"
    - style="opacity:0;fill:#d3d7cf;fill-opacity:0;fill-rule:nonzero;stroke:none;stroke-width:2;marker:none;marker-start:none;marker-mid:none;marker-end:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
    - <path
    - transform="matrix(0.651488,0,0,0.651488,0.30843,0.8446922)"
    - d="M 21.781414,10.983024 A 9.975256,9.975256 0 1 1 1.8309021,10.983024 A 9.975256,9.975256 0 1 1 21.781414,10.983024 z"
    - sodipodi:ry="9.975256"
    - sodipodi:rx="9.975256"
    - sodipodi:cy="10.983024"
    - sodipodi:cx="11.806158"
    - id="path17960"
    - style="opacity:1;fill:#fcaf3e;fill-opacity:1;stroke:url(#linearGradient8659);stroke-width:1.53875661000000008;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline;enable-background:new"
    - sodipodi:type="arc" />
    - <path
    - transform="matrix(0.5513643,0,0,0.5513643,1.490506,1.944353)"
    - d="M 21.781414,10.983024 A 9.975256,9.975256 0 1 1 1.8309021,10.983024 A 9.975256,9.975256 0 1 1 21.781414,10.983024 z"
    - sodipodi:ry="9.975256"
    - sodipodi:rx="9.975256"
    - sodipodi:cy="10.983024"
    - sodipodi:cx="11.806158"
    - id="path17964"
    - style="opacity:1;fill:url(#radialGradient20931);fill-opacity:1;stroke:url(#linearGradient20933);stroke-width:1.81368327;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline;enable-background:new"
    - sodipodi:type="arc" />
    - <path
    - id="path17966"
    - d="M 8.5000005,6.7451615 C 8.5000005,7.9844904 7.604002,8.9903231 6.500001,8.9903231 C 5.3959999,8.9903231 4.5000001,7.9844904 4.5000001,6.7451615 C 4.5000001,5.5058325 5.3959999,4.5000005 6.500001,4.5000005 C 7.604002,4.5000005 8.5000005,5.5058325 8.5000005,6.7451615 z"
    - style="opacity:1;fill:#eeeeec;fill-opacity:1;stroke:url(#linearGradient20942);stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;display:inline;enable-background:new" />
    - <path
    - sodipodi:nodetypes="cscsc"
    - id="path17974"
    - d="M 11,10 C 10.887023,11.226829 9.4422911,12 8.0000003,12 C 6.5577095,12 5.2085418,11.396721 5,10 C 5.6322481,10.739405 6.7426661,11.065616 8.0000003,11.065616 C 9.2573337,11.065616 10.367752,10.739405 11,10 z"
    - style="opacity:1;fill:#b40c0c;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline;enable-background:new" />
    - <a
    - style="opacity:1;display:inline;enable-background:new"
    - id="a22276"
    - transform="matrix(0.8295523,0,0,0.9130993,-3.698956,-3.261344)">
    - <path
    - style="opacity:1;fill:#eeeeec;fill-opacity:1;stroke:url(#linearGradient20915);stroke-width:1.14899766;stroke-miterlimit:4;stroke-opacity:1;display:inline;enable-background:new"
    - d="M 18.5,11 C 18.5,12.38 17.380001,13.5 16,13.5 C 14.62,13.5 13.5,12.38 13.5,11 C 13.5,9.62 14.62,8.5 16,8.5 C 17.380001,8.5 18.5,9.62 18.5,11 z"
    - id="path22835" />
    - </a>
    - <path
    - transform="matrix(0.9747196,0,0,0.5150957,-2.4615398,2.0279474)"
    - d="M 10.732871,9.6526775 A 1.0259361,1.9413869 0 1 1 8.6809988,9.6526775 A 1.0259361,1.9413869 0 1 1 10.732871,9.6526775 z"
    - sodipodi:ry="1.9413869"
    - sodipodi:rx="1.0259361"
    - sodipodi:cy="9.6526775"
    - sodipodi:cx="9.7069349"
    - id="path17968"
    - style="opacity:1;fill:url(#linearGradient11160);fill-opacity:1;stroke:none;stroke-width:0.98640186;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline;enable-background:new"
    - sodipodi:type="arc" />
    - <path
    - transform="matrix(-0.9747196,0,0,0.5150957,18.46154,2.0279474)"
    - d="M 10.732871,9.6526775 A 1.0259361,1.9413869 0 1 1 8.6809988,9.6526775 A 1.0259361,1.9413869 0 1 1 10.732871,9.6526775 z"
    - sodipodi:ry="1.9413869"
    - sodipodi:rx="1.0259361"
    - sodipodi:cy="9.6526775"
    - sodipodi:cx="9.7069349"
    - id="path11156"
    - style="opacity:1;fill:url(#linearGradient11158);fill-opacity:1;stroke:none;stroke-width:0.98640186;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline;enable-background:new"
    - sodipodi:type="arc" />
    - </g>
    -</svg>
    Binary file libpurple/protocols/gg/resources/icons/22x22/apps/im-gadu-gadu.png has changed
    --- a/libpurple/protocols/gg/resources/icons/22x22/apps/scalable/im-gadu-gadu.svg Tue Apr 09 21:50:31 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,206 +0,0 @@
    -<?xml version="1.0" encoding="UTF-8" standalone="no"?>
    -<!-- Created with Inkscape (http://www.inkscape.org/) -->
    -<svg
    - xmlns:dc="http://purl.org/dc/elements/1.1/"
    - xmlns:cc="http://creativecommons.org/ns#"
    - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
    - xmlns:svg="http://www.w3.org/2000/svg"
    - xmlns="http://www.w3.org/2000/svg"
    - xmlns:xlink="http://www.w3.org/1999/xlink"
    - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
    - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
    - width="24"
    - height="24"
    - id="svg2"
    - sodipodi:version="0.32"
    - inkscape:version="0.46"
    - version="1.0"
    - sodipodi:docbase="/home/hbons/GUI/Tango/Gaim Refresh/protocols/22/scalable"
    - sodipodi:docname="gadu-gadu.svg"
    - inkscape:export-filename="/home/hbons/GUI/Tango/Gaim Refresh/protocols/22/gadu-gadu.png"
    - inkscape:export-xdpi="90"
    - inkscape:export-ydpi="90"
    - inkscape:output_extension="org.inkscape.output.svg.inkscape">
    - <defs
    - id="defs4">
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient3150">
    - <stop
    - style="stop-color:#2e3436;stop-opacity:1;"
    - offset="0"
    - id="stop3152" />
    - <stop
    - style="stop-color:#2e3436;stop-opacity:0;"
    - offset="1"
    - id="stop3154" />
    - </linearGradient>
    - <radialGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient3150"
    - id="radialGradient3156"
    - cx="10.748654"
    - cy="10.457643"
    - fx="10.748654"
    - fy="10.457643"
    - r="6.6449099"
    - gradientTransform="matrix(-0.842757,0,0,-0.35721,19.80716,14.19321)"
    - gradientUnits="userSpaceOnUse" />
    - </defs>
    - <sodipodi:namedview
    - id="base"
    - pagecolor="#ffffff"
    - bordercolor="#666666"
    - borderopacity="1.0"
    - inkscape:pageopacity="0.0"
    - inkscape:pageshadow="2"
    - inkscape:zoom="24.007726"
    - inkscape:cx="16.146337"
    - inkscape:cy="13.950539"
    - inkscape:document-units="px"
    - inkscape:current-layer="layer1"
    - showgrid="true"
    - showguides="true"
    - inkscape:guide-bbox="true"
    - fill="#fce94f"
    - inkscape:window-width="1268"
    - inkscape:window-height="971"
    - inkscape:window-x="6"
    - inkscape:window-y="25" />
    - <metadata
    - id="metadata7">
    - <rdf:RDF>
    - <cc:Work
    - rdf:about="">
    - <dc:format>image/svg+xml</dc:format>
    - <dc:type
    - rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
    - </cc:Work>
    - </rdf:RDF>
    - </metadata>
    - <g
    - inkscape:label="Layer 1"
    - inkscape:groupmode="layer"
    - id="layer1">
    - <rect
    - style="opacity:1;fill:#c00;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="rect2235"
    - width="2.0000005"
    - height="3.000001"
    - x="-0.29289341"
    - y="7.3639612"
    - transform="matrix(0.707107,-0.707107,0.707107,0.707107,-1,1)" />
    - <rect
    - style="opacity:1;fill:#c00;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="rect2239"
    - width="2.0000033"
    - height="3.0000048"
    - x="-17.970564"
    - y="8.0710697"
    - transform="matrix(-0.707107,-0.707107,0.707107,-0.707107,-1,1)" />
    - <path
    - sodipodi:type="arc"
    - style="opacity:0.4;fill:url(#radialGradient3156);fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="path3140"
    - sodipodi:cx="10.748654"
    - sodipodi:cy="10.457643"
    - sodipodi:rx="6.6449099"
    - sodipodi:ry="2.3675451"
    - d="M 17.393564 10.457643 A 6.6449099 2.3675451 0 1 1 4.1037445,10.457643 A 6.6449099 2.3675451 0 1 1 17.393564 10.457643 z"
    - transform="matrix(1.429666,0,0,1.267135,-3.866981,6.748756)" />
    - <rect
    - style="opacity:1;fill:#db2424;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="rect2192"
    - width="1.9907103"
    - height="3"
    - x="12"
    - y="1"
    - transform="translate(-1,1)" />
    - <rect
    - style="opacity:1;fill:#db2424;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="rect2203"
    - width="2"
    - height="3"
    - x="12"
    - y="18"
    - transform="translate(-1,1)" />
    - <rect
    - style="opacity:1;fill:#db2424;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="rect2211"
    - width="2"
    - height="3.4306233"
    - x="-12"
    - y="3"
    - transform="matrix(0,-1,1,0,-1,1)" />
    - <rect
    - style="opacity:1;fill:#db2424;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="rect2213"
    - width="2"
    - height="3"
    - x="-12"
    - y="20"
    - transform="matrix(0,-1,1,0,-1,1)" />
    - <rect
    - style="opacity:1;fill:#c00;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="rect2237"
    - width="2.0000019"
    - height="3.0000029"
    - x="-0.29289412"
    - y="23.57716"
    - transform="matrix(0.707107,-0.707107,0.707107,0.707107,-1,1)" />
    - <rect
    - style="opacity:1;fill:#c00;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="rect2241"
    - width="2.0000048"
    - height="3.0000067"
    - x="-17.970566"
    - y="-8.24265"
    - transform="matrix(-0.707107,-0.707107,0.707107,-0.707107,-1,1)" />
    - <path
    - sodipodi:type="arc"
    - style="opacity:1;fill:#fce94f;fill-opacity:1;stroke:#c00;stroke-width:0.73921776;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="path2217"
    - sodipodi:cx="8.0702047"
    - sodipodi:cy="8.0363102"
    - sodipodi:rx="5.5372205"
    - sodipodi:ry="5.5372205"
    - d="M 13.607425 8.0363102 A 5.5372205 5.5372205 0 1 1 2.5329843,8.0363102 A 5.5372205 5.5372205 0 1 1 13.607425 8.0363102 z"
    - transform="matrix(1.354195,0,0,1.35137,1.070845,1.157102)" />
    - <path
    - sodipodi:type="arc"
    - style="opacity:0.7;fill:none;fill-opacity:1;stroke:#c4a000;stroke-width:0.85407299;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.4848485"
    - id="path2222"
    - sodipodi:cx="8.0702047"
    - sodipodi:cy="8.0363102"
    - sodipodi:rx="5.5372205"
    - sodipodi:ry="5.5372205"
    - d="M 13.607425 8.0363102 A 5.5372205 5.5372205 0 1 1 2.5329843,8.0363102 A 5.5372205 5.5372205 0 1 1 13.607425 8.0363102 z"
    - transform="matrix(1.171277,0,0,1.170443,2.525066,2.61184)" />
    - <path
    - sodipodi:type="arc"
    - style="opacity:1;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="path2243"
    - sodipodi:cx="9.8489876"
    - sodipodi:cy="10.583912"
    - sodipodi:rx="1.2626907"
    - sodipodi:ry="1.0417198"
    - d="M 11.111678 10.583912 A 1.2626907 1.0417198 0 1 1 8.5862969,10.583912 A 1.2626907 1.0417198 0 1 1 11.111678 10.583912 z"
    - transform="matrix(0,1.18426,-0.959955,0,20.16009,-1.168408)" />
    - <path
    - sodipodi:type="arc"
    - style="opacity:1;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="path2245"
    - sodipodi:cx="9.8489876"
    - sodipodi:cy="10.583912"
    - sodipodi:rx="1.2626907"
    - sodipodi:ry="1.0417198"
    - d="M 11.111678 10.583912 A 1.2626907 1.0417198 0 1 1 8.5862969,10.583912 A 1.2626907 1.0417198 0 1 1 11.111678 10.583912 z"
    - transform="matrix(0,-1.187938,0.959951,0,3.839963,22.19998)" />
    - <path
    - style="opacity:1;fill:#555753;fill-opacity:1;stroke:none;stroke-width:1.10482609;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - d="M 17.0113,10.955978 C 17.0113,13.182328 15.218558,14.007827 13,14.007827 C 10.819611,14.007827 9.0496187,13.101106 8.9887006,10.913915 L 8,10.942262 C 8.0756746,13.659262 10.29145,15 13,15 C 15.758926,15 17.999999,13.724591 18,10.955978 L 17.0113,10.955978 z "
    - id="path2247"
    - sodipodi:nodetypes="csccscc"
    - transform="translate(-1,1)" />
    - </g>
    -</svg>
    Binary file libpurple/protocols/gg/resources/icons/48x48/apps/im-gadu-gadu.png has changed
    --- a/libpurple/protocols/gg/resources/icons/scalable/apps/im-gadu-gadu.svg Tue Apr 09 21:50:31 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,215 +0,0 @@
    -<?xml version="1.0" encoding="UTF-8" standalone="no"?>
    -<!-- Created with Inkscape (http://www.inkscape.org/) -->
    -<svg
    - xmlns:dc="http://purl.org/dc/elements/1.1/"
    - xmlns:cc="http://creativecommons.org/ns#"
    - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
    - xmlns:svg="http://www.w3.org/2000/svg"
    - xmlns="http://www.w3.org/2000/svg"
    - xmlns:xlink="http://www.w3.org/1999/xlink"
    - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
    - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
    - width="48px"
    - height="48px"
    - id="svg1307"
    - sodipodi:version="0.32"
    - inkscape:version="0.46"
    - sodipodi:docbase="/home/hbons/Desktop/Gaim Refresh/protocols/48/scalable"
    - sodipodi:docname="gadu-gadu.svg"
    - inkscape:export-filename="/home/hbons/Desktop/Gaim Refresh/protocols/48/gadu-gadu.png"
    - inkscape:export-xdpi="90"
    - inkscape:export-ydpi="90"
    - inkscape:output_extension="org.inkscape.output.svg.inkscape">
    - <defs
    - id="defs1309">
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient2447">
    - <stop
    - style="stop-color:#ffffff;stop-opacity:1;"
    - offset="0"
    - id="stop2449" />
    - <stop
    - style="stop-color:#ffffff;stop-opacity:0;"
    - offset="1"
    - id="stop2451" />
    - </linearGradient>
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient3150">
    - <stop
    - style="stop-color:#2e3436;stop-opacity:1;"
    - offset="0"
    - id="stop3152" />
    - <stop
    - style="stop-color:#2e3436;stop-opacity:0;"
    - offset="1"
    - id="stop3154" />
    - </linearGradient>
    - <radialGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient3150"
    - id="radialGradient3156"
    - cx="10.748654"
    - cy="10.457643"
    - fx="10.748654"
    - fy="10.457643"
    - r="6.6449099"
    - gradientTransform="matrix(-0.842757,5.698892e-16,-4.565819e-9,-0.35721,19.80716,14.19321)"
    - gradientUnits="userSpaceOnUse" />
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient2233">
    - <stop
    - style="stop-color:#eeeeec;stop-opacity:1;"
    - offset="0"
    - id="stop2235" />
    - <stop
    - style="stop-color:#eeeeec;stop-opacity:0;"
    - offset="1"
    - id="stop2237" />
    - </linearGradient>
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient2233"
    - id="linearGradient2279"
    - gradientUnits="userSpaceOnUse"
    - x1="24.450865"
    - y1="5.1375499"
    - x2="24.450865"
    - y2="31.487988" />
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient2447"
    - id="linearGradient2453"
    - x1="24.5"
    - y1="0.33943355"
    - x2="24.5"
    - y2="19.724688"
    - gradientUnits="userSpaceOnUse" />
    - </defs>
    - <sodipodi:namedview
    - id="base"
    - pagecolor="#ffffff"
    - bordercolor="#666666"
    - borderopacity="1.0"
    - inkscape:pageopacity="0.0"
    - inkscape:pageshadow="2"
    - inkscape:zoom="15.004828"
    - inkscape:cx="40.53287"
    - inkscape:cy="23.900599"
    - inkscape:current-layer="layer1"
    - showgrid="true"
    - inkscape:grid-bbox="true"
    - inkscape:document-units="px"
    - fill="#ef2929"
    - showguides="true"
    - inkscape:guide-bbox="true"
    - inkscape:window-width="1268"
    - inkscape:window-height="972"
    - inkscape:window-x="6"
    - inkscape:window-y="21" />
    - <metadata
    - id="metadata1312">
    - <rdf:RDF>
    - <cc:Work
    - rdf:about="">
    - <dc:format>image/svg+xml</dc:format>
    - <dc:type
    - rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
    - </cc:Work>
    - </rdf:RDF>
    - </metadata>
    - <g
    - id="layer1"
    - inkscape:label="Layer 1"
    - inkscape:groupmode="layer">
    - <path
    - sodipodi:type="arc"
    - style="opacity:0.5;fill:url(#radialGradient3156);fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="path3140"
    - sodipodi:cx="10.748654"
    - sodipodi:cy="10.457643"
    - sodipodi:rx="6.6449099"
    - sodipodi:ry="2.3675451"
    - d="M 17.393564 10.457643 A 6.6449099 2.3675451 0 1 1 4.1037445,10.457643 A 6.6449099 2.3675451 0 1 1 17.393564 10.457643 z"
    - transform="matrix(3.160314,0,0,2.745459,-9.969125,9.788968)" />
    - <path
    - style="opacity:1;fill:#ef2929;fill-opacity:1;fill-rule:evenodd;stroke:#a40000;stroke-width:0.9999997;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - d="M 22.5,4.5130623 L 22.5,8.6366327 C 19.782905,9.0005399 17.277282,10.079925 15.21875,11.698071 L 12.0625,8.5116761 L 9.5,11.073287 L 12.6875,14.259683 C 11.068793,16.317502 9.9577835,18.791017 9.59375,21.50717 L 5.5,21.50717 L 5.5,25.505784 L 9.59375,25.505784 C 9.9535644,28.234089 11.062591,30.718808 12.6875,32.78451 L 9.5,35.970906 L 12.0625,38.501278 L 15.21875,35.346123 C 17.277282,36.964269 19.782905,38.043654 22.5,38.407561 L 22.5,42.499891 L 26.5,42.499891 L 26.5,38.407561 C 29.229107,38.042044 31.717452,36.944842 33.78125,35.314883 L 36.96875,38.501278 L 39.5,35.970906 L 36.3125,32.78451 C 37.936789,30.719597 39.014761,28.232873 39.375,25.505784 L 43.5,25.505784 L 43.5,21.50717 L 39.375,21.50717 C 39.010966,18.791018 37.931208,16.317502 36.3125,14.259683 L 39.5,11.073287 L 36.96875,8.5116761 L 33.78125,11.698071 L 33.75,11.698071 C 31.691468,10.079924 29.217095,9.0005401 26.5,8.6366327 L 26.5,4.5130623 L 22.5,4.5130623 z M 23.8125,11.354441 C 24.033452,11.344807 24.274977,11.354441 24.5,11.354441 C 31.700731,11.354441 36.65625,16.339482 36.65625,23.537717 C 36.656248,30.735953 31.700731,35.689753 24.5,35.689753 C 17.29927,35.689754 12.502002,30.735951 12.502002,23.537717 C 12.502002,16.564428 16.962974,11.653081 23.8125,11.354441 z "
    - id="path2394"
    - sodipodi:nodetypes="cccccccccccccccccccccccccccccccccccssssc" />
    - <path
    - sodipodi:type="inkscape:offset"
    - inkscape:radius="-1"
    - inkscape:original="M 22.5 4.5 L 22.5 8.625 C 19.782905 8.988907 17.277282 10.069354 15.21875 11.6875 L 12.0625 8.5 L 9.5 11.0625 L 12.6875 14.25 C 11.068793 16.307819 9.9577835 18.783847 9.59375 21.5 L 5.5 21.5 L 5.5 25.5 L 9.59375 25.5 C 9.9535644 28.228306 11.062591 30.715548 12.6875 32.78125 L 9.5 35.96875 L 12.0625 38.5 L 15.21875 35.34375 C 17.277282 36.961896 19.782905 38.042343 22.5 38.40625 L 22.5 42.5 L 26.5 42.5 L 26.5 38.40625 C 29.229107 38.040732 31.717452 36.942459 33.78125 35.3125 L 36.96875 38.5 L 39.5 35.96875 L 36.3125 32.78125 C 37.936789 30.716335 39.014761 28.227089 39.375 25.5 L 43.5 25.5 L 43.5 21.5 L 39.375 21.5 C 39.010966 18.783848 37.931208 16.307819 36.3125 14.25 L 39.5 11.0625 L 36.96875 8.5 L 33.78125 11.6875 L 33.75 11.6875 C 31.691468 10.069353 29.217095 8.9889074 26.5 8.625 L 26.5 4.5 L 22.5 4.5 z M 23.8125 11.34375 C 24.033452 11.334116 24.274977 11.34375 24.5 11.34375 C 31.700731 11.34375 36.65625 16.333015 36.65625 23.53125 C 36.656248 30.729485 31.700731 35.6875 24.5 35.6875 C 17.29927 35.6875 12.5 30.729484 12.5 23.53125 C 12.5 16.55796 16.962974 11.64239 23.8125 11.34375 z "
    - xlink:href="#path2394"
    - style="opacity:0.2;fill:url(#linearGradient2453);fill-opacity:1.0;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.9999997;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="path2445"
    - inkscape:href="#path2394"
    - d="M 23.5,5.5 L 23.5,8.625 C 23.503929,9.1319208 23.127956,9.5616039 22.625,9.625 C 20.085855,9.9650737 17.762357,10.960594 15.84375,12.46875 C 15.44664,12.797268 14.864902,12.77021 14.5,12.40625 L 12.0625,9.9375 L 10.9375,11.0625 L 13.40625,13.53125 C 13.77021,13.896152 13.797268,14.47789 13.46875,14.875 C 11.953567,16.801212 10.932828,19.095048 10.59375,21.625 C 10.530354,22.127956 10.100671,22.503929 9.59375,22.5 L 6.5,22.5 L 6.5,24.5 L 9.59375,24.5 C 10.100671,24.496071 10.530354,24.872044 10.59375,25.375 C 10.929039,27.917339 11.948302,30.223346 13.46875,32.15625 C 13.797268,32.55336 13.77021,33.135098 13.40625,33.5 L 10.9375,35.96875 L 12.03125,37.09375 L 14.5,34.625 C 14.864902,34.26104 15.44664,34.233982 15.84375,34.5625 C 17.762357,36.070656 20.085855,37.066176 22.625,37.40625 C 23.127956,37.469646 23.503929,37.899329 23.5,38.40625 L 23.5,41.5 L 25.5,41.5 L 25.5,38.40625 C 25.496071,37.899329 25.872044,37.469646 26.375,37.40625 C 28.92215,37.065102 31.227806,36.054308 33.15625,34.53125 C 33.55336,34.202732 34.135098,34.22979 34.5,34.59375 L 36.96875,37.0625 L 38.0625,35.96875 L 35.59375,33.5 C 35.22979,33.135098 35.202732,32.55336 35.53125,32.15625 C 37.047015,30.229299 38.038333,27.923642 38.375,25.375 C 38.438396,24.872044 38.868079,24.496071 39.375,24.5 L 42.5,24.5 L 42.5,22.5 L 39.375,22.5 C 38.868079,22.503929 38.438396,22.127956 38.375,21.625 C 38.034921,19.087581 37.042567,16.796296 35.53125,14.875 C 35.202732,14.47789 35.22979,13.896152 35.59375,13.53125 L 38.09375,11.03125 L 36.96875,9.9375 L 34.5,12.40625 C 34.307701,12.592396 34.048815,12.693699 33.78125,12.6875 L 33.75,12.6875 C 33.522871,12.68767 33.302449,12.610522 33.125,12.46875 C 31.202527,10.957555 28.913145,9.9649402 26.375,9.625 C 25.872044,9.5616039 25.496071,9.1319208 25.5,8.625 L 25.5,5.5 L 23.5,5.5 z M 23.78125,10.34375 C 24.062872,10.331471 24.303519,10.34375 24.5,10.34375 C 32.162653,10.34375 37.65625,15.874058 37.65625,23.53125 C 37.656248,31.188442 32.15976,36.6875 24.5,36.6875 C 20.67012,36.6875 17.391923,35.341265 15.09375,33 C 12.795577,30.658735 11.5,27.35261 11.5,23.53125 C 11.5,16.131287 16.473536,10.662367 23.78125,10.34375 z " />
    - <path
    - sodipodi:type="arc"
    - style="opacity:1;fill:#edd400;fill-opacity:1;fill-rule:evenodd;stroke:#a40000;stroke-width:1.08146787;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="path2198"
    - sodipodi:cx="24.450865"
    - sodipodi:cy="23.959385"
    - sodipodi:rx="14.03053"
    - sodipodi:ry="14.03053"
    - d="M 38.481395 23.959385 A 14.03053 14.03053 0 1 1 10.420335,23.959385 A 14.03053 14.03053 0 1 1 38.481395 23.959385 z"
    - transform="matrix(0.924879,0,0,0.924459,1.889957,1.34006)" />
    - <path
    - sodipodi:type="arc"
    - style="opacity:1;fill:url(#linearGradient2279);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.12198293;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="path2277"
    - sodipodi:cx="24.450865"
    - sodipodi:cy="23.959385"
    - sodipodi:rx="14.03053"
    - sodipodi:ry="14.03053"
    - d="M 38.481395 23.959385 A 14.03053 14.03053 0 1 1 10.420335,23.959385 A 14.03053 14.03053 0 1 1 38.481395 23.959385 z"
    - transform="matrix(0.890914,0,0,0.891279,2.716374,2.14037)" />
    - <path
    - sodipodi:type="arc"
    - style="opacity:1;fill:#2e3436;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="path2200"
    - sodipodi:cx="10.54341"
    - sodipodi:cy="-2.7068274"
    - sodipodi:rx="2.4204714"
    - sodipodi:ry="1.7230475"
    - d="M 12.963882 -2.7068274 A 2.4204714 1.7230475 0 1 1 8.1229389,-2.7068274 A 2.4204714 1.7230475 0 1 1 12.963882 -2.7068274 z"
    - transform="matrix(0.827565,0,0,0.87352,11.27774,22.85935)" />
    - <path
    - sodipodi:type="arc"
    - style="opacity:1;fill:#2e3436;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="path2202"
    - sodipodi:cx="10.54341"
    - sodipodi:cy="-2.7068274"
    - sodipodi:rx="2.4204714"
    - sodipodi:ry="1.7230475"
    - d="M 12.963882 -2.7068274 A 2.4204714 1.7230475 0 1 1 8.1229389,-2.7068274 A 2.4204714 1.7230475 0 1 1 12.963882 -2.7068274 z"
    - transform="matrix(0.827567,0,0,0.87352,20.27772,22.86958)" />
    - <path
    - sodipodi:type="arc"
    - style="opacity:1;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#2e3436;stroke-width:4.5851903;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="path2204"
    - sodipodi:cx="24.450865"
    - sodipodi:cy="23.959385"
    - sodipodi:rx="14.03053"
    - sodipodi:ry="14.03053"
    - d="M 36.657066,30.877796 A 14.03053,14.03053 0 0 1 11.928959,30.288496"
    - transform="matrix(0.453563,0,0,0.419476,13.51464,14.26249)"
    - sodipodi:start="0.51564596"
    - sodipodi:end="2.6736"
    - sodipodi:open="true" />
    - <path
    - sodipodi:type="arc"
    - style="opacity:0.5;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.75245398;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="path2465"
    - sodipodi:cx="26.658085"
    - sodipodi:cy="20.675463"
    - sodipodi:rx="9.863492"
    - sodipodi:ry="8.2640066"
    - d="M 36.521577 20.675463 A 9.863492 8.2640066 0 1 1 16.794593,20.675463 A 9.863492 8.2640066 0 1 1 36.521577 20.675463 z"
    - transform="matrix(1.216989,0,0,1.451287,-7.948546,-6.503855)" />
    - </g>
    -</svg>
    --- a/libpurple/protocols/gg/roster.c Tue Apr 09 21:50:31 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,1092 +0,0 @@
    -/* 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 "roster.h"
    -
    -#include <glib/gi18n-lib.h>
    -
    -#include "gg.h"
    -#include "xml.h"
    -#include "utils.h"
    -#include "purplew.h"
    -
    -#define GGP_ROSTER_SYNC_SETT "gg-synchronized"
    -#define GGP_ROSTER_DEBUG 0
    -#define GGP_ROSTER_GROUPID_DEFAULT "00000000-0000-0000-0000-000000000000"
    -#define GGP_ROSTER_GROUPID_BOTS "0b345af6-0001-0000-0000-000000000004"
    -
    -/* TODO: ignored contacts synchronization (?) */
    -
    -typedef struct
    -{
    - int version;
    -
    - PurpleXmlNode *xml;
    -
    - PurpleXmlNode *groups_node, *contacts_node;
    -
    - /**
    - * Key: (uin_t) user identifier
    - * Value: (PurpleXmlNode*) xml node for contact
    - */
    - GHashTable *contact_nodes;
    -
    - /**
    - * Key: (gchar*) group id
    - * Value: (PurpleXmlNode*) xml node for group
    - */
    - GHashTable *group_nodes;
    -
    - /**
    - * Key: (gchar*) group name
    - * Value: (gchar*) group id
    - */
    - GHashTable *group_ids;
    -
    - /**
    - * Key: (gchar*) group id
    - * Value: (gchar*) group name
    - */
    - GHashTable *group_names;
    -
    - gchar *bots_group_id;
    -
    - gboolean needs_update;
    -} ggp_roster_content;
    -
    -typedef struct
    -{
    - enum
    - {
    - GGP_ROSTER_CHANGE_CONTACT_UPDATE,
    - GGP_ROSTER_CHANGE_CONTACT_REMOVE,
    - GGP_ROSTER_CHANGE_GROUP_RENAME,
    - } type;
    - union
    - {
    - uin_t uin;
    - struct
    - {
    - gchar *old_name;
    - gchar *new_name;
    - } group_rename;
    - } data;
    -} ggp_roster_change;
    -
    -static inline ggp_roster_session_data *
    -ggp_roster_get_rdata(PurpleConnection *gc);
    -static void ggp_roster_content_free(ggp_roster_content *content);
    -static void ggp_roster_change_free(gpointer change);
    -static int ggp_roster_get_version(PurpleConnection *gc);
    -static gboolean ggp_roster_timer_cb(gpointer _gc);
    -#if GGP_ROSTER_DEBUG
    -static void ggp_roster_dump(ggp_roster_content *content);
    -#endif
    -
    -/* synchronization control */
    -static gboolean ggp_roster_is_synchronized(PurpleBuddy *buddy);
    -static void ggp_roster_set_synchronized(PurpleConnection *gc,
    - PurpleBuddy *buddy, gboolean synchronized);
    -
    -/* buddy list import */
    -static gboolean ggp_roster_reply_list_read_group(PurpleXmlNode *node,
    - ggp_roster_content *content);
    -static gboolean ggp_roster_reply_list_read_buddy(PurpleConnection *gc,
    - PurpleXmlNode *node, ggp_roster_content *content, GHashTable *remove_buddies);
    -static void ggp_roster_reply_list(PurpleConnection *gc, uint32_t version,
    - const char *reply);
    -
    -/* buddy list export */
    -static const gchar * ggp_roster_send_update_group_add(
    - ggp_roster_content *content, PurpleGroup *group);
    -static gboolean ggp_roster_send_update_contact_update(PurpleConnection *gc,
    - ggp_roster_change *change);
    -static gboolean ggp_roster_send_update_contact_remove(PurpleConnection *gc,
    - ggp_roster_change *change);
    -static gboolean ggp_roster_send_update_group_rename(PurpleConnection *gc,
    - ggp_roster_change *change);
    -static void ggp_roster_send_update(PurpleConnection *gc);
    -static void ggp_roster_reply_ack(PurpleConnection *gc, uint32_t version);
    -static void ggp_roster_reply_reject(PurpleConnection *gc, uint32_t version);
    -
    -/******************************************************************************/
    -
    -static inline ggp_roster_session_data *
    -ggp_roster_get_rdata(PurpleConnection *gc)
    -{
    - GGPInfo *accdata = purple_connection_get_protocol_data(gc);
    - return &accdata->roster_data;
    -}
    -
    -static void ggp_roster_content_free(ggp_roster_content *content)
    -{
    - if (content == NULL)
    - return;
    - g_clear_pointer(&content->xml, purple_xmlnode_free);
    - g_clear_pointer(&content->contact_nodes, g_hash_table_destroy);
    - g_clear_pointer(&content->group_nodes, g_hash_table_destroy);
    - g_clear_pointer(&content->group_ids, g_hash_table_destroy);
    - g_clear_pointer(&content->group_names, g_hash_table_destroy);
    - g_free(content->bots_group_id);
    - g_free(content);
    -}
    -
    -static void ggp_roster_change_free(gpointer _change)
    -{
    - ggp_roster_change *change = _change;
    -
    - if (change->type == GGP_ROSTER_CHANGE_GROUP_RENAME) {
    - g_free(change->data.group_rename.old_name);
    - g_free(change->data.group_rename.new_name);
    - }
    -
    - g_free(change);
    -}
    -
    -static int ggp_roster_get_version(PurpleConnection *gc)
    -{
    - ggp_roster_content *content = ggp_roster_get_rdata(gc)->content;
    - if (content == NULL)
    - return 0;
    - return content->version;
    -}
    -
    -static gboolean ggp_roster_timer_cb(gpointer _gc)
    -{
    - PurpleConnection *gc = _gc;
    -
    - PURPLE_ASSERT_CONNECTION_IS_VALID(gc);
    -
    - ggp_roster_send_update(gc);
    -
    - return TRUE;
    -}
    -
    -#if GGP_ROSTER_DEBUG
    -static void ggp_roster_dump(ggp_roster_content *content)
    -{
    - char *str;
    - int len;
    -
    - g_return_if_fail(content != NULL);
    - g_return_if_fail(content->xml != NULL);
    -
    - str = purple_xmlnode_to_formatted_str(content->xml, &len);
    - purple_debug_misc("gg", "ggp_roster_dump: [%s]\n", str);
    - g_free(str);
    -}
    -#endif
    -
    -/*******************************************************************************
    - * Setup.
    - ******************************************************************************/
    -
    -gboolean ggp_roster_enabled(void)
    -{
    - static gboolean checked = FALSE;
    - static gboolean enabled;
    -
    - if (!checked) {
    - enabled = gg_libgadu_check_feature(
    - GG_LIBGADU_FEATURE_USERLIST100);
    - checked = TRUE;
    - }
    - return enabled;
    -}
    -
    -void ggp_roster_setup(PurpleConnection *gc)
    -{
    - ggp_roster_session_data *rdata = ggp_roster_get_rdata(gc);
    -
    - rdata->content = NULL;
    - rdata->sent_updates = NULL;
    - rdata->pending_updates = NULL;
    - rdata->timer = 0;
    - rdata->is_updating = FALSE;
    -
    - if (ggp_roster_enabled())
    - rdata->timer = g_timeout_add_seconds(2,
    - ggp_roster_timer_cb, gc);
    -}
    -
    -void ggp_roster_cleanup(PurpleConnection *gc)
    -{
    - ggp_roster_session_data *rdata = ggp_roster_get_rdata(gc);
    -
    - g_clear_handle_id(&rdata->timer, g_source_remove);
    - ggp_roster_content_free(rdata->content);
    - g_list_free_full(rdata->sent_updates, ggp_roster_change_free);
    - g_list_free_full(rdata->pending_updates, ggp_roster_change_free);
    -}
    -
    -/*******************************************************************************
    - * Synchronization control.
    - ******************************************************************************/
    -
    -static gboolean ggp_roster_is_synchronized(PurpleBuddy *buddy)
    -{
    - gboolean ret = purple_blist_node_get_bool(PURPLE_BLIST_NODE(buddy),
    - GGP_ROSTER_SYNC_SETT);
    - return ret;
    -}
    -
    -static void ggp_roster_set_synchronized(PurpleConnection *gc,
    - PurpleBuddy *buddy, gboolean synchronized)
    -{
    - ggp_roster_session_data *rdata = ggp_roster_get_rdata(gc);
    - uin_t uin = ggp_str_to_uin(purple_buddy_get_name(buddy));
    - ggp_roster_change *change;
    -
    - purple_blist_node_set_bool(PURPLE_BLIST_NODE(buddy),
    - GGP_ROSTER_SYNC_SETT, synchronized);
    - if (!synchronized) {
    - change = g_new0(ggp_roster_change, 1);
    - change->type = GGP_ROSTER_CHANGE_CONTACT_UPDATE;
    - change->data.uin = uin;
    - rdata->pending_updates =
    - g_list_append(rdata->pending_updates, change);
    - }
    -}
    -
    -void ggp_roster_request_update(PurpleConnection *gc)
    -{
    - GGPInfo *accdata = purple_connection_get_protocol_data(gc);
    - int local_version = ggp_roster_get_version(gc);
    -
    - if (!ggp_roster_enabled()) {
    - purple_debug_warning("gg", "ggp_roster_request_update: "
    - "feature disabled\n");
    - return;
    - }
    -
    - purple_debug_info("gg", "ggp_roster_request_update: local=%u\n",
    - local_version);
    -
    - gg_userlist100_request(accdata->session, GG_USERLIST100_GET,
    - local_version, GG_USERLIST100_FORMAT_TYPE_GG100, NULL);
    -}
    -
    -/*******************************************************************************
    - * Libgadu callbacks.
    - ******************************************************************************/
    -
    -void ggp_roster_reply(PurpleConnection *gc,
    - struct gg_event_userlist100_reply *reply)
    -{
    - if (GG_USERLIST100_FORMAT_TYPE_GG100 != reply->format_type) {
    - purple_debug_warning("gg", "ggp_roster_reply: "
    - "unsupported format type (%x)\n", reply->format_type);
    - return;
    - }
    -
    - if (reply->type == GG_USERLIST100_REPLY_LIST)
    - ggp_roster_reply_list(gc, reply->version, reply->reply);
    - else if (reply->type == 0x01) /* list up to date (TODO: push to libgadu) */
    - purple_debug_info("gg", "ggp_roster_reply: list up to date\n");
    - else if (reply->type == GG_USERLIST100_REPLY_ACK)
    - ggp_roster_reply_ack(gc, reply->version);
    - else if (reply->type == GG_USERLIST100_REPLY_REJECT)
    - ggp_roster_reply_reject(gc, reply->version);
    - else
    - purple_debug_error("gg", "ggp_roster_reply: "
    - "unsupported reply (%x)\n", reply->type);
    -}
    -
    -void ggp_roster_version(PurpleConnection *gc,
    - struct gg_event_userlist100_version *version)
    -{
    - int local_version = ggp_roster_get_version(gc);
    - int remote_version = version->version;
    -
    - purple_debug_info("gg", "ggp_roster_version: local=%u, remote=%u\n",
    - local_version, remote_version);
    -
    - if (local_version < remote_version)
    - ggp_roster_request_update(gc);
    -}
    -
    -/*******************************************************************************
    - * Libpurple callbacks.
    - ******************************************************************************/
    -
    -void
    -ggp_roster_alias_buddy(G_GNUC_UNUSED PurpleProtocolServer *protocol_server,
    - PurpleConnection *gc, const gchar *who,
    - const gchar *alias)
    -{
    - PurpleBuddy *buddy;
    -
    - g_return_if_fail(who != NULL);
    -
    - if (!ggp_roster_enabled())
    - return;
    -
    - purple_debug_misc("gg", "ggp_roster_alias_buddy(\"%s\", \"%s\")\n",
    - who, alias);
    -
    - buddy = purple_blist_find_buddy(purple_connection_get_account(gc), who);
    - g_return_if_fail(buddy != NULL);
    -
    - ggp_roster_set_synchronized(gc, buddy, FALSE);
    -}
    -
    -void
    -ggp_roster_group_buddy(G_GNUC_UNUSED PurpleProtocolServer *protocol_server,
    - PurpleConnection *gc, const gchar *who,
    - const gchar *old_group, const gchar *new_group)
    -{
    - ggp_roster_session_data *rdata = ggp_roster_get_rdata(gc);
    - ggp_roster_change *change;
    -
    - if (!ggp_roster_enabled())
    - return;
    - if (rdata->is_updating)
    - return;
    -
    - purple_debug_misc("gg", "ggp_roster_group_buddy: "
    - "who=\"%s\", group=\"%s\" -> \"%s\")\n",
    - who, old_group, new_group);
    -
    - /* purple_blist_find_buddy(..., who) is not accessible at this moment */
    - change = g_new0(ggp_roster_change, 1);
    - change->type = GGP_ROSTER_CHANGE_CONTACT_UPDATE;
    - change->data.uin = ggp_str_to_uin(who);
    - rdata->pending_updates = g_list_append(rdata->pending_updates, change);
    -}
    -
    -void
    -ggp_roster_rename_group(G_GNUC_UNUSED PurpleProtocolServer *protocol_server,
    - PurpleConnection *gc, const gchar *old_name,
    - PurpleGroup *group, G_GNUC_UNUSED GList *moved_buddies)
    -{
    - ggp_roster_session_data *rdata = ggp_roster_get_rdata(gc);
    - ggp_roster_change *change;
    -
    - if (!ggp_roster_enabled())
    - return;
    -
    - change = g_new0(ggp_roster_change, 1);
    - change->type = GGP_ROSTER_CHANGE_GROUP_RENAME;
    - change->data.group_rename.old_name = g_strdup(old_name);
    - change->data.group_rename.new_name =
    - g_strdup(purple_group_get_name(group));
    - rdata->pending_updates = g_list_append(rdata->pending_updates, change);
    -}
    -
    -void
    -ggp_roster_add_buddy(G_GNUC_UNUSED PurpleProtocolServer *protocol_server,
    - PurpleConnection *gc, PurpleBuddy *buddy,
    - G_GNUC_UNUSED PurpleGroup *group,
    - G_GNUC_UNUSED const gchar *message)
    -{
    - g_return_if_fail(gc != NULL);
    - g_return_if_fail(buddy != NULL);
    -
    - if (!ggp_roster_enabled())
    - return;
    -
    - ggp_roster_set_synchronized(gc, buddy, FALSE);
    -}
    -
    -void
    -ggp_roster_remove_buddy(G_GNUC_UNUSED PurpleProtocolServer *protocol_server,
    - PurpleConnection *gc, PurpleBuddy *buddy,
    - G_GNUC_UNUSED PurpleGroup *group)
    -{
    - ggp_roster_session_data *rdata = ggp_roster_get_rdata(gc);
    - ggp_roster_change *change;
    -
    - if (!ggp_roster_enabled())
    - return;
    -
    - change = g_new0(ggp_roster_change, 1);
    - change->type = GGP_ROSTER_CHANGE_CONTACT_REMOVE;
    - change->data.uin = ggp_str_to_uin(purple_buddy_get_name(buddy));
    - rdata->pending_updates = g_list_append(rdata->pending_updates, change);
    -}
    -
    -/*******************************************************************************
    - * Buddy list import.
    - ******************************************************************************/
    -
    -static gboolean ggp_roster_reply_list_read_group(PurpleXmlNode *node,
    - ggp_roster_content *content)
    -{
    - char *name, *id;
    - gboolean removable;
    - gboolean succ = TRUE, is_bot, is_default;
    -
    - succ &= ggp_xml_get_string(node, "Id", &id);
    - succ &= ggp_xml_get_string(node, "Name", &name);
    - succ &= ggp_xml_get_bool(node, "IsRemovable", &removable);
    -
    - if (!succ) {
    - g_free(id);
    - g_free(name);
    - g_return_val_if_reached(FALSE);
    - }
    -
    - is_bot = (strcmp(id, GGP_ROSTER_GROUPID_BOTS) == 0 ||
    - g_strcmp0(name, "Pomocnicy") == 0);
    - is_default = (strcmp(id, GGP_ROSTER_GROUPID_DEFAULT) == 0 ||
    - g_strcmp0(name, PURPLE_BLIST_DEFAULT_GROUP_NAME) == 0 ||
    - g_strcmp0(name, "[default]") == 0);
    -
    - if (!content->bots_group_id && is_bot)
    - content->bots_group_id = g_strdup(id);
    -
    - if (!removable || is_bot || is_default) {
    - g_free(id);
    - g_free(name);
    - return TRUE;
    - }
    -
    - g_hash_table_insert(content->group_nodes, g_strdup(id), node);
    - g_hash_table_insert(content->group_ids, g_strdup(name), g_strdup(id));
    - g_hash_table_insert(content->group_names, id, name);
    -
    - return TRUE;
    -}
    -
    -static gboolean ggp_roster_reply_list_read_buddy(PurpleConnection *gc,
    - PurpleXmlNode *node, ggp_roster_content *content, GHashTable *remove_buddies)
    -{
    - gchar *alias, *group_name = NULL;
    - uin_t uin;
    - gboolean succ = TRUE;
    - PurpleXmlNode *group_list, *group_elem;
    - PurpleBuddy *buddy = NULL;
    - PurpleGroup *group = NULL;
    - PurpleGroup *currentGroup;
    - gboolean alias_changed;
    - PurpleAccount *account = purple_connection_get_account(gc);
    -
    - succ &= ggp_xml_get_string(node, "ShowName", &alias);
    - succ &= ggp_xml_get_uint(node, "GGNumber", &uin);
    -
    - group_list = purple_xmlnode_get_child(node, "Groups");
    - succ &= (group_list != NULL);
    -
    - if (!succ) {
    - g_free(alias);
    - g_return_val_if_reached(FALSE);
    - }
    -
    - g_hash_table_insert(content->contact_nodes, GINT_TO_POINTER(uin), node);
    -
    - /* check, if alias is set */
    - if (*alias == '\0' ||
    - strcmp(alias, ggp_uin_to_str(uin)) == 0)
    - {
    - g_free(alias);
    - alias = NULL;
    - }
    -
    - /* getting (eventually creating) group */
    - group_elem = purple_xmlnode_get_child(group_list, "GroupId");
    - while (group_elem != NULL) {
    - gchar *id;
    - gboolean isbot;
    -
    - if (!ggp_xml_get_string(group_elem, NULL, &id))
    - continue;
    - isbot = (0 == g_strcmp0(id, content->bots_group_id));
    - group_name = g_hash_table_lookup(content->group_names, id);
    - g_free(id);
    -
    - /* we don't want to import bots;
    - * they are inserted to roster by default
    - */
    - if (isbot) {
    - g_free(alias);
    - return TRUE;
    - }
    -
    - if (group_name != NULL)
    - break;
    -
    - group_elem = purple_xmlnode_get_next_twin(group_elem);
    - }
    - if (group_name) {
    - group = purple_blist_find_group(group_name);
    - if (!group) {
    - group = purple_group_new(group_name);
    - purple_blist_add_group(group, NULL);
    - }
    - }
    -
    - /* add buddy, if doesn't exists */
    - buddy = purple_blist_find_buddy(account, ggp_uin_to_str(uin));
    - g_hash_table_remove(remove_buddies, GINT_TO_POINTER(uin));
    - if (!buddy) {
    - purple_debug_info("gg", "ggp_roster_reply_list_read_buddy: "
    - "adding %u (%s) to buddy list\n", uin, alias);
    - buddy = purple_buddy_new(account, ggp_uin_to_str(uin), alias);
    - purple_blist_add_buddy(buddy, NULL, group, NULL);
    - ggp_roster_set_synchronized(gc, buddy, TRUE);
    -
    - g_free(alias);
    - return TRUE;
    - }
    -
    - /* buddy exists, but is not synchronized - local list has priority */
    - if (!ggp_roster_is_synchronized(buddy)) {
    - purple_debug_misc("gg", "ggp_roster_reply_list_read_buddy: "
    - "ignoring not synchronized %u (%s)\n",
    - uin, purple_buddy_get_name(buddy));
    - g_free(alias);
    - return TRUE;
    - }
    -
    - currentGroup = ggp_purplew_buddy_get_group_only(buddy);
    - alias_changed =
    - (0 != g_strcmp0(alias, purple_buddy_get_alias_only(buddy)));
    -
    - if (currentGroup == group && !alias_changed) {
    - g_free(alias);
    - return TRUE;
    - }
    -
    - purple_debug_misc("gg", "ggp_roster_reply_list_read_buddy: "
    - "updating %u (%s) - alias=\"%s\"->\"%s\", group=%p->%p (%s)\n",
    - uin, purple_buddy_get_name(buddy),
    - purple_buddy_get_alias(buddy), alias,
    - currentGroup, group, group_name);
    - if (alias_changed)
    - purple_buddy_set_local_alias(buddy, alias);
    - if (currentGroup != group)
    - purple_blist_add_buddy(buddy, NULL, group, NULL);
    -
    - g_free(alias);
    - return TRUE;
    -}
    -
    -static void ggp_roster_reply_list(PurpleConnection *gc, uint32_t version,
    - const char *data)
    -{
    - ggp_roster_session_data *rdata = ggp_roster_get_rdata(gc);
    - PurpleXmlNode *xml, *xml_it;
    - PurpleAccount *account;
    - GSList *local_buddies;
    - GHashTable *remove_buddies;
    - GList *update_buddies = NULL, *local_groups, *it, *table_values;
    - ggp_roster_content *content;
    -
    - g_return_if_fail(gc != NULL);
    - g_return_if_fail(data != NULL);
    -
    - account = purple_connection_get_account(gc);
    -
    - purple_debug_info("gg", "ggp_roster_reply_list: got list, version=%u\n",
    - version);
    -
    - xml = purple_xmlnode_from_str(data, -1);
    - if (xml == NULL) {
    - purple_debug_warning("gg", "ggp_roster_reply_list: "
    - "invalid xml\n");
    - return;
    - }
    -
    - ggp_roster_content_free(rdata->content);
    - rdata->content = NULL;
    - rdata->is_updating = TRUE;
    - content = g_new0(ggp_roster_content, 1);
    - content->version = version;
    - content->xml = xml;
    - content->contact_nodes = g_hash_table_new(NULL, NULL);
    - content->group_nodes = g_hash_table_new_full(
    - g_str_hash, g_str_equal, g_free, NULL);
    - content->group_ids = g_hash_table_new_full(
    - g_str_hash, g_str_equal, g_free, g_free);
    - content->group_names = g_hash_table_new_full(
    - g_str_hash, g_str_equal, g_free, g_free);
    -
    -#if GGP_ROSTER_DEBUG
    - ggp_roster_dump(content);
    -#endif
    -
    - /* reading groups */
    - content->groups_node = purple_xmlnode_get_child(xml, "Groups");
    - if (content->groups_node == NULL) {
    - ggp_roster_content_free(content);
    - g_return_if_reached();
    - }
    - xml_it = purple_xmlnode_get_child(content->groups_node, "Group");
    - while (xml_it != NULL) {
    - if (!ggp_roster_reply_list_read_group(xml_it, content)) {
    - ggp_roster_content_free(content);
    - g_return_if_reached();
    - }
    -
    - xml_it = purple_xmlnode_get_next_twin(xml_it);
    - }
    -
    - /* dumping current group list */
    - local_groups = ggp_purplew_account_get_groups(account, TRUE);
    -
    - /* dumping current buddy list
    - * we will:
    - * - remove synchronized ones, if not found in list at server
    - * - upload not synchronized ones
    - */
    - local_buddies = purple_blist_find_buddies(account, NULL);
    - remove_buddies = g_hash_table_new(g_direct_hash, g_direct_equal);
    - while (local_buddies) {
    - PurpleBuddy *buddy = local_buddies->data;
    - uin_t uin = ggp_str_to_uin(purple_buddy_get_name(buddy));
    - local_buddies =
    - g_slist_delete_link(local_buddies, local_buddies);
    -
    - if (!uin)
    - continue;
    -
    - if (ggp_roster_is_synchronized(buddy))
    - g_hash_table_insert(remove_buddies,
    - GINT_TO_POINTER(uin), buddy);
    - else
    - update_buddies = g_list_append(update_buddies, buddy);
    - }
    -
    - /* reading buddies */
    - content->contacts_node = purple_xmlnode_get_child(xml, "Contacts");
    - if (content->contacts_node == NULL) {
    - g_hash_table_destroy(remove_buddies);
    - g_list_free(update_buddies);
    - ggp_roster_content_free(content);
    - g_return_if_reached();
    - }
    - xml_it = purple_xmlnode_get_child(content->contacts_node, "Contact");
    - while (xml_it != NULL) {
    - if (!ggp_roster_reply_list_read_buddy(gc, xml_it, content,
    - remove_buddies))
    - {
    - g_hash_table_destroy(remove_buddies);
    - g_list_free(update_buddies);
    - ggp_roster_content_free(content);
    - g_return_if_reached();
    - }
    -
    - xml_it = purple_xmlnode_get_next_twin(xml_it);
    - }
    -
    - /* removing buddies, which are not present in roster */
    - table_values = g_hash_table_get_values(remove_buddies);
    - it = g_list_first(table_values);
    - while (it) {
    - PurpleBuddy *buddy = it->data;
    - it = g_list_next(it);
    - if (!ggp_roster_is_synchronized(buddy))
    - continue;
    - purple_debug_info("gg", "ggp_roster_reply_list: "
    - "removing %s from buddy list\n",
    - purple_buddy_get_name(buddy));
    - purple_blist_remove_buddy(buddy);
    - }
    - g_list_free(table_values);
    - g_hash_table_destroy(remove_buddies);
    -
    - /* remove groups, which are empty, but had contacts before
    - * synchronization
    - */
    - it = g_list_first(local_groups);
    - while (it) {
    - PurpleGroup *group = it->data;
    - it = g_list_next(it);
    - if (purple_counting_node_get_total_size(PURPLE_COUNTING_NODE(group)) != 0)
    - continue;
    - purple_debug_info("gg", "ggp_roster_reply_list: "
    - "removing group %s\n", purple_group_get_name(group));
    - purple_blist_remove_group(group);
    - }
    - g_list_free(local_groups);
    -
    - /* adding not synchronized buddies */
    - it = g_list_first(update_buddies);
    - while (it) {
    - PurpleBuddy *buddy = it->data;
    - uin_t uin = ggp_str_to_uin(purple_buddy_get_name(buddy));
    - ggp_roster_change *change;
    -
    - it = g_list_next(it);
    - g_assert(uin > 0);
    -
    - purple_debug_misc("gg", "ggp_roster_reply_list: "
    - "adding change of %u for roster\n", uin);
    - change = g_new0(ggp_roster_change, 1);
    - change->type = GGP_ROSTER_CHANGE_CONTACT_UPDATE;
    - change->data.uin = uin;
    - rdata->pending_updates =
    - g_list_append(rdata->pending_updates, change);
    - }
    - g_list_free(update_buddies);
    -
    - rdata->content = content;
    - rdata->is_updating = FALSE;
    - purple_debug_info("gg", "ggp_roster_reply_list: "
    - "import done, version=%u\n", version);
    -}
    -
    -/*******************************************************************************
    - * Buddy list export.
    - ******************************************************************************/
    -
    -static const gchar * ggp_roster_send_update_group_add(
    - ggp_roster_content *content, PurpleGroup *group)
    -{
    - gchar *id;
    - const char *id_existing, *group_name;
    - PurpleXmlNode *group_node;
    - gboolean succ = TRUE;
    -
    - if (group) {
    - group_name = purple_group_get_name(group);
    - id_existing =
    - g_hash_table_lookup(content->group_ids, group_name);
    - } else
    - id_existing = GGP_ROSTER_GROUPID_DEFAULT;
    - if (id_existing)
    - return id_existing;
    -
    - purple_debug_info("gg", "ggp_roster_send_update_group_add: adding %s\n",
    - purple_group_get_name(group));
    -
    - id = g_uuid_string_random();
    -
    - group_node = purple_xmlnode_new_child(content->groups_node, "Group");
    - succ &= ggp_xml_set_string(group_node, "Id", id);
    - succ &= ggp_xml_set_string(group_node, "Name", group_name);
    - succ &= ggp_xml_set_string(group_node, "IsExpanded", "true");
    - succ &= ggp_xml_set_string(group_node, "IsRemovable", "true");
    - content->needs_update = TRUE;
    -
    - g_hash_table_insert(content->group_ids, g_strdup(group_name),
    - g_strdup(id));
    - g_hash_table_replace(content->group_nodes, id, group_node);
    -
    - g_return_val_if_fail(succ, NULL);
    -
    - return id;
    -}
    -
    -static gboolean ggp_roster_send_update_contact_update(PurpleConnection *gc,
    - ggp_roster_change *change)
    -{
    - PurpleAccount *account = purple_connection_get_account(gc);
    - ggp_roster_content *content = ggp_roster_get_rdata(gc)->content;
    - uin_t uin = change->data.uin;
    - PurpleBuddy *buddy;
    - PurpleXmlNode *buddy_node, *contact_groups;
    - gboolean succ = TRUE;
    - const gchar *group_id;
    - gchar *guid;
    -
    - g_return_val_if_fail(change->type == GGP_ROSTER_CHANGE_CONTACT_UPDATE,
    - FALSE);
    -
    - buddy = purple_blist_find_buddy(account, ggp_uin_to_str(uin));
    - if (!buddy)
    - return TRUE;
    - buddy_node = g_hash_table_lookup(content->contact_nodes,
    - GINT_TO_POINTER(uin));
    -
    - group_id = ggp_roster_send_update_group_add(content,
    - ggp_purplew_buddy_get_group_only(buddy));
    -
    - if (buddy_node) { /* update existing */
    - purple_debug_misc("gg", "ggp_roster_send_update_contact_update:"
    - " updating %u...\n", uin);
    -
    - succ &= ggp_xml_set_string(buddy_node, "ShowName",
    - purple_buddy_get_alias(buddy));
    -
    - contact_groups = purple_xmlnode_get_child(buddy_node, "Groups");
    - g_assert(contact_groups);
    - ggp_xmlnode_remove_children(contact_groups);
    - succ &= ggp_xml_set_string(contact_groups, "GroupId", group_id);
    -
    - g_return_val_if_fail(succ, FALSE);
    -
    - return TRUE;
    - }
    -
    - /* add new */
    - guid = g_uuid_string_random();
    - purple_debug_misc("gg", "ggp_roster_send_update_contact_update: "
    - "adding %u...\n", uin);
    - buddy_node = purple_xmlnode_new_child(content->contacts_node, "Contact");
    - succ &= ggp_xml_set_string(buddy_node, "Guid", guid);
    - succ &= ggp_xml_set_uint(buddy_node, "GGNumber", uin);
    - succ &= ggp_xml_set_string(buddy_node, "ShowName",
    - purple_buddy_get_alias(buddy));
    -
    - contact_groups = purple_xmlnode_new_child(buddy_node, "Groups");
    - g_assert(contact_groups);
    - succ &= ggp_xml_set_string(contact_groups, "GroupId", group_id);
    -
    - purple_xmlnode_new_child(buddy_node, "Avatars");
    - succ &= ggp_xml_set_bool(buddy_node, "FlagBuddy", TRUE);
    - succ &= ggp_xml_set_bool(buddy_node, "FlagNormal", TRUE);
    - succ &= ggp_xml_set_bool(buddy_node, "FlagFriend", TRUE);
    -
    - /* we don't use Guid, so update is not needed
    - * content->needs_update = TRUE;
    - */
    - g_free(guid);
    -
    - g_hash_table_insert(content->contact_nodes, GINT_TO_POINTER(uin),
    - buddy_node);
    -
    - g_return_val_if_fail(succ, FALSE);
    -
    - return TRUE;
    -}
    -
    -static gboolean ggp_roster_send_update_contact_remove(PurpleConnection *gc,
    - ggp_roster_change *change)
    -{
    - PurpleAccount *account = purple_connection_get_account(gc);
    - ggp_roster_content *content = ggp_roster_get_rdata(gc)->content;
    - uin_t uin = change->data.uin;
    - PurpleBuddy *buddy;
    - PurpleXmlNode *buddy_node;
    -
    - g_return_val_if_fail(change->type == GGP_ROSTER_CHANGE_CONTACT_REMOVE,
    - FALSE);
    -
    - buddy = purple_blist_find_buddy(account, ggp_uin_to_str(uin));
    - if (buddy) {
    - purple_debug_info("gg", "ggp_roster_send_update_contact_remove:"
    - " contact %u re-added\n", uin);
    - return TRUE;
    - }
    -
    - buddy_node = g_hash_table_lookup(content->contact_nodes,
    - GINT_TO_POINTER(uin));
    - if (!buddy_node) /* already removed */
    - return TRUE;
    -
    - purple_debug_info("gg", "ggp_roster_send_update_contact_remove: "
    - "removing %u\n", uin);
    - purple_xmlnode_free(buddy_node);
    - g_hash_table_remove(content->contact_nodes, GINT_TO_POINTER(uin));
    -
    - return TRUE;
    -}
    -
    -static gboolean ggp_roster_send_update_group_rename(PurpleConnection *gc,
    - ggp_roster_change *change)
    -{
    - PurpleAccount *account = purple_connection_get_account(gc);
    - ggp_roster_content *content = ggp_roster_get_rdata(gc)->content;
    - const char *old_name = change->data.group_rename.old_name;
    - const char *new_name = change->data.group_rename.new_name;
    - PurpleXmlNode *group_node;
    - const char *group_id;
    -
    - g_return_val_if_fail(change->type == GGP_ROSTER_CHANGE_GROUP_RENAME,
    - FALSE);
    -
    - purple_debug_misc("gg", "ggp_roster_send_update_group_rename: "
    - "\"%s\"->\"%s\"\n", old_name, new_name);
    -
    - /* moving to or from default group instead of renaming it */
    - if (0 == g_strcmp0(old_name, PURPLE_BLIST_DEFAULT_GROUP_NAME) ||
    - 0 == g_strcmp0(new_name, PURPLE_BLIST_DEFAULT_GROUP_NAME))
    - {
    - PurpleGroup *group;
    - GList *group_buddies;
    - group = purple_blist_find_group(new_name);
    - if (!group)
    - return TRUE;
    - purple_debug_info("gg", "ggp_roster_send_update_group_rename: "
    - "invalidating buddies in default group\n");
    - group_buddies = ggp_purplew_group_get_buddies(group, account);
    - while (group_buddies) {
    - ggp_roster_set_synchronized(gc, group_buddies->data,
    - FALSE);
    - group_buddies = g_list_delete_link(group_buddies,
    - group_buddies);
    - }
    - return TRUE;
    - }
    - group_id = g_hash_table_lookup(content->group_ids, old_name);
    - if (!group_id) {
    - purple_debug_info("gg", "ggp_roster_send_update_group_rename: "
    - "%s is not present at roster\n", old_name);
    - return TRUE;
    - }
    -
    - group_node = g_hash_table_lookup(content->group_nodes, group_id);
    - if (!group_node) {
    - purple_debug_error("gg", "ggp_roster_send_update_group_rename: "
    - "node for %s not found, id=%s\n", old_name, group_id);
    - g_hash_table_remove(content->group_ids, old_name);
    - return TRUE;
    - }
    -
    - g_hash_table_remove(content->group_ids, old_name);
    - g_hash_table_insert(content->group_ids, g_strdup(new_name),
    - g_strdup(group_id));
    - g_hash_table_insert(content->group_nodes, g_strdup(group_id),
    - group_node);
    - return ggp_xml_set_string(group_node, "Name", new_name);
    -}
    -
    -static void ggp_roster_send_update(PurpleConnection *gc)
    -{
    - GGPInfo *accdata = purple_connection_get_protocol_data(gc);
    - ggp_roster_session_data *rdata = ggp_roster_get_rdata(gc);
    - ggp_roster_content *content = rdata->content;
    - GList *updates_it;
    - gchar *str;
    - int len;
    -
    - /* an update is running now */
    - if (rdata->sent_updates)
    - return;
    -
    - /* no pending updates found */
    - if (!rdata->pending_updates)
    - return;
    -
    - /* not initialized */
    - if (!content)
    - return;
    -
    - purple_debug_info("gg", "ggp_roster_send_update: "
    - "pending updates found\n");
    -
    - rdata->sent_updates = rdata->pending_updates;
    - rdata->pending_updates = NULL;
    -
    - updates_it = g_list_first(rdata->sent_updates);
    - while (updates_it) {
    - ggp_roster_change *change = updates_it->data;
    - gboolean succ = FALSE;
    - updates_it = g_list_next(updates_it);
    -
    - if (change->type == GGP_ROSTER_CHANGE_CONTACT_UPDATE) {
    - succ = ggp_roster_send_update_contact_update(gc, change);
    - } else if (change->type == GGP_ROSTER_CHANGE_CONTACT_REMOVE) {
    - succ = ggp_roster_send_update_contact_remove(gc, change);
    - } else if (change->type == GGP_ROSTER_CHANGE_GROUP_RENAME) {
    - succ = ggp_roster_send_update_group_rename(gc, change);
    - } else {
    - purple_debug_error("gg", "ggp_roster_send_update: not handled");
    - }
    - g_return_if_fail(succ);
    - }
    -
    -#if GGP_ROSTER_DEBUG
    - ggp_roster_dump(content);
    -#endif
    -
    - str = purple_xmlnode_to_str(content->xml, &len);
    - gg_userlist100_request(accdata->session, GG_USERLIST100_PUT,
    - content->version, GG_USERLIST100_FORMAT_TYPE_GG100, str);
    - g_free(str);
    -}
    -
    -static void ggp_roster_reply_ack(PurpleConnection *gc, uint32_t version)
    -{
    - PurpleAccount *account = purple_connection_get_account(gc);
    - ggp_roster_session_data *rdata = ggp_roster_get_rdata(gc);
    - ggp_roster_content *content = rdata->content;
    - GList *updates_it;
    -
    - purple_debug_info("gg", "ggp_roster_reply_ack: version=%u\n", version);
    -
    - /* set synchronization flag for all buddies, that were updated at roster */
    - updates_it = g_list_first(rdata->sent_updates);
    - while (updates_it) {
    - ggp_roster_change *change = updates_it->data;
    - PurpleBuddy *buddy;
    - updates_it = g_list_next(updates_it);
    -
    - if (change->type != GGP_ROSTER_CHANGE_CONTACT_UPDATE)
    - continue;
    -
    - buddy = purple_blist_find_buddy(account,
    - ggp_uin_to_str(change->data.uin));
    - if (buddy)
    - ggp_roster_set_synchronized(gc, buddy, TRUE);
    - }
    -
    - /* we need to remove "synchronized" flag for all contacts, that have
    - * been modified between roster update start and now
    - */
    - updates_it = g_list_first(rdata->pending_updates);
    - while (updates_it) {
    - ggp_roster_change *change = updates_it->data;
    - PurpleBuddy *buddy;
    - updates_it = g_list_next(updates_it);
    -
    - if (change->type != GGP_ROSTER_CHANGE_CONTACT_UPDATE)
    - continue;
    -
    - buddy = purple_blist_find_buddy(account,
    - ggp_uin_to_str(change->data.uin));
    - if (buddy && ggp_roster_is_synchronized(buddy))
    - ggp_roster_set_synchronized(gc, buddy, FALSE);
    - }
    -
    - g_clear_list(&rdata->sent_updates, ggp_roster_change_free);
    -
    - /* bump roster version or update it, if needed */
    - g_return_if_fail(content != NULL);
    - if (content->needs_update) {
    - ggp_roster_content_free(rdata->content);
    - rdata->content = NULL;
    - /* we have to wait for gg_event_userlist100_version
    - * ggp_roster_request_update(gc);
    - */
    - }
    - else
    - content->version = version;
    -}
    -
    -static void ggp_roster_reply_reject(PurpleConnection *gc, uint32_t version)
    -{
    - ggp_roster_session_data *rdata = ggp_roster_get_rdata(gc);
    -
    - purple_debug_info("gg", "ggp_roster_reply_reject: version=%u\n",
    - version);
    -
    - g_return_if_fail(rdata->sent_updates);
    -
    - rdata->pending_updates = g_list_concat(rdata->pending_updates,
    - rdata->sent_updates);
    - rdata->sent_updates = NULL;
    -
    - ggp_roster_content_free(rdata->content);
    - rdata->content = NULL;
    - ggp_roster_request_update(gc);
    -}
    -
    -/******************************************************************************/
    --- a/libpurple/protocols/gg/roster.h Tue Apr 09 21:50:31 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,73 +0,0 @@
    -/* 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
    - */
    -
    -#ifndef PURPLE_GG_ROSTER_H
    -#define PURPLE_GG_ROSTER_H
    -
    -#include <purple.h>
    -#include <libgadu.h>
    -
    -typedef struct
    -{
    - gpointer content;
    - gboolean is_updating;
    -
    - GList *sent_updates;
    - GList *pending_updates;
    -
    - guint timer;
    -} ggp_roster_session_data;
    -
    -/* setup */
    -gboolean ggp_roster_enabled(void);
    -void ggp_roster_setup(PurpleConnection *gc);
    -void ggp_roster_cleanup(PurpleConnection *gc);
    -
    -/* synchronization control */
    -void ggp_roster_request_update(PurpleConnection *gc);
    -
    -/* libgadu callbacks */
    -void ggp_roster_reply(PurpleConnection *gc,
    - struct gg_event_userlist100_reply *reply);
    -void ggp_roster_version(PurpleConnection *gc,
    - struct gg_event_userlist100_version *version);
    -
    -/* libpurple callbacks */
    -void ggp_roster_alias_buddy(PurpleProtocolServer *protocol_server, PurpleConnection *gc, const char *who,
    - const char *alias);
    -void ggp_roster_group_buddy(PurpleProtocolServer *protocol_server, PurpleConnection *gc, const char *who,
    - const char *old_group, const char *new_group);
    -void ggp_roster_rename_group(PurpleProtocolServer *protocol_server, PurpleConnection *, const char *old_name,
    - PurpleGroup *group, GList *moved_buddies);
    -void ggp_roster_add_buddy(PurpleProtocolServer *protocol_server, PurpleConnection *gc, PurpleBuddy *buddy,
    - PurpleGroup *group, const char *message);
    -void ggp_roster_remove_buddy(PurpleProtocolServer *protocol_server, PurpleConnection *gc, PurpleBuddy *buddy,
    - PurpleGroup *group);
    -
    -#endif /* PURPLE_GG_ROSTER_H */
    --- a/libpurple/protocols/gg/servconn.c Tue Apr 09 21:50:31 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,118 +0,0 @@
    -/* 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 "servconn.h"
    -
    -#include "utils.h"
    -
    -#define GGP_SERVCONN_HISTORY_PREF "/plugins/prpl/gg/server_history"
    -#define GGP_SERVCONN_HISTORY_MAXLEN 15
    -
    -typedef struct
    -{
    - GList *server_history;
    - PurpleAccountOption *server_option;
    -} ggp_servconn_global_data;
    -
    -static ggp_servconn_global_data global_data;
    -
    -void ggp_servconn_setup(PurpleAccountOption *server_option)
    -{
    - GList *extra;
    - purple_prefs_add_string_list(GGP_SERVCONN_HISTORY_PREF, NULL);
    -
    - global_data.server_option = server_option;
    - global_data.server_history =
    - purple_prefs_get_string_list(GGP_SERVCONN_HISTORY_PREF);
    - extra = g_list_nth(global_data.server_history, GGP_SERVCONN_HISTORY_MAXLEN);
    - if (extra != NULL) {
    - /* Truncate the list to the maximum. */
    - extra->prev->next = NULL;
    - g_list_free_full(extra, g_free);
    - }
    -
    - if(server_option != NULL) {
    - purple_account_option_string_set_hints(global_data.server_option,
    - ggp_servconn_get_servers());
    - }
    -}
    -
    -void ggp_servconn_cleanup(void)
    -{
    - g_clear_list(&global_data.server_history, g_free);
    -}
    -
    -void ggp_servconn_add_server(const gchar *server)
    -{
    - GList *old_entry;
    -
    - old_entry = g_list_find_custom(global_data.server_history, server,
    - (GCompareFunc)g_strcmp0);
    - if (old_entry) {
    - g_free(old_entry->data);
    - global_data.server_history = g_list_delete_link(
    - global_data.server_history, old_entry);
    - }
    -
    - global_data.server_history = g_list_prepend(global_data.server_history,
    - g_strdup(server));
    - old_entry =
    - g_list_nth(global_data.server_history, GGP_SERVCONN_HISTORY_MAXLEN);
    - if (old_entry != NULL) {
    - /* Truncate the list to the maximum. */
    - old_entry->prev->next = NULL;
    - g_list_free_full(old_entry, g_free);
    - }
    -
    - purple_prefs_set_string_list(GGP_SERVCONN_HISTORY_PREF,
    - global_data.server_history);
    - purple_account_option_string_set_hints(global_data.server_option,
    - ggp_servconn_get_servers());
    -}
    -
    -GSList *
    -ggp_servconn_get_servers(void)
    -{
    - GSList *new_list = NULL;
    - GList *it;
    -
    - it = g_list_first(global_data.server_history);
    - while (it) {
    - new_list = g_slist_append(new_list, g_strdup(it->data));
    - it = g_list_next(it);
    - }
    - return new_list;
    -}
    -
    -void
    -ggp_servconn_remote_disconnect(PurpleConnection *gc)
    -{
    - purple_debug_info("gg", "Server remotely closes connection");
    - purple_account_disconnect(purple_connection_get_account(gc));
    -}
    --- a/libpurple/protocols/gg/servconn.h Tue Apr 09 21:50:31 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,42 +0,0 @@
    -/* 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
    - */
    -
    -#ifndef PURPLE_GG_SERVCONN_H
    -#define PURPLE_GG_SERVCONN_H
    -
    -#include <purple.h>
    -
    -void ggp_servconn_setup(PurpleAccountOption *server_option);
    -void ggp_servconn_cleanup(void);
    -
    -void ggp_servconn_add_server(const gchar *server);
    -GSList * ggp_servconn_get_servers(void);
    -void ggp_servconn_remote_disconnect(PurpleConnection *gc);
    -
    -#endif /* PURPLE_GG_SERVCONN_H */
    --- a/libpurple/protocols/gg/status.c Tue Apr 09 21:50:31 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,449 +0,0 @@
    -/* 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 <libgadu.h>
    -
    -#include "status.h"
    -
    -#include "gg.h"
    -#include "utils.h"
    -
    -struct _ggp_status_session_data
    -{
    - gboolean status_broadcasting;
    - gchar *current_description;
    -};
    -
    -static inline ggp_status_session_data *
    -ggp_status_get_ssdata(PurpleConnection *gc);
    -
    -static gchar * ggp_status_validate_description(const gchar* msg);
    -
    -static inline ggp_status_session_data *
    -ggp_status_get_ssdata(PurpleConnection *gc)
    -{
    - GGPInfo *accdata = purple_connection_get_protocol_data(gc);
    - return accdata->status_data;
    -}
    -
    -void ggp_status_setup(PurpleConnection *gc)
    -{
    - GGPInfo *accdata = purple_connection_get_protocol_data(gc);
    - PurpleAccount *account = purple_connection_get_account(gc);
    -
    - ggp_status_session_data *ssdata = g_new0(ggp_status_session_data, 1);
    - accdata->status_data = ssdata;
    -
    - ssdata->status_broadcasting =
    - purple_account_get_bool(account, "status_broadcasting", TRUE);
    -}
    -
    -void ggp_status_cleanup(PurpleConnection *gc)
    -{
    - ggp_status_session_data *ssdata = ggp_status_get_ssdata(gc);
    - g_free(ssdata->current_description);
    - g_free(ssdata);
    -}
    -
    -static gchar * ggp_status_validate_description(const gchar* msg)
    -{
    - if (msg == NULL || msg[0] == '\0')
    - return NULL;
    -
    - return ggp_utf8_strndup(msg, GG_STATUS_DESCR_MAXSIZE);
    -}
    -
    -GList *
    -ggp_status_types(G_GNUC_UNUSED PurpleProtocol *protocol,
    - G_GNUC_UNUSED PurpleAccount *account)
    -{
    - GList *types = NULL;
    -
    - types = g_list_append(types, purple_status_type_new_with_attrs(
    - PURPLE_STATUS_AVAILABLE, NULL, NULL,
    - TRUE, TRUE, FALSE, "message", _("Message"),
    - purple_value_new(G_TYPE_STRING), NULL));
    -
    - types = g_list_append(types, purple_status_type_new_with_attrs(
    - PURPLE_STATUS_AVAILABLE, "freeforchat", _("Chatty"),
    - TRUE, TRUE, FALSE, "message", _("Message"),
    - purple_value_new(G_TYPE_STRING), NULL));
    -
    - types = g_list_append(types, purple_status_type_new_with_attrs(
    - PURPLE_STATUS_AWAY, NULL, NULL,
    - TRUE, TRUE, FALSE, "message", _("Message"),
    - purple_value_new(G_TYPE_STRING), NULL));
    -
    - types = g_list_append(types, purple_status_type_new_with_attrs(
    - PURPLE_STATUS_UNAVAILABLE, NULL, NULL,
    - TRUE, TRUE, FALSE, "message", _("Message"),
    - purple_value_new(G_TYPE_STRING), NULL));
    -
    - types = g_list_append(types, purple_status_type_new_with_attrs(
    - PURPLE_STATUS_INVISIBLE, NULL, NULL,
    - TRUE, TRUE, FALSE, "message", _("Message"),
    - purple_value_new(G_TYPE_STRING), NULL));
    -
    - types = g_list_append(types, purple_status_type_new_with_attrs(
    - PURPLE_STATUS_OFFLINE, NULL, NULL,
    - TRUE, TRUE, FALSE, "message", _("Message"),
    - purple_value_new(G_TYPE_STRING), NULL));
    -
    - return types;
    -}
    -
    -int ggp_status_from_purplestatus(PurpleStatus *status, gchar **message)
    -{
    - const char *status_id = purple_status_get_id(status);
    - const char *status_message =
    - purple_status_get_attr_string(status, "message");
    -
    - g_return_val_if_fail(message != NULL, 0);
    -
    - *message = NULL;
    - if (status_message) {
    - gchar *stripped = purple_markup_strip_html(status_message);
    - g_strstrip(stripped);
    - *message = ggp_status_validate_description(stripped);
    - g_free(stripped);
    - }
    -
    - if (0 == strcmp(status_id, "available"))
    - return status_message ? GG_STATUS_AVAIL_DESCR : GG_STATUS_AVAIL;
    - if (0 == strcmp(status_id, "freeforchat"))
    - return status_message ? GG_STATUS_FFC_DESCR : GG_STATUS_FFC;
    - if (0 == strcmp(status_id, "away"))
    - return status_message ? GG_STATUS_BUSY_DESCR : GG_STATUS_BUSY;
    - if (0 == strcmp(status_id, "unavailable"))
    - return status_message ? GG_STATUS_DND_DESCR : GG_STATUS_DND;
    - if (0 == strcmp(status_id, "invisible"))
    - return status_message ?
    - GG_STATUS_INVISIBLE_DESCR : GG_STATUS_INVISIBLE;
    - if (0 == strcmp(status_id, "offline"))
    - return status_message ?
    - GG_STATUS_NOT_AVAIL_DESCR : GG_STATUS_NOT_AVAIL;
    -
    - purple_debug_error("gg", "ggp_status_from_purplestatus: "
    - "unknown status requested (%s)\n", status_id);
    - return status_message ? GG_STATUS_AVAIL_DESCR : GG_STATUS_AVAIL;
    -}
    -
    -const gchar * ggp_status_to_purplestatus(int status)
    -{
    - switch (status)
    - {
    - case GG_STATUS_NOT_AVAIL:
    - case GG_STATUS_NOT_AVAIL_DESCR:
    - case GG_STATUS_BLOCKED:
    - case GG_STATUS_UNKNOWN:
    - return purple_primitive_get_id_from_type(
    - PURPLE_STATUS_OFFLINE);
    - case GG_STATUS_FFC:
    - case GG_STATUS_FFC_DESCR:
    - return "freeforchat";
    - case GG_STATUS_AVAIL:
    - case GG_STATUS_AVAIL_DESCR:
    - return purple_primitive_get_id_from_type(
    - PURPLE_STATUS_AVAILABLE);
    - case GG_STATUS_BUSY:
    - case GG_STATUS_BUSY_DESCR:
    - return purple_primitive_get_id_from_type(
    - PURPLE_STATUS_AWAY);
    - case GG_STATUS_INVISIBLE:
    - case GG_STATUS_INVISIBLE_DESCR:
    - return purple_primitive_get_id_from_type(
    - PURPLE_STATUS_INVISIBLE);
    - case GG_STATUS_DND:
    - case GG_STATUS_DND_DESCR:
    - return purple_primitive_get_id_from_type(
    - PURPLE_STATUS_UNAVAILABLE);
    - default:
    - purple_debug_warning("gg", "ggp_status_to_purplestatus: unknown status %#02x\n", status);
    - return purple_primitive_get_id_from_type(
    - PURPLE_STATUS_AVAILABLE);
    - }
    -}
    -
    -const gchar * ggp_status_get_name(const gchar *purple_status)
    -{
    - if (g_strcmp0(purple_status, "freeforchat") == 0)
    - return _("Chatty");
    - return purple_primitive_get_name_from_type(
    - purple_primitive_get_type_from_id(purple_status));
    -}
    -
    -/*******************************************************************************
    - * Own status.
    - ******************************************************************************/
    -
    -void ggp_status_set_initial(PurpleConnection *gc, struct gg_login_params *glp)
    -{
    - ggp_status_session_data *ssdata = ggp_status_get_ssdata(gc);
    - PurpleAccount *account = purple_connection_get_account(gc);
    -
    - glp->status = ggp_status_from_purplestatus(
    - purple_account_get_active_status(account), &glp->status_descr);
    - if (!ggp_status_get_status_broadcasting(gc))
    - glp->status |= GG_STATUS_FRIENDS_MASK;
    - ssdata->current_description = g_strdup(glp->status_descr);
    -}
    -
    -gboolean ggp_status_set(PurpleAccount *account, int status, const gchar* msg)
    -{
    - PurpleConnection *gc = purple_account_get_connection(account);
    - ggp_status_session_data *ssdata = ggp_status_get_ssdata(gc);
    - GGPInfo *accdata = purple_connection_get_protocol_data(gc);
    - gchar *new_description = ggp_status_validate_description(msg);
    -
    - if (!ssdata->status_broadcasting)
    - status |= GG_STATUS_FRIENDS_MASK;
    -
    - if ((status == GG_STATUS_NOT_AVAIL ||
    - status == GG_STATUS_NOT_AVAIL_DESCR) &&
    - 0 == g_strcmp0(ssdata->current_description, new_description))
    - {
    - purple_debug_info("gg", "ggp_status_set: new status doesn't "
    - "differ when closing connection - ignore\n");
    - g_free(new_description);
    - return FALSE;
    - }
    - g_free(ssdata->current_description);
    - ssdata->current_description = new_description;
    -
    - if (msg == NULL)
    - gg_change_status(accdata->session, status);
    - else
    - gg_change_status_descr(accdata->session, status, new_description);
    -
    - return TRUE;
    -}
    -
    -void
    -ggp_status_set_purplestatus(G_GNUC_UNUSED PurpleProtocolServer *protocol_server,
    - PurpleAccount *account, PurpleStatus *status)
    -{
    - int status_gg;
    - gchar *msg = NULL;
    -
    - if (!purple_status_is_active(status))
    - return;
    -
    - status_gg = ggp_status_from_purplestatus(status, &msg);
    - ggp_status_set(account, status_gg, msg);
    - g_free(msg);
    -}
    -
    -void ggp_status_set_disconnected(PurpleAccount *account)
    -{
    - gchar *msg = NULL;
    -
    - ggp_status_from_purplestatus(purple_account_get_active_status(account),
    - &msg);
    - if (!ggp_status_set(account,
    - msg ? GG_STATUS_NOT_AVAIL_DESCR : GG_STATUS_NOT_AVAIL, msg))
    - {
    - g_free(msg);
    - return;
    - }
    -
    - /*
    - struct gg_event *ev;
    - guint64 wait_start = ggp_microtime(), now;
    - int sleep_time = 5000;
    - while ((ev = gg_watch_fd(info->session)) != NULL)
    - {
    - if (ev->type == GG_EVENT_DISCONNECT_ACK)
    - break;
    - now = ggp_microtime();
    - if (now - wait_start + sleep_time >= 100000)
    - break;
    - usleep(sleep_time);
    - sleep_time *= 2;
    - }
    - */
    - g_usleep(100000);
    -
    - g_free(msg);
    -}
    -
    -void ggp_status_fake_to_self(PurpleConnection *gc)
    -{
    - PurpleAccount *account = purple_connection_get_account(gc);
    - PurpleContactInfo *info = PURPLE_CONTACT_INFO(account);
    - PurpleStatus *status = purple_presence_get_active_status(
    - purple_account_get_presence(account));
    - const char *status_msg = purple_status_get_attr_string(status,
    - "message");
    - gchar *status_msg_gg = NULL;
    -
    - if (status_msg != NULL && status_msg[0] != '\0') {
    - status_msg_gg = g_new0(gchar, GG_STATUS_DESCR_MAXSIZE + 1);
    - g_utf8_strncpy(status_msg_gg, status_msg,
    - GG_STATUS_DESCR_MAXSIZE);
    - }
    -
    - purple_protocol_got_user_status(account,
    - purple_contact_info_get_username(info),
    - purple_status_get_id(status),
    - status_msg_gg ? "message" : NULL, status_msg_gg, NULL);
    -
    - g_free(status_msg_gg);
    -}
    -
    -gboolean ggp_status_get_status_broadcasting(PurpleConnection *gc)
    -{
    - return ggp_status_get_ssdata(gc)->status_broadcasting;
    -}
    -
    -void ggp_status_set_status_broadcasting(PurpleConnection *gc,
    - gboolean broadcasting)
    -{
    - PurpleAccount *account = purple_connection_get_account(gc);
    -
    - ggp_status_get_ssdata(gc)->status_broadcasting = broadcasting;
    - purple_account_set_bool(account, "status_broadcasting", broadcasting);
    - ggp_status_set_purplestatus(NULL, account,
    - purple_account_get_active_status(account));
    -}
    -
    -static void
    -ggp_status_broadcasting_dialog_ok(PurpleConnection *gc,
    - PurpleRequestPage *page)
    -{
    - gboolean buddies_only = purple_request_page_get_bool(page, "buddies_only");
    - ggp_status_set_status_broadcasting(gc, !buddies_only);
    -}
    -
    -void ggp_status_broadcasting_dialog(PurpleConnection *gc)
    -{
    - PurpleRequestPage *page;
    - PurpleRequestGroup *group;
    - PurpleRequestField *field;
    -
    - page = purple_request_page_new();
    - group = purple_request_group_new(NULL);
    - purple_request_page_add_group(page, group);
    -
    - field = purple_request_field_bool_new("buddies_only",
    - _("Show status only for buddies"),
    - !ggp_status_get_status_broadcasting(gc));
    - purple_request_group_add_field(group, field);
    -
    - purple_request_fields(gc,
    - _("Change status broadcasting"),
    - _("Please, select who can see your status"),
    - NULL,
    - page,
    - _("OK"), G_CALLBACK(ggp_status_broadcasting_dialog_ok),
    - _("Cancel"), NULL,
    - purple_request_cpar_from_connection(gc), gc);
    -}
    -
    -/*******************************************************************************
    - * Buddy status.
    - ******************************************************************************/
    -
    -void ggp_status_got_others_buddy(PurpleConnection *gc, uin_t uin, int status,
    - const char *descr);
    -
    -/******************************************************************************/
    -
    -void ggp_status_got_others(PurpleConnection *gc, struct gg_event *ev)
    -{
    - if (ev->type == GG_EVENT_NOTIFY60) {
    - struct gg_event_notify60 *notify = ev->event.notify60;
    - int i;
    - for (i = 0; notify[i].uin; i++)
    - ggp_status_got_others_buddy(gc, notify[i].uin,
    - GG_S(notify[i].status), notify[i].descr);
    - } else if (ev->type == GG_EVENT_STATUS60) {
    - struct gg_event_status60 *notify = &ev->event.status60;
    - ggp_status_got_others_buddy(gc, notify->uin,
    - GG_S(notify->status), notify->descr);
    - } else {
    - purple_debug_error("gg", "ggp_status_got_others: unexpected event %d",
    - ev->type);
    - }
    -}
    -
    -void ggp_status_got_others_buddy(PurpleConnection *gc, uin_t uin, int status,
    - const char *descr)
    -{
    - PurpleAccount *account = purple_connection_get_account(gc);
    - PurpleContactInfo *info = PURPLE_CONTACT_INFO(account);
    - PurpleBuddy *buddy = purple_blist_find_buddy(account, ggp_uin_to_str(uin));
    - const gchar *purple_status = ggp_status_to_purplestatus(status);
    - gchar *status_message = NULL;
    - gboolean is_own;
    -
    - is_own = (!g_strcmp0(ggp_uin_to_str(uin),
    - purple_contact_info_get_username(info)));
    -
    - if (!buddy) {
    - if (!is_own) {
    - purple_debug_warning("gg",
    - "ggp_status_got_others_buddy: "
    - "buddy %u not found\n", uin);
    - }
    - return;
    - }
    - ggp_buddy_get_data(buddy)->blocked = (status == GG_STATUS_BLOCKED);
    - ggp_buddy_get_data(buddy)->not_a_friend = (status == GG_STATUS_UNKNOWN);
    -
    - if (descr != NULL) {
    - status_message = g_strdup(descr);
    - g_strstrip(status_message);
    - if (status_message[0] == '\0') {
    - g_free(status_message);
    - status_message = NULL;
    - }
    - }
    -
    - if (uin == ggp_str_to_uin(purple_contact_info_get_username(info))) {
    - purple_debug_info("gg", "ggp_status_got_others_buddy: "
    - "own status changed to %s [%s]\n",
    - purple_status, status_message ? status_message : "");
    - } else if (purple_debug_is_verbose()) {
    - purple_debug_misc("gg", "ggp_status_got_others_buddy: "
    - "status of %u changed to %s [%s]\n", uin,
    - purple_status, status_message ? status_message : "");
    - }
    -
    - if (status_message) {
    - purple_protocol_got_user_status(account, ggp_uin_to_str(uin),
    - purple_status, "message", status_message, NULL);
    - } else {
    - purple_protocol_got_user_status(account, ggp_uin_to_str(uin),
    - purple_status, NULL);
    - }
    -
    - g_free(status_message);
    -}
    --- a/libpurple/protocols/gg/status.h Tue Apr 09 21:50:31 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,64 +0,0 @@
    -/* 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
    - */
    -
    -#ifndef PURPLE_GG_STATUS_H
    -#define PURPLE_GG_STATUS_H
    -
    -#include <purple.h>
    -#include <libgadu.h>
    -
    -typedef struct _ggp_status_session_data ggp_status_session_data;
    -
    -void ggp_status_setup(PurpleConnection *gc);
    -void ggp_status_cleanup(PurpleConnection *gc);
    -
    -GList *ggp_status_types(G_GNUC_UNUSED PurpleProtocol *protocol, PurpleAccount *account);
    -int ggp_status_from_purplestatus(PurpleStatus *status, gchar **message);
    -const gchar * ggp_status_to_purplestatus(int status);
    -const gchar * ggp_status_get_name(const gchar *purple_status);
    -
    -/* own status */
    -
    -void ggp_status_set_initial(PurpleConnection *gc, struct gg_login_params *glp);
    -
    -gboolean ggp_status_set(PurpleAccount *account, int status, const gchar* msg);
    -void ggp_status_set_purplestatus(PurpleProtocolServer *protocol_server, PurpleAccount *account, PurpleStatus *status);
    -void ggp_status_set_disconnected(PurpleAccount *account);
    -void ggp_status_fake_to_self(PurpleConnection *gc);
    -
    -gboolean ggp_status_get_status_broadcasting(PurpleConnection *gc);
    -void ggp_status_set_status_broadcasting(PurpleConnection *gc,
    - gboolean broadcasting);
    -void ggp_status_broadcasting_dialog(PurpleConnection *gc);
    -
    -/* buddy status */
    -
    -void ggp_status_got_others(PurpleConnection *gc, struct gg_event *ev);
    -
    -#endif /* PURPLE_GG_STATUS_H */
    --- a/libpurple/protocols/gg/tcpsocket.c Tue Apr 09 21:50:31 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,265 +0,0 @@
    -/* 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.
    - *
    - * Component written by Tomek Wasilczyk (http://www.wasilczyk.pl).
    - *
    - * This file is dual-licensed under the GPL2+ and the X11 (MIT) licences.
    - * As a recipient of this file you may choose, which license to receive the
    - * code under. As a contributor, you have to ensure the new code is
    - * compatible with both.
    - *
    - * 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 <errno.h>
    -
    -#include <purple.h>
    -
    -#include "tcpsocket.h"
    -
    -#include "gg.h"
    -
    -
    -typedef struct {
    - GSocketConnection *conn;
    - GCancellable *cancellable;
    - PurpleConnection *gc;
    - gpointer priv_gg;
    -} GGPTcpSocketData;
    -
    -static void
    -ggp_tcp_socket_data_free(GGPTcpSocketData *data)
    -{
    - g_return_if_fail(data != NULL);
    -
    - if (data->cancellable != NULL) {
    - g_cancellable_cancel(data->cancellable);
    - g_clear_object(&data->cancellable);
    - }
    -
    - if (data->conn != NULL) {
    - purple_gio_graceful_close(G_IO_STREAM(data->conn), NULL, NULL);
    - g_clear_object(&data->conn);
    - }
    -
    - g_free(data);
    -}
    -
    -static void
    -ggp_tcpsocket_connected(GObject *source, GAsyncResult *res, gpointer user_data)
    -{
    - GGPTcpSocketData *data = user_data;
    - GSocketConnection *conn;
    - GSocket *socket;
    - int fd = -1;
    - GGPInfo *info;
    - GError *error = NULL;
    -
    - conn = g_socket_client_connect_to_host_finish(G_SOCKET_CLIENT(source),
    - res, &error);
    -
    - if (conn == NULL) {
    - if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
    - /* The connection was already closed, return now */
    - g_clear_error(&error);
    - return;
    - }
    -
    - purple_debug_error("gg", "socket failed to connect: %s",
    - error->message);
    - g_clear_error(&error);
    - } else {
    - data->conn = conn;
    -
    - socket = g_socket_connection_get_socket(data->conn);
    -
    - if (socket != NULL) {
    - fd = g_socket_get_fd(socket);
    - }
    - }
    -
    - /* XXX: For some reason if you try to connect and then immediately
    - * disconnect, this gets into a state where ggp_tcpsocket_close()
    - * isn't called. The cancellable is therefore not cancelled, and
    - * the connection is never closed. Guard against that state here.
    - */
    - if (data->gc == NULL ||
    - !g_list_find(purple_connections_get_all(), data->gc)) {
    - purple_debug_error("gg",
    - "disconnected without closing connection: %p",
    - data);
    - ggp_tcp_socket_data_free(data);
    - return;
    - }
    -
    - if (!gg_socket_manager_connected(data, data->priv_gg, fd)) {
    - purple_debug_error("gg", "socket not handled");
    - ggp_tcp_socket_data_free(data);
    - return;
    - }
    -
    - info = purple_connection_get_protocol_data(data->gc);
    -
    - g_clear_handle_id(&info->inpa, g_source_remove);
    -
    - if (info->session->fd < 0)
    - return;
    -
    - /* XXX: This works, but not recommended to use GSocket FDs directly */
    - info->inpa = purple_input_add(info->session->fd,
    - ggp_tcpsocket_inputcond_gg_to_purple(info->session->check),
    - ggp_async_login_handler, data->gc);
    -}
    -
    -static void*
    -ggp_tcpsocket_connect(void *_gc, const char *host, int port, int is_tls,
    - int is_async, void *priv)
    -{
    - PurpleConnection *gc = _gc;
    - GGPTcpSocketData *data;
    - GSocketClient *client;
    - GError *error = NULL;
    -
    - PURPLE_ASSERT_CONNECTION_IS_VALID(gc);
    -
    - g_return_val_if_fail(host != NULL, NULL);
    - g_return_val_if_fail(is_async, NULL);
    -
    - purple_debug_misc("gg", "ggp_tcpsocket_connect(%p, %s:%d, %s, %p)",
    - gc, host, port, is_tls ? "tls" : "tcp", priv);
    -
    - client = purple_gio_socket_client_new(
    - purple_connection_get_account(gc), &error);
    -
    - if (client == NULL) {
    - purple_debug_error("gg", "unable to connect: %s",
    - error->message);
    - g_clear_error(&error);
    - return NULL;
    - }
    -
    - g_socket_client_set_tls(client, is_tls);
    -
    - data = g_new0(GGPTcpSocketData, 1);
    - data->cancellable = g_cancellable_new();
    - data->gc = gc;
    - data->priv_gg = priv;
    -
    - g_socket_client_connect_to_host_async(client, host, port,
    - data->cancellable, ggp_tcpsocket_connected, data);
    - g_object_unref(client);
    -
    - return data;
    -}
    -
    -static void
    -ggp_tcpsocket_close(G_GNUC_UNUSED void *_gc, void *_data) {
    - GGPTcpSocketData *data = _data;
    -
    - ggp_tcp_socket_data_free(data);
    -}
    -
    -static ssize_t
    -ggp_tcpsocket_read(G_GNUC_UNUSED void *_gc, void *_data,
    - unsigned char *buffer, size_t bufsize)
    -{
    - GGPTcpSocketData *data = _data;
    - GPollableInputStream *input;
    - gssize ret;
    - GError *error = NULL;
    -
    - if (data->conn == NULL) {
    - return -1;
    - }
    -
    - input = G_POLLABLE_INPUT_STREAM(
    - g_io_stream_get_input_stream(G_IO_STREAM(data->conn)));
    - ret = g_pollable_input_stream_read_nonblocking(input,
    - buffer, bufsize, NULL, &error);
    -
    - if (ret < 0) {
    - if (g_error_matches(error,
    - G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
    - errno = EAGAIN;
    - } else {
    - purple_debug_error("gg", "socket read error: %s",
    - error->message);
    - }
    -
    - g_clear_error(&error);
    - }
    -
    - return ret;
    -}
    -
    -static ssize_t
    -ggp_tcpsocket_write(G_GNUC_UNUSED void *_gc, void *_data,
    - const unsigned char *data_buf, size_t len)
    -{
    - GGPTcpSocketData *data = _data;
    - GPollableOutputStream *output;
    - gssize ret;
    - GError *error = NULL;
    -
    - if (data->conn == NULL) {
    - return -1;
    - }
    -
    - output = G_POLLABLE_OUTPUT_STREAM(
    - g_io_stream_get_output_stream(G_IO_STREAM(data->conn)));
    - ret = g_pollable_output_stream_write_nonblocking(output,
    - data_buf, len, NULL, &error);
    -
    - if (ret < 0) {
    - if (g_error_matches(error,
    - G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
    - errno = EAGAIN;
    - } else {
    - purple_debug_error("gg", "socket write error: %s",
    - error->message);
    - }
    -
    - g_clear_error(&error);
    - }
    -
    - return ret;
    -}
    -
    -void
    -ggp_tcpsocket_setup(PurpleConnection *gc, struct gg_login_params *glp)
    -{
    - glp->socket_manager_type = GG_SOCKET_MANAGER_TYPE_TLS;
    - glp->socket_manager.cb_data = gc;
    - glp->socket_manager.connect_cb = ggp_tcpsocket_connect;
    - glp->socket_manager.close_cb = ggp_tcpsocket_close;
    - glp->socket_manager.read_cb = ggp_tcpsocket_read;
    - glp->socket_manager.write_cb = ggp_tcpsocket_write;
    -}
    -
    -PurpleInputCondition
    -ggp_tcpsocket_inputcond_gg_to_purple(enum gg_check_t check)
    -{
    - PurpleInputCondition cond = 0;
    -
    - if (check & GG_CHECK_READ)
    - cond |= PURPLE_INPUT_READ;
    - if (check & GG_CHECK_WRITE)
    - cond |= PURPLE_INPUT_WRITE;
    -
    - return cond;
    -}
    --- a/libpurple/protocols/gg/tcpsocket.h Tue Apr 09 21:50:31 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,41 +0,0 @@
    -/* 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.
    - *
    - * Component written by Tomek Wasilczyk (http://www.wasilczyk.pl).
    - *
    - * This file is dual-licensed under the GPL2+ and the X11 (MIT) licences.
    - * As a recipient of this file you may choose, which license to receive the
    - * code under. As a contributor, you have to ensure the new code is
    - * compatible with both.
    - *
    - * 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
    - */
    -
    -#ifndef PURPLE_GG_TCPSOCKET_H
    -#define PURPLE_GG_TCPSOCKET_H
    -
    -#include <purple.h>
    -#include <libgadu.h>
    -
    -void
    -ggp_tcpsocket_setup(PurpleConnection *gc, struct gg_login_params *glp);
    -
    -PurpleInputCondition
    -ggp_tcpsocket_inputcond_gg_to_purple(enum gg_check_t check);
    -
    -#endif /* PURPLE_GG_TCPSOCKET_H */
    --- a/libpurple/protocols/gg/utils.c Tue Apr 09 21:50:31 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,172 +0,0 @@
    -/* 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 <errno.h>
    -#include <netinet/in.h>
    -
    -#include <purple.h>
    -
    -#include "utils.h"
    -
    -#include "gg.h"
    -
    -uin_t ggp_str_to_uin(const char *str)
    -{
    - char *endptr;
    - uin_t uin;
    -
    - if (!str || str[0] < '0' || str[0] > '9')
    - return 0;
    -
    - errno = 0;
    - uin = strtoul(str, &endptr, 10);
    -
    - if (errno == ERANGE || endptr[0] != '\0')
    - return 0;
    -
    - return uin;
    -}
    -
    -const char * ggp_uin_to_str(uin_t uin)
    -{
    - static char buff[GGP_UIN_LEN_MAX + 1];
    -
    - g_snprintf(buff, GGP_UIN_LEN_MAX + 1, "%u", uin);
    -
    - return buff;
    -}
    -
    -uin_t
    -ggp_get_my_uin(PurpleConnection *gc) {
    - PurpleAccount *account = purple_connection_get_account(gc);
    - PurpleContactInfo *info = PURPLE_CONTACT_INFO(account);
    -
    - g_return_val_if_fail(gc != NULL, 0);
    -
    - return ggp_str_to_uin(purple_contact_info_get_username(info));
    -}
    -
    -static gchar * ggp_convert(const gchar *src, const char *srcenc,
    - const char *dstenc)
    -{
    - gchar *dst;
    - GError *err = NULL;
    -
    - if (src == NULL)
    - return NULL;
    -
    - dst = g_convert_with_fallback(src, strlen(src), dstenc, srcenc, "?",
    - NULL, NULL, &err);
    - if (err != NULL) {
    - purple_debug_error("gg", "error converting from %s to %s: %s\n",
    - srcenc, dstenc, err->message);
    - g_error_free(err);
    - }
    -
    - if (dst == NULL)
    - dst = g_strdup(src);
    -
    - return dst;
    -}
    -
    -gchar * ggp_convert_to_cp1250(const gchar *src)
    -{
    - return ggp_convert(src, "UTF-8", "CP1250");
    -}
    -
    -gchar * ggp_convert_from_cp1250(const gchar *src)
    -{
    - return ggp_convert(src, "CP1250", "UTF-8");
    -}
    -
    -gchar * ggp_utf8_strndup(const gchar *str, gsize n)
    -{
    - size_t raw_len;
    - gchar *end_ptr;
    - if (str == NULL)
    - return NULL;
    - raw_len = strlen(str);
    - if (raw_len <= n)
    - return g_strdup(str);
    -
    - end_ptr = g_utf8_offset_to_pointer(str, g_utf8_pointer_to_offset(str, &str[n]));
    - raw_len = end_ptr - str;
    -
    - if (raw_len > n) {
    - end_ptr = g_utf8_prev_char(end_ptr);
    - raw_len = end_ptr - str;
    - }
    -
    - g_assert(raw_len <= n);
    -
    - return g_strndup(str, raw_len);
    -}
    -
    -const gchar * ggp_ipv4_to_str(uint32_t raw_ip)
    -{
    - static gchar buff[INET_ADDRSTRLEN];
    - buff[0] = '\0';
    -
    - g_snprintf(buff, sizeof(buff), "%d.%d.%d.%d",
    - ((raw_ip >> 0) & 0xFF),
    - ((raw_ip >> 8) & 0xFF),
    - ((raw_ip >> 16) & 0xFF),
    - ((raw_ip >> 24) & 0xFF));
    -
    - return buff;
    -}
    -
    -gchar * ggp_free_if_equal(gchar *str, const gchar *pattern)
    -{
    - if (g_strcmp0(str, pattern) == 0) {
    - g_free(str);
    - return NULL;
    - }
    - return str;
    -}
    -
    -uint64_t * ggp_uint64dup(uint64_t val)
    -{
    - uint64_t *ptr = g_new(uint64_t, 1);
    - *ptr = val;
    - return ptr;
    -}
    -
    -JsonParser * ggp_json_parse(const gchar *data)
    -{
    - JsonParser *parser;
    -
    - parser = json_parser_new();
    - if (json_parser_load_from_data(parser, data, -1, NULL))
    - return parser;
    -
    - if (purple_debug_is_unsafe())
    - purple_debug_warning("gg", "Invalid JSON: %s\n", data);
    - return NULL;
    -}
    --- a/libpurple/protocols/gg/utils.h Tue Apr 09 21:50:31 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,94 +0,0 @@
    -/* 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
    - */
    -
    -#ifndef PURPLE_GG_UTILS_H
    -#define PURPLE_GG_UTILS_H
    -
    -#include <libgadu.h>
    -#include <json-glib/json-glib.h>
    -
    -#include <purple.h>
    -
    -/**
    - * Converts stringified UIN to uin_t.
    - *
    - * @param str The string to convert.
    - *
    - * @return Converted UIN or 0 if an error occurred.
    - */
    -uin_t ggp_str_to_uin(const char *str);
    -
    -/**
    - * Stringifies UIN.
    - *
    - * @param uin UIN to stringify.
    - *
    - * @return Stringified UIN.
    - */
    -const char * ggp_uin_to_str(uin_t uin);
    -
    -/**
    - * Gets UIN for the account.
    - *
    - * @param gc The connection, in which account is connected.
    - * @return UIN for this account.
    - */
    -uin_t ggp_get_my_uin(PurpleConnection *gc);
    -
    -/**
    - * Converts encoding of a given string from UTF-8 to CP1250.
    - *
    - * @param src Input string.
    - *
    - * @return Converted string (must be freed with g_free). If src is NULL,
    - * then NULL is returned.
    - */
    -gchar * ggp_convert_to_cp1250(const gchar *src);
    -
    -/**
    - * Converts encoding of a given string from CP1250 to UTF-8.
    - *
    - * @param src Input string.
    - *
    - * @return Converted string (must be freed with g_free). If src is NULL,
    - * then NULL is returned.
    - */
    -gchar * ggp_convert_from_cp1250(const gchar *src);
    -
    -gchar * ggp_utf8_strndup(const gchar *str, gsize n);
    -
    -const gchar * ggp_ipv4_to_str(uint32_t raw_ip);
    -
    -gchar * ggp_free_if_equal(gchar *str, const gchar *pattern);
    -
    -uint64_t * ggp_uint64dup(uint64_t val);
    -
    -JsonParser * ggp_json_parse(const gchar *data);
    -
    -#endif /* PURPLE_GG_UTILS_H */
    --- a/libpurple/protocols/gg/validator.c Tue Apr 09 21:50:31 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,80 +0,0 @@
    -/* 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 "validator.h"
    -
    -#include "utils.h"
    -
    -#include <glib/gi18n-lib.h>
    -
    -gboolean
    -ggp_validator_password(PurpleRequestField *field, gchar **errmsg,
    - G_GNUC_UNUSED gpointer data)
    -{
    - const char *value;
    -
    - g_return_val_if_fail(PURPLE_IS_REQUEST_FIELD_STRING(field), FALSE);
    -
    - value = purple_request_field_string_get_value(PURPLE_REQUEST_FIELD_STRING(field));
    -
    - if (value != NULL) {
    - size_t len = strlen(value);
    - if (6 <= len && len <= 15) {
    - if (g_regex_match_simple(
    - "^[ a-zA-Z0-9~`!@#$%^&*()_+=[\\]{};':\",./?<>\\\\|-]+$",
    - value, 0, 0)) {
    - return TRUE;
    - }
    - }
    - }
    -
    - if (errmsg)
    - *errmsg = g_strdup(_("Password can contain 6-15 alphanumeric characters"));
    - return FALSE;
    -}
    -
    -gboolean ggp_validator_password_equal(PurpleRequestField *field, gchar **errmsg,
    - void *field2_p)
    -{
    - const char *value1, *value2;
    - PurpleRequestField *field2 = field2_p;
    -
    - g_return_val_if_fail(PURPLE_IS_REQUEST_FIELD_STRING(field), FALSE);
    - g_return_val_if_fail(PURPLE_IS_REQUEST_FIELD_STRING(field2), FALSE);
    -
    - value1 = purple_request_field_string_get_value(PURPLE_REQUEST_FIELD_STRING(field));
    - value2 = purple_request_field_string_get_value(PURPLE_REQUEST_FIELD_STRING(field2));
    -
    - if (g_strcmp0(value1, value2) == 0)
    - return TRUE;
    -
    - if (errmsg)
    - *errmsg = g_strdup(_("Passwords do not match"));
    - return FALSE;
    -}
    --- a/libpurple/protocols/gg/validator.h Tue Apr 09 21:50:31 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,41 +0,0 @@
    -/* 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
    - */
    -
    -#ifndef PURPLE_GG_VALIDATOR_H
    -#define PURPLE_GG_VALIDATOR_H
    -
    -#include <purple.h>
    -
    -gboolean ggp_validator_password(PurpleRequestField *field, gchar **errmsg,
    - void *user_data);
    -
    -gboolean ggp_validator_password_equal(PurpleRequestField *field, gchar **errmsg,
    - void *field2);
    -
    -#endif /* PURPLE_GG_VALIDATOR_H */
    --- a/libpurple/protocols/gg/xml.c Tue Apr 09 21:50:31 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,163 +0,0 @@
    -/* 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 <errno.h>
    -
    -#include "xml.h"
    -
    -gboolean
    -ggp_xml_get_string(const PurpleXmlNode *xml, const gchar *childName, gchar **var)
    -{
    - char *str;
    -
    - g_return_val_if_fail(xml != NULL, FALSE);
    - g_return_val_if_fail(var != NULL, FALSE);
    -
    - if (childName != NULL) {
    - xml = purple_xmlnode_get_child(xml, childName);
    - if (xml == NULL)
    - return FALSE;
    - }
    -
    - str = purple_xmlnode_get_data(xml);
    - if (str == NULL)
    - return FALSE;
    -
    - *var = str;
    - return TRUE;
    -}
    -
    -gboolean
    -ggp_xml_get_bool(const PurpleXmlNode *xml, const gchar *childName, gboolean *var)
    -{
    - char *str;
    - gboolean succ;
    -
    - succ = ggp_xml_get_string(xml, childName, &str);
    - if (!succ)
    - return FALSE;
    -
    - *var = (strcmp(str, "true") == 0 ||
    - strcmp(str, "True") == 0 ||
    - strcmp(str, "TRUE") == 0 ||
    - strcmp(str, "1") == 0);
    - g_free(str);
    -
    - return TRUE;
    -}
    -
    -gboolean
    -ggp_xml_get_uint(const PurpleXmlNode *xml, const gchar *childName, unsigned int *var)
    -{
    - char *str, *endptr;
    - gboolean succ;
    - unsigned int val;
    -
    - succ = ggp_xml_get_string(xml, childName, &str);
    - if (!succ)
    - return FALSE;
    -
    - errno = 0;
    - val = strtoul(str, &endptr, 10);
    -
    - succ = (errno != ERANGE && endptr[0] == '\0');
    - g_free(str);
    -
    - if (succ)
    - *var = val;
    - return succ;
    -}
    -
    -gboolean
    -ggp_xml_set_string(PurpleXmlNode *xml, const gchar *childName, const gchar *val)
    -{
    - g_return_val_if_fail(xml != NULL, FALSE);
    - g_return_val_if_fail(val != NULL, FALSE);
    -
    - if (childName != NULL) {
    - PurpleXmlNode *child = purple_xmlnode_get_child(xml, childName);
    - if (child == NULL)
    - child = purple_xmlnode_new_child(xml, childName);
    - xml = child;
    - }
    -
    - ggp_xmlnode_remove_children(xml);
    - purple_xmlnode_insert_data(xml, val, -1);
    -
    - return TRUE;
    -}
    -
    -gboolean
    -ggp_xml_set_bool(PurpleXmlNode *xml, const gchar *childName, gboolean val)
    -{
    - return ggp_xml_set_string(xml, childName, val ? "true" : "false");
    -}
    -
    -gboolean
    -ggp_xml_set_uint(PurpleXmlNode *xml, const gchar *childName, unsigned int val)
    -{
    - gchar buff[20];
    - g_snprintf(buff, sizeof(buff), "%u", val);
    - return ggp_xml_set_string(xml, childName, buff);
    -}
    -
    -void ggp_xmlnode_remove_children(PurpleXmlNode *xml)
    -{
    - PurpleXmlNode *child = xml->child;
    - while (child) {
    - PurpleXmlNode *next = child->next;
    - if (child->type != PURPLE_XMLNODE_TYPE_ATTRIB)
    - purple_xmlnode_free(child);
    - child = next;
    - }
    -}
    -
    -unsigned int ggp_xml_child_count(PurpleXmlNode *xml, const gchar *childName)
    -{
    - PurpleXmlNode *child;
    - unsigned int count = 0;
    -
    - g_return_val_if_fail(xml != NULL, 0);
    -
    - if (childName) {
    - child = purple_xmlnode_get_child(xml, childName);
    - while (child) {
    - child = purple_xmlnode_get_next_twin(child);
    - count++;
    - }
    - } else {
    - child = xml->child;
    - while (child) {
    - child = child->next;
    - count++;
    - }
    - }
    -
    - return count;
    -}
    --- a/libpurple/protocols/gg/xml.h Tue Apr 09 21:50:31 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,47 +0,0 @@
    -/* 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
    - */
    -
    -#ifndef PURPLE_GG_XML_H
    -#define PURPLE_GG_XML_H
    -
    -#include <purple.h>
    -
    -gboolean ggp_xml_get_string(const PurpleXmlNode *xml, const gchar *childName, gchar **var);
    -gboolean ggp_xml_get_bool(const PurpleXmlNode *xml, const gchar *childName, gboolean *var);
    -gboolean ggp_xml_get_uint(const PurpleXmlNode *xml, const gchar *childName, unsigned int *var);
    -
    -gboolean ggp_xml_set_string(PurpleXmlNode *xml, const gchar *childName, const gchar *val);
    -gboolean ggp_xml_set_bool(PurpleXmlNode *xml, const gchar *childName, gboolean val);
    -gboolean ggp_xml_set_uint(PurpleXmlNode *xml, const gchar *childName, unsigned int val);
    -
    -void ggp_xmlnode_remove_children(PurpleXmlNode *xml);
    -
    -unsigned int ggp_xml_child_count(PurpleXmlNode *xml, const gchar *childName);
    -
    -#endif /* PURPLE_GG_XML_H */
    --- a/libpurple/protocols/meson.build Tue Apr 09 21:50:31 2024 -0500
    +++ b/libpurple/protocols/meson.build Tue Apr 09 21:55:54 2024 -0500
    @@ -1,3 +1,2 @@
    subdir('bonjour')
    -subdir('gg')
    subdir('jabber')
    --- a/meson.build Tue Apr 09 21:50:31 2024 -0500
    +++ b/meson.build Tue Apr 09 21:55:54 2024 -0500
    @@ -276,35 +276,13 @@
    endif
    #######################################################################
    -# Check for Gadu-Gadu protocol library (libgadu)
    -#######################################################################
    -
    -libgadu = dependency('libgadu', version : '>= 1.12.0', required : get_option('libgadu'))
    -
    -if libgadu.found()
    - if not compiler.has_function('gg_is_gpl_compliant', dependencies : libgadu)
    - if get_option('libgadu').auto()
    - libgadu = disabler()
    - else
    - message('''
    -libgadu is not compatible with the GPL when compiled with OpenSSL support.
    -
    -To link against libgadu, please recompile it using:
    -./configure --with-openssl=no
    -Then rerun this Meson build
    - ''')
    - endif
    - endif
    -endif
    -
    -#######################################################################
    # Check for Xeme XMPP Library
    #######################################################################
    xeme = dependency('xeme')
    dependency('shoes', required : false)
    -DEFAULT_PRPLS = ['bonjour', 'demo', 'gg', 'ircv3', 'jabber', 'xmpp']
    +DEFAULT_PRPLS = ['bonjour', 'demo', 'ircv3', 'jabber', 'xmpp']
    dynamic_list = get_option('dynamic-prpls').split(',')
    if dynamic_list == ['all']
    @@ -316,8 +294,6 @@
    # The list was empty; do nothing.
    elif prpl == 'bonjour' and not enable_avahi
    # Do nothing.
    - elif prpl == 'gg' and not libgadu.found()
    - # Do nothing.
    elif prpl == 'xmpp' and not xeme.found()
    # Do nothing.
    else
    @@ -327,7 +303,6 @@
    DYNAMIC_BONJOUR = DYNAMIC_PRPLS.contains('bonjour')
    DYNAMIC_DEMO = DYNAMIC_PRPLS.contains('demo')
    -DYNAMIC_GG = DYNAMIC_PRPLS.contains('gg')
    DYNAMIC_IRCV3 = DYNAMIC_PRPLS.contains('ircv3')
    DYNAMIC_JABBER = DYNAMIC_PRPLS.contains('jabber')
    DYNAMIC_XMPP = DYNAMIC_PRPLS.contains('xmpp')