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>